2014-02-15 5 views
6

Ich möchte eine C-Struktur mit einem Array variabler Länge zurück zu C# marshale aber bis jetzt kann ich nichts besseres als eine Zeiger-zu-Struktur-Darstellung und einen Zeiger bekommen schweben.Marshal eine C-Struktur mit einem Array variabler Länge

Unmanaged Darstellung:

typedef float  smpl_t; 

typedef struct { 
    uint_t length; /**< length of buffer */ 
    smpl_t *data; /**< data vector of length ::fvec_t.length */ 
} fvec_t; 

Managed Darstellung:

[StructLayout(LayoutKind.Sequential)] 
public unsafe struct fvec_t1 
{ 
    public uint length; 

    public float* data; 
} 

[DllImport("libaubio-4.dll", EntryPoint = "new_fvec", PreserveSig = true, CharSet = CharSet.Ansi, 
    CallingConvention = CallingConvention.Cdecl)] 
public static extern unsafe fvec_t1* new_fvec1(uint length); 

Was Ich mag würde, ist eine .NET-Stil-Array haben, wo datafloat[] sein würde, aber wenn ich ändern tun, um die Struktur zu das Formular unten bekomme ich Kann die Adresse nicht übernehmen, erhalten die Größe von, oder einen Zeiger auf einen verwalteten Typ in der externen Funktion oben deklarieren.

[StructLayout(LayoutKind.Sequential)] 
public unsafe struct fvec_t1 
{ 
    public uint length; 

    public float[] data; 
} 

Apparently, ist es nicht möglich, eine einen variabler Länge Array vermarshallten zurück wie sie ist, dies richtig ist oder ist es immer noch eine Möglichkeit, dies zu erreichen?

+0

Sie können es einfach genug tun, ein Array als Parameter übergeben. Irgendein Grund, warum Sie das nicht tun? Warum benutzt du 'unsicher '? –

+0

Können Sie das näher erläutern? Die Sache ist, wenn ich MarshalAs.Struct den Anruf funktioniert, aber die zurückgegebene Struktur ist Müll (Länge ist falsch), so denke ich, es hat nicht richtig funktioniert. Im Moment ist das einzige, was funktioniert, einen Zeiger von struct zurückzugeben und manuell auf die Daten in 'data' zuzugreifen. Über unsichere Aussage, ich habe gerade vergessen, es zu entfernen. – Aybe

+0

Sie können das Array nicht innerhalb einer Struktur haben und den Marshaller die Arbeit machen lassen. Aber Sie können das Array als Zeiger übergeben und es marshallen lassen. –

Antwort

6

kurze Antwort Sie nicht mit variabler Länge Array als Array Marschall kann, denn ohne die Größe zu kennen, kann die Interop-Rangier-Dienst nicht auf die Array-Elemente Marschall

aber wenn Sie die Größe, die es wie unten sein wird:

int arr[15] 

Sie werden in der Lage sein, es so Marschall:

[MarshalAs(UnmanagedType.LPArray, SizeConst=15)] int[] arr 

, wenn Sie nicht wissen, die Länge des Arrays einer d das ist, was Sie möchten, können Sie es konvertieren mit InPtr intprt und befassen sich aber zuerst müssen Sie 2 structs wie unten

[StructLayout(LayoutKind.Sequential, CharSet = CharSet.Ansi, Pack = 1)] 
struct fvec_t1 
{ 
    public uint whatever; 

    public int[] data; 
} 

die andere erstellen:

[StructLayout(LayoutKind.Sequential, CharSet = CharSet.Ansi, Pack = 1)] 
struct fvec_t2{ 
    public uint whatever; 
} 

eine Funktion erstellen, initialisieren das Array wie unten

private static int[] ReturnIntArray() 
{ 
    int [] myInt = new int[30]; 

    for (int i = 0; i < myInt.length; i++) 
    { 
     myInt[i] = i + 1; 
    } 

    return myInt; 
} 

Instanziieren der erste struct

fvec_t1 instance = new fvec_t1(); 
instance.whatever=10; 
instance.data= ReturnIntArray(); 

instanziieren die zweite struct

fvec_t2 instance1 = new fvec_t2(); 

instance1.whatever = instance.whatever 

dynamisch Raum für fvec_t2 struct für Datenarray mit erweiterter Raum zuzuteilen

IntPtr ptr = Marshal.AllocHGlobal(Marshal.SizeOf(typeof(fvec_t2)) + Instance.data.Length); 

Übertragung die bestehenden Feldwerte von fvec_t2 an Speicherplatz von PTR zeigte

Marshal.StructureToPtr(instance1, ptr, true); 

Berechnen Sie den Offset des Datenfelds, das b e am Ende eines fvec_t2 struct

int offset = Marshal.SizeOf(typeof(fvec_t2)); 

bekommen Speicheradresse des Datenarray Feld basierend auf dem Versatz.

IntPtr address = new IntPtr(ptr.ToInt32() + offset); 

Datenkopie

Marshal.Copy(instance.data, 0, address, instance.data.Length); 

tun den Anruf

bool success = dllfunction(ptr); 

Marshal.FreeHGlobal(ptr); 
ptr = IntPtr.Zero; 
+0

Sie haben den Titel der Frage nicht gelesen. – Aybe

+0

Ich lese es und ich sagte, dass Sie das nicht tun können :). Wenn du mein Answe nicht magst, kann ich es löschen –

+0

Wenn ich die Größe im Voraus wissen könnte, hätte ich die Frage nicht gestellt :-) – Aybe

-1

Im obigen Fall ptr ich auf jeden Fall die SizeParamIndex verwenden würde.

[StructLayout(LayoutKind.Sequential)] 
public struct fvec_t1 
{ 
    uint length; 
    [MarshalAs(UnmanagedType.LPArray, SizeParamIndex = 0)] float[] data; 
} 

Viel Glück.

+1

Leider funktioniert 'SizeParamIndex' nur für Arrays, die direkt als Parameter übergeben werden, nicht für Arrays innerhalb einer Struktur. Dies muss manuell durchgeführt werden (https://stackoverflow.com/a/22811847/1925996). – piedar

+0

@piedar Wirklich? Das ist bedauerlich und ich bin überrascht. Laut [this] (https://msdn.microsoft.com/en-us/library/eshywdt7 (v = vs.110) .aspx) können Sie *** unter "Deklarieren von Prototypen" *** verwenden SizeConst' da. –