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?
Use the System.Windows.Automation
namespace.
Example (taken from The Old New Thing) which waits for a specific process to open a dialog box, then dismisses it:
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();
}
}
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:
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With