DLL的概念、dll匯出類(轉貼)

來源:互聯網
上載者:User

標籤:style   os   io   使用   strong   ar   for   檔案   div   

1、 DLL的概念
DLL(Dynamic Linkable Library),動態連結程式庫,可以向程式提供一些函數、變數或類。這些可以直接拿來使用。
靜態連結庫與動態連結程式庫的區別:
(1)靜態連結庫與動態連結程式庫都是共用代碼的方式。靜態連結庫把最後的指令都包含在最終產生的EXE檔案中了;動態連結程式庫不必被包含在最終EXE檔案中,EXE檔案執行時可以“動態”地引用和卸載這個與EXE獨立的DLL檔案。
(2) 靜態連結庫中不能再包含其他的動態連結程式庫或者靜態庫,而在動態連結程式庫中還可以再包含其他的動態或靜態連結庫。
動態連結程式庫的分類:Visual C++支援三種DLL,它們分別是Non-MFC DLL(非MFC動態庫)、MFC Regular DLL(MFC規則DLL)、MFC Extension DLL(MFC擴充DLL)。非MFC動態庫不採用MFC類庫結構,其匯出函數為標準的C介面,能被非MFC或MFC編寫的應用程式所調用;MFC規則DLL 包含一個繼承自CWinApp的類,但其無訊息迴圈;MFC擴充DLL採用MFC的動態連結版本建立,它只能被用MFC類庫所編寫的應用程式所調用。
2、 建立一個DLL
2.1 非MFC的DLL
2.1.1聲明匯出函數:
extern “C” __declspec(dllexport) int add(int a, int b);
其中 extern “C”為聲明為C編譯。由於C++編譯器在編譯的時候會造成其函數名的該變,在其他應用程式中導致函數不可調用,而C編譯器則不會在編譯後改變其函數名。這樣如果用C編譯的程式來調用該dll中的函數時,可能會造成找不到該函數。
__declspec(dllexport)表示該函數為DLL輸出函數,即其他應用程式可以調用該函數
從dll中聲明輸出函數有兩種方式:
(1) 另外一種方式是採用模組定義(.def) 檔案聲明,.def檔案為連結器提供了有關被連結程式的匯出、屬性及其他方面的資訊。
(2) 用__declspec(dllexport)來聲明函數
如果使用Visual C++來建立dll,對於同樣用VC建立的exe來說,調用dll沒有什麼問題。而如果用其他工具來建立的exe來調用dll,就會出現問題。因為即使你不用C++編譯器,Microsoft C編譯器也會損害C函數。當用__stdcall將函數輸出時,C編譯器會將函數改為[email protected]的形式。在這裡需要在.def檔案中加入EXPORTS節來輸出函數:
EXPORTS
func
這樣,dll將用func函數名來輸出函數。
另一種方式是用#pragma (linker, “/exports:[email protected]”),告訴編譯器輸出函數func,這種方式沒有前一種好。
如果通過VC++編寫的DLL欲被其他語言編寫的程式調用,應將函數的調用方式聲明為__stdcall方式,WINAPI都採用這種方式,而C/C++ 預設的調用方式卻為__cdecl。__stdcall方式與__cdecl對函數名最終產生符號的方式不同。若採用C編譯方式(在C++中需將函式宣告為extern "C"),__stdcall呼叫慣例在輸出函數名前面加底線,後面加“@”符號和參數的位元組數,形如[email protected];而__cdecl呼叫慣例僅在輸出函數名前面加底線,形如_functionname。
; lib.def : 匯出DLL函數LIBRARY dllTestEXPORTSadd @ 1.def檔案的規則為:
  (1)LIBRARY語句說明.def檔案相應的DLL;
  (2)EXPORTS語句後列出要匯出函數的名稱。可以在.def檔案中的匯出函數名後加@n,表示要匯出函數的序號為n(在進行函數調用時,這個序號將發揮其作用);
  (3).def 檔案中的注釋由每個注釋行開始處的分號 (;) 指定,且注釋不能與語句共用一行。
  由此可以看出,例子中lib.def檔案的含義為產生名為“dllTest”的動態連結程式庫,匯出其中的add函數,並指定add函數的序號為1。2.1.2 Dll的調用方式: 
DLL的調用分為兩種方式:動態和靜態
(1) 動態調用:typedef int(*lpAddFun)(int, int); //宏定義函數指標類型
lpAddFun add;//函數指標
HINSTANCE hDll=LoadLibrary(“path”);
add=(lpAddFun)GetProcAddress(hDll, "add");//獲得dll中的add函數指標
FreeLibrary(hDll);
在從dll調用中返回的函數、指標或者類都是以指標的方式會的,即返回的是函數、變數或類的地址。這裡一定要注意,不能簡單的用函數名來賦值。
(2) 靜態調用:將產生的.dll和.lib檔案拷入到調用dll的工程中,用命令
#pragma comment(lib,"dllTest.lib"),實現靜態調用,即把該dll在編譯的時候也編譯到exe檔案中去,而後在工程中調用時用下面的代碼:
#pragma comment(lib,"dllTest.lib")//.lib檔案中僅僅是關於其對應DLL檔案中函數的重定位資訊extern "C" __declspec(dllimport) add(int x,int y);int main(int argc, char* argv[]){int result = add(2,3);printf("%d",result);return 0;}  由上述代碼可以看出,靜態調用方式的順利進行需要完成兩個動作:  (1)告訴編譯器與DLL相對應的.lib檔案所在的路徑及檔案名稱,#pragma comment(lib,"dllTest.lib")就是起這個作用。
  程式員在建立一個DLL檔案時,連接器會自動為其產生一個對應的.lib檔案,該檔案包含了DLL 匯出函數的符號名及序號(並不含有實際的代碼)。在應用程式裡,.lib檔案將作為DLL的替代檔案參與編譯。
另外一種顯式調用的方式是設定vc中的目錄和includefiles來實現
  (2)聲明匯入函數,extern "C" __declspec(dllimport) add(int x,int y)語句中的__declspec(dllimport)發揮這個作用。
  靜態調用方式不再需要使用系統API來載入、卸載DLL以及擷取DLL中匯出函數的地址。這是因為,當程式員通過靜態連結方式編譯產生應用程式時,應用程式中調用的與.lib檔案中匯出符號相匹配的函數符號將進入到產生的EXE 檔案中,.lib檔案中所包含的與之對應的DLL檔案的檔案名稱也被編譯器儲存在 EXE檔案內部。當應用程式運行過程中需要載入DLL檔案時,Windows將根據這些資訊發現並載入DLL,然後通過符號名實現對DLL 函數的動態連結。這樣,EXE將能直接通過函數名調用DLL的輸出函數,就象調用程式內部的其他函數一樣。
2.1.3 DllMain函數
Windows在載入dll的時候,會首先需要一個入口函數DllMain。當在dll中不定義DllMain的時候,windows會從其他運行庫中調用一個不做任何操作的DllMain函數,直接返回true。DllMain是dll內部的函數,這意味著在調用dll的程式中不能顯式的調用。它是在 dll被調用時自動被調用的。
BOOL APIENTRY DllMain( HANDLE hModule, DWORD ul_reason_for_call,
LPVOID lpReserved)
{
switch (ul_reason_for_call)
{
case: DLL_PROCESS_ATTACH:
break;
case: DLL_THREAD_ATTACH:
break;
case: DLL_THREAD_DETACH:
break;
case: DLL_PROCESS_DETACH:
break;
return TRUE;
} 
}2.2 在dll中匯出變數
1、在dll中定義變數 extern int global;
2、在.def中定義輸出 EXPORTS:
global DATA
3、 在應用程式中調用:#pragma comment(lib,"dllTest.lib")
extern int global;
注意在此引入的變數global,是一個地址,在使用時需要強制轉化為指標後再用,才能得到其值。
(int *)global=10;
在應用工程中引用DLL中全域變數的一個更好方法是:
extern int _declspec(dllimport) global; //用_declspec(dllimport)匯入
通過_declspec(dllimport)方式匯入的就是DLL中全域變數本身而不再是其地址了,建議在一切可能的情況下使用這種方式。
2.3 dll匯出類
在定義的時候用 class _declspec(dllexport) classname{
}
在類中引用的時候用
加入類定義標頭檔:#include “classname.h”
Class _declspec(dllimport) classname 來匯入類
3、 MFC規則Dll
MFC規則DLL的概念體現在兩方面:
  (1) 它是MFC的
  “是MFC的”意味著可以在這種DLL的內部使用MFC;
  (2) 它是規則的
  “是規則的”意味著它不同於MFC擴充DLL,在MFC規則DLL的內部雖然可以使用MFC,但是其與應用程式的介面不能是MFC。而MFC擴充DLL與應用程式的介面可以是MFC,可以從MFC擴充DLL中匯出一個MFC類的衍生類別。
  Regular DLL能夠被所有支援DLL技術的語言所編寫的應用程式調用,當然也包括使用MFC的應用程式。在這種動態串連庫中,包含一個從CWinApp繼承下來的類,DllMain函數則由MFC自動提供。
(1)靜態連結到MFC 的規則DLL
  靜態連結到MFC的規則DLL與MFC庫(包括MFC擴充 DLL)靜態連結,將MFC庫的代碼直接產生在.dll檔案中。在調用這種DLL的介面時,MFC使用DLL的資源。因此,在靜態連結到MFC 的規則DLL中不需要進行模組狀態的切換。
  使用這種方法產生的規則DLL其程式較大,也可能包含重複的代碼。
(2)動態連結到MFC 的規則DLL
  動態連結到MFC 的規則DLL 可以和使用它的可執行檔同時動態連結到 MFC DLL 和任何MFC擴充 DLL。在使用了MFC共用庫的時候,預設情況下,MFC使用主應用程式的資源控制代碼來載入資源模板。這樣,當DLL和應用程式中存在相同ID的資源時(即所謂的資源重複問題),系統可能不能獲得正確的資源。因此,對於共用MFC DLL的規則DLL,我們必須進行模組切換以使得MFC能夠找到正確的資源模板。
我們可以在Visual C++中設定MFC規則DLL是靜態連結到MFC DLL還是動態連結到MFC DLL。8,依次選擇Visual C++的project -> Settings -> General菜單或選項,在Microsoft Foundation Classes中進行設定。3.1規則DLL的建立;
與非MFCdll不同的是,在其定義裡面可以引入MFC類,其他與非MFC一樣
3.2規則DLL的調用
(1)顯示方式LoadLibrary , GetProcAdress , FreeLibrary
(2) 我們照樣可以在EXE程式中隱式調用MFC規則DLL,只需要將DLL工程產生的.lib檔案和.dll檔案拷入當前工程所在的目錄,並在RegularDllCallDlg.cpp檔案(圖12所示對話方塊類的實現檔案)的頂部添加:
#pragma comment(lib,"RegularDll.lib")
3.3共用MFC DLL的規則DLL的模組切換
應用程式進程本身及其調用的每個DLL模組都具有一個全域唯一的HINSTANCE控制代碼,它們代表了DLL或EXE模組在進程虛擬空間中的起始地址。進程本身的模組控制代碼一般為0x400000,而DLL模組的預設控制代碼為0x10000000。如果程式同時載入了多個DLL,則每個DLL模組都會有不同的 HINSTANCE。應用程式在載入DLL時對其進行了重定位。
  共用MFC DLL(或MFC擴充DLL)的規則DLL涉及到HINSTANCE控制代碼問題,HINSTANCE控制代碼對於載入資源特別重要。EXE和DLL都有其自己的資源,而且這些資源的ID可能重複,應用程式需要通過資源模組的切換來找到正確的資源。如果應用程式需要來自於DLL的資源,就應將資源模組控制代碼指定為 DLL的模組控制代碼;如果需要EXE檔案中包含的資源,就應將資源模組控制代碼指定為EXE的模組控制代碼。
模組的切換有三種方式:
(1)在DLL函數中調用:AFX_MANAGE_STATE(AfxGetStaticModuleState());(推薦使用,最簡單)void ShowDlg(void){//方法1:在函數開始處變更,在函數結束時恢複//將AFX_MANAGE_STATE(AfxGetStaticModuleState());作為介面函數的第一//條語句進行模組狀態切換AFX_MANAGE_STATE(AfxGetStaticModuleState());
CDialog dlg(IDD_DLL_DIALOG);//開啟ID為2000的對話方塊dlg.DoModal();}(2)在DLL函數中調用AfxGetResourceHandle();AfxSetResourceHandle(HINSTANCE xxx);
(3)由應用程式自身切換(不推薦,最麻煩)
4、擴充MFCDLL
MFC擴充DLL的內涵為MFC的擴充,使用者使用MFC擴充DLL就像使用MFC本身的DLL一樣。除了可以在MFC擴充DLL的內部使用MFC以外, MFC擴充DLL與應用程式的介面部分也可以是MFC。我們一般使用MFC擴充DLL來包含一些MFC的增強功能,譬如擴充MFC的CStatic、 CButton等類使之具備更強大的能力。
匯出一個類,直接在類聲明標頭檔中使用AFX_EXT_CLASS即可,最後別忘了在調用dll的程式中加入class的標頭檔5、總結:綜上所述:以上幾種dll主要由以下幾種區別:1、動態連結程式庫是將exe程式在程式執行的時候動態載入的,而靜態連結庫是在編譯的時 將其編譯在代碼之中的2、動態連結程式庫可以輸出變數、函數和類。其中每種輸出的方式與調用方式不盡相同:(1)變數:在dll中定義 extern int global;在.def檔案中輸出 EXPORTSglobal DATA或extern _declspec(dllexport)int global(不用輸出檔案了)在程式中調用:靜態調用: #pragma comment(lib,"dllTest.lib")extern int _declspec(dllimport) global;動態調用:(2)函數:在dll中定義extern “C” __declspec(dllexport) int add(int a, int b);也可以在.def檔案中輸出該函數EXPORTSadd @ 1在程式中調用:靜態調用:#pragma comment(lib,"dllTest.lib")extern "C" __declspec(dllimport) add(int x,int y);動態調用:typedef int(*lpAddFun)(int, int); //宏定義函數指標類型
lpAddFun add;//函數指標
HINSTANCE hDll=LoadLibrary(“path”);
add=(lpAddFun)GetProcAddress(hDll, "add");//獲得dll中的add函數指標
FreeLibrary(hDll);
在從dll調用中返回的函數、指標或者類都是以指標的方式會的,即返回的是函數、變數或類的地址。這裡一定要注意,不能簡單的用函數名來賦值。(3)類:在dll中定義:
在定義的時候用 class _declspec(dllexport) classname{
}
在類中引用的時候用
加入類定義標頭檔:#include “classname.h”
Class _declspec(dllimport) classname 來匯入類
3、除了擴充MFC的dll外,其他的dll均可被其他語言編寫的應用程式來調用。

DLL的概念、dll匯出類(轉貼)

聯繫我們

該頁面正文內容均來源於網絡整理,並不代表阿里雲官方的觀點,該頁面所提到的產品和服務也與阿里云無關,如果該頁面內容對您造成了困擾,歡迎寫郵件給我們,收到郵件我們將在5個工作日內處理。

如果您發現本社區中有涉嫌抄襲的內容,歡迎發送郵件至: info-contact@alibabacloud.com 進行舉報並提供相關證據,工作人員會在 5 個工作天內聯絡您,一經查實,本站將立刻刪除涉嫌侵權內容。

A Free Trial That Lets You Build Big!

Start building with 50+ products and up to 12 months usage for Elastic Compute Service

  • Sales Support

    1 on 1 presale consultation

  • After-Sales Support

    24/7 Technical Support 6 Free Tickets per Quarter Faster Response

  • Alibaba Cloud offers highly flexible support services tailored to meet your exact needs.