使用Qt调用windows api中的setupapi.h库中的SetupDiGetDeviceRegistryProperty和SetupDiGetDeviceProperty函数获取设备管理器中的设备详细信息中的属性值,包括设备实例路径,硬件id,驱动inf名称,驱动版本,显示名称,类名,供应商,设备描述等属性值。
目录导读
- 实现效果
- 相关函数的说明
- SetupDiGetDeviceRegistryProperty 函数
- 封装SetupDiGetDeviceRegistryProperty 函数调用
- 调用示例 :
- SetupDiGetDeviceProperty 函数
- 封装SetupDiGetDeviceProperty 函数调用
- 调用示例:
- 常见DEVPROPTYPE数据类型处理
- DEVPROPTYPE数据类型说明:
- PBYTE类型数据转DEVPROPTYPE数据类型相关结构
- DEVPROP_TYPE_GUID 数据类型处理
- DEVPROP_TYPE_STRING数据类型处理
- DEVPROP_TYPE_STRING_LIST数据类型处理
- DEVPROP_TYPE_FILETIME数据类型处理
实现效果
根据左侧选择的设备管理器,设备实例路径
获取设备大部分DEFINE_DEVPROPKEY属性对应的业务值
并显示到右侧文本框中。
相关函数的说明
涉及到以下函数的使用:
SetupDiGetDeviceRegistryProperty 函数检索指定的即插即用设备属性。
语法:
//! 如果调用成功,SetupDiGetDeviceRegistryProperty 将返回 TRUE。 否则,它将返回 FALSE ,并且可以通过调用 GetLastError 来检索记录的错误。 如果设备不存在请求的属性或属性数据无效,SetupDiGetDeviceRegistryProperty 将返回ERROR_INVALID_DATA错误代码。
WINSETUPAPI BOOL SetupDiGetDeviceRegistryPropertyW([in] HDEVINFO DeviceInfoSet, //设备信息集的句柄,其中包含表示要为其检索即插即用属性的设备的设备信息元素。[in] PSP_DEVINFO_DATA DeviceInfoData, //指向 SP_DEVINFO_DATA 结构的指针,该结构指定 DeviceInfoSet 中的设备信息元素。[in] DWORD Property, //指定要检索的属性[out, optional] PDWORD PropertyRegDataType, //指向接收所检索属性的数据类型的变量的指针。 这是标准注册表数据类型之一。 此参数是可选的,可以为 NULL。[out, optional] PBYTE PropertyBuffer, //指向接收正在检索的属性的缓冲区的指针。 如果此参数设置为 NULL,并且 PropertyBufferSize 也设置为零,则该函数将返回 RequiredSize 中缓冲区的所需大小。[in] DWORD PropertyBufferSize, //PropertyBuffer 缓冲区的大小(以字节为单位)。[out, optional] PDWORD RequiredSize //指向类型为 DWORD 的变量的指针,该变量接收保存所请求属性的数据所需的 PropertyBuffer 缓冲区的所需大小(以字节为单位)。 此参数是可选的,可以为 NULL。
);
使方法通用,主要也是为了避免分配内存不足导致的异常的问题。
方法中的DeviceInfoSet和DeviceInfoData都是SetupDiGetClassDevs和SetupDiEnumDeviceInfo获取的值,详见专栏上一篇。
bool GetSetupDiGetDeviceRegistryProperty(HDEVINFO DeviceInfoSet,PSP_DEVINFO_DATA DeviceInfoData,DWORD Property,PBYTE& PropertyBuffer,DWORD& requiredSize) const
{bool result =SetupDiGetDeviceRegistryProperty(DeviceInfoSet,DeviceInfoData,Property,0,PropertyBuffer,requiredSize,&requiredSize);if(!result && GetLastError()==ERROR_INSUFFICIENT_BUFFER){PropertyBuffer=(PBYTE)malloc(requiredSize);result = SetupDiGetDeviceRegistryProperty(DeviceInfoSet,DeviceInfoData,Property,0,PropertyBuffer,requiredSize,NULL);}if(!result)qDebug()<<"Function SetupDiGetDeviceRegistryProperty Execution failure!";return result;
}
获取SPDRP_FRIENDLYNAME:
检索包含设备的友好名称的REG_SZ字符串
PBYTE FriendlyName=(PBYTE)new WCHAR[MAX_PATH];if(GetSetupDiGetDeviceRegistryProperty(DeviceInfoSet, DeviceInfoData,SPDRP_FRIENDLYNAME,FriendlyName, requiredSize))qDebug()<<"SPDRP_FRIENDLYNAME: "<<QString::fromWCharArray((WCHAR*)FriendlyName);
获取SPDRP_UPPERFILTERS:
检索包含设备上层筛选器驱动程序名称的REG_MULTI_SZ字符串。
PBYTE buffer=new BYTE [MAX_PATH];
if(GetSetupDiGetDeviceRegistryProperty(deviceInfoSet,&deviceInfoData,SPDRP_UPPERFILTERS,buffer))qDebug()<<"SPDRP_CLASS : "<<QString::fromWCharArray((TCHAR*)buffer);
SetupDiGetDeviceProperty 函数检索设备实例属性。
- 语法:
//! 如果成功,则返回 TRUE 。 否则,它将返回 FALSE,并且可以通过调用 GetLastError 来检索记录的错误。
WINSETUPAPI BOOL SetupDiGetDevicePropertyW([in] HDEVINFO DeviceInfoSet, //包含要检索设备实例属性的设备实例的设备信息集的句柄。[in] PSP_DEVINFO_DATA DeviceInfoData, //指向 SP_DEVINFO_DATA 结构的指针,该结构表示要为其检索设备实例属性的设备实例。[in] const DEVPROPKEY *PropertyKey, //指向 DEVPROPKEY 结构的指针,该结构表示所请求的设备实例属性的设备属性键。[out] DEVPROPTYPE *PropertyType, /*指向 DEVPROPTYPE 类型变量的指针,该变量接收请求的设备实例属性的 property-data-type 标识符,其中 property-data-type 标识符是基数据类型标识符与 base-data 类型修饰符之间的按位 OR。*/[out, optional] PBYTE PropertyBuffer, /*指向接收请求的设备实例属性的缓冲区的指针。 仅当缓冲区大到足以保存所有属性值数据时,SetupDiGetDeviceProperty 才检索请求的属性。 指针可以为 NULL。 如果指针设置为 NULL 并提供 RequiredSize , 则 SetupDiGetDeviceProperty 将在 *RequiredSize 中返回属性的大小(以字节为单位)。*/[in] DWORD PropertyBufferSize, /*PropertyBuffer 缓冲区的大小(以字节为单位)。 如果 PropertyBuffer 设置为 NULL, 则 PropertyBufferSize 必须设置为零。*/[out, optional] PDWORD RequiredSize, /*指向 DWORD 类型变量的指针,该变量接收设备实例属性(如果检索到属性)的大小(以字节为单位),如果缓冲区不够大,则接收所需的缓冲区大小。 此指针可以设置为 NULL。*/[in] DWORD Flags //该参数必须设置为零。
);
让任何 DEVPROPTYPE 类型的参数都能够直接调用,也是为了避免分配内存不足导致的异常的问题。
方法中的DeviceInfoSet和DeviceInfoData参数都是通过SetupDiGetClassDevs和SetupDiEnumDeviceInfo获取的值,详见专栏上一篇。
bool GetSetupDiGetDeviceProperty(HDEVINFO deviceInfoSet,PSP_DEVINFO_DATA deviceInfoData,DEVPROPTYPE propType,const DEVPROPKEY * Key,PBYTE& buffer,DWORD& requiredSize) const
{if(requiredSize==0)requiredSize=sizeof(buffer);bool result = SetupDiGetDeviceProperty(deviceInfoSet, deviceInfoData, Key,&propType, buffer, requiredSize, &requiredSize, 0);if(!result && GetLastError()==ERROR_INSUFFICIENT_BUFFER){buffer=(PBYTE)malloc(requiredSize);result = SetupDiGetDeviceProperty(deviceInfoSet, deviceInfoData, Key,&propType, buffer, requiredSize, NULL, 0);}//! 常见异常if(!result){switch (GetLastError()) {case ERROR_INVALID_FLAGS :qDebug()<<"Flags 的值不为零。";break;case ERROR_INVALID_HANDLE :qDebug()<<"DevInfoSet 指定的设备信息集无效。";break;case ERROR_INVALID_PARAMETER :qDebug()<<"提供的参数无效。 一种可能性是设备信息元素无效。";break;case ERROR_INVALID_REG_PROPERTY :qDebug()<<"PropertyKey 提供的属性键无效或属性不可写。";break;case ERROR_INVALID_DATA :qDebug()<<"PropertyType 提供的属性数据类型标识符或 PropertyBuffer 提供的属性值无效。";break;case ERROR_INVALID_USER_BUFFER :qDebug()<<"用户缓冲区无效。 一种可能性是 PropertyBuffer 为 NULL, PropertyBufferSize 不为零。";break;case ERROR_NO_SUCH_DEVINST :qDebug()<<"DevInfoData 指定的设备实例不存在。";break;case ERROR_INSUFFICIENT_BUFFER :qDebug()<<"传递给系统调用的内部数据缓冲区太小。";break;case ERROR_NOT_ENOUGH_MEMORY :qDebug()<<"系统内存不足,无法完成操作。";break;case ERROR_NOT_FOUND :qDebug()<<"找不到未指定的内部元素。 一种可能性是要删除的属性不存在。";break;case ERROR_ACCESS_DENIED :qDebug()<<"调用方没有管理员权限。";break;default:qDebug()<<"get SetupDiGetDeviceProperty is Error!"<<QString::fromWCharArray(Lib_ExtrationDrives::guid_to_wstring(Key->fmtid)) ;}}return result;
}
获取 DEVPKEY_NAME:
设备属性表示设备实例的名称。
DWORD requiredSize=0;PBYTE buffer=new BYTE [MAX_PATH];if(GetSetupDiGetDeviceProperty(DeviceInfoSet,DeviceInfoData,DEVPROP_TYPE_STRING,&DEVPKEY_NAME,buffer,requiredSize)){QString Values=QString::fromWCharArray((TCHAR*)buffer);}
获取 DEVPKEY_Device_DeviceDesc:
设备属性表示设备实例的说明。
DWORD requiredSize=0;PBYTE buffer=new BYTE [MAX_PATH];if(GetSetupDiGetDeviceProperty(DeviceInfoSet,DeviceInfoData,DEVPROP_TYPE_STRING,&DEVPKEY_Device_DeviceDesc,buffer,requiredSize)){QString Values=QString::fromWCharArray((TCHAR*)buffer);}
获取 DEVPKEY_Device_HardwareIds:
设备属性表示设备实例的硬件标识符列表。
DWORD requiredSize=0;PBYTE buffer=new BYTE [MAX_PATH];if(GetSetupDiGetDeviceProperty(DeviceInfoSet,DeviceInfoData,DEVPROP_TYPE_STRING_LIST,&DEVPKEY_Device_HardwareIds,buffer,requiredSize)){QString Values=QString::fromWCharArray((TCHAR*)buffer);}
常见DEVPROPTYPE数据类型处理
DEVPROPTYPE数据类型说明:
属性数据类型标识符
DEVPROPTYPE数据类型 | Value | 描述 |
---|---|---|
DEVPROP_TYPE_EMPTY | 0x00000000 | 无任何属性数据 |
DEVPROP_TYPE_NULL | 0x00000001 | Null 属性数据 |
DEVPROP_TYPE_SBYTE | 0x00000002 | 8 位有符号 int (SBYTE) |
DEVPROP_TYPE_BYTE | 0x00000003 | 8 位无符号 int (BYTE) |
DEVPROP_TYPE_INT16 | 0x00000004 | 16 位有符号 int (SHORT) |
DEVPROP_TYPE_UINT16 | 0x00000005 | 16 位无符号 int (USHORT) |
DEVPROP_TYPE_INT32 | 0x00000006 | 32 位有符号 int (LONG) |
DEVPROP_TYPE_UINT32 | 0x00000007 | 32 位无符号 int (ULONG) |
DEVPROP_TYPE_INT64 | 0x00000008 | 64 位有符号 int (LONG64) |
DEVPROP_TYPE_UINT64 | 0x00000009 | 64 位无符号 int (ULONG64) |
DEVPROP_TYPE_FLOAT | 0x0000000A | 32 位浮点 (FLOAT) |
DEVPROP_TYPE_DOUBLE | 0x0000000B | 64 位浮点 (DOUBLE) |
DEVPROP_TYPE_DECIMAL | 0x0000000C | 128 位数据 (十进制) |
DEVPROP_TYPE_GUID | 0x0000000D | 128 位唯一标识符 (GUID) |
DEVPROP_TYPE_CURRENCY | 0x0000000E | 64 位有符号 int 货币值 (CURRENCY) |
DEVPROP_TYPE_DATE | 0x0000000F | date (DATE) |
DEVPROP_TYPE_FILETIME | 0x00000010 | file time (FILETIME) |
DEVPROP_TYPE_BOOLEAN | 0x00000011 | 8 位布尔 (DEVPROP_BOOLEAN) |
DEVPROP_TYPE_STRING | 0x00000012 | 以 Null 结尾的字符串 |
DEVPROP_TYPE_STRING_LIST |DEVPROP_TYPE_STRING|DEVPROP_TYPEMOD_LIST | 多 sz 字符串列表 | |
DEVPROP_TYPE_SECURITY_DESCRIPTOR | 0x00000013 | 自相对二进制SECURITY_DESCRIPTOR |
DEVPROP_TYPE_SECURITY_DESCRIPTOR_STRING | 0x00000014 | SDDL 格式 (安全描述符字符串) |
DEVPROP_TYPE_DEVPROPKEY | 0x00000015 | 设备属性键 (DEVPROPKEY) |
DEVPROP_TYPE_DEVPROPTYPE | 0x00000016 | 设备属性类型 (DEVPROPTYPE) |
DEVPROP_TYPE_BINARY |DEVPROP_TYPE_BYTE |DEVPROP_TYPEMOD_ARRAY | 自定义二进制数据 | |
DEVPROP_TYPE_ERROR | 0x00000017 | 32 位 Win32 系统错误代码 |
DEVPROP_TYPE_NTSTATUS | 0x00000018 | 32 位 NTSTATUS 代码 |
DEVPROP_TYPE_STRING_INDIRECT | 0x00000019 | 字符串资源 (@[path],-) |
下面是可能的DEVPROP_TYPEMOD_值:
标识 | 值 | 含义 |
---|---|---|
DEVPROP_TYPEMOD_ARRAY | 0x00001000 | 固定大小的数据元素数组 |
DEVPROP_TYPEMOD_LIST | 0x00002000 | 可变大小的数据元素列表 |
PBYTE类型数据转DEVPROPTYPE数据类型相关结构
-
DEVPROP_TYPE_GUID 数据类型处理
将 PropertyType 参数设置为 DEVPROP_TYPE_GUID,将 PropertyBuffer 参数设置为指向包含 GUID 值的缓冲区的指针,并将 PropertyBufferSize 参数设置为 sizeof(GUID)。
可以直接将PBYTE强制转换成GUID*类型
如获取:
DEVPKEY_Device_ClassGuid(实例所属的设备安装类的GUID)
DEVPKEY_Device_BusTypeGuid(设备实例的总线类型的 GUID)
PBYTE Guidbuffer=new BYTE [MAX_PATH];
if(GetSetupDiGetDeviceProperty(DeviceInfoSet,DeviceInfoData,DEVPROP_TYPE_GUID ,&DEVPKEY_Device_ClassGuid,Guidbuffer,requiredSize)){GUID* classguid=(GUID*)Guidbuffer;QString Values=QString::fromWCharArray(guid_to_wstring(*classguid));}
-
DEVPROP_TYPE_STRING数据类型处理
将 PropertyType 参数设置为 DEVPROP_TYPE_STRING,将 PropertyBuffer 参数设置为指向包含 NULL 终止 Unicode 字符串的缓冲区的指针,并将 PropertyBufferSize 参数设置为字符串的大小(以字节为单位),包括 NULL 终止符
可以直接将PBYTE强制转换成TCHAR*类型
如获取:
DEVPKEY_NAME (设备实例的名称)
DEVPKEY_Device_DeviceDesc (设备实例的说明)
DEVPKEY_Device_Service (设备实例安装的服务的名称)
DEVPKEY_Device_DriverVersion (安装在设备实例上的驱动程序的版本)
DEVPKEY_Device_DriverInfPath (安装设备实例的 INF 文件的名称)
//! DEVPROP_TYPE_STRING 数据类型PBYTE buffer=new BYTE [MAX_PATH];if(GetSetupDiGetDeviceProperty(DeviceInfoSet,DeviceInfoData,DEVPROP_TYPE_STRING,&DEVPKEY_NAME,buffer,requiredSize)){QString Values=QString::fromWCharArray((TCHAR*)buffer);}
-
DEVPROP_TYPE_STRING_LIST数据类型处理
将 PropertyType 参数设置为 DEVPROP_TYPE_STRING_LIST,将 PropertyBuffer 参数设置为指向包含 unicode 字符串 REG_MULTI_SZ 列表的缓冲区的指针,并将 PropertyBufferSize 参数设置为列表的大小(以字节为单位),包括最终列表 NULL 终止符
可以读多段字符串,\0表示一段结束 \0\0表示全部结束
如获取:
DEVPKEY_Device_HardwareIds(设备实例的硬件标识符列表)
DEVPKEY_DEVICE_CompatibleIds(设备实例的兼容标识符列表)
PBYTE buffer=new BYTE [MAX_PATH];
if(GetSetupDiGetDeviceProperty(DeviceInfoSet,DeviceInfoData,DEVPROP_TYPE_STRING_LIST,&DEVPKEY_Device_HardwareIds,buffer,requiredSize))
{WCHAR* bufferStr=(WCHAR*)buffer;QStringList valueList;int col=0;WCHAR* item=new WCHAR [MAX_PATH];for(int i=0;i<requiredSize;i++){if(bufferStr[i]!='\0'){item[col]=bufferStr[i];col++;}else{item[col]='\0';valueList.append(QString::fromWCharArray(item));free(item);if(i+1<requiredSize &&bufferStr[i+1]=='\0' )break;item=new WCHAR [MAX_PATH];col=0;}}QString Values=valueList.join("<br/>");
}
将 PropertyType 参数设置为 DEVPROP_TYPE_DATE,将 PropertyBuffer 参数设置为指向包含 FILETIME 结构的缓冲区的指针,并将 PropertyBufferSize 参数设置为 sizeof(FILETIME)。
如获取:
DEVPKEY_Device_InstallDate (指定上次在系统中安装设备实例时的时间戳)
DEVPKEY_Device_FirstInstallDate(指定首次在系统中安装设备实例时的时间戳)
DEVPKEY_Device_DriverDate(表示当前为设备实例安装的驱动程序的日期)
auto FileTimeToString=[&](const FILETIME& ft)->QString{SYSTEMTIME st;FileTimeToSystemTime(&ft, &st);// 如果需要转换为特定时区的时间,可以使用SystemTimeToTzSpecificLocalTime函数// 但这里我们假设只需要本地时间return QString("%1-%2-%3 %4:%5:%6").arg(st.wYear,4,10,QLatin1Char('0')).arg(st.wMonth,2,10,QLatin1Char('0')).arg(st.wDay,2,10,QLatin1Char('0')).arg(st.wHour,2,10,QLatin1Char('0')).arg(st.wMinute,2,10,QLatin1Char('0')).arg(st.wSecond,2,10,QLatin1Char('0'));
};FILETIME * file=(FILETIME *) malloc(sizeof(FILETIME));
requiredSize= sizeof (FILETIME );PBYTE buffer=(PBYTE)file;if(GetSetupDiGetDeviceProperty(DeviceInfoSet,DeviceInfoData,propType ,&PropertyKey ,buffer,requiredSize)){QString Values=FileTimeToString(*file);}