C++中的C#USB驱动程序:SetupDiGetDeviceInterfaceDetail
本文关键字:SetupDiGetDeviceInterfaceDetail 驱动程序 C#USB 中的 C++ | 更新日期: 2023-09-27 17:59:01
我在尝试从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);
}
}
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);
附加注意:如果在64位机器上运行,或在强制64位模式下运行,pDevicePathName指针的上面一行将引用64位指针,而不是32
IntPtr pDevicePathName=新IntPtr(detailDataBuffer.ToInt64()+8);
结构是一个可变大小的结构,不能自动整理。你需要自己去做。
您需要删除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
);
将第一次调用中的IntPtr.Zero
传递给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);
}
对我来说,David Hoffmann的答案是行不通的。但他启发了我这个解决方案:
IntPtr buffer = Marshal.AllocHGlobal((int)requiredSize);
int cbSize = sizeof(DWORD) + 2 * sizeof(CHAR); // cbSize + empty DevicePath
Marshal.WriteInt32(buffer, cbSize);
deviceInterfaceInfoData.cbSize = (DWORD)Marshal.SizeOf(deviceInterfaceInfoData);
if (!SetupDiGetDeviceInterfaceDetail(
deviceInfoSet,
ref deviceInterfaceData,
buffer,
requiredSize,
out requiredSize,
ref deviceInterfaceInfoData))
throw new Win32Exception(error);
int devicePathSize = (int)requiredSize - sizeof(DWORD); // cbSize
char[] devicePathChars = new char[devicePathSize / sizeof(char)];
int offset = sizeof(DWORD); // cbSize
Marshal.Copy(IntPtr.Add(buffer, offset), devicePathChars, 0, devicePathChars.Length);
string devicePath = new(devicePathChars, 0, devicePathChars.Length - 1); // Remove NULL terminator
Marshal.FreeHGlobal(buffer);