Добрый день!
Столкнулся со следующей проблемой. Имеется inproc ActiveX компонент. Он генерирует события в отдельном потоке, причем с довольно большой частотой.
Событийный поток занимается обслуживанием очереди событий. Я привожу код полностью процедуры потока.
void EventManager::Routine()
{
HRESULT hr = E_FAIL;
tstring name;
vector<ml::com::Variant> args;
com_ptr<IDispatch, IID_IDispatch> pIEvent;
com_ptr<IEnumConnections, IID_IEnumConnections> pEnumConnections;
// Блокировка потока, если список стал пустым
if ( m_EventList.empty() )
{
mutex::scoped_lock lock(monitor);
m_NewEvent.wait(lock);
}
// Предотвращаем одновременную генерацию событий из разных потоков.
// (При использовании нескольких экземпляров компоненты в одном процессе)
ExternalLock ext_lock;
mutex::scoped_lock cs_InvokeEvent(m_InvokeEventMutex);
if ( !ExtractEvent(name, args) )
return;
DispParams dp(args);
cs_InvokeEvent.unlock();
hr = pIConnectionPoint->EnumConnections( &pEnumConnections );
cs_InvokeEvent.lock();
if (hr == RPC_E_CALL_CANCELED)
return;
ML_ASSERT( SUCCEEDED(hr) );
CONNECTDATA cd;
cs_InvokeEvent.unlock();
hr = pEnumConnections->Next(1, &cd, NULL);
cs_InvokeEvent.lock();
if (hr == RPC_E_CALL_CANCELED)
return;
ML_ASSERT( SUCCEEDED(hr) );
cs_InvokeEvent.unlock();
hr = cd.pUnk->QueryInterface(IID_IDispatch, (void**)&pIEvent);
cs_InvokeEvent.lock();
if (hr == RPC_E_CALL_CANCELED)
return;
ML_ASSERT( SUCCEEDED(hr) );
BStr memberName( TtoW::from(name).c_str() );
DISPID dispid;
cs_InvokeEvent.unlock();
hr = pITypeInfo->GetIDsOfNames( memberName, 1, &dispid);
cs_InvokeEvent.lock();
if (hr == RPC_E_CALL_CANCELED)
return;
ML_ASSERT( SUCCEEDED(hr) );
cs_InvokeEvent.unlock();
hr = pIEvent->Invoke(dispid, IID_NULL,
GetUserDefaultLCID(), DISPATCH_METHOD,
dp.get(), NULL, NULL, NULL);
cs_InvokeEvent.lock();
if (hr == RPC_E_CALL_CANCELED)
return;
ML_ASSERT( SUCCEEDED(hr) );
}
При старте событийный поток вызывает CoInitialize(NULL).
bool EventManager::InitDone(bool fInitDone)
{
if (fInitDone)
{
CoInitialize(NULL);
CoEnableCallCancellation(NULL);
m_ThreadID = GetCurrentThreadId();
}
else
{
CoUninitialize();
}
return true;
}
Если клиент, работает только с одним экземпляром COM объекта, то все отлично.
Как только клиент использует несколько экземпляров, все падает в тот момент, когда COM объекты начинают генерирование событий. Причем такой эффект не наблюдался на 2-х компьютерах, на которых велась разработка, он проявился на компьютере заказчика уже на объекте, где не представляется возможным подключиться отладчиком.
Долгие размышления привели меня к мысли использовать синглетный мьютекс в процедуре потока:
// Предотвращаем одновременную генерацию событий из разных потоков.
// (При использовании нескольких экземпляров компоненты в одном процессе)
ExternalLock ext_lock;
т. е. мьютекс запрещает одновременное обращение к интерфейсу IDispatch из разных потоков.
Таким образом все работает, но мне непонятно почему проблема не проявилась сразу на компах разработчиков.
И вообще, правильно ли такое решение? Может в COM имеется более изящный подход к подобным проблемам?