2017-08-10 5 views
1

Ich versuche, die SetupGetInfInformation Funktion von Windows SetupAPI zu C# zu marshalieren.Marshalling SetupGetInfInformation von C++ zu C#

Ich habe die (vermarshallten) Strukturen definiert, die notwendig wie folgt:

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

Und dann ich bin versucht, die Funktion zu nutzen, zunächst 0 für das ReturnBufferSize Argument übergeben, um die Größe Puffer notwendig anfordern :

IntPtr ip = new IntPtr(); 

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

Dies funktioniert erfolgreich und liefert konsequent für diese spezifische INF , also bin ich zuversichtlich, dass dies viel richtig funktioniert.

Der nächste Schritt (den Puffer richtig zuordnen und die Funktion ein zweites Mal aufrufen, um den Puffer mit den angeforderten Daten zu füllen) ist, wo ich Schwierigkeiten habe.

Derzeit bin ich versucht, dies (nach oben):

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

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

Diese konsequent stürzt vshost.exe mit einem „vshost32.exe funktioniert nicht mehr“ Dialog, der mich zu Debug oder Schließen Programm erlaubt und keine andere hilfreiche Information.

ich auch die vermarshallten SetupGetInfInformation Funktion Signatur zu ändern versucht haben SP_INF_INFORMATION (statt IntPtr finden Sie in der kommentierten-out Linie oben) zu reflektieren und dabei noch SetupGetInfInformation zum ersten Mal konsequent nennen und erhalten. Ich versuchte dann dafür genügend Platz in einem Puffer zuzuweisen, und übergeben, dass in dem zweiten Aufruf, wie folgt:

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

Dies auch, wie oben vshost.exe in der gleichen Weise abstürzt.

Es scheint mir, dass ich nicht einen richtigen Puffer für den zweiten Anruf zuweisen, aber ich vermisse auch andere erforderliche Elemente.

Würde mir jemand in die richtige Richtung zeigen? Ich habe noch keine Hilfe zu dieser speziellen Funktion hier in StackOverflow gefunden, und die Suche nach dem richtigen Marshalling von Arrays variabler Größe und die Verwendung des Marshals zur Zuweisung/Verschiebung von Speicher war hilfreich (und erzieherisch), hat mich aber nicht darüber hinweggebracht Problem.

EDIT:

Dank Dave Cluderay und Simon Mourier. Ich habe Simon-Lösung als Antwort akzeptiert, aber wollte meine fertigen Code bereitzustellen zu zeigen (komplett), wie SetupGetInfInformation Marschall und nicht verwalteten Speicher freizugeben:

[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

nicht vertraut mit dieser API entweder, aber ich würde denken, zu ändern '[In, Out] ref IntPtr ReturnBuffer' auf' [In, Out] IntPtr ReturnBuffer' könnte help (dh die 'ref' fallen lassen). –

Antwort

1

Diese Art von Struktur ist von variabler Größe (wegen das letzte Mitglied). Sie müssen die API einmal aufrufen, um die Größe zu erhalten, und ein zweites Mal mit dem richtig zugewiesenen Puffer. Hier

ist eine Definition, die funktioniert:

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