Speicherprobleme bei Arrays als Rückgabewert



  • Hallo zusammen,

    ich komme mir etwas blöd vor, sowas fragen zu müssen, aber ich habe bislang dazu nichts hilfreiches gefunden.

    Ich habe mir eine DLL mit C-Schnittstelle entwickelt, die u.a. Funktionen enthält, die wchar_t arrays zurückgeben sollen. Da die Länge dieser Arrays in dem aufrufenden Programm vorher unbekannt ist, lasse ich den Speicher dafür in der DLL reservieren.

    Hier eine Beispiel-DLL-Funktion:

    int getTestString(wchar_t** result)
    {
      std::wstring irgendEinString(L"Das ist ein Test");
      (*result) = new wchar_t[str.size()+1];
      wsprintf((*result), L"%ls",str.c_str());
      return str.size();
    }
    

    Aufgerufen wird das von mir in einem Programm wie folgt:

    wchar_t* testString;
    getTestString(&testString);
    

    Soweit funktioniert das.
    Aber wie kann ich nun den Speicher wieder freigeben?
    Rufe ich in der Applikation nach dem getTestString ein

    delete[] testString
    

    auf, erhalte ich eine Exception.
    In der DLL kann ich den Speicher zwar freigeben, aber da weiß ich ja nicht, zu welchem Zeitpunkt er nicht mehr benötigt wird.

    Irgendwelche Vorschläge?

    Danke und Gruß,
    Thomas



  • Eine DLL kann von mehreren Applikationen gleichzeitig benutzt werden. Die Anforderung und Rückgabe von Speicher muss daher in der DLL (resp. deren Instanzen) geregelt werden. Verpasse der Applikation einfach einen unabhängigen eigenen ausreichenden Speicher. Um wieviele Bytes soll es denn gehen, es sind wohl nur wenige oder soll es ein ganzes Buch sein?



  • Danke für die Antwort!

    Das Problem ist bei mir nicht die Größe der einzelnen Arrays sondern die Tatsache, dass diese Funktionen sekündlich aufgerufen werden und dass das Programm ggf über Stunden und auf Plattformen mit wenig Speicher laufen wird.

    Wenn ich Dich richtig verstehe, ist der Mechanismus prinzipiell schon in Ordnung, geht eben nur nicht mit DLLs?

    Ich zweifle mittlerweile schon an mir 😉

    Viele Grüße,
    Thomas



  • Das Problem ist dass die dll einen anderen CRT Heap hat wie die Anwendung, was bedeutet dass der in der dll allokierte Block der CRT der Anwendung nicht bekannt ist. Daher muss das delete[] in der dll sein und nicht in der exe. Du kannst der dll z.B. eine freeString() Funktion verpassen die einfach nur das delete[] macht und dann eben die benutzen.



  • Vielleicht solltest du besser den Array über einen Zeiger in der Parameterliste wie GetMyString(&string) statt über den Rückgabewert regeln. Die Funktionen der WinApi machen das mit ihren DLLs gewöhnlich auch so. Ansonsten bleibt eine DLL solange funktionsfähig wie die Applikation diese beansprucht oder explizit freigibt.

    Warum verzweifeln, es gibt für alles eine Lösung! 🙂



  • Oder dafür sorgen dass dll und Anwendung die selbe CRT linken und das dynamisch (CRT dynamisch linken ist sowieso was man machen sollte außer man hat einen wirklich guten Grund das nicht zu tun), dann sollten sie auch den selben Heap bekommen. Ich würd aber trotzdem versuchen alles was in der dll allokiert wird auch in der dll wieder freizugeben weil es da sonst auch noch weitere potentielle Probleme gibt...



  • Wenn die Aufgaben der DLL - wie hier - ein wesentlicher Bestandteil der Applikation sein sollen, bindet man die DLL besser statisch für die ausgelagerten Funktionen. Dann gibt es auch keine unerwarteten Komplikationen.



  • Eine dll kann man nicht statisch binden 😉



  • berniebutt schrieb:

    Eine DLL kann von mehreren Applikationen gleichzeitig benutzt werden.

    Ja, kann sie, aber jede Anwendung hat ihren eigenen Prozess. Das ist also kein Grund für irgendwas.

    Die Anforderung und Rückgabe von Speicher muss daher in der DLL (resp. deren Instanzen) geregelt werden.

    Jain. Die Rückgabe muss nur über den selben "Heap" passieren wie die Anforderung. D.h. wenn die DLL und das Programm den selben Heap verwenden ist es gar kein Problem in der Applikation einfach "delete" zu machen.
    Mit MSVC geht das z.B. problemlos, wenn beide (die Applikation und die DLL) mit der "DLL Runtime" compiliert & gelinkt wurden.

    @TKB:
    Mögliche Lösungen

    1. Verwende in der DLL und in der Applikation jeweils die "DLL-Runtime", dann verschwindet das Problem von alleine.

    2. Mach eine "FreeString" Funktion die du aus der DLL exportierst, die Anwendung muss dann "FreeString(x)" statt "delete [] x" verwenden

    3. Verwende einen Smart-Pointer der einen "Deleter" mit abspeichert (shared_ptr, shared_array, unique_ptr). Der "Deleter" kommt dann immer aus deiner DLL, und ruft den "passenden" operator delete auf.

    4. Kapsel alles was du hast in Klassen, und verpass den Klassen eine "Delete" Methode die dann "delete this;" macht. Alternativ kannst du gleich Intrusive Reference Counting (AddRef/Release) implementieren und boost::intrusive_ptr verwenden.



  • Huiuiui, so viele konstruktive Antworten in so kurzer Zeit...
    Vielen Dank dafür!

    Alles klar, da ich mir die Portierung auf ein Linux nicht vollends verbauen möchte, fällt die DLL-Runtime vermutlich raus.
    Dann versuche ich es mit der eigenen Free-Funktion.

    Grüße,
    Thomas



  • TKB schrieb:

    Alles klar, da ich mir die Portierung auf ein Linux nicht vollends verbauen möchte, fällt die DLL-Runtime vermutlich raus.

    Nur um das klarzustellen: Auch wenn du jetzt eine free Funktion baust, die DLL-Runtime solltest du für die Windowsversion sowieso verwenden, ganz unabhängig von allem anderen, ist nicht umsonst die Defaulteinstellung...

    Ich weiß es ist gängige Praxis die Runtime einfach statisch zu linken damit man sich nicht darum kümmern muss dass der User der Anwendung die Runtimes installiert. Gängige Praxis aber leider auch sehr schlechte Praxis, die Gründe warum man nicht statisch linken sollte sind vielfältig:

    • Die dll Runtime ist nur einmal an einem zentralen Ort als dll abgelegt,
    • alle laufenden Prozesse die die selbe Runtime benötigen können sich den Code teilen ->
    • verschwendet nicht unnötig Platz (weder auf der Festplatte noch im RAM).
    • Die statisch gelinkte CRT lässt sich im Gegensatz zur dll nicht updaten (bugfix, security patch, ...)
    • ...

    Darum lieber ordentlich machen, dynamisch linken und den User die Runtime installieren lassen sofern er sie nicht sowieso schon hat.



  • TKB schrieb:

    Alles klar, da ich mir die Portierung auf ein Linux nicht vollends verbauen möchte, fällt die DLL-Runtime vermutlich raus.

    Soweit ich weiss gibt es dieses Problem unter Linux sowieso nicht wirklich. Also wieso kompliziert machen wenn's einfach auch geht?



  • hustbaer schrieb:

    TKB schrieb:

    Alles klar, da ich mir die Portierung auf ein Linux nicht vollends verbauen möchte, fällt die DLL-Runtime vermutlich raus.

    Soweit ich weiss gibt es dieses Problem unter Linux sowieso nicht wirklich.

    Stimmt, ich bin zwar kein Linux Experte aber afaik sollte das dort sowieso gehen weil es da nur einen Heap pro Prozess gibt (sbrk() und dieses Zeug!?). Unter Linux muss man dafür mit globalen Variablen in einem .so aufpassen da der Linker die dort defaultmäßig in einem Bereich ablegt wo sie zwischen allen Prozessen geshared werden.


Anmelden zum Antworten