有什么解决方案可以防止应用程序从活动窗口窃取焦点?
当我启动一个应用程序,切换到做其他事情并且新的应用程序开始接收半句话的文本时,这尤其烦人。
答案1
这是不可能的广泛的对 Windows 内部的操作,你需要克服它。
在日常使用计算机的过程中,有时您需要在操作系统允许您执行另一操作之前执行一项操作,这一点非常重要。为此,操作系统需要将您的注意力锁定在某些窗口上。在 Windows 中,对此行为的控制主要由您使用的各个程序的开发人员负责。
当谈到这个话题时,并不是每个开发人员都能做出正确的决定。
我知道这非常令人沮丧和恼火,但鱼与熊掌不可兼得。在您的日常生活中,可能有很多情况让您完全接受焦点被移到某个 UI 元素或应用程序要求焦点保持锁定在该元素上。但在决定谁是当前领先者时,大多数应用程序在某种程度上是平等的,而且系统永远不可能完美。
不久前,我做了大量研究,想一劳永逸地解决这个问题(但失败了)。我的研究结果可以在烦恼项目页面。
该项目还包括一个应用程序,它通过调用以下命令反复尝试获取焦点:
switch( message ) {
case WM_TIMER:
if( hWnd != NULL ) {
// Start off easy
// SetForegroundWindow will not move the window to the foreground,
// but it will invoke FlashWindow internally and, thus, show the
// taskbar.
SetForegroundWindow( hWnd );
// Our application is awesome! It must have your focus!
SetActiveWindow( hWnd );
// Flash that button!
FlashWindow( hWnd, TRUE );
}
break;
从这个片段我们可以看出,我的研究还集中在我不喜欢的用户界面行为的其他方面。
我尝试解决这个问题的方法是将 DLL 加载到每个新进程中,并挂接导致激活另一个窗口的 API 调用。
最后一部分很简单,这要归功于非常棒的 API 挂接库。我使用了非常棒的mhook 库:
#include "stdafx.h"
#include "mhook-2.2/mhook-lib/mhook.h"
typedef NTSTATUS( WINAPI* PNT_QUERY_SYSTEM_INFORMATION ) (
__in SYSTEM_INFORMATION_CLASS SystemInformationClass,
__inout PVOID SystemInformation,
__in ULONG SystemInformationLength,
__out_opt PULONG ReturnLength
);
// Originals
PNT_QUERY_SYSTEM_INFORMATION OriginalFlashWindow =
(PNT_QUERY_SYSTEM_INFORMATION)::GetProcAddress(
::GetModuleHandle( L"user32" ), "FlashWindow" );
PNT_QUERY_SYSTEM_INFORMATION OriginalFlashWindowEx =
(PNT_QUERY_SYSTEM_INFORMATION)::GetProcAddress(
::GetModuleHandle( L"user32" ), "FlashWindowEx" );
PNT_QUERY_SYSTEM_INFORMATION OriginalSetForegroundWindow =
(PNT_QUERY_SYSTEM_INFORMATION)::GetProcAddress(
::GetModuleHandle( L"user32" ), "SetForegroundWindow" );
// Hooks
BOOL WINAPI
HookedFlashWindow(
__in HWND hWnd,
__in BOOL bInvert
) {
return 0;
}
BOOL WINAPI
HookedFlashWindowEx(
__in PFLASHWINFO pfwi
) {
return 0;
}
BOOL WINAPI
HookedSetForegroundWindow(
__in HWND hWnd
) {
// Pretend window was brought to foreground
return 1;
}
BOOL APIENTRY
DllMain(
HMODULE hModule,
DWORD ul_reason_for_call,
LPVOID lpReserved
) {
switch( ul_reason_for_call ) {
case DLL_PROCESS_ATTACH:
Mhook_SetHook( (PVOID*)&OriginalFlashWindow, HookedFlashWindow );
Mhook_SetHook( (PVOID*)&OriginalFlashWindowEx, HookedFlashWindowEx );
Mhook_SetHook( (PVOID*)&OriginalSetForegroundWindow, HookedSetForegroundWindow );
break;
case DLL_PROCESS_DETACH:
Mhook_Unhook( (PVOID*)&OriginalFlashWindow );
Mhook_Unhook( (PVOID*)&OriginalFlashWindowEx );
Mhook_Unhook( (PVOID*)&OriginalSetForegroundWindow );
break;
}
return TRUE;
}
从我当时的测试来看,这很有效。除了将 DLL 加载到每个新进程中这一部分。可以想象,这没什么好掉以轻心的。我使用了AppInit_DLL 库当时的方法(这根本不够)。
基本上,这很有效。但我从来没有时间写一些适当地将我的 DLL 注入到新进程中。而这方面投入的时间很大程度上掩盖了焦点窃取给我带来的烦恼。
除了 DLL 注入问题之外,还有一种焦点窃取方法,我在 Google Code 上的实现中没有介绍过。一位同事实际上做了一些额外的研究并介绍了该方法。该问题在 SO 上进行了讨论:https://stackoverflow.com/questions/7430864/windows-7-prevent-application-from-losing-focus
答案2
在 Windows 7 中,ForegroundLockTimeout
不再检查注册表项,您可以使用进程监视器来验证这一点。事实上,在 Windows 7 中,它们不允许您更改前台窗口。去阅读关于它的细节,它甚至从 Windows 2000 开始就存在了。
然而,文档很烂,他们互相追逐,想方设法解决这个问题。
因此,出现了一些问题SetForegroundWindow
,或者类似的API函数……
真正正确做到这一点的唯一方法是制作一个小应用程序,定期调用LockSetForegroundWindow
,实际上禁用了对我们有缺陷的 API 函数的任何调用。
如果这还不够(又是一个有问题的 API 调用?),你甚至可以进一步做一些API 监控看看发生了什么,然后你就可以在每个进程上挂接 API 调用之后你就可以摆脱任何调用会弄乱前台。然而讽刺的是,微软并不鼓励这样做……
答案3
答案4
灵感来自Der Hochstapler 的回答因此,我决定编写一个 DLL 注入器,它可以适用于 64 位和 32 位进程,并可防止在 Windows 7 或更新版本上发生焦点窃取:https://blade.sk/stay-focused/
它的工作方式是监视新创建的窗口(使用SetWinEventHook
),并将与 Der Hochstapler 的非常相似的 DLL 注入窗口进程(如果不存在)。它会卸载 DLL 并在退出时恢复原始功能。
从我的测试来看,到目前为止,它运行良好。然而,问题似乎比应用程序调用 更深层次SetForegroundWindow
。例如,当创建一个新窗口时,它会自动进入前台,这也会干扰用户在另一个窗口中输入。
为了处理其他窃取焦点的方法,需要进行更多的测试,并且我很感激有关发生这种情况的场景的任何反馈。
编辑:源代码现已推出。