Probleme mit LoadLibrary und GetProcAddress



  • Hallo ich verzweifel fast, mache doch alles wie unter MSDN beschrieben:
    [url]
    http://msdn.microsoft.com/de-de/library/3y1sfaz2.aspx
    http://msdn.microsoft.com/de-de/library/64tkc9y5(VS.80).aspx
    [/url]

    Header meiner DLL (test.h):
    Das _DLLEXPORT hab ich unter Projekteigenschaften -> C++ -> Präprozessor -> Präprozessordefinitionen eingestellt.

    #ifdef _DLLEXPORT
    #define CLASS_DECLSPEC    __declspec(dllexport)
    #else
    #define CLASS_DECLSPEC    __declspec(dllimport)
    #endif
    
    CLASS_DECLSPEC int test(void);
    

    Diese wurde in der stdafx.h included

    CPP meiner DLL (bis auf das int test(void) {...} hat alles VS2005 autom. gemacht)

    // test.cpp : Definiert den Einstiegspunkt für die DLL-Anwendung.
    //
    
    #include "stdafx.h"
    
    #ifdef _MANAGED
    #pragma managed(push, off)
    #endif
    
    BOOL APIENTRY DllMain( HMODULE hModule,
                           DWORD  ul_reason_for_call,
                           LPVOID lpReserved
    					 )
    {
        return TRUE;
    }
    
    int test(void)
    {
    	return 5;
    }
    
    #ifdef _MANAGED
    #pragma managed(pop)
    #endif
    

    CPP meiner Anwendung, die DLL verwenden will:

    #include <windows.h>
    #include "test.h"
    using namespace std;
    
    typedef int (*FKTPOINTER)(void);
    #define  DLL_DIRECTORY L".\\test.dll"
    
    int main (void)
    {
    	HMODULE hDLL = LoadLibrary(DLL_DIRECTORY);
    	FKTPOINTER pointer = (FKTPOINTER)GetProcAddress(hDLL,"Test");
    	DWORD fehler = GetLastError();
    	return 0;
    };
    

    fehler hat den Wert 127, dh er kann die Funktion Test nicht finden.

    Das Problem ist, das GetProcAddress stets NULL zurückgibt.
    Mein eigentliches Ziel ist es letztlich, eine gesamte Klasse in der DLL zu Kapseln udn diese zu verwenden, aber es geht ja nicht mal dieser einfache Fall.
    Was mach ich denn nur falsch udn wie sieht es richtig aus, am besten gleich mit einer Klasse?

    Danke!



  • Hallo sorry, ich hab den Fehler bemerkt: Ich suche nach Funktion Test() und habe nur eine Funktion test(). Ich glaubs nicht. Jetzt probier ich es mal mit einer Klasse.



  • Hallo sorry dass ich nerve, aber ich sitz auf dem Schlauch. Laut Microsoft kann man auch Klassen mit declspec exportieren und importieren
    [url]
    http://msdn.microsoft.com/de-de/library/81h27t8c.aspx
    [/url]
    Aber wie kann ich dann mit der Klasse arbeiten:
    Header in der DLL (test.h)

    #ifdef _DLLEXPORT
    #define CLASS_DECLSPEC    __declspec(dllexport)
    #else
    #define CLASS_DECLSPEC    __declspec(dllimport)
    #endif
    
    class CLASS_DECLSPEC huhu
    {
    public:
    	int test(void);
    };
    
    extern "C" CLASS_DECLSPEC huhu* GetHuhu(void);
    

    CPP in der DLL:

    #include "stdafx.h"
    
    #ifdef _MANAGED
    #pragma managed(push, off)
    #endif
    
    BOOL APIENTRY DllMain( HMODULE hModule,
                           DWORD  ul_reason_for_call,
                           LPVOID lpReserved
    					 )
    {
        return TRUE;
    }
    
    int huhu::test(void)
    {
    	return 5;
    }
    
    huhu* GetHuhu(void)
    {
    	static huhu f;
    	return &f;
    }
    
    #ifdef _MANAGED
    #pragma managed(pop)
    #endif
    

    CPP der Anwendung:

    #include <windows.h>
    #include "test.h"
    using namespace std;
    
    typedef huhu* (*GETHUHU)(void);
    #define  DLL_DIRECTORY L".\\test.dll"
    
    int main (void)
    {
    	HMODULE hDLL = LoadLibrary(DLL_DIRECTORY);
    
    	GETHUHU pointer = (GETHUHU)GetProcAddress(hDLL,"GetHuhu");
    	DWORD fehler = GetLastError();
    	huhu * instanz = pointer();
    	instanz->test(); //erzeugt LNK2019
    	return 0;
    };
    

    Es kommt LNK2019 bzgl. der FUnktion test().
    Was mach ich falsch.

    Danke.



  • also 🙂

    das was du mit __declspec exportierst, heißt nicht "test", sondern irgnetwie
    so: "_Z8Y@test_hPZ". das nennt man name-mangeling. wenn du vor das __declspec ein

    extern "C"

    schreibst, wird die funktion als C-funktion behandelt, und behält ihren namen.
    dann darfst du aber keine C++ konzepte verwenden, wie namespaces und klassen
    als parameter, etc... (zeiger gehen)

    wenn du klassen exportieren willst, solltest du das am besten über interfaces
    machen:

    common.h, wird von DLL und app eingebunden

    #ifdef DLL
    //...
    #endif
    
    struct base
    {
        virtual int testfunc() = 0;
        virtual void Release() = 0; // lösch-funktion in der DLL, wegen CRT
    };
    
    DLLFUNC base *CreateClass();
    

    dll.h

    #include "common.h"
    
    class test : public base
    {
    public:
        int testfunc();
        void Release();
    };
    

    dll.cpp

    int test::testfunc()
    {
        return 5;
    }
    void test::Release()
    {
        delete this; // so sind delete und new in der selben DLL mit der selben CRT
    }
    
    base *CreateClass()
    {
        return new test;
    }
    

    app.cpp

    #include "common.h"
    
    int main()
    {
        // Load...
        base *b = (*pfn_Create)();
        b->testfunc();
        b->Release();
    }
    


  • Erstmal vielen Dank an helferlein, ich hab es probiert und es geht jetzt.
    Habe paar "Ergänzungen" vorgenommen in:

    common.h

    #ifdef _DLLEXPORT
    #define CLASS_DECLSPEC    __declspec(dllexport)
    #else
    #define CLASS_DECLSPEC    __declspec(dllimport)
    #endif
    
    struct base
    {
        virtual int testfunc() = 0;
        virtual void Release() = 0; // lösch-funktion in der DLL, wegen CRT
    };
    
    extern "C" CLASS_DECLSPEC base *CreateClass();
    

    und in der app.cpp:

    #include <windows.h>
    #include "common.h"
    
    #define  DLL_DIRECTORY L".\\dll.dll"
    typedef base* (*pfn_Create)();
    
    int main()
    {
        HMODULE hDLL = LoadLibrary(DLL_DIRECTORY);
    	pfn_Create pointer = (pfn_Create)GetProcAddress(hDLL,"CreateClass");
    	DWORD fehler = GetLastError();
        base *b = pointer();
        int x = b->testfunc();
        b->Release();
    }
    

    Dies war doch so OK?
    Ich würde es gern besser verstehen, denn wenn ich meine ursprüngliche Lösung ansehe, denk ich mir, es ist doch im Prinzip alles gleich.
    Wieso verwendet man beim Exportieren von Klassen am besten Interfaces?
    Deine CreateClass() macht doch letztlich auch nichts anderes als meine GetInstance(), sie gibt Adresse eines KIndobjektes (bei Dir Typ test) zurück.
    Und was müsste ich machen, wenn ich in der Klasse test zB static Funktionen oder static Member hätte und auf diese in der app.cpp zugreifen will?
    Ausserdem versteh ich nicht, wieso Microsoft sagt, man muss class declspec(...) {...} schreiben, wenn es offensichtlich nicht nötig ist, weil Du machst es ja anders.

    Sorry für die vielen Fragen, aber ich will es echt verstehen für die Zukunft und die Chance nutzen, dass ich einen erfahrenen Programmierer fragen kann.

    Danke vorab 🙂



  • jop, das beispiel schaut gut aus (ich gehe mal davon aus, dass die dll.cpp auch noch drin ist 😉 )

    warum interfaces?

    damit kann man auf geschickte art und weise die implementation verbergen.
    wenn ich eine variable ändern muss, oder eine neue hinzufüge, müsste ich sowohl
    die app als auch die DLL neu kompilieren. wenn ich interfaces nutze, reicht es,
    eine neue DLL zu erstellen. so kann man z.b. updates realisieren.

    außerdem gäbe es die möglichkeit, dass mehrere DLLs von diesem interface erben
    und trotzdem ihre eigene implementation haben. so kann man plugins erstellen 😉

    CreateClass und GetInstance machen im prinzip dasselbe, ist für das DLL-App system
    egal.

    für statische funktionen müsstest du in der tat das __declspec(dllimport) verwenden,
    da man die methoden einer klasse nicht einfach "umbiegen" kann.

    der unterschied ist, dass dllimport statisch ist, und somit nur einmal
    zum programstart geladen wird. danach ist die DLL an die app gebunden und
    damit auch deine statische methode. mit GetProcAddress kannst du das ganze
    schön dynamisch gestalten.

    wenn du natürlich nur einmal zum programmstart die DLL einbinden willst, kannst
    du auch dllimport machen, nur ist das eine microsoft-spezifische erweiterung,
    die ich noch nie benutzt habe. bisher bin ich aber mit dem interface prinzip
    super zerecht gekommen.

    was du möchtest, ist eine klasse direkt zu importieren. deswegen brauchst du
    das __declspec.
    ich exportiere nur eine funktion (die auch __declspec sein muss), welche darauf-
    hin die klasse erstellt.

    hoffe, ich konnte deine fragen beantworten, wenn nicht, frag einfach nochmal 😉



  • Wollte an helferlein danke sagen. Ich hab mir jetzt noch ein Buch besorgt (Windowsprogrammierung von Charles Petzold), dummerweise macht er alles in C, ansonsten super Buch.
    Aber die Antwort hat mir schon sehr geholfen, hab lange drüber nachgedacht und mich hineingedacht.
    Das mit dem static Member (Funktionen und Variablen) und dem Export ganzer Klassen würde ich gern noch ganz verstehen.
    Also angenommen ich möchte folgende Klasse in DLL kapseln(Der Code soll jetzt wirklich nur die Idee schildern):

    class myclass
    {
    public:
    static int x;
    static int fkt1();
    int fkt2();
    
    private:
    ...
    }
    

    Und jetzt möchte ich vom Prinzip her in der Anwendung vom Prinzip wie folgt vorgehen:

    myclass instanz; //oder einen pointer also myclass *pt = ...
    myclass::x = ...;
    myclass::fkt1();
    instanz.fkt2();
    

    Dazu bräuchte man das class __declspec(dllecport/import) myclass{...}
    Aber wie geh ich dann weiter vor, also in der DLL und vor allem in der Anwendung?
    Es würde natürlich auch die Lösung von Dir helferlein gehen, denn wenn ich Instanz habe kann ich ja über diese jederzeit auf static Member zugreifen, auch wenn es nicht so "schön" ist.

    Danke vorab, ist interessantes Thema die DLLs.



  • Hallo,
    auch mich interessiert die gleiche Frage.
    Ich finde den Ansatz der Interfaces sehr gut.

    Gibt es vielleicht ein Buch zum Thema DLL oder ginerell zur Programmstruktur, denn jetzt wo ich genug C++ kann um größere Programme zu schreiben fehlt es mir an Informationen was ich in welcher Art von Datei lege.
    (Ich meine nicht die Objectorientierung sonderen die Konkrete Unterbringung des schon "objectOrientierten" codes in Dateien.)

    Gruß



  • hallo nochmal,
    kann mir jemand sagen was "Inkonsistente DLL-Bindung" bedeutet - diese warnung bekomme ich nähmlich wenn ich den code von oben ausführe.

    \projects\dynamik dll link\multiply\iislowcalculator.cpp(25) : warning C4273: 'CreateClass': Inkonsistente DLL-Bindung.
    \projects\dynamik dll link\application\interface.h(14): Siehe vorherige Definition von 'CreateClass'

    #ifdef _DLLEXPORT
    #define CLASS_DECLSPEC    __declspec(dllexport)
    #else
    #define CLASS_DECLSPEC    __declspec(dllimport)
    #endif 
    class BASE
    {
    public:
    	//virtual ~BASE();
    	virtual int Multiply(int, int) = 0;
    	virtual int Devide(int, int) = 0;
    	virtual void Release() = 0;
    };
    extern "C" CLASS_DECLSPEC BASE *CreateClass();
    

    Und ist es eigentlich egal in welchem project ich die common.h erstelle?
    Ich habe die jetzt im application project und dann über projecteinstellungen ins dll project zum mitbenutzen eingestellt.

    Gruß



  • ohje,
    hat sich erledigt,
    ich habe

    #pragma once vergessen.
    Hatte doppelte deklaration.



  • ich brauche nochmal hilfe,
    ich habe immernoch die gleiche Warnung
    undzwar solange ich das

    #ifdef _DLLEXPORT
    #define CLASS_DECLSPEC    __declspec(dllexport)
    #else
    #define CLASS_DECLSPEC    __declspec(dllimport)
    #endif 
    ...
    ...
    extern "C" CLASS_DECLSPEC BASE *CreateClass();
    

    drin lasse.

    Wenn ich die Präprozessor Anweisungen auskommentiere und dann so weitermache

    extern "C" __declspec(dllexport) iiINTERFACE *GetInterfaceImplementation();
    

    verschwindet die Warnung.

    Woran könnte es liegen?

    Gruß


Anmelden zum Antworten