2010-02-05 5 views
11

RegisterDeviceNotification으로 창을 등록 했으므로 DEV_BROADCAST_DEVICEINTERFACE 메시지를 성공적으로 수신 할 수 있습니다. 그러나 반환 된 구조체의 dbcc_name 필드는 항상 비어 있습니다. 내가 가진 구조체는 다음과 같이 정의된다DEV_BROADCAST_DEVICEINTERFACE 및 Device Instance 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 (2010년 2월 5일 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"(이 경우 장치가 무엇인지).

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

그런 다음에 그 구조를 통과 값을 아래에 도시 된 바와 같이 그것은 가능성

답변

9

글쎄, 위에서 언급 한 것처럼 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

고맙습니다 !! 나는 똑같은 일을하려고 애썼다. –

0

너는이 약간

 
[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를 구성해야 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 :이 ... 지금은 너무 확실하지 않다 흥미, 나는 CodeprojectRegisterDeviceNotification을 사용하여이 문서를 발견하고는 DIFF을 사용 Profiler가 IntPtr으로 정렬되고 API를 호출하는 데 사용되는 RegisterDeviceNotification의 다른 방법은 다음과 같습니다.

+0

StringBuilder 인 필드를 마샬링 할 수 없습니다. 이것은 작동하지 않습니다. – snicker

+0

@Snicker : 방금 실현했습니다 ...이 답변을 조금 더 편집 할 것입니다 .. – t0mm13b

+0

Tom. 편집이 여전히 작동하지 않습니다. 문자열을 LPStr, LPWStr, LPTStr, BStr 또는 ByValTStr로만 마샬링 할 수 있습니다. – snicker

2

이 정보는 또한 SetupAPI을 통해 더 공식적으로 획득 할 수 있습니다 :이 장치 이름을 얻을 수있는 가장 쉬운 방법 것을 발견했다.dbcc_nameSetupDiOpenDeviceInterface으로 전달하고 SetupDiGetDeviceRegistryProperty과 일치하는 이름을 SPDRP_FRIENDLYNAME으로 전달하십시오.

다음은 일부 Delphi 코드입니다. 죄송합니다. C#으로 독립적으로 번역해야합니다. 당신이 유일하게 (당신이 요청하지만, 일반적으로이 또한 필요하지 무엇을) USB 장치를 식별 할 수있는 방법을 필요로하는 경우

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; 

마지막으로 ..., SetupDiGetDeviceInstanceId로 본다.

+0

대답하기에 조금 늦었습니다. 대답은 이미 7 표로 받아 들여졌습니다. –

+4

더 나은 답변 =은 결코 좋지 않습니다. (나는 수동 문자열 분석보다 정식 API를 선호합니다) –

+0

@ NathanSchubkegel이 코드를 C#으로 변환 할 사람을 찾을 수 있으면 받아 들인 대답을 바꿀 것입니다. 이것은 절대적으로 바람직한 해결책이 될 것입니다. 위에 게시 한 코드는 smelly하고 항상 레지스트리의 동일한 위치 (또는 레지스트리가 존재하는)에 의존하기 때문에 winapi 솔루션이 가장 좋습니다 – snicker