2017-08-10 110 views
1

我試圖從Windows的SetupAPI到C#的編號SetupGetInfInformation函數。從C++到C編組SetupGetInfInformation#

我定義必要的(編組)的結構如下:

internal const uint INFINFO_INF_NAME_IS_ABSOLUTE = 2; 

    [StructLayout(LayoutKind.Sequential)] 
    internal struct SP_INF_INFORMATION 
    { 
     public uint InfStyle; 
     public uint InfCount; 
     [MarshalAs(UnmanagedType.ByValArray, SizeConst = 1)] 
     public byte[] VersionData; 
    } 

    [DllImport("setupapi.dll", SetLastError = true)] 
    internal static extern bool SetupGetInfInformation(
     [In] string InfSpec, 
     [In] uint SearchControl, 
     //[In, Out] ref SP_INF_INFORMATION ReturnBuffer, 
     [In, Out] ref IntPtr ReturnBuffer, 
     [In] uint ReturnBufferSize, 
     [In, Out] ref uint RequiredSize 
     ); 

然後我嘗試利用該功能,首先通過使0爲ReturnBufferSize參數,以請求所需的大小的緩衝區:

IntPtr ip = new IntPtr(); 

bool result = SetupAPI.SetupGetInfInformation(
    @"D:\TestDriverFile\intcoed.inf", 
    SetupAPI.INFINFO_INF_NAME_IS_ABSOLUTE, // 2 
    ref ip, 
    0, 
    ref i 
    ); 

這個成功的作品,始終返回這個特定的INF,所以我相信這麼多工作正常。

但是,下一步(正確分配緩衝區並第二次調用該函數以填充請求的數據的緩衝區)是我遇到困難的地方。

目前,我想這(按照以上):

ip = Marshal.AllocHGlobal((int)i); 

result = SetupAPI.SetupGetInfInformation(
    @"D:\TestDriverFile\intcoed.inf", 
    SetupAPI.INFINFO_INF_NAME_IS_ABSOLUTE, 
    ref ip, 
    i, 
    ref i 
    ); 

這始終崩潰vshost.exe與「vshost32.exe已停止工作」的對話,這讓我調試或關閉程序,並沒有其他有用的信息。

我也試圖改變編組SetupGetInfInformation函數簽名,以反映SP_INF_INFORMATION(而不是IntPtr的,看到上面的註釋掉的行),並在這樣做仍然可以一致稱SetupGetInfInformation在第一時間和接收。然後我試圖在緩衝器分配足夠的空間爲它,並通過在第二呼叫,如下所示:

SetupAPI.SP_INF_INFORMATION buf = new SetupAPI.SP_INF_INFORMATION(); 

// Make the first call, passing in ref buf, receive 422 as a response. 

buf.VersionData = new byte[i]; 

result = SetupAPI.SetupGetInfInformation(
    @"D:\TestDriverFile\intcoed.inf", 
    SetupAPI.INFINFO_INF_NAME_IS_ABSOLUTE, 
    ref buf, 
    i, 
    ref i 
    ); 

這也崩潰vshost.exe以相同的方式如上述。

對我來說看起來很明顯,我沒有爲第二次調用分配適當的緩衝區,但我可能會錯過其他所需的項目。

有人會指出我在正確的方向嗎?在StackOverflow中,我還沒有在這個特定的函數上找到任何幫助,尋找正確編組可變大小數組並且使用Marshal來分配/移位內存有幫助(和教育),但是沒有讓我過去問題。

編輯:

感謝戴夫Cluderay和西蒙Mourier。我已經接受了西蒙的溶液,作爲答案,但希望提供我完成的代碼來顯示(完全)如何封送SetupGetInfInformation並釋放非託管內存:

[StructLayout(LayoutKind.Sequential)] 
    public struct SP_INF_INFORMATION 
    { 
     public int InfStyle; 
     public int InfCount; 
     [MarshalAs(UnmanagedType.ByValArray, SizeConst = 1)] 
     public byte[] VersionData; 
    } 

    [DllImport("setupapi.dll", CharSet = CharSet.Unicode, SetLastError = true)] 
    public static extern bool SetupGetInfInformation(
     string InfSpec, 
     int SearchControl, 
     IntPtr ReturnBuffer, 
     int ReturnBufferSize, 
     ref int RequiredSize 
     ); 

    public static void SetupGetInfInformation_NET(
     string infPath, 
     ref SP_INF_INFORMATION infInfo 
     ) 
    { 
     infInfo = new SP_INF_INFORMATION(); 
     int size = 0; 
     IntPtr ip = new IntPtr(); 

     try 
     { 
      if (!SetupAPI.SetupGetInfInformation(infPath, SetupAPI.INFINFO_INF_NAME_IS_ABSOLUTE, IntPtr.Zero, 0, ref size)) 
       throw new Exception("Error calling SetupGetInfInformation() for required buffer size.", new Win32Exception(Marshal.GetLastWin32Error())); 

      if (size == 0) 
       return; 

      ip = Marshal.AllocHGlobal(size); 

      if (!SetupAPI.SetupGetInfInformation(infPath, SetupAPI.INFINFO_INF_NAME_IS_ABSOLUTE, ip, size, ref size)) 
       throw new Exception("Error calling SetupGetInfInformation() to retrieve INF information.", new Win32Exception(Marshal.GetLastWin32Error())); 

      infInfo.InfStyle = Marshal.ReadInt32(ip, 0); // The first 4-byte int is for InfStyle. 
      infInfo.InfCount = Marshal.ReadInt32(ip, 4); // The second 4-byte int is for InfCount. 

      // Marshal the data from the unmanaged buffer to a managed buffer. 
      byte[] buf = new byte[size]; 
      Marshal.Copy(ip, buf, 0, size); 

      // Initialize VersionData to be large enough to hold the VersionData from the managed buffer. We remove 8 bytes (4 for InfStyle, 4 for InfCount.) 
      infInfo.VersionData = new byte[size - 8]; 

      // Copy the VersionData from the managed buffer into infInfo.VersionData, offsetting 8 bytes for InfStyle and InfCount. 
      Array.Copy(buf, 8, infInfo.VersionData, 0, size - 8); 
     } 
     finally 
     { 
      Marshal.FreeHGlobal(ip); 
     } 
    } 
+2

不熟悉這個API下去,但我想改變'[輸入,輸出]裁判的IntPtr ReturnBuffer'到'[輸入,輸出] IntPtr的可能ReturnBuffer'幫助(即放棄'ref')。 –

回答

1

這種結構是可變大小的(因爲最後一名成員)。您必須調用一次API來獲取大小,並且再次使用正確分配的緩衝區。

這是一個可行的定義:

int size = 0; 
if (!SetupGetInfInformation(@"D:\TestDriverFile\intcoed.inf", INFINFO_INF_NAME_IS_ABSOLUTE, 
    IntPtr.Zero, 0, ref size)) // pass NULL the first time 
    throw new Win32Exception(Marshal.GetLastWin32Error()); 

// now, size contains the required buffer size 
var ptr = Marshal.AllocHGlobal(size); 
if (!SetupGetInfInformation(@"D:\TestDriverFile\intcoed.inf", INFINFO_INF_NAME_IS_ABSOLUTE, 
    ptr, size, ref size)) 
    throw new Win32Exception(Marshal.GetLastWin32Error()); 

// now, ptr contains a pointer to a SP_INF_INFORMATION structure 
var InfStyle = Marshal.ReadInt32(ptr); 
var InfCount = Marshal.ReadInt32(ptr, 4); 
... etc... 

[DllImport("setupapi.dll", SetLastError = true)] 
internal static extern bool SetupGetInfInformation(
    string InfSpec, 
    int SearchControl, 
    IntPtr ReturnBuffer, 
    int ReturnBufferSize, 
    ref int RequiredSize 
    );