来自C ++的C#USB驱动程序:SetupDiGetDeviceInterfaceDetail

我试图从C#调用SetupDiGetDeviceInterfaceDetail时遇到问题。 它始终返回1784错误代码(“提供的用户缓冲区对请求的操作无效”)。 这是我的C#代码:

Guid GUID_DEVINTERFACE_DFU = new Guid(0x3fe809ab, 0xfb91, 0x4cb5, 0xa6, 0x43, 0x69, 0x67, 0x0d, 0x52,0x36,0x6e); Guid classGuid = GUID_DEVINTERFACE_DFU; 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 { SP_DEVINFO_DATA devInfoData = new SP_DEVINFO_DATA(); devInfoData.cbSize = (uint)Marshal.SizeOf(typeof(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 = (uint)Marshal.SizeOf(ifData); ifData.Flags = 0; ifData.InterfaceClassGuid = Guid.Empty; ifData.Reserved = IntPtr.Zero; bool result2 = Win32.SetupDiEnumDeviceInterfaces(hDevInfo, IntPtr.Zero, ref classGuid, i, ifData); if(result2 == false) { int error = Marshal.GetLastWin32Error(); if (error != Win32.ERROR_NO_MORE_ITEMS) throw new Win32Exception(error); } uint needed; // This returns: needed=160, result3=false and error=122 ("The data area passed to a system call is too small") bool result3 = Win32.SetupDiGetDeviceInterfaceDetail(hDevInfo, ifData, null, 0, out needed, null); if(result3 == false) { int error = Marshal.GetLastWin32Error(); } IntPtr detailDataBuffer = IntPtr.Zero; SP_DEVICE_INTERFACE_DETAIL_DATA ifDetailsData = new SP_DEVICE_INTERFACE_DETAIL_DATA(); ifDetailsData.devicePath = new byte[needed - 4]; ifDetailsData.cbSize = (uint)Marshal.SizeOf(ifDetailsData); uint nBytes = needed; // This returns always: error = 1784 bool result4 = Win32.SetupDiGetDeviceInterfaceDetail(hDevInfo, ifData, ifDetailsData, nBytes, out needed, null); if (result4 == false) { int error = Marshal.GetLastWin32Error(); if (error != Win32.ERROR_NO_MORE_ITEMS) throw new Win32Exception(error); } } 

Classe Win32:

 using System; using System.Collections.Generic; using System.Linq; using System.Runtime.InteropServices; using System.Text; using System.Threading.Tasks; namespace USB_test { [StructLayout(LayoutKind.Sequential)] public class SP_DEVINFO_DATA { public uint cbSize; public Guid classGuid; public uint devInst; public IntPtr reserved; }; [StructLayout(LayoutKind.Sequential, CharSet = CharSet.Auto)] public class SP_DEVICE_INTERFACE_DETAIL_DATA { public uint cbSize; public byte[] devicePath; } [StructLayout(LayoutKind.Sequential, CharSet=CharSet.Auto)] public class SP_DEVICE_INTERFACE_DATA { public uint cbSize; public Guid InterfaceClassGuid; public uint Flags; public IntPtr Reserved; } public class Win32 { public static uint ANYSIZE_ARRAY = 1000; [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); [DllImport(@"setupapi.dll", SetLastError = true, CharSet = CharSet.Auto)] public static extern Boolean SetupDiEnumDeviceInterfaces(IntPtr hDevInfo, IntPtr devInfo, ref Guid interfaceClassGuid, uint memberIndex, SP_DEVICE_INTERFACE_DATA deviceInterfaceData); [DllImport(@"setupapi.dll", CharSet = CharSet.Auto, SetLastError = true)] public static extern Boolean SetupDiGetDeviceInterfaceDetail(IntPtr hDevInfo, SP_DEVICE_INTERFACE_DATA deviceInterfaceData, SP_DEVICE_INTERFACE_DETAIL_DATA deviceInterfaceDetailData, uint deviceInterfaceDetailDataSize, out uint requiredSize, SP_DEVINFO_DATA deviceInfoData); public const int DIGCF_PRESENT = 0x02; public const int DIGCF_DEVICEINTERFACE = 0x10; public const int SPDRP_DEVICEDESC = (0x00000000); public const long ERROR_NO_MORE_ITEMS = 259L; } } 

如果它可以帮助某人,这就是解决方案:

 IntPtr detailDataBuffer = Marshal.AllocHGlobal((int)needed); Marshal.WriteInt32(detailDataBuffer, (IntPtr.Size == 4) ? (4 + Marshal.SystemDefaultCharSize) : 8); uint nBytes = needed; bool result4 = Win32.SetupDiGetDeviceInterfaceDetail(hDevInfo, ifData, detailDataBuffer, nBytes, out needed, null); if (result4 == false) { int error = Marshal.GetLastWin32Error(); if (error != Win32.ERROR_NO_MORE_ITEMS) throw new Win32Exception(error); } IntPtr pDevicePathName = new IntPtr(detailDataBuffer.ToInt32() + 4); String devicePathName = Marshal.PtrToStringAuto(pDevicePathName); 

结构是一个可变大小的结构,不能自动编组。 你自己需要这样做。

您需要删除SP_DEVICE_INTERFACE_DETAIL_DATA类型。 对你没用。 将SetupDiGetDeviceInterfaceDetail的声明更改为:

 [DllImport(@"setupapi.dll", CharSet = CharSet.Auto, SetLastError = true)] public static extern Boolean SetupDiGetDeviceInterfaceDetail( IntPtr hDevInfo, SP_DEVICE_INTERFACE_DATA deviceInterfaceData, IntPtr deviceInterfaceDetailData, uint deviceInterfaceDetailDataSize, out uint requiredSize, SP_DEVINFO_DATA deviceInfoData ); 

在第一次调用SetupDiGetDeviceInterfaceDetail 。 然后通过调用Marshal.AllocHGlobal分配所需大小的缓冲区。 然后将大小写入该缓冲区的前4个字节。 然后再次调用SetupDiGetDeviceInterfaceDetail

这些方面的东西:

 bool result3 = Win32.SetupDiGetDeviceInterfaceDetail(hDevInfo, ifData, IntPtr.Zero, 0, out needed, null); if(!result3) { int error = Marshal.GetLastWin32Error(); } // expect that result3 is false and that error is ERROR_INSUFFICIENT_BUFFER = 122, // and needed is the required size IntPtr DeviceInterfaceDetailData = Marshal.AllocHGlobal((int)needed); try { uint size = needed; Marshal.WriteInt32(DeviceInterfaceDetailData, (int)size); bool result4 = Win32.SetupDiGetDeviceInterfaceDetail(hDevInfo, ifData, DeviceInterfaceDetailData, size, out needed, null); if(!result4) { int error = Marshal.GetLastWin32Error(); } // do whatever you need with DeviceInterfaceDetailData } finally { Marshal.FreeHGlobal(DeviceInterfaceDetailData); }