Problem mit GetProcAddress



  • Hallo Leute,

    ich hoffe mal, ich bin im richtigen Forum mit der Frage gelandet.

    Ich bin grad am Experimentieren mit dem Verwenden von DLLs, möchte das später evtl mal in meinem Projekten verwenden, scheitere aber an einem Problem:

    GetProcAddress liefert bei mir immer NULL zurück, obwohl ich den Namen der funktion überrpüft habe.

    der code in der DLL ist folgender

    extern "C" {
    int __declspec(dllexport)calculate(int x, int y)
     {
      return x+y;
     }
               }
    

    (DllEntryPoint hab ich so gelassen wie er ist)

    Code vom Programm selbst:

    HINSTANCE mydll;
    typedef int(*func)(int,int);
    func Funktion;
    
    void __fastcall TForm1::FormCreate(TObject *Sender)
    {
     mydll = LoadLibrary("ProjectDLL1.dll");
     Funktion = (func) GetProcAddress(mydll,"calculate");
     int x = Funktion(2,2);
    
    }
    //---------------------------------------------------------------------------
    void __fastcall TForm1::FormDestroy(TObject *Sender)
    {
     FreeLibrary(mydll);
    }
    //---------------------------------------------------------------------------
    

    (ich verwende Borland C++ Builder 5)

    Fehlercode ist übrigens 127, also findet er die Funktion wirklich einfach nicht.

    Ich hoffe, ihr könnt mir helfen,

    mfg,
    Pudel



  • Hoi,

    Was für einen Wert hat denn 'mydll' nach dem Aufruf von LoadLibrary() ?



  • :00F80000, wenn ich mich nicht verlesen hab... Auf jeden fall nich NULL



  • Hö öhm...
    1. Debug Build? Oder Release-Build?
    2. Keinerlei Fehlerbehandlung.
    4. Fehlt nen Lehrzeichen hinter dllexport) ...
    5. usw...



  • 1. ich lass das programm immernoch im debugmodus laufen (hoff ich hab die frage richtig verstanden)
    2. soll nur ein testprogramm sein. Hatte zwischendurch eine Fehlerbehandlung drin, sonst hätt ich etz nicht den System Error Code. Aber eigentlich ist das programm nur zum probiern, später kommt da natürlich ne Fehlerbehandlung rein.
    3. ...
    4. leerzeichen hinter dllexport eingefügt, compiliert, ausprobiert, keine änderung
    btw: ist das in diesem falle nicht egal? wär das erste mal, dass man da ein Leerzeichen vor einer klammer braucht...
    5. ...

    mfg Pudel



  • Hmm... nun... ich zeig dir mal ein Dll-Muster:

    // MeineDll.h
    #ifdef MEINEDLL_EXPORTS
    	#define MEINEDLL_API __declspec(dllexport)
    #else
    	#define MEINEDLL_API __declspec(dllimport)
    #endif
    
    #include ".\meineheaderdatei1.h"
    // ...
    
    MEINEDLL_API int calculate(int, int);
    
    // MeineDll.cpp : Defines the entry point for the DLL application.
    //
    
    #include "MeineDll.h"
    BOOL APIENTRY DllMain(HANDLE /*hModule*/, DWORD dwReasonForCall, LPVOID /*lpReserved*/)
    {
    	switch (dwReasonForCall)
    	{
    	case DLL_PROCESS_ATTACH:
    	case DLL_THREAD_ATTACH:
    	case DLL_THREAD_DETACH:
    	case DLL_PROCESS_DETACH:
    		break;
    	}
        return TRUE;
    } 
    
    MEINEDLL_API int calculate(int x1, int x2)
    {
        return (x1 + x2);
    }
    
    // meineheaderdatei1.h
    #if (!defined _MEIN_HEADER__INCLUDED_)
    #define _MEIN_HEADER__INCLUDED_
    
    class MEINEDLL_API CMyClass
    {
    public:
        CMyClass(void);
        ~CMyClass(void);
    };
    
    // meineheaderdatei1.cpp
    #include "./meinedll.h"
    
    CMyClass::CMyClass(void)
    {
    }
    
    CMyClass::~CMyClass(void)
    {
    }
    

    Dann kannst de die calculate Funktion so laden:

    typedef int(WINAPI*fCalculate)(int, int); 
    
    bool UseFunc(void)
    {
        ::HINSTANCE hMyDll = NULL; 
        fCalculate Calculate; 
    
        hMyDll = ::LoadLibrary("ProjectDLL1.dll"); 
        if (!hMyDll)
        {
            ::MessageBox(NULL, "Fehler beim Laden der Dll!", "Fehler", MB_OK | MB_ICONERROR);
            return false;
        }
    
        Calculate = reinterpret_cast<fCalculate>(::GetProcAddress(hMyDll, "calculate"); 
        if (!Calculate)
        {
            ::MessageBox(NULL, "Fehler beim Laden der Funktion", "Fehler", MB_OK | MB_ICONERROR);
            ::FreeLibrary(hMyDll);
            return false;
        }
    
        int x = Calculate(2, 2);
    
        ::FreeLibrary(hMyDll);
        return true; 
    }
    


  • Pudel des Todes schrieb:

    btw: ist das in diesem falle nicht egal? wär das erste mal, dass man da ein Leerzeichen vor einer klammer braucht...

    Jo das ist total egal...



  • Mach es einfach so wie ich es gesagt hab... und es funktioniert.



  • und wo ist der große Unterschied zu meinem? Dem einzigen was ich nicht folgen kann, ist der teil

    #ifdef MEINEDLL_EXPORTS
        #define MEINEDLL_API __declspec(dllexport)
    #else
        #define MEINEDLL_API __declspec(dllimport)
    #endif
    

    Und ich weiß ehrlich gesagt nicht, wo das MEINEDLL_EXPORTS definiert werden soll oder nicht, kann also auch nicht sagen, was das eigentlich werden soll.
    Aber wenn ich das genau so übernehmen würd wie's dasteht, würd doch am ende in der DLL stehen
    __declspec(dllimport) int calculate(int, int);

    da ja MEINEDLL_EXPORTS nirgends definiert wird.

    Und ob ich etz reinterpret_cast<foo> oder nur (foo) hernehm is ja auch egal, weil nur NULL aus GetProcAdress rauskommt.



  • Hmm 😉 Das ist ein Makro.. stimmt das hab ich vergessen zu schreiben... weiß niht in wie weit der Borland das kann... beim VC kannst de noch wie _DEBUG usw Parameter beim Compilieren angeben(nennt sich Präprozessor definations)... d.h.:
    Debug: MEINEDLL_EXPORTS
    Release: MEINEDLL_EXPORTS

    D.h.... es bleibt bei dll export... aber nehms einfach so... sind nen paar andere Sachen die dir scheinbar noch nicht aufgefallen sind, die verändert sind ...



  • nun gut, nehmen wir an wir kriegen wirklich am ende __declspec(dllexport).

    Dann ist das immernoch schlicht das, was vorher schon in meinem Code stand...
    Präprozessordirektiven haben die Eigenschaft dass sie vom Compiler ausgeführt werden und im eigentlichen programm nicht mehr auftreten, folglich ist der unterschied ob ich sage

    #define foo __declspec(dllexport)
    foo int bar(int x, int y);
    

    oder gleich

    __declspec(dllexport) int bar(int x, int y);
    

    gleich 0!

    Andere Unterschiede die ich gesehen habe:

    1.da steht ein DLLEntyPoint. Wie gesagt, ich hab meinen unverändert gelassen, er ist aber noch im code. Er macht genau so viel wie deiner, nämlich gar nichts.

    2. Du definierst in deinem Programm eine Klasse die du nie verwendest. Schön für dich. Brauch ich nicht.

    3. der Sinn dass du wenn ich das richtig seh die Header deiner DLL auch in deinen Programmcode includest ergibt sich mir nicht.

    das waren alle gravierenden Unterschiede die ich bis jetzt gesehen hab.
    Wäre sehr dankbar, wenn du mir sagen könntest was ich noch übersehen habe.

    Sorry, dass ich da nun so lang rumtu, aber
    1. will ich wissen wo mein fehler liegt
    2. seh ich wirklich keinen unterschied zwischen den codes, der das problem lösen könnte
    3. hasse ich es, fremden code einfach zu importieren, ohne zu wissen, was da nun eigentlich (anderes) steht.



  • Und solangsam gehst du mir auf den ... 😉

    Naja.. also... die Klasse hab ich dir nur als Sample angelegt, damit du sehen kannst wie man das Makro in einer Klasse benutzt...

    Dann... ist eine ungravierende änderung wie du schon sagtest, das alles über eine "Hauptheaderdatei" läuft... ist nicht nötig, aber praktisch.

    Dann... hab ich dir Fehlerüberprüfung reingebaut wie es sein muss 😉

    Guck dir folgende Zeile an:

    typedef int(WINAPI*fCalculate)(int, int);
    

    Hö öhm.. nen reinterpret_cast ist da schon angebracht...

    Soo... wenn du das ganze so buildest wie ichs dir geschrieben hab... funktioniert es zu 100%. An sonnsten sind deine Proj.-Einstellungen nicht korrekt.



  • Pudel des Todes schrieb:

    und wo ist der große Unterschied zu meinem? Dem einzigen was ich nicht folgen kann, ist der teil ...

    der ist eigentlich für dich auch nicht wichtig da du dll dynamisch laden möchtest und nicht statisch binden willst

    Pudel des Todes schrieb:

    Und ich weiß ehrlich gesagt nicht, wo das MEINEDLL_EXPORTS definiert werden soll oder nicht, kann also auch nicht sagen, was das eigentlich werden soll.
    Aber wenn ich das genau so übernehmen würd wie's dasteht, würd doch am ende in der DLL stehen
    __declspec(dllimport) int calculate(int, int);

    ist wiederrum nicht wichtig für deinen fall

    Pudel des Todes schrieb:

    Und ob ich etz reinterpret_cast<foo> oder nur (foo) hernehm is ja auch egal, weil nur NULL aus GetProcAdress rauskommt.

    du hast recht der c++ cast ist auch nicht besser (nur typensicherer aber das ist ein anderes thema)

    lösungsvorschlag:
    mach ne ganze normale dll in der nichts anderes drin steht wie

    __declspec(dllexport) int calculate(int x1, int x2)
    { return (x1 + x2); }
    

    mehr brauchst du im moment nicht.
    dann nimmst du den http://www.dependencywalker.com/ und schaust dir deine dll an, dort steht auch der name der exportiert wurde. namen importieren, fertig

    ps: evtl. solltest du dir auch noch ein paar gedanken zu den aufrufkonventionen machen (declspec, stdcall usw)

    @(d)evil
    [korrektur]



  • Hmm? Er kann die Func net richtig mit GetProcAddress loaden, richtig?

    [quote Pudel des Todes]
    Und ich weiß ehrlich gesagt nicht, wo das MEINEDLL_EXPORTS definiert werden soll oder nicht, kann also auch nicht sagen, was das eigentlich werden soll.
    Aber wenn ich das genau so übernehmen würd wie's dasteht, würd doch am ende in der DLL stehen
    __declspec(dllimport) int calculate(int, int);

    ist wiederrum nicht wichtig für deinen fall
    [/quote]Ich will dir ja nicht dumm kommen.... ABER: Guck mal was ich geschrieben hab. Danke.

    Der einzigst sinnvolle Teil deiner Aussage ist mit DWalker einmal die Dll anzugucken...

    PS: Sry. ich bin müde und garantiere nicht für meine Aussagen.



  • Vielen Dank miller_m, funktioniert jetzt einwandfrei!

    Schönen Abend noch,

    mfg Pudel



  • @Pudel des Todes:
    Naja... das zeigt mir so ziemlich das du dir meinen Code kein einziges Mal wirklich durchgesehen hast... insgesammt steht dort nicht soo viel anderes... k noch nen paar Fehlerabfragen usw mit drin... aber an sonnsten... naja... egal.. ich geh pennen 😃



  • ich wollte eben keine diskussion mit dir anfangen aber sowas fand ich sehr provokativ

    (D)Evil schrieb:

    Mach es einfach so wie ich es gesagt hab... und es funktioniert.

    das es imho keinerlei erklärung zu deinem obrigen code gab.

    (D)Evil schrieb:

    Der einzigst sinnvolle Teil deiner Aussage ist mit DWalker einmal die Dll anzugucken...

    fand ich nicht so, ich habe es nur auf das wesentliche beschränkt. (wenn wir von einer dynamisch geladenen dll ausgehen)

    ist auch jetzt egal ich bin auch müde und habe vielleicht überreagiert deswegen kam gleich von mir die korrektur aber anscheinend zu spät. egal. 😃



  • Um es zusammen zu fassen, der einzig wirklich wichtige Unterschied ist IMHO folgender:

    Pudel des Todes schrieb:

    int __declspec(dllexport)calculate(int x, int y)

    miller_m/(D)Evil schrieb:

    __declspec(dllexport) int calculate(int x1, int x2)

    Alles weitere ist Geschmackssache.

    Mfg, North.



  • North schrieb:

    Alles weitere ist Geschmackssache.

    imho eben nicht, den eine dll statisch oder dynamisch zu linken ist ein unterschied und beim dynamischen laden brauche ich keine präprozessordirektiven, header, etc.
    natürlich will ich wenn ich eine dll schreibe alles abdecken und werde dieses auch dann entsprechend mit einfliessen lassen somit wäre es dann wieder geschmackssache das hat aber wieder nichts mit dem thread zu tun.



  • Da hast du recht, ich wollte lediglich hervorheben, wo im Endeffekt der Fehler in PdT's Code lag. Wenn man eine Library statisch linken will, sollte man sie IMHO ohnehin direkt als .lib erstellen und entsprechend einbinden.

    MfG, North.


Log in to reply