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
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.
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.
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.
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.
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.
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 (CLSIDClass Identifier). 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 COMComponent Object Model (Microsoft) (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.