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

            ivy-jie

            progress ...

            C++博客 首頁 新隨筆 聯系 聚合 管理
              9 Posts :: 41 Stories :: 6 Comments :: 0 Trackbacks

            一、前言

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

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

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

              二、DLL的概念

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

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

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

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

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

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

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

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

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

            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;
            }

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

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

              1)使用導出函數關鍵字_declspec(dllexport)創(chuàng)建MyDll.dll,該動態(tài)鏈接庫中有兩個函數,分別用來實現得到兩個數的最大和最小數。在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
            #include<stdio.h></stdio.h>
            #include"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;
            }

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

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

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

            LIBRARY MyDll
            EXPORTS
            Max
            Min

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

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

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

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

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

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

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

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

            四、動態(tài)鏈接庫DLL的鏈接

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

              1.隱式鏈接

              隱式鏈接就是在程序開始執(zhí)行時就將DLL文件加載到應用程序當中。實現隱式鏈接很容易,只要將導入函數關鍵字_declspec(dllimport)函數名等寫到應用程序相應的頭文件中就可以了。下面的例子通過隱式鏈接調用MyDll.dll庫中的Min函數。首先生成一個項目為TestDll,在DllTest.h、DllTest.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
            #include<stdio.h></stdio.h>
            #include"Dlltest.h"
            void main()
            {int a;
            a=min(8,10)
            printf("比較的結果為%d\n",a);
            }

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

              2.顯式鏈接

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

            #include <studio.h></studio.h>
            #include<widows.h></widows.h>
            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");//加載動態(tài)鏈接庫MyDll.dll文件;
            Max=(pMax)GetProcAddress(hDLL,"Max");
            A=Max(5,8);
            Printf("比較的結果為%d\n",a);
            FreeLibrary(hDLL);//卸載MyDll.dll文件;
            }

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

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

              本文通過通俗易懂的方式,全面介紹了動態(tài)鏈接庫的概念、動態(tài)鏈接庫的創(chuàng)建和動態(tài)鏈接庫的鏈接,并給出個簡單明了的例子,相信讀者看了本文后,能夠創(chuàng)建自己的動態(tài)鏈接庫并應用到后續(xù)的軟件開發(fā)當中去了,當然,讀者要熟練操作DLL,還需要在大量的實踐中不斷摸索,希望本文能起到拋磚引玉的作用。

            posted on 2009-06-22 20:46 ivy-jie 閱讀(181) 評論(0)  編輯 收藏 引用 所屬分類: MFC
            午夜精品久久久久久久久| 久久亚洲国产精品成人AV秋霞 | 精品亚洲综合久久中文字幕| 中文精品99久久国产| 久久天堂电影网| 看全色黄大色大片免费久久久| 久久国产香蕉视频| 精品久久久久国产免费| 欧美成a人片免费看久久| 99久久国产精品免费一区二区| 国产精品欧美久久久天天影视| 久久久久亚洲AV无码网站| 91久久精一区二区三区大全| 久久久久无码国产精品不卡| 97香蕉久久夜色精品国产| 久久99国产精品久久99| 亚洲午夜无码AV毛片久久| 国产成年无码久久久久毛片| 亚洲精品无码久久久久久| 伊人久久大香线蕉影院95| 伊人色综合九久久天天蜜桃| 久久精品国产精品青草app| 一日本道伊人久久综合影| 91超碰碰碰碰久久久久久综合| 久久中文字幕人妻丝袜| 久久国产一区二区| 一本一本久久a久久综合精品蜜桃| 大美女久久久久久j久久| 无码人妻久久一区二区三区| 色偷偷888欧美精品久久久| 久久精品亚洲精品国产色婷| 久久精品成人欧美大片| 亚洲AV伊人久久青青草原| 精品水蜜桃久久久久久久| 青青青青久久精品国产| 国产美女久久精品香蕉69| 久久精品国产只有精品66| 国产色综合久久无码有码| 影音先锋女人AV鲁色资源网久久 | 久久久久亚洲精品日久生情| 国产精品免费久久久久电影网|