7 - Les ressources.

Les ressources.

Article lu   fois.

L'auteur

Site personnel

I. 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.

Image non disponible

II. 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.

III. 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 :

 
Sélectionnez
1.
2.
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) :

 
Sélectionnez
1.
2.
3.
4.
5.
6.
7.
8.
9.
10.
11.
12.
13.
14.
15.
16.
17.
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 :

 
Sélectionnez
1.
2.
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.

 
Sélectionnez
1.
     wc.hIcon = LoadIcon(hinstance,"ICONE2");

IV. 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 :

 
Sélectionnez
1.
2.
3.
4.
5.
6.
7.
8.
9.
10.
11.
12.
13.
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" :

 
Sélectionnez
1.
2.
3.
#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 :

 
Sélectionnez
1.
    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 :

 
Sélectionnez
1.
2.
3.
4.
5.
    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.

V. Code complet :

V-A. resource.h :

 
Sélectionnez
1.
2.
3.
#define IDM_QUIT  1
#define IDM_NEW   2
#define IDM_ABOUT 3

V-B. resource.rc :

 
Sélectionnez
1.
2.
3.
4.
5.
6.
7.
8.
9.
10.
11.
12.
13.
14.
15.
16.
17.
18.
19.
20.
#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

V-C. winmain.c :

 
Sélectionnez
1.
2.
3.
4.
5.
6.
7.
8.
9.
10.
11.
12.
13.
14.
15.
16.
17.
18.
19.
20.
21.
22.
23.
24.
25.
26.
27.
28.
29.
30.
31.
32.
33.
34.
35.
36.
37.
38.
39.
40.
41.
42.
43.
44.
45.
46.
47.
48.
49.
50.
51.
52.
53.
54.
55.
56.
57.
58.
59.
60.
61.
62.
63.
64.
65.
66.
67.
68.
69.
70.
71.
72.
73.
74.
75.
76.
77.
78.
79.
80.
81.
82.
83.
84.
85.
86.
87.
88.
89.
90.
91.
92.
93.
94.
95.
96.
97.
98.
99.
100.
101.
102.
103.
104.
105.
106.
#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.

C/C++
  Les pointeurs du C/C++.   Les listes chaînées.             Liste simple.             Liste triée.             Liste double.   Les arbres.   Les tas.   Le C orienté objets ?
  1 - La fenêtre principale.   2 - Contrôles et messages.   3 - Les commandes.   4 - Dialogue std.   5 - Contexte de périph.   6 - Dessiner.   7 - Les ressources.   8 - Dialogue perso.   9 - Dialogue comm.   10 - Les accélérateurs.
C++ BUILDER
  Trucs et astuces.   Composant.   TRichEdit.   TDrawGrid.   Application MDI.   TThread.   wxWidgets.   Style Win XP.
  Première application.   Construire un menu.   Dessiner.   Sisers, Timers...   Dialogues standards.   Dialogues perso.
DotNet
  Composant C# Builder.   Contrôle WinForm.   Application MDI.
  

Copyright © 2005 CGi. Aucune reproduction, même partielle, ne peut être faite de ce site et de l'ensemble de son contenu : textes, documents, images, etc. sans l'autorisation expresse de l'auteur. Sinon vous encourez selon la loi jusqu'à trois ans de prison et jusqu'à 300 000 € de dommages et intérêts.