Développement d'un add-in pour Microsoft Office avec .Net
Date de publication : 15/04/2005 , Date de mise à jour : 15/04/2005
Par
MORAND Louis-Guillaume (Page perso de Louis-Guillaume MORAND)
Tutoriel pas à pas expliquant le principe, le développement et l'intégration d'un add-in dans Microsoft Office
Avant-propos
1. Création du projet
1.1. Introduction
1.2. Le projet de complément office
2. Fonctionnement d'un add-in
2.1. Structure d'un add-in
2.2. Intégration dans Microsoft Office
2.3. Interaction avec Microsoft Office
3. Exemples concrets d'interactions
3.1. L'environnement
3.2. Les documents
3.3. Le contenu
4. Implémentation avancée
4.1. La barre d'outils
4.2. Le bouton standard
4.3. La combobox
4.4. Le bouton menu
5. Déploiement de l'add-in
5.1. Préparation du setup
5.2. Configuration du setup
5.3. Déploiement manuel
Conclusion
Remerciements
Téléchargements
Avant-propos
Cet article a été réalisé à l'aide de Microsoft Visual Studio .Net 2003, framework 1.1 et Microsoft Office 2003.
Cet article s'adresse aux personnes souhaitant ajouter des fonctionnalités à leurs applications Microsoft Office. Il est donc préférable d'avoir des bonnes bases en C# pour développer le code "fonctionnel".
1. Création du projet
1.1. Introduction
Un add-in est une partie complémentaire d'un logiciel de base, il peut être sous la forme d'un menu principal, un menu contextuel ou même une fonction d'arrière plan. Ici, nous verrons le fonctionnement et la création d'un complément s'intégrant à Microsoft Office, puis nous verrons plus exemples concrets d'interactions avec des applications Office.
Vous saurez donc à la fin de ce cours, comment intégrer un add-in et interagir avec les produits Microsoft Office.
1.2. Le projet de complément office
Lancez Visual Studio .Net puis cliquez sur le menu Fichier > Nouveau > Projet.
Dans la fenêtre qui s'ouvre, choisissez alors Autres Projets > Projets d'Extensibilité puis un complément partagé
Une fenêtre de présentation rapide apparaît, suivie d'une fenêtre vous demandant le language à utiliser. Dans notre cas, nous utiliserons du C#.
Sur la fenêtre suivante, vous devez choisir les applications dans lesquelles sera intégré votre add-in. Pour des soucis de compatibilité (fonctions de votre code spécifiques à telle ou telle application), veillez bien à ne choisir que les applications désirées. Dans cet article, nous verrons comme exemple la création d'un add-in pour Microsoft Word. Nous ne cochons donc que Microsoft Word dans la liste..
La fenêtre suivante vous demande de renseigner un nom pour votre add-in ainsi qu'une description succinte de celui-ci. Ce n'est pas forcément le nom sous lequel il apparaîtra en fonction du type d'implémentation que vous ferez mais essayez que choisir un nom correct, qui convienne à votre add-in dans le cas où ce nom serait visible. Faites de même pour la description.
La fenêtre suivante propose deux options de votre add-in:
La première sert à charger votre add-in automatiquement au chargement de l'application hôte (word, excel, outlook, etc). C'est l'option que vous cocherez dans la plupart des cas.
La deuxième option permet de définir l'accessibilité de votre add-in: soit il ne sera utilisable que par l'utilisateur l'installant, soit par tous les utilisateurs de l'ordinateur sur lequel il est installé.
Apparaît enfin la fenêtre finale, résumant les options choisies pour votre add-in. Si les options vous conviennent, cliquez sur "Terminer" pour que Visual Studio .Net autogénère le projet d'add-in.
Si vous regardez l'explorateur de solutions, vous remarquerez que Visual Studio s'est chargé de générer le projet de votre add-in, ainsi que les classes nécessaires à son fonctionnement, mais également un projet de déploiement préconfiguré pour votre add-in, ce qui vous permettra de redistribuer rapidement et facilement votre add-in sur d'autres ordinateurs.
2. Fonctionnement d'un add-in
Avant d'aller plus loin dans le développement de notre add-in, il est important de comprendre sa structure, tant au niveau de l'arborescence des fichiers et de la nomenclature de ceux-ci, que du code structuré (lui aussi possédant une nomenclature).
2.1. Structure d'un add-in
Regardons tout d'abord la structure du projet:

Notons tout d'abord le fichier assembly.cs qui contient l'assembly de notre projet et qu'il nous faudra remplir (nom, description, version) par la suite. Notons ensuite le très important fichier connect.cs. Ce fichier est obligatoire, il sert en quelque sorte de super constructeur: c'est la classe qui est appelée en premier lieu lors du chargement de l'add-in. Il est indispensable de ne pas renommer la classe qu'il contient et c'est dans cette derniere que ce retrouvera une grosse partie du code fonctionnel, à moins que vous ne préfériez (en fonction de la taille de l'add-in), séparer les fonctions, dans diverses classes.
Quant au projet de déploiement autogénéré par Visual Studio Projet, il inclut les dé pendances nécessaires d'extensibilité (add-in) ainsi que les dépendances nécéssaires pour fonctionner avec Office.
Ouvrons maintenant le fichier connect.cs:
Tout d'abord, le namespace. Même s'il est créé automatiquement, il est important de ne pas le modifier : le modifier risquerait d'entraîner des problèmes d'accessibilité avec d'autres classes.
Puis l'utilisation d'autres namespaces permettant d'appeler des fonctions.
using System;
using Microsoft.Office.Core;
using Extensibility;
using System.Runtime.InteropServices;
|
Puis vient un mini-readme qui explique que pour de certains cas, l'intégration de l'add-in dans l'application hôte peut ne plus se faire correctement (bug reconnu :D). Le premier reflexe serait de le supprimer, néanmoins je vous conseille de le garder, au moins pour vous rappeler qu'il vous suffit d'exécuter le fichier .reg qui se trouve dans le dossier du projet pour "réparer" l'add-in.
#region Read me for add-in installation and setup information.
#endregion
|
Voici ensuite notre classe principale intégrant des interfaces spécifiques à l'interaction: Extensibility.IDTExtensibility2, IDTCommandTarget. Notez aussi l'attribution automatique d'un GUID (unique) à notre add-in.
[GuidAttribute("66CC1D78-2502-4794-A789-2238BD7586D7"), ProgId("MonAddinOffice.Connect")]
public class Connect : Object, Extensibility.IDTExtensibility2, IDTCommandTarget
|
Nous avons ensuite le constructeur. C'est dans celui-ci que nous pouvons mettre notre code d'initialisation (que nous verrons par la suite)
Celui-ci est vide mais comme tout constructeur il est forcément appelé et sert à mettre votre code d'initialisation.
Nous allons maintenant voir ce qu'il lui permet de s'intégrer dans une application Office.
Un add-in utilise l'interface IDTExtensibility2 qui fournit 5 évènements de base et qui correspondent aux évènements courants d'un add-in. Ces méthodes permettent à un programme de jouer le rôle de complément en répondant à des conditions de démarrage et d'arrêt de l'environnement, des évènements de connexion de complément, etc.
L'évènement OnConnection est déclenché lorsque l'add-in est chargé (connecté). Un add-in se charge dans différents cas:
- l'utilisateur charge l'add-in via la boite de dialogue des add-ins
- l'add-in est chargé en même temps que l'applicaton hôte, lorsqu'il a été configuré pour démarrer avec celle-ci
- la propriété Connect d'un objet COMAddIn correspondant est réglé sur : True. L'objet COMAddIn représente un add-in COM dans l'application Office
| méthode OnConnection |
public void OnConnection(object application, Extensibility.ext_ConnectMode connectMode, object addInInst,
ref System.Array custom)
{
}
|
L'évènement OnDisconnection est déclenché lorsque l'add-in est déchargé. Il peut l'être de deux manières.
- l'utilisateur le décharge via la fenêtre des add-ins
- l'application hôte est fermée, donc l'add-in aussi par la même occasion
Astuce: Si vous souhaitez que les actions, exécutées par l'add-in dans l'application hôte, soient annulées pour le prochain démarrage (recherche, autre), vous pouvez mettre le code de nettoyage dans cette méthode.
| méthode OnDisconnection |
public void OnDisconnection(Extensibility.ext_DisconnectMode disconnectMode, ref System.Array custom)
{
}
|
L'évènement OnAddinsUpdate est déclenché lorsque la liste du Gestionnaire de compléments est modifiée; lorsqu'un add-in est chargé ou déchargé.
| méthode OnAddInsUpdate |
public void OnAddInsUpdate(ref System.Array custom)
{
}
|
L'évènement OnStartupComplete est déclenché lorsque l'add-in et l'application hôte ont fini de charger toutes leurs routines de démarrage. Si l'application est chargée complètement mais si l'add-in n'est pas chargé au démarrage alors cet évènement ne s'effectue pas, même si par la suite, l'add-in est chargé via la boite de dialogue des add-ins.
Vous pouvez utiliser l'évènement OnStartupComplete pour interagir avec l'application. Par exemple, proposez des choix à l'utilisateur au démarrage de l'application hôte pour créer différents documents.
| méthode OnStartupComplete |
public void OnStartupComplete(ref System.Array custom)
{
}
|
L'évènement OnBeginShutdown est déclenché lorsque l'application hôte lance sa routine d'extinction. Cet évènement est utile pour agir avant que l'application ne se ferme (autosave par exemple).
| méthode OnBeginShutdown |
public void OnBeginShutdown(ref System.Array custom)
{
}
|
2.2. Intégration dans Microsoft Office
Un add-in est un petit programme intégré dans une application hôte dans laquelle il peut s'intégrer de 2 manières.
La première, invisible, peut-être une méthode qui se lance en arrière-plan et à chaque démarrage de l'application hôte.
La deuxième, visible, peut se faire en ajoutant un menu, un menu contextuel, un bouton ou une barre d'outils.
Pour un add-in qui tourne en arrière plan, il suffit simplement d'ajouter le code fonctionnel dans la méthode OnConnection et ainsi l'add-in sera lancé (selon vos options) avec l'application hôte et débutera directement son fonctionnement.
Mais pour des raisons fonctionnelles et comme nous le verrons les chapitres suivants, il peut être interessant ou tout simplement nécessaire d'ajouter des menus (ou boutons) dans l'environnement de l'application hôte afin de récupérer les choix de l'utilisateur.
Nous allons maintenant voir comment appeler notre add-in depuis l'application hôte. Pour notre exemple, nous créerons un bouton dans la barre d'outils de Microsoft Word qui nous permettra d'appeler nos méthodes.
Tout d'abord, déclarons le bouton sur lequel nous baserons notre add-in.
private CommandBarButton MonBouton;
|
Déclarons ensuite deux variables, l'une représentant les barres d'outils de l'application hôte, la seconde représentant la barre d'outils dans laquelle nous insérerons notre bouton.
CommandBars oCommandBars;
CommandBar oStandardBar;
|
Puis nous les instancions:
oCommandBars = (CommandBars)applicationObject.GetType().InvokeMember("CommandBars",
BindingFlags.GetProperty ,
null,
applicationObject,
null);
oStandardBar = oCommandBars["Standard"];
|
Maintenant que nous avons notre barre d'outils, nous allons y ajouter notre bouton. Pour éviter d'avoir une multitude de boutons, nous tenterons de récupérer notre bouton et s'il n'est pas présent (première utilisation, supprimé par l'utilisateur,etc) alors nous en créerons un nouveau.
try
{
MonBouton = (CommandBarButton)oStandardBar.Controls["Code Coloration"];
}
catch(Exception)
{
object omissing = Missing.Value ;
MonBouton = (CommandBarButton) oStandardBar.Controls.Add(1, omissing , omissing , omissing , omissing);
MonBouton.Caption = "Code Coloration";
MonBouton.Style = MsoButtonStyle.msoButtonCaption;
MonBouton.Tag = "Coloration syntaxique du code";
MonBouton.OnAction = "!<MyCOMAddin.Connect>";
MonBouton.Visible = true;
MonBouton.Click += new _CommandBarButtonEvents_ClickEventHandler(this.MyButton_Click);
}
|
Néanmoins cette méthode, propre, peut entraîner des problèmes, notamment pendant le développement de l'add-in. Il est en effet conseillé de supprimer le bouton existant et de le recréer à l'identique. En effet, si le bouton était présent, il en serait encore à la première version (installée) et chaque nouvelle version de l'add-in ne s'installerait pas correctement.
Il est maintenant nécessaire d'attacher une méthode à notre bouton, mais ne pouvant utiliser le générateur automatique de méthodes de Visual Studio, il nous faut l'écrire nous même avec les bons paramètres.
| Méthode MyButton_Click |
private void MyButton_Click(CommandBarButton cmdBarbutton,ref bool cancel)
{
}
|
C'est dans cette dernière méthode que vous devrez mettre votre code fonctionnel. Soit l'ouverture d'une fenêtre, soit une action sur l'environnement de l'application hôte (créer un document, éditer le texte, etc)
2.3. Interaction avec Microsoft Office
Un add-in est une application qui peut agir en autonome comme n'importe quelle application .Net ou alors, elle peut interagir avec son application hôte: but premier d'un add-in :)
Pour cela, l'add-in peut "travailler" sur des instances représentant l'add-in lui même et l'application hôte.
Cette interaction nécessite une et une seule ligne de code:
Word._Application app=(Word._Application)applicationObject;
|
Cette ligne de code récupère une instance de l'application dans lequelle notre add-in est lancé. Pour obtenir des méthodes spécifiques à chaque application office possible, il est nécessaire de convertir (cast) explicitement le type d'application hôte. Il est alors possible à partir de cet objet, de piloter l'application hôte.
Vous pouvez entre autres, ouvrir un nouveau document, éditer le ou les documents ouverts, ouvrir de nouvelles fenêtres, quitter l'application, modifier les options de l'application, etc.
Note importante: si vous cherchez sur le net (MSDN ou forums), vous trouvez régulièrement des personnes utilisant une autre ligne de code:
Word.ApplicationClass applicationObject = (Word.ApplicationClass)application;
|
Cette ligne récupère également une instance de l'application hôte et permet d'appeler chaque méthode de cette instance, néanmoins aucune de ses méthodes ne marchera. Cela viendrait du fait que ApplicationClass crée une instance "neuve", utilisée pour faire de l'automation classique.
3. Exemples concrets d'interactions
Il n'est pas possible pour moi de montrer toutes les actions et/ou interactions qu'il est possible d'éxécuter dans le cadre d'un add-in office, c'est pourquoi je présenterai dans cette partie, différents cas pratiques que vous pourriez rencontrer durant vos développements.
3.1. L'environnement
Il est tout d'abord possible de modifier l'environnement de travail de différentes manières. La première façon est de modifier les options comme le montre le code suivant. Celui-ci modifie les options d'impression:
| Réglage des paramètres d'application |
app.Options.CommentsColor=Word.WdColorIndex.wdBlue;
app.Options.PrintBackground=true;
app.Options.PrintProperties=true;
|
Il est également possible d'ouvrir les fenêtres de notre choix puis laisser l'utilisateur modifier les paramètres:
| Ouverture de la fenêtre des options d'impression |
object hop=10000000;
app.Dialogs[Word.WdWordDialog.wdDialogToolsOptionsPrint].Show(ref hop);
|
Ou encore, ouvrir une deuxième instance de l'application hôte
3.2. Les documents
Depuis notre add-in, il est également possible de piloter les documents inclus dans l'application hôte.
Vous pouvez par exemple ouvrir un nouveau document
| Nouveau document |
app.Documents.Add(ref missingValue, ref missingValue,ref missingValue, ref missingValue);
|
Ou ouvrir un document existant:
| Ouverture d'un document |
object fileName=@"c:\monfichier.doc";
pp.Documents.Open(ref fileName, ref missingValue, ref missingValue, ref missingValue, ref missingValue,
ref missingValue, ref missingValue, ref missingValue,ref missingValue, ref missingValue, ref missingValue,
ref missingValue, ref missingValue, ref missingValue, ref missingValue, ref missingValue);
|
Vous pouvez alors, via votre add-in, travailler sur ce document et y sauvegarder les changements:
| Sauvegarde du document |
app.ActiveDocument.Save();
|
3.3. Le contenu
Il peut être intéressant d'ouvrir des documents, les sauvegarder ou autre, mais à la seule condition que l'on puisse également les modifier.
Voyons tout d'abord comment insérer du texte dans un document.
| insertion de statistiques sur le document |
object missing = Missing.Value;
app.Selection.TypeText("il y a "+monDoc.ComputeStatistics(Word.WdStatistic.wdStatisticPages,ref missing)
+" pages dans votre documents"
+ " contenant "+monDoc.ComputeStatistics(Word.WdStatistic.wdStatisticWords,ref missing)+ " mots faits de "
+ monDoc.ComputeStatistics(Word.WdStatistic.wdStatisticCharacters,ref missing)+" caractères");
|
Il est également possible de faire des insertions évoluées comme des paragraphes contenant un formatage spécifique (taille, couleur):
| Insertion d'un paragraphe |
object missing = Missing.Value;
Word.Document monDoc= app.ActiveDocument;
Word.Paragraph monPara = monDoc.Content.Paragraphs.Add(ref missing);
monPara.Range.Text="J'insère un paragraphe contenant deux lignes";
monPara.Range.Font.Size=20;
monPara.Range.Font.Color= Word.WdColor.wdColorBlue;
monPara.Range.InsertParagraphAfter();
|
Voyons pour finir le cas d'un mini colorateur syntaxique:
| Mini colorateur syntaxique |
object missingValue = Missing.Value;
Word.Document monDoc= app.ActiveDocument;
string[] keyword={"string","=","(",")"};
for(int i=0;i < keyword.Length;i++)
{
Word.Range monRange = app.Selection.Range;
monRange.Find.Text = keyword[i];
monRange.Find.ClearFormatting();
monRange.Find.Forward=true;
monRange.Find.Execute(ref missingValue, ref missingValue,
ref missingValue, ref missingValue, ref missingValue,
ref missingValue, ref missingValue, ref missingValue,
ref missingValue, ref missingValue, ref missingValue,
ref missingValue, ref missingValue, ref missingValue,
ref missingValue);
while (monRange.Find.Found)
{
if(monRange.Find.Text=="string")
{
monRange.Font.Bold=1;
monRange.Font.Color=Word.WdColor.wdColorBlue;
}
if(monRange.Find.Text=="=" ||monRange.Find.Text=="(" || monRange.Find.Text==")")
{
monRange.Font.Color=Word.WdColor.wdColorRed;
}
monRange.Find.Execute(ref missingValue, ref missingValue,
ref missingValue, ref missingValue, ref missingValue,
ref missingValue, ref missingValue, ref missingValue,
ref missingValue, ref missingValue, ref missingValue,
ref missingValue, ref missingValue, ref missingValue,
ref missingValue);
}
}
|
Ce dernier prend chaque mot clé de la liste et recherche dans la sélection, chaque occurence de celui-ci et le retrouve. Le "finder" de mon range, originellement égal à ma selection, correspond maintenant aux "coordonnées" du mot trouvé. Il est alors possible d'éditer la police de notre range.
4. Implémentation avancée
4.1. La barre d'outils
Comme nous l'avons vu précédemment (chapitre 2.2), la manière la plus simple de s'intégrer dans l'application hôte est d'ajouter un bouton dans l'une des barres d'outils existantes. Néanmoins, pour une meilleure visibilité et aussi pour des raisons fonctionnelles si votre add-in nécessite plusieurs boutons, il est possible de créer votre propre barre d'outils. Il est également possible d'y ajouter toutes sortes de contrôles, ce que nous verrons dans les prochains chapitres.
Il est préconisé de créer vos barres d'outils et boutons dans la méthode Connect() ou dans la méthode OnStartupComplete(). Définissez tout d'abord les objets dont vous avez besoin:
CommandBars oCommandBars;
CommandBar oStandardBar;
|
Puis initialisons notre collection de barre d'outils puis ajoutons lui notre barre personnelle:
oCommandBars = (CommandBars)applicationObject.GetType().InvokeMember("CommandBars",
BindingFlags.GetProperty ,
null,
applicationObject,
null);
oStandardBar = oCommandBars.Add("Ma barre d'outils", Microsoft.Office.Core.MsoBarPosition.msoBarTop, omissing, true);
oStandardBar.Visible=true;
|
Dans l'application hôte, votre barre d'outils sera visible par défaut et apparaîtra avec toutes les autres barres d'outils:
4.2. Le bouton standard
Revoyons maintenant comment ajouter un bouton à cette barre d'outils.
Nous déclarons tout d'abord notre bouton:
CommandBarButton MonBouton;
|
puis nous l'instancions en l'ajoutant directement à notre barre d'outils
MonBouton = (CommandBarButton) oStandardBar.Controls.Add(1, omissing , omissing , omissing , omissing);
|
et le paramètrons:
MonBouton.Caption = "Texte affiché";
MonBouton.Style = MsoButtonStyle.msoButtonCaption;
MonBouton.Tag = "Coloration syntaxique du code";
MonBouton.OnAction = "!<MyCOMAddin.Connect>";
MonBouton.Visible = true;
MonBouton.Click += new _CommandBarButtonEvents_ClickEventHandler(this.MonBouton_Click);
|
4.3. La combobox
Il est également possible d'insérer des contrôles un petit peu plus évolués comme la combobox. Son insertion se fait de la même manière que pour le bouton. Nous créeons tout d'abord notre composant:
object objType = MsoControlType.msoControlDropdown;
Macombo = (CommandBarComboBox)oStandardBar.Controls.Add(objType, omissing, omissing, omissing, (object)true);
|
Puis nous configurons notre combobox:
Macombo.Caption = "MonTexte";
Macombo = MsoComboStyle.msoComboLabel;
Macombo.BeginGroup=true;
Macombo.AddItem("item1",1);
Macombo.AddItem("item2",2);
Macombo.AddItem("item3",3);
Macombo.AddItem("item4",4);
Macombo.ListIndex = 1;
|