Utilisation des DLL`s
Transcription
Utilisation des DLL`s
Traitement de données Création d’une DLL La DLL, à quoi ça sert ? Une DLL (Dynamic Link Library) est avant tout un programme exécutable sans fonction principale (pas de main). Elle regroupe un ensemble de fonctions que divers programmes peuvent se partager. Elle permet de modulariser un programme en blocs fonctionnels. Chaque bloc fonctionne en " boîte noire ", on connaît juste le nom de la fonction, son rôle bien évidemment, ses paramètres d'entrée et de sortie. Utiliser une DLL, cela permet de modifier et/ou corriger le driver sans avoir à toucher aux exécutables qui l'utilisent. Le principal avantage est qu'on peut dissocier lors de son développement la partie IHM (visible par l'opérateur) de la partie intelligente, et donc utiliser pour chaque partie un langage adapté : pour toute la partie graphique des outils spécialisés (Delphi, Visual Basic,...), et pour la partie intelligente du C ou de l'assembleur. On conserve la puissance et facilité de tous les langages possibles. Les DLL permettent au fabricant de matériel de fournir des drivers utilisables afin que l’utilisateur dispose d’un accès plus convivial à ce matériel. Autre avantage mais nettement moins important de nos jours, lors de l'utilisation de DLL, l'OS ne charge qu'une seule fois la partie de code en mémoire, seule la partie DATA est répliquée ; on gagne ainsi quelques (kilo)octets. Construction Avec Visual C++, on crée un nouveau projet de type DLL (donc File/New.../Win32 Dynamic-link library) que l'on va appeler DLL_test pour l'occasion. Ensuite il va falloir ajouter quelques fichiers essentiels à notre DLL : au moins un fichier C contenant le corps de notre projet un fichier RC contenant des informations de versions (facultatif, mais fortement conseillé pour être propre) un fichier DEF contenant les fonctions à exporter dans la DLL Le minimum La seule fonction obligatoire dans la DLL, qui est l'équivalent du MAIN dans un exécutable est DllEntryPoint, cette fonction est appelée au (dé)chargement de la DLL. Curieusement cette fonction n'a pas besoin d'être exportée ! Le code de retour indique si le chargement s'est bien fait ou pas. Ici, on retourne TRUE systématiquement. C'est à cet endroit du code que l'on peut faire quelques initialisations, mais personnellement je préfère faire une fonction xxx_init dédiée. Donc si on veut faire une DLL vide on fera, par exemple, pour DLL_Test.c : #include <windows.h> #Niedercorn LT « la Briquerie » 57100 THIONVILLE Programmation : les DLLs & page 1/3 BOOL WINAPI DllEntryPoint (HINSTANCE hInst, DWORD ul_reason_for_call, LPVOID lpReserved) { return TRUE; } La première fonction On va ajouter une fonction truc qui affiche une boite de dialogue à l'écran. Notre code devient alors : #include <windows.h> #define DllImport __declspec( dllimport ) #define DllExport __declspec( dllexport ) DllExport void pascal DLL_Test_Truc(void); BOOL WINAPI DllEntryPoint (HINSTANCE hInst, DWORD ul_reason_for_call, LPVOID lpReserved) { return TRUE; } DllExport void DLL_Test_Truc(void) { MessageBox(NULL, "coucou", "DLL Test", MB_OK); } Ensuite il faut préciser au compilateur que cette fonction va être exportée : ajouter DllExport avant la déclaration (ATTENTION : le terme DllExport est une redéfinition conseillé par Visual C++, se reporter à la doc du compilateur utilisé pour vérification) Et enfin il faut dire au linker aussi que cette fonction est exportée, on le fait dans le fichier DEF : DLL_Test.DEF LIBRARY DLL_Test CODE MOVEABLE DISCARDABLE DATA SINGLE EXPORTS DLL_Test_Truc Vous pouvez compiler ce mini projet et vérifier votre DLL grâce à Dependency Walker, un utilitaire fourni sur le CD du Resource Kit Microsoft Windows NT Server, qui vous permet de voir entre autre les fonctions exportées, les DLL et leurs fonctions appelées, la version de build.... Un utilitaire très utile pour le développeur. Le passage de paramètres Le seule chose importante à retenir est que la DLL n'est chargée qu'une seule fois en mémoire, mais qu'elle crée un nouveau segment de DATA à chaque chargement, donc on ne peut pas mélanger des données qui viendraient d'applications diverses. Tous les types de paramètres habituellement passables entre fonctions du même exécutable sont utilisables (void, int, pointeurs,....). La seule limite réside en fait dans la facilité de les utiliser dans les différents langages qui se présentent. Utilisation de la DLL Ce n'est pas le tout de savoir développer de belles DLL remplies de fonctions utiles, il faut aussi arriver à les appeler. L’utilisation d’une peut s’effectuer de plusieurs façons : Lors de la compilation : la DLL se trouve dans le répertoire de l’application, elle est indépendante du compilateur utilisé. Un fichier d’entête (*.h) permet de lister les fonctions incluses. Un fichier de librairie (*.lib) permet directement d’utiliser, lors de la compilation, tout ou partie des fonctions incluses dans la DLL. Attention, le fichier *.lib et le fichier *.h sont obtenus après compilation de la DLL et sont ainsi de ce fait adapté à un compilateur donné. Lors de l’exécution : la DLL ne se trouve pas obligatoirement dans le répertoire de l’application, elle est indépendante du compilateur utilisé. #Niedercorn LT « la Briquerie » 57100 THIONVILLE Programmation : les DLLs & page 2/3 Lors de l’exécution du programme, on charge, grâce à des API Windows, la DLL et peut alors utiliser les fonctions qui s’y trouvent. Un fichier d’entête (*.h) permet de lister les fonctions incluses. Dans le premier cas, par exemple pour Visual C++, il faut tout d'abord avoir le fichier .LIB généré lors de la compilation de la DLL. Ensuite il faut ajouter ce fichier .LIB, pour cela, allez dans "Projet/Settings", puis onglet "link" et renseignez le champs "Object/Library modules", Enfin, dans les sources C il faut déclarer les fonctions. Pour cela on reprend à peu près les mêmes déclarations que pour l'exportation, mais on remplace DLLExport par DLLImport. Le mieux est de mettre ça dans un fichier header (.H) qui sera inclut dans chaque source C. Dans notre cas on pourrait faire un DLL_Test.h qui ressemblerait à : #include <windows.h> #define DllImport __declspec( dllimport ) #define DllExport __declspec( dllexport ) DllImport void DLL_Test_Truc(void); Quand on repense un peu à cette méthode, c'est exactement ce que l'on fait lorsqu'on utilise les fonctions de l'API standard de Windows, simplement que tout ce travail est fait par défaut dans le compilateur. Bien sur ici le problème des types de variables est beaucoup plus simple à gérer ! Quelques règles à suivre Lors du développement et surtout de la maintenance (tant évolutive que corrective) d'une DLL, il faut se tenir à quelques règles simples qui éviteront un bon nombre de tracas. D'abord, il faut imposer que la DLL n'existe qu'à un seul exemplaire sur le disque, et pour cela il faut qu'elle soit dans un répertoire visible du PATH, moi je conseille \WINDOWS\SYSTEM(32). Ensuite, lors de l'évolution du code, il faut impérativement tenir compte de la compatibilité avec les anciennes versions de la DLL, ainsi tous les programmes existant continueront à fonctionner normalement et les nouveaux pourront profiter des nouveautés de la librairie. Il faut aussi tenir à jour la ressource version (dans le fichier .RC), en cas de doute sur une DLL c'est beaucoup plus fiable que sa taille ou sa date. Conclusion Voilà pour une présentation rapide d'une DLL. La seule difficulté est dans les déclarations et l'utilisation du fichier .DEF, mystérieusement sous documenté sur tous les nouveaux compilateurs. Une toute petite remarque pour finir, l'abus de DLL nuit à votre santé et à celle de votre micro, donc ne prenez pas la mauvaise habitude de faire plein de DLL pour un oui ou pour un non ; restez thématique dans leurs fonctions. #Niedercorn LT « la Briquerie » 57100 THIONVILLE Programmation : les DLLs & page 3/3