什麼是 DLL

本文說明什麼是動態連結程式庫 (DLL) ,以及使用 DLL 時可能發生的各種問題。 它也會描述開發您自己的 DLL 時應該考慮的一些進階問題。

套用於:Windows 10 - 所有版本
原始 KB 編號: 815065

摘要

在描述 DLL 是什麼時,本文說明動態連結方法、DLL 相依性、DLL 進入點、匯出 DLL 函式和 DLL 疑難排解工具。

本文完成 DLL 與 Microsoft .NET Framework 組件的高階比較。

針對 Windows 作業系統,大部分的作業系統功能都是由 DLL 提供。 此外,當您在這些 Windows 作業系統上執行程式時,大部分的程式功能可能由 DLL 提供。 例如,某些程式可能包含許多不同的模組,而程式的每個模組都會包含在 DLL 中並散發。

使用 DLL 有助於提升程式碼的模組化、程式碼重複使用、高效的記憶體使用量,以及減少磁碟空間。 因此,作業系統和程式的載入速度更快、執行速度更快,而且在電腦上佔用較少的磁碟空間。

當程式使用 DLL 時,稱為相依性的問題可能會導致程式無法執行。 當程式使用 DLL 時,會建立相依性。 如果另一個程式覆寫並中斷此相依性,則原始程式可能無法順利執行。

引入 .NET Framework,大部分的相依性問題都已使用組件來消除。

其他相關資訊

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 衝突。 如果未強制執行回溯相容性,程式可能無法順利執行。

下列清單說明在 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 Walk 會檢查是否有遺漏的 DLL。
  • Dependency Walker 會檢查是否有不正確程式檔案或 DLL。
  • Dependency Walker 會檢查匯入函式和匯出函式是否相符。
  • Dependency Walker 會檢查是否有迴圈相依性錯誤。
  • Dependency Walker 會檢查不正確模組,因為模組適用于不同的作業系統。

藉由使用 Dependency Walker,您可以記錄程式使用的所有 DLL。 它有助於防止和修正未來可能發生的 DLL 問題。 當您安裝 Visual Studio 6.0 時,Dependency Walker 位於下列目錄中:

drive\Program Files\Microsoft Visual Studio\Common\Tools

DLL 通用問題解決器

DLL 通用問題解決器 (DUPS) 工具可用來稽核、比較、記載及顯示 DLL 資訊。 下列清單描述組成 DUPS 工具的公用程式:

  • Dlister.exe

    此公用程式會列舉電腦上的所有 DLL,並將資訊記錄到文字檔案或資料庫檔案。

  • Dcomp.exe

    此公用程式會比較兩個文字檔中所列的 DLL,並產生包含差異的第三個文字檔案。

  • Dtxt2DB.exe

    此公用程式會將使用 Dlister.exe 公用程式和 Dcomp.exe 公用程式所建立的文字檔案載入 dllHell 資料庫。

  • DlgDtxt2DB.exe

    此公用程式提供圖形使用者介面 (GUI) 版本的 Dtxt2DB.exe 公用程式。

DLL 說明資料庫

DLL 說明資料庫可協助您找出 Microsoft 軟體產品所安裝的特定 DLL 版本。

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 函式,或在列出匯出 DLL 函式的檔案 (.def) 建立模組定義。

若要使用函式關鍵字,您必須使用下列關鍵字聲明要匯出的每個函式:
__declspec(dllexport)

若要在應用程式中使用導出的 DLL 函式,您必須使用下列關鍵詞宣告您想要匯入的每個函式: __declspec(dllimport)

一般而言,您會使用一個具有 define 語句和 ifdef 語句的標頭檔案來分隔 export 語句和 import 語句。

您也可以使用模組定義檔案來聲明匯出的 DLL 函式。 當您使用模組定義檔案時,不需要將函式關鍵字新增至匯出的 DLL 函式。 在模組定義檔案中,您會聲明 DLL 的 LIBRARY 語句和 EXPORTS 語句。 以下程式碼是定義檔案的範例。

// SampleDLL.def
//
LIBRARY "sampleDLL"
EXPORTS HelloWorld

範例 DLL 和應用程式

在 Visual C++ 6.0 中,您可以選取 Win32 Dynamic-Link Library 專案類型或 MFC AppWizard (dll) 專案類型來建立 DLL。

下列程式碼是使用 Win32 Dynamic-Link Library 專案類型在 Visual C++ 中建立的 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 組件

引入 .NET 和 .NET Framework,大部分與 DLL 相關聯的問題都已使用組件來消除。 組件是功能的邏輯單元,可在 .NET Common Language Runtime (CLR) 的控制下執行。 組件實際上會以 .dll 檔案或 .exe 檔案的方式存在。 不過,組件在內部與 Microsoft Win32 DLL 不同。

組件檔案包含組件資訊清單、類型中繼資料、Microsoft 中繼語言 (MSIL) 程式碼和其他資源。 組件資訊清單包含組件中繼資料,可提供組件自我描述所需的所有資訊。 下列資訊包含在組件資訊清單中:

  • 組件名稱
  • 版本資訊
  • 文化資訊
  • 強式名稱資訊
  • 檔案的組件清單
  • 類型參考資訊
  • 參考和相依組件資訊

無法直接執行包含在組件中的 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# 中開發組件,然後在 Visual Basic .NET 專案中使用組件。

資料收集

若您需要 Microsoft 支援,建議您按照使用 TSS 收集部署相關問題的資訊所述步驟來收集資訊。

參考