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.
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é.
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.
System::Collections::
ArrayList ^
tableau =
gcnew System::Collections::
ArrayList();
ou bien :
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
Utiliser le wizard pour créer une application windows, sans fichier généré.
Ajouter un fichier CPP ainsi qu'un fichier de ressources.
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)
Nous allons maintenant écrire le squelette de l'application Win32.
Premièrement les includes :
#include
<windows.h>
#include
"resource.h"
Ensuite le main, qui va appeler la boite de dialogue :
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 :
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 :
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).
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.
Voilà, vous avez réussi votre premier programme Win32 qui utilise des extensions managées.
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.
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).
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++)
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).
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
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 ...)
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 :
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
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++
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).
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.
Créer une classe dérivée de la boite de dialogue, comme ci-dessous.
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 ^)
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.
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 :
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.
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.
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).
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.
#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)
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.
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.
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.
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.
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.
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.