list als rückgabewert einer dll klasse



  • Hallo

    Ich hoffe ihr könnt mir helfen mich auf den richtigen Weg zu geleiten..

    Ich habe eine DLL die eine Klasse enthält, welche mittels __declspec(dllexport) exportiert wird. Diese Klasse enthält eine Methode die als Rückgabewert eine list liefert. Im folgenden mal die Methode:

    list<string> ConCore::getPluginList() {
    	list<string> names;
    	while ( Pluglist->has_next() ) {
    		names.push_back( Pluglist->next().name );
    	}
    	return names;
    }
    

    eigentlich nicht weiter komplex. Ich habe bewußt keine Pointer eingesetzt, weil ich der Meinung bin, dass ich dann erst recht eine Exception raufbeschwere, da das list ja nicht mehr zugreifbar ist, sobald ich die Methode verlasse. (hoffe richtig gedacht zu haben). Ich wollte das die Werte in der List automatisch in die lokale Variable des aufrufenden Clients kopiert werden (Stichwort Copy Constructor)

    Das Problem ist nun, wenn ich mit meinem Client auf die Instanz des Objektes, welches in der DLL geladen und instanziiert wurde, zugreife bekomme ich immer eine exception wenn ich den Rückgabewert in die lokale Variable schreiben will.

    Hier erst mal der Aufruf:

    std::list<std::string> plugins;
    		plugins = getCoreInstance()->getPluginList();
    		mainwin->createPluginMenu( plugins );
    

    Ich habs mal absichtlich nochmal auseinandergezogen, damit ich sehe was passiert.
    Die Exception tritt auf wenn das Programm den Wert von

    getCoreInstance()->getPluginList();
    

    der Variable Plugins zuweisen will.

    Ich hab mir schon diverse Gedanken darüber gemacht und ich denke mir, dass es mit der Stringklasse zusammenhängen könnte.

    Ich hoffe das ich das verständlich genug erklärt habe. Falls nicht, gebt mir noch ein paar Minuten. Ich versuche es dann noch zu überarbeiten.

    Auf alle Fälle schon mal danke für eure Hinweise.

    Grüße
    Daimonion



  • Hallo,

    Das kann mit list und/oder mit string zusammenhängen. Hast du die dll mit dem gleichen Compiler erstellt wie das aufrufende Programm?



  • sogar wenn der compiler für dll und exe der gleiche ist, ist dieses verhalten vorprogrammiert, liegt dadran:

    getList() erstellt eine Liste, die Liste holt Speicher mit new, um die objekte zu speichern.

    exe: mylist = dll->getList();
    Der Inhalt von dll->GetList wird in mylist kopiert, könnte soweit gut gehen, aber jetzt kommts:
    dll->getList erzeugt ein temporäres Objekt list<string>. dieses wird nun wieder gelöscht, weil es ja nicht mehr benutzt wird. Also wird der destruktor von std::list in der exe aufgerufen, der constructor der list wurde aber in der dll aufgerufen. Speicher wurde in der dll mit new angefordert, und soll jetzt in der exe mit delete wieder freigegeben werden. Dabei knallt es. Das ist der ganze Grund.

    Es ist generell nicht empfehlenswert, non-built-in typen aus dll-funktionen aus rückgabewert oder als referenzparameter zu übergeben, wegen obigem Grund. Besser ists, wenn deine Funktion einen zeiger auf einen Speicherbereich bekommt und diese Funktion dann nur in den bereich reinschreibt, ohne new/malloc aufzurufen, dann gibts auch keine probleme. Das is leider ein problem von dll-dateien



  • @Braunstein

    Ja die beiden sind im selben Projekt mit dem selben Compiler erstellt.

    @Braunstein
    @Maxi

    Ja, die list und string Klassen aus der std Bibliothek hab ich auch als Fehler gesehen. Ich hab es vorhin nur nicht geschafft dazuzuschreiben, dass ich vermute, dass diese Klassen der Fehler sind. (mußte dringend in ein Meeting)

    Ich war mir beim Programmieren bewußt, dass die beiden Klassen durch die Instanziierung Speicher auf dem Heap anlegen und darin arbeiten. D.h. dass sie beim Verlassen der Funktion, wie du richtig gesagt hast, mit ihrem Destruktor entfernt werden. Ich ging aber in der Annahme, dass diese beiden Klassen Copy-Konstruktoren besitzen, die eine eventuelle Rückgabeinstanz in den anderen Speicherbereich transportieren können. Leider scheint dem nicht so.

    Nun der Versuch meine Schnittstelle doch noch zum Laufen zu bewegen.

    Die Funktion generiert ja eine Liste, mit der ich auf der Clientseite leicht auf dieser Liste iterieren kann, um die einzelnen Strings komfortabel weiter zu verarbeiten. Da es ja, wie Maxi geschrieben hat, zu Fehlern kommt, wenn man solche Templateklassen aus ner DLL rausholen will, muß was anderes her.

    Mit welchem Rückgabetyp würdet ihr denn so eine Schnittstelle implementieren, damit man komfortabel weiterarbeiten kann (Ideal wäre schon sowas wie ein Array oder so)?

    Danke für eure Tipps.

    Grüße
    Daimonion



  • Ich würde ein normales Array nehmen. Bei dll-Schnittstellen verwende ich eigentlich auch immer nur PODs.



  • Hmm, mit PODs kann ich jetzt noch nicht so viel anfangen. Was ist denn das?

    Meinst du mit einem Array ein stinknormales ByteArray, oder Chararray? Wenn ja wie würdest du das denn initialisieren, damit ich auf der Clientseite relativ einfach an die Daten rankomme.

    Ich weiß die Fragen sind eigentlich recht plump, aber gerade bei C++ hab ich mit solch einfachen Dingen noch recht viel zu kämpfen, da ich die Syntax manchmal echt blöd und schwer zu begreifen finde..



  • POD = plain old data
    einfache Datentypen und Strukturen
    int, char, long, float, double ...



  • Genau, ich würde ein stinknormales Array nehmen, welches innerhalb der dll erzeugt wird.
    Dann würde ich noch eine Funktion bereitstellen die dieses Array auch wieder innerhalb der dll löscht.



  • Ah so.. Konnte mir schon irgendwie so was denken, aber wollte noch auf Nummer sicher gehen...

    Okay, bevor ich es mit dem PODs probiere wollte ich es noch mit Pointern als Übergabeparamter probieren (sieht dann meiner Meinung nach besser aus, als wenn wieder extra Arrays existieren um die man sich kümmern muß)

    Ich habe den Methodenaufruf erst mal so abgeändert:

    void ConCore::getPluginList(list<string>* names) {
    

    D.h. er soll einen Pointer auf eine liste bekommen.

    Der Aufruf sieht dann so aus

    std::list<std::string> plugins;
    		getCoreInstance()->getPluginList(&plugins);
    

    Sollte doch eigentlich klappen oder? Oder wird die Liste nur instanziiert?
    Das Problem ist nämlich, das der Pointer nur quark liefert.

    Der Debugger sagt mir als value von names

    names [3435973836](...)

    Naja sieht nicht sehr überzeugend aus 😉

    Unterliegt das hier auftretende Problem, denn immer noch dem oben genannten Fehler oder einem Anderen?

    Wenn ihr euch jetzt fragt: "Was macht der Kerl da eigentlich. Hat der denn überhaupt Ahnung vom Programmieren?" Dann möchte ich euch direkt sagen. Ich lerne gerade per Try and Error.... Und das in der Diplomarbeit. Leider sehr peinlich. 🙄

    Edit: Nach weiterem Nachforchen bin ich erst mal soweit gekommen, dass der richtige Pointer an die Methode übergeben wird, also es wird die richtige Speicheradresse an die Methode übergeben.

    Das Problem besteht vielleicht genau wieder da drin, was Maxi vorhin schon angesprochen hat. Innerhalb der DLL wird der Pointer an der Stelle nicht richtig Derreferenziert, wenn man auf ihn zugreift, also da:

    names->push_back( Pluglist->next().name );
    

    Aber warum ist das so? Ich meine der Pointer bekommt doch durch deklaration mittels list<string>* genau gesagt welcher Typ er ist? Wieso derferenziert er sich nicht richtig? Oder ist die Art der Dereferenzierung (der Pfeil ->) nicht richtig?



  • Die Dereferenzierung ist schon richtig (zumindest theoretisch), aber die Speicherverwaltung vermutlich nicht.



  • Hmm, okay

    Kann man denn feststellen, in welchen Bereichen die Applikation und die dll rumlungern? Sprich kann man irgendwie feststellen, ob die dll in den Speicherbereich der Applikation kommt und da auch auf die nötige Adresse zugreifen kann?

    Meine gedanken dazu sind, dass die dll doch in den Speicherbereich der ladenden applikation eingeblendet wird. Ist das der Fall? Wenn ja, darf die dll auf den Speicherbereich der Applikation zugreifen, oder ist der geschützt?

    Man das wird echt kompliziert. 😞

    Nochmal ein Edit: 🤡

    Also wie ich mich gerade mal so versucht hab zu bilden ist mein Gedanke, dass die dll in den Speicherbereich eingeblendet wird, nicht korrekt.

    Meiner Meinung nach gibt es jetzt nur noch 2 Möglichkeiten.

    1. Ich schaffe es von der dll aus in den Speicherbereich der ladenden Applikation zuzgreifen, indem ich beim Laden der dll sage dass sie diesen Speicherbereich sehen und bearbeiten darf. 👍

    2. Ich beschränke mich wirklich auf primitive Typen, die ich dann halt umständlicher Bearbeiten muß. 👎

    Ich würde ja immer noch Möglichkeit 1 bevorzugen, aber wahrscheinlich wird das, wenn dann nur mit Instabilitäten gehen.

    Naja erst mal drüber schlafen. Ihr könnt mir natürlich gerne noch weitere Tipps geben.

    Grüße
    Daimonion



  • daimonion schrieb:

    dass die dll doch in den Speicherbereich der ladenden applikation eingeblendet wird. Ist das der Fall? Wenn ja, darf die dll auf den Speicherbereich der Applikation zugreifen, oder ist der geschützt?

    ja sie darf zugreifen. das ist so als waere der code der dll in deiner software drinnen. du kannst anstatt einer dll auch eine lib verwenden und sie gleich statisch hinzufuegen. ist der gleiche kaffee.

    Meep Meep



  • na das ist doch schon mal gut.

    Jetzt gilt es nur noch herauszufinden, wieso ich in der dll den Pointer nicht richtig dereferenzieren kann.
    Thema Speicherverwaltung, wie CStoll schon erwähnte. Berechnet die dll vielleicht die Offsetadresse falsch?



  • Äh.
    Ich weiss nicht wieso immer wieder Leute Probleme mit DLLs und dem Heap haben.

    Verwendet einfach die DLL Version der Runtime Library, dann gibt es das Problem nicht. Wir verwenden haufenweise DLLs die Haufenweise STL Objekte (vector, string, ...) by-value hin und herreichen, Speicher wird da kreuz und quer angefordert und freigegeben - alles kein Problem.



  • Hallo und guten Morgen 😉

    Wie meinst du das die DLL der Runtime Library verwenden?



  • Mhhh, vielleicht liege ich damit falsch, aber als ich mal mit Interfaces* über DLL-Grenzen experimentiert hatte, habe ich den delete-Operator überschrieben, und auf Methoden in der DLL (die auch die Allozierung durchgeführt habe) zur Freigabe verwiesen. Vielleicht ist das ja noch ein weiterer Punkt...

    * übrigens auch über Compilergrenzen. Nur kann dafür leider keine 100%ige Funktionsgarantie gegeben werden, unter den 2 Compilern die ich verwendet hatte, funktionierte es soweit getestet (Der eine war VC++ 6 der andere einer der auf einen Watcom Compiler aufgesetzt war).

    cu André



  • Bei verschiedenen Compilern kann man zusätzlich noch das Problem unterschiedlicher STL-Implementationen haben. Ich hatte da schon Probleme bei einer dll aus dem BCB5 in einem Projekt aus dem BCB6.
    Man könnte sicherlich auch die dll-Version der Runtime-Lib nehmen um das zu umschiffen. Das habe ich bisher aber noch nicht probiert da ich die Mitgabe vieler dlls vermeiden wollte. Ich werde das aber demnächst bei mir mal testen.



  • @Braunstein @asc

    ne, ist beides nicht der Fall... Ich arbeite mit einem Compiler und habe bisher noch keinerlei Delete/new operatoren überschrieben.

    Wie ist das denn mit den Runtime Libraries gemeint? Ist das eine Compileroption oder wie kann ich das verstehen? Also ich meine die Aussage von hustbaer.



  • Das ist eine Linkeroption. Man kann die Runtimelib statisch dazulinken oder als dll mitgeben. Wenn du sie als dll mitgibst mußt du halt darauf achten, dass diese dll bei den Sytemen vorhanden ist auf denen du dein Programm einsatzen willst.



  • Braunstein schrieb:

    Das ist eine Linkeroption. Man kann die Runtimelib statisch dazulinken oder als dll mitgeben. Wenn du sie als dll mitgibst mußt du halt darauf achten, dass diese dll bei den Sytemen vorhanden ist auf denen du dein Programm einsatzen willst.

    Das ist mir klar, dass die DLL auf den Systemen dann vorhanden sein muß, wenn ich die bibliothek als DLL compiliere. Wenn ich sie als Lib compiliere und statisch dazulinke, bedeutet das doch, dass ich diese ganze loadlibrary geschichte abhacken kann? Außerdem ist meine bibliothek als lib compiliert, um ein vielfaches größer.

    Irgendwie kann ich mir nicht vorstellen, das Hustbaer das gemeint hat.


Anmelden zum Antworten