COM + WaitForSingleObject - PullRequest
       30

COM + WaitForSingleObject

4 голосов
/ 28 сентября 2010

Я пытался найти хорошую архитектуру для одного приложения в течение последних нескольких дней, и после некоторого исследования я, наконец, застрял, и причина в COM.

У рассматриваемого приложения будетнесколько потоков GUI, и они будут планировать рабочие элементы для рабочего потока.Рабочий поток инициализирует COM с помощью CoInitialize (NULL); создаст несколько компонентов COM и перейдет в цикл, который будет ожидать WaitForMultipleObjects (2, ...) (ExitEvent - чтобы указать, что приложение закрывается, а ManualResetEvent - чтобы указатьчто есть действительно рабочие элементы для обработки), и при успешном ожидании обработает элементы и отправит их обратно в потоки GUI.ManualResetEvent будет сброшено внутри работника, если очередь будет пустой и произойдет в критической секции очереди.

Проблема в том, что COM, как обычно, делает ВСЕ 1000x сложнее ...

ЕслиЯ правильно понимаю, 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 ]

2 голосов
/ 30 сентября 2010

Просто используйте CoWaitForMultipleHandles, и он выполнит необходимую перекачку сообщений в скрытом окне COM для синхронизации между потоками.

Скрытое окно имеет класс OleMainThreadWndClass с OleMainThreadWndName как заголовок, но на win9x его класс WIN95 RPC Wmsg.Он скрыт, что означает, что вы не можете использовать прямую EnumThreadWindows, чтобы найти его.

1 голос
/ 28 сентября 2010

Похоже на излишество, но у меня это сработало:

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 и его взаимодействий с потоками через ком-компартменты ....

Если вы инициализируетев вашей теме с CoInitializeEx( 0, COINIT_MULTITHREADED ) ваши COM-вызовы не будут помещены в очередь сообщений.Но тогда у вас есть проблема объектов, созданных в разных COM квартирах.Их нужно маршалировать ...

...