[英]Most Efficient Way for getting notified on window open
我正在編寫一個應用程序(.NET 4.0中的C#和WPF),它需要打開窗口並關閉它們,如果它們不在它的白名單中。
到目前為止,使用來自User32.dll
EnumDesktopWindows
Windows API,我可以在我的機器上大約10毫秒內枚舉所有打開的窗口。 正如您現在可能已經猜到的那樣,我需要在很短的時間內盡快做到這一點,另一方面選擇較小的時間段會給系統帶來很大的開銷。
問題是,“打開窗口時是否有任何方法可以獲得通知(如使用事件)?無論哪種方式,最有效的方法是什么?
您可以使用RegisterWindowMessage和RegisterShellHookWindow 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
命名空間。
示例(取自舊新事物 )等待特定進程打開對話框,然后解除它:
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();
}
}
如果您從其他應用程序訪問Windows,這不是一件容易的事,但您可以嘗試在Windows中使用Hook 。
聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.