Welcome to OGeek Q&A Community for programmer and developer-Open, Learning and Share
Welcome To Ask or Share your Answers For Others

Categories

0 votes
217 views
in Technique[技术] by (71.8m points)

c# - 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.

So far, using EnumDesktopWindows Windows API from User32.dll, I can enumerate all open windows in about 10 ms on my machine. 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?

See Question&Answers more detail:os

与恶龙缠斗过久,自身亦成为恶龙;凝视深渊过久,深渊将回以凝视…
Welcome To Ask or Share your Answers For Others

1 Reply

0 votes
by (71.8m points)

You can Hook on to Shell to receive messages by using RegisterWindowMessage and RegisterShellHookWindow API functions.

You will need the following Interop imports:

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. 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: enter image description here


与恶龙缠斗过久,自身亦成为恶龙;凝视深渊过久,深渊将回以凝视…
OGeek|极客中国-欢迎来到极客的世界,一个免费开放的程序员编程交流平台!开放,进步,分享!让技术改变生活,让极客改变未来! Welcome to OGeek Q&A Community for programmer and developer-Open, Learning and Share
Click Here to Ask a Question

1.4m articles

1.4m replys

5 comments

57.0k users

...