• dllMain函数


    Windows在加载DLL的时候,需要一个入口函数,就如同控制台或DOS程序需要main函数、Win32程序需要WinMain函数一样。一些例子中,DLL并没有提供DllMain函数,应用工程也能成功引用DLL,这是因为Windows在找不到DllMain的时候,系统会从其它运行库中引入一个不做任何操作的缺省DllMain函数版本,并不意味着DLL可以放弃DllMain函数。

      根据编写规范,Windows必须查找并执行DLL里的DllMain函数作为加载DLL的依据,它使得DLL得以保留在内存里。这个函数并不属于导出函数,而是DLL的内部函数。这意味着不能直接在应用工程中引用DllMain函数,DllMain是自动被调用的。

      我们来看一个DllMain函数的例子。

    BOOL APIENTRY DllMain( HANDLE hModule, DWORD ul_reason_for_call, LPVOID lpReserved)
    {
     switch (ul_reason_for_call)
     {
      case DLL_PROCESS_ATTACH:
       printf(" process attach of dll");
       break;
      case DLL_THREAD_ATTACH:
       printf(" thread attach of dll");
       break;
      case DLL_THREAD_DETACH:
       printf(" thread detach of dll");
       break;
      case DLL_PROCESS_DETACH:
       printf(" process detach of dll");
       break;
     }
     return TRUE;
    }


      DllMain函数在DLL被加载和卸载时被调用,在单个线程启动和终止时,DllMain函数也被调用,ul_reason_for_call指明了被调用的原因。原因共有4种,即PROCESS_ATTACH、PROCESS_DETACH、THREAD_ATTACH和THREAD_DETACH,以switch语句列出。

      来仔细解读一下DllMain的函数头BOOL APIENTRY DllMain( HANDLE hModule, WORD ul_reason_for_call, LPVOID lpReserved )。

      APIENTRY被定义为__stdcall,它意味着这个函数以标准Pascal的方式进行调用,也就是WINAPI方式;

      进程中的每个DLL模块被全局唯一的32字节的HINSTANCE句柄标识,只有在特定的进程内部有效,句柄代表了DLL模块在进程虚拟空间中的起始地址。在Win32中,HINSTANCE和HMODULE的值是相同的,这两种类型可以替换使用,这就是函数参数hModule的来历。

      执行下列代码:

    hDll = LoadLibrary("text.dll");
    if (hDll != NULL)
    {
     addFun = (lpAddFun)GetProcAddress(hDll, MAKEINTRESOURCE(1));
     //MAKEINTRESOURCE直接使用导出文件中的序号
     if (addFun != NULL)
     {
      int result = addFun(2, 3);
      printf(" call add in dll:%d", result);
     }
     FreeLibrary(hDll);
    }


      我们看到输出顺序为:

    process attach of dll
    call add in dll:5
    process detach of dll


      这一输出顺序验证了DllMain被调用的时机。

      代码中的GetProcAddress ( hDll, MAKEINTRESOURCE ( 1 ) )值得留意,它直接通过.def文件中为add函数指定的顺序号访问add函数,具体体现在MAKEINTRESOURCE ( 1 ),MAKEINTRESOURCE是一个通过序号获取函数名的宏,定义为(节选自winuser.h):

    #define MAKEINTRESOURCEA(i) (LPSTR)((DWORD)((WORD)(i)))
    #define MAKEINTRESOURCEW(i) (LPWSTR)((DWORD)((WORD)(i)))
    #ifdef UNICODE
    #define MAKEINTRESOURCE MAKEINTRESOURCEW
    #else
    #define MAKEINTRESOURCE MAKEINTRESOURCEA

     

     

    __stdcall约定

    如果通过VC++编写的DLL欲被其他语言编写的程序调用,应将函数的调用方式声明为__stdcall方式,WINAPI都采用这种方式,而C/C++缺省的调用方式却为__cdecl。__stdcall方式与__cdecl对函数名最终生成符号的方式不同。若采用C编译方式(在C++中需将函数声明为extern "C"),__stdcall调用约定在输出函数名前面加下划线,后面加“@”符号和参数的字节数,形如_functionname@number;而__cdecl调用约定仅在输出函数名前面加下划线,形如_functionname。

      Windows编程中常见的几种函数类型声明宏都是与__stdcall和__cdecl有关的(节选自windef.h):

    #define CALLBACK __stdcall //这就是传说中的回调函数
    #define WINAPI __stdcall //这就是传说中的WINAPI
    #define WINAPIV __cdecl
    #define APIENTRY WINAPI //DllMain的入口就在这里
    #define APIPRIVATE __stdcall
    #define PASCAL __stdcall


      在lib.h中,应这样声明add函数:

    int __stdcall add(int x, int y);


      在应用工程中函数指针类型应定义为:

    typedef int(__stdcall *lpAddFun)(int, int);


      若在lib.h中将函数声明为__stdcall调用,而应用工程中仍使用typedef int (* lpAddFun)(int,int),运行时将发生错误(因为类型不匹配,在应用工程中仍然是缺省的__cdecl调用).

  • 相关阅读:
    清除目录下的文件 java
    基于Android AFW实现的app多开研究
    soap方式调用webserver接口发送短信
    qt编译配置
    Redis常用命令
    vue history模式刷新页面进入404解决方案
    [PyTorch] PyTorch安装对各组件版本的要求
    SQL Server获取连接的IP地址
    Delphi中idHttpServer
    SQL Server 分页问题
  • 原文地址:https://www.cnblogs.com/fwy-walking/p/4125798.html
Copyright © 2020-2023  润新知