乡下人产国偷v产偷v自拍,国产午夜片在线观看,婷婷成人亚洲综合国产麻豆,久久综合给合久久狠狠狠9

  • <output id="e9wm2"></output>
    <s id="e9wm2"><nobr id="e9wm2"><ins id="e9wm2"></ins></nobr></s>

    • 分享

      VC++制作DLL詳解

       imelee 2017-09-27

      1.    DLL的基本概念

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

        1.    靜態(tài)鏈接

      鏈接程序搜索對應的庫文件(.lib),然后將這個對象模塊拷貝到應用程序(.exe)中來。Windows之所不使用靜態(tài)鏈接庫,是因為很多基礎庫被很多應用程序使用。如果每個應用程序一份拷貝,將帶來內(nèi)存的極大浪費。

        2.    動態(tài)鏈接

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

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

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

       

      然后就像普通工程一樣,添加頭文件的聲明以及源文件的實現(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.    動態(tài)鏈接庫

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

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

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

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

        1.    MFC動態(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。具體實現(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。

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

       

       

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

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

        2.    MFC規(guī)則DLL

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

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

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

      鏈接MFC動態(tài)庫資源的切換。這一點需要注意,并且VC默認生成的代碼中也用大篇幅的注釋提示了,并且也給出了如下基本的解釋。

      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動態(tài)庫基本上和鏈接MFC靜態(tài)庫除了上面介紹的不同,導出和添加文件之類的完全一樣。所以下面重點講解鏈接MFC靜態(tài)庫。

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

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

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

      LIBRARY      "MFCDLL"

      EXPORTS

      ; Explicit exports can go here

      ShowDlg @2

      nDllValue DATA

      注釋是通過;來完成的。

      關鍵字LIBRARY,描述DLL的名字,并將此信息寫入記錄DLL信息的.lib文件中。所以如果在Linker\Output File中修改了生成的DLL的名字,注意也一定要與LIBRARY中描述的一致。當然也可以直接注釋掉LIBRARY,這樣生成的DLL信息就直接與Linker\Output File中指定的名字相同。另外LIBARAY后面描述的名字,可以加引號,也可以不加引號。ShowDlg,這個是需要導出的函數(shù)名。@2,這里的2是描述方法的地址索引,可以修改,也可以不使用,系統(tǒng)會生成默認的。其實不僅僅函數(shù)有,變量也有。

       

      如果同時使用了.def和__declspec(dllexport)導出,編譯器會優(yōu)先使用.def文件的導出。.def文件的導出默認是C編譯的,即和extern “c” __declspec(dllexport)的導出效果一樣。

      導入函數(shù)的方法和前面使用的一樣。下面只說一下導入變量的方法。

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

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

      提供按序號導入的原因是這樣導入的速度更快,不用去按名字比對查找。

      這里只介紹了.def文件導出導入的常用的一些方法,但是基本上夠用。如果想更深入的了解.def文件還涉及到很多知識點,可以參考:

      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擴展DLL

      MFC 擴展 DLL 是通常實現(xiàn)從現(xiàn)有 Microsoft 基礎類庫類派生的可重用類的 DLL。

      MFC 擴展 DLL 具有下列功能和要求:

      • 客戶端可執(zhí)行文件必須是用定義的 _AFXDLL 編譯的 MFC 應用程序。
      • 擴展 DLL 也可由動態(tài)鏈接到 MFC 的規(guī)則 DLL 使用。
      • 擴展 DLL 應該用定義的 _AFXEXT 編譯。 這將強制同時定義 _AFXDLL,并確保從 MFC 頭文件中拉入正確的聲明。 它也確保了在生成 DLL 時將 AFX_EXT_CLASS 定義為__declspec(dllexport),這在使用此宏聲明擴展 DLL 中的類時是必要的。
      • 擴展 DLL 不應實例化從 CWinApp 派生的類,而應依賴客戶端應用程序(或 DLL)提供此對象。
      • 但擴展 DLL 應提供 DllMain 函數(shù),并在那里執(zhí)行任何必需的初始化。

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

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

      客戶端必須定義_AFXDLL 編譯,其實就是說客戶端必須使用MFC動態(tài)庫,即共享MFC庫。另外,擴展DLL中顯示對話框,和動態(tài)鏈接MFC的DLL一樣需要進行資源的切換,只是兩個DLL的DllMain函數(shù)不同,導致切換資源的方法不同。擴展DLL的切換方法。MFC擴展DLL的導入導出,基本上和靜態(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ù)來自動完成資源的切換,詳見示例代碼。

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

      想詳細了解擴展DLL的請參考:

      http://msdn.microsoft.com/zh-cn/library/1btd5ea3.aspx

      http://msdn.microsoft.com/zh-cn/library/h5f7ck28(VS.80).aspx

         4.   純資源DLL

      一個純資源 DLL 是一個 DLL,它包含資源如圖標、 位圖、 字符串和對話框。 使用一個純資源 DLL 是共享一組相同的多個程序之間的資源的好辦法。 它也是一個好的方法,以提供資源被針對多種語言進行本地化的應用程序。

      要創(chuàng)建純資源 DLL,請創(chuàng)建一個新的 Win32 DLL (非 MFC) 項目,并將資源添加到項目中。

      • 選擇中的 Win32 項目新項目對話框中,在 Win32 項目向?qū)е兄付?DLL 的項目類型。
      • 為 DLL 創(chuàng)建新資源腳本包含資源 (如字符串或菜單) 并保存.rc 文件。
      • 項目 菜單上,單擊 添加現(xiàn)有項,然后將新的.rc 文件插入到該項目。
      • 指定 /NOENTRY 鏈接器選項。 / NOENTRY 防止鏈接器將 _main 的參考鏈接到 DLL ; 若要創(chuàng)建純資源 DLL,必須使用此選項。
      • 生成 DLL。

      使用純資源 DLL 的應用程序應調(diào)用 LoadLibrary 到顯式鏈接到 DLL。 若要訪問的資源,調(diào)用泛型函數(shù) FindResource 和 LoadResource,其中從事任何種類的資源,或調(diào)用下面的特定資源的函數(shù)之一:

      • FormatMessage
      • LoadAccelerators
      • LoadBitmap
      • LoadCursor
      • LoadIcon
      • LoadMenu
      • LoadString

      應用程序應調(diào)用完成時使用的資源。

      可以調(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ù)獲取指定資源的句柄。

        注意項

        1.    DLL搜索路徑

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

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

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

      void  SetDllDirectory( LPCTSTR lpPathName);

      調(diào)用這個函數(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ù)時,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.

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

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

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

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

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

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

        4.    ClientDLL在設置上必須一致

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

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

        5.    動態(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.

      即只要是動態(tài)鏈接到MFC共享DLL的,必須使用資源切換。

      源碼下載

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

        0條評論

        發(fā)表

        請遵守用戶 評論公約

        類似文章 更多