来自C+的C#USB驱动程序+;:SetupDiGetDeviceInterface详细信息

来自C+的C#USB驱动程序+;:SetupDiGetDeviceInterface详细信息,c#,winapi,pinvoke,C#,Winapi,Pinvoke,试图从C#调用SetupDiGetDeviceInterfaceDetail时遇到问题。它总是返回1784错误代码(“提供的用户缓冲区对于请求的操作无效”)。这是我的C#代码: Classe Win32: using System; using System.Collections.Generic; using System.Linq; using System.Runtime.InteropServices; using System.Text; using System.Threading.

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

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);
附加说明:如果在64位计算机上运行,或在强制64位模式下运行,则pDevicePathName指针的上行将引用64位指针,而不是32位指针


IntPtr pDevicePathName=newintptr(detailDataBuffer.ToInt64()+8)

结构是一个可变大小的结构,无法自动编组。你需要自己去做

您需要删除
SP\u设备\u接口\u详细信息\u数据
类型。这对你没用。将
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
时传递
IntPtr.Zero
。然后通过调用
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);
}

对不起,完成了。感谢您的迟来,我们知道Windows的东西发生了变化,包括它们的DLL。你对此有任何更新吗?我尝试了完整的复制/粘贴,少了几个变量,但没有其他内容。即使在将第三个参数的签名更改为IntPtr后,调用3和4结果仍会失败。非常感谢David的帮助和耐心!您不想写入返回的大小。根据MSDN:cbSize成员始终包含数据结构固定部分的大小,而不是反映末尾可变长度字符串的大小。这行应该写下(IntPtr.Size==8?8:4+Marshal.SystemDefaultCharSize),最后一位非常重要!
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);
}