一. 編寫 DLL File/New/Dll 生成 Dll 的向?qū)В缓罂梢蕴砑訉?dǎo)出函數(shù)和導(dǎo)出類 導(dǎo)出函數(shù):extern "C" __declspec(dllexport) ExportType FunctionName(Parameter) 導(dǎo)出類:class __declspec(dllexport) ExportType ClassName{...} 例子:(說明:只是生成了一個(gè) DLL.dll )
#include "DllForm.h" // TDllFrm 定義
USERES("Dll.res"); USEFORM("DllForm.cpp", DllFrm);
class __declspec(dllexport) __stdcall MyDllClass { //導(dǎo)出類 public: MyDllClass(); void CreateAForm(); TDllFrm* DllMyForm; };
TDllFrm* DllMyForm2; extern "C" __declspec(dllexport) __stdcall void CreateFromFunct();//導(dǎo)出函數(shù)
//--------------------------------------------------------------------------- int WINAPI DllEntryPoint(HINSTANCE hinst, unsigned long reason, void*) { return 1; } //---------------------------------------------------------------------------
MyDllClass::MyDllClass() { }
void MyDllClass::CreateAForm() { DllMyForm = new TDllFrm(Application); DllMyForm->Show(); } //--------------------------------------------------------------------------- void __stdcall CreateFromFunct() { DllMyForm2 = new TDllFrm(Application); DllMyForm2->Show(); }
二. 靜態(tài)調(diào)用 DLL 使用 $BCB path\Bin\implib.exe 生成 Lib 文件,加入到工程文件中 將該文件拷貝到當(dāng)前目錄,使用 implib MyDll.lib MyDll.dll 生成 // Unit1.h // TForm1 定義 #include "DllForm.h" // TDllFrm 定義 //---------------------------------------------------------------------------
__declspec(dllimport) class __stdcall MyDllClass { public: MyDllClass(); void CreateAForm(); TDllFrm* DllMyForm; }; extern "C" __declspec(dllimport) __stdcall void CreateFromFunct();
class TForm1 : public TForm{...}
// Unit1.cpp // TForm1 實(shí)現(xiàn) void __fastcall TForm1::Button1Click(TObject *Sender) { // 導(dǎo)出類實(shí)現(xiàn),導(dǎo)出類只能使用靜態(tài)方式調(diào)用 DllClass = new MyDllClass(); DllClass->CreateAForm(); } //--------------------------------------------------------------------------- void __fastcall TForm1::Button2Click(TObject *Sender) { // 導(dǎo)出函數(shù)實(shí)現(xiàn) CreateFromFunct(); } //---------------------------------------------------------------------------
void __fastcall TForm1::FormClose(TObject *Sender, TCloseAction &Action) { delete DllClass; }
三. 動(dòng)態(tài)調(diào)用 DLL // Unit1.h class TForm1 : public TForm { ... private: // User declarations void (__stdcall *CreateFromFunct)(); ... }
// Unit1.cpp // TForm1 HINSTANCE DLLInst = NULL; void __fastcall TForm1::Button2Click(TObject *Sender) { if( NULL == DLLInst ) DLLInst = LoadLibrary("DLL.dll"); //上面的 Dll if (DLLInst) { CreateFromFunct = (void (__stdcall*)()) GetProcAddress(DLLInst, "CreateFromFunct"); if (CreateFromFunct) CreateFromFunct(); else ShowMessage("Could not obtain function pointer"); } else ShowMessage("Could not load DLL.dll"); }
void __fastcall TForm1::FormClose(TObject *Sender, TCloseAction &Action) { if ( DLLInst ) FreeLibrary (DLLInst); } 四. DLL 作為 MDIChild (子窗體) 【只編寫動(dòng)態(tài)調(diào)用的例子】 實(shí)際上,調(diào)用子窗體的 DLL 時(shí),系統(tǒng)只是檢查應(yīng)用程序的 MainForm 是否為 fsMDIForm 的窗體,這樣只
要把調(diào)用程序的 Application 的 Handle 傳遞給 DLL 的 Application 即可;同時(shí)退出 DLL 時(shí)也要恢復(fù)
Application
// MDIChildPro.cpp // Dll 實(shí)現(xiàn) CPP #include "unit1.h" // TForm1 定義 TApplication *SaveApp = NULL; int WINAPI DllEntryPoint(HINSTANCE hinst, unsigned long reason, void*) { if ( (reason==DLL_PROCESS_DETACH) && SaveApp ) Application = SaveApp ; // 恢復(fù) Application return 1; }
extern "C" __declspec(dllexport) __stdcall void TestMDIChild( //1024X768 TApplication* mainApp, LPSTR lpCaption) { if ( NULL == SaveApp ) // 保存 Application,傳遞 Application { SaveApp = Application; Application = mainApp; } // lpCaption 為子窗體的 Caption TForm1 *Form1 = new TForm1 ( Application, lpCaption ); Form1->Show(); } 注:上面的程序使用 BCB 3.0 編譯成功
五. BCB 調(diào)用 VC 編寫的 DLL 1. 名字分解: 沒有名字分解的函數(shù) TestFunction1 // __cdecl calling convention @TestFunction2 // __fastcall calling convention TESTFUNCTION3 // __pascal calling convention TestFunction4 // __stdcall calling convention 有名字分解的函數(shù) @TestFunction1$QV // __cdecl calling convention @TestFunction2$qv // __fastcall calling convention TESTFUNCTION3$qqrv // __apscal calling convention @TestFunction4$qqrv // __stdcall calling convention 使用 extern "C" 不會(huì)分解函數(shù)名
使用 Impdef MyLib.def MyLib.DLL 生成 def 文件查看是否使用了名字分解
2. 調(diào)用約定: __cdecl 缺省 是 Borland C++ 的缺省的 C 格式命名約定,它在標(biāo)識(shí)符前加一下劃線,以保留 它原來所有的全程標(biāo)識(shí)符。參數(shù)按最右邊參數(shù)優(yōu)先的原則傳遞給棧,然后清棧。 extaern "C" bool __cdecl TestFunction(); 在 def 文件中顯示為 TestFunction @1 注釋: @1 表示函數(shù)的順序數(shù),將在“使用別名”時(shí)使用。
__pascal Pascal格式 這時(shí)函數(shù)名全部變成大寫,第一個(gè)參數(shù)先壓棧,然后清棧。 TESTFUNCTION @1 //def file
__stdcall 標(biāo)準(zhǔn)調(diào)用 最后一個(gè)參數(shù)先壓棧,然后清棧。 TestFunction @1 //def file
__fastcall 把參數(shù)傳遞給寄存器 第一個(gè)參數(shù)先壓棧,然后清棧。 @TestFunction @1 //def file
3. 解決調(diào)用約定: Microsoft 與 Borland 的 __stdcall 之間的區(qū)別是命名方式。 Borland 采用 __stdcall 的方式去掉了名字起前的下劃線。 Microsoft 則是在前加上下劃線,在 后加上 @ ,再后跟為棧保留的字節(jié)數(shù)。字節(jié)數(shù)取決于參數(shù)在棧所占的空間。每一個(gè) 參數(shù)都舍入為 4 的倍數(shù)加起來。這種 Miocrosoft 的 DLL 與系統(tǒng)的 DLL 不一樣。
4. 使用別名: 使用別名的目的是使調(diào)用文件 .OBJ 與 DLL 的 .DEF 文件相匹配。如果還沒有 .DEF 文件,就應(yīng)該先建一個(gè)。然后把 DEF 文件加入 Project。使用別名應(yīng)不斷 修改外部錯(cuò)誤,如果沒有,還需要將 IMPORTS 部分加入 DEF 文件。 IMPORTS TESTFUNCTIOM4 = DLLprj.TestFunction4 TESTFUNCTIOM5 = DLLprj.WEP @500 TESTFUNCTIOM6 = DLLprj.GETHOSTBYADDR @51 這里需要說明的是,調(diào)用應(yīng)用程序的 .OBJ 名與 DLL 的 .DEF 文件名是等價(jià)的, 而且總是這樣。甚至不用考慮調(diào)用約定,它會(huì)自動(dòng)匹配。在前面的例子中,函數(shù)被 說明為 __pascal,因此產(chǎn)生了大寫函數(shù)名。這樣鏈接程序不會(huì)出錯(cuò)。
5. 動(dòng)態(tài)調(diào)用例子 VC DLL 的代碼如下: extern "C" __declspec(dllexport) LPSTR __stdcall BCBLoadVCWin32Stdcall() { static char strRetStdcall[256] = "BCB Load VC_Win32 Dll by __stdcall mode is OK!";
return strRetStdcall; }
extern "C" __declspec(dllexport) LPSTR __cdecl BCBLoadVCWin32Cdecl() { static char strRetCdecl[256] = "BCB Load VC_Win32 Dll by __cdecl mode is OK!";
return strRetCdecl; }
extern "C" __declspec(dllexport) LPSTR __fastcall BCBLoadVCWin32Fastcall() { static char strRetFastcall[256] = "BCB Load VC_Win32 Dll by __fastcall mode is OK!";
return strRetFastcall; }
其實(shí)動(dòng)態(tài)調(diào)用與調(diào)用 BCB 編寫的 DLL 沒有區(qū)別,關(guān)鍵是查看 DLL 的導(dǎo)出函數(shù)名字 可以使用 tdump.exe(BCB工具) 或者 dumpbin.exe(VC工具) 查看 tdump -ee MyDll.dll >1.txt (查看 1.txt 文件即可) 由于 VC6 不支持 __pascall 方式,下面給出一個(gè)三種方式的例子 void __fastcall TForm1::btnBLVCWin32DynClick(TObject *Sender) { /*cmd: tdbump VCWin32.dll >1.txt Turbo Dump Version 5.0.16.4 Copyright (c) 1988, 1998 Borland International Display of File VCWIN32.DLL
EXPORT ord:0000='BCBLoadVCWin32Fastcall::' EXPORT ord:0001='BCBLoadVCWin32Cdecl' EXPORT ord:0002='_BCBLoadVCWin32Stdcall@0' */ if ( !DllInst ) DllInst = LoadLibrary ( "VCWin32.dll" ); if ( DllInst ) { BCBLoadVCWin32Stdcall = (LPSTR (__stdcall *) () ) GetProcAddress ( DllInst, "_BCBLoadVCWin32Stdcall@0" ); //VC Dll // GetProcAddress ( DllInst, "BCBLoadVCWin32Stdcall" ); //BCB Dll if ( BCBLoadVCWin32Stdcall ) { ShowMessage( BCBLoadVCWin32Stdcall() ); } else ShowMessage ( "Can't find the __stdcall Function!" );
BCBLoadVCWin32Cdecl = (LPSTR (__cdecl *) () ) GetProcAddress ( DllInst, "BCBLoadVCWin32Cdecl" ); if ( BCBLoadVCWin32Cdecl ) { ShowMessage( BCBLoadVCWin32Cdecl() ); } else ShowMessage ( "Can't find the __cdecl Function!" );
//Why?不是 'BCBLoadVCWin32Fastcall::',而是 '@BCBLoadVCWin32Fastcall@0'? BCBLoadVCWin32Fastcall = (LPSTR (__fastcall *) () ) //GetProcAddress ( DllInst, "BCBLoadVCWin32Fastcall::" ); GetProcAddress ( DllInst, "@BCBLoadVCWin32Fastcall@0" ); if ( BCBLoadVCWin32Fastcall ) { ShowMessage( BCBLoadVCWin32Fastcall() ); } else ShowMessage ( "Can't find the __fastcall Function!" ); } else ShowMessage ( "Can't find the Dll!" ); }
6. 靜態(tài)調(diào)用例子 靜態(tài)調(diào)用有點(diǎn)麻煩,從動(dòng)態(tài)調(diào)用中可以知道導(dǎo)出函數(shù)的名字,但是直接時(shí)(加入 lib 文件到工程文件)
Linker 提示不能找到函數(shù)的實(shí)現(xiàn) 從 4 看出,可以加入 def 文件連接 (可以通過 impdef MyDll.def MyDll.dll 獲得導(dǎo)出表) 建立與 DLL 文件名一樣的 def 文件與 lib 文件一起加入到工程文件 上面的 DLL(VCWIN32.dll) 的 def 文件為(VCWIN32.def): LIBRARY VCWIN32.DLL
IMPORTS @BCBLoadVCWin32Fastcall = VCWIN32.@BCBLoadVCWin32Fastcall@0 _BCBLoadVCWin32Cdecl = VCWIN32.BCBLoadVCWin32Cdecl BCBLoadVCWin32Stdcall = VCWIN32._BCBLoadVCWin32Stdcall@0
對(duì)應(yīng)的函數(shù)聲明和實(shí)現(xiàn)如下: extern "C" __declspec(dllimport) LPSTR __fastcall BCBLoadVCWin32Fastcall(); extern "C" __declspec(dllimport) LPSTR __cdecl BCBLoadVCWin32Cdecl(); extern "C" __declspec(dllimport) LPSTR __stdcall BCBLoadVCWin32Stdcall();
void __fastcall TfrmStatic::btnLoadDllClick(TObject *Sender) { ShowMessage ( BCBLoadVCWin32Fastcall() ); ShowMessage ( BCBLoadVCWin32Cdecl() ); ShowMessage ( BCBLoadVCWin32Stdcall() ); } 注意:在 BCB 5.0 中,可能直接按下 F9 是不能通過 Linker 的,請(qǐng)先 Build 一次 注:上面的程序使用 BCB 5.0 與 VC6.0 編譯成功
|