DLL und die Objekte in der DLL
-
Hallo
Ich bin auf der Suche nach einer Lösung für eine DLL Implementierung in meiner Anwendung.
Meine Anwendung ist in mehrere Teile aufgeteilt. Auf der einen Seite ist die Grafik, die bei mir in QT realisiert werden soll. Diese Grafik wird als ausführbare Datei kompiliert und soll später einen möglichen Start in mein Programm darstellen.
Auf der anderen Seite habe ich meinen Core. Der Core wird als DLL erstellt und soll an den jeweiligen Schnittstellen (Pluginschnittstelle, Ausgabeschnittstelle) usw. mit den jeweiligen Komponenten verbunden werden. An der Grafikschnittstelle arbeite ich gerade.
Den Core hab ich komplett in einem objektorientiertem Design entwickelt und er stellt diverse Klassen für unterschiedliche Elemente meines Programmes dar. So zum Beispiel eine Core-Klasse die den Core initialisiert (diverse operationen und anlegen von Datenobjekten...). Des Weiteren besitzt der Core noch eine Manager-Klasse die die Kommunikation mit diversen Objekten innerhalb des Cores mit der Grafik realisieren soll.Da das alles als DLL implementiert wird, hab ich natürlich auch das Problem, dass ich einige dieser Klassen und Objekte nach außen hin verfügbar machen muß. Und da kommen wir zu meinem Anliegen.

Ich habe mich innerhalb dieses Themas ein wenig belesen und es kommt in vielen Foren, Blogs und bei vielen Freunden immer wieder auf das selbe raus. Kurz:
Exportierte Wrapperfunktionen in der DLL die den Datenverkehr mit den Objekten aus der DLL handlen.Ein sehr schönes, erklärendes Beispiel wurde auch hier im Forum diskutiert.
http://www.c-plusplus.net/forum/viewtopic-var-t-is-177343.html
Was mein Problem ist, ist dass ich wahrscheinlich nicht nur 2 oder 3 Objektmethoden aufrufen muß, sondern 10-20.
Das ist schon recht viel und ich bin am Überlegen wie ich das Interface zwischen dem Core und der Grafik besser implementieren kann, so dass ich nicht für jede Objektmethode eine eigene Wrappermethode schreiben muß. Zumal der Core ja auch noch massiv weiterentwickelt wird und somit doppelte Arbeit entsteht.Meine Gedanken schweifen da immer wieder zu einer Funktion die alle Aufrufe handelt und die immer dann aufgerufen werden soll, wenn Daten/Objekte in die DLL oder aus der DLL benutzt werden sollen. Probleme hab ich dabei die theoretisch nicht sehr schwierige Funktion in C++ darzustellen und zu implementieren. Das Prinzip der Funktion ist folgendermaßen:
Bei einem Methodenaufruf eines Objektes wird die allgemeine Wrapperfunktion aufgerufen, die als Argumente folgende Daten erhält:
1. Instanzpointer des aufzurufenden Objektes
2. Methodenname der aufgerufen werden soll (idealerweise String oder char*)
3. Array mit Übergabeparameter die die Methode benötigt.Die Wrapperfunktion nimmt sich den Instanzpointer des Objektes, und läd über Refloctions (Invoke Methodik) die jeweilige Methode. Die Parameter werden aus der Arraylist ebenso übergeben.
Soweit meine Idee:
Probleme:1. Reflections (invoke Methode) gibts nicht (wurde mir mitgeteilt von einem Bekannten)
2. Es ist schwer bis gar nicht zu testen ob die übergebenen Argumente dem entsprechen was gefordert wird -> schlecht da instabil bei programmierfehlern.Es gibt sicher noch mehr Probleme, aber nach diesen beiden hab ich schon mal aufgehört nachzudenken.
Nun meine Frage an euch:
Wie würdet ihr diese Aufgabe lösen neben den üblichen Methodiken eine dll zu nutzen, welche gibt es noch? Vorgabe ist nach wie vor der objektorientierte Ansatz. Ist mein Vorschlag überhaupt tragbar oder ist der Aufgrund der genannten und noch nicht genannten Probleme unlösbar. Warum?
Ich hoffe ich habe alles so geschrieben, dass ihr meine Problematik versteht und hoffe auf einige Tipps, die ich umsetzen könnte.
Danke schon mal für eure Hilfe.
Grüße
Daimonion
-
Hallo nochmal.
Ich mach mal noch einen neuen Beitrag, weil es noch einige Infos gibt.
Ich hab ja auch schon ausgiebig rumexperimentiert.
Ich hab, wie gesagt eine Core-Klasse die initialisiert den Rest des Cores, also der DLL.
Diese hat natürlich auch einen Header.
Hier mal abgebildet:#ifdef __DLL__ #define CONCORE __declspec(dllexport) #else #define CONCORE __declspec(dllimport) #endif namespace Core { class ConCore { public: ConCore(); virtual void start(); private: void readConfiguration(char* filename); }; } extern "C" CONCORE Core::ConCore* GetDllClass();Es gibt eine exportierte Methode, GetDllClass(), die mir eine Instanz auf das Objekt zurück gibt.
Des Weiteren habe ich die Methode start() als virtual deklariert. Dazu später mehr.Aufgerufen wird die DLL und die GetDllClass() aus meiner Grafikoberfläche:
void UI::initCore() { //try to Load the coredll HINSTANCE coredll; coredll = LoadLibrary("Contentus Core.dll"); MyClass mc = (MyClass)GetProcAddress(coredll, "GetDllClass"); ConCore* Core = NULL; DWORD err = NULL; if ( NULL != mc ) { Core = (mc)(); } else { DWORD err = GetLastError(); } if (err == NULL) { Core->start(); } }hier kann ich dann auch die Methode Core->start() aufrufen und sie wird auch ausgeführt (durch debugging festgestellt). Das funktiniert aber nur wenn ich start als virtual deklariere. Ohne diese deklaration wirft der Linker mir das Zeugs um die Ohren

Aber warum ist das denn so? Ich mein die lösung ist vielleicht nicht schlecht, aber nur wenn es keine versteckten Tücken gibt.
Grüße
Daimonion
-
Ich würde entweder eine C++ DLL machen (also gleich die ganzen Klassen exportieren), oder aber eine "objektorientierte C-DLL".
Möglichkeit 1 ist vollkommen OK wenn du alle Teile des Programms (alle .EXEs, .DLLs, .OCXs etc.) selbst compilierst, und alle mit den gleichen Einstellungen und dem gleichen Compiler.
Wenn das ein Problem sein sollte, Möglichkeit 2:
/* Foo.h: */ #ifdef __cplusplus extern "C" { #endif struct Foo; /* das Foo Objekt - nur fwd. decl hier, definition irgendwo "privat" in den Sourcen der DLL */ struct Foo* Foo_Create(void); void Foo_Delete(struct Foo* that); void Foo_DoSomething(struct Foo* that, int a, int b); int Foo_DoSomethingElse(struct Foo* that); /* ... */ #ifdef __cplusplus } // extern "C" #endifDie "Wrapper Structs" (eben Foo im Beispiel oben) leitest du dann z.B. einfach von deinen C++ Klassen ab, und machst halt nen Haufen forwarding-Funktionen. Viel Tipparbeit, aber eigentlich keine grosse Sache.
Oder du gehst einen Mittelweg, und exportierst nur "Create" Funktionen die Zeiger auf rein abstrakte Klassen (=nur virtual pure Funktionen) zurückgeben.
Das sollte dann eigentlich mit allen C++ Compilern funktionieren die COM unterstützen.Meiner Meinung nach ist es auf jeden Fall vorzuziehen ganze C++ Klassen mit __declspec(dllexport) zu exportieren, ist am "schönsten" weil man die genau wie andere C++ Klassen im Client der DLL verwenden kann, und auch am wenigsten Aufwand. Und man kann auch einfach Sachen wie std::string im Interface verwenden, was natürlich flach fällt wenn man eine C-DLL macht.
-
Ich würde auch (wie hustbaer es schon erwähnte) eine Factory bauen, die ein Interace returniert.
Das erlaubt die DLL auf Exports für die Factory Methoden zu reduzieren und macht die Schnittstelle und großen Aufwand einfach.
-
Hallo und erst mal Danke für die hilfreichen Antworten.
Zu deinem Beispiel mit der reinen C-objektorientierten DLL hab ich noch ne kleine Verständnisfrage. Kann ich ein C++ ein Struct von einer Klasse ableiten lassen?
hustbaer schrieb:
Oder du gehst einen Mittelweg, und exportierst nur "Create" Funktionen die Zeiger auf rein abstrakte Klassen (=nur virtual pure Funktionen) zurückgeben.
Das sollte dann eigentlich mit allen C++ Compilern funktionieren die COM unterstützen.Kann es sein das ich diesen Mittelweg schon gegangen bin, indem ich die Methoden wie in meinem 2. Post schon als Virtual deklariert hab?
Das exportieren der ganzen Klasse mag der einfachste Weg sein, aber ich weiß nicht wie das Projekt weitergehen soll, wenn ich mal nicht da bin. (und das ist bald) Daher will ich das nur nutzen, wenn ich keine andere elegante Lösung sehe. Das exportieren mittels C-Objektorientierung will ich mir auch aufheben, da es schon recht viel Schreibarbeit ist und meine Zeit langsam knapp wird.
Aber es wird den Weg in mein Wissensrepertoire finden, falls ich es mal wieder brauch.Danke auf alle Fälle für die Tipps. Das hilft mir weiter
-
Nein! Du bist diesen Mittelweg noch nicht gegangen. Du müsstest dazu nur ein Interface returnieren. Dieses Interface ist in dem nutzenden Modul nur über eine virtuelle Klasse bekannt.
-
Hmm, okay, danke, versteh ich.
Die von mir im zweiten Post erläuterte Methode funktioniert ja auch. Rein interessehalber würd ich schon gern wissen warum. Es ist mir unverständlich warum ich mit einer virtual deklarierten Funktion diese dann aufrufen kann. Ist diese Methode Compilerabhängig?
-
Wenn die Methode normalen gebunden ist (nicht virtuell), dann wird auch der Linker benötigt die Startadresse zu finden. Ist die Funktion virtuell, dann erfolgt der Aufruf in über die vtable (einen Array von Zeigern auf Funktionen), dieser Array wird aber durch den Erzeuger der Klasse erzeugt und gefüllt.
Dein Aufrufendes Program weiß also nur. Nimm die soundsovielte Funktion aus dem vtable Array.HTH
Die erreichst, damit etwas ähnliches, aber nicht dasselbe, wie bei puren Interfaces.
-
Okay danke.
Demzufolge legt der Linker die funktionen die virtual markiert sind in ein Array und somit ist diese sichtbar?! (Im übertragenden Sinne gesprochen...)
dann werd ich mal schauen ob ich ein richtiges Interface zusammenbekomme.
Grüße
Daimonion
-
Etwas anders. Das Objekt wird in der DLL angelegt und die DLL füllt die vtable dann.
Der Code den die EXE ausführt weiß nur: virtuelle Funktion, d.h. nimm Funktionszeiger aus vtable. Die entsprechende Funktion liegt in der DLL.
Nichts anderes passiert wenn ein Interface (eine vtable) in der DLL angelegtwird und dieser Zeiger returniert wird.
-
Alles klar. Danke.
Ich werde das jetzt ersst mal alles anwenden und versuchen Erfahrung darin zu sammeln.
Ich finde es gibt im Internet immer nur recht wenige Informationen, daher würde ich, wenn ich mich in dieser Materie besser auskenne einen Artikel (Howto) oder wie immer man das nennen mag darüber schreiben, der dann natürlich auch dem forum hier zugute kommen würde.
Grüße
Daimonion