vb調用vc++dll文件

 ① 關於DLL的創建與調用

  使用VC++的嚮導即可。具體操作如下:打開菜單“File\New”→選擇“Projects\Win32 Dynamic-Link Library”→選擇“A simple DLL project”即可。這時系統會自動生成3個文件:*.cpp,stdafx.cpp,stdafx.h。

  之後將入口函數DLLMain()補充完整,再添加你自定義的函數的代碼。如果你自定義的函數很多,可以將這些函數的聲明部分統一寫入一個頭文件中。再在.cpp文件首部用“#i nclude”語句引入這個頭文件。注意函數聲明前要加上“__declspec(dllexport)”。

  (如果你建DLL時選擇的是第三種類型(加入示例代碼),則在函數聲明及定義前都要加上系統定義的宏“*_API”。)

  在VB中用如下語句聲明:“Declare Function 函數名 Lib "完整路徑\文件名.dll" [Alias "函數別名"] (ByVal 變量1 As 類型1, ByVal 變量2 As 類型2,…) As 類型3”,與調用API函數類似。

  注意:若在窗體代碼的“通用”部分使用,“Declare”前要加“Private”;若在Moudle中使用,“Declare”前要加“Public”。若將DLL文件放在系統目錄(“\Windows\System”或“\WinNT\System32”)或程序可執行文件所在目錄下,“Lib”後只寫出DLL主文件名即可。

  具體的實例代碼見④(修正後的,可直接運行)。 

 

② 關於入口點

  如上編寫Cipher.dll,運行,出現錯誤信息“找不到DLL入口點(Error 453)”在調試中耗費1.5天時間在網上找資料解決此問題。

網上指出可能原因:

1。沒有注意函數名大小寫。DLL中的函數與VB中的函數聲明必須大小寫完全相同。

2。沒有聲明入口函數。需要在dll的.def文件中加上入口函數。

    如:EXPORTS

        SetData @1

        GetData @2

    如果DLL工程原本沒有使用.def文件,可以自己建立一個加入到工程中。

3。採用了C++編譯方式

    在C++中編譯函數時會將函數名進行轉換。解決的方法有兩個:

  (1)如果沒有使用C++的類,可以將.cpp文件改名爲.c,就不進行這種轉換了。

  (2)在函數定義前加上extern "c",如:

       extern "c" void _stdcall kk(double k)

需要指出的是,VC++允許不編寫.def文件,而是在函數定義前加

_declspec(dllexport)修飾符,這樣的函數也可以被外部程序調用。

 

實際問題所在:(.DEF是關鍵)

排查完上面可能存在的問題後,發現VB調用時依舊是“找不到DLL入口點”

查看VC程序結構,發現編譯時沒有將.DEF文件加載進工程裏面。

這就導致生成的DLL文件沒有外部接口。總結自己還是VC不熟悉所致。

 

出現這一錯誤的原因是C++編譯器在編譯時對函數名Encrypt作了修改。打開快速查看程序(D:\WINNT\System32\Viewers\QuikView.exe),將Cipher.dll拖入查看窗口,找到字段“?Encrypt@@YAHHH@Z”,發現函數名被加了一串字符。

  解決方法有二。第一,直接在VB聲明中將“?Encrypt@@Y AHHH@Z”作爲別名放在“Alias”後即可;第二,在Cipher.dll代碼中在語句“__declspec(dllexport) int __stdcall Encrypt(int p, int k);”前加上“extern "C" ”,編譯後,用QuikView查看,函數名變爲“_Encrypt”,之後再在VB聲明中做相應調整即可。

 (對於使用宏的DLL,在“#define”語句中,對宏“Cipher_API”的替換值做更改即可。)

  進行了③的更改後,程序又找不到入口點了。再用QuikView查看,發現函數名變爲“_Encrypt@8”。還有解決方法。在Cipher.dll工程中添加一個文本文件,命名爲“Cipher.def”,添加代碼如④。編譯後再用QuikView查看,函數名變回“Encrypt”,在VB中調用,運行正常。注意使用了.def文件,就不需要再使用“extern "C" ”了。

 

③ 關於調用約定

  採用②中第二種解決方法,運行,出現錯誤信息“DLL調用約定錯誤(Error 49)”。

原因是調用約定共有5種方式:__fastcall、__pascal、__stdcall、__cdecl及__thiscall(成員函數的調用方式,但不能使用它顯示聲明一個函數),VC++默認調用方式爲__cdecl,而VB默認調用方式則爲__stdcall。

 

解決方法是,更改代碼如下(指定調用方式):

  __declspec(dllexport) int __stdcall Encrypt(int p, int k);

      …多個函數…

   int __stdcall Encrypt(int p, int k)

     {

         int c = p + k;

         return c;

     }

 

④ 源代碼

Cipher.dll:

  Cipher.cpp內容:

  //引入預編譯頭文件  __declspec(dllexport)是關鍵

  #include “stdafx.h”

  #define CIPHER_API __declspec(dllexport)   //宏替換功能

   //聲明我的函數  

  CIPHER_API int __stdcall Encrypt( int p, int k )

 ………… 多個函數…

以上兩條語句等價於:

__declspec(dllexport) int __stdcall Encrypt(int p, int k);

  //DLL入口函數 (實驗過可選)

  BOOL APIENTRY DllMain(HANDLE hModule,

                                          DWORD ul_reason_for_call,

                                          LPVOID lpReserved)

   {

       switch (ul_reason_for_call)

       {

       case DLL_PROCESS_ATTACH: 

       case DLL_THREAD_ATTACH: 

       case DLL_THREAD_DETACH: 

       case DLL_PROCESS_DETACH:

           break;

       }

       return TRUE;

   }

   //我的函數__stdcall關鍵

   int __stdcall Encrypt(int p, int k)

   {

       int c = p + k;

       return c;

   }

…多個函數…

…………………

//我的問題:沒有將.DEF文件加入工程,導致生成的dll文件調用函數沒有入口點

Cipher.def:

  LIBRARY Cipher

  EXPORTS Encrypt

編譯後,將Cipher.dll複製到VB程序可執行文件所在目錄。

也可以在調用時輸入一個完整的路徑。

 

在VB中調用:

  Option Explicit

  Private Declare Function Encrypt Lib “Cipher” (ByVal p As Long, ByVal k As Long) As Long

  Private Sub Form_Load()

  Dim c As Long

  c = Encrypt(24, 8)

  Text1.Text = c

  End Sub

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章