1. 程式人生 > >DLL編寫與調用

DLL編寫與調用

情況 程序 this 運行 return 提高 這樣的 程序發布 instance

一 編寫動態鏈接庫DLL


DLL簡稱動態鏈接庫,是Windows中程序的重要組成部分。想象一下,一個程序需要多人共同完成開發,怎麽個共同法?這時我們就要考慮把程序分為好幾個模塊,團隊每一個成員開發一個模塊。問題來了:如何將模塊組合並成一個完整系統?還有,我們開發的軟件需要不斷升級,如何升級?難道每次非得把整個工程重新編譯一次再發布給用戶嗎?解決這些問題的科學辦法,就是開發動態鏈接庫DLL。
現在以開發myDLL.dll動態鏈接庫為例,講講BCB中開發動態鏈接庫的方法。
1、新建立一個工程:File-New-Other...在New卡中選擇DLL Wizard
2、將工程存為myDLL.bpr
3、在myDLL.cpp中寫接口代碼:
////---------------------------------------------------------------------------

#include <vcl.h>
#include <windows.h>
#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
////---------------------------------------------------------------------------
extern "C" __declspec(dllexport) __stdcall int myAdd(int,int);
extern "C" __declspec(dllexport) __stdcall AnsiString aboutMe(void);
int add(int n1,int n2);
#pragma argsused
int WINAPI DllEntryPoint(HINSTANCE hinst, unsigned long reason, void* lpReserved)
{
return 1;
}
////---------------------------------------------------------------------------
__declspec(dllexport) __stdcall int myAdd(int n1,int n2)
{
int T;
T=add(n1,n2);
return T;
}
int add(int n1,int n2)
{
return n1+n2;
}
__declspec(dllexport) __stdcall AnsiString aboutMe(void)
{
return "曾棕根好你個大笨蛋,居然現在才學會用DLL!半年前施勇強就告訴了你呀!研究進度太慢!";

}
4、需要註意的是,在編寫DLL這樣的程序時,要力求簡單,少用大量內存分配,盡量按照標準C的程序設計方法,以模塊化結構設計為好,少采用面向對象的程序設計方法。
5、進入Project-Options:
勾掉Linker頁中的Use Dynamic RTL
勾掉Packages頁中的Build with runtime packages
按一次Compiler中的Release按鈕
在Version Info頁中勾選Include version information in project,並勾選Auto-increment build number,再在裏面設置好版權信息
6、現在可以進入Project-Build myDLL生成myDLL.dll和myDLL.lib這兩個文件。

二 靜態調用動態鏈接庫DLL

調用DLL有兩種方式,一種是靜態調用,另一種就是動態調用。靜態調用需要LIB庫文件和DLL文件,程序編譯時,需要用到LIB文件,發布時這個LIB文件就不再需要,而且,編譯系統時,這個動態鏈接庫已編譯進程序,這樣,在程序一開始運行時就會查找這個DLL文件,如果這個DLL文件不存在,那麽,程序是啟動不起來的。相反,動態調用DLL則不是這樣,它只需要DLL文件,程序運行時,程序不需要知道這個DLL文件當前是否存在,只有當程序運行到某個點,才需要去調用DLL文件多個應用程序調用DLL時,DLL 在內存中只產生一個實例,因此,可以有效地節省內存空間,提高系統的運行效率。註意到,DLL 的編制與編程語言無關,只要遵守DLL的接口規範,許多語言都可以開發出高效的DLL程序,其它語言開發的DLL,同樣可以在BCB中調用。
下面介紹以myDLL.dll為例靜態調用DLL的步驟:
1、將myDLL.dll和myDLL.lib文件拷入到開發工程中,註意到,應用程序發布時,這個lib文件是不需要的。如果是其它語言開發的DLL,在沒有lib文件的情況下,可以用implib.exe工具程序,生成一個lib文件,用法:
implib.exe 文件名.lib 文件名.DLL
2、Project-Add to project將myDLL.lib庫導入到工程。
如果要從工程中清除庫文件,方法有兩種:
a、Project-Remove from project
b、View-Project Manager
3、在工程的Unit1.cpp中寫程序代碼:
////---------------------------------------------------------------------------

#include <vcl.h>
#pragma hdrstop

#include "Unit1.h"
////---------------------------------------------------------------------------
#pragma package(smart_init)
#pragma resource "*.dfm"
TForm1 *Form1;
extern "C" __declspec(dllimport) __stdcall int myAdd(int,int);
extern "C" __declspec(dllimport) __stdcall AnsiString aboutMe(void);
////---------------------------------------------------------------------------
__fastcall TForm1::TForm1(TComponent* Owner)
: TForm(Owner)
{
}
////---------------------------------------------------------------------------

void __fastcall TForm1::Button1Click(TObject *Sender)
{
int n;
n=myAdd(1,2);
ShowMessage(IntToStr(n));

}
////---------------------------------------------------------------------------
void __fastcall TForm1::Button2Click(TObject *Sender)
{
ShowMessage(aboutMe());
}
////---------------------------------------------------------------------------

三 動態調用動態鏈接庫DLL

動態調用DLL函數可分為八步:
第一步:函數定義。這裏的函數為地址轉換函數。下面這個函數其實就是定義 int __stdcall myAdd(int,int);
int __stdcall (*myAdd)(int,int);
第二步:定義模塊句柄,全局變量,它是載入DLL文件後的實例
HINSTANCE HmyDLL;
第三步:裝入DLL文件,同時獲得它的句柄
HmyDLL=LoadLibrary("myDLL.dll");
第四步:定義函數地址變量
FARPROC P;
第五步:獲取動態鏈接庫內的某一函數的內存地址
P=GetProcAddress(HmyDLL,"myAdd");
第六步:強制類型轉換,即將所獲取的函數地址強制轉換為函數
myAdd=(int __stdcall (__cdecl *)(int,int))P;
第七步:函數調用
n=myAdd(10,20);
第八步:釋放DLL
FreeLibrary(HmyDLL);

下面以動態調用myDLL.dll函數為例,進行講解:
////---------------------------------------------------------------------------

#include <vcl.h>
#pragma hdrstop

#include "Unit1.h"
////---------------------------------------------------------------------------
#pragma package(smart_init)
#pragma resource "*.dfm"
TForm1 *Form1;
////第一步:函數定義。這裏的函數為地址轉換函數。下面這個函數其實就是定義 int __stdcall myAdd(int,int);
int __stdcall (*myAdd)(int,int);
AnsiString __stdcall (*aboutMe)(void);
////第二步:定義模塊句柄,全局變量,它是載入DLL文件後的實例
HINSTANCE HmyDLL;
////---------------------------------------------------------------------------
__fastcall TForm1::TForm1(TComponent* Owner)
: TForm(Owner)
{
////第三步:裝入DLL文件,同時獲得它的句柄
HmyDLL=LoadLibrary("myDLL.dll");
}
////---------------------------------------------------------------------------

void __fastcall TForm1::Button1Click(TObject *Sender)
{
int n;
////第四步:定義函數地址變量
FARPROC P;
if(HmyDLL!=NULL)
{
////第五步:獲取動態鏈接庫內的某一函數的內存地址
P=GetProcAddress(HmyDLL,"myAdd");
if(P==NULL)
{
ShowMessage("打開myAdd()函數錯誤!");
}
else
{
////第六步:強制類型轉換,即將所獲取的函數地址強制轉換為函數
myAdd=(int __stdcall (__cdecl *)(int,int))P;
////第七步:函數調用
n=myAdd(10,20);
ShowMessage(IntToStr(n));
}
}
else
{
ShowMessage("打開動態鏈接庫文件myDLL.dll錯誤!");
}
}
////---------------------------------------------------------------------------

void __fastcall TForm1::FormDestroy(TObject *Sender)
{
////第八步:釋放DLL
FreeLibrary(HmyDLL);
}
////---------------------------------------------------------------------------
void __fastcall TForm1::Button2Click(TObject *Sender)
{
FARPROC P;
if(HmyDLL!=NULL)
{
P=GetProcAddress(HmyDLL,"aboutMe");
if(P==NULL)
{
ShowMessage("打開aboutMe()函數錯誤!");
}
else
{
aboutMe=(AnsiString __stdcall (__cdecl *)(void))P;
ShowMessage(aboutMe());
}
}
else
{
ShowMessage("打開動態鏈接庫文件myDLL.dll錯誤!");
}
}
////---------------------------------------------------------------------------
註意:動態調入myDLL.dll文件後,它在內存中只存在一個副本,這時,動態鏈接庫文件已於關閉狀態。

DLL編寫與調用