1. 程式人生 > >動態連結庫開發說明

動態連結庫開發說明

本文章轉載至:http://www.cnblogs.com/hanford/p/6177904.html#_Toc425102248

1基本概念

1.1 一個簡單的例子

下面將使用VC++建立一個動態連結庫檔案。這個檔案將匯出兩個函式StringReverseAStringReverseW,前者將一個ANSI字串逆序,後者將一個Unicode字串逆序。

1.1.1 新建一個VC++專案

對於VC++6.0而言,專案型別請選擇

Win32 Dynamic-Link Library。輸入專案名稱後,單擊"OK"按鈕。

在接下來的介面裡,選擇"An empty DLL project",然後單擊"Finish"按鈕。

在接下來的介面裡單擊"OK"按鈕完成專案建立。

對於VC++9.0(即VC++2008)而言,專案型別請選擇Win32。輸入專案名稱後,單擊"確定"按鈕。

在接下來的介面裡,請選擇"應用程式設定"下的"DLL"和"空專案"。單擊"完成"按鈕完成專案建立。

1.1.2 新增原始檔

對於VC++6.0而言,在Workspace 視窗的 FileView 

選項卡內,右鍵單擊"Test files",在右鍵選單裡單擊【Add Files to Project...】選單項

輸入原始檔名後,單擊"OK"按鈕

彈出對話方塊裡詢問是否在專案裡增加Test.c這個檔案的引用。請單擊"是"按鈕。

此時滑鼠雙擊Test.c。因為這個檔案還不存在,VC++6.0會提示是否建立,請單擊"是"按鈕。

對於VC++9.0而言,在解決方案資源管理器裡,右鍵單擊"Test",在右鍵選單裡單擊【新增】【新建項】選單項。

接下來的介面內,請選擇"C++檔案(.cpp)",並輸入原始檔名Test.c,然後單擊"新增"按鈕。完成

Test.c檔案的新增和建立。

1.1.3 輸入原始碼

Test.c裡輸入如下原始碼:

#include <windows.h>

/***************************************************************************\

將一個 Unicode 字串逆序

\***************************************************************************/

__declspec(dllexport) wchar_t* WINAPI StringReverseW(wchar_t*wzStr)

{

if(wzStr)

{

int p1 = 0;

int p2 = wcslen(wzStr) - 1;

wchar_t t;

while(p1 < p2)

{

t = wzStr[p1];

wzStr[p1++] = wzStr[p2];

wzStr[p2--] = t;

}

}

return wzStr;

}

/***************************************************************************\

將一個 ANSI 字串逆序

\***************************************************************************/

__declspec(dllexport) char* WINAPI StringReverseA(char*szStr)

{

if(szStr)

{

int nLenA = strlen(szStr) + 1;

int nLenW = MultiByteToWideChar(CP_ACP,0,szStr,nLenA,NULL,0);

wchar_t*pStrW = (wchar_t*)malloc(nLenW * sizeof(wchar_t));

MultiByteToWideChar(CP_ACP,0,szStr,nLenA,pStrW,nLenW);

StringReverseW(pStrW);

WideCharToMultiByte(CP_ACP,0,pStrW,nLenW,szStr,nLenA,NULL,NULL);

free(pStrW);

}

return szStr;

}

1.1.4 __declspec(dllexport)

__declspec(dllexport)修飾符用來匯出函式StringReverseAStringReverseW。它還可以匯出變數和類,這個後面介紹。

1.1.5 WINAPI

WINAPI 其實就是__stdcall。以StringReverseA為例,呼叫它時,引數szStr將被壓入棧中,從StringReverseA返回時,引數szStr需要出棧。__stdcall表示由StringReverseA自己執行出棧操作。假如將__stdcall去掉或換為__cdecl,則由呼叫StringReverseA的函式負責執行出棧操作。說了這麼多,最重要的是:某些語言,如VB6.0只支援__stdcall,所以為了讓這個dll被儘可能多的程式語言支援,請使用WINAPI

1.1.6 匯出符號

現在可以編譯程式,生成Test.dll了。使用eXeScope6.30開啟Test.dll,可以看到Test.dll確實匯出了兩個函式。請注意:每個匯出函式都有一個序號,它是一個正整數。

不過有意思的是:匯出函式的名稱並不是StringReverseAStringReverseW,而是[email protected][email protected]

如果把Test.c改名為Test.cpp,則匯出的名稱更為複雜。請參考下圖:

這是什麼原因呢?因為Test.c的副檔名為cVC++使用C編譯器進行編譯。Test.cpp的副檔名為cppVC++使用C++編譯器進行編譯。C++為了實現函式過載,編譯時會根據引數型別和個數對函式名進行再次命名。

1.1.7 DEF檔案

如何防止VC++編譯器生成dll時將匯出函式名更改掉?答案就是使用模組定義檔案。請在VC++專案裡增加模組定義檔案Test.def。這個檔名可以是1.defA.def……只要副檔名是def即可。編輯Test.def,使其內容如下:

EXPORTS

StringReverseA

StringReverseW

上述內容表示:匯出函式StringReverseAStringReverseW。此時,這兩個函式前面的__declspec(dllexport)修飾符將不再需要。

DEF檔案的功能還有很多,具體請參考MSDN

1.2 呼叫動態庫

生成的動態庫檔案可以被多種程式語言使用。限於篇幅下面僅介紹VC++如何呼叫動態庫。

1.2.1 隱式連結

編譯動態庫檔案時,同時會生成Lib檔案。使用動態庫的VC++程式可以連結這個Lib檔案,這就是隱式連結。可參考的程式碼如下:

#include <windows.h>

#include <stdio.h>

__declspec(dllimport) char* WINAPI StringReverseA(char*szStr);    

#pragma comment(lib,"D:/VC6/Test/Debug/Test.lib")

void main()

{

char szStr[] = "隱式連結動態庫";

puts(StringReverseA(szStr));

}

__declspec(dllimport) char* WINAPI StringReverseA(char*szStr);    是函式宣告。修飾符__declspec(dllimport)表示這是一個匯入函式。去除這個修飾符不影響程式的編譯、執行,但有了__declspec(dllimport)之後,生成的程式碼更小,執行更快。

注意:對於C++程式而言,可能需要這樣宣告函式:

extern "C"

{

__declspec(dllimport) char* WINAPI StringReverseA(char*szStr);    

}

extern "C" 的作用是:告訴C++編譯器連線時不要以C++語法修改StringReverseA的名稱。為什麼說是"可能"需要extern "C"呢?這與dll的編譯有關係。如果使用C編譯器編譯dll,則需要extern "C";如果使用C++編譯器編譯dll,則不需要extern "C"

#pragma comment(lib,"D:/VC6/Test/Debug/Test.lib")表示連結的時候使用D:\VC6\Test\Debug\Test.Lib檔案。就是編譯動態庫時產生的那個Lib檔案。

採用隱式連結,執行程式的時候動態庫檔案首先被載入至記憶體。系統如何定位dll檔案呢?其搜尋順序為:exe所在目錄、當前目錄(GetCurrentDirectory)、System32目錄(GetSystemDirectory)、Windows目錄(GetWindowsDirectory)、環境變數PATH指定的目錄。不用記這麼多,最保險的做法就是將dllexe放在同一資料夾下。如果為了多個exe程式共享一個dll,請將這個dll檔案複製到System32目錄下。

1.2.2 顯式連結

顯式連結可以靈活控制動態庫檔案的載入、解除安裝。其使用步驟如下:

1、使用LoadLibrary函式載入動態庫檔案至記憶體;

2、使用GetProcAddress函式獲得匯出函式的地址;

3、呼叫匯出函式;

4、使用FreeLibrary解除安裝動態庫檔案。

可參考如下程式碼:

#include <windows.h>

#include <stdio.h>

void main()

{

HINSTANCE hDll = LoadLibrary("Test.dll"); //載入動態庫檔案

if(hDll)

{//載入成功