1.dll的入口函数

类似程序的入口函数main或者WinMain一样,DLL也有一个入口函数,就是DllMain

2.DllMain函数原型

BOOL APIENTRY DllMain( HMODULE hModule,             //指向自身的句柄DWORD  ul_reason_for_call,   //调用原因LPVOID lpReserved            //静态加载为非NULL,动态加载为NULL)
{switch (ul_reason_for_call){case DLL_PROCESS_ATTACH:  //进程创建的时候调用case DLL_THREAD_ATTACH:   //线程创建的时候调用case DLL_THREAD_DETACH:   //线程结束的时候调用case DLL_PROCESS_DETACH:  //进程结束的时候调用break;}return TRUE;
}

3.何时调用DllMain

静态链接时(通过包含头文件和lib文件的方式调用DLL)或动态链接(通过LoadLibrary和FreeLibrary的方式调用DLL)都会调用DllMain函数。DllMain的第二个参数ul_reason_for_call指明了系统调用DLL的原因,它可能是:

DLL_PROCESS_ATTACH:  // 1 //进程创建的时候调用
DLL_THREAD_ATTACH:     // 2 //线程创建的时候调用
DLL_THREAD_DETACH:    // 3 //线程结束的时候调用
DLL_PROCESS_DETACH:  // 0 //进程结束的时候调用

3.1DLL_PROCESS_ATTACH

一个程序要调用DLL里的函数,先要把DLL文件映射到进程的地址空间。要把一个DLL文件映射到进程的地址空间,有两种方法:静态链接和动态链接。每个进程第一次将一个DLL文件映射到进程的地址空间时,系统调用该DLL的DllMain函数,传递的ul_reason_for_call参数为DLL_PROCESS_ATTACH。此时动态加载lpReserved为NULL,静态为非NULL。

注意:如果同一个进程后来为已经映射进来的DLL再次调用LoadLibrary或者LoadLibraryEx,操作系统只会增加DLL的使用次数,它不会再用DLL_PROCESS_ATTACH调用DLL的DllMain函数。

3.2DLL_PROCESS_DETACH

与DLL_PROCESS_ATTACH相对应,当DLL从进程的地址空间解除映射时,系统调用DllMain,传递ul_reason_for_call参数为DLL_PROCESS_DETACH。当DLL处理该值时,应该执行进程相关的清理工作。

1)FreeLibrary解除DLL映射(有几个LoadLibrary,就要有几个FreeLibrary)此刻lpReserved为NULL。

2)进程结束后会解除DLL映射。如果进程的终结调用TerminateProcess,系统不会调用DLL_PROCESS_DETACH来调用DLL的DllMain函数,这意味着DLL在进程结束前没有机会执行任何清理工作。此刻lpReserved为为NULL

3.3DLL_THREAD_ATTACH

当进程创建一线程时,系统查看当前映射到进程地址空间的所有DLL文件映像,并用值DLL_THREAD_ATTACH调用DLL的DllMain函数。新创建的线程负责执行这次的DLL的DllMain函数,只有当所有的DLL都处理完这一通知后,系统才允许进程开始执行它的线程函数。

注意:进程中的每次建立线程,都会用值DLL_THREAD_ATTACH调用DllMain函数,哪怕是线程中建立线程也一样。

3.4DLL_THREAD_DETACH

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

注意:如果线程的结束是因为系统中的一个线程调用了TerminateThread,系统就不会用值DLL_THREAD_DETACH来调用所有DLL的DllMain函数。

4.实例

导出DLL:

1)dllmain.cpp:

// dllmain.cpp : 定义 DLL 应用程序的入口点。
#include "stdafx.h"
#include <iostream>
using namespace std;BOOL APIENTRY DllMain( HMODULE hModule,             //指向自身的句柄DWORD  ul_reason_for_call,   //调用原因LPVOID lpReserved            //静态加载为非NULL,动态加载为NULL)
{cout << "实例句柄:" << hModule << "    调用原因:" << ul_reason_for_call << "    链接方式:" << lpReserved << endl;switch (ul_reason_for_call){case DLL_PROCESS_ATTACH:  //进程创建的时候调用cout << "DLL_PROCESS_ATTACH" << endl;break;case DLL_THREAD_ATTACH:   //线程创建的时候调用cout << "DLL_THREAD_ATTACH" << endl;break;case DLL_THREAD_DETACH:   //线程结束的时候调用cout << "DLL_THREAD_DETACH" << endl;break;case DLL_PROCESS_DETACH:  //进程结束的时候调用cout << "DLL_PROCESS_DETACH" << endl;break;}return TRUE;
}

2).cpp文件:

// DynamicLib.cpp: 定义 DLL 应用程序的导出函数。
//#include "stdafx.h"#define _DLL_EXPORTS
#include "func.h"#include <iostream>
using namespace std;void print()
{cout << "Hello World" << endl;
}

3)func.h文件:

#pragma once#ifdef _DLL_EXPORTS
#define DLL_API _declspec(dllexport)
#else
#define DLL_API _declspec(dllimport)
#endifextern "C" DLL_API void print();

4.1静态调用(包含头文件和lib文件的方式)

main.cpp文件:

#include <iostream>
using namespace std;
#include "func.h"#pragma comment(lib,"DynamicLib.lib")int main()
{cout << "-----------------main-----------------" << endl;print();cout << "---------------end main---------------" << endl;//system("pause");return 0;
}

为了看到完成的输出,我们利用命令提示符运行生成的.exe文件:

4.2动态调用(通过LoadLibrary的方式)

main.cpp文件:

#include <iostream>
using namespace std;
//#include "func.h"
#include <windows.h>
//#pragma comment(lib,"DynamicLib.lib")int main()
{cout << "-----------------main-----------------" << endl;typedef void(*PFUN)();HMODULE h = LoadLibrary("DynamicLib.dll");PFUN fun = (PFUN)GetProcAddress(h, "print");fun();FreeLibrary(h);cout << "---------------end main---------------" << endl;//system("pause");return 0;
}

结果:

4.3在静态调用中创建一个线程

main.cpp:

#include <iostream>
using namespace std;
#include "func.h"
#include <windows.h>
#pragma comment(lib,"DynamicLib.lib")CRITICAL_SECTION g_cs;DWORD WINAPI ThreadFun(LPVOID lpThreadParameter
)
{cout << "-----------------thread---------------" << endl;cout << "Hello World" << endl;cout << "-------------- end thread-------------" << endl;return 0;
}int main()
{InitializeCriticalSection(&g_cs);cout << "-----------------main-----------------" << endl;print();cout << "---------------end main---------------" << endl;HANDLE hthread = CreateThread(NULL, 0, ThreadFun, NULL, 0, NULL);WaitForSingleObject(hthread, INFINITE);CloseHandle(hthread);DeleteCriticalSection(&g_cs);//system("pause");return 0;
}

结果:

5.总结

1)创建进程调用DLL_PROCESS_ATTACH;进程结束调用DLL_PROCESS_DETACH

2)创建线程调用DLL_THREAD_ATTACH;线程结束调用DLL_THREAD_DETACH

3)静态调用链接方式(参数lpReserved)为非空;动态调用链接方式(参数lpReserved)为空

9.DLL的入口函数DllMain函数相关推荐

  1. Delphi 如何解决在DLL的入口函数中创建或结束线程时卡死

    先看一下使用Delphi开发DLL时如何使用MAIN函数, 通常情况下并不会使用到DLL的MAIN函数,因为delphi的框架已经把Main函数隐藏起来 而工程函数的 begin  end 默认就是M ...

  2. vs 创建win32 dll(带有DllMain函数)项目时遇到的一系列问题

    好久没有写win32 的dll 了,之前一直用带窗体的mfc dll(方面测试嘛-毕竟),搞得现在忘了正经的dll怎么写了,没办法,在这里总结下,防止以后再忘.(以后肯定忘,可能我明天就忘了 哈哈哈) ...

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

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

  4. DLL入口点函数DllMain

     每个DLL都可以有一个入口点函数DllMain,系统会在不同的时刻调用此函数.以下是DllMain的一般形式: BOOL WINAPI DllMain(HINSTANCE hinstDLL, / ...

  5. DLL入口点函数DllMain .

    每个DLL都可以有一个入口点函数DllMain,系统会在不同的时刻调用此函数.以下是DllMain的一般形式: BOOL WINAPI DllMain( HINSTANCE hinstDLL, // ...

  6. DllMain函数详解

    源码下载: DLL – 动态链接库 DllMainTest – 测试DLL的DllMain 1  DLL的进入/退出函数 1.1  DllMain简介 跟exe有个main或者WinMain入口函数一 ...

  7. DLL导出类和导出函数

    from:https://blog.csdn.net/goodluckmt/article/details/52691297 1.动态库DLL中的类或者函数有时候要被其他的库调用,因此需要被其他库调用 ...

  8. vs2010 导出创建dll 导入使用dll 导出有命名空间的类 函数 外部函数

    2. #include "targetver.h" // dlltest.cpp : 定义 DLL 应用程序的导出函数. //#include "stdafx.h&quo ...

  9. C语言startup()函数,WSAStartup()函数以及DLL的加载

    本节讲解 Windows 下 DLL 的加载,学习 Linux Socket 的读者可以跳过. WinSock(Windows Socket)编程依赖于系统提供的动态链接库(DLL),有两个版本: 较 ...

  10. ildasm.exe 查看c# dll 信息,符号 、函数等等

    ildasm.exe 查看c# dll 信息,符号 .函数等等 运行 ildasm.exe 把 C# 的 dll 拖入程序中即可

最新文章

  1. 在Jira停售后,你决定赌Atlassian Data Center?
  2. 【Sphinx】MySQL+Sphinx 全文检索的使用和测试
  3. java面试 socket_【面试题】Java 的通信编程,编程题(或问答),用JAVA SOCKET 编程,读服务器几个字符,再写入本地显示?...
  4. SAP财务模块中,科目余额表中的数据存在数据库的哪张表中?
  5. 阡陌路-车行天下之新手必备手册
  6. java中的序列化和反序列化
  7. python将字典写入json文件中
  8. Kubernetes容器上下文环境
  9. C# WPF 表单更改提示
  10. 带你走近AngularJS - 基本功能介绍
  11. DreamFactory入门指南 - 第2章安装和配置DreamFactory
  12. java thread应用_JAVA线程应用Thread
  13. Memcached安装及配置
  14. 成功凤凰刷机刷出诺基亚E5系统版本042.014纯净简体中文版
  15. 中国移动互联网行业深度报告
  16. QT界面窗口的显示和隐藏,关闭
  17. 征途私服门:一个程序员和一个黑客群的故事
  18. 用python批量修改图片尺寸
  19. java开发手机app_java 怎么开发手机app接口?
  20. 机制设计专栏(2)-说一说IC机制

热门文章

  1. xps15u盘装linux,戴尔XPS 15 9575笔记本安装win10系统的操作教程
  2. 高通平台开发系列讲解(音频篇)Codec驱动移植步骤
  3. excel服务器 微信设置,微信电脑版excel怎么编辑
  4. CAD打开文件总是弹出要求选择字体怎么办
  5. java数字转换成大写字母
  6. 鸿蒙智慧屏安装应用,谁说华为智慧屏不能装APP,我来打脸了,附零难度安装APP教程...
  7. 【Arduino】步进电机驱动程序Stepper_Motor_TB6600
  8. 微信小程序测试号申请页面不显示AppID 和AppSecret的解决办法
  9. 2019ZIF郑州工博会 盛大起航
  10. u3d_fw 框架使用