通过设备接口打开设备的步骤
- 1、下载文档前请自行甄别文档内容的完整性,平台不提供额外的编辑、内容补充、找答案等附加服务。
- 2、"仅部分预览"的文档,不可在线预览部分如存在完整性等问题,可反馈申请退款(可完整预览的文档不适用该条件!)。
- 3、如文档侵犯您的权益,请联系客服反馈,我们会尽快为您处理(人工客服工作时间:9:00-18:30)。
通过设备接口打开设备的步骤
一、驱动程序
1.驱动程序框架的创建
(1)用VC建立一个新工程。
在VC IDE环境中选择File|New,弹出New对话框。
在对话框中,选择Project选项卡。
在Project选项卡中,选择Win32 Application 。
设置工程名为OpenGuid.如图1所示,单击OK,进入下一个对话框,在对话框中选择一个空的工程。
如图2。
图1
图2
(2).新建两个文件GuidOpen.h与GuidOpen.cpp.这两个文件的具体写法,详见程序编写。
也能够直接添加现成的已经写好的文件,张帆这本书中,通常都是用的HelloWDM.h与HelloWDM.cpp.
(3).增加新的编译版本,去掉Debug与Release版本。
在Build | Configuration如图3与图4。
图3
图4
(4).修改工程属性。
选择Project|Setting,在弹出的对话框中,选择General选项卡,将Intermediate files 与Output files改为MyDriver_Check,这个名字英语C/C++中,所设置的Fo与Fd后面的文件名相一致。
如图5。
图5
将C/C++选项卡中,原有的Project Options内容全部删掉,换成一下内容。
/nologo /Gz /MLd /W3 /WX /Z7 /Od /D WIN32=100 /D_X86_=1 /D WINVER=0x500 /D DBG=1
/Fo"MyDriver_Check/" /Fd"MyDriver_Check/" /FD /c
其中:
/nologo:表示不显示编译的版本信息
/Gz:默认函数调用使用标准调用(_stdcall)
/MLd
/W3:使用第三级警告模式
/WX:将警告信息转换为错误信息,最大程度保证代码可靠
/Z7:用Z7模式产生调试信息?
/Od:关闭调试模式,VC的调试命令不能调试内核下的程序
/D WIN32=100 /D_X86_=1 /D WINVER=0x500 /D DBG=1:定义4个宏(不明白为什么)
/Fo"MyDriver_Check/:MyDriver_Check/为Output Directories中“创建”的文件夹,存放中间生成的目标代码路径
/Fd"MyDriver_Check/": MyDriver_Check/为存放.PDB文件的文件夹
/FD:生成文件依奈
/c:只进行编译,不连接
图6
选择Link选项卡,将原有的Project Options 内容全部删除,替换成如下内容:
wdm.lib /nologo /base:"0x10000" /stack:0x400000,0x1000 /entry:"DriverEntry" /subsystem:console
/incremental:no /pdb:"MyDriver_Check/GuidOpen.pdb" /debug /machine:I386 /nodefaultlib
/out:"MyDriver_Check/GuiOpen.sys" /pdbtype:sept /subsystem:native /driver /SECTION:INIT,D /IGNORE:4078 其中:
wdm.lib:链接WDM库
/nologo:链接时不显示版本信息
/base:"0x10000":加载驱动时,设定加载到虚拟内存的地址
/stack:0x400000,0x1000:设定函数使用堆栈的地址与大小
/entry:"DriverEntry":入口函数的地址(为符合标准函数调用的)
/subsystem:console:设置子系统
/incremental:no:非递曾式链接
/pdb:"MyDriver_Check/GuidOpen.pdb":设置pdb文件的文件名为GuidOpen,储存于MyDriver_Check文件夹下面C/C++属性页中的设置一样。
/debug:以Debug方式链接
/machine:I386:产生代码为386兼容的平台下的
/nodefaultlib:不使用默认的库
/out:"MyDriver_Check/GuidOpen.sys":输出2进制的代码的文件名,储存于MyDriver_Check文件夹下与C/C++属性页中的设置一样。
/pdbtype:sept:设置pdb文件的类型
/subsystem:native:子系统为内核系统
/driver:编译驱动
/SECTION:INIT,D:将INIT的段设置为可抛弃的
/IGNORE:4078:忽略4078号警告错误
图7
(5).修改VC的lib目录与include 目录。
Tools->Options->Directories属性页下的
Show directories for
切换到Includefie
添加DDK的头文件(安装的ddk的目录文件夹)\Inc\w2k
(ddk的目录文件夹)\Inc\ddk\wdm\w2k
置于最上面
添加库文件(安装的ddk的目录文件夹)\lib\w2k\i386
置于最上面
2.驱动程序说明
(1)重要驱动程序中重要的数据结构
驱动对象(DRIVER_OBJECT)在驱动加载时被内核中的对象管理程序所创建,由内核中的I/O管理器负责加载。
typedef struct _DRIVER_OBJECT{
CSHORT Type;
CSHORT Size;
PDEVICE_OBJECT DeviceObject;
ULONG Flags;
PVOID DriverStart;
ULONG DriverSize;
PVOID DriverSection;
PDRIVER_EXTENSION DriverExtension;
UNICODE_STRING DriverName;
PUNICODE_STRING HardwareDatabase;
PFAST_IO_DISPATCH FastIoDispatch;
PDRIVER_INITIALIZE DriverInit;
PDRIVER_STARTIO DriverStartIo;
PDRIVER_UNLOAD DriverUnload;
PDRIVER_DISPATCH MajorFunction[IRP_MJ_MAXIMUM_FUNCTION+1];
} DRIVER_OBJECT;
typedef struct _DRIVER_OBJECT *PDRIVER_OBJECT;
DeviceObject:每个驱动程序会有一个或者多个设备对象。
设备对象是由程序员自己创建的,而非操作系统完成,在驱动被卸载时,遍历每个设备对象,并将其删除。
设备对象(DEVICE_OBJECT)
typedef struct _DEVICE_OBJECT{
struct _DRIVER_OBJECT *DriverObject;
struct _DEVICE_OBJECT *NextDevice;
struct _DEVICE_OBJECT *AttachedDevice;
struct _IRP *CurrentIrp;
ULONG Flags;
struct _DEVOBJ_EXTENSION *DeviceObjectExtension; ......................
} DEVICE_OBJECT;
typedef struct _DEVICE_OBJECT *PDEVICE_OBJECT;//ntndis
设备扩展是由程序员制定内容与大小,由I/O管理器创建的,同时储存在非分页内存中。
(2)WDM驱动程序基本结构,在WDM驱动程序中,完成一个设备操作,至少需要两个设备对象共同完成,一个是物理设备对象(Physical Device Object)PDO与功能设备对象(Function Device Object)FDO。
当PC插入一个设备时,PDO会自动创建。
确切的说是由总线驱动创建的,PDO不能单独操作设备,需要配合FDO一起使用。
系统会检测到新设备,要求安装驱动程序,需要安装的驱动程序指的就是WDM程序,此驱动程序负责创建FDO,同时附加到PDO上。
(3)驱动程序分析
头文件中,除了生命函数之外,还要定义一个设备扩展。
typedef struct _DEVICE_EXTENSION
{
PDEVICE_OBJECT fdo;
PDEVICE_OBJECT NextStackDevice;
UNICODE_STRING interfaceName; //设备接口
} DEVICE_EXTENSION, *PDEVICE_EXTENSION;
当驱动程序被加载时,首先进入DriverEntry函数。
DriverEntry要紧是对驱动程序进行初始化,它是由系统进程所调用的,在Windows中有个特殊的进程叫做系统进程,打开进程管理器,里面有一个名为System的进程就是系统进程。
系统进程在系统启动的时候就被创建了。
驱动加载时,系统进程启动新的线程,调用执行体组建中的对象管理器,创建一个驱动对象。
这个驱动对象是一个DRIVER_OBJECT的结构体。
另外,系统进程调用执行体组建中的配置管理器程序,查询词驱动程序对应的注册表项。
DriverEntry函数由两个参数PDRIVER_OBJECT pDeriverObject 是刚才被创建的驱动对象的指针,与PUNICODE_STRING pRegistryPath,设备服务键的的键名字符串的指针。
在这个函数中,要紧是对系统进程创建的驱动对象进行初始化。
DriverEntry函数中,它对驱动对象的初始化通常是对例程的设置,卸载例程,AddDevice与IRP 派遣函数。
具体代码如下:
pDriverObject->DriverExtension->AddDevice = GuidOpenAddDevice;
pDriverObject->MajorFunction[IRP_MJ_PNP] = GuidOpenPnp;
pDriverObject->MajorFunction[IRP_MJ_DEVICE_CONTROL] =
pDriverObject->MajorFunction[IRP_MJ_CREATE] =
pDriverObject->MajorFunction[IRP_MJ_CLOSE] =
pDriverObject->MajorFunction[IRP_MJ_READ] =
pDriverObject->MajorFunction[IRP_MJ_WRITE] = GuidOpenDispatchRoutine;
pDriverObject->DriverUnload = GuidOpenUnload;
另外,设备服务键的键名有的时候候需要储存下来,由于这个字符串不是长期存在的,假如以后想使用这个UNICODE 字符串,就务必先把它复制到安全的地方,这个字符串的内容通常是
\REGISTRY\MACHINE\SYSTEM\ControlSet\Service\[服务名].DriverEntry的返回值是NTSTATUS,是被定义的为32位的无符号长整形。
0~0X7FFFFFFF被认为是正确的。
而0X80000000~0XFFFFFFFF被认为是错误的。
接着驱动程序进入GuidOpenAddDevice例程。
它的要紧任务是创建设备对象(功能设备对象)并将其附加到PDO之上。
它有两个参数PDRIVER_OBJECT DriverObject,驱动对象与PDEVICE_OBJECT PhysicalDeviceObject 设备对象,就是底层总线驱动创建的PDO对象。
创建设备对象,用IoCreatDevice(
IN PDRIVER_OBJECT DriverObject,//系统创建的驱动对象
IN ULONG DeviceExtensionSize,//程序员自己定义的(在头文件中)设备扩展的大小
IN PUNICODE_STRING DeviceName OPTIONAL,//设备名,能够为空,如今,I/O管理器会自动以一个数字作为该设备对象的名称
IN DEVICE_TYPE DeviceType,//设备类型
IN ULONG DeviceCharacteristics,//对设备的进一步描述
IN BOOLEAN Exclusize,//是否一次只能进行一次IRP处理
OUT PDEVICE_OBJECT *DeviceObject)//新创建的设备对象
具体代码如下
PDEVICE_OBJECT fdo;
status = IoCreateDevice(
DriverObject,
sizeof(DEVICE_EXTENSION),
NULL,//没有指定设备名
FILE_DEVICE_UNKNOWN,
0,
FALSE,
&fdo);
如今,功能设备对象,创建完毕,接着是将,功能设备对象FDO附加到物理设备对象上PDO。
利用函数PDEVICE_OBJECT IoAttachDeviceToDeviceStack(
IN PDEVICE_OBJECT SourceDevice,//新创建的功能设备对象FDO
IN PDEVICE_OBJECT TargetDevice//物理设备对象PDO
);
该函数调用成功的话返回一个设备对象,附加设备对象的设备对象,比如在这里,返回的是PDO,假如,The returned device object pointer can differ from TargetDevice if TargetDevice had additional drivers layered on top of it.假如该函数运行失败,则返回NULL。
具体程序,我们将该函数返回的设备对象,储存在,设备扩展中,这样一来,我们就需要先得到新创建的功能设备对象的设备扩展。
//得到设备扩展
PDEVICE_EXTENSION pdx = (PDEVICE_EXTENSION)fdo->DeviceExtension;
pdx->fdo = fdo;
//将FDO附加到PDO上
pdx->NextStackDevice = IoAttachDeviceToDeviceStack(fdo, PhysicalDeviceObject);
WDM驱动程序,设备名无法被用户模式下的应用程序查询到,应用程序能够通过符号链接,设备名或者
者是设备接口来访问设备。
本文只介绍设备接口方式。
设备接口就是一组全局标志,他是一个128位构成的数字,并能保证在全世界范围内不可能冲突。
VC中有一个创建GUID的工具,叫guidgen.exe,它在D:\Program Files\Microsoft Visual Studio\Common\Tools中,运行它,它为用户提供了四种方式产生guid,事实上它们都是128位的,只是输出的形式不一致而已,通常选择第二种,单击New GUID会产生新的的guid,单击Copy将这个guid复制到新建的guid.h头文件中。
DEFINE_GUID(<<name>>,
0x5dada759, 0xde9a, 0x45e2, 0x8f, 0xb4, 0x1a, 0xa8, 0x8b, 0x1d, 0xe7, 0x8);
需要将《name》换成自己为这个接口而起的名字,比如MY_WDM_DEVICE.另外需要注意,在创建guid 时,程序中应该包含头文件#include <initguid.h>,其中initguid.h只能在一个.cpp中包含,DEFINE_GUID ()宏定义了,假如包含了一个initguid.h,那么定义一个guid, 假如没有包含一个initguid.h ,则定义一个extern guid指向定义的那个guid, 因此项目中务必有一个文件.cpp包含initguid.h,否则会出错。
但假如包含了多个的initguid.h,也会出错否则会出现错误。
unresolved external symbol _MY_WDM_DEVICE
创建设备接口用函数NTSTATUS
IoRegisterDeviceInterface(
IN PDEVICE_OBJECT PhysicalDeviceObject,
IN CONST GUID *InterfaceClassGuid,
IN PUNICODE_STRING ReferenceString OPTIONAL,
OUT PUNICODE_STRING SymbolicLinkName//将GUID输出一串UNICODE字符串
);
具体代码创建设备接口
status = IoRegisterDeviceInterface(PhysicalDeviceObject, &MY_WDM_DEVICE, NULL,
&pdx->interfaceName); 其中pdx->interfaceName就是暴露给应用程序的符号链接。
包含四部分如图8
图8
(1)何种总线设备,比如ROOT
(2)类设备的名称,LIUYOUJINDEVICE
(3)这种设备的第几个设备#0000
(4)制定的设备接口GUID。
设置接口
IoSetDeviceInterfaceState(&pdx->interfaceName, TRUE);
//设置操作模式
fdo->Flags |= DO_BUFFERED_IO | DO_POWER_PAGABLE;
fdo->Flags &= ~DO_DEVICE_INITIALIZING;
实现即插即用
即插即用IRP即IRP_MJ_PNP,它通常是由即插即用管理器发送给WDM驱动程序的。
不一致情况下,即插即用管理器会发送不一致子类型的IRP_MJ_PNP IRP。
在IRP_MJ_PNP派遣函数中要处理不一致子功能代码的IRP,本程序使用函数指针的方法。
首先初始化一个函数指针构成的数组,然后在派遣函数中推断是那种子功能代码。
根据这个子功能代码区寻找行的函数指针,再通过指针找到针对具体子功能代码所作的操作函数。
加载驱动时,所用到的各个IRP_MJ_PNP子功能代码。
1.953 Default Enter DefaultPnpHandler
11.953 Default Leave DefaultPnpHandler
11.953 Default Enter GuidOpenPnp
11.953 Default PNP Request (IRP_MN_FILTER_RESOURCE_REQUIREMENTS)修改I/O资源需求列表
11.953 Default Enter DefaultPnpHandler
11.953 Default Leave DefaultPnpHandler
11.953 Default Leave GuidOpenPnp
11.953 Default Enter GuidOpenPnp
11.953 Default PNP Request (IRP_MN_START_DEVICE)配置并初始化设备
11.953 Default Enter HandleStartDevice
11.953 Default Enter DefaultPnpHandler
11.953 Default Leave DefaultPnpHandler
11.953 Default Leave HandleStartDevice
11.953 Default Leave GuidOpenPnp
11.953 Default Enter GuidOpenPnp
11.953 Default PNP Request (IRP_MN_QUERY_CAPABILITIES)取设备能力
11.953 Default Enter DefaultPnpHandler
11.953 Default Leave DefaultPnpHandler
11.953 Default Leave GuidOpenPnp
11.953 Default Enter GuidOpenPnp
11.953 Default PNP Request (IRP_MN_QUERY_PNP_DEVICE_STATE)取设备状态
11.953 Default Enter DefaultPnpHandler
11.953 Default Leave DefaultPnpHandler
11.953 Default Leave GuidOpenPnp
11.953 Default Enter GuidOpenPnp
11.953 Default PNP Request (IRP_MN_QUERY_DEVICE_RELATIONS)给出与制定特征有关的设备列表
11.953 Default Enter DefaultPnpHandler
11.953 Default Leave DefaultPnpHandler
11.953 Default Leave GuidOpenPnp
对IRP_MN_DEVICE的处理
3.应用程序说明
创建一个对话框类型的驱动程序框架。
(1)首先需要将驱动程序中的guid.h文件copy到应用程序中,同时添加到应用程序的工程里。
(2)应用程序的Porject|Setting|Link的Object/library modules里要添加setupapi.lib库,否则会有类似这样的错误:GuidOpen_AppDlg.obj :unresolved external symbol __imp__SetupDiGetDeviceInterfaceDetailA@24 GuidOpen_AppDlg.obj : error LNK2001: unresolved external symbol __imp__SetupDiDestroyDeviceInfoList@4 GuidOpen_AppDlg.obj : error LNK2001: unresolved external symbol
__imp__SetupDiEnumDeviceInterfaces@20
GuidOpen_AppDlg.obj : error LNK2001: unresolved external symbol __imp__SetupDiGetClassDevsA@16
程序中应该包含setupapi.h头文件,否则,会出现这样的错误:
error C2065: 'HDEVINFO' : undeclared identifier
error C2146: syntax error : missing ';' before identifier 'info'
error C2065: 'info' : undeclared identifier
error C2065: 'SetupDiGetClassDevs' : undeclared identifier
error C2065: 'DIGCF_PRESENT' : undeclared identifier
error C2065: 'DIGCF_INTERFACEDEVICE' : undeclared identifier ...............................................................................................................................
同时还务必包含的一个头文件是#include <initguid.h>
打开设备的代码如下:
void CGuidOpen_AppDlg::OnOPENDEVICE()
{
// TODO: Add your control notification handler code here
HANDLE hDevice = GetDeviceViaInterface((LPGUID)&MY_WDM_DEVICE,0);
if (hDevice == INVALID_HANDLE_VALUE)
{
MessageBox("打开设备出错!");
// return 1;
}
MessageBox("设备打开成功");
CloseHandle(hDevice);
// return 0;
}
HANDLE CGuidOpen_AppDlg::GetDeviceViaInterface(GUID *pGuid, DWORD instance)
{
//获得类信息
HDEVINFO info = SetupDiGetClassDevs(pGuid, NULL, NULL, DIGCF_PRESENT |
DIGCF_INTERFACEDEVICE);
if(info==INVALID_HANDLE_VALUE)
{
MessageBox("No HDEVINFO available for this GUID");
return NULL;
}
// Get interface data for the requested instance
SP_INTERFACE_DEVICE_DATA ifdata;
ifdata.cbSize = sizeof(ifdata);
if(!SetupDiEnumDeviceInterfaces(info, NULL, pGuid, instance, &ifdata))
{
MessageBox("No SP_INTERFACE_DEVICE_DATA available for this GUID instance");
SetupDiDestroyDeviceInfoList(info);
return NULL;
}
// Get size of symbolic link name
DWORD ReqLen;
SetupDiGetDeviceInterfaceDetail(info, &ifdata, NULL, 0, &ReqLen, NULL);
PSP_INTERFACE_DEVICE_DETAIL_DATA ifDetail =
(PSP_INTERFACE_DEVICE_DETAIL_DATA)(new char[ReqLen]);
if( ifDetail==NULL)
{
SetupDiDestroyDeviceInfoList(info);
return NULL;
}
// Get symbolic link name
ifDetail->cbSize = sizeof(SP_INTERFACE_DEVICE_DETAIL_DATA);
if( !SetupDiGetDeviceInterfaceDetail(info, &ifdata, ifDetail, ReqLen, NULL, NULL))
{
SetupDiDestroyDeviceInfoList(info);
delete ifDetail;
return NULL;
}
// printf("Symbolic link is %s\n",ifDetail->DevicePath);
// Open file
HANDLE rv = CreateFile( ifDetail->DevicePath,
GENERIC_READ | GENERIC_WRITE,
FILE_SHARE_READ | FILE_SHARE_WRITE,
NULL, OPEN_EXISTING, FILE_ATTRIBUTE_NORMAL, NULL);
if( rv==INVALID_HANDLE_VALUE) rv = NULL;
delete ifDetail;
SetupDiDestroyDeviceInfoList(info);
return rv;
}
函数解析:
WINSETUPAPI BOOL WINAPI
SetupDiGetDeviceInterfaceDetail(
IN HDEVINFO DeviceInfoSet,
IN PSP_DEVICE_INTERFACE_DATA DeviceInterfaceData,
OUT PSP_DEVICE_INTERFACE_DETAIL_DATA DeviceInterfaceDetailData..OPTIONAL,
IN DWORD DeviceInterfaceDetailDataSize,
OUT PDWORD RequiredSize..OPTIONAL,
OUT PSP_DEVINFO_DATA DeviceInfoData OPTIONAL
);
参数
DeviceInfoSet
指向设备信息集的指针,它包含了所要接收信息的接口。
该句柄通常由SetupDiGetClassDevs函数返回。
DeviceInterfaceData
一个指向SP_DEVICE_INTERFACE_DATA结构的指针,该结构指定了DeviceInfoSet 参数中设备的接口。
这个类型的指针通常由SetupDiEnumDeviceInterfaces 函数返回。
DeviceInterfaceDetailData
一个指向SP_DEVICE_INTERFACE_DETAIL_DATA结构的指针,该结构用于接收指定接口的信息。
该参数是可选的且能够为NULL。
假如DeviceInterfaceDetailSize 参数为0,该参数务必为NULL。
假如该参数被指定,主调者务必在调用该函数之前,设置SP_DEVICE_INTERFACE_DETAIL_DATA 结构的cbSize 成员为sizeof(SP_DEVICE_INTERFACE_DETAIL_DATA)。
cbSize 成员总是包含数据结构的固定部分的长度。
DeviceInterfaceDetailDataSize
DeviceInterfaceDetailData 参数指定的缓冲的大小。
该缓冲的大小不能小于
(offsetof(SP_DEVICE_INTERFACE_DETAIL_DATA, DevicePath) + sizeof(TCHAR)) 字节。
假如DeviceInterfaceDetailData 参数为NULL,该参数务必为0.
RequiredSize
一个指向变量的指针,该变量接收请求的DeviceInterfaceDetailData 缓冲的大小。
这个大小包含了结构的固定部分的大小再加上设备路径字符串的长度。
该参数是可选的,也能够是NULL。
DeviceInfoData
一个指向缓冲的指针,该缓冲接收关于支持请求的接口的设备的信息。
主调者务必设置DeviceInfoData.cbSize 成员为sizeof(SP_DEVINFO_DATA)。
该参数是可选的,也能够为NULL。
返回值
假如函数顺利完成,则返回TRUE,假如有错误,则返回FALSE。
SetupDiGetDeviceInterfaceDetail函数用来传回另外一个与前一个函数所识别的接口有关的结构InterfaceClassGuid。
包含设备的路径InterfaceClassGuid->DevicePath成员是一个设备路径。
它能够作为应用程序打开设备时CreateFile的第一个参数。
值得注意的是:第一次调用该函数时,其中的DeviceInterfaceDetailDataSize无法预知。
故能够两次调用。
第一次获取长度,第二次获取正确值。
WINSETUPAPI BOOL WINAPI
SetupDiEnumDeviceInterfaces(
IN HDEVINFO DeviceInfoSet,//指向设备信息集的指针,它包含了所要接收信息的接口。
该句柄通常由SetupDiGetClassDevs函数返回。
IN PSP_DEVINFO_DATA DeviceInfoData, OPTIONAL
IN LPGUID InterfaceClassGuid, //指向GUID的指针
IN DWORD MemberIndex,
OUT PSP_DEVICE_INTERFACE_DATA DeviceInterfaceData//The caller must set DeviceInterfaceData.cbSize to sizeof(SP_DEVICE_INTERFACE_DATA) before calling this function. ); SetupDiEnumDeviceInterfaces函数用来读取识别一个接口的结构指针,每一次调用务必传第一个数组的索引来指定一个接口
HDEVINFO
SetupDiGetClassDevs(
IN LPGUID ClassGuid, OPTIONAL
IN PCTSTR Enumerator, OPTIONAL
IN HWND hwndParent, OPTIONAL
IN DWORD Flags
);
输入参数:
PGUID ClassGuid
在创建设备列表的时候提供一个指向GUID的指针。
假如设定了标志DIGCF_ALLCLASSES,则这个参数能够忽略,且列表结果中包含所有已经安装的设备类别。
PCTSTR Enumerator
提供包含设备实例的枚举注册表分支下的键名,能够通过它获取设备信息。
假如这个参数没有指定,则要从整个枚举树中获取所有设备实例的设备信息。
HWND hwndParent
提供顶级窗口的句柄,所有用户接口能够使用它来与成员联系。
DWORD Flags
提供在设备信息结构中使用的操纵选项。
能够是下列数值:
DIGCF_PRESENT - 只返回当前存在的设备。
DIGCF_ALLCLASSES - 返回所有已安装的设备。
假如这个标志设置了,ClassGuid参数将被忽略。
DIGCF_PROFILE - 只返回当前硬件配置文件中的设备。
编辑本段返回值
HDEVINFO
假如函数运行成功,返回设备信息结构的句柄,该结构包含与指定参数匹配的所有已安装设备。
假如失败,则返回INVALID_HANDLE_VALUE。
调用GetLastError能够获得更多错误信息。
编辑本段说明
使用此函数,需要包含头文件setupapi.h。
此外,在project setting中的link页面需要添加setupapi.lib。
在setuapi.h中有如下定义:
typedef PVOID HDEVINFO;
即HDEVINFO是个无类型指针
也就是这个函数能返回一个包含某个设备集合信息的一个指针。
这里的handle暂且这么懂得。
用SetupDiGetClassDevs得到的这个handle,最后记得要调用SetupDiDestroyDeviceInfoList来删除。
The caller of this function must delete the returned device information set when it is no longer needed by calling SetupDiDestroyDeviceInfoList。