过去几天我一直试图为一个应用程序找到一个好的架构,经过一些研究后我终于卡住了,原因就是COM。
有问题的应用程序将有多个GUI线程,他们将为工作线程安排工作项。工作线程将通过CoInitialize(NULL)初始化COM;创建几个COM组件并进入循环,等待WaitForMultipleObjects(2,...)(ExitEvent - 表示应用程序正在关闭,ManualResetEvent - 表示实际上有工作项处理),并且在成功等待时,将处理项目并将它们PostMessage回GUI线程。如果队列为空并且将在队列关键部分内发生,则将在worker内重置ManualResetEvent。
问题在于COM,像往常一样,使得一切都变得更难了......
如果我理解正确,CoInitialize(NULL);创建一个隐藏窗口,在WaitForSingle / MultipleObject / s期间发布的任何消息都可能导致死锁。
所以,我需要调用MsgWaitForMultiple对象。如果消息未正确泵送,则反过来可能会失败。不幸的是,我不太明白如何以正确的方式泵送它们。我是否必须创建自己的消息循环?如果COM决定创建一个消息框,应用程序会崩溃吗?
到目前为止,似乎我必须这样做?
HANDLE hEvents[2] = {};
int ThreadProc(LPVOID lpParam) {
int nRetVal = 0;
CoInitialize(NULL);
CComPtr<ISomething> smthn;
smthn.CoCreateInstance(...);
MSG msg = {};
bool bRun = true;
while(bRun) {
while(PeekMessage(&msg, ??NULL/-1??, 0, 0, PM_REMOVE)) { /*Which one here?*/
if(msg.Message == WM_QUIT) {
bRun = false;
nRetVal = msg.wParam;
break;
}
TranslateMessage(&msg);
DispatchMessage(&msg);
}
if(MsgWaitForMultipleObjects(2, &hEvents, ...)) {
if(exitevent) { bRun = false; nRetVal = 0; }
else if(processevent) { [processdata] }
}
}
smthn.release();
CoUninitialize();
return nRetVal;
}
但是隐藏的窗口,消息框呢,我甚至在正确的道路上呢?
答案 0 :(得分:2)
只需使用CoWaitForMultipleHandles
,它就会在隐藏的COM窗口上进行必要的消息处理,以进行线程间同步。
隐藏窗口属于OleMainThreadWndClass
类,其中OleMainThreadWndName
为标题,但在win9x上,其类为WIN95 RPC Wmsg
。这是隐藏的,这意味着你不能直接使用EnumThreadWindows
来找到它。
答案 1 :(得分:1)
看起来像矫枉过正,但这对我有用:
int waitAndDispatch( HANDLE* event_handle, unsigned int ev_count, DWORD timeout )
{
int rval = -1;
bool bLoop = true; // if the loop should terminate
HANDLE* pHList = new HANDLE[ev_count];
for( unsigned int i = 0; i < ev_count; ++i )
{
pHList[i] = event_handle[i];
}
while( bLoop )
{
DWORD res = ::MsgWaitForMultipleObjects( ev_count, pHList, false, timeout, QS_ALLPOSTMESSAGE | QS_SENDMESSAGE );
if( res == WAIT_OBJECT_0 + ev_count ) // messages arrived
{
MSG tmpMsg;
bool hasMsg = true;
while( bLoop && hasMsg )
{
::PeekMessage( &tmpMsg, 0, 0, 0, PM_NOREMOVE );
if( ::PeekMessage( &tmpMsg, 0, WM_USER, WM_USER, PM_REMOVE ) || // WM_USER for COM
::PeekMessage( &tmpMsg, 0, 0, WM_KEYFIRST - 1, PM_REMOVE ) // all destroy update, ...
)
{
DWORD val = ::WaitForMultipleObjects( ev_count, pHList, false, 0 );
if( val >= WAIT_OBJECT_0 && val <= (WAIT_OBJECT_0 + ev_count) )
{
rval = val - WAIT_OBJECT_0;
bLoop = false;
}
::DispatchMessage( &tmpMsg );
}
else
{
hasMsg = false;
}
}
}
else if( res >= WAIT_OBJECT_0 && res < (WAIT_OBJECT_0 + ev_count) )
{
rval = res - WAIT_OBJECT_0;
bLoop = false;
}
else if( res == WAIT_TIMEOUT )
{
rval = ev_count;
bLoop = false;
}
else
{
rval = -1;
bLoop = false;
}
}
delete[] pHList;
return rval;
}
我不得不为VB6编写这篇文章,并在com隔层上编写线程交互......
如果使用CoInitializeEx( 0, COINIT_MULTITHREADED )
初始化线程单元,则COM调用不会排队到消息队列中。但是你有在不同COM公寓中创建的对象的问题。这些需要整理......