前言 網路上流傳的BCB編寫和調用DLL的方法多來源於一篇《BCB 編寫 DLL 終極手冊》,多數網站在轉載此文章時也並未註明出處和作者,甚為心寒,且在轉載過程中難免有紕漏,致使一些例子無法正確運行,我根據網路資料,重新整理了一下,發布出來。 如欲轉載,請註明出處和作者,並向作者發一封郵件,謝謝。 一.注意: 建立動態連結程式庫時,如果想你建立的動態連結程式庫並非只用於Borland開發工具,那麼就需要遵循發下規則: (1).在匯出函數的傳回值和參數中不要使用Borland特有的資料類型和結構體,如AnsiString之類,請使用C/C++標準的資料類型或使用C/C++標準資料類型定義的結構體(特別不要使用String資料類型,BCB DLL嚮導產生的DLL工程檔案中大篇幅的說明就是對此的說明,請自己查閱); (2).請使用extern "C"命名規範,這樣,產生的DLL中的匯出函數,就不會使用C++的命名規範,而是使用的C命名規範,即匯出函數不會名字分解,而是和你定義的函數相同; (3).匯出函數請使用WIN32 API的調用方式__stdcall(即WINAPI)或VC與BorlandC++的呼叫慣例__cdecl,不要使用Borland特有的__fastcall呼叫慣例,否則只有Borland開發工具才可以使用這些動態連結程式庫;二.執行個體: (1).匯出函數(不使用VCL) 使用BCB建立DLL嚮導來建立一個工程,選擇不使用VCL,代碼如下: //--------------------------------------------------------------------------- #include //--------------------------------------------------------------------------- // Important note about DLL memory management when your DLL uses the // static version of the RunTime Library: // // If your DLL exports any functions that pass String objects (or structs/ // classes containing nested Strings) as parameter or function results, // you will need to add the library MEMMGR.LIB to both the DLL project and // any other projects that use the DLL. You will also need to use MEMMGR.LIB // if any other projects which use the DLL will be performing new or delete // operations on any non-TObject-derived classes which are exported from the // DLL. Adding MEMMGR.LIB to your project will change the DLL and its calling // EXE's to use the BORLNDMM.DLL as their memory manager. In these cases, // the file BORLNDMM.DLL should be deployed along with your DLL. // // To avoid using BORLNDMM.DLL, pass string information using "char *" or // ShortString parameters. // // If your DLL uses the dynamic version of the RTL, you do not need to // explicitly add MEMMGR.LIB as this will be done implicitly for you //--------------------------------------------------------------------------- #pragma argsused int WINAPI DllEntryPoint(HINSTANCE hinst, unsigned long reason, void* lpReserved) { return 1; } //--------------------------------------------------------------------------- extern "C" __declspec(dllexport) int __stdcall Calc(int a, int b) //匯出函數 { return a+b; } //--------------------------------------------------------------------------- 儲存工程為DLLs,編譯,就會在工程目錄下產生DLLs.dll(大小應該是8K),同時還會自動在同一目錄下產生DLLs.lib靜態庫,如果沒有產生,請自己手動使用implib.exe工具產生(使用方法見本文說明)。 (2).匯出函數(使用VCL) 使用BCB DLL嚮導建立一個工程,選擇使用VCL支援,建立立一個表單(採用預設名稱TForm1)存為Unit1.cpp,設定表單大小為一般OKCANCEL對框的大小(這裡設為277*119),在表單上添加兩個按鈕:Button1標籤為"確定",ModalResult為mrOk,Button2標籤為"取消",ModalResult為mrCancel; 然後在DLL主程式中引用剛才建立的表單,並添加匯出函數,代碼如下: //--------------------------------------------------------------------------- #include #include #pragma hdrstop //--------------------------------------------------------------------------- // Important note about DLL memory management when your DLL uses the // static version of the RunTime Library: // // If your DLL exports any functions that pass String objects (or structs/ // classes containing nested Strings) as parameter or function results, // you will need to add the library MEMMGR.LIB to both the DLL project and // any other projects that use the DLL. You will also need to use MEMMGR.LIB // if any other projects which use the DLL will be performing new or delete // operations on any non-TObject-derived classes which are exported from the // DLL. Adding MEMMGR.LIB to your project will change the DLL and its calling // EXE's to use the BORLNDMM.DLL as their memory manager. In these cases, // the file BORLNDMM.DLL should be deployed along with your DLL. // // To avoid using BORLNDMM.DLL, pass string information using "char *" or // ShortString parameters. // // If your DLL uses the dynamic version of the RTL, you do not need to // explicitly add MEMMGR.LIB as this will be done implicitly for you //--------------------------------------------------------------------------- #include "Unit.h" //引用設計的表單 #pragma argsused int WINAPI DllEntryPoint(HINSTANCE hinst, unsigned long reason, void* lpReserved) { return 1; } //--------------------------------------------------------------------------- extern "C" __declspec(dllexport) int __stdcall UserClick(void) //匯出函數 { TForm1 *Form1 = new TForm1(NULL); if(Form1->ShowModal() == mrOk) { delete Form1; return 1; } else { delete Form1; return 0; } } //--------------------------------------------------------------------------- 儲存工程為DLLs,編譯,就會在工程目錄下產生DLLs.dll,同時還會自動在同一目錄下產生DLLs.lib靜態庫,如果沒有產生,請自己手動使用implib.exe工具產生。 (3).匯出類 使用BCB建立DLL嚮導來建立一個工程,選擇不使用VCL(本例不使用VCL,是否支援VCL是視你自己的應用而定),代碼如下: //--------------------------------------------------------------------------- #include //--------------------------------------------------------------------------- // Important note about DLL memory management when your DLL uses the // static version of the RunTime Library: // // If your DLL exports any functions that pass String objects (or structs/ // classes containing nested Strings) as parameter or function results, // you will need to add the library MEMMGR.LIB to both the DLL project and // any other projects that use the DLL. You will also need to use MEMMGR.LIB // if any other projects which use the DLL will be performing new or delete // operations on any non-TObject-derived classes which are exported from the // DLL. Adding MEMMGR.LIB to your project will change the DLL and its calling // EXE's to use the BORLNDMM.DLL as their memory manager. In these cases, // the file BORLNDMM.DLL should be deployed along with your DLL. // // To avoid using BORLNDMM.DLL, pass string information using "char *" or // ShortString parameters. // // If your DLL uses the dynamic version of the RTL, you do not need to // explicitly add MEMMGR.LIB as this will be done implicitly for you //--------------------------------------------------------------------------- #pragma argsused int WINAPI DllEntryPoint(HINSTANCE hinst, unsigned long reason, void* lpReserved) { return 1; } //--------------------------------------------------------------------------- __declspec(dllexport) __stdcall class TTest{ //聲明匯出類 int a,b; public: void __stdcall SetValue(int x, int y); int __stdcall Calc(void); }; //--------------------------------------------------------------------------- void __stdcall TTest::SetValue(int x, int y) //類的實現 { this->a = x; this->b = y; } //--------------------------------------------------------------------------- int __stdcall TTest::Calc(void) //類的實現 { return this->a + this->b; } //--------------------------------------------------------------------------- 儲存工程為DLLs,編譯,就會在工程目錄下產生DLLs.dll,同時還會自動在同一目錄下產生DLLs.lib靜態庫,如果沒有產生,請自己手動使用implib.exe工具產生。 注意,並非所有的代碼都需要在工程DLL主檔案中寫,匯出函數和類等都可以在其它單元中設計,然後在主檔案中#include即可,看自己的習慣,當然了,一個巨大的DLL工程,不可能寫在一個檔案中的。 (4).使用(1)建立的DLL 建立一個工程。 [1].靜態調用: 向工程中添加(1)中產生的靜態庫DLLs.lib,然後在.h中添加匯出函數的定義,如下: extern "C" __declspec(dllimport) int __stdcall Calc(int,int); //匯出DLL中函數 然後就可以在這個單元中使用int __stdcall Calc(int,int)這個函數了,如: ShowMessage(IntToStr(Calc(5,10)); [2].動態調用 不需要添加靜態庫.lib檔案,只要在需要調用時才載入DLL,如下: HINSTANCE Hdl; int __stdcall (*Calc)(int,int); //定義函數原型 Hdl = ::LoadLibrary("DLLs.dll"); //載入DLL if(Hdl != NULL) { Calc = (int __stdcall (*)(int,int))::GetProcAddress(Hdl,"Calc"); //擷取函數入口地址 if(Calc != NULL) { ShowMessage(IntToStr(Calc(5,10))); //調用DLL中函數 } else { ShowMessage("不能找到函數入口!"); } ::FreeLibrary(Hdl); //一定不要忘記調用完畢後釋放DLL } else { ShowMessage("不能載入DLL!"); } (5).使用(2)建立的DLL [1].靜態調用: 向工程中添加(2)中產生的靜態庫DLLs.lib,然後在.h中添加匯出函數的定義,如下: extern "C" __declspec(dllimport) int __stdcall UserClick(void); //匯出DLL中函數 然後就可以在這個單元中使用int __stdcall UserClick(void)這個函數了,如: if(UserClick()) { ShowMessage("使用者點擊“確定”"); } else { ShowMessage("使用者點擊“取消”或直接關閉"); } [2].動態調用 不需要添加靜態庫.lib檔案,只要在需要調用時才載入DLL,如下: HINSTANCE Hdl; int __stdcall (*UserClick)(void); Hdl = ::LoadLibrary("DLLs.dll"); if(Hdl != NULL) { UserClick = (int __stdcall (*)(void))::GetProcAddress(Hdl,"UserClick"); if(UserClick != NULL) { if(UserClick()) { ShowMessage("使用者點擊“確定”"); } else { ShowMessage("使用者點擊“取消”或直接關閉"); } } else { ShowMessage("不能找到函數入口!"); } ::FreeLibrary(Hdl); } else { ShowMessage("不能載入DLL!"); } (6).使用(3)建立的DLL DLL中匯出的類,只能使用表態的方式來調用。本例中的DLL調用方法如下: 向工程中添加(3)中產生的靜態庫DLLs.lib,然後在.h中添加匯出類的聲明,如下: __declspec(dllimport) __stdcall class TTest{ int a,b; public: void __stdcall SetValue(int x, int y); int __stdcall Calc(void); }; 然後就可以在這個單元中使用TTest這個類了,如: TTest *Inst = new TTest; Inst->SetValue(5,10); ShowMessage(IntToStr(Inst->Calc())); delete Inst; 三.一些工具: (1).impdef.exe 用法:impdef.exe deffile.def yourdll.dll 產生指定DLL檔案的def檔案,可以用它來查看DLL中的函式宣告。例如,BCB使用VC的DLL時,可能需要查看一下VC中匯出函數的函數名;或者未使用extern "C"呼叫慣例時,可以用它來查看DLL中匯出函數的C++命名方式,從而可以正確調用。 (2).implib.exe 用法:implib.exe libfile.lib yourdll.dll 用此工具來產生DLL調用的靜態庫,用於DLL的靜態調用方式。 (3).vctobpru.exe 用此工具可以把VC的工程匯入到BCB工程中,它自動轉換VC的工程檔案為BCB工程檔案,產生相應的.bpr等檔案。 (4).tdump.exe(VC中為dumpbin.exe) 用法:tdump.exe [options] [inputfile] [listfile] [options] 用此工具可以查看DLL中的匯出函式宣告等等,具體用法請運行tdump.exe(不加參數)。 |