Windows设备树及设备信息管理接口
Windows下的所有设备都会挂接在其设备树上,设备树由PNP管理器来维护。如一个设备树如下图所示:
设备树上每个节点代表一个设备,凡是有子节点的节点是一个总线设备,其负责枚举下子节点设备,这样进行层层枚举,形成一棵设备树。其中根节点由PNP管理器创建,其用于挂载ACPI子节点。ACPI是一个抽象的设备,用于枚举系统中根总线与设备,是距离CPU最近的设备。
ACPI表示高级配置和电源管理接口(Advanced Configuration and Power Management Interface)
我们知道,一个物理设备安装完驱动后,其分类有两种方法:
- 第一种是按设备类型归类,如磁盘存储类,音频类,视频类。如设备管理器就是默认按设备类型显示设备的。
- 第二种是按接口类型,如USB接口类
我们可以通过Windows驱动预定义设备GUID来查看一下关于这两类分类的GUID,一类对应的是设备的ClassGuid,另一个是设备的InterfaceGuid。
这里说一下为什么会用接口类型GUID呢?
这里有以下几个原因:
- 设备名的命名因人喜好不同,也容易冲突,导出也麻烦
- 使用接口GUID,可以做为一个标准,凡是所有设备同类设备都可以注册该GUID
- 系统按指定格式方便对设备实例名的统一
- 可以实现一个驱动对应多个硬件设备的驱动,这样不用考虑命名的问题
在SetupApi函数中,所以根据设备的分类不同,也会出现两大类函数集,分别为 device setup classes 和 device interface classes函数集。
对于使用了分类的设备,其会根据其ClassGuid创建设备信息集合链表,形成如下设备链表:
设备信息集合使用SetupDiCreateDeviceInfoList函数来创建集合链表,而对应的设备信息的相关函数有SetupDiOpenDeviceInfo。
- HDEVINFO 集合
- SP_DEVINFO_DATA
- SP_DEVINFO_DATA
- SP_DEVINFO_DATA
- SP_DEVINFO_DATA
如某一驱动中的INF文件中ClassGuid内容如下:
[Version]
Signature="$WINDOWS NT$"
Class=System
ClassGuid={4D36E97D-E325-11CE-BFC1-08002BE10318}
可以通通过如下获取设备信息:
DEFINE_GUID(GUID_SYSTEM,
0x4D36E97D, 0xE325, 0x11CE, 0xbf, 0xc1, 0x08, 0x00, 0x2b, 0xe1, 0x03, 0x18);
static BOOL
is_exist_device(drv_info_t *pinfo)
{
HDEVINFO hdevinfoset;
SP_DEVINFO_DATA devinfo;
BOOL exist = FALSE;
//hdevinfoset = SetupDiCreateDeviceInfoList(&GUID_SYSTEM, NULL);
hdevinfoset = SetupDiCreateDeviceInfoList(NULL, NULL);
if (hdevinfoset == INVALID_HANDLE_VALUE) {
return FALSE;
}
memset(&devinfo, 0, sizeof(SP_DEVINFO_DATA));
devinfo.cbSize = sizeof(SP_DEVINFO_DATA);
if (SetupDiOpenDeviceInfo(hdevinfoset, pinfo->device_id, NULL, 0, &devinfo))
exist = TRUE;
SetupDiDestroyDeviceInfoList(hdevinfoset);
return exist;
}
而对于以接口的方式的相关函数,可以通过SetupDiEnumDeviceInterfaces获取SP_INTERFACE_DEVICE_DATA。
SetupAPI的定位
SetupAPI是一个被用来执行安装设备的一系列操作的方法的集合,主要用于安装设备驱动(device driver),被用在类安装程序(class installers)、协安装程序(co-installers)和设备安装应用程序(device installation applications)中。
下图描绘了设备安装过程中的组件,也包括SetupAPI在其中所扮演的角色.