La programmation Windows

Objectif :     Se familiariser avec la programmation Windows, en étudiant une application très simple, écrire une phrase dans une fenêtre.

Cette application sera complétée par d’autres fonctionnalités et construite de 3 façons différentes :

  • en utilisant l’API (Application Programming Interface) Windows 32 bits,
  • en utilisant l’API Windows et les classes MFC (Microsoft Foundation Classes),
  • en utilisant les outils de développement de Visual C++ 6.0, App Wizard et Class Wizard.

1- Programmation avec l’API Win32

Le listing du programme Windows permettant de créer une fenêtre et d’écrire au centre de celle-ci est présenté ci-dessous :

#include <windows.h>
#include <string.h>
LONG WINAPI WndProc( HWND, UINT, WPARAM, LPARAM );
char szAppName[] = "Mon premier programme windows";
int WINAPI WinMain( HINSTANCE hInstance, HINSTANCE hPrevInstance,PSTR szCmdLine, int iCmdShow )
{
 HWND hwnd;
 MSG msg;
 WNDCLASSEX wndclass;
  wndclass.cbSize = sizeof( wndclass ); //taille de l’objet
  wndclass.style = CS_HREDRAW | CS_VREDRAW ;
  wndclass.lpfnWndProc = (WNDPROC) WndProc;
  wndclass.cbClsExtra = 0;   //extra bytes de la classe
  wndclass.cbWndExtra = 0;   // extra bytes de la fenêtre
  wndclass.hInstance = hInstance;
  wndclass.hIcon = LoadIcon( NULL, IDI_APPLICATION ); //visible par Alt Tab
  wndclass.hCursor = LoadCursor( NULL, IDC_ARROW );
  wndclass.hbrBackground = (HBRUSH) GetStockObject( WHITE_BRUSH );
  wndclass.lpszMenuName = NULL;
  wndclass.lpszClassName = szAppName; //nom de la classe
  wndclass.hIconSm = LoadIcon( NULL, IDI_APPLICATION );
  RegisterClassEx( &wndclass );
  hwnd = CreateWindow( szAppName, "Mon premier programme windows",
                    WS_OVERLAPPEDWINDOW, CW_USEDEFAULT, CW_USEDEFAULT,
                    CW_USEDEFAULT, CW_USEDEFAULT, HWND_DESKTOP, NULL, hInstance, NULL );
  ShowWindow( hwnd, iCmdShow );
  UpdateWindow( hwnd );
  while( GetMessage( &msg, NULL, 0, 0 ) )
  {
      TranslateMessage( &msg );
      DispatchMessage( &msg );
  }
  return (msg.wParam);
}
LONG WINAPI WndProc( HWND hwnd, UINT iMsg, WPARAM wParam , LPARAM lParam )
{
 HDC hdc;
 PAINTSTRUCT ps;
 RECT rect;

 switch( iMsg )
 {
   case WM_PAINT:
       hdc = BeginPaint( hwnd, &ps );
       GetClientRect( hwnd, &rect );
       DrawText( hdc, szAppName, strlen(szAppName),&DT_SINGLELINE | DT_CENTER | DT_VCENTER );
       EndPaint( hwnd, &ps );
       return (0);
   case WM_DESTROY:       
       PostQuitMessage( 0 );
       return (0);
  }
 return (DefWindowProc( hwnd, iMsg, wParam, lParam ));
}

La fonction principale WinMain reçoit quatre paramètres qui sont :

  • un handle qui identifie l’instance de l’application qui vient d’être lancée : hInstance (Un handle est une variable qui identifie un objet),
  • un handle qui identifie toutes les instances précédentes de l’application : hPrevInstance (pour les applications Win32 ce paramètre est toujours nul),
  • un pointeur sur une chaîne de caractères contenant les paramètres de la ligne de commande de l’application : szCmdLine,
  • un entier qui précise le style de la fenêtre à son lancement : iCmdShow. Les valeurs de cet entier peuvent être les suivantes :
Value Meaning
SW_HIDE
Hides the window and activates another window.
SW_MINIMIZE
Minimizes the specified window and activates the top-level window in the system’s list.
SW_RESTORE
Activates and displays a window. If the window is minimized or maximized, Windows restores it to its original size and position (same as SW_SHOWNORMAL).
SW_SHOW
Activates a window and displays it in its current size and position.
SW_SHOWMAXIMIZED
Activates a window and displays it as a maximized window.
SW_SHOWMINIMIZED
Activates a window and displays it as an icon.
SW_SHOWMINNOACTIVE
Displays a window as an icon. The active window remains active.
SW_SHOWNA
Displays a window in its current state. The active window remains active.
SW_SHOWNOACTIVATE
Displays a window in its most recent size and position. The active window remains active.
SW_SHOWNORMAL
Activates and displays a window. If the window is minimized or maximized, Windows restores it to its original size and position (same as SW_RESTORE).

Cette fonction WinMain initialise ensuite une structure de donnée qui définit les caractéristiques fondamentales de la fenêtre : structure WNDCLASSEX. Les principaux champs de cette structure sont les suivants :

  • le style de la fenêtre : wndclass.style = CS_HREDRAW | CS_VREDRAW,
  • l’adresse de la procédure de fenêtre (qui sera chargée de recevoir et de gérer les messages concernant la fenêtre) : wndclass.lpfnWndProc = (WNDPROC) WndProc,
  • le handle d’instance : wndclass.hInstance = hInstance,
  • le handle d’icône : wndclass.hIcon = LoadIcon( NULL, IDI_APPLICATION ), et wndclass.hIconSm = LoadIcon( NULL, IDI_APPLICATION ),
  • le handle du curseur : wndclass.hCursor = LoadCursor( NULL, IDC_ARROW ),
  • la couleur de fond : wndclass.hbrBackground = (HBRUSH)GetStockObject( WHITE_BRUSH )
  • le nom du menu : wndclass.lpszMenuName = NULL.

 WinMain appelle ensuite la fonction API RegisterClassEx afin d’enregistrer une classe de fenêtre. Une application doit spécifier une classe de fenêtre lorsqu’elle crée une fenêtre, et une classe doit être enregistrée avant de pouvoir être utilisée. C’est pourquoi on passe l’adresse de cette structure WINDCLASSEX à la fonction RegisterClassEx.

Une fois la WNDCLASSEX déclarée, WinMain appelle la fonction CreateWindow pour créer la fenêtre de l’application. Cette fonction comporte les paramètres suivants :

  • un pointeur sur le nom de la classe fenêtre : lpClassName :szAppName
  • un pointeur sur le nom de la fenêtre (titre) : lpWindowName : « Mon premier programme windows »
  • le style de la fenêtre : dwStyle : WS_OVERLAPPEDWINDOW(style très courant qui crée une fenêtre de niveau supérieur, munie d’un cadre permettant de la redimensionner, d’une barre de titre, d’un menu système et d’icônes permettant de la réduire, de la zoomer et de la fermer),
  • la position initiale de la fenêtre (en pixels) : x et y : CW_USEDEFAULT                      CW_USEDEFAULT (indique à Windows de prendre des valeurs par défaut)
  • la largeur et la hauteur de la fenêtre : nWidth et nHeight : CW_USEDEFAULT CW_USEDEFAULT (valeurs par défaut)
  • le handle de la fenêtre parent: hWndParent : HWND_DESKTOP (le bureau puisque de niveau supérieur mais on aurait pu mettre NULL),
  • le handle du menu associé à la fenêtre : hMenu : NULL (aucun ici),
  • le handle d’instance de l’application : hInstance : hInstance (paramètre passé à WinMain)
  • un pointeur vers des données de création de fenêtre : lpParam : NULL(aucun ici)

La fenêtre créée par CreateWindow ne s’affiche pas tout de suite à l’écran car elle n’a pas été créée avec le style WS_VISIBLE (il faudrait le combiner avec WS_OVERLAPPEDWINDOW), c’est pourquoi WinMain fait appel à ShowWindow et à UpdateWindow. La fonction ShowWindow utilise le handle de fenêtre retourné par CreateWindow et le paramètre passé à WinMain : iCmdShow qui contient les paramètres d’affichage de la fenêtre. La fonction UpdateWindow garantit qu’un message WM_PAINT est immédiatement envoyé afin que la fenêtre soit rafraîchie.

Arrive ensuite la boucle des messages. Pour lire et ventiler les messages, WinMain exécute une simple boucle While qui appelle, de manière répétée, les fonctions API GetMessage, TranslateMessage et DispatchMessage :

GetMessage examine la file de messages. S’il y en a un, ce dernier est retiré de la file et copié dans msg. Autrement GetMessage se bloque sur la file et ne rend pas la main avant qu’un message ne soit disponible. msg est une instance de la structure MSG dont les champs contiennent des paramètres du message et entre autres son ID. TranslateMessage convertit un message clavier de bas niveau en un message WM_CHAR plus facile à interpréter. DispatchMessage ventile un message vers la procédure de fenêtre WndProc dans notre cas. La boucle des messages continue à s’exécuter jusqu’à ce que GetMessage renvoie 0, ce qui n’arrive que lorsqu’on lit un message WM_QUIT. Dans ce cas l’exécution se poursuit à l’instruction return qui termine WinMain et ainsi l’application se termine.

Les messages ventilés par DispatchMessage génèrent des appels à la procédure de fenêtre de l’application : WinProc qui ici ne gère que deux messages : WM_PAINT et WM_DESTROY. Tous les autres messages sont passés à la fonction DefWindowProc qui les renvoie à Windows (ce qui explique entre autres la bidirectionnalité des flèches entre un thread et le thread brut d’entrée sur la figure de la page 2). La structure switch case vérifie l’identité du message (passé dans le paramètre iMsg) et active le gestionnaire de message concerné.

Le gestionnaire de WM_PAINT appelle la fonction BeginPaint qui permet d’obtenir un handle de contexte de périphérique (ici l’écran) qui permettra de dessiner à l’écran. La fonction BeginPaint reçoit le handle de fenêtre qui a été passé comme paramètre à la fonction de fenêtre : hwnd et l’adresse d’une structure PAINTSTRUCT qui contient des informations qui peuvent être utilisées pour dessiner dans la zone client d’une fenêtre. La fonction GetClientRect permet de retrouver les coordonnées de la zone client de notre fenêtre grâce au paramètre hwnd. Enfin la fonction DrawText réalise l’affichage désiré en utilisant le handle de contexte de périphérique : hdc. Le texte affiché est contenu dans la variable szAppName, la longueur du texte est précisée grâce à la fonction strlen, la destination est notre zone client précisée par l’adresse de la structure RECT et enfin le format du texte est précisé par le dernier paramètre. La fonction EndPaint libère le handle de contexte de périphérique et signale à Windows que le traitement de WM_PAINT est terminé.

Le gestionnaire de WM_DESTROY appelle la fonction PostQuitMessage qui expédie un message WM_QUIT à la file des messages de l’application ce qui force l’application à se terminer.

Pour enrichir notre application, il faudra ajouter des gestionnaires à la structure switch case.

2- Programmation avec l’API Win32 et les classes MFC

MFC est la bibliothèque de classe C++ fournie par Microsoft pour envelopper l’API Window dans un environnement orienté objet (voir l’ensemble des classes fournies par la version 6 de Visual Studio). Dans une application MFC, on aura rarement besoin d’appeler les fonctions API. À la place, on créera des objets à partir des classes MFC et on appellera des fonctions membres appartenant à ces objets. Beaucoup de ces fonctions membres sont une enveloppe des fonctions API et portent le même nom.

Le listing du programme permettant de créer une fenêtre et d’écrire au centre de celle-ci est présenté ci-dessous :

Fichier TP12.h

class CTP12App : public CWinApp
{
public:
   virtual BOOL InitInstance();
};
class CMainWindow : public CFrameWnd
{
 public:
   CMainWindow();
 protected:
   afx_msg void OnPaint();
   DECLARE_MESSAGE_MAP()
};

Fichier TP12.CPP

#include <afxwin.h>
#include "TP12.h"
CTP12App TP12App;
BOOL CTP12App::InitInstance()
{
 m_pMainWnd = new CMainWindow;
 m_pMainWnd->ShowWindow( m_nCmdShow );
 m_pMainWnd->UpdateWindow();
 return TRUE;
}
BEGIN_MESSAGE_MAP( CMainWindow, CFrameWnd )
   ON_WM_PAINT()
END_MESSAGE_MAP()
CMainWindow::CMainWindow()
{
  Create( NULL, AfxGetAppName() );
} 

void CMainWindow::OnPaint()
{
   CPaintDC dc( this );
   CRect rect;
   GetClientRect( &rect );
   dc.DrawText( AfxGetAppName(), -1, &rect, DT_SINGLELINE | DT_CENTER | DT_VCENTER );
}

 Le cœur d’une application MFC est formé par un objet application fondé sur la classe CWinApp. La fenêtre cadre de l’application est fournie par la classe CFrameWnd. C’est pourquoi le fichier tp12.h contient les déclarations de deux classes l’une étant dérivée de CWinApp et l’autre étant dérivée de CFrameWnd.

La première classe CTP12App ne contient pas de données membres et se contente de redéfinir une seule fonction héritée de CWinApp : InitInstance. Cette fonction est appelée juste après le démarrage de l’application et son objectif est de créer une fenêtre pour cette application. En réalité, les MFC se chargent de lancer une fonction WinMain (obtenue par le fichier winmain.cpp des MFC) qui crée l’application, d’appeler InitInstance, de lancer une fonction Run qui exécute la boucle des messages et enfin d’appeler une fonction ExitInstance lorsque la boucle des messages reçoit un message WM_QUIT. Les MFC masquent donc la fonction WinMain et la boucle des messages qui existent nécessairement dans un programme Windows.

Le code de la fonction InitInstance commence par instancier un objet CMainWindow et recopie son adresse dans la donnée membre m_pMainWnd. L’instanciation de cet objet a pour conséquence de lancer le constructeur de cette classe qui appelle donc la fonction Create (de la classe CFrameWnd) pour créer la fenêtre cadre de l’application. Cette fonction ne reçoit ici que deux paramètres mais les six autres sont initialisés par défaut .

Une fois la fenêtre créée, elle est affichée grâce aux fonctions ShowWindow et UpdateWindow (fonctions membres de la classe CWnd dont CMainWindow hérite via CFrameWnd). La fonction ShowWindow prend en paramètre : m_nCmdShow qui est une variable membre de CWinApp qui renferme le paramètre nCmdShow passé à WinMain. La fonction UpdateWindow permet d’envoyer un message WM_PAINT pour forcer le dessin de la fenêtre. Pour terminer, la fonction InitInstance retourne TRUE ce qui permet à l’application de se poursuivre (FALSE met un terme à l’application).

La deuxième fonction de la classe CMainWindow : OnPaint répond au message WM_PAINT. Cette fonction commence par construire un objet CPaintDC (dérivé de CDC) qui fournit un handle sur le contexte de périphérique, ici l’écran (this est un pointeur sur l’objet traité) et un objet CRect qui sera initialisé aux coordonnées de la zone client par la fonction GetClientRect. La fonction DrawText appliquée au contexte de périphérique va permettre d’afficher le texte centré à l’écran.

Avec l’API Window, la boucle des messages permet immédiatement de savoir ce que fait le message considéré. Avec les MFC, l’association message-fonction n’apparaît pas clairement. Cette association est réalisée par la table d’interception des messages. Toute classe dérivant de la classe CCmdTarget (et c’est le cas pour la classe de notre application qui dérive de la classe CWinApp qui dérive elle-même de CCmdTarget) peut avoir sa table d’interception des messages. Pour cela, il faut ajouter la ligne DECLARE_MESSAGE_MAP à la déclaration de la classe et dans l’implémentation de la classe, il faut créer et initialiser la table d’interception en plaçant entre les lignes BEGIN_MESSAGE_MAP et END_MESSAGE_MAP des macros identifiant les messages que la classe traitera.

La macro BEGIN_MESSAGE_MAP doit préciser la classe à laquelle appartient la table d’interception et la classe de base (ici CMainWindow, CFrameWnd) car ces tables d’interception sont passées par héritage. Ceci permet à l’architecture d’application de repérer la table d’interception associée à la classe. Les macros contenues dans cette table sont définies dans un fichier d’en tête des MFC (afxmsg_.h). Le nom de la macro et de la fonction associée s’obtiennent à partir du message : pour le message WM_PAINT, la macro s’appelle ON_WM_PAINT et la fonction associée s’appelle On_Paint. Il faudra consulter l’aide pour connaître les paramètres attendus et celui retourné par cette fonction. Les paramètres passés viennent des paramètres wParam et lParam accompagnant le message. Si on veut traiter un message pour lequel les MFC ne fournissent pas de macro, il faut créer une ligne qui utilise la macro ON_MESSAGE qui prend deux paramètres : l’identificateur du message et l’adresse de la fonction associée : ON_MESSAGE(WM_MESSAGE, OnMessage). Les commandes de menu utiliseront sur le même principe la macro ON_COMMAND : ON_COMMAND(ID_APP_ABOUT, OnAppAbout).

Le dernier élément de ces fichiers est la variable TP12App (CTP12App TP12App;) qui est l’objet application et qui doit être unique et déclaré de manière globale de façon à être instancié en mémoire au tout début de l’application.

 

3- Utilisation de AppWizard et ClassWizard

Ces deux utilitaires de Visual C++ permettent de créer automatiquement un squelette d’application que le programmeur se charge ensuite de compléter. Cette application met en œuvre l’architecture document-vue. Elle est basée sur des objets documents (qui dérivent de la classe CDocument) qui contiennent les données et sur des objets vues (qui dérivent de la classe CView) qui présentent des vues de ces données. Un objet document peut avoir un nombre quelconque de vues associées mais une vue n’est associée qu’à un seul document. Dans l’architecture document-vue, la vue est une fenêtre ajustée à la fenêtre cadre de façon à servir de zone client à son parent. Ainsi lorsqu’on redimensionne la fenêtre cadre, la vue suit le mouvement.

Les MFC aident à produire des applications à architecture document-vue car :

  • elles fournissent une bonne partie de la logique requise par l’enregistrement sur disque des documents et pour leur relecture (sérialisation),
  • elles reconnaissent lorsqu’une application est sur le point de se terminer et fournissent à l’utilisateur l’occasion d’enregistrer les modifications apportées au document (fonction SetModifiedFlag),
  • elles simplifient grandement l’impression et l’aperçu avant impression.

Une application (appelée TP13) créée par ces utilitaires comprend cinq classes que l’onglet ClassView permet de visualiser :

tp13

  • la classe d’application CTP13App dérivée de CWinApp (tp13.cpp et tp13.h)
  • la classe de fenêtre cadre CMainFrame dérivée de CFrameWnd (MainFrm.cpp et MainFrm.h)
  • la classe document CTP13Doc dérivée de CDocument (tp13Doc.cpp et tp13Doc.h)
  • la classe vue CTP13View dérivée de CView (tp13View.cpp et tp13View.h)
  • la classe CAbouDialog dérivée de CDialog (visible dans l’onglet Resource View)

L’onglet FileView permet de retrouver les fichiers précédemment cités et quelques autres :

  • StdAfx.cpp et StdAfx.h : fichiers utilisés pour construire un en tête précompilé (tp13.pch) et un fichier objet précompilé (SdtAfx.obj),
  • TP13.rc                             : fichier des ressources du projet (visible dans l’onglet Resource View),
  • Resource.h                       : fichier qui contient les définitions des identificateurs que vous avez ajoutés à votre projet,
  • ToolBar.bmp                    : qui contient la barre d’outil de votre application,
  • Tp13.ico et tp13Doc.ico  : qui contiennent les icônes de l’application,
  • Tp13.rc2                           : qui contient les ressources qui ne seront pas éditées par Visual Studio (selon votre souhait),
  • ReadMe.txt                      : qui contient la description des fichiers de votre projet.

Pour aller plus loin : détails de code

La classe d’application CTP13App contient deux fonctions membres :

  • OnAppAbout qui affiche la boîte de dialogue A propos de. La table d’interception des messages contient la ligne de ON_COMMAND(ID_APP_ABOUT, OnAppAbout) qui à l’identificateur ID_APP_ABOUT du menu associe la fonction OnAppAbout
  • InitInstance qui est toujours la fonction de démarrage de l’application.

Cette fonction comprend les instructions suivantes :

  • SetRegistryKey(_T(« Local AppWizard-Generated Applications »)) qui ordonne à l’application de stocker le nom des derniers fichiers utilisés dans la base de registre (branche : HKEY_CURRENT_USER\Software\Local AppWizard-Generated Applications\TP13\Recent File List).
  • LoadStdProfileSettings() qui indique à l’architecture d’inclure dans le menu File la liste des derniers fichiers utilisés.
  • CSingleDocTemplate* pDocTemplate;
            pDocTemplate = new CSingleDocTemplate( IDR_MAINFRAME, RUNTIME_CLASS(CTP13Doc),
                             RUNTIME_CLASS(CMainFrame),       // main SDI frame window
                             RUNTIME_CLASS(CTP13View))

qui créent un modèle de document SDI (Single Document Interface) à partir de la classe CSingleDocTemplate. Ce modèle de document identifie la classe document employée pour représenter les données de l’application, la classe de fenêtre cadre et la classe de vue utilisée pour afficher ces données. Ce modèle contient aussi un identificateur (IDR_MAINFRAME) de ressources afin de charger le menu, la barre d’outils, les accélérateurs …(la macro MFC RUNTIME_CLASS retourne un pointeur vers une structure CRunTimeClass pour la classe spécifiée).

  • AddDocTemplate(pDocTemplate) qui ajoute le modèle de document créé à la liste des modèles de document maintenue par l’objet application. Chaque modèle ainsi enregistré définit un type de document géré par l’application (les applications SDI n’en gèrent qu’un contrairement aux applications MDI : Multiple Document Interface).
  • CCommandLineInfo cmdInfo et ParseCommandLine(cmdInfo) qui permettent d’initialiser un objet CCommandLineInfo avec des valeurs qui représentent les paramètres saisis sur la ligne de commande (souvent un nom de fichier).
  • if (!ProcessShellCommand(cmdInfo)) return FALSE qui traite la ligne de commande (entre autres : ProcessShellCommand appelle CWinApp::OnFileNew pour démarrer l’application avec un document vide si aucun nom de fichier n’a été spécifié ou appelle CWinApp::OpenDocumentFile pour démarrer l’application en chargeant le fichier spécifié). C’est pendant cette phase que l’architecture crée le document, la fenêtre cadre et les objets vues. Si ProcessShellCommand retourne TRUE, l’application se poursuit, sinon elle se ferme. Une fois l’application démarrée et les objets créés, la boucle des messages intervient et traite ces derniers. Un des premiers messages est un message WM_PAINT qui vient des lignes suivantes :
m_pMainWnd->ShowWindow(SW_SHOW);
m_pMainWnd->UpdateWindow();

Sous Windows98 et NT, un double clic sur l’icône d’un document exécute l’application ayant créé ce document et l’application ouvre alors ce document. Il en va de même si on choisit la commande Ouvrir du menu contextuel du document. De plus on peut imprimer un document soit par la commande Imprimer du menu contextuel, soit en déposant l’icône du document sur l’icône de l’imprimante. Pour que tout cela fonctionne, il faut que l’application « registre » ses types de document auprès du shell. Cela implique l’insertion d’une série de rubriques dans la branche HKEY_CLASSES_ROOT de la base de registre. Une application MFC simplifie cette insertion : il suffit d’appeler CWinApp::RegisterShellFileTypes avec le paramètre TRUE (dans la fonction InitInstance après la fonction AddDocTemplate). Le premier champ de cette chaîne correspond au titre de la fenêtre cadre, le second champ est le nom associé au nouveau document (lors de la sauvegarde), le troisième est le nom descriptif du type de document (nécessaire uniquement pour une application MDI pour distinguer les différents documents), le quatrième est le descriptif du type de document accompagné de l’extension par défaut (cette chaîne s’affiche dans les boîtes de dialogue Open et Save As), le sixième est le libellé (sans espace) qui identifie le type de document dans la base de registre (word.document par exemple) et enfin le septième est un descriptif pour le type de document (Application Microsoft Word par exemple). Les champs sont séparés par \n. Essayez ceci avec le TP Boîte de dialogue.

L’architecture d’application doit créer des objets documents, vues et fenêtre cadre en cours d’exécution, il faut que les classes concernées intègrent un mécanisme appelé création dynamique. MFC facilite la conception de classes dynamiques via des macros DECLARE_DYNCREATE et IMPLEMENTE_DYNCREATE. La première est à insérer dans la déclaration de la classe avec comme paramètre le nom de la classe dynamique et la seconde est à appeler en dehors de la classe avec comme paramètres le nom de la classe dynamique et le nom de la classe de base. On retrouve l’appel de ces macros dans les classes CxxxDoc, CxxxView et CMainFrame.

La classe Document permet de mémoriser les données. Habituellement, elles sont rangées comme variable membres de cette classe. Une encapsulation stricte impose que les données soient privées et que l’on y accède via des fonctions membres publiques.

La classe vue sert à visualiser les données du document et à traduire les saisies de l’utilisateur (en particulier les messages souris et clavier qui sont reçus par les objets vues). Cette classe possède une fonction OnDraw qui est activée par la réception du message WM_PAINT et qui est codée de la manière suivante :

void CTP13View::OnDraw(CDC* pDC)
{
  CTP13Doc* pDoc = GetDocument();
  ASSERT_VALID(pDoc); //test du pointeur (non nul)
}

elle reçoit un pointeur sur un contexte de périphérique : pDC. Ce pointeur sera adapté à l’opération à effectuer, c’est à dire qu’il pointera la zone client pour pouvoir rafraîchir l’affichage et pointera l’imprimante lorsque l’utilisateur souhaitera imprimer le document (c’est l’architecture d’application qui se charge d’adapter le pointeur). Dans cette fonction, nous pouvons aussi voir qu’il nous est fourni un pointeur vers le document associé à la vue : pDoc. Les attributs et les méthodes de la classe document (ici CTP13Doc) seront donc accessibles pour réaliser un affichage (ou une impression). Une fonction peut être ajoutée à la classe View : OnInitialUpdate qui est appelée lors de l’ouverture-création d’un document. Elle permettra donc de réaliser les initialisations des données membres de la classe vue.

La classe fenêtre cadre CMainFrame contient une fonction OnCreate qui crée la fenêtre cadre avec une barre d’outils et une barre d’état et une fonction PreCreateWindow que l’on peut compléter en utilisant le paramètre CREATESTRUCT& cs pour modifier la fenêtre lors de son ouverture.

Toutes les classes possédent la déclaration DECLARE_MESSAGE_MAP() ce qui fait qu’elles peuvent toutes traiter des messages de commande. Ceci est déclaré entre les macros BEGIN_MESSAGE_MAP et END_MESSAGE_MAP.

Continuer l’apprentissage