Texel
Win32 | Index | Contact

Les bases de la programmation Windows

Chapitre 4:
Les ressources



Les programmes utilisent très souvent des fichiers bitmaps, audio, ou autres. Plutôt que de rassembler des centaines de ces fichiers dans un répertoire, il est plus pratique de les inclure à l'intérieur de l'exécutable ou de les rassembler dans un fichier unique (Exemple dans un jeu : un fichier par niveaux). Ces fichiers sont appelés "ressources". Les ressources ne rassemblent pas uniquement des fichiers. Ils peuvent aussi servir à créer des menus (grâce aux scriptes de ressources) ou stocker des fonctions utilisables par plusieurs applications ( grâce aux DLLs). Nous n'expliquerons pas ici comment intégrer des fonctions dans des dLLs



I. Inclure des ressources dans un exécutable

Pour intégrer une ressource dans un exécutable, vous devez écrire un script de ressources. Il s'agit simplement d'un fichier texte avec l'extension ".rc" contenant un code très simple qui liste toutes vos ressources. A la compilation, les fichiers indiqués dans le script seront stockés dans l'exécutable. Pendant l'exécution du programme les ressources ne seront heureusement pas automatiquement chargées en mémoire. Vous devez pour cela utiliser des fonctions du genre LoadIcon, LoadCursor, LoadBitmap, LoadImage ou PlaySound


1) Icônes, curseurs, bitmaps, son wave

Voici le contenu complet d'un script pour intégrer dans un exécutable un curseur, un icône personnalisé, ainsi qu'une image bitmap et un son wave:

#include<windows.h>
	
ICON_ICON	ICON	"icon.ico"
CURSOR_CURSEUR	CURSOR	"curseur.cur"
IMG_BITMAP	BITMAP 	"image.bmp" 
SONG		WAVE	"song.wav"








Pour chaque fichier à stocker, une seule ligne suffit dans le script. Vous y inscrivez dans l'ordre:
_ Un identifiant pour représenter votre fichier.
_ Le type de votre ressource.
_ Le nom du fichier qui sera inclus dans l'exécutable.

Vous pouvez choisir d'identifier votre ressource par le nombre ou la chaîne de caractère de votre choix. Le plus simple est d'utiliser une chaîne de caractère. Dans ce cas, avec votre compilateur, vous incluez simplement le fichier ".rc" dans votre projet. Puis lorsque vous charger votre ressource, vous mettez l'identifiant entre guillemets à la place du nom du fichier. Exemple pour un bitmap:


hbitmap=(HBITMAP)LoadImage(
	hInstance,	
	"IMG_BITMAP",
	IMAGE_BITMAP,
	0,0,
	NULL
);









Contrairement à l'exemple du chapitre précédent, le premier argument n'est pas NULL mais a été remplacé par l'handle de l'instance de l'application puisque la ressource est dans notre application. Pensez-y aussi pour LoadIcon, LoadCursor, LoadBitmap … Nous n'avons plus besoin ici de la constante LR_LOADFROMFILE puisque ce n'est pas un fichier que nous chargeons.

Si vous utilisez un nombre comme identifiant, vous devez ensuite le transformer en chaîne de caractère car les fonctions qui chargent vos ressources ne prennent en argument que des chaînes de caractères. Utilisez pour cela la macro MAKEINTRESOURCE qui prend en argument l'identifiant:

Dans le .rc :

101		BITMAP 	"image.bmp"



Dans le .c :

… LoadImage(… , MAKEINTRESOURCE (101), …);	




Si vous aimez la complication, vous pouvez aussi utiliser un #define pour remplacez votre nombre par une constante de type chaîne de caractère. Celui-ci doit alors être inscrit dans un fichier d'en-tête d'extension ".h" et inclus dans votre ".rc" et votre ".c". Bien sûr la macro MAKEINTRESOURCE est toujours indispensable (avec le nom de l'identifiant à la place d'un nombre).

Pour les icônes, les curseurs, les images bitmaps et les sons waves, les types de ces ressources sont respectivement ICON, CURSOR, BITMAP et WAVE. Les noms de ces types sont imposés, contrairement aux type des ressources personnalisées. Le type de la ressource est souvent suivit du mot DISCARABLE. Cet attribut indique que la ressource peut être libérer de la mémoire par Windows pour économiser de l'espace mémoire (elle sera dans ce cas automatiquement recharger dès que ce sera nécessaire). Cet attribut est inutile puisqu'il s'agit d'une valeur par défaut. Notez qu'avec le compilateur Visual C++ ( et peut-être aussi Borland C++ Builder) les scriptes de ressource peuvent être écrit pour vous par le compilateur (on les crée par l'interface). La meilleure façon de débuter est de les écrire soi même avec un éditeur de texte, puis de les importer dans votre projet.



2) Les tables de chaînes

Vous pouvez mettre des chaînes de caractères en ressources. Ceci peut par exemple vous faciliter la vie lorsque vous faite un programme multi-langue. Les chaînes de caractères sont alors écrites dans les fichiers .rc, dans ce qu'on appel une table de chaîne.
Voici un exemple de scripte contenant une table de chaîne:

STRINGTABLE
BEGIN
1	 "premier texte"
2	 "deuxième texte"
END







C'est très simple. Vous commencer par inscrire STRINGTABLE pour indiquer que ce qui suit est une table de chaîne. BEGIN et END annoncent le début et la fin de l'inscription de toutes vos chaînes de caractères. Chaque chaîne est identifié par un nombre et vous n'avez pas besoin d'utiliser MAKEINTRESOURCE. Mais vous pouvez utiliser des #define dans un fichier d'en-tête si vous préférez donner des noms à vos ressources.

Chaques chaînes doivent être écrite sur la même ligne. Elles ne doivent pas dépasser 4097 caractères. Tout comme pour la fonction printf, les opérateurs \n, \t, %d, %f ... sont valable pour mettre en forme le texte ou afficher des variables.

Pour charger le texte, il suffit d'utiliser la fonction LoadString:

int LoadString(
HINSTANCE hInstance,	// handle de l'application  
UINT uID, 		// identificateur de la ressource
LPTSTR lpBuffer,	// pointer vers un buffer qui reçoit le texte
int nBufferMax 		// nbr max de caractère à transférer au buffer
); 








Si vous voulez afficher des variables, pensez à envoyer le buffer à des fonctions tell que sprintf pour le compléter.


3) Les ressources personnalisées

Jusqu'ici, nous avons parler de type de fichiers spécifiques tel que les bitmaps, les icônes, les sons waves… Ca devrait vous suffire pour la majorité de vos applications. Mais vous pouvez mettre en ressource n'importe quel fichier, quelle que soit son extension. Pour ce faire, il vous suffit d'inventer le nom d'un type et de l'inscrire dans votre scripte de ressource exactement comme pour les bitmaps.

Pour utiliser une ressource, il vous faut obtenir l'handle de celle-ci. Vous devez utiliser deux fonctions. La première retourne un handle vers "un block d'information spécifique à la ressource" (J'espère que j'ai bien traduit).

HRSRC FindResource(
HMODULE hModule,	// handle de ce qui contient la ressource
LPCTSTR lpName,		// pointer vers l'identifiant de la ressource
LPCTSTR lpType 		// pointer vers le type de la ressource 
);







Ensuite, il faut passer en argument cette handle à la fonction ci-dessous pour obtenir l'handle de la ressource qui sera de type global:

HGLOBAL LoadResource(
HMODULE hModule,	// handle de ce qui contient la ressource
HRSRC hResInfo 		// handle de FindResource
);







Avoir un handle c'est bien. Avoir un pointeur vers le premier octet de la ressource, c'est mieux. C'est justement ce que fait cette dernière fonction:

LPVOID LockResource(HGLOBAL hResData );




L'argument est l'handle de la ressource obtenue grâce à LoadResource. Le pointeur retourné est de type VOID. Forcez son type suivant vos besoins.



II. Les bibliothèques de ressources.

Vous pouvez mettre un ensemble de ressource dans un fichier unique et séparé de votre programme. Ce type de fichier est appelé DLLs ( dynamic-link library). Les fichiers DLL sont des bibliothèques qui peuvent contenir aussi bien des fonctions que des ressources. Elles sont indépendantes des programmes. N'importe qu'elle application peut utiliser les fonctions et ressources d'une DLL créer à l'origine pour une autre application (à condition de savoir ce qu'elles contiennent). Windows utilise lui-même un très grand nombre de DLLs.

Nous allons seulement parler ici des bibliothèques de ressources. Elles sont super simple à créer et utiliser.

1) Créer un DLL de ressource

Les DLLs doivent être compilés ("comme" un exécutable). Sous Visual, il suffit de créer un projet de type "Win32 Dinamic-Link Library" au lieu de "Win32 application". Il doit y avoir l'équivalent sous Borland et DevCpp.
Le projet d'une DLL de ressource est constitué de deux fichiers. Le premier est un scripte de ressource d'extension ".rc". Il s'écrit exactement de la même manière que pour les ressources d'exécutables. Le deuxième fichier est un ".c" avec un code très cours et toujours identique. Voici l'intégralité du code de ce fichier:


#include<windows.h>

int WINAPI DllMain (HINSTANCE,DWORD fdwReason,PVOID pvReserved)
{
return TRUE;
}








Le premier paramètre est l'handle d'instance de la DLL (Comme pour WinMain). Le deuxième paramètre indique la raison pour laquelle la DLL a été appelée. Pour le dernier paramètre, je n'est trouvé aucune documentation. De toute façon, vous n'avez pas besoin de savoir à quoi servent ces paramètres pour débuter. Vous devez faire retourner à la fonction DllMain une valeur différente de zéro pour indiquer que l'initialisation s'est bien passée.

Il ne vous reste plus qu'à compiler. L'extension d'une DLL est ".DLL" par défaut, mais vous pouvez leur donner l'extension que vous voulez.


2) Utiliser une DLL de ressource

Pour utiliser les ressources d'une DLL dans un programme, il faut la charger en mémoire et obtenir un handle de celle-ci:

HINSTANCE LoadLibrary(LPCTSTR lpLibFileName );	




L'unique argument de cette fonction est le nom de votre fichier DLL (entre guillemets). Vous affectez l'handle qu'elle retourne à un handle que vous avez préalablement déclarer et de type HINSTANCE. Vous pouvez par exemple appeler cette fonction dans la procédure de fenêtre lors du message WM_CREATE.

Ensuite pour utiliser les ressources de la DLL, vous procédez de la même façon que pour les ressources d'exécutables, sauf que vous passez l'handle de la DLL au lieu de celui de l'application au fonctions LoadImage, LoadBitmap, LoadIcon, LoadCursor

Enfin, pour libérer la bibliothèque avant de quitter le programme, il faut appeler la fonction FreeLibrary:


BOOL FreeLibrary(HMODULE hLibModule);




L'argument est l'handle de la DLL à libérer. Cette fonction peut être appeler lors du traitement du message WM_DESTROY.

A l'exécution du programme, si la DLL ne se trouve pas dans le même répertoire que l'exécutable, son emplacement sera automatiquement recherché (dans cet ordre) dans les dossiers suivant:
le dossier courant,
le dossier système de Windows
ou le dossier de Windows (où est l'OS).


Conclusion

Vous pouvez télécharger le code source correspondant à ce chapitre ici. Vous venez de finir le dernier chapitre de ce tutorial sur les bases de l'API WIN32. Il est loin d'être complet, mais j'espère néanmoins qu'il est assez claire et qu'il vous permettra de débuter facilement dans la programmation sous Windows. Si vous trouvez des erreurs ou si certaines partie ne sont pas assez claire, envoyez moi un e-mail.




Version originale: Fevrier 2001
Dernière mise à jour: Juin 2002
Par Grégory Smialek
www.texel.fr.fm