如何从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_name
的SetupDiOpenDeviceInterface
获取友好名称。
这里有一些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。 ..