Communicating between C# and VBA

前端 未结 3 1453
长发绾君心
长发绾君心 2021-01-05 02:12

At the request of my boss I created a small set of scripts that are used to periodically monitor the status of certain devices and processes. This info is subsequently proce

3条回答
  •  庸人自扰
    2021-01-05 03:10

    Creating a C# COM-visible class is pretty easy, and (as you said in your comments) it is fun. Here's a small sample.

    In a new C# Library project, add:

    using System;
    using System.Runtime.InteropServices;
    using System.Windows.Forms;
    
    namespace CSharpCom
    {
        [ComVisible(true)]
        [InterfaceType(ComInterfaceType.InterfaceIsIDispatch)]
        //The 3 GUIDs in this file need to be unique for your COM object.
        //Generate new ones using Tools->Create GUID in VS2010
        [Guid("18C66A75-5CA4-4555-991D-7115DB857F7A")] 
        public interface ICSharpCom
        {
            string Format(string FormatString, [Optional]object arg0, [Optional]object arg1, [Optional]object arg2, [Optional]object arg3);
            void ShowMessageBox(string SomeText);
        }
    
        //TODO: Change me!
        [Guid("5D338F6F-A028-41CA-9054-18752D14B1BB")] //Change this 
        [InterfaceType(ComInterfaceType.InterfaceIsIDispatch)]
        interface ICSharpComEvents
        {
            //Add event definitions here. Add [DispId(1..n)] attributes
            //before each event declaration.
        }
    
        [ComVisible(true)]
        [ClassInterface(ClassInterfaceType.None)]
        [ComSourceInterfaces(typeof(ICSharpComEvents))]
        //TODO: Change me!
        [Guid("C17C5EAD-AA14-464E-AD32-E9521AC17134")]
        public sealed class CSharpCom : ICSharpCom
        {
            public string Format(string FormatString, [Optional]object arg0, [Optional]object arg1, [Optional]object arg2, [Optional]object arg3)
            {
                return string.Format(FormatString, arg0, arg1, arg2, arg3);   
            }
    
            public void ShowMessageBox(string SomeText)
            {
                MessageBox.Show(SomeText);
            }
        }
    }
    

    You will want to go into your project properties, to the "Signing" tab, check the box to sign your assembly, and create a new "strong name key file". This will help to prevent versioning issues with your registered DLL.

    Compile it, and register the DLL using regasm in a Visual Studio command prompt. You will use either 32 or 64-bit regasm depending on what version of Office you are using... you will find RegAsm in C:\windows\Microsoft.NET\Framework or C:\windows\Microsoft.NET\Framework64 (32 and 64-bit, respectively):

    C:\windows\Microsoft.NET\Framework\v4.0.30319\RegAsm.exe /codebase /tlb CSharpCom.dll

    This will register your DLL with Windows, so now in the VBA editor, you should be able to go to Tools->References and find your COM's namespace "CSharpCom". Check that box, and now you should be able to create your COM objects in VBA:

    Sub TestCom()
        Dim c As CSharpCom.CSharpCom
        Set c = New CSharpCom.CSharpCom
        c.ShowMessageBox c.Format("{0} {1}!", "Hello", "World")
    End Sub
    

    You can add forms to your COM object, and open them when the VBA calls a particular method; you should be able to use this to create a form with a progress bar. One thing to consider, though, is that VBA is single-threaded. This means that everything else gets frozen while your code is running, so things might get a little tricky.

    Off the top of my head, you could create 3 methods in your COM-visible class: one to open the form, one to update progress (call VBA's DoEvents() method right after your VBA calls the update() method on your COM object, to allow Office to process screen updates), and one to close it. You should call Dispose() on the form; which could be done in the "close" method, but I think it could potentially cause memory leaks/problems if your VBA crashes, and your close method is never called -- just something else to consider.

提交回复
热议问题