Charger des fonctions depuis une dll

HeXa2 Messages postés 22 Date d'inscription mardi 10 mars 2009 Statut Membre Dernière intervention 9 avril 2009 - 8 avril 2009 à 12:02
cs_rt15 Messages postés 3874 Date d'inscription mardi 8 mars 2005 Statut Modérateur Dernière intervention 7 novembre 2014 - 9 avril 2009 à 15:47
Bonjour!

Je viens de finir de compiler une DLL: H2BUSDLL.dll
cette dll a pour but de gerer une communication sur le port com (que je vais interfacer avec un autre soft apres).

Pour le moment, je veux la tester, donc j'ai créé un nouveau projet testH2bus sous VC++

J'ai trouvé pas mal de petits bouts de code sur le net et j'ai fait un peu de tetris pour parvenir a une solution qui marche bien.. ou plutot qui devrait bien marcher...

je vous met les infos:
<hr size="2" width="100%" />dans le .def de ma dll:
; H2BUSDLL.def : déclare les paramètres de module pour la DLL.

LIBRARY      "H2BUSDLL"
EXPORTS
    init_com        @1
    write_data_s    @2
    write_data_m    @3
    read_data_s        @4
    read_data_m        @5
    end_com         @6
<hr size="2" width="100%" />dans mon code de testH2bus.cpp:
HINSTANCE H2busdll;

 uint LoadH2busdll()
 {
     int LoadStat=0;
 
     H2busdll=::LoadLibrary("H2BUSDLL.dll" );
     if(H2busdll!=NULL)
     {
         (FARPROC) init_com= GetProcAddress(H2busdll, "init_com" );
         if(init_com!=NULL)
         {
            (FARPROC) write_data_s=GetProcAddress(H2busdll, "write_data_s" );
       
            if(write_data_s!=NULL)
            {
                (FARPROC) write_data_m=GetProcAddress(H2busdll, "write_data_m" );
       
                if(write_data_m!=NULL)
                {
                    (FARPROC) read_data_s=GetProcAddress(H2busdll, "read_data_s" );
       
                    if(read_data_s!=NULL)
                    {
                        (FARPROC) read_data_m=GetProcAddress(H2busdll, "read_data_m" );
       
                        if(read_data_m!=NULL)
                        {
                            (FARPROC) end_com=GetProcAddress(H2busdll, "end_com" );
                           
                            if (end_com!=NULL)
                        {
                            LoadStat=1; /* Flag de chargement correct */
       
                            }}}}}}
       
                 if(!LoadStat)
                 {
                                 printf("Erreur de chargement du pilote (H2BUSDLL.DLL)\n" );          
                 }
         }
         else // ie (H2BUSDLL == 0)
         {
                 printf("Impossible de charger le pilote (H2BUSDLL.dll)\n" );
         }
   
     return (LoadStat);  
    }
<hr size="2" width="100%" />Et bien sur, les erreurs qu'il me renvoit:
1>------ Début de la génération : Projet : testH2BUS, Configuration : Debug Win32 ------
1>Compilation en cours...
1>testH2BUS.cpp
1>.\testH2BUS.cpp(29) : error C2664: 'LoadLibraryW' : impossible de convertir le paramètre 1 de 'const char [13]' en 'LPCWSTR'
1>        Les types pointés n'ont aucun rapport entre eux ; conversion nécessitant reinterpret_cast, cast de style C ou cast de style fonction
1>.\testH2BUS.cpp(32) : error C2106: '=' : l'opérande gauche doit être une l-value
1>.\testH2BUS.cpp(35) : error C2106: '=' : l'opérande gauche doit être une l-value
1>.\testH2BUS.cpp(39) : error C2106: '=' : l'opérande gauche doit être une l-value
1>.\testH2BUS.cpp(43) : error C2106: '=' : l'opérande gauche doit être une l-value
1>.\testH2BUS.cpp(47) : error C2106: '=' : l'opérande gauche doit être une l-value
1>.\testH2BUS.cpp(51) : error C2106: '=' : l'opérande gauche doit être une l-value
1>Le journal de génération a été enregistré à l'emplacement "\\..\Mes documents\Visual Studio 2008\Projects\testH2BUS\testH2BUS\Debug\BuildLog.htm"
1>testH2BUS - 7 erreur(s), 0 avertissement(s)
======== Génération : 0 a réussi, 1 a échoué, 0 mis à jour, 0 a été ignoré ==========
<hr size="2" width="100%" />l'erreur C2664 est au niveau de "  H2busdll=::LoadLibrary("H2BUSDLL.dll" ); "
La je pense que l'erreur est corigeable facilement en definissant un "CWSTR" et en envoyant l'adresse d'un pointeur qui va dessus
mais pour toutes les erreurs suivantes, je suis un peu perdu.... au niveau des "   (FARPROC) read_data_s=GetProcAddress(H2busdll, "read_data_s" );  "
il me dit que je ne peux pas alors que je suis en train de definir les fonctions
pour la msdn, c'est une erreur du genre 1=a au lieu de a=1..mais je suis pas du tout convaincu que cel apuisse venir de la

Un coup de main ca serait cool
Mici
HeXa

22 réponses

cs_rt15 Messages postés 3874 Date d'inscription mardi 8 mars 2005 Statut Modérateur Dernière intervention 7 novembre 2014 13
8 avril 2009 à 13:51
Salut,

1>.\testH2BUS.cpp(29) : error C2664: 'LoadLibraryW' : impossible de
convertir le paramètre 1 de 'const char [13]' en 'LPCWSTR'

La fonction est terminée par un W -> donc tu es en train de faire de la compilation en unicode. Tu peux passer à a de la compilation MBCS dans la configuration de ton projet; ou faire une include de tchar.h et utiliser la macro _T pour ajouter un L devant la chaîne.

LoadLibrary(_T("H2BUSDLL.dll"));

A quoi servent tes :: avant le LoadLibrary ??

1>        Les types pointés n'ont aucun rapport entre eux ;
conversion nécessitant reinterpret_cast, cast de style C ou cast de
style fonction

Mouarf. Comprend pas grand chose. Tu peux pas faire du C plutôt ?

Tu es bien avancé de récupérer du FARPROC. Pourquoi ne pas récupérer directement la fonction ?

Jette un oeil là dessus.
0
HeXa2 Messages postés 22 Date d'inscription mardi 10 mars 2009 Statut Membre Dernière intervention 9 avril 2009
8 avril 2009 à 14:40
A quoi servent tes :: avant le LoadLibrary ??
euh... a rien..la fonction a une portée globale, donc ca revient au meme..

j'ai jetté un oeil au lien (wiki) mais je ne suis pas plus avancé. apparement, le codeur a definit un type spécifique pour la fonction... je peux faire la meme chose mais ca m'a pas l'air tres leger a mettre en oeuvre.

Est ce que c'est la bonne solution?

Ca doit pas etre si compliqué de definir des fonctions dans un soft avec la definition qui est contenue dans une dll, sachant que la dll a été developpée par moi-meme...
0
cs_rt15 Messages postés 3874 Date d'inscription mardi 8 mars 2005 Statut Modérateur Dernière intervention 7 novembre 2014 13
8 avril 2009 à 14:44
"le codeur a definit un type spécifique pour la fonction..."

C'est mieux : pour appeler ta fonction, il va te falloire son proto ! Tu n'es pas très avancé avec ton FARPROC.
0
HeXa2 Messages postés 22 Date d'inscription mardi 10 mars 2009 Statut Membre Dernière intervention 9 avril 2009
8 avril 2009 à 14:48
donc typiquement avec 6 fonctions a appeler, il faut definir 6 structures d'appel avec le nombre d'argument qui va bien?

Je pensais que ca rendait un code pas tres "clean", de plus il s'avere que je ne sais pas exactement ce qu'est un farproc (j'ai lu des bribes d'informations sur les pointeurs distants en win3.x mais ca s'arrete la).

Bon, je vais definir mes structures et je vous tient au courant

Mici
0

Vous n’avez pas trouvé la réponse que vous recherchez ?

Posez votre question
cs_ghuysmans99 Messages postés 3982 Date d'inscription jeudi 14 juillet 2005 Statut Membre Dernière intervention 30 juin 2013 16
8 avril 2009 à 18:56
Faut le dire, t'es pas forcément obligé d'utiliser ta DLL comme ça ...
Tu peux aussi faire comme cela :
#include "LeHeaderDeMaDLL.h"
#pragma comment(lib,"LeFichierLibDeMaDLL.lib")
...
Et tu n'as plus rien à faire : il te suffit d'appeler tes fonctions comme si elles étaient dans ton source.
0
HeXa2 Messages postés 22 Date d'inscription mardi 10 mars 2009 Statut Membre Dernière intervention 9 avril 2009
9 avril 2009 à 09:07
Serieux???

meme pas les extern "c" toussa toussa...??
Jvais tester ca des tout de suite
0
HeXa2 Messages postés 22 Date d'inscription mardi 10 mars 2009 Statut Membre Dernière intervention 9 avril 2009
9 avril 2009 à 11:22
Salut!

Bon, ca n'a pas l'air de fonctionner.
Lorsque je fais juste la déclaration des fonctions, ca compile parfaitement.
mais des que j'utilise une des fonctions (ici init_com) il me renvoit deux erreurs au linkage:
<hr size="2" width="100%" />1>tstBUS.obj : error LNK2028: jeton non résolu (0A00000F) "unsigned int __clrcall init_com(char * const,unsigned int)" (?init_com@@$$FYMIQADI@Z) référencé dans la fonction "private: void __clrcall tstBUS::Form1::button1_Click(class System::Object ^,class System::EventArgs ^)" (?button1_Click@Form1@tstBUS@@$$FA$AAMXP$AAVObject@System@@P$AAVEventArgs@4@@Z)
1>tstBUS.obj : error LNK2019: symbole externe non résolu "unsigned int __clrcall init_com(char * const,unsigned int)" (?init_com@@$$FYMIQADI@Z) référencé dans la fonction "private: void __clrcall tstBUS::Form1::button1_Click(class System::Object ^,class System::EventArgs ^)" (?button1_Click@Form1@tstBUS@@$$FA$AAMXP$AAVObject@System@@P$AAVEventArgs@4@@Z)
<hr size="2" width="100%" />
Est ce que ca peut venir du fait que le compilateur décore les symboles de mes fonctions?
Besoin que je fournisse plus d'elements?
Dans tous les cas, merci beaucoup de tous ces conseils
0
cs_rt15 Messages postés 3874 Date d'inscription mardi 8 mars 2005 Statut Modérateur Dernière intervention 7 novembre 2014 13
9 avril 2009 à 11:42
__clrcall

On va pas être copains ! Tu compile en dotnet. Je n'ai pas la moindre idée des conséquences de cela pour la compilation d'une dll.

ghuysmans99 te propose une liaison statique avec ta dll. C'est aussi expliqué sur la page wikipedia de mon lien plus haut. Il te faut notemment __declspec(dllimport).

Après tu peux mettre le extern "C" ou pas... Si c'est des fonctions compliquées et genre en __clrcall, alors il risque de ne pas le falloire. Si c'est des fonctions bateaux (init_com(char * const,unsigned int), c'est pas méchant), tu peux mettre le extern "C", pour ne pas avoir de déco et ainsi faciliter l'emploi de la dll depuis un autre langage que le C++. Et j'utiliserais la convention stdcall pour la même raison (C'est le mécanisme utilisé par la couche win32 de windows).

J'éssairais plutôt comme ça dans mon .h :
#ifdef _DLL
#define DLL_FUNC __declspec(dllexport)
#else
#define DLL_FUNC __declspec(dllimport)
#endif
extern"C"DLL_FUNCunsigned int __stdcall init_com(char * const,unsigned int);

Avec _DLL définit dans la dll et pas dans l'exe.

Dependspermet de déterminer ce qui est exporté par une dll.
Tu peux ainsi savoir si ta dll export les fonction correctement.
0
HeXa2 Messages postés 22 Date d'inscription mardi 10 mars 2009 Statut Membre Dernière intervention 9 avril 2009
9 avril 2009 à 12:06
Je connaissais pas dependency walker, mais j'utilise dllexp qui me donne le nom des fonctions exportées et leur adresse. A priori, ma dll exporte bien ses symboles, le probleme c'est a l'usage.

Je ne connais pas grand chose aux conventions d'appels (c'est ma premiere dll et mon premier VRAI projet en c++)

je ne connais donc que ce que j'ai pu trouver sur des forums divers et variés.

est ce que si je retire les conventions d'appels de mes fonctions dans la dll, ca marchera mieux?
je met mes bouts de code pour la fonction init_com
Dans le headerdll:
uint init_com(char comporttoopen[], uint bauds);
Dans le testdll.cpp (la ou je veux creer une instance de mes fonctions)
#include "H2BUSDLL.h"
#pragma comment(lib,"H2BUSDLL.lib")
Dans le Form1.h (le header de la classe graphique avec le bouton qui est censé appeler la fonction)
private: System::Void button1_Click(System::Object^  sender, System::EventArgs^  e) {

             init_com("COM1", 4800);
             
         }
};
et j'obtient les 2 erreurs de au dessus.

Mais je ne vous pas trop ou je peux mettre le bout de code que tu m'as passé. Dans ce que j'ai fait, j'ai mis les fonctions a exporter dans un .def
dans le .def de ma dll:
; H2BUSDLL.def : déclare les paramètres de module pour la DLL.

LIBRARY      "H2BUSDLL"
EXPORTS
    init_com        @1
    write_data_s    @2
    write_data_m    @3
    read_data_s        @4
    read_data_m        @5
    end_com         @6

je suis "un petit peu" (euphemisme) perdu la..
0
cs_rt15 Messages postés 3874 Date d'inscription mardi 8 mars 2005 Statut Modérateur Dernière intervention 7 novembre 2014 13
9 avril 2009 à 12:15
Il faudrait mettre ce code dans "headerdll". Et plus besoin de .def. __declspec(dllexport) fait le boulot.
0
cs_rt15 Messages postés 3874 Date d'inscription mardi 8 mars 2005 Statut Modérateur Dernière intervention 7 novembre 2014 13
9 avril 2009 à 12:25
0
HeXa2 Messages postés 22 Date d'inscription mardi 10 mars 2009 Statut Membre Dernière intervention 9 avril 2009
9 avril 2009 à 13:50
Yes!!! ca marche nickel...

En résumé:
Je crée une dll.
dans la dll:
HEADER
...blablabla...
extern "C" __declspec(dllexport) uint __stdcall init_com(char comporttoopen[], uint bauds);
...blablabla...
FICHIER CPP
...blablabla...

extern "C" __declspec(dllexport) uint __stdcall init_com(char comporttoopen[], uint bauds)
{
Ce que fait la fonction
}

...blablabla...
...blablabla...

Dans le fichier qui utilise la dll:
HEADER

...blablabla...

extern "C" __declspec(dllimport) uint __stdcall init_com(char comporttoopen[], uint bauds);

...blablabla...
FICHIER CPP

...blablabla...
#pragma comment(lib,"Lalibrairiequiaétégénéréeavecladll.lib")
...blablabla...

uint resultat = init_com ("COM1", 4800);

...blablabla...

le tout compile bien et link sans probleme...
Merci à tous pour vos conseils et les solutions proposées.

Je note également que les symboles n'ont pas l'air d'etre décorés et que tout fonctionne a Merveille
0
HeXa2 Messages postés 22 Date d'inscription mardi 10 mars 2009 Statut Membre Dernière intervention 9 avril 2009
9 avril 2009 à 13:55
Bon, j'ai rien dit, les symboles sont décorés....

avec le soft "DLL Export viewer " je vois que la fonction init_com s'appelle _init_com@8

je procede comment avec le symbole décoré?
j'ai essayé de remplacer "init_com" par "_init_com@8" lors de l'appel de la fonction, mais ca marche pas mieux
0
cs_rt15 Messages postés 3874 Date d'inscription mardi 8 mars 2005 Statut Modérateur Dernière intervention 7 novembre 2014 13
9 avril 2009 à 13:56
Bon alors...

__declspec(dllimport) ne sert qu'à avoir de meilleurs perfs. Mais il y a des risques de perte de compatibilité avec la dll vu que l'import se fait par ordinal. Donc on oublie.

Si on utilise __declspec(dllexport) avec la convention stdcall (Qui je le rappelle est la plus portable pour une dll), VC exporte les fonctions avec la décoration. La décoration c'est moche. Donc on oublie.

Si on utilise pas __declspec(dllexport), il faut un .def.
0
HeXa2 Messages postés 22 Date d'inscription mardi 10 mars 2009 Statut Membre Dernière intervention 9 avril 2009
9 avril 2009 à 14:03
on tourne en rond non???
a moins que je puisse utiliser le .def dans ma dll et un __declspec(dllimport) dans le programme client...?
0
cs_rt15 Messages postés 3874 Date d'inscription mardi 8 mars 2005 Statut Modérateur Dernière intervention 7 novembre 2014 13
9 avril 2009 à 14:11
Utilise le .def, mais pas dllimport. Et rien à la place de dllimport.

Partage un .h tout bête entre le projet du .exe et le projet de la dll :
(#ifdef __cplusplus pour que le header soit utilisable dans un programme C).
<hr size="2" width="100%" />#ifndef _C_DLL_
#define _C_DLL_

#ifdef __cplusplus
extern "C"
{
#endif

unsigned int __stdcall init_com(char * const, unsigned int);

#ifdef __cplusplus
}
#endif

#endif /* _C_DLL_ */
<hr size="2" width="100%" />Pour le reste, tu connais :
<hr size="2" width="100%" />LIBRARY      "C_Dll"
EXPORTS
    init_com @1
<hr size="2" width="100%" />#include "windows.h"

#define _DLL

#include "C_Dll.h"

unsigned int __stdcall init_com(char * const, unsigned int)
{
  MessageBox(0, "kikoo", "kikoo", MB_OK);
  return 12;
}
<hr size="2" width="100%" />#include "stdio.h"
#include "..\C_Dll.h"

#pragma comment(lib,"C_Dll.lib")

int main()
{
  printf("%u\n", init_com("toto", 2));

  return 0;
}
0
cs_rt15 Messages postés 3874 Date d'inscription mardi 8 mars 2005 Statut Modérateur Dernière intervention 7 novembre 2014 13
9 avril 2009 à 14:19
Mais l'exe importe la fonction par ordinal...
0
cs_rt15 Messages postés 3874 Date d'inscription mardi 8 mars 2005 Statut Modérateur Dernière intervention 7 novembre 2014 13
9 avril 2009 à 14:49
Pour que l'import ne se fasse pas par ordinal, il ne faut pas spécifier d'ordinal dans le .def (J'imagine que cela a une influence sur le .lib).
0
HeXa2 Messages postés 22 Date d'inscription mardi 10 mars 2009 Statut Membre Dernière intervention 9 avril 2009
9 avril 2009 à 14:51
je sais meme pas ce que ca veut dire mais... d'accord..
0
cs_rt15 Messages postés 3874 Date d'inscription mardi 8 mars 2005 Statut Modérateur Dernière intervention 7 novembre 2014 13
9 avril 2009 à 15:14
Ci-dessus, un import par ordinal et un import par nom sous depends.


Dans la dll, chaque fonction exportée l'est par un nom et par un numéro ordinal.

Ensuite, dans l'exe, avec un import par ordinal, windows cherche la fonction avec l'ordinal correspondant.
Avec un import par nom, windows va chercher la fonction avec le nom correspondant.


Suppose que tu compile un .exe qui utilise une .dll compilée avec le .def suivant :
toto @1


Ensuite, suppose que tu ajoutes une fonction à ta dll :
tata @1
toto @2


Eh bien si tu ne recompiles pas ton .exe et qu'il utilises cette nouvelle dll, au moment ou l'exe va appeler toto, il ne va pas appeler toto mais tata. Car c'est tata qui est importée avec l'ordinal 1.


Alors que si tu fais un import par nom ->
toto


puis
tata
toto


Pas de souci. Ton exe chargera bien toto.


Donc pour se faciliter la vie en matière de compatibilité descendante, on utilise des imports par nom (C'est ce qui est fait sous pour le win32 encore une fois).
0
Rejoignez-nous