Introduction :
Dans ce chapitre nous allons aborder les ressources.
Tout d'abord qu'appelle t'on les ressources dans un programme Windows ?
Ce sont des données de toutes natures qui sont incorporées à l'exécutable.
Cela peut être des images, des icônes, des chaînes de caractères,
des blocs de données de toutes sortes...
Mais aussi des boîtes de dialogue et des menus qui seront construits à partir de
script.
Incorporation des ressources à l'exécutable :
L'incorporation des ressources à l'exécutable est réalisée par le linker.
Nous devons lui fournir un fichier contenant les ressources. Ces fichiers
ont l'extension ".res". Ils sont compilés par un compilateur de ressources,
auquel on fourni un fichier de script de ressources.
Les fichiers de script de ressources sont des fichiers texte ayant l'extension ".rc".
Il existe aussi des éditeurs de ressources permettant de créer visuellement les
fichiers ressources.
Ce tutoriel n'étant pas déstiné à des outils particuliés, nous utiliserons uniquement
les scripts de ressources dans les exemples qui vont suivre.
Si vous utilisez un EDI, il suffit d'ajouter les scripts de ressources
au projet (pour Dev-C++ il faut les ajouter par le menu "Projects" puis "Projects Options"
puis par le bouton "Load Resource", pour C++ Builder il faut tout simplement
ajouter les fichiers au projet).
La compilation des ressources sera dans ce cas transparente.
Nous allons en voir deux dans ce chapitre : les icônes et les menus.
Le script de ressources :
Nous allons rependre l'exemple du chapitre 3 - Les commandes et notifications.
Nous allons d'abord ajouter des icônes dans les ressources de l'exécutable.
Créons tout d'abord un fichier script de ressources nommé "resource.rc" avec
le texte suivant :
1 ICON icone.ico
2 ICON autre.ico
Chaque ligne est composée de trois éléments, le premier est un identificateur,
le second le type de ressource (ICON pour une icône dans l'exemple) le dernier
est le contenu, dans le cas de l'exemple ce sera le contenu d'un fichier.
Ils doivent bien sur exister.
Les ayant mit dans le même dossier que les fichiers sources du programme,
je n'ai pas indiqué le chemin pour les localiser.
S'ils se trouvent ailleurs il faudra bien sur mettre
le chemin (j'ai du tout de même le mettre pour la compilation avec Dev-C++ malgré
qu'ils se trouvaient dans le même dossier).
Certain compilateur de resources demande d'inclure les fichiers entêtes de Windows
qui contiennent la définition des identificateurs utilisés dans le
fichier script de ressources.
Il peux y avoir des différences notables entre les différents compilateurs de ressources.
Je vous invite à consulter l'aide de celui que vous utilisez si vous rencontrez des problèmes
de compilation.
Je le répète une dernière fois, ce fichier devra être ajouté au projet si vous
possédez un EDI ou compilé à l'aide d'un compilateur de ressources et lié à l'exécutable
si vous n'avez pas d'EDI.
A ce stade, si vous compilez et que vous visualisez le fichier exécutable
dans l'explorateur de fichiers, il devrait avoir la première icône
de la liste (ce n'est pas forcement vrai si vous utilisez un EDI,
ils peuvent ajouter automatiquement une icône aux ressources de l'exécutable).
Nous allons maintenant mettre la deuxième icône comme icône de la fenêtre
(celle qui ce trouve à gauche sur la barre de titre) :
int WINAPI WinMain(HINSTANCE hinstance, HINSTANCE hPrevInstance,
LPSTR lpCmdLine, int nCmdShow)
{
WNDCLASS wc;
wc.style = 0 ;
wc.lpfnWndProc = MainWndProc;
wc.cbClsExtra = 0;
wc.cbWndExtra = 0;
wc.hInstance = NULL;
wc.hIcon = LoadIcon(hinstance,MAKEINTRESOURCE(2));
wc.hCursor = LoadCursor(NULL, IDC_ARROW);
wc.hbrBackground = NULL;
wc.lpszMenuName = "MyMenu";
wc.lpszClassName = "MaWinClass";
if(!RegisterClass(&wc)) return FALSE;
Pour cela on affecte le champ hIcon de la classe de fenêtre avec une icône
que l'on charge avec la fonction LoadIcon. Elle reçoit comme paramètre le handle
d'instance de l'application, pour son second paramètre elle attend un pointeur
sur une chaîne de caractères identifiant l'icône. Dans le script de ressource
nous l'avons identifié avec une valeur numérique. C'est avec la macro MAKEINTRESOURCE
que nous faions cette conversion.
Nous aurions pu les identifier avec des valeurs littérales :
ICONE1 ICON icone.ico
ICONE2 ICON autre.ico
Dans ce cas LoadIcon aurait reçu un pointeur sur
la chaîne de caractère identifiant l'icône dans son second paramètre.
wc.hIcon = LoadIcon(hinstance,"ICONE2");
Ressources de type menu :
Toujours avec le même exemple, nous allons supprimer le menu que l'on avait créé
par programme au chapitre 3 et le créer avec un script de ressources :
LEMENU MENU
BEGIN
POPUP "Fichier"
BEGIN
MENUITEM "&Nouveau", IDM_NEW
MENUITEM SEPARATOR
MENUITEM "&Quitter", IDM_QUIT
END
POPUP "Aide"
BEGIN
MENUITEM "A propos...", IDM_ABOUT
END
END
Nous retrouvons là aussi nos trois éléments : l'identificateur de la ressource
(littérale dans cet exemple),
suivi de son type (MENU : barre de menu) puis de son contenu.
Ici le contenu n'est pas un fichier, mais
un script de construction de barre de menu, balisé par les mots BEGIN et END.
Elle possède deux sous menu (POPUP : menu déroulant) "fichier" et "Aide".
Le premier possède trois options (MENUITEM) "&Nouveau", SEPARATOR et "&Quitter".
La liste des options d'un sous menu est aussi balisée par les mots
BEGIN et END. Chaque option doit être identifiée par une constante numérique.
Cette constante sera envoyée à la procédure de fenêtre quand l'option du menu sera
actionnée. Il est préférable de les nommer pour plus de clarté dans le code.
Comme elles devront être connues du script de ressource et du fichier source du programme,
nous les définissons dans un fichier entête séparé, nommé dans cet exemple "resource.h" :
#define IDM_QUIT 1
#define IDM_NEW 2
#define IDM_ABOUT 3
Ce fichier devra donc être inclut dans le fichier source et dans le fichier
script de ressource (voir code à la fin de ce document).
Le menu sera ensuite affecté à la classe de fenêtre.
Ceci en affectant au champ lpszMenuName de la classe de fenêtre,
un pointeur sur une chaîne de caractères contenant le nom
litéral du menu :
wc.lpszMenuName = "LEMENU";
C'est tout pour sa construction.
La récupération des actions du menu ce fait sur réception
des messages WM_COMMAND de la même manière
qu'au chapitre 3 :
switch (uMsg)
{
case WM_COMMAND:
if(LOWORD(wParam) == IDM_QUIT) PostMessage(hwnd, WM_CLOSE,0,0);
/*... */
Vous avez du remarquer que j'ai mis le caractère '&' dans le texte des options de menu
"&Nouveau" et "&Quitter". Ce signe sert à souligner le caractère qui le suit et
à donner la possibilité d'activer l'option correspondante avec la touche du clavier
correspondant à ce caractère. Ceci uniquement quand le popup menu est ouvert.
A ne pas confondre avec les accélérateurs clavier que l'on verra lors d'un chapitre
ultérieur.
Nous aurons l'occasion de revenir sur les différents types de ressources au cours
des chapitres suivants.
Code complet :
resource.h :
#define IDM_QUIT 1
#define IDM_NEW 2
#define IDM_ABOUT 3
resource.rc :
#include <windows.h>
#include "resource.h"
1 ICON icone.ico
2 ICON autre.ico
LEMENU MENU
BEGIN
POPUP "Fichier"
BEGIN
MENUITEM "&Nouveau", IDM_NEW
MENUITEM SEPARATOR
MENUITEM "&Quitter", IDM_QUIT
END
POPUP "Aide"
BEGIN
MENUITEM "A propos...", IDM_ABOUT
END
END
winmain.c :
#include <windows.h>
#include "resource.h"
HINSTANCE hinst;
LRESULT CALLBACK MainWndProc(HWND, UINT, WPARAM, LPARAM);
int WINAPI WinMain(HINSTANCE hinstance, HINSTANCE hPrevInstance,
LPSTR lpCmdLine, int nCmdShow)
{
HWND hwnd;
MSG msg;
WNDCLASS wc;
hinst = hinstance;
wc.style = 0 ;
wc.lpfnWndProc = MainWndProc;
wc.cbClsExtra = 0;
wc.cbWndExtra = 0;
wc.hInstance = hinstance;
wc.hIcon = LoadIcon(hinstance,MAKEINTRESOURCE(2));
wc.hCursor = LoadCursor(NULL, IDC_ARROW);
wc.hbrBackground = NULL;
wc.lpszMenuName = "LEMENU";
wc.lpszClassName = "MaWinClass";
if(!RegisterClass(&wc)) return FALSE;
hwnd = CreateWindow("MaWinClass", "Titre", WS_OVERLAPPEDWINDOW,
CW_USEDEFAULT, CW_USEDEFAULT, 400, 300,
NULL, NULL, hinstance, NULL);
if (!hwnd) return FALSE;
ShowWindow(hwnd, nCmdShow);
while (GetMessage(&msg, NULL, 0, 0))
{
TranslateMessage(&msg);
DispatchMessage(&msg);
}
return msg.wParam;
}
/******************************************************************************/
LRESULT CALLBACK MainWndProc(HWND hwnd, UINT uMsg, WPARAM wParam, LPARAM lParam)
{
static HWND hEdit;
static BOOL EditNotChg = TRUE;
switch (uMsg)
{
case WM_CREATE:
{
HFONT hFont;
hEdit = CreateWindow("edit", "Texte",
WS_CHILD | WS_VISIBLE | ES_MULTILINE | ES_WANTRETURN | WS_VSCROLL,
0, 0, 0, 0, hwnd, NULL, hinst, NULL);
hFont = (HFONT)GetStockObject(ANSI_FIXED_FONT);
SendMessage(hEdit,WM_SETFONT,(UINT)hFont,TRUE);
SendMessage(hEdit, EM_SETMARGINS, EC_LEFTMARGIN | EC_RIGHTMARGIN,
MAKELONG(5, 5));
return 0;
}
case WM_CLOSE:
if(EditNotChg ||
MessageBox(hwnd,"Le texte a été modifié.\r\nEtes vous sûr de \
vouloir fermer l'application ?"
,"Question ?",MB_YESNO | MB_ICONQUESTION ) == IDYES)
DestroyWindow(hwnd);
return 0;
case WM_COMMAND:
if(LOWORD(wParam) == IDM_QUIT) PostMessage(hwnd, WM_CLOSE,0,0);
if(LOWORD(wParam) == IDM_NEW)
if(EditNotChg ||
MessageBox(hwnd,"Le texte a été modifié.\r\nEtes vous sûr de \
vouloir fermer votre travail ?"
,"Question ?",MB_YESNO | MB_ICONQUESTION ) == IDYES)
{
SendMessage(hEdit,WM_SETTEXT,0,(long)"");
EditNotChg = TRUE;
}
if(LOWORD(wParam) == IDM_ABOUT)
MessageBox(hwnd,"Mon beau programme !","A propos !",MB_OK);
if(HIWORD(wParam) == EN_CHANGE) EditNotChg = FALSE;
return 0;
case WM_SIZE:
MoveWindow(hEdit, 0, 0, LOWORD(lParam), HIWORD(lParam), TRUE);
return 0;
case WM_DESTROY:
PostQuitMessage(0);
return 0;
default:
return DefWindowProc(hwnd, uMsg, wParam, lParam);
}
}
J'ai testé les compilations avec C++ Builder et DevC++.
A vos PC.
CGi
Avec la contribution de nico-pyright(c) pour la relecture.
|