Comment puis-je exécuter une macro à partir d'un add-in VBE, sans Application.Exécuter?

j'écris un add-in COM pour le VBE, et l'une des fonctionnalités de base consiste à exécuter du code VBA existant en cliquant sur un bouton de barre de commandes.

le code est un code de test unitaire écrit par l'utilisateur, dans un standard (.bas) module qui ressemble à quelque chose comme ceci:

Option Explicit
Option Private Module

'@TestModule
Private Assert As New Rubberduck.AssertClass

'@TestMethod
Public Sub TestMethod1() 'TODO: Rename test
    On Error GoTo TestFail

    'Arrange:

    'Act:

    'Assert:
    Assert.Inconclusive

TestExit:
    Exit Sub
TestFail:
    Assert.Fail "Test raised an error: #" & Err.Number & " - " & Err.Description
End Sub

donc j'ai ce code qui obtient l'instance courante de l'hôte Application objet:

protected HostApplicationBase(string applicationName)
{
    Application = (TApplication)Marshal.GetActiveObject(applicationName + ".Application");
}

Voici le ExcelApp classe:

public class ExcelApp : HostApplicationBase<Microsoft.Office.Interop.Excel.Application>
{
    public ExcelApp() : base("Excel") { }

    public override void Run(QualifiedMemberName qualifiedMemberName)
    {
        var call = GenerateMethodCall(qualifiedMemberName);
        Application.Run(call);
    }

    protected virtual string GenerateMethodCall(QualifiedMemberName qualifiedMemberName)
    {
        return qualifiedMemberName.ToString();
    }
}

Fonctionne comme un charme. J'ai même code pour WordApp,PowerPointApp et AccessApp, trop.

Le problème est que Outlook Application l'objet n'expose pas un Run méthode, donc je suis, bien, coincé.


Comment puis-je exécuter le code VBA à partir D'un add-in COM pour le VBE, sans <!--12?

Cette réponse liens blog sur MSDN qui s'annonce prometteur, alors j'ai essayé ceci:

public class OutlookApp : HostApplicationBase<Microsoft.Office.Interop.Outlook.Application>
{
    public OutlookApp() : base("Outlook") { }

    public override void Run(QualifiedMemberName qualifiedMemberName)
    {
        var app = Application.GetType();
        app.InvokeMember(qualifiedMemberName.MemberName, BindingFlags.InvokeMethod, null, Application, null);
    }
}

Mais alors le meilleur que j'obtiens est un COMException cela dit "nom inconnu", et les perspectives.EXE process exiting avec le code -1073741819 (0xc0000005) 'Access violation' - et il explose tout aussi bien avec Excel.


UPDATE

ce code VBA fonctionne, si je mets TestMethod1 à l'intérieur ThisOutlookSession:

Outlook.Application.TestMethod1

Notez que TestMethod1 n'est pas inscrit comme membre de Outlook.Application en VBA IntelliSense.. mais de toute façon il arrive travailler.

La question est: comment faire pour que cela fonctionne avec réflexion?

22
demandé sur Community 2015-08-12 03:57:14

3 réponses

mise à Jour 3:

j'ai trouvé ce post sur MSDN forums: Call Outlook VBA sub from VSTO.

évidemment il utilise VSTO et j'ai essayé de le convertir en un VBE AddIn, mais a rencontré des problèmes au travail avec x64 Windows avec un problème de classe de Registre:

COMException (0x80040154): récupération de L'usine de classe COM pour composant avec CLSID {55F88893-7708-11D1-ACEB-006008961DA5} échoué à l' erreur suivante: 80040154 classe non inscrite

quoi Qu'il en soit c'est la réponse des gars qui estime qu'il l'a fait fonctionner:

début du Forum MSDN

j'ai trouvé un moyen! Qu'est-ce qui pourrait être déclenché par VSTO et VBA? Le Presse-papiers!!

j'ai donc utilisé le presse-papiers pour passer des messages d'un environnement à l'autre. autre. Voici quelques codes qui expliqueront mon astuce:

VSTO:

'p_Procedure is the procedure name to call in VBA within Outlook

'mObj_ou_UserProperty is to create a custom property to pass an argument to the VBA procedure

Private Sub p_Call_VBA(p_Procedure As String)
    Dim mObj_of_CommandBars As Microsoft.Office.Core.CommandBars, mObj_ou_Explorer As Outlook.Explorer, mObj_ou_MailItem As Outlook.MailItem, mObj_ou_UserProperty As Outlook.UserProperty

    mObj_ou_Explorer = Globals.Menu_AddIn.Application.ActiveExplorer
    'I want this to run only when one item is selected

    If mObj_ou_Explorer.Selection.Count = 1 Then
        mObj_ou_MailItem = mObj_ou_Explorer.Selection(1)
        mObj_ou_UserProperty = mObj_ou_MailItem.UserProperties.Add("COM AddIn-Azimuth", Outlook.OlUserPropertyType.olText)
        mObj_ou_UserProperty.Value = p_Procedure
        mObj_of_CommandBars = mObj_ou_Explorer.CommandBars

        'Call the clipboard event Copy
        mObj_of_CommandBars.ExecuteMso("Copy")
    End If
End Sub

VBA:

créer une classe pour les événements Explorer et piéger cet événement:

Public WithEvents mpubObj_Explorer As Explorer

'Trap the clipboard event Copy
Private Sub mpubObj_Explorer_BeforeItemCopy(Cancel As Boolean)
Dim mObj_MI As MailItem, mObj_UserProperty As UserProperty

    'Make sure only one item is selected and of type Mail

    If mpubObj_Explorer.Selection.Count = 1 And mpubObj_Explorer.Selection(1).Class = olMail Then
        Set mObj_MI = mpubObj_Explorer.Selection(1)
        'Check to see if the custom property is present in the mail selected
        For Each mObj_UserProperty In mObj_MI.UserProperties
            If mObj_UserProperty.Name = "COM AddIn-Azimuth" Then
                Select Case mObj_UserProperty.Value
                    Case "Example_Add_project"
                        '...
                    Case "Example_Modify_planning"
                        '...
                End Select
                'Remove the custom property, to keep things clean
                mObj_UserProperty.Delete

                'Cancel the Copy event.  It makes the call transparent to the user
                Cancel = True
                Exit For
            End If
        Next
        Set mObj_UserProperty = Nothing
        Set mObj_MI = Nothing
    End If
End Sub

End Of MSDN Forum Post

ainsi l'auteur de ce code ajoute un UserProperty à un élément de courrier et passe le nom de la fonction de cette façon. Encore une fois, cela nécessiterait un certain code de chaudière dans Outlook et au moins 1 article de courrier.

mise à Jour 3a:

80040154 classe non inscrite je recevais était parce que malgré le ciblage de la plateforme x86 quand j'ai traduit le code de VSTO VB.Net to VBE C# I was instantiating items, eg:

Microsoft.Office.Core.CommandBars mObj_of_CommandBars = new Microsoft.Office.Core.CommandBars();

après avoir gaspillé plusieurs heures de plus, j'ai trouvé ce code, qui a couru!!!

enter image description here

Le VBE de Code C# (à partir de ma réponse faire une réponse VBE AddIn ici):

namespace VBEAddin
{
    [ComVisible(true), Guid("3599862B-FF92-42DF-BB55-DBD37CC13565"), ProgId("VBEAddIn.Connect")]
    public class Connect : IDTExtensibility2
    {
        private VBE _VBE;
        private AddIn _AddIn;

        #region "IDTExtensibility2 Members"

        public void OnConnection(object application, ext_ConnectMode connectMode, object addInInst, ref Array custom)
        {
            try
            {
                _VBE = (VBE)application;
                _AddIn = (AddIn)addInInst;

                switch (connectMode)
                {
                    case Extensibility.ext_ConnectMode.ext_cm_Startup:
                        break;
                    case Extensibility.ext_ConnectMode.ext_cm_AfterStartup:
                        InitializeAddIn();

                        break;
                }
            }
            catch (Exception ex)
            {
                MessageBox.Show(ex.ToString());
            }
        }

        private void onReferenceItemAdded(Reference reference)
        {
            //TODO: Map types found in assembly using reference.
        }

        private void onReferenceItemRemoved(Reference reference)
        {
            //TODO: Remove types found in assembly using reference.
        }

        public void OnDisconnection(ext_DisconnectMode disconnectMode, ref Array custom)
        {
        }

        public void OnAddInsUpdate(ref Array custom)
        {
        }

        public void OnStartupComplete(ref Array custom)
        {
            InitializeAddIn();
        }

        private void InitializeAddIn()
        {
            MessageBox.Show(_AddIn.ProgId + " loaded in VBA editor version " + _VBE.Version);
            Form1 frm = new Form1();
            frm.Show();   //<-- HERE I AM INSTANTIATING A FORM WHEN THE ADDIN LOADS FROM THE VBE IDE!
        }

        public void OnBeginShutdown(ref Array custom)
        {
        }

        #endregion
    }
}

le code Form1 que j'instancie et charge à partir de la méthode VBE IDE InitializeAddIn ():

namespace VBEAddIn
{
    public partial class Form1 : Form
    {
        public Form1()
        {
            InitializeComponent();
        }

        private void button1_Click(object sender, EventArgs e)
        {
            Call_VBA("Test");
        }

        private void Call_VBA(string p_Procedure)
        {
            var olApp = new Microsoft.Office.Interop.Outlook.Application();
            Microsoft.Office.Core.CommandBars mObj_of_CommandBars;

            Microsoft.Office.Core.CommandBars mObj_of_CommandBars = new Microsoft.Office.Core.CommandBars();
            Microsoft.Office.Interop.Outlook.Explorer mObj_ou_Explorer;
            Microsoft.Office.Interop.Outlook.MailItem mObj_ou_MailItem;
            Microsoft.Office.Interop.Outlook.UserProperty mObj_ou_UserProperty;

            //mObj_ou_Explorer = Globals.Menu_AddIn.Application.ActiveExplorer
            mObj_ou_Explorer = olApp.ActiveExplorer();

            //I want this to run only when one item is selected
            if (mObj_ou_Explorer.Selection.Count == 1)
            {
                mObj_ou_MailItem = mObj_ou_Explorer.Selection[1];
                mObj_ou_UserProperty = mObj_ou_MailItem.UserProperties.Add("JT", Microsoft.Office.Interop.Outlook.OlUserPropertyType.olText);
                mObj_ou_UserProperty.Value = p_Procedure;
                mObj_of_CommandBars = mObj_ou_Explorer.CommandBars;

                //Call the clipboard event Copy
                mObj_of_CommandBars.ExecuteMso("Copy");
            }
        }
    }
}

The ThisOutlookSession Code:

Public WithEvents mpubObj_Explorer As Explorer

'Trap the clipboard event Copy
Private Sub mpubObj_Explorer_BeforeItemCopy(Cancel As Boolean)
Dim mObj_MI As MailItem, mObj_UserProperty As UserProperty

MsgBox ("The mpubObj_Explorer_BeforeItemCopy event worked!")
    'Make sure only one item is selected and of type Mail

    If mpubObj_Explorer.Selection.Count = 1 And mpubObj_Explorer.Selection(1).Class = olMail Then
        Set mObj_MI = mpubObj_Explorer.Selection(1)
        'Check to see if the custom property is present in the mail selected
        For Each mObj_UserProperty In mObj_MI.UserProperties
            If mObj_UserProperty.Name = "JT" Then

                'Will the magic happen?!
                Outlook.Application.Test

                'Remove the custom property, to keep things clean
                mObj_UserProperty.Delete

                'Cancel the Copy event.  It makes the call transparent to the user
                Cancel = True
                Exit For
            End If
        Next
        Set mObj_UserProperty = Nothing
        Set mObj_MI = Nothing
    End If
End Sub

VBA Outlook Méthode:

Public Sub Test()
MsgBox ("Will this be called?")
End Sub

très tristement, j'ai le regret de vous informer que mes efforts ont été infructueux. Peut-être que cela fonctionne de VSTO (Je n'ai pas essayé) mais après avoir essayé comme un chien aller chercher un os, je suis maintenant prêt à abandonner!

Jamais le moins comme consolation, vous pouvez trouver une idée folle dans l'historique de révision de cette réponse (elle montre une façon de se moquer d'un modèle D'objet de bureau) pour exécuter des tests d'unité VBA de bureau qui sont privés avec des paramètres.

je vais vous parler hors ligne de contribuer au projet Github de RubberDuck, j'ai écrit un code qui fait la même chose que schéma relationnel du cahier de travail de Prodiance avant Microsoft les a rachetés et a inclus leur produit dans la vérification de bureau et la Version Serveur De Contrôle.

vous voudrez peut-être examiner ce code avant de le rejeter entièrement, Je n'ai même pas pu obtenir l'événement mpubObj_Explorer_BeforeItemCopy pour fonctionner, donc si vous pouvez obtenir ce travail normalement dans Outlook vous pourriez vous en tirer mieux. (J'utilise Outlook 2013 à la maison, donc 2010 pourrait être différent).

ps vous penseriez après avoir sauté sur une jambe dans le sens antihoraire, cliqué sur mes doigts tout en frottant ma tête dans le sens horaire comme Solution de contournement Méthode 2 dans cet article de KB que j'aurais cloué... je viens de perdre plus de cheveux!


mise à Jour 2:

à l'Intérieur de votre Outlook.Application.TestMethod1 ne pouvez-vous pas simplement utiliser la méthode VB classics CallByName pour ne pas avoir besoin de réflexion? Vous devez définir une propriété de chaîne de caractères "Sub/FunctionNameToCall" avant d'appeler la méthode contenant le CallByName pour spécifier quelle Sous/fonction appeler.

Malheureusement, les utilisateurs seraient requis pour insérer un code de Plaque de chaudière dans un de leurs modules.


mise à Jour 1:

C'est d'aller à son vraiment dodgy, mais puisque le modèle d'objet de Outlooks a complètement bloqué sa méthode D'exécution, vous pouvez recourir à... SendKeys(oui, je sais, mais il fonctionne).

Malheureusement, le oApp.GetType().InvokeMember("Run"...) méthode décrite ci-dessous fonctionne pour applications bureautiques sauf Outlook - basée sur les Propriétés de la section dans le présent Article: https://support.microsoft.com/en-us/kb/306683, désolé, je ne savais pas que jusqu'à présent, et l'a trouvé très frustrant d'essayer et d' l'article MSDN trompeuse, en fin de compte Microsoft, il est verrouillé par:

enter image description here ** Notez que SendKeys est supporté et le seul autre moyen connu en utilisant ThisOutlookSession n'est pas: https://groups.google.com/forum/?hl=en#!topic/microsoft.public.outlook.program_vba/cQ8gF9ssN3g - même si Sue n'est pas Microsoft PSS qu'elle aurait demandé et a trouvé sa non pris en charge.


Vieux... La méthode ci-dessous fonctionne avec les applications Office, sauf pour Outlook

le problème est que L'objet D'application D'Outlook n'expose pas une méthode D'exécution, donc je suis, bien, coincé. Cette réponse est liée à un blog post sur MSDN qui semble prometteur, donc j'ai essayé cela ... mais OUTLOOK.EXE processus s'arrête avec le code -1073741819 (0xc0000005) "Access violation"

La question est: comment faire pour que cela fonctionne avec réflexion?

1) Voici le code que J'utilise qui fonctionne pour Excel (devrait fonctionner pour Outlook de la même façon), en utilisant le .net reference: Microsoft.Bureau.Interop.Excel v14 (not the ActiveX COM De référence):

using System;
using Microsoft.Office.Interop.Excel;

namespace ConsoleApplication5
{
class Program
{
static void Main(string[] args)
{
    RunVBATest();
}

public static void RunVBATest()
{
    Application oExcel = new Application();
    oExcel.Visible = true;
    Workbooks oBooks = oExcel.Workbooks;
    _Workbook oBook = null;
    oBook = oBooks.Open("C:\temp\Book1.xlsm");

    // Run the macro.
    RunMacro(oExcel, new Object[] { "TestMsg" });

    // Quit Excel and clean up (its better to use the VSTOContrib by Jake Ginnivan).
    oBook.Saved = true;
    oBook.Close(false);
    System.Runtime.InteropServices.Marshal.ReleaseComObject(oBook);
    System.Runtime.InteropServices.Marshal.ReleaseComObject(oBooks);
    System.Runtime.InteropServices.Marshal.ReleaseComObject(oExcel);
}

private static void RunMacro(object oApp, object[] oRunArgs)
{
    oApp.GetType().InvokeMember("Run",
        System.Reflection.BindingFlags.Default |
        System.Reflection.BindingFlags.InvokeMethod,
        null, oApp, oRunArgs);

    //Your call looks a little bit wack in comparison, are you using an instance of the app?
    //Application.GetType().InvokeMember(qualifiedMemberName.MemberName, BindingFlags.InvokeMethod, null, Application, null);
}
}
}
}

2) Assurez-vous de mettre le Macro code dans un Module (un fichier Global BAS)..

Public Sub TestMsg()

MsgBox ("Hello Stackoverflow")

End Sub

3) Assurez-vous d'activer la Macro sécurité et L'accès de confiance au modèle d'objet du projet VBA:

enter image description here

5
répondu Jeremy Thompson 2017-05-23 12:26:41

essayez ce fil, il semble que Outlook est différent, mais alors je pense que vous le savez déjà. Le piratage donné peut-être suffisant.

créez votre code en tant que Subs publics et mettez le code dans le module de classe ThisOutlookSession. Vous pouvez alors utiliser Outlook.Application.MySub () pour appeler votre sous-marin nommé MySub. Bien sûr, changez ça pour le bon nom.

social MSDN: < Application.Exécuter > équivalent pour Microsoft Outlook

4
répondu S Meaden 2015-08-12 10:16:09

EDIT-This approche utilise un contrôle de barre de commandes comme un proxy et évite la nécessité d'événements et de tâches, mais vous pouvez en savoir plus sur l'ancienne approche plus loin.

var app = Application;
var exp = app.ActiveExplorer();
CommandBar cb = exp.CommandBars.Add("CallbackProxy", Temporary: true);
CommandBarControl btn = cb.Controls.Add(MsoControlType.msoControlButton, 1);
btn.OnAction = "MyCallbackProcedure";
btn.Execute();
cb.Delete();

Il est intéressant de noter que Outlook semble seulement comme ProjectName.ModuleName.MethodName ou MethodName lors de l'attribution de la valeur OnAction. Il n'a pas fonctionné quand il a été assigné comme ModuleName.MethodName

Réponse Originale À Cette Question...

succès - Il semble que Outlook VBA et Rubberduck parlez-vous, mais après Rubberduck peut déclencher un code VBA à exécuter. Mais sansApplication.Run, et sans aucune méthode dans cette session D'écriture ayant des DispIDs ou quoi que ce soit qui ressemble à une bibliothèque de Type formelle, il est difficile pour Rubberduck d'appeler quoi que ce soit directement...

Heureusement, le Application gestionnaires d'événements pour ThisOutlookSession nous permettent de déclencher un événement à partir D'un C# DLL/Rubberduck, et nous pouvons ensuite utiliser cet événement pour ouvrir les lignes de communication. Et, cette méthode ne nécessite pas la présence d'éléments, de règles ou de dossiers préexistants. C'est possible seulement en éditant la VBA.

j'utilise un TaskItem, mais vous pourriez probablement utiliser n'importe Item qui déclenche l' ApplicationItemLoad événement. De même, je suis en utilisant le Subject et Body attributs, mais vous pouvez choisir des propriétés différentes (en fait, l'attribut corps est problématique parce que Outlook semble pour ajouter un espace blanc, mais pour l'instant, je suis à la manipulation).

ajouter ce code à ThisOutlookSession

Option Explicit

Const RUBBERDUCK_GUID As String = "Rubberduck"

Public WithEvents itmTemp As TaskItem
Public WithEvents itmCallback As TaskItem

Private Sub Application_ItemLoad(ByVal Item As Object)
  'Save a temporary reference to every new taskitem that is loaded
  If TypeOf Item Is TaskItem Then
    Set itmTemp = Item
  End If
End Sub

Private Sub itmTemp_PropertyChange(ByVal Name As String)
  If itmCallback Is Nothing And Name = "Subject" Then
    If itmTemp.Subject = RUBBERDUCK_GUID Then
      'Keep a reference to this item
      Set itmCallback = itmTemp
    End If
    'Discard the original reference
    Set itmTemp = Nothing
  End If
End Sub

Private Sub itmCallback_PropertyChange(ByVal Name As String)
  If Name = "Body" Then

    'Extract the method name from the Body
    Dim sProcName As String
    sProcName = Trim(Replace(itmCallback.Body, vbCrLf, ""))

    'Set up an instance of a class
    Dim oNamedMethods As clsNamedMethods
    Set oNamedMethods = New clsNamedMethods

    'Use VBA's CallByName method to run the method
    On Error Resume Next
    VBA.CallByName oNamedMethods, sProcName, VbMethod
    On Error GoTo 0

    'Discard the item, and destroy the reference
    itmCallback.Close olDiscard
    Set itmCallback = Nothing
  End If
End Sub

ensuite, créez un module de classe appelé clsNamedMethods les méthodes que vous souhaitez appeler.

    Option Explicit

    Sub TestMethod1()
      TestModule1.TestMethod1
    End Sub

    Sub TestMethod2()
      TestModule1.TestMethod2
    End Sub

    Sub TestMethod3()
      TestModule1.TestMethod3
    End Sub

    Sub ModuleInitialize()
      TestModule1.ModuleInitialize
    End Sub

    Sub ModuleCleanup()
      TestModule1.ModuleCleanup
    End Sub

    Sub TestInitialize()
      TestModule1.TestInitialize
    End Sub

    Sub TestCleanup()
      TestModule1.TestCleanup
    End Sub

et ensuite implémenter le réel méthodes dans un module standard appelé TestModule1

Option Explicit
Option Private Module

'@TestModule
'' uncomment for late-binding:
'Private Assert As Object
'' early-binding requires reference to Rubberduck.UnitTesting.tlb:
Private Assert As New Rubberduck.AssertClass

'@ModuleInitialize
Public Sub ModuleInitialize()
    'this method runs once per module.
    '' uncomment for late-binding:
    'Set Assert = CreateObject("Rubberduck.AssertClass")
End Sub

'@ModuleCleanup
Public Sub ModuleCleanup()
    'this method runs once per module.
End Sub

'@TestInitialize
Public Sub TestInitialize()
    'this method runs before every test in the module.
End Sub

'@TestCleanup
Public Sub TestCleanup()
    'this method runs afer every test in the module.
End Sub

'@TestMethod
Public Sub TestMethod1() 'TODO Rename test
    On Error GoTo TestFail

    'Arrange:

    'Act:

    'Assert:
    Assert.AreEqual True, True

TestExit:
    Exit Sub
TestFail:
    Assert.Fail "Test raised an error: #" & Err.Number & " - " & Err.Description
End Sub

'@TestMethod
Public Sub TestMethod2() 'TODO Rename test
    On Error GoTo TestFail

    'Arrange:

    'Act:

    'Assert:
    Assert.Inconclusive

TestExit:
    Exit Sub
TestFail:
    Assert.Fail "Test raised an error: #" & Err.Number & " - " & Err.Description
End Sub

'@TestMethod
Public Sub TestMethod3() 'TODO Rename test
    On Error GoTo TestFail

    'Arrange:

    'Act:

    'Assert:
    Assert.Fail

TestExit:
    Exit Sub
TestFail:
    Assert.Fail "Test raised an error: #" & Err.Number & " - " & Err.Description
End Sub

puis, à partir du code C#, vous pouvez déclencher le code Outlook VBA avec:

TaskItem taskitem = Application.CreateItem(OlItemType.olTaskItem);
taskitem.Subject = "Rubberduck";
taskitem.Body = "TestMethod1";

Notes

il s'agit d'une validation de principe, donc je sais qu'il y a des problèmes qui doivent être réglés. Par exemple, tout nouveau TaskITem qui a un sujet de "Rubberduck" va être traité comme une charge utile.

j'utilise une classe VBA standard ici, mais la classe pourrait être statique (en éditant les attributs), et la méthode CallByName devrait toujours fonctionner.

une fois que la DLL est capable d'exécuter le code VBA en de cette façon, d'autres mesures peuvent être prises pour resserrer l'intégration:

  1. vous pourriez passer des pointeurs de méthode de nouveau à C#\Rubberduck en utilisant le AddressOf opérateur, et puis C# pourrait appeler ces procédures par leurs pointeurs de fonction, en utilisant quelque chose comme Win32 CallWindowProc

  2. vous pouvez créer une classe VBA avec un membre par défaut, puis assigner une instance de cette classe à une propriété C# DLL qui nécessite un gestionnaire de callback. (similaire à la propriété OnReadyStateChange du MSXML2.Xmlhttp60 object)

  3. vous pouvez passer des détails en utilisant un objet COM, comme Rubberduck le fait déjà avec la classe Assert.

  4. Je n'ai pas pensé à celui-ci, mais je me demande si vous avez défini une classe VBA avec PublicNotCreatable instanciation, si vous pourriez passer ensuite que C#?

Et enfin, bien que cette solution implique une petite quantité de réutilisable, il aurait à jouer gentil avec les gestionnaires d'événements, et je n'ai pas traité ce.

2
répondu ThunderFrame 2016-06-05 15:11:59