2017-08-10 10 views
1

SetupGetInfInformation関数をWindowsのSetupAPIからC#にマーシャリングしようとしています。私は、必要に応じて(整列化)の構造を定義したSetupGetInfInformationをC++からC#にマーシャリングする

は次のとおりです。

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

そして私は、必要なサイズのバッファを要求するために、まずReturnBufferSize引数に0を渡すことによって、機能を利用しようとしています:

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ためを返しますので、私はこのくらいが適切に機能している確信しています。

しかし、次のステップ(適切にバッファを割り当てて、要求されたデータでバッファを満たすために関数を2回目に呼び出す)は難しいところです。

現在、私は(上記以下)、これをしようとしている:

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は動作を停止しました」その他の有用な情報はありません。

Iは、(代わりのIntPtrの、上記コメントアウトされた行を参照)SP_INF_INFORMATIONを反映するように整列SetupGetInfInformation関数シグネチャを変更しようとしている、そうすることで、依然として一貫SetupGetInfInformation最初に呼び出し、を受信することができます。私はそれのためのバッファに十分なスペースを割り当て、次のように、2回目の呼び出しでそれを渡そうとしました:

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をクラッシュします。

2番目の呼び出しに適切なバッファを割り当てていないことは明らかですが、他の必須項目も欠落している可能性があります。

誰かが正しい方向に私を向けるのでしょうか?私はStackOverflowのこの特定の関数についてまだ助けを見つけていません。そして、可変サイズの配列を適切にマーシャリングし、マーシャルを使用してメモリを割り当て/シフトすることは役に立ちました(そして教育しています)が、問題。

EDIT:デイブ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

が、私は変更すると思うだろう 'のIntPtr ReturnBuffer'に' [IN、OUT] REF [IN、OUT]のIntPtr ReturnBuffer'かもしれませんが:ここ

は作品の定義ですヘルプ(つまり、 'ref'を落とす)。 –

答えて

1

この種の構造は、(理由の可変サイズであります最後のメンバー)。サイズを取得するにはAPIを1回呼び出す必要があります。また、適切に割り当てられたバッファーを使用して2回目に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 
    ); 
のいずれか、このAPIに精通していない
関連する問題