如何从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;
}
很可能你需要改变这种略带
[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 ...
该信息还可以通过SetupAPI更正式收购。通过dbcc_name
到SetupDiOpenDeviceInterface
并通过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
。
回答迟了一点。答案已经被七票接受。 – 2013-12-11 15:53:06
对于更好的答案=)永远不会太晚(我喜欢正式的API,而不是手动字符串解析任何一天) – 2014-01-09 02:24:14
@NathanSchubkegel如果你能找到某人把它翻译成C#,我会改变接受的答案。这将是绝对可取的解决方案,我上面发布的代码很臭,并且依赖于始终位于注册表中相同位置的东西(或者存在于注册表中的所有位置),WINAPI解决方案是最好的 – snicker 2014-01-09 22:53:49
致谢!我一直在尝试做同样的事情。 – 2013-05-17 16:15:19