0

长话短说:在使用 COM inproc-server (dll) 的 C# 应用程序中,我遇到“0x80010100:系统调用失败”异常,并且在调试模式下还有 ContextSwitchDeadlock 异常。

现在更详细:

1)C#app初始化STA,创建COM对象(注册为“Apartment”);然后 in 订阅它的连接点,并开始使用该对象。

2) 在某个阶段,COM 对象会生成很多事件,将一个非常大的 COM 对象集合作为参数传递,这些对象是在同一个单元中创建的。

3)C#端的event-handler处理上述集合,偶尔调用对象的一些方法。在某个阶段,后面的调用开始失败,并出现上述异常。

在 COM 端,公寓使用了一个隐藏窗口,其 winproc 如下所示:

typedef std::function<void(void)> Functor;
LRESULT CALLBACK WndProc(HWND hwnd, UINT msg, WPARAM wParam, LPARAM lParam)   
{   
  switch(msg)   
  {   
    case AM_FUNCTOR:
    {
      Functor *f = reinterpret_cast<Functor *>(lParam);
      (*f)();
      delete f;
    }
    break;   
    case WM_CLOSE:   
      DestroyWindow(hwnd);   
    break;   
    default:   
      return DefWindowProc(hwnd, msg, wParam, lParam);   
  }   
  return 0;   
} 

事件从 COM 服务器的其他部分发布到此窗口:

void post(const Functor &func)
{
  Functor *f = new Functor(func);
  PostMessage(hWind_, AM_FUNCTOR, 0, reinterpret_cast<LPARAM>(f));
}

这些事件是与实际参数绑定的标准 ATL CP 实现,它们归结为如下内容:

pConnection->Invoke(id, IID_NULL, LOCALE_USER_DEFAULT, DISPATCH_METHOD, &params, &varResult, NULL, NULL);

在 C# 中,处理程序如下所示:

private void onEvent(IMyCollection objs)
{
  int len = objs.Count; // usually 10000 - 25000
  foreach (IMyObj obj in objs)
  {
    // some of the following calls fail with 0x80010100
    int id = obj.id;
    string name = obj.name;
    // etc...
  }
}

===================

那么,上述问题是否会仅仅因为公寓的消息队列中过多地加载了它试图传递的事件而发生呢?或者应该完全阻止消息循环以导致这种行为?

让我们假设消息队列有 2 个连续事件,这些事件评估为“onEvent”调用。第一个输入 C# 托管代码,它尝试重新输入非托管代码,同一个单元。通常,这是允许的,我们经常这样做。什么时候,什么情况下会失败?

谢谢。

4

1 回答 1

2

即使有多个公寓,这也应该适用,前提是:

  • 只有一个线程响应外部事件,例如网络流量、计时器、发布的消息等。
  • 其他线程只为 COM 请求提供服务(即使它们在处理过程中回调到主线程)。

  • 两个线程队列都不会填满,从而阻止 COM 与线程通信。

首先: 看起来有些对象与其他对象不在同一个公寓中。您确定所有对象都在 STA 中创建吗?

您所描述的是典型的死锁 - 两个独立的线程,每个线程都在等待另一个。这就是我期望在不同线程上使用 C# 和 COM 端操作的设计发生的情况。

如果所有对象都在同一个线程上,并且隐藏窗口在该线程上,你应该没问题,所以我认为你需要检查一下。(显然,这包括由 COM 端创建并传递给 C# 端的任何其他对象。)

您可以尝试通过在调试器中按“暂停”并检查每个线程中的代码来调试它(如果您看到 RPCRT*.DLL,这意味着您正在查看代理)。或者,您可以从 C# 和 COM 端以及您的 WndProc 的各个关键点 DebugPrint 当前线程 ID - 它们都应该是相同的。

其次:它应该与多个线程一起工作,前提是只有一个线程生成工作项,而另一个线程只做响应调用的主机 COM 对象(即不生成来自计时器、网络流量、发布消息等的调用) ,在这种情况下,可能是线程队列已满,COM 无法回复调用。

您应该使用受临界区保护的双端队列,而不是使用线程队列。

您可能会维护队列上/下项目的计数器,以查看这是否是问题所在。

于 2012-02-21T15:31:37.773 回答