• <ins id="pjuwb"></ins>
    <blockquote id="pjuwb"><pre id="pjuwb"></pre></blockquote>
    <noscript id="pjuwb"></noscript>
          <sup id="pjuwb"><pre id="pjuwb"></pre></sup>
            <dd id="pjuwb"></dd>
            <abbr id="pjuwb"></abbr>

            君子性非異也,善假于物也。

            如有恒,何須三更起,半夜眠;最怕莫,三天打魚兩天曬網(wǎng),竹籃打水一場空!
            posts - 31, comments - 23, trackbacks - 0, articles - 30
              C++博客 :: 首頁 :: 新隨筆 :: 聯(lián)系 :: 聚合  :: 管理

            一、前言

              自從微軟推出 16 位的 Windows 操作系統(tǒng)起,此后每種版本的 Windows 操作系統(tǒng)都非常依賴于動(dòng)態(tài)鏈接庫 (DLL) 中的函數(shù)和數(shù)據(jù),實(shí)際上 Windows 操作系統(tǒng)中幾乎所有的內(nèi)容都由 DLL 以一種或另外一種形式代表著,例如顯示的字體和圖標(biāo)存儲(chǔ)在 GDI DLL 中、顯示 Windows 桌面和處理用戶的輸入所需要的代碼被存儲(chǔ)在一個(gè) User DLL 中、 Windows 編程所需要的大量的 API 函數(shù)也被包含在 Kernel DLL 中。

              在 Windows 操作系統(tǒng)中使用 DLL 有很多優(yōu)點(diǎn),最主要的一點(diǎn)是多個(gè)應(yīng)用程序、甚至是不同語言編寫的應(yīng)用程序可以共享一個(gè) DLL 文件,真正實(shí)現(xiàn)了資源 " 共享 " ,大大縮小了應(yīng)用程序的執(zhí)行代碼,更加有效的利用了內(nèi)存;使用 DLL 的另一個(gè)優(yōu)點(diǎn)是 DLL 文件作為一個(gè)單獨(dú)的程序模塊,封裝性、獨(dú)立性好,在軟件需要升級(jí)的時(shí)候,開發(fā)人員只需要修改相應(yīng)的 DLL 文件就可以了,而且,當(dāng) DLL 中的函數(shù)改變后,只要不是參數(shù)的改變 , 程序代碼并不需要重新編譯。這在編程時(shí)十分有用,大大提高了軟件開發(fā)和維護(hù)的效率。

              既然 DLL 那么重要,所以搞清楚什么是 DLL 、如何在 Windows 操作系統(tǒng)中開發(fā)使用 DLL 是程序開發(fā)人員不得不解決的一個(gè)問題。本文針對(duì)這些問題,通過一個(gè)簡單的例子,即在一個(gè) DLL 中實(shí)現(xiàn)比較最大、最小整數(shù)這兩個(gè)簡單函數(shù),全面地解析了在 Visual C++ 編譯環(huán)境下編程實(shí)現(xiàn) DLL 的過程,文章中所用到的程序代碼在 Windows98 系統(tǒng)、 Visual C++6.0 編譯環(huán)境下通過。

              二、 DLL 的概念

               DLL 是建立在客戶 / 服務(wù)器通信的概念上,包含若干函數(shù)、類或資源的庫文件,函數(shù)和數(shù)據(jù)被存儲(chǔ)在一個(gè) DLL (服務(wù)器)上并由一個(gè)或多個(gè)客戶導(dǎo)出而使用,這些客戶可以是應(yīng)用程序或者是其它的 DLL DLL 庫不同于靜態(tài)庫,在靜態(tài)庫情況下,函數(shù)和數(shù)據(jù)被編譯進(jìn)一個(gè)二進(jìn)制文件(通常擴(kuò)展名為 *.LIB ), Visual C++ 的編譯器在處理程序代碼時(shí)將從靜態(tài)庫中恢復(fù)這些函數(shù)和數(shù)據(jù)并把他們和應(yīng)用程序中的其他模塊組合在一起生成可執(zhí)行文件。這個(gè)過程稱為 " 靜態(tài)鏈接 " ,此時(shí)因?yàn)閼?yīng)用程序所需的全部內(nèi)容都是從庫中復(fù)制了出來,所以靜態(tài)庫本身并不需要與可執(zhí)行文件一起發(fā)行。

              在動(dòng)態(tài)庫的情況下,有兩個(gè)文件,一個(gè)是引入庫( .LIB )文件,一個(gè)是 DLL 文件,引入庫文件包含被 DLL 導(dǎo)出的函數(shù)的名稱和位置, DLL 包含實(shí)際的函數(shù)和數(shù)據(jù),應(yīng)用程序使用 LIB 文件鏈接到所需要使用的 DLL 文件,庫中的函數(shù)和數(shù)據(jù)并不復(fù)制到可執(zhí)行文件中,因此在應(yīng)用程序的可執(zhí)行文件中,存放的不是被調(diào)用的函數(shù)代碼,而是 DLL 中所要調(diào)用的函數(shù)的內(nèi)存地址,這樣當(dāng)一個(gè)或多個(gè)應(yīng)用程序運(yùn)行是再把程序代碼和被調(diào)用的函數(shù)代碼鏈接起來,從而節(jié)省了內(nèi)存資源。從上面的說明可以看出, DLL .LIB 文件必須隨應(yīng)用程序一起發(fā)行,否則應(yīng)用程序?qū)?huì)產(chǎn)生錯(cuò)誤。

              微軟的 Visual C++ 支持三種 DLL ,它們分別是 Non-MFC Dll (非 MFC 動(dòng)態(tài)庫)、 Regular Dll (常規(guī) DLL )、 Extension Dll (擴(kuò)展 DLL )。 Non-MFC DLL 指的是不用 MFC 的類庫結(jié)構(gòu),直接用 C 語言寫的 DLL ,其導(dǎo)出的函數(shù)是標(biāo)準(zhǔn)的 C 接口,能被非 MFC MFC 編寫的應(yīng)用程序所調(diào)用。 Regular DLL: 和下述的 Extension Dlls 一樣,是用 MFC 類庫編寫的,它的一個(gè)明顯的特點(diǎn)是在源文件里有一個(gè)繼承 CWinApp 的類(注意:此類 DLL 雖然從 CWinApp 派生,但沒有消息循環(huán)) , 被導(dǎo)出的函數(shù)是 C 函數(shù)、 C++ 類或者 C++ 成員函數(shù)(注意不要把術(shù)語 C++ 類與 MFC 的微軟基礎(chǔ) C++ 類相混淆),調(diào)用常規(guī) DLL 的應(yīng)用程序不必是 MFC 應(yīng)用程序,只要是能調(diào)用類 C 函數(shù)的應(yīng)用程序就可以,它們可以是在 Visual C++ Dephi Visual Basic Borland C 等編譯環(huán)境下利用 DLL 開發(fā)應(yīng)用程序。

              常規(guī) DLL 又可細(xì)分成靜態(tài)鏈接到 MFC 和動(dòng)態(tài)鏈接到 MFC 上的,這兩種常規(guī) DLL 的區(qū)別將在下面介紹。與常規(guī) DLL 相比,使用擴(kuò)展 DLL 用于導(dǎo)出增強(qiáng) MFC 基礎(chǔ)類的函數(shù)或子類,用這種類型的動(dòng)態(tài)鏈接庫,可以用來輸出一個(gè)從 MFC 所繼承下來的類。

              擴(kuò)展 DLL 是使用 MFC 的動(dòng)態(tài)鏈接版本所創(chuàng)建的,并且它只被用 MFC 類庫所編寫的應(yīng)用程序所調(diào)用。例如你已經(jīng)創(chuàng)建了一個(gè)從 MFC CtoolBar 類的派生類用于創(chuàng)建一個(gè)新的工具欄,為了導(dǎo)出這個(gè)類,你必須把它放到一個(gè) MFC 擴(kuò)展的 DLL 中。擴(kuò)展 DLL 和常規(guī) DLL 不一樣,它沒有一個(gè)從 CWinApp 繼承而來的類的對(duì)象,所以,開發(fā)人員必須在 DLL 中的 DllMain 函數(shù)添加初始化代碼和結(jié)束代碼。

            三、動(dòng)態(tài)鏈接庫的創(chuàng)建

              在 Visual C++6.0 開發(fā)環(huán)境下,打開 FileNewProject 選項(xiàng),可以選擇 Win32 Dynamic-Link Library MFC AppWizard[dll] 來以不同的方式來創(chuàng)建 Non-MFC Dll Regular Dll Extension Dll 等不同種類的動(dòng)態(tài)鏈接庫。

               1 Win32 Dynamic-Link Library 方式創(chuàng)建 Non-MFC DLL 動(dòng)態(tài)鏈接庫

              每一個(gè) DLL 必須有一個(gè)入口點(diǎn),這就象我們用 C 編寫的應(yīng)用程序一樣,必須有一個(gè) WINMAIN 函數(shù)一樣。在 Non-MFC DLL DllMain 是一個(gè)缺省的入口函數(shù),你不需要編寫自己的 DLL 入口函數(shù),用這個(gè)缺省的入口函數(shù)就能使動(dòng)態(tài)鏈接庫被調(diào)用時(shí)得到正確的初始化。如果應(yīng)用程序的 DLL 需要分配額外的內(nèi)存或資源時(shí),或者說需要對(duì)每個(gè)進(jìn)程或線程初始化和清除操作時(shí),需要在相應(yīng)的 DLL 工程的 .CPP 文件中對(duì) DllMain() 函數(shù)按照下面的格式書寫。
             

             

            BOOL APIENTRY DllMain(HANDLE hModule,DWORD ul_reason_for_call,LPVOID lpReserved)
            {
            switch( ul_reason_for_call )
            {
            case DLL_PROCESS_ATTACH:
            .......
            case DLL_THREAD_ATTACH:
            .......
            case DLL_THREAD_DETACH:
            .......
            case DLL_PROCESS_DETACH:
            .......
            }
            return TRUE;
            }

              參數(shù)中, hMoudle 是動(dòng)態(tài)庫被調(diào)用時(shí)所傳遞來的一個(gè)指向自己的句柄 ( 實(shí)際上,它是指向 _DGROUP 段的一個(gè)選擇符 ) ul_reason_for_call 是一個(gè)說明動(dòng)態(tài)庫被調(diào)原因的標(biāo)志,當(dāng)進(jìn)程或線程裝入或卸載動(dòng)態(tài)鏈接庫的時(shí)候,操作系統(tǒng)調(diào)用入口函數(shù),并說明動(dòng)態(tài)鏈接庫被調(diào)用的原因,它所有的可能值為: DLL_PROCESS_ATTACH: 進(jìn)程被調(diào)用、 DLL_THREAD_ATTACH: 線程被調(diào)用、 DLL_PROCESS_DETACH: 進(jìn)程被停止、 DLL_THREAD_DETACH: 線程被停止; lpReserved 為保留參數(shù)。到此為止, DLL 的入口函數(shù)已經(jīng)寫了,剩下部分的實(shí)現(xiàn)也不難,你可以在 DLL 工程中加入你所想要輸出的函數(shù)或變量了。

              我們已經(jīng)知道 DLL 是包含若干個(gè)函數(shù)的庫文件,應(yīng)用程序使用 DLL 中的函數(shù)之前,應(yīng)該先導(dǎo)出這些函數(shù),以便供給應(yīng)用程序使用。要導(dǎo)出這些函數(shù)有兩種方法,一是在定義函數(shù)時(shí)使用導(dǎo)出關(guān)鍵字 _declspec(dllexport) ,另外一種方法是在創(chuàng)建 DLL 文件時(shí)使用模塊定義文件 .Def 。需要讀者注意的是在使用第一種方法的時(shí)候,不能使用 DEF 文件。下面通過兩個(gè)例子來說明如何使用這兩種方法創(chuàng)建 DLL 文件。

               1 )使用導(dǎo)出函數(shù)關(guān)鍵字 _declspec(dllexport) 創(chuàng)建 MyDll.dll ,該動(dòng)態(tài)鏈接庫中有兩個(gè)函數(shù),分別用來實(shí)現(xiàn)得到兩個(gè)數(shù)的最大和最小數(shù)。在 MyDll.h MyDLL.cpp 文件中分別輸入如下原代碼:
             

            //MyDLL.h
            extern "C" _declspec(dllexport) int Max(int a, int b);
            extern "C" _declspec(dllexport) int Min(int a, int b);
            //MyDll.cpp
            #i nclude
            #i nclude"MyDll.h"
            int Max(int a, int b)
            {
            if(a>=b)return a;
            else
            return b;
            }
            int Min(int a, int b)
            {
            if(a>=b)return b;
            else
            return a;
            }

              該動(dòng)態(tài)鏈接庫編譯成功后,打開 MyDll 工程中的 debug 目錄,可以看到 MyDll.dll MyDll.lib 兩個(gè)文件。 LIB 文件中包含 DLL 文件名和 DLL 文件中的函數(shù)名等,該 LIB 文件只是對(duì)應(yīng)該 DLL 文件的 " 映像文件 " ,與 DLL 文件中, LIB 文件的長度要小的多,在進(jìn)行隱式鏈接 DLL 時(shí)要用到它。讀者可能已經(jīng)注意到在 MyDll.h 中有關(guān)鍵字 "extern C" ,它可以使其他編程語言訪問你編寫的 DLL 中的函數(shù)。

               2 )用 .def 文件創(chuàng)建工程 MyDll

              為了用 .def 文件創(chuàng)建 DLL ,請(qǐng)先刪除上個(gè)例子創(chuàng)建的工程中的 MyDll.h 文件,保留 MyDll.cpp 并在該文件頭刪除 #i nclude MyDll.h 語句,同時(shí)往該工程中加入一個(gè)文本文件,命名為 MyDll.def ,再在該文件中加入如下代碼:


            LIBRARY MyDll
            EXPORTS
            Max
            Min

              其中 LIBRARY 語句說明該 def 文件是屬于相應(yīng) DLL 的, EXPORTS 語句下列出要導(dǎo)出的函數(shù)名稱。我們可以在 .def 文件中的導(dǎo)出函數(shù)后加 @n ,如 Max@1 Min@2 ,表示要導(dǎo)出的函數(shù)順序號(hào),在進(jìn)行顯式連時(shí)可以用到它。該 DLL 編譯成功后,打開工程中的 Debug 目錄,同樣也會(huì)看到 MyDll.dll MyDll.lib 文件。

               2 MFC AppWizard[dll] 方式生成常規(guī) / 擴(kuò)展 DLL

              在 MFC AppWizard[dll] 下生成 DLL 文件又有三種方式,在創(chuàng)建 DLL 是,要根據(jù)實(shí)際情況選擇創(chuàng)建 DLL 的方式。一種是常規(guī) DLL 靜態(tài)鏈接到 MFC ,另一種是常規(guī) DLL 動(dòng)態(tài)鏈接到 MFC 。兩者的區(qū)別是:前者使用的是 MFC 的靜態(tài)鏈接庫,生成的 DLL 文件長度大,一般不使用這種方式,后者使用 MFC 的動(dòng)態(tài)鏈接庫,生成的 DLL 文件長度小;動(dòng)態(tài)鏈接到 MFC 的規(guī)則 DLL 所有輸出的函數(shù)應(yīng)該以如下語句開始:

             

            AFX_MANAGE_STATE(AfxGetStaticModuleState( )) // 此語句用來正確地切換 MFC 模塊狀態(tài)

              最后一種是 MFC 擴(kuò)展 DLL ,這種 DLL 特點(diǎn)是用來建立 MFC 的派生類, Dll 只被用 MFC 類庫所編寫的應(yīng)用程序所調(diào)用。前面我們已經(jīng)介紹過, Extension DLLs Regular DLLs 不一樣,它沒有一個(gè)從 CWinApp 繼承而來的類的對(duì)象,編譯器默認(rèn)了一個(gè) DLL 入口函數(shù) DLLMain() 作為對(duì) DLL 的初始化,你可以在此函數(shù)中實(shí)現(xiàn)初始化 , 代碼如下:
             

            BOOL WINAPI APIENTRY DLLMain(HINSTANCE hinstDll DWORD reason LPVOID flmpload)
            {
            switch(reason)
            {
            ……………//
            初始化代碼;

            }
            return true;
            }

              參數(shù) hinstDll 存放 DLL 的句柄,參數(shù) reason 指明調(diào)用函數(shù)的原因, lpReserved 是一個(gè)被系統(tǒng)所保留的參數(shù)。對(duì)于隱式鏈接是一個(gè)非零值,對(duì)于顯式鏈接值是零。

              在 MFC 下建立 DLL 文件,會(huì)自動(dòng)生成 def 文件框架,其它與建立傳統(tǒng)的 Non-MFC DLL 沒有什么區(qū)別,只要在相應(yīng)的頭文件寫入關(guān)鍵字 _declspec(dllexport) 函數(shù)類型和函數(shù)名等,或在生成的 def 文件中 EXPORTS 下輸入函數(shù)名就可以了。需要注意的是在向其它開發(fā)人員分發(fā) MFC 擴(kuò)展 DLL 時(shí),不要忘記提供描述 DLL 中類的頭文件以及相應(yīng)的 .LIB 文件和 DLL 本身,此后開發(fā)人員就能充分利用你開發(fā)的擴(kuò)展 DLL 了。

             應(yīng)用程序使用DLL可以采用兩種方式:一種是隱式鏈接,另一種是顯式鏈接。在使用DLL之前首先要知道DLL中函數(shù)的結(jié)構(gòu)信息。Visual C++6.0VCin目錄下提供了一個(gè)名為Dumpbin.exe的小程序,用它可以查看DLL文件中的函數(shù)結(jié)構(gòu)。另外,Windows系統(tǒng)將遵循下面的搜索順序來定位DLL 1.包含EXE文件的目錄,2.進(jìn)程的當(dāng)前工作目錄, 3Windows系統(tǒng)目錄, 4Windows目錄,5.列在Path環(huán)境變量中的一系列目錄。

              1.隱式鏈接

              隱式鏈接就是在程序開始執(zhí)行時(shí)就將DLL文件加載到應(yīng)用程序當(dāng)中。實(shí)現(xiàn)隱式鏈接很容易,只要將導(dǎo)入函數(shù)關(guān)鍵字_declspec(dllimport)函數(shù)名等寫到應(yīng)用程序相應(yīng)的頭文件中就可以了。下面的例子通過隱式鏈接調(diào)用MyDll.dll庫中的Min函數(shù)。首先生成一個(gè)項(xiàng)目為TestDll,在DllTest.hDllTest.cpp文件中分別輸入如下代碼:
             

             

             

            //Dlltest.h
            #pragma comment(lib
            "MyDll.lib")
            extern "C"_declspec(dllimport) int Max(int a,int b);
            extern "C"_declspec(dllimport) int Min(int a,int b);
            //TestDll.cpp
            #i nclude
            #i nclude"Dlltest.h"
            void main()
            {int a;
            a=min(8,10)
            printf("
            比較的結(jié)果為 %d " a);
            }
             

              在創(chuàng)建 DllTest.exe 文件之前,要先將 MyDll.dll MyDll.lib 拷貝到當(dāng)前工程所在的目錄下面,也可以拷貝到 windows System 目錄下。如果 DLL 使用的是 def 文件,要?jiǎng)h除 TestDll.h 文件中關(guān)鍵字 extern "C" TestDll.h 文件中的關(guān)鍵字 Progam commit 是要 Visual C+ 的編譯器在 link 時(shí),鏈接到 MyDll.lib 文件,當(dāng)然,開發(fā)人員也可以不使用 #pragma comment(lib "MyDll.lib") 語句,而直接在工程的 Setting->Link 頁的 Object/Moduls 欄填入 MyDll.lib 既可。

               2 .顯式鏈接

              顯式鏈接是應(yīng)用程序在執(zhí)行過程中隨時(shí)可以加載 DLL 文件,也可以隨時(shí)卸載 DLL 文件,這是隱式鏈接所無法作到的,所以顯式鏈接具有更好的靈活性,對(duì)于解釋性語言更為合適。不過實(shí)現(xiàn)顯式鏈接要麻煩一些。在應(yīng)用程序中用 LoadLibrary MFC 提供的 AfxLoadLibrary 顯式的將自己所做的動(dòng)態(tài)鏈接庫調(diào)進(jìn)來,動(dòng)態(tài)鏈接庫的文件名即是上述兩個(gè)函數(shù)的參數(shù),此后再用 GetProcAddress() 獲取想要引入的函數(shù)。自此,你就可以象使用如同在應(yīng)用程序自定義的函數(shù)一樣來調(diào)用此引入函數(shù)了。在應(yīng)用程序退出之前,應(yīng)該用 FreeLibrary MFC 提供的 AfxFreeLibrary 釋放動(dòng)態(tài)鏈接庫。下面是通過顯式鏈接調(diào)用 DLL 中的 Max 函數(shù)的例子。
             

             

            #i nclude
            #i nclude
            void main(void)
            {
            typedef int(*pMax)(int a,int b);
            typedef int(*pMin)(int a,int b);
            HINSTANCE hDLL;
            PMax Max
            HDLL=LoadLibrary("MyDll.dll");//
            加載動(dòng)態(tài)鏈接庫 MyDll.dll 文件;
            Max=(pMax)GetProcAddress(hDLL,"Max");
            A=Max(5,8);
            Printf("
            比較的結(jié)果為 %d " a);
            FreeLibrary(hDLL);//
            卸載 MyDll.dll 文件;
            }

              在上例中使用類型定義關(guān)鍵字 typedef ,定義指向和 DLL 中相同的函數(shù)原型指針,然后通過 LoadLibray() DLL 加載到當(dāng)前的應(yīng)用程序中并返回當(dāng)前 DLL 文件的句柄,然后通過 GetProcAddress() 函數(shù)獲取導(dǎo)入到應(yīng)用程序中的函數(shù)指針,函數(shù)調(diào)用完畢后,使用 FreeLibrary() 卸載 DLL 文件。在編譯程序之前,首先要將 DLL 文件拷貝到工程所在的目錄或 Windows 系統(tǒng)目錄下。

              使用顯式鏈接應(yīng)用程序編譯時(shí)不需要使用相應(yīng)的 Lib 文件。另外,使用 GetProcAddress() 函數(shù)時(shí),可以利用 MAKEINTRESOURCE() 函數(shù)直接使用 DLL 中函數(shù)出現(xiàn)的順序號(hào),如將 GetProcAddress(hDLL,"Min") 改為 GetProcAddress(hDLL, MAKEINTRESOURCE(2)) (函數(shù) Min() DLL 中的順序號(hào)是 2 ),這樣調(diào)用 DLL 中的函數(shù)速度很快,但是要記住函數(shù)的使用序號(hào),否則會(huì)發(fā)生錯(cuò)誤。 

            Feedback

            # re: 基于 Visual C++6.0 的 DLL 編程實(shí)現(xiàn)[未登錄]  回復(fù)  更多評(píng)論   

            2007-12-04 21:04 by 夢在天涯
            經(jīng)典文章,我也轉(zhuǎn)過
            久久人人爽人人爽人人AV东京热| 国产精品99久久久久久猫咪| 久久精品二区| 久久艹国产| 久久线看观看精品香蕉国产| 国产成人久久精品一区二区三区| 无码精品久久久久久人妻中字| 久久精品一本到99热免费| 日日狠狠久久偷偷色综合免费 | 亚洲AV无码1区2区久久| 久久九九兔免费精品6| 亚洲av成人无码久久精品| 99re久久精品国产首页2020| 精品国产婷婷久久久| 久久久久国产| 久久久久久久精品成人热色戒| 久久精品国产亚洲av麻豆图片| 伊人久久综合精品无码AV专区| 久久精品人人做人人妻人人玩| 国产精品久久久久久影院| 国产亚州精品女人久久久久久 | 久久精品国产精品亚洲精品 | 久久精品人人做人人爽电影蜜月 | 久久久久久国产精品无码超碰| 久久综合香蕉国产蜜臀AV| 国产成人精品久久二区二区| 97超级碰碰碰碰久久久久| 亚洲国产成人久久精品99 | 777久久精品一区二区三区无码| 久久久久久国产精品美女| 国产A三级久久精品| 久久AV高清无码| 思思久久99热只有频精品66| 久久久久人妻精品一区二区三区| 国产精品美女久久久久av爽| 97久久国产露脸精品国产| 精品久久久久中文字幕一区| 久久综合88熟人妻| 日韩一区二区三区视频久久| 久久99国产精品久久99果冻传媒| 久久亚洲sm情趣捆绑调教|