如何从 DEV_BROADCAST_DEVICEINTERFACE 和设备实例 ID 获取友好的设备名称

发布于 2024-08-20 22:48:25 字数 1493 浏览 7 评论 0原文

我已使用 RegisterDeviceNotification 注册了一个窗口,并且可以成功接收 DEV_BROADCAST_DEVICEINTERFACE 消息。但是,返回的结构中的 dbcc_name 字段始终为空。我的结构定义如下:

[StructLayout(LayoutKind.Sequential)]
public struct DEV_BROADCAST_DEVICEINTERFACE
{
    public int dbcc_size;
    public int dbcc_devicetype;
    public int dbcc_reserved;
    public Guid dbcc_classguid;
    [MarshalAs(UnmanagedType.LPStr)]
    public string dbcc_name;
}

我在 WM_DEVICECHANGE 消息。

这应该有效吗?

或者甚至更好...是否有其他方法可以在连接时获取设备的名称?

编辑(02/05/2010 20:56GMT):

我发现了如何通过这样做来填充 dbcc_name 字段:

[StructLayout(LayoutKind.Sequential, CharSet = CharSet.Auto)]
public struct DEV_BROADCAST_DEVICEINTERFACE
{
    public int dbcc_size;
    public int dbcc_devicetype;
    public int dbcc_reserved;
    public Guid dbcc_classguid;
    [MarshalAs(UnmanagedType.ByValTStr, SizeConst=255)]
    public string dbcc_name;
}

但我仍然需要一种方法来从什么中获取“友好”名称int dbcc_name。它看起来像下面这样:

\?\USB#VID_05AC&PID_1294​​&MI_00#0#{6bdd1fc6-810f-11d0-bec7-08002be2092f}

我真的只想说“Apple iPhone”(这就是本例中的设备)。

I've registered a window with RegisterDeviceNotification and can successfully recieve DEV_BROADCAST_DEVICEINTERFACE messages. However, the dbcc_name field in the returned struct is always empty. The struct I have is defined as such:

[StructLayout(LayoutKind.Sequential)]
public struct DEV_BROADCAST_DEVICEINTERFACE
{
    public int dbcc_size;
    public int dbcc_devicetype;
    public int dbcc_reserved;
    public Guid dbcc_classguid;
    [MarshalAs(UnmanagedType.LPStr)]
    public string dbcc_name;
}

And I'm using Marshal.PtrToStructure on the LParam of the WM_DEVICECHANGE message.

Should this be working?

Or even better... Is there an alternative way to get the name of a device upon connection?

EDIT (02/05/2010 20:56GMT):

I found out how to get the dbcc_name field to populate by doing this:

[StructLayout(LayoutKind.Sequential, CharSet = CharSet.Auto)]
public struct DEV_BROADCAST_DEVICEINTERFACE
{
    public int dbcc_size;
    public int dbcc_devicetype;
    public int dbcc_reserved;
    public Guid dbcc_classguid;
    [MarshalAs(UnmanagedType.ByValTStr, SizeConst=255)]
    public string dbcc_name;
}

but I still need a way to get a "Friendly" name from what is int dbcc_name. It looks like the following:

\?\USB#VID_05AC&PID_1294&MI_00#0#{6bdd1fc6-810f-11d0-bec7-08002be2092f}

And I really just want it to say "Apple iPhone" (which is what the device is in this case).

如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。

扫码二维码加入Web技术交流群

发布评论

需要 登录 才能够评论, 你可以免费 注册 一个本站的账号。

评论(4

时常饿 2024-08-27 22:48:25

好吧,如上所述,我发现了如何正确填充 dbcc_name。我发现这是获取设备名称的最简单方法:

private static string GetDeviceName(DEV_BROADCAST_DEVICEINTERFACE dvi)
{
    string[] Parts = dvi.dbcc_name.Split('#');
    if (Parts.Length >= 3)
    {
        string DevType = Parts[0].Substring(Parts[0].IndexOf(@"?\") + 2);
        string DeviceInstanceId = Parts[1];
        string DeviceUniqueID = Parts[2];
        string RegPath = @"SYSTEM\CurrentControlSet\Enum\" + DevType + "\\" + DeviceInstanceId + "\\" + DeviceUniqueID;
        RegistryKey key = Registry.LocalMachine.OpenSubKey(RegPath);
        if (key != null)
        {
            object result = key.GetValue("FriendlyName");
            if (result != null)
                return result.ToString();
            result = key.GetValue("DeviceDesc");
            if (result != null)
                return result.ToString();
        }
    }
    return String.Empty;
}

Well, as noted above I found out how to get dbcc_name to populate correctly. I found that this was the easiest way to get the device name:

private static string GetDeviceName(DEV_BROADCAST_DEVICEINTERFACE dvi)
{
    string[] Parts = dvi.dbcc_name.Split('#');
    if (Parts.Length >= 3)
    {
        string DevType = Parts[0].Substring(Parts[0].IndexOf(@"?\") + 2);
        string DeviceInstanceId = Parts[1];
        string DeviceUniqueID = Parts[2];
        string RegPath = @"SYSTEM\CurrentControlSet\Enum\" + DevType + "\\" + DeviceInstanceId + "\\" + DeviceUniqueID;
        RegistryKey key = Registry.LocalMachine.OpenSubKey(RegPath);
        if (key != null)
        {
            object result = key.GetValue("FriendlyName");
            if (result != null)
                return result.ToString();
            result = key.GetValue("DeviceDesc");
            if (result != null)
                return result.ToString();
        }
    }
    return String.Empty;
}
决绝 2024-08-27 22:48:25

还可以通过 SetupAPI。将 dbcc_name 传递给 SetupDiOpenDeviceInterface,并通过传入 SPDRP_FRIENDLYNAMESetupDiGetDeviceRegistryProperty 获取友好名称。

这里有一些 Delphi 代码可以做到这一点。 (抱歉,您必须独立翻译为 C#)。

function ConvertDbccNameToFriendlyName(aDeviceInterfaceDbccName : string) : string;
var
  deviceInfoHandle : HDEVINFO;
  deviceInfoData : SP_DEVINFO_DATA;
  deviceInterfaceData : SP_DEVICE_INTERFACE_DATA;
  deviceInstanceId : string;
  memberIndex : Cardinal;
begin
  result := '';

  // Create a new empty "device info set"
  deviceInfoHandle := SetupDiCreateDeviceInfoList(nil, 0);
  if deviceInfoHandle <> INVALID_HANDLE_VALUE then
  begin
    try
      // Add "aDeviceInterfaceDbccName" to the device info set
      FillChar(deviceInterfaceData, SizeOf(deviceInterfaceData), 0);
      deviceInterfaceData.cbSize := SizeOf(deviceInterfaceData);
      if SetupDiOpenDeviceInterface(deviceInfoHandle, PChar(aDeviceInterfaceDbccName),     0, @deviceInterfaceData) then
      begin
        try
          // iterate over the device info set
          // (though I only expect it to contain one item)
          memberIndex := 0;
          while true do
          begin
            // get device info that corresponds to the next memberIndex
            FillChar(deviceInfoData, SizeOf(deviceInfoData), 0);
            deviceInfoData.cbSize := SizeOf(deviceInfoData);
            if not SetupDiEnumDeviceInfo(deviceInfoHandle, memberIndex, deviceInfoData) then
            begin
              // The enumerator is exhausted when SetupDiEnumDeviceInfo returns false
              break;
            end
            else
            begin
              Inc(memberIndex);
            end;

            // Get the friendly name for that device info
            if TryGetDeviceFriendlyName(deviceInfoHandle, deviceInfoData, {out} friendlyName) then
            begin
              result := friendlyName;
              break;
            end;
          end;
        finally
          SetupDiDeleteDeviceInterfaceData(deviceInfoHandle, deviceInterfaceData);
        end;
      end;
    finally
      SetupDiDestroyDeviceInfoList(deviceInfoHandle);
    end;
  end;
end;

function TryGetDeviceFriendlyName(
  var aDeviceInfoHandle : HDEVINFO;
  var aDeviceInfoData : SP_DEVINFO_DATA;
  out aFriendlyName : string) : boolean;
var
  valueBuffer : array of byte;
  regProperty : Cardinal;
  propertyRegDataType : DWord;
  friendlyNameByteSize : Cardinal;
  success : boolean;
begin
  aFriendlyName := '';
  result := false;

  // Get the size of the friendly device name
  regProperty := SPDRP_FRIENDLYNAME;
  friendlyNameByteSize := 0;
  SetupDiGetDeviceRegistryProperty(
    aDeviceInfoHandle,     // handle to device information set
    aDeviceInfoData,       // pointer to SP_DEVINFO_DATA structure
    regProperty,           // property to be retrieved
    propertyRegDataType,   // pointer to variable that receives the data type of the property
    nil,                   // pointer to PropertyBuffer that receives the property
    0,                     // size, in bytes, of the PropertyBuffer buffer.
    friendlyNameByteSize); // pointer to variable that receives the required size of PropertyBuffer

  // Prepare a buffer for the friendly device name (plus space for a null terminator)
  SetLength(valueBuffer, friendlyNameByteSize + sizeof(char));

  success := SetupDiGetDeviceRegistryProperty(
    aDeviceInfoHandle,
    aDeviceInfoData,
    regProperty,
    propertyRegDataType,
    @valueBuffer[0],
    friendlyNameByteSize,
    friendlyNameByteSize);

  if success then
  begin
    // Ensure that only 'friendlyNameByteSize' bytes are used.
    // Ensure that the string is null-terminated.
    PChar(@valueBuffer[friendlyNameByteSize])^ := char(0);

    // Get the returned value as a string
    aFriendlyName := StrPas(PChar(@valueBuffer[0]));
  end;

  result := success;
end;

最后...如果您需要一种唯一标识 USB 设备的方法(不是您所要求的,但通常也是需要的),请查看 SetupDiGetDeviceInstanceId

This information can also be acquired more formally through SetupAPI. Pass dbcc_name to SetupDiOpenDeviceInterface and get the friendly name with SetupDiGetDeviceRegistryProperty passing in SPDRP_FRIENDLYNAME.

Here's some Delphi code that will do it. (Sorry, you'll have to translate to C# independently).

function ConvertDbccNameToFriendlyName(aDeviceInterfaceDbccName : string) : string;
var
  deviceInfoHandle : HDEVINFO;
  deviceInfoData : SP_DEVINFO_DATA;
  deviceInterfaceData : SP_DEVICE_INTERFACE_DATA;
  deviceInstanceId : string;
  memberIndex : Cardinal;
begin
  result := '';

  // Create a new empty "device info set"
  deviceInfoHandle := SetupDiCreateDeviceInfoList(nil, 0);
  if deviceInfoHandle <> INVALID_HANDLE_VALUE then
  begin
    try
      // Add "aDeviceInterfaceDbccName" to the device info set
      FillChar(deviceInterfaceData, SizeOf(deviceInterfaceData), 0);
      deviceInterfaceData.cbSize := SizeOf(deviceInterfaceData);
      if SetupDiOpenDeviceInterface(deviceInfoHandle, PChar(aDeviceInterfaceDbccName),     0, @deviceInterfaceData) then
      begin
        try
          // iterate over the device info set
          // (though I only expect it to contain one item)
          memberIndex := 0;
          while true do
          begin
            // get device info that corresponds to the next memberIndex
            FillChar(deviceInfoData, SizeOf(deviceInfoData), 0);
            deviceInfoData.cbSize := SizeOf(deviceInfoData);
            if not SetupDiEnumDeviceInfo(deviceInfoHandle, memberIndex, deviceInfoData) then
            begin
              // The enumerator is exhausted when SetupDiEnumDeviceInfo returns false
              break;
            end
            else
            begin
              Inc(memberIndex);
            end;

            // Get the friendly name for that device info
            if TryGetDeviceFriendlyName(deviceInfoHandle, deviceInfoData, {out} friendlyName) then
            begin
              result := friendlyName;
              break;
            end;
          end;
        finally
          SetupDiDeleteDeviceInterfaceData(deviceInfoHandle, deviceInterfaceData);
        end;
      end;
    finally
      SetupDiDestroyDeviceInfoList(deviceInfoHandle);
    end;
  end;
end;

function TryGetDeviceFriendlyName(
  var aDeviceInfoHandle : HDEVINFO;
  var aDeviceInfoData : SP_DEVINFO_DATA;
  out aFriendlyName : string) : boolean;
var
  valueBuffer : array of byte;
  regProperty : Cardinal;
  propertyRegDataType : DWord;
  friendlyNameByteSize : Cardinal;
  success : boolean;
begin
  aFriendlyName := '';
  result := false;

  // Get the size of the friendly device name
  regProperty := SPDRP_FRIENDLYNAME;
  friendlyNameByteSize := 0;
  SetupDiGetDeviceRegistryProperty(
    aDeviceInfoHandle,     // handle to device information set
    aDeviceInfoData,       // pointer to SP_DEVINFO_DATA structure
    regProperty,           // property to be retrieved
    propertyRegDataType,   // pointer to variable that receives the data type of the property
    nil,                   // pointer to PropertyBuffer that receives the property
    0,                     // size, in bytes, of the PropertyBuffer buffer.
    friendlyNameByteSize); // pointer to variable that receives the required size of PropertyBuffer

  // Prepare a buffer for the friendly device name (plus space for a null terminator)
  SetLength(valueBuffer, friendlyNameByteSize + sizeof(char));

  success := SetupDiGetDeviceRegistryProperty(
    aDeviceInfoHandle,
    aDeviceInfoData,
    regProperty,
    propertyRegDataType,
    @valueBuffer[0],
    friendlyNameByteSize,
    friendlyNameByteSize);

  if success then
  begin
    // Ensure that only 'friendlyNameByteSize' bytes are used.
    // Ensure that the string is null-terminated.
    PChar(@valueBuffer[friendlyNameByteSize])^ := char(0);

    // Get the returned value as a string
    aFriendlyName := StrPas(PChar(@valueBuffer[0]));
  end;

  result := success;
end;

Finally... if you need a way to uniquely identify a USB device (not what you asked for, but commonly this is also needed), look into SetupDiGetDeviceInstanceId.

彻夜缠绵 2024-08-27 22:48:25

您可能需要稍微更改一下

[StructLayout(LayoutKind.Sequential)]
public struct DEV_BROADCAST_DEVICEINTERFACE
{
    public int dbcc_size;
    public int dbcc_devicetype;
    public int dbcc_reserved;
    public Guid dbcc_classguid;
    [MarshalAs(UnmanagedType.LPStr)]
    public StringBuilder dbcc_name;
}

dbcc_size 设置为 255,并构造 StringBuilder,如下所示:

DEV_BROADCAST_DEVICEINTERFACE dbd = new DEV_BROADCAST_DEVICEINTERFACE;
dbd.dbcc_size = 255;
dbd.dbcc_name = new StringBuilder(dbd.dbcc_size);

然后传入该结构,应填充 dbcc_name 的值。

编辑:窃笑的评论之后...我想到了另一种方式...

public struct DEV_BROADCAST_DEVICEINTERFACE
{
    public int dbcc_size;
    public int dbcc_devicetype;
    public int dbcc_reserved;
    public Guid dbcc_classguid;
[System.Runtime.InteropServices.MarshalAsAttribute(System.Runtime.InteropServices.UnmanagedType.ByValArray, SizeConst = 255, ArraySubType = System.Runtime.InteropServices.UnmanagedType.LPArray)]
    public string dbcc_name;
}

设置dbcc_size到 255,然后从那里获取...

编辑#2:这很有趣...现在不太确定,我发现这篇文章在<上使用了RegisterDeviceNotification a href="http://www.codeproject.com/KB/system/DriveDetector.aspx" rel="nofollow noreferrer">Codeproject ,它使用不同的 RegisterDeviceNotification 方式,该结构被编组为IntPtr 用于调用API...

It is likely you need to change this slightly

[StructLayout(LayoutKind.Sequential)]
public struct DEV_BROADCAST_DEVICEINTERFACE
{
    public int dbcc_size;
    public int dbcc_devicetype;
    public int dbcc_reserved;
    public Guid dbcc_classguid;
    [MarshalAs(UnmanagedType.LPStr)]
    public StringBuilder dbcc_name;
}

Set the dbcc_size to 255, and construct the StringBuilder as shown below:

DEV_BROADCAST_DEVICEINTERFACE dbd = new DEV_BROADCAST_DEVICEINTERFACE;
dbd.dbcc_size = 255;
dbd.dbcc_name = new StringBuilder(dbd.dbcc_size);

Then pass that structure in, the value of dbcc_name should be populated.

Edit: after snicker's comment...I thought of this another way...

public struct DEV_BROADCAST_DEVICEINTERFACE
{
    public int dbcc_size;
    public int dbcc_devicetype;
    public int dbcc_reserved;
    public Guid dbcc_classguid;
[System.Runtime.InteropServices.MarshalAsAttribute(System.Runtime.InteropServices.UnmanagedType.ByValArray, SizeConst = 255, ArraySubType = System.Runtime.InteropServices.UnmanagedType.LPArray)]
    public string dbcc_name;
}

Set the dbcc_size to 255, and take it from there...

Edit#2: This is interesting...am not so sure now, I found this article that uses RegisterDeviceNotification on Codeproject and it uses a different way of RegisterDeviceNotification in that the struct is marshalled into a IntPtr and is used to call the API...

一场春暖 2024-08-27 22:48:25

解析设备实例的设备接口不是一个好主意

DEVPKEY_NAME 是您的最佳查询应该做的。

首先,您必须将设备接口名称转换为设备实例 ID。

像这样的SetupAPI:

void GetDeviceInstanceId(const wchar_t* device_interface, wchar_t* instance_id, size_t len)
{
  HDEVINFO devinfo;
  SP_DEVICE_INTERFACE_DATA interface_data;
  SP_DEVINFO_DATA device_data;
  
  devinfo = SetupDiCreateDeviceInfoList(NULL, NULL);
  
  memset(interface_data, 0, sizeof(interface_data));
  interface_data.cbSize = sizeof(interface_data);
  SetupDiOpenDeviceInterface(&devinfo, device_interface, 0, interface_data);
  
  memset(device_data, 0, sizeof(device_data));
  device_data.cbSize = sizeof(device_data);
  SetupDiGetDeviceInterfaceDetail(devinfo, interface_data, NULL, 0, NULL, device_data);
  
  SetupDiGetDeviceProperty(devinfo, device_data, DEVPKEY_Device_InstanceId, DEVPROP_TYPE_STRING, instance_id, len);
  // alternatively:
  //SetupDiGetDeviceInstanceId(devinfo, device_data, instance_id, len);

  SetupDiDeleteDeviceInterfaceData(devinfo, interface_data);
  SetupDiDestroyDeviceInfoList(devinfo);
}

void GetDeviceName(const wchar_t *instance_id, wchar_t *name, size_t len)
{
  HDEVINFO devinfo;
  SP_DEVINFO_DATA device_data;
  
  memset(device_data, 0, sizeof(device_data));
  device_data.cbSize = sizeof(device_data);
  SetupDiOpenDeviceInfo(devinfo, instance_id, NULL, 0, device_data);

  SetupDiGetDeviceProperty(devinfo, &device_data, DEVPKEY_NAME, DEVPROP_TYPE_STRING, name, len, 0);
  
  SetupDiDestroyDeviceInfoList(devinfo);
}

或者使用 更现代的cfgmgr32 API:

void GetDeviceInstanceId(const wchar_t* device_interface, wchar_t *buffer, size_t len)
{
  DEVPROPTYPE type = DEVPROP_TYPE_STRING;

  CM_Get_Device_Interface_Property(device_interface, &DEVPKEY_Device_InstanceId, &type, (PBYTE)buffer, len, 0); // CR_SUCCESS
}

void GetDeviceName(const wchar_t *instance_id, wchar_t *buffer, size_t len)
{
  DEVINST device;
  DEVPROPTYPE type = DEVPROP_TYPE_STRING;
  
  CM_Locate_DevNode(&device, instance_id, 0); // CR_SUCCESS
  CM_Get_DevNode_Property(device, &DEVPKEY_NAME, &type, (PBYTE)buffer, len, 0); // CR_SUCCESS
}

Parsing device interface of device instance is not a good idea.

DEVPKEY_NAME is a best query you should do.

First, you have to convert device interface name to device instance id.

Something like this with SetupAPI:

void GetDeviceInstanceId(const wchar_t* device_interface, wchar_t* instance_id, size_t len)
{
  HDEVINFO devinfo;
  SP_DEVICE_INTERFACE_DATA interface_data;
  SP_DEVINFO_DATA device_data;
  
  devinfo = SetupDiCreateDeviceInfoList(NULL, NULL);
  
  memset(interface_data, 0, sizeof(interface_data));
  interface_data.cbSize = sizeof(interface_data);
  SetupDiOpenDeviceInterface(&devinfo, device_interface, 0, interface_data);
  
  memset(device_data, 0, sizeof(device_data));
  device_data.cbSize = sizeof(device_data);
  SetupDiGetDeviceInterfaceDetail(devinfo, interface_data, NULL, 0, NULL, device_data);
  
  SetupDiGetDeviceProperty(devinfo, device_data, DEVPKEY_Device_InstanceId, DEVPROP_TYPE_STRING, instance_id, len);
  // alternatively:
  //SetupDiGetDeviceInstanceId(devinfo, device_data, instance_id, len);

  SetupDiDeleteDeviceInterfaceData(devinfo, interface_data);
  SetupDiDestroyDeviceInfoList(devinfo);
}

void GetDeviceName(const wchar_t *instance_id, wchar_t *name, size_t len)
{
  HDEVINFO devinfo;
  SP_DEVINFO_DATA device_data;
  
  memset(device_data, 0, sizeof(device_data));
  device_data.cbSize = sizeof(device_data);
  SetupDiOpenDeviceInfo(devinfo, instance_id, NULL, 0, device_data);

  SetupDiGetDeviceProperty(devinfo, &device_data, DEVPKEY_NAME, DEVPROP_TYPE_STRING, name, len, 0);
  
  SetupDiDestroyDeviceInfoList(devinfo);
}

Or with more modern cfgmgr32 API:

void GetDeviceInstanceId(const wchar_t* device_interface, wchar_t *buffer, size_t len)
{
  DEVPROPTYPE type = DEVPROP_TYPE_STRING;

  CM_Get_Device_Interface_Property(device_interface, &DEVPKEY_Device_InstanceId, &type, (PBYTE)buffer, len, 0); // CR_SUCCESS
}

void GetDeviceName(const wchar_t *instance_id, wchar_t *buffer, size_t len)
{
  DEVINST device;
  DEVPROPTYPE type = DEVPROP_TYPE_STRING;
  
  CM_Locate_DevNode(&device, instance_id, 0); // CR_SUCCESS
  CM_Get_DevNode_Property(device, &DEVPKEY_NAME, &type, (PBYTE)buffer, len, 0); // CR_SUCCESS
}

~没有更多了~
我们使用 Cookies 和其他技术来定制您的体验包括您的登录状态等。通过阅读我们的 隐私政策 了解更多相关信息。 单击 接受 或继续使用网站,即表示您同意使用 Cookies 和您的相关数据。
原文