Zeiger aus C-DLL zurückgeben



  • Ich bin jetzt schon im Wochenende - aber am Montag kann ich das gerne selbst mal ausprobieren.
    Zum Thema Aufrufer in anderen Programmiersprachen:
    Ich habe vor kurzem eine DLL bearbeiten müssen, die von einer Anwendung in UNIPHASE - keine Ahnung was das ist, oder ob es überhaupt richtig geschrieben ist - aufgerufen wird. Diese DLL gibt einen Zeiger auf ein global definiertes Char-Array zurück, und das funktioniert vom Allerfeinsten - ist natürlich nicht threadsicher. Das Problem hierbei war, daß der Aufrufer eine mir unbekannte Fremdanwendung ist und ich deshalb nicht verlangen durfte, daß mir Speicher zur Verfügung gestellt wird, oder wieder freigegeben wird.
    Deshalb war (und bin ich eigentlich auch noch immer ;-)) ich der Meinung, daß das auch gehen muß, wenn der Speicher in der DLL dynamisch allokiert wird.
    Im Mom. muß ich eine DLL schreiben, die von einem Delphi-Programm aufgerufen werden soll, da laß ich mir jedoch Speicher und Speichergröße anliefern.
    Solange ich irgendwelchen Einfluß auf den Aufrufer habe, würde ich auch immer versuchen, den Speicher von diesem bereitstellen zu lassen.



  • Okay denn ... bei mir funktioniert folgendes einwandfrei:

    #ifndef DLL_H
    #define DLL_H
    
    #ifdef __cplusplus
    extern "C"
    {
    #endif
    
    #include <windows.h>
    
    #ifdef BUILD_DLL
    #define EXPORT __declspec (dllexport)
    #else
    #define EXPORT __declspec (dllimport)
    #endif
    
    int WINAPI DllMain(HINSTANCE hInstance, DWORD fdwReason, PVOID pvReserved);
    
    EXPORT char * TestEins(const char * in);
    EXPORT void TestZwei(const char * in, char ** out);
    
    #ifdef __cplusplus
    }
    #endif
    
    #endif
    
    #include <stdio.h>
    #include <stdlib.h>
    
    #define BUILD_DLL
    
    #include "testdll.h"
    
    int WINAPI DllMain(HINSTANCE hInstance, DWORD fdwReason, PVOID pvReserved)
    {
    	return true;
    }
    
    char * Reverse(const char *text)
    {
    	int len = strlen(text);
    	int i;
    	char * res = (char*) malloc(len + 1);
    
    	for(i = 0; i < len; ++i)
    		res[i] = text[len - i - 1];
    
    	res[len] = 0;	
    
    	return res;	
    }
    
    EXPORT char * TestEins(const char * in)
    {
    	char *tmp;
    
    	printf("*** TestEins Eingabe: %s\n", in);
    	tmp = Reverse(in);
    	printf("*** TestEins Ausgabe: %s\n", tmp);
    	return tmp;
    }
    
    EXPORT void TestZwei(const char * in, char ** out)
    {
    	printf("*** TestZwei Eingabe: %s\n", in);
    	*out = Reverse(in);
    	printf("*** TestZwei Ausgabe: %s\n", *out);
    }
    
    #include <stdio.h>
    #include "testdll.h"
    
    int main ()
    {
    	char test[] = "ABCDEFGH";
    	char *res1;
    	char *res2;
    
    	//Reihenfolge umkehren
    	res1 = TestEins(test);
    	printf("\nres1 = %s\n\n", res1);
    
    	//und wieder zurück	
    	TestZwei(res1, &res2);
    	printf("\nres2 = %s\n", res2);
    
    	free(res1);
    	free(res2);
    }
    

  • Mod

    1. Man kann über Modulgrenzen hinweg Speicher allokieren und freigeben mit malloc/free, wenn die gleiche CRT-DLL verwendet wird. (Statisches linken der CRT schließt sich aus)
    2. Es steht einem auch frei die WinAPI Heap Funktionen zu verwenden.
    3. Das ganze kann man strukturell umgehen indem man entsprechende Factory Methoden für Allokation und Deallokation in der DLL schafft.

    Wenn Speicher von einem Moudl EXE/DLL aus allokiert wird, dann sollte dieser auch wieder von dem Modul freigegeben werden.
    Ich rate von 1+2 ab...



  • Vielen Dank für eure Antworten!

    Eigentlich bin ich auch der Ansicht, dass Reservieren und Freigeben von Speicherbereichen in der selben Instanz erfolgen soll, habe aber im aktuellen Fall wenig Einfluss drauf!

    @Belli:

    Hm, so ähnlich hatte ich es auch schon probiert:

    malloc() in der DLL und free() im Aufrufer.

    Habe allerdings immer nur einen Zeiger übergeben und nicht die Adresse des Zeigers, daher wird es wohl nicht geklappt haben!

    Warum muss ich hier die Adresse des Zeigers (für den Rückgabeparameter im Prozedurfall) übergeben? Kann mir das jemand vielleicht noch etwas näher erläutern?

    Hatte bisher den Zeiger der DLL übergeben, diese hatte Speicher reserviert und ihm diesen Zeiger zugewiesen (also zieger = malloc(...)). Danach wurden die Zeichen in diesen Speicherbereich geschrieben und zum Aufrufer zurückgekehrt.
    Dieser konnte die Zeichen aber nicht ausgeben!

    Habe hier also irgendwo noch nen Verständnisfehler, das zu Zeiger auf Zeiger führt. Gibt es da keine andere Möglichkeit?

    Hatte noch an Variablen gedacht, die in der DLL verfügbar sind (während der gesamten Instanzlaufzeit) und für die dann Speicher reserviert wird, der beim Entladen wieder freigegeben wird.
    Die entsprechenden Adressen werden dann dem reingegebenen Zeiger mitgeteilt.
    Oder brauche ich hier auch Zeiger auf Zeiger (also Adresse des Zeigers)?

    Ciao



  • implementier doch einfach eine free methode in der dll die nur free() aufruft



  • Reth schrieb:

    Warum muss ich hier die Adresse des Zeigers (für den Rückgabeparameter im Prozedurfall) übergeben? Kann mir das jemand vielleicht noch etwas näher erläutern?

    Weil Du den Wert des Zeigers beim Aufrufer verändern willst. Wenn Du nur den Zeiger übergibst (per value), dann kannst Du ihn in einer aufgerufenen Funktion (unabhängig davon, ob das in einer DLL, oder in Deinem eigenen Modul ist) nur lokal überschreiben.
    Angenommen, Du übergibst einen int-wert an eine Funktion und änderst diesen dort, ist die Änderung beim Aufrufer nicht sichtbar - erst wenn Du die Adresse des ints übergibst und via Dereferenzierung den Wert änderst.
    Genau so ist es beim Zeiger auch - Du willst ja nicht den Speicher beschreiben, auf den Dein Zeiger beim Aufrufer zeigt (erstmal zeigt der ja nirgendwohin), sondern den Zeigerwert selbst ändern, deshalb mußt Du die Adresse des Zeigers übergeben, und via Dereferenzierung den Zeigerwert auf den von malloc gelieferten Bereich setzen.



  • @Belli:

    Vielen Dank. Verstanden.

    Gibt es denn eine Möglichkeit, die mit einem einfachen Zeiger auskommt oder mit den Heap-Funktionen?

    Ciao



  • Nur, wenn Du Deinen char-Pointer in einer Struktur versteckst:

    [...]
    
    struct MyPtr
    {
    	char * ptr;
    };
    
    [...]
    
    EXPORT void TestZwei(const char * in, struct MyPtr * out);
    
    [...]
    
    [...]
    
    EXPORT void TestZwei(const char * in, struct MyPtr * out)
    {
    	printf("*** TestZwei Eingabe: %s\n", in);
    	out->ptr = Reverse(in);
    	printf("*** TestZwei Ausgabe: %s\n", out->ptr);
    }
    
    char test[] = "ABCDEFGH";
    	char *res1;
    	struct MyPtr res2;
    
    	[...]
    
    	//und wieder zurück	
    	TestZwei(res1, &res2);
    	printf("\nres2 = %s\n", res2.ptr);
    
    	free(res1);
    	free(res2.ptr);
    

    Allerdings denke ich, daß diese Verfahrensweise die Gefahr birgt, daß man nicht mehr weiß, was denn nun via free freizugeben ist.


  • Mod

    HeapAlloc/HeapFree
    oder BSTR!



  • Martin Richter schrieb:

    HeapAlloc/HeapFree
    oder BSTR!

    BSTR hab ich mir noch nicht angesehen.

    HeapAlloc und HeapFree hatte ich mal probiert, hat aber noch nicht geklappt.

    Gibt es da einen Link zu einem funktionierenden Beispiel?

    Funktioniert das auch, wenn das aufrufenden Programm in einer eher exotischen Programmiersprache gehalten ist (in meinem Fall KCML)?

    Ciao


  • Mod

    Was geht nicht? Zeig mal bitte etwas Code.


Anmelden zum Antworten