I've a problem trying to call SetupDiEnumDeviceInterfaces from C#. It always returns 1784 error code ("The supplied user buffer is not valid for the requested operation"). On the same machine if I execute the corrisponding C++ code the function is successful. This is my C# code:
Guid classGuid = GUID_DEVINTERFACE_DFU; // Guid(0x3fe809ab, 0xfb91, 0x4cb5, 0xa6, 0x43, 0x69, 0x67, 0x0d, 0x52,0x36,0x6e)
IntPtr hDevInfo = Win32.SetupDiGetClassDevs(ref classGuid, IntPtr.Zero, IntPtr.Zero, Win32.DIGCF_DEVICEINTERFACE | Win32.DIGCF_PRESENT);
if (hDevInfo.ToInt32() == Win32.INVALID_HANDLE_VALUE)
{
Console.WriteLine("read hardware information error");
}
else
{
uint i = 0;
SP_DEVINFO_DATA devInfoData = new SP_DEVINFO_DATA();
devInfoData.classGuid = Guid.Empty;
devInfoData.devInst = 0;
devInfoData.reserved = IntPtr.Zero;
bool result = Win32.SetupDiEnumDeviceInfo(hDevInfo, i, devInfoData);
if (false == result)
{
int error = Marshal.GetLastWin32Error();
if (error != Win32.ERROR_NO_MORE_ITEMS)
throw new Win32Exception(error);
}
SP_DEVICE_INTERFACE_DATA ifData = new SP_DEVICE_INTERFACE_DATA();
ifData.cbSize = 50;
ifData.Flags = 0;
ifData.InterfaceClassGuid = Guid.Empty;
ifData.Reserved = IntPtr.Zero;
bool result2 = Win32.SetupDiEnumDeviceInterfaces(hDevInfo, IntPtr.Zero, ref classGuid, i, ref ifData);
if(result2 == false)
{
int error = Marshal.GetLastWin32Error();
if (error != Win32.ERROR_NO_MORE_ITEMS)
throw new Win32Exception(error);
}
}
[StructLayout(LayoutKind.Sequential)]
public class SP_DEVINFO_DATA
{
public uint cbSize = (uint)Marshal.SizeOf(typeof(SP_DEVINFO_DATA));
public Guid classGuid;
public uint devInst;
public IntPtr reserved;
};
[StructLayout(LayoutKind.Sequential, CharSet=CharSet.Auto)]
public class SP_DEVICE_INTERFACE_DATA
{
public uint cbSize = (uint)Marshal.SizeOf(typeof(SP_DEVICE_INTERFACE_DATA));
public Guid InterfaceClassGuid;
public uint Flags;
public IntPtr Reserved;
}
public class Win32
{
[DllImport("setupapi.dll", SetLastError = true)]
public static extern IntPtr SetupDiGetClassDevs(ref Guid ClassGuid, IntPtr Enumerator, IntPtr hwndParent, uint Flags);
[DllImport("setupapi.dll", SetLastError = true)]
public static extern Boolean SetupDiEnumDeviceInfo(IntPtr lpInfoSet, UInt32 dwIndex, SP_DEVINFO_DATA devInfoData);
public const int DIGCF_PRESENT = 0x02;
public const int DIGCF_DEVICEINTERFACE = 0x10;
public const long ERROR_NO_MORE_ITEMS = 259L;
}
And this is my C++ code:
GUID Guid=GUID_DFU; // { 0x3fe809ab, 0xfb91, 0x4cb5, { 0xa6, 0x43, 0x69, 0x67, 0x0d, 0x52,0x36,0x6e } }
HDEVINFO info = SetupDiGetClassDevs(&Guid, NULL, NULL, DIGCF_PRESENT | DIGCF_INTERFACEDEVICE);
if (info!=INVALID_HANDLE_VALUE)
{
SP_INTERFACE_DEVICE_DATA ifData;
ifData.cbSize=sizeof(ifData);
bool result = SetupDiEnumDeviceInterfaces(info, NULL, &Guid, devIndex, &ifData);
}
The code is running on Windows 8.1 x64 and the C# application platform is x86.
You declare
SP_DEVICE_INTERFACE_DATA
to be a class in the C# code. That means that it is already a reference. You then pass theSP_DEVICE_INTERFACE_DATA
instance by reference toSetupDiEnumDeviceInfo
. Which means that you are passingSP_DEVICE_INTERFACE_DATA**
rather thanSP_DEVICE_INTERFACE_DATA*
, in C++ terms.Either:
SP_DEVICE_INTERFACE_DATA
to be a struct, orSP_DEVICE_INTERFACE_DATA
as a class, but pass it by value. That is, as you did withSP_DEVINFO_DATA
andSetupDiEnumDeviceInfo
.You are setting
cbSize
incorrectly. It should be:There's no point in setting the other members of
SP_DEVICE_INTERFACE_DATA
since they are ignored by the function that you call. That function's job is to populate those members.There may be other problems. It's hard to say because you have not posted your actual code. The C++ code does not compile, and the C# code is incomplete.