一、 編寫第一個(gè)入門級dll文件 1. 新建一個(gè)dlltest的dll工程,加入一源文件dlltest.cpp,包含add和subtract兩個(gè)函數(shù)如下: _declspec(dllexport) int add(int a,int b)
{
return a+b;
}
_declspec(dllexport) int subtract(int a,int b)
{
return a-b;
} 注意: 在函數(shù)名前加上_declspec(dllexport),這樣編譯后在連接的時(shí)候才會(huì)生成dlltest.lib(引入庫文件)和dlltest.exp(輸出庫文件),并且在命令提示符下用dumpbin命令可以看到導(dǎo)出了哪些函數(shù)。 方法:在命令提示符下,轉(zhuǎn)到dll文件所在目錄下,輸入dumpbin –exports dlltest.dll,列出導(dǎo)出函數(shù)如下:
這里注意到函數(shù)名字已經(jīng)被改成了?add@@YAHHH@Z,這種現(xiàn)象叫做名字粉碎,是為了支持函數(shù)重載而做的。 2.編寫一個(gè)基于對話框的MFC程序測試DLL,工程名為calldll,放置兩個(gè)按紐add和subtract,響應(yīng)按紐消息,調(diào)用這個(gè)DLL的add和subtract函數(shù)。 先添加響應(yīng)按鈕消息的函數(shù)OnAdd和OnSubtract,然后在函數(shù)所在源文件中寫完整函數(shù)體如下: //extern int add(int,int); //extern int subtract(int,int); _declspec(dllimport) int add(int,int); _declspec(dllimport) int subtract(int,int); void CCalldllDlg::OnAdd() { // TODO: Add your control notification handler code here CString str; str.Format('2+3=%d',add(2,3)); MessageBox(str); } void CCalldllDlg::OnSubtract() { // TODO: Add your control notification handler code here CString str; str.Format('3-2=%d',subtract(3,2)); MessageBox(str); } 這里采用隱式鏈接的方式加載動(dòng)態(tài)鏈接庫: 注意:要用關(guān)鍵字extern先聲明這兩個(gè)函數(shù),表明這兩個(gè)函數(shù)是在外部定義的(不過程序中將它注釋掉了)。最好是用_declspec(dllimport)表明函數(shù)是從動(dòng)態(tài)鏈接庫的lib文件中引入的,這樣效率更高。 將文件dlltest.lib拷貝到此工程目錄下,并在Project Settings的Link標(biāo)簽下添加此文件:(否則編譯會(huì)成功,但連接時(shí)會(huì)出錯(cuò)提示找不到函數(shù)的定義)
編譯連接生成calldll.exe,這時(shí)用Dumpbin -importscalldll.exe查看它的輸入信息,可以看到它加載了dlltest.dll。 運(yùn)行calldll.exe(要成功運(yùn)行還需將dlltest.dll拷貝到工程目錄或此exe所在目錄下)
這樣,最簡單的第一個(gè)dll就完成了。 二、 編寫涉及到類和頭文件的dll文件 1. 新建一個(gè)dlltest的dll工程,加入一頭文件dlltest.h和一源文件dlltest.cpp。 dlltest.h: #ifdef DLL_API
#else
#define DLL_API _declspec(dllimport)
#endif
DLL_API int add(int,int);
DLL_API int subtract(int,int);
class DLL_API Point1 //將整個(gè)類導(dǎo)出
{
public:
void output(int x,int y);
void output1(int x,int y);
};
class Point2
{
public:
DLL_API void output(int x,int y); //僅導(dǎo)出類中的一個(gè)函數(shù)
void output1(int x,int y);
}; dlltest.cpp: #define DLL_API _declspec(dllexport) #include 'dlltest.h' #include <windows.h> #include <stdio.h> int add(int a,int b) { return a+b; } int subtract(int a,int b) { return a-b; } void Point1::output(int x,int y) { HWND hwnd=GetForegroundWindow(); HDC hdc=GetDC(hwnd); char buf[50]; memset(buf,0,50); sprintf(buf,'x=%d,y=%d',x,y); TextOut(hdc,0,0,buf,strlen(buf)); } void Point1::output1(int x,int y) { HWND hwnd=GetForegroundWindow(); HDC hdc=GetDC(hwnd); char buf[50]; memset(buf,0,50); sprintf(buf,'x=%d,y=%d',x,y); TextOut(hdc,0,0,buf,strlen(buf)); } void Point2::output(int x,int y) { HWND hwnd=GetForegroundWindow(); HDC hdc=GetDC(hwnd); char buf[50]; memset(buf,0,50); sprintf(buf,'x=%d,y=%d',x,y); TextOut(hdc,0,20,buf,strlen(buf)); } void Point2::output1(int x,int y) { HWND hwnd=GetForegroundWindow(); HDC hdc=GetDC(hwnd); char buf[50]; memset(buf,0,50); sprintf(buf,'x=%d,y=%d',x,y); TextOut(hdc,0,20,buf,strlen(buf)); } 2. 編寫一個(gè)基于對話框的MFC程序測試DLL,此時(shí)在工程中包含上面這個(gè)頭文件,不用在聲明導(dǎo)入的類和函數(shù)了。 三、 使用模塊定義文件和動(dòng)態(tài)加載動(dòng)態(tài)鏈接庫 1. 新建dlltest.dll工程,加入一源文件dlltest.cpp,包含add和subtract兩個(gè)函數(shù)如下: int add(int a,int b)
{
return a+b;
}
int subtract(int a,int b)
{
return a-b;
}
2. 使用模塊定義文件防止文件名改變,在目錄中新建dlltest.def文件,增加到工程。 LIBRARY dlltest EXPORTS add subtract 注:LIBRARY和EXPORTS的用法參照MSDN. 3. 編譯后用dumpbin查看到函數(shù)名沒有改變。
4. 用動(dòng)態(tài)加載的方法來調(diào)用dll文件。 void CCalldllDlg::OnAdd()
{
// TODO: Add your control notification handler code here
HINSTANCE hInst;
hInst=LoadLibrary('dlltest.dll');
typedef int (/*_stdcall*/ *ADDPROC)(int a,int b);
//ADDPROC Add=(ADDPROC)GetProcAddress(hInst,'?add@@YAHHH@Z');
//ADDPROC Add=(ADDPROC)GetProcAddress(hInst,MAKEINTRESOURCE(1));
ADDPROC Add=(ADDPROC)GetProcAddress(hInst,'add');
if(!Add)
{
MessageBox('獲取函數(shù)地址失??!');
return;
}
CString str;
str.Format('2+3=%d',Add(2,3));
MessageBox(str);
FreeLibrary(hInst);
} 注意: 1. 這里調(diào)用的GetProcAddress函數(shù)中的第二個(gè)參數(shù)是動(dòng)態(tài)鏈接庫中導(dǎo)出的函數(shù)名,如果動(dòng)態(tài)鏈接庫中沒有用模塊定義文件防止函數(shù)名粉碎,則要用注釋掉的GetProcAddress(hInst,'?add@@YAHHH@Z'),另外也可按序號訪問GetProcAddress(hInst,MAKEINTRESOURCE(1))。 2. 使用模塊定義文件后,如果改變調(diào)用約定為_stdcall,函數(shù)名也不會(huì)被改變,但如果加上_stdcall定義函數(shù),調(diào)用時(shí)也需要加上_stdcall,否則會(huì)出錯(cuò)。 3. 動(dòng)態(tài)加載不需要將文件dlltest.lib拷貝到此工程目錄下,并在Project Settings的Link標(biāo)簽下添加此文件,只需將dll文件拷貝到工程目錄下即可,并且通過dumpbin -imports calldll.exe查看它的輸入信息時(shí),可以看到它并沒有加載dlltest.dll。 4. 動(dòng)態(tài)加載的好處是需要時(shí)再加載,可以提高執(zhí)行的效率。當(dāng)不使用DLL時(shí),調(diào)用FreeLibrary減少DLL的使用計(jì)數(shù),釋放DLL資源,減少系統(tǒng)負(fù)擔(dān)。 5. 不使用模塊定義文件,也可用extern 'C'使函數(shù)名保持不變,如#define DLL1_API extern 'C' _declspec(dllexport),但它只能導(dǎo)出全局函數(shù),不能導(dǎo)出類的成員函數(shù),并且如果調(diào)用約定被改成了別的方式,此時(shí)函數(shù)名也被改變,所以一般不用這種方式。 |
|