2015-06-22 140 views
1

我試圖從C#調用SetupDiGetDeviceInterfaceDetail時遇到問題。它始終返回1784錯誤代碼(「提供的用戶緩衝區對於請求的操作無效」)。這是我的C#代碼:來自C++的C#USB驅動程序:SetupDiGetDeviceInterfaceDetail

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); 
+0

對不起,完成。謝謝 –

回答

2

的結構是一個可變大小結構,不能自動編組。你需要自己做。

您需要刪除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); 
} 
+2

非常感謝大衛的幫助和耐心! –

+1

你不想編寫返回的大小。每個MSDN:cbSize成員始終包含數據結構的固定部分的大小,而不是最終反映可變長度字符串的大小。該行應寫入(IntPtr.Size == 8?8:4 + Marshal.SystemDefaultCharSize) – Jeff

+0

最後一點非常重要! – KansaiRobot