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

我已经使用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消息的LParam上使用Marshal.PtrToStructure

这应该有用吗?

或者甚至更好……是否有另一种方法可以在连接时获取设备的名称?

编辑(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”(在这种情况下就是设备)。

好吧,如上所述,我发现了如何正确填充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; } 

也可以通过SetupAPI更正式地获取此信息。 将SetupDiOpenDeviceInterface传递给SetupDiOpenDeviceInterface并通过传递dbcc_nameSetupDiOpenDeviceInterface获取友好名称。

这里有一些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

您可能需要稍微改变一下

 [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 ,它使用了一种不同的RegisterDeviceNotification方式,因为struct被编组到IntPtr并用于调用API。 ..