Win32:我的应用程序在用户调整窗口大小时冻结

时间:2010-06-23 13:33:41

标签: winapi loops message resize freeze

我写了一个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有点冗长,但我在一个小测试应用程序中得到了同样的行为。这与VS Project Wizard创建的wndproc相同:

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 个答案:

答案 0 :(得分:14)

Windows上会发生许多模态操作。 Win32模态操作指的是通过启动自己的事件处理循环将应用程序置于“模式”直到模式完成的函数。常见的应用程序模式包括拖放操作,移动/大小操作,在应用程序可以继续之前弹出需要输入的对话框。

所以发生的事情是:你的消息循环没有被运行。 您的窗口收到了传递给DefWindowProc的WM_LBUTTONDOWN消息。 DefWindowProc确定用户正在尝试以交互方式调整窗口大小或移动窗口并输入大小/移动模态函数。此功能在消息处理循环中查看鼠标消息,以便它可以拦截它们以提供交互式大小调整体验,并且仅在调整大小操作完成时退出 - 通常是通过用户释放保持按钮或按下escape。 / p>

您收到通知 - DefWindowProc在进入和退出模态事件处理循环时发送WM_ENTERSIZEMOVE和WM_EXITSIZEMOVE消息。

要继续生成“空闲”消息,通常在调用模态函数之前创建一个计时器(SetTimer) - 或者当收到DefWindowProc正在进入模态函数的消息时 - 模态循环将继续调度WM_TIMER消息...并从定时器消息处理程序调用idle proc。当模态函数返回时销毁计时器。

答案 1 :(得分:5)

当DefWindowProc在wParam中使用SC_MOVE或SC_SIZE处理WM_SYSCOMMAND时,它会进入一个循环,直到用户通过释放鼠标按钮或按Enter或escape来停止它。这样做是因为它允许程序通过处理WM_PAINT和WM_NCPAINT消息来呈现客户区(您的小部件或游戏或其他任何内容)以及边框和标题区域(您仍应在Window Procedure中接收这些事件)。

它适用于普通的Windows应用程序,这些应用程序由于接收消息而在其Window Procedure中进行大部分处理。它只影响在窗口过程之外进行处理的程序,例如游戏(通常是全屏但不受影响)。

然而,有一种解决方法:自己处理WM_SYSCOMMAND,调整大小或移动自己。这需要付出很多努力,但可能证明是值得的。或者,您可以使用setjmp / longjmp在发送WM_SIZING时从Window Procedure中逃脱,或者沿着相同的行使用Windows Fibers;这些都是黑客的解决方案。

我在上周末解决了它(使用第一种方法),如果您感兴趣我已经将代码发布到sourceforge上的公共域。只需确保阅读README,特别是警告部分。这是:https://sourceforge.net/projects/win32loopl/

答案 2 :(得分:1)

您仍然可以收到WM_PAINT消息,您只需告诉WinAPI您想要它(在NeHe OpenGL教程中看到):

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

它仍会阻止你的while / PeekMessage循环! WinAPI只是直接调用WndProc

答案 3 :(得分:0)

在调整大小期间,Windows会向您的程序发送相当多的消息。我没有证明这一点,但你描述的行为很熟悉。我建议在while(...)循环中调用你的函数IdleProcess()来处理某些事件,例如你的应用程序在窗口大小调整期间经常会收到的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()不会创建或使用任何事件。如果是这样的话,事情会变得复杂得多。