Bienvenue à Blogs CodeS-SourceS Identification | Inscription | Aide

CoqBlog

.NET is good :-)
{ Blog de coq }

Actualités

Quelques trucs intéressants (13/02/2011)

 

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.
Quelques trucs intéressants (30/01/2011)

 

.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

Dépassements de capacité en C# : la valeur par défaut de "Check for arithmetic overflow/underflow" pourrait vous surprendre

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 :

Capture d'écran de la fenêtre d'options de build d'un projet C# dans Visual Studio

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
    • add
    • add.ovf
    • add.ovf.un
  • 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.

Quelques trucs intéressants (20/12/2010)

 

.NET

  • Petit point sur la relance d’une exception (blog de Vincent Labatut)
    Au cas où vous ne l'auriez pas vu passer. Ca ne fait jamais de mal un petit rappel sur les implications du choix de la méthode utilisée pour relancer une exception qu'on ne peut pas gérer.
    Utile notamment pour les cas où la décision de gérer ou non l'exception ne dépend pas que de son type mais d'autres tests sur son contexte : le langage C# ne fournissant actuellement pas de support pour les filtres d'exceptions autres que les filtres de type, il faut bien relancer l'exception d'une manière ou d'une autre.
  • How to: Determine which Microsoft .Net Framework version and service pack installed? (blog MSDN "Le Café Central de DeVa")
    Dans la série des trucs difficile à retrouver le jour où on en a besoin : la liste des clés de registre concernant les versions de .NET installées, de .NET 1.0 (ouch !) à .NET 4.0.
    Je profite de ce post riche en clés de registre pour rappeler l'existence du sympathique mais trop peu connu outil RegJump.

 

Sécurité

  • H@ckRAM, j'ai la mémoire qui flanche (blog d'Arnaud Malard)
    Ou ce qu'il est possible de faire si on obtient un accès en lecture et/ou écriture au contenu de la RAM d'une machine.
    Un papier instructif (même si comme moi on ne dispose pas au premier abord des compétences liées) que j'ai découvert au détour d'un article sur SecurityVibes.
    Un support de présentation est aussi disponible ici
  • Breaking the Web's Cookie Jar (blog de Jeff Atwood)
    Je n'ai pas pu tester faute de matériel adapté, mais il est toujours bon de rappeler qu'il vaut mieux réfléchir avant de se connecter à un réseau public (qu'il soit wifi ou filaire d'ailleurs).
    A lire aussi : Online services security report card

 

Sécurité / Stabilité / Performance

  • Why does StreamReader.ReadLine throw OutOfMemoryException? (rapport de bug sur Microsoft Connect)
    A priori ce bug ne sera pas corrigé avant la prochaine version de .NET, donc vérifiez bien que vos codes n'appellent jamais la méthode ReadLine une fois qu'un précédent appel à retourné null (normalement ce n'est jamais le cas, mais un bug est si vite arrivé). Ou mieux, validez les flux en entrée pour filtrer ceux qui ne contiennent que le premier octet du préambule UTF-8 (0xEF).

 

Fun

  • A Bluescreen By Any Other Color (blog de Mark Russinovich)
    Le mode d'emploi pour patcher le code en mémoire afin de changer les couleurs du fameux BSOD, comme Mark l'a fait en fin de sa session The Case of the Unexplained du TechEd 2010 sans que ce soit visible sur la vidéo.
    Ca ne sert à rien, mais bon... On ne risque pas grand chose à faire ça en machine virtuelle (en pensant, sous Windows Virtual PC, à couper les Integration Features avant de déclencher le crash).
Jeu de données téléchargeable : n-grammes du "Books Ngram Viewer" de Google Labs

Si vous aimez jouer avec les mots, vous voudrez peut-être jeter un oeil aux datasets du Books Ngram Viewer de Google Labs, en n'oubliant pas bien sûr de consulter la licence précisée sur la page de téléchargement des jeux de données.

Les jeux de données ne sont à l'heure actuelle pas tous complet (voir status indiqué sur chacun d'entres eux), mais celui concernant le français l'est.
Bien noter que le jeu de données français complet (1510 fichiers zip) représente tout de même un joli 48Go.

Concernant l'encoding des fichiers "TSV" (valeurs séparées par des tabulations), il s'agit à priori d'UTF-8.

Découvert via Official Google Research Blog: Find out what’s in a word, or five, with the Google Books Ngram Viewer

Paint.NET + Remote Desktop : correction du bug de décalage de l'image sur copier/coller

"Fixed: Copy-paste from a Remote Desktop session will no longer be ‘shifted’ by 3 pixels."
Source : Paint.NET v3.5.6 Beta (Build 3970) now available

Ha ! Ca fait un sacré bout de temps que je l'attend cette correction.
Plus besoin de bidouiller pour le transfert. :-)

Quelques trucs intéressants (07/11/2010)

 

Debug / Performance

 

Sécurité / Stabilité / Performance

  • SDL Regex Fuzzer (centre de téléchargement Microsoft)
    Un outil qui peut aider lors de l'analyse de regex pour limiter les risques d'attaque ReDos (Regular Expression Denial of Service).
    La gamme d'outils mis à disposition dans le cadre du Security Development Lifecycle sont disponibles sur cette page.

 

Visual Studio

  • Hotfixes available for ‘scrolling context menu’ problem (blog Visual Studio)
    Les patches destinés à corriger le très énervant problème du menu contextuel de Visual Studio 2010 qui passe en mode "scroll" alors qu'il y aurait largement la place d'afficher tous les éléments à l'écran.
  • Help Viewer 1.1 Preview Video (blog de Paul O'Rear)
    Un aperçu de ce qui devrait être apporté par le SP1 de Visual Studio 2010 au niveau du visualiseur de l'aide locale.
    Ca pourrait intéresser ceux qui ne peuvent déployer des outils comme H3Viewer sur tous leurs environements de travail.

 

Windows

  • Windows Command Reference (centre de téléchargement Microsoft)
    Un fichier CHM contenant notamment une liste alphabétique des outils en ligne de commande [fournis par Microsoft] disponibles sous Windows.
    C'est toujours pratique d'avoir ce type de référence sous la main, au moins pour retrouver l'outil qu'on cherche mais dont on ne se rappelle pas forcément le nom.
    Les explications concernant les outils ne sont pas forcément complètes (par exemple sur l'entrée concernant robocopy il n'est pas fait mention du swith /MT), mais une fois l'outil trouvé il suffit de se référer à la documentation fournie avec celui-ci.
    Dommage aussi que certaines entrées soient carrément vides.
Donnez votre avis sur la connectivité à SQL Server

Si vous voulez donner un peu votre avis sur la connectivité à SQL Server et avoir une chance d'influencer la priorité de certains développements dans ce domaine, vous avez jusqu'au 03/11/2010 01H00 du matin (heure de Paris) pour répondre au sondage SQL Connectivity Customer Survey (Fall 2010).

De mon côté j'ai apprécié de voir l'entrée "Tabular Data Stream Compression (improves network performance)" sur la 10ème question, et j'ai profité de la 11ème question pour suggérer plus d'options orientées "push" sur la classe SqlBulkCopy.

Plus de détails sur le blog SQL Protocols : Last Chance: Help Shape The Future Of SQL Server!

Outlook et marquage automatique des éléments comme lus : ça se désactive

Aller, une fois n'est pas coutume : un petit post orienté bureautique :-)

C'est une fonctionnalité qui m'énerve en général énormément dans les logiciels qui l'offrent : le marquage automatique d'un élément comme lu lors du changement de sélection ou après un laps de temps donné.
J'ai horreur de ça : ce n'est pas parce que j'ai survolé un élément que je le considère comme lu. Je préfère décider moi même de l'état de l'élément et le marquer comme lu explicitement (Ctrl+Q en général).
C'est valable aussi bien pour les logiciels de collaboration que pour les aggrégateurs.

Du coup il s'agit d'une des premières choses que je désactive dans Outlook, et je me suis aperçu dernièrement que cette option que je croyais largement connue ne l'était pas forcément, tout comme celle qui permet de désactiver l'envoi du mail sur le raccourci clavier Ctrl+Entrée.

Donc, pour ceux qui apprécient cela autant que moi, vous trouverez votre bonheur sous la forme du décochage de 2 options (mutuellement exclusives) disponibles dans la boite de dialogue "Reading Pane" :

  • Mark items as read when viewed in the Reading Pane
  • Mark item as read when selection changes

Je n'ai pas de version française sous la main mais si j'en crois les références de localisation ces options doivent s'appeler :

  • Marquer les éléments comme lus quand ils ont été affichés dans le Volet de lecture
  • Marquer l'élément comme lu lors du changement de sélection

 

Sous Outlook 2010, vous pouvez accéder à cette boite de dialogue de 2 façons :

File => Options => Mail => Outlook panes
Capture d'écran de la boite de dialogue "Reading Pane" accessible via le chemin File => Options => Mail => Outlook panes

Par le ruban : View => Reading Pane => Options... (qui ne sera actif que si un dossier avec volet de lecture est actif)
Capture d'écran du chemin d'accès à la boite de dialogue "Reading Pane" via le ruban

Bonne désactivation ;-)

Quelques trucs intéressants (16/10/2010)

Cette fois-ci :

  • Debug / Performance
  • Sécurité / Stabilité / Performance
  • Sécurité
  • SQL Server

 

Debug / Performance

 

Sécurité / Stabilité / Performance

  • Why does StreamReader.ReadLine throw OutOfMemoryException? (rapport de bug sur Microsoft Connect)
    Un rapport de bug sur la classe StreamReader que je suivais depuis sa publication : un bug au niveau de la méthode ReadLine peut entrainer une grosse dégradation des performances du système l'utilisant, si on lui soumet un flux spécialement bâti.
    L'exemple donné dans le rapport est suffisamment démonstratif, son exécution montre bien où se situe le problème côté stabilité et performance.
    239 = 0xEF, le premier octet du préambule UTF-8 qui est censé en compter 3.
    Le problème se pose bien sûr avec un autre type de flux que MemoryStream, le problème étant côté StreamReader.
    Le bug a dernièrement été marqué comme fixé sans aucune explication (notamment sur la disponibilité du patch) donc vous aurez probablement envie de mettre en place vous même une validation plus poussée des flux entrants afin d'éviter un éventuel déni de service intentionnel ou accidentel.

 

Sécurité

  • Authentication and Passwords: The Good, the Bad, and the Really Ugly! (webcast d'une session TechEd 2010 de Marcus Murray, en anglais, 1H15 environ)
    Instructif. Ne pas se méprendre en lisant le titre de la session : ça ne parle pas que de mots de passe. 
    Au passage je n'avais encore jamais vu personne utiliser OneNote comme support de présentation.
  • AntiXSS 4.0 Released (blog Security Tools)
    Une nouvelle version de cette librairie dont je vous avais déjà parlé est disponible.
    Contrairement à ce que son numéro de version pourrait laisser penser, elle ne nécessite pas .NET 4.0 pour fonctionner mais au minimum .NET 3.5.
    Pour rappel elle contient entre autres des méthodes HtmlEncode et UrlEncode similaires à celles de la classe HttpUtility, à l'exception du fait qu'elles fonctionnent sur un principe de "white list" et non pas de "black list" : on dresse la liste des caractères valides et tous les autres sont encodés.
    Elle fournit aussi d'autres méthodes :
    • CssEncode
    • HtmlAttributeEncode
    • HtmlEncode
    • HtmlFormUrlEncode
    • JavaScriptEncode
    • LdapDistinguishedNameEncode
    • LdapFilterEncode
    • UrlEncode
    • VisualBasicScriptEncode
    • XmlAttributeEncode
    • XmlEncode
    • GetSafeHtml
    • GetSafeHtmlFragment

 

SQL Server

XmlReader / XmlWriter initialisés sur Stream, TextWriter, TextReader, ... : attention à la fermeture

S'il y a bien une chose qu'il faut faire quand on utilise une classe dont le suffixe est "Reader" ou "Writer" en l'initialisant avec une instance d'un type dérivé de Stream, TextWriter ou TextReader (etc...), c'est vérifier son comportement vis-à-vis de cette instance.
Et en particulier celui lors de la fermeture de ce Reader/Writer, effectuée en général par un appel explicite à une méthode Close ou Dispose (ou un appel implicite à Dispose au moyen d'un bloc using).

Il ne faut surtout pas tenter de deviner : si la documentation n'est pas extrèmement claire, Reflector et/ou Reference Source sont nos amis.
Car si on devine mal on peut se retrouver, dans le cas de XmlReader / XmlWriter, avec un bug qui ne sera pas forcément détecté durant la phase de test mais plutôt durant la phase de production et surtout durant les phases d'évolution / maintenance (encore plus loin dans le cycle de vie).

Avis aux lecteurs qui passeraient ici quelques temps après publication :
Ce post se place dans le contexte de .NET 4.0 RTM.
Bien qu'un changement de ce comportement sur les classes existant dans le Framework .NET à ce jour soit peu probable (ce serait un changement brisant avec pas mal de conséquences), nous n'en sommes pas à l'abri : donc vérifiez, comme il le faut toujours, que les informations exposées ci-dessous sont toujours valides (et n'hésitez pas, si vous le désirez, à laisser un commentaire si ce n'est plus le cas ou si j'ai fait une erreur dès maintenant).

 

 

Le problème

Pour illustrer un peu le problème, prenons l'exemple de 2 méthodes qui ont l'air très similaires mais dont l'une pose un léger problème : elle laisse une instance de FileStream ouverte derrière elle, et donc un handle de fichier.

private static void DoSomethingWithStreamReader(String filePath)
{
    using (StreamReader reader = new StreamReader(
        File.OpenRead(filePath)))
    {
        // ...
    }
}

// ATTENTION : Cette méthode ne ferme pas le flux ouvert sur le fichier.
// WARNING : this method does not close the opened file stream.
private static void DoSomethingWithXmlReader(String filePath)
{
    using (XmlReader reader = XmlReader.Create(
        File.OpenRead(filePath)))
    {
        // ...
    }
}

Cette méthode pose d'ailleurs le même souci :

// ATTENTION : Cette méthode ne ferme pas le flux ouvert sur le fichier.
// WARNING : this method does not close the opened file stream.
private static void DoSomethingWithXmlReaderByStreamReader(String filePath)
{
    using (XmlReader reader = XmlReader.Create(
        new StreamReader(filePath)))
    {
        // ...
    }
}

 

Ce simple code, avec placement d'un point d'arrêt sur l'appel de Console.WriteLine, devrait permettre de constater le fait que le fichier n'est pas fermé :

static void Main(string[] args)
{
    String doSomethingWithStreamReaderFilePath = @"D:\Temp\DoSomethingWithStreamReader.xml";
    String doSomethingWithXmlReaderFilePath = @"D:\Temp\DoSomethingWithXmlReader.xml";

    DoSomethingWithStreamReader(doSomethingWithStreamReaderFilePath);
    DoSomethingWithXmlReader(doSomethingWithXmlReaderFilePath);

    Console.WriteLine("Press a key to quit...");
    Console.ReadKey(true);
}


En utilisant Process Explorer par exemple :

 Capture de la fenêtre de Process Explorer montrant le handle de fichier non fermé

 

 

Les conséquences

Pas de fuite extrêmement grave ici : la finalisation de l'instance de la classe dérivée de Stream entrainera la fermeture des handles, et ce bien avant la fermeture du processus.
Mais ça reste tout de même un problème, surtout que sur ce genre de fuite il se peut très bien qu'aucun problème ne soit constaté avant longtemps : il suffit que l'application ne réutilise pas le fichier, ou alors qu'il ne lui faille qu'un accès partagé en lecture.

Il se peut même que le problème ne soit jamais constaté dans le cas d'une application réutilisant le fichier en accès exclusif : si la finalisation intervient toujours avant cette réutilisation, le problème ne sera probablement jamais levé.

Jusqu'au jour où un changement survient dans l'application ou son environnement :

  • la charge de la machine hébergeant l'application diminue, entrainant un stress moins fort sur le Garbage Collector
  • l'application est déplacée vers une machine avec une configuration bien plus confortable
  • le code est migré vers une nouvelle version de .NET, pour laquelle le Garbage Collector ne fonctionne plus exactement de la même manière
  • le code est modifié : un traitement long et/ou gourmand qui se trouvait entre les 2 accès au fichier est supprimé / optimisé
  • le code est modifié : quelqu'un introduit un second traitement sur le fichier là où il n'y en avait auparavant qu'un seul
  • des traitements parallèles s'exécutant dans le même processus sont arrêtés
  • ...

 

 

Pourquoi le flux reste ouvert

Contrairement à d'autres classes comme StreamReader/StreamWriter les implémentations de XmlReader et XmlWriter ne partent pas du principe que, lorsque nous appelons leur méthode Close ou Dispose, nous n'avons plus besoin du flux que nous leur avions affecté.
Ces classes ne considèrent pas qu'elle deviennent propriétaires des flux.

C'est à nous d'effectuer la fermeture du flux au bon moment, ou de demander au code de XmlReader/XmlWriter de le faire pour nous en le spécifiant à la création :

Quelques trucs intéressants (19/09/2010)

Cette fois-ci :

  • .NET
  • Sécurité
  • Visual Studio

 

.NET

  • Using AppDomains to make Non-Threadsafe code Threadsafe (blog de Jeffrey Richter)
    Cette stratégie se base sur le fait que la valeur d'un champ static est spécifique à un AppDomain particulier (en laissant de côté le cas où plusieurs AppDomain partageraient une instance d'un type héritant de MarshalByRefObject).
    J'avais envisagé ce type de solution quand je m'étais retrouvé dans une situation similaire lors de l'utilisation de SMO (SQL Server Management Objects) dans un contexte multithreadé alors qu'une certaine partie de SMO souffre d'un bug dans le code de gestion d'un cache interne, implémenté avec un champ static. 
    Cependant j'avais aussi la possibilité de centraliser les accès à SMO (utilisation d'un petit nombre de fonctionnalités) et j'ai finalement opté pour une solution un peu moins complexe basée sur des verrous (par type d'objet SQL Server, et uniquement pour les requêtes démarrant pendant le premier accès à un type) et une stratégie de nouvelle tentative au premier échec pour les rares incidents restant.
    En gros un compromis stabilité / concurrence misant sur le fait que le bug ne causait pas de corruption d'état en cas d'étouffement de l'erreur (c'est une stratégie risquée car vulnérable aux changements dans le code tiers).

 

Sécurité

  • Understanding the ASP.NET Vulnerability (blog Security Research & Defense)
    Le point sur la vulnérabilité ASP.NET qui serait causée par la divulgation par .NET de détails dans les messages d'erreur lors du décryptage, ouvrant la voie à des attaques de type "padding oracle".
    A noter qu'à priori des détails d'erreur ne sont pas forcément nécessaires pour que ce type d'attaque fonctionne : un simple retour avec un code 500 au lieu de 200 serait suffisant.
    A ce jour la solution de contournement proposée consiste à désactiver l'affichage des erreurs détaillées (configuration customErrors, que les applications en production sont censées avoir en mode "On" ou "RemoteOnly") avec redirection vers une page unique pour toutes les erreurs et à ajouter au code de cette page une temporisation à durée aléatoire (appel de Thread.Sleep avec timeout entre 0 et 255 ms).
    Si ma compréhension du document Practical Padding Oracle Attacks est bonne (je ne suis pas spécialiste dans ce domaine donc mon avis est à prendre avec des pincettes), ça n'éliminera peut être pas complètement les risques mais ça devrait considérablement allonger les temps de traitement d'une attaque éventuelle.

 

Visual Studio

  • Solution Explorer Tools (When to Sync to Current File) – Extension #11 (blog de Sara Ford)
    Ceux qui ne supportent pas le comportement automatique de l'option "Track Active Item in Solution Explorer" ("Suivre un élément actif dans l'Explorateur de solutions" ?) apprécieront peut être l'extension Solution Explorer Tools (pour Visual Studio 2010) qui ajoute simplement 3 boutons dans l'explorateur de solution : "Select current item", "Collapse all" et "Collapse all except current item" (une combinaison des 2 premiers).
    Il faudra bien sûr penser à désactiver l'option "Track Active Item in Solution Explorer" pour profiter pleinement des fonctionnalités de cette extension.
    A priori ces fonctionnalités sont incluses dans d'autres extensions, mais si on veut juste celles-ci autant ne pas installer plus riche.

 

Divers

Quelques trucs intéressants (12/09/2010)

Cette fois-ci :

  • .NET
  • Debug / Performance
  • Sécurité
  • SQL Server (côté serveur et côté client)

 

.NET

  • PropertyChanged sur les indexeurs (blog d'Olivier Dahan)
    J'utilise très rarement les mécanismes de binding, mais dans ce post j'ai surtout découvert un attribut que j'avais complètement raté malgré le fait qu'il existe depuis .NET 1.0 : IndexerName (namespace System.Runtime.CompilerServices).
    On en trouve notamment trace dans les spécifications du langage C#, en section 17.5.2.1 The IndexerName attribute de la version 4 (2,87Mo, HTML) :
    "Indexers are implemented in .NET using indexed properties, and have a name in the .NET metadata. If no IndexerName attribute is present for an indexer, then the name Item is used by default. The IndexerName attribute enables a developer to override this default and specify a different name."

 

Debug / Performance

  • Windows Sysinternals Primer: Process Explorer, Process Monitor, and More (webcast d'une session TechEd 2010 de Aaron Margosis et Tim Reckmeyer, en anglais, 1H15 environ)
    Ceux qui ne connaissent pas encore ou n'utilisent pas encore les outils Process Explorer, Process Monitor et PsExec seront peut être intéressés par cette session qui en présente quelques fonctionnalités.
    Ceux qui connaissent déjà découvriront peut être quelques nouveautés.
    A noter concernant la démo de filtrage sur une plage de dates dans Process Monitor : depuis cette session, 2 nouveaux items "Exclude Events Before" et "Exclude Events After" sont disponibles dans le menu contextuel d'un event pour faire beaucoup plus rapidement ce que fait le speaker :-)
    Ces nouveaux items sont apparus avec la version 2.92 (30/08/2010).
  • The Case of the Unexplained, 2010 - Troubleshooting with Mark Russinovich (webcast d'une session TechEd 2010 de Mark Russinovich, en anglais, 1H20 environ)
    Pour le côté instructif mais aussi pour le côté fun (vers 12 minutes, pour ne citer qu'un exemple), et la clôture de session pour le moins... différente (dont l'effet n'est bien sûr pas visible dans la vidéo, mais il "suffit" de reproduire en machine virtuelle par exemple).

 

Sécurité

 

SQL Server (côté serveur et côté client)

  • Windows 7 AppCompat changes and SQL Server client and setup hangs (blog SQL Protocols)
    Si vous vous retrouvez, sur Windows 7 ou Server 2008 R2, avec des applications qui bloquent quand elles tentent de se connecter à SQL Server, vous aurez peut être intérêt à vérifier l'état d'activation de ce SwitchBack Compatibility Engine. D'ailleurs il vaut même mieux vérifier cet état avant de déployer SQL Server ou une application qui s'y connecte.
    Ca peut arriver avec le setup de SQL Server lui même, et ça n'est pas forcément flagrant vu que l'interface graphique n'est pas elle même bloquée. De quoi perdre pas mal de temps à attendre pour rien.

 

Divers

  • What Did My Manifest Do: A Referral Was Returned from the Server (blog de Sasha Goldshtein)
    Une explication de ce qui peut se cacher derrière un message d'erreur très peu explicite : "A referral was returned from the server"... Il faut le savoir que ça peut être en relation avec une valeur du manifeste de l'application, liée à l'UAC...
WinDbg / SOS / PSSCOR2 : Failed to load data access DLL (mscordacwks)

Ceux d'entre nous qui analysent des dumps d'applications .NET (notamment ceux créés via WER après un crash) en dehors de l'environnement initial ont probablement tous été confrontés au moins une fois au message suivant, à la saisie d'une commande SOS / PSSCOR :

Failed to load data access DLL, 0x80004005
Verify that 1) you have a recent build of the debugger (6.2.14 or newer)
            2) the file mscordacwks.dll that matches your version of mscorwks.dll is
                in the version directory
            3) or, if you are debugging a dump file, verify that the file
                mscordacwks_<arch>_<arch>_<version>.dll is on your symbol path.
            4) you are debugging on the same architecture as the dump file.
                For example, an IA64 dump file must be debugged on an IA64
                machine.

You can also run the debugger command .cordll to control the debugger's
load of mscordacwks.dll.  .cordll -ve -u -l will do a verbose reload.
If that succeeds, the SOS command should work on retry.

If you are debugging a minidump, you need to make sure that your executable
path is pointing to mscorwks.dll as well.

Celà se produit quand la version de .NET qui exécutait l'application pour laquelle le dump a été généré n'est pas exactement la même que sur la machine où ce dump est analysé, et que les serveurs de symboles publics de Microsoft n'ont pas pu délivrer automatiquement la version demandée.

Une fois que nous nous sommes assurés des points 1 et 4, le plus simple reste encore de récupérer la version de mscordacwks.dll de la machine ayant généré le dump, avant que l'installation de .NET ne soit mise à jour par un éventuel patch : mscordacwks.dll est livrée via les redistribuables .NET, et donc potentiellement mise à jour par les Service Packs et autres patches.
Je n'ai pas connaissance d'un dépôt public des différentes versions de mscordacwks.dll chez Microsoft : si les serveurs de symboles habituels (http://referencesource.microsoft.com/symbols et http://msdl.microsoft.com/download/symbols) ne fournissent pas ces fichiers, je ne vois pas d'autre solution que celle que je décris ci-dessous.

 

Où trouver le fichier sur la machine d'où provient le dump ?

Ce fichier se trouve dans le répertoire d'installation de .NET soit par exemple, pour .NET 2.0, "C:\Windows\Microsoft.NET\Framework\v2.0.50727" ou "C:\Windows\Microsoft.NET\Framework64\v2.0.50727".

En cas de doute sur l'architecture et/ou la version de .NET sous laquelle s'exécutait l'application et qu'il vous sera difficile de demander les fichiers après avoir obtenu le dump, faites vous livrer l'ensemble des versions disponibles. Par exemple :

  • .NET 2.0 x86 : Framework\v2.0.50727\mscordacwks.dll
  • .NET 2.0 x64 : Framework64\v2.0.50727\mscordacwks.dll
  • .NET 4.0 x86 : Framework\v4.0.30319\mscordacwks.dll
  • .NET 4.0 x64 : Framework64\v4.0.30319\mscordacwks.dll

 

Comment savoir où mettre ce fichier ?

En laissant de côté l'utilisation de ".cordll -lp Path" (afin de ne plus être embêté par la suite pour la même version de mscorwks.dll), le mieux est probablement d'utiliser la commande donnée dans le message initial : ".cordll -ve -u -l", en l'agrémentant de la commande "!sym noisy" afin d'obtenir plus de détails.

0:000> !sym noisy; .cordll -ve -u -l
noisy mode - symbol prompts on
CLR DLL status: No load attempts

Une fois cette commande exécutée, ré-émettons notre commande initiale.
Le message sera alors plus verbeux et contiendra des informations de ce genre :

0:000> !pe
CLRDLL: Unable to get version info for 'c:\Symbols\mscorwks.dll\4BE902C7590000\mscordacwks.dll', Win32 error 0n87
SYMSRV:  c:\Symbols\mscordacwks_x86_x86_2.0.50727.3615.dll\4BE902C7590000\mscordacwks_x86_x86_2.0.50727.3615.dll not found
SYMSRV: 
http://referencesource.microsoft.com/symbols/mscordacwks_x86_x86_2.0.50727.3615.dll/4BE902C7590000/mscordacwks_x86_x86_2.0.50727.3615.dll not found
SYMSRV:  c:\Symbols\mscordacwks_x86_x86_2.0.50727.3615.dll\4BE902C7590000\mscordacwks_x86_x86_2.0.50727.3615.dll not found
SYMSRV: 
http://msdl.microsoft.com/download/symbols/mscordacwks_x86_x86_2.0.50727.3615.dll/4BE902C7590000/mscordacwks_x86_x86_2.0.50727.3615.dll not found
CLRDLL: Unable to find mscordacwks_x86_x86_2.0.50727.3615.dll by mscorwks search
CLRDLL: Unable to find 'mscordacwks_x86_x86_2.0.50727.3615.dll' on the path
CLRDLL: ERROR: Unable to load DLL mscordacwks_x86_x86_2.0.50727.3615.dll, Win32 error 0n2
...

Nous avons donc apparemment le choix entre 2 chemins. Personnellement je préfère la version spécifiant explicitement l'architecture, et de toute façon je ne suis pas certains qu'utiliser l'autre soit vraiment une bonne idée.

Dans le cas présent, il ne nous reste donc "plus qu'à" prendre le fichier mscordacwks.dll issu du répertoire Framework\v2.0.50727 de la machine, le renommer en "mscordacwks_x86_x86_2.0.50727.3615.dll" et à le placer dans le répertoire "C:\Symbols\mscordacwks_x86_x86_2.0.50727.3615.dll\4BE902C7590000\", sans doute créé pour l'occasion.

Nous pouvons à présent ré-émettre la commande ".cordll -ve -u -l" (pour forcer une nouvelle tentative de chargement de la dll mscordacwks sans relancer la session de debug) puis la commande que nous voulions exécuter il y a 15 minutes.

Facile, non ? ;-)

Bien sûr s'il existe plus simple et/ou rapide, je suis preneur.

Quelques trucs intéressants (05/09/2010)

Cette fois-ci :

  • .NET
  • Debug / Performance
  • Sécurité
  • SQL Server

 

.NET

  • Determining if a type is defined in the .NET Framework (blog de Scott Dorman)
    Ha tiens, je n'avais jamais vraiment pensé à utiliser le jeton de clé publique pour déterminer si un type fait partie de la BCL ou non. En même temps je n'en ai encore jamais vraiment eu le besoin.
    Sans doute pas 100% sûr comme solution mais c'est une piste comme une autre, à compléter par d'autres tests (racine d'espace de nom, ...).
    Concernant l'implémentation proposée, je ne suis pas trop d'accord avec certains points comme la visibilité publique de la classe ByteArrayEqualityComparer vu le côté ultra-spécifique et dangereux (à l'évolution) du code de la méthode Equals, mais ce n'est qu'un exemple qu'il faut bien sûr adapter à ses besoins.

 

Debug / Performance

 

Sécurité

  • Account Lockout Policy == DoS (blog de David Totzke)
    Tout à fait !
    J'aime bien la formulation du titre, c'est exactement ça : ces stratégies de verrouillage de compte compliquent la vie de ceux qui veulent nous voler des informations (pour simplifier) et facilitent celle de ceux qui veulent simplement nous nuire, nous ralentir.

 

SQL Server

  • T-SQL UDTs. (Huh!) What are they good for? (blog de Bart Duncan)
    Attention, on parle bien ici des "user-defined type" dans le sens alias de type système SQL Server, pas des UDTs CLR définis par du code .NET.
    Les arguments en défaveur de l'utilisation de ces alias de types sont intéressants à lire, tout comme l'exemple en leur faveur donné par Michael J Swart dans le second commentaire : un alias UTCDateTime présente l'avantage de la clareté sur un point où on se fait souvent avoir (l'utilisation du type datetimeoffset n'étant pas toujours envisageable), même si personnellement j'ai tendance à mettre cette précision directement dans le nom de la colonne.
    Enfin, quoi qu'il en soit, je n'aime pas trop les alias.
  • [SSMS] Visualise permissions using PivotViewer (suggestion de Jamie Thomson sur Microsoft Connect)
    La visualisation de certaines choses dans SQL Server Management Studio au travers de Pivot, pourquoi pas ? Peut être avec une collection de type Dynamic. Ou alors il faut juste reprendre le principe et pas l'implémentation particulière de Pivot.
    En tout cas voir passer cette suggestion m'aura au moins bien amusé !

 

Divers

  • The Mystery of the Missing Memory -or- It Pays to Know Your Hardware (blog Ask the Performance Team)
    Ca ne peut pas faire de mal de lire ce genre de choses, des fois qu'un jour on se trouve dans un cas où on ne trouve pas trace d'une moitié de la mémoire physique d'une machine.
    Personnellement je ne connaissais pas ce mécanisme de Redundant Memory / RAS Memory Mirroring.
SQL : Fonctions d'agrégation MIN/MAX et valeurs NULL

Les fonctions d'agrégation comme MIN et MAX ignorent les valeurs NULL présentes dans le jeu de données sur lequel porte leur calcul, d'où le fameux message d'avertissement :

Warning: Null value is eliminated by an aggregate or other SET operation.

Avertissement : la valeur NULL est éliminée par un agrégat ou par une autre opération SET.

Personnellement, j'aurais préféré que ces fonctions renvoient NULL si une des valeurs qui leur sont passées valait NULL.

 

 

L'exemple de problème et une proposition de solution

Ce post sur lequel je suis tombé ce weekend, décrit un cas dans lequel ce comportement pose problème.
L'auteur y contourne le problème en utilisant COALESCE dans l'expression passée à MAX, afin de remplacer la valeur NULL par une valeur connue et n'étant pas censée faire partie des valeurs présentes dans la table cible. Ce qui lui permet ensuite de tester le retour de MAX, et de replacer la valeur NULL si ce retour vaut notre fameuse valeur de remplacement :

...
CASE WHEN MAX(COALESCE(EndDate, '12/31/2099')) = '12/31/2099' THEN NULL ELSE MAX(EndDate) END AS Date
...
Source : Select NULL As Max or Min in SQL Query

Comme le dit l'auteur : ce n'est pas forcément très élégant mais ça marche.
J'ajouterais que ce n'est pas extrèmement intrusif donc ça reste assez compréhensible pour la personne amenée à lire le code par la suite.

 

 

Le problème avec ce type de solution de contournement

Ce qui me dérange avec cette technique de remplacement c'est que justement nous sommes tributaires du fait que jamais personne n'utilisera notre valeur de remplacement pour autre chose, et qu'elle ne sera donc jamais présente en tant que valeur réelle de la colonne.
L'exemple utilise une date relativement proche, le 31/12/2099, mais il aurait très bien pu utiliser le 31/12/9999 (ou le 01/01/1753 pour le cas du MIN).

De mon point de vue ça n'aurait pas changé le problème de fond : pour notre valeur de remplacement, nous choisissons bien sûr une valeur qui n'a aucune chance d'être valide du point de vue métier des applications qui produisent et exploitent les données : dommage que les personnes en charge de la maintenance et des évolutions de ces applications risquent de se faire la même réflexion si ils viennent eux aussi à avoir besoin d'une solution rapide (ne remettant pas en cause le modèle de stockage etc).

 

Exemple de scénario (oui, assez improbable : mais qui n'a jamais rencontré un bug causé par une situation improbable ?) :

  1. Nous choisissons d'utiliser la valeur 31/12/9999 comme valeur de remplacement dans notre requête de reporting.
  2. Six mois plus tard, les personnes chargées de la maintenance du système produisant et traitant les données prennent la décision d'utiliser la valeur 31/12/9999 pour marquer un état "supprimé", qui sera de toute façon retraité par l'application à la lecture des données : pas le temps de revoir le modèle de stockage et le code reposant dessus pour mettre en place cet état.
  3. Un mois plus tard, quelqu'un s'aperçoit que le reporting affiche des éléments "supprimés" comme étant "en cours" : il ne nous reste plus qu'à espérer que ce rapport ne servait pas à des tâches vitales pour l'entreprise.

 

Pour ma part je préfère donc en général chercher une autre solution, quitte à ce que les coûts d'exécution soient tels qu'ils me poussent à changer le modèle de données.

 

 

Solutions envisageables dans le cas de l'exemple, sans utiliser de valeurs de remplacement (et sans utiliser MIN/MAX)

Ces solutions sont données à titre d'exemple et ne doivent pas être prises tel quel sans réflexion poussée.
Comme d'habitude il faut mesurer l'impact sur des volumes réels et prendre une décision suivant les constatations effectuées : la solution présentant des risques sera en fin de compte peut être envisageable, mais au moins nous avons une alternative (qui peut aider à plaider en la faveur d'une éventuelle refonte nécessaire du modèle de stockage, vu la tête des requêtes...)

 

Pour l'extraction des valeurs MIN

Il est possible d'éviter le problème en utilisant le comportement de la clause ORDER BY vis-à-vis des valeurs NULL :

Null values are treated as the lowest possible values.
Source : ORDER BY Clause (Transact-SQL)

Nous pouvons utiliser une CTE et ROW_NUMBER pour parvenir à nos fins :

;WITH IntermediateDataCTE AS
(
    SELECT
        StoreID,
        ROW_NUMBER() OVER (PARTITION BY StoreID
                           ORDER BY EndDate ASC) AS DateRank,
        EndDate AS Date
    FROM WorkSchedule
)
SELECT
    StoreID,
    Date
FROM IntermediateDataCTE
WHERE DateRank = 1

Si la valeur NULL est présente nous obtenons NULL, sinon nous obtenons la date la plus petite pour la partition donnée (StoreID, qui était la valeur utilisée dans la clause GROUP BY).

 

Pour l'extraction des valeurs MAX

Nous ne pouvons pas simplement nous contenter de changer l'ordre de tri.
Nous allons devoir complexifier un peu la partie ORDER BY de ROW_NUMBER, en lui ajoutant un premier critère de tri sur l'état NULL/NOT NULL de la date... au moyen d'une instruction CASE WHEN...

ORDER BY CASE WHEN EndDate IS NULL THEN 1 ELSE 0 END DESC, EndDate DESC

Si une valeur NULL est présente, elle sera en première position du fait du tri DESC sur 1, alors que dans le cas contraire c'est le tri DESC sur la date qui placera la plus grande en première position.
C'est moche et ça nécessitera un bon bloc de commentaires expliquant l'intention de la requête, mais c'est toujours mieux qu'un risque de bug (en partant du principe que les performances sont acceptables pour le jeu de données cible).

Ce qui donne

;WITH IntermediateDataCTE AS
(
    SELECT
        StoreID,
        ROW_NUMBER() OVER (PARTITION BY StoreID
                           ORDER BY CASE WHEN EndDate IS NULL THEN 1 ELSE 0 END DESC,
                                    EndDate DESC) AS DateRank,
        EndDate AS Date
    FROM WorkSchedule
)
SELECT
    StoreID,
    Date
FROM IntermediateDataCTE
WHERE DateRank = 1

Si la valeur NULL est présente nous obtenons NULL, sinon nous obtenons la date la plus grande pour la partition donnée (StoreID, qui était la valeur utilisée dans la clause GROUP BY).

A noter que l'expression "CASE WHEN EndDate IS NULL THEN 1 ELSE 0 END" peut éventuellement être définie en tant que formule d'une colonne calculée (persistée au besoin), ce qui lui permettrait de prendre part à un index. Comme ce n'est pas forcément une pratique bienvenue dans tous les cas et environnements, renseignez vous bien avant de l'envisager.

Quelques trucs intéressants (15/08/2010)

Cette fois-ci, divers sujets :

  • Debug / Performance
  • SQL Server

 

Debug / Performance

 

SQL Server

Quelques trucs intéressants (01/08/2010)

Cette fois-ci, divers sujets :

  • .NET
  • Debug / Network Monitor
  • SQL Server

 

.NET

  • Stream.CopyTo Method (MSDN Library) 
    Apparue avec .NET 4 : la méthode qui fait pour vous l'opération de lecture sur le flux source et écriture sur le flux de destination.
    Pratique pour éviter de faire soi-même la boucle de lecture/écriture quand on n'a absolument rien à faire avec les données entre les deux phases.

 

Debug / Network Monitor

 

SQL Server

  • SQL Server - Count(*) or Count(1) ? - Which is better? (blog de Dave Ballantyne)
    Une petite piqûre de rappel sur ce type de sujet ne fait jamais de mal.
    Au passage, autre rappel : COUNT(MaColonne) n'est PAS équivalent à COUNT(*) (ou COUNT(1)), sauf si la colonne "MaColonne" ne peut recevoir de valeurs indéfinies (la colonne est définie NOT NULL).
    Si pour la colonne "MaColonne" des enregistrements contiennent des valeurs non définies (NULL), elles ne seront pas comptées dans le total.
    Dans ce cas là, la valeur de COUNT(MaColonne) sera égale à celle de COUNT(*) moins le nombre de valeurs indéfinies pour la colonne "MaColonne".
    Souvenez vous, le fameux message "Warning: Null value is eliminated by an aggregate or other SET operation." ;-)

 

Divers

  • Jeux de données : The CFReDS Project et Digital Corpora Govdocs1 (via Greg Duncan)
    CFReDS et Digital Corpora sont à la base orientés investigation, mais il n'en reste pas moins que certains jeux de données peuvent servir à autre chose, comme par exemple le jeu "Govdocs1" et son quasi million de fichiers de divers types (on parle d'environ 300Go d'archives zip).
    Travailler sur un jeu de données réelles ça peut sans doute servir, mais peut être dangereux : à vos risques et périls. Et il va falloir se pencher sur les éventuelles clauses légales pour ce qui est de l'exportation et de l'exploitation des fichiers en dehors de leur pays d'origine etc...
  • Scaling Facebook to 500 Million Users and Beyond (Facebook Engineering's Notes)
    Ca se laisse lire, et c'est parfois utile de se tenir au courant des informations données par ceux qui font face à de tels niveaux de charge.
    Même si on a peu de chances d'arriver à une situation similaire (une charge énorme avec des données très dynamiques), certaines choses mises en oeuvre sur de tels projets peuvent être utilisées sur des projets plus modestes (notamment pour les cas où le succès de ces projets dépasserait toutes les espérances).
Support du multi-écrans dans le client bureau à distance (Remote Desktop Services, ex-Terminal Services)

C'est maintenant possible avec un serveur distant sur Windows Server 2008 R2.

Du vrai multi-écrans et non pas le mode "span" qui forme un seul écran large avec les 2 écrans et nécessite, si ma mémoire est bonne, des résolutions identiques sur tous les écrans.

Si vous avez jeté un oeil au document "What Is New in Remote Desktop Services in Windows Server 2008 R2" dont je vous parlais l'autre jour, vous aurez peut être remarqué une entrée de la section "Remote Desktop Client Experience" en page 25 :

Multiple monitor support. Remote Desktop Connection (RDC) 7.0 and Windows Server 2008 R2 enable support for up to 16 monitors. This feature supports connecting to a remote session with any monitor configuration that is supported on the client computer. Programs function just like they do when they are running on the client computer.

J'ai depuis eu l'occasion de tester et effectivement, c'est plutôt sympa.

Pour en profiter il suffit d'activer l'option "Use all my monitors for the remote session" dans la fenêtre de paramétrage du client RDP (je n'ai pas de client en français sous la main mais si on en croit le site de terminologie, ça doit être "Utiliser tous les moniteurs pour la session à distance").
La version de mstsc.exe livrée avec Windows 7 supporte cette fonctionnalité.

 Capture d'écran de l'onglet "Display" de la boite de dialogue d'option du client Remote Desktop Connection, montrant l'option "Use all my monitors for the remote session"

 

Et bonne nouvelle pour ceux qui disposent de configurations avec des écrans ne supportant pas les mêmes résolutions : c'est supporté.

Capture d'écran de la boite de dialogue "Screen Resolution" montrant la résolution de l'écran principal

Capture d'écran de la boite de dialogue "Screen Resolution" montrant la résolution de l'écran secondaire

Quelques trucs intéressants (19/07/2010)

Cette fois-ci, divers sujets :

  • Debug
  • Développement parallèle
  • Sécurité
  • SQL Server
  • Network Monitor
  • Visual Studio 2010

 

Debug

  • ‘Hidden’ ETW Stack Trace Feature – Get Stacks All Over the Place! (blog Ntdebugging)
    Les stacktraces, c'est toujours utile (et cette entrée va me servir de pense-bête).
  • New Features in FiddlerCore 2.2.9.9 (blog Fiddler)
    FiddlerCore nous permet d'utiliser le moteur de Fiddler dans une application .NET, sans l'interface graphique de l'outil.
    J'apprécie moyennement la proposition de gestion des exceptions à l'arrêt du thread de capture ("You can also ensure that any exceptions in the network-handling thread don’t bring down your application by creating a manifest for your executable (named appname.exe.config, in the same folder as appname.exe) which specifies “legacyUnhandledExceptionPolicy enabled=1”."), un peu trop global à mon gout. 
    Mais bon, ça n'empêchera pas cet outil de rendre de grands services à l'occasion ! Et au pire des cas, si FiddlerCore doit être utilisé dans une application plus vaste, il "suffira" de mettre en oeuvre une architecture avec un processus esclave pour héberger le moteur.

 

Développement parallèle

 

Sécurité

  • [EN FRANCAIS] La tokenization, pilule magique de la conformité PCI (Jerome Saiz, SecurityVibes)
    Vous avez peut-être vu passer quelques informations concernant la "tokenization", principalement dans le contexte banquaire et commerce en ligne.
    Voici une présentation du sujet, en français. Après tout, nous sommes tous concernés (surtout avec des commercant en ligne ayant un comportement énervant).
    A noter bien sûr que cette "tokenization" n'est pas forcément utile que pour le domaine bancaire, c'est applicable pour la confidentialité en général (protection de la vie privée, ...) : dans la mesure du possible, si plusieurs systèmes doivent se référer à une personne sans que le messager aie besoin d'avoir accès aux données réelles, autant qu'il transporte un jeton et pas les informations. Même cryptées. Même si elles ne sortent pas de la sphère du SI. Voyageons léger.
  • The Placebo Effect (blog "Secure Computing: Sec-C")
    Une reflexion sur le fait que HTTPS peut donner à l'utilisateur d'un navigateur une fausse impression de sécurité totale du dialogue entre le navigateur et un serveur HTTP, alors que ce n'est pas forcément le cas.

 

SQL Server

  • Revisiting ISNULL, COALESCE, and the Perils of Micro-Optimization (blog d'Adam Machanic)
    Le paragraphe "ISNULL has a really cool use case." contient une information intéressante sur la manière dont ISNULL peut influer sur la définition d'une table créée par un ordre SELECT INTO.
    En la lisant, cette information m'a dit quelque chose : il me semble l'avoir déjà vue sur un blog francophone. Que l'auteur se dénonce s'il passe par ici ! ;-)

 

SQL Server + Network Monitor

 

Visual Studio 2010

 

Divers

  • My Kerberos Checklist… (blog CSS SQL Server Engineers)
    Parce que c'est souvent un casse tête, surtout quand on ne s'y frotte pas chaque jour... Hop, un autre pense-bête.
    Les systèmes utilisés pour l'exemple sont SQL Server, SharePoint (et donc IIS) et Reporting Services mais ça reste applicable à n'importe quel scénario où une architecture avec un sous-système de confiance (trusted subsystem) n'est pas envisageable/souhaitable.
  • Hardware Accelerated Blur Pack (Bruce Bowyer-Smith, forum Plugins de Paint.NET, via Rick Brewster)
    Un pack d'effets reprenant certains de ceux fournis avec Paint.NET, mais faisant cette fois-ci reposer les calculs sur le GPU.
    Ceux qui disposent du matériel requis (supportant DirectCompute) apprécieront peut être.


Les 10 derniers blogs postés

- Compte rendu : SharePoint / O365 : des pratiques pour une meilleure productivité par The Mit's Blog le 12-12-2014, 18:11

- [TFS] Suppression des feature SQL Entreprise en masse par Blog de Jérémy Jeanson le 12-06-2014, 09:18

- [Clean Code] règles de nommage par Fathi Bellahcene le 12-04-2014, 22:59

- Windows To Go 10 et Upgrades impossibles par Blog de Jérémy Jeanson le 12-04-2014, 21:38

- SharePoint OnPremise: Statistiques d’utilisation pour traquer les sites fantomes par Blog Technique de Romelard Fabrice le 12-03-2014, 10:28

- SharePoint 2007: Script PowerShell permettant le backup de toutes les collections de sites d’une application Web par Blog Technique de Romelard Fabrice le 12-02-2014, 10:00

- Xamarin : un choix précieux par .net is good... C# is better ;) le 12-01-2014, 15:10

- Office 365: Comparaison des composants pour préparer votre migration de SharePoint 2007 vers Office 365 par Blog Technique de Romelard Fabrice le 11-28-2014, 16:20

- Créer un périphérique Windows To Go 10 ! par Blog de Jérémy Jeanson le 11-21-2014, 04:54

- RDV à Genève le 12 décembre pour l’évènement “SharePoint–Office 365 : des pratiques pour une meilleure productivité !” par Le blog de Patrick [MVP Office 365] le 11-19-2014, 10:40