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

Utiliser le C++ managé et le framework.net 2.0 dans des applications Win32 et MFC avec Visual C++ 2005

A travers ce tutoriel, vous trouverez une introduction à la migration au framework .net 2.0 à travers l'utilisation du langage C++/CLI pour des applications Windows (API, MFC). Je détaillerais aussi, à titre d'exemple concret, comment sérialiser en XML avec le framework.net 2.0.

Article lu   fois.

L'auteur

Site personnel

Liens sociaux

Viadeo Twitter Facebook Share on Google+   

1.Introduction

Vous êtes un développeur qui avez des bases dans le langage C/C++ ainsi qu'une pratique de la programmation Windows à travers son API ou à travers les Microsoft Foundation Classes (MFC) et vous souhaitez vous orienter vers la programmation .Net grâce au framework 2.0 en C++/CLI ?
Alors ce tutoriel est pour vous.

Vous trouverez dans ce cours, une introduction à la syntaxe C++ appliquée à .Net. Je montrerai comment utiliser du C++ managé et comment utiliser le framework 2.0 dans des applications non managées, à partir d'un programme utilisant les API windows et à partir d'un programme MFC.

J'illustrerai avec un programme exemple la sérialisation en XML, en utilisant le framework 2.0.

Ce tutoriel a pour vocation d'illustrer la programmation .net en C++ et n'est pas une présentation exhaustive du framework 2.0. Vous pourrez retrouver plus d'informations à ce sujet sur https://dotnet.developpez.com. Je tiens à aborder ici les caractéristiques de l'utilisation du framework 2.0 et du C++/CLI dans des applications Windows développées avec l'API Win32 ou les MFC.

A travers ce tutoriel, j'utiliserai le logiciel Visual Studio 2005 de Microsoft et plus particulièrement Visual C++ 2005 ainsi que le framework dotnet 2.0.

J'appellerai indifféremment C++ managé (managed) , C++/CLI , C++.Net, MC++ le langage que Microsoft a établi pour utiliser son framework.net.

Pour commencer, vous pouvez trouver une présentation de Visual Studio 2005 à cette adresse.

2.Pourquoi passer au C++ managé ?

La première question qui vient à l'esprit quand on programme depuis longtemps sous Windows, à travers l'API ou les MFC, est : " Pourquoi passer au C++.net ? ".

  • La première raison est bien sur de pouvoir accéder au framework.net de Microsoft. Il est impossible d'y accéder depuis du code non managé, il est donc primordial de passer au C++ managé.
  • Pour porter son application petit à petit en C++ managé en utilisant la possibilité de combiner du C++ non managé et du C++ managé. Ainsi, il est facile d'accéder au framework en intégrant des classes ou des fonctions en C++ managé dans son application existante et de migrer petit à petit son application.
  • Parce que WinFX, la nouvelle API des OS longhorn est une API managée. Démarrer dès maintenant avec le framework .Net permettra d'intégrer ces fonctionnalités plus facilement.
  • Pour utiliser les nouvelles fonctionnalités du C++/CLI, comme le garbage collector pour ne citer que lui.
  • Pour pouvoir continuer à appeler des classes développées en C++ non managé à travers des wrappers qui permettent d'appeler des dlls ou des librairies en C++ non managé.
  • Pour pouvoir accéder aux objets COMC++ à travers le support de code non managé ou avec les extensions managées.
  • Pour la liberté de pouvoir continuer à utiliser du C++ non managé pour gérer du code critique dont les performances doivent être maximales.
  • Enfin, ceci n'a rien à voir avec le C++ managé, mais passer à Visual C++ 2005 permet d'avoir un compilateur performant qui gère mieux la norme C++ ISO.

3.Le langage C++/CLI

Je ne vais pas faire une longue présentation de ce langage en m'attardant sur la définition des nouveautés dans le C++/CLI ; je vous renvoie vers la documentation des spécifications.
Vous pourrez aussi trouver à cette adresse, une introduction au monde du C++/CLI. Cet article définissant les concepts inhérents au C++/CLI. Aller au tutoriel d'introduction au monde du C++/CLI.

Enfin, je vais vous présenter rapidement les concepts que j'utilise dans ce tutoriel.

3.1.Définition rapide de .Net et du framework

Pour résumer en termes simples, .Net représente la volonté de Microsoft de délivrer le logiciel comme un service.
Pour les développeurs, il s'agit d'une nouvelle approche de conception, de développement et de déploiement du logiciel.
Pour les architectures, il s'agit d'augmentation de capacité de déploiement des applications Internet : moins sur les clients et plus sur les serveurs, pour une meilleure utilisation d'internet, orientant le futur d'Internet vers un système d'exploitation plutôt qu'en un réseau d'ordinateurs.

Le framework .Net comprend tout ce qu'il faut pour développer, déployer, exécuter des Web Services, des applications Web et des applications Windows. On peut voir le framework comme une application 3-tiers :

  • Technologies pour développer des applications
  • Bibliothèque de classes du framework .Net
  • Common language runtime (CLR)

Le CLR est le moteur d'exécution du noyau .Net pour exécuter des applications. Il apporte des services tels que la sécurité d'accès de code, la gestion de la vie d'un objet, la gestion des ressources, la sûreté des types, etc ...

En terme de déploiement, Visual Studio 2005 propose des outils de déploiement extrêmement simples et rapides. De plus, il faut veiller à ce que le framework 2.0 soit installé sur la machine.

3.2.Le garbage collector

Le garbage collector est un mécanisme qui permet à un ordinateur de détecter et de supprimer les objets managés du heap qui ne sont plus référencés par une application.
Le garbage collector du framework .net ajoute la fonctionnalité intéressante de compacter la mémoire après libération des objets managés inutilisés.
Le garbage collector est une révolution de programmation, car il annonce la fin des fuites mémoires, qui font rager tout développeur. Un développeur n'a plus à se soucier d'appeler la destruction de ses objets avec l'opérateur delete.

3.3.Les objets managés

On définit une classe managée par le mot clé ref.

 
Sélectionnez
ref class X
{
	System::String ^ str;
};

La création d'un objet managé se fait grâce à l'opérateur gcnew. Il est identique à new, sauf qu'il crée l'objet sur le tas CLI. Il retourne un handle sur l'objet managé alloué.

 
Sélectionnez
String ^ str = gcnew String("Ma chaîne managée");

Remarque : Il n'y a pas besoin de delete correspondant au gcnew, c'est le garbage collector qui s'occupe de libérer la mémoire lorsque l'objet n'est plus référencé. Nonobstant, l'opérateur delete existe toujours, et peut être utilisé pour détruire explicitement l'objet.

3.4.Les handles

Cela fait quelques exemples que j'utilise ^. Qu'est-ce donc ?
Il s'agit d'un handle vers un objet managé qui est en fait une référence vers cet objet managé. Attention ce n'est pas un pointeur. Un handle est une référence sur un objet managé sur le tas (heap) managé, alors que les pointeurs pointent vers une zone mémoire. Les handles n'ont pas besoin d'être détruits grâce au garbage collector. Comme dit au dessus, on utilise gcnew pour instancier un handle.

3.5.Les espaces de nom (namespace)

Les classes (objets) .Net sont regroupées sémantiquement en catégories, sous la forme de bibliothèques. Elles forment un espace de noms : namespace. Exemple : le namespace System::Collections contient les différents objets de collections du framework.net.
Ils sont accessibles directement en indiquant le chemin et en utilisant les :: ou bien en important le namespace directement avec using namespace.

 
Sélectionnez
System::Collections::ArrayList ^ tableau = gcnew System::Collections::ArrayList();

ou bien :

 
Sélectionnez
using namespace System::Collections;				
ArrayList ^ tableau = gcnew ArrayList();

3.6."It Just Work"

Sous Visual Studio 2003, la technologie d'intéropabilité s'appelle IJW : "It Just Works". Avec Visual Studio 2005, elle est désormais appelée "Interop Technologies". Vous entendrez parler indifféremment des deux, qui représentent le même concept.
Pour toute méthode native dans une application, le compilateur crée un point d'entrée managé et un point d'entrée non managé. L'un deux est l'actuelle méthode d'implémentation alors que l'autre permet de créer un pont et permet l'utilisation des opérations de Marshaling. C'est le point d'entré managé qui est toujours utilisé, sauf quand le compilateur ne peut pas retranscrire le code en MSIL ou lorsque l'on utilise le "#pragma unmanaged".
IJW permet d'accéder directement, sans ajouter de code, à des méthodes non managées à partir de code managé. On peut de même utiliser des wrapper pour avoir un accès direct aux objets COM, ou aux dll natives.
Toujours sans code ajouté, sans DLLImport, le méchanisme IJW est bien souvent plus rapide. Il s'agit juste d'inclure le fichier d'entête et de linker avec la librairie d'import.

4.Intégrer du C++ managé à une application Win32 développée avec l'API

Nous allons montrer, dans ce paragraphe, comment intégrer (mixer) du C++ managé avec un programme WIN32 classique développé avec l'API Windows.

Tout d'abord, créer un nouveau projet de type Win32 / Win32 Project

Image non disponible

Utiliser le wizard pour créer une application windows, sans fichier généré.

Image non disponible

Ajouter un fichier CPP ainsi qu'un fichier de ressources.

Image non disponible

Dans les ressources, insérer une nouvelle dialog, changer le titre, ajouter trois static ... Bref, vous la customisez pour qu'elle ressemble un peu à l'image ci-dessous. (n'oubliez pas d'utiliser le panneau de propriétés pour changer les captions et centrer la dialogue, en bas à droite)

Image non disponible

Nous allons maintenant écrire le squelette de l'application Win32.
Premièrement les includes :

 
Sélectionnez
#include <windows.h>
#include "resource.h"

Ensuite le main, qui va appeler la boite de dialogue :

 
Sélectionnez
int APIENTRY WinMain(HINSTANCE hInstance, HINSTANCE hPrevInstance, LPSTR lpCmdLine, int nCmdShow)
{
	DialogBoxParam(hInstance, (LPCTSTR)IDD_DIALOG1, 0, dialogProc, 0);
	return 0;
}

Ensuite la callback qui gère la boite de dialogue :

 
Sélectionnez
BOOL CALLBACK dialogProc(HWND handleDialog, UINT mssg, WPARAM wParam, LPARAM lParam)
{
	static HWND hStatic1, hStatic2, hStatic3;
	switch(mssg)
	{
		case WM_INITDIALOG:
			{
				SetClassLong(handleDialog, GCL_HICON, (LONG)LoadIcon(0, IDI_APPLICATION));
				hStatic1 = GetDlgItem(handleDialog, IDC_STATIC1);
				hStatic2 = GetDlgItem(handleDialog, IDC_STATIC2);
				hStatic3 = GetDlgItem(handleDialog, IDC_STATIC3);
				return TRUE;
			}
		case WM_COMMAND:
			switch(wParam)
			{
				case IDOK:
				case IDCANCEL:
					EndDialog(handleDialog, 0);
					return 0;
			}
			break;
	}
	return 0;
}

Rien de sorcier donc, on récupère les handles des statics à l'initialisation, et on ferme le tout lors d'un click sur les boutons.

Maintenant, essayons d'intégrer du c++ managé.
Pour ce faire, nous allons préciser dans l'entête, que nous allons utiliser le namespace system :

 
Sélectionnez
using namespace System;

Puis nous allons récupérer la version du framework, la version de l'OS et le nom de l'utilisateur. Comme décrit dans la FAQ Dotnet, nous allons utiliser les méthodes du namespace Environment.
Nous récupérons donc des objets String ^ (handle sur un objet String managé). Il va falloir convertir des String ^ en chaîne de caractères (LPCWSTR ou wchar_t *) UNICODE (en effet, par défaut les propriétés du projet sont positionnées à Use Unicode Character Set). Sans rentrer dans le détail, nous allons utiliser la méthode InteropServices::Marshal::StringToHGlobalUni, qui crée une copie de la chaîne dans une zone mémoire. Nous pourrons alors la caster en LPCWSTR pour affichage avec SetWindowText. (Sans oublier de libérer la chaîne de caractères après utilisation).

 
Sélectionnez
Version ^ ver = Environment::Version;
IntPtr p =  System::Runtime::InteropServices::Marshal::StringToHGlobalUni("Version Framework : " + ver->ToString());
LPCWSTR str2 = reinterpret_cast<LPCWSTR>(static_cast<void *>(p));
SetWindowText(hStatic1, str2);
System::Runtime::InteropServices::Marshal::FreeHGlobal(p);

Remarque : On aurait utilisé la méthode StringToHGlobalAnsi pour convertir un String ^ en char *.

Il ne reste plus maintenant qu'à dire au compilateur que nous voulons utiliser les extensions managées. Pour ce faire, nous allons dans les propriétés du projet, et nous activons le Common Langage Runtime Support comme indiqué sur l'image ci dessous.

Image non disponible

Voilà, vous avez réussi votre premier programme Win32 qui utilise des extensions managées.

Image non disponible

Remarque : Nous sommes encore obligés de nous traîner les char * et autres LPCWSTR pour utiliser l'API Win32.

Vous pouvez télécharger les sources du projet Win32 ainsi que l'exécutable à cette adresse : Télécharger ici (22 ko)

5.Intégrer du C++ managé à une application MFC

Comme au chapitre précédent, nous allons montrer, dans ce paragraphe, comment intégrer (mixer) du C++ managé avec un programme développé avec les MFC.

Tout d'abord, créer un projet MFC / MFC Application.

Image non disponible

J'ai choisi de faire l'application basée sur une boite de dialogue, comme indiquée sur l'image. Aller jusqu'au bout du wizard (sans oublier de positionner le titre de la boite de dialogue).

Image non disponible

Dessiner la boite de dialogue de manière à ce qu'il y ait un CEdit (multiligne et want Return à True), une action pour un chargement, une pour un enregistrement et une pour quitter (j'ai choisi arbitrairement de faire des boutons dans mon exemple, mais vous pouvez tout aussi bien utiliser un menu ou une barre d'outils). (Voir FAQ VC++)

Image non disponible

Associer ensuite une variable au CEdit, comme indiqué sur le dessin. (J'ai fait de même pour le bouton charger, vous comprendrez plus bas pourquoi).

Image non disponible
Image non disponible

Comme nous l'avons déjà vu, n'oubliez pas d'aller régler la propriété qui permet d'utiliser le Common Langage Runtime.

Nous ajoutons ces deux namespaces

 
Sélectionnez
using namespace System;
using namespace System::IO;

IO pour tout ce qui est gestion de fichier. En effet, je vais illustrer cet exemple en utilisant des manipulations de fichiers (comme on pouvait le deviner à construire l'IHM)

Ensuite, dans la méthode OnInitDialog(), on va griser le bouton "charger" en fonction de l'existence du fichier de sauvegarde. (voyez comme j'ai été original pour choisir le nom du fichier ...)

 
Sélectionnez
loadButton.EnableWindow(File::Exists("fichier.txt")); // loadButton est ma variable associée au bouton "charger"

Double-clicker dans les ressources sur chaque boutons pour générer la fonction OnButtonClick.
Au chargement, nous aurons ce code ci :

 
Sélectionnez
	try
	{
		StreamReader ^ sr = gcnew StreamReader("fichier.txt");
		String ^ texte = sr->ReadToEnd();
		CString chaine = texte;
		m_myEdit.SetWindowTextA(chaine);
		sr->Close();
	}
	catch (Exception ^ ex)
	{
		m_myEdit.SetWindowTextA("Impossible de lire le fichier");
	}

Nous créons donc un objet StreamReader, qui nous sert à récupérer le texte contenu dans le fichier. Notez comme il est beaucoup plus simple de convertir un String ^ managé en CString plutôt qu'un char *. Puis nous affichons le contenu du fichier dans le CEdit.
Il est fortement recommandé de traiter les exceptions sur la gestion de fichier (même succinctement, comme ici)

L'enregistrement dans le fichier repose sur le même principe, sauf que nous utilisons un StreamWritter

 
Sélectionnez
	try
	{
		StreamWriter ^ sw = gcnew StreamWriter("fichier.txt");
		CString chaine;
		m_myEdit.GetWindowTextA(chaine);
		String ^ texte = gcnew String (chaine);
		sw->Write(texte);
		sw->Close();
	}
	catch (Exception ^ ex)
	{
		m_myEdit.SetWindowTextA("Impossible d'enregistrer le fichier");
	}

Il ne nous manque plus que l'action pour quitter : Voir FAQ VC++

 
Sélectionnez
AfxGetMainWnd()->PostMessage(WM_SYSCOMMAND,SC_CLOSE,0);

Vous pouvez télécharger les sources du projet MFC ainsi que l'exécutable à cette adresse : Télécharger ici (68 ko)

6.Un exemple concret : Sérialisation en XML

S'il y avait bien quelque chose que je n'avais pas réussi à bien appréhender en MFC, c'était la notion de sérialisation. Voici venu le temps de me réconcilier avec la sérialisation grâce à l'avènement de XML et à sa facilité d'utilisation grâce aux classes .NET.

Je vais donc, dans cette partie, vous illustrer ce concept à travers un petit exemple, volontairement simpliste pour éviter de se perdre dans des tribulations hors-sujet.

Il s'agit d'élaborer une petite application de carnet d'adresse.

Elle sera composée d'une boite de dialogue qui permet la saisie des nom/prénom/email d'une personne.

Nous aurons une fenêtre principale qui permettra de lister les noms de toutes les personnes dans le carnet d'adresses, puis lorsque nous cliquerons sur un nom, nous pourrons observer les détails de la personne.

Avec bien sur, un chargement/enregistrement en XML avec le framework .net de notre carnet d'adresse.

Pour ce faire, créons d'abord une application MFC (basée sur une boite de dialogue pour l'exemple).
Dessinons donc notre interface comme ci dessous, à savoir un bouton "ajouter", une listbox et un edit (multiligne).

Image non disponible

C'est au tour de la boite de saisie. Un bouton "ajouter", un bouton "annuler", et 3 paires de Static/Edit pour le nom, prénom et email à saisir.

Image non disponible

Créer une classe dérivée de la boite de dialogue, comme ci-dessous.

Image non disponible

Je l'ai appelé pour ma part, CEditPersonne ... la boite d'édition des données de la personne !

Créons ensuite une nouvelle classe (CPersonne), dont le but est de stocker les coordonnées d'une personne. (bouton droit sur le projet Add, Class). Donc, 3 attributs (public, soyons fous !) de type chaîne de caractères managées (String ^)

 
Sélectionnez
ref class CPersonne
{
public:
	System::String ^ nom;
	System::String ^ prenom;
	System::String ^ email;
	CPersonne(void);
	~CPersonne(void);
};

Cette classe étant managée, nous la faisons précéder du mot clé ref.

Dans la classe CEditPersonne, rajoutons un attribut public qui contient la personne courante, dont nous allons créer les coordonnées.

 
Sélectionnez
gcroot <CPersonne ^> currPersonne;

gcroot, très important ici, permet de préciser que nous voulons utiliser un objet managé, dans une classe non managée. Si nous n'avions pas utilisé gcroot, nous aurions eu cette erreur de compilation :

 
Sélectionnez
error C3265: cannot declare a managed 'currPersonne' in an unmanaged 'CEditPersonne'
	may not declare a global or static variable, 
		or a member of a native type that refers to objects in the gc heap

Changeons aussi le constructeur par défaut de la classe, pour lui passer un handle sur l'objet managé de la personne courante.

 
Sélectionnez
CEditPersonne::CEditPersonne(CPersonne ^ currPersonne, CWnd* pParent /*=NULL*/)	: CDialog(CEditPersonne::IDD, pParent)
{
	this->currPersonne = currPersonne;
}

Dans les ressources, un double-click sur le bouton "Ajouter" permet de générer la fonction qui sera appelée lors du click sur le bouton "Ajouter".
Récupérons donc les données entrées, et stockons les dans notre objet courant.

 
Sélectionnez
void CEditPersonne::OnBnClickedOk()
{
	CString temp;
	GetDlgItem(IDC_NOM)->GetWindowText(temp);
	currPersonne->nom = gcnew String(temp);
	GetDlgItem(IDC_PRENOM)->GetWindowText(temp);
	currPersonne->prenom = gcnew String(temp);
	GetDlgItem(IDC_EMAIL)->GetWindowText(temp);
	currPersonne->email = gcnew String(temp);
	OnOK();
}

Par commodité, nous allons placer le focus sur le premier Edit lors de l'initialisation de la boite de dialogue. (n'oubliez pas de retourner FALSE, comme ci-dessous).

 
Sélectionnez
BOOL CEditPersonne::OnInitDialog()
{
	GetDlgItem(IDC_NOM)->SetFocus();
	return FALSE;
}

Comme nous souhaitons avoir une collection de personnes, nous allons utiliser un objet de collection du framework.net, à savoir un System::Collections::ArrayList.
Rajoutons donc une classe, qui contiendra un handle vers cet objet de collection. C'est cette classe que nous sérialiserons en une seule fois un peu plus bas.

 
Sélectionnez
#include "personne.h"

[System::Xml::Serialization::XmlInclude(CPersonne::typeid)]

public ref class ObjSerialize
{
public:
	System::Collections::ArrayList ^ listePersonne;
	ObjSerialize(void);
};

Nous voyons ici donc la déclaration de l'objet listePersonne, puis une déclaration étonnante en début de classe, qui permet d'indiquer que nous souhaitons sérialiser un objet que le framework ne connaît pas. C'est cette déclaration qui va lui permettre de connaître notre objet CPersonne.
La classe est managée (mot clé ref) et doit être publique pour permettre la sérialisation.

Nous rajoutons donc un objet de ce type dans la classe d'application. (notez l'emploi de gcroot)

 
Sélectionnez
gcroot<ObjSerialize ^> objSerialize;

Nous voulons faire apparaître la boite de dialogue de saisie des coordonnées lorsque nous cliquons sur le bouton de la boite de dialogue principale. Procéder comme avant, en double-cliquant sur le bouton pour générer la fonction d'interception du click sur le bouton.

 
Sélectionnez
void CMfcSerialisationDlg::OnBnClickedButton1()
{
	CPersonne ^ currPersonne = gcnew CPersonne();
	CEditPersonne myDialog(currPersonne);
	if (myDialog.DoModal()==IDOK) // appui sur ok
	{
		CMfcSerialisationApp *pTheApp = static_cast<CMfcSerialisationApp *>(AfxGetApp());
		pTheApp->objSerialize->listePersonne->Add(currPersonne);
		IntPtr p =  System::Runtime::InteropServices::Marshal::StringToHGlobalAnsi(currPersonne->nom);
		LPCTSTR str2 = reinterpret_cast<LPCTSTR>(static_cast<void *>(p));
		m_listbox.AddString(str2);
		System::Runtime::InteropServices::Marshal::FreeHGlobal(p);
	}
	else
		delete currPersonne; // on détruit explicitement l'objet
}

Ce bout de code permet également d'ajouter une ligne avec le nom de la personne dans la listbox.

Lorsque l'on cliquera sur une ligne de la listbox, nous afficherons le détail de la personne.

 
Sélectionnez
void CMfcSerialisationDlg::OnLbnSelchangeList2()
{
	// TODO: Add your control notification handler code here
	CString str;
	int nIndex = m_listbox.GetCurSel();
	if (nIndex != LB_ERR)
	{
		m_listbox.GetText( nIndex, str);
		String ^ curNom = gcnew String(str);
		CMfcSerialisationApp *pTheApp = static_cast<CMfcSerialisationApp *>(AfxGetApp());
		System::Collections::IEnumerator ^ e = pTheApp->objSerialize->listePersonne->GetEnumerator();
		while(e->MoveNext())
		{
			CPersonne ^ currPersonne = dynamic_cast<CPersonne ^>(e->Current);
			if (curNom == currPersonne->nom)
			{
				CString output =   currPersonne->nom + "\r\n" 
								 + currPersonne->prenom + "\r\n" 
								 + currPersonne->email;
				m_myEdit.SetWindowTextA(output);
			}
		}
	}
}

J'introduis ici la notion d'énumérateur (IEnumerator). Ceux qui voudront plus de détails, pourront se reporter à la MSDN. Sachez toutefois que c'est objet me permet de naviguer dans l'ArrayList, un peu comme pourrait le faire un for each.

Je vous ai donc présenté ici (sans rentrer dans tous les détails, n'hésitez pas à regarder le code complet) l'application classique, en introduisant de nouveaux concepts .net.
Voici venu maintenant le temps de parler de sérialisation. Et là, tout devient magique, le framework.net fait tout tout seul.

Vous devez toutefois rajouter une référence vers la bibliothèque XML. Bouton droit, references et vous obtenez l'écran suivant ; après appui sur "Add new reference", vous pourrez choisir la référence System.Xml.

Image non disponible

Cela va donc nous permettre d'utiliser les objets XML et notamment l'objet XmlSerializer.
Lors de la fermeture de l'application, nous créons donc un nouvel objet XmlSerializer qui prend en paramètre la classe que nous souhaitons sérialiser. Associé à un objet StreamWriter que nous avons déjà vu, il ne nous reste plus qu'à invoquer la méthode Serialize sur notre objet à sérialiser.

 
Sélectionnez
void CMfcSerialisationDlg::OnBnClickedCancel()
{
	System::Xml::Serialization::XmlSerializer ^ sr;
	sr = gcnew System::Xml::Serialization::XmlSerializer(ObjSerialize::typeid);
	System::IO::StreamWriter ^ writer = gcnew System::IO::StreamWriter("fichier.xml");
	try
	{
		CMfcSerialisationApp *pTheApp = static_cast<CMfcSerialisationApp *>(AfxGetApp());
		sr->Serialize(writer, pTheApp->objSerialize);
	}
	catch (Exception ^ e)
	{
		CString message = e->InnerException->Message->ToString();
		AfxMessageBox("Impossible de sérialiser" + message);
	}
	OnCancel();
}

J'ai ajouté une gestion des exceptions, c'est capricieux ces bêtes là !

Simple non ?

Pour le chargement, rien de plus simple : au moment du InitDialog, il ne nous reste plus qu'à désérialiser.

 
Sélectionnez
	CMfcSerialisationApp *pTheApp = static_cast<CMfcSerialisationApp *->(AfxGetApp());
	if (System::IO::File::Exists("fichier.xml"))
	{
		try
		{
			System::IO::StreamReader ^ sr = gcnew System::IO::StreamReader("fichier.xml");
			System::Xml::Serialization::XmlSerializer ^ xs;
			xs = gcnew System::Xml::Serialization::XmlSerializer(ObjSerialize::typeid);
			pTheApp->objSerialize =	static_cast<ObjSerialize ^>(xs->Deserialize(sr));
			sr->Close();
		}
		catch (Exception ^ e)
		{
			CString message = e->InnerException->Message->ToString();
			AfxMessageBox(message);
		}
	}

	System::Collections::IEnumerator ^ e = pTheApp->objSerialize->listePersonne->GetEnumerator();
	while(e->MoveNext())
	{
		CPersonne ^ currPersonne = dynamic_cast<CPersonne ^>(e->Current);
		IntPtr p =  System::Runtime::InteropServices::Marshal::StringToHGlobalAnsi(currPersonne->nom);
		LPCTSTR str2 = reinterpret_cast<LPCTSTR>(static_cast<void *>(p));
		m_listbox.AddString(str2);
		System::Runtime::InteropServices::Marshal::FreeHGlobal(p);
	}

Nous voyons dans ce bout de code, une nouvelle utilisation de l'énumérateur, pour remplir la listbox avec les infos chargées.

Voilà, vous savez tout sur cette application, Vous pouvez télécharger les sources du projet MFC de sérialisation ainsi que l'exécutable à cette adresse : Télécharger ici (39 ko)

7.Pourquoi ne pas passer aux Winforms ?

Microsoft Windows Forms (ou Winforms) est le nom donné à la partie du framework .net responsable de l'interface graphique (GUI), donnant accès aux contrôles windows managés.
Il est très important de comprendre que les Winforms ne sont pas les MFC. Ce n'est pas une nouvelle version, ou amélioration des MFC. Les winforms sont écrites en objet pur et quelques classes ont des noms ou des fonctionnalités qui se rapprochent, mais la comparaison s'arrête ici.
Winform se rapproche beaucoup plus des forms comme on peut les connaître en VB. Microsoft s'est basé sur le modèle de développement des interfaces graphiques en VB, des forms, des contrôles et des propriétés pour créer un nouveau langage équivalent pour le framework.net.

Les avantages des winforms sont :

  • Très simple à prendre en main, grâce à l'outil de design inclus dans l'IDE. Il suffit de glisser-déposer des contrôles sur la form, de cliquer sur ces contrôles pour générer les évènements, etc ...
  • Le code est plus simple à faire et est plus propre.
  • Le code est mieux orienté objet.
  • L'implémentation de l'interface graphique est plus complète.

Cependant, vouloir utiliser à tout prix les Winforms en C++ est à mon avis une erreur stratégique.
Les Winforms n'ont pas un framework aussi complet et riche que les MFC. Il n'y a pas non plus d'architecture vue/document. Passer aux Winforms impliquerait d'avoir à ré-écrire tout cela.
De même, a part ce qui est cité plus haut, les Winforms n'apportent rien de plus que les MFC. Il est donc complètement inutile de se priver des classes des MFC juste pour utiliser les Winforms.
Comme je l'ai expliqué au cours de ce tutoriel, nous sommes capables d'utiliser le framework.net à partir d'une application MFC, et c'est à mon avis amplement suffisant.
Même si cela est possible, utiliser les Winforms avec du C++ managé est contre productif. Les MFC nous offrent une grande palette d'outil et nous avons tout intérêt à les utiliser.
Dans le cadre de la migration d'un gros projet développé en C++ avec les MFC en .Net, redévelopper l'IHM avec les Winforms est suicidaire.
On parle d'un portage ou d'une migration dans l'optique d'utiliser les performances du nouveau compilateur et de pouvoir utiliser les extensions managées du framework.net.

Remarque : Il existe des projets open source comme Genghis qui tentent de proposer des services d'un même niveau que les MFC pour .Net.

8.Migrer son projet

Avec IJW, vous ne devriez pas avoir trop de mal à faire migrer ou à porter vos applications.
Cependant, des erreurs communes de portage et de migration sont recensées par Farscape à cet emplacement : Migration projets VC6/.net vers VC2005

Remerciements

Je remercie toute l'équipe C++, notamment Anomaly, pour leur relecture attentive du document et aussi Farscape pour sa relecture du programme MFC.

Contact

Si vous constatez une erreur dans le tutorial, dans le source, dans la programmation ou pour toutes informations, n'hésitez pas à me contacter par mail, ou par le forum.

Vous avez aimé ce tutoriel ? Alors partagez-le en cliquant sur les boutons suivants : Viadeo Twitter Facebook Share on Google+   

Les sources présentées sur cette page sont libres de droits et vous pouvez les utiliser à votre convenance. Par contre, la page de présentation constitue une œuvre intellectuelle protégée par les droits d'auteur. Copyright © 2006 Nico-pyright(c). 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.