Nota: La soluzione di lavoro finale è dopo la modifica!Passare la struttura da C++ non gestito a C#
Spero che qualcuno possa aiutarmi con un problema che ho cercato di risolvere negli ultimi giorni.
Sto provando a passare una struct da una DLL C++ non gestita a uno script C#. Questo è quello che ho finora:
C++
EXPORT_API uchar *detectMarkers(...) {
struct markerStruct {
int id;
} MarkerInfo;
uchar *bytePtr = (uchar*) &MarkerInfo;
...
MarkerInfo.id = 3;
return bytePtr;
}
C#
[DllImport ("UnmanagedDll")]
public static extern byte[] detectMarkers(...);
...
[StructLayout(LayoutKind.Explicit, Size = 16, Pack = 1)]
public struct markerStruct
{
[MarshalAs(UnmanagedType.U4)]
[FieldOffset(0)]
public int Id;
}
...
markerStruct ByteArrayToNewStuff(byte[] bytes){
GCHandle handle = GCHandle.Alloc(bytes, GCHandleType.Pinned);
markerStruct stuff = (markerStruct)Marshal.PtrToStructure(
handle.AddrOfPinnedObject(), typeof(markerStruct));
handle.Free();
return stuff;
}
...
print(ByteArrayToNewStuff (detectMarkers(d, W, H, d.Length)).Id);
Il problema è che questo funziona, ma il valore stampato è completamente spento (a volte la stampa circa 400, a volte valore int max).
Immagino che ci sia qualcosa di sbagliato nel modo in cui ho eseguito il marshalling della struttura in C#. Qualche idea?
Edit:
Questa è la soluzione di lavoro utilizzando ref:
C++
struct markerStruct {
int id;
};
...
EXPORT_API void detectMarkers(... , markerStruct *MarkerInfo) {
MarkerInfo->id = 3;
return;
}
C#
[DllImport ("ArucoUnity")]
public static extern void detectMarkers(... ,
[MarshalAs(UnmanagedType.Struct)] ref MarkerStruct markerStruct);
...
[StructLayout(LayoutKind.Sequential, CharSet = CharSet.Ansi, Pack = 1)]
public struct MarkerStruct
{
public int Id;
}
...
detectMarkers (d, W, H, d.Length, ref markerInfo);
print(markerInfo.Id);
Ho provato ad usare ref, ma non sono ancora riuscito a ottenere il valore giusto ... Potresti dare un'occhiata alla mia domanda modificata? – mkolarek
@kolarek: Come ho detto nella mia risposta, quando si usa la parola chiave 'ref' o' out' C# passerà effettivamente un puntatore. Quindi usa 'void detectMarkers (/*...*/ markerStruct * MarkerInfo)' sul lato C++ e poi 'MarkerInfo-> id = 3;'. Inoltre, elimina l'attributo 'In' nella firma p/invoke, che significa non recuperare i dati da C++, che ovviamente è l'opposto di quello che vuoi. –
Grazie mille, l'ho installato e funzionante! – mkolarek