C# und PInvoke -> C Struct Hack



  • Hallo

    Ich sollte eine Funktion + Zugehöriger Structur aus einem c++ Project in c# wrappen.

    Was im allgemeinen für mich kein Problem darstellt aber mit der vorhandenen Struktur habe ich so meine Schwierigkeiten.

    Die Struktur sieht wie folgt aus:

    typedef struct tagDM_SEND_DATA_STRUCTA
    {
        // andere Felder einfachheitshalber weg gelassen
        DWORD                 dwDataSize;                            
        BYTE                  byData[1];  /* Array, das die Daten enthält */
    } DM_SEND_DATA_STRUCTA, FAR *LPDM_SEND_DATA_STRUCTA;
    

    Was mir erstmal Rätsel aufgab war das Byte Array mit der Größe 1.
    Wer braucht den so was, dachte ich zunächst.

    Bis ich dann die Erklärung fand: Ein sogenannter "C Struct Hack"
    Ein beliebig groß definierbares Feld am Ende einer Struktur.
    https://www.codeproject.com/articles/702065/c-struct-hack

    Und nun die Frage an die Experten die sich mit PInvoke auskennen. Wie wrappe ich so einen "C Struct Hack"

    Und das in beide Richtungen. Also wie verpacke ich nachher die Daten in dieses byte Array und wie entpacke ich die ankommenden Daten?



  • Bin mir jetzt nicht sicher, aber ich würde einfach ein Byte-Array reingeben. - Am Ende siehst du ja wie viele Einträge im Array sind auf C# Seite.



  • Also zunächst mal muss ich es ja erst mal deklarieren.
    Und ein Byte Array mit nicht definierter Größe geht nicht

    Ich habe nun mehrere Dinge ausprobiert.

    Und bin nun bei "byte byData" gelandet. Das war das einzigste was bisher überhaupt funktioniert hat.

    Das Einzigste was ich ab und zu bekomme sind Exceptions die mich auf Speicherbereichsverletzung von nicht verwaltetem Code hinweisen. Dabei schmiert dann mein ganzes Programm ab.

    Was nachher in c++ ankommt kann ich nicht sagen. Hier fehlt mir der Quellcode. Habe nur die Dll und das Headerfile.



  • Sorry. Hatte ich vergessen:

    "byte Bydata" hatte ich nur in der zu versendenden Struktur deklariert.

    Die Struktur die ich empfange sieht etwas anders aus.
    In c++ ist das Feld byData aber gleich definiert:

    BYTE       byData[1];
    

    Dieses Feld habe ich wiederum in c# wie folgt gewrappt:

    [MarshalAs(UnmanagedType.ByValArray, SizeConst = 2048)]
    public Byte[] byData;
    

    Ich musste hier die größe des Arrays anlegen.

    Aber die Exception wird auch nur im Fall es empfangens in c# geworfen. Aber bevor ich überhaupt die Chance habe an einen Haltepunkt in meinem Programm zu gelangen.



  • Was dir helfen kann ist das ICustomMarshaler Interface.

    Für dein Struct reicht ein

    struct MyNativStruct
    {
        byte[] data;
    }
    

    Bei der Invoke Definition machst du dann

    [DllImport("meine.dll")]
    public static extern void myNativMethod([MarshalAs(UnmanagedType.CustomMarshaler, MarshalTypeRef = typeof(MyCustomMarshaler))] MyNativStruct param);
    

    Und eben noch als eigener Marshaller etwas in folgender Richtung

    public class MyCustomMarshaler : ICustomMarshaler
        {
            public void CleanUpManagedData(object ManagedObj)
            {
            }
    
            public void CleanUpNativeData(IntPtr pNativeData)
            {
                Marshal.FreeHGlobal(pNativeData);
            }
    
            public int GetNativeDataSize()
            {
                throw new NotImplementedException();
            }
    
            public virtual IntPtr MarshalManagedToNative(object ManagedObj)
            {
                if (ManagedObj == null)
                    return IntPtr.Zero;
                if (!(ManagedObj is MyNativStruct))
                    throw new MarshalDirectiveException("check your P/Invoke");
    
                MyNativStruct s = (MyNativStruct)ManagedObj;
                IntPtr pNativeData = Marshal.AllocHGlobal(4 + s.data.Length);
                Marshal.Write(pNativData, s.data.Length);
                Marshal.Copy(s.data, 0, IntPtr.Add(pNativeData, 4), s.data.Length);
                return pNativeData;
            }
    
            public virtual object MarshalNativeToManaged(IntPtr pNativeData)
            {
                if (pNativeData == IntPtr.Zero)
                    return null;
    
                MyNativStruct s = new MyNativStruct();
                int size = Marshal.ReadInt32(pNativData);
                s.data = new byte[size];
                Marshal.Copy(pNativData, s.data, 0, s.data.Length);
                return s;
            }
        }
    

Anmelden zum Antworten