如何(或是否可能)从通过 VSTO 添加到 Excel 的功能区调用 VBA(子函数或函数)

How (or is it possible) to call VBA (sub or function) from ribbon added to Excel through VSTO

我正在开发一个项目,其中有一个 Visual Studio C# Excel 加载项项目 (VSTO)。从 VSTO 代码中,我将带有按钮的功能区添加到 Excel (Office.IRibbonExtensibility)。 从 Excel 中如此添加的按钮,我可以对 C# 代码(在 VSTO 加载项中)中的方法进行回调调用。 我正在寻找(但没有找到)从该按钮调用 VBA(函数的子函数)到 Excel 文件中的 VBA 代码的方法。 换句话说,从那个描述的按钮我知道如何调用 C# 中的代码但不知道如何调用 Excel 文件本身中的 VBA 代码。 我花了很多时间搜索信息并测试一些盲目想法,但没有找到任何东西或从我的测试中获得一些好的结果。 如果能朝着正确的方向开始,我将不胜感激。

不确定这是否是稍后添加的正确位置和方式(2/10/20 @17:08 GMT -7)注意(在获得以下答案后)。我创建了一个小型演示项目并将其上传到 github。项目中也有一个视频 (mp4) 文件来展示它是如何工作的。 https://github.com/MNemteanu/ExcelVSTOAddInDemo

这是可能的。
您必须注意 VBA 代码存储在特定工作簿中,而 VSTO 加载项在应用程序中加载,尽管工作簿处于活动状态。 none 双方都知道对方,除非开发人员知道。
为了实现这种交互,您必须了解以下内容:
1.宏持有工作簿名称;
2. 宏名称。

了解这一点,您将能够应用 3d 评论中发布的解决方案。
下面是一个例子。
先决条件:
1. 我已经准备好启用宏的工作簿"VBA.xlsm";
2. 此工作簿在常规模块中有一个名为 "Foo" 的宏。

实施:
1. 创建新的 VSTO 加载项;
2. 添加名为 "Ribbon1" 的功能区(可视化设计器)并将其设置为具有 "Custom" ControlIdType(成为名为 "Test" 的单独选项卡);
3. 将名为 "callVBA" 的按钮添加到该功能区,它将检查书籍的名称并尝试 运行 工作簿的宏。

我没有向 ThisAddIn.cs class 添加任何代码。我使用的唯一代码是功能区中的按钮单击事件处理程序 class:

public partial class Ribbon1
    {
        private void Ribbon1_Load(object sender, RibbonUIEventArgs e)
        {

        }

        private void callVBA_Click(object sender, RibbonControlEventArgs e)
        {
            if (Globals.ThisAddIn.Application.ActiveWorkbook.Name == "VBA.xlsm")
            {
                Globals.ThisAddIn.Application.Workbooks["VBA.xlsm"].Application.Run("Foo");
            }
        }
    }

这很简单,但需要你准备好先决条件。
工作原理:

更新 1

这是一种更复杂的方法,它根据是否存在包含所需宏的工作簿来检查打开的工作簿和 enables/disables 特定按钮。它还检查新打开的工作簿并处理最近关闭的工作簿,只有一个 Workbook_Activate 事件。
如果你不做任何检查 - 你可能会得到 System.Runtime.InteropServices.COMException

Message=Can't move focus to the control because it is invisible, not enabled, or of a type that does not accept the focus.

 public partial class Ribbon1
    {
        private bool vbaMacroFound;

        private void Ribbon1_Load(object sender, RibbonUIEventArgs e)
        {
            CheckButtons();
            Globals.ThisAddIn.Application.WorkbookActivate += new Excel.AppEvents_WorkbookActivateEventHandler(Workbook_Activate);
        }

        private void callVBA_Click(object sender, RibbonControlEventArgs e)
        {
            if (vbaMacroFound)
            {
                Globals.ThisAddIn.Application.Workbooks["VBA.xlsm"].Application.Run("Foo");
            }
        }

        private void Workbook_Activate(Excel.Workbook Wb)
        {
            CheckButtons();
        }

        private void CheckButtons()
        {
            vbaMacroFound = false;
            this.callVBA.Enabled = false;
            this.callVBA.ScreenTip = "There is no specified macro in none of active workbooks";

            foreach (Excel.Workbook book in Globals.ThisAddIn.Application.Workbooks)
            {

                if (book.Name.Equals("VBA.xlsm"))
                {
                    this.callVBA.Enabled = true;
                    this.callVBA.ScreenTip = "Call the sub from VBA";
                    vbaMacroFound = true;
                }

            }
        }
    }