COM + WaitForSingleObject

时间:2010-09-28 15:32:40

标签: c++ multithreading winapi com synchronization

过去几天我一直试图为一个应用程序找到一个好的架构,经过一些研究后我终于卡住了,原因就是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;
}

但是隐藏的窗口,消息框呢,我甚至在正确的道路上呢?

2 个答案:

答案 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公寓中创建的对象的问题。这些需要整理......