当前位置: 首页 > news >正文

libusb源码学习:几个函数加载的宏(windows)

首先,我们复习一下C语言中的函数指针:

//C语言中的函数指针
#include <iostream>

// 下面typeFunc就一个函数指针,指向的函数类型 f 为
// void f(void)
typedef void (*typeFunc)(void);

void realFunc(void) {
	std::cout << "test" << std::endl;
}

int main() {
	typeFunc myf = realFunc;
	myf(); // 相当于调用realFunc();
	return 0;
}

然后我们看一下libusb中定义的那几个宏


/*
 * Macros for handling functions within a DLL
 */
#define DLL_FUNC_NAME(name) __dll_##name##_func_t

#define DLL_DECLARE_FUNC_PREFIXNAME(api, ret, prefixname, name, args)	\
	typedef ret (api * DLL_FUNC_NAME(name))args;			\
	static DLL_FUNC_NAME(name) prefixname = NULL

#define DLL_DECLARE_FUNC(api, ret, name, args)				\
	DLL_DECLARE_FUNC_PREFIXNAME(api, ret, name, name, args)
#define DLL_DECLARE_FUNC_PREFIXED(api, ret, prefix, name, args)		\
	DLL_DECLARE_FUNC_PREFIXNAME(api, ret, prefix##name, name, args)

#define DLL_LOAD_FUNC_PREFIXNAME(dll, prefixname, name, ret_on_failure)	\
	do {								\
		HMODULE h = DLL_HANDLE_NAME(dll);			\
		prefixname = (DLL_FUNC_NAME(name))GetProcAddress(h,	\
				DLL_STRINGIFY(name));			\
		if (prefixname)						\
			break;						\
		prefixname = (DLL_FUNC_NAME(name))GetProcAddress(h,	\
				DLL_STRINGIFY(name) DLL_STRINGIFY(A));	\
		if (prefixname)						\
			break;						\
		prefixname = (DLL_FUNC_NAME(name))GetProcAddress(h,	\
				DLL_STRINGIFY(name) DLL_STRINGIFY(W));	\
		if (prefixname)						\
			break;						\
		if (ret_on_failure)					\
			return FALSE;					\
	} while (0)

#define DLL_LOAD_FUNC(dll, name, ret_on_failure)			\
	DLL_LOAD_FUNC_PREFIXNAME(dll, name, name, ret_on_failure)
#define DLL_LOAD_FUNC_PREFIXED(dll, prefix, name, ret_on_failure)	\
	DLL_LOAD_FUNC_PREFIXNAME(dll, prefix##name, name, ret_on_failure)

我们发现,本来调用的函数名称如:SetupDiGetClassDevsA,

现在统统都加上了指针前缀,变成:pSetupDiGetClassDevsA

这些函数是在windows_usb.h中定义的,展开其中的一个定义,过程如下,

DLL_DECLARE_FUNC_PREFIXED(WINAPI, HDEVINFO, p, SetupDiGetClassDevsA, (LPCGUID, PCSTR, HWND, DWORD));
<==等价于==>
DLL_DECLARE_FUNC_PREFIXNAME(WINAPI, HDEVINFO, pSetupDiGetClassDevsA, SetupDiGetClassDevsA, (LPCGUID, PCSTR, HWND, DWORD))
<==等价于==>
typedef HDEVINFO (WINAPI * __dll_SetupDiGetClassDevsA_func_t)(LPCGUID, PCSTR, HWND, DWORD);
static __dll_SetupDiGetClassDevsA_func_t pSetupDiGetClassDevsA = NULL

就是说,
pSetupDiGetClassDevsA相当于定义了一个函数指针,类型为__dll_SetupDiGetClassDevsA_func_t的变量:

问题是,__dll_SetupDiGetClassDevsA_func_t这个函数指针类型是在哪里定义的呢?

static BOOL init_dlls(void)
{
	DLL_GET_HANDLE(Cfgmgr32);
	DLL_LOAD_FUNC(Cfgmgr32, CM_Get_Parent, TRUE);
	DLL_LOAD_FUNC(Cfgmgr32, CM_Get_Child, TRUE);

	// Prefixed to avoid conflict with header files
	DLL_GET_HANDLE(AdvAPI32);
	DLL_LOAD_FUNC_PREFIXED(AdvAPI32, p, RegQueryValueExW, TRUE);
	DLL_LOAD_FUNC_PREFIXED(AdvAPI32, p, RegCloseKey, TRUE);

	DLL_GET_HANDLE(OLE32);
	DLL_LOAD_FUNC_PREFIXED(OLE32, p, IIDFromString, TRUE);

	DLL_GET_HANDLE(SetupAPI);
	DLL_LOAD_FUNC_PREFIXED(SetupAPI, p, SetupDiGetClassDevsA, TRUE);
	DLL_LOAD_FUNC_PREFIXED(SetupAPI, p, SetupDiEnumDeviceInfo, TRUE);
	DLL_LOAD_FUNC_PREFIXED(SetupAPI, p, SetupDiEnumDeviceInterfaces, TRUE);
	DLL_LOAD_FUNC_PREFIXED(SetupAPI, p, SetupDiGetDeviceInstanceIdA, TRUE);
	DLL_LOAD_FUNC_PREFIXED(SetupAPI, p, SetupDiGetDeviceInterfaceDetailA, TRUE);
	DLL_LOAD_FUNC_PREFIXED(SetupAPI, p, SetupDiGetDeviceRegistryPropertyA, TRUE);
	DLL_LOAD_FUNC_PREFIXED(SetupAPI, p, SetupDiDestroyDeviceInfoList, TRUE);
	DLL_LOAD_FUNC_PREFIXED(SetupAPI, p, SetupDiOpenDevRegKey, TRUE);
	DLL_LOAD_FUNC_PREFIXED(SetupAPI, p, SetupDiOpenDeviceInterfaceRegKey, TRUE);

	return TRUE;
}

其中,DLL_GET_HANLE相当于LoadLibraryA(),

#define DLL_GET_HANDLE(name)					\
	do {							\
		DLL_HANDLE_NAME(name) = DLL_LOAD_LIBRARY(name);	\
		if (!DLL_HANDLE_NAME(name))			\
			return FALSE;				\
	} while (0)


//API macros - leveraged from libusb-win32 1.x
#ifndef _WIN32_WCE
#define DLL_STRINGIFY(s) #s  //把传入的名称字符串化:名称变成字符串)
#define DLL_LOAD_LIBRARY(name) LoadLibraryA(DLL_STRINGIFY(name))
#else
#define DLL_STRINGIFY(s) L#s
#define DLL_LOAD_LIBRARY(name) LoadLibrary(DLL_STRINGIFY(name))
#endif

核心是这个DLL_LOAD_FUNC_PREFIXED的定义;在这里定义了类型为__dll_SetupDiGetClassDevsA_func_t的变量pSetupDiGetClassDevsA,并且其函数地址为GetProcAddress(h, "SetupDiGetClassDevsA");

其相应的宏展开过程如下所示,

DLL_LOAD_FUNC_PREFIXED(SetupAPI, p, SetupDiGetClassDevsA, TRUE);
<==eqivalent to ==>
DLL_LOAD_FUNC_PREFIXNAME(SetupAPI, pSetupDiGetClassDevsA, SetupDiGetClassDevsA, TRUE)	\
	do {
		HMODULE h = DLL_HANDLE_NAME(SetupAPI);
		pSetupDiGetClassDevsA = (DLL_FUNC_NAME(SetupDiGetClassDevsA))GetProcAddress(h,
				DLL_STRINGIFY(SetupDiGetClassDevsA));
		if (pSetupDiGetClassDevsA)
			break;
		pSetupDiGetClassDevsA = (DLL_FUNC_NAME(SetupDiGetClassDevsA))GetProcAddress(h,
				DLL_STRINGIFY(SetupDiGetClassDevsA) DLL_STRINGIFY(A));
		if (pSetupDiGetClassDevsA)
			break;
		pSetupDiGetClassDevsA = (DLL_FUNC_NAME(SetupDiGetClassDevsA))GetProcAddress(h,
				DLL_STRINGIFY(SetupDiGetClassDevsA) DLL_STRINGIFY(W));
		if (pSetupDiGetClassDevsA)
			break;
		if (TRUE)
			return FALSE;
	} while (0)

 

相关文章:

  • MCU_如何通过硬件VID 查找生产厂家
  • MCU_WireShark USB抓包内容解析
  • MCU_Wireshark USB 抓包过滤(抓特定端口地址)
  • STM32F4xx usb库源码详解 custom HID
  • STM32F4xx usb库源码详解:HAL_PCDEx_SetRxFiFo 和 HAL_PCDEx_SetTxFiFo
  • Libuv 1.34.2 源码详解 ---- 以uvCat为例讲解
  • 步进电机的细分驱动中1-2相, W1-2相, 2W1-2相, 4W1-2相 表示什么意思?
  • MCU_关于STM32Fxxx中断EXTI产生时多次(两次)进入中断的原因
  • MCU_通过windows串口API控制RTS和DTR
  • MCU_STM32的HAL库中的宏DMA_FLAG_TCIF0_4/DMA_FLAG_TCIF1_5/DMA_FLAG_TCIF2_6/DMA_FLAG_TCIF3_7
  • LWIP_TCP如何理解数据发送,何时使用tcp_recved函数
  • MCU_使用STM32CUBEMX配置STM32F107/407 RMII-ETHERNET要注意的细节:PHY Address和MCO时钟
  • MCU_STM32CUBEMX v5.5.0的一个BUG:ethernetif_input引起进入HardFault_Handler
  • MCU_STM32CUBEMX配置生成CAN2的初始化代码的修改
  • MCU_STM32F4xx使用CCM RAM
  • hadoop集群管理系统搭建规划说明
  • HTTP 简介
  • javascript 总结(常用工具类的封装)
  • mysql 5.6 原生Online DDL解析
  • Promise面试题2实现异步串行执行
  • Python代码面试必读 - Data Structures and Algorithms in Python
  • Webpack入门之遇到的那些坑,系列示例Demo
  • 复杂数据处理
  • 计算机常识 - 收藏集 - 掘金
  • 简单数学运算程序(不定期更新)
  • 解决iview多表头动态更改列元素发生的错误
  • 开源中国专访:Chameleon原理首发,其它跨多端统一框架都是假的?
  • 驱动程序原理
  • 吐槽Javascript系列二:数组中的splice和slice方法
  • 推荐一款sublime text 3 支持JSX和es201x 代码格式化的插件
  • 微信小程序填坑清单
  • 赢得Docker挑战最佳实践
  • RDS-Mysql 物理备份恢复到本地数据库上
  • ​ 轻量应用服务器:亚马逊云科技打造全球领先的云计算解决方案
  • ​​​​​​​sokit v1.3抓手机应用socket数据包: Socket是传输控制层协议,WebSocket是应用层协议。
  • ​【原创】基于SSM的酒店预约管理系统(酒店管理系统毕业设计)
  • ​DB-Engines 12月数据库排名: PostgreSQL有望获得「2020年度数据库」荣誉?
  • ​无人机石油管道巡检方案新亮点:灵活准确又高效
  • #我与虚拟机的故事#连载20:周志明虚拟机第 3 版:到底值不值得买?
  • (1)(1.11) SiK Radio v2(一)
  • (20050108)又读《平凡的世界》
  • (LNMP) How To Install Linux, nginx, MySQL, PHP
  • (TOJ2804)Even? Odd?
  • (第27天)Oracle 数据泵转换分区表
  • (二)学习JVM —— 垃圾回收机制
  • (黑马C++)L06 重载与继承
  • (四)库存超卖案例实战——优化redis分布式锁
  • (一)硬件制作--从零开始自制linux掌上电脑(F1C200S) <嵌入式项目>
  • (原創) 如何優化ThinkPad X61開機速度? (NB) (ThinkPad) (X61) (OS) (Windows)
  • (转)EOS中账户、钱包和密钥的关系
  • (转)EXC_BREAKPOINT僵尸错误
  • (转)Windows2003安全设置/维护
  • (转载)深入super,看Python如何解决钻石继承难题
  • (最优化理论与方法)第二章最优化所需基础知识-第三节:重要凸集举例
  • .mat 文件的加载与创建 矩阵变图像? ∈ Matlab 使用笔记