本文章相关视频教程下载地址http://pan.baidu.com/s/1slK2Hat
对于可执行文件,其入口函数为WinMain(),对于动态库文件,其入口函数为DllMain()。当系统加载动态库文件时,就会调用该文件的DllMain()函数。
1 DllMain()函数的格式
在VS2015中创建一个Win32动态库项目,项目名为“DLL_Variant_Test”。此时,在“解决方案资源管理器”的“源文件”中包含了3个源文件:DLL_Variant_Test.cpp、dllmain.cpp和stdafx.cpp。可以在DLL_Variant_Test.cpp中定义动态库的导出函数,而动态库的入口函数在dllmain.cpp中。
dllmain()函数的格式为
BOOL APIENTRY DllMain( HMODULE hModule,
DWORD ul_reason_for_call,
LPVOID lpReserved
)
其中,APIENTRY的定义为
#define APIENTRY WINAPI
表示DllMain()是Windows API函数。第一个参数hModule表示动态库模块的句柄,当动态库第一次被加载时,它的句柄会通过此参数传递到dllMain()函数中;ul_reason_for_call表示调用该动态库入口函数的原因;lpReserved是保留参数。
2 DllMain()函数的默认定义
在DllMain()函数的默认定义中,通过switch/case语句对其第二个参数ul_reason_for_call进行判断,该参数的取值及含义如表1所示。
取值 | 含义 |
DLL_PROCESS_ATTACH | 进程第一次加载动态库并调用DllMain函数 |
DLL_PROCESS_DETACH | 进程结束 |
DLL_THREAD_ATTACH | 当前进程正创建一个新线程 |
DLL_THREAD_DETACH | 线程结束 |
3 DllMain()函数的调用时机
通过输出信息来验证DllMain()函数的调用时机。
3.1 在入口函数中添加输出信息
在DLL_Variant_Test项目的dllmain.cpp中添加头文件
#include <iostream>
之后将dllMain()函数修改为如下形式
BOOL APIENTRY DllMain( HMODULE hModule,
DWORD ul_reason_for_call,
LPVOID lpReserved
)
{
switch (ul_reason_for_call)
{
case DLL_PROCESS_ATTACH:
std::cout << "DLL_PROCESS_ATTACH.\n";
break;
case DLL_THREAD_ATTACH:
std::cout << "DLL_THREAD_ATTACH.\n";
break;
case DLL_THREAD_DETACH:
std::cout << "DLL_THREAD_DETACH.\n";
break;
case DLL_PROCESS_DETACH:
std::cout << "DLL_PROCESS_DETACH.\n";
break;
}
return TRUE;
}
对于调用动态库的不同原因,会输出不同的信息。
3.2 创建用来调用动态库的程序
创建一个Win32控制台程序,在该控制台程序中,使用LoadLibrary()函数动态加载在“3.1”中生成的动态库,再加载完动态库后,通过CreateThread()函数创建一个线程,等待线程结束后,再使用FreeLibrary()函数释放该动态库。代码如下所示
#include "stdafx.h"
#include <Windows.h>
#include <iostream>
using namespace std;
DWORD WINAPI MyThread(LPVOID lpParameter)
{
return 0;
}
int main()
{
HINSTANCE hinstLib;
HANDLE myThread_Handle;
hinstLib = LoadLibrary(_T("DLL_Variant_Test.dll"));
myThread_Handle = CreateThread(NULL, 0, (LPTHREAD_START_ROUTINE)MyThread, NULL, 0, NULL);
WaitForSingleObject(myThread_Handle, INFINITE);
FreeLibrary(hinstLib);
return 0;
}
程序运行效果如图1所示。
图1 程序运行效果
当调用LoadLibrary()函数时,动态库的dllMain()函数被调用,此时ul_reason_for_call的值是DLL_PROCESS_ATTACH;当调用CreateThread()函数创建线程时,ul_reason_for_call的值是DLL_THREAD_ATTACH;使用WaitForSingleObject()函数等待创建的线程结束时,ul_reason_for_call的值是DLL_THREAD_DETACH;最后调用FreeLibrary()函数释放动态库时,ul_reason_for_call的值是DLL_PROCESS_DETACH。