10

我写了一个win32应用程序。我自己实现了消息循环,如下所示:

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

              IdleProcess();
     }

我的应用程序中有一个可调整大小的窗口。通常,IdleProcess() 每秒被调用几次。当用户抓住可调整大小窗口的角或边缘时,IdleProcess() 不会再被调用,直到用户释放鼠标按钮。

这里会发生什么?

我尝试用 if 交换内部 while ,但这并没有改变行为。似乎在调整大小开始时,该消息的处理程序在调整大小完成之前不会返回?

有没有办法改变这一点并在每秒调整几次大小期间调用 IdleProcess() ?

谢谢马克

编辑:

我用 if 替换内部 while 的意思是:

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

          IdleProcess();
 }

我的窗口 Proc 有点长,但我在一个小型测试应用程序中得到了相同的行为。这与 VS 项目向导创建的 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

4 回答 4

17

在 Windows 上发生了许多模态操作。Win32 模态操作是指通过启动自己的事件处理循环直到模式完成将应用程序置于“模式”的功能。常见的应用程序模式包括拖放操作、移动/调整大小操作、任何时候弹出需要输入才能继续应用程序的对话框。

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

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

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

于 2010-06-23T15:35:43.603 回答
6

当 DefWindowProc 在 wParam 中使用 SC_MOVE 或 SC_SIZE 处理 WM_SYSCOMMAND 时,它会进入一个循环,直到用户通过释放鼠标按钮或按下 enter 或 escape 来停止它。这样做是因为它允许程序通过处理 WM_PAINT 和 WM_NCPAINT 消息(您仍然应该在窗口过程中接收这些事件)来渲染客户区域(您的小部件或游戏或任何被绘制的地方)以及边框和标题区域。

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

但是,有一种解决方法:自己处理 WM_SYSCOMMAND,调整大小或移动自己。这需要大量的努力,但可能证明是值得的。或者,您可以使用 setjmp/longjmp 在发送 WM_SIZING 时从窗口过程中退出,或者使用相同的行中的 Windows 光纤;不过,这些都是骇人听闻的解决方案。

我在上周末解决了它(使用第一种方法),如果您有兴趣,我已将代码发布到 sourceforge 上的公共领域。请务必阅读自述文件,尤其是警告部分。这是:https ://sourceforge.net/projects/win32loopl/

于 2014-01-20T17:26:49.740 回答
1

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

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

它仍然会阻止你的while/ PeekMessage-loop!WinAPI 只是WndProc直接调用你的。

于 2015-09-27T09:53:03.020 回答
0

在调整大小期间,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() 不会创建或使用任何事件。如果真是这样,事情就会变得复杂得多。

于 2010-06-23T14:28:09.017 回答