从 Excel vba 调用 c# .net 库方法并传递工作簿对象

Call c# .net library method from Excel vba and pass the Workbook object

情况..

对于我的项目,我需要对某些 ​​Excel 文档的内容执行一些复杂的操作。所以我认为用 c# 编写逻辑比在 VBA 宏中编写逻辑要方便得多。由于我需要在不止一个文档中使用此逻辑,并且在不同的文档中需要此逻辑的不同部分,因此我想从 VBA 宏中调用 c# 方法。

所以..

我是this answer来的,它确实完美地解决了这个问题。但是,似乎因为我需要将 ThisWorkbook 对象从 VBA 传递到 .net 方法中,所以 Excel.exe 在关闭应用程序(任务管理器 > 详细信息)后在后台保持活动状态。下次我打开 Excel 时,它立即崩溃,然后 Excel.exe 正确关闭。
这是我遇到的唯一问题,我很沮丧我无法解决它。有什么想法吗?

我考虑过的..

一个可行的(不完美的)解决方案是制作文档级 VSTO 加载项。但是对于需要为每个不同的文档进行小的更改和重新编译整个项目,我并不感到非常兴奋。如果我可以集中 C# 逻辑并从不同文档中要求它,那就更好了。

制作应用程序级别的 VSTO 插件也是不可取的,因为我工作的公司的所有用户都需要安装 VSTO 插件才能打开任何需要它的文档。这是太大的风险和努力。我宁愿集中它,以防止重复并确保始终调用最新版本。

我的代码..

using System.Runtime.InteropServices;
using Excel = Microsoft.Office.Interop.Excel;

namespace ExcelVSTOUtils
{
    [ComVisible(true), ClassInterface(ClassInterfaceType.AutoDispatch)]
    public class Utilities
    {
        public Excel.Workbook _ThisWorkbook { get; private set; }

        // parameterless constructor since vba does not support it
        public Utilities(/*Excel.Workbook ThisWorkbook*/)
        {
        }

        // helper function to set the workbook property after all
        public void Initialize(Excel.Workbook ThisWorkbook)
        {
            _ThisWorkbook = ThisWorkbook;
        }

        // method 1
        public void Method1()
        {
            // doing something complex with _ThisWorkbook ..
        }

        // method 2
        public void Method2()
        {
            // doing something complex with _ThisWorkbook ..
        }
    }
}

'ThisWorkbook object

Private Utilities As New ExcelVSTOUtils.Utilities

Private Sub Workbook_Open()

    Utilities.Initialize ThisWorkbook
End Sub

Sub Method1()

    Utilities.Method1
End Sub

Sub Method2()

    Utilities.Method2
End Sub

因此,我决定使用应用程序级别的插件,并在每个需要它的工作簿中签入 Workbook_Open Sub(如果在继续之前安装了插件)。为实现这一目标,我遵循了 this tutorial 并且它运行良好。但是,这是应用程序级别,但我需要 class 个绑定到特定工作簿的实例。

解决方法:

public partial class ThisAddIn
{
    private AddInUtilities utilities;

    protected override object RequestComAddInAutomationService()
    {
        if (utilities == null)
            utilities = new AddInUtilities();

        return utilities;
    }
}

[ComVisible(true), ClassInterface(ClassInterfaceType.None)]
public class AddInUtilities
{
    public WbVSTO GetWbVSTO()
    {
        return new WbVSTO(Globals.ThisAddIn.Application.ThisWorkbook);
    }
}

[ComVisible(true), ClassInterface(ClassInterfaceType.None)]
public class WbVSTO
{
    private Excel.Workbook _ThisWorkbook;

    public WbVSTO(Excel.Workbook ThisWorkbook)
    {
        _ThisWorkbook = ThisWorkbook;
    }
}
'ThisWorkbook object

Private AppVSTO As Object
Private WbVSTO As Object

Private Sub Workbook_Open()

    Set AppVSTO = Application.COMAddIns("AppVSTO").Object
    Set WbVSTO = AppVSTO.GetWbVSTO()
End Sub

非常重要的是让所有需要暴露的classes都Excel ComVisible。