长话短说:在使用COM inproc-server (dll)的C#应用程序中,我遇到了"0x80010100:系统调用失败“异常,在调试模式下也遇到了ContextSwitchDeadlock异常。
现在更详细地说:
1) C# app初始化STA,创建一个COM对象(注册为“公寓”);然后在订阅它的连接点,并开始使用该对象。
2)在某些阶段,COM对象生成许多事件,将在同一单元中创建的COM对象的一个非常大的集合作为参数传递。
3) C#端的事件处理程序处理上述集合,偶尔调用对象的一些方法。在某些阶段,后一种调用开始失败,出现上述异常。
在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, ¶ms, &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...
}
}==================
那么,仅仅因为公寓的消息队列被它试图传递的事件加载得太多,就会发生上述问题吗?或者消息循环应该被完全阻塞才能导致这样的行为?
让我们假设消息队列有两个连续的事件,它们的计算结果是"onEvent“调用。第一个进入C#托管代码,它试图重新进入非托管代码,即同一单元。通常,这是允许的,而且我们经常这样做。什么时候,在什么情况下,它会失败?
谢谢。
发布于 2012-02-21 23:31:38
这应该适用于多套公寓,但前提是:
和
首先:看起来有些对象和其他对象不在同一单元中。您确定所有对象都在STA中创建吗?
您所描述的是一个典型的死锁--两个独立的线程,每个线程都在等待另一个线程。这就是我期望在不同线程上使用C#和COM端运行设计时会发生的事情。
如果所有的对象都在同一个线程上,并且隐藏窗口在那个线程上,那么你应该没问题,所以我认为你需要检查这一点。(显然,这包括由COM端创建并传递给C#端的任何其他对象。)
您可以尝试通过在调试器中按"pause“并检查每个线程中有哪些代码来调试它(如果您看到RPCRT*.DLL,这意味着您正在查看代理)。或者,您可以从C#和COM端的不同临界点以及您的WndProc中DebugPrint当前线程ID -它们都应该是相同的。
其次:它应该与多线程一起工作,前提是只有一个线程生成工作项,而另一个线程只做响应调用的宿主COM对象(即不从计时器、网络流量、发布的消息等生成调用),在这种情况下,可能是线程队列已满,COM无法响应调用。
您应该使用由临界区保护的双队列,而不是使用线程队列。
每个消息队列最多只能发送10,000条消息。这个限制应该足够大。如果您的应用程序超出了限制,则应对其进行重新设计,以避免消耗太多系统资源。
您可以维护一个队列上/队列外的项目计数器,以查看这是否是问题所在。
https://stackoverflow.com/questions/9376773
复制相似问题