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

Ara*_*shv 9 c# windows api wpf winapi

我正在编写一个应用程序(.NET 4.0中的C#和WPF),它需要打开窗口并关闭它们,如果它们不在它的白名单中.

到目前为止,使用EnumDesktopWindowsWindows API User32.dll,我可以在我的机器上大约10毫秒枚​​举所有打开的窗口.正如您现在可能已经猜到的那样,我需要在很短的时间内尽快做到这一点,另一方面选择较小的时间段会给系统带来很大的开销.

问题是,"打开窗口时是否有任何方法可以获得通知(如使用事件)?无论哪种方式,最有效的方法是什么?

the*_*000 18

您可以使用RegisterWindowMessageRegisterShellHookWindow API函数挂钩到Shell以接收消息.

您将需要以下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);
}
Run Code Online (Sandbox Code Playgroud)

为了能够挂接到shell,您需要一个继承自Form的类并重写WndProc函数.您可以使此窗体具有在窗口更改其状态时将引发的事件.

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);
    }
}
Run Code Online (Sandbox Code Playgroud)

然后,在您的应用程序的主要功能中,您可以拥有例如:

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

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


Mit*_*tch 9

使用System.Windows.Automation命名空间.

示例(取自旧新事物)等待特定进程打开对话框,然后解除它:

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();
    }
}
Run Code Online (Sandbox Code Playgroud)

  • 我检查了它,它非常简单。感谢您的评论,但是从安全的角度来看,它有一件令人讨厌的事情,这使它不适合。尽管一开始看起来很棒,但事实证明,在窗口完成初始化屏幕上所需的所有内容之前,不会触发该事件!例如,如果您打开多个 `eventvwr.msc` 实例,每个实例都需要花费大量时间在开始页面中填充摘要,这会导致事件在窗口完成创建摘要之前未触发(大约 8-10 秒) (2认同)

小智 0

如果您从其他应用程序访问窗口,这将不是一件容易的任务,但您可以尝试在窗口中使用Hooks