DLL 快速指南



DLL - 簡介

動態連結是一種在執行時將應用程式連結到庫的機制。庫保留在其自己的檔案中,不會複製到應用程式的可執行檔案中。DLL 在應用程式執行時連結到應用程式,而不是在應用程式建立時連結。DLL 可能包含到其他 DLL 的連結。

很多時候,DLL 放在副檔名不同的檔案中,例如 .EXE、.DRV 或 .DLL。

DLL 的優點

下面列出了使用 DLL 檔案的一些優點。

使用更少的資源

DLL 檔案不會與主程式一起載入到 RAM 中;除非需要,否則它們不會佔用空間。當需要 DLL 檔案時,它會被載入並執行。例如,只要 Microsoft Word 使用者正在編輯文件,印表機 DLL 檔案就不需要在 RAM 中。如果使用者決定列印文件,則 Word 應用程式會導致印表機 DLL 檔案被載入並執行。

促進模組化架構

DLL 有助於開發模組化程式。它可以幫助您開發需要多種語言版本的大型程式或需要模組化架構的程式。模組化程式的一個示例是會計程式,它具有許多可以在執行時動態載入的模組。

有助於輕鬆部署和安裝

當 DLL 中的函式需要更新或修復時,DLL 的部署和安裝不需要將程式重新連結到 DLL。此外,如果多個程式使用相同的 DLL,則所有程式都將受益於更新或修復。當您使用定期更新或修復的第三方 DLL 時,此問題可能會更頻繁地發生。

如果 DLL 連結在模組定義檔案中的 IMPORTS 部分中指定為編譯的一部分,則應用程式和 DLL 可以自動連結到其他 DLL。否則,您可以使用 Windows LoadLibrary 函式顯式載入它們。

重要的 DLL 檔案

  • COMDLG32.DLL - 控制對話方塊。

  • GDI32.DLL - 包含大量用於繪製圖形、顯示文字和管理字型的函式。

  • KERNEL32.DLL - 包含數百個用於管理記憶體和各種程序的函式。

  • USER32.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 資料夾的路徑)。

DLL - 註冊

為了使用 DLL,它必須透過在登錄檔中輸入相應的引用來註冊。有時會發生登錄檔引用損壞並且無法再使用 DLL 的函式的情況。可以透過開啟“開始-執行”並輸入以下命令來重新註冊 DLL

regsvr32 somefile.dll

此命令假定 somefile.dll 位於 PATH 中的目錄或資料夾中。否則,必須使用 DLL 的完整路徑。也可以使用開關“/u”取消註冊 DLL 檔案,如下所示。

regsvr32 /u somefile.dll

這可用於開啟和關閉服務。

DLL - 工具

有幾個工具可用於幫助您解決 DLL 問題。下面討論其中一些。

依賴項檢視器

依賴項檢視器工具(depends.exe)可以遞迴掃描程式使用的所有依賴 DLL。當您在依賴項檢視器中開啟程式時,依賴項檢視器會執行以下檢查

  • 檢查缺少的 DLL。
  • 檢查無效的程式檔案或 DLL。
  • 檢查匯入函式和匯出函式是否匹配。
  • 檢查迴圈依賴錯誤。
  • 檢查由於模組適用於不同的作業系統而無效的模組。

透過使用依賴項檢視器,您可以記錄程式使用的所有 DLL。這可能有助於防止和糾正將來可能發生的 DLL 問題。安裝 Microsoft Visual Studio 6.0 後,依賴項檢視器位於以下目錄中

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 - 此實用程式提供 Dtxt2DB.exe 實用程式的圖形使用者介面 (GUI) 版本。

DLL - 提示

編寫 DLL 時請記住以下提示

  • 使用正確的呼叫約定(C 或 stdcall)。

  • 注意傳遞給函式的引數的正確順序。

  • **切勿**使用直接傳遞給函式的引數調整陣列大小或連線字串。請記住,您傳遞的引數是 LabVIEW 資料。更改陣列或字串的大小可能會導致覆蓋儲存在 LabVIEW 記憶體中的其他資料,從而導致崩潰。**如果**您傳遞 LabVIEW 陣列控制代碼或 LabVIEW 字串控制代碼,並且使用 Visual C++ 編譯器或 Symantec 編譯器編譯 DLL,**則可以**調整陣列大小或連線字串。

  • 將字串傳遞給函式時,選擇要傳遞的正確字串型別。C 或 Pascal 或 LabVIEW 字串控制代碼。

  • Pascal 字串的長度限制為 255 個字元。

  • C 字串以 NULL 結尾。如果您的 DLL 函式以二進位制字串格式(例如,透過 GPIB 或序列埠)返回數字資料,它可能會將 NULL 值作為資料字串的一部分返回。在這種情況下,傳遞短整數(8 位)陣列最可靠。

  • 如果您正在使用陣列或字串資料,**始終**傳遞一個足夠大的緩衝區或陣列來容納函式放入緩衝區的任何結果,除非您將它們作為 LabVIEW 控制代碼傳遞,在這種情況下,您可以使用 Visual C++ 或 Symantec 編譯器下的 CIN 函式調整它們的大小。

  • 如果您使用的是 _stdcall,請在模組定義檔案的 EXPORTS 部分列出 DLL 函式。

  • 在模組定義檔案的 EXPORTS 部分列出其他應用程式呼叫的 DLL 函式,或在函式宣告中包含 _declspec (dllexport) 關鍵字。

  • 如果您使用 C++ 編譯器,請在您的標頭檔案中使用 extern .C.{} 語句匯出函式,以防止名稱修飾。

  • 如果您正在編寫自己的 DLL,則不應在另一個應用程式將 DLL 載入到記憶體中時重新編譯 DLL。在重新編譯 DLL 之前,請確保使用該特定 DLL 的所有應用程式都已從記憶體中解除安裝。它確保 DLL 本身不會載入到記憶體中。如果您忘記這一點並且您的編譯器沒有警告您,您可能會無法正確重建。

  • 使用另一個程式測試您的 DLL,以確保函式(和 DLL)的行為正確。使用編譯器的偵錯程式或一個簡單的 C 程式(您可以在其中呼叫 DLL 中的函式)對其進行測試將幫助您確定可能的困難是 DLL 本身固有的還是與 LabVIEW 相關的。

DLL - 示例

我們已經瞭解瞭如何編寫 DLL 以及如何建立“Hello World”程式。該示例必須讓您瞭解建立 DLL 的基本概念。

在這裡,我們將介紹使用 Delphi、Borland C++ 和 VC++ 建立 DLL 的方法。

讓我們逐一檢視這些示例。

廣告

© . All rights reserved.