Marshalling, wie funktioniert es richtig.

  • C#
  • .NET (FX) 4.5–4.8

Es gibt 2 Antworten in diesem Thema. Der letzte Beitrag () ist von seh.

    Marshalling, wie funktioniert es richtig.

    Hallo,

    folgende Deklaration einer Funktion gibt es in einem nativem Prozess:

    C-Quellcode

    1. BOOL CWorld::IntersectObjLine( D3DXVECTOR3 *pOut, const D3DXVECTOR3 &vPos, const D3DXVECTOR3 &vEnd, BOOL bSkipTrans, BOOL bWithTerrain, BOOL bWithObject )


    Ich würde gerne mittels C-Sharp diese Funktion aufrufen. Ich habe den Funktionszeiger dieser Funktion, habe aber Probleme die Parameter für den Delegaten richtig aufzubauen.

    Die ersten drei Parameter sind ja lediglich Zeiger zu den Strukturen. Das bedeutet, in einem 32-Bit Prozess, ist die Größe der einzelnen Parameter jeweils auch nur 32 - Bit ( 4 Byte ) groß.
    Also muss der Delegat ja auch irgendwie nur die Zeiger zu den System.Numerics.Vector3 - Strukturen als Parameter haben.
    So meine Theorie. In der Praxis funktioniert das auch, allerdings musste ich um das Ganze funktional und bequem zu machen eine Wrapper-Funktion schreiben die mittels GCHandle.Alloc(...)
    erstmal die Vektoren an eine Adresse zu pinnen, diese kann ich dann an die Delegat-Instanz übergeben. Funktioniert, allerdings wenn ich die Funktion öfter pro Sekunde aufrufe, tauchen Fehler auf. AccessViolations, oder sogar sowas hier:


    Ich denke also, dass irgendwas immer noch nicht stimmt.

    Falls es jemand aus dem Funktionsnamen noch nicht herleiten kann, die Funktion führt einen sog. Raycast zwischen Start - und Endvektor aus und liefert ein Ergebnis ob es eine Kollision gab und wenn ja an welcher Position.
    Diese Funktion wird vom Spiel auch öfter pro Sekunde benutzt, also kann es nicht an der Funktion selbst liegen.

    Meine Wrapper Funktion sowie der Delegat:

    C#-Quellcode

    1. [UnmanagedFunctionPointer(CallingConvention.ThisCall)]
    2. private unsafe delegate int DIntersectObjLine(IntPtr pWorld, IntPtr pVecOut, IntPtr vPos, IntPtr vEnd, int bSkipTrans = 0, int bWithTerrain = 0, int bWithObject = 1);
    3. private DIntersectObjLine IntersectObjLineInternal;


    Initialisiert wird der Delegat im Konstruktor:

    C#-Quellcode

    1. IntersectObjLineInternal = Marshal.GetDelegateForFunctionPointer<DIntersectObjLine>(Offsets.NeuzBase + 0x59E4E0);


    Die Wrapper Funktion:

    C#-Quellcode

    1. public bool IntersectObjLine(out Vector3 vecOut, Vector3 vPos, Vector3 vEnd, bool bSkipTrans = false, bool bWithTerrain = false, bool bWithObject = true)
    2. {
    3. if (IntersectObjLineInternal == null)
    4. throw new NullReferenceException("IntersectObjLineInternal has not been initialized!");
    5. var outVec = Vector3.Zero;
    6. var vPosHandle = GCHandle.Alloc(vPos, GCHandleType.Pinned);
    7. var vEndHandle = GCHandle.Alloc(vEnd, GCHandleType.Pinned);
    8. var vOutHandle = GCHandle.Alloc(outVec, GCHandleType.Pinned);
    9. var res = Convert.ToBoolean(IntersectObjLineInternal(Base, vOutHandle.AddrOfPinnedObject(), vPosHandle.AddrOfPinnedObject(), vEndHandle.AddrOfPinnedObject(), Convert.ToInt32(bSkipTrans), Convert.ToInt32(bWithTerrain), Convert.ToInt32(bWithObject)));
    10. vecOut = Marshal.PtrToStructure<Vector3>(vOutHandle.AddrOfPinnedObject());
    11. vPosHandle.Free();
    12. vEndHandle.Free();
    13. vOutHandle.Free();
    14. return res;
    15. }


    Nun meine Frage lautet eigentlich, ob es eine einfachere Methode gibt, die Parameter zu übergeben, ohne so umständlich über GCHandle.Alloc die Adresse herauszufinden.
    Mein erster alternativer Ansatz war es, die Vektor-Parameter mittels [MarshalAs(UnmanagedType.LPStruct)] zu kennzeichnen.
    Der Delegat sah in etwa so aus:

    C#-Quellcode

    1. private unsafe delegate int DIntersectObjLine(IntPtr pWorld, IntPtr pVecOut, [MarshalAs(UnmanagedType.LPStruct)] Vector3 vPos, [MarshalAs(UnmanagedType.LPStruct)] Vector3 vEnd, int bSkipTrans = 0, int bWithTerrain = 0, int bWithObject = 1);

    Zwar benutze ich hier für den pVecOut Parameter immer noch einen IntPtr weil ich mir bei dem nicht sicher war ob es funktionieren würde, den hatte ich also immer noch mit einem GCHandle - Dings versorgt.
    Allerdings ist das beim Aufruf sofort abgestürzt. Ich dachte mir halt, dass der Parameter Vector3 vPos und vEnd dann eben als Pointer zu solch einer Struktur umgewandelt wird, aber dem ist anscheinend nicht so oder ich habe es noch falsch angewandt.

    Gibt es hier jemanden der sich damit besser auskennt?

    Danke im Voraus

    seh schrieb:

    Die ersten drei Parameter sind ja lediglich Zeiger zu den Strukturen.
    Probierma, diese mit ref DEINE_STRUKTUR struct zu marshallen.
    Falls Du diesen Code kopierst, achte auf die C&P-Bremse.
    Jede einzelne Zeile Deines Programms, die Du nicht explizit getestet hast, ist falsch :!:
    Ein guter .NET-Snippetkonverter (der ist verfügbar).
    Programmierfragen über PN / Konversation werden ignoriert!