长话短说:在使用 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, ¶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...
}
}
===================
那么,上述问题是否会仅仅因为公寓的消息队列中过多地加载了它试图传递的事件而发生呢?或者应该完全阻止消息循环以导致这种行为?
让我们假设消息队列有 2 个连续事件,这些事件评估为“onEvent”调用。第一个输入 C# 托管代码,它尝试重新输入非托管代码,同一个单元。通常,这是允许的,我们经常这样做。什么时候,什么情况下会失败?
谢谢。