最有效的窗口打开通知方式

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

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

问题是:“有没有什么办法在窗口打开时得到通知(如使用事件)?无论哪种方式,这样做的最有效的方法是什么?

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

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

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

输出样本: 在这里输入图像说明

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

例子(取自Old New Thing )等待特定进程打开一个对话框,然后解除它:

 using System.Windows.Automation; using System.Diagnostics; using System.Threading; class Program { [System.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(); } } 

如果你从其他应用程序访问窗口,这不是一件容易的事情,但你可以尝试在窗口中使用钩子 。