2013-08-29 23 views
7

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

risposta

5

Si sta restituendo un puntatore a una variabile locale che è già stata distrutta prima che .NET possa leggerlo. Questa è una pessima idea in puro C++ e una pessima idea con p/invoke.

Invece, hanno C# passare un puntatore a una struttura (basta usare la parola ref) e il codice C++ basta compilarlo.

+0

Ho provato ad usare ref, ma non sono ancora riuscito a ottenere il valore giusto ... Potresti dare un'occhiata alla mia domanda modificata? – mkolarek

+0

@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. –

+0

Grazie mille, l'ho installato e funzionante! – mkolarek

3

Il variabile MarkerInfo è loc al e va fuori campo quando la funzione ritorna. Non restituire puntatori a variabili locali, gli oggetti a cui puntano non esisteranno più.

+0

Grazie! Hai un suggerimento su cosa dovrei fare invece? – mkolarek

0

Andando a dare a questo un vortice ... thx per il posto ...

// new struct and generic return for items to 
struct _itemStruct 
{ 
    unsigned int id; // 0 by default, so all lists should start at 1, 0 means unassigned 
    wchar_t *Name; 
}; 

// for DLL lib precede void with the following... 
// EXPORT_API 
void getItems(std::vector<_itemStruct *> *items) 
{ 
    // set item list values here 


    //unsigned char *bytePtr = (unsigned char*)&items; // manual pointer return 

    return; 
}; 

/* // In theory c# code will be... 
[StructLayout(LayoutKind.Sequential, CharSet = CharSet.Ansi, Pack = 1)] 
public struct _itemStruct 
{ 
    public unsigned int Id; 
    public string Name; 
} 

[DllImport ("ListOfItems")] // for ListOfItems.DLL 
public static extern void getItems(
[MarshalAs(UnmanagedType.Struct)] ref List<_itemStruct> items); 
// */ 
Problemi correlati