Développer des applications sous Windows Vista - partie 3

Louis-Guillaume MORAND

Avec l'arrivée du nouveau système d'exploitation Windows Vista, un grand nombre de fonctionnalités ont été présentées aux utilisateurs, tant au niveau de la sécurité que d'améliorations graphiques ou interactives. Pour les développeurs, il est alors possible d'intégrer ces nouveautés au sein de leurs applications et c'est ce que cet article va tâcher de présenter.

N'hésitez pas à commenter cet article ! Commentez ♪

- Titre : Développer des applications sous Windows Vista - partie 3

- Auteur : Louis-Guillaume MORAND

- Parution : 18 janvier 2008

- Licence : Ce document est issu de http://www.developpez.com et reste la propriété exclusive de son auteur. La copie, modification et/ou distribution par quelque moyen que ce soit est soumise à l'obtention préalable de l'autorisation de l'auteur.

Sommaire

- Introduction

- I. Créer une application dans le panneau de configuration

- I-A. Création d'un GUID

- I-B. Création de notre applet

- I-C. Enregistrer l'applet

- I-D. L'ajout des tâches

- I-E. Pour aller un peu plus loin

- I-E-1. Changer l'icône de l'applet

- I-E-2. Faire apparaitre le bouclier de l'UAC à côté des tâches critiques

- I-E-3. Lancer une application à partir du titre de l'applet

- I-F. Conclusion et liens complémentaires

- II. Amélioration des interfaces graphiques

- II-A. La textBox améliorée

- II-B. Le SplitButton

- II-C. Sélection semi-transparente sur les listViews

- Conclusion

- III. Remerciements

Introduction

Après une première et une seconde partie sur les développements qu'il est possible de faire dans Vista, nous allons continuer à découvrir ce qu'il est possible de faire sous ce nouveau système d'exploitation.

I - Créer une application dans le panneau de configuration

Dans un article précédent, j'expliquai comment il était possible de personnaliser le Welcome Center et dans un autre, j'expliquai le développement de widget pour la SideBar. Aujourd'hui, nous allons nous attaquer à quelque chose de pas forcément plus compliqué, moins évident à mettre en pratique, mais qui, j'en suis sûr, pourra trouver une utilité pour certains d'entre vous. Nous allons voir comment personnaliser le panneau de configuration pour y faire apparaitre nos propres éléments.

Depuis de nombreuses années, la personnalisation d'une application ou d'un système d'exploitation fait partie de mes passe-temps favoris. Et alors que je cherchais à comprendre comment marchait Windows 2000 à l'époque, je me rappelle avoir été intrigué par un élément nvidia ajouté à mon panneau de configuration et j'ai cherché à l'enlever, car…l'icône était laide (oui oui). J'ai alors découvert qu'il s'agissait simplement de fichier *.cpl développé et enregistré sur le système.
Pour ceux qui voulaient développer leur propre composant, il leur fallait suivre scrupuleusement les étapes décrites sur la MSDN.

Une chance pour nous, tout ceci a été revu sous Windows Vista et il est devenu très simple de personnaliser le panneau de configuration, peut-être même aussi simple que la personnalisation du Welcome Center.

Voici un exemple simple de ce que vous saurez faire avec cet article.

I-A - Création d'un GUID

Ce petit mini chapitre est presque hors contexte puisqu'il va simplement vous expliquer comment générer un GUID. Un GUID est un Global Unique IDentifier, c'est-à-dire, une chaine de caractères servant d'identifiant unique permettant de désigner un élément, programme ou autre, de façon unique, un peu comme le numéro sur votre carte d'identité. Pratiquement tous les éléments de votre système, que ce soit un dossier système particulier, ou un gestionnaire d'extension et bien d'autres choses, ont leur GUID et notre preview handler, n'échappera pas à la règle. Un GUID est de la forme {CEC124F7-656C-458b-9E7C-43462DE09D01} et en créer un à la main en s'assurant qu'il est unique n'est pas évident. Il faudrait regarder dans votre registre qu'il n'est pas utilisé et si un jour vous deviez utiliser ce GUID sur des postes clients, vous n'auriez pas la certitude de son unicité. Il n'y a donc pas de solution magique et il vous faut utiliser un petit utilitaire fourni par Microsoft qui se nomme GUIDGEN.exe. Pour générer un GUID, rien de tel que Visual Studio et son menu Tools > Generate GUID. Choisissez alors un format registre.

Si vous avez Visual Studio 2008, alors là, manque de chance, le menu n'existe pas, l'outil n'est plus installé avec Visual Studio par défaut. Vous avez alors deux choix, soit le GUIDGEN.exe a été installé et se trouve alors dans C:\Program Files\Microsoft Visual Studio 9.0\Common7\Tools\, soit tout simplement, il n'est pas sur votre organisateur, et il faut le télécharger sur le site de Microsoft. Une fois celui-ci téléchargé et placé où bon vous semble, vous devez cliquer sur le menu Tools > External Tools… pour configurer un nouveau menu qui fera appel à l'utilitaire. Et c'est tout. Voilà, c'était une petite parenthèse que je jugeais nécessaire, car je suis tombé sur ce problème au moment de la rédaction de cet article.

I-B - Création de notre applet

Comme je l'ai dit, vous pourriez avoir besoin de lancer une application développée pour votre entreprise et vous aimeriez la lancer depuis le panneau de configuration. Si par le passé, vous deviez créer une DLL (renommée en .cpl) et implémentant CplApplet, il est maintenant possible, simplement à l'aide de quelques clés registres et d'un fichier XML de rajouter plusieurs actions aux panneaux de configurations.

La première étape consiste à générer un GUID comme vu dans le chapitre précédent. Créez ce GUID et notez-le dans un fichier texte. Profitez-en pour créer trois autres GUID que vous copierez aussi dans le fichier texte afin de ne pas les oublier. Durant cet article, et pour que ce soit clair pour tout le monde, j'utiliserai des GUID factices, mais facilement reconnaissables. J'aurais ainsi :
{00000000-0000-0000-0000-000000000000}
{00000000-0000-0000-0000-000000000001}
{00000000-0000-0000-0000-000000000002}
{00000000-0000-0000-0000-000000000003}

Commençons par enregistrer ce GUID dans le registre. Ouvrez Regedit.exe et rendez-vous à HKEY_CLASSES_ROOT\CLSID\ et créez une nouvelle clé nommée avec votre premier GUID. Dans sa clé présente par défaut, mettez la valeur « MonPremierApplet ». Cette chaine peut-être n'importe laquelle, elle ne nous sera pas utile dans le cas de cet article.
Créez ensuite quatre autres clés de type chaine comme le tableau suivant

Nom de la clé

Valeur que moi j'ai mise

Description

InfoTip

mon tooltip à moi !

Le texte affiché par l'infobulle quand la souris survolera l'applet

System.ApplicationName

Developpez.MonApplet

Le nom de votre applet. Essayez d'utiliser la forme des espaces de noms, à savoir Entreprise.NomApplet

System.ControlPanel.Category

1,8

Catégories où apparaitra votre applet

System.Software.TasksFileUrl

c:\infos.xml

Chemin vers un fichier XML de configuration de l'applet. Créez un fichier xml vide sur votre disque et pointez dessus

Pour les catégories, les valeurs possibles sont les suivantes :

Valeur

Catégorie

1

Apparence et personnalisation

2

Matériel et audio

3

Réseau et Internet

4

non utilisé…

5

Système et maintenance

6

Horloge, langue et région

7

Options d'ergonomie

8

Programmes

9

Comptes utilisateurs et protection des utilisateurs

10

Sécurité

11

Ordinateur Mobile (uniquement visible sur les ordinateurs portables)

Vous devriez avoir quelque chose ressemblant à ceci :

I-C - Enregistrer l'applet

Il reste une étape importante pour utiliser l'applet créé. Pour rappel, nous avons déclaré un GUID et rajouté quelques informations, mais rien ne permet à Windows de savoir que ce GUID est un applet. Nous allons donc nous rendre dans le registre à la clé : HKEY_LOCAL_MACHINE\SOFTWARE\Microsoft\Windows\CurrentVersion\Explorer\ControlPanel\NameSpace\ et vous allez rajouter une clé avec comme nom, celui de votre premier GUID. Puis dans sa clé par défaut, vous allez taper le nom de votre applet : « MonPremierApplet ». Avec cette simple ligne, vous indiquez au système d'aller charger les informations de ce GUID qui est censé représenter un applet du panneau de configuration. Si vous vous amusez à indiquer un mauvais ID, votre applet n'apparaitra pas, mais n'empêchera pas le chargement des autres applets.

I-D - L'ajout des tâches

Maintenant que votre applet est visible dans le panneau de configuration (s'il ne l'est pas, recommencez les étapes depuis le début), nous allons le personnaliser. Vous vous rappelez que nous avons créé un fichier XML vide nommé infos.xml. Ouvrez-le et collez le code suivant :

<?xml version="1.0" ?>
<applications xmlns="http://schemas.microsoft.com/windows/cpltasks/v1"
xmlns:sh="http://schemas.microsoft.com/windows/tasks/v1">

<application id="{00000000-0000-0000-0000-000000000000}">   <!-- id de votre applet -->
</application>

</applications>

À l'intérieur de celui-ci, vous allez décrire trois tâches (le nombre importe peu, mais évitez d'en mettre plus de quatre ou cinq pour des raisons d'esthétisme. Chaque tâche ressemblera à ceci :

<sh:task id="{00000000-0000-0000-0000-000000000001}">
<sh:name>Lancer Visual Studio</sh:name>
<sh:keywords>visual;studio;developpement;dev</sh:keywords>
<sh:command>C:\Program Files\Microsoft Visual Studio 9.0\Common7\IDE\devenv.exe</sh:command>
</sh:task>

Faites donc de même pour créer deux tâches de plus, l'une lançant une page web et l'autre un fichier de votre disque dur.

<sh:task id="{00000000-0000-0000-0000-000000000002}">
<sh:name>Visiter Developpez.com</sh:name>
<sh:keywords>developpez;lgmorand;</sh:keywords>
<sh:command>http://www.developpez.com</sh:command>
</sh:task>
<sh:task id="{00000000-0000-0000-0000-000000000003}">
<sh:name>Ouvrir un fichier</sh:name>
<sh:keywords>fichier;aide;</sh:keywords>
<sh:command>C:\test.txt</sh:command>
</sh:task>

Dans le registre, nous avons précisé que l'applet, apparaitrait dans deux catégories (la 1 et la 8), nous allons alors créer une catégorie dans le XML tel que suit :

<category id="1">
<sh:task idref="{00000000-0000-0000-0000-000000000001}"/>
<sh:task idref="{00000000-0000-0000-0000-000000000002}"/>
<sh:task idref="{00000000-0000-0000-0000-000000000003}"/>
</category>
<category id="8">
<sh:task idref="{00000000-0000-0000-0000-000000000003}"/>
<sh:task idref="{00000000-0000-0000-0000-000000000002}"/>
</category>

Vous noterez que l'une n'a que deux tâches, et surtout elle ne les a pas dans le même ordre. Ceci a pour but de permettre de créer UN SEUL applet, mais qui sera affiché sous différentes formes et affichant différentes tâches selon la catégorie dans laquelle il est chargé.

Voici le résultat final :

I-E - Pour aller un peu plus loin

I-E-1 - Changer l'icône de l'applet

Il est possible de configurer l'icône de l'applet qui apparaitra. Dans le registre, dans CLSID, sous la clé de votre applet, rajoutez la clé DefaultIcon et dans sa sous-clé par défaut, tapez le chemin d'une image PNG.
C'est tout !

I-E-2 - Faire apparaitre le bouclier de l'UAC à côté des tâches critiques

Les recommandations de Microsoft pour le développement sur Windows Vista requièrent qu'une icône particulière apparaisse à côté des applications lançant une tâche nécessitant une élévation de privilèges.
Pour cela, rien de bien compliqué, il suffit de rajouter l'attribut needsElevation sur la tâche de votre choix

<sh:task id="{00000000-0000-0000-0000-000000000001}" needsElevation="true">

I-E-3 - Lancer une application à partir du titre de l'applet

Nous avons vu comment ajouter des tâches et lancer des applications à partir de celle-ci. Nous allons voir qu'il est possible de lancer une application depuis le titre de l'applet ou si vous préférez, avoir un applet fonctionnel, sans avoir à déclarer des tâches.
Pour cela, rendez-vous dans le registre dans CLSID/votre_guid, puis ajoutez une sous-clé Shell contenant une sous-clé Open et enfin une sous-clé Command, dont la valeur par défaut contiendra le chemin vers l'application que vous souhaitez lancer. Exemple :

I-F - Conclusion et liens complémentaires

Vous avez donc vu, comment très facilement, il était possible au sein d'une entreprise ou même simplement chez vous, de personnaliser.

Si vous voulez creuser un peu plus sur la création d'éléments du panneau de configuration, je vous conseille les deux liens suivants :
The Windows Vista and Windows Server 2008 Developer Story: Developing for Control Panel
Guidelines about Control Panels

II - Amélioration des interfaces graphiques

Pour continuer sur la lancée, je finis les articles sur le développement sur Windows Vista par un chapitre sur la création de contrôles « à la Vista ». Nous allons donc voir comment créer ces contrôles. Il n'y a bien sûr aucune magie là-dessous et il n'est pas question de recoder un contrôle de zéro, mais simplement de vous présenter des API non ou peu documentées que vous pourriez trouver en fouillant allègrement la MSDN comme ici.

II-A - La textBox améliorée

Améliorer une textBox ? Que peut-on rajouter à un simple champ de saisie ? Un filtre ? Le framework 2.0 contient déjà les MaskedEditBox. Pourquoi pas un message d'aide à la saisie.

Il s'agirait d'un texte qui serait affiché dans la textbox tant que l'utilisateur n'a pas saisi de texte. Cela peut se faire en envoyant un message particulier à une textbox ainsi que le texte à afficher.

Voici ce à quoi ça ressemblerait :

Et bien les API systèmes le permettent, notamment grâce à l'envoi d'un message particulier au contrôle : le message EM_SETCUEBANNER. L'implémentation n'est pas complexe, il suffit d'envoyer ce message ainsi que le texte à afficher, au contrôle que l'on souhaite voir afficher le texte.

Voici un custom control pour lequel vous pouvez en mode design, définir le texte à afficher :

Contrôle MaTextBox

public class MaTextbox : TextBox
{
[DllImport("user32.dll", CharSet = CharSet.Auto)]
private static extern Int32 SendMessage(IntPtr hWnd, int msg,
int wParam, [MarshalAs(UnmanagedType.LPWStr)] string lParam);

private const int EM_SETCUEBANNER = 0x1501;
private string _cueText = String.Empty;

[Category("Custom"),
Description("Texte affiché comme conseil"),
]
public string CueText
{
get { return _cueText; }
set
{
_cueText = value;

SendMessage(this.Handle, EM_SETCUEBANNER, 0, _cueText);
}
}
}

II-B - Le SplitButton

Le SplitButton est un nouveau contrôle apparu sous Windows Vista qui permet de mixer un bouton avec un contextMenu. Le bouton ne permet pas simplement d'afficher un contextMenu lorsqu'on clique dessus puisque cela, nous pouvions déjà le faire avec un bouton normal. Non, il s'agit d'un bouton « normal » proposant également une petite flèche permettant d'afficher un menu contextuel.

Là encore, tout n'est question que d'envoyer un message (BS_SPLITBUTTON) et d'utiliser un second (BCM_SETDROPDOWNSTATE). Néanmoins, si faire apparaitre un SplitButton est facile, pour le rendre parfaitement fonctionnel, et utiliser un contextmenu (pas un contextmenustrip), cela devient une autre paire de manches. Plutôt que réinventer la roue, je vous donne ici une classe que j'ai repris d'un certain Nicholas Kwan et que j'ai corrigée (même s'il reste des bugs) :

Classe SplitButton

using System;
using System.Drawing;
using System.Runtime.InteropServices;
using System.Windows.Forms;

/*************************
*  modifié depuis un contrôle fait par Nicholas Kwan
*  http://www.tevine.com
* *********************/

namespace developpez.com.vista.controls
{ //This control now supports dropdowns.
public delegate void DropDownClicked();

public class SplitButton : Button
{
[DllImport("user32.dll", CharSet = CharSet.Unicode)]
private static extern IntPtr SendMessage(HandleRef hWnd, UInt32 Msg, IntPtr wParam, string lParam);
[DllImport("user32.dll", CharSet = CharSet.Auto)]
public static extern IntPtr SendMessage
(IntPtr hWnd, int msg, int wParam, int lParam);

public const int BCM_FIRST = 0x00001600;
public const int BCM_SETDROPDOWNSTATE = BCM_FIRST + 0x0006;
public const int BS_SPLITBUTTON = 0x0000000C;
public const int WM_KILLFOCUS = 0x08;
public const int WM_LBUTTONDOWN = 0x201;
public const int WM_LBUTTONUP = 0x202;
public const int WM_MOUSELEAVE = 0x2A3;
public const int WM_PAINT = 0x0F;
public const int WM_USER = 0x400;

private ContextMenu ddm_ = new ContextMenu();
public int dropdownpushed;

public int isatdropdown;
public int isbumped;
public int ismousedown;

public SplitButton()
{
FlatStyle = FlatStyle.System;
DropDown_Clicked += launchmenu;
ddm_.Collapse += CloseMenuDropdown;
}

//Set the theme
protected override CreateParams CreateParams
{
get
{
CreateParams cParams = base.CreateParams;
cParams.Style |= BS_SPLITBUTTON;
return cParams;
}
}

public ContextMenu Dropdownmenu
{
get { return ddm_; }
set
{
ddm_ = value;
this.ddm_.Collapse += new EventHandler(this.CloseMenuDropdown);
}
}

protected override void WndProc(ref Message m)
{
// Listen for operating system messages; Filter out a lot of messages here
//The dropdown glyph changes:
//Mousedown (at dropdown area) => Dropdown Event fired => Glyph changes => MouseUp => Glyph Changes
switch (m.Msg)
{
case (BCM_SETDROPDOWNSTATE):
//PROBLEM: other buttons also have would encounter this message
if (m.HWnd == Handle)
{
////This message seems to occur when the drop down is clicked;
////Occures several times, but one of them have the value of 1 in WParams
if (m.WParam.ToString() == "1")
{
if (dropdownpushed == 0)
{
//One time-initiation per mouse press
dropdownpushed = 1;
DropDown_Clicked();
}
}
if (ismousedown == 1)
{
isatdropdown = 1;
}
}
break;
case (WM_PAINT):
//Paints the control to have the dropdown when needed
if (dropdownpushed == 1)
{
SetDropDownState(1);
}
break;
case (WM_LBUTTONDOWN):
ismousedown = 1;
break;
case (WM_MOUSELEAVE):
if (isatdropdown == 1)
{
SetDropDownState(0);
isatdropdown = 0;
ismousedown = 0;
}
break;
case (WM_LBUTTONUP):
if (isatdropdown == 1)
{
SetDropDownState(0);
isatdropdown = 0;
ismousedown = 0;
}
break;
case (WM_KILLFOCUS):
if (isatdropdown == 1)
{
SetDropDownState(0);
isatdropdown = 0;
ismousedown = 0;
}
break;
}
base.WndProc(ref m);
}

public void SetDropDownState(int Pushed)
{
if (Pushed == 0)
{
dropdownpushed = 0; //Removes dropdown pushed state
}
SendMessage(this.Handle, BCM_SETDROPDOWNSTATE, Pushed, 0);
}

public event DropDownClicked DropDown_Clicked;
//The event which would be fired whenever the drop-down is clicked on.

private void InitializeComponent()
{
SuspendLayout();
ResumeLayout(false);
}

public void launchmenu()
{
if (ddm_.MenuItems.Count != 0)
{
ddm_.Show(this, new Point(Width, Height), LeftRightAlignment.Left);
}
}

public void CloseMenuDropdown(object sender, EventArgs e)
{
//Hmm... contextmenu "collapse" state doesn't seem to fire up when a menuitem is clicked. Does not work here yet.
SetDropDownState(0);
}
}
}

Voici le résultat obtenu :

II-C - Sélection semi-transparente sur les listViews

Certains ne le savent peut-être pas, mais l'explorateur Windows n'est principalement composé que d'une listView améliorée dans laquelle apparaissent les fichiers et les dossiers de votre disque dur. Lorsque vous sélectionnez des fichiers, un carré semi-transparent bleu apparait. Nous allons voir qu'il est très simple d'inclure cette sélection au sein de nos listViews.

Il suffit pour cela d'envoyer simplement le code du double-buffering LVS_EX_DOUBLEBUFFER, qui sert normalement à éviter le scintillement des contrôles.

const int LVS_EX_DOUBLEBUFFER = 0x00010000;
SendMessage(listView1.Handle, 0x1000 + 54, LVS_EX_DOUBLEBUFFER, LVS_EX_DOUBLEBUFFER);

Conclusion

Et voilà que se termine déjà la troisième partie du développement d'applications sous Windows Vista. Il se peut fort que ce soit le dernier de la série. En effet, depuis octobre, je tourne exclusivement sous Windows Seven et celui-ci permet de nouvelles choses encore plus intéressantes. Il est donc fort possible que je m'oriente vers la réécriture plus complète et plus structurée du développement d'applications, mais cette fois-ci, appliqué au futur système d'exploitation de Microsoft.

III - Remerciements

Je tiens à remercier ram-0000 pour ses corrections et conseils apportés à l'article.