Visual Studio Developer Center > Visual C# Forums > Visual C# Language > SetupDiOpenDeviceInterface in C#

Answered SetupDiOpenDeviceInterface in C#

  • Monday, September 22, 2008 10:27 PM
     
     
    Hello,
     
    I'm trying to get informations about an USB device inserted into my system.
    My application works in C++ and I'm porting it to c#.

    Everytime, the call to SetupDiOpenDeviceInterface fails with ERROR_INVALID_USER_BUFFER.
    The doc says that it does not matter when info is filled. But this is not my case.
    I'm now running out of ideas please help.

    I first call RegisterDeviceNotification to get notified when an USB device is inserted.
           public bool DoRegisterDeviceInterface(IntPtr mHandle,Guid InterfaceClassGuid)
            {
                DEV_BROADCAST_DEVICEINTERFACE NotificationFilter = new DEV_BROADCAST_DEVICEINTERFACE();
                NotificationFilter.dbcc_size = Marshal.SizeOf(NotificationFilter);
                NotificationFilter.dbcc_devicetype = DBT_DEVTYP_DEVICEINTERFACE;
                NotificationFilter.dbcc_classguid = InterfaceClassGuid;

                IntPtr buffer = Marshal.AllocHGlobal(NotificationFilter.dbcc_size);
                Marshal.StructureToPtr(NotificationFilter, buffer, true);
               
                mDeviceNotifyHandle = RegisterDeviceNotification(mHandle, buffer, DEVICE_NOTIFY_WINDOW_HANDLE | DEVICE_NOTIFY_ALL_INTERFACE_CLASSES);
                return true;
            }

    I'm correctly notified when it fires:
          protected override void WndProc(ref Message m)
            {
                switch (m.Msg)
                {
                    case WM_DEVICECHANGE:
                        // The WParam value identifies what is occurring.
                        // n = (int)m.WParam;
                        switch (m.WParam.ToInt32())
                        {
                            case DBT_DEVICEARRIVAL:
                            case DBT_DEVICEREMOVECOMPLETE:
                                DEV_BROADCAST_DEVICEINTERFACE dvi = (DEV_BROADCAST_DEVICEINTERFACE)m.GetLParam(typeof(DEV_BROADCAST_DEVICEINTERFACE));
                                DoGetDeviceDetails(dvi);
                                break;
                        }
                        break;
                    case WM_QUIT:
                        device.DoUnRegisterDeviceInterface();
                        break;
                }
                base.WndProc(ref m);
            }

          private void DoGetDeviceDetails(DEV_BROADCAST_DEVICEINTERFACE dvi)
            {
               if (device == null)
                {
                    device = new DeviceInformation();
                }

                if (!device.GetUSBDevices())  // This line sets the hDev
                    return;

                SP_DEVINFO_DATA DeviceInfoData = device.SetupDiGetDeviceInterfaceDetail(dvi);
                if (DeviceInfoData != null)
                {
                    if (DeviceInfoData.DevInst != 0)
                    {
                        string desc, hardwareid, classguid, classdesc;
                        hardwareid = device.GetHardwareID(DeviceInfoData);
                        classdesc = device.GetClass(DeviceInfoData);
                        desc = device.GetDescription(DeviceInfoData);
                        classguid = device.GetClassGuid(DeviceInfoData);
                        AddDeviceItem(hardwareid, classdesc, desc, classguid);
                    }
                }

            }


      [StructLayout(LayoutKind.Sequential, CharSet = CharSet.Auto,Pack=4)] // Tried with or without Pack
        public class DEV_BROADCAST_DEVICEINTERFACE
        {
            public int dbcc_size;
            public int dbcc_devicetype;
            public int dbcc_reserved;
            public Guid dbcc_classguid;
            [MarshalAs(UnmanagedType.ByValTStr, SizeConst = 250)] // Tried different values.
            public string dbcc_name; // tchar[1]
        };

        [StructLayout(LayoutKind.Sequential)]
        public class SP_DEVINFO_DATA
        {
            public int cbSize;
            public Guid ClassGuid;
            public int DevInst;    // DEVINST handle
            public ulong Reserved;
        };
     
           [DllImport("setupapi.dll", SetLastError = true)]
            public static extern Boolean
                SetupDiOpenDeviceInterfaceA(
                    IntPtr DeviceInfoSet,
                    IntPtr DevicePath, // In the .h header, it's a PCSTR
                    int OpenFlags,
                    ref SP_DEVINFO_DATA DeviceInterfaceData);

            [DllImport("setupapi.dll")]
            public static extern Boolean
                SetupDiGetDeviceInterfaceDetailA(
                    IntPtr DeviceInfoSet,
                    ref SP_DEVINFO_DATA DeviceInterfaceData,
                    IntPtr DeviceInterfaceDetailData,
                    int DeviceInterfaceDetailDataSize,
                    ref int RequiredSize,
                    ref SP_DEVINFO_DATA DeviceInfoData);

          private IntPtr hDev;

            public bool GetUSBDevices()
            {

                Guid GUID_DEVINTERFACE_USB_DEVICE = new Guid("A5DCBF10-6530-11D2-901F-00C04FB951ED");
                hDev = SetupDiGetClassDevsA(  // hDev is correctly set
                    ref GUID_DEVINTERFACE_USB_DEVICE,
                    null,
                    IntPtr.Zero,
                    DIGCF_DEVICEINTERFACE | DIGCF_PROFILE
                    );
                if (hDev.ToInt32() == -1)
                    return false;

                return true;

            }

            public SP_DEVINFO_DATA SetupDiGetDeviceInterfaceDetail(DEV_BROADCAST_DEVICEINTERFACE dvi)
            {
                SP_DEVINFO_DATA DeviceInfoData = new SP_DEVINFO_DATA();  // Lazy coding: Actually it's SP_DEVICE_INTERFACE_DATA structure, but they are equivalent.
                DeviceInfoData.cbSize = Marshal.SizeOf(DeviceInfoData);
                DeviceInfoData.DevInst = 0;
                DeviceInfoData.ClassGuid = System.Guid.Empty;
                DeviceInfoData.Reserved = 0;

                if (!SetupDiOpenDeviceInterfaceA(
                    hDev,
                   // dvi.dbcc_name, // I feel that the problem is here. I tried several syntax, the value below is the content of dbcc_name.
                   Marshal.StringToHGlobalAnsi(@"\\?\USB#VID_07AB&PID_FCCA#FW520_200000000000B703#{a5dcbf10-6530-11d2-901f-00c04fb951ed}"),
                    0,
                    ref DeviceInfoData))
                {
                    // Get the last error and display it.
                    int error = Marshal.GetLastWin32Error(); // Always fail.
                  //  return (SP_DEVINFO_DATA)null;
                }

                SP_DEVINFO_DATA dd = new SP_DEVINFO_DATA();
                dd.cbSize = Marshal.SizeOf(dd);
                int val = 0;
                if (!SetupDiGetDeviceInterfaceDetailA(
                    hDev,
                    ref DeviceInfoData,
                    IntPtr.Zero,
                    0,
                    ref val,
                    ref dd))
                    {
                        int error = Marshal.GetLastWin32Error(); // Always fail.
                        //no such device:
                        SetupDiDestroyDeviceInfoList(hDev);
                    }
                return dd;
            }


    Any help would be gratly appreciated.
    Thanks.

Answers

  • Thursday, September 25, 2008 11:02 AM
     
     Answered
    Hmm,

    I'll try this evening to replace the class SP_DEVINFO_DATA by a struct.

    Hope this will do the trick.

    • Marked As Answer by ilinfoMVP Thursday, September 25, 2008 6:54 PM
    •  

All Replies

  • Tuesday, September 23, 2008 6:52 AM
     
     
    In SP_DEVINFO_DATA, the Reserved member should be an IntPtr, not a ulong. That affects the struct size on 32-bit systems.

    Also, in the SetupDiOpenDeviceInterface function declaration, I'd change the type of the DevicePath parameter to string. The way you're calling it now (with IntPtr as the parameter type and an inline call to Marshal.StringToHGlobalAnsi) is leaking memory.

    Mattias, C# MVP
  • Tuesday, September 23, 2008 6:59 PM
     
     
    I tried to replace the ulong by an IntPtr and set DevicePath to be a string.

    but unfortunately the problem remains the same.

    Regards.
  • Wednesday, September 24, 2008 8:53 PM
     
     
    Here are my findings.
    I found the problem but I don't know how to solve it, please help me understand what I'm doing wrong.

    This code in C++ works:

    SP_DEVICE_INTERFACE_DATA DeviceInterfaceData;
    DeviceInterfaceData.cbSize =
    sizeof(DeviceInterfaceData);[...]
    if (SetupDiOpenDeviceInterface(
     DeviceInfoSet,
     lpdbv2->dbcc_name,
     0,
     &DeviceInterfaceData
     )== 0)

    I debug it using Windbg and I endup with this line of assembly that checks the size of DeviceInterfaceData.cbSize

    eax=76d9c680 ebx=76d9c65c ecx=0012fa14 edx=00000000 esi=00000000 edi=001e2c28
    eip=76cbca46 esp=0012f460 ebp=0012f464 iopl=0         nv up ei pl zr na pe nc
    cs=001b  ss=0023  ds=0023  es=0023  fs=003b  gs=0000             efl=00000246
    SETUPAPI!DeviceInterfaceDataFromNode+0x3a:
    76cbca46 83391c          cmp     dword ptr [ecx],1Ch  ds:0023:0012fa14=0000001c
    0:000> dd ecx
    0012fa14  0000001c cccccccc cccccccc cccccccc
    0012fa24  cccccccc cccccccc cccccccc cccccccc
    0012fa34  cccccccc 001e2c28 cccccccc cccccccc
    0012fa44  001e22f8 cccccccc cccccccc cccccccc
    0012fa54  cccccccc cccccccc 001e22f8 cccccccc
    0012fa64  cccccccc cccccccc cccccccc cccccccc
    0012fa74  cccccccc cccccccc cccccccc cccccccc
    0012fa84  cccccccc cccccccc cccccccc cccccccc


    Now I do the same in C#:
    SP_DEVINFO_DATA DeviceInfoData = new SP_DEVINFO_DATA(); // Note it should be SP_DEVICE_INTERFACE_DATA but the structures are identical
    // DeviceInfoData.cbSize = Marshal.SizeOf(DeviceInfoData);
    DeviceInfoData.cbSize = 28;
    if (!SetupDiOpenDeviceInterfaceA(
        hDev,
        // dvi.dbcc_name,
        @"\\?\USB#VID_07AB&PID_FCCA#FW520_200000000000B703#{a5dcbf10-6530-11d2-901f-00c04fb951ed}",
        0,
        ref DeviceInfoData))

    When I debug it using windbg:
    0:000> t
    Breakpoint 1 hit
    eax=76d9c680 ebx=76d9c65c ecx=002aeaac edx=00000000 esi=00000000 edi=005f70c0
    eip=76cbca46 esp=002ae820 ebp=002ae824 iopl=0         nv up ei pl zr na pe nc
    cs=001b  ss=0023  ds=0023  es=0023  fs=003b  gs=0000             efl=00000246
    setupapi!DeviceInterfaceDataFromNode+0x3a:
    76cbca46 83391c          cmp     dword ptr [ecx],1Ch  ds:0023:002aeaac=00612a18

    0:000> dd 002aeaac
    002aeaac  00612a18 00196fd8 5c3f5c5c 23425355
    002aeabc  5f444956 42413730 44495026 4343465f
    002aeacc  57462341 5f303235 30303032 30303030
    002aeadc  30303030 33303742 35617b23 66626364
    002aeaec  362d3031 2d303335 32643131 3130392d
    002aeafc  30302d66 66343063 31353962 007d6465
    002aeb0c  00196c00 002aeb24 79e7b080 00000006
    002aeb1c  00196ca8 00000006 002aeb30 79e7b06b
    0:000> dd poi(ecx)
    00612a18  0000001c 00000000 00000000 00000000
    00612a28  00000000 00000000 00000000 abababab
    00612a38  abababab feeefeee 00000000 00000000
    00612a48  63574a97 00000e3e 00612ae0 00581fc0

    Then it fails and returns 0x6f8 ERROR_INVALID_USER_BUFFER

    To summarise,
    In the C++ code, ecx contains the length of the structure.
    In C#, ecx contains the memory address of the length of the structure.

    I stuck here and don't knwo why it happens and how to solve it.

    Thanks in advance.

  • Thursday, September 25, 2008 11:02 AM
     
     Answered
    Hmm,

    I'll try this evening to replace the class SP_DEVINFO_DATA by a struct.

    Hope this will do the trick.

    • Marked As Answer by ilinfoMVP Thursday, September 25, 2008 6:54 PM
    •  
  • Thursday, September 25, 2008 6:53 PM
     
     
    ...

    And that works.
    • Marked As Answer by ilinfoMVP Thursday, September 25, 2008 6:54 PM
    • Unmarked As Answer by ilinfoMVP Thursday, September 25, 2008 6:54 PM
    •