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

问题描述:

我注册了一个带有RegisterDeviceNotification的窗口,并且可以成功接收DEV_BROADCAST_DEVICEINTERFACE消息。但是,返回的结构中的dbcc_name字段始终为空。该结构我的定义是这样的:如何从DEV_BROADCAST_DEVICEINTERFACE和设备实例ID获取友好设备名称

[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消息的Lparam型使用Marshal.PtrToStructure

这应该工作吗?

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

EDIT(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中获得“Friendly”名称。它看起来像如下:

\ \ USB#VID_05AC & PID_1294​​ & MI_00#0#{6bdd1fc6-810f-11D0-BEC7-08002BE2092F}

我真的只是希望它说什么? “Apple iPhone”(在这种情况下就是这个设备)。

那么,如上所述,我发现如何让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; 
} 
+0

致谢!我一直在尝试做同样的事情。 – 2013-05-17 16:15:19

很可能你需要改变这种略带

 
[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:这很有趣......现在我不那么肯定,我发现这篇文章,关于Codeproject使用RegisterDeviceNotification,它使用一个diff注册设备通知的不同方式在于该结构被编组为IntPtr并用于调用API ...

+0

您不能编组字段是StringBuilders。这不起作用。 – snicker 2010-02-05 16:59:53

+0

@ Snicker:刚刚意识到...将编辑这个答案更多.. – t0mm13b 2010-02-05 17:03:14

+0

汤姆。你的编辑仍然不起作用。您只能将字符串封送到LPStr,LPWStr,LPTStr,BStr或ByValTStr。 – snicker 2010-02-05 17:36:50

该信息还可以通过SetupAPI更正式收购。通过dbcc_nameSetupDiOpenDeviceInterface并通过SPDRP_FRIENDLYNAME得到友好名称SetupDiGetDeviceRegistryProperty

下面是一些将执行此操作的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

+0

回答迟了一点。答案已经被七票接受。 – 2013-12-11 15:53:06

+4

对于更好的答案=)永远不会太晚(我喜欢正式的API,而不是手动字符串解析任何一天) – 2014-01-09 02:24:14

+0

@NathanSchubkegel如果你能找到某人把它翻译成C#,我会改变接受的答案。这将是绝对可取的解决方案,我上面发布的代码很臭,并且依赖于始终位于注册表中相同位置的东西(或者存在于注册表中的所有位置),WINAPI解决方案是最好的 – snicker 2014-01-09 22:53:49