通过设备接口打开设备详细步骤
一、驱动程序
1.驱动程序框架的创建
(1)用VC建立一个新工程。在VCIDE环境中选择File|New,弹出New对话框。在对话框中,选
择Project选项卡。在Project选项卡中,选择Win32Application。设置工程名为OpenGuid.如图1所示,
单击OK,进入下一个对话框,在对话框中选择一个空的工程。如图2。
图1
图2
(2).新建两个文件GuidOpen.h和.这两个文件的具体写法,详见程序编写。也能够直截了
当添加现成的差不多写好的文件,张帆这本书中,一样差不多上用的HelloWDM.h和.
(3).增加新的编译版本,去掉Debug和Relea版本。在Build|Configuration如图3和图4。
图3
图4
(4).修改工程属性。选择Project|Setting,在弹出的对话框中,选择General选项卡,将Intermediatefiles
和Outputfiles改为MyDriver_Check,那个名字英语C/C++中,所设置的Fo和Fd后面的文件名相一致。
如图5。
图5
将C/C++选项卡中,原有的ProjectOptions内容全部删掉,换成一下内容。
/nologo/Gz/MLd/W3/WX/Z7/Od/DWIN32=100/D_X86_=1/DWINVER=0x500/DDBG=1
/Fo"MyDriver_Check/"/Fd"MyDriver_Check/"/FD/c
其中:
/nologo:表示不显示编译的版本信息
/Gz:默认函数调用采纳标准调用(_stdcall)
/MLd
/W3:采纳第三级警告模式
/WX:将警告信息转换为错误信息,最大程度保证代码可靠
/Z7:用Z7模式产生调试信息?
/Od:关闭调试模式,VC的调试命令不能调试内核下的程序
/DWIN32=100/D_X86_=1/DWINVER=0x500/DDBG=1:定义4个宏(不明白什么缘故)
/Fo"MyDriver_Check/:MyDriver_Check/为OutputDirectories中“创建”的文件夹,存放中间生成的目标代码
路径
/Fd"MyDriver_Check/":MyDriver_Check/为存放.PDB文件的文件夹
/FD:生成文件依奈
/c:只进行编译,不连接
图6
选择Link选项卡,将原有的ProjectOptions内容全部删除,替换成如下内容:
/nologo/ba:"0x10000"/stack:0x400000,0x1000/entry:"DriverEntry"/subsystem:console
/incremental:no/pdb:"MyDriver_Check/"/debug/machine:I386/nodefaultlib
/out:"MyDriver_Check/"/pdbtype:pt/subsystem:native/driver/SECTION:INIT,D/IGNORE:4078
其中:
:链接WDM库
/nologo:链接时不显示版本信息
/ba:"0x10000":加载驱动时,设定加载到虚拟内存的地址
/stack:0x400000,0x1000:设定函数使用堆栈的地址与大小
/entry:"DriverEntry":入口函数的地址(为符合标准函数调用的)
/subsystem:console:设置子系统
/incremental:no:非递曾式链接
/pdb:"MyDriver_Check/":设置pdb文件的文件名为GuidOpen,储存于MyDriver_Check文件
夹下面C/C++属性页中的设置一样。
/debug:以Debug方式链接
/machine:I386:产生代码为386兼容的平台下的
/nodefaultlib:不使用默认的库
/out:"MyDriver_Check/":输出2进制的代码的文件名,储存于MyDriver_Check文件夹下与
C/C++属性页中的设置一样。
/pdbtype:pt:设置pdb文件的类型
/subsystem:native:子系统为内核系统
/driver:编译驱动
/SECTION:INIT,D:将INIT的段设置为可抛弃的
/IGNORE:4078:忽略4078号警告错误
图7
(5).修改VC的lib名目和include名目。
Tools->Options->Directories属性页下的
Showdirectoriesfor
切换到Includefie
添加DDK的头文件(安装的ddk的名目文件夹)\Inc\w2k
(ddk的名目文件夹)\Inc\ddkwdmw2k
置于最上面
添加库文件(安装的ddk的名目文件夹)libw2ki386
置于最上面
2.驱动程序说明
(1)重要驱动程序中重要的数据结构
驱动对象(DRIVER_OBJECT)在驱动加载时被内核中的对象治理程序所创建,由内核中的I/O治理器负
责加载。
typedefstruct_DRIVER_OBJECT{
CSHORTType;
CSHORTSize;
PDEVICE_OBJECTDeviceObject;
ULONGFlags;
PVOIDDriverStart;
ULONGDriverSize;
PVOIDDriverSection;
PDRIVER_EXTENSIONDriverExtension;
UNICODE_STRINGDriverName;
PUNICODE_STRINGHardwareDataba;
PFAST_IO_DISPATCHFastIoDispatch;
PDRIVER_INITIALIZEDriverInit;
PDRIVER_STARTIODriverStartIo;
PDRIVER_UNLOADDriverUnload;
PDRIVER_DISPATCHMajorFunction[IRP_MJ_MAXIMUM_FUNCTION+1];
}DRIVER_OBJECT;
typedefstruct_DRIVER_OBJECT*PDRIVER_OBJECT;
DeviceObject:每个驱动程序会有一个或多个设备对象。设备对象是由程序员自己创建的,而非操作系统完
成,在驱动被卸载时,遍历每个设备对象,并将其删除。
设备对象(DEVICE_OBJECT)
typedefstruct_DEVICE_OBJECT{
struct_DRIVER_OBJECT*DriverObject;
struct_DEVICE_OBJECT*NextDevice;
struct_DEVICE_OBJECT*AttachedDevice;
struct_IRP*CurrentIrp;
ULONGFlags;
struct_DEVOBJ_EXTENSION*DeviceObjectExtension;
......................
}DEVICE_OBJECT;
typedefstruct_DEVICE_OBJECT*PDEVICE_OBJECT;//ntndis
设备扩展是由程序员制定内容和大小,由I/O治理器创建的,同时储存在非分页内存中。
(2)WDM驱动程序差不多结构,在WDM驱动程序中,完成一个设备操作,至少需要两个设备对象共同
完成,一个是物理设备对象(PhysicalDeviceObject)PDO和功能设备对象(FunctionDeviceObject)FDO。
当PC插入一个设备时,PDO会自动创建。确切的说是由总线驱动创建的,PDO不能单独操作设备,需要
配合FDO一起使用。系统会检测到新设备,要求安装驱动程序,需要安装的驱动程序指的确实是WDM
程序,此驱动程序负责创建FDO,同时附加到PDO上。
(3)驱动程序分析
头文件中,除了生命函数之外,还要定义一个设备扩展。
typedefstruct_DEVICE_EXTENSION
{
PDEVICE_OBJECTfdo;
PDEVICE_OBJECTNextStackDevice;
UNICODE_STRINGinterfaceName;//设备接口
}DEVICE_EXTENSION,*PDEVICE_EXTENSION;
当驱动程序被加载时,第一进入DriverEntry函数。DriverEntry要紧是对驱动程序进行初始化,它
是由系统进程所调用的,在Windows中有个专门的进程叫做系统进程,打开进程治理器,里面有一个名为
System的进程确实是系统进程。系统进程在系统启动的时候就被创建了。驱动加载时,系统进程启动新的
线程,调用执行体组建中的对象治理器,创建一个驱动对象。那个驱动对象是一个DRIVER_OBJECT的结
构体。另外,系统进程调用执行体组建中的配置治理器程序,查询词驱动程序对应的注册表项。
DriverEntry函数由两个参数PDRIVER_OBJECTpDeriverObject是刚才被创建的驱动对象的指针,和
PUNICODE_STRINGpRegistryPath,设备服务键的的键名字符串的指针。在那个函数中,要紧是对系统进程
创建的驱动对象进行初始化。
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字符串,就必须先把它复制到安全的地点,那个字符串的内容一样是
REGISTRYMACHINESYSTEMControlSetService[服务名].DriverEntry的返回值是NTSTATUS,是被定
义的为32位的无符号长整形。0~0X7FFFFFFF被认为是正确的。而0X80000000~0XFFFFFFFF被认为是错
误的。
接着驱动程序进入GuidOpenAddDevice例程。它的要紧任务是创建设备对象(功能设备对象)并
将其附加到PDO之上。它有两个参数PDRIVER_OBJECTDriverObject,驱动对象和PDEVICE_OBJECT
PhysicalDeviceObject设备对象,确实是底层总线驱动创建的PDO对象。
创建设备对象,用IoCreatDevice(
INPDRIVER_OBJECTDriverObject,//系统创建的驱动对象
INULONGDeviceExtensionSize,//程序员自己定义的(在头文件中)设备扩展的大小
INPUNICODE_STRINGDeviceNameOPTIONAL,//设备名,能够为空,现在,I/O治理器会自动以一
个数字作为该设备对象的名称
INDEVICE_TYPEDeviceType,//设备类型
INULONGDeviceCharacteristics,//对设备的进一步描述
INBOOLEANExclusize,//是否一次只能进行一次IRP处理
OUTPDEVICE_OBJECT*DeviceObject)//新创建的设备对象
具体代码如下
PDEVICE_OBJECTfdo;
status=IoCreateDevice(
DriverObject,
sizeof(DEVICE_EXTENSION),
NULL,//没有指定设备名
FILE_DEVICE_UNKNOWN,
0,
FALSE,
&fdo);
现在,功能设备对象,创建完毕,接着是将,功能设备对象FDO附加到物理设备对象上PDO。利用函数
PDEVICE_OBJECTIoAttachDeviceToDeviceStack(
INPDEVICE_OBJECTSourceDevice,//新创建的功能设备对象FDO
INPDEVICE_OBJECTTargetDevice//物理设备对象PDO
);
该函数调用成功的话返回一个设备对象,附加设备对象的设备对象,例如在那个地点,返回的是PDO,假
如,ThereturneddeviceobjectpointercandifferfromTargetDeviceifTargetDevicehadadditionaldrivers
layeredontopofit.假如该函数运行失败,则返回NULL。
具体程序,我们将该函数返回的设备对象,储存在,设备扩展中,如此一来,我们就需要先得到新创建的
功能设备对象的设备扩展。
//得到设备扩展
PDEVICE_EXTENSIONpdx=(PDEVICE_EXTENSION)fdo->DeviceExtension;
pdx->fdo=fdo;
//将FDO附加到PDO上
pdx->NextStackDevice=IoAttachDeviceToDeviceStack(fdo,PhysicalDeviceObject);
WDM驱动程序,设备名无法被用户模式下的应用程序查询到,应用程序能够通过符号链接,设备名或者
是设备接口来访问设备。本文只介绍设备接口方式。设备接口确实是一组全局标志,他是一个128位组成
的数字,并能保证在全世界范畴内可不能冲突。VC中有一个创建GUID的工具,叫,它在
D:ProgramFilesMicrosoftVisualStudioCommonTools中,运行它,它为用户提供了四种方式产生guid,
事实上它们差不多上128位的,只是输出的形式不同而已,一样选择第二种,单击NewGUID会产生新的
的guid,单击Copy将那个guid复制到新建的guid.h头文件中。
DEFINE_GUID(<
0x5dada759,0xde9a,0x45e2,0x8f,0xb4,0x1a,0xa8,0x8b,0x1d,0xe7,0x8);
需要将《name》换成自己为那个接口而起的名字,例如MY_WDM_DEVICE.另外需要注意,在创建guid
时,程序中应该包含头文件#include
()宏定义了,假如包含了一个initguid.h,那么定义一个guid,假如没有包含一个initguid.h,则定义一个
externguid指向定义的那个guid,因此项目中必须有一个文件.cpp包含initguid.h,否则会出错。但假如包含
了多个的initguid.h,也会出错否则会显现错误。
unresolvedexternalsymbol_MY_WDM_DEVICE
创建设备接口用函数NTSTATUS
IoRegisterDeviceInterface(
INPDEVICE_OBJECTPhysicalDeviceObject,
INCONSTGUID*InterfaceClassGuid,
INPUNICODE_STRINGReferenceStringOPTIONAL,
OUTPUNICODE_STRINGSymbolicLinkName//将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_PNPIRP。在IRP_MJ_PNP派遣函数中要处理不同子功
能代码的IRP,本程序采纳函数指针的方法。第一初始化一个函数指针组成的数组,然后在派遣函数中判
定是那种子功能代码。依照那个子功能代码区查找行的函数指针,再通过指针找到针对具体子功能代码所
作的操作函数。加载驱动时,所用到的各个IRP_MJ_PNP子功能代码。
1.953DefaultEnterDefaultPnpHandler
11.953DefaultLeaveDefaultPnpHandler
11.953DefaultEnterGuidOpenPnp
11.953DefaultPNPRequest(IRP_MN_FILTER_RESOURCE_REQUIREMENTS)修改I/O资源需求
列表
11.953DefaultEnterDefaultPnpHandler
11.953DefaultLeaveDefaultPnpHandler
11.953DefaultLeaveGuidOpenPnp
11.953DefaultEnterGuidOpenPnp
11.953DefaultPNPRequest(IRP_MN_START_DEVICE)配置并初始化设备
11.953DefaultEnterHandleStartDevice
11.953DefaultEnterDefaultPnpHandler
11.953DefaultLeaveDefaultPnpHandler
11.953DefaultLeaveHandleStartDevice
11.953DefaultLeaveGuidOpenPnp
11.953DefaultEnterGuidOpenPnp
11.953DefaultPNPRequest(IRP_MN_QUERY_CAPABILITIES)取设备能力
11.953DefaultEnterDefaultPnpHandler
11.953DefaultLeaveDefaultPnpHandler
11.953DefaultLeaveGuidOpenPnp
11.953DefaultEnterGuidOpenPnp
11.953DefaultPNPRequest(IRP_MN_QUERY_PNP_DEVICE_STATE)取设备状态
11.953DefaultEnterDefaultPnpHandler
11.953DefaultLeaveDefaultPnpHandler
11.953DefaultLeaveGuidOpenPnp
11.953DefaultEnterGuidOpenPnp
11.953DefaultPNPRequest(IRP_MN_QUERY_DEVICE_RELATIONS)给出与制定特点相关的设
备列表
11.953DefaultEnterDefaultPnpHandler
11.953DefaultLeaveDefaultPnpHandler
11.953DefaultLeaveGuidOpenPnp
对IRP_MN_DEVICE的处理
3.应用程序说明
创建一个对话框类型的驱动程序框架。
(1)第一需要将驱动程序中的guid.h文件copy到应用程序中,同时添加到应用程序的工程里。
(2)应用程序的Porject|Setting|Link的Object/librarymodules里要添加库,否则会有类似如此
的错误:GuidOpen_:unresolvedexternalsymbol__imp__SetupDiGetDeviceInterfaceDetailA@24
GuidOpen_:errorLNK2001:unresolvedexternalsymbol__imp__SetupDiDestroyDeviceInfoList@4
GuidOpen_:errorLNK2001:unresolvedexternalsymbol
__imp__SetupDiEnumDeviceInterfaces@20
GuidOpen_:errorLNK2001:unresolvedexternalsymbol__imp__SetupDiGetClassDevsA@16
程序中应该包含tupapi.h头文件,否则,会显现如此的错误:
errorC2065:'HDEVINFO':undeclaredidentifier
errorC2146:syntaxerror:missing';'beforeidentifier'info'
errorC2065:'info':undeclaredidentifier
errorC2065:'SetupDiGetClassDevs':undeclaredidentifier
errorC2065:'DIGCF_PRESENT':undeclaredidentifier
errorC2065:'DIGCF_INTERFACEDEVICE':undeclaredidentifier
...............................................................................................................................
同时还必须包含的一个头文件是#include
打开设备的代码如下:
voidCGuidOpen_AppDlg::OnOPENDEVICE()
{
//TODO:Addyourcontrolnotificationhandlercodehere
HANDLEhDevice=GetDeviceViaInterface((LPGUID)&MY_WDM_DEVICE,0);
if(hDevice==INVALID_HANDLE_VALUE)
{
MessageBox("打开设备出错!");
//return1;
}
MessageBox("设备打开成功");
CloHandle(hDevice);
//return0;
}
HANDLECGuidOpen_AppDlg::GetDeviceViaInterface(GUID*pGuid,DWORDinstance)
{
//获得类信息
HDEVINFOinfo=SetupDiGetClassDevs(pGuid,NULL,NULL,DIGCF_PRESENT|
DIGCF_INTERFACEDEVICE);
if(info==INVALID_HANDLE_VALUE)
{
MessageBox("NoHDEVINFOavailableforthisGUID");
returnNULL;
}
//Getinterfacedatafortherequestedinstance
SP_INTERFACE_DEVICE_DATAifdata;
=sizeof(ifdata);
if(!SetupDiEnumDeviceInterfaces(info,NULL,pGuid,instance,&ifdata))
{
MessageBox("NoSP_INTERFACE_DEVICE_DATAavailableforthisGUIDinstance");
SetupDiDestroyDeviceInfoList(info);
returnNULL;
}
//Getsizeofsymboliclinkname
DWORDReqLen;
SetupDiGetDeviceInterfaceDetail(info,&ifdata,NULL,0,&ReqLen,NULL);
PSP_INTERFACE_DEVICE_DETAIL_DATAifDetail=
(PSP_INTERFACE_DEVICE_DETAIL_DATA)(newchar[ReqLen]);
if(ifDetail==NULL)
{
SetupDiDestroyDeviceInfoList(info);
returnNULL;
}
//Getsymboliclinkname
ifDetail->cbSize=sizeof(SP_INTERFACE_DEVICE_DETAIL_DATA);
if(!SetupDiGetDeviceInterfaceDetail(info,&ifdata,ifDetail,ReqLen,NULL,NULL))
{
SetupDiDestroyDeviceInfoList(info);
deleteifDetail;
returnNULL;
}
//printf("Symboliclinkis%sn",ifDetail->DevicePath);
//Openfile
HANDLErv=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;
deleteifDetail;
SetupDiDestroyDeviceInfoList(info);
returnrv;
}
函数解析:
WINSETUPAPIBOOLWINAPI
SetupDiGetDeviceInterfaceDetail(
INHDEVINFODeviceInfoSet,
INPSP_DEVICE_INTERFACE_DATADeviceInterfaceData,
OUTPSP_DEVICE_INTERFACE_DETAIL_DATADeviceInterfaceDetailData..OPTIONAL,
INDWORDDeviceInterfaceDetailDataSize,
OUTPDWORDRequiredSize..OPTIONAL,
OUTPSP_DEVINFO_DATADeviceInfoDataOPTIONAL
);
参数
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参数指定的缓冲的大小。该缓冲的大小不能小于
(offtof(SP_DEVICE_INTERFACE_DETAIL_DATA,DevicePath)+sizeof(TCHAR))字节。
假如DeviceInterfaceDetailData参数为NULL,该参数必须为0.
RequiredSize
一个指向变量的指针,该变量接收要求的DeviceInterfaceDetailData缓冲的大小。那个大小包含了结构的
固定部分的大小再加上设备路径字符串的长度。该参数是可选的,也能够是NULL。
DeviceInfoData
一个指向缓冲的指针,该缓冲接收关于支持要求的接口的设备的信息。主调者必须设置
成员为sizeof(SP_DEVINFO_DATA)。该参数是可选的,也能够为NULL。
返回值
假如函数顺利完成,则返回TRUE,假如有错误,则返回FALSE。
SetupDiGetDeviceInterfaceDetail函数用来传回另外一个与前一个函数所识别的接口有关的结构
InterfaceClassGuid。包括设备的路径InterfaceClassGuid->DevicePath成员是一个设备路径。它能够作为应
用程序打开设备时CreateFile的第一个参数。值得注意的是:第一次调用该函数时,其中的
DeviceInterfaceDetailDataSize无法预知。故能够两次调用。第一次猎取长度,第二次猎取正确值。
WINSETUPAPIBOOLWINAPI
SetupDiEnumDeviceInterfaces(
INHDEVINFODeviceInfoSet,//指向设备信息集的指针,它包含了所要接收信息的接口。该句柄通常
由SetupDiGetClassDevs函数返回。
INPSP_DEVINFO_DATADeviceInfoData,OPTIONAL
INLPGUIDInterfaceClassGuid,//指向GUID的指针
INDWORDMemberIndex,
OUTPSP_DEVICE_INTERFACE_DATADeviceInterfaceData//Thecallermustt
tosizeof(SP_DEVICE_INTERFACE_DATA)beforecallingthisfunction.);
SetupDiEnumDeviceInterfaces函数用来读取识别一个接口的结构指针,每一次调用必须传第一个数组的索
引来指定一个接口
HDEVINFO
SetupDiGetClassDevs(
INLPGUIDClassGuid,OPTIONAL
INPCTSTREnumerator,OPTIONAL
INHWNDhwndParent,OPTIONAL
INDWORDFlags
);
输入参数:
PGUIDClassGuid
在创建设备列表的时候提供一个指向GUID的指针。假如设定了标志DIGCF_ALLCLASSES,则那个参数
能够忽略,且列表结果中包括所有差不多安装的设备类别。
PCTSTREnumerator
提供包含设备实例的枚举注册表分支下的键名,能够通过它猎取设备信息。假如那个参数没有指定,则要
从整个枚举树中猎取所有设备实例的设备信息。
HWNDhwndParent
提供顶级窗口的句柄,所有用户接口能够使用它来与成员联系。
DWORDFlags
提供在设备信息结构中使用的操纵选项。能够是以下数值:
DIGCF_PRESENT-只返回当前存在的设备。
DIGCF_ALLCLASSES-返回所有已安装的设备。假如那个标志设置了,ClassGuid参数将被忽略。
DIGCF_PROFILE-只返回当前硬件配置文件中的设备。
编辑本段返回值
HDEVINFO
假如函数运行成功,返回设备信息结构的句柄,该结构包含与指定参数匹配的所有已安装设备。假如失败,
则返回INVALID_HANDLE_VALUE。调用GetLastError能够获得更多错误信息。
编辑本段说明
使用此函数,需要包含头文件tupapi.h。
此外,在projecttting中的link页面需要添加。
在tuapi.h中有如下定义:
typedefPVOIDHDEVINFO;
即HDEVINFO是个无类型指针
也确实是那个函数能返回一个包含某个设备集合信息的一个指针。那个地点的handle暂且这么明白得。
用SetupDiGetClassDevs得到的那个handle,最后记得要调用SetupDiDestroyDeviceInfoList来删除。
Thecallerofthisfunctionmustdeletethereturneddeviceinformationtwhenitisnolongerneededbycalling
SetupDiDestroyDeviceInfoList
本文发布于:2023-01-01 18:56:56,感谢您对本站的认可!
本文链接:http://www.wtabcd.cn/fanwen/fan/90/73723.html
版权声明:本站内容均来自互联网,仅供演示用,请勿用于商业和其他非法用途。如果侵犯了您的权益请与我们联系,我们将在24小时内删除。
留言与评论(共有 0 条评论) |