什麼是動態鏈接庫(DLL)以及常見問題

概要
  本文說明什麼是動態鏈接庫 (DLL) 以及在使用 DLL 時可能發生的各種問題。
  然後,本文說明在開發您自己的 DLL 時應該考慮的一些高級問題。在說明什麼是 DLL 的過程中,本文將說明動態鏈接方法、DLL 依賴性、DLL 入口點、導出 DLL 函數以及 DLL 故障排除工具。
  本文最後將從較高的層次對 DLL 與 Microsoft .NET Framework 程序集作一比較。
  簡介
  對於“適用於”一節中列出的 Microsoft Windows 操作系統,操作系統的大量功能是由動態鏈接庫 (DLL) 提供的。另外,當您在這些 Windows 操作系統之一上運行某一程序時,該程序的很多功能可能是由 DLL 提供的。例如,某些程序可能包含很多不同的模塊,而該程序的每個模塊都包含在 DLL 中並從中分發。
  使用 DLL 有助於促進代碼的模塊化、代碼重用、內存的有效使用和減少所佔用的磁盤空間。因此,操作系統和程序能夠更快地加載和運行,並且在計算機中佔用較少的磁盤空間。
  當程序使用 DLL 時,一個稱爲依賴性的問題可能導致該程序無法運行。當程序使用 DLL 時,就會創建一個依賴項。如果其他程序改寫和損壞了該依賴項,原來的那個程序就可能無法成功運行。
  在引入 Microsoft .NET Framework 之後,大多數依賴性問題都已經通過使用程序集消除了。
  什麼是 DLL?
  DLL 是一個包含可由多個程序同時使用的代碼和數據的庫。例如,在 Windows 操作系統中,Comdlg32 DLL 執行與對話框有關的常見函數。因此,每個程序都可以使用該 DLL 中包含的功能來實現“打開”對話框。這有助於促進代碼重用和內存的有效使用。
  通過使用 DLL,程序可以實現模塊化,由相對獨立的組件組成。例如,一個計帳程序可以按模塊來銷售。可以在運行時將各個模塊加載到主程序中(如果安裝了相應模塊)。因爲模塊是彼此獨立的,所以程序的加載速度更快,而且模塊只在相應的功能被請求時才加載。
  此外,可以更爲容易地將更新應用於各個模塊,而不會影響該程序的其他部分。例如,您可能具有一個工資計算程序,而稅率每年都會更改。當這些更改被隔離到 DLL 中以後,您無需重新生成或安裝整個程序就可以應用更新。
  下表說明了 Windows 操作系統中的一些作爲 DLL 實現的文件: •ActiveX 控件 (.ocx) 文件
  ActiveX 控件的一個示例是日曆控件,它使您可以從日曆中選擇日期。
  •控制面板 (.cpl) 文件
  .cpl 文件的一個示例是位於控制面板中的項。每個項都是一個專用 DLL。
  •設備驅動程序 (.drv) 文件
  設備驅動程序的一個示例是控制打印到打印機的打印機驅動程序。
  DLL 的優點
  下表說明了當程序使用 DLL 時提供的一些優點: •使用較少的資源
  當多個程序使用同一個函數庫時,DLL 可以減少在磁盤和物理內存中加載的代碼的重複量。這不僅可以大大影響在前臺運行的程序,而且可以大大影響其他在 Windows 操作系統上運行的程序。
  •推廣模塊式體系結構
  DLL 有助於促進模塊式程序的開發。這可以幫助您開發要求提供多個語言版本的大型程序或要求具有模塊式體系結構的程序。模塊式程序的一個示例是具有多個可以在運行時動態加載的模塊的計帳程序。
  •簡化部署和安裝
  當 DLL 中的函數需要更新或修復時,部署和安裝 DLL 不要求重新建立程序與該 DLL 的鏈接。此外,如果多個程序使用同一個 DLL,那麼多個程序都將從該更新或修復中獲益。當您使用定期更新或修復的第三方 DLL 時,此問題可能會更頻繁地出現。
  DLL 依賴項
  當某個程序或 DLL 使用其他 DLL 中的 DLL 函數時,就會創建依賴項。因此,該程序就不再是獨立的,並且如果該依賴項被損壞,該程序就可能遇到問題。例如,如果發生下列操作之一,則該程序可能無法運行: •依賴 DLL 升級到新版本。
  •修復了依賴 DLL。
  •依賴 DLL 被其早期版本覆蓋。
  •從計算機中刪除了依賴 DLL。
  這些操作通常稱爲 DLL 衝突。如果沒有強制實現向後兼容性,則該程序可能無法成功運行。
  下表說明了爲了幫助最大限度地減少依賴性問題而在 Microsoft Windows 2000 和較高版本的 Windows 操作系統中引入的更改: •Windows 文件保護
  在 Windows 文件保護中,操作系統禁止未經授權的代理更新或刪除系統 DLL。因此,當程序安裝操作嘗試刪除或更新被定義爲系統 DLL 的 DLL 時,Windows 文件保護將尋找有效的數字簽名。
  •專用 DLL
  通過專用 DLL 可以使程序避免遭受對共享 DLL 進行的更改。專用 DLL 使用版本特定信息或空 .local 文件來強制要求程序所使用的 DLL 的版本。要使用專用 DLL,請在程序根文件夾中查找 DLL。然後,對於新程序,請向該 DLL 中添加版本特定信息。對於舊程序,請使用空 .local 文件。每個方法都告訴操作系統使用位於程序根文件夾中的專用 DLL。
 DLL 故障排除工具
  可以使用多個工具來幫助您解決 DLL 問題。以下是其中的部分工具。
  Dependency Walker
  Dependency Walker 工具可以遞歸掃描以尋找程序所使用的所有依賴 DLL。當您在 Dependency Walker 中打開程序時,Dependency Walker 會執行下列檢查: •Dependency Walker 檢查是否丟失 DLL。
  •Dependency Walker 檢查是否存在無效的程序文件或 DLL。
  •Dependency Walker 檢查導入函數和導出函數是否匹配。
  •Dependency Walker 檢查是否存在循環依賴性錯誤。
  •Dependency Walker 檢查是否存在由於針對另一不同操作系統而無效的模塊。
  通過使用 Dependency Walker,您可以記錄程序使用的所有 DLL。這可能有助於避免和更正將來可能發生的 DLL 問題。當您安裝 Microsoft Visual Studio 6.0 時,Dependency Walker 將位於以下目錄中:
  drive\Program Files\Microsoft Visual Studio\Common\Tools
  DLL Universal Problem Solver
  DLL Universal Problem Solver (DUPS) 工具用於審覈、比較、記錄和顯示 DLL 信息。下表說明了組成 DUPS 工具的實用工具: •Dlister.exe
  該實用工具枚舉計算機中的所有 DLL,並且將此信息記錄到一個文本文件或數據庫文件中。
  •Dcomp.exe
  該實用工具比較在兩個文本文件中列出的 DLL,併產生包含差異的第三個文本文件。
  •Dtxt2DB.exe
  該實用工具將通過使用 Dlister.exe 實用工具和 Dcomp.exe 實用工具創建的文本文件加載到 dllHell 數據庫中。
  •DlgDtxt2DB.exe
  該實用工具提供 Dtxt2DB.exe 實用工具的圖形用戶界面 (GUI) 版本。
  有關 DUPS 工具的更多信息,請單擊下面的文章編號,以查看 Microsoft 知識庫中相應的文章:
  247957 ([url]http://support.microsoft.com/kb/247957/[/url]) 示例:使用 DUPS.exe 解決 DLL 兼容性問題
  DLL 幫助數據庫
  DLL 幫助數據庫幫助您查找由 Microsoft 軟件產品安裝的特定版本的 DLL。有關 DLL 幫助數據庫的更多信息,請訪問下面的 Microsoft 網站:
  [url]http://support.microsoft.com/dllhelp/[/url] ([url]http://support.microsoft.com/dllhelp/[/url])
DLL 開發
  本節介紹您在開發自己的 DLL 時應該考慮的問題和要求。
  DLL 的類型
  當您在應用程序中加載 DLL 時,可以使用兩種鏈接方法來調用導出的 DLL 函數。這兩種鏈接方法是加載時動態鏈接和運行時動態鏈接。
  加載時動態鏈接
  在加載時動態鏈接中,應用程序像調用本地函數一樣對導出的 DLL 函數進行顯式調用。要使用加載時動態鏈接,請在編譯和鏈接應用程序時提供頭文件 (.h) 和導入庫文件 (.lib)。當您這樣做時,鏈接器將向系統提供加載 DLL 所需的信息,並在加載時解析導出的 DLL 函數的位置。
  運行時動態鏈接
  在運行時動態鏈接中,應用程序調用 LoadLibrary 函數或 LoadLibraryEx 函數以在運行時加載 DLL。成功加載 DLL 後,可以使用 GetProcAddress 函數獲得要調用的導出的 DLL 函數的地址。在使用運行時動態鏈接時,無需使用導入庫文件。
  下面的列表說明了有關何時使用加載時動態鏈接以及何時使用運行時動態鏈接的應用程序條件: •啓動性能
  如果應用程序的初始啓動性能很重要,則應使用運行時動態鏈接。
  •易用性
  在加載時動態鏈接中,導出的 DLL 函數類似於本地函數。這使您可以方便地調用這些函數。
  •應用程序邏輯
  在運行時動態鏈接中,應用程序可以分支,以便按照需要加載不同的模塊。在開發多語言版本時,這一點很重要。
  DLL 入口點
  在創建 DLL 時,可以有選擇地指定入口點函數。當進程或線程將它們自身附加到 DLL 或者將它們自身從 DLL 分離時,將調用入口點函數。您可以使用入口點函數根據 DLL 的需要來初始化數據結構或者銷燬數據結構。此外,如果應用程序是多線程的,則可以在入口點函數中使用線程本地存儲 (TLS) 來分配各個線程專用的內存。下面的代碼是一個 DLL 入口點函數的示例。
以下是引用片段:
  BOOL APIENTRY DllMain(
  HANDLE hModule, // Handle to DLL module
  DWORD ul_reason_for_call, // Reason for calling function
  LPVOID lpReserved ) // Reserved
  {
  switch ( ul_reason_for_call )
  {
  case DLL_PROCESS_ATTACHED:
  // A process is loading the DLL.
  break;
  case DLL_THREAD_ATTACHED:
  // A process is creating a new thread.
  break;
  case DLL_THREAD_DETACH:
  // A thread exits normally.
  break;
  case DLL_PROCESS_DETACH:
  // A process unloads the DLL.
  break;
  }
  return TRUE;
  }
  當入口點函數返回 FALSE 值時,如果您使用的是加載時動態鏈接,則應用程序不啓動。如果您使用的是運行時動態鏈接,則只有個別 DLL 不會加載。
  入口點函數只應執行簡單的初始化任務,不應調用任何其他 DLL 加載函數或終止函數。例如,在入口點函數中,不應直接或間接調用 LoadLibrary 函數或 LoadLibraryEx 函數。此外,不應在進程終止時調用 FreeLibrary 函數。
  注意:在多線程應用程序中,請確保將對 DLL 全局數據的訪問進行同步(線程安全),以避免可能的數據損壞。爲此,請使用 TLS 爲各個線程提供唯一的數據。
導出 DLL 函數
  要導出 DLL 函數,您可以嚮導出的 DLL 函數中添加函數關鍵字,也可以創建模塊定義文件 (.def) 以列出導出的 DLL 函數。
  要使用函數關鍵字,您必須使用以下關鍵字來聲明要導出的各個函數:
  __declspec(dllexport)
  要在應用程序中使用導出的 DLL 函數,您必須使用以下關鍵字來聲明要導入的各個函數:
  __declspec(dllimport)
  通常情況下,您最好使用一個包含 define 語句和 ifdef 語句的頭文件,以便分隔導出語句和導入語句。
  您還可以使用模塊定義文件來聲明導出的 DLL 函數。當您使用模塊定義文件時,您不必嚮導出的 DLL 函數中添加函數關鍵字。在模塊定義文件中,您可以聲明 DLL 的 LIBRARY 語句和 EXPORTS 語句。下面的代碼是一個定義文件的示例。
  // SampleDLL.def
  //
以下是引用片段:
  LIBRARY "sampleDLL"
  EXPORTS
  HelloWorld
  示例 DLL 和應用程序
  在 Microsoft Visual C++ 6.0 中,可以通過選擇“Win32 動態鏈接庫”項目類型或“MFC 應用程序嚮導 (dll)”來創建 DLL。
  下面的代碼是一個在 Visual C++ 中通過使用“Win32 動態鏈接庫”項目類型創建的 DLL 的示例。
  // SampleDLL.cpp
  //
  #include "stdafx.h"
  #define EXPORTING_DLL
  #include "sampleDLL.h"
  BOOL APIENTRY DllMain( HANDLE hModule,
  DWORD ul_reason_for_call,
  LPVOID lpReserved
  )
  {
  return TRUE;
  }
  void HelloWorld()
  {
  MessageBox( NULL, TEXT("Hello World"), TEXT("In a DLL"), MB_OK);
  }
  // File: SampleDLL.h
  //
  #ifndef INDLL_H
  #define INDLL_H
  #ifdef EXPORTING_DLL
  extern __declspec(dllexport) void HelloWorld() ;
  #else
  extern __declspec(dllimport) void HelloWorld() ;
  #endif
  #endif
  下面的代碼是一個“Win32 應用程序”項目的示例,該示例調用 SampleDLL DLL 中的導出 DLL 函數。
  // SampleApp.cpp
  //
以下是引用片段:
  #include "stdafx.h"
  #include "sampleDLL.h"
  int APIENTRY WinMain(HINSTANCE hInstance,
  HINSTANCE hPrevInstance,
  LPSTR lpCmdLine,
  int nCmdShow)
  {
  HelloWorld();
  return 0;
  }
  注意:在加載時動態鏈接中,您必須鏈接在生成 SampleDLL 項目時創建的 SampleDLL.lib 導入庫。
  在運行時動態鏈接中,您應使用與以下代碼類似的代碼來調用 SampleDLL.dll 導出 DLL 函數。
以下是引用片段:
  ...
  typedef VOID (*DLLPROC) (LPTSTR);
  ...
  HINSTANCE hinstDLL;
  DLLPROC HelloWorld;
  BOOL fFreeDLL;
  hinstDLL = LoadLibrary("sampleDLL.dll");
  if (hinstDLL != NULL)
  {
  HelloWorld = (DLLPROC) GetProcAddress(hinstDLL, "HelloWorld");
  if (HelloWorld != NULL)
  (HelloWorld);
  fFreeDLL = FreeLibrary(hinstDLL);
  }
  ...
當您編譯和鏈接 SampleDLL 應用程序時,Windows 操作系統將按照以下順序在下列位置中搜索 SampleDLL DLL: 1.應用程序文件夾
  2.當前文件夾
  3.Windows 系統文件夾
  注意:GetSystemDirectory 函數返回 Windows 系統文件夾的路徑。
  4.Windows 文件夾
  注意:GetWindowsDirectory 函數返回 Windows 文件夾的路徑。
  .NET Framework 程序集
  在引入 Microsoft .NET 和 .NET Framework 以後,大多數與 DLL 相關聯的問題已經通過使用程序集消除了。程序集是在 .NET 公共語言運行庫 (CLR) 控制之下運行的邏輯功能單元。程序集實際上是作爲 .dll 文件或 .exe 文件存在的。但是,在內部,程序集與 Microsoft Win32 DLL 大不相同。
  程序集文件包含程序集清單、類型元數據、Microsoft 中間語言 (MSIL) 代碼和其他資源。程序集清單包含程序集元數據,以提供使程序集成爲自描述程序集所需的全部信息。程序集清單中包含以下信息: •程序集名稱
  •版本信息
  •區域性信息
  •強名稱信息
  •程序集文件列表
  •類型引用信息
  •引用和依賴程序集信息
  程序集中包含的 MSIL 代碼是無法直接執行的,需要通過 CLR 來執行。默認情況下,當您創建一個程序集時,該程序集是應用程序專有的。要創建共享程序集,需要爲該程序集分配強名稱,然後在全局程序集緩存中發佈該程序集。
  下表說明了程序集的一些功能,並將其與 Win32 DLL 的功能進行了比較: •自描述
  當您創建程序集時,CLR 運行該程序集所需的全部信息都包含在程序集清單中。程序集清單包含一個依賴程序集列表。因此,CLR 可以維護一組在應用程序中使用的一致的程序集。在 Win32 DLL 中,當您使用共享 DLL 時,無法維護應用程序中使用的一組 DLL 之間的一致性。
  •版本控制
  在程序集清單中,版本信息由 CLR 記錄和實施。另外,可以通過版本策略來實施版本特定用法。在 Win32 DLL 中,無法由操作系統實施版本控制。相反,您必須確保 DLL 向後兼容。
  •並行部署
  程序集支持並行部署。一個應用程序可以使用一個版本的程序集,而另一個應用程序可以使用另一不同版本的程序集。從 Windows 2000 開始,通過將 DLL 放置到應用程序文件夾中支持並行部署。另外,Windows 文件保護能夠防止系統 DLL 被未經授權的代理改寫或替換。
  •獨立和隔離
  通過使用程序集開發的應用程序可以是獨立的,並且與計算機中正在運行的其他應用程序隔離。這一特性有助於創建零干擾安裝。
  •執行
  程序集在程序集清單所提供的並且由 CLR 控制的安全權限下運行。
  •語言無關性
  可以通過使用任何一種受支持的 .NET 語言來開發程序集。例如,可以在 Microsoft Visual C# 中開發程序集,然後在 Microsoft Visual Basic .NET 項目中使用該程序集。
  參考
  有關 DLL 和 .NET Framework 程序集的更多信息,請訪問下面的 Microsoft 網站:
  DLL 幫助數據庫
  [url]http://support.microsoft.com/dllhelp[/url] ([url]http://support.microsoft.com/dllhelp[/url])
  DLL 衝突 (DLL conflicts)
  [url]http://msdn.microsoft.com/library/en-us/dnsetup/html/dlldanger1.asp[/url] ([url]http://msdn.microsoft.com/library/en-us/dnsetup/html/dlldanger1.asp[/url])
  在應用程序中實現並行組件共享 (Implementing side-by-side component sharing in applications)
  [url]http://msdn.microsoft.com/library/en-us/dnsetup/html/sidebyside.asp[/url] ([url]http://msdn.microsoft.com/library/en-us/dnsetup/html/sidebyside.asp[/url])
  如何生成和維護用於 Windows XP 的獨立應用程序和並行程序集 (How to build and service isolated applications and side-by-side assemblies for Windows XP)
  [url]http://msdn.microsoft.com/library/en-us/dnwxp/html/sidexsidewinxp.asp[/url] ([url]http://msdn.microsoft.com/library/en-us/dnwxp/html/sidexsidewinxp.asp[/url])
  使用 .NET Framework 簡化部署和解決 DLL 衝突 (Simplifying deployment and solving DLL conflicts with the .NET Framework)
  [url]http://msdn.microsoft.com/library/en-us/dndotnet/html/dplywithnet.asp[/url] ([url]http://msdn.microsoft.com/library/en-us/dndotnet/html/dplywithnet.asp[/url])
  .NET Framework 開發人員指南:程序集
  [url]http://msdn.microsoft.com/library/en-us/cpguide/html/cpconassemblies.asp[/url] ([url]http://msdn.microsoft.com/library/en-us/cpguide/html/cpconassemblies.asp[/url])
  創建 Win32 DL (Creating a Win32 DLL)
  [url]http://msdn.microsoft.com/library/en-us/vccore98/html/_core_overview.3a_.creating_a_win32_dll.asp[/url] ([url]http://msdn.microsoft.com/library/en-us/vccore98/html/_core_overview.3a_.creating_a_win32_dll.asp[/url])
  運行時動態鏈接
  [url]http://msdn2.microsoft.com/en-us/library/ms685090.aspx[/url] ([url]http://msdn2.microsoft.com/en-us/library/ms685090.aspx[/url])
  線程本地存儲 (Thread local storage)
  [url]http://msdn2.microsoft.com/en-us/library/ms686749.aspx[/url]
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章