Introduction

Nous allons décrire dans cet article la facilité de la conception d’une application Windows sur Visual C++ 6.0, assistée par AppWizard et les MFC. Dans un premier lieu, pour démontrer l’avantage apporté par l’utilisation des MFC, nous vous décrierons très rapidement la structure d’une application Win32 sans faire appel aux MFC.. Ensuite, pour entrer dans le vif du sujet, nous présenterons les solutions fournies par Microsoft dans le cadre d’un développement rapide et efficace : AppWizard, ClassWizard. Par la suite, nous détaillerons étape par étape, la réalisation d’une application de base de type ” Hello World “, avec l’aide de ces outils et des MFC. Dans la dernière partie de l’article, nous aborderons la réalisation d’un Agenda qui sera finalisé dans un deuxieme article. Cet agenda illustre réellement l’utilité des MFC, en effet, l’ensemble de ces actions possibles sera exclusivement développé avec des MFC.

1er partie : Structure d’un programme Windows

Différence principale entre application Win32 et Dos

Lorsque vous écrivez un programme sous Dos, c’est le système d’exploitation qui obéit. Vous appelez le système lorsque vous avez besoin d’un service. Concrètement, vous avez la possibilité de vous adressez directement aux composants matériels de votre ordinateur. Sous Windows, la situation est renversée. En effet, votre programme est asservi et c’est Windows qui commande. Ainsi lorsque vous utilisez un programme Windows, vous dialoguez avec Windows, qui se charge de communiquer avec l’application à votre place. Ce fonctionnement est justifié puisque comme nous le savons, Windows autorise l’exécution simultanée de plusieurs programmes. Il est donc indispensable que Windows puisse gérer le partage des ressources de l’ordinateur entre ces différents programmes, dans le but de maintenir une cohérence et stabilité du système.

Caractéristiques des programmes sous Windows

Les programmes Windows réagissent à des événements. Le code d’une application Windows est en grande partie consacré au traitement des événements provoqués par l’intervention de l’utilisateur. Concrètement, vous aurez par exemple, une partie de code définie pour répondre à un clic sur un bouton “OK”. Windows enregistre chaque événement dans un message, et place ce dernier dans une file d’attente associée au programme auquel le message est destiné. Ce message est traité par une fonction déterminée pour la gestion de ces messages : WndProc( ) ou WindowsProc( ). En détails, lors de l’envoi d’un message à votre programme, Windows appelle la fonction WindowsProc( ), et transmet les données nécessaires à votre programme pour répondre à l’évènement, sous la forme d’arguments de cette fonction.Remarques : il n’est pas nécessaire d’écrire du code pour traiter chaque message. En effet, vous pouvez retenir ceux qui présentent un intérêt pour votre programme et y répondre, et renvoyer les autres à Windows en appelant une fonction standard de Windows : DefWindowsProc( ), qui se chargera de les traiter. Toutes les communications entre une application et Windows font appel à l’interface de programmation d’application Windows ou API Windows. Il s’agit en faite d’un millier de fonctions fournies en standard avec Windows et destinées à être employées par votre applications. L’API est constituée de tant de fonctions qu’il est quasi impossible de les exploiter en l’état, il est déjà difficile d’essayer de comprendre ce qu’elles permettent de faire.

Structure d’un programme Windows

Sous sa forme la plus réduite, un programme écrit exclusivement à l’aide de l’API Windows comporte deux fonctions :

– une fonction WinMain( ) appelée au début de l’exécution du programme et contenant son initialisation de base, par analogie, il s’agie de la fonction main( ) d’un programme console, – et la fonction WindowsProc ( ).

Ces deux fonctions composent un programme complet, mais elles ne sont pas directement liées. En effet, WinMain( ) n’appelle pas WindowsProc( ). C’est Windows qui assure l’intégration du programme, en établissant le lien avec ces deux fonctions : la fonction WinMain( ) communique avec Windows en appelant certaines des fonctions de l’API Windows. Il en va de même pour WinProc( ).

Remarques : L’API Windows n’est pas spécialement conçue pour Visual C++, ni pour le C++ de manière générale. Elle doit pouvoir être utilisée dans des programmes écrits dans une variété de langages, dont la plupart ne sont pas orientés objet.

Code complet d’un programme Windows pour créer une fenêtre

Résultat :

Avantages apportés par l’utilisation des MFC et de AppWizard

Vous pouvez constatez la complexité du code rédigé pour afficher une simple fenêtre. Imaginez pour une fenêtre plus complexe avec plusieurs boutons,etc… Le but de cet article n’étant pas de vous apprendre les subtilités de ce code, nous ne nous attardons pas sur des explications relatives à ce sujet. En effet, l’assimilation des notions concernant la création et l’affichage d’une fenêtre, la boucle de messages et l’analyse des messages transmis à une application sont tous inclus dans le code que Visual C++ peut générer automatiquement. Visual C++ présente les fonctions de l’API Windows selon une approche orientée objet et facilite l’utilisation de l’interface (API) en proposant davantage de fonctionnalités par défaut sous une forme beaucoup plus structurée et conviviale grâce à la bibliothèque Microsoft Foundation Classes ou MFC. Visual C++ offre des outils de développement : AppWizard, ClassWizard, pour simplifier l’édition et la gestion de notre code. Nous détaillerons ces outils dans la suite de l’article.

Suite de l’article<% article_numero = 44 %>

2e partie : Les MFC et les outils de développement proposés par Visual C++

Les MFC (Microsoft Foundation Classes)

Les MFC constituent un ensemble de classes prédéfinies autour desquelles s’articule la programmation Windows avec Visual C++ : l’écriture d’un programme Windows entraîne la création et l’utilisation d’objets MFC, ou d’objets de classes dérivées des MFC. Les objets créés contiennent des fonctions membres permettant la communication avec Windows, l’échange de messages entre eux, et le traitement des messages Windows. Les MFC représentent un vaste domaine et font intervenir un grand nombre de classes. Elles composent une structure complète de développement d’applications, où il vous suffit d’effectuer les opérations de personnalisation dont vous avez besoin pour adapter les programmes à vos exigences.

Par convention, sachez que toutes :

– les classes des MFC portent des noms qui commencent par C, comme CDocument ou CView ; – les données membres d’une classe MFC commencent par le préfixe m_.

Il va de soi, qu’il est préférable d’adopter cette convention lors de la définition de nos classes, ou de leur dérivation à partir des classes de la bibliothèque MFC.

Il est inutile de retenir les classes dont vous avez besoin dans votre programme et les classes de base à utiliser dans leur définition. Comme nous allons le voir, c’est Visual C++ qui s’en charge.

Les outils de développement de Visual C++

Dans le développement de vos programmes Windows, vous serez amené à vous servir de deux outils.

AppWizard, pour construire le code élémentaire, squelette de votre application Windows au moyen de la bibliothèque MFC. Il s’agit d’un assistant à la programmation Windows extrêmement performant, car, pour créer votre application, il vous suffit de personnaliser un programme tout prêt. Il contient même des explications sur l’endroit où vous devez insérer le code spécifique à l’application. – ClassWizard, facilite l’extension des classes générées par AppWizard comme éléments de base de votre programme Windows. Il permet d’ajouter des classes reposant sur des classes des MFC, pour la prise en charge des fonctionnalités à intégrer dans votre programme. Il convient de noter que ClassWizard ne reconnaît pas les classes ne dérivant pas des classes MFC.

Notre programme “Hello World” pour illustrer les outils

L’étendu des services que peuvent fournir ces outils sont conséquent, dans cet article, nous n’en présenterons qu’une partie, nécessaire à l’élaboration de « Hello World » et de la conception de notre agenda. L’application à saisie de notre nom, et après confirmation, nous affichera un message « Hello notre_nom ».

Les notions abordées

– Boite de dialogue : La plupart des programmes Windows font appel à des boite de dialogue pour gérer certaines de leurs entrées de données : cliquez sur une commande de menu et une boîte de dialogue s’affiche, contenant divers contrôles vous permettant de saisir des informations. Pratiquement tous les éléments de la boîte de dialogue sont des contrôles. Pour être plus précis, une boîte de dialogue est une fenêtre, et chacun de ses contrôles une fenêtres spéciales.

– Les 3 différents contrôles abordés :

– Contrôle statique : il contient des informations statiques, telles que titres ou instructions, ou simplement des éléments d’illustration ; – Bouton commande : comme OK ou ANNULER, permettent généralement de refermer une boite de dialogue, ou de confirmer un choix ; – Contrôle d’édition : réduit à sa simple expression, il permet de saisir et de modifier une ligne de texte.

– à savoir pour la gestion des classes avec ClassWizard :

Comme nous l’avons vu plus haut, Windows communique avec notre programme en lui envoyant des messages ; de même, les MFC vous permettent de définir des fonctions destinées à gérer les seuls messages qui vous intéressent, sans vous préoccuper des autres. Ces fonctions sont appelées gestionnaires de messages, ou simplement gestionnaires. Comme votre application est un programme MFC, les gestionnaires de messages sont dans tous les cas des fonctions membres de l’une des classes de votre application. Une table de correspondance des messages établit la relation entre un message donnée et la fonction qu’il est censé prendre en charge à l’intérieur de votre programme, sachant que chaque classe du programme habilitée à gérer les messages Windows en possède une. La table de correspondance des messages de la classe se présente sous la forme d’un tableau de fonctions membres prenant en charge les messages de Windows. Toutes ses entrées associent un message spécifique à une fonction ; de sorte que l’arrivée de chaque message déclenche l’appel de la fonction correspondante.AppWizard et ClassWizard créent automatiquement une table de correspondance de messages lorsque vous ajouter une classe pouvant gérer des messages. Dans cette table c’est principalement ClassWizard qui se charge des ajouts et des suppressions, même si dans certain cas vous êtes amené à modifier la table vous-même.

Notre programme étape par étape

Lancez AppWizard :

– de la barre de menus : File | New ou CTRL+N ; – choisir : MFC AppWizard (exe) ; – appeler votre projet : HelloToi.

Lorsque vous cliquez sur OK, vous êtes à la première étape de la boîte de dialogue d’AppWizard permettant de créer l’application. On va choisir une application à base de boîte de dialogue. L’aperçu du type d’application est affiché (coin supérieur gauche) pour vous donner une idée du résultat final. Le reste des étapes de AppWizard ne nous intéresse pas dans le cadre de notre exemple. On clique pour fermer AppWizard et confirmer nos choix sur FINISH.

Vous obtenez un récapitulatif des éléments insérés dans votre projet par AppWizard. Il vous suffit de cliquer sur OK pour qu’AppWizard crée tous les fichiers programme composant une application.

On va supprimer les différents contrôles générés automatiques, pour voir comment en définir nous même. Pour cela, on effectue un clic gauche sur un contrôle et on supprime avec le bouton SUPPR du clavier.

On va par la même occasion, réduire la taille de la boite de dialogue avec les carrés de redimension, situer au niveau de tous les bord de notre fenêtre.

On va crée un contrôle statique , avec le bouton qui se trouve sur le menu control, puis on le définit sur notre boîte dialogue avec le curseur. Le texte affiché est « Static » par défaut, pour le modifier en « Votre nom : », il faut accéder aux propriétés du control, pour cela : on effectue un clic droit sur ce contrôle puis on sélectionne PROPERTIES.

On change dans CAPTION, le texte en « Votre nom : ». CAPTION correspond juste au texte afficher par le contrôle. On confirme en refermant la fenêtre avec la petite croix de la fenêtre.

On va définir maintenant un contrôle d’édition. On accède au propriété de ce contrôle, et on se rend compte qu’il n’a pas de champ CAPTION, en effet, les contrôles sont différents et leurs propriétés aussi. IDC_EDIT1 est l’ID qui identifie le contrôle nouvellement crée. Pour plus de clarté, on va renommer ce contrôle en IDC_NOM.

De même, il faut penser à récupere le texte qu’on va saisir, pour cela, on va utiliser ClassWizard afin de creer une donnée membre qui stockera le texte tapé dans le contrôle. Pour y accéder, on effectue un clic droit sur ce contrôle et on sélectionne ClassWizard.

Celui-ci s’ouvre sur l’onglet qui nous intéresse : Member Variables.

En cliquant sur le bouton : Add Variable, on peut associer le texte saisie dans ce contrôle à une variable de type CString (chaîne en français), qu’on nomme m_nom. (m_ convention, voir plus haut dans l’article).

NB: CString fait partie des classes fournies par Microsoft (MFC). Ses intances permettent de faire des manipulations sur les chaines de caractères bien plus interressante que l’aurait permis une simple variable char.

On confirme par OK, et on sort de ClassWizard par OK aussi. On va crée un contrôle bouton commande. De la même façon que pour le contrôle statique et d’édition, on change sont ID en « IDC_OK », et CAPTION en « OK ».

Il faut maintenant associer une action à notre bouton OK. Pour cela, on va aussi ce servir de ClassWizard. De l’onglet : Message maps, on va associé IDC_OK à un message. Quand on sélectionne IDC_OK, Visual C++ nous associe deux actions possibles avec ce genre de contrôle, nous allons va choisir BN_CLIKED (est cliquez).

On clique sur :Add Function, puis définir la fonction qui va gérer ce message. Visual C++ nous propose OnOK comme nom de fonction, cela nous convient, on confirme par OK.

On clique sur Edit Code, pour écrire le code correspondant à cette fonction. Visual C++, nous y amène automatiquement.

On tape le code suivant :

On compile et exécute notre application :

On rentre un nom. On clique sur OK.

IoI, Za marche !!! Pour fermer, l’application, on clique OK, puis la croix de la fenêtre.Remarques : Pour revenir à la boite de dialogue, on clique sur IDC_HELLOTOI_DIALOG.

Pour revenir, sur le code, de la même fenêtre on clique sur l’onglet Classes, on dévellope avec la croix les HelloToi classes est clique sur la classe à modifier qu nous interesse.

Conclusion de la 2e partie

Pour obtenir une fenêtre avec des boutons, qui traite nos données, nous n’avons écrit que 4 lignes de code qui ne concernent pas l’interface graphique générée unique avec AppWizard. L’efficacité et l’aide apporté par cet outil par rapport à l’exemple de la fenêtre de notre 1er partie est incontestable. Nous allons maintenant aborder la 3e partie de notre article : l’agenda. Cette Agenda sera développé avec les outils précédemment décrits et une classe des MFC qui gère pour nous les liste chaînés. Vous verrez ainsi, une autre classe des MFC qui nous facilite la vie.

Suite de l’article<% article_numero = 44 %>

3e partie : Création d’un agenda gâce aux MFC

Manipulation des données membres via UpdateData()

Dans notre précédent exemple “Hello World”, vous avez sans doute remarqué l’utilisation d’une fonction nommée UpdateData. Cette fonction permet au programme de récupérer, et d’initialiser les données membres des contrôle d’une fenetre (par exemple, le texte tapé dans un contrôle d’édition).En fournissant l’argument true à cette fonction le programme récupère l’intégralité des données entrés par l’utilisateur via l’interface graphique, et les stock dans les variables créer à cet effet. C’est d’ailleurs la méthode que nous avons utilisé dans notre programme Hello World, nous avons créer une “Member variable” via Class Wizard que nous avons appelé m_nom puis nous appelons la fonction UpdateData(true) lors d’un clic sur le bouton OK. Ainsi le texte tapé dans le contrôle d’édition se retrouve copié dans m_nom.

L’opération inverse est bien évidement possible en passant l’argument false à la fonction UpdateData.Dans ce cas, la valeur stocké dans la “Member Variable” est envoyé dans le contrôle lui étant rattaché. Ainsi si nous stockions dans m_nom la chaine de caractère “Test”, et que nous appelions la fonctions UpdateData(false) , le contrôle IDC_NOM aurait affiché “Test”.

MFC + Liste chainée = CList

Vous avez certainement déjà utilisé des listes chaînées dans vos programmes au paravent. Si ce n’est pas le cas, sachez que la conception d’une liste chaînée consiste à relier, via l’utilisation de pointeur, une série d’entités de même type, le plus souvent des structures. Chaque entité ayant l’adresse de l’entité ayant l’adresse de la suivante (et de la précédente dans le cas d’une liste doublement chaînée).

L’élaboration d’une liste chaînée, ainsi que le groupe de fonctions permettant de la manipuler étant une opération relativement délicate, Microsoft à inclus dans les MFC la Class CList qui simplifie grandement l’implémentation d’une liste chaînée dans une application. De plus un nombre impressionnant de méthodes permettant de manipuler cette liste chaînée est disponible.

La déclaration d’une liste chaînée CList se fait de la manière suivante:

CList <Type_objet,Type_transfert&> nom_liste;

Type_objet étant le type d’élément qui composera la liste chaînée (par exemple CBox), etType_transfert représente la façon dont les éléments seront injectés dans la liste chaînée, ici il s’agit d’un transfert par référence, il peut également se faire par pointeur ou par valeur (une copie donc). nom_liste étant le nom de l’objet désignant notre liste chaînée. Cet objet possède une batterie de méthodes permettant de manipuler la liste chaînée, en voici quelques une :

Accés de tête ou de fin de liste
GetHead Renvoie le premier élément de la liste
GetTail Renvoie le dernier élément de la liste
Opérations
RemoveHead Supprime le premier élément de la liste
RemoveTail Supprime le dernier élément de la liste
AddHead Insert un élément en tête de liste
AddTail Insert un élément en fin de liste
RemoveAll Supprime tous les éléments de la liste
Iteration
GetHeadPosition Retourne la postion du premier élément de la liste
GetTailPosition Retourne la position du dernier élément de la liste
GetNext Renvoie l’élément suivant
GetPrev Renvoie l’élément précédent
Récupération et modification
GetAt Renvoie un élément à une position donnée
SetAt Remplace l’élément d’une position par un autre
Insertion
InsertBefore Insert un élément juste avant celui se trouvant à la position précisée
InsertAfter Insert un élément juste aprés celui se trouvant à la position précisée
Recherche
Find Renvoie la position d’un élément passé en paramêtre
FindIndex Renvoie la position, sur une base d’index de 0, d’un élément passé en paramêtre
Status
GetCount Renvoie le nombre d’élement de la liste
IsEmpty Test si la liste est vide (aucun élément)

Comme vous pouvez le constater, ces fonctions sont d’une grande utilité dans la gestion d’une liste chaînée !!!

Pour plus d’information sur CList, consultez la MSDN de Visual C++.

Notre programme Agenda pour illustrer ces nouvelles fonctionnalitées

Le but de ce deuxième exemple est de montrer que l’élaboration d’un programme d’une complexité moyenne se fait de manière très simple via les MFC.

Nous allons donc développer un agenda qui stockera (en mémoire uniquement) le nom, prénom, adresse et numéro de téléphone de contacts. Il sera possible d’ajouter, supprimer, modifier et de naviguer dans notre agenda. Il va sans dire que ces contacts seront gérés en mémoire via liste chaînée de type CList.

Etape 1: Création de l’application MFC Agenda avec AppWizzard

Puis…

On click sur Finish

Etape 2 : Insérer les contrôles de notre agendas comme nous l’avons fais avec “Hello World”

On continue, en attribuant à chaque contrôle facilement identifiable

ID choisis dans notre exemple:

Contrôle d’édition pour le nom IDC_NOM
Contrôle d’édition pour le prénom IDC_PRENOM
Contrôle d’édition pour l’ adresse IDC_ADRESSE
Contrôle d’édition pour le numéro de téléphone IDC_PHONE
Contrôle bouton suivant IDC_NEXT
Contrôle bouton précédent IDC_PREV
Contrôle bouton ajouter IDC_ADD
Contrôle bouton modifier IDC_MODIDY
Contrôle bouton supprimer IDC_DEL

Etape 3 : Ajouter les variables membres des contrôles avec ClassWizzard (Ctrl+W)

Etapes 4 : Définir les fonctions qui seront appelé en cas de clic sur un des boutons

Etape 5 : Elaboration et ajout de notre class contact dans notre projet Agenda

Nos contact seront en fait des objets d’une class contact ayant 4 variables membres publics de type CString que vous deviner sans doute : nom,prénom,adresse,et phone.

En voici la forme:

class contact

{

public :

CString nom,prenom,adresse,tel;

};

Pour insérer cette classe à notre projet il suffit de cliquer sur Agenda classes dans l’onglet ClassView:

Nous choisirons ensuite l’option Generic class comme type de class, car notre classe n’est pas une classe MFC:

Nous entrons le nom de notre classe, puis validons:

Nous constatons que notre classe est maintenant ajouter à notre projet.

Il nous suffit maintenant d’ajouter les 4 membres de cette classe, et l’header de la class CContact dans le .cpp qui l’utilisera (c’est à dire AgendaDlg.cpp).

Pour ajouter ces 4 membres nous faisont un clic droit sur notre class CContact , et choisissont l’option Add Member Variable

Dans la fenetre suivante, nous entrons le type de variable, et le nom de la variable, et Visual C++ s’occupe d’injecter cette déclaration à l’intérieur du code

nous recommençons cette opération pour chaque membres à ajouter dans notre classe contact. Puis nous ajoutons “Contact.h” dans AgendaDlg.cpp

Etape 6 : Déclaration de notre liste chainée

Maintenant que nous avons notre classe contact, nous pouvons déclarer notre liste chainé reposant sur ce type de class (CContact). Pour se faire nous allons ajouter dans la déclaration de la classe AgendaDlg la ligne de code suivante:

Nous déclarons ainsi un objet CList qui s’apelle list_contact qui stockera sous forme d’une liste chaînée les différents contacts que nous entrerons, ainsi qu’une variable current_pos de type POSITION représentant la position courante dans la liste chainée.

Etape 7 : Ajout d’une objet tampon nommé current

Etant donnée que les données récupérer avec UpdateData(true) ne sont pas retourné sous forme d’objet CContact nous de vont créer un objet current dans lequel nous stockerons les valeurs récupérées par le UpdateData.

Etape 8 : Editer la méthode OnAdd

Nous devons maintenant éditer le code de la fonction OnAdd pour que les informations entrées dans l’interface soient insérées sous forme d’une entité de la liste chaînée:

Etape 9 : Editer la méthode OnModify

Ici nous allons utiliser la méthode SetAt pour modifier un contact à une position donnée:

Etape 10 : Editer la méthode OnDel

Pour supprimer un élément de la liste chainée nous allons utliser la méthode RemoveAt :

Etape 11 : Editer la méthode OnNext

Pour récupérer l’objet suivant dans la liste chaînée list_contact, il suffit d’appeler la méthode GetNext(position_courante). Cette méthode renvoie l’élément suivant celui se trouvant à la position position_courante. Il convient également de vérifier lors d’un appel à la fonction GetNext que nous ne “sortons pas” de la liste chaînée et accédons à des zones mémoires protégées. Le code de la méthode OnNext est donc celui ci:

Etape 12 : Editer la méthode OnPrev

Comme vous pouvez l’imaginer très peu de choses différencie cette méthode avec la précédente, nous appelons tous simplement la méthode GetPrev, au lieu de GetNext et nous vérifions si nous ne somme pas au début de la liste avec GetHeadPosition au lieu de GetTailPosition, le reste du code restant inchangé pour des raisons évidentes.

Etape 13 : Compiler le projet et l’executer

Après avoir compilé le projet, nous pouvons voir que notre application fonctionne parfaitement bien, et gère toutes les options que nous voulions !!!

Télécharger le code source ici

La sérialisation : un puissant systeme de sauvegarde

Bien que notre programme agenda fonctionne parfaitement bien, il à un handicape majeur : les données sont stockées en mémoire, résultat en quittant l’application, nous perdons l’intégralité des informations entrées. Une solution serait de créer un fichier texte, et d’y stocker les informations. Il faudrait alors définir un certain nombre de balises à l’intérieur du fichier texte pour que des fonctions qu’on rajouterait au code puissent se retrouver dans le fichier. Il va sans dire que cela demanderait une quantité de travail supplémentaire non négligeable.

Heureusement Microsoft Visual C++ 6.0 propose une solution permettant de gérer l’intégralité des données manipuler par votre programme. Il s’agit de la sérialisation. Cette méthode permet de sauvegarder des objets et de les recharger en mémoire avec un minimum d’effort, toute la partie concernant la gestion du fichier de sauvegarde est géré par le processus de sérialisation. En fait tous ce qu’il nous reste à faire c’est d’implémenter cette méthode dans nos classes, et de procéder à une sauvegarde ou à un chargement des objets du programme en fonction des besoins de l’utilisateur.

Nous nous servirons donc de la sérialisation pour stocker les informations manipulées par notre agenda, c’est à dire notre liste chaînée contenue dans l’objet list_contact.