dll是Dynamic-link library的縮寫。dl的好處在於當程式呼叫到dll檔中的函式,才會把dll載入記憶體,所以節省了記憶體空間。因為當你用完後就可以free掉那 塊記憶體,不像一般在程式內部宣告的函數會一直佔用記憶體空間。使用dll還有一個好處,就是在程式中用到很多函數,如果將部份函數在dl中,程式的維護 會比較容易。
在C語言中,要呼叫dll要先熟悉函數指標,函數指標跟一般指標不同,在於他指向的不是一般型別而是函數。函數指標的宣告方式,跟函數原型的宣告差不多,宣告的形式為回傳值型別 (*函數指標變數名)(參數1型別,參數2型別,.....),在本例中我們是將函數指標定成一個新型別,宣告為 typedef void(*FuncPtr)(LPCTSTR),這樣在我們要用到這類型的函數指標,只要宣告成如 FuncPtr 指標變數名稱就 不用寫的落落長,這裡要注意的是你宣告的函數指標要能跟你要呼叫的dll中的函數能配對。宣告完指標接著,就是要利用Windows API中的LoadLibrary將dll載入獲取控制碼,然後再透過GetProcAddress取得dll中欲操作的函數位址,將宣告的函數指標指向 這個位址。然後就跟使用一般函數般使用函數指標,例如在本例中我是這樣使用myDllFunc(TEXT(\"Hello, World!\")),使用完記得用FreeLibrary將dll佔的記憶體free掉,大致上的操作流程是這樣。說了那麼多還是要看程式碼才會更容易了解。首先我們要先創一個dll檔,步驟如下(開發環境為Code::Block):
(1)開啟Code::Block(廢話XD
(2)File->New->Project,選Dynamic Link Library,按Go
(3)輸入Project名稱,Next
(4)這邊基本上不用更動,直接按Finish
(5)代碼
main.h部分
|
01
02
03
04
05
06
07
08
09
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
|
#ifndef __MAIN_H__#define __MAIN_H__#include <windows.h>/* To use this exported function of dll, include this header * in your project. */#ifdef BUILD_DLL #define DLL_EXPORT __declspec(dllexport)#else #define DLL_EXPORT __declspec(dllimport)#endif#ifdef __cplusplusextern \"C\"{#endifvoid DLL_EXPORT SomeFunction(const LPCSTR sometext);#ifdef __cplusplus}#endif#endif // __MAIN_H__ |
main.h部分
|
01
02
03
04
05
06
07
08
09
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
|
#include \"main.h\"// a sample exported functionvoid DLL_EXPORT SomeFunction(const LPCSTR sometext){ MessageBoxA(0, sometext, \"DLL Message\", MB_OK | MB_ICONINFORMATION);}BOOL WINAPI DllMain(HINSTANCE hinstDLL, DWORD fdwReason, LPVOID lpvReserved){ switch (fdwReason) { case DLL_PROCESS_ATTACH: // attach to process // return FALSE to fail DLL load break; case DLL_PROCESS_DETACH: // detach from process break; case DLL_THREAD_ATTACH: // attach to thread break; case DLL_THREAD_DETACH: // detach from thread break; } return TRUE; // succesful} |
這裡的DllMai n跟Console中的main或WindowsAPI視窗程式中的WinMain一樣都是程式的入口,而其中switch是判斷dll處在什麼狀態 DLL_PROCESS_ATTACH是dll在主執行緒被載入(LoadLibrary之後)發生,所以我們也可以在這之中加入訊息方塊,讓我們知道說 dll被呼叫, DLL_PROCESS_DETACH在主執行緒被卸載(FreeLibrary之後)發生, DLL_THREAD_ATTACH及DLL_THREAD_DETACH則是在子執行緒中被載入或卸載發生。接著重點來,我們最重要的是要將我們要使用 的函數放在dll中,讓他能被其他程式呼叫所以,還要編寫所要使用的函數,他的宣告形式為 傳回值型別 DLL_EXPORT 函數名(參數1型別 參數1,參數2型別參數2,.....),然後在這邊的函數原型宣告放在main.h中,放在如下圖程式碼所示刪節號的區塊。
|
01
02
03
04
05
06
07
08
09
10
|
#ifdef __cplusplusextern \"C\"{#endif......#ifdef __cplusplus}#endif |
這邊一開始Code::Block就幫你實際完成了SomeFunction這個函數,這個函數的功能主要是將由sometext傳入的Unicode字 串,透過WindowsAPI中的MessageBox函數產生一個訊息方塊顯示sometext的字串,相關的函數使用方法可按此查詢。這邊就直直接這 個函數就好,以上是dll部分的解釋。
(6)接著按build(Ctrl-F9)編譯成dll檔,而產生的dll檔會示你Build target的不同放在不同資料夾,如果是Debug就放在Debug資料夾,Realse就放在Realse資料夾,她們茲間的差別在於編譯出的檔案大 小不一樣,Build targe是Debug的會比較大。我這邊是用Build targe是Debug,之後我們就要將產生的dl檔和呼叫dll的程式在一起,這點很重要。
接著式呼叫dll程式碼的部分,首先在Code::Block下創一個新的檔案,程式碼如下:
CallDll.c
|
01
02
03
04
05
06
07
08
09
10
11
12
13
14
15
16
17
18
19
20
21
22
23
|
#include<windows.h>typedef void (*FuncPtr)(LPCTSTR);int main(void){ FuncPtr myDllFunc; BOOL linkSuccessFlag = FALSE, fFreeResult; HINSTANCE hinstLib = LoadLibrary(TEXT(\"CallDll.dll\")); if (hinstLib != NULL) { myDllFunc = (FuncPtr) GetProcAddress(hinstLib, \"SomeFunction\"); if (myDllFunc != NULL) { linkSuccessFlag = TRUE; myDllFunc(TEXT(\"Hello, World!\")); } fFreeResult = FreeLibrary(hinstLib); } } if (!linkSuccessFlag) printf(\"error; %u\\n\", GetLastError());system(\"PAUSE\");return 0; |
這段程式中,首先我們先宣告一個函數指標myDllFunc ,之後HINSTANCE hinstLib = LoadLibrary(TEXT(\"CallDll.dll\")), 是做將dll載入的動作,並傳會一個控制碼放在hinstLib ,如果載入失敗,hinstLib 值會為NULL,如果連結成功,傳回的值則為dll在記憶體中的linear address。在接下來的if判斷式則是判斷dll有沒有有被載入,如果有被載入在繼續做載入函數的動作,將函數指標指向dll中欲操作的函數位址myDllFunc = (FuncPtr) GetProcAddress(hinstLib, \"SomeFunction\"), 接著的if判斷式則是判斷函數有沒有載入成功有的話,就執行 myDllFunc(TEXT(\"Hello, World!\")),linkSuccessFlag 設為TRUE,讓程式能彈出一個訊息方塊顯示Hello,World!,這裡的linkSuccessFlag是作為判斷有沒有正確載入dll。如果是 FALSE,代表dll沒有正確被載入,透過最後一個判斷勢將錯誤印出,以上就是程式的解釋。
程式執行的畫面如下:
