共用方式為


開發 DLL

適用於:Excel 2013 |Office 2013 |Visual Studio

連結庫是編譯程式代碼的主體,可為可執行檔應用程式提供一些功能和數據。 連結庫可以是靜態連結或動態連結,而且通常會分別擴展名為 .lib 和 .dll。 靜態庫 (例如 C 執行時間連結庫) 會在編譯時連結至應用程式,因此會成為所產生可執行檔的一部分。 應用程式會在需要時載入 DLL,通常是在應用程式啟動時。 一個 DLL 可以載入並動態連結至另一個 DLL。

使用 DLL 的優點

DLL 的主要優點如下:

  • 所有應用程式都可以在磁碟上共用單一復本。
  • 應用程式的可執行檔會保持較小。
  • 它們可讓大型開發項目細分。 應用程式和 DLL 開發人員只需要同意其各自元件之間的介面。 這個介面是由 DLL 匯出。
  • DLL 開發人員可以更新 DLL,或許是為了讓 DLL 更有效率或修正錯誤,而不需要更新所有使用 DLL 的應用程式,前提是 DLL 的導出介面不會變更。

您可以使用 DLL 在 Microsoft Excel 中新增工作表函式和命令。

建立 DLL 的資源

若要建立 DLL,您需要下列專案:

  • 原始碼編輯器。
  • 編譯程式,可將原始程式碼轉換成與硬體相容的物件程序代碼。
  • 鏈接器,可從靜態庫新增程序代碼,其中使用 和 來建立可執行的 DLL 檔案。

新式集成開發環境 (IDE) ,例如 Microsoft Visual Studio 提供所有這些專案。 它們也提供更多功能:智慧型手機編輯器、偵錯程式代碼的工具、管理多個專案的工具、新的專案精靈,以及許多其他重要工具。

您可以使用數種語言建立 DLL,例如 C/C++、Pascal 和 Visual Basic。 假設 Excel 提供的 API 原始碼是 C 和 C++,本檔中只會考慮這兩種語言。

匯出函式和命令

編譯 DLL 專案時,編譯程式和鏈接器必須知道要匯出哪些函式,才能讓它們可供應用程式使用。 本節說明完成此作業的方式。

編譯程式編譯原始程式碼時,一般而言,它們會從其在原始程式碼中的外觀變更函式的名稱。 他們通常會藉由在稱為名稱裝飾的進程中,將 新增至名稱的開頭和/或結尾來執行此動作。 您必須確定函式是以可辨識的名稱匯出給載入 DLL 的應用程式。 這可能表示告知連結器將裝飾名稱與較簡單的導出名稱產生關聯。 匯出名稱可以是原始出現在原始碼中的名稱或其他名稱。

名稱的裝飾方式取決於語言,以及如何指示編譯程式讓函式可供使用,也就是呼叫慣例。 DLL 所使用之 Windows 的標準進程間呼叫慣例稱為 WinAPI 慣例。 它會在 Windows 頭檔中定義為 WINAPI,然後使用 Win32 宣告子 __stdcall定義。

與 Excel 搭配使用的 DLL 匯出函式 (不論是工作表函數、宏表對等函數或使用者定義的命令) 都應一律使用 WINAPI / __stdcall 呼叫慣例。 必須明確地在函式的定義中包含 WINAPI 規範,因為Win32編譯程式中的預設值是使用 __cdecl 呼叫慣例,如果未指定,也就是定義為 WINAPIV

您可以透過下列幾種方式之一,告訴連結器要匯出函式,以及外部已知的名稱:

  • 將函式放在 DEF 檔案的 EXPORTS 關鍵詞 後面,然後設定 DLL 專案設定以在連結時參考此檔案。
  • 在函式定義中使用 __declspec (dllexport) 宣告子。
  • 使用 #pragma 預處理器指示詞將訊息傳送至連結器。

雖然您的專案可以使用這三種方法,而且編譯程式和連結器支持它們,但您不應該嘗試使用上述其中一種方式導出一個函式。 例如,假設 DLL 包含兩個原始程式碼模組,一個 C 和一個C++,分別包含兩個要導 出的函式,my_C_exportmy_Cpp_export 。 為了簡單起見,假設每個函式都採用單一雙精確度數值自變數,並傳回相同的數據類型。 下列各節概述使用這些方法導出每個函式的替代方法。

使用 DEF 檔案

double WINAPI my_C_export(double x)
{
/* Modify x and return it. */
    return x * 2.0;
}
double WINAPI my_Cpp_export(double x)
{
// Modify x and return it.
    return x * 2.0;
}

DEF 檔案接著必須包含這些行。

EXPORTS my_C_export = _my_C_export@8 my_Cpp_export

在 EXPORTS 語句後面 行的一般語法如下所示。

entryname[=internalname] [@ordinal[NONAME]] [DATA] [PRIVATE]

請注意,C 函式已裝飾,但 DEF 檔案會明確強制連結器使用原始原始程式碼名稱公開函式, (在此範例中) 。 鏈接器會使用原始程式碼名稱隱含地導出C++函式,因此不需要在DEF檔案中包含裝飾名稱。

針對 32 位 Windows API 函數調用,C 編譯函式的裝飾慣例如下: function_name 會變成 function_name@n ,其中 n 是以所有自變數所採用的十進位表示的位元元組數目,每個自變數的位元組會四捨五入到最接近的四個倍數。

注意事項

在 Win32 中,所有指標都是四個字節寬。 傳回型別不會影響名稱裝飾。

您可以強制C++編譯程式在 extern “C” {...} 內括住函式和任何函式原型,以公開C++函式的未更正名稱 區塊,如本範例所示。 (此處省略大括弧 {} ,因為宣告只會參考緊接在它後面的函式程式代碼區塊) 。

extern "C"
double WINAPI my_undecorated_Cpp_export(double x)
{
// Modify x and return it.
    return x * 2.0;
}

當您將 C 函式原型放在可包含在 C 或 C++ 來源檔案中的頭檔時,您應該包含下列前置處理器指示詞。

#ifdef __cplusplus
extern "C" {
#endif
double WINAPI my_C_export(double x);
double WINAPI my_Cdecorated_Cpp_export(double x);
#ifdef __cplusplus
}
#endif

使用 __declspec (dllexport) 宣告子

__declspec (dllexport) 關鍵詞可用於 函式的宣告,如下所示。

__declspec(dllexport) double WINAPI my_C_export(double x)
{
/* Modify x and return it. */
    return x * 2.0;
}

__declspec (dllexport) 關鍵詞必須加入宣告的最左邊。 這種方法的優點是函式不需要列在 DEF 檔案中,而且導出狀態在定義上是正確的。

如果您想要避免使用C++名稱裝飾來提供C++函式,您必須如下所示宣告函式。

extern "C"
__declspec(dllexport) double WINAPI my_undecorated_Cpp_export(double x)
{
// Modify x and return it.
    return x * 2.0;
}

鏈接器會讓函式以my_undecorated_Cpp_export方式提供,也就是在原始程式碼中顯示且沒有裝飾的名稱。

使用 #pragma 預處理器連結器指示詞

最新版本的 Microsoft Visual Studio 支援兩個預先定義的宏,當與 #pragma 指示詞搭配使用時,可讓您指示連結器直接從函式程式代碼內匯出函式。 宏是 FUNCTIONFUNCDNAME (請注意每個端) 的雙底線,這會分別展開為未裝飾和裝飾的函式名稱。

例如,當您使用 Microsoft Visual Studio 時,這些行可以併入通用頭檔,如下所示。

#if _MSC_VER > 1200 // Later than Visual Studio 6.0
#define EXPORT comment(linker, "/EXPORT:"__FUNCTION__"="__FUNCDNAME__)
#else // Cannot use this way of exporting functions.
#define EXPORT
#endif // else need to use DEF file or __declspec(dllexport)

如果此標頭包含在原始程序檔中,則可以接著匯出兩個範例函式,如下所示。

C 程式代碼:

double WINAPI my_C_export(double x)
{
#pragma EXPORT
/* Modify x and return it. */
    return x * 2.0;
}

C++程式代碼:

double WINAPI my_Cpp_export(double x)
{
#pragma EXPORT
// Modify x and return it.
    return x * 2.0;
}

請注意,指示詞必須放在函式主體內,而且只有在未設定任何編譯程式選項 /EP/P 時才會展開。 這項技術會移除 DEF 檔案的需求,或 __declspec (dllexport) 宣告,並使用函式程式碼保留其匯出狀態的規格。

另請參閱