捕获到应用程序的所有windows消息



是否有一个全局的WinProc在一个应用程序中的所有窗口或一种方法来捕获所有的绘画消息到应用程序的一段时间?我们使用的一些第三方库添加了自己的winproc控件(例如DevExpress ribbon, Docking Manager)。

我们有一些代码应该在后台线程中运行,但目前不可能。此代码可能需要很长时间才能运行,显然在此期间应用程序会变得无响应。我们不能使用processmessages之类的东西,因为这会导致代码被重新输入。为了解决这个问题,我认为应该可以创建我们自己的ProcessMessages版本来查找特定的消息。我的想法是,我将创建一个带有进度条和取消按钮的小面板,然后只允许取消按钮的窗口句柄的消息。

处理单个消息的简化代码如下所示:

begin
if PeekMessage(aMsg, 0, 0, 0, PM_NOREMOVE) then
begin
Unicode := (aMsg.hwnd = 0) or IsWindowUnicode(aMsg.hwnd);
if Unicode then
MsgExists := PeekMessageW(aMsg, 0, 0, 0, PM_REMOVE)
else
MsgExists := PeekMessageA(aMsg, 0, 0, 0, PM_REMOVE);
if MsgExists then
begin
Result := True;
if aMsg.hwnd = aButtonWindowsHandle then
begin
TranslateMessage(aMsg);
if Unicode then
DispatchMessageW(aMsg)
else
DispatchMessageA(aMsg);
end
else
begin
// WM_PAINT is a special message. If you don't handle a WM_PAINT
// then windows will just stick it back in the queue again.
if (aMsg.message = WM_PAINT) and (aMsg.hwnd <> 0) then
begin
// Queue this paint message so we do an update when the
// processing is finished.
FQueuedPaintMessages.Add(aMsg);
// Paint nothing here otherwise Windows will send it again and
// insist that we paint it.
if BeginPaint(aMsg.hwnd, paintStruct) <> 0 then
EndPaint(aMsg.hwnd, paintStruct);
end;
end;
end;
end;
end;

调用方式如下:

while ProcessMessage(msg) do {loop};

这似乎工作正常,但在测试期间,我捡起了几个情况下,其中一个Windows API调用(PeekMeesage, TranslateMessage, DispatchMessage,或两个绘画调用)触发回调到我们的对接控件的WndProc通过KiUserCallbackDispatcher在ntdll.dll。对接控制最终会触发油漆,这是个问题。我不确定Windows API调用它是什么,因为堆栈跟踪在那一点上缺少一些行。它只发生在我们的一台测试机器上,所以我不能放入断点。

我知道这远非一个理想的解决方案,我们最好重写代码,以便它可以在后台线程中运行,但这将是大量的工作,目前是不可行的。

看起来SetWindowsHookEx与WH_MOUSE_LL可能是一个解决方案,但仍然需要一个消息循环。

与其说是你问题的答案,不如说是你问题的潜在解决方案…

而不是用"cancel"按钮,而是显示一个标签,上面写着"按住ESC取消进程"。然后,在您的进程中,您可以通过定期调用GetAsyncKeyState来获取ESC键的状态。我没有对它进行测试,但我希望它至少"足够好"。

选择的答案给了我一个解决我的问题,但@IInspectable回答了关于捕获所有油漆消息的问题。对于其他正在看这个问题的人。答案是:

"这些都不可能在合理的努力下可靠地工作。这只是与体制抗争的一次笨拙尝试,而体制将会赢得这场抗争。

换句话说。别试着去做。

最新更新