Callback über Funktionszeiger bringt Programm zum Absturz
-
Noch immer programmiere ich an einem dll-Plugin für ein externes Programm rum. Diesmal geht es um folgendes:
Ich zeige erstmal den source, danach die Erklärung:
gemeinsame Header-Dateistruct ServerConfiguration { char* address; int port; void (*listenerFunction)(const char*); };
Aktivierungsfunktion der DLL, sowie callback Funktion
void receiveMessage(const char* msg) { printf("%s", msg); } int startServerProxy(...) { struct ServerConfiguration config; config.address = "127.0.0.1"; config.port = 4242; config.listenerFunction = receiveMessage; // ** PART1 ** // void (*f)(const char*) = receiveMessage; // (*f)("Test"); // *********** _beginthread(startServer, 0, &config); }
Datei mit Thread-Methode
void (*listenerFunction)(const char*); void startServer(void* arg) { struct ServerConfiguration* config = (struct ServerConfiguration*)arg; listenerFunction = config->listenerFunction; (*listenerFunction)("Test"); }
Also der code der DLL wird ausgeführt über die Funktion startServerProxy. Dort wird die Struktur 'ServerConfiguration' mit allen Infos beladen, die die Funktion 'startServer' braucht Funktion. startServer wird als eigener Thread geladen und soll in der Lage sein, über den Funktionszeiger 'listenerFunction' Nachrichten an die Funktion 'receiveMessage' senden zu können.
Mein Programm, für das ich die Dll programmiere, quittiert mir den Aufruf des Funktionszeigers mit einem Absturz, was erfahrungsgemäß eine Speicherverletzung o.ä. bedeutet.
Was ich nicht verstehe ist, die Funktionszeiger haben zumindest einen Wert und außerdem funktioniert der code, den ich unter PART1 auskommentiert habe.Kann jemand den Fehler erahnen?
-
f()
reicht.
denn () ist bereits eine indirektion...
-
Die Variable "config" liegt auf dem Stack und wird zerstört sobald die Funktion startServerProxy zurückkehrt. Probier mal, obs funktioniert wenn ein static davor steht:
int startServerProxy(...) { :arrow_right: static struct ServerConfiguration config; config.address = "127.0.0.1"; config.port = 4242; config.listenerFunction = receiveMessage; // ** PART1 ** // void (*f)(const char*) = receiveMessage; // (*f)("Test"); // *********** _beginthread(startServer, 0, &config); }
-
Danke, Du hast völlig Recht, es lag an der lokalen Variable. Aber ich hatte mir darüber Gedanken gemacht und dachte, es gäbe keine Probleme:
Zunächst wird die Struktur lokal angelegt, und 'listenerFunction' bekommt einen Funktionszeiger zugewiesen. In 'startServer' wird ja nur der Zeiger kopiert, das bedeutet, selbst, wenn nach Ablauf der Funktion 'startServerProxy' die Struktur verworfen wird befindet sich an der kopierten Zeigeradresse noch immer die Funktion, der Aufruf sollte also gültig sein. Oder irre ich mich?
-
Der Funktionspointer ist schon noch gültig, aber die Struktur in der dieser Pointer gespeichert ist, ist halt nicht mehr gültig (manchmal kanns auch gut gehen).
-
Aber durch die Anweisung:
listenerFunction = config->listenerFunction;
wird der Pointer doch kopiert (im Sinne dupliziert), somit kann es doch egal sein, ob die Struktur 'config' verfällt, die Adresse (mit gültiger Funktion darin) ist somit doch gerettet..
-
Theoretisch schon...
Aber ich würde mich nicht darauf verlassen, dass der Hauptthread sich noch in startServerProxy(...) befindet, wenn der zu startende Thread dann auch tatsächlich gestartet wird. _beginthread() heißt nur, dass (irgendwann wenn Windows dazu Lust hat) ein Thread gestartet wird. Der Hauptthread läuft parallel weiter. Da nach beginthread() nichts mehr steht, ist die Wahrscheinlichkeit ziemlich hoch, dass diese Funktion bereits verlassen wurde, wenn der Thread gestartet wird und bis zu der Zeile kommt wo der Pointer kopiert wird.
-
Ahh ok, jetzt hab ich es. Danke für Deine Hilfe!