Qt案例 调用WINDOWS API中的SETUPAPI.H库获取设备管理器中设备的详细信息中的属性值(二)

使用Qt调用windows api中的setupapi.h库中的SetupDiGetDeviceRegistryPropertySetupDiGetDeviceProperty函数获取设备管理器中的设备详细信息中的属性值,包括设备实例路径,硬件id,驱动inf名称,驱动版本,显示名称,类名,供应商,设备描述等属性值。

实现效果

根据左侧选择的设备管理器,设备实例路径
获取设备大部分DEFINE_DEVPROPKEY属性对应的业务值
并显示到右侧文本框中。
在这里插入图片描述

相关函数的说明

涉及到以下函数的使用:

  • SetupDiGetDeviceRegistryProperty 函数

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。
);
  • 封装SetupDiGetDeviceRegistryProperty 函数调用

使方法通用,主要也是为了避免分配内存不足导致的异常的问题。
方法中的DeviceInfoSetDeviceInfoData都是SetupDiGetClassDevsSetupDiEnumDeviceInfo获取的值,详见专栏上一篇。

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 函数

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 //该参数必须设置为零。
);
  • 封装SetupDiGetDeviceProperty 函数调用

让任何 DEVPROPTYPE 类型的参数都能够直接调用,也是为了避免分配内存不足导致的异常的问题。
方法中的DeviceInfoSetDeviceInfoData参数都是通过SetupDiGetClassDevsSetupDiEnumDeviceInfo获取的值,详见专栏上一篇。

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_EMPTY0x00000000无任何属性数据
DEVPROP_TYPE_NULL0x00000001Null 属性数据
DEVPROP_TYPE_SBYTE0x000000028 位有符号 int (SBYTE)
DEVPROP_TYPE_BYTE0x000000038 位无符号 int (BYTE)
DEVPROP_TYPE_INT160x0000000416 位有符号 int (SHORT)
DEVPROP_TYPE_UINT160x0000000516 位无符号 int (USHORT)
DEVPROP_TYPE_INT320x0000000632 位有符号 int (LONG)
DEVPROP_TYPE_UINT320x0000000732 位无符号 int (ULONG)
DEVPROP_TYPE_INT640x0000000864 位有符号 int (LONG64)
DEVPROP_TYPE_UINT640x0000000964 位无符号 int (ULONG64)
DEVPROP_TYPE_FLOAT0x0000000A32 位浮点 (FLOAT)
DEVPROP_TYPE_DOUBLE0x0000000B64 位浮点 (DOUBLE)
DEVPROP_TYPE_DECIMAL0x0000000C128 位数据 (十进制)
DEVPROP_TYPE_GUID0x0000000D128 位唯一标识符 (GUID)
DEVPROP_TYPE_CURRENCY0x0000000E64 位有符号 int 货币值 (CURRENCY)
DEVPROP_TYPE_DATE0x0000000Fdate (DATE)
DEVPROP_TYPE_FILETIME0x00000010file time (FILETIME)
DEVPROP_TYPE_BOOLEAN0x000000118 位布尔 (DEVPROP_BOOLEAN)
DEVPROP_TYPE_STRING0x00000012以 Null 结尾的字符串
DEVPROP_TYPE_STRING_LIST |DEVPROP_TYPE_STRING|DEVPROP_TYPEMOD_LIST多 sz 字符串列表
DEVPROP_TYPE_SECURITY_DESCRIPTOR0x00000013自相对二进制SECURITY_DESCRIPTOR
DEVPROP_TYPE_SECURITY_DESCRIPTOR_STRING0x00000014SDDL 格式 (安全描述符字符串)
DEVPROP_TYPE_DEVPROPKEY0x00000015设备属性键 (DEVPROPKEY)
DEVPROP_TYPE_DEVPROPTYPE0x00000016设备属性类型 (DEVPROPTYPE)
DEVPROP_TYPE_BINARY |DEVPROP_TYPE_BYTE |DEVPROP_TYPEMOD_ARRAY自定义二进制数据
DEVPROP_TYPE_ERROR0x0000001732 位 Win32 系统错误代码
DEVPROP_TYPE_NTSTATUS0x0000001832 位 NTSTATUS 代码
DEVPROP_TYPE_STRING_INDIRECT0x00000019字符串资源 (@[path],-)

下面是可能的DEVPROP_TYPEMOD_值:

标识含义
DEVPROP_TYPEMOD_ARRAY0x00001000固定大小的数据元素数组
DEVPROP_TYPEMOD_LIST0x00002000可变大小的数据元素列表

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/>");
}
  • DEVPROP_TYPE_FILETIME数据类型处理

将 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);
  }
  • 17
    点赞
  • 17
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

得鹿梦鱼、

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值