2010-02-05 22 views
11

He registrado una ventana con RegisterDeviceNotification y puedo recibir con éxito los mensajes DEV_BROADCAST_DEVICEINTERFACE. Sin embargo, el campo dbcc_name en la estructura devuelta siempre está vacío. La estructura que tengo se define como tal:Cómo obtener el nombre de dispositivo amigable de DEV_BROADCAST_DEVICEINTERFACE y la ID de instancia de dispositivo

[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; 
} 

y estoy usando Marshal.PtrToStructure en el LParam del mensaje WM_DEVICECHANGE.

¿Debería estar funcionando?

O mejor ... ¿Hay alguna forma alternativa de obtener el nombre de un dispositivo en la conexión?

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

descubrí cómo llegar al campo dbcc_name para poblar al hacer esto:

[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; 
} 

pero todavía necesitan una manera para obtener un nombre "Amigable" de lo que es int dbcc_name. Se parece a la siguiente:

\ \ USB # VID_05AC & PID_1294 ​​& MI_00 # 0 # {6BDD1FC6-810F-11d0-BEC7-08002BE2092F}

Y realmente sólo quiero que diga? "Apple iPhone" (que es lo que es el dispositivo en este caso).

Respuesta

9

Bueno, como se señaló anteriormente, descubrí cómo hacer que dbcc_name se rellene correctamente. Me pareció que esta era la forma más fácil de obtener el nombre del dispositivo:

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

¡GRACIAS! He estado tratando de hacer lo mismo. –

0

Es probable que necesite cambiar este ligeramente

 
[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; 
} 

Establecer la dbcc_size a 255, y construir el StringBuilder como se muestra a continuación:

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

A continuación, pasar esa estructura en el valor de dbcc_name debe completarse.

Editar: después reírse 's comentario ... pensé en esto de otra manera ...

 
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; 
} 

Establecer la dbcc_size a 255, y a partir de ahí ...

Edición # 2: esto es interesante ... no estoy tan seguro ahora, me encontré con este artículo que utiliza RegisterDeviceNotification en Codeproject y utiliza un diff Cada forma de RegisterDeviceNotification en que la estructura se organiza en un IntPtr y se utiliza para llamar a la API ...

+0

No se pueden ordenar los campos que son StringBuilders. Esto no funciona – snicker

+0

@Snicker: acabo de darme cuenta ... editaré esta respuesta un poco más ... – t0mm13b

+0

Tom. Tu edición aún no funciona. Solo puede ordenar cadenas a LPStr, LPWStr, LPTStr, BStr o ByValTStr. – snicker

2

Esta información también puede ser adquirido de manera más formal a través SetupAPI.Pase dbcc_name a SetupDiOpenDeviceInterface y obtenga el nombre descriptivo con SetupDiGetDeviceRegistryProperty pasando por SPDRP_FRIENDLYNAME.

Aquí hay un código Delphi que lo hará. (Lo siento, tendrás que traducir a C# de forma independiente).

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; 

Por último ... si usted necesita una manera de identificar de forma exclusiva un dispositivo USB (no lo que pidieron, pero normalmente esto también es necesaria), mira en SetupDiGetDeviceInstanceId.

+0

Un poco tarde para responder. Una respuesta ya ha sido aceptada con siete votos. –

+4

Nunca es demasiado tarde para una mejor respuesta =) (Prefiero una API formal sobre el análisis de cadenas manual cualquier día) –

+0

@NathanSchubkegel si puede encontrar a alguien que traduzca esto a C#, cambiaré la respuesta aceptada. Esta sería la solución absolutamente preferible, el código que publiqué anteriormente es maloliente y se basa en que algo siempre está en la misma ubicación en el registro (o en el que existe el registro), una solución de winapi es la mejor – snicker

Cuestiones relacionadas