2009-08-24 5 views
5

Sto riscontrando qualche problema nel marshalling di un puntatore a un array di stringhe. Sembra innocuo come questo:Puntatore Marshalling a una matrice di stringhe

typedef struct 
{ 
    char* listOfStrings[100]; 
} UnmanagedStruct; 

Questo in realtà è inserito all'interno di un altro struttura come questa:

typedef struct 
{ 
    UnmanagedStruct umgdStruct; 
} Outerstruct; 

codice non gestito richiama in codice gestito e restituisce Outerstruct come IntPtr con la memoria allocata e valori compilato .

mondo Gestito:

[StructLayout(LayoutKind.Sequential)] 
public struct UnmanagedStruct 
{ 
    [MarshalAs(UnmanagedType.LPArray, ArraySubType=UnmanagedType.LPStr, SizeConst=100)] 
    public string[] listOfStrings; 
} 

[StructLayout(LayoutKind.Sequential)] 
public struct Outerstruct 
{ 
    public UnmanagedStruct ums; 
} 

public void CallbackFromUnmanagedLayer(IntPtr outerStruct) 
{ 
    Outerstruct os = Marshal.PtrToStructure(outerStruct, typeof(Outerstruct)); 
    // The above line FAILS! it throws an exception complaining it cannot marshal listOfStrings field in the inner struct and that its managed representation is incorrect! 
} 

Se cambio listOfStrings in modo che sia semplicemente un IntPtr, Marshal.PtrToStructure funziona ma ora non riesco a estrarre listOfStrings ed estrarre le stringhe una alla volta.

risposta

1

ok .. mi sembra di aver capito di lavorare. Dovrebbe essere il marshalling come IntPtr []

Questo sembra funzionare:

[StructLayout(LayoutKind.Sequential)] 
public struct UnmanagedStruct 
{ 
    [MarshalAs(UnmanagedType.ByValArray, SizeConst=100)] 
    public IntPtr[] listOfStrings; 
} 

for (int i = 0; i < 100; ++i) 
{ 
    if (listOfstrings[i] != IntPtr.Zero) 
     Console.WriteLine(Marshal.PtrToStringAnsi(listOfStrings[i])); 
}  
+0

ByValArray == array sul posto, LPArray == un puntatore a un array. Sebbene SizeConst debba ancora funzionare con un LPArray, quindi l'errore nel marshalling era un po 'strano. –

+0

Oh, dovrebbe funzionare anche se hai una stringa pubblica [] listOfStrings, è il ByValArray che fa la differenza credo. –

4

Marshalling tutto tranne una stringa molto semplice è complessa e piena di casi laterali difficili da individuare. Di solito è meglio andare con il percorso sicuro/semplice nella definizione della struct e aggiungere alcune proprietà del wrapper per riordinare un po 'le cose.

In questo caso vorrei andare con la matrice di IntPtr e poi aggiungere una proprietà wrapper che converte le stringhe

[StructLayout(LayoutKind.Sequential)] 
public struct UnmanagedStruct 
{ 
    [MarshalAs(UnmanagedType.LPArray, ArraySubType=UnmanagedType.LPStr, SizeConst=100)] 
    public IntPtr[] listOfStrings; 

    public IEnumerable<string> Strings { get { 
     return listOfStrings.Select(x =>Marshal.PtrToStringAnsi(x)); 
    } 
} 
+0

Jared Grazie per la convalida! Ho postato una risposta alla mia domanda proprio ora prima di vedere la tua. Una domanda: come posso formattare il mio codice nei post? Sembravano tutti incasinati e qualcuno doveva modificarli e correggerli tutto il tempo. – Dilip

+0

@Dilip, seleziona lo snippet di codice e premi CTRL + K. Ciò risolverà la formattazione indentando ogni cosa 4 spazzi – JaredPar

+0

@ Jared: solo un rapido followup. Il codice continua a bombardare se utilizzo UnmanagedType.LPArray. Funziona solo UnmanagedType.ByValArray. Ora capisco che cosa stava suggerendo KeeperOfTheSoul nei suoi commenti. – Dilip