編寫DLL

想一想仍是把這個記錄下吧,雖然不難,但因爲平時寫得很少,總是搞忘了。windows

一、咱們來編寫一個簡單的DLL程序。函數

首先,咱們來看下入口函數DllMain()。DllMain()有3個參數:工具

(1)hModule:DLL模塊的句柄。spa

(2)ul_reason_for_call:DllMain函數被調用的緣由。其取值有4種,分別是DLL_PROCESS_ATTACH(當DLL被某進程加載時DllMain被調用)、DLL_PROCESS_DETACH(當DLL被某進程卸載時DllMain被調用)、DLL_THREAD_ATTACH(進程中有線程被建立時DllMain被調用)、DLL_THREAD_DETACH(進程中有線程結束時DllMain被調用)。線程

(3)lpReserved:保留項。code

函數前面的APIENTRY是一個宏,定義以下:blog

#define APIENTRY    WINAPI

WINAPI也是一個宏,表示一種函數調用約定。進程

 

咱們須要對DllMain()進行一下填充,加個switch。後面詳見例子。咱們還須要爲之添加一個簡單的導出函數。該函數定義以下:文檔

extern "C" __declspec(dllexport) VOID MsgBox(char *szMsg);

extern "C"表示該函數以C方式導出。字符串

其實現以下:

VOID MsgBox(char *szMsg){
    char szModuleName[MAX_PATH]={0};
    GetModuleFileName(NULL,szModuleName,MAX_PATH);
    MessageBox(NULL,szMsg,szModuleName,MB_OK);
}

運行函數後彈出一個對話框,顯示一個字符串,並顯示其所在的進程的進程名。咱們分別在DLL_PROCESS_ATTACH和DLL_PROCESS_DETACH下加一個對該函數的調用。

以下:

#include <windows.h>

extern "C" __declspec(dllexport) VOID MsgBox(char *szMsg);

BOOL APIENTRY DllMain( HANDLE hModule, DWORD  ul_reason_for_call, LPVOID lpReserved){
    switch(ul_reason_for_call){
    case DLL_PROCESS_ATTACH:
        MsgBox("DLL_PROCESS_ATTACH");
        break;
    case DLL_PROCESS_DETACH:
        MsgBox("DLL_PROCESS_DETACH");
        break;
    case DLL_THREAD_ATTACH:
        break;
    case DLL_THREAD_DETACH:
        break;
    }
    return TRUE;
}
VOID MsgBox(char *szMsg){
    char szModuleName[MAX_PATH]={0};
    GetModuleFileName(NULL,szModuleName,MAX_PATH);
    MessageBox(NULL,szMsg,szModuleName,MB_OK);
}

編譯該代碼,會生成兩個好玩的文件:6_10_4.dll和6_10_4.lib。前面是DLL文件,後面是庫文件。

二、靜態調用:

建立一個控制檯程序,創建一個cpp文件,添加代碼以下:

#include <windows.h>

extern "C" VOID MsgBox(char *szMsg);
#pragma comment(lib,"6_10_4")

int main(int argc,char* argv[]){
    MsgBox("hello first dll!");
    return 0;
}

對該代碼直接編譯連接,會報錯:沒法找到6_10_4.lib文件。把這個文件複製到這個cpp目錄下,運行會報錯,提示沒有找到6_10_4.dll文件。一樣,也須要把該文件置於cpp目錄下。至此,沒有問題了,運行依次彈出三個對話框:

三、動態調用:

靜態調用就是在編譯程序時便把dll信息寫入程序裏,而動態調用則是在運行時導入dll信息。

控制檯程序cpp代碼以下:

#include <windows.h>

typedef VOID (*PFUNMSG)(char *);

int main(int argc,char* argv[]){
    HMODULE hModule=LoadLibrary("6_10_4.dll");
    if(hModule==NULL){
        MessageBox(NULL,"6_10_4.dll文件不存在","DLL加載失敗",MB_OK);
        return -1;
    }
    PFUNMSG pFunMsg=(PFUNMSG)GetProcAddress(hModule,"MsgBox");
    pFunMsg("hello first dll!");
    return 0;
}

這裏不要求dll文件必定在cpp目錄下,在LoadLibrary函數裏寫入dll的路徑便可。運行結果與前面的同樣。

四、其餘

對於未文檔化的API,或是沒有提供頭文件的API,咱們能夠利用LoadLibrary()和GetProcAddress()這兩個API函數來實現對前面那些API的調用。

LoadLibrary():

HMODULE LoadLibrary(
    LPCTSTR lpFileName  //file name of module
);

該函數只有一個參數,即要加載的DLL文件的路徑。

GetProcAddress():

GetProcAddress(
    HMODULE hModule,    //handle to DLL module
    LPCSTR  lpProcName  //function name
);

該函數有兩個參數,hModule是模塊的句柄,lpProcName指定要獲取函數地址的函數名稱。

 

另外說下,vc6自帶的工具"Depends"挺好用的,能夠查看dll程序的導出函數等。它在:菜單「開始」->「程序」->「Microsoft Visual Studio 6.0」->「Microsoft Visual Studio 6.0 Tools」->「Depends」。

相關文章
相關標籤/搜索