wpf 在窗口打开时获得通知的最有效方式
声明:本页面是StackOverFlow热门问题的中英对照翻译,遵循CC BY-SA 4.0协议,如果您需要使用它,必须同样遵循CC BY-SA许可,注明原文地址和作者信息,同时你必须将它归于原作者(不是我):StackOverFlow
原文地址: http://stackoverflow.com/questions/21912686/
Warning: these are provided under cc-by-sa 4.0 license. You are free to use/share it, But you must attribute it to the original authors (not me):
StackOverFlow
Most Efficient Way for getting notified on window open
提问by Arashv
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 EnumDesktopWindowsWindows 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.
到目前为止,使用EnumDesktopWindowsWindows API User32.dll,我可以在大约 10 毫秒内在我的机器上枚举所有打开的窗口。正如您现在可能已经猜到的那样,我需要在短时间内执行此操作以尽可能快,另一方面,选择较小的时间段会给系统带来很大的开销。
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?
问题是,“有没有办法在窗口打开时得到通知(比如使用事件)?无论哪种方式,最有效的方法是什么?
回答by thepirat000
You can Hook on to Shell to receive messages by using RegisterWindowMessageand RegisterShellHookWindowAPI functions.
您可以使用RegisterWindowMessage和RegisterShellHookWindowAPI 函数挂钩到 Shell 以接收消息。
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 Formand overrides the WndProcfunction. You can make this Form to have an Event that will be raised when a Window change its state.
为了能够连接到外壳程序,您需要一个继承自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);
}
}
And then, in your Main function of your application, you can have for example:
然后,在您的应用程序的 Main 函数中,您可以具有例如:
static void Main(string[] args)
{
var f = new SystemProcessHookForm();
f.WindowEvent += (sender, data) => Console.WriteLine(data);
while (true)
{
Application.DoEvents();
}
}
Output sample:

输出样本:

回答by Mitch
Use the System.Windows.Automationnamespace.
使用System.Windows.Automation命名空间。
Example (taken from The Old New Thing) which waits for a specific process to open a dialog box, then dismisses it:
示例(取自The Old New Thing)等待特定进程打开对话框,然后关闭它:
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();
}
}

