Partager via


Cet article a fait l'objet d'une traduction automatique.

Windows avec C++

Le disque virtuel API dans Windows 7

Kenny Kerr

Cet article est basé sur une version préliminaire de Windows 7. Toutes les informations dans le présent document sont susceptibles d'être modifiées.

Contenu

Le format VHD
Création de disques virtuels
L'ouverture de disques virtuels
Association des disques virtuels
Interrogation des disques virtuels
Quel est le suivant

Comme je écrire cela, la version bêta de Windows 7 a été disponible pendant quelques jours, et je doit DIS il y a beaucoup à comme. Comme d'habitude, J'AI présentés sous le capot de découvrir les nouveaux dans le SDK Windows. Windows 7 est très bien un mineur version comme loin que concerne le Kit de développement logiciel (SDK), c'est une bonne chose. Les principes fondamentaux d'écrire des applications C++ natives pour Windows 7 n'ont pas changé bien par rapport à la façon dont elles changé pour Windows Vista. Avoir dit qui, cependant, Windows 7 dispose de complètement nouveau fonctionnalités qu'êtes certain de toute personne souhaitant pour tirer parti de la plate-forme d'intérêt.

Une des ces fonctionnalités est l'API de disque virtuel. Bien que conçu avec d'autres formats N'oubliez pas, l'API de disque virtuel dans la version bêta de Windows 7 est très bien adapté au format Microsoft disque dur virtuel (VHD) popularisée par produits de virtualisation de Microsoft telles que la technologie Hyper-V et Virtual PC.

Cela réellement amène me, car j'ai été impliqué avec la virtualisation des années un certain. Lorsque j'ai commencé Utilisation de la technologie de virtualisation il y a presque dix ans, VMware était le leader clair. Puis 2003 Microsoft a acquis machine virtuelle technologie Connectix et tout ce que modifié. Tout d'un sudden il y avait deux lecteurs de grandes. Il était évident le format VHD Microsoft a acquis de Connectix était parfaitement le sens que Microsoft souhaitiez faire avec la virtualisation, à savoir pour l'activer dans une plate-forme. Tandis que les formats de disque virtuel de VMware étaient propriétaire et très convoluted, souvent remplaçant entièrement une version par la suivante, le format VHD était à partir du début simple et suffisamment flexible pour le test de temps de veille. Les années intermédiaires, le format VHD a révélé lui-même encore et encore avoir été adopté par les autres produits et technologies de Microsoft et par autres sociétés de logiciels, grandes et petites.

Pour cette raison, je suis heureux de savoir que Windows 7 prend en une charge du format VHD en mode natif. Cela signifie que les utilisateurs et les administrateurs peuvent facilement créer et joindre disques virtuels comme s'il s'agissait de périphériques de stockage physique supplémentaire, sans installer les pilotes tiers ou des outils. Vous pouvez, par exemple, utiliser le composant logiciel enfichable MMC Gestion de disque (Microsoft Management Console) ou l'outil de ligne de commande DISKPART pour créer et joignez disques virtuels. Vous pouvez puis partition, mettre en forme et utiliser comme n'importe quel autre disque dur sur votre ordinateur.

En outre, vous prendre également le contrôle très précis la création et la gestion des disques virtuels. Au cœur de ces fonctionnalités est VirtDisk.dll, qui fournit une API C plus bas niveau, appelée l'API de disque virtuel, pour créer et manipuler des disques virtuels. Cette API permet d'accéder à un nombre de pilotes en mode noyau sont nécessaires pour réellement représenter le disque et ses volumes dans le sous-système de stockage afin que les pilotes du système de fichiers peuvent être rajoutées sur haut sans devoir les connaissances de la source réelle de l'emplacement de stockage.

Outils de gestion de disque interagissent généralement avec disques virtuels via le virtuel disque service (VDS) que lui-même repose sur l'API de disque virtuel pour gérer le stockage en fonction de VHD. Bien entendu la technologie Hyper-V possède son propre basé sur WMI (Windows Management Instrumentation) API pour créer et manipuler des machines virtuelles, et il trop repose sur l'API de disque virtuel.

Avant que je plonger dans et indiquent comment utiliser l'API de disque virtuel, je décrirai rapidement les principes fondamentaux du format VHD. Vous verrez qu'étant donné cette nouvelle API, vous pouvez maintenant jeter quantité de code que vous avez besoin précédemment pour la gestion des disques virtuels.

Le format VHD

Le format VHD fournit trois types d'images différente : fixe, les disques dynamiques et différenciation. Tous les types de disque trois comprennent les VHD 512 octets pied de page qui se trouve à la fin du fichier disque. Plus sur ce dans une minute.

Des disques fixes sont du type plus simple et fournissent les meilleures performances parce que le fichier du disque est entièrement ventilé pour prendre en charge la taille demandée lorsque les disques sont créées. Un disque fixe de 500 Mo sera exactement 500 x 1 024 x 1 024 + 512 octets la taille. Étant donné que le pied de page est à la fin de la disquette, stockage sur le disque peut aligner avec le début du fichier pour garantir l'accès aléatoire plus simple et fastest possible.

Les disques dynamiques sont appelés disques fragmentés par l'API de disque virtuel et fragmentation est un bon moyen de considérer les. Les fichiers de disque sont initialement créés avec juste assez d'espace pour stocker le pied de page VHD, ainsi que des métadonnées supplémentaires nécessaires pour gérer la nature dynamique de l'allocation de stockage. Telles que données sont écrites sur le disque, plus de blocs de stockage sont affectées à la fin du fichier disque. Les disques dynamiques sont intéressant, car ils occupent beaucoup moins d'espace sinon utilisé à sa capacité maximale qui est le cas la plupart du temps. L'inconvénient est que le indirection supplémentaire et la gestion des métadonnées nécessaire pour lire, écrire et agrandir un disque dynamique sur la demande prend son espace sur les performances. Pour cette raison, les disques dynamiques sont prises en test scénarios, tandis que des disques fixes sont préférés dans les scénarios de fabrication, car les performances une priorité élevée.

Des disques de différenciation sont très similaires aux disques dynamiques en interne, mais les autres caractéristiques sont très différentes. Comme avec les disques dynamiques, utilisation des disques de différenciation dynamiquement attribué blocs, mais ces blocs contiennent uniquement les modifications liées à un disque parent. Ce type de disque dépend donc un disque parent de la fonction. Le parent d'un disque de différenciation peut être un fixe ou un disque dynamique. Le parent peut, en fait être un autre disque de différenciation, permettant une chaîne ou une arborescence de disques à créer. Disques qui représentent des nœuds feuilles peuvent être librement écrits pour mais non-feuille nœuds doivent être considérées comme en lecture seule car les disques de différenciation descendants reposent sur les combler les vides, donc à dire et si elles étaient pour la modifier serait très probablement entraîner disques virtuels endommagés.

Comme je L'AI mentionné précédemment, les différents types de disques partagent un pied de page courants. Ce pied de page contient des informations telles que la taille logique de la disquette, géométrie du disque, le type de disque, le disque identificateur global unique, etc.. Pour les disques dynamiques et différenciation le pied de page inclut également une valeur de décalage qui indique où l'en-tête dynamique réside par rapport au début d'un disque. Cette structure secondaire chiefly contient des informations peuvent être amenées à rechercher et d'identifier un disque parent, si nécessaire, ainsi que décalage un autre qui indique où se trouve bloc table d'allocation le disque (BAT). Ce tableau indique les blocs ont été affectés, ainsi que les décalages absolues dans le fichier de disque.

Avec cette introduction à d'en, nous allons plonger dans et Examinons l'API de disque virtuel.

Gardez à l'esprit que l'API est conçue pour permettre Microsoft pour ajouter la prise en charge d'autres formats dans le futur. En fait, prend en charge pour le format d'image disque optique ISO a été considéré comme mais n'a pas été ajouté en tant que la version bêta de Windows 7. J'espère que le fournisseur de prise en charge de disque virtuel nécessaire sera inclus dans la version de publication. Cela permet aux utilisateurs de joindre et les images mount ISO en tant que disques durs en lecture seule.

Création de disques virtuels

Disques virtuels sont représentés par opaques poignées, comme autre système des objets comme des fichiers, clés de Registre et ainsi de suite. La fonction CloseHandle familier est encore utilisée pour fermer un descripteur de disque virtuel. De l'Active Template Library ATL (CHandle classe est un bon choix pour gérer cette ressource. Vous pouvez dériver une classe « VirtualDisk » de CHandle à encapsuler des partie le code réutilisable nécessaire manipuler des disques virtuels.

Lorsque vous ouvrez ou créer un disque virtuel, vous devez spécifier le type de stockage du disque. Pour ce faire avec la structure VIRTUAL_STORAGE_TYPE. Types de stockage sont définis pour les formats ISO et VHD. La structure spécifie également fournisseur qui fournit l'implémentation pour le type de stockage particulier. Voici comment vous pouvez identifier le type de stockage VHD :

VIRTUAL_STORAGE_TYPE storageType =
{
    VIRTUAL_STORAGE_TYPE_DEVICE_VHD,
    VIRTUAL_STORAGE_TYPE_VENDOR_MICROSOFT
};

La fonction CreateVirtualDisk crée tous les trois types de disques virtuels. Ainsi que le type de stockage, vous devez alimenter une structure CREATE_VIRTUAL_DISK_PARAMETERS. Comment cette structure est remplie dépend du type de disque virtuel que vous souhaitez créer. La plupart des structures D'API de disque virtuel utiliser un schéma de versions pour prendre en charge des futures mises à jour à l'API. Les structures de commencent par un membre nommé version suivie par une union des structures. Par exemple, voici comment vous pouvez renseigner les champs communs pour la création de disques virtuels :

CREATE_VIRTUAL_DISK_PARAMETERS parameters =
{
    CREATE_VIRTUAL_DISK_VERSION_1
};
parameters.Version1.MaximumSize = size;
parameters.Version1.BlockSizeInBytes = CREATE_VIRTUAL_DISK_PARAMETERS_DEFAULT_BLOCK_SIZE;
parameters.Version1.SectorSizeInBytes = CREATE_VIRTUAL_DISK_PARAMETERS_DEFAULT_SECTOR_SIZE;

La taille du disque virtuel est spécifiée en octets et doit être un multiple de 512. Le bloc de taille de taille et le secteur est configurable, mais si vous devez vérifier le niveau plus élevé de compatibilité entre les implémentations les valeurs par défaut sont un bon choix. La structure Version1 fournit également un membre UniqueId, mais si vous laissez cette mise à zéro les, la fonction CreateVirtualDisk génère un GUID pour vous. Le membre SourcePath peut-être également être spécifié pour tous les types de disque à l'exception des disques de différenciation. Cette demande CreateVirtualDisk pour copier le contenu du disque source dans le disque virtuel nouvellement créé. Les deux est inutile de même type. En fait, le disque source ne même pas à être un disque virtuel et peut être un disque physique que vous souhaitez créer une copie de.

la figure 1 fournit les bases d'une classe wrapper VirtualDisk qui inclut une fonction membre CreateFixed pour la création fixe disques virtuels. Notez que pour les disques fixes vous devez spécifier l'indicateur CREATE_VIRTUAL_DISK_FLAG_FULL_PHYSICAL_ALLOCATION. Création d'un disque dynamique est identique la création d'un disque fixe sauf que vous devez omettre cet indicateur et que vous pouvez spécifier l'indicateur CREATE_VIRTUAL_DISK_FLAG_NONE. Création d'un disque de différenciation est également plus le même. La différence est que vous ne devez pas définir la taille, telle qu'elle est déduite à partir du disque parent, et vous devez spécifier l'objet parent avec le membre ParentPath de la structure Version1. Impossible de définir le SourcePath pour les disques de différenciation pour des raisons évidentes.

La Figure 1 Création des disques

class VirtualDisk : public CHandle
{
public:

    DWORD CreateFixed(PCWSTR path,
                      ULONGLONG size,
                      VIRTUAL_DISK_ACCESS_MASK accessMask,
                      __in_opt PCWSTR source,
                      __in_opt PSECURITY_DESCRIPTOR securityDescriptor,
                      __in_opt OVERLAPPED* overlapped)
    {
        ASSERT(0 == m_h);
        ASSERT(0 != path);
        ASSERT(0 == size % 512);

        VIRTUAL_STORAGE_TYPE storageType =
        {
            VIRTUAL_STORAGE_TYPE_DEVICE_VHD,
            VIRTUAL_STORAGE_TYPE_VENDOR_MICROSOFT
        };

        CREATE_VIRTUAL_DISK_PARAMETERS parameters =
        {
            CREATE_VIRTUAL_DISK_VERSION_1
        };

        parameters.Version1.MaximumSize = size;
        parameters.Version1.BlockSizeInBytes = CREATE_VIRTUAL_DISK_        PARAMETERS_DEFAULT_BLOCK_SIZE;
        parameters.Version1.SectorSizeInBytes = CREATE_VIRTUAL_DISK_        PARAMETERS_DEFAULT_SECTOR_SIZE;
        parameters.Version1.SourcePath = source;

        return ::CreateVirtualDisk(&storageType,
                                   path,
                                   accessMask,
                                   securityDescriptor,
                                   CREATE_VIRTUAL_DISK_FLAG_FULL_                                   PHYSICAL_ALLOCATION,
                                   0, // no provider-specific flags
                                   &parameters,
                                   overlapped,
                                   &m_h);
    } 

La fonction CreateVirtualDisk fournit quelques paramètres supplémentaires qui sont important de mentionner. L'énumération VIRTUAL_DISK_ACCESS_MASK fournit un ensemble d'indicateurs pour contrôler l'accès de l'API vous accorde les appelants via le handle résultant. Bien que vous puissiez spécifier VIRTUAL_DISK_ACCESS_ALL, c'est interdit souvent car elle vous empêche d'exécuter certaines opérations telles qu'interroger un disque virtuel lié comme un périphérique de stockage. L'autre fonctionnalité utile est la possibilité de spécifier une structure OVERLAPPED. Ceci est pris en charge par un certain nombre des fonctions API disque virtuel et a comme peut s'y attendre pour effet de l'opération asynchrone. Simplement fournir un manuel réinitialiser l'événement et sera signalé à la fin.

L'ouverture de disques virtuels

La fonction OpenVirtualDisk peut être utilisée pour ouvrir un disque virtuel. Comme avec création de disque virtuel, vous devez fournissent une structure VIRTUAL_STORAGE_TYPE pour identifier le type de stockage. Une structure OPEN_VIRTUAL_DISK_PARAMETERS peut également être fournie mais est généralement uniquement nécessaire lorsque manipulation relations disque de différenciation.

la figure 2 fournit une fonction membre ouvrir pour ajouter à la classe wrapper VirtualDisk démarrée dans la figure 1 . L'ouverture de disques virtuels est généralement plus simple que leur création, mais certains des indicateurs et options doivent être utilisé d'une manière très spécifique pour permettre certaines opérations de maintenance telles que la fusion et joindre disques virtuels.

Disques virtuels figure 2 ouverture

DWORD Open(PCWSTR path,
           VIRTUAL_DISK_ACCESS_MASK accessMask,
           OPEN_VIRTUAL_DISK_FLAG flags, // OPEN_VIRTUAL_DISK_FLAG_NONE
           ULONG readWriteDepth) // OPEN_VIRTUAL_DISK_RW_DEPTH_DEFAULT
{
    ASSERT(0 == m_h);
    ASSERT(0 != path);

    VIRTUAL_STORAGE_TYPE storageType =
    {
        VIRTUAL_STORAGE_TYPE_DEVICE_VHD,
        VIRTUAL_STORAGE_TYPE_VENDOR_MICROSOFT
    };

    OPEN_VIRTUAL_DISK_PARAMETERS parameters =
    {
        OPEN_VIRTUAL_DISK_VERSION_1
    };

    parameters.Version1.RWDepth = readWriteDepth;

    return ::OpenVirtualDisk(&storageType,
                             path,
                             accessMask,
                             flags,
                             &parameters,
                             &m_h);
}

Association des disques virtuels

La version bêta de Windows 7 utilise le terme en surface ou surfacing, pour attacher le disque en tant qu'un périphérique de stockage dans le système d'exploitation. Ce ensuite devenu le mot plus évident joindre. La fonction AttachVirtualDisk (appelé SurfaceVirtualDisk dans la version bêta) associe le disque virtuel. Le disque virtuel est identifié par un handle auparavant obtenu par un appel à CreateVirtualDisk ou OpenVirtualDisk. Vous devez vous assurer que la poignée dispose de l'accès approprié définie pour. Pour attacher et détacher un disque virtuel, vous devez également disposer des privilèges SE_MANAGE_VOLUME_NAME présents dans votre jeton. Ce privilège est supprimé à partir jeton d'un administrateur lorsque contrôle des comptes d'utilisateurs est utilisé, vous devez peut-être élever votre application pour pouvoir utiliser le jeton non restreint qui inclut ce privilège.

la figure 3 fournit une fonction membre Joindre pour ajouter à la classe wrapper VirtualDisk. Le paramètre ATTACH_VIRTUAL_DISK_FLAG (appelé SURFACE_VIRTUAL_DISK_FLAG dans la version bêta) est comment contrôle de la méthode dans laquelle le disque virtuel est associée.

La figure 3 association des disques

DWORD Attach(ATTACH_VIRTUAL_DISK_FLAG flags,
              __in_opt PSECURITY_DESCRIPTOR securityDescriptor,
              __in_opt OVERLAPPED* overlapped)
{
    ASSERT(0 != m_h);


    return ::AttachVirtualDisk(m_h,
                                securityDescriptor,
                                flags,
                                0, // no provider-specific flags
                                0, // no parameters
                                overlapped);
}

ATTACH_VIRTUAL_DISK_FLAG_READ_ONLY (appelé SURFACE_VIRTUAL_DISK_FLAG_READ_ONLY dans la version bêta) peut être spécifié pour vous assurer que le disque attaché est protégé en écriture. Il ne peut pas être remplacée par une tentative de rendre le disque inscriptible avec VDS.

ATTACH_VIRTUAL_DISK_FLAG_NO_DRIVE_LETTER (appelé SURFACE_VIRTUAL_DISK_FLAG_NO_DRIVE_LETTER dans la version bêta) empêche Windows d'automatiquement affectation de lettres de lecteur à tous les volumes présents dans le disque virtuel. Vous êtes libre puis à monter les volumes par programmation ou pas du tout, en fonction de vos besoins. La fonction GetVirtualDiskPhysicalPath peut être utilisée pour identifier le chemin physique où le disque virtuel a été joint.

ATTACH_VIRTUAL_DISK_FLAG_PERMANENT_LIFETIME (appelé SURFACE_VIRTUAL_DISK_FLAG_PERMANENT_LIFETIME dans la version bêta) garantit que le disque virtuel reste attaché même après fermeture de la poignée de disque virtuel. Échec de spécifier cet indicateur entraîne le disque virtuel est détaché automatiquement lorsque le handle est fermé. Pour détacher le disque virtuel dans ce cas, vous devez appeler la fonction DetachVirtualDisk (appelée UnsurfaceVirtualDisk dans la version bêta).

Interrogation des disques virtuels

La fonction GetVirtualDiskInformation permet d'interroger un disque virtuel pour les différentes classes d'informations. Les informations sont communiquées à l'utilisation de la structure GET_VIRTUAL_DISK_INFO qui utilise le même motif version utilisé par la plupart des autres structures de API de disque virtuel. Par exemple, pour obtenir des informations taille du disque vous définir la structure version à GET_VIRTUAL_DISK_INFO_SIZE. Membre syndicat taille puis sera rempli. la figure 4 illustre cela.

Informations sur la figure 4 Getting taille

DWORD GetSize(__out ULONGLONG& virtualSize,
              __out ULONGLONG& physicalSize,
              __out ULONG& blockSize,
              __out ULONG& sectorSize) const
{
    ASSERT(0 != m_h);

    GET_VIRTUAL_DISK_INFO info =
    {
        GET_VIRTUAL_DISK_INFO_SIZE
    };

    ULONG size = sizeof(GET_VIRTUAL_DISK_INFO);

    const DWORD result = ::GetVirtualDiskInformation(m_h,
                                                     &size,
                                                     &info,
                                                     0); // fixed size

    if (ERROR_SUCCESS == result)
    {
        virtualSize = info.Size.VirtualSize;
        physicalSize = info.Size.PhysicalSize;
        blockSize = info.Size.BlockSize;
        sectorSize = info.Size.SectorSize;
    }

    return result;
} 

GetVirtualDiskInformation fonction fonctionne sur une poignée de disque virtuel, donc à nouveau vous devez vous assurer que vous disposez de l'accès approprié. Dans ce cas l'autorisation VIRTUAL_DISK_ACCESS_GET_INFO est requise. Car certaines des informations qui peuvent être obtenues à l'aide de GetVirtualDiskInformation est variable à une longueur, il fournit des deux paramètres supplémentaires pour spécifier quel espace de stockage est initialement fourni et combien a finalement rempli. Pour la plupart des informations que vous interroge la taille est connue à l'avance d'et le dernier paramètre peut être omis, comme c'est le cas dans la figure 4 .

L'exception notable est lors de l'interrogation d'un disque de différenciation pour l'emplacement, ou le chemin d'accès, d'un disque virtuel parent. Dans ce cas, vous devez commencer par déterminer la quantité de mémoire est requise par un appel initial à GetVirtualDiskInformation. Cet appel va échouer avec ERROR_INSUFFICIENT_BUFFER mais vous fournir avec la taille de la mémoire tampon doit être affectée. Vous pouvez ensuite appeler la fonction une deuxième fois pour en fait obtenir les informations. L'indicateur version GET_VIRTUAL_DISK_INFO_PARENT_LOCATION est utilisé pour obtenir l'emplacement du parent. Il est cependant, un peu plus compliqué toujours. Puisque conservant une référence à un parent est donc essentiel pour l'opération d'un disque de différenciation, le format VHD fournit un degré de redondance qui peut être exploitée doit le lien pour l'objet parent être rompu. Suffice il de dire que l'interrogation de l'emplacement parent implique l'analyse d'une séquence de chaînes nul, terminé par une chaîne vide. Il est identique au type de valeur de Registre REG_MULTI_SZ. figure 5 illustre comment il est fait. Collection CAtlArray de ATL exemple utilise classe ainsi CString classe D'ATL.

La figure 5 Getting parent emplacement

DWORD GetParentLocation(__out bool& resolved,
                        __out CAtlArray<CString>& paths) const
{
    ASSERT(0 != m_h);

    GET_VIRTUAL_DISK_INFO info =
    {
        GET_VIRTUAL_DISK_INFO_PARENT_LOCATION
    };

    ULONG size = sizeof(GET_VIRTUAL_DISK_INFO);

    DWORD result = ::GetVirtualDiskInformation(m_h,
                                               &size,
                                               &info,
                                               0); // not used

    if (ERROR_INSUFFICIENT_BUFFER != result)
    {
        return result;
    }

    CAtlArray<BYTE> buffer;

    if (!buffer.SetCount(size))
    {
        return ERROR_NOT_ENOUGH_MEMORY;
    }

    GET_VIRTUAL_DISK_INFO* pInfo = reinterpret_cast<GET_VIRTUAL_DISK_    INFO*>(buffer.GetData());
    pInfo->Version = GET_VIRTUAL_DISK_INFO_PARENT_LOCATION;

    result = ::GetVirtualDiskInformation(m_h,
                                         &size,
                                         pInfo,
                                         0); // not used

    if (ERROR_SUCCESS == result)
    {
        resolved = 0 != pInfo->ParentLocation.ParentResolved;
        PCWSTR path = pInfo->ParentLocation.ParentLocationBuffer;

        while (0 != *path)
        {
            paths.Add(path);

            path += paths[paths.GetCount() - 1].GetLength() + 1;
        }
    }

    return result;
}

Quel est le suivant

L'API de disque virtuel fournit quelques autres fonctions principalement destinées à la maintenance ou de réparer disques virtuels. La fonction MergeVirtualDisk permet de vous permet de fusionner les modifications apportées à un disque de différenciation en un disque parent. Il prend en charge fusion avec n'importe quel parent dans la chaîne. Fonctions sont également fournies pour compacter et développer des disques virtuels, ainsi que mise à jour des métadonnées relationnelle dans le cas des disques de différenciation.

Notez que le SDK Windows pour la version bêta de Windows 7 omis le fichier VirtDisk.lib nécessaire pour lier aux fonctions API disque virtuel. Cela sera corrigé pour la version. Les développeurs utilisant la version bêta peuvent utiliser les fonctions LoadLibrary et GetProcAddress pour charger et résoudre les fonctions ou générer le fichier lib vous-même.

Veuillez envoyer vos questions et commentaires à mmwincpp@microsoft.com

Kenny Kerr est un artisan logiciel spécialisé dans le développement de logiciels pour Windows. Il a une passion pour écrire et il enseigne aux développeurs sur la conception programmation et de logiciels. Vous pouvez contacter Kenny à weblogs.asp. NET/kennykerr.