- DLL 有用資源
- DLL - 快速指南
- DLL - 有用資源
- 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,
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語句的標頭檔案來分離匯出語句和匯入語句。
您還可以使用模組定義檔案來宣告匯出的 DLL 函式。使用模組定義檔案時,不必嚮導出的 DLL 函式新增函式關鍵字。在模組定義檔案中,您宣告 DLL 的LIBRARY語句和EXPORTS語句。以下程式碼是定義檔案的示例。
// SampleDLL.def // LIBRARY "sampleDLL" EXPORTS HelloWorld
編寫示例 DLL
在 Microsoft Visual C++ 6.0 中,您可以透過選擇Win32 動態連結庫專案型別或MFC AppWizard (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
呼叫示例 DLL
以下程式碼是呼叫 SampleDLL DLL 中匯出 DLL 函式的 Win32 應用程式專案示例。
// 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:
應用程式資料夾
當前資料夾
Windows 系統資料夾(GetSystemDirectory函式返回 Windows 系統資料夾的路徑)。
Windows 資料夾(GetWindowsDirectory函式返回 Windows 資料夾的路徑)。