SetupDiOpenDeviceInterface in C#
-
Monday, September 22, 2008 10:27 PMHello,
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
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 AMIn 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 PMI 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 PMHere 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;
sizeof(DeviceInterfaceData);[...]
DeviceInterfaceData.cbSize =
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
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.

