我们有一个本地 C++ 应用程序,它通过 COM 支持一些各种类型的 VBA 宏。其中一种类型VBAExtension
向核心 C++ 应用程序注册自身,从而生成(派生自的类)的实例IConnectionPointImpl<Extension, &DIID_IExtensionEvents, CComDynamicUnkArray>
。这很好用;给定适当的 VBAExtension 对象,核心和其他 VBA 宏都可以访问 IExtensionEvents 上的方法。
我们还有一个 .NET 程序集(用 C# 编写),它也在运行时加载到核心应用程序中。由于历史原因,程序集由自动运行的 VBA 宏加载;然后,当用户按下特定按钮时,另一个 VBA 宏运行程序集的主入口点,这会弹出一个System.Windows.Forms
对话框以进行进一步的交互。
这就是设置。我看到一些奇怪的行为VBAExtension
从 .NET 程序集中访问这些方法。具体来说,我从程序集中的不同位置运行以下代码:
foreach (VBAExtension ve in app.Extensions)
{
System.Diagnostics.Debug.Print("Ext: " + ve.Name);
}
如果我从程序集主对象的构造函数运行它;或者从程序集的主入口点(在显示对话框之前),一切都很好——我得到了VBAExtension
打印出来的 s 的名称。
但是,如果我从程序集(模态- 我们正在调用form.ShowDialog()
)WinForm中的按钮启动的命令中运行相同的代码,则ve.Name
s 都是空白的。子类pDispatch->Invoke
调用IConnectionPointImpl
成功(返回 S_OK),但没有设置任何返回变量。
如果我将对话框更改为非模态(使用 调用form.Show()
),则名称将再次起作用。表单的模态(modalness?)似乎会影响IConnectionPointImpl
调用是否成功。
有谁知道发生了什么?
编辑:自从第一次发布以来,我已经证明重要的不是调用堆栈;相反,它是调用是否来自模态对话框。我已经更新了正文。
编辑 2:根据 Hans Passant 的回答,以下是他的诊断问题的答案:
- 正如预期的那样,在良好(无模式)的情况下,如果我重命名 VBA 事件处理程序,则不会出现错误。该调用不返回任何数据。
- 我已将 MsgBox 调用放入 VBA 处理程序;它在无模式情况下显示,但在模式情况下不显示。因此,在模态情况下不执行处理程序。
- 通过使用
Err
,我可以知道如果我们在 VBA 处理程序中遇到异常,我们会得到一个 VBA 错误对话框。一旦清除这一点,C++Invoke
调用将 0x80020009(“发生异常”)作为返回码,并且 pExcepInfo 填充了通用故障值(VBA 已经吞下了实际细节) - 该事件不会在模态对话框的第二次显示时触发,无论是紧随第一个对话框之后还是在第二次调用 C# 加载项期间。
作为下一步,我将尝试深入研究我们的消息循环。