2013-08-29 17 views
7

Uwaga: Ostateczne rozwiązanie robocze jest po edycji!Przekazywanie struktury z niezarządzanego C++ do C#

Mam nadzieję, że ktoś może mi pomóc z problemem, który próbowałem rozwiązać przez ostatnie kilka dni.

Próbuję przekazać struct z niezarządzanego DLL C++ do skryptu C#. To, co mam tak daleko:

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

Problemem jest to, że to działa, ale wartość drukowana jest całkowicie wyłączony (czasami drukuje to około 400, czasami maksymalna wartość int).

Zgaduję, że jest coś złego w tym, w jaki sposób przygotowałem strukturę w C#. Jakieś pomysły?

Edit:

Jest to roztwór roboczy przy użyciu 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); 

Odpowiedz

5

Zwracasz wskaźnik do zmiennej lokalnej, która została już zniszczona przed .NET można go odczytać. To zły pomysł w czystym C++ i zły pomysł z p/invoke.

Zamiast mieć C# przekazać wskaźnik do struktury (wystarczy użyć słowa kluczowego ref) i kodu C++ tylko wypełnić go.

+0

Próbowałem użyć ref, ale nadal nie udało mi się uzyskać właściwej wartości ... Czy mógłbyś rzucić okiem na moje zmodyfikowane pytanie? – mkolarek

+0

@kolarek: Tak jak powiedziałem w mojej odpowiedzi, kiedy użyjesz słowa kluczowego 'ref' lub' out' C# faktycznie przekaże wskaźnik. Tak więc użyj 'void detectMarkers (/ *...*/ markerStruct * MarkerInfo)' po stronie C++, a następnie 'MarkerInfo-> id = 3;'. Należy również pozbyć się atrybutu 'In' w sygnaturze p/invoke, co oznacza, że ​​nie odzyskasz danych z C++, co oczywiście jest przeciwieństwem tego, co chcesz. –

+0

Dziękuję bardzo, mam go uruchomione! – mkolarek

3

zmienna MarkerInfo jest loc al i wychodzi z zakresu, gdy funkcja zwraca. Nie zwracaj wskaźników do zmiennych lokalnych, obiekty, na które wskazują, już nie istnieją.

+0

Dzięki! Czy masz sugestię, co powinienem zrobić zamiast tego? – mkolarek

0

zamiar dać ten wir ... thx na stanowisko ...

// 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); 
// */ 
Powiązane problemy