9.DLL的入口函数DllMain函数

版权声明:转载请注明出处: https://blog.csdn.net/qq_33757398/article/details/82230360

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)为空

猜你喜欢

转载自blog.csdn.net/qq_33757398/article/details/82230360
今日推荐