Il s'agit d'un point qui peut être facilement oublié, surtout si on ne réalise pas souvent de code d'accès aux données "bas niveau" : affecter une référence nulle à la propriété Value d'une instance de la classe SqlParameter ne provoque PAS l'envoi d'un NULL au serveur SQL.
Dans les faits, affecter une référence nulle à un paramètre sur un appel de procédure stockée équivaut à demander à ce que la valeur par défaut du paramètre soit utilisée : quand on doit envoyer un NULL au serveur, on doit affecter DBNull.Value en tant que valeur du paramètre.
Si on ne veille pas à ce point on risque de rencontrer des comportements non désirés (liste probablement non-exhaustive) :
- Levée d'une exception SqlException avec ce type de message : "Procedure or function 'sp_DoSomething' expects parameter '@Name', which was not supplied." ("La procédure ou fonction 'sp_DoSomething' attend le paramètre '@Name', qui n'a pas été fourni.").
- Utilisation non désirée de la valeur par défaut du paramètre de procédure stockée.
Je considère le cas d'utilisation non désirée de la valeur par défaut comme le plus problématique car il peut mener à une corruption de données directe.
Par "corruption de données directe" j'entends le cas où l'action en elle-même corrompt les données, donc l'absence de risque de corruption directe ne veut pas dire qu'il n'y aura pas un de ces cas de corruption indirecte qui peut survenir quand un code étouffe un cas d'erreur non attendu.
A ma connaissance l'oubli d'utilisation de DBNull.Value dans le cas de requêtes INSERT/UPDATE paramétrées ne devrait pas provoquer de corruption de données directe : il devrait toujours entrainer la levée d'une exception avec un message du type "The parameterized query '...' expects the parameter '...', which was not supplied." ("La requête paramétrée '...' attend le paramètre '...', qui n'a pas été fourni.").
Je pense qu'il est nécessaire de s'assurer que les équipes de développement sont conscientes de ce genre de détail de fond même si elles utilisent habituellement des frameworks qui masquent la plomberie ADO.NET : ça évitera peut-être quelques problèmes le jour où elles devront travailler sans.
La connaissance de ce genre de détails peut s'avérer utile lors de phases de debug sur du code hérité ou tout simplement sur du code mettant en oeuvre des librairies tierces.
Illustrons un peu tout ça avec un exemple de ce qu'il se passe dans le cas d'une procédure stockée.
Prenons ce prototype de procédure stockée :
CREATE PROCEDURE dbo.sp_DoSomething
(
@Id int,
@Name nvarchar(1024)
)
AS
BEGIN
PRINT N'Something';
END
Cette méthode qui l'utilise (à noter que j'utilise les définitions de paramètres "à l'ancienne" mais le comportement de AddWithValue est le même) :
public void DoSomething(Int32 id, String name)
{
using (SqlConnection connection = new SqlConnection(this.ConnectionString))
{
using (SqlCommand command = new SqlCommand("sp_DoSomething", connection))
{
command.CommandType = CommandType.StoredProcedure;
command.Parameters.Add("@Id", SqlDbType.Int, 4).Value = id;
command.Parameters.Add("@Name", SqlDbType.NVarChar, 1024)
.Value = name;
connection.Open();
command.ExecuteNonQuery();
}
}
}
Et cet appel de la méthode :
instance.DoSomething(
1, // id
null // name
);
Le résultat de l'exécution sera similaire à celui que nous aurions eu en utilisant le code TSQL suivant dans SQL Server Management Studio :
EXEC sp_DoSomething @Id=1, @Name=DEFAULT;
Alors que nous espérions probablement ceci :
EXEC sp_DoSomething @Id=1, @Name=NULL;
Notre procédure stockée ne spécifiant pas de valeur par défaut pour le paramètre @Name, cet appel va échouer avec l'erreur citée plus haut.
Le risque de corruption de données peut intervenir quand une valeur par défaut est définie pour le paramètre @Name :
CREATE PROCEDURE [dbo].[sp_DoSomething]
(
@Id int,
@Name nvarchar(1024) = N'lorem ipsum'
)
AS
BEGIN
PRINT @Name;
END
Si on utilise le même code côté .NET, il n'y aura pas d'erreur mais un print de "lorem ipsum".
Si le paramètre est utilisé pour être persisté ou pour prendre des décisions, on a utilisé une valeur "lorem ipsum" à la place d'un NULL, avec toutes les conséquences que cela peut avoir.
En général le code que nous voulions produire aurait été quelque chose de ce genre :
public void DoSomething(Int32 id, String name)
{
using (SqlConnection connection = new SqlConnection(this.ConnectionString))
{
using (SqlCommand command = new SqlCommand("sp_DoSomething", connection))
{
command.CommandType = CommandType.StoredProcedure;
command.Parameters.Add("@Id", SqlDbType.Int, 4).Value = id;
command.Parameters.Add("@Name", SqlDbType.NVarChar, 1024)
.Value = name ?? (Object)DBNull.Value;
connection.Open();
command.ExecuteNonQuery();
}
}
}
On pourrait se dire qu'il suffit que les membres de l'équipe soient au courant du fait que l'affectation d'une référence nulle entraine l'utilisation de la valeur par défaut mais dans les faits je pense que ce serait déstabilisant pour beaucoup de personnes, donc dangereux pour les phases de maintenance et lors de l'intégration de nouvelles personnes à l'équipe.
On s'attend en général plus à un parallèle entre null (C#) et NULL (SQL) qu'à ce genre de comportement donc autant établir le fait que null ne doit jamais être affecté explicitement comme valeur de paramètre, et garder uniquement l'absence d'ajout du paramètre comme déclencheur de l'utilisation de la valeur par défaut.
Ce n'est que mon avis et il est largement influencé par le fait que je ne travaille jamais dans des environnements où la définition d'une valeur par défaut pour un paramètre sert à autre chose qu'à éviter de briser du code existant pendant une phase de transition (ou pour des paramètres de déclenchement d'un mode debug/trace verbeuse qui doit être désactivé par défaut) : il existe peut-être des cas où cet avis n'est pas pertinent.
Je dois bien admettre que je ne suis pas très fan de cette fonctionnalité.
Sous Visual Studio elle s'appelle "Set Next Statement" ("Définir l'instruction suivante"), sous SharpDevelop c'est "Set current statement" (ou "Définir l'instruction suivante" si l'IDE est en français) et à ma connaissance cette fonctionnalité n'est pas disponible sous MonoDevelop (du moins sous Windows).
Dans tous les cas, il est important que l'utilisateur du debugger comprenne bien ce que fait cette fonctionnalité et surtout ce qu'elle ne fait pas.
A noter que dans ce post je ne parle en aucun cas de la fonctionnalité IntelliTrace apparue avec Visual Studio 2010 (édition Ultimate minimum...), sur laquelle je n'ai de toute façon aucun recul.
Où se trouve cette fonctionnalité ?
Lors d'une session de debug elle est utilisable, par exemple lorsqu'un point d'arrêt est atteint, depuis les menus contextuels ou par simple déplacement du curseur jaune.
Sous Visual Studio :

Sous SharpDevelop :

Ce que cette fonctionnalité ne fait pas
La fonctionnalité Set Next Statement n'est PAS une machine à remonter le temps : si nous l'utilisons pour déplacer le point d'exécution à un endroit que nous avons déjà passé, les effets des instructions qui ont eu lieu entre ce point dans le passé et le point présent ne seront PAS annulés.
Il n'y a aucun mécanisme de rollback sur les changements d'états, les changements en mémoire en général et encore moins sur les éléments externes (bases de données, fichiers, ...)
Ce que cette fonctionnalité fait
Elle ne fait rien d'autre que ce qu'elle annonce : elle nous permet de changer le point d'exécution, c'est-à-dire de définir l'instruction à partir de laquelle l'exécution reprendra quand l'exécution du thread sera relancée.
Cette manoeuvre peut être effectuée, au sein d'une méthode, vers l'avant (pour éviter d'exécuter certaines instructions) ou vers l'arrière (pour recommencer l'exécution de certaines instructions).
Un cas d'utilisation courant de cette fonctionnalité est lorsque l'utilisateur du debugger effectue un step over sur un appel de méthode au lieu du step into qu'il voulait faire.
Dans ce genre de cas l'utilisateur du debugger va vouloir rejouer l'exécution de la méthode sans reprendre sa session de debug de zéro.
Cette manoeuvre est rarement sans risque : il faudrait pour cela que l'exécution de la méthode ne modifie en rien l'état du programme, et qu'elle n'utilise aucune donnée qui soit modifiée par un autre thread ou un élément externe.
A quoi faut-il faire attention en utilisant cette fonctionnalité ?
A tout.
Il faut faire attention aux changements d'état qu'effectue le code qui va être rejoué :
- pour éviter les corruptions de données
- pour éviter, à l'issue de la phase de debug, de remonter un contexte de reproduction d'un bug qui n'est pas celui d'origine mais un effet de bord de la manoeuvre
- ...
Il faut faire attention à l'état actuel du programme et comprendre les effets de la reprise du code en un point donné car :
- nous n'avons pas envie de libérer un verrou que nous n'avons pas acquis
- nous n'avons pas envie d'utiliser l'instance d'une classe dont nous avons déjà appelé la méthode Dispose
- nous n'avons pas envie d'utiliser une référence qui a été affecté à null après utilisation
- ...
Les cas du verrou et de l'appel de Dispose sont intéressants car ils impliquent que l'utilisateur du debugger soit capable de faire attention aux instructions de bloc qui ne sont pas forcément très remarquables (telles que lock et using) et de se représenter mentalement ce que le compilateur va générer comme code à la compilation.
Dans le cas contraire, il va devoir comprendre pourquoi il obtient des exceptions étonnantes lors de la reprise d'exécution, telle que la très claire "System.Threading.SynchronizationLockException : Object synchronization method was called from an unsynchronized block of code." si le point d'exécution est repositionné en plein milieu d'un bloc lock alors que ce dernier avait été complété.
Car après tout, le mot clé lock n'est pas grand chose d'autre qu'une syntaxe plus concise pour laquelle le compilateur va générer l'équivalent d'un bloc try/finally utilisant la classe Monitor.
Si le mot clé lock n'avait pas existé, nous n'aurions pas écrit ça
private void DoSomething()
{
lock (this.lockTarget)
{
// ...
}
}
mais ça
private void DoSomething()
{
System.Threading.Monitor.Enter(this.lockTarget);
try
{
// ...
}
finally
{
System.Threading.Monitor.Exit(this.lockTarget);
}
}
Et là on s'aperçoit tout de suite mieux des conséquences d'un repositionnement du point d'exécution à hauteur du commentaire, donc dans le bloc try.
Avant de se risquer à utiliser "Set Next Statement" dans un code de ce genre, l'utilisateur du debugger devra aussi être capabable, le cas échéant, de se rendre compte des conséquences de l'exécution de code hors verrou qu'il a provoquée alors qu'elle n'était pas censée arriver afin de prendre la décision d'arrêter la session de debug à temps.
Le cas du bloc using est sensiblement identique à celui du bloc lock hormis le fait que la levée, sur les appels de méthodes de la classe, d'une exception ObjectDisposedException à même d'informer l'utilisateur du debugger de son erreur avant qu'il n'aille trop loin dépendra de l'implémentation du pattern Dispose et du design général de la classe.
Décidément, je n'aime vraiment pas ce "Set Next Statement"...
Debug
Debug / outils
- CLR Stack Explorer – Preview (Sasha Goldshtein)
Une preview d'un outil permettant notamment d'afficher les callstacks des threads managés sans sortir un debugger, mais aussi des informations sur les verrous. Outil à suivre.
Pour rappel, concernant les callstacks, Process Hacker commence aussi à fournir ce genre de visu.
Sécurité
- How to Preview Shortened URLs (TinyURL, bit.ly, is.gd, and more) (Joshua Long, via Kate Gregory)
Pour ceux qui détestent faire confiance aux services d'obfuscation de raccourcissement d'url dans certains contextes : ce post, faisant encore l'objet de mises à jour, donne des informations sur la manière d'obtenir l'URL réelle qui se cache sous la version raccourcie.
L'existence d'extensions de navigateurs offrant cette fonctionnalité rend la vie plus facile mais ça peut être utile de connaitre la version manuelle.
Internet Explorer
- PrivBar Update (Aaron Margosis)
Pour ceux qui ont besoin d'obtenir rapidement les informations concernant les privilèges dont dispose une instance d'Internet Explorer, Aaron Margosis a livré une mise à jour de son extension PrivBar.
Divers
- Greg's Cable Map - Cablemap.info (Greg Mahlknecht)
Une carte des câbles de télécommunications sous-marins montrant les points d'atterrissement et donnant accès à quelques détails sur les différents câbles.
Cette carte est le résultat d'un travail de recoupement de données par Greg Mahlknecht (avec visiblement des contributions d'autres personnes) et pas forcément une carte précise, mais c'est déjà très bien ! La précision du positionnement est donnée dans les détails de chaque câble.
On ne sait jamais quand ça peut servir ce genre de chose (oui, les probabilités que ce soit justement "jamais" sont plutôt fortes, je sais !).
Steve Johnson a livré une mise à jour de son extension SOSEX, en y ajoutant notamment quelques commandes.
Rien que mdso, remplissant le même rôle que la commande dso (DumpStackObjects) de SOS/PSSCOR, vaut le détour : elle sera sans doute appréciée par ceux qui ont un jour maudit la commande dso pour son affichage en "double" des objets présents plusieurs fois sur la stack ou pour l'impossibilité de filtrer la sortie sur certains types.
La commande msdo résoud ce problème :
- par défaut la commande élimine les doublons (le switch /a permet d'afficher la liste complète au cas où la localisation exacte de chaque entrée serait nécessaire)
- les switches /mt et /t permettent de ne lister que les objets des types spécifiés, soit par MethodTable, soit par un filtre sur le nom du/des type(s)
ex :
!mdso /mt:045096ac
!mdso /t:*Button
Je vois laisse découvrir la liste des nouvelles commandes et les améliorations apportées aux commandes existantes sur le post de Steve : Major SOSEX Update
La version 2.20 de Process Hacker, un outil similaire à Process Explorer dont je vous avais parlé il y a peu, intègre entres autres évolutions l'affichage amélioré des call stacks des threads managés.
Ca n'atteint bien sûr pas (encore?) le niveau de détails qu'on obtient sous Visual Studio ou avec SOS/PSSCOR sous WinDbg (!CLRStack / !DumpStack) mais ça permet déjà d'avoir une meilleure vision de ce qu'il se passe sur le thread.
Pour un thread exécutant du code .NET, les versions précédentes donnaient ce type d'affichage de call stack :

Alors qu'à partir de la version 2.20 nous disposons d'un affichage plus pratique, du point de vue du développeur .NET :

Voici donc un point sur lequel Process Hacker présente un avantage sur Process Explorer qui n'offre pas encore cette fonctionnalité (au moins jusqu'à la version 15.03) :

Ca fait plusieurs fois que je vois des choses de ce genre et ça à tendance à m'énerver.
On reçoit un mail (HTML, pas texte) contenant un lien qui semble brut, par exemple vers un profil sur un réseau social quelconque.
Prenons comme exemple quelque chose de ce genre (complètement fabriqué pour l'occasion, toute coïncidence avec un système existant serait involontaire) :

En passant le curseur au dessus du lien on s'aperçoit que sa cible n'est pas du tout celle affichée par le texte mais qu'il pointe vers autre chose, probablement un système de tracking qui fera ensuite la redirection vers la page réelle (parce que des fois ce n'est même pas clair) :

C'est désagréable car en fin de compte on à l'impression que l'expéditeur à utilisé une technique similaire à celles qu'on observe dans les tentatives de phishing (hameçonnage).
Autant dire que ça ne joue pas en faveur de l'émetteur qui, pour ma part, perd une partie de sa crédibilité, voire plus si je suis pris dans un moment de fatigue et que je me fais "avoir" en cliquant sur le lien trop vite.
La différence de perception entre cette pratique et l'exemple ci-dessous est importante :

Dans ce second cas j'ai plus de chances de penser, par réflexe, à vérifier le lien avant de cliquer : c'est justement pour celà que les liens faisant semblant d'être brut sont plutôt utilisés dans les emails malveillants.
Si vous êtes un expéditeur légitime vous n'avez rien à gagner à utiliser ce genre de technique douteuse, que ce soit volontairement ou non.
Si vous êtes utilisateur d'un système de mailing qui mettrait automatiquement à votre disposition un système de suivi en remplaçant vos liens par des liens de tracking, vérifiez ce qu'il se passe en vous envoyant une de vos communications au préalable.
Et dans le doute bâtissez vos communications HTML (pour le mode texte forcément ça ne s'applique pas) en évitant d'y placer des URLs brutes qui ne soient pas celles du système de tracking : ça vous évitera de vous retrouver involontairement dans ce cas.
Debug / Performance
Outils
Sécurité
- De l’usage « non sécurisé » des flashcodes (blog Conix Security)
Un exemple concret d'une des raisons pour lesquelles il faut, tout autant que pour n'importe quel lien, réfléchir avant de scanner un des codes-barres qu'on trouve maintenant un peu partout, notamment s'il est affiché dans un lieu public et si son utilisation amène à divulguer des informations bancaires ou personnelles. - “AlwaysInstallElevated” is Equivalent to Granting Administrative Rights (post d'Aaron Margosis)
Un paramètre de configuration permettant de mettre à disposition des utilisateurs un moyen d'élever leurs privilèges, super... A garder désactivé donc.
SQL Server
- SQL Server Version Database (SQLSecurity.com, via Greg Duncan)
Une liste des différents builds de SQL Server existant au fil des livraisons de Service Packs et de Cumulative Updates, avec les liens vers les articles de KB ou les pages du Download Center.
Utile pour déterminer ce qui peut manquer sur une installation existante afin d'évaluer la nécessité de passage d'un Cumulative Update, et le cas échéant son niveau d'urgence.
Cette page liste actuellement les builds des versions 6.5 à 2008 R2.
Une liste du même genre est disponible sur le blog de Pankaj Mittal : SQL Server Version History
Windows
Divers
Il est parfois nécessaire de déterminer la plateforme cible d'un assembly quand on ne dispose que de celui-ci et pas de la configuration de build utilisée pour le générer, par exemple pour s'assurer que les dépendances qui vont être chargées par un processus hôte sont bien compatibles au lieu de le découvrir à l'exécution via une exception BadImageFormatException.
Il existe plusieurs moyens d'y parvenir, le choix se faisant suivant le besoin de fond et le contexte (et les préférences personnelles), dont ceux-ci que nous allons détailler dans cet article :
- par un outil simple dédié à .NET
- par un outil moins simple
- par code avec une librairie existante
- par interprétation directe du fichier PE (avancé / par curiosité)
Contexte de rédaction :
Environnement : .NET 4.0 sous Windows 7 x86 et x64, Visual Studio 2010, Windows SDK v7.1
Version des documents cités :
Version des librairies utilisées pour les exemples de code :
Dans cet article nous allons prendre pour support 4 assemblies .NET 4.0 générés depuis du code C# avec la configuration de solution suivante :

Le cas des assemblies mixtes, contenant à la fois du code managé et natif, ne sera pas abordé.
Cet article part aussi du principe que l'assembly cible n'a pas été obfusqué d'une manière rendant impossible la lecture des en-têtes spécifiques à .NET.
Bien que n'ayant jamais eu affaire à la plateforme Itanium (IA-64), je l'ai incluse dans cet article car il peut y avoir confusion avec la plateforme x64 dans certains outils.
Les outils fournis par Microsoft seront lancés depuis la ligne de commande du SDK Windows version 7.1.
Par un outil simple dédié à .NET
Un outil comme ILSpy fourni la réponse assez facilement dans les propriétés de l'assembly chargé, par une des valeurs suivantes : AnyCPU, x64, x86 ou Itanium-64.

Par un outil moins simple
Si pour une raison ou une autre vous ne pouvez pas utiliser un outil comme ILSpy, il est possible de se débrouiller, un peu moins simplement, avec ceux fournis par le SDK Windows (gratuitement) ou Visual Studio.
Suivant le contexte et le niveau de certitude requis, 2 outils sont utilisables :
A noter que dans le cas présent CFF Explorer (GUI) pourrait remplacer DUMPBIN, par exemple si vous disposez déjà des outils .NET mais n'avez pas envie d'installer la partie C++ de Visual Studio ou du SDK.
Pour éviter tout risque de corruption accidentelle, utilisez cet outil sur une copie du fichier à inspecter : CFF Explorer permet de modifier les valeurs qu'il affiche.
Corflags
Commençons par CorFlags : les valeurs qui nous intéressent sont "PE" et "32BIT", et on remarque tout de suite une ambigüité entre les deux plateformes 64bit (x64 et Itanium) pour lesquelles l'outil affiche les mêmes valeurs :
corflags AnyCPULibrary.dll
[...]
Version : v4.0.30319
CLR Header: 2.5
PE : PE32
CorFlags : 1
ILONLY : 1
32BIT : 0
Signed : 0
corflags x86Library.dll
[...]
Version : v4.0.30319
CLR Header: 2.5
PE : PE32
CorFlags : 3
ILONLY : 1
32BIT : 1
Signed : 0
corflags x64Library.dll
[...]
Version : v4.0.30319
CLR Header: 2.5
PE : PE32+
CorFlags : 1
ILONLY : 1
32BIT : 0
Signed : 0
corflags ItaniumLibrary.dll
[...]
Version : v4.0.30319
CLR Header: 2.5
PE : PE32+
CorFlags : 1
ILONLY : 1
32BIT : 0
Signed : 0
La valeur "PE32+" pour les deux types d'architectures n'est pas forcément étonnante vu qu'elle est probablement issue du magic number présent dans les en-têtes du fichier Portable Executable et l'absence du flag "32BIT" dans une image 64bit est logique.
Si vous êtes certain de ne pas avoir à la fois des assemblies x64 et Itanium vous pouvez vous contenter d'utiliser CorFlags avec le tableau de correspondance suivant :

Dans le cas contraire, il est plus prudent d'utiliser les options /CLRHEADER et /HEADERS de DUMPBIN.
DUMPBIN
Si CorFlags laisse trop d'incertitudes, il est possible de se débrouiller avec les données fournies par les options /CLRHEADER et /HEADERS de DUMPBIN.
Voici un exemple de sortie avec notre assembly x86 :
dumpbin x86Library.dll /CLRHEADER /HEADERS
[...]
FILE HEADER VALUES
14C machine (x86)
3 number of sections
4DFA7751 time date stamp Thu Jun 16 23:36:17 2011
0 file pointer to symbol table
0 number of symbols
E0 size of optional header
2102 characteristics
Executable
32 bit word machine
DLL
OPTIONAL HEADER VALUES
10B magic # (PE32)
8.00 linker version
800 size of code
[...]
clr Header:
48 cb
2.05 runtime version
2058 [ 5B4] RVA [size] of MetaData Directory
3 flags
IL Only
32-Bit Required
0 entry point token
[...]
Les valeurs qui nous intéressent sont :
- le type de machine, défini par un des champs de l'en-tête de fichier du format COFF/PE : "machine (x86)" (14C) dans l'exemple ci-dessus.
- la présence du flag "32BITREQUIRED" dans la valeur "Flags" de l'en-tête CLI : "32-Bit Required" dans l'exemple.
Pour la liste des valeurs possibles pour le type de machine, voir la section COFF File Header (Object and Image) du document Microsoft PE and COFF Specification, ou les définitions IMAGE_FILE_MACHINE_* dans le fichier WinNT.h
Pour la liste des valeurs combinables pour la valeur "Flags" de l'en-tête CLI, voir la section File format extensions to PE / Section headers / CLI header / Runtime flags du document ECMA-335 : Common Language Infrastructure (CLI).
Quand le flag "32BITREQUIRED" est défini, la caractéristique "IMAGE_FILE_32BIT_MACHINE" (indiquée par "32 bit word machine" dans l'exemple) est censée être définie dans l'en-tête de fichier COFF, mais nous n'en tiendront pas directement compte.
La lecture des résultats est cette fois-ci sans ambigüité :

Dans CFF Explorer les libellés associés aux types de machines sont différents, mais les valeurs restent bien sûr les mêmes et le tableau ci-dessus reste utilisable.
Type de machine :

Flags .NET :

Par code avec une librairie existante
Il existe au moins 2 librairies permettant de déterminer la plateforme cible d'un assembly .NET :
Pour le sujet qui nous intéresse, Cecil est probablement la solution la plus facile à prendre en main quand on n'a pas eu de contact avec ces librairies auparavant.
Cecil est d'ailleurs actuellement utilisé par ILSpy, l'outil montré en début d'article.
Voici des exemples de code basique à adapter à vos besoins, notamment pour ce qui est de la gestion des erreurs.
Je n'ai jamais utilisé ces librairies en production, ces exemples de code sont uniquement donnés à titre d'illustration.
L'énumération TargetPlatform utilisée par les 2 exemples est définie comme ceci :
public enum TargetPlatform
{
Unknown = 0,
AnyCPU = 1,
x86 = 2,
x64 = 3,
Itanium = 4
}
Cecil
Nécessite une simple référence à l'assembly Mono.Cecil.
using Mono.Cecil;
// ...
public class TargetPlatformUtilities
{
public static TargetPlatform GetTargetPlatform(String assemblyFilePath)
{
// !!! CODE D'ILLUSTRATION NON PRET POUR PRODUCTION !!!
// !!! SAMPLE CODE NOT READY FOR PRODUCTION !!!
// TODO : error handling, ...
TargetPlatform detectedPlatform = TargetPlatform.Unknown;
ModuleDefinition module = ModuleDefinition.ReadModule(assemblyFilePath);
switch (module.Architecture)
{
case TargetArchitecture.AMD64:
detectedPlatform = TargetPlatform.x64;
break;
case TargetArchitecture.I386:
if ((module.Attributes & ModuleAttributes.Required32Bit) == ModuleAttributes.Required32Bit)
{
detectedPlatform = TargetPlatform.x86;
}
else
{
detectedPlatform = TargetPlatform.AnyCPU;
}
break;
case TargetArchitecture.IA64:
detectedPlatform = TargetPlatform.Itanium;
break;
default:
detectedPlatform = TargetPlatform.Unknown;
break;
}
return detectedPlatform;
}
}
CCI Metadata
Nécessite des références aux assemblies :
- Microsoft.Cci.MetadataHelper
- Microsoft.Cci.MetadataModel
- Microsoft.Cci.PeReader
using Microsoft.Cci;
// ...
public class TargetPlatformUtilities
{
public static TargetPlatform GetTargetPlatform(String assemblyFilePath)
{
// !!! CODE D'ILLUSTRATION NON PRET POUR PRODUCTION !!!
// !!! SAMPLE CODE NOT READY FOR PRODUCTION !!!
// TODO : error handling, ...
TargetPlatform detectedPlatform = TargetPlatform.Unknown;
using (MetadataReaderHost host = new PeReader.DefaultHost())
{
IModule module = host.LoadUnitFrom(assemblyFilePath) as IModule;
if (module != null)
{
switch (module.Machine)
{
case Machine.AMD64:
detectedPlatform = TargetPlatform.x64;
break;
case Machine.I386:
if (module.Requires32bits)
{
detectedPlatform = TargetPlatform.x86;
}
else
{
detectedPlatform = TargetPlatform.AnyCPU;
}
break;
case Machine.IA64:
detectedPlatform = TargetPlatform.Itanium;
break;
default:
detectedPlatform = TargetPlatform.Unknown;
break;
}
}
}
return detectedPlatform;
}
}
Par interprétation directe du fichier PE (avancé / par curiosité)
Les librairies comme CCI Metadata et Cecil contiennent le code nécessaire pour interpréter le fichier Portable Executable représentant l'assembly .NET, mais fournissent beaucoup plus de services que la simple détermination de plateforme cible.
Si vous désirez écrire un code dédié à cette seule tâche, il va falloir entrer dans le détail du format PE/COFF et ses extensions pour la CLI.
Voici quelques explications basées sur ma compréhension générale du sujet, c'est-à-dire du point de vue d'un développeur .NET sur plateforme Windows et donc actuellement confronté uniquement aux architectures x86 et x64 (ou x86-64/AMD64/Intel 64/...).
Dans la mesure du possible et suivant leur licence, il est sans doute plus prudent de se baser sur le code d'interprétation PE des librairies comme Cecil et CCI Metadata vu qu'elles ont déjà été éprouvées.
L'éditeur hexa utilisé ci-dessous est HxD. Si vous ne pouvez vous servir que de Visual Studio, il vous suffit d'ouvrir l'assembly dans l'éditeur binaire au lieu de l'éditeur de ressources (Ctrl+O => dropdown du bouton Open => Open With... => Binary Editor).
Type de machine
D'après le document Microsoft PE and COFF Specification le type de machine est stocké sur 2 octets en première valeur de l'en-tête COFF, soit à l'offset de fichier défini par l'offset de la signature PE + 0x4.
L'offset de la signature PE est définie par la valeur sur 4 octets située à l'offset de fichier 0x3C.
Exemple avec notre assembly x86 :
Position de l'en-tête PE, ici 0x80

L'en-tête PE en lui-même ("PE\0\0")

Et juste après, en première entrée de la partie COFF File Header (offset 0x84), la définition du type de machine

0x14C, c'est-à-dire x86 (donc AnyCPU ou x86 du point de vue de la plateforme cible de compilation).
32-bit required ?
Pour déterminer cette valeur nous avons besoin d'accéder aux données de l'en-tête CLI ("CLR Runtime Header"), et plus particulièrement à la valeur Flags.
Nous devons récupérer l'adresse et la taille de cet en-tête dans la 15ème entrée de la liste des Optional Header Data Directories.
Nous allons nous reposer sur les documents Microsoft PE and COFF Specification et ECMA-335 : Common Language Infrastructure (CLI) (section File format extensions to PE / Section headers / CLI header / Runtime flags).
Format PE
Certains offsets et tailles de données vont maintenant varier suivant le fait que le fichier PE est au format PE32 ou PE32+ ("PE64").
Pour déterminer le format, il faut lire le magic number qui est la première valeur de l'en-tête optionnel Optional Header (obligatoire dans le cas d'une image, donc notre cas) : cette valeur sur 2 octets se trouve à l'offset de fichier [offset COFF File Header]+0x14 (=[offset Optional Header]).
Soit dans le cas de notre assembly x86, à l'offset 0x98
Magic number = 0x10B, soit le format PE32.
L'assembly AnyCPU déclarera la même valeur, alors que les assembly x64 et Itanium déclareront une valeur 0x20B (PE32+).
Nombre d'entrées Data Directories
Même s'il doit normalement être de 16 (0x10), nous allons vérifier que le nombre de Data Directories est bien supérieur ou égal à 15 (0xF).
Cette valeur sur 4 octets, NumberOfRvaAndSizes, se lit à l'offset [offset Optional Header]+0x5C dans le cas de PE32 (ou [offset Optional Header]+0x6C dans celui de PE32+).
Dans le cas de notre assembly x86, ce sera donc à l'offset 0xF4 :
Nous avons bien 16 entrées.
Data directory concernant l'en-tête CLI
Nous savons que l'entrée donnant l'adresse et la taille de l'en-tête CLI est la 15ème de la liste et se situe à l'offset [offset Optional Header]+0xD0 dans le cas de PE32 (ou [offset Optional Header]+0xE0 dans celui de PE32+).
Dans le cas de notre assembly x86, ce sera donc à l'offset 0x168 :
VirtualAddressCLIHeader = 0x00002008
SizeCLIHeader = 0x48
Détermination de l'offset de fichier de l'en-tête CLI : localisation de la Section Table
Comme nous travaillons sur le fichier et pas en mémoire, la valeur de VirtualAddress (Relative Virtual Address, RVA) ne nous sera pas très utile : nous avons besoin de la convertir en offset de fichier au moyen des en-têtes de section.
La table des sections (Section Table) se trouve directement après l'en-tête optionnel.
Comme la taille l'en-tête optionnel n'est pas fixe, nous aurons besoin de la valeur SizeOfOptionalHeader (2 octets) de l'en-tête COFF, lisible à l'offset de fichier [offset COFF File Header]+0x10
Soit dans notre exemple l'offset 0x94 :
La taille de l'en-tête optionnel est 0xE0 (224 octets) donc notre Section Table se trouve à l'offset [offset Optional Header]+0xE0 (=[offset Section Table])
Soit dans notre cas l'offset 0x178.
Détermination de l'offset de fichier de l'en-tête CLI : nombre de sections
Le nombre de sections est défini par la valeur NumberOfSections (2 octets) de l'en-tête COFF, lisible à l'offset de fichier [offset COFF File Header]+0x2
Soit dans notre exemple l'offset 0x86 :
Nous avons donc 3 sections.
Détermination de l'offset de fichier de l'en-tête CLI : recherche de la section intéressante
La taille de chaque en-tête de section est de 40 octets (0x28), nos 3 en-têtes sont donc situés aux offsets 0x178, 0x1A0 et 0x1C8.
Ce qui nous intéresse en premier lieu est de trouver quelle section contient notre en-tête CLI, donc celle pour laquelle ce test est vérifié : VirtualAddress <= VirtualAddressCLIHeader < VirtualAddress+VirtualSize
Pour rappel, VirtualAddressCLIHeader = 0x00002008
La valeur VirtualSize (4 octets) est située à l'offset [offset Section Header]+0x8
La valeur VirtualAddress (4 octets) est située à l'offset [offset Section Header]+0xC
Dans notre exemple, c'est la section .text qui correspond ([offset Section Header]=0x178) :
VirtualAddressSection = 0x00002000
VirtualSizeSection = 0x744
Détermination de l'offset de fichier de l'en-tête CLI : calcul de l'offset
Nous avons besoin de la valeur PointerToRawData (4 octets) de la section correspondant à VirtualAddressCLIHeader, située à l'offset [offset Section Header]+0x14
PointerToRawData = 0x200
Nous pouvons maintenant déterminer l'offset de fichier de notre en-tête CLI : il s'agit de la valeur définie par PointerToRawDataSection + (VirtualAddressCLIHeader - VirtualAddressSection).
[offset CLI Header] = 0x200 + (0x00002008 - 0x00002000)
[offset CLI Header] = 0x208

Extraction de la valeur Flags de l'en-tête CLI
Le document ECMA-335 : Common Language Infrastructure (CLI) décrit l'en-tête CLI dans sa section File format extensions to PE / Section headers / CLI header / Runtime flags
La valeur (4 octets) est située à l'offset [offset CLI Header]+0x10, soit 0x218 pour notre assembly x86 :
0x3 = COMIMAGE_FLAGS_ILONLY | COMIMAGE_FLAGS_32BITREQUIRED
32-bit required = true
En reprenant tout cela avec l'assembly x64, ça donne :
Position de l'en-tête PE : 0x80
[offset COFF File Header] = 0x84
Type de machine (offset 0x84) : 0x8664 (x64)
[offset Optional Header] = 0x98
Format PE (offset 0x98) : 0x20B (PE32+)
NumberOfRvaAndSizes (offset 0x104) : 0x10
VirtualAddressCLIHeader (offset 0x178) = 0x00002000
SizeCLIHeader (offset 0x17C) : 0x48
SizeOfOptionalHeader (offset 0x94): 0xF0
[offset Section Table] = 0x188
NumberOfSections (offset 0x86) : 2
Section contenant le header CLI : .text (0x00002000, 0x6EB)
[offset Section Header] = 0x188
PointerToRawData (offset 0x19C) : 0x200
[offset CLI Header] = 0x200
Flags (offset 0x210) : 0x1 (COMIMAGE_FLAGS_ILONLY, 32-bit required = false)
Un peu de détente avec un recueil de 0x8f* dessins de Geek&Poke qui ciblaient les développeurs en particulier.
Il est apparemment sorti en octobre 2010 mais je viens seulement de prendre le temps de le consulter et ça vaut le coup d'oeil, pour tout développeur ayant un minimum d'humour ;-)
Le livre est organisé sous forme d'affirmations illustrées par un dessin (ironique), et son introduction donne un bon aperçu du ton :

Il n'y a apparemment pas d'article dédié sur le site de l'auteur, Oliver Widder, donc je vais reproduire ici les détails donnés par le cadre affiché en colonne gauche du blog Geek&Poke (si vous tombez tardivement sur ce post, reportez vous au site Geek&Poke pour être certain d'obtenir la dernière version) :

Trois moyens de le lire, donc :
* : selon les propres termes de l'auteur
Développement
Debug / Performance
- Psscor4 Managed-Code Debugging Extension for WinDbg (centre de téléchargement MS)
Même si la version de SOS pour CLR 4.0 a apporté quelques commandes supplémentaires par rapport à la version CLR 2.0, on n'y retrouvait pas encore l'intégralité des ajouts de PSSCOR2. Ce problème est maintenant résolu grace à la mise à disposition de PSSCOR4.
Un bémol de mon côté : sur la totalité de mes configurations je suis confronté à un problème avec cette extension dans sa version actuelle (26/04/2011). En effet, la sortie de la commande DumpHeap (exemple : !DumpHeap -type Exception) m'affiche deux lignes par objet :
0:004> !dumpheap -type Exception
Loading the heap objects into our cache.
Address MT Size
01a01024 5049fdc4 84 0 System.Exception
01a01024 5049fdc4 84
Comme ça se produit aussi quand la commande spécifie le paramètre -short, ça peut s'avérer problématique au delà de la gêne visuelle.
Le problème a été remonté et j'attend des nouvelles, mais si vous n'y êtes pas confronté vous pouvez profiter pleinement de l'extension dès maintenant. - ProcExp and XPerf tracing (blog de Maarten Van De Bospoort)
Il me semble avoir déjà eu un problème similaire en jouant avec ETW et il ne m'avait pas traversé l'esprit de vérifier si un outil d'analyse/monitoring du type Process Explorer/Process Monitor/... était en cours d'exécution. - Shared Source CLI 2.0 Internals (ebook gratuit, blog de Joel Pobar)
Ce n'est plus tout jeune et ce n'est pas le genre de livre qu'on ouvre tous les jours, mais autant l'avoir sous la main quand on se retrouve dans les sources de Rotor au fil de ses recherches.
Visiblement j'avais zappé l'annonce de la sortie du livre car j'étais encore sur le draft jusqu'à ce que je retombe sur le blog de Joel Pobar et un post sur Rotor à la fin duquel il est lié.
Le lien est aussi disponible sur cette page, section "Book Contributions" > "Shared Source CLI 2.0 Internals (co-author)"
Divers
- Unicode 6.0 — One character at a time (blog BabelStone)
Un slideshow Javascript qui tente (suivant les polices disponibles sur la machine) d'afficher les 109384 caractères d'Unicode 6.0, en réponse (en quelque sorte) à une vidéo de 33 minutes en affichant 49571.
De quoi martyriser un peu son navigateur avec du simple texte.
La slideshow est paramétrable tant sur les plages de caractères à afficher que sur le tempo d'affichage et la taille de caractère : vous n'êtes pas obligé d'utiliser le mode violent.
Au passage, l'auteur est aussi celui de 2 outils dont je vous avais parler il y a quelques temps : BabelPad et BabelMap.
Internet Explorer 9 bascule par défaut sur le rendu en mode de compatibilité pour les sites situés dans la zone Intranet.
Ca peut s'avérer légèrement agaçant quand on possède une majorité de sites qui n'en ont pas besoin et qui dans certains cas ont un rendu assez désagréable dans ce mode.
Ce comportement existait aussi sous IE8 mais ne m'avait jamais vraiment posé problème, alors que sous IE9 si.
Pour changer cela il faut aller dans les paramètres du mode de compatibilité accessibles via le menu principal (Alt ou F10) :

Et décocher l'option "Afficher les sites intranet dans Affichage de compatibilité" ("Display intranet sites in Compatibility View") :

Les sites posant problème pourront toujours être basculés en mode de compatibilité au cas par cas.
Côté GPO, je n'ai pas vraiment trouvé l'équivalent dans la liste des options disponibles pour Internet Explorer.
Avant Internet Explorer 9, cette information était visible simplement dans la barre d'état.
Maintenant même les personnes qui voudraient sacrifier quelques pixels d'espace d'affichage pour obtenir quelques informations contextuelles ne "peuvent" plus le faire vu que la barre d'état ne contient plus vraiment d'information utile.
La dernière fois je m'étais demandé où cette information pouvait maintenant se trouver, sans prendre le temps de réellement chercher sur le moment, et je viens de le découvrir en lisant un post d'Eric Lawrence : la zone se voit tout simplement dans la boite de dialogue de propriétés de la page (je me demande si ce c'était pas déjà le cas sur les versions antérieures) :

Bon c'est vrai que je suis un peu idiot de ne pas avoir pensé à vérifier que cette information ne se trouvait pas à cet endroit, mais en même temps jusqu'à maintenant je l'avais toujours eue sous les yeux...
Je me demande si cette décision de mise à mort de la barre d'état a été prise pour qu'un maximum de personnes laisse cette barre d'état désactivée : pour moi la raison "affichage retiré pour ne pas déstabiliser l'utilisateur lambda avec des informations techniques" ne tient pas vraiment vu que la barre d'état est masquée par défaut.
Et je n'ai pas trouvé d'option qui permette de redonner une utilité à la barre d'état, mais je l'ai peut-être tout simplement ratée.
Debug / Performance
- CLR 4 Does Not Use LoadLibrary to Load Assemblies (blog de Sasha Goldshtein)
Il s'agit d'un changement apporté par .NET 4 qui n'est pas forcément celui qu'on remarque le plus dans la liste ".NET Framework 4 Migration Issues" (section Core > Reflection) : "To prevent redundant loading of assemblies and to save virtual address space, the CLR now loads assemblies by using only the Win32 MapViewOfFile function. It no longer also calls the LoadLibrary function."
L'auteur met en avant une conséquence de ce changement : l'absence de module entraine l'absence de chargement des symboles et par conséquent l'absence des numéros de ligne dans les stacktraces quand on utilise des outils WinDbg et les extensions SOS/SOSEX.
A noter que Visual Studio 2010 et MDbg sont capables d'utiliser les symboles quand un dump de processus .NET 4 leur est soumis, donc au pire des cas on peut utiliser un de ces outils pour récupérer le numéro de ligne et conserver les extensions habituelles pour le debug avancé. - Silverlight Memory Leaks and AutomationPeers (blog de Sergio Loscialo)
Un cas intéressant qui montre notamment que si l'environnement cible d'une application risque d'inclure de l'UI Automation, il vaut mieux en tenir compte durant le développement et les tests.
Dans le cas d'applications Silverlight grand public autant dire qu'il vaut mieux en tenir compte systématiquement, vu l'hétérogénéité de configurations à laquelle il faut s'attendre. - Top Customer Misconceptions about Software Problems (blog de Roberto Alexis Farah)
Une exposition de 13 idées reçues sur l'analyse d'incidents et le debug.
Je suis presque certain que chacun d'entre nous peut avoir une impression de déjà-vu dans au moins un des cas :-)
Outils / SQL Server
- SQL Sentry Plan Explorer (outil gratuit, SQL Sentry)
Un outil permettant de faciliter l'analyse de plan d'exécution SQL Server, comparé à ce qui est disponible dans SSMS.
A priori il était disponible en version beta depuis octobre 2010 mais je le découvre seulement maintenant via un post de Jonathan Kehayias, qui propose un script PowerShell permettant de nettoyer un plan d'exécution des StatementText afin de ne pas les divulguer à l'extérieur.
Divers
- Mapping Sockets to a Process In .NET Code (blog Fiddler)
Je m'étais déjà demandé comment Fiddler et d'autres outils comme Network Monitor faisaient le lien entre une connexion et le processus lié, mais je n'avais jamais pris le temps de réellement creuser le sujet. En fin de compte ils utilisent une fonction, de l'API IP Helper, que j'ai déjà utilisée pour autre chose.
Le post donne un exemple d'utilisation de GetExtendedTcpTable depuis du code .NET. - The double-Ctrl+Alt+Del feature is really a kludge (blog The Old New Thing)
Si vous vous demandiez pourquoi la technique du double "Ctrl+Alt+Suppr" qui était disponible sur l'écran de bienvenue de Windows XP ne l'est plus dans les versions ultérieures, ce post contient la réponse.
Ce post n'est plus tout jeune mais je l'avais raté à l'époque (ou ne m'en souvenais plus) et je viens de (re)tomber dessus.
PowerShell
- PowerShell Language now licensed under the Community Promise (blog Windows PowerShell)
Ou la possibilité d'implémenter librement PowerShell sur d'autres plateformes que Windows.
Intéressant. Je ne travaille jamais moi-même sur d'autres plateformes, mais j'avoue que l'idée de pouvoir échanger en script PowerShell avec ceux qui le font est séduisante. On verra bien comment ça va évoluer.
En dehors de l'aspect ouverture aux autres plateformes, ça fait aussi un peu de lecture pour les curieux !
Je me suis dernièrement retrouvé devant un cas de blocage de SQL Server Management Studio 2008 R2 assez "intéressant" : un clic droit sur le noeud du serveur local dans l'explorateur d'objets entrainait un blocage de SSMS pendant un laps de temps assez long (que je n'ai pas réellement mesuré).

Une fois le blocage terminé les tentatives suivantes réussissaient sans problème particulier, jusqu'à l'ouverture de session suivante.
MAJ 28/03/2011 : j'ai un peu étoffée la partie expliquant le blocage du thread d'interface graphique.
Une petite analyse rapide avec WinDbg/PSSCOR2 a montré une stack intéressante :
La clé du problème
OS Thread Id: 0x1da4 (5)
ESP EIP
0766efc0 77d104f2 [ComPlusMethodFrameGeneric: 0766efc0] System.Management.IWbemServices.GetObject_(System.String, Int32, System.Management.IWbemContext, System.Management.IWbemClassObjectFreeThreaded ByRef, IntPtr)
0766efe0 6751983a System.Management.ManagementObject.Initialize(Boolean)
0766f048 67515b32 System.Management.ManagementBaseObject.get_Properties()
0766f054 67515f06 System.Management.ManagementBaseObject.GetPropertyValue(System.String)
0766f064 67515e55 System.Management.ManagementBaseObject.get_Item(System.String)
0766f068 6acff89e Microsoft.SqlServer.Management.UI.VSIntegration.ObjectExplorer.ROOT.CIMV2.Win32.Service.get_State()
0766f070 6acfc5dc Microsoft.SqlServer.Management.UI.VSIntegration.ObjectExplorer.Service.UpdateState(Boolean)
0766f0b0 6acfcb7c Microsoft.SqlServer.Management.UI.VSIntegration.ObjectExplorer.Service.BeginServiceLookupInt()
0766f0e4 6acfb97d Microsoft.SqlServer.Management.UI.VSIntegration.ObjectExplorer.Service+AsyncWmiBinding.RequestHandler(System.Object)
0766f0f0 6f769f4f System.Threading._ThreadPoolWaitCallback.WaitCallback_Context(System.Object)
0766f0f8 6f795731 System.Threading.ExecutionContext.runTryCode(System.Object)
0766f528 70061b5c [HelperMethodFrame_PROTECTOBJ: 0766f528] System.Runtime.CompilerServices.RuntimeHelpers.ExecuteCodeWithGuaranteedCleanup(TryCode, CleanupCode, System.Object)
0766f590 6f795627 System.Threading.ExecutionContext.RunInternal(System.Threading.ExecutionContext, System.Threading.ContextCallback, System.Object)
0766f5ac 6f780255 System.Threading.ExecutionContext.Run(System.Threading.ExecutionContext, System.Threading.ContextCallback, System.Object)
0766f5c4 6f76a4b3 System.Threading._ThreadPoolWaitCallback.PerformWaitCallbackInternal(System.Threading._ThreadPoolWaitCallback)
0766f5d8 6f76a349 System.Threading._ThreadPoolWaitCallback.PerformWaitCallback(System.Object)
0766f768 70061b5c [GCFrame: 0766f768]
est ici l'appel "Microsoft.SqlServer.Management.UI.VSIntegration.ObjectExplorer.ROOT.CIMV2.Win32.Service.get_State()" : SSMS semble être bloqué pendant la tentative de récupération du statut du service Windows de SQL Server.
Il s'agit d'un thread effectuant un traitement probablement lancé en asynchrone lors de la connexion au serveur, et qui possède peut-être un verrou intéressant :
0:000> !threads
[...]
PreEmptive GC Alloc Lock
ID OSID ThreadOBJ State GC Context Domain Count APT Exception
0 1 cc8 0093e220 2004220 Enabled 03a4731c:03a47f70 00936680 0 STA
4 2 16b4 033f8918 b220 Enabled 00000000:00000000 00936680 0 MTA (Finalizer)
5 3 1da4 034afa70 180b220 Enabled 00000000:00000000 00936680 3 MTA (Threadpool Worker)
7 4 10e0 07a9d370 80a220 Enabled 00000000:00000000 00936680 0 MTA (Threadpool Completion Port)
8 5 804 07ab3ff0 1220 Enabled 00000000:00000000 00936680 0 Ukn
[...]
0:000> !SyncBlk
Index SyncBlock MonitorHeld Recursion Owning Thread Info SyncBlock Owner
149 033ff1ac 3 1 034afa70 1da4 5 0387db14 System.Object
Waiting threads: 0
-----------------------------
Total 152
CCW 39
RCW 36
ComClassFactory 0
Free 9
De son côté le thread de l'interface graphique est en attente de l'obtention d'un verrou, lors d'un appel à une autre méthode WMI :
0:000> !CLRStack
OS Thread Id: 0xcc8 (0)
ESP EIP
003de2ac 77d100ed [GCFrame: 003de2ac]
003de37c 77d100ed [HelperMethodFrame_1OBJ: 003de37c] System.Threading.Monitor.Enter(System.Object)
003de3d4 6acfd475 Microsoft.SqlServer.Management.UI.VSIntegration.ObjectExplorer.Service.get_CanStart()
003de404 6ad10c0c Microsoft.SqlServer.Management.UI.VSIntegration.ObjectExplorer.ConfigureServicesHandler.UpdateMenuCommandStatus(System.ComponentModel.Design.MenuCommand)
003de418 6ace01c9 Microsoft.SqlServer.Management.UI.VSIntegration.ObjectExplorer.DefaultMenuHandler.UpdateMenuCommandsStatus(System.ComponentModel.Design.MenuCommand)
003de444 6acdf3b1 Microsoft.SqlServer.Management.UI.VSIntegration.ObjectExplorer.DefaultMenuHandler.GetMenuItems(System.Collections.ArrayList)
003de500 6acdffcc Microsoft.SqlServer.Management.UI.VSIntegration.ObjectExplorer.DefaultMenuHandler.GetMenuItems()
003de504 6acdc40b Microsoft.SqlServer.Management.UI.VSIntegration.ObjectExplorer.ExplorerHierarchyNode.ShowContextMenu(System.Drawing.Point)
003de528 6acdd77b Microsoft.SqlServer.Management.UI.VSIntegration.ObjectExplorer.LazyTreeView.WmContextMenu(System.Windows.Forms.Message ByRef)
003de554 6acdd847 Microsoft.SqlServer.Management.UI.VSIntegration.ObjectExplorer.LazyTreeView.WndProc(System.Windows.Forms.Message ByRef)
003de568 6d0b84a0 System.Windows.Forms.Control+ControlNativeWindow.OnMessage(System.Windows.Forms.Message ByRef) f:\dd\ndp\fx\src\WinForms\Managed\System\WinForms\Control.cs:14059
003de570 6d0b8421 System.Windows.Forms.Control+ControlNativeWindow.WndProc(System.Windows.Forms.Message ByRef) f:\dd\ndp\fx\src\WinForms\Managed\System\WinForms\Control.cs:14114
003de584 6d0b82fa System.Windows.Forms.NativeWindow.Callback(IntPtr, Int32, IntPtr, IntPtr) f:\dd\ndp\fx\src\WinForms\Managed\System\WinForms\NativeWindow.cs:647
003de72c 02daa074 [NDirectMethodFrameStandalone: 003de72c] System.Windows.Forms.UnsafeNativeMethods.SendMessage(System.Runtime.InteropServices.HandleRef, Int32, IntPtr, IntPtr)
003de748 6d0c3654 System.Windows.Forms.Control.SendMessage(Int32, IntPtr, Int32) f:\dd\ndp\fx\src\WinForms\Managed\System\WinForms\Control.cs:11345
003de764 6d929d1e System.Windows.Forms.TreeView.WmNotify(System.Windows.Forms.Message ByRef) f:\dd\ndp\fx\src\WinForms\Managed\System\WinForms\TreeView.cs:2891
003de7e4 6d0ebdb7 System.Windows.Forms.TreeView.WndProc(System.Windows.Forms.Message ByRef) f:\dd\ndp\fx\src\WinForms\Managed\System\WinForms\TreeView.cs:3339
003deaf4 6acdd89a Microsoft.SqlServer.Management.UI.VSIntegration.ObjectExplorer.LazyTreeView.WndProc(System.Windows.Forms.Message ByRef)
003deb08 6d0b84a0 System.Windows.Forms.Control+ControlNativeWindow.OnMessage(System.Windows.Forms.Message ByRef) f:\dd\ndp\fx\src\WinForms\Managed\System\WinForms\Control.cs:14059
003deb10 6d0b8421 System.Windows.Forms.Control+ControlNativeWindow.WndProc(System.Windows.Forms.Message ByRef) f:\dd\ndp\fx\src\WinForms\Managed\System\WinForms\Control.cs:14114
003deb24 6d0b82fa System.Windows.Forms.NativeWindow.Callback(IntPtr, Int32, IntPtr, IntPtr) f:\dd\ndp\fx\src\WinForms\Managed\System\WinForms\NativeWindow.cs:647
003deccc 02daa074 [NDirectMethodFrameStandalone: 003deccc] System.Windows.Forms.UnsafeNativeMethods.SendMessage(System.Runtime.InteropServices.HandleRef, Int32, IntPtr, IntPtr)
003dece8 6d0c36d4 System.Windows.Forms.Control.SendMessage(Int32, IntPtr, IntPtr) f:\dd\ndp\fx\src\WinForms\Managed\System\WinForms\Control.cs:11340
003ded04 6d0c2fdb System.Windows.Forms.Control.ReflectMessageInternal(IntPtr, System.Windows.Forms.Message ByRef) f:\dd\ndp\fx\src\WinForms\Managed\System\WinForms\Control.cs:10742
003ded14 6d0cd417 System.Windows.Forms.Control.WmNotify(System.Windows.Forms.Message ByRef) f:\dd\ndp\fx\src\WinForms\Managed\System\WinForms\Control.cs:13094
003ded2c 6d0b8962 System.Windows.Forms.Control.WndProc(System.Windows.Forms.Message ByRef) f:\dd\ndp\fx\src\WinForms\Managed\System\WinForms\Control.cs:13811
003ded30 6d0c1baa [InlinedCallFrame: 003ded30] f:\dd\ndp\fx\src\WinForms\Managed\System\WinForms\ScrollableControl.cs:1491
003ded90 6d0c1b60 System.Windows.Forms.ContainerControl.WndProc(System.Windows.Forms.Message ByRef) f:\dd\ndp\fx\src\WinForms\Managed\System\WinForms\ContainerControl.cs:1898
003ded98 6d0a02d0 System.Windows.Forms.UserControl.WndProc(System.Windows.Forms.Message ByRef) f:\dd\ndp\fx\src\WinForms\Managed\System\WinForms\UserControl.cs:378
003deda0 6d0b84a0 System.Windows.Forms.Control+ControlNativeWindow.OnMessage(System.Windows.Forms.Message ByRef) f:\dd\ndp\fx\src\WinForms\Managed\System\WinForms\Control.cs:14059
003deda8 6d0b8421 System.Windows.Forms.Control+ControlNativeWindow.WndProc(System.Windows.Forms.Message ByRef) f:\dd\ndp\fx\src\WinForms\Managed\System\WinForms\Control.cs:14114
003dedbc 6d0b82fa System.Windows.Forms.NativeWindow.Callback(IntPtr, Int32, IntPtr, IntPtr) f:\dd\ndp\fx\src\WinForms\Managed\System\WinForms\NativeWindow.cs:647
003df180 02daa074 [InlinedCallFrame: 003df180] System.Windows.Forms.UnsafeNativeMethods.CallWindowProc(IntPtr, IntPtr, Int32, IntPtr, IntPtr)
003df17c 6d0b8ad3 System.Windows.Forms.NativeWindow.DefWndProc(System.Windows.Forms.Message ByRef) f:\dd\ndp\fx\src\WinForms\Managed\System\WinForms\NativeWindow.cs:810
003df1c0 6d0b8a2c System.Windows.Forms.Control.DefWndProc(System.Windows.Forms.Message ByRef) f:\dd\ndp\fx\src\WinForms\Managed\System\WinForms\Control.cs:5737
003df1c4 6d618ac3 System.Windows.Forms.TreeView.WmMouseDown(System.Windows.Forms.Message ByRef, System.Windows.Forms.MouseButtons, Int32) f:\dd\ndp\fx\src\WinForms\Managed\System\WinForms\TreeView.cs:2601
003df1e4 6d929610 System.Windows.Forms.TreeView.WndProc(System.Windows.Forms.Message ByRef) f:\dd\ndp\fx\src\WinForms\Managed\System\WinForms\TreeView.cs:3077
003df4f4 6acdd8a6 Microsoft.SqlServer.Management.UI.VSIntegration.ObjectExplorer.LazyTreeView.WndProc(System.Windows.Forms.Message ByRef)
003df508 6d0b84a0 System.Windows.Forms.Control+ControlNativeWindow.OnMessage(System.Windows.Forms.Message ByRef) f:\dd\ndp\fx\src\WinForms\Managed\System\WinForms\Control.cs:14059
003df510 6d0b8421 System.Windows.Forms.Control+ControlNativeWindow.WndProc(System.Windows.Forms.Message ByRef) f:\dd\ndp\fx\src\WinForms\Managed\System\WinForms\Control.cs:14114
003df524 6d0b82fa System.Windows.Forms.NativeWindow.Callback(IntPtr, Int32, IntPtr, IntPtr) f:\dd\ndp\fx\src\WinForms\Managed\System\WinForms\NativeWindow.cs:647
Et c'est justement celui tenu par le thread 5 :
0:000> kv
ChildEBP RetAddr Args to Child
[...]
003de270 7007c5b6 ffffffff 00000001 00000000 mscorwks!CLREvent::WaitEx+0xf7 (FPO: [Non-Fpo])
003de284 7017c002 ffffffff 00000001 00000000 mscorwks!CLREvent::Wait+0x17 (FPO: [Non-Fpo])
003de310 701c146c 0093e220 ffffffff 0093e220 mscorwks!AwareLock::EnterEpilog+0x8c (FPO: [Non-Fpo])
003de32c 701c13f0 a1dfa9ce 003de3ec 0387dae4 mscorwks!AwareLock::Enter+0x61 (FPO: [Non-Fpo])
003de3cc 6acfd475 0387db14 00000000 00000000 mscorwks!JIT_MonEnterWorker_Portable+0xb3 (FPO: [Non-Fpo])
003de3fc 6ad10c0c 0351d148 03898ce0 038980d8 ObjectExplorer_ni+0x37d475
003de410 6ace01c9 00000000 00000000 00000000 ObjectExplorer_ni+0x390c0c
003de43c 6acdf3b1 00000000 00000000 00000000 ObjectExplorer_ni+0x3601c9
003de4f8 6acdffcc 6acdc40b 0391179c 038980d8 ObjectExplorer_ni+0x35f3b1
003de518 6acdd77b 000000bf 0000000a 00000000 ObjectExplorer_ni+0x35ffcc
003de54c 6acdd847 00000000 003de588 0351d4e8 ObjectExplorer_ni+0x35d77b
003de560 6d0b84a0 003de57c 6d0b8421 00000000 ObjectExplorer_ni+0x35d847
[...]
La présence du thread d'arrière plan ne fait qu'allonger le temps d'attente, le thread de l'interface graphique aurait probablement connu le même blocage s'il n'avait pas été là.
Un rapide coup d'oeil aux processus WmiPrvSE montre que l'un d'entres eux est (une fois de plus) dans un sale état à cause de quelques problèmes comme celui-ci : The "Win32_Service" WMI class leaks memory in Windows Server 2008 R2 and in Windows 7

Les connaisseurs auront remarqué que le commit size du processus sélectionné a atteint le plafond autorisé par le job, dont on peut voir les paramètres via la boite de dialogue de propriétés de processus fournie par Process Explorer :

Ceux qui utilisent System Center Operations Manager (SCOM) pour surveiller leurs serveurs ont sans doute déjà reçu quelques avertissements et constaté quelques crashes de ces processus WmiPrvSE, vu que l'agent de SCOM fait un usage assez intensif de WMI.
Pour ma part je n'avais encore jamais été confronté à ce type de conséquence avec le blocage de l'application cliente.
Un arrêt (brutal, pas le choix dans mon cas) du processus fait disparaitre le problème constaté sur SSMS.
Informer de ce problème les DBAs, développeurs et équipes techniques en général pour qu'ils ne soient pas trop étonnés me semble une bonne idée.
Notamment pour qu'ils attendent un peu avant de prendre la décision de tuer leur processus SSMS, afin de ne pas perdre quelques scripts non sauvegardés ou quelques requêtes en cours d'exécution. Ca a tendance à les rendre un poil grincheux ;-).
Debug
- Debug Analyzer.NET (outil créé par Sukesh Ashok Kumar, Support Escalation Engineer Microsoft)
Un outil qui mérite sans doute qu'on s'intéresse à son évolution : il se propose tout simplement de permettre l'analyse de dumps mémoire d'applications .NET en utilisant des plugins écris en .NET.
Je n'ai pas encore testé l'outil en profondeur mais en tout cas il me parait au minimum être une bonne chose pour permettre aux personnes qui sont rebutées par WinDbg+SOS/PSSCOR d'effectuer une analyse en amont avant de faire intervenir une personne ayant acquise l'expérience permettant de creuser plus en profondeur. Ca permettra peut-être de réduire la charge de travail de cette personne sur les incidents ne nécessitant pas réellement une analyse en profondeur. - Fiddler and the IE9 Release Candidate (blog MSDN Fiddler)
Entres autres, une nouvelle du côté de la gestion du proxy sous Internet Explorer 9 : examiner les requêtes sur localhost/127.0.0.1/... devrait maintenant être un peu moins "compliqué".
Ce changement n'affecte que IE9, pas forcément les autres applications qui ignoraient aussi les proxys pour les requêtes sur les interfaces de loopback. - Prise de dumps avec WER (blog de Sébastien Bovo)
Une description des clés de registre disponibles (depuis Windows Server 2008 et Windows Vista SP1) pour paramétrer la génération d'un dump local par Windows Error Reporting. Ca peut toujours servir.
Divers
... il fait des trucs qui déchirent, comme afficher des poissons dans un bocal... mais n'a toujours pas de raccourci clavier pour "New session". Ou alors j'ai raté un truc.

Et moi qui croyait que le feedback avait été pris en considération.
J'ai créé une nouvelle entrée sur Connect mais :
- Je n'ai pas accès au type de feedback Suggestion, juste au type Bug...
- Le programme IE ne dispose pas du système de vote sur les entrées...
Donc la seule chose à votre disposition pour exprimer votre envie de voir apparaitre ce raccourci clavier est le lien "I can too" (attention, "vote" non réversible) :

A votre bon coeur : Keyboard shortcut for the "New session" feature
Debug / Performance
- CLR Profiler for .NET Framework 4 (centre de téléchargement Microsoft)
La version 4, livrée avec les sources, de l'outil CLR Profiler, comme ça avait été annoncé de manière indirecte il y a quelques mois.
Il permet d'analyser des applications .NET 2.0, 3.x et 4.0 ainsi que des applications Silverlight 4.
Cet outil peut faire pâle figure à côté d'autres outils plus avancés comme notamment ceux intégrés à Visual Studio (dont la partie capture de données peut être utilisée sans déployer VS) mais il peut tout de même rendre service.
Un post de David Broman donne plus de détails sur cette nouvelle version. - CLR Managed Debugger (mdbg) Sample 4.0 (centre de téléchargement Microsoft)
Une mise à jour, livrée avec les sources, du debugger .NET en ligne de commande mdbg faisant habituellement partie du SDK Windows.
Les nouveautés succintement annoncées par la page de téléchargement sont le support du debug Silverlight et le support de debug de dump, mais ce dernier était déjà disponible sur la version livrée avec le SDK Windows pour .NET 4.0 RTM.
N'ayant jamais tenté de debug Silverlight, j'avoue ne pas forcément voir les différences (en dehors de la présence des sources) avec la version présente dans le SDK Windows : il est surtout pratique d'avoir les binaires disponibles de manière séparée du SDK.
Sécurité
Divers
- Dimensions (en français, anglais, ...)
J'ai trouvé l'idée et la réalisation intéressantes.
Pour reprendre les termes des auteurs de ce sympathique film mathématique, car je doute de faire mieux :
"Une promenade mathématique...
Un film pour tout public.
Neuf chapitres, deux heures de maths, pour découvrir progressivement la quatrième dimension. Vertiges mathématiques garantis!" - Microsoft Mathematics 4.0 (centre de téléchargement Microsoft)
Parce que des fois la calculatrice fournie avec Windows est trop légère.
Voir aussi le site de l'outil.
Ca ne me sert pas tous les jours, mais c'est toujours bon à avoir sous la main.
Une autre ressource pratique dans le genre est Wolfram|Alpha, pour peu qu'on sache lui expliquer ce qu'on veut.
Le webcast de la première (ou plutôt, d'après eux, de la version beta) FailCon version française est disponible : La Fail - conférence: 1ere conférence dédiée à l’échec entrepreneurial
C'est intéressant, même si on n'est pas directement entrepreneur soi-même (comme c'est mon cas) et que l'on n'a pas forcément dans l'idée d'entreprendre à court terme : après tout ce qui s'applique à l'entrepreneuriat pourrrait très bien s'appliquer ailleurs.
A priori les mentalités vis-à-vis de l'échec évoluent et on s'éloigne de l'idée qu'une personne échouant une fois échouera toujours.
Cette idée est d'ailleurs, de mon point de vue, particulièrement étonnante quand elle est exprimée par quelqu'un travaillant plus ou moins dans la conception et le développement de systèmes informatiques : après tout il arrive que des codes que nous produisons échouent. Dans ce cas nous corrigeons, et de manière générale nous concevons le système de manière à ce qu'il puisse assurer une certaine qualité de service lors des échecs de certains de ses composants : un échec d'un code ne veut pas dire que le développeur ne fera rien de bon de sa carrière, tant qu'il apprend de ses erreurs (et que l'équipe complète en tire parti).
En quoi les personnes qui produisent des entreprises (si je peux me permettre l'expression) seraient-elles différentes de celles qui produisent du code ?
Voici quelques répères temporel dans la vidéo du webcast :
- Vers 00:07:10, Gilles Babinet : le point de vue d'un entrepreneur ("L'importance de l'échec dans la vie de l'entrepreneur")
- Vers 00:31:30, Michel Dahan : le point de vue d'un investisseur ("Le point de vue du VC")
- Vers 00:55:00, Daniel Kahn : le point de vue d'un avocat d'affaires ("Bien réussir son échec pour rebondir et autres histoires vécues")
- Vers 01:14:00, un bon 45 minutes de table ronde ("L'acceptation de l’échec entrepreneurial dans la société française")
.NET
- I think MaxLength needs protection to assure safer text (blog MSDN de Michael Kaplan)
C'est sûr qu'un texte tronqué en plein milieu d'une séquence, c'est moche.
Bon courage pour obtenir un correctif pour ça, mais il est vrai qu'au minimum un peu plus de précision sur la règle de troncation serait la bienvenue dans la documentation, tant côté WinForm que WPF (en tout cas j'aime bien sa proposition de reformulation :p).
Debug / Performance
- How Many Secrets do .NET PDB Files Really Contain? (blog de John Robbins)
Un post qui n'est plus tout jeune vu qu'il date d'août 2009 mais toujours intéressant à (re)lire, vu qu'il peut aider à justifier un déploiement de PDBs pour des binaires .NET.
"When you have a co-worker who writes 300 line methods, having the exact line massively helps in the call stack." : qui n'a jamais été confronté à ce problème ? (pour un code écrit par un autre ou par soi-même, bien entendu) - Mysteries of Windows Memory Management Revealed Part 1 / Part 2 (webcasts des sessions de Mark Russinovich, PDC 2010)
Toujours intéressant d'avoir quelques informations et rappels au sujet de la gestion de la mémoire sous Windows, surtout si on n'y est pas directement confronté tous les jours : un minimum de connaissances ne peut pas faire de mal lors de l'analyse d'incident.
Au passage Tim Sneath avait publier des comptes-rendus de ces sessions : Part 1 / Part 2
Sécurité / Protection des données personnelles
- Platform Updates: New User Object fields, Edge.remove Event and More (blog Facebook Developers)
Ma première réaction en voyant la partie "User Address and Mobile Phone Number" a été de me dire que ça allait être intéressant de voir quel type d'application nuisible va voir le jour...
Je doute que la majorité des utilisateurs lisent complètement la liste de permissions demandées avant de les accorder à une application... Il n'y a qu'à voir le nombre de personnes qui tombent dans le panneau des "WOW! Clique ici [...]", "MDR! Clique ici [...]", etc sans se rendre compte qu'ils permettent une publication automatique d'un lien dans leur profil / sur leur mur...
A priori ils ont eu suffisamment de remontées pour revoir leur copie, reste à voir ce qu'ils vont mettre en place.
En tout cas le plus simple reste comme toujours de ne pas donner ces informations.
Divers
Contrairement à une idée qui semble répandue, pour les opérations sur des valeurs entières non constantes l'option "Check for arithmetic overflow/underflow" est désactivée par défaut : les dépassements de capacité ne seront pas signalés à l'exécution du code, et ne le seront à la compilation que pour les valeurs constantes. Les opérations et conversions s'exécutent donc par défaut dans un contexte unchecked.
Du moins c'est le cas dans l'environnement de développement Visual Studio et en entrée de csc.exe, ça ne l'est peut-être pas pour d'autres environnements de build ou d'autres compilateurs.
Le contexte de rédaction de cet article est Visual Studio 2010 et .NET 4.0, il se peut que la situation change pour des versions ultérieures de ces produits (même si je n'y crois pas trop).
2 cas à distinguer : les opérations/conversions sur valeurs constantes, et les autres
Pour les opérations sur valeurs constantes, c'est-à-dire celles pour lesquelles les valeurs sont connues au moment de la compilation, le compilateur émettra par défaut l'erreur "The operation overflows at compile time in checked mode" s'il détecte une opération qui donnerait lieu à un dépassement de capacité et ce quel que soit le contexte de vérification.
Ainsi le code suivant ne compilera pas :
Int32 sum = Int32.MaxValue + 1;
Il nécessitera l'utilisation du mot clé unchecked pour déclarer explicitement que le dépassement de capacité est voulu pour un bloc de code :
Int32 sum;
unchecked
{
sum = Int32.MaxValue + 1;
}
ou pour une expression :
Int32 sum = unchecked(Int32.MaxValue + 1);
A noter que le code suivant, malgré le fait qu'on utilise la même constante, compilera sans problème avec la version actuelle du compilateur C# (et produira un résultat erroné sans levée d'exception s'il est compilé dans un contexte unchecked) :
Int32 max = Int32.MaxValue;
Int32 sum = max + 1;
Pour les conversions de valeurs constantes, le compilateur émettra aussi une erreur.
Par exemple les codes suivant provoqueront la levée d'une erreur "Constant value '9223372036854775807' cannot be converted to a 'int' (use 'unchecked' syntax to override)" et "Constant value '-1' cannot be converted to a 'uint' (use 'unchecked' syntax to override)" :
Int32 value = (Int32)Int64.MaxValue;
UInt32 value = (UInt32)(-1);
Là encore, remplacer l'utilisation directe de la constante de l'opération par une variable contenant sa valeur permettra de "tromper" le compilateur (et produira aussi un résultat erroné sans levée d'exception s'il est compilé dans un contexte unchecked) :
Int64 largeValue = Int64.MaxValue;
Int32 value = (Int32)largeValue;
Int32 negativeValue = -1;
UInt32 value = (UInt32)negativeValue;
Pour les opérations/conversions sur valeurs non constantes la vérification des dépassements de capacité est désactivée par défaut : pas d'erreur à la compilation, pas d'exception System.OverflowException à l'exécution.
L'obtention de valeurs erronées est un problème, mais ce n'est pas le seul
Même si l'obtention de valeurs erronées peut se révéler être un gros problème, il y a un autre aspect à ne pas négliger avec ce problème de dépassement de capacité : la boucle infinie.
Qui n'a pas un jour été confronté à une boucle infinie en voulant parcourir la plage de valeurs complète d'un type entier via une boucle ?
Que ce soit un en utilisant "for"
// NE PAS UTILISER CECI (boucle infinie si utilisé dans un contexte unchecked)
// DO NOT USE THAT (infinite loop if used in an unchecked context)
for (Byte currentByte = Byte.MinValue; currentByte <= Byte.MaxValue; currentByte++)
{
// ...
}
ou "while" / "do...while"
// NE PAS UTILISER CECI (boucle infinie si utilisé dans un contexte unchecked)
// DO NOT USE THAT (infinite loop if used in an unchecked context)
Byte currentByte = Byte.MinValue;
do
{
// ...
currentByte++;
} while (currentByte <= Byte.MaxValue);
Ca peut-être amusant quand on s'en aperçoit durant les tests (on met ça sur le coup de la fatigue), mais ça l'est beaucoup moins quand on ne s'en aperçoit pas et qu'au lieu du type de code donné en exemple ci-dessus on se trouve en présence de l'utilisation d'une valeur externe pour définir les bornes de la boucle.
Comme par exemple avec une méthode de ce genre (qui reste un exemple bateau avec une implémentation naïve mais cependant probable) :
public class Result
{
public Boolean OperationSucceeded { get; internal set; }
public Object OperationResult { get; internal set; }
public Int32 TriesCount { get; internal set; }
}
public Result TryNTimes(Int32 maxTries)
{
if (maxTries <= 0)
{
throw new ArgumentException("...");
}
Result result = new Result();
Boolean operationSucceeded = false;
Int32 currentTryNumber = 1;
do
{
// ...
if (operationSucceeded == true)
{
// ...
}
else
{
currentTryNumber++;
}
} while (operationSucceeded == false && currentTryNumber <= maxTries);
result.OperationSucceeded = operationSucceeded;
result.TriesCount = currentTryNumber;
return result;
}
Si la valeur Int32.MaxValue est passée à la méthode TryNTimes, le bloc "do...while" de cette dernière part dans une boucle infinie.
Si les tests ne couvrent pas les bornes extrêmes ça peut s'avérer plutôt dangereux, surtout si les données proviennent de l'extérieur et que les opérations effectuées ne peuvent être couvertes par un timeout : on ouvre la voie à une attaque par déni de service.
Et quand je dis "proviennent de l'extérieur" je parle à la fois des spécifications directes (valeur entière passée en paramètre, dans un fichier etc) ou indirectes (nombre d'octets d'un fichier, etc). Si nous n'utilisons pas directement les valeurs mais effectuons des calculs avec, il se peut aussi que l'attaquant s'arrange pour obtenir la valeur fatidique en sortie de calcul : toujours partir du principe qu'il aura plus d'imagination que nous.
Les options pour activer la vérification
Avant toute chose, une information qu'il faut absolument garder à l'esprit : l'activation de la vérification des dépassements de capacité est une option qui agit au moment de la compilation (C# vers IL) et non pas à l'exécution.
Il n'y a, à ma connaissance, pas d'équivalent de cette option qui soit activable au moment de la compilation JIT (dans le cas contraire la prise en charge devrait descendre jusqu'à ngen pour que les images générées prennent en charge les 2 cas) : référencer un assembly depuis un autre pour lequel la vérification des dépassements de capacité aura été activée de manière globale ne provoquera pas l'activation de la vérification dans le code de l'assembly référencé.
Il existe en gros 2 manières de changer le contexte de vérification : un qui aura une portée projet, l'autre qui agira de manière plus fine au niveau d'une ou plusieurs expressions.
Portée projet : les options de projet Visual Studio / du compilateur csc
Pour activer la vérification de dépassement de capacité pour tout un projet, il suffit de cocher l'option "Check for arithmetic overflow/underflow" dans les propriétés du projet :

Dans la majorité des cas on voudra sans doute avoir le même paramétrage sur toutes les configurations de build de la solution, et dans ce cas il suffit de veiller à sélectionner "All Configurations" avant de procéder au changement de valeur.
Pour les cas où on désire n'activer la vérification des dépassements que sur les builds de debug et de test, il faudra alors passer par une affectation plus fine des valeurs.
Cette option changera la façon dont Visual Studio/MSBuild gèrera la valeur de l'option /checked lors de l'appel à csc.exe.
Portée plus fine : les mots clés checked / unchecked
Le mot clé checked permet de placer une expression, ou l'ensemble des expressions d'un bloc de code, dans un contexte checked : en cas de dépassement de capacité une exception OverflowException sera levée.
Le mot clé unchecked permet à l'inverse de placer une expression, ou l'ensemble des expressions d'un bloc de code, dans un contexte unchecked : en cas de dépassement de capacité aucune exception ne sera levée.
Il est important de bien noter au moins 2 points sur ces mots clés :
- Ils surchargent les options définies pour le projet : il est ainsi tout à fait possible d'effectuer des opérations sans vérification de dépassement de capacité dans un projet pour lequel elles sont activées (et vice versa).
En cas d'imbrication de blocs checked/unchecked autour d'une expression, c'est le contexte le plus profond (celui le plus proche de l'expression) qui sera appliqué. - Leur effet ne s'applique qu'aux expressions de leur bloc et ne s'étend pas à celles des méthodes appelées au sein de ce bloc, même si ces dernières font partie du même assembly : pour ces méthodes c'est le contexte qu'elles définissent qui s'appliquera, ou à défaut celui défini pour le projet.
Impact sur les performances
A ma connaissance le contexte de vérification ne donne pas lieu à autre chose qu'un choix différent au niveau de l'instruction IL qui va effectuer l'opération demandée :
- Addition
- Conversion vers Int32
- conv.i4
- conv.ovf.i4
- conv.ovf.i4.un
- ...
Une recherche du terme "ovf" sur la liste des OpCodes disponibles lors de l'émission de code donne un aperçu des possibilités.
Par exemple dans le cadre d'une addition de 2 entiers signés :
public Int32 Sum(Int32 value1, Int32 value2)
{
return value1 + value2;
}
Cette méthode compilée dans un contexte unchecked donnera le code IL suivant :
.method public hidebysig instance int32
Sum(int32 value1,
int32 value2) cil managed
{
// Code size 4 (0x4)
.maxstack 8
IL_0000: ldarg.1
IL_0001: ldarg.2
IL_0002: add
IL_0003: ret
}
Alors que compilée dans un contexte checked elle donnera plutôt ceci :
.method public hidebysig instance int32
Sum(int32 value1,
int32 value2) cil managed
{
// Code size 4 (0x4)
.maxstack 8
IL_0000: ldarg.1
IL_0001: ldarg.2
IL_0002: add.ovf
IL_0003: ret
}
Pour une addition de 2 entiers non signés :
public UInt32 Sum(UInt32 value1, UInt32 value2)
{
return value1 + value2;
}
Cette méthode compilée dans un contexte unchecked donnera le code IL suivant :
.method public hidebysig instance uint32
Sum(uint32 value1,
uint32 value2) cil managed
{
// Code size 4 (0x4)
.maxstack 8
IL_0000: ldarg.1
IL_0001: ldarg.2
IL_0002: add
IL_0003: ret
}
Alors que compilée dans un contexte checked elle donnera plutôt ceci :
.method public hidebysig instance uint32
Sum(uint32 value1,
uint32 value2) cil managed
{
// Code size 4 (0x4)
.maxstack 8
IL_0000: ldarg.1
IL_0001: ldarg.2
IL_0002: add.ovf.un
IL_0003: ret
}
Rien n'est gratuit : l'activation de la vérification des overflows a fatalement un coût.
Il faut mesurer les différences dans le contexte de chaque projet, avec la configuration de build finale (optimisations actives ou non, ...) et de préférence sur la plateforme matérielle finale afin de déterminer si l'activation plus ou moins fine des vérifications apporte suffisamment de bénéfices pour justifier le surcoût.
Si le coût est inacceptable en production, un compromis pourra aussi être d'activer les vérifications sur les binaires de test mais pas sur ceux de production.
Les 10 derniers blogs postés
-
MBA : Pourquoi faire et comment le choisir ? par
Blog Technique de Romelard Fabrice le il y a 16 heures et 23 minutes
-
Y'a des erreurs qui peuvent rendre le développeur violent par
Aleks's Blog le 02-02-2012, 16:33
-
[Hyper-V 3] Présentation des commandlets PowerShell par
Blog de SPBrouillet (Pierrick BROUILLET) le 01-31-2012, 16:01
-
IIS7 – Compression GZIP par
Atteint de JavaScriptite Aiguë [Cyril Durand] le 01-31-2012, 15:52
-
SharePoint 15 Technical Preview Managed Object Model Software Development Kit par
Matthew le 01-31-2012, 12:34
-
Office 15 Technical Preview - Open specification Update par
Matthew le 01-31-2012, 10:14
-
TFS Integration Tools – Installation par
Vivien Fabing le 01-31-2012, 00:06
-
Test par
RonnyK le 01-30-2012, 16:56
-
[SharePoint 2010] Désactiver le correcteur orthographique dans les pages d’un site de publication par
Jean-Christophe Brabant le 01-30-2012, 09:30
-
[SharePoint 2010] Site internet et performances : poids et nombre des ressources par
Arnault Nouvel le 01-30-2012, 00:52