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 __cplusplus
extern \"C\"
{
#endif
 
void 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 function
void 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 __cplusplus
extern \"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沒有正確被載入,透過最後一個判斷勢將錯誤印出,以上就是程式的解釋。
程式執行的畫面如下:

文章標籤
全站熱搜
創作者介紹
創作者 jeff810123 的頭像
jeff810123

在我心中有一個夢

jeff810123 發表在 痞客邦 留言(0) 人氣(7,162)