1 - La fenêtre principale.

La fenêtre principale.

Article lu   fois.

L'auteur

Site personnel

Liens sociaux

Viadeo Twitter Facebook Share on Google+   

I. Introduction :

Windows est un système d'exploitation proposant une interface graphique. Dans ce premier article, nous allons créer une application composée seulement d'une fenêtre principale, dont le but et de montrer l'architecture d'un programme Windows.

Image non disponible

Prenez garde au terme de fenêtre ! Dans Windows, il signifie bien souvent objet visuel, par exemple, un bouton est une fenêtre ! On emploie aussi pour ces objets le terme de contrôles. La fenêtre apparaissant au démarrage de l'application est souvent appelée fenêtre principale. Dans ce tutoriel, nous emploierons le terme de fenêtre pour désigner une fenêtre du type de la fenêtre principale et le mot contrôle pour désigner les objets qu'elles peuvent contenir tels les boutons...

II. La fonction WinMain :

Le point d'entrée d'une application Windows est la fonction WinMain. C'est l'équivalent de la fonction main des applications classiques. Elle est appelée par le système d'exploitation au lancement du programme. Il lui fournit 4 paramètres.

 
Sélectionnez
int WINAPI WinMain(HINSTANCE hinstance, HINSTANCE hPrevInstance,
                                                 LPSTR lpCmdLine, int nCmdShow);
  • Le premier paramètre est le handle d'instance de l'application. C'est un numéro unique attribué par le système d'exploitation qui lui permet de l'identifier.
  • Le second paramètre est toujours NULL pour les applications Win32.
  • Le troisième paramètre est un pointeur sur la ligne de commande.
  • Et enfin le quatrième et dernier paramètre est un identificateur qui indique comment Windows désire afficher la fenêtre (normal, caché, minimisé ...).

C'est donc dans cette fonction (WinMain) que nous allons créer la fenêtre principale.

III. Création de la fenêtre :

La fonction qui permet de créer une fenêtre se nomme CreateWindow :

 
Sélectionnez
HWND CreateWindow(
    LPCTSTR lpClassName,  // Pointeur sur une classe de fenêtre.
    LPCTSTR lpWindowName,  // Pointeur sur le texte de la fenêtre.
    DWORD dwStyle,  // Style de la fenêtre.
    int x,  // Position horizontale de la fenêtre.
    int y,  // Position verticale de la fenêtre.
    int nWidth,  // Largeur de la fenêtre.
    int nHeight,  // Hauteur de la fenêtre.
    HWND hWndParent,  // Handle de la fenêtre parent.
    HMENU hMenu,  // Handle de menu ou ID de contrôle.
    HANDLE hInstance,  // Handle d'instance de l'application.
    LPVOID lpParam   // Pointeur sur des données passées à WM_CREATE.
   );

En voici un exemple d'utilisation :

 
Sélectionnez
    HWND hwnd;

    hwnd = CreateWindow("MaWinClass", "Titre", WS_OVERLAPPEDWINDOW,
                                   CW_USEDEFAULT, CW_USEDEFAULT, 400, 300,
                                                   NULL, NULL, hinstance, NULL);

Le premier paramètre qu'elle reçoit est un pointeur sur une chaîne de caractères identifiant la classe de fenêtre. Les classes de fenêtre sont des modéles pour construire les fenêtres (le terme de classe n'a rien à voir avec les classes du C++). Si pour les contrôles standard nous avons des classes de fenêtres prédéfinies et globale, nous devrons en créer une pour la fenêtre principale. Nous devons pour cela remplir une structure de type WNDCLASS (définie dans winuser.h)

 
Sélectionnez
    WNDCLASS wc;

    wc.style = 0;
    wc.lpfnWndProc = MainWndProc;
    wc.cbClsExtra = 0;
    wc.cbWndExtra = 0;
    wc.hInstance = hinstance;
    wc.hIcon = LoadIcon(NULL, IDI_APPLICATION);
    wc.hCursor = LoadCursor(NULL, IDC_ARROW);
    wc.hbrBackground = (HBRUSH)(COLOR_3DFACE + 1);
    wc.lpszMenuName =  NULL;
    wc.lpszClassName = "MaWinClass";

Les champs qu'il nous importe d'affecter pour l'instant à cette structure sont :

  • Le champ lpszClassName avec un pointeur sur la chaîne de caractères identifiant la classe de fenêtre. Celle que l'on a passé comme paramètre à la fonction CreateWindow.
  • Le champ hInstance sera initialisé avec le handle d'instance hinstance que nous a fourni la fonction WinMain.
  • Le champ hIcon avec le handle d'une icône standard de Windows qui nous est retournée par la fonction LoadIcon.
  • Le champ hCursor avec le handle d'un curseur prédéfini de Windows (en l'occurrence la flèche) qui nous est retourné par la fonction LoadCursor.
  • Le champ hbrBackground avec le handle d'un pinceau qui servira à peindre la surface client de la fenêtre. Dans cet exemple nous lui affecterons une couleur prédéfinie du système ((l'identificateur de la couleur) + 1 ).
  • Le champ lpfnWndProc de type WNDPROC reçoit un pointeur de fonction sur la procédure de fenêtre que nous allons voir plus loin dans ce document.

Nous passons sous silence pour l'instant les autres champs de cette structure.

Remplir cette structure ne suffit pas, il faut aussi l'enregistrer au niveau du sytème (souvenez-vous qu'elles sont identifiées par une chaîne de caractères). Cet enregistrement sera effectué par la fonction RegisterClass qui reçoit comme paramètre l'adresse de la variable wc de type WNDCLASS créée précédemment :

 
Sélectionnez
     RegisterClass(&wc);

Revenons-en maintenant à la fonction CreateWindow.

  • Son deuxième paramètre reçoit un pointeur sur le texte de la fenêtre. Dans le cas de la fenêtre principale, ce sera le texte du titre (si cela avait été un bouton, le texte se serait affiché sur le bouton).
  • Le troisième paramètre est le style de la fenêtre. Ce sont des identificateurs qui sont défini dans le fichier winuser.h. Nous ne les passerons pas en revue car ils sont trop nombreux. Dans l'exemple, nous lui donnerons la valeur WS_OVERLAPPEDWINDOW. Ce paramètre permet d'avoir une fenêtre avec une barre de titre, un menu system, les boutons minimiser et maximiser et que l'on peut redimensionner.
  • Les deux paramètres suivants sont la position à l'écran de la fenêtre par rapport au coin haut gauche du bureau. Dans l'exemple, ils ont la valeur par défaut fournie par Windows : CW_USEDEFAULT.
  • Les deux paramètres suivants sont la taille de la fenêtre : largeur et hauteur. Ils peuvent aussi avoir la valeur par défaut CW_USEDEFAULT.
  • Le paramètre suivant désigne le parent de la fenêtre. Il est utile pour les contrôles enfants. En ce qui concerne la fenêtre principale nous l'initialiserons à NULL.
  • Le paramètre suivant désigne un menu pour la fenêtre, il peut être à NULL s'il n'y a pas de menu ou si le menu a été référencé dans le champ lpszMenuName de la classe de fenêtre.
  • L'avant-dernier paramètre reçoit le handle d'instance de l'application qui crée la fenêtre, celui qui est fourni par WinMain.
  • Le dernier paramètre sert à passer des données à la procédure de fenêtre quand elle reçoit un message WM_CREATE. Dans cet exemple, il sera initialisé à NULL.

Après s'être exécutée, la fonction CreateWindow nous retourne le handle de la fenêtre qu'elle vient de créer. Les handles de fenêtre sont des identificateurs uniques attribués par Windows qui désignent chaque fenêtres. Ils sont de type HWND.

Notre fenêtre étant maintenant créée, il faut la rendre visible par l'appel de la fonction ShowWindow :

 
Sélectionnez
     ShowWindow(hwnd, nCmdShow);

Cette fonction reçoit comme paramètre le handle de la fenêtre et un identificateur qui indique comment on désire l'afficher (normal, caché, minimisé ...). En général, on lui donne celui fourni par WinMain (voir plus haut dans ce document).

Ensuite, on appelle UpdateWindow avec comme paramètre le handle de la fenêtre. Cette fonction sert à rafraîchir l'affichage de la zone client. Dans cet exemple, elle n'est pas vraiment utile puisque la zone client est vide.

 
Sélectionnez
     UpdateWindow(hwnd);

IV. La boucle de messages :

Revenons au fonctionnement de Windows. Pour communiquer avec une application ou ses diverses fenêtres, Windows leurs envoie des messages. Par exemple, si vous cliquez sur le bouton fermeture (en haut à droite de la fenêtre), Windows va créer un message approprié qu'il va envoyer dans la file d'attente de l'application. La file d'attente est un tampon où sont stockés les messages en attente de traitement.

C'est à nous de coder l'extraction des messages de la file d'attente :

 
Sélectionnez
    MSG msg;

    while (GetMessage(&msg, NULL, 0, 0))
    {
        TranslateMessage(&msg);
        DispatchMessage(&msg);
    }

C'est le rôle de la fonction GetMessage, dont le premier paramètre est un pointeur sur une variable de type MSG où elle doit stocker les données du message qu'elle vient de récupérer.

Nous devons donc définir cette variable de type MSG, qui est une structure composé d'un champ hwnd qui contient le handle de la fenêtre à qui il est destiné, un champ message identifiant le message (par exemple WM_CLOSE pour demander à la fenêtre de ce fermer) et de deux champs wParam et lParam contenant des données ayant rapport au message et de significations différentes selon le message.

Revenons à GetMessage,

  • Son deuxième paramètre est le handle de fenêtre dont on veut récupérer les messages. Il sera mis à NULL car on veut récupérer tous les messages de l'application (destinés à toutes ses fenêtres).
  • Les deux derniers paramètres seront mis à zéro, ils servent au filtrage des messages.

GetMessage renvoie toujours la valeur TRUE sauf si elle vient de récupérer le message WM_QUIT, ce qui met fin à la boucle de message donc à l'application.

Ensuite on passe le message à la fonction TranslateMessage qui traduit les messages WM_KEYDOWN (appui des touches clavier) en message WM_CHAR avec le code de caractère correspondant dans le champ wParam du message.

Ensuite, c'est à la fonction DispatchMessage de traiter le message. Son rôle est de l'envoyer à la procédure de fenêtre. Celle à qui il est destiné.

V. La procédure de fenêtre :

Voici la procédure de fenêtre de notre fenêtre principale dont nous avions passé un pointeur à la classe de fenêtre au début de ce document (on passe ce pointeur à la classe de fenêtre car c'est nous qui la créons, mais le système d'exploitation qui l'appelle, il doit donc la localiser).

 
Sélectionnez
LRESULT CALLBACK MainWndProc(HWND, UINT, WPARAM, LPARAM);


LRESULT CALLBACK MainWndProc(HWND hwnd, UINT uMsg, WPARAM wParam, LPARAM lParam)
{
    switch (uMsg)
    {
        case WM_CREATE:

            return 0;

        case WM_DESTROY:
            PostQuitMessage(0);
            return 0;

        default:
            return DefWindowProc(hwnd, uMsg, wParam, lParam);
    }
}

Les paramètres qu'elle reçoit sont les données du message en cours de traitement. Celui qui a été envoyé par DispatchMessage. C'est à nous, développeurs, d'implémenter des actions en fonction du message que la procédure de fenêtre reçoit. Dans cet exemple, un seul message est traité : WM_DESTROY. Il nous indique que la fenêtre est fermée et a été détruite. Comme il s'agit de la fenêtre principale de l'application, il est d'usage de fermer l'application. Ce que nous ferons en appelant la fonction PostQuitMessage qui a pour fonction de poster un message WM_QUIT dans la file d'attente. Qui comme nous l'avons vu plus haut dans ce document, met fin à la boucle de messages et donc à l'application. Les messages non traités doivent l'être par la fonction DefWindowProc. Fonction qui implémente le comportement par défaut d'une fenêtre.

VI. La Compilation :

Pour compiler un programme fenêtré sous windows, il faut créer un projet GUI sinon windows ne cherchera pas la fonction WinMain mais probablement la fonction main qui n'existe pas dans notre code.

Le type de projet sera par exemple : Win32 GUI Project sous Code::Blocks ou Projet Win32 sous Visual C++. En ligne de commande ce sera une option du linker ex : /SUBSYSTEM:WINDOWS sous Visual C++ ou -Wl,--subsystem,windows sous MinGW.

VII. Code complet :

 
Sélectionnez
#include <windows.h>


LRESULT CALLBACK MainWndProc(HWND, UINT, WPARAM, LPARAM);

int WINAPI WinMain(HINSTANCE hinstance, HINSTANCE hPrevInstance,
                                                LPSTR lpCmdLine, int nCmdShow)
{
    HWND hwnd;
    MSG msg;
    WNDCLASS wc;

    wc.style = 0;
    wc.lpfnWndProc = MainWndProc;
    wc.cbClsExtra = 0;
    wc.cbWndExtra = 0;
    wc.hInstance = hinstance;
    wc.hIcon = LoadIcon(NULL, IDI_APPLICATION);
    wc.hCursor = LoadCursor(NULL, IDC_ARROW);
    wc.hbrBackground = (HBRUSH)(COLOR_3DFACE + 1);
    wc.lpszMenuName =  NULL;
    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);
    UpdateWindow(hwnd);


    while (GetMessage(&msg, NULL, 0, 0))
    {
        TranslateMessage(&msg);
        DispatchMessage(&msg);
    }
    return msg.wParam;
}
/******************************************************************************/

LRESULT CALLBACK MainWndProc(HWND hwnd, UINT uMsg, WPARAM wParam, LPARAM lParam)
{
    switch (uMsg)
    {
        case WM_CREATE:

            return 0;

        case WM_DESTROY:
            PostQuitMessage(0);
            return 0;

        default:
            return DefWindowProc(hwnd, uMsg, wParam, lParam);
    }
}

Ce code a été testé sous VC++, CodeBlocks(Mingw), C++ Builder.

A vos PC.

CGi

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.