sva*_*agt 204 windows window-focus
是否有任何解决方案可以防止应用程序从活动窗口窃取焦点?
当我启动一个应用程序,切换到做其他事情并且新应用程序开始接收半句文本时,这尤其令人讨厌。
Der*_*ler 53
如果不对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;
Run Code Online (Sandbox Code Playgroud)
正如我们从这段代码中看到的,我的研究还集中在我不喜欢的用户界面行为的其他方面。
我试图解决这个问题的方法是将一个 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;
}
Run Code Online (Sandbox Code Playgroud)
从我当时的测试来看,这很好用。除了将 DLL 加载到每个新进程中的部分。正如人们所想象的那样,没有什么可以掉以轻心的。我当时使用了AppInit_DLLs方法(这根本不够)。
基本上,这很好用。但是我从来没有时间写一些东西来正确地将我的 DLL 注入到新进程中。而在这方面投入的时间在很大程度上掩盖了焦点窃取给我带来的烦恼。
除了 DLL 注入问题,还有一个焦点窃取方法,我在 Google Code 上的实现中没有涉及。一位同事实际上做了一些额外的研究并涵盖了该方法。在 SO 上讨论了这个问题:https : //stackoverflow.com/questions/7430864/windows-7-prevent-application-from-losing-focus
Tam*_*man 24
在 Windows 7 中,ForegroundLockTimeout不再检查注册表项,您可以使用 Process Monitor 进行验证。事实上,在 Windows 7 中,它们不允许您更改前景窗口。去阅读它的详细信息,它甚至从 Windows 2000 开始就存在了。
然而,文档很糟糕,他们互相追逐并找到解决方法。
所以,有一些错误SetForegroundWindow,或者类似的 API 函数......
真正正确地做到这一点的唯一方法是制作一个定期调用的小型应用程序LockSetForegroundWindow,实际上禁用对我们有缺陷的 API 函数的任何调用。
如果这还不够(另一个有问题的 API 调用?),您可以更进一步进行一些API 监控以查看发生了什么,然后您只需将 API 调用挂接到每个进程上,然后您就可以摆脱任何混乱的调用前景。然而,具有讽刺意味的是,微软不鼓励这样做......
Sim*_*ens 18
TweakUI 中有一个选项可以执行此操作。它可以防止可疑的软件开发人员用来强制专注于他们的应用程序的大多数常见技巧。
不过,这是一场持续的军备战争,所以我不知道它是否适用于所有情况。
更新:根据EndangeredMassa 的说法,TweakUI 不适用于 Windows 7。
har*_*ymc 16
我相信可能存在一些混淆,因为有两种“窃取焦点”的方法:(1)窗口进入前台,以及(2)窗口接收按键。
这里提到的问题可能是第二个,其中 windows 通过将自己置于前台来声明焦点 - 无需用户请求或许可。
这里的讨论必须在 XP 和 7 之间分开。
在 XP 中有一个注册表黑客,它使 XP 在防止应用程序窃取焦点方面与 Windows 7 的工作方式相同:
HKEY_CURRENT_USER\Control Panel\Desktop。ForegroundLockTimeout并将其十六进制值设置为30d40。(下面的讨论也主要适用于 XP。)
请理解,Windows 无法完全阻止应用程序窃取焦点并保持功能。例如,如果在文件复制过程中,您的防病毒软件检测到可能存在的威胁,并希望弹出一个窗口要求您采取行动,如果此窗口被阻止,那么您将永远无法理解为什么复制永远不会终止。
在 Windows 7 中,Windows 本身的行为只能进行一种修改,即使用MS-Windows focus-follows-mouse Registry hacks,其中焦点和/或激活始终指向光标下的窗口。可以添加延迟以避免应用程序在桌面上弹出。
请参阅此文章:Windows 7-鼠标悬停使窗口处于活动状态-启用。
否则,必须检测并中和有罪的程序:如果这始终是获得焦点的同一个应用程序,则该应用程序被编程为获得焦点,并且可以通过禁用它从计算机启动来防止这种情况发生,或者使用该应用程序提供的一些设置来避免这种行为。
您可以使用VB 代码中包含的 VBS 脚本,该脚本可识别谁在窃取焦点,作者使用该脚本将罪魁祸首识别为打印机软件的“回拨”更新程序。
当所有其他方法都失败时,如果您发现了这个编程错误的应用程序,那么一个绝望的措施是将其最小化,并希望它不会因此而出现在前面。一种更强大的最小化形式是使用Best Free Application Minimizer 中列出的免费产品之一对托盘进行 最小化。
绝望中的最后一个想法是通过使用诸如Desktops或Dexpot 之类的产品来虚拟地破坏您的桌面,并在另一个桌面而不是默认桌面上完成您的工作。
[编辑]
由于 Microsoft 已停用 Archive Gallery,以下是转载的上述 VB 代码:
Declare Auto Function GetForegroundWindow Lib "user32.dll" () As Integer
Declare Auto Function GetWindowThreadProcessId Lib "user32.dll" (ByVal hwnd As Integer, ByRef procid As Integer) As UInteger
Private Sub Form1_Load(ByVal sender As System.Object, ByVal e As System.EventArgs) Handles MyBase.Load
Me.RichTextBox1.AppendText("Starting up at " & Now & vbCrLf)
End Sub
Private Sub GoingAway(ByVal sender As Object, ByVal e As System.EventArgs) Handles Me.Deactivate, Me.LostFocus
Dim hwnd As Integer = GetForegroundWindow()
' Note that process_id will be used as a ByRef argument
' and will be changed by GetWindowThreadProcessId
Dim process_id As Integer = 1
GetWindowThreadProcessId(hwnd, process_id)
If (process_id <> 1) Then
Dim appExePath As String = Process.GetProcessById(process_id).MainModule.FileName()
Me.RichTextBox1.AppendText("Lost focus at " & Now & " due to " & appExePath & vbCrLf)
Else
Me.RichTextBox1.AppendText("Lost focus due to unknown cause.")
End If
End Sub
Run Code Online (Sandbox Code Playgroud)
受Der Hochstapler 的回答的启发,我决定编写一个 DLL 注入器,它适用于 64 位和 32 位进程,并防止在 Windows 7 或更高版本上窃取焦点:https : //blade.sk/stay-focused/
它的工作方式是SetWinEventHook监视新创建的窗口(使用),并将与 Der Hochstapler 的 DLL 非常相似的 DLL 注入到窗口进程中(如果尚不存在)。它卸载 DLL 并在退出时恢复原始功能。
从我的测试来看,到目前为止它运行良好。然而,这个问题似乎不仅仅是应用程序调用SetForegroundWindow. 例如,当创建一个新窗口时,它会自动进入前台,这也会干扰用户在另一个窗口中键入内容。
为了处理其他的焦点窃取方法,需要更多的测试,我很感激任何关于它发生的场景的反馈。
编辑:源代码现在可用。
| 归档时间: |
|
| 查看次数: |
117254 次 |
| 最近记录: |