[gelöst] Zeiger auf Memberfunktion / LibCurl



  • Hallo,
    ich versuche gerade mich in LibCurl einzuarbeiten. Mit C-Code funktioniert es bisher ganz gut, aber sobald ich die Funktionalität in einer Klasse kapseln möchte, bekomme ich eine Zugriffsverletzung.

    Ich vermute, dass ich da was mit dem Zeiger auf die Memberfunktion falsch mache. Ist aber nur eine Vermutung, ich kann mir gerade keinen Reim darauf machen, was da schief läuft.

    Hier mal der C-Code

    #include <tchar.h>
    #include "curl/curl.h"
    
    FILE* datei;
    bool startSaving = true;
    size_t saveToFile( char *ptr, size_t size, size_t nmemb, void *userdata);
    unsigned int start();
    
    int _tmain(int argc, _TCHAR* argv[])
    {
    	start();
    }
    
    size_t saveToFile( char *ptr, size_t size, size_t nmemb, void *userdata)
    {
    	if(startSaving)
    	{
    		startSaving = false;
    		printf("Lade herunter...");
    	}
    	fwrite(ptr,size,nmemb,datei);
    	return size*nmemb;
    }
    unsigned int start()
    {
    	CURLcode returnCode;
    	CURL* curl = curl_easy_init( );
    	if(curl==NULL)
    	{
    		printf("Fehler: curl==NULL\n");
    		return 1;
    	}
    	returnCode = curl_easy_setopt(curl,CURLOPT_URL,"http://192.168.0.8/install_1.exe");
    	if(returnCode!=CURLE_OK)
    	{
    		printf("Fehler: curl_easy_setopt(curl,CURLOPT_URL,\"http://192.168.0.8/install_1.exe\")\n");
    		return 2;
    	}
    	returnCode = curl_easy_setopt(curl,CURLOPT_WRITEFUNCTION,saveToFile);
    	if(returnCode!=CURLE_OK)
    	{
    		printf("Fehler: curl_easy_setopt(curl,CURLOPT_WRITEFUNCTION,saveToFile)\n");
    		return 3;
    	}
    
    	datei = fopen("D:\\install_1.exe","wb");
    	if(datei==NULL)
    	{
    		printf("Fehler: Konnte Datei nicht oeffnen\n");
    		return 4;		
    	}
    	returnCode = curl_easy_perform(curl);
    	if(returnCode!=CURLE_OK)
    	{
    		printf("Fehler: curl_easy_perform(curl)\n");
    		return 5;
    	}
    
    	curl_easy_cleanup(curl);
    
    	fclose(datei);
    	system("pause");
    	return 0;
    }
    

    und hier der entsprechende C++ Code:

    #include <tchar.h>
    #include "curl/curl.h"
    
    class Downloader
    {
    	private:
    		FILE* datei;
    		bool startSaving;
    		size_t saveToFile( char *ptr, size_t size, size_t nmemb, void *userdata);
    	public:
    		Downloader();
    		unsigned int start();
    };
    Downloader::Downloader()
    {
    	startSaving = true;
    }
    
    int _tmain(int argc, _TCHAR* argv[])
    { 
    	Downloader* dl = new Downloader();
    	dl->start();
    }
    
    size_t Downloader::saveToFile( char *ptr, size_t size, size_t nmemb, void *userdata)
    {
    	if(startSaving)
    	{
    		startSaving = false;
    		printf("Lade herunter...");
    	}
    	fwrite(ptr,size,nmemb,datei);
    	return size*nmemb;
    }
    unsigned int Downloader::start()
    {
    	CURLcode returnCode;
    	CURL* curl = curl_easy_init( );
    	if(curl==NULL)
    	{
    		printf("Fehler: curl==NULL\n");
    		return 1;
    	}
    	returnCode = curl_easy_setopt(curl,CURLOPT_URL,"http://192.168.0.8/install_1.exe");
    	if(returnCode!=CURLE_OK)
    	{
    		printf("Fehler: curl_easy_setopt(curl,CURLOPT_URL,\"http://192.168.0.8/install_1.exe\")\n");
    		return 2;
    	}
    
    	returnCode = curl_easy_setopt(curl,CURLOPT_WRITEFUNCTION,&Downloader::saveToFile);
    	if(returnCode!=CURLE_OK)
    	{
    		printf("Fehler: curl_easy_setopt(curl,CURLOPT_WRITEFUNCTION,saveToFile)\n");
    		return 3;
    	}
    
    	datei = fopen("D:\\install_1.exe","wb");
    	if(datei==NULL)
    	{
    		printf("Fehler: Konnte Datei nicht oeffnen\n");
    		return 4;		
    	}
    	returnCode = curl_easy_perform(curl);
    	if(returnCode!=CURLE_OK)
    	{
    		printf("Fehler: curl_easy_perform(curl)\n");
    		return 5;
    	}
    
    	curl_easy_cleanup(curl);
    
    	fclose(datei);
    	system("pause");
    	return 0;
    }
    

    Wenn ich die C-Variante ausführe, steht in der Konsole "Lade herunter...", das Programm läuft weiter und im Windows-Explorer sehe ich wie eine Datei angelegt wird und die immer größer wird. Passt also...

    In der C++ Variante sehe ich auch in der Konsole "Lade herunter..." stehen, aber unmittelbar danach stürzt mir mein Programm ab mit einer Zugriffsverletzung.

    Meiner Vermutung nach ist der Knackpunkt eben Zeile 51 im C++ Code.

    returnCode = curl_easy_setopt(curl,CURLOPT_WRITEFUNCTION,&Downloader::saveToFile);
    

    Wenn ich da nur

    returnCode = curl_easy_setopt(curl,CURLOPT_WRITEFUNCTION,saveToFile);
    

    hinschreibe, meckert der Compiler und sagt, ich solle eben &Downloader::saveToFile hinschreiben.

    Irgendwie funktioniert es ja auch, denn sonst würde die Konsolen-Ausgabe ja nicht kommen.
    Aber wieso stürzt es dann ab?

    EDIT:
    DIe Zeilen

    printf("Fehler: Konnte Datei nicht oeffnen\n");curl_easy_perform(curl);
    

    geändert in

    printf("Fehler: Konnte Datei nicht oeffnen\n");
    

    ... hat aber nichts mit dem Fehler zu tun...

    MfG



  • Klassenmethoden haben noch einen (im Code nicht sichtbaren) this* Parameter.
    Deine Funktionsignatur sieht also in Wirklichkeit so aus:

    size_t Downloader::saveToFile(Downloader* this, char *ptr, size_t size, size_t nmemb, void *userdata);

    Damit kommt die lib natürlich nicht zurecht. Das Problem kannst du beheben, indem du die Funktion statisch machst, aber dann hast du auch nur wieder eine freie Funktion wie in deinem ersten Beispiel.

    greetz KN4CK3R



  • size_t saveToFile( char *ptr, size_t size, size_t nmemb, void *userdata);
    

    Ratet mal, wozu userdata da ist.



  • Ei jei jei...

    @KN4CK3R: Das mit dem this-Zeiger habe ich mittlerweile nach stundenlanger Recherche auch mitbekommen. Trotzdem vielen Dank.
    Das Problem ist halt, wenn ich die Funktion statisch mache, dann kann ich nicht auf die Variablen des Objekts zugreifen.

    @TyRoXx: Sehr guter Tip, danke!

    Und ich dachte mir für einen Bruchteil einer Sekunde noch: "wie schön wäre es wenn ich einen Zeiger auf mein Objekt als zusätzlichen Parameter übergeben könnte". Hab aber nicht weiter verfolgt ob und wie man void userdata* setzen kann. Hätte nicht gedacht dass ich das irgendwo bestimmen kann was mit void userdata* reinkommt - dabei schreit die Bezeichnung "userdata" geradezu danach, individuelle Daten zu übergeben...
    Tatsächlich stehts in der Doku drin dass man das setzen kann. We liest ist im Vorteil... Aber ich bin nicht darauf gekommen dass ich das als Zeiger auf mein Objekt verwenden könnte. Damit ist die statische Funktion auch kein Problem mehr.

    Also ein einfacher weiterer Aufruf war die Lösung:

    curl_easy_setopt(curl,CURLOPT_WRITEDATA,this);
    

    und dann eben in saveToFile(...)

    Downloader* dl = (Downloader*)userdata;
    


  • SchlechterInformatiker schrieb:

    und dann eben in saveToFile(...)

    Downloader* dl = (Downloader*)userdata;
    

    Mach da mal den C-Cast weg, und schreib es mit einem static_cast.


Anmelden zum Antworten