Creation de DLL en C/C++


La création des librairies de liaison dynamique (DLL) est devenue indispensable en terme de conception logicielle. Les DLL permettent de partager une application en plusieurs modules fonctionnels qui sont chargés par un module exécutable (EXE). Une DLL exporte des symboles : des variables, des fonctions ou des classes C++. L'exportation ou l'importation d'un symbole se fait via les directives de compilation suivante:
__declspec(dllexport)
__declspec(dllimport)
Une DLL contient le même code qu'un exécutable mais les déclarations des symboles sont préfixées par une directive d'exportation. 
Le programme qui va utiliser les symboles de cette DLL doit utiliser les déclarations des symboles qui sont préfixées par une directive d'importation. 
Pour éviter d'avoir deux définitions de symboles, on utilise une constante qui prend soit une valeur soit l'autre suivant que c'est la DLL ou l'EXE qui est construit. Ce fichier contient la liste des symboles exportés.
// Fichier CustomDll.h
#ifdef CUSTOMDLL_EXPORTS
#define CUSTOMDLL_API __declspec(dllexport)
#else
#define CUSTOMDLL_API __declspec(dllimport)
#endif

CUSTOMDLL_API BOOL GetFileTimeByName( LPCSTR lpszFile, FILETIME * ftCreate, FILETIME * ftAccess, FILETIME * ftWrite );
Le code de la DLL déclare la constante CUSTOMDLL_EXPORTS et fait un #include du fichier.
Le code d'un EXE ne fait qu'inclure le fichier, ainsi la constante CUSTOMDLL_API vaut le valeur d'importation (__declspec(dllimport)).

Chaque DLL possède un point d'entrée ; c'est la fonction DllMain:
BOOL WINAPI DllMain(
  HINSTANCE hinstDLL,  // handle to the DLL module
  DWORD fdwReason,     // reason for calling function
  LPVOID lpvReserved   // reserved
);

Il existe quatre raisons pour lesquels cette fonction est appelée:
- DLL_PROCESS_ATTACH : le processus (EXE) courant  charge la DLL
- DLL_THREAD_ATTACH : le processus courant créé un nouveau thread
- DLL_THREAD_DETACH : un thread du processus courant se termine
- DLL_PROCESS_DETACH : la DLL est déchargée du processus courant

La compilation du projet de la DLL produit un fichier DLL et un fichier d'extension LIB. Pour charger de manière statique une DLL, le programme client peut ajouter ce fichier d'extension LIB à son projet ou bien utiliser la directive de compilation suivante: 

#pragma comment(lib, "..\\CustomDll\\Release\\CustomDll.lib")
Voici le code de l'application client :
// TestCustomDll.cpp : Defines the entry point for the console application.
//

#include <stdio.h>
#include <windows.h>
#include "..\\CustomDll\\CustomDll.h"
#pragma comment(lib, "..\\CustomDll\\Release\\CustomDll.lib")

int main(int argc, char* argv[])
{
	FILETIME ftCreate, ftAccess, ftWrite;
	SYSTEMTIME stCreate, stAccess, stWrite;
	char szWinDir[255];
	char szFile[255];

	GetWindowsDirectory(szWinDir, 255);
	sprintf(szFile, "%s\\EXPLORER.EXE", szWinDir);

	if( GetFileTimeByName( szFile, &ftCreate, &ftAccess, &ftWrite) == FALSE )
	{
		printf("GetFileTimeFrom failed\n");
		return 0;
	}

	FileTimeToSystemTime(&ftCreate, &stCreate);
	FileTimeToSystemTime(&ftAccess, &stAccess);
	FileTimeToSystemTime(&ftWrite, &stWrite);
	printf("File:%s, Create DateTime:%02d/%02d/%02d %02d:%02d:%02d\n", szFile, (int)stCreate.wDay, (int)stCreate.wMonth, (int)stCreate.wYear, (int)stCreate.wHour, (int)stCreate.wMinute, (int)stCreate.wSecond);
	printf("File:%s, Access DateTime:%02d/%02d/%02d %02d:%02d:%02d\n", szFile, (int)stAccess.wDay, (int)stAccess.wMonth, (int)stAccess.wYear, (int)stAccess.wHour, (int)stAccess.wMinute, (int)stAccess.wSecond);
	printf("File:%s, Write DateTime:%02d/%02d/%02d %02d:%02d:%02d\n", szFile, (int)stWrite.wDay, (int)stWrite.wMonth, (int)stWrite.wYear, (int)stWrite.wHour, (int)stWrite.wMinute, (int)stWrite.wSecond);
	return 0;
}
Lorsque la DLL est construite pour être utilisée aussi bien en C qu'en C++, il faut que la déclaration des symboles soit encapsulées dans un bloc extern "C" décrit comme suit:
// Fichier CustomDll.h
#ifdef CUSTOMDLL_EXPORTS
#define CUSTOMDLL_API __declspec(dllexport)
#else
#define CUSTOMDLL_API __declspec(dllimport)
#endif

#ifdef __cplusplus
extern "C" 
{
	CUSTOMDLL_API BOOL GetFileTimeByName( LPCSTR lpszFile, FILETIME * ftCreate, FILETIME * ftAccess, FILETIME * ftWrite );
}
#else
	CUSTOMDLL_API BOOL GetFileTimeByName( LPCSTR lpszFile, FILETIME * ftCreate, FILETIME * ftAccess, FILETIME * ftWrite );
#endif

 
© 2001 Christophe Pichaud. All rights reserved.