Il existe encore couramment des situations où l’on souhaite appeler du code .NET 8 depuis VBA. C’est notamment le cas lorsqu’on veut conserver tels quels les actifs Excel ou Access existants, tout en déportant vers C# uniquement les parties lourdes - traitement de chaînes, HTTP, chiffrement, logique métier.
Cependant, si l’on s’appuie sur CreateObject pour une liaison tardive (late binding), le côté VBA finit criblé de Object. L’IntelliSense s’affaiblit, les fautes de frappe dans les noms de méthodes ne sont détectées qu’à l’exécution, et on s’enfonce peu à peu dans un bourbier reposant sur des chaînes de caractères.
Cette fois, nous nous concentrons donc sur l’exposition d’une DLL .NET 8 à COM, la génération d’une bibliothèque de types (TLB) avec dscom, et son utilisation typée depuis VBA par liaison anticipée (early binding).
Nous laissons de côté cette fois le récit ancien de .NET Framework + RegAsm, l’écriture manuelle d’IDL compilée avec MIDL, ainsi que le Reg-Free COM. Ici, nous ne traitons que le chemin unique .NET 8 / hôte COM / dscom / liaison anticipée VBA.
Par ailleurs, tout le code présenté dans cet article est publié sur GitHub sous forme d’un ensemble d’exemples complet, compilable et vérifiable (bibliothèque exposée à COM, scripts de génération et d’enregistrement du TLB, modules VBA, tests unitaires).
dotnet8-dll-typed-vba-com-dscom-tlb - komurasoft-blog-samples (GitHub)
1. La conclusion d’abord
Pour poser d’abord la conclusion, voici le déroulé.
- Compiler la bibliothèque de classes .NET 8 avec
EnableComHosting=true - Créer une interface explicite et une classe à exposer à COM
- Régler la classe sur
ClassInterfaceType.None, sans se réfugier dansAutoDual - Mettre l’interface utilisée depuis VBA en
InterfaceIsDual - À partir du
*.dllproduit par la compilation, générer un*.tlbavecdscom tlbexport - Enregistrer
*.comhost.dllavecregsvr32 - Enregistrer
*.tlbavecdscom tlbregister - Ajouter la référence dans VBA et l’utiliser de façon typée, comme dans
Dim x As NomDeLaBibliothèque.IVotreInterface
En somme, l’architecture est la suivante : le point d’entrée COM est le *.comhost.dll produit par le SDK .NET, l’information de type est le *.tlb produit par dscom, et VBA effectue une liaison anticipée en consultant ce TLB.
2. Vue d’ensemble de cette architecture
Voyons d’abord, en un seul schéma, qui joue quel rôle.
flowchart LR
VBA["VBA / Excel / Access"] -->|informations de type via le TLB référencé| TLB["VbaTypedComSample.tlb"]
VBA -->|appels COM| COMHOST["VbaTypedComSample.comhost.dll"]
COMHOST --> DOTNET["VbaTypedComSample.dll (.NET 8)"]
DOTNET --> RUNTIME[".NET 8 Runtime"]
Voici le rôle de chacun.
| Fichier | Rôle |
|---|---|
VbaTypedComSample.dll |
L’implémentation .NET 8 elle-même |
VbaTypedComSample.comhost.dll |
Le point d’entrée appelé depuis COM |
VbaTypedComSample.tlb |
L’information de type que VBA consulte |
VbaTypedComSample.deps.json |
Les informations de résolution des dépendances |
VbaTypedComSample.runtimeconfig.json |
Les informations de démarrage du runtime .NET |
Ce qui compte ici, c’est que le TLB est ce dont VBA a besoin pour connaître les types, et que le comhost est ce qui est nécessaire comme point d’entrée d’activation COM.
Le fait qu’on ne puisse pas simplement remettre le .dll seul et considérer l’affaire close illustre bien ce que le monde COM a de peu direct.
3. À décider en premier - Faire correspondre le 32 bits et le 64 bits
Se tromper ici fait basculer, avec une probabilité assez élevée, vers l’erreur Le composant ActiveX ne peut pas créer l'objet.
Veillez à faire correspondre la bitness d’Office / VBA et celle du serveur COM.
| Côté consommateur | Repère côté .NET | Génération du TLB | Commande d’enregistrement |
|---|---|---|---|
| Office 64 bits | x64 / win-x64 |
dscom |
C:\Windows\System32\regsvr32.exe |
| Office 32 bits (sur Windows 64 bits) | x86 / win-x86 |
dscom32.exe |
C:\Windows\SysWOW64\regsvr32.exe |
Avec l’hôte COM de .NET 5+, laisser le projet en AnyCPU tend à faire pencher *.comhost.dll du côté 64 bits, ce qui peut ne pas correspondre à un Office 32 bits. Il est donc plus sûr d’expliciter x86 / x64 en fonction d’Office.
Le code de cet article prend Office 64 bits comme exemple. Pour un Office 32 bits, remplacez mentalement le x64 qui apparaît plus loin par x86, et win-x64 par win-x86.
4. Construire le côté .NET 8
Ici, nous construisons un exemple minimal où VBA peut appeler Add, Divide et Hello.
4.1 .csproj
<Project Sdk="Microsoft.NET.Sdk">
<PropertyGroup>
<TargetFramework>net8.0-windows</TargetFramework>
<Nullable>enable</Nullable>
<ImplicitUsings>enable</ImplicitUsings>
<EnableComHosting>true</EnableComHosting>
<PlatformTarget>x64</PlatformTarget>
<NETCoreSdkRuntimeIdentifier>win-x64</NETCoreSdkRuntimeIdentifier>
</PropertyGroup>
</Project>
Le point clé est EnableComHosting. Avec ce réglage, VbaTypedComSample.comhost.dll est généré au moment de la compilation.
4.2 Garder tout l’assembly invisible pour COM par défaut
Comme on ne veut marquer ComVisible(true) que sur les types exposés à COM, il est plus simple de régler l’ensemble de l’assembly sur false.
using System.Runtime.InteropServices;
[assembly: ComVisible(false)]
4.3 Écrire l’interface et la classe à exposer
using System.Runtime.InteropServices;
namespace VbaTypedComSample;
[ComVisible(true)]
[Guid("2A1BBEDE-DE6E-4C34-AD60-2E9E0E33E999")]
[InterfaceType(ComInterfaceType.InterfaceIsDual)]
public interface ICalculator
{
[DispId(1)]
int Add(int x, int y);
[DispId(2)]
double Divide(double x, double y);
[DispId(3)]
string Hello(string name);
}
[ComVisible(true)]
[Guid("FAD1C752-0BB6-4DDD-889F-FE446350847A")]
[ClassInterface(ClassInterfaceType.None)]
[ComDefaultInterface(typeof(ICalculator))]
public class Calculator : ICalculator
{
public Calculator()
{
}
public int Add(int x, int y) => checked(x + y);
public double Divide(double x, double y)
{
if (y == 0)
{
throw new ArgumentOutOfRangeException(nameof(y), "Impossible de diviser par zéro.");
}
return x / y;
}
public string Hello(string name)
{
if (string.IsNullOrWhiteSpace(name))
{
return "Hello";
}
return $"Hello, {name}";
}
}
Les points à retenir dans ce code sont les suivants.
- Attribuer un
Guidséparé à l’interface et à la classe - Utiliser
ClassInterfaceType.Nonepour ne pas dépendre d’une interface de classe générée automatiquement - Utiliser
InterfaceIsDualpour faciliter l’utilisation depuis VBA - Attribuer des
DispIdpermet de réduire les accidents lorsqu’on modifie l’ordre des méthodes après publication - Comme la classe est instanciée (
New) par COM, prévoir un constructeur public sans argument
5. Compiler
Effectuez une compilation Release.
dotnet build -c Release
Après la compilation, le dossier de sortie contient au moins les fichiers suivants.
bin/
Release/
net8.0-windows/
VbaTypedComSample.dll
VbaTypedComSample.comhost.dll
VbaTypedComSample.deps.json
VbaTypedComSample.runtimeconfig.json
C’est ce dossier qui sert pour la distribution et l’enregistrement. Si vous changez l’emplacement plus tard, il faudra refaire l’enregistrement.
6. Générer le TLB avec dscom
6.1 Pour le 64 bits
Installez d’abord dscom.
dotnet tool install --global dscom
Ensuite, générez le TLB à partir de l’assembly compilé.
dscom tlbexport .\bin\Release\net8.0-windows\VbaTypedComSample.dll --out .\bin\Release\net8.0-windows\VbaTypedComSample.tlb
6.2 Pour Office 32 bits
C’est un petit piège ici. Pour générer un TLB destiné à un Office 32 bits, il est plus sûr d’utiliser dscom32.exe.
.\tools\dscom32.exe tlbexport .\bin\Release\net8.0-windows\VbaTypedComSample.dll --out .\bin\Release\net8.0-windows\VbaTypedComSample.tlb
7. Enregistrer l’hôte COM et le TLB
Exécutez ceci depuis une invite de commandes / un PowerShell avec des droits administrateur.
7.1 Pour un Office 64 bits / COM 64 bits
$out = Resolve-Path .\bin\Release\net8.0-windows
C:\Windows\System32\regsvr32.exe "$out\VbaTypedComSample.comhost.dll"
dscom tlbregister "$out\VbaTypedComSample.tlb"
7.2 Pour un Office 32 bits (sur Windows 64 bits)
$out = Resolve-Path .\bin\Release\net8.0-windows
C:\Windows\SysWOW64\regsvr32.exe "$out\VbaTypedComSample.comhost.dll"
.\tools\dscom32.exe tlbregister "$out\VbaTypedComSample.tlb"
Deux choses se produisent ici.
regsvr32enregistre*.comhost.dllcomme serveur COMtlbregisterenregistre*.tlbcomme bibliothèque de types
8. Ajouter la référence dans VBA et l’utiliser de façon typée
- Ouvrir Excel ou Access
- Ouvrir l’éditeur VBA
Outils->Références- Si la bibliothèque apparaît dans la liste, cocher la case
- Si elle n’apparaît pas dans la liste, choisir
VbaTypedComSample.tlbviaParcourir...
Option Explicit
Public Sub UseCalculator()
Dim calc As VbaTypedComSample.ICalculator
Set calc = New VbaTypedComSample.Calculator
Debug.Print calc.Add(10, 20)
Debug.Print calc.Divide(10, 4)
Debug.Print calc.Hello("VBA")
End Sub
Cela apporte les avantages suivants côté VBA.
- L’IntelliSense fonctionne
- Les fautes de frappe dans les noms de méthodes sont plus faciles à repérer avant l’exécution
- L’API publique peut être consultée dans l’Object Browser
- Plus lisible qu’un usage brut de
Object
8.1 Les exceptions deviennent des erreurs COM côté VBA
Par exemple, lorsqu’une exception est levée côté .NET, comme avec Divide(10, 0), elle apparaît côté VBA comme une erreur COM.
Option Explicit
Public Sub UseCalculatorWithErrorHandling()
On Error GoTo EH
Dim calc As VbaTypedComSample.ICalculator
Set calc = New VbaTypedComSample.Calculator
Debug.Print calc.Divide(10, 0)
Exit Sub
EH:
Debug.Print Err.Number
Debug.Print Err.Description
End Sub
9. Comment penser la distribution
Ce qui compte lors de la distribution, c’est de déployer l’ensemble des fichiers produits, et non la DLL seule.
VbaTypedComSample.dll
VbaTypedComSample.comhost.dll
VbaTypedComSample.deps.json
VbaTypedComSample.runtimeconfig.json
VbaTypedComSample.tlb
(ainsi que l'ensemble des DLL dépendantes si nécessaire)
De plus, le PC client a besoin du runtime .NET 8 correspondant. L’hôte COM ne fonctionne pas en déploiement self-contained ; il doit en principe être exploité en mode framework-dependent.
10. Pièges courants
10.1 Ne pas laisser le projet en AnyCPU
Si la bitness de VBA / Office et celle de l’hôte COM divergent, les échecs prennent une forme assez déroutante.
- Pour un Office 64 bits :
x64/win-x64 - Pour un Office 32 bits :
x86/win-x86
10.2 Ne pas utiliser ClassInterfaceType.AutoDual
Cela paraît pratique à première vue, mais c’est facile à casser dès qu’on touche à l’ordre des membres ou à la composition après publication.
Pour un usage typé stable depuis VBA, la pratique établie consiste à définir une interface explicite et à régler la classe sur ClassInterfaceType.None.
10.3 Ne pas régénérer les GUID à la légère
En COM, le GUID constitue le contrat lui-même. Remplacer l’IID ou le CLSID à la légère après publication casse les références et enregistrements VBA existants.
10.4 Ne pas casser une interface déjà publiée
En COM, même « ajouter simplement une méthode après coup » ne se passe pas toujours sans heurts.
- Conserver
ICalculatortel quel - Si le changement est important, créer un nouveau
ICalculator2 - La classe peut implémenter les deux
10.5 Garder des types sobres
À la frontière exposée à VBA, il est plus sûr de rester simple.
Pour commencer, les types suivants s’accordent bien.
intdoubleboolstringDateTimedecimalenum
10.6 Ne pas mettre à jour pendant qu’Office est ouvert
Excel ou Access peut garder la main sur la DLL, ce qui provoque des complications lors de la compilation ou du réenregistrement.
- Fermer Office
- Désenregistrer si nécessaire
- Recompiler
- Enregistrer à nouveau
11. Résumé
Le sujet « utiliser une DLL .NET 8 depuis VBA avec typage fort » n’est pas une procédure si effrayante une fois qu’on le ramène à l’exposition COM + la génération du TLB avec dscom. Côté .NET 8, on règle EnableComHosting=true et on prépare une interface explicite (classe en ClassInterfaceType.None, interface côté VBA en InterfaceIsDual), on génère le TLB avec dscom tlbexport, on enregistre *.comhost.dll avec regsvr32 et *.tlb avec dscom tlbregister. Il ne reste plus ensuite qu’à ajouter la référence dans VBA et à utiliser la liaison anticipée.
En cas de doute, l’astuce consiste à penser séparément l’hôte COM et le TLB.
- Le point d’entrée d’activation est
*.comhost.dll - L’information de type est
*.tlb - L’implémentation elle-même est
*.dll
12. Références
- Code d’exemple complet de cet article (bibliothèque exposée à COM, scripts, VBA, tests) - komurasoft-blog-samples (GitHub)
- Expose .NET components to COM - Microsoft Learn
- Qualify .NET types for interoperation - Microsoft Learn
- ComInterfaceType Enum - Microsoft Learn
- ClassInterfaceType Enum - Microsoft Learn
- COM Callable Wrapper - Microsoft Learn
- DispIdAttribute Class - Microsoft Learn
- dscom - NuGet Gallery
- How to use the Regsvr32 tool and troubleshoot Regsvr32 error messages - Microsoft Support
- .NET 8 downloads
Articles associés
Articles récents partageant les mêmes étiquettes, pour approfondir des sujets proches.
Le problème d'EXCEL.EXE qui reste actif lors de la manipulation d'Excel en C# — schémas de libération des références COM et décision de remplacement
Analyse du problème du processus EXCEL.EXE qui reste actif après une automatisation COM d'Excel depuis C#, à partir du comptage de référe...
Migrer les macros Excel VBA vers Power Automate — Ce qu'il faut remplacer par des scripts Office, et ce qu'il faut garder en VBA
Un guide pour savoir si les macros Excel VBA peuvent migrer vers Power Automate : ce que les scripts Office peuvent remplacer, ce que seu...
Compatibilité descendante des interfaces DLL et COM — Tableau de décision : quels changements cassent les appelants
Quels changements apportés à une DLL ou à un composant COM cassent réellement leurs appelants ? Nous détaillons les trois niveaux de comp...
Les applications métier fonctionnent-elles sous Windows on Arm ? — La réalité de l'émulation x64 (Prism) et des DLL/COM natifs
Une réponse, destinée aux développeurs et aux services informatiques, à la question « notre application métier fonctionnera-t-elle sous W...
Comment choisir la communication inter-processus sous Windows ── Tableau de décision : tubes nommés / TCP / gRPC / mémoire partagée / COM
Comment choisir le moyen de faire communiquer des applications Windows entre elles ? Cet article organise les tubes nommés, le TCP local,...
Sujets associés
Ces pages replacent le sujet dans un contexte plus large de services et de décisions.
Thèmes techniques Windows
Portail des sujets sur le développement Windows, l'analyse des incidents et la valorisation des actifs existants.
Migration ActiveX
Choisir de conserver, encapsuler ou remplacer des composants COM / ActiveX / OCX.
Services liés à ce sujet
Cet article est directement lié aux services suivants.
Développement d'applications Windows
La conception de la surface d'intégration entre VBA, COM, Office, .NET 8 et la génération de bibliothèque de types est étroitement liée au développement d'applications Windows, ce qui rend ce thème compatible avec notre service de développement d'applications Windows.
Conseil technique et revue de conception
Si vous souhaitez clarifier la conception de la frontière entre des actifs VBA existants et .NET 8 - y compris la bitness, l'enregistrement, la génération de TLB et la stratégie de déploiement - cela se prête bien à une mission de conseil technique et de revue de conception.
Questions fréquentes
Questions souvent posées lors d’une consultation sur le sujet de cet article.
- Que faut-il pour utiliser une DLL .NET 8 depuis VBA avec typage fort (liaison anticipée) ?
- Compilez la bibliothèque de classes .NET 8 avec EnableComHosting=true pour générer *.comhost.dll, définissez une interface explicite visible par COM marquée InterfaceIsDual ainsi qu'une classe réglée sur ClassInterfaceType.None, puis créez *.tlb avec dscom tlbexport. Enregistrez ensuite *.comhost.dll avec regsvr32 et *.tlb avec dscom tlbregister, puis ajoutez ce TLB dans les références VBA : vous pourrez alors écrire Dim x As NomDeLaBibliothèque.IVotreInterface pour un usage typé, avec IntelliSense et inspection de l'API dans l'Object Browser. En résumé, *.comhost.dll est le point d'entrée COM, *.tlb est l'information de type que VBA consulte, et *.dll contient l'implémentation elle-même.
- Quelle est la cause de l'erreur « Le composant ActiveX ne peut pas créer l'objet » ?
- La cause la plus fréquente est une incompatibilité de bitness entre Office/VBA et le serveur COM. Pour un Office 64 bits, compilez en x64/win-x64 et enregistrez avec le regsvr32 de System32 ; pour un Office 32 bits (sur un Windows 64 bits), compilez en x86/win-x86, enregistrez avec le regsvr32 de SysWOW64, et générez le TLB avec dscom32.exe. Avec l'hôte COM de .NET 5+, laisser le projet en AnyCPU tend à faire pencher *.comhost.dll du côté 64 bits, ce qui peut ne pas correspondre à un Office 32 bits ; il est donc plus sûr d'expliciter x86/x64 en fonction d'Office.
- Faut-il éviter d'utiliser ClassInterfaceType.AutoDual ?
- Cela semble pratique à première vue, mais c'est facile à casser dès qu'on touche à l'ordre des membres ou à la composition après publication, donc mieux vaut l'éviter. Pour un usage typé stable depuis VBA, la pratique établie consiste à définir une interface explicite, à régler la classe sur ClassInterfaceType.None, et à mettre l'interface utilisée par VBA en InterfaceIsDual. Assigner des DispId réduit les accidents lors d'un changement d'ordre des méthodes. Par ailleurs, en COM, le GUID constitue le contrat lui-même : régénérer l'IID ou le CLSID à la légère après publication casse les références et enregistrements VBA existants.
- Pour la distribution, suffit-il de fournir la DLL seule ?
- Non, la DLL seule ne fonctionnera pas. Il faut regrouper l'implémentation elle-même (*.dll), *.comhost.dll, *.deps.json, *.runtimeconfig.json, *.tlb, et si nécessaire l'ensemble des DLL dépendantes. Le PC client a en outre besoin du runtime .NET 8 correspondant, car l'hôte COM ne fonctionne pas en déploiement self-contained mais doit être exploité en mode framework-dependent. Notez aussi que si vous changez l'emplacement de déploiement par la suite, il faudra refaire l'enregistrement.
Profil de l’auteur
Page de présentation de l’auteur de l’article.
Go Komura
Représentant de KomuraSoft LLC
Spécialisé dans le développement de logiciels Windows, le conseil technique et l’analyse de pannes, notamment pour les systèmes existants et les incidents difficiles à reproduire.
Liens publics