IdentifiantMot de passe
Loading...
Mot de passe oublié ?Je m'inscris ! (gratuit)

API Windows en C

Les ressources

Les ressources. ♪

Article lu   fois.

L'auteur

Profil ProSite 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 fournit 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 destiné à des outils particuliers, 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 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 sûr exister. Les ayant mis dans le même dossier que les fichiers source du programme, je n'ai pas indiqué le chemin pour les localiser. S'ils se trouvent ailleurs, il faudra bien sûr mettre le chemin (j'ai dû tout de même le mettre pour la compilation avec Dev-C++ malgré qu'ils se trouvaient dans le même dossier).

Certains compilateurs de ressources demandent d'inclure les fichiers entêtes de Windows qui contiennent la définition des identificateurs utilisés dans le fichier script de ressources.

Il peut 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.

À 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 se trouve à gauche sur la barre de titre) :

 
Sélectionnez
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 faisons cette conversion.

Nous aurions pu les identifier avec des valeurs littérales :

 
Sélectionnez
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
     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
LEMENU MENU
BEGIN
  POPUP "Fichier"
    BEGIN
       MENUITEM "&Nouveau", IDM_NEW
       MENUITEM SEPARATOR
       MENUITEM "&Quitter", IDM_QUIT
    END
  POPUP "Aide"
    BEGIN
       MENUITEM "À 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-menus (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
#define IDM_QUIT  1
#define IDM_NEW   2
#define IDM_ABOUT 3

Ce fichier devra donc être inclus dans le fichier source et dans le fichier script de resource (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 littéral du menu :

 
Sélectionnez
    wc.lpszMenuName =  "LEMENU";

C'est tout pour sa construction. La récupération des actions du menu se fait sur réception des messages WM_COMMAND de la même manière qu'au chapitre 3 :

 
Sélectionnez
    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. À 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
#define IDM_QUIT  1
#define IDM_NEW   2
#define IDM_ABOUT 3

V-B. resource.rc 

 
Sélectionnez
#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 "À propos...", IDM_ABOUT
    END
END

V-C. winmain.c

 
Sélectionnez
#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\nÊtes-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++.

À vos PC.

CGi

Avec la contribution de nicopyright(c) pour la relecture.

Copyright © 2005 CGi. Aucune reproduction, même partielle, ne peut être faite de ce site ni 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.