Introduction
Dans le dernier
article, nous avons terminé notre moteur de recherche. Nous allons
maintenant réaliser une application radicalement différente mais toujours
dédiée TabletPC et utilisant la SDK déjà
présentée. Ceci sera l’occasion d’aller plus avant dans la programmation
.Net : nous allons nous attaquer à des problèmes d’interopérabilité
COM/.Net, d’interfaces graphiques, d’accès à l’API Windows, de déploiement,
ainsi qu’à une multitude d’obstacles ponctuels, comme la protection de
ressources critiques. Notre objectif est en effet de réaliser une barre
d’outils intégrée à Internet Explorer pour TabletPC. La barre devra permettre
une utilisation la plus naturelle possible.
Voici donc le premier article
d’une nouvelle série consacrée à ce projet dont se dégagent trois parties
essentielles : l’intégration de la barre à Internet Explorer (IE) et
l’installation sur la machine cible, l’IHM et enfin la logique d’arrière plan.
Dans une première partie, nous
allons présenter en détails le projet dont nous vous proposerons
une première version compilée. Ensuite, nous expliquerons
comment réaliser et intégrer une barre d’outils à IE en .Net. Nous
développerons finalement le projet de déploiement.
Présentation du projet
Nous
souhaitons réaliser une barre d’outils utilisable « normalement »
(clavier et souris) ou exclusivement au stylet. Cette barre devra permettre la
navigation sur le web, la recherche de mots via des moteurs de recherche, et la
saisie assistée de formulaires web. Il faudra tenir compte des remarques
concernant l’interface faites dans l’article « Introduction
à la programmation sur Tablet PC ». En outre, la reconnaissance d’urls
manuscrites étant délicate, il faudra le plus possible proposer des raccourcis
à l’utilisateur : historique, concaténation des préfixes courants – http,
www… – et de suffixes de domaines.
Vous n’en
êtes à lire que les premières lignes de ce projet ; cependant, la barre
est, à la rédaction de ce premier article, quasiment finalisée. En voici donc
une copie d’écran qui en dira plus long sur nos objectifs qu’un grand
discours :

Comme vous pouvez le voir, le
premier objectif était de permettre la saisie d’urls : à cet effet, trois
boutons à cocher pour les préfixes, une zone de saisie pour le corps de l’url
et cinq boutons de navigation. La zone de saisie s’agrandit lorsque le stylet
s’approche et un historique apparaît dès le début de l’écriture :

Le second objectif était de
lancer une recherche web sur les mots écrits dans la zone de saisie. C’est ce
que permet le bouton le plus à droite.
Enfin, il fallait faciliter le
remplissage au stylet de formulaires web. Pour cela, un menu contenant les mots
les plus fréquents (vos nom, prénom, adresse mail…) apparaît lorsque vous
cliquez sur le bouton « Paramètres utilisateur ». Cela dispense
l’utilisateur de manœuvres parfois complexes lorsqu’il doit écrire des
adresses, noms ou numéros de téléphone au stylet.

Le tout se devait d’être le plus
configurable possible. Une boîte de dialogue devait permettre l’entrée des
paramètres utilisateurs (un fichier de configuration par profil et non par
machine).
Pour avoir une
idée du résultat final, nous vous proposons de télécharger ici
le fichier d’installation de notre InkToolBar.
Il s’agit d’une première version β compilée à l’heure où nous rédigeons ce
premier article ; de nouvelles versions seront donc certainement proposées
dans les articles suivants. Comme d’habitude, les codes sources seront en
téléchargement. Cependant, les différentes parties du projet ne seront
disponibles qu’au fil des articles ; l’intégralité de la solution et de
son code sera, quant à elle, fournie dans le dernier article.
Entrons maintenant dans le vif du
sujet !
Création d’une ToolBar pour Internet Explorer
La création
d’une barre d’outils pour IE passe par l’implémentation de diverses interfaces
COM et par l’enregistrement de la barre dans la base de registre. Ces
opérations sont décrites à cette adresse.
De nombreux exemples fourmillent sur le web (tapez « BandObject Internet
Explorer » dans votre moteur préféré). Malheureusement, lorsque vous développez
une barre pour IE en .Net, les choses se compliquent un peu et l’aide se
fait plus rare…
Implémentation des interfaces COM
Plusieurs
interfaces sont à implémenter. Il faut tout d’abord les déclarer :
[ComImport]
[InterfaceType(ComInterfaceType.InterfaceIsIUnknown)]
[Guid("FC4801A3-2BA9-11CF-A229-00AA003D7352")]
public interface IObjectWithSite
{
void
SetSite([In ,MarshalAs(UnmanagedType.IUnknown)] Object
pUnkSite);
void
GetSite(ref Guid riid,
[MarshalAs(UnmanagedType.IUnknown)]
out Object ppvSite);
}
Les trois premiers attributs
permettent d’importer l’interface depuis le monde COM : ComImport précise que l’interface est COM, puis les
deux autres attributs indiquent le GUID de l’interface (que l’on peut trouver
dans la base de registre en recherchant le nom de l’interface) et son type. Enfin,
au sein de l’interface, on déclare toutes les méthodes sans oublier
de « marshaller » les paramètres.
Une fois l’interface déclarée,
c’est au développeur d’en réaliser l’implémentation. Pour cela, il faut créer
une classe qui hérite de toutes les interfaces nécessaires. C’est dans cette
classe qu’il faut implémenter chaque méthode des interfaces COM. Nos recherches
préliminaires nous ont permis de trouver un site décrivant toute
l’implémentation C# (http://www.codeproject.com/csharp/dotnetbandobjects.asp).
Vous trouverez ici le projet, fortement inspiré
de celui de Pavel Zolnikov, que nous allons utiliser pour la création de notre
barre d’outils. Le projet contient à l’origine trois fichiers ; nous n’en
conservons que deux, le fichier Attribute.cs
étant inutile dans notre cas, comme nous l’expliquerons plus
loin. Les deux autres fichiers sont : BandObject.cs et ComInterop.cs.
ComInterop déclare toutes les
interfaces COM tel qu’expliqué plus haut. BandObject.cs
contient une classe BandObject qui
dérive de UserControl ainsi que de
toutes les interfaces COM nécessaires.
Nous avons également apporté quelques modifications à ces deux
fichiers. Mis à part la correction d’une erreur de déclaration de constantes
dans le projet de Zolnikov, nous avons modifié les fonctions d’enregistrement.
Nous avons également complété la méthode TranslateAcceleratorIO.
Cette méthode spécifie les messages que IE doit passer à notre barre lorsque
celle-ci a le focus.
public virtual
Int32 TranslateAcceleratorIO(ref MSG msg)
{
int
returnValue = 1; // S_FALSE
if(msg.message
== 0x100)
switch(msg.wParam)
{
case
(uint)Keys.Tab:
case
(uint)Keys.F6:
if(SelectNextControl(ActiveControl,
ModifierKeys ==
Keys.Shift ? false : true, true, true, false))
returnValue = 0; //S_OK
break;
case
(uint)Keys.Back:
case
(uint)Keys.Left:
case
(uint)Keys.Right:
case
(uint)Keys.Home:
case
(uint)Keys.Delete:
case
(uint)Keys.End:
case
(uint)Keys.Shift:
case
(uint)Keys.Up:
case
(uint)Keys.Down:
case
(uint)Keys.Return:
case
(uint)Keys.Escape:
SendMessage((int)this.ActiveControl.Handle,
(int)msg.message, (int)msg.wParam,
(int)msg.lParam);
returnValue = 0; //S_OK
break;
case (uint)Keys.C: // Ce cas est traité pour le Ctrl+C
if((GetKeyState(0x11/*VK_CONTROL*/)
& 0x80) ==
0x80)
// Si la touche contrôle est appuyée
{
// On envoie le message "WM_COPY" au contrôle
actif
SendMessage((int)this.ActiveControl.Handle,
0x301 /*WM_COPY*/, 0, 0);
returnValue = 0; //S_OK
}
break;
default:
break;
}
return returnValue;
}
Nous utilisons ici la méthode SendMessage de l’API Windows qui permet d’envoyer un message à une
fenêtre (en l’occurrence, au contrôle actif de notre barre d’outils). Il faut
donc la déclarer de la manière suivante :
[DllImport("user32.dll",EntryPoint="SendMessage")]
private static
extern int SendMessage(int _WindowHandler, int
_WM_USER,
int
_data, int _id);
Nous traitons également le cas du Ctrl + C (copier) car il semble particulier (les tests montrent
qu’il n’est pas transmis à notre barre si nous ne le déclarons pas
explicitement). Il suffit donc d’envoyer, sur appui simultané des deux touches,
un message de copie au contrôle actif de la barre (ce problème ne se pose pas
pour le Ctrl + V).
Enfin, notez que ce projet nécessite l’ajout d’une référence
COM vers shdocvw.dll. Cette dll
contient en effet la classe WebBrowserClass
qui permet d’instancier le membre Explorer
qui représente l’instance de IE en cours d’exécution (c’est à partir de cet
objet Explorer que l’on peut par
exemple lancer une navigation dans IE avec la méthode Navigate). Cette dll, automatiquement wrappée, est présente dans le projet proposé ici (Interop.shdocvw.dll).
Voici comment on utilise ce projet : on écrit une classe qui dérive de BandObject dans une bibliothèque de
classes .Net (.dll). On lui attribue un GUID : pour cela, ouvrez le menu « Outils »,
cliquez sur « Créer un GUID » :
Ensuite, ajoutez l’attribut GUID à la nouvelle classe :
//
Exposition comme objet COM
[Guid("0AC1CE45-B85B-4675-9A3A-1F15EB4F590E")]
//
Attribut perso servant à passer le nom, le style... de notre
ToolBar
/// <summary>
///
Classe de notre InkToolBar héritant de BandObject
/// </summary>
public class
InkToolBar : BandObject
{
Dans cette classe (qui hérite donc indirectement de UserControl), on réalise l’interface
graphique de la barre ainsi que sa logique. Si l’on souhaite accéder à des
fonctions de IE (comme la navigation sur une page web), il suffit d’appeler des
méthodes de l’objet Explorer (Navigate par exemple).
Après la compilation, il y a normalement plusieurs opérations
à effectuer :
-
l’enregistrement de toutes les dll du projet de la
barre dans le GAC
-
l’enregistrement de la dll principale en tant que
composant COM
-
la modification de la base de registre afin de notifier
IE de la présence d’une nouvelle barre
L’inscription COM de la sortie
principale de notre projet nécessite en effet dans notre cas que l’on place la
dll dans le GAC pour que IE puisse la trouver. A partir de là, on se trouve
obligé de placer également toutes les dll utilisées par cette sortie principale
dans le GAC. Tout cela se faisait dans le projet original de Zolnikov au moment
de la compilation du projet : l’inscription COM ainsi que la copie dans le
GAC étaient configurées dans les actions de post-compilation (disponibles
uniquement pour un projet C++) à l’aide des deux exécutables gacutil et regasm :
|

|
|
Les actions d’après
génération du projet d’exemple d’utilisation de BandObject de Zolnikov
|
La modification de la base de registre se faisait quant à
elle automatiquement lors de l’appel à regasm.
En effet, BandObject contient deux
méthodes qui sont lancées lors des appels à regasm
(enregistrement) ou regasm /u
(désenregistrement) :
/// <summary>
/// Called
when derived class is registered as a COM server.
/// </summary>
[ComRegisterFunctionAttribute]
public
static void
Register(Type t)
{
. . .
et :
/// <summary>
/// Called
when derived class is unregistered as a COM server.
/// </summary>
[ComUnregisterFunctionAttribute]
public
static void
Unregister(Type t)
{
. . .
Tout se déroule donc au moment de la compilation… Nous
allons devoir changer ces méthodes d’enregistrement COM et de modification de
la base de registre pour que l’on puisse déployer notre barre…
Programme d’installation
En
résumé : pour fonctionner correctement, toutes les dll de notre projet
devront être placées dans le GAC. En outre, il faut enregistrer la dll
principale de la solution (celle qui contient la classe dérivant de BandObject et qui implémente toutes les
fonctionnalités de notre barre d’outils) en tant que composant COM. Enfin, il
faut modifier la base de registre pour que IE soit notifié de l’arrivée de
notre nouvelle barre.
Nous allons
donc réaliser un projet de déploiement. Placer toutes les dll de notre solution
dans le GAC de la machine cible n’est pas un problème : le projet de setup permet de choisir le dossier de
destination de chaque assembly sur la machine cible :

N’oubliez pas toutefois de signer chaque assembly (cela
est nécessaire pour copier un assembly dans le GAC) : générez un fichier qui
contient les clés publique et privée à l’aide de l’outil sn.exe, puis ajoutez l’attribut :
[assembly: AssemblyKeyFile(@"..\..\..\nomduficher.snk")]
Cette ligne est généralement présente à la fin du fichier AssemblyInfo.cs.
Nous devons
également enregistrer le composant principal en tant que composant COM sur la
machine cible. Pour cela, la méthode traditionnelle consisterait à exécuter regasm. Cet utilitaire est normalement
utilisé lorsque l’on souhaite inscrire un composant .Net en COM. Pour cela, il
crée les clés nécessaires dans la base de registre.
Cependant, regasm
peut ne pas être installé sur la machine cible (et dans tout les cas, pas dans
un répertoire connu à l’avance). De plus, on s’interdit de déployer regasm sur la machine cible. Il faut
donc enregistrer le composant en composant COM « à la main ».
Pour connaître les clés à ajouter, il suffit d’exécuter regasm une fois (sur la machine de
développement) avec l’option regfile :
regasm nom_de_assembly
/regfile:nom_fichier_sortie
Cela enregistre dans le fichier spécifié (créé au format .reg) les actions effectuées lors de
l’enregistrement d’un composant .Net en composant COM.
En outre, il faut ajouter quelques clés à ce fichier .reg pour spécifier à IE l’existence de
la barre. Ces clés sont ajoutées de la manière suivante :
[HKEY_CLASSES_ROOT\CLSID\{0AC1CE45-B85B-4675-9A3A-1F15EB4F590E}\Implemented
Categories\{62C8FE65-4EBB-45E7-B440-6E39B2CDBF29}]
[HKEY_CLASSES_ROOT\CLSID\{0AC1CE45-B85B-4675-9A3A-1F15EB4F590E}\Implemented
Categories\{00021494-0000-0000-C000-000000000046}]
[HKEY_CLASSES_ROOT\CLSID\{0AC1CE45-B85B-4675-9A3A-1F15EB4F590E}\Implemented
Categories\{00021492-0000-0000-C000-000000000046}]
[HKEY_LOCAL_MACHINE\SOFTWARE\Microsoft\Internet
Explorer\Toolbar]
"{0AC1CE45-B85B-4675-9A3A-1F15EB4F590E}"="InkToolBar"
Tout cela nous dispense, comme dit
plus haut, de la création d’un attribut personnalisé comme dans le projet
original de Zolnikov.
Une fois que vous avez ajouté ces dernières clés, votre
fichier .reg représente entièrement
les actions à effectuer pour l’enregistrement en composant COM. Dans le projet
de setup, il est possible de modifier
la base de registre de la machine cible, soit en ajoutant les clés une à une,
soit en important un fichier .reg.
C’est évidemment pour cette dernière option que nous allons opter :

Toutes les clés nécessaires sont ainsi incorporées au projet
de setup.
Lorsque l’installation aura lieu
sur la machine cible, les clés seront créées dans la base de registre de la
même manière que lors de l’exécution de regasm.
Notez bien toutefois que les clés
enregistrées dans le programme de setup
correspondent à un numéro de version précis de la dll, ainsi qu’à une clé de
signature publique précise pour la dll. Si vous changez le numéro de version,
et/ou la clé publique de la dll, vous devez modifier les clés concernées, soit
à la main, soit en générant à nouveau le fichier .reg comme décrit plus haut (sans oublier d’y ajouter les clés
nécessaires à l’apparition de la barre dans IE).
Conclusion
A ce stade,
nous avons réalisé une barre d’outils générique en .Net. Le programme
d’installation permet de déployer proprement cette barre sur les machines
cibles.
Il faut
maintenant remplir la barre et lui ajouter toutes les fonctionnalités voulues.
Pour cela, nous devons réaliser l’interface la plus ergonomique possible. Nous
allons donc voir dans les prochains articles la création de contrôles dédiés à
notre projet. Ensuite nous verrons les points délicats dans l’implémentation
des fonctionnalités.