Nel mio codice C# sto provando a recuperare una matrice di strutture da una legacy DLL C++ (il codice che non posso cambiare).Marshalling di una matrice di strutture da C++ a C#?
In quel codice C++, la struttura è definita in questo modo:
struct MyStruct
{
char* id;
char* description;
};
Il metodo che chiamo (get_my_structures) restituisce un puntatore a una matrice di strutture myStruct:
MyStruct* get_my_structures()
{
...
}
C'è un altro metodo che restituisce il numero di strutture, quindi so quante strutture vengono restituite. MyStruct
Nel mio codice C#, ho definito in questo modo:
[StructLayout(LayoutKind.Sequential)]
public class MyStruct
{
[MarshalAsAttribute(UnmanagedType.LPStr)] // <-- also tried without this
private string _id;
[MarshalAsAttribute(UnmanagedType.LPStr)]
private string _description;
}
La firma di interoperabilità si presenta così:
[DllImport("legacy.dll", EntryPoint="get_my_structures")]
public static extern IntPtr GetMyStructures();
Infine, il codice che recupera la matrice di strutture MyStruct assomiglia questo:
int structuresCount = ...;
IntPtr myStructs = GetMyStructures();
int structSize = Marshal.SizeOf(typeof(MyStruct)); // <- returns 8 in my case
for (int i = 0; i < structuresCount; i++)
{
IntPtr data = new IntPtr(myStructs.ToInt64() + structSize * i);
MyStruct ms = (MyStruct) Marshal.PtrToStructure(data, typeof(MyStruct));
...
}
il problema è che solo la prima struttura (uno alla o ffset zero) viene eseguito correttamente il marshalling. Quelli successivi hanno valori falsi nei membri _id e _description. I valori non sono completamente trascinati, o così sembra: sono stringhe da altre posizioni di memoria. Il codice stesso non si blocca.
Ho verificato che il codice C++ in get_my_structures() restituisce dati corretti. I dati non vengono cancellati o modificati per errore durante o dopo la chiamata.
Visto in un debugger, C++ layout della memoria dei dati restituiti assomiglia a questo:
0: id (char*) <---- [MyStruct 1]
4: description (char*)
8: id (char*) <---- [MyStruct 2]
12: description (char*)
16: id (char*) <---- [MyStruct 3]
...
[Aggiornamento 18/11/2009]
Ecco come il codice C++ prepara queste strutture (il codice reale è molto più brutto, ma questo è un primo sufficiente approssimazione):
static char buffer[12345] = {0};
MyStruct* myStructs = (MyStruct*) &buffer;
for (int i = 0; i < structuresCount; i++)
{
MyStruct* ms = <some other permanent address where the struct is>;
myStructs[i].id = (char*) ms->id;
myStructs[i].description = (char*) ms->description;
}
return myStructs;
Certo, il codice di cui sopra fa un po ' brutto casting e copia i puntatori grezzi in giro, ma sembra ancora farlo correttamente. Almeno questo è quello che vedo nel debugger: il precedente (statico) buffer contiene tutti questi puntatori char char * memorizzati uno dopo l'altro, e puntano a posizioni valide (non locali) in memoria.
L'esempio di Pavel mostra che questo è davvero l'unico posto in cui le cose possono andare storte. Proverò ad analizzare cosa succede in quei punti "finali" in cui le stringhe sono realmente, non nelle posizioni in cui i puntatori vengono memorizzati.
Se provo a utilizzare StringBuilder, ottengo ArgumentException quando provo a farlo: int itemSize = Marshal.SizeOf (typeof (MyStruct)); Il messaggio di errore è "Tipo" Non è possibile eseguire il marshalling di MyStruct come struttura non gestita, non è possibile calcolare dimensioni o offset significativi ". – vladimir
@vladimir: stai usando 'UnmanagedType.LPTStr'?Inoltre: prova a specificare il 'CharSet' come gli altri suggeriti. – fretje
"Le stringhe sono membri validi delle strutture, tuttavia i buffer StringBuilder non sono validi nelle strutture." -> http://msdn.microsoft.com/en-us/library/s9ts558h.aspx#Mtps_DropDownFilterText –