简体   繁体   English

在窗口打开时收到通知的最有效方式

[英]Most Efficient Way for getting notified on window open

I am writing an app (C# and WPF in .NET 4.0) that needs to get open windows and close them if they are not in it's white-list. 我正在编写一个应用程序(.NET 4.0中的C#和WPF),它需要打开窗口并关闭它们,如果它们不在它的白名单中。

So far, using EnumDesktopWindows Windows API from User32.dll , I can enumerate all open windows in about 10 ms on my machine. 到目前为止,使用来自User32.dll EnumDesktopWindows Windows API,我可以在我的机器上大约10毫秒内枚举所有打开的窗口。 As you've probably guessed by now, I need to do this in small periods of time to be as quick as possible and on the other hand selecting small time periods will put a big overhead on the system. 正如您现在可能已经猜到的那样,我需要在很短的时间内尽快做到这一点,另一方面选择较小的时间段会给系统带来很大的开销。

The question is, "Is there any way to get notified when a window is opened (like using an event)? Either way, what is the most efficient way of doing this? 问题是,“打开窗口时是否有任何方法可以获得通知(如使用事件)?无论哪种方式,最有效的方法是什么?

You can Hook on to Shell to receive messages by using RegisterWindowMessage and RegisterShellHookWindow API functions. 您可以使用RegisterWindowMessageRegisterShellHookWindow API函数挂钩到Shell以接收消息。

You will need the following Interop imports: 您将需要以下Interop导入:

public static class Interop
{
    public enum ShellEvents : int
    {
        HSHELL_WINDOWCREATED = 1,
        HSHELL_WINDOWDESTROYED = 2,
        HSHELL_ACTIVATESHELLWINDOW = 3,
        HSHELL_WINDOWACTIVATED = 4,
        HSHELL_GETMINRECT = 5,
        HSHELL_REDRAW = 6,
        HSHELL_TASKMAN = 7,
        HSHELL_LANGUAGE = 8,
        HSHELL_ACCESSIBILITYSTATE = 11,
        HSHELL_APPCOMMAND = 12
    }
    [DllImport("user32.dll", EntryPoint = "RegisterWindowMessageA", CharSet = CharSet.Ansi, SetLastError = true, ExactSpelling = true)]
    public static extern int RegisterWindowMessage(string lpString);
    [DllImport("user32", CharSet = CharSet.Ansi, SetLastError = true, ExactSpelling = true)]
    public static extern int DeregisterShellHookWindow(IntPtr hWnd);
    [DllImport("user32", CharSet = CharSet.Ansi, SetLastError = true, ExactSpelling = true)]
    public static extern int RegisterShellHookWindow(IntPtr hWnd);
    [DllImport("user32", EntryPoint = "GetWindowTextA", CharSet = CharSet.Ansi, SetLastError = true, ExactSpelling = true)]
    public static extern int GetWindowText(IntPtr hwnd, System.Text.StringBuilder lpString, int cch);
    [DllImport("user32", EntryPoint = "GetWindowTextLengthA", CharSet = CharSet.Ansi, SetLastError = true, ExactSpelling = true)]
    public static extern int GetWindowTextLength(IntPtr hwnd);
}

To be able to hook on to the shell, you'll need a class that inherits from Form and overrides the WndProc function. 为了能够挂接到shell,您需要一个继承自Form的类并重写WndProc函数。 You can make this Form to have an Event that will be raised when a Window change its state. 您可以使此窗体具有在窗口更改其状态时将引发的事件。

public class SystemProcessHookForm : Form
{
    private readonly int msgNotify;
    public delegate void EventHandler(object sender, string data);
    public event EventHandler WindowEvent;
    protected virtual void OnWindowEvent(string data)
    {
        var handler = WindowEvent;
        if (handler != null)
        {
            handler(this, data);
        }
    }

    public SystemProcessHookForm()
    {
        // Hook on to the shell
        msgNotify = Interop.RegisterWindowMessage("SHELLHOOK");
        Interop.RegisterShellHookWindow(this.Handle);
    }

    protected override void WndProc(ref Message m)
    {
        if (m.Msg == msgNotify)
        {
            // Receive shell messages
            switch ((Interop.ShellEvents)m.WParam.ToInt32())
            {
                case Interop.ShellEvents.HSHELL_WINDOWCREATED:
                case Interop.ShellEvents.HSHELL_WINDOWDESTROYED:
                case Interop.ShellEvents.HSHELL_WINDOWACTIVATED:
                    string wName = GetWindowName(m.LParam);
                    var action = (Interop.ShellEvents)m.WParam.ToInt32();
                    OnWindowEvent(string.Format("{0} - {1}: {2}", action, m.LParam, wName));
                    break;
            }
        }
        base.WndProc(ref m);
    }

    private string GetWindowName(IntPtr hwnd)
    {
        StringBuilder sb = new StringBuilder();
        int longi = Interop.GetWindowTextLength(hwnd) + 1;
        sb.Capacity = longi;
        Interop.GetWindowText(hwnd, sb, sb.Capacity);
        return sb.ToString();
    }

    protected override void Dispose(bool disposing)
    {
        try { Interop.DeregisterShellHookWindow(this.Handle); }
        catch { }
        base.Dispose(disposing);
    }
}

And then, in your Main function of your application, you can have for example: 然后,在您的应用程序的主要功能中,您可以拥有例如:

static void Main(string[] args)
{
    var f = new SystemProcessHookForm();
    f.WindowEvent += (sender, data) => Console.WriteLine(data); 
    while (true)
    {
        Application.DoEvents();
    }
}

Output sample: 输出样本: 在此输入图像描述

Use the System.Windows.Automation namespace. 使用System.Windows.Automation命名空间。

Example (taken from The Old New Thing ) which waits for a specific process to open a dialog box, then dismisses it: 示例(取自旧新事物 )等待特定进程打开对话框,然后解除它:

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

class Program
{
    [STAThread]
    public static void Main(string[] args)
    {     
        Automation.AddAutomationEventHandler(
            WindowPattern.WindowOpenedEvent,
            AutomationElement.RootElement,
            TreeScope.Children,
            (sender, e) =>
            {
                var element = sender as AutomationElement;

                Console.WriteLine("new window opened");
            });

        Console.ReadLine();

        Automation.RemoveAllEventHandlers();
    }
}

如果您从其他应用程序访问Windows,这不是一件容易的事,但您可以尝试在Windows中使用Hook

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM