如何监控焦点变化?

11

有时我在打字,很少出现某些东西突然夺取了焦点,我阅读了一些解决方案(甚至包括VB监视器),但它们并不适用于我。是否有任何全局的Windows“句柄”,可以处理任何焦点更改?

无论是C、C++、VB.NET、C#、Anything .NET还是与Windows相关的批处理、PoweShell、VBS脚本...... 只要我能够监视每个焦点更改并将其记录到文件/ cmd窗口/可视化窗口中即可。

类似于:

   void event_OnWindowsFocusChange(int OldProcID, int NewProcID);

这将非常有用。或者可能已经有针对此的工具(但我找不到?)

2个回答

21

其中一种方法是使用Windows UI自动化API。它公开了一个全局焦点变更事件。这里是我用C#编写的快速示例。请注意,您需要添加对UIAutomationClient和UIAutomationTypes的引用。

using System.Windows.Automation;
using System.Diagnostics;

namespace FocusChanged
{
    class Program
    {
        static void Main(string[] args)
        {
            Automation.AddAutomationFocusChangedEventHandler(OnFocusChangedHandler);
            Console.WriteLine("Monitoring... Hit enter to end.");
            Console.ReadLine();
        }

        private static void OnFocusChangedHandler(object src, AutomationFocusChangedEventArgs args)
        {
            Console.WriteLine("Focus changed!");
            AutomationElement element = src as AutomationElement;
            if (element != null)
            {
                string name = element.Current.Name;
                string id = element.Current.AutomationId;
                int processId = element.Current.ProcessId;
                using (Process process = Process.GetProcessById(processId))
                {
                    Console.WriteLine("  Name: {0}, Id: {1}, Process: {2}", name, id, process.ProcessName);
                }
            }
        }
    }
}

1
哇,太棒了!只是它不能检测到焦点切换到Internet Explorer,但这不是问题,我知道它没有窃取我的焦点。现在我要留意那个抢占我焦点的混蛋!嘿嘿……好吧,有点夸张了。谢谢!而且,在Internet Explorer中,它甚至可以检测到每个点击的按钮!太棒了! - user1182183
为什么要在GetProcessById中使用using语句? - CamHart
1
@CamHart 因为 Process 是可丢弃的,当你使用完对象后将其处理掉是一个好习惯。 - Mike Zboray
我想在一个Windows服务(C#)应用程序中实现这个功能,并将数据发送到数据库,但事件没有被触发。有人知道这是否可能吗?谢谢! - Gabriel Grancea
Gabriel - Windows服务在它们自己的专用会话中运行。 它们无法与包含当前登录用户的会话下运行的进程进行交互。 有应对这种限制的策略。 这是一篇很好的文章,解释了所有这些内容。 https://www.codeproject.com/Articles/35773/Subverting-Vista-UAC-in-Both-32-and-64-bit-Archite - Casey Chester

2
您可以使用钩子来监视焦点变化。使用SetWindowsHookEx(),使用WH_SHELL钩子即可完成。回调函数会收到HSHELL_WINDOWACTIVATED通知。
这并不容易实现,特别是在托管语言中,因为它需要一个可注入的DLL。您也无法可靠地区分意图的焦点更改或进程推动窗口并窃取焦点。尽管Windows试图防止这种情况,但AttachThreadInput()方法却是一个后门,可以愚弄该代码。
很容易知道是哪个进程导致了这个问题。毕竟,它试图激活其中一个窗口。卸载该程序是最简单和最好的解决方法。

我有一些Visual Basic代码是可以工作的: http://pastebin.com/H7prRMQZ 但是当我将AppDomain.GetCurrentThreadID()更改为0&并以管理员权限运行应用程序时,它会显示挂钩失败。对此有什么想法吗?如果需要完整的代码:http://pastebin.com/wj5gFMWs - user1182183
你还没有接近。钩子错误,模块句柄错误,语言错误。请注意我的答案中关于使用托管代码的具体警告。如果你真的想追求这个问题,那么你需要这个:http://www.codeproject.com/Articles/18638/Using-Window-Messages-to-Implement-Global-System-H - Hans Passant
这是一段非常好的代码,不幸的是它只能检测到焦点切换到全局钩子测试窗口,而不能检测到其他窗口(我以管理员身份运行它)。 - user1182183

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接