概述
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所有,欢迎分享本文,转载请保留出处!
你可能也喜欢
- ♥ Windows 核心编程 _ 用户模式:线程同步三07/23
- ♥ X86_64汇编学习记述四08/09
- ♥ Soui应用 动画一06/24
- ♥ Windows Dll自卸载相关10/19
- ♥ 包管理器:各平台安装卸载相关记述09/17
- ♥ COM组件_207/22