DLL's nutzen, 2 grundlegende Verständnissfragen



  • Hintergrund
    Ich arbeite derzeit an meinem Diplom und schreibe dafür mit einem anderen Diplomanten zusammen eine Windows Anwendung. Er erzeugt die grafische Oberfläche und ich eine 3D Darstellung von verschiedenen Messwerten. Damit diese beiden Teile möglichst autark agieren können, hab ich mich entschlossen meinen Teil als DLL zu realisieren. Nun arbeiten wir bereits seit 3 Wochen täglich an dem Programm und hatten bisher keine Probleme. Ich hatte ihm anfangs eine spezielle Headerdatei geschrieben, die nur die Schnittstellen der Engine Klasse beinhaltete. Er hat dann ein Objekt der Engine Klasse erzeugt und gestartet. Er hatte also nur einen "minimal-Header" meiner Engine Klasse, der lediglich die Klasse selbst mit 2 Methoden kannte. diese beiden Methoden hatten wir uns als Schnittstelle ausgemacht. Eine initialisiert DirectX und die andere startet dann die eigentliche Ausgabe.

    Nun lief das lange Zeit wunderbar. Trotz das beide Projekte für sich, also GUI und Engine, aus vielen einzelnen Quelltext- und Headerdateien bestehen, brauchte ich ihm immer nur meine aktuelle DLL zuschicken. Denn nachdem einmal am Anfang der Minimal-Header meiner Engine bei der GUI eingebunden war, war es dem Programm völlig Wurst ob in der DLL-Version der Engine dann noch mehr drin ist. So brauchte er immer nur die DLL in sein Programmverzeichnis kopieren und seine .exe zu starten. Da sich an der Schnittstelle zwischen GUI und Engine auch nichts weiter ändert, dachte ich das funktioniert so.

    Nun allerdings fängt das Programm immer mehr an rumzuzicken. Es stürzt gelegentlich an den unmöglichsten Stellen mit Laufzeitfehlern ab. Zum testen hat ich dann einmal die GUI neu kompiliert. Diesmal fügte ich aber alle Header der DLL mit zur GUI hinzu. Das Programm war dann auf einmal bedeutend stabiler. Das zerbrach natürlich unsere Hoffnung/Illusion/Grundlage das die GUI lediglich die von ihr direkt genutzten Methoden der DLL kennen muss.

    daher stellen sich mir ein paar Fragen:
    - Ist es überhaupt möglich, wie oben beschrieben, dem Hauptprogramm nur eine abgespeckte Header-Version der DLL zu geben, die lediglich die direkt aufgerufenen Methoden beinhaltet oder benötigt das Hauptprogramm immer alle Header der gesamten DLL? (Was bedeuten würde, das ich bei jeder änderung ihm alle Header neu zuschicken müsste und er auch immer wieder diese einbinden müsste.)

    - Gibt es vielleicht eine (simple) Alternative wie man es schafft, das das Hauptprogramm die DLL aufruft ohne alles von der DLL zu wissen? (Also das es nur die direkt aufgerufenen Methoden/Klassen kennt)

    - Wenn ich zu ein und der gleichen Klasse 2 verschiedene Headerversionen habe. Eine binde ich in das Hauptprogramm ein und compile dieses. Die zweite binde ich in die DLL ein und compile diese. Welche Version wird dann beim Aufruf des Hauptprogrammes (welches ja die DLL nutzt) verwendet?



  • kokunze schrieb:

    - Ist es überhaupt möglich, wie oben beschrieben, dem Hauptprogramm nur eine abgespeckte Header-Version der DLL zu geben, die lediglich die direkt aufgerufenen Methoden beinhaltet oder benötigt das Hauptprogramm immer alle Header der gesamten DLL? (Was bedeuten würde, das ich bei jeder änderung ihm alle Header neu zuschicken müsste und er auch immer wieder diese einbinden müsste.)

    Wenn es sicht dabei um "Klassen" handelt: NEIN!
    Wenn es "reine" C-Funktionen sind: Ja

    Bei Klassen muss dem "benutzer" die gesamte Klasse mitgegeben werden, damit der die passenden Einsprünge für virtuelle Methoden findet.

    kokunze schrieb:

    - Gibt es vielleicht eine (simple) Alternative wie man es schafft, das das Hauptprogramm die DLL aufruft ohne alles von der DLL zu wissen? (Also das es nur die direkt aufgerufenen Methoden/Klassen kennt)

    ich rate immer dazu: Macht ein "reines" C-Interface! Also nur Funktionen und arbeite nicht mit Klassen! Dann bist Du auch unabhängig von der Compiler-Version! Wenn Du Klassen verwendest, ist es abhängig vom Compiler und kann auch nicgt via "LoadLibrary" verwendet werden...
    Also: Nur C-Funktionen, der Form:

    HMYINST InitInstance();
    void CallMethod1(HMYINST int, int param1, const wchar_t *szParam2);
    BOOL CallMethod2(HMYINST int);
    ReleaseInstance(HMYINST inst);
    

    kokunze schrieb:

    - Wenn ich zu ein und der gleichen Klasse 2 verschiedene Headerversionen habe. Eine binde ich in das Hauptprogramm ein und compile dieses. Die zweite binde ich in die DLL ein und compile diese. Welche Version wird dann beim Aufruf des Hauptprogrammes (welches ja die DLL nutzt) verwendet?

    Wie gesagt: Das verursacht nur Probleme, wenn Du wirtuelle Methoden hast! Ganz zu schweigen davon, dass dies nicht wartbar ist!



  • Hm, ich versteh zwar was du meinst, kann es aber leider nicht direkt auf mein Projekt übertragen. Vielleicht kannst du mir noch etwas genauer sagen, wie es dann für mein Projekt aussehen würde.

    Als Hilfe post ich mal die entscheidenden Stellen aus den Programmen. Die komplette Klassendefinition lass ich mal weg, das wär wohl zuviel. Aber ich denke das Anliegen dürfte aus folgenden Codefetzen hervorgehen.

    Der Teil der Klasse, der im Hauptprogramm benötigt wird.

    class __declspec(dllexport) CEngine : public IEngine
    {
    public:
    	bool	InitEngine(HINSTANCE hInst, HWND &hParentWnd, CParameters params, CNoiseData nData);	
    	void	MainLoop(void);		// Die Hauptschleife der 3D-Anwendung	
    
    };	// ~class CEngine;
    

    Der Aufruf der DLL im Hauptprogramm.

    if (m_Engine.InitEngine(m_hInstance, m_hMainWnd, m_pParams, m_pNoiseData) == true)
    		m_Engine.MainLoop();
    

    Sprich das Hauptprogramm hat ein Memberobjekt der Klasse CEngine, mit Namen m_Engine. Für dieses ruft es dann die initialisierung und den MainLoop der Engine auf.



  • Ich würde hier sowas machen:

    // Wenn es pro DLL nur max. eine Instanz geben darf:

    bool InitEngine(HINSTANCE hInst, HWND hParentWnd, CParameters params, CNoiseData nData);
    // Wobei hier natprlich "CParameters" und "CNoiseData" ja auch Klassen sind, die dann aber auch als normales C-Interface verfügbar sein sollten!
    void MainLoop(void);
    void Deinit();
    

    // Wenn es pro DLL mehrere Instanzen geben darf:

    HMYINST InitEngine(HINSTANCE hInst, HWND hParentWnd, CParameters params, CNoiseData nData);
    // Wobei hier natprlich "CParameters" und "CNoiseData" ja auch Klassen sind, die dann aber auch als normales C-Interface verfügbar sein sollten!
    void MainLoop(HMYINST inst);
    void Deinit(HMYINST inst);
    


  • Hmm...
    und wenn ich quasi diese Header Datei als Schnittstelle nehme, könnte das CEngine Objekt ja dann innerhalb der DLL angelegt werden. Sprich dann bräuchte das Hauptprogramm nur den besagten kurzen Header und der Rest wäre für dieses verborgen. Klingt gut...



  • Wobei Du noch den Nachteil hast, dass Du zwei Klassen übergibst: CParameters und CNoiseData.
    Die solltest Du, wenn möglich, auch noch eliminieren, sonst macht das ganze relativ wenig Sinn...
    z.B. via:

    HMYINST InitEngine(HINSTANCE hInst, HWND hParentWnd); 
    void AddParam(HMYINST inst, const char *key, const char *value);
    void AddNoiseData(HMYINST inst, /* keine ahnung */);
    void MainLoop(HMYINST inst); 
    void Deinit(HMYINST inst);
    

    PS: HMYINST ist einfach ein "void*" welcher intern auf den Klassenzeige gesated wird (reinterprect_cast)



  • Ja das stimmt, das mit den beiden gemeinsam genutzten Klassen war mir eh schon ein Dorn im Auge.

    EDIT,

    kleine Frage, warum "const char" bei AddParameter?



  • "const char*" sollte man immer verwenden, wenn man den String nicht verändert!



  • Achso, klar.

    Danke für deine Hilfe!


Anmelden zum Antworten