GCHandle.Alloc sagt das der Typ keine primiten Datentypen enthält

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

Es gibt 18 Antworten in diesem Thema. Der letzte Beitrag () ist von RodFromGermany.

    GCHandle.Alloc sagt das der Typ keine primiten Datentypen enthält

    Hallo zusammen,

    wir haben hier auf der Arbeit eine Software die sich wenn angegeben Bibliotheken dazu lädt und mittels externer Aufrufe Funktionen ausführt.
    Da mir die Basis einer jeden Plugin DLL aber gewaltig auf den S*** geht und alles viel zu umständlich ist, habe ich mich ein wenig informiert und rausgefunden, dass man mit .NET genau solche Exports erstellen kann (Gibt da ein NuGet Package .NET Exports oder so ähnlich).

    Damit kann ich jede x beliebige Funktion mit [DllExport] kennzeichnen und schwubs kann unsere Software die Methoden auch schon aufrufen. Das funktioniert! Habbich getestet!

    Jetzt gibt es bei mir im Projekt nachher eine struct Analysis

    Diese hat einen string Konstruktor und einen string Member. Der string Member wird im Konstruktor zugewiesen.
    Eine Export Funktion muss jetzt diese Analysis Klasse instanzieren und den Pointer auf die Struct in einem DWORD zurückliefern.

    Jetzt kommt der Part an dem ich keine Ahnung habe wie sowas funktioniert. Ich habe schon rausgefunden, dass dies wohl mit GCHandle.Alloc(analysisObj, GCHandleType.Pinned) funktionieren soll.
    Mit dem GCHandle kann ich dann AddrOfPinnedObj aufrufen und habe dadurch den Pointer.

    Allerdings haperts beim GCHandle.Alloc und der meckert das mein Objekt keine primitiven Datentypen enthält. Gut dachte ich mir string ersetzen wir dann mal testweise durch nen integer weil der ja primitiv ist und voila es funktioniert. Aber ich brauche einen string in diesem Objekt.

    Wie also genau funktioniert das ganze jetzt mit dem string datentyp?

    Danke im Voraus
    Bilder
    • devenv_2017-09-19_14-33-00.png

      20,18 kB, 696×307, 95 mal angesehen
    • devenv_2017-09-19_14-36-41.png

      6,07 kB, 406×233, 102 mal angesehen

    Dieser Beitrag wurde bereits 1 mal editiert, zuletzt von „seh“ ()

    string ist kein primitiver Datentyp, sondern ein Referenztyp, den kann man nicht pinnen. Von dem Nuget Paket hab ich noch nie gehört, ist unter diesem Namen auch nicht einfach zu finden und sollte eigt. auch nicht nötig sein.

    Die Frage ist, wie sieht der primitive Datentyp in der Originalsprache aus? Ich vermute mal es ist C, also wie sieht die struct in C aus?
    Ich wollte auch mal ne total überflüssige Signatur:
    ---Leer---
    Hi @jvbsl.

    Sorry das ich da nicht direkt drauf gekommen bin. In C++ ist der Konstruktor wie folgt definiert:

    C-Quellcode

    1. Analysis (const wchar_t *aRegistry);


    Um ehrlich zu sein, die Analysis struct ist in C++ eine Klasse und keine Struct. Ich habe es mit einer Klasse versucht, aber da ging es mit GCHandle.Alloc erst Recht nicht.
    In der Klasse Analysis ist der Member zum ablegen des registryPath ein CString.

    Der genaue Name des NuGet Pakets lautet: UnmanagedExports.

    jvbsl schrieb:

    und sollte eigt. auch nicht nötig sein.


    Das wäre mir sogar noch lieber, wenn es eine Möglichkeit gibt Methoden zu exportieren ohne eine 3rdParty Bibliothek.
    Ohh ups mein Fehler, du willst exportieren. Keine Ahnung ob man dabei die Bibliothek braucht, scheint aber keine zusätzliche Abhängigkeiten zu erzeugen, also dürfte irrelevant sein.

    C#-Quellcode

    1. ... New([MarshalAs(UnmanagedType.LPWStr)]string aRegistry)

    sollte auch funktionieren

    Jenachdem ob das Hauptprogramm zugriff auf die Variable RegistryPath über das MemoryLayout braucht oder nicht kannst du entweder eine Art verweis erstellen, die nur auf den string, oder gar das Komplette Analysis Objekt verweis, anschließend würde es dir freistehen beliebige Datentypen zu erstellen und du müsstest in Zugriffen über DLLExports eben immer mit diesem verweis arbeiten...
    Edit: achja ansonsten musst du halt dafür sorgen, dass es ein primitiver Datentyp bleibt. Dafür kannst du ebenfalls das MarshalAs-Attribut auf strings in der struct anwenden, was dann entweder einem pointer entspricht, oder mit fixer Längenangabe(SizeConst setzen) eben einem array mit fixer größe.
    Ich wollte auch mal ne total überflüssige Signatur:
    ---Leer---
    Also wie gesagt um es zu pinnen brauchst du dann schon ne feste größe, dann sollte es(wenn ich nicht falsch liege) funktionieren. Das MarshalAs geht leider eben nur in umgekehrter Richtung. Da er nicht anfängt rekursiv die objekte zu pinnen. Ich würde sowieso zum Lösungsansatz 1 tendieren.
    Oder brauchst du wirklich Zugriff auf den RegistryPath von der C++ Seite und wenn ja, wie sieht dieser Zugriff aus, oder kannst du die Art des Zugriffs selbst festlegen?
    Ich wollte auch mal ne total überflüssige Signatur:
    ---Leer---
    Die Sache ist, die Software ruft die exportierte Funktion ​New auf und übergibt den Pfad zur Registry.
    Die ​New muss ein Objekt von der Analysis Struktur anlegen die dann in der Memory bleibt und gleichzeitig den Pointer zu dieser Struktur zurückliefert.
    Beim nächsten Aufruf der exportierten Methode Auswertung wird der Pointer zu dieser Struktur als Parameter angegeben und dann muss die Struktur von dort aus weiter verwendet werden können.
    @seh Probierma, aus der struct Analysis eine class Analysis zu machen.
    Beim Laden von DLL-Assemblies muss eine Klasse instanziiert werden, eine struct ist leider kein Reference-Typ.
    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!
    Leute ich hab es selbst rausgefunden.
    Die Stichworte lauten: Marshal.AllocHGlobal, Marshal.StructureToPtr und Marshal.PtrToStructure.

    Mit AllocHGlobal lege ich unverwalteten Speicherplatz für den Prozess an mit der Größe der Analysis Struktur.
    Mit StructureToPtr kopiere ich die Struktur dann in den unverwalteten Speicher. Die New hat den Pointer durch AllocHGlobal und gibt diesen zurück.
    Und in der Auswertung wird dieser Pointer wieder als Parameter übergeben und dann kann ich mit PtrToStructure das Objekt wieder in den verwalteten Speicher laden. Und siehe da, der Parameter mit dem string ist sogar auch dann da wie das gewollt ist.

    Die Frage ist jetzt nur, so weit ich verstanden habe muss wenn man AllocHGlobal verwendet auch wieder der Speicher freigegeben werden. Wann mache ich das am besten? Die Dll besteht eigentlich nur aus exportierten Funktionen die von der Software aufgerufen werden.

    @RodFromGermany Das hatte ich zuerst probiert. Ich hatte Analysis zuerst als Klasse, weil in C++ ists ja auch ne Klasse und keine Struktur. Aber das hat auch nicht geklappt. Ich habe jetzt eine funktionierende und eigentlich ja auch logische Lösung.

    Edit: Ich denke ich rufe FreeHGlobal auf sobald die Auswertung beendet ist, danach brauche ich nämlich das Analysis Objekt nicht mehr.

    Dieser Beitrag wurde bereits 1 mal editiert, zuletzt von „seh“ ()

    Ja das kannst du natürlich machen, ergibt nur wenig Sinn, da dieses Objekt im Unmanaged Memory auf ein Objekt im Managed Memory verweist, damit kann C++ nichts anfangen und durch die Runtime könnte das Objekt sogar noch verschoben worden sein, sodass das Objekt gar nicht mehr gültig ist. Deshalb würde ich wie bereits gesagt mein oben genanntes herangehen über die Referenz empfehlen. Da weißt du die größe, du kannst beliebig auf C# seite programmieren und hast auch nicht so das Problem mit dem Memory.

    Außerdem würde ich eine Release Methode anlegen, das ist ein quasi Standard, dass man immer eine Methode zum erstellen und eine zum freigeben hat...
    Ich wollte auch mal ne total überflüssige Signatur:
    ---Leer---

    jvbsl schrieb:

    damit kann C++ nichts anfangen


    Die C++ Seite, also die Software, braucht auch nichts damit anzufangen. Sie braucht lediglich den Pointer zu dieser Struktur... Wie die Struktur aussieht ist der Software relativ Schnuppe, es geht eigentlich nur darum einen Pointer in der New zurückzuliefern damit die Software den Pointer in der Auswertung wieder als Parameter übergeben kann. Die Struktur brauche ich lediglich in meinem C# Projekt. Sonst nirgends.

    Die Auswertungs Methode liefert noch eine Struktur wo ein HDC von einem Fenster in der Software enthalten ist. Dieses HDC benutze ich in der Auswertung dann zum zeichnen mit GDI auf dem Fenster der Software. Die Auswertdaten kommen über eine TCP/IP Schnittstelle, d.h. dort hab ich mit dem ganzen Marshalling Gedöns hoffentlich nichts mehr zu tun :D
    @seh Verwende beim nächsten Mal gleich die Suchfunktion: Austausch von Daten zwischen einer VB.NET-exe und einer C-DLL, 32 und 64 Bit
    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!
    @RodFromGermany: nur dass das hier genau umgekehrt ist...

    @seh: genau deshalb gibt es keinen Sinn der C++ DLL etwas zu geben, mit der sie erst nichts anfangen kann^^
    Ich wollte auch mal ne total überflüssige Signatur:
    ---Leer---

    jvbsl schrieb:

    genau umgekehrt
    Beim Verpacken von Strukturen ist es doch egal, wer callt bzw. gecallt wird. ;)
    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!

    RodFromGermany schrieb:

    Verwende beim nächsten Mal gleich die Suchfunktion

    Ich suche mit Google nach meinen Problemen, und ich suche explizit im Board nach Problemen indem ich in meine URL Leiste (Chrome) vb-para eingebe und mit dem Tabulator vervollständige. Dadurch kann ich im VB-Paradise Forum suchen. Ich hätte den Beitrag von dir vielleicht auch gefunden wenn ich nach den richtigen Schlüsselwörtern gesucht hätte. Aber dein Thread hat auch keine Tags also wird er auch schlechter gefunden. Ich treibe mich aber meist auch auf Stackoverflow für sowas rum, nur finde ich dort manchmal eben nicht genau das was ich suche.

    seh schrieb:

    den richtigen Schlüsselwörtern
    Ja, das ist bei der Suchmaschine im Forum echt ein Problem, die ist absolut nicht flexibel.
    Meist suche ich im Forum nach Beiträgen, von denen ich genau weiß, wonach ich suchen muss, z.B. "TabControl ohne Reiter".
    Wenn ich das nict weiß, geh ich über Google und editiere den Suchstring, bis ich das passende finde.
    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!
    @RodFromGermany Wird beim nächsten Mal so gemacht, danke für den Hinweis :)

    jvbsl schrieb:

    genau deshalb gibt es keinen Sinn der C++ DLL etwas zu geben, mit der sie erst nichts anfangen kann^^

    Ja, das es keinen Sinn ergibt weil C++ damit nichts anfängt ergibt in meinen Augen auch Sinn :thumbsup:
    Das Problem ist halt, dass die Software halt so aufgebaut ist, gibt es denn eine Möglichkeit das Objekt innerhalb der DLL zu speichern? Ich meine es gibt doch eigentlich nie einen wirklichen Kontext wenn immer nur exportierte Methoden aufgerufen werden oder? Man kann doch in der DLL keine globalen Variablen anlegen oder versteh ich da etwas nicht richtig?
    klar kannst du das, davon hab ich ja auch die ganze Zeit geredet, anders wäre der Ansatz gar nicht möglich^^. Würde mir dafür einfach ein Singleton anlegen, dass die nötigen Daten hält und auch die Verweisnummern, dann kannst du die Objekte einfach in ner List halten und die Referenznummern sind einfach nur der Index in dieser List, die auch wieder freigegeben werden können. Man kann dann immer einen freien Slot suchen, oder mittels RemoveAt tatsächlich komplett aus der Liste löschen, das letztere ist einfacher, mit dem ersteren kann man weiter optimieren, nur denke ich nicht, dass dies hier nötig ist.
    Ich wollte auch mal ne total überflüssige Signatur:
    ---Leer---

    seh schrieb:

    oder versteh ich da etwas nicht richtig?
    Wenn Du von Deiner .NET-Instanz eine C/C++-DLL aufrufst, wird die DLL in einer Instanz geladen. Klar kannst Du in diese Instanz Daten packen.
    Falls Du Angst hast, dass es mehrere Instanzen geben könnte, kannst Du ja mal das DLL-Handle auslesen und vergleichen.
    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!