[solved] Dynamisch eingebundene DLL schmeißt exceptions.



  • Ich möchte mein Programm zur Laufzeit mit neuen DLLs versorgen, um beliebig Inhalt durch neue Methoden hinzuzufügen.

    Das Laden der DLL funktioniert, nur irgendwie funktioniert das benutzen der entsprechend implementierte Funktionen nicht.

    So lade ich die DLL und führe exemplarisch eine Funktion aus, die eine exception wirft:

    Unbehandelte Ausnahme bei 0x000007FEE346309D (msvcp110d.dll) in Prog.exe: 0xC0000005: Zugriffsverletzung beim Lesen an Position 0xFFFFFFFFFFFFFFFF
    
    HMODULE					hDLL;
        DllClassGetFunction     fGet;
    	funct					fMeth;
        StrategyInterface		*cClass;
    
    	hDLL = LoadLibrary(getLPCWFromStr("Extra.dll").c_str()); // Funktioniert, hDLL ist nicht null.
    
        if(hDLL == NULL)  {
    		return ErrType::Failure;
        }
        fGet = (DllClassGetFunction)(GetProcAddress(hDLL,"getClass"));	// Funktion wird auch gefunden.
        if(fGet == NULL) {
    
    		return ErrType::Failure;
    
        } else {
    		StrategyInterface* bla = fGet();
    		bla->getStrategyName();		// hier gibt es einen Fehler.
    		return ErrType::Success;
    	}
    

    Sowohl das ausführende Programm sowie das Projekt mit dem ich die dlls erstelle haben die gleichen Interface header:

    class StrategyInterface {
    	public:
    		virtual				~StrategyInterface(){}
    		virtual _ErrType	method(vector<StreamDataBlock>* scope, StreamPlotable* sPlotable) = 0;
    		virtual string		getStrategyName() = 0;
    		virtual string		getStrategyBrief() = 0;
    
    		void				assignCaption		(StreamSubDataBlock* sSDB, SubStreamPlotable* sSP);
    		void				saveLargerNum		(_u32& dest, _u32 comp);
    		void				saveSmalerNum		(_u32& dest, _u32 comp);
    };
    

    Und das hier ist der Code zum erstellen der DLL:

    class Strategy : public StrategyInterface {
    public:
    	ErrType method(vector<StreamDataBlock>* scope, StreamPlotable* sPlotable) {
    		return ErrType::Success;
    	}
    
    	string		getStrategyName() {
    		return "blubb";
    	}
    	string		getStrategyBrief() {
    		return "bla";
    	}
    
    };
    
    #define STRATEGYDLL_API __declspec(dllexport)
    
    BOOL APIENTRY DllMain(HMODULE hModule, 
                          DWORD dwReasonForCall, 
                          LPVOID pReserved)
    {
        switch(dwReasonForCall)
        {
            case DLL_PROCESS_ATTACH:
                break;
            case DLL_PROCESS_DETACH:
                break;
        }
    
        return TRUE;
    }
    
    extern "C"
    STRATEGYDLL_API Strategy*	getClass() {
    	return new Strategy;
    }
    

    Wenn ich mit dem pointer den ich von fGet() bekomme eine der nicht virtuellen Funktionen aus dem Interface aufrufe funktioniert alles.



  • niemand eine idee woran das liegt?

    ich hab noch etwas anderes getestet:

    virtual int			bla(int* a) = 0; // diese funktion habe ich dem Interface hinzugefügt.
    
    int			bla(int* a) {
    		int b = (*a) + 1;
    		return b; // und so in der dll implementiert.
    	}
    

    Diese Funktion lässt sich auffrufen...
    gibt aber ein fehlerhaftes ergebnis zurück.
    Muss man irgendetwas mit pointern und dlls beachten?
    Ich versteh vorallem nicht wieso es nicht klappt. Eigentlich müsste ich doch einfach eine Instanz dieser Klasse bekommen.



  • [quote="cl90"]niemand eine idee woran das liegt?[(quote]
    Wenn fGet() so ist wie bla(), ist der Fehler sonneklar.

    Aber nicht klar ist, wie man Dir beibringt, erstmal ein gutes C++-Buch (vielleicht nicht Wolf, P&P, M+T, …Kompenium, …in 10 Tagen) ganz durchzulesen und ein klitzewenig C++ zu lernen, *bevor* Du ein Game proggers.



  • DLL und Dein Programm sind mit demselben Compiler und denselben Einstellungen erstellt?

    Ansonsten ist es keine gute Idee Instanzen von Klassen als Parameter oder Rückgabewerte von Funktionen zu verwenden.
    Nicht umsonst verwendet die WinApi nur structs unds Char-Zeiger.



  • Er reicht doch da nur Zeiger rum!?



  • volkard schrieb:

    Aber nicht klar ist, wie man Dir beibringt, erstmal ein gutes C++-Buch (vielleicht nicht Wolf, P&P, M+T, …Kompenium, …in 10 Tagen) ganz durchzulesen und ein klitzewenig C++ zu lernen, *bevor* Du ein Game proggers.

    Hättest du wenigstens den Post gelesen bevor du so überflüssige Kommentare abgibst wär dir nicht nur klar geworden das fGet() nicht so aussieht, und wir hätten uns dein MiMi erspaart.
    Du bist wohl perfekt geboren worden hm?
    Und wer sagt das es um ein bescheuertes Spiel geht.

    MichelRT schrieb:

    DLL und Dein Programm sind mit demselben Compiler und denselben Einstellungen erstellt?

    Ja. Beide MSVC2012 beide x64 eingestellt. Muss wirklich alles exakt gleich sein? Könnte Multibytezeichensatz soetwas auch beinflussen? also muss ich wirklich jede Projekteinstellung durchsehen?

    MichelRT schrieb:

    Ansonsten ist es keine gute Idee Instanzen von Klassen als Parameter oder Rückgabewerte von Funktionen zu verwenden.
    Nicht umsonst verwendet die WinApi nur structs unds Char-Zeiger.

    Ok.. Jemand hatte mir erzählt es wäre durchaus gängig ganze Klassen aus Dlls zu holen. Gibt es denn andere Wege Klassen dynamisch in ein Programm zu kriegen? Also um ein Nachrüsten von Funktionen zu ermöglichen.



  • Belli schrieb:

    Er reicht doch da nur Zeiger rum!?

    Er reicht einen Zeiger auf ein Interface herum.
    Mit dem Zeiger ruft er eine Methode auf -> Bumm.
    Da liegt doch die Vermutung nahe, dass das aufrufende Progamm irgendwie das auf das der Zeiger zeigt falsch interpretiert 😃

    Deswegen meine Frage nach Compiler und Compilereinstellungen.



  • MichelRT schrieb:

    Belli schrieb:

    Er reicht doch da nur Zeiger rum!?

    Er reicht einen Zeiger auf ein Interface herum.
    Mit dem Zeiger ruft er eine Methode auf -> Bumm.
    Da liegt doch die Vermutung nahe, dass das aufrufende Progamm irgendwie das auf das der Zeiger zeigt falsch interpretiert 😃

    Deswegen meine Frage nach Compiler und Compilereinstellungen.

    Exakt. Aber ich habe keine Ahnung was ich dagegen tun kann...
    Ich habe jetzt mal in den sourcecode der DLL reinDebugt. Der Pointer den ich z.b. an die Funktion bla(int* a) übergebe, wird völlig woanders auf den stack gelegt...
    Auf der aufrufenden Seite hat der Zeiger eine ganz andere Adresse als in der DLL selbst.

    Muss ich mein _StrategyInterface im aufrufendem programm auch mit den Tags wie (__declspec(dllimport)) versehen?

    Kann es vlt sein das die Funktionspointer die ich über "fGet = (DllClassGetFunction)(GetProcAddress(hDLL,"getClass"));" hole irgendwie quatsch sind?

    Muss das Klassen Interface zu 100% Abstrakt sein? oder darf ich da wie jetzt auch vor definierte funktionen drin haben?



  • MichelRT schrieb:

    Belli schrieb:

    Er reicht doch da nur Zeiger rum!?

    Er reicht einen Zeiger auf ein Interface herum.
    Mit dem Zeiger ruft er eine Methode auf -> Bumm.
    Da liegt doch die Vermutung nahe, dass das aufrufende Progamm irgendwie das auf das der Zeiger zeigt falsch interpretiert 😃

    Deswegen meine Frage nach Compiler und Compilereinstellungen.

    Mein Zwischenruf bezog sich auf den Hinweis " keine gute Idee Instanzen von Klassen als Parameter oder Rückgabewerte von Funktionen zu verwenden."



  • Bei mir funktioniert's:

    // DLL
    class StrategyInterface {
    public:
        virtual             ~StrategyInterface(){}
        virtual string      getStrategyName() = 0;
        virtual string      getStrategyBrief() = 0;
    };
    class Strategy : public StrategyInterface {
    public: 
        string      getStrategyName() {
            return "blubb";
        }
        string      getStrategyBrief() {
            return "bla";
        }
    
    };
    #define STRATEGYDLL_API __declspec(dllexport)
    BOOL APIENTRY DllMain(HMODULE hModule,
                          DWORD dwReasonForCall,
                          LPVOID pReserved)
    {
        switch(dwReasonForCall)
        {
            case DLL_PROCESS_ATTACH:
                break;
            case DLL_PROCESS_DETACH:
                break;
        }
        return TRUE;
    }
    extern "C"
    STRATEGYDLL_API Strategy*   getClass() {
        return new Strategy;
    }
    
    // exe
    class StrategyInterface {
    public:
        virtual             ~StrategyInterface(){}
        virtual string      getStrategyName() = 0;
        virtual string      getStrategyBrief() = 0;
    };
    typedef StrategyInterface*(__stdcall *getClass)(void);
    int main()
    {
        HMODULE                 hDLL;
        getClass                fGet;
        StrategyInterface       *cClass;
    
        hDLL = LoadLibraryA("VTableTest.dll"); // Funktioniert, hDLL ist nicht null.
    
        if(hDLL == NULL)
    	{
            MessageBoxA(NULL, "Can't load library!", "Error", NULL);
        }
        fGet = (getClass)(GetProcAddress(hDLL,"getClass"));  // Funktion wird auch gefunden.
        if(fGet == NULL)
    	{
            MessageBoxA(NULL, "Can't get process address!", "Error", NULL);
        }
    	else
    	{
            StrategyInterface* bla = fGet();
            std::cout << bla->getStrategyName() << std::endl;     // hier gibt es einen Fehler.
        }
    	system("pause");
    }
    


  • okay... vielen Dank dafür das du es mal getestet hast!
    Du hast zwar nur die vereinfachte Version getestet aber vom Ding her müsste es also funktionieren.
    Ich werd das gleich mal auch in der vereinfachten Version testen und dann schauen wo da bei mir der wurm drin ist.



  • Belli schrieb:

    MichelRT schrieb:

    Belli schrieb:

    Er reicht doch da nur Zeiger rum!?

    Er reicht einen Zeiger auf ein Interface herum.
    Mit dem Zeiger ruft er eine Methode auf -> Bumm.
    Da liegt doch die Vermutung nahe, dass das aufrufende Progamm irgendwie das auf das der Zeiger zeigt falsch interpretiert 😃

    Deswegen meine Frage nach Compiler und Compilereinstellungen.

    Mein Zwischenruf bezog sich auf den Hinweis " keine gute Idee Instanzen von Klassen als Parameter oder Rückgabewerte von Funktionen zu verwenden."

    Und mein Einwurf bezog sich auf Methoden und funktionen einer DLL.
    Klar darf man sonst Objekte als Parameter oder Rückgabewerte verwenden. 😃



  • Lass mal den

    typedef DllClassGetFunction
    

    sehen.
    Wenn da nicht so wie im vereinfachten Beispiel von asdfghjklöäqwert der stdcall steht dann ist es logisch dass Du Müll auf dem Stack siehst.



  • Ja, aber er reicht ja keine Instanzen (Objekte) herum, sondern lediglich Zeiger auf solche. Und das sollte meiner Meinung nach auch zwischen Client einer DLL und DLL keine Probleme bereiten.



  • Belli schrieb:

    Ja, aber er reicht ja keine Instanzen (Objekte) herum, sondern lediglich Zeiger auf solche. Und das sollte meiner Meinung nach auch zwischen Client einer DLL und DLL keine Probleme bereiten.

    Naja, wenn Compiler A den vtable einer Klasse vor das Objekt packt und Compiler B ans Ende und dann in B eine DLL aus A geladen wird und versucht eine virtuelle Funktion aufzurufen....



  • Sollte die V-Table nicht immer am selben Ort sein? Steht doch bestimmt in der Spezifikation, sonst hätte man das Problem unnötiger weiße bei anderen Versuchen Libs einzubinden ja auch.



  • Mr.Long schrieb:

    Sollte die V-Table nicht immer am selben Ort sein? Steht doch bestimmt in der Spezifikation, sonst hätte man das Problem unnötiger weiße bei anderen Versuchen Libs einzubinden ja auch.

    Soweit ich weis hat C++ keine einheitliche ABI, Microsoft hat allerdings durch COM einen quasi - Standard etabliert (also nicht für die komplette ABI sondern nur für die v-Table).

    floorball



  • Unter was ordnet man dann __stdcall, __fastcall usw. ein?



  • calling conventions, stammt aus C



  • Nathan schrieb:

    Belli schrieb:

    Ja, aber er reicht ja keine Instanzen (Objekte) herum, sondern lediglich Zeiger auf solche. Und das sollte meiner Meinung nach auch zwischen Client einer DLL und DLL keine Probleme bereiten.

    Naja, wenn Compiler A den vtable einer Klasse vor das Objekt packt und Compiler B ans Ende und dann in B eine DLL aus A geladen wird und versucht eine virtuelle Funktion aufzurufen....

    Ist das denn wirklich so schwer zu verstehen?

    MichelRT schrieb:

    Ansonsten ist es keine gute Idee Instanzen von Klassen als Parameter oder Rückgabewerte von Funktionen zu verwenden.

    Belli schrieb:

    Ja, aber er reicht ja keine Instanzen (Objekte) herum, sondern lediglich Zeiger auf solche.

    Was ist daran auszusetzen?



  • Belli schrieb:

    Nathan schrieb:

    Belli schrieb:

    Ja, aber er reicht ja keine Instanzen (Objekte) herum, sondern lediglich Zeiger auf solche. Und das sollte meiner Meinung nach auch zwischen Client einer DLL und DLL keine Probleme bereiten.

    Naja, wenn Compiler A den vtable einer Klasse vor das Objekt packt und Compiler B ans Ende und dann in B eine DLL aus A geladen wird und versucht eine virtuelle Funktion aufzurufen....

    Ist das denn wirklich so schwer zu verstehen?

    Genau dasselbe Problem ist auch mit Pointern...


Anmelden zum Antworten