C++ 有没有一种方法可以;延迟“;WM\U激活/WM\U激活EAPP消息?
考虑一个只有一个窗口的应用程序 如果窗口被激活-即如果窗口过程(C++ 有没有一种方法可以;延迟“;WM\U激活/WM\U激活EAPP消息?,c++,winapi,C++,Winapi,考虑一个只有一个窗口的应用程序 如果窗口被激活-即如果窗口过程(WindowProc回调函数)接收到消息WM\u ACTIVATE(或者类似的WM\u ACTIVATEAPP),它将执行一个操作。 (操作是检查打开的文件是否有更改;如果是,则询问用户是否要重新加载文件) 问题是:WM\u ACTIVATE/WM\u ACTIVATEAPP在用户执行诸如单击应用程序的关闭按钮或通过拖动标题栏移动窗口等操作时也会发送。但是,这些消息在收到结果消息(如WM\u CLOSE或WM\u MOVE之前被接收
WindowProc
回调函数)接收到消息WM\u ACTIVATE
(或者类似的WM\u ACTIVATEAPP
),它将执行一个操作。
(操作是检查打开的文件是否有更改;如果是,则询问用户是否要重新加载文件)
问题是:WM\u ACTIVATE
/WM\u ACTIVATEAPP
在用户执行诸如单击应用程序的关闭按钮或通过拖动标题栏移动窗口等操作时也会发送。但是,这些消息在收到结果消息(如WM\u CLOSE
或WM\u MOVE
之前被接收。在这些情况下,人们显然希望等到用户完成操作后再问他任何问题
是否有可能延迟处理
WM_ACTIVATE
/WM_ACTIVATEAPP
消息(在窗口即将激活时发送)并首先处理其他消息(在激活窗口的过程中发送)?关键是(如果我没有弄错的话)我不可能知道在最初处理WM_ACTIVATE
/WM_ACTIVATEAPP
消息时是否会收到另一条消息,那么我应该如何根据将来会发生什么来改变我的行为呢?同时,我无法找到另一条在激活窗口后发送的消息(这基本上是这里需要的)。我认为您不能延迟此消息,因为消息序列是由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;
}
好问题
我不认为有任何方法可以延迟这些消息的处理,它们在逻辑上是以正确的顺序出现的——但正如你的问题所述,你看不到未来
但是,您可以做的是改变处理这些消息的方式,也许可以实现使用计时器。你必须想象一系列可能的事件
一种方法可能是创建一个类来处理这个问题。它处理WM\u ACTIVATE
/WM\u ACTIVATEAPP
。一旦收到此消息,立即启动计时器。如果您收到一个WM_MOVE
,处理它,重置计时器
如果您得到一个WM_CLOSE
运行您的激活代码,那么可以调用默认窗口proc或显式调用destronWindow
如果计时器过期(半秒?您可能需要使用确切的阈值),那么您可以执行激活代码
这就是我脑海中的要点,如果我没有很好地解释我自己,我深表歉意。正如我在上面所评论的,我强烈反对在这里使用计时器 在某些情况下,您不想立即执行操作,因为客户端仍在执行某些操作(移动、调整大小等)。这些操作通常需要捕获鼠标。因此,我的建议是: 当窗口被激活时,在窗口中发布一些命令消息。如果它被关闭,它将永远无法处理该命令 当处理该消息时,检查您是否正在捕获鼠标,以及是否设置了一个标志;当您收到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;
}
不得不放弃这次投票,看来我们得出了同样的结论!我强烈反对在这里使用计时器:如果我缓慢地移动车窗,没有延迟就足够了,但另一方面,长时间的延迟,将允许用户在收到弹出窗口之前修改文件的陈旧内容。@VladFeinstein:没错,但我使用计时器只是为了推迟检查文件更新,直到您收到进一步的消息并知道窗口被激活的原因。因此,如果在
WM\u激活后收到WM\u移动
消息,您必须重置(!!!而不是取消!!!)计时器。因此,在您停止移动窗口(并且不再收到WM_MOVE
消息)后,重置计时器将过期,并检查文件是否有更新(在我的情况下,在您停止移动窗口50毫秒后)。对于WM_SIZE
和所有其他涉及用户操作的消息也必须这样做……这从根本上说是有缺陷的:您的更新不取决于时间,而是取决于状态。例如,如果我抓取您的(非活动)标题栏并暂停,当我按住时,将不会有任何消息、移动或其他。这足够让你更新吗?你看到我下面建议的解决方案了吗?WM_CLOSE
是一个特例,但仅仅因为用户稍微移动了一点窗口就破坏了应用程序的此功能似乎很愚蠢。您可以将“是否要重新加载文件?”对话框设置为非模态,这样用户仍然可以自由地与主窗口进行交互(如果他们关闭主窗口,您只需关闭对话框)。就像数据点一样,当应用程序执行这类操作时,它会让我非常恼火。出于这样或那样的原因,我经常切换窗口,我不认为它会有副作用。我同意@HarryJohnston的观点,从用户体验的角度来看,你所做的任何事情都需要非侵入性。弹出的模型对话框是不可接受的。我讨厌你的软件。找到一种更优雅的方式来实现这一点,比如非侵入式覆盖,这种方式不会导致我所做的任何事情停滞不前。一旦它是非侵入性的,它会立即