VC++制作DLL具体解释
1.????DLL的基本概念
應用程序(exe)要引用目標代碼(.obj)外部的函數時,有兩種實現途徑——靜態鏈接和動態鏈接。
1.????靜態鏈接
鏈接程序搜索相應的庫文件(.lib),然后將這個對象模塊復制到應用程序(.exe)中來。Windows之所不使用靜態鏈接庫。是由于非常多基礎庫被非常多應用程序使用。假設每一個應用程序一份拷貝,將帶來內存的極大浪費。
2.????動態鏈接
鏈接程序搜索到相應的庫文件(.lib)。然后依據函數名得到相應的函數入口地址,就可以進行編譯鏈接。
直到真正執行的時候,應用程序才會從lib文件里記錄的DLL名字去搜索同名的DLL。然后將DLL的執行代碼內存映射到exe中來。動態鏈接庫的優點是多個應用程序能夠共用一份DLL的代碼段內存。可是數據段則是每一個調用進程一份拷貝。
2.????靜態鏈接庫
靜態鏈接庫的使用比較簡單。一般使用例如以下方式創建。
?
然后就像普通project一樣,加入頭文件的聲明以及源文件的實現。
編譯該project就能夠得到StaticLib.lib文件了。
調用者調用.lib庫也很easy。僅僅須要包括頭文件聲明以及指明.lib庫路徑就可以。如:
#include "..\StaticLib\StaticLib.h"
#pragma comment (lib, "..\\Lib\\staticlib.lib")
或者在Configuration Properties\Liker\Input\Additional Dependencies中指明.lib庫路徑。
3.????動態鏈接庫
Visual C++支持三種DLL。它們各自是Non-MFC DLL(非MFC動態庫)、MFC Regular DLL(MFC規則DLL)、MFC Extension DLL(MFC擴展DLL)。他們之間的差別簡單概括例如以下:
非MFC動態庫:即Win32DLL,不採用MFC庫函數,其導出函數為標準的C接口,能被非MFC和MFC編寫的應用程序所調用。
MFC規則DLL:包括一個繼承自CWinApp的類,但其無消息循環。能夠使用MFC,可是接口不能為MFC。
MFC擴展DLL:採用MFC的動態鏈接版本號創建,它僅僅能被用MFC類庫所編寫的應用程序所調用。
1.????非MFC動態庫? ?
創建Win32DLL
?
DLL生成向導提供一些簡單的演示樣例,使得建立Win32DLL變得更簡單。
| 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); |
調用程序有兩種方式來調用DLL。
1.?????隱式鏈接到DLL ??
須要完畢3步,頭文件、.lib文件和DLL。
詳細實現例如以下:
#include "..\StaticLib\StaticLib.h"? #pragma comment(lib, "..\\Lib\\staticlib.lib")
或者在Configuration Properties\Liker\Input\Additional Dependencies中指明.lib庫路徑。
DLL的搜索路徑見文末.
2.?????顯式鏈接到DLL
首先LoadLibary指定的DLL。然后GetProcAddress得到指定函數的入口指針。而且通過函數入口指針來訪問DLL的函數。最后通過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);?
假設要保證導出的函數名是不帶修飾的。一定要將指定函數為C編譯器編譯。
否則函數名須要以被修飾過的以“?”開始的函數名來獲取函數的入口指針。上圖為Dependency Walker查看到的。
除了直接以函數名獲取入口地址外,還能夠用索引獲取函數入口地址。
GetProcAddress獲取的是入口地址。所以除了能夠獲取函數的入口地址,相同能夠獲取變量的地址。
2.????MFC規則DLL
MFC規則的DLL有兩種。一種鏈接MFC動態庫的,一種是鏈接MFC靜態庫的。
選擇MFC動態庫還是靜態庫與調用者有關系。由于調用者必須與DLL鏈接MFC庫一致,否則會導致庫調用的沖突。
假設不是追求追求生成的exe和DLL占較小的空間,推薦使用MFC靜態庫。
MFC規則DLL的導出基本上同Win32DLL一樣,相同不同意導出繼承自MFC庫的類。
不同點主要體如今MFC規則DLL中能夠使用MFC庫,事實上WIN32DLL假設包括了MFC頭文件以及鏈接庫,也是能夠使用MFC庫的。
? 1.?????鏈接MFC動態庫
鏈接MFC動態庫資源的切換。這一點須要注意,而且VC默認生成的代碼中也用大篇幅的凝視提示了,而且也給出了例如以下主要的解釋。
| 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靜態庫
鏈接MFC動態庫基本上和鏈接MFC靜態庫除了上面介紹的不同,導出和加入文件之類的全然一樣。所以以下重點解說鏈接MFC靜態庫。
DLL導出變量、函數以及類有兩種方法。前面使用的都是通過keyword來導出。另外另一種方法,即通過模塊定義(.def)文件來導出。
我們來看一下模塊定義(.def)文件的基本格式:
; MFCDLL.def : Declares the module parameters for the DLL.
LIBRARY????? "MFCDLL"
EXPORTS
; Explicit exports can go here
ShowDlg @2
nDllValue DATA
凝視是通過;來完畢的。
keywordLIBRARY,描寫敘述DLL的名字,并將此信息寫入記錄DLL信息的.lib文件里。
所以假設在Linker\Output File中改動了生成的DLL的名字,注意也一定要與LIBRARY中描寫敘述的一致。當然也能夠直接凝視掉LIBRARY,這樣生成的DLL信息就直接與Linker\Output File中指定的名字同樣。另外LIBARAY后面描寫敘述的名字,能夠加引號。也能夠不加引號。
ShowDlg,這個是須要導出的函數名。@2,這里的2是描寫敘述方法的地址索引。能夠改動,也能夠不使用。系統會生成默認的。事實上不只函數有,變量也有。
?
假設同一時候使用了.def和__declspec(dllexport)導出,編譯器會優先使用.def文件的導出。
.def文件的導出默認是C編譯的,即和extern “c” __declspec(dllexport)的導出效果一樣。
導入函數的方法和前面使用的一樣。
以下僅僅說一下導入變量的方法。
pnDLLValue = (int*)GetProcAddress(hModule, MAKEINTRESOURCE(3));
提供按序號導入的原因是這樣導入的速度更快。不用去按名字比對查找。
這里僅僅介紹了.def文件導出導入的經常使用的一些方法,可是基本上夠用。假設想更深入的了解.def文件還涉及到非常多知識點。能夠參考:
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擴展DLL
MFC 擴展 DLL 是通常實現從現有 Microsoft 基礎類庫類派生的可重用類的 DLL。
MFC 擴展 DLL 具有下列功能和要求:
- client可運行文件必須是用定義的?_AFXDLL?編譯的 MFC 應用程序。
- 擴展 DLL 也可由動態鏈接到 MFC 的規則 DLL 使用。
- 擴展 DLL 應該用定義的?_AFXEXT?編譯。?這將強制同一時候定義?_AFXDLL。并確保從 MFC 頭文件里拉入正確的聲明。?它也確保了在生成 DLL 時將?AFX_EXT_CLASS?定義為__declspec(dllexport),這在使用此宏聲明擴展 DLL 中的類時是必要的。
- 擴展 DLL 不應實例化從?CWinApp?派生的類。而應依賴client應用程序(或 DLL)提供此對象。
- 但擴展 DLL 應提供?DllMain?函數,并在那里運行不論什么必需的初始化。
擴展 DLL 是使用 MFC 動態鏈接庫版本號(也稱作共享 MFC 版本號)生成的。
?僅僅實用共享 MFC 版本號生成的 MFC 可運行文件(應用程序或規則 DLL)才干使用擴展 DLL。?client應用程序和擴展 DLL 必須使用同樣版本號的 MFCx0.dll。?使用擴展 DLL,能夠從 MFC 派生新的自己定義類,然后將此“擴展”版本號的 MFC 提供給調用 DLL 的應用程序。
擴展 DLL 也可用于在應用程序和 DLL 之間傳遞 MFC 派生的對象。?與已傳遞的對象關聯的成員函數存在于創建對象所在的模塊中。?因為在使用 MFC 的共享 DLL 版本號時正確導出了這些函數,因此能夠在應用程序和它載入的擴展 DLL 之間任意傳遞 MFC 或 MFC 派生的對象指針。
client必須定義_AFXDLL?編譯,事實上就是說client必須使用MFC動態庫。即共享MFC庫。另外。擴展DLL中顯示對話框,和動態鏈接MFC的DLL一樣須要進行資源的切換,僅僅是兩個DLL的DllMain函數不同,導致切換資源的方法不同。擴展DLL的切換方法。MFC擴展DLL的導入導出。基本上和靜態鏈接MFC的DLL一樣。
HINSTANCE oldHInst = AfxGetResourceHandle();
HINSTANCE hInst = LoadLibrary("ExDll.dll");
AfxSetResourceHandle(hInst);
CMyDlg dlg;
dlg.DoModal();
AfxSetResourceHandle(oldHInst);
另外,還能夠使用構造函數、析構函數來自己主動完畢資源的切換,詳見演示樣例代碼。
MFC擴展DLL的使用比較復雜,尤其是涉及資源導出之類的,所以假設沒必要,盡量少用FMC擴展DLL,可以用MFC常規DLL代表的盡量取代。
想具體了解擴展DLL的請參考:
http://msdn.microsoft.com/zh-cn/library/1btd5ea3.aspx
http://msdn.microsoft.com/zh-cn/library/h5f7ck28(VS.80).aspx
? ?4. ? 純資源DLL
一個純資源 DLL 是一個 DLL,它包括資源如圖標、 位圖、 字符串和對話框。?使用一個純資源 DLL 是共享一組同樣的多個程序之間的資源的好辦法。?它也是一個好的方法,以提供資源被針對多種語言進行本地化的應用程序。
要創建純資源 DLL,請創建一個新的 Win32 DLL (非 MFC) 項目,并將資源加入到項目中。
- 選擇中的 Win32 項目新項目對話框中,在 Win32 項目向導中指定 DLL 的項目類型。
- 為 DLL 創建新資源腳本包括資源 (如字符串或菜單) 并保存.rc 文件。
- 在項目?菜單上。單擊?加入現有項,然后將新的.rc 文件插入到該項目。
- 指定?/NOENTRY?鏈接器選項。
/ NOENTRY 防止鏈接器將 _main 的參考鏈接到 DLL ; 若要創建純資源 DLL。必須使用此選項。
- 生成 DLL。
使用純資源 DLL 的應用程序應調用?LoadLibrary?到顯式鏈接到 DLL。
?若要訪問的資源。調用泛型函數?FindResource?和?LoadResource,當中從事不論什么種類的資源,或調用以下的特定資源的函數之中的一個:
- FormatMessage
- LoadAccelerators
- LoadBitmap
- LoadCursor
- LoadIcon
- LoadMenu
- LoadString
應用程序應調用句完畢時使用的資源。
能夠調用與資源切換同樣的方式完畢資源的切換。
| 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); |
也能夠調用指定資源函數獲取指定資源的句柄。
注意項
1.????DLL搜索路徑
- 當前進程的可運行模塊所在的文件夾。
- 當前文件夾。
- Windows?系統文件夾。?GetSystemDirectory?函數檢索此文件夾的路徑。
- Windows?文件夾。
?GetWindowsDirectory?函數檢索此文件夾的路徑。
- PATH?環境變量中列出的文件夾。
上面是EXE默認的搜索DLL路徑。
可是有有時我們希望更改DLL存放的文件夾。那么就須要在搜索路徑上做一些改動了。
假設去改上的模塊文件夾、當前文件夾,會導致程序中使用文件夾上的不便,所以不建議改動上面這些文件夾。Windows事實上提供了改動DLL搜索路徑的API。
void? SetDllDirectory( LPCTSTR lpPathName);
調用這個函數之后,DLL的搜索路徑改變為:
- 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);
以參數dwFlags為 _WITH_ALTERED_SEARCH_PATH調用上面的函數時,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.
通過上面兩個改動DLL搜索路徑的API,我們能夠發現。LoadLibraryEx會將一個新添的搜索路徑放在其它全部搜索路徑之前。而SetDllDirectory則將搜索路徑放在第2位。
通過實驗也發現,LoadLibraryEx的DLL搜索時間明顯少于SetDllDirectory設置之后的DLL搜索時間。所以建議使用LoadLibraryEx改動DLL搜索路徑。
2.????DLL中的靜態變量
假設是在一個進程中,幾個模塊共同調用同一個DLL。那么DLL中的靜態變量是全局共用的。
3.????關于釋放DLL內存的問題
Windows同意一個進程有多個Head。我們知道每一個DLL會有自己的數據區。也就是說每一個DLL都會有自己的Head。
Windows有一個規則。即誰的Head誰負責,也就是說每一個DLL必須得自己負責Head上的內存申請以及釋放。
簡單的內存申請釋放非常easy發現。可是有一些vector、CString、CStringArray等,它們的內部實現事實上都是有動態申請內存的。所以假設導出函數的參數涉及到這些類型時。也會導致內存釋放的問題。
4.????Client與DLL在設置上必須一致
MD/MDd;MT/MTd。
以上僅僅列舉了一些easy出現的錯誤。
總之假設在Client端鏈接出錯時。就應該考慮Client與DLL的一致性問題了。
5.????動態鏈接到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.
即僅僅要是動態鏈接到MFC共享DLL的,必須使用資源切換。
源代碼下載
總結
以上是生活随笔為你收集整理的VC++制作DLL具体解释的全部內容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: Android经常使用的五种弹出对话框
- 下一篇: Sass 基础(三)