国产探花免费观看_亚洲丰满少妇自慰呻吟_97日韩有码在线_资源在线日韩欧美_一区二区精品毛片,辰东完美世界有声小说,欢乐颂第一季,yy玄幻小说排行榜完本

首頁 > 學院 > 開發設計 > 正文

Visual C++中動態鏈接庫技術淺談

2019-11-17 05:10:06
字體:
來源:轉載
供稿:網友
摘要:本文比較了Visual C++所支持的三種動態鏈接庫,列出了各自不同的特點和應用場合,具體地描述了三種動態鏈接庫的建立和調用的方法。

   要害字:動態鏈接庫;導出函數;調用

  引言

   較大的應用程序都由很多模塊組成,這些模塊分別完成相對獨立的功能,它們彼此協作來完成整個軟件系統的工作。在構造軟件系統時,假如將所有模塊的源代碼都靜態編譯到整個應用程序的EXE文件中,會產生一些問題:一個缺點是增加了應用程序的大小,它會占用更多的磁盤空間,程序運行時也會消耗較大的內存空間,造成系統資源的浪費;另一個缺點是,在編寫大的EXE程序時,在每次修改重建時都必須調整編譯所有源代碼,增加了編譯過程的復雜性,也不利于階段性的單元測試;而且,一些模塊的功能可能較為通用,在構造其它軟件系統時仍會被使用。

   Windows系統平臺上提供了一種完全不同的較有效的編程和運行環境,你可以將獨立的程序模塊創建為較小的動態鏈接庫(Dynamic Linkable Library,DLL)文件,并可對它們單獨編譯和測試。在運行時,只有當EXE程序確實要調用這些DLL模塊的情況下,系統才會將它們裝載到內存空間中。這種方式不僅減少了EXE文件的大小和對內存空間的需求,而且使這些DLL模塊可以同時被多個應用程序使用。

  動態鏈接庫概述

   動態鏈接庫技術是Windows最重要的實現技術之一,Windows的許多新功能、新特性都是通過DLL來實現的。其實,Windows本身就是由許多DLL組成的,它最基本的三大組成模塊Kernel、GDI和User都是DLL。

   一般來說,DLL是一種磁盤文件,以.dll、.DRV、.FON、.SYS和許多以.EXE為擴展名的系統文件都可以是DLL。它由全局數據、服務函數和資源組成,在運行時被系統加載到進程的虛擬空間中,成為調用進程的一部分。假如與其它DLL之間沒有沖突,該文件通常映射到進程虛擬空間的同一地址上。DLL模塊中包含各種導出函數,用于向外界提供服務,Windows在加載DLL模塊時將進程函數調用與DLL文件的導出函數相匹配。DLL可以有自己的數據段,但沒有自己的堆棧,DLL模塊需要的堆棧內存都是從運行進程的堆棧中分配出來的,使用與調用它的應用程序相同的堆棧模式;一個DLL在內存中只有一個實例;DLL實現了代碼封裝性;DLL的編制與具體的編程語言及編譯器無關。

  動態鏈接庫的分類

   微軟的Visual C++支持三種DLL,它們分別是Non-MFC Dll(非MFC動態庫)、Regular Dll(常規DLL)、Extension Dll(擴展DLL)。

   1、Non-MFC DLL(非MFC動態庫)

   這種動態鏈接庫指的是不用MFC的類庫結構,直接用C語言寫的DLL,其導出的函數是標準的C接口,能被非MFC或MFC編寫的應用程序所調用。假如建立的DLL不需要使用MFC,那么應該建立Non-MFC DLL,因為使用MFC會增大用戶庫的大小,從而浪費用戶的磁盤和內存空間。

   2、Regular DLL(常規DLL)

   這種動態鏈接庫和下述的Extension Dll一樣,是用MFC類庫編寫的,它的一個明顯的特點是在源文件里有一個繼續CWinApp的類(注重:此類DLL雖然從CWinApp派生,但沒有消息循環),被導出的函數是C函數、C++類或者C++成員函數(注重不要把術語C++類與MFC的微軟基礎C++類相混淆),調用常規DLL的應用程序不必是MFC應用程序,只要是能調用類C函數的應用程序就可以,它們可以是在Visual C++、Delphi、Visual Basic、Borland C等編譯環境下利用DLL開發應用程序。常規DLL又可細分成靜態鏈接到MFC和動態鏈接到MFC兩種:

   (1)靜態連接到MFC的動態連接庫只被VC的專業般和企業版所支持。該類DLL里的輸出函數可以被任意Win32程序使用,包括使用MFC的應用程序。輸出函數有如下形式:

extern "C" EXPORT YourExportedFunction( );
   假如沒有extern "C"修飾,輸出函數僅僅能從C++代碼中調用。

   (2)動態鏈接到MFC的常規DLL里的輸出函數可以被任意Win32程序使用,包括使用MFC的應用程序。所有從DLL輸出的函數應該以如下語句開始:

AFX_MANAGE_STATE(AfxGetStaticModuleState( ))
   此語句用來正確地切換MFC模塊狀態。

   3、Extension Dll(擴展DLL)

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

   1) 它沒有一個從CWinApp派生的對象;

   2) 它必須有一個DLLMain函數;

   3) DLLMain調用AfxInitExtensionModule函數,必須檢查該函數的返回值,假如返回0,DLLMmain也返回0;

   4) 假如它希望輸出CRuntimeClass類型的對象或者資源(Resources),則需要提供一個初始化函數來創建一個CDynLinkLibrary對象。并且,有必要把初始化函數輸出;

   5) 使用擴展DLL的MFC應用程序必須有一個從CWinApp派生的類,而且,一般在InitInstance里調用擴展DLL的初始化函數。   動態連接庫的建立

   1、Non-MFC DLL的建立


   每一個DLL必須有一個入口點,就象用C編寫的應用程序時,必須有一個WINMAIN函數一樣。在Non-MFC DLL中DllMain是一個缺省的入口函數,你不需要編寫自己的DLL入口函數,用這個缺省的入口函數就能使動態鏈接庫被調用時得到正確的初始化。假如應用程序的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是動態庫被調用時所傳遞來的一個指向自己的句柄(實際上,它是指向_DGROUP段的一個選擇符);
ul_reason_for_call是一個說明動態庫被調原因的標志,當進程或線程裝入或卸載動態鏈接庫的時候,操作系統調用入口函數,并說明動態鏈接庫被調用的原因,它所有的可能值為:

   (1)DLL_PROCESS_ATTACH: 進程被調用或調用Load Library,DLL被鏈接到當前進程的地址空間并被初始化;

   (2)DLL_THREAD_ATTACH: 當前進程創建一個新線程,DLL在新線程正文內被調用;

   (3)DLL_PROCESS_DETACH: 調用DLL的進程被終止,DLL被卸載;

   (4)DLL_THREAD_DETACH: 調用DLL的線程被終止,DLL被卸載;

   lpReserved為保留參數。

   假如在DLL中加入想要輸出的函數、變量、C++類或其它函數,可以調用VC的要害字_declspec(dllexport)。

   2、MFC AppWizard[dll]方式下Regular DLL和Extension DLL的建立

   在MFC AppWizard[dll]下生成的DLL文件有三種方式:靜態鏈接到MFC的常規DLL、動態鏈接到MFC的常規DLL以及MFC擴展DLL,在創建DLL是,要根據實際情況選擇創建DLL的方式。

   靜態鏈接到MFC的常規DLL和靜態連接到MFC常規DLL的區別是:前者使用的是MFC的靜態鏈接庫,生成的DLL文件長度大,一般不使用這種方式;后者使用MFC的動態鏈接庫,生成的DLL文件長度小;動態鏈接到MFC的常規DLL所有輸出的函數應該以如下語句開始:

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

   MFC擴展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是一個被系統所保留的參數。對于隱式鏈接是一個非零值,對于顯式鏈接值是零。

  動態連接庫的調用

   動態鏈接庫的調用可以分為兩種:一種是隱式調用,一種是顯示調用。

   1、隱式的調用

   這種調用方式需要把產生動態連接庫時產生的.LIB文件加入到應用程序的工程中,在使用DLL中的函數時,只須說明一下后就可以直接通過函數名調用DLL的輸出函數,調用方法和程序內部其他的函數是一樣的。隱式調用不需要調用Load Library()和Free Library()。程序員在建立一個DLL文件時,鏈接程序會自動生成一個與之對應的LIB導入文件。該文件包含了每一個DLL導出函數的符號名和可選的標識號,但是并不含有實際的代碼。LIB文件作為DLL的替代文件被編譯到應用程序項目中。

   當程序員通過隱式調用方式編譯生成應用程序時,應用程序中的調用函數與LIB文件中導出符號相匹配,這些符號或標識號被寫入到生成的EXE文件中。LIB文件中也包含了對應的DLL文件名(但不是完全的路徑名),鏈接程序也將其存儲在EXE文件內部。
當應用程序運行過程中需要加載DLL文件時,Windows根據這些信息發現并加載DLL,然后通過符號名或標識號實現對DLL函數的動態鏈接。所有被應用程序調用的DLL文件都會在應用程序EXE文件加載時被加載在到內存中。

   2、顯式調用

   這種調用方式是指在應用程序中用Load Library或MFC提供的AfxLoadLibrary顯式的將自己所做的動態連接庫調進來,并指定DLL的路徑作為參數。LoadLibary返回HINSTANCE參數,應用程序在調用GetProcAddress函數時使用這一參數。當完成對動態鏈接庫的導入以后,再使用GetProcAddress()獲取想要引入的函數,該函數將符號名或標識號轉換為DLL內部的地址,之后就可以象使用本應用程序自定義的函數一樣來調用此引入函數了。在應用程序退出之前,應該用Free Library或MFC提供的AfxFreeLibrary釋放動態連接庫。

   使用顯式調用方式可以讓程序員來決定DLL文件何時加載或不加載,而操作系統在載入應用程序時不必要將所有該應用程序所引用的DLL都一起加載到內存中,只要在使用某個DLL時再將其載入,這樣就可以減少應用程序在初始加載時所使用的時間和對內存的消耗。在對DLL加載的過程中,Windows將遵循下面的搜索順序來定位DLL:

   ①包含EXE文件的目錄;

   ②進程的當前工作目錄 ;

   ③Windows系統目錄 ;

   ④Windows目錄 ;

   ⑤列在Path環境變量中的一系列目錄。

  總結

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

發表評論 共有條評論
用戶名: 密碼:
驗證碼: 匿名發表
主站蜘蛛池模板: 井陉县| 体育| 宁夏| 平泉县| 汝城县| 炎陵县| 大港区| 同江市| 上林县| 绵阳市| 宁远县| 抚顺县| 公主岭市| 苍溪县| 杭州市| 闽清县| 澄城县| 三穗县| 湘潭县| 苏尼特右旗| 连江县| 休宁县| 温泉县| 江川县| 钟山县| 祁连县| 容城县| 中宁县| 论坛| 滦南县| 巴彦淖尔市| 奉贤区| 阜新市| 阜康市| 襄樊市| 镇坪县| 黄平县| 黄平县| 丁青县| 海淀区| 吕梁市|