web-dev-qa-db-fra.com

Canonical: Comment appeler des méthodes .NET à partir d'Excel VBA

J'ai trouvé un moyen d'appeler du code .NET 2 directement à partir d'une macro VBA:

Dim clr As mscoree.CorRuntimeHost
Set clr = New mscoree.CorRuntimeHost
clr.Start
Dim domain As mscorlib.AppDomain
clr.GetDefaultDomain domain
Dim myInstanceOfDotNetClass As Object
Set myInstanceOfDotNetClass = domain.CreateInstanceFrom("SomeDotNetAssembly.dll", "Namespace.Typename").Unwrap
Call myInstanceOfDotNetClass.ExecuteSomeDotNetMethod

(Pour que ce code fonctionne, j'ai dû ajouter des références à mscoree.tlb et mscorlib.tlb au VBA Excel en utilisant Outils -> Références .. dans Excel)

Mais cela ne fonctionne que pour les assemblys .NET CLR 2, jusqu'à .NET Framework version 3.5.

Maintenant, je dois le faire fonctionner avec .NET 4.

J'ai compris que .NET CLR4 a introduit une autre façon, indépendante de la version, de créer une instance du runtime et j'ai également trouvé un exemple de code assez simple écrit en C++: http://dev.widemeadows.de/ 2014/02/04/hébergement-le-net-4-runtime-in-a-native-process /

Mais mes compétences Excel VBA ne sont pas suffisantes pour traduire ces quelques lignes de code en une macro VBA fonctionnelle. Est-ce que quelqu'un peut m'aider s'il vous plaît?

22
user1983691

La stratégie par défaut empêche le CLR 4 d'échanger le code hérité du CLR 2:

Set clr = New mscoree.CorRuntimeHost

Pour activer l'exécution héritée, vous pouvez soit créer le fichier Excel.exe.config dans le dossier où Excel.exe est situé:

<?xml version="1.0" encoding="utf-8" ?>
<configuration>
  <startup useLegacyV2RuntimeActivationPolicy="true">
    <supportedRuntime version="v4.0"/>
  </startup>
</configuration>

Ou vous pouvez appeler la fonction native CorBindToRuntimeEx au lieu de New mscoree.CorRuntimeHost:

Private Declare PtrSafe Function CorBindToRuntimeEx Lib "mscoree" ( _
    ByVal pwszVersion As LongPtr, _
    ByVal pwszBuildFlavor As LongPtr, _
    ByVal startupFlags As Long, _
    ByRef rclsid As Long, _
    ByRef riid As Long, _
    ByRef ppvObject As mscoree.CorRuntimeHost) As Long

Private Declare PtrSafe Function VariantCopy Lib "oleaut32" (dest, src) As Long


''
' Creates a .Net object with the CLR 4 without registration.  '
''
Function CreateInstance(Assembly As String, typeName As String) As Variant
  Const CLR$ = "v4.0.30319"

  Static domain As mscorlib.AppDomain
  If domain Is Nothing Then
    Dim Host As mscoree.CorRuntimeHost, hr&, T&(0 To 7)
    T(0) = &HCB2F6723: T(1) = &H11D2AB3A: T(2) = &HC000409C: T(3) = &H3E0AA34F
    T(4) = &HCB2F6722: T(5) = &H11D2AB3A: T(6) = &HC000409C: T(7) = &H3E0AA34F

    hr = CorBindToRuntimeEx(StrPtr(CLR), 0, 3, T(0), T(4), Host)
    If hr And -2 Then err.Raise hr

    Host.Start
    Host.GetDefaultDomain domain
  End If

  VariantCopy CreateInstance, domain.CreateInstanceFrom(Assembly, typeName).Unwrap
End Function
5
Florent B.

Voici une réponse canonique sur les 3 principales méthodes pour appeler .Net depuis Excel (ou VBA).

Les trois méthodes fonctionnent dans .Net 4.0.

1. XLL

Le fournisseur tiers Add-In Express offre des fonctionnalités XLL, mais son Excel-DNA gratuit et facile à utiliser l'auteur est icihttps://stackoverflow.com/users/44264

Voici un extrait de la page Excel-DNA: https://Excel-dna.net/

Introduction

Excel-DNA est un projet indépendant pour intégrer .NET dans Excel. Avec Excel-DNA, vous pouvez créer des compléments natifs (.xll) pour Excel à l'aide de C #, Visual Basic.NET ou F #, fournissant des fonctions définies par l'utilisateur (UDF) hautes performances, des interfaces de ruban personnalisées et plus encore. L'ensemble de votre complément peut être regroupé dans un seul fichier .xll ne nécessitant aucune installation ni enregistrement.

Mise en route

Si vous utilisez une version de Visual Studio qui prend en charge le gestionnaire de packages NuGet (y compris Visual Studio 2012 Express pour Windows Desktop), la façon la plus simple de créer un complément Excel-DNA est de:

Créez un nouveau projet de bibliothèque de classes en Visual Basic, C # ou F #. Utilisez la boîte de dialogue Gérer les packages NuGet ou la console du gestionnaire de packages pour installer le package Excel-DNA:

PM> Install-Package Excel-DNA

Ajoutez votre code (C #, Visual Basic.NET ou F #):

using ExcelDna.Integration;
public static class MyFunctions
{
    [ExcelFunction(Description = "My first .NET function")]
    public static string SayHello(string name)
    {
        return "Hello " + name;
    }
}

Compilez, chargez et utilisez votre fonction dans Excel:

=SayHello("World!")

2. Compléments d'automatisation

Cet article par Eric Carter montre comment le faire, l'article manque des tas d'images, donc je copie/colle l'article entier et j'ai recréé les images pour les conserver.

REF: https://blogs.msdn.Microsoft.com/eric_carter/2004/12/01/writing-user-defined-functions-for-Excel-in-net/

Excel permet la création de fonctions définies par l'utilisateur qui peuvent être utilisées dans les formules Excel. Un développeur doit créer un type spécial de DLL appelée XLL. Excel vous permet également d'écrire des fonctions personnalisées dans VBA qui peuvent être utilisées dans des formules Excel. Malheureusement, Excel ne prend pas en charge ni ne recommande d'écrire un XLL qui utilise du code managé. Si vous êtes prêt à courir le risque que votre XLL ne s'exécute pas dans les versions actuelles ou futures d'Excel, il existe des solutions qui permettent ce scénario: recherchez sur le Web "XLL managé".

Heureusement, il existe un moyen plus simple de créer une fonction définie par l'utilisateur qui ne vous oblige pas à créer une DLL XLL. Excel XP, Excel 2003 et Excel 2007 prennent en charge ce que l'on appelle un complément d'automatisation. Un complément d'automatisation peut être créé tout simplement en C # ou VB.NET. Je vais vous montrer un exemple en C #.

Tout d'abord, lancez Visual Studio et créez un nouveau projet de bibliothèque de classes C # appelé AutomationAddin pour cet exemple.

Ensuite, dans votre fichier Class1.cs, entrez le code ci-dessous. Remplacez le GUID par votre propre GUID que vous créez en utilisant Générer GUID dans le menu Outils de Visual Studio).

using System;
using System.Runtime.InteropServices;
using Microsoft.Win32;

namespace AutomationAddin
{

  // Replace the Guid below with your own guid that
  // you generate using Create GUID from the Tools menu
  [Guid("A33BF1F2-483F-48F9-8A2D-4DA68C53C13B")] 
  [ClassInterface(ClassInterfaceType.AutoDual)]
  [ComVisible(true)]
  public class MyFunctions
  {
    public MyFunctions()
    {

    }

    public double MultiplyNTimes(double number1, double number2, double timesToMultiply)
    {
      double result = number1;
      for (double i = 0; i < timesToMultiply; i++)
      {
        result = result * number2;
      }
      return result;
    }

    [ComRegisterFunctionAttribute]
    public static void RegisterFunction(Type type)
    {
      Registry.ClassesRoot.CreateSubKey(GetSubKeyName(type, "Programmable"));
      RegistryKey key = Registry.ClassesRoot.OpenSubKey(GetSubKeyName(type, "InprocServer32"), true);
      key.SetValue("", System.Environment.SystemDirectory + @"\mscoree.dll",RegistryValueKind.String);
    }

    [ComUnregisterFunctionAttribute]
    public static void UnregisterFunction(Type type)
    {
      Registry.ClassesRoot.DeleteSubKey(GetSubKeyName(type, "Programmable"), false);
    }

    private static string GetSubKeyName(Type type, string subKeyName)
    {
      System.Text.StringBuilder s = new System.Text.StringBuilder();
      s.Append(@"CLSID\{");
      s.Append(type.GUID.ToString().ToUpper());
      s.Append(@"}\");
      s.Append(subKeyName);
      return s.ToString();
    }  
  }
}

Une fois ce code écrit, affichez les propriétés du projet en double-cliquant sur le nœud des propriétés sous le projet dans l'Explorateur de solutions. Cliquez sur l'onglet Build et cochez la case qui dit "Register for COM Interop". À ce stade, vous avez une étape supplémentaire si vous utilisez Windows Vista ou supérieur. Visual Studio doit être exécuté avec des privilèges d'administrateur pour s'inscrire à COM Interop. Enregistrez votre projet et quittez Visual Studio. Recherchez ensuite Visual Studio dans le menu Démarrer et faites un clic droit dessus et choisissez "Exécuter en tant qu'administrateur". Rouvrez votre projet dans Visual Studio. Choisissez ensuite "Build" pour créer le complément.

enter image description here

Lancez maintenant Excel et accédez à la boîte de dialogue Serveurs d'automatisation en procédant comme suit:

  1. Lancez Excel et cliquez sur le bouton Microsoft Office dans le coin supérieur gauche de la fenêtre.

  2. Choisissez les options Excel.

  3. Cliquez sur l'onglet Compléments dans la boîte de dialogue Options Excel.

  4. Choisissez les compléments Excel dans la zone de liste déroulante intitulée Gérer. Cliquez ensuite sur le bouton Aller.

  5. Cliquez sur le bouton Automation dans la boîte de dialogue Compléments.

Vous pouvez trouver la classe que vous avez créée en recherchant AutomationAddin.MyFunctions dans la liste des compléments Automation:

enter image description here

Maintenant, essayons d'utiliser la fonction MultiplyNTimes dans Excel. Créez d'abord une feuille de calcul simple qui a un nombre, un deuxième nombre pour multiplier le premier par et un troisième nombre pour combien de fois vous voulez multiplier le premier nombre par le deuxième nombre. Un exemple de feuille de calcul est présenté ici:

enter image description here

Cliquez sur une cellule vide dans le classeur sous les nombres, puis cliquez sur le bouton Insérer une fonction dans la barre de formule. Dans la boîte de dialogue des formules disponibles, déroulez la liste déroulante "Ou sélectionnez une catégorie" et choisissez "AutomationAddin.MyFunctions.

enter image description here

Cliquez ensuite sur la fonction MultiplyNTimes comme indiqué ici:

enter image description here

Lorsque vous appuyez sur le bouton OK, Excel affiche une boîte de dialogue pour vous aider à récupérer les arguments de fonction dans la feuille de calcul, comme illustré ici:

enter image description here

Enfin, cliquez sur OK et affichez votre feuille de calcul finale comme indiqué ici avec votre formule personnalisée dans la cellule C3.

enter image description here


3. Appel de .Net depuis Excel VBA

REF: Appel d'une méthode de bibliothèque .net à partir de vba

En utilisant le code du projet Automation.AddIn, nous pouvons facilement appeler la fonction MultiplyNTimes à partir d'Excel VBA.

Ajoutez d'abord une référence à DLL d'Excel, pour ce faire, vous devrez être dans l'éditeur VB. Appuyez sur Alt + F11, puis cliquez sur le menu Outils et références:

enter image description here

Sélectionnez la DLL AutomationAddIn:

enter image description here

Ajoutez le code VBA pour appeler la DLL .Net:

Sub Test()

Dim dotNetClass As AutomationAddIn.MyFunctions
Set dotNetClass = New AutomationAddIn.MyFunctions

Dim dbl As Double
dbl = dotNetClass.MultiplyNTimes(3, 2, 5)

End Sub

Et bon hop!

enter image description here


Enfin, il existe d'excellents articles MSDN sur Excel et .Net par "Andrew Whitechapel" - google them

20
Jeremy Thompson

Voici votre solution, testée pour .NET 2.0 et .NET 4.0, 32 bits et 64 bits, gracieuseté de Soraco Technologies.

La solution proposée ci-dessous utilise une liaison tardive et ne nécessite pas l'enregistrement des assemblys .NET.

Déclarations

Ajoutez les déclarations suivantes à votre projet:

#If VBA7 Then
Private Declare PtrSafe Function GetShortPathName Lib “Kernel32.dll” Alias “GetShortPathNameW” (ByVal LongPath As LongPtr, ByVal ShortPath As LongPtr, ByVal Size As Long) As Long
Private Declare PtrSafe Function SetDllDirectory Lib “Kernel32.dll” Alias “SetDllDirectoryW” (ByVal Path As LongPtr) As Long
Private Declare PtrSafe Sub LoadClr_x64 Lib “QlmCLRHost_x64.dll” (ByVal clrVersion As String, ByVal verbose As Boolean, ByRef CorRuntimeHost As IUnknown)
Private Declare PtrSafe Sub LoadClr_x86 Lib “QlmCLRHost_x86.dll” (ByVal clrVersion As String, ByVal verbose As Boolean, ByRef CorRuntimeHost As IUnknown)
#Else
Private Declare Function GetShortPathName Lib “Kernel32.dll” Alias “GetShortPathNameW” (ByVal LongPath As Long, ByVal ShortPath As Long, ByVal Size As Long) As Long
Private Declare Function SetDllDirectory Lib “Kernel32.dll” Alias “SetDllDirectoryW” (ByVal Path As Long) As Long
Private Declare Sub LoadClr_x64 Lib “QlmCLRHost_x64.dll” (ByVal clrVersion As String, ByVal verbose As Boolean, ByRef CorRuntimeHost As IUnknown)
Private Declare Sub LoadClr_x86 Lib “QlmCLRHost_x86.dll” (ByVal clrVersion As String, ByVal verbose As Boolean, ByRef CorRuntimeHost As IUnknown)
#End If ‘ WinAPI Declarations

' Declare variables
Dim m_myobject As Object
Dim m_homeDir As String

Initialisation

Vous devez initialiser la variable m_homeDir sur le chemin où se trouvent les assemblys .NET.

Par exemple, si vous installez les assemblys .NET dans le même dossier que les fichiers Excel ou MS-Access, vous devez initialiser m_homeDir pour:

Excel: m_homeDir = ThisWorkbook.Path

Accès: m_homeDir = CurrentProject.Path

. Création d'objet NET

Ajoutez le code suivant à votre projet.

Private Function GetMyObject(dllPath As String, dllClass As String) As Object
    Dim LongPath As String
    Dim ShortPath As String

    LongPath = “\\?\” & m_homeDir
    ShortPath = String$(260, vbNull)

    PathLength = GetShortPathName(StrPtr(LongPath), StrPtr(ShortPath), 260)
    ShortPath = Mid$(ShortPath, 5, CLng(PathLength – 4))

    Call SetDllDirectory(StrPtr(ShortPath))
    Dim clr As mscoree.CorRuntimeHost

    If Is64BitApp() Then
        Call LoadClr_x64(“v4.0”, False, clr)
    Else
        Call LoadClr_x86(“v4.0”, False, clr)
    End If

    Call clr.Start

    Dim domain As mscorlib.AppDomain
    Call clr.GetDefaultDomain(domain)

    Dim myInstanceOfDotNetClass As Object
    Dim handle As mscorlib.ObjectHandle

    Set handle = domain.CreateInstanceFrom(dllPath, dllClass)

    Dim clrObject As Object
    Set GetMyObject = handle.Unwrap

    Call clr.Stop
End Function

Private Function Is64BitApp() As Boolean

    #If Win64 Then
        Is64BitApp = True
    #End If
End Function

Instanciez l'objet .NET

Vous êtes maintenant prêt à instancier votre objet .NET et à commencer à l'utiliser. Ajoutez le code suivant à votre application:

m_homeDir = ThisWorkbook.Path 

m_myobject = GetMyObject(m_homeDir & “\yourdotnet.dll”, “namespace.class”)

Le premier argument est le chemin d'accès complet à la DLL .NET.

Le deuxième argument est le nom complet du type demandé, y compris l'espace de noms mais pas l'assembly, tel que renvoyé par la propriété Type.FullName.

DLL requises

La solution nécessite le déploiement de 2 DLL chargées d'héberger le .NET CLR. Les DLL doivent être déployées dans le même dossier que votre fichier Excel ou MS-Access.

Les DLL peuvent être téléchargées à partir du site Web de Soraco: https://soraco.co/products/qlm/QLMCLRHost.Zip

Licence LGPL-2.1

Nous vous accordons par la présente le droit d'utiliser nos DLL tant que votre application n'est pas en concurrence directe ou indirecte avec Quick License Manager . Vous pouvez utiliser ces DLL dans vos applications commerciales ou non commerciales.

7
sam.porter

Je ne sais pas si c'était juste une coïncidence ou parce que j'ai posté une question connexe. SO m'a montré votre question et je pense que je pourrais aussi contribuer quelque chose.

Lorsque vous travaillez avec VBA et DLL, la plupart des solutions que j'ai vues jusqu'à présent me disent d'enregistrer le DLL et de le rendre com/gac visible. Si vous faites cela sur votre PC, c'est absolument Très bien, mais si vous distribuez votre application VBA, vous ne voulez pas vraiment installer de DLL dans leur système. Vous n'avez peut-être pas la permission ou vous ne voulez pas vraiment passer par le processus d'installation/désinstallation ou jouer avec des problèmes de référencement.

Cependant, vous pouvez charger dynamiquement les DLL à l'aide de certaines API Windows.

[~ # ~] dll [~ # ~]

Maintenant, la question est de savoir comment accéder à la DLL .NET à partir de vba? Si vos clients ont une architecture OS mixte x86 x64, vous devez gérer cela en conséquence. Supposons que nous travaillons sur un bureau 32 bits/Excel.

Si vous créez une DLL .NET et que vous souhaitez y accéder à partir de VBA, un message d'erreur semblable à "Impossible de trouver le point d'entrée de la DLL" s'affiche. heureusement Robert Giesecke a créé un wrapper abstrait qui vous permettra de créer des consommables simples DLL via VBA).

n modèle peut être trouvé ici.

Tout ce que vous avez à faire

  1. Créer un nouveau projet de classe dans Visual Studio
  2. Définissez la plate-forme du projet x86 pour 32 bits et autrement
  3. Créez vos méthodes au sein d'une classe principale.
  4. créer une autre classe qui renverra votre classe principale en tant qu'objet (revient à vba)
  5. (suivez le modèle de son site web)

Supposons que vous avez suivi son modèle et créé une méthode de test comme suit.

[ComVisible(true), ClassInterface(ClassInterfaceType.AutoDual)]
public class YOUR_MAIN_CLASS
{
    [return: MarshalAs(UnmanagedType.BStr)]
    public string FN_RETURN_TEXT(string iMsg)
    {

        return "You have sent me: " + iMsg + "...";
    }
}

et votre classe unmanagedexport:

static class UnmanagedExports
{
    [DllExport]
    [return: MarshalAs(UnmanagedType.IDispatch)]
    static object YOUR_DLL_OBJECT()
    {
        return new YOUR_MAIN_CLASS();
    }
}

Préparation pour accéder à la dll du côté vba

Ajoutez le DLL à votre dossier racine:

#If VBA7 Then 
    Public Declare PtrSafe Function LoadLibrary Lib "kernel32" Alias "LoadLibraryA" (ByVal lpLibFileName As String) As LongPtr
    Public Declare PtrSafe Function YOUR_DLL_OBJECT Lib "YOUR_DLL.dll" () As Object
#Else
    Public Declare Function LoadLibrary Lib "kernel32" Alias "LoadLibraryA" (ByVal strFilePath As String) As Long
    Public Declare Function YOUR_DLL_OBJECT Lib "YOUR_DLL.dll" () As Object
#End If

Maintenant, il s'agit de charger la DLL et de créer et d'accéder à des objets dans vba. ce serait:

LoadLibrary (FN_APP_GET_BASE_PATH & "YOUR_DLL.dll")
dim mObj as object
set mObj = YOUR_DLL_OBJECT()
debug.print mObj.FN_RETURN_TEXT("Testing ..")

la sortie doit être

"You have sent me: Testing ....."

Avantages Personnellement, je n'aime pas installer et référencer les DLL. En suivant le modèle ci-dessus, vous n'avez pas besoin de référencer quoi que ce soit, vous n'avez pas besoin d'installer quoi que ce soit, il suffit de charger et de travailler avec votre DLL en toute liberté.

[~ # ~] note [~ # ~] : Je suppose que le code dll/.net est le vôtre et vous pouvez le recompiler avec les modèles ci-dessus pour .

J'ai eu du succès avec le modèle ci-dessus et j'ai créé des notifications non bloquantes .NET pour vba, vous pouvez les consulter ici: "toast" non bloquant comme les notifications pour Microsoft Access (VBA)

4
krish KM