9.DLL的入口函数DllMain函数

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

好的,以下是一个简单的 `hid.dll` 导出函数的示例代码,供参考: ```c #include <Windows.h> #include <hidsdi.h> BOOL WINAPI DllMain(HINSTANCE hinstDLL, DWORD fdwReason, LPVOID lpvReserved) { switch (fdwReason) { case DLL_PROCESS_ATTACH: // DLL 初始化 break; case DLL_PROCESS_DETACH: // DLL 卸载 break; case DLL_THREAD_ATTACH: // 新线程创建 break; case DLL_THREAD_DETACH: // 线程结束 break; } return TRUE; } // 导出函数:获取 HID 设备信息 extern "C" __declspec(dllexport) BOOL GetHidDeviceInfo( DWORD dwVendorId, // 设备厂商 ID DWORD dwProductId, // 设备产品 ID PHIDD_ATTRIBUTES pHidAttributes // 输出设备属性结构体 ) { BOOL bResult = FALSE; HDEVINFO hDeviceInfo = NULL; // 枚举所有 HID 设备 hDeviceInfo = SetupDiGetClassDevs(&GUID_DEVINTERFACE_HID, NULL, NULL, DIGCF_PRESENT | DIGCF_DEVICEINTERFACE); if (hDeviceInfo == INVALID_HANDLE_VALUE) { return FALSE; } // 遍历所有 HID 设备 DWORD dwIndex = 0; SP_DEVICE_INTERFACE_DATA deviceInterfaceData = { 0 }; deviceInterfaceData.cbSize = sizeof(SP_DEVICE_INTERFACE_DATA); while (SetupDiEnumDeviceInterfaces(hDeviceInfo, NULL, &GUID_DEVINTERFACE_HID, dwIndex, &deviceInterfaceData)) { // 获取设备接口数据 DWORD dwRequiredSize = 0; SetupDiGetDeviceInterfaceDetail(hDeviceInfo, &deviceInterfaceData, NULL, 0, &dwRequiredSize, NULL); if (dwRequiredSize == 0) { break; } PSP_DEVICE_INTERFACE_DETAIL_DATA pDeviceInterfaceDetailData = (PSP_DEVICE_INTERFACE_DETAIL_DATA)LocalAlloc(LPTR, dwRequiredSize); if (pDeviceInterfaceDetailData == NULL) { break; } pDeviceInterfaceDetailData->cbSize = sizeof(SP_DEVICE_INTERFACE_DETAIL_DATA); if (SetupDiGetDeviceInterfaceDetail(hDeviceInfo, &deviceInterfaceData, pDeviceInterfaceDetailData, dwRequiredSize, NULL, NULL)) { // 打开设备 HANDLE hDevice = CreateFile(pDeviceInterfaceDetailData->DevicePath, GENERIC_READ | GENERIC_WRITE, FILE_SHARE_READ | FILE_SHARE_WRITE, NULL, OPEN_EXISTING, 0, NULL); if (hDevice != INVALID_HANDLE_VALUE) { // 获取设备属性 HIDD_ATTRIBUTES hidAttributes = { 0 }; hidAttributes.Size = sizeof(HIDD_ATTRIBUTES); if (HidD_GetAttributes(hDevice, &hidAttributes)) { // 比较设备厂商 ID 和产品 ID if (hidAttributes.VendorID == dwVendorId && hidAttributes.ProductID == dwProductId) { // 输出设备属性 CopyMemory(pHidAttributes, &hidAttributes, sizeof(HIDD_ATTRIBUTES)); bResult = TRUE; CloseHandle(hDevice); break; } } CloseHandle(hDevice); } } LocalFree(pDeviceInterfaceDetailData); ++dwIndex; } SetupDiDestroyDeviceInfoList(hDeviceInfo); return bResult; } ``` 此示例代码中,我们定义了一个名为 `GetHidDeviceInfo` 的函数,该函数可以用来获取指定厂商 ID 和产品 ID 的 HID 设备的属性信息。我们将该函数作为 `hid.dll` 的导出函数,供其他程序调用。 需要注意的是,在 `DllMain` 函数中,我们可以进行 DLL 的初始化和卸载工作,例如打开和关闭一些资源,但是我们需要避免在此函数中进行太多的操作,以免影响系统的性能和稳定性。
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

深山里的小白羊

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值