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)
#endif
extern "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)为空