2017-08-10 2 views
1

J'essaye de marshal la fonction SetupGetInfInformation de Windows 'SetupAPI à C#.Marshalling SetupGetInfInformation de C++ à C#

J'ai défini les (mobilisées) structures nécessaires comme suit:

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

Et puis je tente d'utiliser la fonction, d'une part en passant 0 pour l'argument ReturnBufferSize, pour demander le tampon de taille nécessaire :

IntPtr ip = new IntPtr(); 

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

cela fonctionne avec succès et retourne constamment pour cette INF spécifique, donc je suis convaincu que ce bien fonctionne correctement.

Cependant, l'étape suivante (allocation appropriée du tampon et appel de la fonction une seconde fois pour remplir le tampon avec les données demandées) est l'endroit où j'ai des difficultés.

Actuellement, je suis en train ce (suite ci-dessus):

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

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

Cette plante toujours vshost.exe avec un « vshost32.exe a cessé de fonctionner » dialogue, ce qui me permet de déboguer ou d'un programme Fermer , et aucune autre information utile.

J'ai aussi essayé de changer la signature de la fonction SetupGetInfInformation mobilisées pour refléter SP_INF_INFORMATION (au lieu de IntPtr, voir la ligne commentée ci-dessus), et ce faisant, on peut encore appeler constamment SetupGetInfInformation la première fois et recevoir . Je me suis alors tenté d'allouer suffisamment d'espace dans un tampon pour elle, et que passer dans le deuxième appel, comme suit:

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

Ce crashe également vshost.exe de la même façon que ci-dessus.

Il me semble évident que je n'alloue pas un tampon approprié pour le second appel, mais il se peut que je manque également d'autres éléments requis.

Est-ce que quelqu'un me dirigerait dans la bonne direction? Je n'ai encore trouvé aucune aide sur cette fonction spécifique sur StackOverflow, et la recherche de matrices de tailles variables et l'utilisation du Maréchal pour allouer/déplacer la mémoire ont été utiles (et instructives), mais ne m'ont pas dépassé problème.

EDIT:

Merci à Dave Cluderay et Simon Mourier. Je l'ai accepté la solution de Simon comme la réponse, mais je voulais donner mon code fini de montrer (complètement) comment maréchal SetupGetInfInformation et de libérer la mémoire non gérée:

[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

Vous n'êtes pas familier avec cette API non plus, mais je pense changer '[In, Out] ref IntPtr ReturnBuffer' à' [In, Out] IntPtr ReturnBuffer' pourrait aider (c'est à dire laisser tomber le 'ref'). –

Répondre

1

Ce type de structure est de taille variable (en raison de le dernier membre). Vous devez appeler l'API une fois pour obtenir la taille et une seconde fois avec le tampon correctement alloué.

Voici une définition qui fonctionne:

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