0x80010100: системный вызов не выполнен, исключение ContextSwitchDeadlock
Короче говоря: в приложении C#, которое работает с COM inproc-сервером (dll), я сталкиваюсь с исключением "0x80010100: системный вызов не выполнен", а в режиме отладки также исключение ContextSwitchDeadlock.
Теперь подробнее
1) Приложение C# инициализирует 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...
}
}
==================
Итак, может ли описанная выше проблема возникнуть только потому, что очередь сообщений квартиры слишком загружена событиями, которые она пытается доставить? Или цикл сообщений должен быть полностью заблокирован, чтобы вызвать такое поведение?
Предположим, что в очереди сообщений есть 2 последовательных события, которые оцениваются как вызов onEvent. Первый вводит управляемый код C#, который пытается повторно ввести неуправляемый код той же квартиры. Обычно это разрешено, и мы делаем это много. Когда, при каких обстоятельствах он может потерпеть неудачу?
Благодарю.
1 ответ
Это должно работать даже с несколькими квартирами при условии, что:
- Только один из потоков отвечает на внешние события, такие как сетевой трафик, таймеры, отправленные сообщения и т. Д.
- Другие потоки обслуживают только COM-запросы (даже если они перезванивают основному потоку во время обработки).
А ТАКЖЕ
- ни одна из цепочек потоков никогда не заполняется, что не позволяет COM взаимодействовать с потоком.
Во-первых: похоже, что некоторые объекты не находятся в одной квартире с другими объектами. Вы уверены, что все объекты создаются в STA?
То, что вы описываете, это классический тупик - два независимых потока, каждый ждет другого. Это то, что я ожидаю, когда этот дизайн будет работать со сторонами C# и COM в разных потоках.
Вы должны быть в порядке, если все объекты находятся в одном потоке, а также скрытое окно находится в этом потоке, поэтому я думаю, что вам нужно это проверить. (Очевидно, что это включает любые другие объекты, которые создаются на стороне COM и передаются на сторону C#.)
Вы можете попробовать отладить это, нажав "pause" в отладчике и проверив, какой код был в каждом потоке (если вы видите RPCRT*.DLL, это означает, что вы смотрите на прокси). В качестве альтернативы вы можете DebugPrint идентифицировать текущий поток из различных критических точек как на C#, так и на стороне COM и в вашем WndProc - все они должны быть одинаковыми.
Во-вторых: он должен работать с несколькими потоками при условии, что только один из потоков генерирует рабочие элементы, а другой ничего не делает, кроме размещения COM-объектов, которые отвечают на вызовы (то есть не генерирует вызовы от таймеров, сетевого трафика, опубликованных сообщений и т. Д.) в этом случае может случиться так, что очередь потока заполнена и COM не сможет ответить на вызов.
Вместо использования очереди потока, вы должны использовать deque, защищенный критическим разделом.
- http://msdn.microsoft.com/en-us/library/windows/desktop/ms644944(v=vs.85).aspx
Существует ограничение в 10000 опубликованных сообщений на одну очередь сообщений. Этот предел должен быть достаточно большим. Если ваше приложение превышает лимит, оно должно быть переработано, чтобы избежать использования большого количества системных ресурсов.