2013-05-27 3 views
0

У меня есть многопоточное приложение и в определенных потоках я создаю окна с использованием ATL CWindowImpl<>. У меня есть статический метод, который я использую в качестве процедуры потока. Мне нужно создать окно в потоке, потому что мне нужно некоторое из моего сообщения с потоком до be synchronous, а PostThreadMessage() явно асинхронно. Когда мое окно получает сообщение WM_DESTROY (обработчик, определяемое MESSAGE_HANDLER макро), он вызывает PostQuitMessage(), как показан в этом методе:Правильное закрывающее окно, созданное в отдельном потоке с использованием ATL

LRESULT MyATLWindowClass::OnDestroy(UINT uMsg, 
            WPARAM wParam, 
            LPARAM lParam, 
            BOOL& bHandled) { 
    ::PostQuitMessage(0); 
    return 0; 
} 

Я использую пользовательское сообщение для резьбы с использованием PostThreadMessage(), чтобы указать на поток что пришло время прекратить свое существование. Обращаясь к этому настраиваемому сообщению, я вызываю метод CWindowImpl::DestroyWindow(), который, похоже, правильно уничтожает окно, когда мой обработчик сообщения OnDestroy получает вызов. Однако, похоже, что нисходящий поток когда-либо получает сообщение WM_QUIT для обработки. Ниже приводится упрощенная версия моей процедуры потока.

unsigned int WINAPI MyATLWindowClass::ThreadProc(LPVOID lpParameter) { 
    // Initialize COM on the thread 
    ::CoInitializeEx(NULL, COINIT_APARTMENTTHREADED); 

    // Create the window using ATL 
    MyATLWindowClass new_window; 
    HWND session_window_handle = new_window.Create(
     /* HWND hWndParent */ HWND_MESSAGE, 
     /* _U_RECT rect */ CWindow::rcDefault, 
     /* LPCTSTR szWindowName */ NULL, 
     /* DWORD dwStyle */ NULL, 
     /* DWORD dwExStyle */ NULL, 
     /* _U_MENUorID MenuOrID */ 0U, 
     /* LPVOID lpCreateParam */ NULL); 

    // Initialize the message pump on the thread. 
    MSG msg; 
    ::PeekMessage(&msg, NULL, WM_USER, WM_USER, PM_NOREMOVE); 

    // Run the message loop 
    BOOL get_message_return_value; 
    while ((get_message_return_value = ::GetMessage(&msg, NULL, 0, 0)) != 0) { 
    if (get_message_return_value == -1) { 
     // GetMessage handling logic taken from MSDN documentation 
     break; 
    } else { 
     if (msg.message == WD_SIGNAL_THREAD_SHUTDOWN) { 
     // Requested thread shutdown, so destroy the window 
     new_window.DestroyWindow(); 
     } else if (msg.message == WM_QUIT) { 
     // Process the quit message and exit the message loop 
     // to terminate the thread 
     break; 
     } else { 
     ::TranslateMessage(&msg); 
     ::DispatchMessage(&msg); 
     } 
    } 
    } 

    // Uninitialize COM on the thread before exiting 
    ::CoUninitialize(); 
    return 0; 
} 

Обратите внимание, что это, кажется, не имеет значения, если я называю DestroyWindow() или если я отправить WM_CLOSE сообщений окна. Насос сообщения потока не принимает WM_QUIT в любом случае. Должно ли сообщение сообщения владельца потока получать такое сообщение? Где мое недоразумение о том, как взаимодействует поток сообщения и насос сообщений окна? Или что мне не хватает о том, как классы окон ATL создают и управляют окнами?

+0

'WM_QUIT' - это прекратить все приложение, и вы просто уничтожаете свое окно ... Почему вы ожидаете, что' WM_QUIT' придет? Это не обязательно, если у вас нет конкретной причины. Если ваш поток здесь предназначен только для размещения окна, то просто отслеживайте «WM_NCDESTROY» в потоке потока, и как только вы его обработали для своего окна, самое время для завершения потока. –

+0

В соответствии с [документацией] (http://msdn.microsoft.com/en-us/library/windows/desktop/ms644945%28v=vs.85%29.aspx) для 'PostQuitMessage()', эта функция API должен отправить сообщение 'WM_QUIT' в очередь сообщений потока и должно быть доступно для каждого потока. Я неверно истолковал документацию? – JimEvans

+2

GetMessage возвращает 0 для сообщения WM_QUIT, поэтому цикл while завершается без выполнения цикла. Поэтому вам не нужен тест для == WM_QUIT –

ответ

1

GetMessage() никогда не возвращает WM_QUIT. Это сообщение заставляет его возвращать 0 вместо этого, предназначенный для прекращения вашего цикла сообщений.

Остерегайтесь значительных опасностей использования PostThreadMessage(). Он должен никогда не использовать в потоке, который также отображает окна, как тот, который вы используете. Проблема в том, что он не принимает аргумент HWND. Таким образом, только сообщение может видеть сообщение, оно не будет доставлено в любое окно с DispatchMessage(). Это происходит не так, когда вводится модальный цикл сообщений, который находится вне вашего контроля. Подобно модальному циклу, который заставляет MessageBox работать. Или тот, который Windows использует, чтобы позволить пользователю изменять размер окна. Или тот, который использует DialogBox(). Etcetera. Всегда используйте PostMessage(), используйте свой собственный номер сообщения.

+0

Я просто укажу здесь, что мое окно никогда, никогда, никогда не отображается пользователю, и просто используется как удобство синхронизации для связи между потоками. Это поведение, которое я хочу. Если это приложение когда-либо на самом деле начинает рисовать окна на экране, этот код необходимо будет реорганизовать в любом случае. Кстати, я перешел в «PostThreadMessage», потому что просто отправляя «WM_CLOSE» в окно (с соответствующим обработчиком сообщений) и ожидая выхода потока, он будет сильно сбой во время выполнения C. С другой стороны, этот подход работает. – JimEvans

+0

О, и для тех, кто держит счет дома, это приложение с открытым исходным кодом, в частности приложение IEDriverServer.exe проекта [Selenium project] (http://code.google.com/p/selenium/). Если кто-то хочет взломать мою явную некомпетентность при программировании Windows, будь моим гостем. [Патчи с благодарностью приняты] (https://github.com/SeleniumHQ/selenium). – JimEvans

0

Некоторые поздние дополнительные мысли. Вы могли бы благополучно завершить свой цикл обработки сообщений, как только вы обнаружили WD_SIGNAL_THREAD_SHUTDOWN:

if (msg.message == WD_SIGNAL_THREAD_SHUTDOWN) { 
    // Requested thread shutdown, so destroy the window 
    new_window.DestroyWindow(); 
    break; // exit the message loop 
    } 

DestroyWindow является синхронным вызовом, окно будет полностью разрушено, прежде чем он вернется, и вы можете выйти из цикла. Таким образом, публикация WM_QUIT будет излишней.

Кроме того, вы можете использовать окно message-only, если окна невидимы, и его единственная цель - обрабатывать сообщения.

Смежные вопросы