一区二区三区日韩精品-日韩经典一区二区三区-五月激情综合丁香婷婷-欧美精品中文字幕专区

分享

VC++制作DLL詳解

 imelee 2017-09-27

1.    DLL的基本概念

應(yīng)用程序(exe)要引用目標(biāo)代碼(.obj)外部的函數(shù)時(shí),有兩種實(shí)現(xiàn)途徑——靜態(tài)鏈接和動(dòng)態(tài)鏈接。

  1.    靜態(tài)鏈接

鏈接程序搜索對(duì)應(yīng)的庫文件(.lib),然后將這個(gè)對(duì)象模塊拷貝到應(yīng)用程序(.exe)中來。Windows之所不使用靜態(tài)鏈接庫,是因?yàn)楹芏嗷A(chǔ)庫被很多應(yīng)用程序使用。如果每個(gè)應(yīng)用程序一份拷貝,將帶來內(nèi)存的極大浪費(fèi)。

  2.    動(dòng)態(tài)鏈接

鏈接程序搜索到對(duì)應(yīng)的庫文件(.lib),然后根據(jù)函數(shù)名得到對(duì)應(yīng)的函數(shù)入口地址,即可進(jìn)行編譯鏈接。直到真正運(yùn)行的時(shí)候,應(yīng)用程序才會(huì)從lib文件中記錄的DLL名字去搜索同名的DLL,然后將DLL的執(zhí)行代碼內(nèi)存映射到exe中來。動(dòng)態(tài)鏈接庫的好處是多個(gè)應(yīng)用程序可以共用一份DLL的代碼段內(nèi)存。但是數(shù)據(jù)段則是每個(gè)調(diào)用進(jìn)程一份拷貝。

2.    靜態(tài)鏈接庫

靜態(tài)鏈接庫的使用比較簡單,一般使用如下方式創(chuàng)建。

 

然后就像普通工程一樣,添加頭文件的聲明以及源文件的實(shí)現(xiàn)。

編譯該工程就可以得到StaticLib.lib文件了。

調(diào)用者調(diào)用.lib庫也非常簡單,只需要包含頭文件聲明以及指明.lib庫路徑即可。如:

#include "..\StaticLib\StaticLib.h"

#pragma comment (lib, "..\\Lib\\staticlib.lib")

或者在Configuration Properties\Liker\Input\Additional Dependencies中指明.lib庫路徑。

3.    動(dòng)態(tài)鏈接庫

Visual C++支持三種DLL,它們分別是Non-MFC DLL(非MFC動(dòng)態(tài)庫)、MFC Regular DLL(MFC規(guī)則DLL)、MFC Extension DLL(MFC擴(kuò)展DLL)。他們之間的區(qū)別簡單概括如下:

非MFC動(dòng)態(tài)庫:即Win32DLL,不采用MFC庫函數(shù),其導(dǎo)出函數(shù)為標(biāo)準(zhǔn)的C接口,能被非MFC和MFC編寫的應(yīng)用程序所調(diào)用。

MFC規(guī)則DLL:包含一個(gè)繼承自CWinApp的類,但其無消息循環(huán),可以使用MFC,但是接口不能為MFC。

MFC擴(kuò)展DLL:采用MFC的動(dòng)態(tài)鏈接版本創(chuàng)建,它只能被用MFC類庫所編寫的應(yīng)用程序所調(diào)用。

  1.    MFC動(dòng)態(tài)庫   

創(chuàng)建Win32DLL

 

DLL生成向?qū)峁┮恍┖唵蔚氖纠?,使得建立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_API int nWin32DLL; 
   
extern “C” WIN32DLL_API int fnWin32DLL(void);

調(diào)用程序有兩種方式來調(diào)用DLL。

    1.     隱式鏈接到DLL   

需要完成3步,頭文件、.lib文件和DLL。具體實(shí)現(xiàn)如下:

1
2
3
#include "..\StaticLib\StaticLib.h" 
#pragma comment(lib, "..\\Lib\\staticlib.lib")

或者在Configuration Properties\Liker\Input\Additional Dependencies中指明.lib庫路徑。

DLL的搜索路徑見文末.

    2.     顯式鏈接到DLL

首先LoadLibary指定的DLL,然后GetProcAddress得到指定函數(shù)的入口指針,并且通過函數(shù)入口指針來訪問DLL的函數(shù),最后通過FreeLibrary制裁DLL。

復(fù)制代碼
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);
復(fù)制代碼

 

 

如果要保證導(dǎo)出的函數(shù)名是不帶修飾的,一定要將指定函數(shù)為C編譯器編譯。否則函數(shù)名需要以被修飾過的以“?”開始的函數(shù)名來獲取函數(shù)的入口指針。上圖為Dependency Walker查看到的。

除了直接以函數(shù)名獲取入口地址外,還可以用索引獲取函數(shù)入口地址。GetProcAddress獲取的是入口地址,所以除了可以獲取函數(shù)的入口地址,同樣可以獲取變量的地址。

  2.    MFC規(guī)則DLL

MFC規(guī)則的DLL有兩種,一種鏈接MFC動(dòng)態(tài)庫的,一種是鏈接MFC靜態(tài)庫的。選擇MFC動(dòng)態(tài)庫還是靜態(tài)庫與調(diào)用者有關(guān)系。因?yàn)檎{(diào)用者必須與DLL鏈接MFC庫一致,否則會(huì)導(dǎo)致庫調(diào)用的沖突。如果不是追求追求生成的exe和DLL占較小的空間,推薦使用MFC靜態(tài)庫。

MFC規(guī)則DLL的導(dǎo)出基本上同Win32DLL一樣,同樣不允許導(dǎo)出繼承自MFC庫的類。不同點(diǎn)主要體現(xiàn)在MFC規(guī)則DLL中可以使用MFC庫,其實(shí)WIN32DLL如果包含了MFC頭文件以及鏈接庫,也是可以使用MFC庫的。

     1.     鏈接MFC動(dòng)態(tài)庫

鏈接MFC動(dòng)態(tài)庫資源的切換。這一點(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)庫

鏈接MFC動(dòng)態(tài)庫基本上和鏈接MFC靜態(tài)庫除了上面介紹的不同,導(dǎo)出和添加文件之類的完全一樣。所以下面重點(diǎn)講解鏈接MFC靜態(tài)庫。

DLL導(dǎo)出變量、函數(shù)以及類有兩種方法,前面使用的都是通過關(guān)鍵字來導(dǎo)出。另外還有一種方法,即通過模塊定義(.def)文件來導(dǎo)出。

我們來看一下模塊定義(.def)文件的基本格式:

; MFCDLL.def : Declares the module parameters for the DLL.

LIBRARY      "MFCDLL"

EXPORTS

; Explicit exports can go here

ShowDlg @2

nDllValue DATA

注釋是通過;來完成的。

關(guān)鍵字LIBRARY,描述DLL的名字,并將此信息寫入記錄DLL信息的.lib文件中。所以如果在Linker\Output File中修改了生成的DLL的名字,注意也一定要與LIBRARY中描述的一致。當(dāng)然也可以直接注釋掉LIBRARY,這樣生成的DLL信息就直接與Linker\Output File中指定的名字相同。另外LIBARAY后面描述的名字,可以加引號(hào),也可以不加引號(hào)。ShowDlg,這個(gè)是需要導(dǎo)出的函數(shù)名。@2,這里的2是描述方法的地址索引,可以修改,也可以不使用,系統(tǒng)會(huì)生成默認(rèn)的。其實(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ù)的方法和前面使用的一樣。下面只說一下導(dǎo)入變量的方法。

  1. 直接通過關(guān)鍵字__declspec(dllimport) int nDLLValue;
  2. int* pnDLLValue = (int*)GetProcAddress(hModule, "nDllValue");

pnDLLValue = (int*)GetProcAddress(hModule, MAKEINTRESOURCE(3));

提供按序號(hào)導(dǎo)入的原因是這樣導(dǎo)入的速度更快,不用去按名字比對(duì)查找。

這里只介紹了.def文件導(dǎo)出導(dǎo)入的常用的一些方法,但是基本上夠用。如果想更深入的了解.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ǔ)類庫類派生的可重用類的 DLL。

MFC 擴(kuò)展 DLL 具有下列功能和要求:

  • 客戶端可執(zhí)行文件必須是用定義的 _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 定義為__declspec(dllexport),這在使用此宏聲明擴(kuò)展 DLL 中的類時(shí)是必要的。
  • 擴(kuò)展 DLL 不應(yīng)實(shí)例化從 CWinApp 派生的類,而應(yīng)依賴客戶端應(yīng)用程序(或 DLL)提供此對(duì)象。
  • 但擴(kuò)展 DLL 應(yīng)提供 DllMain 函數(shù),并在那里執(zhí)行任何必需的初始化。

擴(kuò)展 DLL 是使用 MFC 動(dòng)態(tài)鏈接庫版本(也稱作共享 MFC 版本)生成的。 只有用共享 MFC 版本生成的 MFC 可執(zhí)行文件(應(yīng)用程序或規(guī)則 DLL)才能使用擴(kuò)展 DLL。 客戶端應(yīng)用程序和擴(kuò)展 DLL 必須使用相同版本的 MFCx0.dll。 使用擴(kuò)展 DLL,可以從 MFC 派生新的自定義類,然后將此“擴(kuò)展”版本的 MFC 提供給調(diào)用 DLL 的應(yīng)用程序。

擴(kuò)展 DLL 也可用于在應(yīng)用程序和 DLL 之間傳遞 MFC 派生的對(duì)象。 與已傳遞的對(duì)象關(guān)聯(lián)的成員函數(shù)存在于創(chuàng)建對(duì)象所在的模塊中。 由于在使用 MFC 的共享 DLL 版本時(shí)正確導(dǎo)出了這些函數(shù),因此可以在應(yīng)用程序和它加載的擴(kuò)展 DLL 之間隨意傳遞 MFC 或 MFC 派生的對(duì)象指針。

客戶端必須定義_AFXDLL 編譯,其實(shí)就是說客戶端必須使用MFC動(dòng)態(tài)庫,即共享MFC庫。另外,擴(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ù)來自動(dòng)完成資源的切換,詳見示例代碼。

MFC擴(kuò)展DLL的使用比較復(fù)雜,尤其是涉及資源導(dǎo)出之類的,所以如果不是必需,盡量少用FMC擴(kuò)展DLL,能夠用MFC常規(guī)DLL代表的盡量代替。

想詳細(xì)了解擴(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ì)多種語言進(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。 若要訪問的資源,調(diào)用泛型函數(shù) FindResource 和 LoadResource,其中從事任何種類的資源,或調(diào)用下面的特定資源的函數(shù)之一:

  • 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)程的可執(zhí)行模塊所在的目錄。
  • 當(dāng)前目錄。
  • Windows 系統(tǒng)目錄。 GetSystemDirectory 函數(shù)檢索此目錄的路徑。
  • Windows 目錄。 GetWindowsDirectory 函數(shù)檢索此目錄的路徑。
  • PATH 環(huán)境變量中列出的目錄。

上面是EXE默認(rèn)的搜索DLL路徑。但是有有時(shí)我們希望更改DLL存放的目錄,那么就需要在搜索路徑上做一些修改了。

如果去改上的模塊目錄、當(dāng)前目錄,會(huì)導(dǎo)致程序中使用目錄上的不便,所以不建議修改上面這些目錄。Windows其實(shí)提供了修改DLL搜索路徑的API。

void  SetDllDirectory( LPCTSTR lpPathName);

調(diào)用這個(gè)函數(shù)之后,DLL的搜索路徑改變?yōu)椋?/p>

  • 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.

通過上面兩個(gè)修改DLL搜索路徑的API,我們可以發(fā)現(xiàn),LoadLibraryEx會(huì)將一個(gè)新添的搜索路徑放在其他所有搜索路徑之前。而SetDllDirectory則將搜索路徑放在第2位。通過實(shí)驗(yàn)也發(fā)現(xiàn),LoadLibraryEx的DLL搜索時(shí)間明顯少于SetDllDirectory設(shè)置之后的DLL搜索時(shí)間。所以建議使用LoadLibraryEx修改DLL搜索路徑。

  2.    DLL中的靜態(tài)變量

如果是在一個(gè)進(jìn)程中,幾個(gè)模塊共同調(diào)用同一個(gè)DLL,那么DLL中的靜態(tài)變量是全局共用的。

  3.    關(guān)于釋放DLL內(nèi)存的問題

Windows允許一個(gè)進(jìn)程有多個(gè)Heap。我們知道每個(gè)DLL會(huì)有自己的數(shù)據(jù)區(qū),也就是說每個(gè)DLL都會(huì)有自己的Heap。Windows有一個(gè)規(guī)則,即誰的Heap誰負(fù)責(zé),也就是說每個(gè)DLL必須得自己負(fù)責(zé)Head上的內(nèi)存申請(qǐng)以及釋放。

簡單的內(nèi)存申請(qǐng)釋放很容易發(fā)現(xiàn),但是有一些vector、CString、CStringArray等,它們的內(nèi)部實(shí)現(xiàn)其實(shí)都是有動(dòng)態(tài)申請(qǐng)內(nèi)存的,所以如果導(dǎo)出函數(shù)的參數(shù)涉及到這些類型時(shí),也會(huì)導(dǎo)致內(nèi)存釋放的問題。

  4.    ClientDLL在設(shè)置上必須一致

    1. 是否動(dòng)態(tài)鏈接到MFC庫。
    2. 運(yùn)行時(shí)庫也必須一致。MD/MDd;MT/MTd。
    3. 字符集是否一致,是否Unicode之類的必須一致。
    4. 導(dǎo)入導(dǎo)出的聲明必須一致。
    5. 須相同的修飾名extern “C”;
    6. 必須相同的調(diào)用方式,_cdecl,_stdcall,_fastcall必須導(dǎo)入導(dǎo)出時(shí)一致。

以上只列舉了一些容易出現(xiàn)的錯(cuò)誤??傊绻贑lient端鏈接出錯(cuò)時(shí),就應(yīng)該考慮Client與DLL的一致性問題了。

  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的,必須使用資源切換。

源碼下載

    本站是提供個(gè)人知識(shí)管理的網(wǎng)絡(luò)存儲(chǔ)空間,所有內(nèi)容均由用戶發(fā)布,不代表本站觀點(diǎn)。請(qǐng)注意甄別內(nèi)容中的聯(lián)系方式、誘導(dǎo)購買等信息,謹(jǐn)防詐騙。如發(fā)現(xiàn)有害或侵權(quán)內(nèi)容,請(qǐng)點(diǎn)擊一鍵舉報(bào)。
    轉(zhuǎn)藏 分享 獻(xiàn)花(0

    0條評(píng)論

    發(fā)表

    請(qǐng)遵守用戶 評(píng)論公約

    類似文章 更多

    中文字幕人妻综合一区二区| 91超频在线视频中文字幕| 久久中文字人妻熟女小妇| 欧美激情区一区二区三区| 亚洲做性视频在线播放| 99久久人妻中文字幕| 成人精品视频在线观看不卡| 少妇熟女亚洲色图av天堂| 亚洲欧美日产综合在线网| 久久99青青精品免费观看| 中字幕一区二区三区久久蜜桃| 又色又爽又黄的三级视频| 一本久道久久综合中文字幕| 九九热九九热九九热九九热| 欧美日韩国产黑人一区| 午夜视频成人在线观看| 狠狠干狠狠操亚洲综合| 99久只有精品免费视频播放| 精品人妻av区波多野结依| 黄色片一区二区在线观看| 午夜直播免费福利平台| 好骚国产99在线中文| 一级欧美一级欧美在线播| 一区二区三区日韩经典| 国内午夜精品视频在线观看| 开心久久综合激情五月天| 国产日韩中文视频一区| 五月天丁香亚洲综合网| 欧美加勒比一区二区三区| 九九九热视频免费观看| 久久热在线视频免费观看| 欧美日本道一区二区三区| 绝望的校花花间淫事2| 人妻中文一区二区三区| 爱草草在线观看免费视频| 深夜视频在线观看免费你懂 | 国产在线日韩精品欧美| 国产一区二区三区香蕉av| 国产又粗又长又爽又猛的视频| 久久国产成人精品国产成人亚洲| 一二区中文字幕在线观看|