Vous ne trouvez pas de réponse à votre problème ? Alors posez la question dans le forum. Souvenez-vous qu'il n'y a jamais de question bête, mais rester dans l'ignorance parce que l'on n'ose pas poser une question, ça c'est une erreur !

UTILISER LES APIS DE DLLS SANS AVOIR LE FICHIER .LIB


Information sur la source

Catégorie :API Niveau : Débutant Date de création : 15/08/2004 Date de mise à jour : 16/08/2004 00:17:51 Vu / téléchargé: 5 211 / 343

Note :
8 / 10 - par 2 personnes
8,00 / 10

  • 1

  • 2

  • 3

  • 4

  • 5

  • 6

  • 7

  • 8

  • 9

  • 10

Commentaire sur cette source (27)
Ajouter un commentaire et/ou une note

Description

Ceci permet d'utiliser APIs de DLLs sans avoir le fichier .lib qui nous manque parfois de façon complète et relativement simplement.

C'est ma première source C++ (je fesais du VB avant) donc soyez pas trop chiens s.v.p
 

Source

  • //À mettre dans un fichier .h appart qui sera à include
  • //Mettre les définitions d'API ici
  • //Comment on défénit le type d'une API? Regardez :
  • //typedef %type de ce que retourne l'API% (%Convention d'appel%)* %le nom de ce nouveau type%) (%les paramètres de l'API%);
  • //%le nom du type de l'API% %le nom de la variable qui contiendra l'API%;
  • //En général, __stdcall sera la convention d'appel requise mais il ce peut que ce soit __cdecl
  • typedef int (__stdcall* apiMsgBox) (HWND Handle, LPCSTR Texte, LPCSTR Titre, UINT Flags);
  • apiMsgBox MonMsgBox;
  • typedef int (__stdcall* apiGetTickCount) (void);
  • apiGetTickCount MonGetTickCount;
  • typedef void (__cdecl* apitest) (LPCSTR a, int b);
  • //LA CONVENTION D'APPEL PAR DÉFAULT EN C++ EST __cdecl donc il ne faut pas __stdcall mais rien ou __cdecl
  • apitest Montest;
  • //}
  • HMODULE hmodDlls[1];//Dans ce tableau, on stock les modules
  • int Count=1;//On Commence à 1
  • void InitializeAPIs()//Cette fonction initialize les APIs
  • {
  • hmodDlls[Count]=LoadLibrary(TEXT("User32.dll"));//On load la DLL User32.dll
  • if (!hmodDlls[Count]==0){cout << "Chargement du module #" << Count << " : " << hmodDlls[Count] << "\n";}else{cout << "Impossible de charger le module #" << Count << "!\n";}
  • MonMsgBox=(apiMsgBox)GetProcAddress(hmodDlls[Count],TEXT("MessageBoxA"));
  • //La variable MonMsgBox (qui est du type apiMsgBox) reçcit comme valeur un pointeur vers la fonction MessageBoxA de User32.dll
  • //Si on voudrait déclarer une nouvelle API qui est dans User32.dll, on le ferait ICI
  • Count++;//Quand on a fini avec un module, on incrémente Count pour faire de la place pour un nouveau module si on en a besoins
  • //Nouveau module : Kernel32.dll
  • hmodDlls[Count]=LoadLibrary(TEXT("Kernel32.dll"));//On load la DLL User32.dll
  • if (!hmodDlls[Count]==0){cout << "Chargement du module #" << Count << " : " << hmodDlls[Count] << "\n";}else{cout << "Impossible de charger le module #" << Count << "!\n";}
  • MonGetTickCount=(apiGetTickCount)GetProcAddress(hmodDlls[Count],TEXT("GetTickCount"));//...
  • Count++;
  • hmodDlls[Count]=LoadLibrary(TEXT("Ma dll.dll"));//On load la DLL Drole.dll (que j'ai fait en C++)
  • if (!hmodDlls[Count]==0){cout << "Chargement du module #" << Count << " : " << hmodDlls[Count] << "\n";}else{cout << "Impossible de charger le module #" << Count << "!\n";}
  • Montest=(apitest)GetProcAddress(hmodDlls[Count],TEXT("test"));//...
  • cout << "\n\n";//laisser deux lignes vides
  • }
  • void TerminateAPIs()//Tout nettoyer
  • {
  • for(int i = 1; i <= Count ; i++)//Pour chaque module qu'on a loader
  • {
  • cout << "Nettoyage du module #" << i << " : " << hmodDlls[i] << "\n";//Dire que le module à été nettoyer
  • FreeLibrary(hmodDlls[i]);//Relacher le module
  • }
  • cout << "\n";//laisser une ligne vide à la fin
  • }
//À mettre dans un fichier .h appart qui sera à include
//Mettre les définitions d'API ici

//Comment on défénit le type d'une API? Regardez :

//typedef %type de ce que retourne l'API% (%Convention d'appel%)* %le nom de ce nouveau type%) (%les paramètres de l'API%);

//%le nom du type de l'API% %le nom de la variable qui contiendra l'API%;

//En général, __stdcall sera la convention d'appel requise mais il ce peut que ce soit __cdecl

typedef int (__stdcall* apiMsgBox) (HWND Handle, LPCSTR Texte, LPCSTR Titre, UINT Flags);
apiMsgBox MonMsgBox;


typedef int (__stdcall* apiGetTickCount) (void);
apiGetTickCount MonGetTickCount;


typedef void (__cdecl* apitest) (LPCSTR a, int b);
//LA CONVENTION D'APPEL PAR DÉFAULT EN C++ EST __cdecl donc il ne faut pas __stdcall mais rien ou __cdecl
apitest Montest;

//}


HMODULE hmodDlls[1];//Dans ce tableau, on stock les modules
int Count=1;//On Commence à 1

void InitializeAPIs()//Cette fonction initialize les APIs
{


hmodDlls[Count]=LoadLibrary(TEXT("User32.dll"));//On load la DLL User32.dll
if (!hmodDlls[Count]==0){cout << "Chargement du module #" << Count << " : " << hmodDlls[Count] << "\n";}else{cout << "Impossible de charger le module #" << Count << "!\n";}

MonMsgBox=(apiMsgBox)GetProcAddress(hmodDlls[Count],TEXT("MessageBoxA"));
//La variable MonMsgBox (qui est du type apiMsgBox) reçcit comme valeur un pointeur vers la fonction MessageBoxA de User32.dll

//Si on voudrait déclarer une nouvelle API qui est dans User32.dll, on le ferait ICI

Count++;//Quand on a fini avec un module, on incrémente Count pour faire de la place pour un nouveau module si on en a besoins

//Nouveau module : Kernel32.dll

hmodDlls[Count]=LoadLibrary(TEXT("Kernel32.dll"));//On load la DLL User32.dll
if (!hmodDlls[Count]==0){cout << "Chargement du module #" << Count << " : " << hmodDlls[Count] << "\n";}else{cout << "Impossible de charger le module #" << Count << "!\n";}

MonGetTickCount=(apiGetTickCount)GetProcAddress(hmodDlls[Count],TEXT("GetTickCount"));//...

Count++;

hmodDlls[Count]=LoadLibrary(TEXT("Ma dll.dll"));//On load la DLL Drole.dll (que j'ai fait en C++)
if (!hmodDlls[Count]==0){cout << "Chargement du module #" << Count << " : " << hmodDlls[Count] << "\n";}else{cout << "Impossible de charger le module #" << Count << "!\n";}

Montest=(apitest)GetProcAddress(hmodDlls[Count],TEXT("test"));//...

cout << "\n\n";//laisser deux lignes vides
}

void TerminateAPIs()//Tout nettoyer
{
	for(int i = 1; i <= Count ; i++)//Pour chaque module qu'on a loader
	{
	cout << "Nettoyage du module #" << i << " : " << hmodDlls[i] << "\n";//Dire que le module à été nettoyer
	FreeLibrary(hmodDlls[i]);//Relacher le module
	}
cout << "\n";//laisser une ligne vide à la fin
}

Conclusion

Alors? Pour plus d'explications, veuillez me les demander.
 

Fichier Zip

Pour les "Membres Club", vous pouvez télécharger directement un fichier contenu dans le zip sans télécharger le zip en entier !

Télécharger le zip

Historique

15 août 2004 03:51:42 :
Ajout de la source
15 août 2004 04:05:00 :
Modification de la source affichée sur ce site
15 août 2004 06:07:34 :
Correction de la faute de frappe car GetTickCount retourne le nombre de milisecondes et non le nombre de seconde
16 août 2004 00:17:51 :
Version 2.0 : correction de cette terrible erreur que m'a fait remarquer brunews

Commentaires et avis

signaler à un administrateur
Commentaire de aardman le 15/08/2004 04:27:05

Salut,
Juste un petit truc a corriger: GetTickCount retourne le nombre de milliseconde qui se sont écoulées depuis qu'on a booté l'ordi, pas le nombre de seconde depuis minuit..

signaler à un administrateur
Commentaire de DeadlyPredator le 15/08/2004 06:04:45

OUPS!! désolé, faute de frappe : corrigé

signaler à un administrateur
Commentaire de DeadlyPredator le 15/08/2004 06:09:30

Et j'aurais une question. Est-ce qu'il y aurait moyen de généré le type qui définit la fonction durant l'éxécution du code (sans avoir à utiliser callwindowproc pour appeller l'API)?

signaler à un administrateur
Commentaire de BruNews le 15/08/2004 11:24:54 administrateur CS

Salut,

InitializeAPIs();
init les pointeurs de fonction MAIS decharge les librairies de l'adresse referencee par les pointeurs.
Tu appelles ensuite ces fonctions par les pointeurs. Si tu avais teste avec d'autres DLLs que les 3 grosses du kernel qui sont par force deja dans ton espace memoire, c'eut ete le crash a coup sur.

signaler à un administrateur
Commentaire de racpp le 15/08/2004 16:24:11 administrateur CS

Salut tout le monde,
Je viens de tester ce source avec une DLL personelle. Comme tu l'as dit Brunews, ça plante. C'est normal car on appelle une API d'une DLL déja déchargée  de la mémoire. Mais l'idée est très bonne. Ces API doivent  être initialisées dans le Main (ou WinMain)  du code puis leurs DLLs seront déchargées de la mémoire quand on en a plus besoin. Je vais très bientôt poster un source qui donne un exemple montrant l'intéret de cette méthode.
A+

signaler à un administrateur
Commentaire de DeadlyPredator le 15/08/2004 22:18:02

DÉSOLÉ je n'y avais pas penser (je suis bête) j'arrange ça tout de suite

signaler à un administrateur
Commentaire de DeadlyPredator le 16/08/2004 06:28:07

Pour en revenir à ma question, pourrait-on déclarer le type de l'API durant l'Éxécution du code?

signaler à un administrateur
Commentaire de BlackGoddess le 16/08/2004 10:20:09

tu peux utiliser de l'asm pour lui construire sa liste d'arguments a la volée


HMODULE hmodDlls[1];//Dans ce tableau, on stock les modules
int Count=1;//On Commence à 1

hmodDlls[Count]=.....

j'ai du mal a comprendre comment le code marche ...

signaler à un administrateur
Commentaire de BruNews le 16/08/2004 10:28:26 administrateur CS

Fais toujours commencer tes tableaux a 0.

signaler à un administrateur
Commentaire de DeadlyPredator le 16/08/2004 19:44:45

je suis débutant en cpp. Quand tu dit commence à 0 brunews, tu dit remplir à partir de 0?. Le principe de tableau est que dans celui ci, on stock le chaque dll loadé pour qu'à la fin, on la unload. Je vais étudier ça BlackGoddess...

signaler à un administrateur
Commentaire de BruNews le 16/08/2004 20:06:35 administrateur CS

Au debut tu as par exemple:
#define MAX 10
int array[MAX];
int count = 0;

Tu ajoutes un elem x:
if(count < MAX) array[count++] = x;

Parcours:
for(int i = 0; i < count; i++) Display(array[i]);

etc...

signaler à un administrateur
Commentaire de racpp le 17/08/2004 04:13:35 administrateur CS

Salut,
Je viens de poster un source  montrant l'intérêt de ton idée DeadlyPredator :
http://www.cppfrance.com/code.aspx?ID=25462

A propos de ta question, je ne suis pas sûr d'avoir bien compris. Mais je peux dire que, à ma connaissance, rien ne permet à un programme, pendant son exécution, de déterminer le type de retour ou le nombre et le type des paramètres d'une fonction d'une DLL. Tout ce qu'il peut savoir, c'est le nombre total des octets des paramètres d'entrée de la fonction. Aucun des moniteurs de DLLs que  j'ai testé n'est capable d'afficher ni le nombre ni les types de paramètres  d'une fonction exportée de la DLL visionée. S'il existait  un moyen, les développeurs de ces utilitaires l'auraient  utilisé. Normalement, un programmeur doit être suffisament documenté sur la fonction qu'il veut appeler. Le type d'API sera donc declaré dans le code source. Franchement, je ne vois pas l'intérêt  de trouver le nombre ou les types de paramètres d'une fonction qu'on ne connait pas.
J'espère que tout ve que je viens de dire est en rapport avec ta question.  Sinon, tu nous précises un peu plus.
A+

signaler à un administrateur
Commentaire de DeadlyPredator le 17/08/2004 06:42:57

brunews ton idée à l'air bien mais personellement, je déteste les limites(sauf celle du type de donné lui-même). Je crois avoir entendu parler de fonctions avec des noms du genre malloc et ralloc(pas sûre) qui permettent de créer ou de redimensionner un tableau. racpp, je voulais savoir si on pouvait déclarer un type dans le code afin de créer un système genre :

void InitApi(LPCSTR Module, LPCSTR Name, LPCSTR Params[])

qui permettrait d'utiliser les APIs en entrant les paramètres durant l'éxécution du code. ça aurait été plus simple à utiliser.

signaler à un administrateur
Commentaire de BruNews le 17/08/2004 10:17:01 administrateur CS

Je n'indiquais que le mode d'acces aux tableaux en partant de base ZERO, rien d'autre.

signaler à un administrateur
Commentaire de racpp le 17/08/2004 16:09:21 administrateur CS

Salut,
J'avoue ne pas avoir encore bien compris. Donne un exemple précis illustrant le problème à résoudre.
A bientôt.

signaler à un administrateur
Commentaire de BlackGoddess le 17/08/2004 16:27:18

HMODULE hmodDlls[1];//Dans ce tableau, on stock les modules
int Count=1;//On Commence à 1

hmodDlls[Count]=.....

=>

hmodDlls[1] déclare un tableau a une seule case, accessible par hmodDlls[0]
hmodDlls[Count]=..... (Count = 1) => l'index est en dehors du tableau, tu ne peux pas ecrire ca.

signaler à un administrateur
Commentaire de BruNews le 17/08/2004 16:29:41 administrateur CS

le 1er index d'un tableau est elem[0] et non 1.

signaler à un administrateur
Commentaire de racpp le 17/08/2004 20:43:43 administrateur CS

Il fallait écrire par exemple:
int const nombre=10; //nombre de modules à stocker.
HMODULE hmodDlls[nombre];
int count=0;//index 1er élément toujours =0
//count peut varier de 0 à 9.

A propos de la remarque que tu as faite sur ma source,  je pense que l'API est toujours appelée directement pendant l'exécution du programme par son adresse mémoire et en passant les paramètres dans la pile. La définition du mode d'appel n'est utile que pour  le compilateur. Que ce soit dans un .LIB  ou par pointeur le résultat  est le même. Mais j'aimerais bien voir l'avis de quelqu'un qui  a fait des tests réels pour en avoir le coeur net. Si tu l'as déja fait,  donne moi un exemple de petite source (ou un lien) illustrant la différence entre les deux méthodes. Merci.
A bientôt.

signaler à un administrateur
Commentaire de DeadlyPredator le 18/08/2004 21:33:06

Si tu regarde ton prog avec Depends, tu va voir que l'API que tu a appelé avec mon système n'y est pas donc elle n'est pas dans la genre de table des importations du prog donc je ne sais plus trop... car c'est vrai aussi qu'on l'appelle via le pointeur.

signaler à un administrateur
Commentaire de racpp le 19/08/2004 18:14:53 administrateur CS

Salut,
L'idée de l'utilisation des pointeurs pour appeler les fonctions d'une DLL n'est pas nouvelle. Il suffit de regarder le nombre de sources, déposées dans ce site, qui font appel à cette méthode. Ce qui m'a plu dans ton idée c'est le fait de  nous avoir rappelé  qu'on peut l'utiliser pour se passer des fichiers ".LIB" et ".h" relatifs à une DLL.

A propos de la différence entre les deux méthodes, voici toutes les explications fournies par Microsoft:

http://msdn.microsoft.com/library/fre/default.asp?url=/library/FRE/vccore/html/_core_link_an_executable_to_a_dll.asp

Je viens d'y apprendre que l'utilisation  des ".LIB" et ".h" ne sert qu'à simplifier la programmation et que l'utilisation  directe par pointeurs et bien meilleure. Elle permet, entre autres, d'optimiser l'exécutable final pour de meilleures performances. La manière d'appeler les fonctions est identique pour les deux cas.
Voilà   à bientôt.

signaler à un administrateur
Commentaire de racpp le 19/08/2004 18:20:13 administrateur CS

Désolé pour la petite faute  :)

Je viens d'y apprendre que l'utilisation  des ".LIB" et ".h" ne sert qu'à simplifier la programmation et que l'utilisation  directe par pointeurs est bien meilleure. Elle permet, entre autres, d'optimiser l'exécutable final pour de meilleures performances. La manière d'appeler les fonctions est identique pour les deux cas.

signaler à un administrateur
Commentaire de DeadlyPredator le 23/08/2004 23:36:35

je suis très septique racpp ... ça va prendre des tests.

signaler à un administrateur
Commentaire de BlackGoddess le 24/08/2004 09:48:33

euh ... dans le cas du liage statique, le nom des dll et de leurs fonctions utilisées sont écrit dans la section importation de l'executable. C'est l'OS qui charge toutes les dll décrites dans la section en meme temps que l'executable, et qui les décharge en meme temps que l'executable (sauf si elles sont partagées, et encore le partage ne marche que sur les NT je crois, bref). Si une dll ou une fonction exportée n'est pas trouvée, l'os met un message d'erreur et ne charge pas l'executable.
Dans le cas du liage dynamique, c'est a l'executable de charger lui meme ses DLLs (LoadLybraryA, FreeLibrary, GetProcAddress). c'est à lui de gérer si une dll n'est pas trouvé également.
A part qu'elle n'est pas chargée au meme moment, c'est la meme chose... (gardons a l'esprit également que kernel32.dll devra probablement etre liée statiquement : en effet, comment appeler LoadLibraryA pour utiliser kernel32.dll si c'est elle meme qui exporte la fonction ?

signaler à un administrateur
Commentaire de BlackGoddess le 24/08/2004 09:50:30

également, avec le liage dynamique, l'exportation directe de classes en C++ n'est pas possible.

signaler à un administrateur
Commentaire de DeadlyPredator le 25/08/2004 00:49:54

c'est possible BlackGoddess que les 2 méthodes aient la même vitesse. Mais alors pourquoi vb6 n'utilise pas une des 2 méthodes et qu'il appelle une api qui appelle une autre api?

signaler à un administrateur
Commentaire de BlackGoddess le 25/08/2004 17:04:33

lors d'une déclaration
declare sub ... alias ... lib ... ( ... )
vb6 ne fait rien. par contre au 1er appel d'une des fonctions, la dll est chargée dynamiquement (LoadLibrary) (par une des dll vb6, qui elle est liée statiquement à l'executable) puis la fonction executée. La dll est déchargée a la fin du processus.

signaler à un administrateur
Commentaire de racpp le 26/08/2004 01:45:03 administrateur CS

Salut,
Voici ce que dit Microsoft :
La plupart des applications utilisent la liaison implicite (statique) car c'est la méthode de liaison la plus simple à adopter. Cependant, la liaison explicite (dynamique) est parfois nécessaire. Voici quelques raisons courantes qui justifient l'utilisation de la liaison explicite :
1) L'application ne découvre le nom d'une DLL qu'elle doit charger qu'au moment de l'exécution. Par exemple, l'application peut avoir besoin d'obtenir le nom de la DLL et des fonctions exportées à partir d'un fichier de configuration.
2) Un processus utilisant la liaison implicite est interrompu par le système d'exploitation si la DLL est introuvable au moment du démarrage du processus. Un processus utilisant la liaison explicite n'est pas interrompu dans ce cas et peut essayer de récupérer à partir de l'erreur. Par exemple, le processus peut signaler l'erreur à l'utilisateur et lui demander de spécifier un autre chemin d'accès de la DLL.
3) Un processus qui utilise la liaison implicite est également interrompu si l'une des DLL auxquelles il est lié possède une fonction DllMain qui échoue. Un processus utilisant la liaison explicite n'est pas interrompu dans ce cas.
4) Une application liée de manière implicite à de nombreuses DLL peut être lente à démarrer car Windows charge toutes les DLL en même temps que l'application. Pour accélérer le démarrage, une application peut être liée de manière implicite aux DLL requises immédiatement après le chargement, puis attendre d'être liée explicitement aux autres DLL au fur et à mesure des besoins.
5) Avec la liaison explicite, il n'est plus nécessaire de lier l'application à une bibliothèque d'importation. Si les ordinaux d'exportation changent à la suite de modifications apportées à la DLL, les applications utilisant la liaison explicite n'ont pas besoin d'être rééditées (à supposer qu'elles appellent GetProcAddress à l'aide d'un nom de fonction et non avec une valeur ordinale), alors que les applications utilisant la liaison implicite doivent subir une réédition de leurs liens avec la nouvelle bibliothèque d'importation.

Ajouter un commentaire



Nos sponsors

Sondage...

CalendriCode

Octobre 2008
LMMJVSD
  12345
6789101112
13141516171819
20212223242526
2728293031  

Consulter la suite du CalendriCode



Développement réalisé par Nicolas SOREL (Nix) avec l'aide de : Cyril DURAND et Emmanuel BAÏSE, Merci à Vincent pour ses précieux conseils
CodeS-SourceS.com© Toute reproduction même partielle est interdite sauf accord écrit du Webmaster
CodeS-SourceS.com© est une marque déposée tous droits réservés
Temps d'éxécution de la page : 0,125 sec

Google Coop CodeS-SourceS Google Coop CodeS-SourceS


Certaines images présentes sur le site (notament certains avatars) sont issues des collections IconShock, donc si vous souhaitez utiliser ces icons vous devez les acheter, ne les copiez pas et ne utilisez pas dans vos sites et applications sans les avoir commandé.