Programmer en 'C' sous Windows.
Leçon 01 : Ma première application Windows (Programme minimal).

Page suivante Page precedente Sommaire des cours Page d'accueil du site VB-AUDIO Audio Mechanic and Sound Breeder
Langage : Anglais Langage : Francais

MinPrg01 (programme minimal sous windows)
Ce premier source-code propose un exemple de programme windows minimal. On affiche une fenêtre principale avec ses composants usuels : Caption bar classique (barre de titre), Menu basique, et avec ses propriétés standards, par exemple le fait que la fenêtre puisse être redimensionnée, déplacée, fermée etc... Comme la plupart de ces fonctions sont gérées par le système, vous allez voir que notre programme ne fait pratiquement rien.

MinPrg01 : My First Window Program


Les Fichiers Organisation du Source Code
MiniPrg_01.exe (40KB 14-MAR-2008)
MiniPrg_01.zip (5KB 14-MAR-2008)
minprg.h
minprg.c
minprg.rc
MinPrg01 : Source Organization

Que fait ce programme ? :
Ce programme crée et affiche une fenêtre Windows classique avec un menu défini par script dans le fichier ressource.
Une chaîne de caractères est affichée dans la partie cliente de la fenêtre.
Le menu utilisateur (Command) permet de choisir 3 différents items :
  • "Do Something" affiche une autre chaîne de caractères directement dans la partie cliente
  • "About" Affiche une boite de dialogue système muni d'un bouton OK.
  • "Quit" Affiche une boite de dialogue système pour confirmer la fermeture de l'application.

  • Les grands principes évoqués :
    Un programme sous Windows (qui est un système d'exploitation événementiel) se résume dans le principe, à une boucle de messages et une Callback, c'est à dire une fonction standardisée (appelée par le système), qui va recueillir les messages, et éventuellement les traiter. Notre programme principale (fonction WinMain), après avoir créé une fenêtre, entre dans une boucle infinie (jusqu'à la fin du programme) qui va continuellement :

  • Attendre un nouveau message (fonction GetMessage).
  • Traduire et diffuser ce message (fonctions TranslateMessage et DispatchMessage).

    Ce message, traduit et diffusé, arrive dans une Callback Windows, celle relative à la fenêtre concernée par l'événement. Par exemple faire un clic souris sur un bouton, va déclencher l'événement WM_LBUTTONDOWN qui sera communiqué à la Callback de ce bouton et qui le traitera comme bon lui semble.

    Sous Windows, tout ce que vous voyez à l'écran est dans une fenêtre Windows et tout programme ayant une partie visible à l'écran est composé de fenêtres. Je pense que c'est ce qui a inspirer les gens qui ont trouvé le nom du système. Blague à part, toute action utilisateur (clavier , souris, etc...) est traduite en un événement qui arrivera dans la Callback de la fenêtre concernée ou de la fenêtre désignée comme telle. Par exemple pour le clavier, c'est la fenêtre qui a le 'Focus' (la fenêtre active) qui reçoit les événements clavier.

  • Quelques points du source :
    Dans notre exemple de programme, notre Callback ne répond qu'à très peu de message. D'ailleurs une Callback ne peut pas répondre à tous les messages, c'est pourquoi on fait appel à la fonction du système DefWindowProc qui traite tous les messages que nous ne gérons pas. Par exemple nous ne gérons pas l'affichage des bords et du fond de la fenêtre, de la barre de titre et du menu. Nous ne gérons pas non plus les actions utilisateurs qui font bouger ou redimensionner la fenêtre, le déploiement des menus etc...

    Notre programme se contente donc de répondre à 5 messages :
  • WM_CREATE : qui notifie que la fenêtre est en passe d'être créée. Usuellement on profite de ce message pour faire des initialisations, des allocations mémoire, des créations d'objets...
  • WM_COMMAND : nous est envoyé par les menus, quand l'utilisateur a sélectionné un item.
  • WM_PAINT : demande à ce que la partie cliente de la fenêtre soit dessinée. Ici nous ne faisons qu'afficher une chaîne de caractères. Il faut savoir que la fenêtre est en charge de son aspect sur toute la partie cliente, et qu'à tout moment elle doit pouvoir être redessinée.
  • WM_CLOSE : Est reçu quand l'utilisateur veux fermer la fenêtre, soit par un clic sur la croix en haut à droite, soit par la combinaison de touche ALT+F4, soit par le menu système etc...
  • WM_DESTROY : Notifie la destruction de la fenêtre. On en profitera pour libérer d'éventuelles allocations mémoire etc... notons l'appel à la fonction du système PostQuitMessage qui poste l'événement WM_QUIT dans la 'message queue' qui va entraîner la fin de la boucle (GetMessage renvoie ZERO dans ce cas).


  • Questions / Réponses:
    A quoi sert le RegisterClass ?
    A enregistrer auprès du système une classe de fenêtre (comme une classe d'objet). Chaque fenêtre est associée à une classe, et l'on peut créer plusieurs fenêtres d'une même classe (plusieurs boutons poussoirs par exemple). Dans notre programme, on définit une classe que l'on appelle "MainWindowClass", avec aussi notre Callback (voir structure WNDCLASS). Ensuite on peut créer notre fenêtre principale, qui est forcément une fenêtre spécifique, et qui donc nécessite d'être associée à une classe spécifique, avec une Callback spécifique. Quand le système reçoit un événement (par exemple un clic souris) il retrouve la classe de la fenêtre concernée et appelle la Callback associée.

    Quand est-ce que les messages arrivent dans ma Callback ?
    Par nature, le système peut transmettre un message n'importe quand, mais toujours l'un après l'autre. En effet notre exemple n'est pas MultiThread, et tous les messages qui arrivent à notre Callback passent par notre boucle de messages au sein du même Thread (le thread dit primaire). Par conséquent, notre programme ne fait jamais deux choses en même temps et ne peux pas recevoir de nouveaux messages pendant qu'il en traite un.

    Quand on affiche la chaîne avec le Menu " Do Something " cette chaîne ne reste pas affichée, par exemple elle disparaît quand on redimensionne la fenêtre, pourquoi ?
    A chaque fois que nécessaire le système demande à la fenêtre de se redessiner (via le message WM_PAINT notamment). Donc seules les affichages fait en réponse au message WM_PAINT sont persistants.


    Source : minprg.c

    /*--------------------------------------------------------------------------------*/
    /* 'C' Programming Under WIN32                                     V.Burel (c)2007*/
    /*                                                                                */
    /*  WEB  : http://pagesperso-orange.fr/vb-audio/fr/pub/programming/index.htm      */
    /*--------------------------------------------------------------------------------*/
    /* MinPrg is a minimal program under windows (like a Hello World)                 */
    /*--------------------------------------------------------------------------------*/
    /*  To compile With Microsoft VC2005 you need to create an empty Win32 project    */
    /*  with the following options :                                                  */
    /*  - Configuration Properties / General : Char Set = NOT SET                     */
    /*  - Configuration Properties / C/C++ / Preprocessor : _CRT_SECURE_NO_DEPRECATE  */
    /*--------------------------------------------------------------------------------*/
    #ifndef __cplusplus
       #ifndef STRICT
          #define STRICT
       #endif
    #endif
    #include <windows.h>
    #include <stdio.h>
    #include "minprg.h"
    
    //
    //define globals 
    //
    static HWND        G_hwnd_MainWindow   =NULL;
    static HINSTANCE   G_hinstance         =NULL;
    
    /*******************************************************************************/
    /**                                QUIT & ABOUT                               **/
    /*******************************************************************************/
    
    void ManageCloseMessage(HWND hw)
    {
       int rep;
       char titre[]="Close Application...";
       char message[512];
       
       //Get the string from resource, otherwise we use a constant.
       if (LoadString(G_hinstance, IDS_CONFIRMCLOSE, message, 512) == 0)
          strcpy(message,"Do you Want To Close This Application ?\n
                         (but there is a problem to load resources)");
       //Open System Dialog Box 
       rep=MessageBox(hw,message,titre,MB_APPLMODAL | MB_YESNO | MB_ICONQUESTION);
       if (rep == IDNO) return;
    
       PostMessage(hw,WM_DESTROY,0,0L); //DestroyWindow(G_hwnd_MainWindow);
    }
    
    void ManageAboutBox(HWND hw)
    {
       char titre[]="About...";
       char message[512];
       strcpy(message,SZPUBLICNAME);
       strcat(message,"\nVersion : ");
       strcat(message,SZPUBLICVERSION);
       strcat(message,"\nStandalone Application\n");
       strcat(message,"\nExample of 'C' Source code\n");
       
       MessageBox(hw,message,titre,MB_APPLMODAL | MB_OK | MB_ICONINFORMATION);
    }
    
    /*******************************************************************************/
    
    void ManageMenu(HWND hw, WPARAM p,LPARAM w)
    {
       char sss[256];
       HDC dc;
       switch(LOWORD(p))
       {
          case IDT_DOSOMETHING:
             dc=GetDC(hw);
             strcpy(sss,"I write something in the Client Area");
             TextOut(dc,0,0,sss,(int)strlen(sss));
             ReleaseDC(hw,dc);
             break;
          case IDM_QUIT:
             ManageCloseMessage(hw);
             break;
          case IDM_ABOUT:
             ManageAboutBox(hw);
             break;
    
       }
    }
    
    /*******************************************************************************/
    
    BOOL InitSoftware(HWND hw)
    {
       return TRUE;
    }
    
    BOOL EndSoftware(HWND hw)
    {
       return TRUE;
    }
    
    /*******************************************************************************/
    /*                                  CALL BACK                                  */
    /*******************************************************************************/
    
    LRESULT CALLBACK MainWindowManageEvent(HWND hw,  //handle of the window.
                                     UINT msg,       //Message Ident.
                                     WPARAM p1,      //parameter 1.
                                     LPARAM p2)      //parameter 2
    {
       char sss[256];
       HDC dc;
       PAINTSTRUCT ps;
       switch (msg)
       {
    
    
          case WM_CREATE:
             //return -1 here cancel the window creation
    		 if (InitSoftware(hw) == FALSE) return -1;
             break;
          case WM_COMMAND:
             ManageMenu(hw,p1,p2);
             break;
    
          case WM_PAINT:
             dc=BeginPaint(hw,&ps);
             strcpy(sss,"This text is always displayed");
             TextOut(dc,0,30,sss,(int)strlen(sss));
             EndPaint(hw,&ps);
               break;
          case WM_CLOSE:
             ManageCloseMessage(hw);
             break;
          case WM_DESTROY:
             EndSoftware(hw);
             PostQuitMessage(0);
             break;
          default:
             return (DefWindowProc(hw,msg,p1,p2));
    
       }
       return (0L);
    }
    
    /*******************************************************************************/
    /**                              MAIN PROCDURE                                **/
    /*******************************************************************************/
    
    int APIENTRY WinMain(HINSTANCE handle_app,   //Application hinstance.
                         HINSTANCE handle_prev,  //NULL.
                         LPTSTR param,           //Command Line Parameter.
                         int com_show)           //How to display window (optionnal).
    {
       long   wstyle;
       MSG      msg;    
       char   szWindowClassName[]="MainWindowClass";
       char *   title="Sorry";
    
       WNDCLASS   wc;
       //we first store the APP Hinstance
       G_hinstance=handle_app;
    
       //here you can make some early initialization and analyze command line if any.
    
       //we define a window class to create a window from this class 
       wc.style      =CS_HREDRAW | CS_VREDRAW;        //property.
       wc.lpfnWndProc=(WNDPROC)MainWindowManageEvent; //Adresse of our Callback.
       wc.cbClsExtra =0;                              //to store some byte inside a class object.
       wc.cbWndExtra =0;                              //to store some byte inside a window object.
       wc.hInstance  =handle_app;                     //handle of the application hinstance.
       wc.hIcon      =LoadIcon(NULL, IDI_APPLICATION);//handle of icon displayed in the caption.
       wc.hCursor    =LoadCursor(NULL,IDC_ARROW);     //handle of cursor mouse .
       wc.hbrBackground=(HBRUSH)(COLOR_MENU+1);       //Background color.
       wc.lpszMenuName="MyMainAppMenu";               //pointer on menu defined in resource.
       wc.lpszClassName=szWindowClassName;            //pointer on class name.
    
       //register class.
       if (RegisterClass(&wc)==0)
       {
          MessageBox(NULL,"Failed to register main class...",title,MB_APPLMODAL | MB_OK | MB_ICONERROR);
          return 0;
       }
       //then we can create a windows from this class.
       
       //Classical Window without Sizing border.
       //wstyle=WS_DLGFRAME | WS_OVERLAPPED | WS_VISIBLE | WS_MINIMIZEBOX | WS_SYSMENU;
       
       //classical Main Window
       wstyle=WS_OVERLAPPEDWINDOW | WS_VISIBLE;
       G_hwnd_MainWindow=CreateWindow(szWindowClassName, // address of registered class name.
                       SZPUBLICNAME,                     // address of window name string
                       wstyle,                           // window style
                       CW_USEDEFAULT,                    // horizontal position of window
                       CW_USEDEFAULT,                    // vertical position of window
                       UI_WIN_DX,                        // window width
                       UI_WIN_DY,                        // window height
                       NULL,                             // parent handle is NULL for a main window.
                       NULL,                             // menu name defined in resource 
                                                         //(NULL if defined in the Class).
                       handle_app,                       // handle of application instance
                       NULL);                            // address of window-creation data
    
       if (G_hwnd_MainWindow==NULL)
       {
          MessageBox(NULL,"Failed to create window...",title,MB_APPLMODAL | MB_OK | MB_ICONERROR);
          return 0;
       }
       ShowWindow(G_hwnd_MainWindow,SW_SHOW);         //Display the window.
       UpdateWindow(G_hwnd_MainWindow);               //Send WM_PAINT.
       /*---------------------------------------------------------------------------*/
       /*                             Messages Loop.                                */
       /*---------------------------------------------------------------------------*/
       while (GetMessage(&msg,NULL,0,0)) //Get Message if any.
       {
          TranslateMessage(&msg);        //Translate the virtuel keys event.
          DispatchMessage(&msg);         //DispatchMessage to the related window.
       }
       //here you can make last uninitialization and release
       return (int)(msg.wParam);
    }
    
    
    

    Source : minprg.h

    #ifndef __MINPRG_H__
    #define __MINPRG_H__
    
    //version information (for program)
    #define SZPUBLICVERSION      "1.0.0.0"           //displayed version in about box
    #define SZPUBLICNAME         "Minimal Program"   //displayed title in main window 
    
    //Information for Main window 
    #define UI_WIN_DX   600
    #define UI_WIN_DY   400
    
    //version information (used in resource file)
    #define __FILEVERSION__         1,0,0,0
    #define __PRODUCTVERSION__      1,0,0,0
    #define __SZFILEVERSION__      "1, 0, 0, 0\0"
    #define __SZPRODUCTVERSION__   "1, 0, 0, 0\0"
     
       #define __COMMENTS__         "Example of source code"
       #define __COMPANYNAME__      "Audio Mechanic & Sound Breeder\0"
       #define __FILEDESCRIPTION__  "Minimal Windows Application\0"
       #define __INTERNALNAME__     "MinPrg"
       #define __LEGALCOPYRIGHT__   "Copyright V.Burel©2007\0"
       #define __ORIGINALFILENAME__ "MinPrg.EXE\0"
       #define __PRODUCTNAME__      "MinPrg\0"
    
    
    //definitions for MENU
    
    #define IDT_DOSOMETHING    50
    #define IDM_QUIT           100
    #define IDM_ABOUT          101
    
    //definitions for STRING-TABLE
    
    #define IDS_CONFIRMCLOSE	100
    
    #endif /*__MINPRG_H__*/
    
    

    Source : minprg.rc

    #include "minprg.h"
    
    /////////////////////////////////////////////////////////////////////////////
    //
    // Menu
    //
    
    MyMainAppMenu MENU DISCARDABLE 
    BEGIN
        POPUP "&Command"
        BEGIN
            MENUITEM "&Do Something",            IDT_DOSOMETHING
            MENUITEM SEPARATOR
            MENUITEM "&About",                   IDM_ABOUT
            MENUITEM "&Quit",                    IDM_QUIT
        END
    END
    
    
    /////////////////////////////////////////////////////////////////////////////
    //
    // STRING
    //
    
    STRINGTABLE
    BEGIN
    	IDS_CONFIRMCLOSE		"Do you want to close this application ?"
    END
    
    
    /////////////////////////////////////////////////////////////////////////////
    //
    // Version
    //
    
    1 VERSIONINFO
     FILEVERSION __FILEVERSION__
     PRODUCTVERSION __PRODUCTVERSION__
     FILEFLAGSMASK 0x3fL
    #ifdef _DEBUG
     FILEFLAGS 0x1L
    #else
     FILEFLAGS 0x0L
    #endif
     FILEOS 0x0L
     FILETYPE 0x1L
     FILESUBTYPE 0x0L
    BEGIN
        BLOCK "StringFileInfo"
        BEGIN
            BLOCK "000004b0"
            BEGIN
                VALUE "Comments", __COMMENTS__
                VALUE "CompanyName", __COMPANYNAME__
                VALUE "FileDescription", __FILEDESCRIPTION__
                VALUE "FileVersion", __SZFILEVERSION__
                VALUE "InternalName", __INTERNALNAME__
                VALUE "LegalCopyright", __LEGALCOPYRIGHT__
                VALUE "OriginalFilename", __ORIGINALFILENAME__
                VALUE "ProductName", __PRODUCTNAME__
                VALUE "ProductVersion", __SZPRODUCTVERSION__
          END
        END
        BLOCK "VarFileInfo"
        BEGIN
            VALUE "Translation", 0x0, 1200
        END
    END
    


    Page suivante Page precedente Sommaire des cours Page d'accueil du site VB-AUDIO

    Copyright V.Burel ©2008. Tous droits réservés. Toute spécification technique et toute information produits, spécifiées dans ce site web peuvent subir des modifications sans préavis.