VC DLL总结
一、DLL的導(dǎo)出方法
1、使用_declspec(dllexport) 方法
???? DLL里全是C++的類(lèi)的話,你無(wú)法在DEF里指定導(dǎo)出的函數(shù),只能用__declspec(dllexport)導(dǎo)出類(lèi)。
extern "C" _declspec(dllexport) int sum(int a,int b);//本文所有的例子只有一個(gè)sum即加法函數(shù)。
???? ? 在制作DLL導(dǎo)出函數(shù)時(shí)由于C++存在函數(shù)重載,因此__declspec(dllexport) function(int,int) 在DLL會(huì)被decorate,例如被decorate成為 function_int_int,而且不同的編譯器decorate的方法不同,造成了在用GetProcAddress取得function地址時(shí)的不便,使用extern "C"時(shí),上述的decorate不會(huì)發(fā)生,因?yàn)镃沒(méi)有函數(shù)重載,但如此一來(lái)被extern"C"修飾的函數(shù),就不具備重載能力,可以說(shuō)extern 和 extern "C"不是一回事。
??????? 在VC++中,如果生成DLL可以不使用.def文件。只需要在VC++的函數(shù)定義前要加__declspec(dllexport)修飾就可以了。但是使用__declspec(dllexport)和使用.def文件是有區(qū)別的。如果DLL是提供給VC++用戶使用的,只需要把編譯DLL時(shí)產(chǎn)生的.lib提供給用戶,它可以很輕松地調(diào)用你的DLL。但是如果DLL是供VB、PB、Delphi用戶使用的,那么會(huì)產(chǎn)生一個(gè)小麻煩。因?yàn)閂C++對(duì)于__declspec(dllexport)聲明的函數(shù)會(huì)進(jìn)行名稱(chēng)轉(zhuǎn)換,如下面的函數(shù):
????__declspec(dllexport) int __stdcall IsWinNT()
????會(huì)轉(zhuǎn)換為IsWinNT@0,這樣你在VB中必須這樣聲明:
????Declare Function IsWinNT Lib "my.dll" Alias "IsWinNT@0" () As Long
????@的后面的數(shù)由于參數(shù)類(lèi)型不同而可能不同。這顯然不太方便。所以如果要想避免這種轉(zhuǎn)換,就要使用.def文件方式。
????EXPORTS后面的數(shù)可以不給,系統(tǒng)會(huì)自動(dòng)分配一個(gè)數(shù)。對(duì)于VB、PB、Delphi用戶,通常使用按名稱(chēng)進(jìn)行調(diào)用的方式,這個(gè)數(shù)關(guān)系不大,但是對(duì)于使用.lib鏈接的VC程序來(lái)說(shuō),不是按名稱(chēng)進(jìn)行調(diào)用,而是按照這個(gè)數(shù)進(jìn)行調(diào)用的,所以最好給出。如:
EXPORTS?
test @1?
?????? 若要導(dǎo)出函數(shù),__declspec(dllexport) 關(guān)鍵字必須出現(xiàn)在調(diào)用約定關(guān)鍵字的左邊(如果指定了關(guān)鍵字)。例如:
復(fù)制代碼
__declspec(dllexport) void __cdecl Function1(void);
若要導(dǎo)出類(lèi)中的所有公共數(shù)據(jù)成員和成員函數(shù),關(guān)鍵字必須出現(xiàn)在類(lèi)名的左邊,如下所示:
復(fù)制代碼
class __declspec(dllexport) CExampleExport : public CObject
{ ... class definition ... };
2、使用def文件定義導(dǎo)出
?
.def 文件必須至少包含下列模塊定義語(yǔ)句:
?
-
文件中的第一個(gè)語(yǔ)句必須是 LIBRARY 語(yǔ)句。此語(yǔ)句將 .def 文件標(biāo)識(shí)為屬于 DLL。LIBRARY 語(yǔ)句的后面是 DLL 的名稱(chēng)。鏈接器將此名稱(chēng)放到 DLL 的導(dǎo)入庫(kù)中。
-
EXPORTS 語(yǔ)句列出名稱(chēng),可能的話還會(huì)列出 DLL 導(dǎo)出函數(shù)的序號(hào)值。通過(guò)在函數(shù)名的后面加上 @ 符和一個(gè)數(shù)字,給函數(shù)分配序號(hào)值。當(dāng)指定序號(hào)值時(shí),序號(hào)值的范圍必須是從 1 到 N,其中 N 是 DLL 導(dǎo)出函數(shù)的個(gè)數(shù)。如果希望按序號(hào)導(dǎo)出函數(shù)。
-
.def 文件中的注釋由每個(gè)注釋行開(kāi)始處的分號(hào) (;) 指定,且注釋不能與語(yǔ)句共享
-
用def文件還可以改變導(dǎo)出的函數(shù)的名字,例如?? myadd = add? 使得導(dǎo)出的函數(shù)叫myadd,而不是add
二、 dll文件的導(dǎo)入
1. 隱式的加載時(shí)鏈接
VC中加載DLL的LIB文件的方法有以下三種:
????? ①LIB文件直接加入到工程文件列表中
在VC中打開(kāi)File View一頁(yè),選中工程名,單擊鼠標(biāo)右鍵,然后選中“Add Files to Project”菜單,在彈出的文件對(duì)話框中選中要加入DLL的LIB文件即可。
????? ②設(shè)置工程的 Project Settings來(lái)加載DLL的LIB文件
打開(kāi)工程的 Project Settings菜單,選中Link,然后在Object/library modules下的文本框中輸入DLL的LIB文件。
?????? ③通過(guò)程序代碼的方式
加入預(yù)編譯指令#pragma comment (lib,”*.lib”),這種方法優(yōu)點(diǎn)是可以利用條件預(yù)編譯指令鏈接不同版本的LIB文件。因?yàn)?#xff0c;在Debug方式下,產(chǎn)生的LIB文件是Debug版本,如Regd.lib;在Release方式下,產(chǎn)生的LIB文件是Release版本,如Regr.lib。
?????? 當(dāng)應(yīng)用程序?qū)LL的LIB文件加載后,還需要把DLL對(duì)應(yīng)的頭文件(*.h)包含到其中,在這個(gè)頭文件中給出了DLL中定義的函數(shù)原型,然后聲明。
?????? 在創(chuàng)建DllTest.exe文件之前,要先將MyDll.dll和MyDll.lib拷貝到當(dāng)前工程所在的目錄下面,也可以拷貝到windows的System目錄下。如果DLL使用的是def文件,要?jiǎng)h除TestDll.h文件中關(guān)鍵字extern "C"。TestDll.h文件中的關(guān)鍵字Progam commit是要Visual C+的編譯器在link時(shí),鏈接到MyDll.lib文件,當(dāng)然,開(kāi)發(fā)人員也可以不使用#pragma comment(lib,"MyDll.lib")語(yǔ)句,而直接在工程的Setting->Link頁(yè)的Object/Moduls欄填入MyDll.lib既可。
2.顯式鏈接
顯式鏈接是應(yīng)用程序在執(zhí)行過(guò)程中隨時(shí)可以加載DLL文件,也可以隨時(shí)卸載DLL文件,這是隱式鏈接所無(wú)法作到的,所以顯式鏈接具有更好的靈活性,對(duì)于解釋性語(yǔ)言更為合適。不過(guò)實(shí)現(xiàn)顯式鏈接要麻煩一些。在應(yīng)用程序中用LoadLibrary或MFC提供的AfxLoadLibrary顯式的將自己所做的動(dòng)態(tài)鏈接庫(kù)調(diào)進(jìn)來(lái),動(dòng)態(tài)鏈接庫(kù)的文件名即是上述兩個(gè)函數(shù)的參數(shù),此后再用GetProcAddress()獲取想要引入的函數(shù)。自此,你就可以象使用如同在應(yīng)用程序自定義的函數(shù)一樣來(lái)調(diào)用此引入函數(shù)了。在應(yīng)用程序退出之前,應(yīng)該用FreeLibrary或MFC提供的AfxFreeLibrary釋放動(dòng)態(tài)鏈接庫(kù)。下面是通過(guò)顯式鏈接調(diào)用DLL中的Max函數(shù)的例子。
#include
#include
void main(void)
{
typedef int(*pMax)(int a,int b);
typedef int(*pMin)(int a,int b);
HINSTANCE hDLL;
PMax Max
HDLL=LoadLibrary("MyDll.dll");//加載動(dòng)態(tài)鏈接庫(kù)MyDll.dll文件;
Max=(pMax)GetProcAddress(hDLL,"Max");
A=Max(5,8);
Printf("比較的結(jié)果為%d\n",a);
FreeLibrary(hDLL);//卸載MyDll.dll文件;
}
在上例中使用類(lèi)型定義關(guān)鍵字typedef,定義指向和DLL中相同的函數(shù)原型指針,然后通過(guò)LoadLibray()將DLL加載到當(dāng)前的應(yīng)用程序中并返回當(dāng)前DLL文件的句柄,然后通過(guò)GetProcAddress()函數(shù)獲取導(dǎo)入到應(yīng)用程序中的函數(shù)指針,函數(shù)調(diào)用完畢后,使用FreeLibrary()卸載DLL文件。在編譯程序之前,首先要將DLL文件拷貝到工程所在的目錄或Windows系統(tǒng)目錄下。
使用顯式鏈接應(yīng)用程序編譯時(shí)不需要使用相應(yīng)的Lib文件。另外,使用GetProcAddress()函數(shù)時(shí),可以利用MAKEINTRESOURCE()函數(shù)直接使用DLL中函數(shù)出現(xiàn)的順序號(hào),如將GetProcAddress(hDLL,"Min")改為GetProcAddress(hDLL, MAKEINTRESOURCE(2))(函數(shù)Min()在DLL中的順序號(hào)是2),這樣調(diào)用DLL中的函數(shù)速度很快,但是要記住函數(shù)的使用序號(hào),否則會(huì)發(fā)生錯(cuò)誤。
?顯式調(diào)用的方法很簡(jiǎn)單,調(diào)用LoadLibrary()和GetProcAddress()即可。
HMODULE LoadLibrary(LPCTSTR lpFileName // file name of module);參數(shù)解釋:lpFileName:指定要載入的動(dòng)態(tài)鏈接庫(kù)的名稱(chēng)。 返回值:如何函數(shù)調(diào)用成功,則返回值是庫(kù)模塊的句柄,否則返回NULL。FARPROC GetProcAddress(HMODULE hModule, // handle to DLL moduleLPCSTR lpProcName // function name ); ? 參數(shù)解釋:hModule:包含此函數(shù)的DLL模塊的句柄。LoadLibrary或者GetModuleHandle函數(shù)可以返回此句柄。 lpProcName:包含函數(shù)名的以NULL結(jié)尾的字符串,或者指定函數(shù)的序數(shù)值。 返回值:如果函數(shù)調(diào)用成功,返回值是DLL中的輸出函數(shù)地址,否則返回NULL。 如下羅列出DLL顯式調(diào)用的流程: (1)使用LoadLibrary()加載指定名稱(chēng)的DLL。 (2)使用GetProcAddress()獲取指定名稱(chēng)的函數(shù)地址,并將其轉(zhuǎn)換成函數(shù)類(lèi)型。 (3)通過(guò)函數(shù)地址調(diào)用函數(shù)。 (4)調(diào)用FreeLibrary()函數(shù)卸載DLL。 假設(shè)在DLL中有一導(dǎo)出函數(shù):extern "C" __declspec(dllexport)? int tulip (int a ,int b); 在程序中調(diào)用DLL中的這一函數(shù): //定義一個(gè)函數(shù)指針?typedef int (? * FUNCTION )(int,int);? ?? int main() { HINSTANCE hLib; hLib = ::LoadLibrary(_T("MyDll.dll")); FUNCTION tulipFunc; //定義一個(gè)函數(shù)指針變量 tulipFunc =(FUNCTION )::GetProcAddress(hLib ,"tulip");? //調(diào)用dll里的函數(shù)
tulipFunc(8,10); ::FreeLibrary(hLib); return 0; }
轉(zhuǎn)載于:https://www.cnblogs.com/carekee/articles/2334607.html
總結(jié)
- 上一篇: Js 原型对象与原型链(转)
- 下一篇: int main(int argc,ch