Laden...

Marshalling Array of Structs

Erstellt von scrabbl vor 7 Jahren Letzter Beitrag vor 7 Jahren 3.590 Views
S
scrabbl Themenstarter:in
211 Beiträge seit 2010
vor 7 Jahren
Marshalling Array of Structs

Hallo,

ich hab eine C# DLL die aus einem C++ Programm heraus ausgerufen wird. Ich hab die Funktionen der DLL dazu an der COM Schnittstelle sichtbar gemacht. Es klappt soweit auch, ich kann beispielsweise ein einfaches Array (mit Integer Werten) übergeben. Das Interface dazu schaut so aus:

[ComVisible(true)]
    [Guid("51B8FD66-37B1-433A-B061-8F7B5258D1DD")]
    public interface IWrapper
    {
        [return: MarshalAs(UnmanagedType.U1)]
        Boolean create_update([MarshalAs(UnmanagedType.LPArray, ArraySubType = UnmanagedType.U4, SizeConst = 10)] uint[] values);
    }

Jetzt ändert sich allerdings das C++ Programm, so das in Zukunft ein Array voller Structs übergeben wird. Das struct ist dabei recht simple und beinhaltet nur zwei unsigned integer.

typedef struct abc
{
unsigned int wert1;
unsigned int wert2;
}ABC;

Das wird jetzt in ein Array vom Typ ABC gefüllt und soll an die DLL geschickt warden. Ich probiere jetzt schon eine ganze Weile aber ich komme nicht dahinter. Geht das überhaupt ? Was ich so aus stackoverflow finde geht in diese Richtung aber ich werde da einfach nicht schlau draus.

Ich muss mir im C# Code wohl auch ein struct mit demselben Aufbau anlagen, dort die Werte richtig marshallen und in der Funktion dann das struct erwarten ? Ich bekomme es aber einfach nicht zum laufen 😦

Grüße

2.079 Beiträge seit 2012
vor 7 Jahren

Selber gemacht hab ich es noch nicht, aber ich vermute, Du musst am Array-Parameter das MarshalAs-Attribut mit den Feldern SafeArrayUserDefinedSubType und SizeConst ausstatten.

Darüber kannst Du den Item-Typ und die Anzahl Items angeben.
Blöd ist nur, dass die Anzahl dann statisch wäre.

Allgemein brauchst Du aber immer irgendwo auch die Anzahl der Items. Über das MarshalAs-Attribut kannst Du die statisch mit geben, wenn das nicht ausreicht, musst Du sie auf eigene Weise mit geben, entweder als weiteren Parameter oder in einer weiteren Struct als zusätzliches Feld.

Auf C++-Seite musst Du dann die Anzahl Items nutzen um dir das Array wieder zusammen zusetzen.

Zwei Artikel con Microsoft, den ich auf die Schnelle gefunden habe:
Default Marshaling for Arrays
Marshaling Different Types of Arrays

NuGet Packages im Code auslesen
lock Alternative für async/await

Beim CleanCode zählen nicht die Regeln, sondern dass wir uns mit diesen Regeln befassen, selbst wenn wir sie nicht befolgen - hoffentlich nach reiflichen Überlegungen.

W
872 Beiträge seit 2005
vor 7 Jahren

Bitte sei präzise.
Wie sieht genau die C++ Signatur aus?
Welche Länge hast Du, wenn Du eine sizeof in einem kleinen C++ Programm und C# ausführst?
Wenn die Längen gleich sind, dann kannst Du an das Befüllen denken.
Das die Länge statisch ist, ist normal - es sei denn Du hast eine Längenfeld in der C++ Struktur - dann müßtest Du selber mit einer flexiblen Länge arbeiten.

S
scrabbl Themenstarter:in
211 Beiträge seit 2010
vor 7 Jahren

Hallo zusammen,

kam leider erst jetzt wieder dazu, daher erst die späte Antwort.

Bitte sei präzise.
Welche Länge hast Du, wenn Du eine sizeof in einem kleinen C++ Programm und C# ausführst?

Ich hab es probiert, die Längen sind beide 8.
Die Struktur in C++ sieht folgendermaßen aus:


typedef struct Features
{
    UINT32 tag;
    UINT32 value;
} FEATURES;

sizeof(FEATURES) = 8.

Und nun will dieses C++ Programm ein Array dieses Structs an C# übergeben:


FEATURES myArray[49];
//Array befüllen
ptr->do_something(myArray);

Wobei ptr der Pointer auf das Interface des C# Programmes ist, welches in an der COM Schnittstelle sichtbar gemacht habe.

Auf C# Seite sieht das Ganze aktuell so aus dann:


[StructLayout(LayoutKind.Sequential)]
    public struct Feature
    {
        [MarshalAs(UnmanagedType.U4)] public uint tag;
        [MarshalAs(UnmanagedType.U4)] public uint value;
    }

    [ComVisible(true)]
    [Guid("51B8FD66-37B1-433A-B061-8F7B5258D1DD")]
    public interface IWrapper
    {
        [return: MarshalAs(UnmanagedType.U1)]
        Boolean do_something([MarshalAs(UnmanagedType.LPArray, ArraySubType = UnmanagedType.LPStruct, SizeConst = 49)] Feature[] values);
    }

Das funktioniert so aber nicht. Er sagt mir auf C++ Seite immer das er das Argument FEATURES[49] nicht nach myDLL::_ValueType* konvertieren kann.

Strings, Integer, Arrays mit Integer, all das hab ich schon ausprobiert zu übergeben und das alles funktioniert einwandfrei, nur das Array mit Structs bekomme ich nicht hin 😦

Grüße

2.079 Beiträge seit 2012
vor 7 Jahren

Ich kann jetzt nur raten, aber probier doch mal UnmanagedType.SafeArray anstatt LPArray
Dazu das Feld SafeArrayUserDefinedSubType mit dem entsprechenden ArrayItem-Typ setzen
SizeConst muss natürlich gesetzt bleiben, aber vielleicht solltest Du darüber nach denken, die Größe mit zu geben um so auch auch andere Array-Größen reagieren zu können?

NuGet Packages im Code auslesen
lock Alternative für async/await

Beim CleanCode zählen nicht die Regeln, sondern dass wir uns mit diesen Regeln befassen, selbst wenn wir sie nicht befolgen - hoffentlich nach reiflichen Überlegungen.

S
scrabbl Themenstarter:in
211 Beiträge seit 2010
vor 7 Jahren

Die Größe ist fest, die kann ich mitgeben, mit dem SafeArray klappt es aber auch nicht.

Ich hab es jetzt anders hinbekommen, das C# Interface sieht jetzt so aus:


[return: MarshalAs(UnmanagedType.U1)]
Boolean do_something(IntPtr values);

Jetzt kann ich auf C++ Seite tatsächlich das gesamte Array übergeben und auf C# Seite dann folgendermaßen zusammenbauen:


Feature[] myArray = new Feature[49];
for(int i = 0; i < 49; i++)
{
    myArray[i] = (Feature)Marshal.PtrToStructure(values, typeof(Feature));
    values += Marshal.SizeOf(typeof(Feature));
}

So klappt es.
Allerdings lässt es mir trotzdem keine Ruhe, es muss doch irgendwie möglich sein das Array of Structs im Ganzen zu übergeben ohne Pointer und ohne das händisch zusammenbauen hinterher.

W
872 Beiträge seit 2005
vor 7 Jahren

Würde mir da nicht zuviel den Kopf zerbrechen...
Wenn das Auspacken nicht 100.000 mal am Tag läuft, spielt das keine wirkliche Rolle.

S
scrabbl Themenstarter:in
211 Beiträge seit 2010
vor 7 Jahren

Würde mir da nicht zuviel den Kopf zerbrechen...
Wenn das Auspacken nicht 100.000 mal am Tag läuft, spielt das keine wirkliche Rolle.

Das nicht aber es hätte mich einfach interessiert, da packt mich immer der Ehrgeiz 😄

Weil andersrum geht es, das hab ich vor Jahren mal gemacht. Ein Array of Structs von C# nach C++ übergeben im Ganzen. Nur andersrum will es nicht gelingen.