• 编写DLL


    想想还是把这个记录下吧,虽然不难,但由于平时写得不多,老是搞忘了。

    1、我们来编写一个简单的DLL程序。

    首先,我们来看下入口函数DllMain()。DllMain()有3个参数:

    (1)hModule:DLL模块的句柄。

    (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:保留项。

    函数前面的APIENTRY是一个宏,定义如下:

    #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文件,后面是库文件。

    2、静态调用:

    创建一个控制台程序,建立一个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目录下。至此,没有问题了,运行依次弹出三个对话框:

    3、动态调用:

    静态调用就是在编译程序时便把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的路径即可。运行结果与前面的一样。

    4、其他

    对于未文档化的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”。

  • 相关阅读:
    Effective C++ 学习笔记(12)
    Effective C++ 学习笔记(6)
    Effective C++ 学习笔记(13)
    Effective C++ 学习笔记(11)
    Effective C++ 学习笔记(10)
    (转)C++函数后加const的意义
    Effective C++ 学习笔记(14)
    Effective C++ 学习笔记(7)
    Effective C++ 学习笔记(9)
    Effective C++ 学习笔记(8)
  • 原文地址:https://www.cnblogs.com/jiu0821/p/4606304.html
Copyright © 2020-2023  润新知