VC++動態(tài)鏈接庫創(chuàng)建和調用全過程詳解_第1頁
VC++動態(tài)鏈接庫創(chuàng)建和調用全過程詳解_第2頁
VC++動態(tài)鏈接庫創(chuàng)建和調用全過程詳解_第3頁
已閱讀5頁,還剩19頁未讀 繼續(xù)免費閱讀

下載本文檔

版權說明:本文檔由用戶提供并上傳,收益歸屬內容提供方,若內容存在侵權,請進行舉報或認領

文檔簡介

1、1.概論先來闡述一下DLL(Dynamic Linkable Library)的概念,你可以簡單的把DLL 看成一種倉庫,它提供給你一些可以直接拿來用的變量、函數或類。在倉庫的發(fā)展史上經歷了“無庫靜態(tài)鏈接庫動態(tài)鏈接庫”的時代。靜態(tài)鏈接庫與動態(tài)鏈接庫都是共享代碼的方式,如果采用靜態(tài)鏈接庫,則無論你愿不愿意, lib 中的指令都被直接包含在最終生成的EXE 文件中了。但是若使用DLL ,該 DLL 不必被包含在最終EXE 文件中,EXE 文件執(zhí)行時可以“動態(tài) ”地引用和卸載這個與EXE 獨立的DLL 文件。靜態(tài)鏈接庫和動態(tài)鏈接庫的另外一個區(qū)別在于靜態(tài)鏈接庫中不能再包含其他的動態(tài)鏈接庫或者靜態(tài)庫,而

2、在動態(tài)鏈接庫中還可以再包含其他的動態(tài)或靜態(tài)鏈接庫。對動態(tài)鏈接庫,我們還需建立如下概念:( 1)DLL的編制與具體的編程語言及編譯器無關只要遵循約定的 DLL 接口規(guī)范和調用方式,用各種語言編寫的 DLL 都可以相互調用。譬如 Windows 提供的系統(tǒng) DLL (其中包括了 Windows 的 API ),在任何開發(fā)環(huán)境中都能被調用,不在乎其是 Visual Basic 、Visual C+ 還是 Delphi 。( 2)動態(tài)鏈接庫隨處可見我們在 Windows 目錄下的system32 文件夾中會看到kernel32.dll 、user32.dll 和 gdi32.dll,windows 的

3、大多數API 都包含在這些DLL 中。 kernel32.dll 中的函數主要處理內存管理和進程調度; user32.dll 中的函數主要控制用戶界面;gdi32.dll 中的函數則負責圖形方面的操作。一般的程序員都用過類似MessageBox 的函數,其實它就包含在user32.dll這個動態(tài)鏈接庫中。由此可見DLL 對我們來說其實并不陌生。(3)VC 動態(tài)鏈接庫的分類Visual C+ 支持三種 DLL ,它們分別是Non-MFC DLL (非 MFC 動態(tài)庫)、MFC RegularDLL( MFC規(guī)則DLL )、 MFC Extension DLL(MFC擴展DLL)。非MFC動態(tài)庫不

4、采用MFC類庫結構,其導出函數為標準的C 接口,能被非MFC或MFC 編寫的應用程序所調用;MFC 規(guī)則 DLL包含一個繼承自CWinApp循環(huán);MFC 擴展 DLL 采用 MFC 的動態(tài)鏈接版本創(chuàng)建,它只能被用MFC程序所調用。的類,但其無消息類庫所編寫的應用由于本文篇幅較長,內容較多, 勢必需要先對閱讀本文的有關事項進行說明,式給出。問:本文主要講解什么內容?下面以問答形答:本文詳細介紹了 DLL 編程的方方面面, 努力學完本文應可以對 DLL 有較全面的掌握,并能編寫大多數 DLL 程序。問:如何看本文?答:本文每一個主題的講解都附帶了源代碼例程,可以隨文下載(每個工程都經WINRAR

5、壓縮)。所有這些例程都由筆者編寫并在VC+6.0 中調試通過。當然看懂本文不是讀者的最終目的,讀者應親自動手實踐才能真正掌握DLL 的奧妙。問:學習本文需要什么樣的基礎知識?答:如果你掌握了 C,并大致掌握了 C+ ,了解一點 MFC 的知識,就可以輕松地看懂本文。2.靜態(tài)鏈接庫對靜態(tài)鏈接庫的講解不是本文的重點,但是在具體講解 DLL 之前,通過一個靜態(tài)鏈接庫的例子可以快速地幫助我們建立 “庫 ”的概念。圖 1 建立一個靜態(tài)鏈接庫如圖 1,在 VC+6.0 中 new 一個名稱為 libTest 的 static library 工程(單擊此處下載本工程附件),并新建 lib.h 和 lib.

6、cpp 兩個文件, lib.h 和 lib.cpp 的源代碼如下:/文件: lib.h#ifndef LIB_H#define LIB_Hextern "C" int add(int x,int y);/聲明為 C 編譯、連接方式的外部函數#endif/文件: lib.cpp#include "lib.h"int add(int x,int y)return x + y;編譯這個工程就得到了一個.lib 文件,這個文件就是一個函數庫,它提供了add 的功能。將頭文件和 .lib 文件提交給用戶后,用戶就可以直接使用其中的add 函數了。標準 Turbo

7、C2.0中的C 庫函數(我們用來的scanf、printf 、memcpy、strcpy 等)就來自這種靜態(tài)庫。下面來看看怎么使用這個庫, 在 libTest 工程所在的工作區(qū)內 new 一個 libCall 工程。libCall工程僅包含一個 main.cpp 文件,它演示了靜態(tài)鏈接庫的調用方法,其源代碼如下:#include <stdio.h>#include ".lib.h"#pragma comment( lib, ".debuglibTest.lib" ) /指定與靜態(tài)庫一起連接 int main(int argc, char* a

8、rgv)printf( "2 + 3 = %d", add( 2, 3 ) );靜態(tài)鏈接庫的調用就是這么簡單,或許我們每天都在用,可是我們沒有明白這個概念。代碼中 #pragma comment( lib , ".debuglibTest.lib" )的意思是指本文件生成的.obj 文件應與libTest.lib 一起連接。如果不用 #pragma comment 指定,則可以直接在VC+ 中設置, 如圖 2,依次選擇tools、options 、directories 、library files 菜單或選項, 填入庫文件路徑。 圖 2 中加紅圈的部分

9、為我們添加的 libTest.lib 文件的路徑。圖 2 在 VC 中設置庫文件路徑這個靜態(tài)鏈接庫的例子至少讓我們明白了庫函數是怎么回事,它們是哪來的。 我們現在有下列模糊認識了:( 1)庫不是個怪物, 編寫庫的程序和編寫一般的程序區(qū)別不大,只是庫不能單獨執(zhí)行;( 2)庫提供一些可以給別的程序調用的東東,別的程序要調用它必須以某種方式指明它要調用之。以上從靜態(tài)鏈接庫分析而得到的對庫的懵懂概念可以直接引申到動態(tài)鏈接庫中,動態(tài)鏈接庫與靜態(tài)鏈接庫在編寫和調用上的不同體現在庫的外部接口定義及調用方式略有差異。3.庫的調試與查看在具體進入各類 DLL 的詳細闡述之前,有必要對庫文件的調試與查看方法進行一

10、下介紹,因為從下一節(jié)開始我們將面對大量的例子工程。由于庫文件不能單獨執(zhí)行, 因而在按下 F5(開始 debug 模式執(zhí)行) 或 CTRL+F5 (運行)執(zhí)行時,其彈出如圖 3 所示的對話框, 要求用戶輸入可執(zhí)行文件的路徑來啟動庫函數的執(zhí)行。這個時候我們輸入要調用該庫的 EXE 文件的路徑就可以對庫進行調試了,其調試技巧與一般應用工程的調試一樣。圖 3 庫的調試與 “運行 ”通常有比上述做法更好的調試途徑,那就是將庫工程和應用工程(調用庫的工程)放置在同一 VC 工作區(qū),只對應用工程進行調試,在應用工程調用庫中函數的語句處設置斷點,執(zhí)行后按下F11,這樣就單步進入了庫中的函數。第2 節(jié)中的 li

11、bTest 和 libCall 工程就放在了同一工作區(qū),其工程結構如圖4 所示。圖 4 把庫工程和調用庫的工程放入同一工作區(qū)進行調試上述調試方法對靜態(tài)鏈接庫和動態(tài)鏈接庫而言是一致的。所以本文提供下載的所有源代碼中都包含了庫工程和調用庫的工程, 這二者都被包含在一個工作區(qū)內, 這是筆者提供這種打包下載的用意所在。動態(tài)鏈接庫中的導出接口可以使用Visual C+ 的 Depends 工具進行查看,讓我們用打開系統(tǒng)目錄中的user32.dll,看到了吧?紅圈內的就是幾個版本的MessageBox真的在這里啊,原來它就在這里啊!Depends 了!原來它圖 5 用 Depends 查看 DLL當然 D

12、epends 工具也可以顯示 DLL 的層次結構,若用它打開一個可執(zhí)行文件則可以看出這個可執(zhí)行文件調用了哪些 DLL 。好,讓我們正式進入動態(tài)鏈接庫的世界,先來看看最一般的DLL ,即非MFCDLL( 待續(xù).)上節(jié)給大家介紹了靜態(tài)鏈接庫與庫的調試與查看(動態(tài)鏈接庫(DLL) 編程深入淺出(一 )),本節(jié)主要介紹非MFC DLL 。4.非 MFC DLL4.1 一個簡單的DLL第 2 節(jié)給出了以靜態(tài)鏈接庫方式提供add 函數接口的方法, 接下來我們來看看怎樣用動態(tài)鏈接庫實現一個同樣功能的add 函數。如圖 6,在 VC+ 中 new 一個 Win32 Dynamic-LinkLibrary 工程

13、 dllTest(單擊此處下載本工程附件) 。注意不要選擇MFC AppWizard(dll),因為用MFC AppWizard(dll)建立的將是第 5、 6 節(jié)要講述的MFC動態(tài)鏈接庫。圖 6 建立一個非 MFC DLL在建立的工程中添加lib.h 及 lib.cpp 文件,源代碼如下:/*文件名: lib.h*/#ifndef LIB_H#define LIB_Hextern "C" int _declspec(dllexport)add(int x, int y);#endif/*文件名: lib.cpp*/#include "lib.h"int

14、 add(int x, int y)return x + y;與第 2 節(jié)對靜態(tài)鏈接庫的調用相似,我們也建立一個與程 dllCall ,它調用DLL 中的函數add,其源代碼如下:#include <stdio.h>#include <windows.h>DLL工程處于同一工作區(qū)的應用工typedef int(*lpAddFun)(int, int); / int main(int argc, char *argv) 宏定義函數指針類型HINSTANCE hDll; /DLL句柄lpAddFun addFun; / 函數指針hDll = LoadLibrary(&quo

15、t;.DebugdllTest.dll");if (hDll != NULL)addFun = (lpAddFun)GetProcAddress(hDll, "add");if (addFun != NULL)int result = addFun(2, 3);printf("%d", result);FreeLibrary(hDll);return 0;分析上述代碼,dllTest工程中的lib.cpp文件與第2 節(jié)靜態(tài)鏈接庫版本完全相同,不同在于 lib.h 對函數 add 的聲明前面添加了_declspec(dllexport) 語句。這

16、個語句的含義是聲明函數 add 為 DLL 的導出函數。 DLL 內的函數分為兩種:(1)DLL 導出函數,可供應用程序調用;(2) DLL 內部函數,只能在 DLL 程序使用,應用程序無法調用它們。而應用程序對本 DLL 的調用和對第 2 節(jié)靜態(tài)鏈接庫的調用卻有較大差異,下面我們來逐一分析。首先,語句typedef int ( * lpAddFun)(int,int)定義了一個與add 函數接受參數類型和返回值均相同的函數指針類型。隨后,在main 函數中定義了lpAddFun 的實例 addFun;其次,在函數 main 中定義了一個 DLL HINSTANCE 句柄實例 hDll ,通過

17、 Win32 Api 函數 LoadLibrary 動態(tài)加載了 DLL 模塊并將 DLL 模塊句柄賦給了 hDll ;再次,在函數 main 中通過 Win32 Api 函數 GetProcAddress 得到了所加載 DLL 模塊中函數 add 的地址并賦給了 addFun。經由函數指針 addFun 進行了對 DLL 中 add 函數的調用;最后,應用工程使用完 DLL 后,在函數 main 中通過 Win32 Api 函數 FreeLibrary 釋放了已經加載的 DLL 模塊。通過這個簡單的例子,我們獲知DLL 定義和調用的一般概念:(1)DLL 中需以某種特定的方式聲明導出函數(或變

18、量、類);(2) 應用工程需以某種特定的方式調用 DLL 的導出函數(或變量、類) 。下面我們來對 “特定的方式進行 ”闡述。4.2 聲明導出函數DLL中導出函數的聲明有兩種方式:一種為4.1 節(jié)例子中給出的在函數聲明中加上_declspec(dllexport) ,這里不再舉例說明; 另外一種方式是采用模塊定義 (.def) 文件聲明, .def 文件為鏈接器提供了有關被鏈接程序的導出、屬性及其他方面的信息。下面的代碼演示了怎樣同 .def 文件將函數 add 聲明為 DLL 導出函數(需在 dllTest 工程中添加 lib.def 文件):; lib.def :導出 DLL 函數LIBR

19、ARY dllTestEXPORTSadd 1.def 文件的規(guī)則為:(1)LIBRARY語句說明 .def 文件相應的DLL ;(2)EXPORTS 語句后列出要導出函數的名稱。可以在.def 文件中的導出函數名后加n,表示要導出函數的序號為n(在進行函數調用時,這個序號將發(fā)揮其作用);(3).def 文件中的注釋由每個注釋行開始處的分號(;) 指定,且注釋不能與語句共享一行。由此可以看出,例子中 lib.def 文件的含義為生成名為 “dllTest的”動態(tài)鏈接庫,導出其中的 add 函數,并指定 add 函數的序號為 1。4.3 DLL 的調用方式在 4.1 節(jié)的例子中我們看到了由 “L

20、oadLibrary-GetProcAddress- FreeLibrary”統(tǒng)系 Api 提供的三位一體 “DLL加載 -DLL 函數地址獲取 -DLL釋放 ”方式,這種調用方式稱為DLL 的動態(tài)調用。動態(tài)調用方式的特點是完全由編程者用API 函數加載和卸載DLL ,程序員可以決定DLL 文件何時加載或不加載,顯式鏈接在運行時決定加載哪個DLL 文件。與動態(tài)調用方式相對應的就是靜態(tài)調用方式,“有動必有靜 ”,這來源于物質世界的對立統(tǒng)一。 “動與靜 ”,其對立與統(tǒng)一竟無數次在技術領域里得到驗證,譬如靜態(tài)IP 與 DHCP 、靜態(tài)路由與動態(tài)路由等。從前文我們已經知道,庫也分為靜態(tài)庫與動態(tài)庫DLL

21、 ,而想不到,深入到 DLL 內部,其調用方式也分為靜態(tài)與動態(tài)。“動與靜 ”,無處不在。周易已認識到有動必有靜的動靜平衡觀, 易系辭曰: “動靜有常,剛柔斷矣”。哲學意味著一種普遍的真理,因此,我們經常可以在枯燥的技術領域看到哲學的影子。靜態(tài)調用方式的特點是由編譯系統(tǒng)完成對DLL 的加載和應用程序結束時DLL 的卸載。當調用某 DLL 的應用程序結束時,若系統(tǒng)中還有其它程序使用該DLL ,則 Windows對 DLL 的應用記錄減 1,直到所有使用該 DLL 的程序都結束時才釋放它。靜態(tài)調用方式簡單實用,但不如動態(tài)調用方式靈活。下面我們來看看靜態(tài)調用的例子(單擊此處下載本工程附件),將編譯 d

22、llTest 工程所生成的 .lib 和 .dll 文件拷入dllCall 工程所在的路徑,dllCall 執(zhí)行下列代碼:#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;由上述代碼可以看出,靜態(tài)調

23、用方式的順利進行需要完成兩個動作:(1) 告 訴 編 譯 器 與DLL相 對 應 的 .lib文 件 所 在 的 路 徑 及 文 件 名 , #pragmacomment(lib,"dllTest.lib")就是起這個作用。程序員在建立一個DLL 文件時, 連接器會自動為其生成一個對應的.lib 文件,該文件包含了 DLL導出函數的符號名及序號(并不含有實際的代碼)。在應用程序里,.lib 文件將作為 DLL 的替代文件參與編譯。(2) 聲 明 導 入 函 數 , extern"C"_declspec(dllimport)add(intx,inty) 語

24、 句 中 的_declspec(dllimport)發(fā)揮這個作用。靜態(tài)調用方式不再需要使用系統(tǒng)API 來加載、卸載 DLL 以及獲取DLL 中導出函數的地址。這是因為,當程序員通過靜態(tài)鏈接方式編譯生成應用程序時,應用程序中調用的與.lib文件中導出符號相匹配的函數符號將進入到生成的EXE 文件中, .lib 文件中所包含的與之對應的DLL文件的文件名也被編譯器存儲在EXE 文件內部。當應用程序運行過程中需要加載 DLL 文件時, Windows 將根據這些信息發(fā)現并加載DLL ,然后通過符號名實現對DLL函數的動態(tài)鏈接。這樣,EXE 將能直接通過函數名調用DLL 的輸出函數,就象調用程序內部的

25、其他函數一樣。4.4 DllMain函數Windows 在加載 DLL 的時候,需要一個入口函數,就如同控制臺或DOS 程序需要main函數、 WIN32 程序需要 WinMain 函數一樣。在前面的例子中, DLL 并沒有提供 DllMain 函數,應用工程也能成功引用 DLL ,這是因為 Windows 在找不到 DllMain 的時候,系統(tǒng)會從其它運行庫中引入一個不做任何操作的缺省 DllMain 函數版本,并不意味著 DLL 可以放棄DllMain函數。根據編寫規(guī)范, Windows 必須查找并執(zhí)行DLL 里的 DllMain 函數作為加載DLL 的依據,它使得 DLL 得以保留在內存

26、里。這個函數并不屬于導出函數,而是 DLL 的內部函數。這意味著不能直接在應用工程中引用DllMain 函數, DllMain 是自動被調用的。我們來看一個DllMain函數的例子(單擊此處下載本工程附件)。BOOL APIENTRY DllMain( HANDLE hModule,DWORD ul_reason_for_call,LPVOID lpReserved)switch (ul_reason_for_call)case DLL_PROCESS_A TTACH:printf("nprocess attach of dll");break;case DLL_THREA

27、D_ATTACH:printf("nthread attach of dll");break;case DLL_THREAD_DETACH:printf("nthread detach of dll");break;case DLL_PROCESS_DETACH:printf("nprocess detach of dll");break;return TRUE;DllMain函數在DLL被加載和卸載時被調用,在單個線程啟動和終止時,DLLMain函數也被調用, ul_reason_for_call 指明了被調用的原因。 原因共有 4

28、 種,即 PROCESS_ATTACH 、 PROCESS_DETACH 、 THREAD_ATTACH 和 THREAD_DETACH ,以 switch 語句列出。來仔細解讀一下 DllMain 的函數頭 BOOL APIENTRY DllMain( HANDLE hModule, WORD ul_reason_for_call, LPVOID lpReserved ) 。APIENTRY 被定義為 _stdcall ,它意味著這個函數以標準Pascal 的方式進行調用,也就是 WINAPI 方式;進程中的每個DLL 模塊被全局唯一的32 字節(jié)的 HINSTANCE句柄標識,只有在特定的進

29、程內部有效,句柄代表了DLL模塊在進程虛擬空間中的起始地址。在Win32中,HINSTANCE和HMODULE的值是相同的,這兩種類型可以替換使用,這就是函數參數hModule的來歷。執(zhí)行下列代碼:hDll = LoadLibrary(".DebugdllTest.dll");if (hDll != NULL)addFun = (lpAddFun)GetProcAddress(hDll, MAKEINTRESOURCE(1);/MAKEINTRESOURCE直接使用導出文件中的序號if (addFun != NULL)int result = addFun(2, 3);pr

30、intf("ncall add in dll:%d", result);FreeLibrary(hDll);我們看到輸出順序為:process attach of dllcall add in dll:5process detach of dll這一輸出順序驗證了DllMain 被調用的時機。代碼中的 GetProcAddress ( hDll, MAKEINTRESOURCE ( 1 ) )值得留意, 它直接通過 .def文件中為add 函數指定的順序號訪問add 函數,具體體現在MAKEINTRESOURCE( 1 ),MAKEINTRESOURCE是一個通過序號獲取函

31、數名的宏,定義為(節(jié)選自winuser.h ):#define MAKEINTRESOURCEA(i) (LPSTR)(DWORD)(WORD)(i)#define MAKEINTRESOURCEW(i) (LPWSTR)(DWORD)(WORD)(i)#ifdef UNICODE#define MAKEINTRESOURCE MAKEINTRESOURCEW#else#define MAKEINTRESOURCE MAKEINTRESOURCEA4.5 _stdcall 約定如果通過VC+ 編寫的 DLL欲被其他語言編寫的程序調用,應將函數的調用方式聲明為_stdcall 方式,WINAPI

32、都采用這種方式, 而 C/C+ 缺省的調用方式卻為_cdecl。_stdcall方式與 _cdecl 對函數名最終生成符號的方式不同。若采用 C 編譯方式 (在 C+ 中需將函數聲明為 extern "C") , _stdcall 調用約定在輸出函數名前面加下劃線,后面加“”符號和參數的字節(jié)數,形如 _functionnamenumber ;而 _cdecl 調用約定僅在輸出函數名前面加下劃線,形如 _functionname 。Windows 編程中常見的幾種函數類型聲明宏都是與_stdcall 和 _cdecl 有關的(節(jié)選自windef.h ):#define CAL

33、LBACK _stdcall /這就是傳說中的回調函數#define WINAPI _stdcall /這就是傳說中的WINAPI#define WINAPIV _cdecl#define APIENTRY WINAPI /DllMain的入口就在這里#define APIPRIVATE _stdcall#define PASCAL _stdcall在 lib.h 中,應這樣聲明add 函數:int _stdcall add(int x, int y);在應用工程中函數指針類型應定義為:typedef int(_stdcall *lpAddFun)(int, int);若 在lib.h中將函數

34、聲明為_stdcall調用,而應用工程中仍使用typedefint(*lpAddFun)(int,int) ,運行時將發(fā)生錯誤 (因為類型不匹配, 在應用工程中仍然是缺省的 _cdecl 調用),彈出如圖 7 所示的對話框。圖 7 調用約定不匹配時的運行錯誤圖 8 中的那段話實際上已經給出了錯誤的原因,即“This is usually a result of ”。單擊此處下載_stdcall 調用例子工程源代碼附件。4.6 DLL 導出變量DLL 定義的全局變量可以被調用進程訪問;DLL 也可以訪問調用進程的全局數據,我們來看看在應用工程中引用DLL 中變量的例子(單擊此處下載本工程附件)。

35、/*文件名: lib.h*/#ifndef LIB_H#define LIB_Hextern int dllGlobalVar;#endif/*文件名: lib.cpp */#include "lib.h"#include <windows.h>int dllGlobalVar;BOOL APIENTRY DllMain(HANDLE hModule, DWORD ul_reason_for_call, LPVOID lpReserved)switch (ul_reason_for_call)case DLL_PROCESS_A TTACH:dllGlobalV

36、ar = 100; / 在 dll 被加載時,賦全局變量為100break;case DLL_THREAD_ATTACH:case DLL_THREAD_DETACH:case DLL_PROCESS_DETACH:break;return TRUE;文件名: lib.def;在 DLL 中導出變量LIBRARY "dllTest"EXPORTSdllGlobalVar CONSTANT;或 dllGlobalVar DATAGetGlobalVar從 lib.h 和 lib.cpp 中可以看出, 全局變量在是一樣的。若要導出某全局變量,我們需要在變量名CONSTANT/過

37、時的方法DLL 中的定義和使用方法與一般的程序設計.def 文件的 EXPORTS 后添加:或變量名DATA/VC+提示的新方法在主函數中引用DLL 中定義的全局變量:#include <stdio.h>#pragma comment(lib,"dllTest.lib")extern int dllGlobalVar;int main(int argc, char *argv)printf("%d ", *(int*)dllGlobalVar);*(int*)dllGlobalVar = 1;printf("%d ", *

38、(int*)dllGlobalVar);return 0;特別要注意的是用extern int dllGlobalVar 聲明所導入的并不是DLL 中全局變量本身,而是其地址,應用程序必須通過強制指針轉換來使用DLL中的全局變量。這一點,從*(int*)dllGlobalVar可以看出。因此在采用這種方式引用DLL 全局變量時,千萬不要進行這樣的賦值操作:dllGlobalVar = 1;其結果是dllGlobalVar 指針的內容發(fā)生變化,程序中以后再也引用不到DLL中的全局變量了。在應用工程中引用DLL 中全局變量的一個更好方法是:#include <stdio.h>#prag

39、ma comment(lib,"dllTest.lib")extern int _declspec(dllimport) dllGlobalVar; /用 _declspec(dllimport) 導入int main(int argc, char *argv)printf("%d ", dllGlobalVar);dllGlobalVar = 1; / 這里就可以直接使用, 無須進行強制指針轉換printf("%d ", dllGlobalVar);return 0;通過 _declspec(dllimport) 方式導入的就是 D

40、LL 中全局變量本身而不再是其地址了, 筆者建議在一切可能的情況下都使用這種方式。4.7 DLL 導出類DLL 中定義的類可以在應用工程中使用。下面的例子里,我們在 DLL 中定義了 point 和 circle 兩個類,并在應用工程中引用了它們(單擊此處下載本工程附件) 。/文件名: point.h ,point 類的聲明#ifndef POINT_H#define POINT_H#ifdef DLL_FILEclass _declspec(dllexport) point / 導出類 point#elseclass _declspec(dllimport) point / 導入類 poin

41、t#endifpublic:float y;float x;point();point(float x_coordinate, float y_coordinate);#endif/文件名: point.cpp , point 類的實現#ifndef DLL_FILE#define DLL_FILE#endif#include "point.h"/類 point 的缺省構造函數point:point()x = 0.0;y = 0.0;/類 point 的構造函數point:point(float x_coordinate, float y_coordinate)x = x_

42、coordinate;y = y_coordinate;/文件名: circle.h , circle 類的聲明#ifndef CIRCLE_H#define CIRCLE_H#include "point.h"#ifdef DLL_FILEclass _declspec(dllexport)circle / 導出類 circle#elseclass _declspec(dllimport)circle / 導入類 circle#endifpublic:void SetCentre(const pointrePoint);void SetRadius(float r);float GetGirth();float GetArea();circle();private:float radius;point centre;#endif/文件名: circle.cpp , circle 類的實現#ifndef DLL_FILE#define DLL_FILE#endif#include "circle.h"#define PI 3.1415926/circle 類的構造函數circle:circle()centre = point(0,

溫馨提示

  • 1. 本站所有資源如無特殊說明,都需要本地電腦安裝OFFICE2007和PDF閱讀器。圖紙軟件為CAD,CAXA,PROE,UG,SolidWorks等.壓縮文件請下載最新的WinRAR軟件解壓。
  • 2. 本站的文檔不包含任何第三方提供的附件圖紙等,如果需要附件,請聯(lián)系上傳者。文件的所有權益歸上傳用戶所有。
  • 3. 本站RAR壓縮包中若帶圖紙,網頁內容里面會有圖紙預覽,若沒有圖紙預覽就沒有圖紙。
  • 4. 未經權益所有人同意不得將文件中的內容挪作商業(yè)或盈利用途。
  • 5. 人人文庫網僅提供信息存儲空間,僅對用戶上傳內容的表現方式做保護處理,對用戶上傳分享的文檔內容本身不做任何修改或編輯,并不能對任何下載內容負責。
  • 6. 下載文件中如有侵權或不適當內容,請與我們聯(lián)系,我們立即糾正。
  • 7. 本站不保證下載資源的準確性、安全性和完整性, 同時也不承擔用戶因使用這些下載資源對自己和他人造成任何形式的傷害或損失。

評論

0/150

提交評論