OVH Cloud OVH Cloud

Faire une DLL de classe chargeable dynamiquement

5 réponses
Avatar
smu
Bonjour,

Je cherche une méthode pour rendre une DLL de classe chargeable
dynamiquement.
J'ai trouvé pas mal d'information sur le net mais rien ne semble vouloir
fonctionner. Il me semble avoir compris que cela dépendait du
compilateur, la méthode semble varier entre VC++ 4 et VC++ 5. De même,
elle semble différente avec les compilateurs de chez Borland.

Moi, je travaille avec un VC++ 6.

Le problème se situe à l'édition des liens puisque les méthodes de la
classe ne peuvent, évidement, pas être résolu.

Quels sont les modifications à apporter pour que l'édition des liens se
passe bien ?

Je vous fournis les sources existants à ce jour.

D'avance merci.

smu

Voici les sources de la DLL :

------------ myClass.h ------------
#ifndef MY_CLASS_HEADER__
#define MY_CLASS_HEADER__

#ifdef DLLCLASS_EXPORTS
#define IMPEXP __declspec(dllexport)
#else
#define IMPEXP __declspec(dllimport)
#endif /* DLLCLASS_EXPORTS */

class IMPEXP myClass
{
public:
myClass(void);
~myClass(void);
void set(int local);
int get(void);

private:
int number;
bool initialized;
};

extern "C" IMPEXP myClass * myClass_createInstance(void);

typedef myClass * (* myClass_createInstancePTR)(void);

#endif /* MY_CLASS_HEADER__ */

------------ myClass.cpp ------------
#include "myClass.h"
#include <iostream>

myClass::myClass(void)
{
std::cout << "Initialization" << std::endl;
initialized = false;
number = 0;
}

myClass::~myClass(void)
{
std::cout << "Destruction" << std::endl;
}

void myClass::set(int local)
{
initialized = true;
number = local;
}

int myClass::get(void)
{
if (!initialized)
{
throw -1;
}
return number;
}

myClass * myClass_createInstance(void)
{
return new myClass;
}

------------ DLLClass.cpp ------------
#include <windows.h>

BOOL APIENTRY DllMain( HANDLE hModule,
DWORD ul_reason_for_call,
LPVOID lpReserved)
{
return TRUE;
}

Et voici les sources de l'application de test :

------------ DLLClassEXEDynamically.cpp ------------

#include "../DLLClass/myClass.h"
#include <stdlib.h>
#include <iostream>
#include <windows.h>

int main(int argc, char * argv[])
{
HMODULE hDLL;
myClass * check = NULL;
myClass_createInstancePTR myClass_createInstance = NULL;

hDLL = LoadLibrary("../DLLClass/Debug/DLLClass.dll");
if (NULL == hDLL)
{
std::cout << "Could not load the DLL" << std::endl;
return EXIT_FAILURE;
}
myClass_createInstance = (myClass_createInstancePTR)GetProcAddress(hDLL,

"myClass_createInstance");

std::cout << "Trying to create a new instance of the class" << std::endl;
check = myClass_createInstance();

try
{
std::cout << "The class contains: " << check->get() << std::endl;
}
catch(int)
{
std::cout << "An exception throwed" << std::endl;
}

check->set(5);

try
{
std::cout << "The class contains: " << check->get() << std::endl;
}
catch(int)
{
std::cout << "An exception throwed" << std::endl;
}

std::cout << "Trying to delete the instance of the class" << std::endl;
delete check;

FreeLibrary(hDLL);
return EXIT_SUCCESS;
}

5 réponses

Avatar
Arnaud Debaene
smu wrote:
Bonjour,

Je cherche une méthode pour rendre une DLL de classe chargeable
dynamiquement.
J'ai trouvé pas mal d'information sur le net mais rien ne semble
vouloir fonctionner. Il me semble avoir compris que cela dépendait du
compilateur, la méthode semble varier entre VC++ 4 et VC++ 5. De même,
elle semble différente avec les compilateurs de chez Borland.


Oui, c'est dépendant du compilo à cause du name mangling...

Moi, je travaille avec un VC++ 6.


VC4 à 6... c'est un peu ordinausoresque tout çà... On est à VC 7.1
aujourd'hui! (dont une version gratuite est téléchargeable...)

Le problème se situe à l'édition des liens puisque les méthodes de la
classe ne peuvent, évidement, pas être résolu.

Quels sont les modifications à apporter pour que l'édition des liens
se passe bien ?


Il n'y a pas de solution miracle. La méthode habituellement utilisée, c'est
de n'exporter de la DLL qu'une méthode factory déclarée extern "C", mais
c'est déjà ce que tu fais donc tout va bien. Quel est le problème exactement
(message d'erreur?). Utilises depends pour vérifier ce qui est effectivement
exporté de la DLL.

Arnaud
Avatar
smu
Arnaud Debaene a écrit :
smu wrote:

Bonjour,

Je cherche une méthode pour rendre une DLL de classe chargeable
dynamiquement.
J'ai trouvé pas mal d'information sur le net mais rien ne semble
vouloir fonctionner. Il me semble avoir compris que cela dépendait du
compilateur, la méthode semble varier entre VC++ 4 et VC++ 5. De même,
elle semble différente avec les compilateurs de chez Borland.



Oui, c'est dépendant du compilo à cause du name mangling...


Moi, je travaille avec un VC++ 6.



VC4 à 6... c'est un peu ordinausoresque tout çà... On est à VC 7.1
aujourd'hui! (dont une version gratuite est téléchargeable...)


Le problème se situe à l'édition des liens puisque les méthodes de la
classe ne peuvent, évidement, pas être résolu.

Quels sont les modifications à apporter pour que l'édition des liens
se passe bien ?



Il n'y a pas de solution miracle. La méthode habituellement utilisée, c'est
de n'exporter de la DLL qu'une méthode factory déclarée extern "C", mais
c'est déjà ce que tu fais donc tout va bien. Quel est le problème exactement
(message d'erreur?). Utilises depends pour vérifier ce qui est effectivement
exporté de la DLL.

Arnaud





Merci de me répondre.

Vu que je veux charger la DLL à l'exécution, je ne lie pas le fichier
".lib" généré lors de la compilation de la dite DLL.

Du coup, lorsque je compile l'exécutable, l'éditeur de lien me signale
qu'il n'arrive pas à résoudre les symboles des méthodes de mon objet
(set, get & ~myClass).

DLLMainEXEDynamically.obj : error LNK2001: unresolved external symbol
"__declspec(dllimport) public: void __thiscall myClass::set(int)"
(__imp_?@@)
DLLMainEXEDynamically.obj : error LNK2001: unresolved external symbol
"__declspec(dllimport) public: int __thiscall myClass::get(void)"
(__imp_?@@QAEHXZ)
DLLMainEXEDynamically.obj : error LNK2001: unresolved external symbol
"__declspec(dllimport) public: __thiscall myClass::~myClass(void)"
(__imp_??1myClass@@)

Ce que je comprends bien mais je me demande comment on contourne le
problème parce que l'éditeur de lien ne sera pas d'accord tant que l'on
ne lui donnera l'indication qui lui permet de résoudre les symboles.

smu
Avatar
Frédéric Lachasse
"smu" wrote in message
news:4221aec1$0$4071$
Bonjour,

Je cherche une méthode pour rendre une DLL de classe chargeable
dynamiquement.
J'ai trouvé pas mal d'information sur le net mais rien ne semble vouloir
fonctionner. Il me semble avoir compris que cela dépendait du compilateur,
la méthode semble varier entre VC++ 4 et VC++ 5. De même, elle semble
différente avec les compilateurs de chez Borland.

Moi, je travaille avec un VC++ 6.

Le problème se situe à l'édition des liens puisque les méthodes de la
classe ne peuvent, évidement, pas être résolu.

Quels sont les modifications à apporter pour que l'édition des liens se
passe bien ?

Je vous fournis les sources existants à ce jour.

D'avance merci.

smu

Voici les sources de la DLL :

------------ myClass.h ------------
#ifndef MY_CLASS_HEADER__
#define MY_CLASS_HEADER__

#ifdef DLLCLASS_EXPORTS
#define IMPEXP __declspec(dllexport)
#else
#define IMPEXP __declspec(dllimport)
#endif /* DLLCLASS_EXPORTS */

class IMPEXP myClass
{
public:
myClass(void);
~myClass(void);
void set(int local);
int get(void);

private:
int number;
bool initialized;
};

extern "C" IMPEXP myClass * myClass_createInstance(void);

typedef myClass * (* myClass_createInstancePTR)(void);

#endif /* MY_CLASS_HEADER__ */

------------ myClass.cpp ------------
#include "myClass.h"
#include <iostream>

myClass::myClass(void)
{
std::cout << "Initialization" << std::endl;
initialized = false;
number = 0;
}

myClass::~myClass(void)
{
std::cout << "Destruction" << std::endl;
}

void myClass::set(int local)
{
initialized = true;
number = local;
}

int myClass::get(void)
{
if (!initialized)
{
throw -1;
}
return number;
}

myClass * myClass_createInstance(void)
{
return new myClass;
}

------------ DLLClass.cpp ------------
#include <windows.h>

BOOL APIENTRY DllMain( HANDLE hModule,
DWORD ul_reason_for_call,
LPVOID lpReserved)
{
return TRUE;
}

Et voici les sources de l'application de test :

------------ DLLClassEXEDynamically.cpp ------------

#include "../DLLClass/myClass.h"
#include <stdlib.h>
#include <iostream>
#include <windows.h>

int main(int argc, char * argv[])
{
HMODULE hDLL;
myClass * check = NULL;
myClass_createInstancePTR myClass_createInstance = NULL;

hDLL = LoadLibrary("../DLLClass/Debug/DLLClass.dll");
if (NULL == hDLL)
{
std::cout << "Could not load the DLL" << std::endl;
return EXIT_FAILURE;
}
myClass_createInstance = (myClass_createInstancePTR)GetProcAddress(hDLL,

"myClass_createInstance");

std::cout << "Trying to create a new instance of the class" <<
std::endl;
check = myClass_createInstance();

try
{
std::cout << "The class contains: " << check->get() << std::endl;
}
catch(int)
{
std::cout << "An exception throwed" << std::endl;
}

check->set(5);

try
{
std::cout << "The class contains: " << check->get() << std::endl;
}
catch(int)
{
std::cout << "An exception throwed" << std::endl;
}

std::cout << "Trying to delete the instance of the class" << std::endl;
delete check;

FreeLibrary(hDLL);
return EXIT_SUCCESS;
}



La solution est d'utiliser des méthodes virtuelles. En effet, les fonctions
virtulles n'ont pas besoin d'être exportées, car on y accède par la
"v-table" de l'objet.

Utiliser des méthodes virtuelles permet aussi de séparer complètement
l'interface d'accès à la classe dans la DLL et son implémentation,
permettant facilement de changer le contenue de la DLL sans changer, ni même
recompiler l'exécutable tant que l'interface reste identique.

Note that only the factory fonction needs to be exported using
__declspec(dllexport). The class does not.

------- myClass.h -------
class myClass
{
public:
virtual ~myClass() {}
virtual void set(int value) = 0;
virtual int get() = 0;
};

extern "C" typedef myClass* (*myClass_createInstancePTR)();

------- myClassDLL.cpp ------
#include "myClass.h"

class myClassDll : public myClass
{
myClassDll();
virtual ~myClassDll();
virtual void set(int value);
virtual int get();

int number;
boolean initialized;
};

myClassDll::myClassDll()
{ ... }

myClassDll::~myClassDll()
{ ... }

void myClassDll::set(int value)
{ ... }

int myClassDll:get()
{ ... }

extern "C" __declspec(dllexport) myClass* myClass_createInstance()
{
return new myClassDll();
}

---- main.cpp ----
#include "myClass.h"
#include <windows.h>

int main()
{
hDLL = LoadLibrary("../DLLClass/Debug/DLLClass.dll");
myClass_createInstancePTR createInstance = (myClass_createInstancePTR)
GetProcAddress(hDLL, "myClass_createInstance");

myClass* instance = myClass_createInstance();

instance->set(5);

std::cout << "The class contains: " << instance->get() << std::endl;
delete instance;
FreeLibrary(hDLL);
return 0;
}
Avatar
smu
Frédéric Lachasse a écrit :
"smu" wrote in message
news:4221aec1$0$4071$

Bonjour,

Je cherche une méthode pour rendre une DLL de classe chargeable
dynamiquement.
J'ai trouvé pas mal d'information sur le net mais rien ne semble vouloir
fonctionner. Il me semble avoir compris que cela dépendait du compilateur,
la méthode semble varier entre VC++ 4 et VC++ 5. De même, elle semble
différente avec les compilateurs de chez Borland.

Moi, je travaille avec un VC++ 6.

Le problème se situe à l'édition des liens puisque les méthodes de la
classe ne peuvent, évidement, pas être résolu.

Quels sont les modifications à apporter pour que l'édition des liens se
passe bien ?

Je vous fournis les sources existants à ce jour.

D'avance merci.

smu

Voici les sources de la DLL :

------------ myClass.h ------------
#ifndef MY_CLASS_HEADER__
#define MY_CLASS_HEADER__

#ifdef DLLCLASS_EXPORTS
#define IMPEXP __declspec(dllexport)
#else
#define IMPEXP __declspec(dllimport)
#endif /* DLLCLASS_EXPORTS */

class IMPEXP myClass
{
public:
myClass(void);
~myClass(void);
void set(int local);
int get(void);

private:
int number;
bool initialized;
};

extern "C" IMPEXP myClass * myClass_createInstance(void);

typedef myClass * (* myClass_createInstancePTR)(void);

#endif /* MY_CLASS_HEADER__ */

------------ myClass.cpp ------------
#include "myClass.h"
#include <iostream>

myClass::myClass(void)
{
std::cout << "Initialization" << std::endl;
initialized = false;
number = 0;
}

myClass::~myClass(void)
{
std::cout << "Destruction" << std::endl;
}

void myClass::set(int local)
{
initialized = true;
number = local;
}

int myClass::get(void)
{
if (!initialized)
{
throw -1;
}
return number;
}

myClass * myClass_createInstance(void)
{
return new myClass;
}

------------ DLLClass.cpp ------------
#include <windows.h>

BOOL APIENTRY DllMain( HANDLE hModule,
DWORD ul_reason_for_call,
LPVOID lpReserved)
{
return TRUE;
}

Et voici les sources de l'application de test :

------------ DLLClassEXEDynamically.cpp ------------

#include "../DLLClass/myClass.h"
#include <stdlib.h>
#include <iostream>
#include <windows.h>

int main(int argc, char * argv[])
{
HMODULE hDLL;
myClass * check = NULL;
myClass_createInstancePTR myClass_createInstance = NULL;

hDLL = LoadLibrary("../DLLClass/Debug/DLLClass.dll");
if (NULL == hDLL)
{
std::cout << "Could not load the DLL" << std::endl;
return EXIT_FAILURE;
}
myClass_createInstance = (myClass_createInstancePTR)GetProcAddress(hDLL,

"myClass_createInstance");

std::cout << "Trying to create a new instance of the class" <<
std::endl;
check = myClass_createInstance();

try
{
std::cout << "The class contains: " << check->get() << std::endl;
}
catch(int)
{
std::cout << "An exception throwed" << std::endl;
}

check->set(5);

try
{
std::cout << "The class contains: " << check->get() << std::endl;
}
catch(int)
{
std::cout << "An exception throwed" << std::endl;
}

std::cout << "Trying to delete the instance of the class" << std::endl;
delete check;

FreeLibrary(hDLL);
return EXIT_SUCCESS;
}




La solution est d'utiliser des méthodes virtuelles. En effet, les fonctions
virtulles n'ont pas besoin d'être exportées, car on y accède par la
"v-table" de l'objet.

Utiliser des méthodes virtuelles permet aussi de séparer complètement
l'interface d'accès à la classe dans la DLL et son implémentation,
permettant facilement de changer le contenue de la DLL sans changer, ni même
recompiler l'exécutable tant que l'interface reste identique.

Note that only the factory fonction needs to be exported using
__declspec(dllexport). The class does not.

------- myClass.h -------
class myClass
{
public:
virtual ~myClass() {}
virtual void set(int value) = 0;
virtual int get() = 0;
};

extern "C" typedef myClass* (*myClass_createInstancePTR)();

------- myClassDLL.cpp ------
#include "myClass.h"

class myClassDll : public myClass
{
myClassDll();
virtual ~myClassDll();
virtual void set(int value);
virtual int get();

int number;
boolean initialized;
};

myClassDll::myClassDll()
{ ... }

myClassDll::~myClassDll()
{ ... }

void myClassDll::set(int value)
{ ... }

int myClassDll:get()
{ ... }

extern "C" __declspec(dllexport) myClass* myClass_createInstance()
{
return new myClassDll();
}

---- main.cpp ----
#include "myClass.h"
#include <windows.h>

int main()
{
hDLL = LoadLibrary("../DLLClass/Debug/DLLClass.dll");
myClass_createInstancePTR createInstance = (myClass_createInstancePTR)
GetProcAddress(hDLL, "myClass_createInstance");

myClass* instance = myClass_createInstance();

instance->set(5);

std::cout << "The class contains: " << instance->get() << std::endl;
delete instance;
FreeLibrary(hDLL);
return 0;
}





Les modifications sont claires et fonctionnelles. Je peux charger la DLL
à la volée et créer des instances de ma classe. C'est parfait.

Merci beaucoup

Je vais peut-être abuser mais si je veux utiliser cette DLL en statique
au travers du fichier ".lib", j'obtiens une erreur à l'édition des liens :

DLLMainEXEStatically.obj : error LNK2001: unresolved external symbol
"public: __thiscall myClassDLL::myClassDLL(void)" (??0myClassDLL@@)

Il y a t-il une solution à ce problème ?

D'avance merci

smu
Avatar
Frédéric Lachasse
"smu" wrote in message
news:42223485$0$31363$
Frédéric Lachasse a écrit :
"smu" wrote in message
news:4221aec1$0$4071$

Bonjour,

Je cherche une méthode pour rendre une DLL de classe chargeable
dynamiquement.
J'ai trouvé pas mal d'information sur le net mais rien ne semble vouloir
fonctionner. Il me semble avoir compris que cela dépendait du
compilateur, la méthode semble varier entre VC++ 4 et VC++ 5. De même,
elle semble différente avec les compilateurs de chez Borland.

Moi, je travaille avec un VC++ 6.

Le problème se situe à l'édition des liens puisque les méthodes de la
classe ne peuvent, évidement, pas être résolu.

Quels sont les modifications à apporter pour que l'édition des liens se
passe bien ?

Je vous fournis les sources existants à ce jour.

D'avance merci.

smu

Voici les sources de la DLL :

------------ myClass.h ------------
#ifndef MY_CLASS_HEADER__
#define MY_CLASS_HEADER__

#ifdef DLLCLASS_EXPORTS
#define IMPEXP __declspec(dllexport)
#else
#define IMPEXP __declspec(dllimport)
#endif /* DLLCLASS_EXPORTS */

class IMPEXP myClass
{
public:
myClass(void);
~myClass(void);
void set(int local);
int get(void);

private:
int number;
bool initialized;
};

extern "C" IMPEXP myClass * myClass_createInstance(void);

typedef myClass * (* myClass_createInstancePTR)(void);

#endif /* MY_CLASS_HEADER__ */

------------ myClass.cpp ------------
#include "myClass.h"
#include <iostream>

myClass::myClass(void)
{
std::cout << "Initialization" << std::endl;
initialized = false;
number = 0;
}

myClass::~myClass(void)
{
std::cout << "Destruction" << std::endl;
}

void myClass::set(int local)
{
initialized = true;
number = local;
}

int myClass::get(void)
{
if (!initialized)
{
throw -1;
}
return number;
}

myClass * myClass_createInstance(void)
{
return new myClass;
}

------------ DLLClass.cpp ------------
#include <windows.h>

BOOL APIENTRY DllMain( HANDLE hModule,
DWORD ul_reason_for_call,
LPVOID lpReserved)
{
return TRUE;
}

Et voici les sources de l'application de test :

------------ DLLClassEXEDynamically.cpp ------------

#include "../DLLClass/myClass.h"
#include <stdlib.h>
#include <iostream>
#include <windows.h>

int main(int argc, char * argv[])
{
HMODULE hDLL;
myClass * check = NULL;
myClass_createInstancePTR myClass_createInstance = NULL;

hDLL = LoadLibrary("../DLLClass/Debug/DLLClass.dll");
if (NULL == hDLL)
{
std::cout << "Could not load the DLL" << std::endl;
return EXIT_FAILURE;
}
myClass_createInstance =
(myClass_createInstancePTR)GetProcAddress(hDLL,

"myClass_createInstance");

std::cout << "Trying to create a new instance of the class" <<
std::endl;
check = myClass_createInstance();

try
{
std::cout << "The class contains: " << check->get() << std::endl;
}
catch(int)
{
std::cout << "An exception throwed" << std::endl;
}

check->set(5);

try
{
std::cout << "The class contains: " << check->get() << std::endl;
}
catch(int)
{
std::cout << "An exception throwed" << std::endl;
}

std::cout << "Trying to delete the instance of the class" << std::endl;
delete check;

FreeLibrary(hDLL);
return EXIT_SUCCESS;
}




La solution est d'utiliser des méthodes virtuelles. En effet, les
fonctions virtulles n'ont pas besoin d'être exportées, car on y accède
par la "v-table" de l'objet.

Utiliser des méthodes virtuelles permet aussi de séparer complètement
l'interface d'accès à la classe dans la DLL et son implémentation,
permettant facilement de changer le contenue de la DLL sans changer, ni
même recompiler l'exécutable tant que l'interface reste identique.

Note that only the factory fonction needs to be exported using
__declspec(dllexport). The class does not.

------- myClass.h -------
class myClass
{
public:
virtual ~myClass() {}
virtual void set(int value) = 0;
virtual int get() = 0;
};

extern "C" typedef myClass* (*myClass_createInstancePTR)();

------- myClassDLL.cpp ------
#include "myClass.h"

class myClassDll : public myClass
{
myClassDll();
virtual ~myClassDll();
virtual void set(int value);
virtual int get();

int number;
boolean initialized;
};

myClassDll::myClassDll()
{ ... }

myClassDll::~myClassDll()
{ ... }

void myClassDll::set(int value)
{ ... }

int myClassDll:get()
{ ... }

extern "C" __declspec(dllexport) myClass* myClass_createInstance()
{
return new myClassDll();
}

---- main.cpp ----
#include "myClass.h"
#include <windows.h>

int main()
{
hDLL = LoadLibrary("../DLLClass/Debug/DLLClass.dll");
myClass_createInstancePTR createInstance =
(myClass_createInstancePTR)
GetProcAddress(hDLL, "myClass_createInstance");

myClass* instance = myClass_createInstance();

instance->set(5);

std::cout << "The class contains: " << instance->get() << std::endl;
delete instance;
FreeLibrary(hDLL);
return 0;
}





Les modifications sont claires et fonctionnelles. Je peux charger la DLL à
la volée et créer des instances de ma classe. C'est parfait.

Merci beaucoup

Je vais peut-être abuser mais si je veux utiliser cette DLL en statique au
travers du fichier ".lib", j'obtiens une erreur à l'édition des liens :

DLLMainEXEStatically.obj : error LNK2001: unresolved external symbol
"public: __thiscall myClassDLL::myClassDLL(void)" (??0myClassDLL@@)

Il y a t-il une solution à ce problème ?



Certainement, car cela ne devrait pas arriver. Cela veut dire que tu as une
erreur quelque part dans la manière avec lequel le programme est
compilé/linké ou les headers sont organisés. En clair, dans une partie qui
n'est pas dans les sources ci-dessus.

Malheureusement, je n'ai pas assez de renseignement pour t'aider plus (ma
boule de crystal est en panne :-) )...

--
Frédéric Lachasse - ECP86