Win32: мое приложение зависает, пока пользователь изменяет размер окна - PullRequest
8 голосов
/ 23 июня 2010

Я пишу приложение Win32.Я сам реализовал цикл сообщений следующим образом:

     bool programcontinue = true;
     while(programcontinue)
     {
              while (PeekMessage(&Msg, NULL, 0, 0, PM_REMOVE))
              {
                       TranslateMessage(&Msg);
                       DispatchMessage(&Msg);
              }

              IdleProcess();
     }

В моем приложении есть окно с изменяемым размером.Обычно IdleProcess () вызывается несколько раз в секунду.Когда пользователь захватывает угол или край окна с изменяемым размером, IdleProcess () больше не вызывается, пока пользователь не отпустит кнопку мыши.

Что здесь происходит?

Я попытался обменятьсявнутреннее время с if, но это не меняет поведение.Кажется, что когда начинается изменение размера, обработчик этого сообщения не возвращается, пока изменение размера не будет выполнено?

Есть ли способ изменить это и вызвать IdleProcess () во время изменения размера несколько раз в секунду?

Спасибо, Марк

РЕДАКТИРОВАТЬ:

Что я имею в виду, если заменить внутреннее время на if, если:

 bool programcontinue = true;
 while(programcontinue)
 {
          if (PeekMessage(&Msg, NULL, 0, 0, PM_REMOVE))  // <<<<
          {
                   TranslateMessage(&Msg);
                   DispatchMessage(&Msg);
          }

          IdleProcess();
 }

Мой процесс Proc немного длинен, нополучить такое же поведение с небольшим тестовым приложением.Это идентично wndproc, который создает Мастер проекта VS:

LRESULT CALLBACK WndProc(HWND hWnd, UINT message, WPARAM wParam, LPARAM lParam)
{
    int wmId, wmEvent;
    PAINTSTRUCT ps;
    HDC hdc;

    switch (message)
    {
    case WM_COMMAND:
        wmId    = LOWORD(wParam);
        wmEvent = HIWORD(wParam);
        // Parse the menu selections:
        switch (wmId)
        {
        case IDM_ABOUT:
            DialogBox(hInst, MAKEINTRESOURCE(IDD_ABOUTBOX), hWnd, About);
            break;
        case IDM_EXIT:
            DestroyWindow(hWnd);
            break;
        default:
            return DefWindowProc(hWnd, message, wParam, lParam);
        }
        break;
    case WM_PAINT:
        hdc = BeginPaint(hWnd, &ps);
        // TODO: Add any drawing code here...
        EndPaint(hWnd, &ps);
        break;
    case WM_DESTROY:
        PostQuitMessage(0);
        break;
    default:
        return DefWindowProc(hWnd, message, wParam, lParam);
    }
    return 0;
}

Ответы [ 4 ]

14 голосов
/ 23 июня 2010

Существует ряд модальных операций, которые происходят в Windows.Модальные операции Win32 относятся к функциям, которые переводят приложение в «режим», запуская собственный цикл обработки событий до завершения режима.Обычные режимы приложения включают операции перетаскивания, операции перемещения / размера, каждый раз, когда появляется диалоговое окно, которое требует ввода, прежде чем приложение сможет продолжить.

Итак, что происходит: ваш цикл сообщений НЕ выполняется.Ваше окно получило сообщение WM_LBUTTONDOWN, которое вы передали в DefWindowProc.DefWindowProc определил, что пользователь пытается интерактивно изменить размер или переместить окно, и ввел модальную функцию изменения размера / перемещения.Эта функция находится в цикле обработки сообщений, отслеживая сообщения мыши, так что она может перехватывать их, чтобы обеспечить интерактивную настройку размера, и завершается только после завершения операции определения размера - обычно пользователем, отпускающим удерживаемую кнопку, или нажатием клавиши escape.

Вы получаете уведомление об этом - DefWindowProc отправляет сообщения WM_ENTERSIZEMOVE и WM_EXITSIZEMOVE при входе и выходе из модального цикла обработки событий.

Чтобы продолжить генерировать «неактивные» сообщения, обычно создайте таймер (SetTimer) перед вызовом модальной функции - или при получении сообщения о том, что DefWindowProc вводит модальную функцию - модальный цикл будет продолжать отправлять сообщения WM_TIMER ... и вызывать idle proc из обработчика сообщений таймера.Уничтожьте таймер, когда вернется модальная функция.

5 голосов
/ 20 января 2014

Когда DefWindowProc обрабатывает WM_SYSCOMMAND с помощью SC_MOVE или SC_SIZE в wParam, он входит в цикл, пока пользователь не остановит его, отпустив кнопку мыши или нажав клавишу ввода или выхода.Это происходит потому, что она позволяет программе отображать как клиентскую область (где нарисованы ваши виджеты или игра, так и все остальное), а также границы и заголовки, обрабатывая сообщения WM_PAINT и WM_NCPAINT (вы все равно должны получать эти события в своей процедуре окна).

Он отлично работает для обычных приложений Windows, которые выполняют большую часть своей обработки внутри своей оконной процедуры в результате получения сообщений.Это влияет только на программы, которые выполняют обработку вне оконной процедуры, такие как игры (которые обычно являются полноэкранными и не затрагиваются в любом случае).

Однако, есть способ обойти это: обработайте WM_SYSCOMMAND самостоятельно, измените размер или переместите себя,Это требует больших усилий, но может оказаться, что оно того стоит.В качестве альтернативы, вы можете использовать setjmp / longjmp для выхода из оконной процедуры при отправке WM_SIZING или Windows Fibers по тем же линиям;хотя это хакерские решения.

Я решил (используя первый метод) на прошлых выходных, если вам интересно, я опубликовал код в открытом доступе на sourceforge.Просто не забудьте прочитать README, особенно раздел предостережения.Вот оно: https://sourceforge.net/projects/win32loopl/

1 голос
/ 27 сентября 2015

Вы все еще можете получить сообщение WM_PAINT, вам просто нужно сообщить WinAPI, что вы этого хотите (см. Руководства по NeHe OpenGL):

windowClass.style           = CS_HREDRAW | CS_VREDRAW | CS_OWNDC;   // Redraws The Window For Any Movement / Resizing

Это все равно заблокирует ваш while / PeekMessage -попад! WinAPI просто вызывает ваш WndProc напрямую.

0 голосов
/ 23 июня 2010

Во время изменения размера Windows отправляет довольно много сообщений в вашу программу.Я не доказал это, но поведение, которое вы описываете, знакомо.Я бы предложил вызывать вашу функцию IdleProcess () также в цикле while (...) для определенных событий, таких как WM_SIZING, которые ваше приложение будет часто получать при изменении размера окна:

 bool programcontinue = true;
 while(programcontinue)
 {
          while (PeekMessage(&Msg, NULL, 0, 0, PM_REMOVE))
          {
                   TranslateMessage(&Msg);
                   DispatchMessage(&Msg);
                   if(Msg.message == WM_SIZING)
                       IdleProcess();
          }

          IdleProcess();
 }

Будьте внимательныэто предполагает, что IdleProcess () не создает и не потребляет никаких событий.Если это так, все становится намного сложнее.

...