DLLs mit Klassen?
-
Hallo allerseits,
ich habe ein Programm welches Messwerte von einem Trainingsgerät darstellt, auswertet und analysiert.
Da nun aber verschiedene Trainingsgeräte angeschlossen werden sollen (also immer nur eines). Habe ich mir gedacht gliedere ich die Datenaufnahme aus dem eigentlichen Programm aus und benutze dafür eine DLL. Also dann für das jeweilige Gerät eine andere DLL. Diese kümmert sich dann um die Verbindung, Datenaufbereitung etc. und übergibt die eigentlichen Messwerte dann fix und fertig an meine Hauptsoftware wo sie nur noch in ein Array geschrieben werden brauchen mit dem dann weitergearbeitet werden kann.
Ich kann inzwischen wärend der Laufzeit diese "PlugIns" laden, exportierte Funktionen aufrufen und auch entladen.
Aber wie bekomme ich es jetzt hin, das ich zum Beispiel einfach eine Funktion "Start()" der DLL aufrufe und die ganze DLL sich dann quasi wie ein eigenes Programm verhält (mit Klassen etc.), welches aber 2 oder 3 Funktionszeiger zur Datenübergabe ins Hauptprogramm kennt?
-
Hi,
bin zwar auf dem Gebiet auch erst im einlernen, aber schau dir mal das an:So wie ich dich verstehe, hast du deine DLL explizit verknüpft, rufst sie also immer wieder neu auf und "entlädst" sie danach wieder.
Wenn du sie aber völlig normal als wie eine Methode deines Progis verwenden willst, muss du sie implizit einbinden, also eine .lib machen und diese dann einbinden.
Gruß Squeegee
-
Hi,
danke für die Antwort. Das kenne ich auch, also quasi direkt den Header einbinden und die LIB dazulinken (die ja die wirklichen funktionen dann nicht enthält).
Aber genau so möchte ich es ja nicht haben.ich lade die DLL über AfxLoadLibrary und hole mir dann den Funktionspointer mit FARPROC GetProcAddress(HMODULE, strFunction).
Aber damit kann ich ja immer nur eine Funktion aufrufen. Und diese sollte es eben irgendwie den Rest regeln. Klar soll es dann noch 2-3 weitere Funktionen als Schnittstelle geben.
Frühes binden der DLL geht leider nicht, weil ja während des Betriebs über einen Dialog einfach ne andere "Input-DLL" geladen werden können soll.
Ich hoffe ihr versteht was ich meine und könnt mir tipps geben wonach ich am besten suchen muss, mir fällt irgendwie nix mehr ein. Oder ist das Ansatz falsch?
p.s.: ich glaub im Winamp die Input und- Visualisation-Plugins machen sowas ähnliches..
-
Auch wenn man die DLLL zur Laufzeit lädt kann man sie in deinem Fall gut verwenden.
Was du willst ist ja ein Plugin. Du hast also eine Funktion in der DLL die in jeder dll gleich aussieht. Von Hauptthread rufts du nun diese Funktion (wird aus DLL exportiert) auf und übergibst mögliche Daten. In der DLL-Funktion läuft dann allles weitere ab. Klasseninstanz erstellen, etc.
Zum Schluß oder wann auch immer gibst du dem Hauptthread wieder Daten. Nicht der Hauptthread fragt die Daten ab sondern die DLL liefert ihm Daten wenn sie da sind.
So ist der Sinn eines Plugins.
-
Genauso hab ich mir das auch gedacht
Nur mein Problem ist momentan halt den einstieg zu finden. Ich habe jetzt zum testen erstmal eine Funktion Start(HWND xxx) erstellt. Ich kann natürlich innerhalb dieser eine Klasse instanzieren, die dann aber natürlich bei Funktionsende entfernt wird.
Ich kann diese Klasse natürlich auch auf dem Heap instanzieren, aber da ich ja nix habe um mir den Zieger darauf zu merken kann ich damit ja auch nicht viel anfangen.
Irgendwie brauch ich sone Schleife in der auch alle Windows-Programme laufen bis sie beendet werden. Ich habe soetwas zwar schonmal programmiert als ich mich ein bischen mit OpenGL befasst habe, aber die MFC erledigt das für einen ja eigentlich schon alles, kann man diese Funktionalität dafür nicht irgendwie nutzen, so das man nur noch die gewünschten Schnittstellenfunktionen exportieren muss?vielleicht stehe ich mir auch nur gerade selbst im Weg, aber ich bekomme dazu einfach keinen Ansatz
-
Du weiß aber schon wie ein Programm funktioniert?
Wenn du die Dll lädst hast du ein Handle auf die Funktion dieser DLL. In der DLL erstellt du eine Klasse und ruft die Funktionen auf. Sobald du die Funktion verläßt beendnet sich auch deine Klasse und dann die Funktion in der DLL und kehr wieder zurück wo du die DLL geladen hast.
Also hast du eine Schleife in der DLL selbst und dort macht du was immer du willst.
Willst du in der DLL funktionen abarbeiten und gleichzeitg auch im Hauptthread darstellen brauchst du eh threads
-
Jupp, so in etwa war mir das aber nur klar.
Allerdings das mit dem zurückkehren nach dem Abarbeiten einer Funktion nicht
Habe ich aber eben auch herausgefunden.
Vor allem das eine MFC-DLL ja sofort wenn sie geladen wird eine Klasse instanziert (im standardfall theApp). Und man dann über globale exportierte
Funktionen auch einfach die Methoden der Klasse aufrufen kann.Das innerhalb der Klasse dann ein Thread gestartet werden muss der parallel zum Hauptprogramm arbeiten (und in meinem Falle Daten sammelt) ist mir natürlich klar
supi, ich glaub damit komme ich schonmal ein Riesen stück weiter
DANKE..
-
So da bin ich auch schon wieder
Das bisherige hat alles wunderbar geklappt, ich kann inzwischen die DLL wunderbar von meinem Hauptprogramm aus steuern.
Allerdings habe ich jetzt ein Problem mit dem Rückweg. Unix-Tom hat ja genau geschrieben wie ich mir das vorstelle. Meine DLL hat also getan was sie sollte und möchte nun die Daten an das Hauptprogramm übergeben (also eine Funktion/Methode) im Hauptprogramm aufrufen.
Das ganze müsste dann ja eine Callback-Funktion werden, oder?Ich habe in der DLL jetzt eine exportiert Funktion mit einem void-parameter erstellt. Beim Aufruf aus dem Hauptprogramm heraus übergebe ich den Pointer auf die Funktion die später ausgeführt werden soll.
Diesen Pointer habe ich nun in der DLL - aber wie rufe ich die Funktion darüber auf? irgendwie muss ich jetzt ja den Pointer den ich habe so casten das die DLL erkennt das es eine Funktion ist. (ist erstmal nur eine "void func()" - Methode).
Die Function der DLL sieht momentan folgendermaßen aus:void SetFunc(void callback) { callback(); }
Ich bekomme bisher natürlich immer den Fehler:
error C2182: 'callback' : Ungueltige Verwendung des Typs 'void'
wie erkläre ich nun das in callback eigentlich der Zeiger auf eine funktion vom typ "void func()" ist?
edit: Problem gelöst
es mussvoid SetFunc(void callback()) { callback(); }
heissen, dann klappt's auch mit der Funktion..
-
So und vorerst wohl hoffentlich zum letzten mal.
Ich möchte ja den Pointer einer Methode an eine Funktion der DLL übergeben, damit diese dann später über die die Methode aufrufen kann.
Ich dachte auch zuerst das funktioniert. Nur hatte ich leider als Funktionstyp bei der Übergabe "void func(void)" angegeben und beim Aufruf dann vergessen den Parameter anzugeben. Da lief das Programm natürlich bis es versuchte auf den nicht übergebenen Pointer auf die Methode zuzugreifen - pengAber nun bekomme ich es einfach nicht hin den Pointer auf die Methode an die Funktion der DLL zu übergeben.
Hier mal ein bischen Code dazu:
Aufruf der Funktion aus der DLL:
void CDllclientDlg::ModuleFunction(CString strFunction) { //.. AfxLoadLibrary etc. pp. typedef void (*MYPROC) (void callback(void)); MYPROC proc = NULL; proc = (MYPROC)GetProcAddress(hModule, strFunction); if(proc == NULL) { proc(TestFunc); // <-- FEHLER: Konvertierung des Parameters 1 von '' in 'void (__cdecl *)(void)' nicht moeglich } }
Die einfache Funktion die übergeben werden soll
void CDllclientDlg::TestFunc() { TRACE("Diese kommt auf dem Hauptprogramm\n"); }
Die Funktion der DLL steht ja oben schon, aber der Fehler liegt hat darin, das ich es nicht hinbekomme "TestFunc" and proc zu übergeben, wieso ist "TestFunc" laut Fehlermeldung überhaupt vom Typ '' ?!?
puh..
-
&TestFunc ?
-
Die Adressen von Funktionen bekommt man, wenn man die Klammern weglässt
Aber ich habs zum Spass einfach mal ausprobiert:
error C2276: '&' : Ungueltige Operation auf Ausdruck einer gebundenen Member-Funktion
trotzdem danke
ich habe das ganze bei Threadprogrammierung schonmal gemacht. Nur da gehörte die Funktion zur gleichen Klasse und kam nicht aus ner DLL, aber soviel ich die beiden Versionen auch vergleiche, ich sehe keinen Unterschied. Nur das dieses hier durch die "proc" Variable halt komplizierter ist (und nicht funktioniert)
-
So funktioniert jetzt auch
Die MSDN hat mich auf die Lösung gebracht.
Es muss heissen:
typedef void (*MYPROC) (...);
In allen möglichen MSDN-Beiträgen Beiträgen wird der Funktionszeiger für Funktionen mit Parametern immer nur mit
typedef void (*MYPROC) ();
angegeben - in einem Kommentar steht das das nur bei C geht, aber das c++ dann keine Parameter akzeptiert und man die Parameter angeben muss. Naja und dann hab ich einfach die unbekannte Parameteranzahl für C++ eingesetzt, da ich nen Funktionspointer als Parameter eines Funktionspointers nicht engegeben bekommen habe
edit: Falls es wen interessiert:
http://support.microsoft.com/default.aspx?scid=kb;en-us;117428
-
Warum denn nicht so?
Also das ist jetzt nicht getestet aber so müsste es doch gehen, oder?void CKlassA::machs() { typedef void(CKLasseA::*f)(void); typedef void(*b)(f); B = static_cast<b>(GetProcAddr(.....)); B(machs_auch); }
-
Hi,
danke für die Antwort
ich habe das jetzt mal so eingebaut:
typedef void(CDllclientDlg::*f)(void); typedef void(*b)(f); b B = static_cast<b>(GetProcAddress(m_Modules.GetAt(i), strFunction)); B(TestFunc);
Also so ganz funzt das so noch nicht.
error C2440: 'static_cast' : 'int (__stdcall *)(void)' kann nicht in 'void (__cdecl )(void (__thiscall CDllclientDlg::)(void))' konvertiert werden
Wobei mir dabei das erste typedef noch nicht so ganz klar ist. Soll die als Platzhalter für den Parameter dienen? Weil den Methodennamen gebe ich ja beim Aufruf dann an, oder?
-
Das erste typedef sagt deinem Compiler, dass der Funktionspointer auf eine Klassenmethode zeigt.
Ich nutze das gerne bei Threads, da ich so ein gute Kapselung habe.
SP übergebe ich meinen Threads (die ja statisch sein müssen) eine Klasseninstanz und einen Klassenfunktionspointer. So kann ich dann aus meinem statischen Thread eine bestimmte Klassenfunktion einer Klasse aufrufen.Vll. kommt der Fehler nicht mehr, wenn du das weglässt.
Wenn es dich interessiert, dann poste ich hier mal die Sache mit dem Thread.
-
Öhh, was jetzt weglassen?
Wenn ich das erste typedef weglasse bekomme ich ja wieder den Fehler das mein Funktionspointer nicht zu param1 "void (__cdecl *)(void)" konvertiert werden kann.
Bei Worker-Threads mache ich es bisher so, das ich eine statische Methode erstelle, die einen Pointer auf die (eigene) Klasse erhält (zweiter param von AfxBeginThread), den ich dann explizit zum Typ der Klasse caste und darüber einfach eine nicht-statische Methode der Klasse aufrufe die dann ganz normale auf alle Member etc zugreifen kann.
Aber zeig mal ruhig wie du das machst - interessiert mich immer
-
Ich meinte lass das CKlasseA:: weg
___
.hclass Huho { static UINT THREAD(LPVOID); void func(void); typedef void(Huho::*f)(void); struct t_data( t_data(Huho* pClass_, f pFunc_) : pClass(pClass_) , pFunc(pFunc_) {} ~t_data() { pClass = 0; pFunc = 0; } void operator() () { if(pClass && pFunc) { (pClass->*pFunc)(); } } private: Huho* pClass; f pFunc; }; };
.cpp
void Huho::ThreadAufruf() { AfxBeginThread(THREAD, new t_data(this, func)); } void Huho::func() { /* ... */ } UINT Huho::THREAD(LPVOID param) { t_data *data = static_cast<t_data*>(param); (*data)(); delete data; return 0; }