概述
DllMain是Windows动态链接库(DLL)的入口函数,类似于可执行程序的main函数- 它在以下四种情况下会被系统自动调用:
DLL被加载到进程内存DLL被卸载- 进程创建新线程
- 线程退出
参数
- 保留参数
lpReserved:
DLL_PROCESS_ATTACH时为NULL
DLL_PROCESS_DETACH时表示卸载方式
|
1 2 3 4 5 |
BOOL APIENTRY DllMain( HMODULE hModule, // 当前DLL模块的句柄 DWORD ul_reason_for_call, // 触发调用的原因(4种情况) LPVOID lpReserved // 保留参数(系统内部使用) ); |
返回值
- 返回
TRUE:DLL初始化成功 - 返回
FALSE(仅对DLL_PROCESS_ATTACH有效):DLL会被立即卸载
注意
- 禁止在
DllMain中执行的操作
|
1 2 3 4 5 6 |
// ❌ 危险操作示例 case DLL_PROCESS_ATTACH: LoadLibrary("Another.dll"); // 可能引发死锁 CreateThread(...); // 线程创建顺序不可控 MessageBox(NULL, ...); // 可能卡住进程 break; |
- 当多个线程同时加载/卸载
DLL时:
|
1 2 3 4 5 6 7 8 9 10 11 |
// 需要加锁保护共享资源 case DLL_PROCESS_ATTACH: EnterCriticalSection(&g_cs); g_refCount++; LeaveCriticalSection(&g_cs); break; case DLL_PROCESS_DETACH: EnterCriticalSection(&g_cs); if(--g_refCount == 0) FreeResource(); LeaveCriticalSection(&g_cs); break; |
场景
DLL_PROCESS_ATTACH
DLL首次被加载到进程地址空间时
|
1 2 3 4 5 6 7 8 |
case DLL_PROCESS_ATTACH: // 初始化全局变量 g_hModule = hModule; // 创建互斥体/事件等内核对象 g_hMutex = CreateMutex(NULL, FALSE, NULL); // 初始化日志系统 InitLogSystem(); break; |
DLL_PROCESS_DETACH
DLL即将被卸载(进程退出或调用FreeLibrary)
|
1 2 3 4 5 6 7 8 |
case DLL_PROCESS_DETACH: // 释放资源 CloseHandle(g_hMutex); // 保存配置到文件 SaveConfig(); // 关闭日志系统 ShutdownLogSystem(); break; |
DLL_THREAD_ATTACH
- 进程创建新线程时(该线程第一次调用
DLL函数前)
|
1 2 3 4 5 6 |
case DLL_THREAD_ATTACH: // 初始化线程局部存储(TLS) TlsAlloc(); // 记录线程启动日志 Log("Thread %d created", GetCurrentThreadId()); break; |
DLL_THREAD_DETACH
- 线程退出时(线程正常退出前)
|
1 2 3 4 5 6 |
case DLL_THREAD_DETACH: // 清理线程局部存储 TlsFree(); // 记录线程结束日志 Log("Thread %d exited", GetCurrentThreadId()); break; |
问题
- 关于创建线程触发
DLL_THREAD_ATTACH的具体细节- 总结就是:
- 当进程内创建任何新线程时(通过
CreateThread、_beginthreadex等API),所有已加载到该进程的DLL都会收到DLL_THREAD_ATTACH通知 - 但是,主线程(进程启动时的第一个线程)不会触发
DLL_THREAD_ATTACH - 另外,如果动态库加载前已存在的线程:若线程在
DLL被加载(LoadLibrary)之前就已创建,则该线程不会触发其DLL_THREAD_ATTACH - 动态库加载失败:如果
DLL在DLL_PROCESS_ATTACH阶段返回FALSE,则后续线程创建不会触发它的DLL_THREAD_ATTACH
| 条件 | 是否触发 | 说明 |
| 进程启动时创建主线程 | ✅ | 主线程触发 DLL_PROCESS_ATTACH,不触发 DLL_THREAD_ATTACH |
| 进程中新建任何线程 | ✅ | 新线程触发所有已加载 DLL 的 DLL_THREAD_ATTACH(无论是否调用其函数) |
| 调用动态库的导出函数 | ❌ | 函数调用在现有线程中运行,不会创建新线程,因此不触发 |
| 动态库被其他模块加载 | ❌ | 加载行为本身只触发 DLL_PROCESS_ATTACH |
示例
|
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 |
#include <windows.h> #include <stdio.h> HANDLE g_hLogFile; BOOL APIENTRY DllMain(HMODULE hModule, DWORD ul_reason_for_call, LPVOID lpReserved) { switch (ul_reason_for_call) { case DLL_PROCESS_ATTACH: // 初始化日志文件 g_hLogFile = CreateFile(L"mylog.txt", GENERIC_WRITE, FILE_SHARE_READ, NULL, CREATE_ALWAYS, FILE_ATTRIBUTE_NORMAL, NULL); break; case DLL_PROCESS_DETACH: if(g_hLogFile != INVALID_HANDLE_VALUE) { CloseHandle(g_hLogFile); } break; case DLL_THREAD_ATTACH: // 记录线程创建 if(g_hLogFile != INVALID_HANDLE_VALUE) { DWORD bytesWritten; WriteFile(g_hLogFile, "Thread created\n", 15, &bytesWritten, NULL); } break; case DLL_THREAD_DETACH: // 记录线程退出 if(g_hLogFile != INVALID_HANDLE_VALUE) { DWORD bytesWritten; WriteFile(g_hLogFile, "Thread exited\n", 14, &bytesWritten, NULL); } break; } return TRUE; } |
声明:本文为原创文章,版权归Aet所有,欢迎分享本文,转载请保留出处!
你可能也喜欢
- ♥ Windbg:命令了解学习一08/19
- ♥ Windows创建进程实例:权限及子进程监控相关10/19
- ♥ 各平台调试方法总结记述一09/25
- ♥ Windows 核心编程 _ 进程三06/19
- ♥ COM组件_207/22
- ♥ 关于多字节和宽字节二12/04