订阅.NET中的Vista事件(例如,打开窗口)

时间:2009-04-23 22:35:46

标签: events windows-vista

我正在尝试为Vista构建自己的小工具箱。

其中一个功能是“窗口放置工具”,它将窗口置于保存位置。我能想象的另一个工具是firefox或thunderbird的扩展...

要使这些工具正常工作,我需要它们能够在Vista中捕获“事件”。 举个具体的例子:

  • 资源管理器已打开新窗口
  • 用户启动了Firefox
  • 鼠标移动

对于鼠标案例,有一些C#的例子。 我也知道目录观察者,漂亮的小帮手。

我现在需要的是“新窗口打开事件”

任何想法如何监控这一点,而不是每5秒迭代一次当前窗口列表(我已经知道如何使用DLLImports获取Windows,并使用托管代码获取进程。但是当资源管理器进程打开一个新进程时我没有事件视窗)

感谢您的帮助, 克里斯

4 个答案:

答案 0 :(得分:1)

你所谈论的事情并不简单。

您将需要注册hook,并且您将不得不构建一个在另一个进程的执行上下文中调用的回调过程 - 这不是.NET代码(可能是C代替),并且必须在DLL中。每次发生某类事件时都会调用该回调过程。它将检查它收到的事件并过滤掉您感兴趣的事件,然后向您的应用程序发送您想要的通知(可能通过PostMessage)。然后,您将点击应用程序的主消息循环来拦截这些消息,然后您可以从那里发起.NET事件或任何您想要的事件。

编写钩子回调是很棘手的东西,因为代码在另一个进程中运行,而不是你自己的进程,并且内存管理和并发问题需要一些预先考虑。出于同样的原因,它不会在C#中完成。但理想情况下,这个回调代码会非常小而且非常快,因为它会经常被调用。

另请注意,虽然在Win32中完全“合法”,但这些系统挂钩具有巨大的功能,并且通常被恶意软件用来改变系统的工作方式。因此,如果您尝试在客户的计算机上执行此类操作,则可能会与防病毒软件发生冲突。

另请注意,系统挂钩的深远影响也意味着简单的编程错误会占用整个系统,您可能会在调试时自己发现这些错误;所以在点击“run”之前保存所有内容。

祝你好运!

修改

进行了一些搜索以查看是否有任何方法可以在C#中编写钩子proc,并想出了这个:

How to set a Windows hook in Visual C# .NET

这几乎就是你要找的东西,但并不完全。挂钩过程可以是全局的(这意味着它们可以在每个应用程序上运行)或线程(仅在您的应用程序中运行)。该文件指出:

  

.NET Framework不支持全局挂钩

     

除了   WH_KEYBOARD_LL低级钩子和   WH_MOUSE_LL低级钩子,你不能   实现全局钩子   Microsoft .NET Framework。安装一个   全局钩子,钩子必须有本机   DLL导出将自己注入另一个   需要有效的流程,   调用的一致功能。这个   行为需要DLL导出。该   .NET Framework不支持DLL   出口。托管代码没有概念   一个函数的一致值   指针因为这些功能   指针是构建的代理   动态。

再次,这意味着要监视应用程序视图之外的内容,您需要设置一个不能用.NET编写的全局钩子。

答案 1 :(得分:1)

我有与此完全相同的问题,我认为我有一个可行的解决方案。最初我调查了与'tylerl'提到的类似的选项。然而,在我的情况下,我没有使用'SetWindowsHookEx',而是尝试使用类似的函数'RegisterShellHookWindows'。

不幸的是,这只能成功地向我提供有关何时创建/销毁窗口子集的通知。它提供通知的唯一窗口是任务栏上显示的窗口。

由于我不喜欢攻击其他进程,或编写SetWindowHookEx所需的本机代码,我尝试深入研究.NET 4.0中引入的.NET自动化API,我认为这有你的答案问题(至少在检测窗户何时打开/关闭时)。

以下是使用此API检测正在打开/关闭的窗口的代码段:

    using System.Windows.Automation;

    private void StartMonitoringForWindowEvents()
    {
        Task.Factory.StartNew(() =>
        {
            AutomationEventHandler windowOpenedHandler = new AutomationEventHandler(OnWindowOpened);

            System.Windows.Automation.Automation.AddAutomationEventHandler(
                WindowPattern.WindowOpenedEvent, AutomationElement.RootElement, 
                TreeScope.Descendants, windowOpenedHandler);
        });
    }

    private void OnWindowOpened(object source, AutomationEventArgs eventArgs)
    {
        try
        {
            AutomationElement sourceElement = (AutomationElement)source;

            string message = string.Format(
                "Automation.WindowOpened PID: {0}, Handle: {1}, Name:{2}",
                sourceElement.Current.ProcessId,
                sourceElement.Current.NativeWindowHandle,
                sourceElement.Current.Name);

            Debug.WriteLine(message);

            // for each created window, register to watch for it being closed
            RegisterForClosedWindowEvent(sourceElement);
        }
        catch
        {
        }
    }

    private void RegisterForClosedWindowEvent(AutomationElement element)
    {
        try
        {
            string elementName = element.Current.Name;
            int processId = element.Current.ProcessId;
            int nativeHandle = element.Current.NativeWindowHandle;

            AutomationEventHandler windowClosedHandler = new AutomationEventHandler(
                (ignoreSource, ignoreArgs) => OnWindowClosed(nativeHandle, processId, elementName));

            System.Windows.Automation.Automation.AddAutomationEventHandler(
                WindowPattern.WindowClosedEvent, element,
                TreeScope.Element, windowClosedHandler);
        }
        catch
        {
        }
    }

    private void OnWindowClosed(int nativeHandle, int processId, string elementName)
    {
        string message = string.Format(
            "Automation.WindowClosed PID: {0}, Handle: {1}, Name:{2}",
            processId,
            nativeHandle,
            elementName);

        Debug.WriteLine(message);
    }

您需要添加对程序集“UIAutomationClient”和“UIAutomationClientTypes”的引用。

以下是MSDN文档的链接(您可能特别想查看有关事件的信息): http://msdn.microsoft.com/en-us/library/ms747327.aspx

重要实施注意事项:

1。)请注意,在示例中,我使用任务工厂注册接收自动化事件。在注册自动化事件或通常与自动化API交互时,避免使用UI线程尤为重要。这样做(并且通常很快)会导致死锁。因此,我使用任务工厂来确保通过线程池完成注册。

这也意味着,将在线程池上接收事件......因此,如果您需要执行任何UI更新,则必须将这些更新封送到UI线程。

2。)你还会注意到,我在注册时(使用闭包)捕获了可能被关闭的元素所需的任何信息。这是因为,一旦元素关闭,我们将无法再访问此信息 - 因为元素已被破坏。

菲尔

答案 2 :(得分:0)

答案不是C#(或.Net)特定的。你需要调用SetWindowsHookEx(WH_CBT,...)。这将允许知道何时创建,销毁,移动,调整大小等窗口。您还需要从窗口获取相关信息,以确定是否需要执行某些操作。也许是GetClassInfo,GetWindowLong和GetWindowText。

SetWindowsHookEx的问题在于,为了从每个窗口获取事件,您需要有一个单独的win32 dll,并导出相关函数。虽然您可能已经成功完成了here概述的程序。

答案 3 :(得分:0)

要扩展Joel Lucsy的答案,您需要使用Win32 API。但是,有一个很好的库Managed Windows API,它提供了一个基于面向对象的通用API包装器。它可能有你需要的东西。