Créer une occurrence d'Excel dans la version souhaitée

Tests pratiques en VBnet

Laurent Gaillard

Cet article propose une synthèse récapitulative des méthodes envisagées intuitivement lorsque l'on souhaite créer une occurrence d'Excel dans une version précise. Il s'applique tout autant aux autres logiciels de la suite Office, tels que Word ou PowerPoint.

J'ai écrit cet article dans le but de venir efficacement en aide aux développeurs qui rencontrent ce problème, afin qu'ils évitent de perdre du temps à faire de nombreux tests infructueux. J'espère que ce but sera atteint et que vous y trouverez les réponses recherchées.

Commentez

- Titre : Créer une occurrence d'Excel dans la version souhaitée

- Auteur : Laurent Gaillard

- Parution : 27 février 2015

- Mise à jour : 30 avril 2015

- Public visé :

- Licence : Copyright ® 2015 Laurent Gaillard. Aucune reproduction, même partielle, ne peut être faite de ce site et de l'ensemble de son contenu : textes, documents, images, etc. sans l'autorisation expresse de l'auteur. Sinon vous encourez selon la loi jusqu'à trois ans de prison et jusqu'à 300 000 € de dommages et intérêts.

Sommaire

- I. Introduction

- II. Problématique

- III. Environnement de test

- IV. Tests

- IV-A. Méthode 1.0 New Excel.Application

- IV-B. Méthode 2.0 CreateObject

- IV-C. Méthode 3.0 GetTypeFromProgID/CreateInstance

- IV-D. Méthode 4.0 Shell / GetObject

- IV-E. Méthode 5.0 Shell/Marshal.GetActiveObject

- IV-F. Méthode 6.0 Shell pour automation

- V. Analyse et explication

- V-A. Plusieurs classes d'application pour un même CLSID

- V-B. Une seule version d'Office installée

- V-C. Le bon paramétrage du Shell ?

- VI. Conclusion

- VI-A. Se risquer à la méthode 6.0

- VI-B. Installer une version d'Office par machine virtuelle

- VII. Conseils supplémentaires

- VII-A. Choisir la version des références

- VII-B. Choisir entre liaison anticipée et tardive

- VII-C. Ne jamais coder un chemin en dur

- VIII. Remerciements et références

I - Introduction

Il est très facile d'avoir plusieurs versions de Microsoft Office© installées sur le même poste. Une fois dans cette configuration, quel développeur n'a pas rêvé de pouvoir choisir la version d'Excel ou de Word utilisée dans son programme ?

C'est après avoir trouvé de nombreuses questions, et aussi de nombreuses réponses dans différents forums que j'ai choisi d'écrire cet article pour proposer une synthèse, je l'espère claire, à tous ceux qui se poseront la même question à l'avenir.

II - Problématique

Si vous êtes aujourd'hui en train de lire cet article, ou bien vous êtes en phase de recherche sur le sujet, ou bien vous avez réalisé vos premiers tests… et ça ne fonctionne pas comme vous le souhaitez !

Car pour ne rien vous cacher : non, vous ne pouvez pas choisir simplement la version d'Excel de l'occurrence que vous allez créer avec votre programme en utilisant les syntaxes habituelles. Vous constaterez la plupart du temps que c'est la version la plus récente installée sur votre poste qui est utilisée pour la création d'une nouvelle occurrence.

La série de tests décrits dans cet article nous amènera à la conclusion qu'il est conseillé d'avoir une seule version d'Office installée sur le poste (cf. chapitre V.B), la gestion de plusieurs versions passant en général par l'utilisation de machines virtuelles (cf. chapitre VI.B).

III - Environnement de test

Mes tests sont réalisés sous Windows 7 Édition Familiale Premium SP1 avec la plateforme .NET 4.0.

Mes versions d'Office installées sont Office 2007 et Office 2013.

Tous les extraits de code de l'article seront en VBNet (développés sous Visual Studio Community 2013).

IV - Tests

Sur mon poste, Office 2007 et Office 2013 sont installés. Mon but est de créer une occurrence d'Excel 2007.

IV-A - Méthode 1.0 New Excel.Application

La première méthode consiste à utiliser la classe publique Microsoft.Office.Interop.Excel.Application qui va effectuer une liaison anticipée avec Excel. C'est la syntaxe standard préconisée sous .Net

Méthode 1.0

Dim objMyExcel As Microsoft.Office.Interop.Excel.Application
If objMyExcel Is Nothing Then
objMyExcel = New Microsoft.Office.Interop.Excel.Application
objMyExcel.Visible = True
MsgBox(objMyExcel.name & Space(1) & objMyExcel.version)
End If

Pour utiliser la classe publique Microsoft.Office.Interop.Excel.Application, il faut ajouter une référence COM à la bibliothèque d'objets Excel. Prenons soin de choisir la référence à la bibliothèque 12.0 (qui correspond à la version 2007) et non pas 15.0 (qui correspond à la version 2013).

Résultat : KO ! Bien que l'on choisisse la référence à la version 2007, le programme crée une occurrence d'Excel 2013.

IV-B - Méthode 2.0 CreateObject

Cette fois-ci, on utilise la méthode CreateObject qui va effectuer une liaison tardive avec Excel. CreateObject nous permet de préciser la classe d'application d'Excel : on choisit naturellement "Excel.Application.12" pour cibler la version Excel 2007.

Méthode 2.0

Dim objMyExcel As Object
If objMyExcel Is Nothing Then
'// CreateObject == Creates and returns a reference to a COM object
objMyExcel = Microsoft.VisualBasic.Interaction.CreateObject("Excel.Application.12")
objMyExcel.Visible = True
MsgBox(objMyExcel.name & Space(1) & objMyExcel.version)
End If

Résultat : KO ! Bien que l'on précise la classe d'application d'Excel 2007, le programme crée une occurrence d'Excel 2013.

IV-C - Méthode 3.0 GetTypeFromProgID/CreateInstance

Toujours en liaison tardive, on utilise System.Activator.CreateInstance pour créer l'occurrence, et System.Type.GetTypeFromProgID pour préciser la version d'Excel 2007.

Méthode 3.0

Dim objMyExcel As Object
If objMyExcel Is Nothing Then
'// CreateInstance == Creates an instance of the specified type using that type's default constructor
'// GetTypeFromProgID == Gets the type associated with the specified program identifier (ProgID)
objMyExcel = System.Activator.CreateInstance(System.Type.GetTypeFromProgID("Excel.Application.12"))
objMyExcel.Visible = True
MsgBox(objMyExcel.name & Space(1) & objMyExcel.version)
End If

Résultat : KO ! Bien que l'on précise la classe d'application d'Excel 2007, le programme crée une occurrence d'Excel 2013. La déclaration de objMyExcel en liaison anticipée (As Microsoft.Office.Interop.Excel.Application) ne change rien au résultat.

IV-D - Méthode 4.0 Shell / GetObject

Comme les méthodes 1.0, 2.0 et 3.0 ne permettent pas de créer une occurrence d'Excel de la version voulue, on va lancer l'exécutable de la version d'Excel 2007 et tenter de récupérer l'occurrence.

Méthode 4.0

Dim objMyExcel As Object
If objMyExcel Is Nothing Then
Call Microsoft.VisualBasic.Interaction.Shell("C:\Program Files (x86)\Microsoft Office\Office12\Excel.exe")
'// GetObject == Returns a reference to an object provided by a COM component
objMyExcel = Microsoft.VisualBasic.Interaction.GetObject("C:\Program Files (x86)\Microsoft Office\Office12\Excel.exe")
objMyExcel.Visible = True
MsgBox(objMyExcel.name & Space(1) & objMyExcel.version)
End If

Résultat : KO ! La commande Shell lance bien l'occurrence d'Excel 2007 précisée dans le path, mais le GetObject génère l'erreur « Impossible de créer le composant ActiveX ».

L'utilisation de Microsoft.VisualBasic.Interaction.GetObject(, "Excel.Application.12") ne change rien au résultat.

IV-E - Méthode 5.0 Shell/Marshal.GetActiveObject

Cette méthode est une variante fonctionnelle du GetObject. Comme dans la méthode 4.0, on va lancer l'exécutable de la version d'Excel 2007 et tenter de récupérer l'occurrence.

Méthode 5.0

Dim objMyExcel As Object
If objMyExcel Is Nothing Then
Call Microsoft.VisualBasic.Interaction.Shell("C:\Program Files (x86)\Microsoft Office\Office12\Excel.exe")
objMyExcel = System.Runtime.InteropServices.Marshal.GetActiveObject("Excel.Application.12")
objMyExcel.Visible = True
MsgBox(objMyExcel.name & Space(1) & objMyExcel.version)
End If

Résultat : KO ! La commande Shell lance bien l'occurrence d'Excel 2007 précisée dans le path, mais le GetActiveObject tente de récupérer une occurrence de la version la plus récente installée sur le poste. Si Excel 2013 n'est pas lancé sur le poste, le GetActiveObject génère l'erreur « Opération non disponible ». Si Excel 2013 est lancé sur le poste, le GetActiveObject récupère l'occurrence d'Excel 2013 et pas celle de 2007, bien que l'on précise la classe d'application d'Excel 2007.

IV-F - Méthode 6.0 Shell pour automation

Cette méthode consiste à paramétrer l'appel au Shell avec un suffixe particulier qui permet au GetObject de récupérer l'occurrence.

Méthode 6.0

Dim objMyExcel As Object
If objMyExcel Is Nothing Then
'// Shell == Runs an executable program and returns an integer containing the program's process ID if it is still running
Call Microsoft.VisualBasic.Interaction.Shell("C:\Program Files (x86)\Microsoft Office\Office12\Excel.exe" & " /automation -Embedding")
objMyExcel = Microsoft.VisualBasic.Interaction.GetObject(, "Excel.Application")
objMyExcel.Visible = True
MsgBox(objMyExcel.name & Space(1) & objMyExcel.version)
End If

Résultat : OK ! On récupère bien l'occurrence d'Excel 2007, sans même avoir précisé la classe d'application d'Excel 2007. Avec ce paramétrage, l'utilisation de Marshall.GetActiveObject fonctionne également. Enfin, on peut l'utiliser en déclarant une liaison anticipée pour objMyExcel.

V - Analyse et explication

V-A - Plusieurs classes d'application pour un même CLSID

Chaque version d'Excel possède sa propre classe d'application

Version d'Office

Classe d'application d'Excel

7.0

Excel.Application.5

97

Excel.Application.8

2000

Excel.Application.9

2003

Excel.Application.11

2007

Excel.Application.12

2010

Excel.Application.14

2013

Excel.Application.15

Cependant, toutes les versions ont le même identifiant de classe (CLSID). C'est la dernière version installée qui contient le chemin d'accès au serveur Automation.

C'est pourquoi, sur un poste où l'on a installé Office 2007, puis Office 2013 :

- New Microsoft.Office.Interop.Excel.Application crée une occurrence d'Excel 2013 (Méthode 1.0) ;

- CreateObject("Excel.Application"), CreateObject("Excel.Application.12") ou CreateObject("Excel.Application.15") crée toujours une occurrence d'Excel 2013 (Méthode 2.0) ;

- GetTypeFromProgID("Excel.Application.12") donne le type Excel indépendamment de la version (Méthode 3.0) ;

- Marshal.GetActiveObject("Excel.Application.12") essaye toujours de récupérer une occurrence d'Excel 2013 (Méthode 5.0).

V-B - Une seule version d'Office installée

Une solution raisonnable semble donc d'avoir une seule version d'office installée sur le poste.

Notez que, comme le précise Microsoft, il est déconseillé d'installer plusieurs versions d'Office sur le même poste : « Si vous installez plusieurs versions de Microsoft Excel sur un ordinateur, vous pouvez rencontrer des difficultés lorsque vous essayez d'utiliser Automation pour contrôler une version spécifique de Microsoft Excel. »

Vous l'avez compris, si une seule version d'Excel est installée, l'identifiant de classe Excel fera automatiquement référence à cette unique version.

Si vous avez uniquement Excel 2007 installé sur le poste, les méthodes 1.0, 2.0, 3.0 et 5.0 permettent donc d'affecter une occurrence d'Excel 2007 à notre variable objMyExcel.

V-C - Le bon paramétrage du Shell ?

La méthode 6.0 présente une syntaxe qui permet bien d'instancier l'occurrence d'Excel 2007 dans notre variable objMyExcel.

Cependant, comment s'assurer que la bibliothèque d'objets Excel référencée pour Excel 2007 (via Microsoft Excel 12.0 Object Library ) ne fera pas malgré tout référence à la dernière version d'Excel installée, c'est-à-dire à l'objet Excel 2013 ?

VI - Conclusion

En tant que développeur interagissant avec Excel (ou autre produit d'Office), la maintenance d'applications existantes ou le développement de nouvelles applications destinées à plusieurs types d'installation nous imposent d'avoir à disposition plusieurs versions d'Office.

VI-A - Se risquer à la méthode 6.0

Il y a selon moi un risque de conflit entre l'occurrence d'Excel 2007 ouverte et l'utilisation de la bibliothèque d'objets d'Excel 2013.

C'est pourquoi je vous déconseille d'utiliser cette méthode, même si le défi de récupérer l'occurrence d'Excel 2007 sur un poste où Office 2007 cohabite avec Office 2013 est finalement une réussite !

VI-B - Installer une version d'Office par machine virtuelle

Si, comme pour moi, la méthode 6.0 ne vous convient pas, vous devrez vous résigner à n'installer qu'une seule version d'Office par poste. C'est pourquoi, à moins de disposer de x machines, il faut créer une machine virtuelle par version d'Office à gérer.

Il existe de nombreux logiciels de virtualisation dont les plus connus sont VirtualBox, Windows Virtual PC, VMware. Sans entrer dans les détails (je vous laisse effectuer vos propres recherches), je vous conseille VirtualBox, tant pour sa gratuité que pour sa polyvalence.

En résumé, il faut créer une machine virtuelle par environnement à tester (XP, Vista, Seven…), puis dupliquer ces machines virtuelles pour obtenir une version de chaque environnement par version d'Office à tester (2007, 2010, 2013…).

Pour strictement bien faire, pour développer une application devant être compatible avec Excel 2007, il faut non seulement tester sur une machine virtuelle avec uniquement Office 2007 installé, mais aussi développer dans cet environnement.

VII - Conseils supplémentaires

VII-A - Choisir la version des références

Si votre application est destinée à tourner avec plusieurs versions d'Office, veillez à développer votre application avec une référence à la version d'Office la plus ancienne des versions avec lesquelles vous souhaitez être compatible. Votre application utilisera automatiquement une version plus récente quand elle sera exécutée sur poste ne contenant pas votre version de développement.

Par exemple, si vous souhaitez que votre application soit compatible avec les versions 2007, 2010 et 2013, vous devez faire référence à la bibliothèque de version 2007.

Note : Les références disponibles pour ajouter à votre projet sont bien sûr dépendantes des versions d'Office installées sur votre PC.

VII-B - Choisir entre liaison anticipée et tardive

Pour de meilleures performances, préférez toujours la liaison anticipée (early binding) à la liaison tardive (late binding). Consultez l'article du support Microsoft en référence à la fin de cet article.

VII-C - Ne jamais coder un chemin en dur

Certaines méthodes décrites dans cet article précisent le chemin d'accès au dossier d'Excel 2007 dans sa version 32 bits ("C:\Program Files (x86)\Microsoft Office\Office12\Excel.exe"). Pour un codage propre, il convient d'utiliser les constantes énumérées « SpecialFolder » pour récupérer les chemins d'accès au dossier des programmes, en prenant soin de distinguer les installations x86 et en précisant la version d'Excel (cf. chapitre V.A). Consultez l'énumération Microsoft Msdn en référence à la fin de cet article.

VIII - Remerciements et références

- La plupart des méthodes détaillées sont issues des solutions proposées par les contributeurs de la discussion à l'origine de cet article.

- Information sur le choix de la version de la bibliothèque COM (Microsoft Component Object Model) à référencer dans votre projet.

- Information sur le choix de l'utilisation d'une liaison anticipée ou tardive (early binding et late binding).

- Les recommandations de Microsoft pour l'utilisation de plusieurs versions d'Excel sur le même poste.

- Récupérer les chemins d'accès du dossier contenant les fichiers programme (x86 ou non x86).

Merci tout particulièrement à Francis Walter d'avoir guidé mes premiers pas dans le forum des développeurs. Merci à Christophe Louvet et à Pierre Fauconnier pour leur relecture technique. Merci également à Claude Leloup pour l'orthographe.