VC++制作DLL具体解释
1.????DLL的基本概念
應(yīng)用程序(exe)要引用目標(biāo)代碼(.obj)外部的函數(shù)時(shí),有兩種實(shí)現(xiàn)途徑——靜態(tài)鏈接和動(dòng)態(tài)鏈接。
1.????靜態(tài)鏈接
鏈接程序搜索相應(yīng)的庫(kù)文件(.lib),然后將這個(gè)對(duì)象模塊復(fù)制到應(yīng)用程序(.exe)中來(lái)。Windows之所不使用靜態(tài)鏈接庫(kù)。是由于非常多基礎(chǔ)庫(kù)被非常多應(yīng)用程序使用。假設(shè)每一個(gè)應(yīng)用程序一份拷貝,將帶來(lái)內(nèi)存的極大浪費(fèi)。
2.????動(dòng)態(tài)鏈接
鏈接程序搜索到相應(yīng)的庫(kù)文件(.lib)。然后依據(jù)函數(shù)名得到相應(yīng)的函數(shù)入口地址,就可以進(jìn)行編譯鏈接。
直到真正執(zhí)行的時(shí)候,應(yīng)用程序才會(huì)從lib文件里記錄的DLL名字去搜索同名的DLL。然后將DLL的執(zhí)行代碼內(nèi)存映射到exe中來(lái)。動(dòng)態(tài)鏈接庫(kù)的優(yōu)點(diǎn)是多個(gè)應(yīng)用程序能夠共用一份DLL的代碼段內(nèi)存。可是數(shù)據(jù)段則是每一個(gè)調(diào)用進(jìn)程一份拷貝。
2.????靜態(tài)鏈接庫(kù)
靜態(tài)鏈接庫(kù)的使用比較簡(jiǎn)單。一般使用例如以下方式創(chuàng)建。
?
然后就像普通project一樣,加入頭文件的聲明以及源文件的實(shí)現(xiàn)。
編譯該project就能夠得到StaticLib.lib文件了。
調(diào)用者調(diào)用.lib庫(kù)也很easy。僅僅須要包括頭文件聲明以及指明.lib庫(kù)路徑就可以。如:
#include "..\StaticLib\StaticLib.h"
#pragma comment (lib, "..\\Lib\\staticlib.lib")
或者在Configuration Properties\Liker\Input\Additional Dependencies中指明.lib庫(kù)路徑。
3.????動(dòng)態(tài)鏈接庫(kù)
Visual C++支持三種DLL。它們各自是Non-MFC DLL(非MFC動(dòng)態(tài)庫(kù))、MFC Regular DLL(MFC規(guī)則DLL)、MFC Extension DLL(MFC擴(kuò)展DLL)。他們之間的差別簡(jiǎn)單概括例如以下:
非MFC動(dòng)態(tài)庫(kù):即Win32DLL,不採(cǎi)用MFC庫(kù)函數(shù),其導(dǎo)出函數(shù)為標(biāo)準(zhǔn)的C接口,能被非MFC和MFC編寫的應(yīng)用程序所調(diào)用。
MFC規(guī)則DLL:包括一個(gè)繼承自CWinApp的類,但其無(wú)消息循環(huán)。能夠使用MFC,可是接口不能為MFC。
MFC擴(kuò)展DLL:採(cǎi)用MFC的動(dòng)態(tài)鏈接版本號(hào)創(chuàng)建,它僅僅能被用MFC類庫(kù)所編寫的應(yīng)用程序所調(diào)用。
1.????非MFC動(dòng)態(tài)庫(kù)? ?
創(chuàng)建Win32DLL
?
DLL生成向?qū)峁┮恍┖?jiǎn)單的演示樣例,使得建立Win32DLL變得更簡(jiǎn)單。
| 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 | // The following ifdef block is the standard way of creating macros which make exporting?? // from a DLL simpler. All files within this DLL are compiled with the WIN32DLL_EXPORTS? // symbol defined on the command line. this symbol should not be defined on any project? // that uses this DLL. This way any other project whose source files include this file see?? // WIN32DLL_API functions as being imported from a DLL, whereas this DLL sees symbols? // defined with this macro as being exported.? #ifdef WIN32DLL_EXPORTS? #define WIN32DLL_API __declspec(dllexport)? #else? #define WIN32DLL_API __declspec(dllimport)? #endif? ??? // This class is exported from the Win32DLL.dll? class?WIN32DLL_API CWin32DLL {? public:? ????CWin32DLL(void);? ????// TODO: add your methods here.? ????int?Add(int?x,int?y);? };? ??? extern?WIN32DLL_APIint?nWin32DLL;? extern?“C” WIN32DLL_API int?fnWin32DLL(void); |
調(diào)用程序有兩種方式來(lái)調(diào)用DLL。
1.?????隱式鏈接到DLL ??
須要完畢3步,頭文件、.lib文件和DLL。
詳細(xì)實(shí)現(xiàn)例如以下:
#include "..\StaticLib\StaticLib.h"? #pragma comment(lib, "..\\Lib\\staticlib.lib")
或者在Configuration Properties\Liker\Input\Additional Dependencies中指明.lib庫(kù)路徑。
DLL的搜索路徑見(jiàn)文末.
2.?????顯式鏈接到DLL
首先LoadLibary指定的DLL。然后GetProcAddress得到指定函數(shù)的入口指針。而且通過(guò)函數(shù)入口指針來(lái)訪問(wèn)DLL的函數(shù)。最后通過(guò)FreeLibrary制裁DLL。
1 typedef int (*PADDFUN)(void); 2 3 HINSTANCE hModule = LoadLibrary("Win32DLL.dll"); 4 5 PADDFUN pAddFun = (PADDFUN)GetProcAddress(hModule, "GetValue"); 6 7 pAddFun = (PADDFUN)GetProcAddress(hModule, MAKEINTRESOURCE(5)); 8 9 FreeLibrary(hModule);?
假設(shè)要保證導(dǎo)出的函數(shù)名是不帶修飾的。一定要將指定函數(shù)為C編譯器編譯。
否則函數(shù)名須要以被修飾過(guò)的以“?”開(kāi)始的函數(shù)名來(lái)獲取函數(shù)的入口指針。上圖為Dependency Walker查看到的。
除了直接以函數(shù)名獲取入口地址外,還能夠用索引獲取函數(shù)入口地址。
GetProcAddress獲取的是入口地址。所以除了能夠獲取函數(shù)的入口地址,相同能夠獲取變量的地址。
2.????MFC規(guī)則DLL
MFC規(guī)則的DLL有兩種。一種鏈接MFC動(dòng)態(tài)庫(kù)的,一種是鏈接MFC靜態(tài)庫(kù)的。
選擇MFC動(dòng)態(tài)庫(kù)還是靜態(tài)庫(kù)與調(diào)用者有關(guān)系。由于調(diào)用者必須與DLL鏈接MFC庫(kù)一致,否則會(huì)導(dǎo)致庫(kù)調(diào)用的沖突。
假設(shè)不是追求追求生成的exe和DLL占較小的空間,推薦使用MFC靜態(tài)庫(kù)。
MFC規(guī)則DLL的導(dǎo)出基本上同Win32DLL一樣,相同不同意導(dǎo)出繼承自MFC庫(kù)的類。
不同點(diǎn)主要體如今MFC規(guī)則DLL中能夠使用MFC庫(kù),事實(shí)上WIN32DLL假設(shè)包括了MFC頭文件以及鏈接庫(kù),也是能夠使用MFC庫(kù)的。
? 1.?????鏈接MFC動(dòng)態(tài)庫(kù)
鏈接MFC動(dòng)態(tài)庫(kù)資源的切換。這一點(diǎn)須要注意,而且VC默認(rèn)生成的代碼中也用大篇幅的凝視提示了,而且也給出了例如以下主要的解釋。
| 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 | //TODO: If this DLL is dynamically linked against the MFC DLLs, //??????? any functions exported from this DLL which call into //??????? MFC must have the AFX_MANAGE_STATE macro added at the //??????? very beginning of the function. // //??????? For example: // //??????? extern "C" BOOL PASCAL EXPORT ExportedFunction() //??????? { //??????????? AFX_MANAGE_STATE(AfxGetStaticModuleState()); //??????????? // normal function body here //??????? } // //??????? It is very important that this macro appear in each //??????? function, prior to any calls into MFC.? This means that //??????? it must appear as the first statement within the //??????? function, even before any object variable declarations //??????? as their constructors may generate calls into the MFC //??????? DLL. // //??????? Please see MFC Technical Notes 33 and 58 for additional //??????? details. |
2.?????鏈接MFC靜態(tài)庫(kù)
鏈接MFC動(dòng)態(tài)庫(kù)基本上和鏈接MFC靜態(tài)庫(kù)除了上面介紹的不同,導(dǎo)出和加入文件之類的全然一樣。所以以下重點(diǎn)解說(shuō)鏈接MFC靜態(tài)庫(kù)。
DLL導(dǎo)出變量、函數(shù)以及類有兩種方法。前面使用的都是通過(guò)keyword來(lái)導(dǎo)出。另外另一種方法,即通過(guò)模塊定義(.def)文件來(lái)導(dǎo)出。
我們來(lái)看一下模塊定義(.def)文件的基本格式:
; MFCDLL.def : Declares the module parameters for the DLL.
LIBRARY????? "MFCDLL"
EXPORTS
; Explicit exports can go here
ShowDlg @2
nDllValue DATA
凝視是通過(guò);來(lái)完畢的。
keywordLIBRARY,描寫敘述DLL的名字,并將此信息寫入記錄DLL信息的.lib文件里。
所以假設(shè)在Linker\Output File中改動(dòng)了生成的DLL的名字,注意也一定要與LIBRARY中描寫敘述的一致。當(dāng)然也能夠直接凝視掉LIBRARY,這樣生成的DLL信息就直接與Linker\Output File中指定的名字同樣。另外LIBARAY后面描寫敘述的名字,能夠加引號(hào)。也能夠不加引號(hào)。
ShowDlg,這個(gè)是須要導(dǎo)出的函數(shù)名。@2,這里的2是描寫敘述方法的地址索引。能夠改動(dòng),也能夠不使用。系統(tǒng)會(huì)生成默認(rèn)的。事實(shí)上不只函數(shù)有,變量也有。
?
假設(shè)同一時(shí)候使用了.def和__declspec(dllexport)導(dǎo)出,編譯器會(huì)優(yōu)先使用.def文件的導(dǎo)出。
.def文件的導(dǎo)出默認(rèn)是C編譯的,即和extern “c” __declspec(dllexport)的導(dǎo)出效果一樣。
導(dǎo)入函數(shù)的方法和前面使用的一樣。
以下僅僅說(shuō)一下導(dǎo)入變量的方法。
pnDLLValue = (int*)GetProcAddress(hModule, MAKEINTRESOURCE(3));
提供按序號(hào)導(dǎo)入的原因是這樣導(dǎo)入的速度更快。不用去按名字比對(duì)查找。
這里僅僅介紹了.def文件導(dǎo)出導(dǎo)入的經(jīng)常使用的一些方法,可是基本上夠用。假設(shè)想更深入的了解.def文件還涉及到非常多知識(shí)點(diǎn)。能夠參考:
http://blog.csdn.net/henry000/article/details/6852521
http://msdn.microsoft.com/zh-cn/library/28d6s79h.aspx
http://msdn.microsoft.com/zh-cn/library/54xsd65y.aspx
http://msdn.microsoft.com/zh-cn/library/d91k01sh.aspx
3.????MFC擴(kuò)展DLL
MFC 擴(kuò)展 DLL 是通常實(shí)現(xiàn)從現(xiàn)有 Microsoft 基礎(chǔ)類庫(kù)類派生的可重用類的 DLL。
MFC 擴(kuò)展 DLL 具有下列功能和要求:
- client可運(yùn)行文件必須是用定義的?_AFXDLL?編譯的 MFC 應(yīng)用程序。
- 擴(kuò)展 DLL 也可由動(dòng)態(tài)鏈接到 MFC 的規(guī)則 DLL 使用。
- 擴(kuò)展 DLL 應(yīng)該用定義的?_AFXEXT?編譯。?這將強(qiáng)制同一時(shí)候定義?_AFXDLL。并確保從 MFC 頭文件里拉入正確的聲明。?它也確保了在生成 DLL 時(shí)將?AFX_EXT_CLASS?定義為_(kāi)_declspec(dllexport),這在使用此宏聲明擴(kuò)展 DLL 中的類時(shí)是必要的。
- 擴(kuò)展 DLL 不應(yīng)實(shí)例化從?CWinApp?派生的類。而應(yīng)依賴client應(yīng)用程序(或 DLL)提供此對(duì)象。
- 但擴(kuò)展 DLL 應(yīng)提供?DllMain?函數(shù),并在那里運(yùn)行不論什么必需的初始化。
擴(kuò)展 DLL 是使用 MFC 動(dòng)態(tài)鏈接庫(kù)版本號(hào)(也稱作共享 MFC 版本號(hào))生成的。
?僅僅實(shí)用共享 MFC 版本號(hào)生成的 MFC 可運(yùn)行文件(應(yīng)用程序或規(guī)則 DLL)才干使用擴(kuò)展 DLL。?client應(yīng)用程序和擴(kuò)展 DLL 必須使用同樣版本號(hào)的 MFCx0.dll。?使用擴(kuò)展 DLL,能夠從 MFC 派生新的自己定義類,然后將此“擴(kuò)展”版本號(hào)的 MFC 提供給調(diào)用 DLL 的應(yīng)用程序。
擴(kuò)展 DLL 也可用于在應(yīng)用程序和 DLL 之間傳遞 MFC 派生的對(duì)象。?與已傳遞的對(duì)象關(guān)聯(lián)的成員函數(shù)存在于創(chuàng)建對(duì)象所在的模塊中。?因?yàn)樵谑褂?MFC 的共享 DLL 版本號(hào)時(shí)正確導(dǎo)出了這些函數(shù),因此能夠在應(yīng)用程序和它載入的擴(kuò)展 DLL 之間任意傳遞 MFC 或 MFC 派生的對(duì)象指針。
client必須定義_AFXDLL?編譯,事實(shí)上就是說(shuō)client必須使用MFC動(dòng)態(tài)庫(kù)。即共享MFC庫(kù)。另外。擴(kuò)展DLL中顯示對(duì)話框,和動(dòng)態(tài)鏈接MFC的DLL一樣須要進(jìn)行資源的切換,僅僅是兩個(gè)DLL的DllMain函數(shù)不同,導(dǎo)致切換資源的方法不同。擴(kuò)展DLL的切換方法。MFC擴(kuò)展DLL的導(dǎo)入導(dǎo)出。基本上和靜態(tài)鏈接MFC的DLL一樣。
HINSTANCE oldHInst = AfxGetResourceHandle();
HINSTANCE hInst = LoadLibrary("ExDll.dll");
AfxSetResourceHandle(hInst);
CMyDlg dlg;
dlg.DoModal();
AfxSetResourceHandle(oldHInst);
另外,還能夠使用構(gòu)造函數(shù)、析構(gòu)函數(shù)來(lái)自己主動(dòng)完畢資源的切換,詳見(jiàn)演示樣例代碼。
MFC擴(kuò)展DLL的使用比較復(fù)雜,尤其是涉及資源導(dǎo)出之類的,所以假設(shè)沒(méi)必要,盡量少用FMC擴(kuò)展DLL,可以用MFC常規(guī)DLL代表的盡量取代。
想具體了解擴(kuò)展DLL的請(qǐng)參考:
http://msdn.microsoft.com/zh-cn/library/1btd5ea3.aspx
http://msdn.microsoft.com/zh-cn/library/h5f7ck28(VS.80).aspx
? ?4. ? 純資源DLL
一個(gè)純資源 DLL 是一個(gè) DLL,它包括資源如圖標(biāo)、 位圖、 字符串和對(duì)話框。?使用一個(gè)純資源 DLL 是共享一組同樣的多個(gè)程序之間的資源的好辦法。?它也是一個(gè)好的方法,以提供資源被針對(duì)多種語(yǔ)言進(jìn)行本地化的應(yīng)用程序。
要?jiǎng)?chuàng)建純資源 DLL,請(qǐng)創(chuàng)建一個(gè)新的 Win32 DLL (非 MFC) 項(xiàng)目,并將資源加入到項(xiàng)目中。
- 選擇中的 Win32 項(xiàng)目新項(xiàng)目對(duì)話框中,在 Win32 項(xiàng)目向?qū)е兄付?DLL 的項(xiàng)目類型。
- 為 DLL 創(chuàng)建新資源腳本包括資源 (如字符串或菜單) 并保存.rc 文件。
- 在項(xiàng)目?菜單上。單擊?加入現(xiàn)有項(xiàng),然后將新的.rc 文件插入到該項(xiàng)目。
- 指定?/NOENTRY?鏈接器選項(xiàng)。
/ NOENTRY 防止鏈接器將 _main 的參考鏈接到 DLL ; 若要?jiǎng)?chuàng)建純資源 DLL。必須使用此選項(xiàng)。
- 生成 DLL。
使用純資源 DLL 的應(yīng)用程序應(yīng)調(diào)用?LoadLibrary?到顯式鏈接到 DLL。
?若要訪問(wèn)的資源。調(diào)用泛型函數(shù)?FindResource?和?LoadResource,當(dāng)中從事不論什么種類的資源,或調(diào)用以下的特定資源的函數(shù)之中的一個(gè):
- FormatMessage
- LoadAccelerators
- LoadBitmap
- LoadCursor
- LoadIcon
- LoadMenu
- LoadString
應(yīng)用程序應(yīng)調(diào)用句完畢時(shí)使用的資源。
能夠調(diào)用與資源切換同樣的方式完畢資源的切換。
| 1 2 3 4 5 6 7 8 9 10 11 | HINSTANCE oldHInst = AfxGetResourceHandle(); HINSTANCE hInst = LoadLibrary("ExDll.dll"); AfxSetResourceHandle(hInst); CMyDlg dlg; dlg.DoModal(); AfxSetResourceHandle(oldHInst); |
也能夠調(diào)用指定資源函數(shù)獲取指定資源的句柄。
注意項(xiàng)
1.????DLL搜索路徑
- 當(dāng)前進(jìn)程的可運(yùn)行模塊所在的文件夾。
- 當(dāng)前文件夾。
- Windows?系統(tǒng)文件夾。?GetSystemDirectory?函數(shù)檢索此文件夾的路徑。
- Windows?文件夾。
?GetWindowsDirectory?函數(shù)檢索此文件夾的路徑。
- PATH?環(huán)境變量中列出的文件夾。
上面是EXE默認(rèn)的搜索DLL路徑。
可是有有時(shí)我們希望更改DLL存放的文件夾。那么就須要在搜索路徑上做一些改動(dòng)了。
假設(shè)去改上的模塊文件夾、當(dāng)前文件夾,會(huì)導(dǎo)致程序中使用文件夾上的不便,所以不建議改動(dòng)上面這些文件夾。Windows事實(shí)上提供了改動(dòng)DLL搜索路徑的API。
void? SetDllDirectory( LPCTSTR lpPathName);
調(diào)用這個(gè)函數(shù)之后,DLL的搜索路徑改變?yōu)?#xff1a;
- The directory from which the application loaded.
- The directory specified by the?lpPathName?parameter.
- The system directory. Use the?GetSystemDirectory?function to get the path of this directory. The name of this directory is System32.
- The 16-bit system directory. There is no function that obtains the path of this directory, but it is searched. The name of this directory is System.
- The Windows directory. Use the?GetWindowsDirectory?function to get the path of this directory.
- The directories that are listed in the PATH environment variable.
HMODULE LoadLibraryEx(?LPCTSRlpFileName,HANDLEhFile,?DWORD?dwFlags);
以參數(shù)dwFlags為 _WITH_ALTERED_SEARCH_PATH調(diào)用上面的函數(shù)時(shí),DLL搜索路徑例如以下:
- The directory specified by the?lpFileName?path. In other words, the directory that the specified executable module is in.
- The current directory.
- The system directory. Use the GetSystemDirectory function to get the path of this directory.
- The 16-bit system directory. There is no function that obtains the path of this directory, but it is searched.
Windows Me/98/95:??This directory does not exist.
- The Windows directory. Use the GetWindowsDirectory function to get the path of this directory.
- The directories that are listed in the PATH environment variable.
通過(guò)上面兩個(gè)改動(dòng)DLL搜索路徑的API,我們能夠發(fā)現(xiàn)。LoadLibraryEx會(huì)將一個(gè)新添的搜索路徑放在其它全部搜索路徑之前。而SetDllDirectory則將搜索路徑放在第2位。
通過(guò)實(shí)驗(yàn)也發(fā)現(xiàn),LoadLibraryEx的DLL搜索時(shí)間明顯少于SetDllDirectory設(shè)置之后的DLL搜索時(shí)間。所以建議使用LoadLibraryEx改動(dòng)DLL搜索路徑。
2.????DLL中的靜態(tài)變量
假設(shè)是在一個(gè)進(jìn)程中,幾個(gè)模塊共同調(diào)用同一個(gè)DLL。那么DLL中的靜態(tài)變量是全局共用的。
3.????關(guān)于釋放DLL內(nèi)存的問(wèn)題
Windows同意一個(gè)進(jìn)程有多個(gè)Head。我們知道每一個(gè)DLL會(huì)有自己的數(shù)據(jù)區(qū)。也就是說(shuō)每一個(gè)DLL都會(huì)有自己的Head。
Windows有一個(gè)規(guī)則。即誰(shuí)的Head誰(shuí)負(fù)責(zé),也就是說(shuō)每一個(gè)DLL必須得自己負(fù)責(zé)Head上的內(nèi)存申請(qǐng)以及釋放。
簡(jiǎn)單的內(nèi)存申請(qǐng)釋放非常easy發(fā)現(xiàn)。可是有一些vector、CString、CStringArray等,它們的內(nèi)部實(shí)現(xiàn)事實(shí)上都是有動(dòng)態(tài)申請(qǐng)內(nèi)存的。所以假設(shè)導(dǎo)出函數(shù)的參數(shù)涉及到這些類型時(shí)。也會(huì)導(dǎo)致內(nèi)存釋放的問(wèn)題。
4.????Client與DLL在設(shè)置上必須一致
MD/MDd;MT/MTd。
以上僅僅列舉了一些easy出現(xiàn)的錯(cuò)誤。
總之假設(shè)在Client端鏈接出錯(cuò)時(shí)。就應(yīng)該考慮Client與DLL的一致性問(wèn)題了。
5.????動(dòng)態(tài)鏈接到MFC共享DLL ??
If this DLL is dynamically linked against the MFC DLLs,any functions exported from this DLL which call into?MFC must have the AFX_MANAGE_STATE macro added at the?very beginning of the function.
即僅僅要是動(dòng)態(tài)鏈接到MFC共享DLL的,必須使用資源切換。
源代碼下載
總結(jié)
以上是生活随笔為你收集整理的VC++制作DLL具体解释的全部?jī)?nèi)容,希望文章能夠幫你解決所遇到的問(wèn)題。
- 上一篇: Android经常使用的五种弹出对话框
- 下一篇: 什么是SVC模式【转】