繁体   English   中英

通过模态WinForms从C ++调用IConnectionPointImpl接口时出现问题

[英]Problems calling IConnectionPointImpl interface from C++ invoked via modal WinForms

我们有一个本机C ++应用程序,它通过COM支持一些各种类型的VBA宏。 其中一种类型VBAExtension向核心C ++应用程序注册,从而导致IConnectionPointImpl<Extension, &DIID_IExtensionEvents, CComDynamicUnkArray>的实例(派生自IConnectionPointImpl<Extension, &DIID_IExtensionEvents, CComDynamicUnkArray> 这很好用; 给定适当的VBAExtension对象,核心VBA和其他VBA宏都可以访问IExtensionEvents上的方法。

我们还有一个.NET程序集(用C#编写),该程序集也在运行时加载到核心应用程序中。 由于历史原因,程序集是通过自动运行的VBA宏加载的; 然后,当用户按下特定按钮时,另一个VBA宏将运行程序集的主入口点,这将弹出System.Windows.Forms对话框以进行进一步的交互。

这就是设置。 我看到从.NET程序VBAExtension访问VBAExtension方法有一些奇怪的行为。 具体来说,我正在程序集中的不同位置运行以下代码:

foreach (VBAExtension ve in app.Extensions)
{
    System.Diagnostics.Debug.Print("Ext: " + ve.Name);
}

如果我从程序集主对象的构造函数运行它; 或从程序集的主入口点开始(显示对话框之前),一切都很好–我得到了打印出来的VBAExtension的名称。

但是,如果我从程序集的( 模态 -我们称为form.ShowDialog() )WinForm中的按钮开始的命令中运行相同的代码,则ve.Name都为空。 IConnectionPointImpl子类进行的pDispatch->Invoke调用成功(返回S_OK),但未设置任何返回变量。

如果我将对话框更改为非模式对话框(由form.Show()调用),则名称将再次起作用。 表单的形式(模态?)似乎会影响IConnectionPointImpl调用是否成功。

有人知道发生了什么吗?

编辑:自从第一次发布以来,我已经证明,重要的不是调用调用堆栈; 而是通过模态对话框进行调用。 我已经更新了正文。

编辑2: Per Hans Passant的答案,以下是他的诊断性问题的答案:

  • 不出所料,在正常情况下(无模式),如果我重命名VBA事件处理程序,则没有错误。 该调用仅不返回任何数据。
  • 我已经将MsgBox调用放入VBA处理程序中; 它在无模式情况下显示,但在无模式情况下不显示。 因此,处理程序不会在模式情况下执行。
  • 通过使用Err ,我可以告诉我们,如果我们在VBA处理程序中遇到异常,则会出现一个VBA错误对话框。 清除该错误后,C ++ Invoke调用将0x80020009(“发生异常”)作为返回代码,并用通用失败值填充了pExcepInfo(VBA吞噬了实际的详细信息)
  • 在第一个对话框之后或在C#加载项的第二次调用期间,该事件不会在模式对话框的第二个显示上触发。

下一步,我将尝试深入探讨我们的消息循环。

这个问题上几乎没有什么硬道理可以作为答案。 可能很简单,可能是令人讨厌的内存损坏问题,也可能是VBA解释器内部对线程状态的模糊依赖。 粗略的诊断是VBA事件处理程序根本没有运行。 一般而言,这并不是罕见的事故,Basic中用于声明事件处理程序的声明式样式几乎没有诊断订阅问题的好方法。 许多VBA程序员在解决此类“为什么事件处理程序为什么不运行”的问题时,一头雾水。

首先收集一些困难的事实,并将其添加到您的问题中:

  • 首先,验证您的C ++代码实际上可以看到根本没有事件处理程序。 使用良好的版本,重命名事件处理程序。 期望您不这样做,引发接收器未订阅的事件不是错误。
  • 验证事件处理程序实际上是在错误版本中执行的。 除了分配BSTR参数外,还可以执行其他操作,您可以轻松地看到某些内容,例如磁盘上的文件。
  • 验证您可以在事件处理程序中正确诊断异常。 分配Err对象,并验证您的C ++代码生成正确的诊断。 请注意,您的IDispatch :: Invoke()调用会为pExcepInfo传递NULL,这不是生成诊断的好方法。
  • 检查事件是否在第二次显示窗口时运行,如果运行,则说明执行顺序有问题。

将重点放在ShowDialog()上。 这种方法确实有很多副作用。 不再起作用的第一件事是C ++代码中的消息循环。 现在是.NET消息循环分发消息。 看看您的副作用,除了简单地执行GetMessage / DispatchMessage()之外,还要做更多的工作。 这项工作不再完成。 还要在您的代码库中搜索PostThreadMessage(),当.NET代码启动时,这些消息就会掉在地上。

并且请记住,当C#代码调用ShowDialog()时,您的本机C ++代码将失去控制。 关闭窗口后,它才能重新获得控制权。 这可能会触发一个简单的执行顺序问题,即在使C#代码运行之后,您的C ++代码不应做任何重要的事情。

暂无
暂无

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM