GERBELOTBARILLON.COM

Parce qu'il faut toujours un commencement...

Ecrire une DLL en C/C++ sous Windows

Utilisation de Microsoft Visual Studio Express

Le choix s’est naturellement porté sur l’IDE Visual Studio C++ Express 2010 pour des raisons évidentes : sous Windows on développe avec des outils Windows (tout comme on utilise les outils GNU sous Linux/Unix, et xCode/Objective-C sous Mac OS X). En plus, c’est un outil gratuit, certes limité à des logiciels non exploités commercialement, mais pour nous ce sera très bien.

Commencez par ouvrir Visual C++ et, dans l’assistant Nouveau Projet, choisissez Projet Win32.
Dans les paramètres de l’application, choisissez DLL comme type d’application et ne cochez pas les cases Projet Vide et Exporter des symboles (en d’autres termes, laissez les cases décochées).
L’assistant de VC++ se charge alors de créer une arborescence de fichiers sources et d’entêtes qui seront utilisés par le compilateur et le linkeur lors de la création de la DLL. Parmi tous ces fichiers, essentiellement réservés pour le système, se trouvent :

1. Dllmain.cpp, qui constitue le point d’entrée de la DLL une fois celle-ci compilée. Le rôle de la fonction DllMain est essentiellement de gérer le chargement ou le déchargement de la DLL dans la mémoire de Windows. Comme une DLL constitue une bibliothèque dynamique, elle peut-être chargée en mémoire lors de l’utilisation d’une de ses fonctions, et être déchargée de la mémoire une fois son travail terminé. La fonction DllMain() constitue alors le passage obligé pour que le système conserve sa stabilité lors de l’utilisation des DLLs. Il faut faire le parallèle avec les programmes écrits en C, où l’API Windows impose la présence d’une fonction WinMain(), qui est le point d’entrée du programme. Pour DllMain(), la seule chose à réellement avoir retenu est qu’elle doit renvoyer la valeur True.

2. Un fichier portant le nom de votre projet.cpp, dans lequel vous allez écrire vos fonctions. Toutes les fonctions devant pouvoir être exportées par la DLL doivent respecter la convention d’écriture suivante :

extern « C » __declspec(dllexport) <type de la fonction> <nom de la fonction>([<paramètres>])
Cette déclaration indique au compilateur de ne pas rajouter de décorations sur les noms de fonctions exportées. Si vous utilisez le compilateur C, nommez votre fichier avec une extension .c et déclarez vos fonctions exportées avec :
__declspec(dllexport) <type de la fonction> <nom de la fonction>([<paramètres>])
Une fois toutes vos fonctions définies, il suffit de lancer la compilation du projet. Si tout se passe sans erreur, vous devriez avoir un fichier pour chaque extension .exp, .lib et .dll dans les divers dossiers du projet. Si vous vous intéressez à l’usage des bibliothèques dynamiques (sinon vous ne liriez pas ce texte u_u), l’objet de vos désirs est le fichier .dll. Sinon, vous pouvez également écrire un programme qui utilisera la bibliothèque en mode statique. Mais il n’y a pas tellement d’intérêt (c’est mon avis perso)...

Exemple de DLL

Considérons le projet nommé SimpleDLL. Lors de sa création, en respectant ce qui a été écrit précédemment, vous avez un ensemble de fichiers présents dans l’arborescence dont le fichier SimpleDLL.cpp. C’est ce fichier qui va nous intéresser car il va contenir les déclarations de vos fonctions.

Supposons que notre DLL exporte des fonctions de somme et de produit de 2 entiers. Voici sa construction :

Fichier SimpleDLL.cpp

	      #define SIMPLEDLL_EXPORT extern "C" __declspec(dllexport)

	      SIMPLEDLL_EXPORT int Somme(int a, int b)
	      {
	         return(a + b);
	      }

	      SIMPLEDLL_EXPORT int Produit(int a, int b)
	      {
	         return(a * b);
	      }
	  
Il vous suffit ensuite de lancer la compilation du projet (touche F7 de l’IDE VC++) pour obtenir un fichier DLL.
Tester le fonctionnement de la DLL

Une fois la DLL compilée, ouvrez un nouveau projet Application console et saisissez le code ci-après :


	  #include <windows.h> // pour utiliser LoadLibrary() et FreeLibrary()
	  #include <stdio.h> // pour printf()


	  typedef int (__cdecl *FUNC)(int, int);


	  void main(int argc, char **argv)
	  {
	    HINSTANCE handle = LoadLibrary(TEXT(« SimpleDLL.dll »));
            if (handle != NULL)
            {
		FUNC fn = (FUNC)GetProcAddress(handle, « Somme »);
		printf(« Somme de 5 + 6 = %d\n », fn(5, 6));
		FreeLibrary(handle);
	    }
	    else
            {
		printf(« Erreur de chargement de la DLL\n »);
	    }
	  }
	  
Compilez votre source, copier le fichier DLL dans le chemin défini par Windows pour la recherche des DLLs et exécutez votre programme depuis une commande MS-DOS. Il devrait vous afficher le résultat du calcul demandé... Pour mémoire, le chemin de recherche d’une DLL comprend :

Compiler directement depuis MS-DOS

Si vous n’avez pas véritablement envie d’utiliser l’IDE Visual C++ pour réaliser vos projets de DLL, sachez que vous pouvez utiliser simplement le compilateur fourni qui se trouve dans le dossier VC\bin de votre installation. Il se nomme cl.exe, dispose de (trop) nombreuses fonctions que vous pouvez voir directement sur le MSDN de Microsoft et existe depuis Visual Studio 2005. Vous pouvez également lancer une invite de commande développeur depuis le menu de Visual Studio afin de créer un pseudo-environnement de compilation.

Sachez simplement qu’il suffit de lancer la commande cl.exe /LD SimpleDLL.cpp pour générer le fichier DLL suivant la syntaxe du C++. Si vous voulez vérifier que votre DLL a bien exporté les fonctions que vous attendez, vous pouvez utiliser la commande dumpbin /exports nom_de_la_dll.

Si vous utilisez MingW, créez la DLL par

gcc -shared -o nom_dll.dll nom_dll.c
. Pour vérifier que l'export s'est bien déroulé, utilisez l'utilitaire nm par
nm --extern-only --demangle nom_dll.dll | grep nom_de_la_fonction
.

© LGB - 201x+