Avant-propos▲
Souvent, nous utilisons des Custom Tools sans le savoir. Ces outils simplifient le quotidien du développeur en générant par exemple des classes utilitaires à partir de fichiers XML. Visual Studio propose déjà un ensemble de Custom Tools, comme un générateur de code pour les fichiers de ressources et de configuration. Ces outils permettent de gagner un temps non négligeable pendant la phase de développement d'un projet. Il est bien plus facile d'accéder aux valeurs d'un fichier de ressources par l'intermédiaire d'une classe qu'en réalisant une requête XPath ou LinQ.
Visual Studio offre la possibilité de créer de tels outils et cet article explique chaque étape de la création d'un Custom Tool. Dans un 1er temps, nous verrons ce qu'est un Custom Tool et à quoi sert le Custom Tool proposé par l'article. Dans une 2e partie, nous créerons une librairie où nous implémenterons le Custom Tool. Une fois créé, nous le testerons avec une application console. Finalement, nous réaliserons un programme d'installation afin de le déployer aisément. Il est à noter que le langage utilisé est le C#, mais qu'il est possible de traduire le code de l'article en VB .Net assez facilement.
I. Qu'est-ce qu'un Custom Tool ?▲
Un Custom Tool n'existe pas sans parler de Visual Studio. On peut dire simplement que c'est un générateur de code. Visual studio utilise par exemple un Custom Tool pour générer la classe utilitaire associée à un fichier de type « resx ». Celui-ci prend en entrée le fichier de ressource et construit une classe facilitant l'accès aux données XML. On en déduit qu'un Custom Tool accepte un fichier en entrée et crée un fichier en sortie.
Le fichier créé est rattaché au fichier source. Visual Studio le traduit visuellement comme cela :
La plupart des Custom Tools génèrent du code à partir de fichiers XML. Mais il est possible de faire l'inverse ou par exemple de générer un fichier de documentation à partir d'une classe commentée. On remarque que tout fichier contenu dans un projet Visual Studio peut avoir un Custom Tool rattaché. Ceci est visible dans la fenêtre de propriété :
Pour exécuter un Custom Tool, il suffit de sauvegarder le fichier où le Custom Tool est rattaché. Ceci a pour effet d'exécuter la méthode « Generate » définie par l'interface « IVsSingleFileGenerator » de l'outil, que nous présenterons plus en détail lors de la phase d'implémentation. La méthode génère ensuite le fichier en sortie et Visual Studio 2008 l'attache au fichier source.
II. Objectif du Custom Tool proposé dans l'exemple▲
L'exemple choisi crée un Custom Tool qui utilise en entrée un fichier XML de déclaration de champs « fields » pour un site SharePoint, et produit une classe facilitant l'accès aux données.
Le Custom Tool proposé permet de récupérer l'identifiant unique (GUID) et le nom de chaque champ déclaré dans le fichier XML. L'outil génère ensuite une classe statique composée de propriétés statiques retournant les identifiants de chaque champ. Voici un extrait du fichier XML utilisé (se trouvant dans l'archive compressée de l'article) :
III. Réalisation▲
III-A. Prérequis logiciels▲
Voici la liste des composants logiciels nécessaires pour la réalisation du Custom Tool :
- Microsoft Visual Studio 2008 (toutes les versions sauf Express). Préférez la version anglaise, car l'article se base dessus. Cependant, il est facile de traduire les références anglophones ;
- Microsoft Visual Studio 2008 SDK (http://www.microsoft.com/downloads/details.aspx?familyid=30402623-93ca-479a-867c-04dc45164f5b&displaylang=en);
- L'archive de l'article (Lien HTTP). Celle-ci contient notamment le fichier XML de référence pour le Custom Tools, s'appelant « fields.xml ». Elle contient l'intégralité du code source du Custom Tool.
III-B. Création du projet▲
Tout d'abord, il est nécessaire de créer un projet de type « Class Library ». Nommez le « HelloCustomTool » et cochez la case « Create directory for solution ». Laissez le nom de la solution comme tel et finalement validez la création du projet.
Une fois le projet créé, il faut ensuite définir une référence à l'assembly « Microsoft.VisualStudio.Shell.Interop ». Celui-ci est fourni dans le SDK de Visual Studio que vous avez préalablement téléchargé. Cliquez avec le bouton droit sur le projet dans l'explorateur de solution et sélectionnez « Add reference ». Dans l'onglet « .Net », vous trouvez l'assembly.
Avant de commencer à développer le Custom Tool, il est nécessaire de signer la librairie. Allez dans les propriétés du projet (Clic droit sur le projet, puis sélectionnez Properties). Dans l'onglet « Signing », signez fortement l'assembly avec une nouvelle clé. Nommez-la « key.snk » et désactivez la protection par mot de passe.
Renommez ensuite le fichier « Class1.cs » en « HelloCustomTool.cs ». Nous sommes enfin prêts à implémenter le Custom Tool.
III-C. Implémentation de la classe principale : HelloCustomTool▲
Ajoutons tout d'abord les clauses « using » nécessaires à la classe « HelloCustom Tool » :
using
System;
using
System.
CodeDom.
Compiler;
using
System.
Runtime.
InteropServices;
using
Microsoft.
VisualStudio.
Shell.
Interop;
Dans un 2e temps, faites hériter la classe « HelloCustomTool » de l'interface « IVsSingleFileGenerator » et définissez les corps des méthodes suivantes « Generate » et « DefaultExtension ». Nous devons ensuite attacher un identifiant unique à notre classe. Pour cela, utilisez un attribut « Guid ». Allez dans le menu « Tools » et sélectionnez « Create GUID ». Choisissez l'option 4 et appuyez sur le bouton « Copy » et « Exit ». Ajoutez ensuite un attribut « Guid » à la classe « HelloCustomTool » et passez en paramètre l'identifiant stocké dans le presse-papier (Ctrl+V). Voici le code de la classe « HelloCustomTool » :
namespace
HelloCustomTool
{
[Guid(
"E9832F96-4614-4530-ABF6-0438953D2355"
)]
public
class
HelloCustomTool :
IVsSingleFileGenerator
{
///
<
summary
>
/// Determine l'extension du fichier créé
///
<
/summary
>
///
<
param
name
=
"ext"
><
/param
>
///
<
returns
><
/returns
>
public
int
DefaultExtension
(
out
string
pExt)
{
pExt =
".cs"
;
return
0
;
}
///
<
summary
>
/// Méthode appelée lors de la sauvegarde du fichier source
///
<
/summary
>
///
<
param
name
=
"pInputFilePath"
>
Chemin du fichier source
<
/param
>
///
<
param
name
=
"pInputFileContents"
>
Contenu du fichier source
<
/param
>
///
<
param
name
=
"pNamespace"
><
/param
>
///
<
param
name
=
"pOutputFileContents"
>
Pointeur stockant l'adresse du contenu généré
<
/param
>
///
<
param
name
=
"pOutputFileContentSize"
>
Taille du contenu généré
<
/param
>
///
<
param
name
=
"pGenerateProgress"
>
Indicateur de progression du Custom Tool
<
/param
>
///
<
returns
><
/returns
>
public
int
Generate
(
string
pInputFilePath,
string
pInputFileContents,
string
pNamespace,
IntPtr[]
pOutputFileContents,
out
uint
pOutputFileContentSize,
IVsGeneratorProgress pGenerateProgress)
{
if
(
pInputFileContents ==
null
)
throw
new
ArgumentNullException
(
pInputFileContents);
// Création du fournisseur de code permettant de transformer un graphe d'instructions et d'expressions en code
CodeDomProvider codeProvider =
CodeDomProvider.
CreateProvider
(
"C#"
);
// Génération de la classe "Fields" composée de propriétés statiques renvoyant des GUID
byte
[]
generatedStuff =
Generator.
GenerateCode
(
pInputFileContents,
codeProvider,
pNamespace,
pGenerateProgress);
if
(
generatedStuff ==
null
)
{
pOutputFileContents[
0
]
=
IntPtr.
Zero;
pOutputFileContentSize =
0
;
}
else
{
// Copie du flux en mémoire pour que Visual Studio puisse le récupérer
pOutputFileContents[
0
]
=
Marshal.
AllocCoTaskMem
(
generatedStuff.
Length);
Marshal.
Copy
(
generatedStuff,
0
,
pOutputFileContents[
0
],
generatedStuff.
Length);
pOutputFileContentSize =
(
uint
)generatedStuff.
Length;
}
return
0
;
}
}
}
La fonction « DefaultExtension » permet de définir l'extension du fichier créé par le Custom Tool. Elle prend en paramètre l'extension à renvoyer. La méthode « Generate » est appelée pour générer le fichier en sortie. Dans le corps de celle-ci, une instance de la classe « CodeDomProvider » est créée. Celle-ci permet de générer le code au format souhaité (ici en C#) à partir d'un graphe d'instructions et d'expressions. Ce graphe est construit grâce aux classes et méthodes de la bibliothèque « CodeDom ». La chaine de caractères « pInputFileContents » contient l'intégralité du fichier XML. Cette chaine servira à générer la classe « Fields » avec ses propriétés.
Notez l'appel à la méthode « Generator.GenerateCode ». Celle-ci prend en entrée le contenu du fichier XML, le provider de code créé, l'espace de nom ainsi que l'indicateur de progression du Custom Tool. Dans la prochaine partie, nous allons créer la classe « Generator » qui a pour rôle de générer le code de la classe « Fields ».
III-D. Implémentation de la classe Generator▲
Commencez par ajouter une classe au projet « HelloCustomTool ». Nommez cette classe « Generator ». Voici la liste des « using » à insérer dans notre nouvelle classe :
using
System;
using
System.
CodeDom;
using
System.
CodeDom.
Compiler;
using
System.
IO;
using
System.
Reflection;
using
System.
Runtime.
InteropServices;
using
System.
Text;
using
System.
Xml;
using
Microsoft.
VisualStudio.
Shell.
Interop;
Avant d'implémenter la méthode « GenerateCode », rendez la classe publique et statique en ajoutant les mots clés « public » et « static » comme suit :
namespace
HelloCustomTool
{
public
static
class
Generator
{
Voici le corps de la méthode « GenerateCode » :
///
<
summary
>
/// Méthode générant la classe publique contenant l'ensemble des accesseurs statiques
///
<
/summary
>
///
<
param
name
=
"pInputFileContents"
>
Contenu du fichier XML
<
/param
>
///
<
param
name
=
"pCodeProvider"
>
Objet servant à générer le code à partir du graph d'instructions et d'expressions créé
<
/param
>
///
<
param
name
=
"pCodeGeneratorProgress"
>
Indicateur de progression du Custom Tool
<
/param
>
///
<
returns
>
Tableau d'octets représentant le code de la classe générée
<
/returns
>
public
static
byte
[]
GenerateCode
(
string
pInputFileContents,
CodeDomProvider pCodeProvider,
string
pNamespace,
IVsGeneratorProgress pCodeGeneratorProgress)
{
CodeCompileUnit compileUnit;
StreamWriter writer =
new
StreamWriter
(
new
MemoryStream
(
),
Encoding.
UTF8);
XmlDocument doc =
new
XmlDocument
(
);
doc.
LoadXml
(
pInputFileContents);
// Generate class graph
compileUnit =
CreateClass
(
doc,
pNamespace);
if
(
pCodeGeneratorProgress !=
null
)
pCodeGeneratorProgress.
Progress
(
0x4b
,
100
);
// Generate Code
pCodeProvider.
GenerateCodeFromCompileUnit
(
compileUnit,
writer,
null
);
if
(
pCodeGeneratorProgress !=
null
)
{
int
errCode =
pCodeGeneratorProgress.
Progress
(
100
,
100
);
if
(
errCode <
0
)
{
Marshal.
ThrowExceptionForHR
(
errCode);
}
}
writer.
Flush
(
);
return
writer.
BaseStream.
StreamToBytes
(
);
}
Dans un 1er temps, nous initialisons un objet « StreamWriter » qui permet de récupérer sous forme de chaine de caractère le code créé. Le contenu XML de la variable « pInputFileContent » est ensuite chargé dans un objet « XmlDocument » afin de récupérer facilement les éléments et attributs XML. Celui-ci est ensuite passé à la méthode « CreateClass » chargée de construire le graphe « CodeDom » représentant la classe. Le graphe en question est ensuite converti en code C# et stocké dans l'objet « StreamWriter » créé préalablement. Et finalement, le code généré est renvoyé sous la forme d'un tableau d'octets (byte[]). Notons l'utilisation de la méthode « SteamToBytes » sur la propriété « BaseStream » de l'objet « writer ». En réalité, c'est une méthode d'extension dont voici la définition et l'implémentation :
///
<
summary
>
/// Méthode renyant un tableau d'octets à partir d'un flux
///
<
/summary
>
///
<
param
name
=
"pStream"
><
/param
>
///
<
returns
><
/returns
>
public
static
byte
[]
StreamToBytes
(
this
Stream pStream)
{
if
(
pStream.
Length ==
0
)
return
new
byte
[
0
];
long
pos =
pStream.
Position;
pStream.
Position =
0
;
byte
[]
buffer =
new
byte
[(
int
)pStream.
Length];
pStream.
Read
(
buffer,
0
,
buffer.
Length);
pStream.
Position =
pos;
return
buffer;
}
Insérez cette méthode dans la classe « Generator ». Voyons maintenant la méthode « CreateClass », qu'il vous faut aussi insérer dans la classe « Generator » :
///
<
summary
>
/// Méthode construisant la classe publique "Fields".
/// Celle-ci appelle la méthode CreateFields qui ajoute les propriétés statiques
///
<
/summary
>
///
<
param
name
=
"pXmlDoc"
><
/param
>
///
<
returns
><
/returns
>
public
static
CodeCompileUnit CreateClass
(
XmlDocument pXmlDoc,
string
pNamespace)
{
// Configuration du graph
CodeCompileUnit code =
new
CodeCompileUnit
(
);
code.
UserData.
Add
(
"AllowLateBound"
,
false
);
code.
UserData.
Add
(
"RequireVariableDeclaration"
,
true
);
// Définition du namespace
CodeNamespace nameSpace =
new
CodeNamespace
(
pNamespace);
code.
Namespaces.
Add
(
nameSpace);
// Définition de la classe publique "Fields"
CodeTypeDeclaration classObject =
new
CodeTypeDeclaration
(
"Fields"
);
nameSpace.
Types.
Add
(
classObject);
classObject.
TypeAttributes =
TypeAttributes.
Public;
// Génération des propriétés statiques de la classe
XmlElement rootElement =
pXmlDoc.
DocumentElement;
XmlNodeList xmlFields =
rootElement.
GetElementsByTagName
(
"Field"
);
CreateFields
(
classObject,
xmlFields);
CodeGenerator.
ValidateIdentifiers
(
code);
return
code;
}
Cette méthode statique crée la classe publique « Fields » et insère pour chaque élément XML <Field> une propriété statique. Cette opération est d'ailleurs déléguée à la méthode « CreateFields » que voici et que vous devez insérer dans la classe « Generator » :
///
<
summary
>
/// Méthode qui ajoute une propriété statique pour chaque noeud XML
///
<
/summary
>
///
<
param
name
=
"pClassObject"
><
/param
>
///
<
param
name
=
"pXmlFields"
><
/param
>
public
static
void
CreateFields
(
CodeTypeDeclaration pClassObject,
XmlNodeList pXmlFields)
{
foreach
(
XmlNode xmlField in
pXmlFields)
{
string
propName =
string
.
Empty,
propId =
string
.
Empty;
// Récupération des valeurs des attributs ID et Name
if
(
xmlField.
Attributes.
GetNamedItem
(
"ID"
) !=
null
)
propId =
xmlField.
Attributes.
GetNamedItem
(
"ID"
).
Value;
if
(
xmlField.
Attributes.
GetNamedItem
(
"Name"
) !=
null
)
propName =
xmlField.
Attributes.
GetNamedItem
(
"Name"
).
Value;
// Création de la propriété public et statique, renvoyant un guid
// Exemple :
// public static global::System.Guid TTT_Headlines {
// get { return new System.Guid("{B6684A6E-FBF5-4223-9679-56A7B4C3A356}"); }
// }
Type propertyType =
typeof
(
Guid);
CodeTypeReference propertyTypeReference =
new
CodeTypeReference
(
propertyType,
CodeTypeReferenceOptions.
GlobalReference);
CodeMemberProperty property =
new
CodeMemberProperty
(
);
property.
Name =
propName;
property.
HasGet =
true
;
property.
HasSet =
false
;
property.
Type =
propertyTypeReference;
property.
Attributes =
MemberAttributes.
Public |
MemberAttributes.
Static;
// Création de l'instruction return
CodeMethodReturnStatement returnStatement =
new
CodeMethodReturnStatement
(
new
CodeObjectCreateExpression
(
typeof
(
Guid),
new
CodePrimitiveExpression
(
propId)));
property.
GetStatements.
Add
(
returnStatement);
// Ajout de la propriété à la classe "Fields"
pClassObject.
Members.
Add
(
property);
}
}
Pour chaque élément <Field> contenu dans le fichier XML sont récupérés les attributs « ID » et « Name ». L'attribut « Name » sert de nom pour la propriété statique et « ID » est utilisé dans l'instanciation du GUID retourné. Chaque propriété générée est ensuite ajoutée à la classe « Fields ».
La classe « Generator » est dès à présent fonctionnelle. Cependant, nous allons tout d'abord tester que le code généré est bien formé. Pour cela, nous allons ajouter une application de type console à notre solution.
IV. Test du Custom Tool à l'aide d'une application Console▲
Cliquez avec le bouton droit sur la solution dans l'explorateur de solution et ajoutez un nouveau projet. Choisissez le modèle « Console Application », nommez le projet « Test » et validez la création.
Il faut ensuite ajouter le projet « HelloCustomTool » en tant que référence dans le projet « Test ». Vous le trouverez dans l'onglet « Project » de la fenêtre « Add Reference ».
Dans l'archive compressée de l'article, se trouve le fichier XML « fields.xml ». Récupérez-le et ajoutez-le au projet en cliquant avec le bouton droit sur le projet « Test » dans l'explorateur de solution et en choisissant « Add Existing Item ». Sélectionnez-le, validez et définissez la propriété « Copy to Output directory » à « Copy Always » dans la fenêtre de propriété du fichier XML. Cette manipulation ordonne à Visual Studio de copier le fichier XML dans le répertoire de sortie du projet « Test » à chaque compilation du programme.
Voici le corps de la méthode « Main » de la classe « Program ». La logique suivante appelle la méthode « GenerateCode » en passant le contenu du fichier XML « fields.xml » sous forme de chaine de caractères.
using
System;
using
System.
Xml;
using
HelloCustomTool;
namespace
Test
{
class
Program
{
static
void
Main
(
string
[]
args)
{
XmlDocument xmlDoc =
new
XmlDocument
(
);
xmlDoc.
Load
(
"fields.xml"
);
string
result =
Generator.
GenerateCode
(
xmlDoc.
OuterXml);
Console.
WriteLine
(
result);
Console.
Read
(
);
}
}
}
Afin de tester plus facilement notre Custom Tool, la méthode « GenerateCode » de la classe « Generator » a été surchargée par une nouvelle méthode ne prenant que le contenu du fichier XML en entrée, dont voici l'implémentation :
///
<
summary
>
/// Méthode de test du Custom Tool
///
<
/summary
>
///
<
param
name
=
"pInputFileContents"
><
/param
>
///
<
returns
><
/returns
>
public
static
string
GenerateCode
(
string
pInputFileContents)
{
CodeCompileUnit compileUnit;
StreamWriter writer =
new
StreamWriter
(
new
MemoryStream
(
),
Encoding.
UTF8);
XmlDocument doc =
new
XmlDocument
(
);
doc.
LoadXml
(
pInputFileContents);
compileUnit =
CreateClass
(
doc,
"HelloCustomTool"
);
CodeDomProvider codeProvider =
CodeDomProvider.
CreateProvider
(
"C#"
);
codeProvider.
GenerateCodeFromCompileUnit
(
compileUnit,
writer,
null
);
writer.
Flush
(
);
System.
Text.
Encoding enc =
System.
Text.
Encoding.
UTF8;
return
enc.
GetString
(
StreamToBytes
(
writer.
BaseStream));
}
Rajoutez cette méthode à la classe statique « Generator » de votre librairie « HelloCustomTool ».
Nous sommes enfin prêts à tester notre Custom Tool ! Pressez la touche F5 pour compiler et lancer l'application de test. Auparavant, assurez-vous d'avoir sélectionné le projet « Test » en tant que projet de démarrage.
Normalement, vous devriez voir l'intégralité du code de la classe « Fields » générée dans une fenêtre de commande.
V. Déploiement du Custom Tool▲
Pour que Visual Studio puisse exécuter notre Custom Tool, nous avons besoin de rajouter quelques clés dans la base de registres aux endroits suivants :
- HKEY_LOCAL_MACHINE\SOFTWARE\Microsoft\VisualStudio\9.0\CLSID ;
- HKEY_LOCAL_MACHINE\SOFTWARE\Microsoft\VisualStudio\9.0\Generators\{FAE04EC1-301F-11D3-BF4B-00C04F79EFBC}.
Nous allons profiter de notre programme d'installation pour définir automatiquement ces clés dans la base de registres. De même, nous insèrerons la librairie « HelloCustomTool » dans le GAC.
Commençons par ajouter le 3e et dernier projet à notre solution de type « Setup Project ». Nommez-le « Installation » et validez sa création.
Il faut maintenant définir l'installation de la librairie « HelloCustomTool » dans le GAC. Faites un clic droit sur le projet « Installation » dans l'explorateur de solution et sélectionnez « View > File System ». Sur la partie gauche de la fenêtre venant d'apparaître, faites un clic droit et sélectionnez « Add Special Folder > Global Assembly Cache Folder ». Assurez-vous de sélectionner ce nouveau répertoire, et dans la partie droite, faites un clic droit et « Add > Project Output ». Validez la fenêtre qui apparaît.
Il ne reste plus qu'à définir les clés dans la base de registre. Pour cela, sélectionnez le projet d'installation dans l'explorateur de solution, cliquez droit dessus et sélectionnez « View > Registry ». Dans l'arborescence à gauche, créez les clés suivantes (clique droit sur un dossier et « New > Key »):
- HKEY_LOCAL_MACHINE\SOFTWARE\Microsoft\VisualStudio\9.0\CLSID :
- HKEY_LOCAL_MACHINE\SOFTWARE\Microsoft\VisualStudio\9.0\Generators\{FAE04EC1-301F-11D3-BF4B-00C04F79EFBC}.
Pour la clé « CLSID », ajoutez une clé égale à la valeur du GUID de la classe « HelloCustomTool ». Dans notre exemple le GUID correspond à {E9832F96-4614-4530-ABF6-0438953D2355}.
Ajoutez les chaines de caractères suivantes :
- (Default) : Hello Custom Tool (Description, laissez le champ (Name) vide) ;
- Assembly : HelloCustomTool, Version=1.0.0.0, Culture=neutral, PublicKeyToken=XXXXXXXXXX (Nom de l'assembly stocké dans le GAC) ;
- Class : HelloCustomTool.HelloCustomTool (Nom complet de la classe du Custom Tool) ;
- InprocServer32 : [SystemFolder]mscoree.dll ;
- ThreadingModel : Both.
Voici ce que cela donne en image :
Pour récupérer le PublicKeyToken de la librairie « HelloCustomTool », vous pouvez utiliser l'utilitaire « sn.exe ». En ouvrant une fenêtre de commande Visual Studio 2008, placez-vous dans le répertoire « Debug » de votre projet « HelloCustomTool » et exécutez la commande suivante :
sn.exe -T HelloCustomTool.dll
Vous pouvez aussi utiliser un « External Tool » dédié à cela. Le lien suivant vous explique comment faire :
Concernant la clé finissant par {FAE04EC1-301F-11D3-BF4B-00C04F79EFBC}, celle-ci indique que notre Custom Tool concerne les projets C#. Rajoutez une clé nommée « HelloCustomTool »à l'intérieur et définissez les valeurs suivantes :
- (Default) : Hello Custom Tool (Description du Custom Tool) ;
- CLSID: {E9832F96-4614-4530-ABF6-0438953D2355} (GUID de la classe HelloCustomTool) ;
- GeneratesDesignTimeSource (DWORD):1
Il ne reste plus qu'à installer et tester votre Custom Tool. Pour cela, compilez votre projet d'installation. Fermez Visual Studio 2008, allez dans le répertoire « Debug » de votre projet d'installation et double cliquez sur l'installeur. Une fois l'installation terminée, relancez Visual Studio 2008, ouvrez la solution, sélectionnez le fichier « fields.xml » et dans sa fenêtre de propriété, définissez « Custom Tool » à « HelloCustomTool ». Ouvrez le fichier « Fields.xml » et faites un « Ctrl +S ». Vous verrez apparaître un fichier « Fields.cs » contenant votre classe « Fields ».
Conclusion▲
Cet article montre comment créer un Custom Tool dans Visual Studio 2008, le déboguer et générer un programme d'installation. L'exemple peut servir de base pour créer d'autres Custom Tools qui simplifieront le travail du développeur. Visual Studio 2008 propose bien d'autres technologies qui ont pour finalité de générer du code, par exemple les DSL, pour « Domain Specific Language ». Ceux-ci permettent la création de langages graphiques utilisés pour générer n'importe quel type de sortie, dont nous verrons la création dans un prochain article.
En dernier lieu, je tiens à remercier Louis-Guillaume Morand pour son aide précieuse.