1 DLLMain()函数的功能

Windows在加载DLL的时候,需要一个入口函数,就如同控制台或DOS程序需要main函数、Win32程序需要WinMain函数一样。根据编写规范,Windows必须查找并执行DLL里的DllMain函数作为加载DLL的依据,它使得DLL得以保留在内存里。这个函数并不属于导出函数,而是DLL的内部函数。这意味着不能直接在应用工程中引用DllMain函数,DllMain是自动被调用的。

注:一些例子中,DLL并没有提供DllMain函数,应用工程也能成功引用DLL,这是因为Windows在找不到DllMain的时候,系统会从其它运行库中引入一个不做任何操作的缺省DllMain函数版本,并不意味着DLL可以放弃DllMain函数。

2 使用示例

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

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

3  参数含义

DllMain的函数头定义为:
BOOL APIENTRY DllMain( HANDLE hModule, WORD ul_reason_for_call, LPVOID lpReserved )
其中,APIENTRY被定义为__stdcall,它意味着这个函数以标准Pascal的方式进行调用,也就是WINAPI方式;
函数参数hModule是进程中的每个DLL模块被全局唯一的32字节的HINSTANCE句柄标识(句柄代表了DLL模块在进程虚拟空间中的起始地址,只有在特定的进程内部有效。在Win32中,HINSTANCE和HMODULE的值是相同的,这两种类型可以替换使用。
参数ul_reason_for_call指明了被调用的原因。共有4种,即PROCESS_ATTACH、PROCESS_DETACH、THREAD_ATTACH和THREAD_DETACH,以switch语句列出。
lpReserved 表示一个保留参数,目前已经很少使用
4 调用方式

DllMain函数在以下几种情况被调用:

1) DLL被加载

一个程序要调用Dll里的函数,首先要先把DLL文件映射到进程的地址空间。要把一个DLL文件映射到进程的地址空间,有两种方法:静态链接和动态链接的LoadLibrary或者LoadLibraryEx。

当一个DLL文件被映射到进程的地址空间时,系统调用该DLL的DllMain函数,传递的fdwReason参数为DLL_PROCESS_ATTACH。这种调用只会发生在第一次映射时。如果同一个进程后来为已经映射进来的DLL再次调用LoadLibrary或者LoadLibraryEx,操作系统不会再用DLL_PROCESS_ATTACH调用DLL的DllMain函数,而是增加DLL的使用次数。不同进程用LoadLibrary同一个DLL时,每个进程的第一次映射都会用DLL_PROCESS_ATTACH调用DLL的DllMain函数。

2) DLL被卸载

当DLL被从进程的地址空间解除映射时,系统调用了它的DllMain,传递的fdwReason值是DLL_PROCESS_DETACH。当DLL处理该值时,它应该执行进程相关的清理工作。DLL被从进程的地址空间解除映射的情况有两种:
   ① FreeLibrary()被调用。(有几个LoadLibrary,就要有几个FreeLibrary)
   ② 进程结束。在进程结束前还没有解除DLL的映射,进程结束后会解除DLL映射。(如果进程的终结是因为调用了TerminateProcess,系统就不会用DLL_PROCESS_DETACH来调用DLL的DllMain函数。这就意味着DLL在进程结束前没有机会执行任何清理工作。)
注意:当用DLL_PROCESS_ATTACH调用DLL的DllMain函数时,如果返回FALSE,说明没有初始化成功,系统仍会用DLL_PROCESS_DETACH调用DLL的DllMain函数。因此,必须确保没有清理那些没有成功初始化的东西

3) 单个线程启动

当进程创建一线程时,系统查看当前映射到进程地址空间中的所有DLL文件映像,并用值DLL_THREAD_ATTACH调用DLL的DllMain函数。新创建的线程负责执行这次的DLL的DllMain函数,只有当所有的DLL都处理完这一通知后,系统才允许进程开始执行它的线程函数。
注意跟DLL_PROCESS_ATTACH的区别,我们在前面说过,第n(n>=2)次以后地把DLL映像文件映射到进程的地址空间时,只增加使用次数,而不用DLL_PROCESS_ATTACH调用DllMain。DLL_THREAD_ATTACH不同,进程中的每次建立线程,都会用值DLL_THREAD_ATTACH调用DllMain函数,哪怕是线程中建立线程也一样。

4) 单个线程终止

如果线程调用了ExitThread来结束线程(线程函数返回时,系统也会自动调用ExitThread),系统查看当前映射到进程空间中的所有DLL文件映像,并用DLL_THREAD_DETACH来调用DllMain函数,通知所有的DLL去执行线程级的清理工作。
注意:如果线程的结束是因为系统中的一个线程调用了TerminateThread(),系统就不会用值DLL_THREAD_DETACH来调用所有DLL的DllMain函数。

5 注意事项

__stdcall约定

为了使VC++编写的DLL能被其他语言编写的程序调用, 函数的调用方式应显式声明为__stdcall方式,因为在C/C++中,缺省的调用方式为__cdecl。
__stdcall方式与__cdecl调用方式的区别在于会将函数名处理成不同的符号。具体如下:
__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);

  执行下列代码:

hDll = LoadLibrary("text.dll");
if (hDll != NULL)
{
 addFun = (lpAddFun)GetProcAddress(hDll, MAKEINTRESOURCE(1));
 //MAKEINTRESOURCE直接使用导出文件中的序号
 if (addFun != NULL)
 {
  int result = addFun(2, 3);
  printf("\ncall 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

dllMain函数的作用相关推荐

  1. DllMain中不当操作导致死锁问题的分析--进程对DllMain函数的调用规律的研究和分析

    不知道大家是否思考过一个过程:系统试图运行我们写的程序,它是怎么知道程序起始位置的?很多同学想到,我们在编写程序时有个函数,类似Main这样的名字.是的!这就是系统给我们提供的控制程序最开始的地方(注 ...

  2. python函数的作用是_Python函数一

    函数 一.函数的作用 函数就是一段具有独立功能的代码块整合到一个整体并命名,在需要的位置调用这个名称即可完成对应的需求 函数在开发过程中,可以更高效的实现代码重用 二,函数的使用步骤 2.1 定义函数 ...

  3. python中匿名函数的作用_Python 中的匿名函数,你会用吗

    原标题:Python 中的匿名函数,你会用吗 概念 我们从一个例子引入. 这里有一个元素为非空字符串的列表,按字符串最后一个字母将列表进行排序.如果原列表是 ['abc', 'g', 'def'],则 ...

  4. java中的string函数_java中string.trim()函数的作用实例及源码

    trim()的作用:去掉字符串首尾的空格. public static void main(String arg[]){ String a=" hello world "; Str ...

  5. VC++ inline内联函数的作用解决方案

    VC++ inline内联函数的作用解决方案 __attribute__((always_inline)) attribute((always_inline))的意思是强制内联,所有加了attribu ...

  6. C语言中内联函数的作用 inline

    C语言中内联函数的作用 inline C语言中内联函数到底有什么作用? 试想一下,每当我们在假设就在主函数中调用另外一个函数的时候,那么这个函数就要入栈或者出栈,比如说下面的一个例子: 点击(此处)折 ...

  7. python主函数的作用_python中main函数的用法

    原博文 2020-03-27 20:25 − **什么场景下会有main函数?** 当该python脚本被作为模块(module)引入(import)时,其中的main()函数将不会被执行. **ma ...

  8. 【Android 逆向】ptrace 函数 ( C 标准库 ptrace 函数简介 | ptrace 函数真实作用 )

    文章目录 一.C 标准库 ptrace 函数简介 二.ptrace 函数真实作用 一.C 标准库 ptrace 函数简介 ptrace 函数 : 在 C 标准库 中有一个 ptrace 函数 , 该函 ...

  9. STM32中NVIC_SystemReset()函数的作用?什么时候用?

    STM32中NVIC_SystemReset()函数的作用?什么时候用? STM32软件复位有两种方式 (1)方式一:NVIC_SystemReset()函数用来复位STM32. 注意1:从SYSRE ...

  10. Softmax 函数及其作用(含推导)

    Softmax函数的定义及作用 Softmax是一种形如下式的函数: P(i)=exp(θTix)∑Kk=1exp(θTkx) 其中 θi和x是列向量, θTix可能被换成函数关于x的函数 fi(x) ...

最新文章

  1. 增加ESXI中虚拟机CENTOS系统分区容量
  2. 模组使用之NB-IoT模组的工作模式、PSM、DRX和eDRX状态说明
  3. SSO单点登录之——JWT
  4. Git,Github和Gitlab简介和基本使用
  5. C#序列化反序列化对象为base64字符串
  6. 需要掌握的数学知识(待补充)
  7. BLAS+BLACS+LAPACK+SCALAPACK安装
  8. 用ACL 对Dos***进行分类的示例
  9. ejb 属于哪一层,作用是什么,什么时候用
  10. 【python】直方图均衡化和自适应均衡化图像
  11. 解决Android Studio 新建导入项目时死掉
  12. 抓包软件 Fiddler 了解一下?
  13. 如何查看class文件内容
  14. 计算机导论国内外发展,计算机导论第一章计算机发展历程.ppt
  15. Linux daemontools安装及使用
  16. java 生成ppt_Java 创建并应用PPT幻灯片母版
  17. 文字转语音播报,兼容多种浏览器
  18. LA 4490 Help Bubu
  19. Python爬虫_03_urllib_xpath_JsonPath_BeautifulSoup应用及案例
  20. DLang 编译实验

热门文章

  1. HTML作业制作服装推广软文,盘点:服装推广软文写作技巧
  2. 计算机后端维护,机房智能交通后台系统运行维护内容.doc
  3. 工业交换机在智能交通电子警察系统解决方案的应用
  4. 发那科机器人示教器按键解读
  5. 极品飞车最高通缉Ps2的秘籍
  6. idea安装插件后无法启动
  7. 各国语言缩写以及国际域名缩写
  8. java io和nio_Java IO与NIO比较
  9. 初探微信小程序渗透测试
  10. 谷歌恐龙游戏HTML,谷歌浏览器小恐龙游戏