UTILISER LES APIS DE DLLS SANS AVOIR LE FICHIER .LIB
cs_aardman
Messages postés1905Date d'inscriptionmercredi 22 janvier 2003StatutMembreDernière intervention17 septembre 2012
-
15 août 2004 à 04:27
racpp
Messages postés1909Date d'inscriptionvendredi 18 juin 2004StatutModérateurDernière intervention14 novembre 2014
-
26 août 2004 à 01:45
Cette discussion concerne un article du site. Pour la consulter dans son contexte d'origine, cliquez sur le lien ci-dessous.
racpp
Messages postés1909Date d'inscriptionvendredi 18 juin 2004StatutModérateurDernière intervention14 novembre 201417 26 août 2004 à 01:45
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.
BlackGoddess
Messages postés338Date d'inscriptionjeudi 22 août 2002StatutMembreDernière intervention14 juin 2005 25 août 2004 à 17:04
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.
DeadlyPredator
Messages postés222Date d'inscriptionjeudi 15 janvier 2004StatutMembreDernière intervention30 juin 2008 25 août 2004 à 00:49
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?
BlackGoddess
Messages postés338Date d'inscriptionjeudi 22 août 2002StatutMembreDernière intervention14 juin 2005 24 août 2004 à 09:50
également, avec le liage dynamique, l'exportation directe de classes en C++ n'est pas possible.
BlackGoddess
Messages postés338Date d'inscriptionjeudi 22 août 2002StatutMembreDernière intervention14 juin 2005 24 août 2004 à 09:48
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 ?
DeadlyPredator
Messages postés222Date d'inscriptionjeudi 15 janvier 2004StatutMembreDernière intervention30 juin 2008 23 août 2004 à 23:36
je suis très septique racpp ... ça va prendre des tests.
racpp
Messages postés1909Date d'inscriptionvendredi 18 juin 2004StatutModérateurDernière intervention14 novembre 201417 19 août 2004 à 18:20
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.
racpp
Messages postés1909Date d'inscriptionvendredi 18 juin 2004StatutModérateurDernière intervention14 novembre 201417 19 août 2004 à 18:14
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:
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.
DeadlyPredator
Messages postés222Date d'inscriptionjeudi 15 janvier 2004StatutMembreDernière intervention30 juin 2008 18 août 2004 à 21:33
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.
racpp
Messages postés1909Date d'inscriptionvendredi 18 juin 2004StatutModérateurDernière intervention14 novembre 201417 17 août 2004 à 20:43
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.
BruNews
Messages postés21040Date d'inscriptionjeudi 23 janvier 2003StatutModérateurDernière intervention21 août 2019 17 août 2004 à 16:29
le 1er index d'un tableau est elem[0] et non 1.
BlackGoddess
Messages postés338Date d'inscriptionjeudi 22 août 2002StatutMembreDernière intervention14 juin 2005 17 août 2004 à 16:27
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.
racpp
Messages postés1909Date d'inscriptionvendredi 18 juin 2004StatutModérateurDernière intervention14 novembre 201417 17 août 2004 à 16:09
Salut,
J'avoue ne pas avoir encore bien compris. Donne un exemple précis illustrant le problème à résoudre.
A bientôt.
BruNews
Messages postés21040Date d'inscriptionjeudi 23 janvier 2003StatutModérateurDernière intervention21 août 2019 17 août 2004 à 10:17
Je n'indiquais que le mode d'acces aux tableaux en partant de base ZERO, rien d'autre.
DeadlyPredator
Messages postés222Date d'inscriptionjeudi 15 janvier 2004StatutMembreDernière intervention30 juin 2008 17 août 2004 à 06:42
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 :
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+
BruNews
Messages postés21040Date d'inscriptionjeudi 23 janvier 2003StatutModérateurDernière intervention21 août 2019 16 août 2004 à 20:06
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...
DeadlyPredator
Messages postés222Date d'inscriptionjeudi 15 janvier 2004StatutMembreDernière intervention30 juin 2008 16 août 2004 à 19:44
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...
BruNews
Messages postés21040Date d'inscriptionjeudi 23 janvier 2003StatutModérateurDernière intervention21 août 2019 16 août 2004 à 10:28
Fais toujours commencer tes tableaux a 0.
BlackGoddess
Messages postés338Date d'inscriptionjeudi 22 août 2002StatutMembreDernière intervention14 juin 2005 16 août 2004 à 10:20
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 ...
DeadlyPredator
Messages postés222Date d'inscriptionjeudi 15 janvier 2004StatutMembreDernière intervention30 juin 2008 16 août 2004 à 06:28
Pour en revenir à ma question, pourrait-on déclarer le type de l'API durant l'Éxécution du code?
DeadlyPredator
Messages postés222Date d'inscriptionjeudi 15 janvier 2004StatutMembreDernière intervention30 juin 2008 15 août 2004 à 22:18
DÉSOLÉ je n'y avais pas penser (je suis bête) j'arrange ça tout de suite
racpp
Messages postés1909Date d'inscriptionvendredi 18 juin 2004StatutModérateurDernière intervention14 novembre 201417 15 août 2004 à 16:24
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+
BruNews
Messages postés21040Date d'inscriptionjeudi 23 janvier 2003StatutModérateurDernière intervention21 août 2019 15 août 2004 à 11:24
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.
DeadlyPredator
Messages postés222Date d'inscriptionjeudi 15 janvier 2004StatutMembreDernière intervention30 juin 2008 15 août 2004 à 06:09
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)?
DeadlyPredator
Messages postés222Date d'inscriptionjeudi 15 janvier 2004StatutMembreDernière intervention30 juin 2008 15 août 2004 à 06:04
OUPS!! désolé, faute de frappe : corrigé
cs_aardman
Messages postés1905Date d'inscriptionmercredi 22 janvier 2003StatutMembreDernière intervention17 septembre 20123 15 août 2004 à 04:27
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..
26 août 2004 à 01:45
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.
25 août 2004 à 17:04
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.
25 août 2004 à 00:49
24 août 2004 à 09:50
24 août 2004 à 09:48
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 ?
23 août 2004 à 23:36
19 août 2004 à 18:20
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.
19 août 2004 à 18:14
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.
18 août 2004 à 21:33
17 août 2004 à 20:43
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.
17 août 2004 à 16:29
17 août 2004 à 16:27
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.
17 août 2004 à 16:09
J'avoue ne pas avoir encore bien compris. Donne un exemple précis illustrant le problème à résoudre.
A bientôt.
17 août 2004 à 10:17
17 août 2004 à 06:42
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.
17 août 2004 à 04:13
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+
16 août 2004 à 20:06
#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...
16 août 2004 à 19:44
16 août 2004 à 10:28
16 août 2004 à 10:20
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 ...
16 août 2004 à 06:28
15 août 2004 à 22:18
15 août 2004 à 16:24
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+
15 août 2004 à 11:24
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.
15 août 2004 à 06:09
15 août 2004 à 06:04
15 août 2004 à 04:27
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..