我们有一个本机C ++应用程序,它支持一些不同类型的VBA宏。其中一种类型VBAExtension
将自己注册到核心C ++应用程序,从而生成一个(从IConnectionPointImpl<Extension, &DIID_IExtensionEvents, CComDynamicUnkArray>
派生的类的实例)。这很好用;在给定适当的VBAExtension对象的情况下,核心和其他VBA宏都可以访问IExtensionEvents上的方法。
我们还有一个.NET程序集(用C#编写),它也在运行时加载到核心应用程序中。由于历史原因,程序集由自动运行的VBA宏加载;然后,当用户按下某个特定按钮时,另一个VBA宏会运行程序集的主入口点,这会打开System.Windows.Forms
对话框以进行进一步的交互。
这就是设置。我看到一些奇怪的行为从.NET程序集中访问VBAExtension
方法。具体来说,我从程序集中的各个位置运行以下代码:
foreach (VBAExtension ve in app.Extensions)
{
System.Diagnostics.Debug.Print("Ext: " + ve.Name);
}
如果我从程序集的主对象的构造函数中运行它;或者从程序集的主入口点(在显示对话框之前),一切都很好 - 我得到了VBAExtension
的名称。
但是,如果我从程序集中的按钮(模态 - 我们正在调用form.ShowDialog()
)WinForm启动命令运行相同的代码,ve.Name
s都是空白的。 pDispatch->Invoke
子类的IConnectionPointImpl
调用成功(返回S_OK),但不设置任何返回变量。
如果我将对话框更改为非模态(使用form.Show()
调用),则名称会再次起作用。表单的模态(模态?)似乎会影响IConnectionPointImpl
调用是否成功。
任何人都知道发生了什么事?
编辑:自首次发帖以来,我已经证明了重要的是调用调用堆栈并不重要;相反,它是否是从模态对话框进行调用。我已经更新了正文。
编辑2:根据Hans Passant的回答,以下是他的诊断问题的答案:
Err
,我可以看出,如果我们在VBA处理程序中遇到异常,我们会得到一个VBA错误对话框。清除后,C ++ Invoke
调用将0x80020009(发生“异常”)作为返回码,并且pExcepInfo填入一般失败值(VBA已吞下实际细节)我将尝试深入挖掘我们的消息循环作为下一步。
答案 0 :(得分:4)
在这个问题中,很难找到答案。可能是非常简单的事情,可能是一个令人讨厌的内存损坏问题或线程状态上的VBA解释器内的模糊依赖。粗略的诊断是VBA事件处理程序根本没有运行。这通常不是一个罕见的事故,Basic中用于声明事件处理程序的声明式样式几乎没有很好的方法来诊断订阅问题。许多VBA程序员已经失去了一堆头发试图解决“为什么事件处理程序没有运行”这样的问题。
先收集一些事实并将其添加到您的问题中:
稍微关注ShowDialog()。这种方法确实有很多副作用。不再有效的第一件事是C ++代码中的消息循环。它现在是调度消息的.NET消息循环。看看你的副作用,做更多的工作,而不仅仅是GetMessage / DispatchMessage()。这项工作不再完成。同时在你的代码库中搜索PostThreadMessage(),当.NET代码泵出时,这些消息就会落在地板上。
请记住,当C#代码调用ShowDialog()时,您的本机C ++代码将失去控制权。在关闭窗口之前,它不会重新获得控制权。这可以触发一个简单的执行顺序问题,你的C ++代码不应该做任何重要的事情,无论它做什么来使C#代码运行。