.Net and VisualStudio ALM
Article tagué Visual Studio 2010
TFS : Faire des rollback avec TFS 2010 et TFS 11
5/04/12
J’ai eu plusieurs fois des questions sur comment marche les rollback dans Visual Studio 2010 et Team Foundation Server 2010 ; plutôt que de me répéter tout le temps j’ai décidé de faire un billet
Note : bien que les captures d’écran soient sous Visual Studio 11, le principe est le même sous Visual Studio 2010.
Dans TFS le principe du rollback ou de l’annulation d’un changeset est d’obtenir une nouvelle version du ou des fichiers résultat sous la forme d’une modification en attente :

La modification sera alors, après archivage, visible dans l’historique des fichiers. (et pourra être annulée si besoin
)
Depuis TFS 2010 il est possible de faire des rollback sans passer par les PowerTools en utilisant la ligne de commande via tf.exe rollback. La documentation est disponible sur la MSDN http://msdn.microsoft.com/fr-fr/library/dd380776(VS.100).aspx. Ce n’est pas des plus “user friendly”
Heureusement Microsoft à ajouter une interface graphique avec les TFS PowerTools August 2011 qui est en standard dans VS 11. Dans l’explorateur de sources, si vous faites un clic droit sur un élément vous verrez la commande Rollback :

Si vous cliquez dessus vous verrez alors la fenêtre de dialogue permettant de spécifier les différentes options de rollback :

Cette fenêtre vous permet de spécifier l’élément sur lequel faire le rollback et le type de rollback que vous souhaitez faire. Pour voir à quoi correspondent les 3 types de rollback nous utiliserons le code suivant :
Changeset 21
public class RollbackDemo
{
public int GetValue(int i)
{
return i;
}
public DateTime GetDate()
{
return DateTime.Now;
}
}
Changeset 22
public class RollbackDemo
{
public int GetValue(int i)
{
return i + 1;
}
public DateTime GetDate()
{
return DateTime.Now;
}
}
Changeset 23
public class RollbackDemo
{
public int GetValue(int i)
{
return i + 1;
}
public DateTime GetDate()
{
return DateTime.Today;
}
}
L’option Rollback changes from a single changeset permet d’annuler les modifications ajouté via un changeset particulier tout en gardant les modifications qui sont intervenu après. Pour cela Visual Studio va utiliser le même mécanisme que pour un merge en se basant sur les versions du fichier :
- Avant le changeset sélectionné,
- Le changeset sélectionné,
- La dernière version du fichier.
Dans notre exemple si l’on décide d’annuler le changeset 22 on obtiendra alors automatiquement le résultat :
public class RollbackDemo
{
public int GetValue(int i)
{
return i;
}
public DateTime GetDate()
{
return DateTime.Now;
}
}
La méthode GetValue est revenue à sa définition originale (changeset 21) alors que la méthode GetDate est restée inchangée !
Bien entendu si un conflit existe VS proposera à l’utilisateur de le résoudre manuellement comme pour un merge :

L’option Rollback changes from a range of changesets est la même que la précédente mais appliquée à un ensemble de changeset consécutifs. Attention à l’utilisation de cette option, il y a de gros risque d’y avoir des conflits (surtout si la plage de changeset est importante) pour lesquels il faudra se tordre le cerveau pour les résoudre
La dernière option Rollback to a specific version est tout simplement une annulation de l’ensemble des modifications survenues depuis la version spécifiée. On pourra spécifier la version voulu via plusieurs options :
- Numéro de changeset,
- Date,
- Etiquette,
- Version dans le workspace
Si l’on décide revenir au changeset 21, on se retrouvera alors avec le code suivant :
public class RollbackDemo
{
public int GetValue(int i)
{
return i + 1;
}
public DateTime GetDate()
{
return DateTime.Now;
}
}
Les méthodes GetValue et GetDate sont revenues à leurs définitions exactes du changeset 21.
Quelque soit le type de rollback choisi la mise à jour n’est faite que sur l’élément sélectionnée, il faut donc faire attention avec les dépendances (par exemple un rollback supprimant une fonction toujours utilisée dans un autre fichier) !
Voila j’espère que les différents types de rollback sont maintenant plus clair. Notez aussi que l’ensemble de ce que je vous ai montré marche avec la ligne de commande
Carpe Diem.
VS 2010 : Analyse de code en ligne de commande
28/01/11
Microsoft vient de publier un PowerTools pour pouvoir exécuter l’analyse de code de Visual Studio 2010 en ligne de commande :
Visual Studio Code Metrics PowerTool 10.0
Le résultat est stocké dans un fichier XML et n’est malheureusement pas intégré à TFS pour du reporting ou à TeamBuild mais c’est déjà un début ! Le temps de faire joujou avec et je reviens avec un billet pour montrer l’intégration avec TeamBuild
Carpe Diem.
VS 2010 / TFS 2010 : Le SP1 arrive en beta
9/12/10
Brian Harry vient d’annoncer la disponibilité du service pack 1 en version beta de Visual Studio 2010, TFS 2010 et du Framework .Net 4.0. Pour l’instant seul les abonnés MSDN y ont accès mais la version publique devrait être dispo aujourd’hui.
Voici la liste de ce que contient le service pack :
- Un viewer pour l’aide en local.
- L’intégration des outils Silverlight 4.
- La possibilité de créer des projets de tests unitaires ciblant le framework 3.5 (enfin !).
- Le support du 64bit et de Sharepoint pour l’intellitrace.
- Un outil d’analyse de performances pour les applications Silverlight.
- La possibilité, lors de la compilation, d’intégrer les assemblies du runtime VB en temps que références directes.
- Un grand nombre de correction de bug. (la liste est disponible sur le billet de Brian)
Voila il ne reste plus qu’a tester tout ça
Carpe Diem.
Visual Studio 2010 et la modélisation
4/11/10
Un petit billet pour vous signaler la sortie du numéro 135 de Programmez :
Il contient un article co-écrit par Loïc Baumann, Jason De Oliveira et moi sur la modélisation avec VisualStudio 2010.
Carpe Diem.
VS 2010 : Analyseur personnalisé pour les graphes de dépendances
31/05/10
Après vous avoir montré comment personnaliser l’IntelliTrace, je vais aujourd’hui vous montrer comment créer vos propres analyseurs pour les graphes de dépendance dans les outils d’architecture de VisualStudio 2010.
Avant de commencer encore un petit avertissement (toujours en gras et rouge pour être bien visible
) :
Microsoft ne fournit pas et ne fournira ni documentation ni support sur ce que je vais expliquer car même si cela est possible, ce n’est pas une fonctionnalité supportée.
Maintenant que vous êtes prévenu nous allons voir comment créer un analyseur qui affichera l’ensemble des noeuds commençant par “Qetza.”.
On commence par créer une bibliothèque de classe et par référencer les trois assemblies suivantes :
- Microsoft.VisualStudio.Progression.Common
- Microsoft.VisualStudio.Progression.GraphModel
- Microsoft.VisualStudio.Progression.Interfaces
Ces assemblies se trouve dans le sous-répertoire “Common7\IDE\PrivateAssemblies” de votre répertoire d’installation de VisualStudio 2010 (par défaut “C:\Program Files\Microsoft Visual Studio 10.0″).
Il faut ensuite créer une classe qui implémentera l’interface Microsoft.VisualStudio.Progression.IProvider et ajouter l’attribut Microsoft.VisualStudio.Progression.Provider à la classe :
using System;
using Microsoft.VisualStudio.Progression;
using Microsoft.VisualStudio.Progression.GraphModel;
namespace Qetza.VisualStudio.Analyzers
{
/// <summary>
/// The Name analyzer searches the graph linearly for nodes with Labels that start with "Qetza".
/// </summary>
[Provider(Name = "NameAnalyzer")]
public class NameAnalyzer : IProvider
{
#region IProvider Members
/// <summary>
/// Initialize the named analyzer.
/// </summary>
/// <param name="serviceProvider">Not Used</param>
public void Initialize(IServiceProvider serviceProvider)
{
throw new NotImplementedException();
}
/// <summary>
/// Describes the types of node and link categories produced by this provider,
/// where each Node in the graph is the Id of a Node Category and each link between the
/// nodes describes the type of navigations possible between nodes.
/// </summary>
public Graph Schema
{
get { throw new NotImplementedException(); }
}
#endregion
}
}
L’attribut est important car il est utilisé par le mécanisme de VisualStudio pour chercher et charger les classes implémentant IProvider.
L’interface IProvider déclare deux méthodes :
- Initialize : cette méthode est appelée pour initialiser le provider. C’est dans cette méthode que nous allons enregistrer notre action personnalisée.
- Schema : cette propriété renvoi les types de catégories de noeuds et de lien créés par le provider. Dans notre cas aucun.
Nous allons maintenant implémenter la méthode Initialize :
static GraphProperty IsNamed = GraphProperty.Register(
"IsNamed",
typeof(bool),
new GraphMetadata(
Resources.IsNamed,
Resources.IsNamedDescription,
null,
GraphMetadataFlags.Default
),
typeof(NameAnalyzer)
);
public void Initialize(IServiceProvider serviceProvider)
{
var action = Microsoft.VisualStudio.Progression.Action.Register(
"AnalyzeNamed",
Resources.FindNamed,
ActionFlags.IsAnalyzer | ActionFlags.IsGlobal,
null
);
action[DgmlProperties.AnalyzerAnnotation] = IsNamed;
action[DgmlProperties.AnalyzerStyle] = string.Format(
CultureInfo.CurrentCulture,
@"<Style TargetType='Node' xmlns='http://schemas.microsoft.com/vs/2009/dgml' GroupLabel='{0}' ValueLabel='True'>
<Condition Expression='{1}' />
<Setter Property='Background' Value='#FF008080' />
</Style>",
Resources.IsNamed.Replace("'", "'"),
IsNamed.Id
);
action.ActionHandlers.Add(this.OnAnalyzeNamed);
action.EnabledChanged += this.OnEnabledChanged;
action[DgmlProperties.BrowserGroup] = Microsoft.VisualStudio.Progression.Global.Resources.BrowserGroup_ActionAnalysis_Id_NOLOC_;
}
Afin de pouvoir sauver pour chaque noeud du graphe si celui-ci commence ou non par “Qetza.” nous avons besoins d’enregistrer une GraphProperty, dans l’exemple IsNamed. Un peu comme les DependencyProperty en WPF et Silverlight nous devons spécifier plusieurs informations :
- Un identifiant unique : “IsNamed”.
- Le type de valeur de la propriété : booléen.
- Des métadonnées dont le libellé (Resources.IsNamed), une description (Resources.IsNamedDescription) et des flags.
- Le type déclarant la propriété : NameAnalyzer.
Lors de l’initialisation nous créons et enregistrons une action à afficher dans le menu contextuel. Comme pour la GraphProperty nous devons spécifier plusieurs informations :
- Un identifiant : “AnalyzeNamed”.
- Un libellé : Resources.FindNamed.
- Des flags : IsAnalyzer et IsGlobal pour indiquer qu’il s’agit d’un analyseur.
- Une icone : null.
Nous spécifions ensuite plusieurs propriétés (qui sont des GraphProperty) :
- AnalyzerStyle : le style en XAML à appliquer aux noeuds ayant la valeur true pour la propriété IsNamed. Attention : l’attribut GroupLabel doit avoir la même valeur que le libellé de la GraphProperty associée (dans l’exemple j’encode aussi les apostrophes pour ne pas “cassé” le XAML) et l’attribut Expression doit avoir la même valeur de l’identifiant de la GraphProperty.
- ActionHandlers : on peut via cette propriété ajouter (ou supprimer) des méthodes à appeler lorsque l’action est exécutée.
- EnabledChanged : on peut via cet évènement gérer ce qui ce passe lorsque l’action est activée ou désactivée.
Maintenant que l’action est enregistrée il faut s’occuper du corps de l’analyseur, la méthode OnAnalyzeNamed :
public void OnAnalyzeNamed(ActionContext context)
{
this.m_graph = context.Graph;
this.m_context = context;
var result = context.OutputObjects;
using (GraphTransactionScope scope = new GraphTransactionScope("NamedAnalyzer"))
{
// reset any prior name information.
this.ClearNamed();
this.ThrowIfCancelled();
// analyzse nodes
foreach (Node node in this.m_graph.VisibleNodes)
{
if (node.Label.StartsWith("Qetza."))
{
node[IsNamed] = true;
result.Add(node);
}
this.ThrowIfCancelled();
}
scope.Complete();
}
}
public void ThrowIfCancelled()
{
if (this.m_context.Cancel)
{
throw new OperationCanceledException();
}
}
private void ClearNamed()
{
foreach (Node node in this.m_graph.Nodes)
{
if (node.HasValue(IsNamed))
{
node.ClearValue(IsNamed);
}
}
}
La méthode OnAnalyzeNamed va, dans une transaction spécifique pour les graphes :
- Remettre à zéro l’ensemble des informations sur les noeuds vis-à-vis de la propriété IsNamed (méthode ClearNamed).
- Pour chaque noeud visible du graphe passé en paramètre “associer” la GraphProperty IsNamed avec la valeur true au noeud si le libellé de celui-ci commence par “Qetza.” et ajouter ce noeud à la liste des noeuds mis à jour.
- Lors de chaque boucle on test si l’opération a été annulée afin de s’arrêter via la méthode ThrowIfCancelled.
Il ne reste plus qu’a gérer l’activation/désactivation de l’analyseur :
/// <summary>
/// Handle event when analyzer is enabled/disabled.
/// </summary>
private void OnEnabledChanged(object sender, EventArgs e)
{
if (this.m_graph != null)
{
using (GraphTransactionScope scope = new GraphTransactionScope("DisableUndo"))
{
// we always clear names whether we are enabled or disabled, since OnAnalyzeNames will always re-do the names anyway.
this.ClearNamed();
scope.Complete();
}
}
}
Dans une transaction nous remettons à zéro tous les noeuds. En effet dans notre exemple l’analyseur repasse sur l’ensemble des noeuds lors de son exécution.
Et voila notre analyseur personnalisé (et pas forcement très intéressant mais il fallait bien un exemple :p ) est terminé ! Afin de le tester et de pouvoir ensuite le distribuer facilement il suffit de créer un projet de type VSIX. Dans le contenu du package on ajoutera une entrée pointant sur notre bibliothèque de classes en spécifiant le type personnalisé “Microsoft.VisualStudio.DirectedGraph.Provider”. Le résultat en images :


Pour résumer, afin de créer un analyseur de graphe de dépendances personnaliser il faut :
- Implémenter l’interface Microsoft.VisualStudio.Progression.IProvider.
- Ajouter l’attribut Microsoft.VisualStudio.Progression.Provider à la classe.
- Enregistrer une (ou plusieurs) GraphProperty pour stocker les données de notre analyseur pour chaque noeud.
- Enregistrer une action pour déclencher le code de notre analyseur et définir le style d’affichage des noeuds.
Voila il ne vous reste plus qu’a faire marcher votre imagination pour créer des analyseurs plus intéressant que le mien
Carpe Diem.
Vous retrouverez la solution que j’ai utilisée pour ce billet à l’adresse http://blog.qetza.net/wp-content/uploads/2010/05/Qetza.Analyzers.zip .
VS 2010 : Personnalisation des évènements de l’IntelliTrace
8/03/10
Mon dernier billet date de 3 mois, il est temps de mit remettre
Aujourd’hui je vais vous montrer comment vous pouvez personnaliser l’IntelliTrace de VisualStudio 2010 afin qu’il trace les évènements de vos propres méthodes. Mais avant de commencer il faut que je vous préviennes (en rouge et gras pour que ce soit bien visible
) :
Microsoft ne fournit pas et ne fournira ni documentation ni support sur ce que je vais expliquer. Par ailleurs il n’est pas sur que la personnalisation de l’IntelliTrace ne soit pas modifiée dans le prochain VisualStudio (d’ou la non documentation et le non support).
Maintenant que je vous ai prévenu passons dans le vif du sujet : comment faire pour que je puisse avoir dans l’IntellTrace les appels à mes propres méthodes ! Afin de vous l’expliquer nous allons utiliser la classe suivante :
public class ColoredConsole
{
public static void WriteLine(int color, bool value)
{
ColoredConsole.WriteLine((ConsoleColor)color, () => Console.WriteLine(value));
}
public void WriteLine2(int color, bool value)
{
ColoredConsole.WriteLine((ConsoleColor)color, () => Console.WriteLine(value));
}
public static void WriteLine(ConsoleColor color, char value)
{
ColoredConsole.WriteLine(color, () => Console.WriteLine(value));
}
public static void WriteLine(ConsoleColor color, decimal value)
{
ColoredConsole.WriteLine(color, () => Console.WriteLine(value));
}
//...
public static void WriteLine(ConsoleColor color, string format, params object[] arg)
{
ColoredConsole.WriteLine(color, () => Console.WriteLine(format, arg));
}
private static void WriteLine(ConsoleColor color, Action writeLine)
{
var foreColor = Console.ForegroundColor;
Console.ForegroundColor = color;
writeLine();
Console.ForegroundColor = foreColor;
}
}
Cette classe permet d’écrire une ligne dans la console en spécifiant une couleur. Nous allons maintenant voir comment obtenir le résultat suivant dans l’IntelliTrace :

Comme vous pouvez le voir l’IntelliTrace a capturé l’appel aux méthodes de la classe ColoredConsole avec leurs paramètres d’entrées (ex: Console Output "3/7/2010 12:09:14 PM" in DarkBlue).
Pour personnaliser les évènements de l’IntelliTrace il faut modifier le fichier CollectionPlan.xml se trouvant dans le sous répertoire de VisualStudio "Team Tools\TraceDebugger Tools\en". Ce fichier xml contient la liste des évènements (méthodes) que doit pouvoir tracer l’IntelliTrace. La première chose à faire est de déclarer une nouvelle catégorie d’évènements. Pour cela on va rajouter un élément Category sous TracePointProvider/Categories :
<Categories> <Category Id="system.data" _locID="category.SystemData">ADO.NET</Category> <Category Id="system.web" _locID="category.SystemWeb">ASP.NET</Category> <Category Id="console" _locID="category.SystemConsole">Console</Category> <Category Id="data.binding" _locID="category.DataBinding">Data Binding</Category> <Category Id="environment.variables" _locID="category.SystemEnvironment">Environment Variables</Category> <Category Id="file.access" _locID="category.FileAccess">File</Category> <Category Id="gesture" _locID="category.Gesture">Gesture</Category> <Category Id="lazy" _locID="category.Lazy">Lazy Initialization</Category> <Category Id="registry.access" _locID="category.RegistryAccess">Registry</Category> <Category Id="servicemodel" _locID="category.SystemServiceModel">ServiceModel</Category> <Category Id="threading" _locID="category.Threading">Threading</Category> <Category Id="tracing" _locID="category.Tracing">Tracing</Category> <Category Id="user.prompt" _locID="category.UserPrompt">User Prompt</Category> <Category Id="workflow" _locID="category.SystemActivities">Workflow</Category> <Category Id="xml" _locID="category.SystemXml">XML</Category> <!-- Catégorie personnalisé --> <Category Id="qetza.coloredconsole" _locID="category.Qetza.ColoredConsole">ColoredConsole</Category> </Categories>
Il faut noter que ce fichier n’est lu qu’une fois au démarrage de VisualStudio, si vous le modifier il faudra redémarrer VisualStudio pour que votre modification soit prise en compte. Une fois redémarré avec la modification ci-dessus, en ouvrant les options de l’IntelliTrace, dans la liste des catégories vous devriez voir "ColoredConsole" :

Il va maintenant falloir remplir cette catégorie. Pour cela il va falloir déclarer un module, une assembly dans laquelle l’IntelliTrace pourra trouver des évènements. On va donc ajouter notre assembly à la liste de celle déjà présentes dans l’élément TracePointProvider/ModulesSpecifications :
<ModuleSpecifications> <ModuleSpecification Id="mscorlib">mscorlib.dll</ModuleSpecification> <ModuleSpecification Id="mscorlib2_only" maxVersion="2.0">mscorlib.dll</ModuleSpecification> <ModuleSpecification Id="mscorlib4_plus" minVersion="4.0">mscorlib.dll</ModuleSpecification> <ModuleSpecification Id="presentationframework">PresentationFramework.dll</ModuleSpecification> <ModuleSpecification Id="system">System.dll</ModuleSpecification> <ModuleSpecification Id="system.activities4_plus" minVersion="4.0">System.Activities.dll</ModuleSpecification> <ModuleSpecification Id="system.data">System.Data.dll</ModuleSpecification> <ModuleSpecification Id="system.servicemodel">System.ServiceModel.dll</ModuleSpecification> <ModuleSpecification Id="system.web">System.Web.dll</ModuleSpecification> <ModuleSpecification Id="system.web2_only" maxVersion="2.0">System.Web.dll</ModuleSpecification> <ModuleSpecification Id="winforms">System.Windows.Forms.dll</ModuleSpecification> <ModuleSpecification Id="system.xml">System.Xml.dll</ModuleSpecification> <!-- Module personnalisé --> <ModuleSpecification Id="qetza.consoleex.coloredconsole">Qetza.ConsoleEx.dll</ModuleSpecification> </ModuleSpecifications>
Comme vous pouvez le voir, en plus de l’identifiant qui est obligatoire, l’élément ModuleSpecification peut avoir les attributs optionnels minVersion et/ou maxVersion permettant de distinguer un même assembly en fonction de sa version.
Maintenant que notre module est déclaré il va falloir indiquer l’ensemble des méthodes que nous voulons tracer. Pour cela il faut ajouter un élément DiagnosticEventSpecification par méthode sous l’élément TracePointProvider/DiagnosticEventSepcifications :
<DiagnosticEventSpecification enabled="false">
<CategoryId>qetza.coloredconsole</CategoryId>
<SettingsName _locID="settingsName.Qetza.ColoredConsole.WriteLine.Char">ColoredWriteLine (Char)</SettingsName>
<SettingsDescription _locID="settingsDescription.Qetza.ColoredConsole.WriteLine.Char">Console Output with a colored Char passed in.</SettingsDescription>
<Bindings>
<Binding>
<ModuleSpecificationId>qetza.consoleex.coloredconsole</ModuleSpecificationId>
<TypeName>Qetza.ConsoleEx.ColoredConsole</TypeName>
<MethodName>WriteLine</MethodName>
<MethodId>Qetza.ConsoleEx.ColoredConsole.WriteLine(System.ConsoleColor,System.Char):System.Void</MethodId>
<ShortDescription _locID="shortDescription.Qetza.ColoredConsole.WriteLine.Char">"{0}"</ShortDescription>
<LongDescription _locID="longDescription.Qetza.ColoredConsole.WriteLine.Char">Console Output "{0}".</LongDescription>
<DataQueries>
<DataQuery index="1" maxSize="0" type="Char" _locID="dataquery.Qetza.ColoredConsole.WriteLine.Char" _locAttrData="name" name="value" query="" />
</DataQueries>
</Binding>
</Bindings>
</DiagnosticEventSpecification>
Une petite explication s’impose
- L’attribut enabled de l’élément DiagnosticEventSpecification permet de spécifier si la trace doit être active ou non par défaut.
- L’élément CategoryId contient l’identifiant de la catégorie dans laquelle cet évènement doit se trouver. Dans l’exemple ci-dessus il s’agit de notre catégorie.
- L’élément SettingsName permet de spécifier le nom de l’évènement tel qu’il apparaîtra dans sous la catégorie dans les options
- L’élément SettingsDescription permet de spécifier le texte de description qui apparaîtra en tooltip sur le nom de l’évènement.
- L’élément Bindings va contenir l’ensemble des binding à mettre en place lorsque l’évènement sera sélectionné. Chaque binding est décrit par un élément Binding. Vous pouvez donc activer la trace pour plusieurs méthodes via une seule entrée en spécifiant plus d’un élément Binding.
- L’élément ModuleSpecificationId permet de spécifier dans quel module se trouve la méthode à tracer.
- L’élément TypeName contient le nom complet du type contenant la méthode à tracer.
- L’élément MethodeName contient le nom de la méthode à tracer.
- L’élément MethodId contient l’identifiant de la méthode à tracer et doit avoir le format suivant : <type complet de la classe>.<nom de la méthode>(<types complets des paramètres séparés par des virgules>):<type complet de la valeur de retour>
- L’élément ShortDescription contient le texte qui sera afficher dans l’IntelliTrace sur la première ligne juste après le nom de la catégorie. Cette chaine de caractère peut contenir des marqueurs comme pour la méthode String.Format. Les valeurs qui seront fournit sont les valeurs récupérées via les éléments DataQuery déclaré dans le fichier CollectionPlan.xml.
- L’élément LongDescription contient le texte qui sera afficher sur la deuxième dans l’IntelliTrace. Comment pour ShortDescription ce texte peut contenir des marqueurs.
Avant de voir la suite voilà une petite image représentant visuellement les éléments que nous venons de voir :

Avec ce que nous avons vu vous pouvez déjà personnaliser l’IntelliTrace afin qu’il trace les appels à vos méthodes. Il reste maintenant à voir comment lui indiquer qu’elle sont les valeurs que vous souhaitez capturer à l’appel de la méthode. Pour cela il existe deux méthodes :
- La première est de déclarer un élément DataQueries avec ses sous éléments DataQuery dans le fichier CollectionPlan.xml sous le Binding correspondant.
- La deuxième est d’écrire une classe implémentant l’interface IProgrammableDataQuery déclarée dans l’assembly Microsoft.VisualStudio.IntelliTrace.dll.
Bien entendu ces méthodes ne sont pas équivalente. La première, entièrement déclarative, est la plus simple à mettre en place mais à une limitation, elle ne peut capturer que des valeurs de type simple :
- Boolean
- Char
- Double
- Single (float)
- Int16
- UInt16
- Int32
- UInt32
- Int64
- UInt64
- String (une exception mais surement car ce type est très utilisé)
Afin de capturer une valeur d’un de ces types il faut donc spécifier un élément DataQuery. Cet élément accepte les attributs suivant :
- index : L’attribut est obligatoire et :
- S’il vaut –1 cela indique que la source est la valeur de retour (voir plus bas).
- S’il vaut 0 cela indique que la source est l’instance courante si la méthode est une méthode d’instance. Si la méthode est une méthode statique alors 0 indique que la source est le premier paramètre de la méthode.
- S’il vaut 1 cela indique que la source est dans le cas d’une méthode d’instance le premier paramètre et dans le cas d’une méthode statique le deuxième paramètre.
- Et ainsi de suite pour les autres paramètres de la méthode.
- maxSize : dans le cas d’une chaine de caractère cet attribut spécifie le nombre maximum de caractère à capturer. Il est obligatoire dans le cas d’une chaine de caractère.
- type : cet attribut obligatoire spécifie le type de la valeur à capturer. Le type doit être un des types spécifié ci-dessus.
- name : Il s’agit du nom que l’on va donner à la valeur capturer afin de la reconnaitre facilement.
- query : l’attribut le plus "complexe". Lorsque la valeur spécifié par l’attribut index est un objet, cet attribut permet de spécifier le nom d’un champs (public, protected ou private) dont on veut capturer la valeur ou l’ensemble des champs permettant d’accéder à la valeur que l’on souhaite capturer (un peu comme un PropertyPath en WPF ou Silverlight mais sur des champs). Bien entendu la valeur finale doit être d’un des types spécifiés ci-dessus.
Comme pour les autres éléments, voici une représentation visuelle :

Concernant le cas particulier des valeurs de retour, comme indiqué ci-dessus pour pouvoir capturer la valeur de retour il faut ajouter un élément DataQuery avec un index de -1. Mais il faut aussi ajouter à l’élément Binding qui va contenir ce DataQuery l’attribut onReturn="true". Cela implique que vous ne pouvez pas capturer dans une même trace les paramètres d’entrés et les paramètres de sorties. Vous pouvez par contre regrouper les deux Binding dans un même élément DiagnosticEventSpecification :
<DiagnosticEventSpecification enabled="false">
<CategoryId>test</CategoryId>
<SettingsName _locID="test">Test</SettingsName>
<SettingsDescription _locID="Test">Test input/output trace.</SettingsDescription>
<Bindings>
<Binding>
<ModuleSpecificationId>test</ModuleSpecificationId>
<TypeName>Test.TestClass</TypeName>
<MethodName>Read</MethodName>
<MethodId>Test.TestClass.Read(System.String):System.String</MethodId>
<ShortDescription _locID="read">Reading {0}</ShortDescription>
<LongDescription _locID="read">Trying to read "{0}".</LongDescription>
<DataQueries>
<DataQuery index="1" maxSize="256" type="String" _locID="test.String" _locAttrData="name" name="value" query="" />
</DataQueries>
</Binding>
<Binding onReturn="true">
<ModuleSpecificationId>test</ModuleSpecificationId>
<TypeName>Test.TestClass</TypeName>
<MethodName>Read</MethodName>
<MethodId>Test.TestClass.Read(System.String):System.String</MethodId>
<ShortDescription _locID="read.return">Read {0}</ShortDescription>
<LongDescription _locID="read.return">Tried to read "{0}".</LongDescription>
<DataQueries>
<DataQuery index="-1" maxSize="256" type="String" _locID="test.String" _locAttrData="name" name="value" query="" />
</DataQueries>
</Binding>
</Bindings>
</DiagnosticEventSpecification>
Voila pour la première méthode
La deuxième méthode permet via l’implémentation de l’interface IProgrammableDataQuery d’aller un beaucoup plus loin lorsque l’on veut capturer des objets. Commençons par implémenter l’interface :
public class ColoredWriteLineDataQuery : IProgrammableDataQuery
{
#region IProgrammableDataQuery Members
public object[] EntryQuery(object thisArg, object[] args)
{
// thisArg contient l'instance de l'objet tracé
// args contient les paramètres d'entrés
// renvoi les paramètres d'entrés à prendre en compte pour le formattage
}
public object[] ExitQuery(object returnValue)
{
// returnValue contient la valeur de retour
// renvoi les paramètre de sortie à prendre en compte pour le formattage
}
public List<CollectedValueTuple> FormatCollectedValues(object[] results)
{
// renvoi une liste décrivant les paramètres d'entrés ou de sorties
// la liste contient des triplets de chaines de caractères (nom, valeur, type)
}
public string FormatLongDescription(object[] results)
{
// renvoi la description longue à partir des valeurs reçues
// ces valeurs proviennent soit de EntryQuery soit de ExitQuery
}
public string FormatShortDescription(object[] results)
{
// renvoi la description courte à partir des valeurs reçues
// ces valeurs proviennent soit de EntryQuery soit de ExitQuery
}
public List<Location> GetAlternateLocations(object[] results)
{
// je ne sais pas...
}
#endregion
}
Comme vous pouvez le voir on peut faire à peu près ce que l’on veut. Une fois cette classe créée il faut copier l’assembly la contenant dans le sous répertoire de VisualStudio "Team Tools\TraceDebugger Tools\" et mettre à jour le fichier CollectionPlan.xml :
<DiagnosticEventSpecification enabled="false">
<CategoryId>qetza.coloredconsole</CategoryId>
<SettingsName _locID="settingsName.Qetza.ColoredConsole.WriteLine.DateTime">ColoredWriteLine (DateTime)</SettingsName>
<SettingsDescription _locID="settingsName.Qetza.ColoredConsole.WriteLine.DateTime">Console Output with a colored DateTime passed in.</SettingsDescription>
<Bindings>
<Binding>
<ModuleSpecificationId>qetza.consoleex.coloredconsole</ModuleSpecificationId>
<TypeName>Qetza.ConsoleEx.ColoredConsole</TypeName>
<MethodName>WriteLine</MethodName>
<MethodId>Qetza.ConsoleEx.ColoredConsole.WriteLine(System.ConsoleColor,System.DateTime):System.Void</MethodId>
<ProgrammableDataQuery>
<ModuleName>Qetza.ConsoleEx.DataQueries.dll</ModuleName>
<TypeName>Qetza.ConsoleEx.DataQueries.ColoredWriteLineDataQuery</TypeName>
</ProgrammableDataQuery>
</Binding>
</Bindings>
</DiagnosticEventSpecification>
Dans le cas de l’utilisation de IProgrammableDataQuery on remplace l’élément DataQueries par l’élément ProgrammableDataQuery qui va contenir les informations nécessaire pour trouver la classe à utiliser :
- ModuleName : le nom de l’assembly contenant l’implémentation de l’interface.
- TypeName : le nom complet de la classe implémentant l’interface.
Pour les quelques courageux qui ont eu la patience de tout lire : bravo !
Et voilà c’en ait finit de ce long (qui a dit très long ?
) billet sur comment personnaliser l’IntellTrace de VisualStudio 2010 afin de tracer ces propres méthodes. Une dernière petite remarque avant la fin, vous pouvez bien entendu spécifier plusieurs DiagnosticEventSpecification pour une même méthode, ils seront alors tous appelés.
Je tiens aussi à remercie Ian Huff de Microsoft pour son aide : Merci !
Carpe Diem.
Vous retrouverez la solution que j’ai utilisée pour ce billet à l’adresse http://blog.qetza.net/wp-content/uploads/2010/03/Qetza.IntelliTrace.zip.
TechDays 2010
2/02/10
Comme vous le savez surement les TechDays 2010 ont lieu du 8 au 10 février au palais des congrès à Paris. Si vous êtes intéressé par VS 2010 et TFS 2010 je vous encourage vivement à venir, il y aura plein de sessions intéressantes.
J’animerai d’ailleurs avec Vincent Labatut la session sur Team Lab : Laboratoires de tests avec la virtualisation (IND203), venez nombreux
Carpe Diem.
Dossier VS 2010 dans Programmez
5/01/10
Un petit billet pour vous signaler la sortie du numéro 126 de Programmez :
Il contient un dossier sur Visual Studio 2010 que mes collègues Loïc Baumann et Vincent Labatut et moi même avons écrit. Si vous voulez en apprendre plus sur VS 2010 courrez l’acheter ! (non je ne reçois pas de royalties
).
Dans le prochain numéro nous vous parlerons de TFS 2010.
Carpe Diem.




