Verwendung von __declspec(dllimport)



  • Hallo,

    ich habe bisher immer DEF-Files benutzt, um Funktionen aus einer DLL zu exportieren.

    Nun habe ich versucht selbiges mit dem

    __declspec(dllexport)
    

    -Schlüsselwort zu tun.

    Das ganze sieht (beispielhaft) so aus:

    // DLL.h 
    #ifdef DLL_EXPORTS
    #define DLL_API __declspec(dllexport)
    #else
    #define DLL_API __declspec(dllimport)
    #endif
    
    DLL_API VOID MyFunc(int, int) {};
    

    Diese Funktion möchte ich nun in meinem Program aufrufen:

    // main.cpp
    char* lpDLLPath = "DLL PATH";
    
    HMODULE hDLL = LoadLibrary(lpDLLPath);
    
    __try 
    {
    	LP_MY_FUNC lpMyFunc = 
    		reinterpret_cast<LP_MY_FUNC>(GetProcAddress(hDLL, "MyFunc"));
    
    	lpMyFunc(77, 99);
    }
    __finally {
    	FreeLibrary(hDLL);
    }
    

    Nur leider ist lpMyFunc "NULL". Wenn ich wieder mein Modul Definitions File benutze, funktioniert es ...

    Wie genau benutzt man

    __declspec(dllexport)
    

    also? Danke für die Hilfe, schon mal im voraus.



  • Willst du die DLL denn wirklich dynamisch laden?
    Wenn nicht: einfach Header-File inkludieren, und die Funktion aufrufen. Und mit dem .LIB File der DLL linken -> fertig.

    Ansonsten brauchst du den "Decorated Name" der Funktion.

    Oder du könntest die Funktion natürlich als extern "C" deklarieren.

    // DLL.h 
    #ifdef DLL_EXPORTS
    #define DLL_API __declspec(dllexport)
    #else
    #define DLL_API __declspec(dllimport)
    #endif
    
    extern "C" {
    
    DLL_API VOID MyFunc(int, int) {};
    
    // ... weitere exportierte funktionen ...
    
    } // extern "C"
    

    Wenn ich mich richtig erinnere müsste dann "MyFunc" als name passen. (Wenn "MyFunc" nicht geht, probier "_MyFunc")



  • hustbaer schrieb:

    Wenn ich mich richtig erinnere müsste dann "MyFunc" als name passen. (Wenn "MyFunc" nicht geht, probier "_MyFunc")

    Danke für die Hilfe. Ich habe noch nie mit __declspec(dllimport/dllexport) gearbeitet. Ich werde es dann aber gleich mal ausprobieren.

    Darf ich auch noch fragen, warum sich dass bei dem DEF-File anders verhält?

    EDIT:
    Okay, ich habe es nun als extern "C" in der DLL deklariert ... kein Erfolg. Immer noch liefert GetProcAddress NULL zurück. Auch mit dem Underscore davor.

    Woran kann dass noch liegen?



  • FrEEzE2046 schrieb:

    ich habe bisher immer DEF-Files benutzt, um Funktionen aus einer DLL zu exportieren.

    Sehr gut! Warum willst Du davon weg?

    FrEEzE2046 schrieb:

    Nun habe ich versucht selbiges mit dem

    __declspec(dllexport)
    

    -Schlüsselwort zu tun
    ....
    Nur leider ist lpMyFunc "NULL".

    Das ist so korrekt. Du solltest Dir mal die erzeugte DLL mit www.dependencywalker.com anschauen... dann siehst Du auch warum 😉

    Ganz einfach: Das "Name-Mangeling" ist anders... wenn Du es via DEF-File machst, dann wird genau der Namen exportiert den Du angegeben hast. Per dllexport sieht der Namen anders aus... wenn Du zusätzlich extern "C" verwendest, sieht er zwar immerhin besser aus, aber auch nicht so wie Du willst...

    Also mein Tipp: Wenn Du Funktionen via "String-Name" ansprechen willst/musst, dann kommst Du um eine DEF-Datei nicht drum rum.
    Nur: Warum willst Du das? Verwendet doch die Import-Lib...



  • Also,

    ich will eigentlich gar nicht davon weg. Ich wollte mir nur die Funktion von __declspec(dllexport) anschauen und wissen wie es funktioniert.

    Welcher Vorteil liegt denn dann überhaupt bei der Verwendung davon? Es muss doch einen Grund geben, warum man es anstatt des DEF-Files benutzt.



  • Es ist einfacher, da Du nicht zwei Dinge Pflegen musst (Source-Code und DEF-Datei). Macht aber, wie Du gesehen hast, nur Sinn, wenn Du auch die Import-Lib verwendest 😉

    Und Du kannst auch ganze Klassen exportieren, ohne dass Du Dir den Finger brichst...



  • Jochen Kalmbach schrieb:

    Es ist einfacher, da Du nicht zwei Dinge Pflegen musst (Source-Code und DEF-Datei). Macht aber, wie Du gesehen hast, nur Sinn, wenn Du auch die Import-Lib verwendest 😉

    Und Du kannst auch ganze Klassen exportieren, ohne dass Du Dir den Finger brichst...

    Okay, dass ist natürlich logisch und ein Grund. Vielen Dank.



  • Hier mal ein kleines Beispiel:
    Du hast bespielsweise eine Klasse für Physikberechnungen. Diese soll in eine DLL ausgelagert werden. Später soll ein Programm diese Klasse benutzen können.

    Also erstmal Header schreiben.

    //Physic.h
    #pragma once
    
    //Klasse in DLL exportieren
    #ifdef PHYSIC_EXPORTS
        #define PHYSIC_API __declspec(dllexport)
    #else
        #define PHYSIC_API __declspec(dllimport)
        #pragma comment(lib, "Physic.dll") //<- Linkt das Programm automatisch mit der Lib, um die DLL bei der Ausführung zu laden
    #endif
    
    //Klasse für Physikberechnungen
    class PHYSIC_API CollisionsTester
    {
    private:
        //Liste aller Objekte, die kollidieren können
        std::list<BasePhysicObject>* pObjectList;
    
    public:
        //Init und Exit
        CollisionsTester();
        ~CollisionsTester();
    
        //Bewegen und auf Kollision testen
        void Update();
    
        //Neues Objekt hinzufügen
        void AddObject(BasePhysicObject* pNewObject);
    }
    
    //Singleton Collisionstester erstellen
    //So wird es zwar eigentlich nicht gemacht, aber so exportiert man Funktionen in DLLs
    CollosionsTester* PHYSIC_API GetSingletonCollisionsTester();
    

    Dazu noch die passende Implementierung

    //Physic.cpp
    #include"Physic.h"
    
    CollosionsTester::CollosionsTester()
    {
        pObjectList= new std::list<BasePhysicObject>;
        //...
        //Was ein Konstruktor eben so macht ;)
    }
    
    CollosionsTester::~CollosionsTester()
    {
        delete pObjectList;
        //...
        //Was ein Destruktor eben so macht ;)
    }
    
    void CollosionsTester::Update()
    {
        //...
    }
    
    void CollosionsTester::AddObject(BasePhysicObject* pNewObject)
    {
        //...
    }
    
    CollosionsTester* PHYSIC_API GetSingletonCollisionsTester()
    {
        static CollisionsTester TheOnlyOne;
        return(&TheOnlyOne);
    }
    

    Alles kompilieren und zur DLL linken.
    Jetzt brauchen wir nur noch das Programm, welches die DLL benutzen soll.
    Dafür kopiert man die entstandene DLL und die gleichnamige Lib und die Header in den Ordner des Programms, welches die DLL benutzen soll.

    Das sieht dann so aus:

    //Main.h
    #include"Physic.h"
    //Das wars, mehr muss dann vom Programm nicht mehr gemacht werden, um die DLL zu laden
    
    int main()
    {
        //...
    
        CollisionsTester* pTester= GetSingletonCollisionsTester();
        BasePhysicObject* pBox1, pMesh2; //Box und Mesh sind von BasePhysicObject abgeleitet
        pBox1= new Box(Position1, Velocity1, Rotation1, RotationVelocity1);
        pMesh2= new Mesh(Vertices2, Indices2, Position2, Velocity2, Rotation2, RotationVelocity2);
    
        //...
    
        pTester->AddObject(pBox1);
        pTester->AddObject(pMesh2);
    
        //...
    
        pTester->Update();
    
        //...
    
        delete pBox1;
        delete pMesh2;
        return(0);
    }
    

    Das wars eigntlich.
    Wenn du dich noch mehr dafür interessierst, such bei google nach DLL Tutorial C++, da findest du massig



  • Ich muss den Thread nochmal aus der Versenkung holen. Ich habe jetzt mal folgende Beispielimplementation gemacht:

    //C++Library.h
    
    #ifndef _CLIBRARY_H_
    #define _CLIBRARY_H_
    
    #ifdef CLIBRARY_EXPORTS
    #	define CLIBRARY_API __declspec(dllexport)
    #else
    #	define CLIBRARY_API __declspec(dllimport)
    #endif
    
    extern "C"  {
    	CLIBRARY_API
    	unsigned long ShowMessage(
    		const char*			// message
    	);
    }
    
    #endif
    
    //C++Library.cpp
    
    // C++Library.cpp : Defines the exported functions for the DLL application.
    //
    #include "stdafx.h"
    
    ULONG ShowMessage(LPCSTR lpszMessage)
    {
    	MessageBox(NULL, lpszMessage, "Message", 0);
    	return strlen(lpszMessage);
    }
    

    Ich möchte die Funktion nun in einem anderen Projekt benutzen:

    //main.cpp
    
    #pragma comment( lib, "..\\..\\C++Library\\Release\\C++Library.lib" )
    
    #include <iostream>
    #include "..\\..\\C++Library\\C++Library\\C++Library.h"
    
    using namespace std;
    
    int main( int argc, char** argv )
    {
    	cout << ShowMessage("Dies ist ein Test") << endl;
    
    	return 0;
    }
    

    Da bekomme ich nur die Nachricht:

    Error	1	error LNK2019: unresolved external symbol "__declspec(dllimport) unsigned long __cdecl ShowMessage(char const *)" (__imp_?ShowMessage@@YAKPBD@Z) referenced in function _main	main.obj
    

    Wenn ich die Headerinkludierung weglasse und folgende Deklaration in main.cpp implementiere, dann geht es:

    //extern
    unsigned long ShowMessage(const char*);
    

    1. Was ist oben der Fehler? Warum unresolved external symbol?
    2. Warum geht es, wenn ich die Funktion neu deklariere? Und muss ich da extern angeben?

    Wenn Ihr mir weiterhelfen könntet, wäre das echt super. Danke im voraus.



  • zuersteinmal wäre es sinnvoll wenn die library.cpp auch die library.h einbindet 🙂



  • eingebundener einband schrieb:

    zuersteinmal wäre es sinnvoll wenn die library.cpp auch die library.h einbindet 🙂

    oh weia ... 🙄


Anmelden zum Antworten