c# - 在窗口打开时获得通知的最有效方式

标签 c# wpf windows api winapi

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

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

问题是,“有没有什么方法可以在窗口打开时收到通知(比如使用事件)?无论哪种方式,最有效的方法是什么?

最佳答案

您可以使用 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);
}

为了能够连接到 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);
    }
}

然后,在应用程序的 Main 函数中,例如:

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

输出样本: enter image description here

关于c# - 在窗口打开时获得通知的最有效方式,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/21912686/

相关文章:

c# - 在 UpdatePanel 之外更新控件

wpf - 发生某些事情时更改控制属性 - wpf xaml

c# - System.Xaml 和 System.Windows.Markup 的 Xaml 阅读器之间的区别?

Windows 批处理脚本从 FTP 下载 N 个最新文件并将它们提供给 EXE 文件

c++ - 如何只运行一个应用程序实例

windows - 无法从 Windows 中的 docker 访问托管应用程序

c# - 如何正确地在内部创建一个类?

c# - 使用 LINQ 和 MongoDb 重新创建位置数组运算符

java - Xamarin Android java.lang.InstantiationException : java. lang.Class 没有零参数构造函数

WPF ListView : Howto refresh manually?