有没有办法“延迟”WM_ACTIVATE / WM_ACTIVATEAPP消息?

时间:2015-11-03 18:36:33

标签: c++ winapi

考虑使用单一窗口的应用程序。

如果窗口被激活 - 即窗口过程(WindowProc回调函数)收到消息WM_ACTIVATE(或类似的WM_ACTIVATEAPP) - 它会执行操作。 (操作是检查打开的文件是否有变化;如果是,则询问用户是否要重新加载文件)

问题:当用户执行单击应用程序的关闭按钮或通过拖动标题栏移动窗口等操作时,也会发送WM_ACTIVATE / WM_ACTIVATEAPP。但是,之前会收到这些消息,会收到WM_CLOSEWM_MOVE之类的结果消息。在那些情况下,显然想要等到用户完成他的行动之后才向他询问任何事情。

是否有可能延迟处理WM_ACTIVATE / WM_ACTIVATEAPP消息(在窗口即将被激活时调度)并处理其他消息(在此过程中调度)首先激活窗口?关键是(如果我没有记错的话)我不可能知道如果在最初处理WM_ACTIVATE / WM_ACTIVATEAPP消息时会收到另一条消息,那么如何我是否应该根据将来会发生什么来改变我的行为?同时,在激活窗口后,我无法找到另一条将被发送的消息(这基本上就是这里需要的)。

3 个答案:

答案 0 :(得分:2)

我认为您不能延迟此消息,因为消息序列是由Windows定义的。

实现所需行为的一种方法是使用延迟几毫秒的计时器推迟检查文件更改。这将允许您在窗口被激活后检查文件更新,因为用户想要编辑某些内容或它正在关闭。如果延迟足够短,用户甚至不会注意到......

以下代码段显示了如何实现此目的:

#define IDT_UPDATE_TIMER   1000


UINT_PTR timerId = NULL;   /* needs to be stored along with other window data */

switch (uMsg)
{
   WM_ACTIVATE:
   WM_ACTIVATEAPP:
      /* create timer to delay checking for file updates... */
      timerId = SetTimer(hWnd, IDT_UPDATE_TIMER, 50, NULL);
      break;

   WM_CLOSE:
      /* cancel timer since window is being closed */
      KillTimer(hWnd, timerId);
      break;

   WM_TIMER:
      switch (wParam)
      {
         case IDT_UPDATE_TIMER:
            /* cancel timer to avoid retesting the file every 50ms */
            KillTimer(hWnd, timerId);

            /* check for file updates... */
            break;
      }
      break;
}

答案 1 :(得分:1)

正如我上面评论的那样,我强烈反对在这里使用计时器。

当您不想立即采取行动时,有一些条件,因为客户仍在执行某些操作(移动,调整大小等)。这些操作通常需要捕获鼠标。所以我的建议是:

在激活时向窗口发布一些命令消息。如果它被关闭,它将永远不会处理该命令。

处理该消息时,检查您是否正在捕获鼠标,如果是 - 请设置标记;当你收到WM_CAPTURECHANGED消息时检查它,如果设置了 - 将相同的消息重新发给你自己。

以下是代码:

LRESULT CALLBACK WndProc(HWND hWnd, UINT message, WPARAM wParam, LPARAM lParam)
{
    static bool bWaitForCapture = false;
    switch (message)
    {
    case WM_COMMAND:
        switch (LOWORD(wParam))
        {
        case WM_APP+1:
            if ((bWaitForCapture = ::GetCapture() == hWnd) != true)
                ::OutputDebugString(L"WM_COMMAND : WM_APP+1\n");
            break;
        case IDM_EXIT:
            DestroyWindow(hWnd);
            break;
        default:
            return DefWindowProc(hWnd, message, wParam, lParam);
        }
        break;
    case WM_DESTROY:
        PostQuitMessage(0);
        break;
    case WM_ACTIVATE:
        if (wParam != WA_INACTIVE)
            ::PostMessage(hWnd, WM_COMMAND, WM_APP + 1, 0);
        break;
    case WM_CAPTURECHANGED:
        if (bWaitForCapture)
            ::PostMessage(hWnd, WM_COMMAND, WM_APP + 1, 0);
        break;
    default:
        return DefWindowProc(hWnd, message, wParam, lParam);
    }
    return 0;
}

答案 2 :(得分:0)

好问题!

我认为没有任何方法可以延迟处理这些消息,它们在逻辑上确实以正确的顺序显示 - 但正如您的问题所述,您无法看到未来。< / p>

但是,您可以做的是改变处理这些消息的方式,可能会实现计时器的使用。您必须想象一系列可能的事件。

一种方法可能是创建一个类来处理这个问题。它处理WM_ACTIVATE / WM_ACTIVATEAPP。收到此消息后,立即启动计时器。如果您收到WM_MOVE,请处理它,重置计时器。

如果您获得WM_CLOSE运行激活码,则可以调用默认窗口proc或显式调用DestroyWindow

如果计时器到期(半秒?您可能需要使用确切的阈值),那么您可以执行激活码。

如果我没有很好地解释自己,那就是我头脑中的那种主旨并道歉。