|
|||||||||||||||||||||
|
|||||||||||||||||||||
|
Announcements
Chapters
Services
Feature Zones
|
![]() Closing banned windows automatically Popup Killer is yet another program that automatically closes banned windows. Popup Killer works in the system tray, checks browser windows on a regular interval basis, updates banned windows declared in an XML file, and finally provides ease of use, thanks to hotkeys. There is already a similar program developed in C++, with an article published in Code Project. But the one presented in this article is done with C#, and has a better and faster implementation for banned window lookup. This is not exactly a new topic, and there are many such programs on the Internet. But I didn't want to miss the opportunity to show how these things can easily be built using C# :
Building a System tray appStart a new Windows C# Application, and drag&drop the
Adding a systray to your Windows C# application To make sure the system tray icon and the Application.exe icon match,
choose a .ico file and set it in the
A systray app would be a little weird, if there was a visible task for it in
the task bar. To avoid it, we just need to set the this.ShowInTaskbar = false;
The systray display is almost ready! Of course, we don't have a context menu and are not yet able to make the application visible or hide it. Application switchFirst of all, the main public void HideApp()
{
this.WindowState = FormWindowState.Minimized;
Hide();
}
public void ShowApp()
{
Show();
this.WindowState = FormWindowState.Normal;
}
An interesting feature is to let the user close the protected override void OnClosing(CancelEventArgs e)
{
// method overidden so the form can be minimized, instead of closed
e.Cancel = true;
// let's minimize the form, and hide it
this.WindowState = FormWindowState.Minimized;
Hide();
}
Of course we must provide an explicit alternative to exit the application. That's done through the Exit option from the systray context menu, whose click event is implemented like this : private void menu_App_Exit(object sender, System.EventArgs e)
{
NativeWIN32.UnregisterHotKey(Handle, 100);
// hide icon from the systray
notifyIcon1.Visible = false;
Application.Exit();
}
Adding a Context menuAdding a context menu is the same as adding a system tray, there is a
An interesting thing to mention at this point is that there is no other means
to popup a context menu than doing this association, although the public void Show(Control ctrl, Point pos);
This method cannot be used because in our particular case, the only available
control object is the application Adding menu items to this menu is straight forward. Click on the context menu so it shows the real context menu instance, and start adding items or separators. Double-click on each item to add a click event handler. ![]() Building context menus in the .Net environment Once the context menu is set, the option items must be enabled or disabled
depending on application state. For that purpose, the context menu always raises
the associated private void menu_App_BeforePopup(object sender, System.EventArgs e)
{
if ( this.WindowState == FormWindowState.Minimized )
{
// App_Show is a System.Windows.Forms.MenuItem object
App_Show.Enabled = true;
// App_Hide is a System.Windows.Forms.MenuItem object
App_Hide.Enabled = false;
}
else
{
App_Show.Enabled = false;
App_Hide.Enabled = true;
}
}
Implementing a timerThe .Net framework // explicit namespace (Timer also in System.Threading)
m_Timer = new System.Timers.Timer();
m_Timer.Elapsed += new ElapsedEventHandler(OnTimerKillPopup);
// for instance 3000 milliseconds
m_Timer.Interval = m_nInterval;
m_Timer.Enabled = true; // start timer
protected void OnTimerKillPopup(Object source, ElapsedEventArgs e)
{
m_Timer.Enabled = false; // pause the timer
FindPopupToKill();
m_Timer.Enabled = true;
}
Native Win32 Window lookupWhat the application does is check all open Internet Explorer windows for their captions, and compare them against a known list of banned names. For each match, we automatically close the Internet Explorer window as if we had manually right clicked on the window border to show the sys menu, and clicked on Close option. Compared to the C++ implementation mentioned at the beginning of the article, we have to say that we are more than willing to be smarter and avoid the overhead of enumerating all open windows each 3 seconds. Why this? I have done some performance testing, and while only 8 applications were running on my system and shown in the task bar, no less than 180 windows were actually available to me. From those 180 windows, only 2 were top-level Internet Explorer windows, and only one out of the 2 was banned. At this point, we are in the The first available implementation we have uses Process[] myProcesses = Process.GetProcessesByName("IEXPLORE");
foreach(Process myProcess in myProcesses)
{
FindPopupToKill(myProcess);
}
protected void FindPopupToKill(Process p)
{
// traverse all threads and enum all windows attached to the thread
foreach (ProcessThread t in p.Threads)
{
int threadId = t.Id;
NativeWIN32.EnumThreadProc callbackProc =
new NativeWIN32.EnumThreadProc(MyEnumThreadWindowsProc);
NativeWIN32.EnumThreadWindows(threadId,
callbackProc, IntPtr.Zero /*lParam*/);
}
}
// callback used to enumerate Windows attached to one of the threads
bool MyEnumThreadWindowsProc(IntPtr hwnd, IntPtr lParam)
{
public const int WM_SYSCOMMAND = 0x0112;
public const int SC_CLOSE = 0xF060;
// get window caption
NativeWIN32.STRINGBUFFER sLimitedLengthWindowTitle;
NativeWIN32.GetWindowText(hwnd, out sLimitedLengthWindowTitle, 256);
String sWindowTitle = sLimitedLengthWindowTitle.szText;
if (sWindowTitle.Length==0) return true;
// find this caption in the list of banned captions
foreach (ListViewItem item in listView1.Items)
{
if ( sWindowTitle.StartsWith(item.Text) )
NativeWIN32.SendMessage(hwnd, NativeWIN32.WM_SYSCOMMAND,
NativeWIN32.SC_CLOSE,
IntPtr.Zero); // try soft kill
}
return true;
}
public class NativeWIN32
{
public delegate bool EnumThreadProc(IntPtr hwnd, IntPtr lParam);
[DllImport("user32.dll", CharSet=CharSet.Auto)]
public static extern bool EnumThreadWindows(int threadId,
EnumThreadProc pfnEnum, IntPtr lParam);
// used for an output LPCTSTR parameter on a method call
[StructLayout(LayoutKind.Sequential, CharSet=CharSet.Auto)]
public struct STRINGBUFFER
{
[MarshalAs(UnmanagedType.ByValTStr, SizeConst=256)]
public string szText;
}
[DllImport("user32.dll", CharSet=CharSet.Auto)]
public static extern int GetWindowText(IntPtr hWnd,
out STRINGBUFFER ClassName, int nMaxCount);
[DllImport("user32.dll", CharSet=CharSet.Auto)]
public static extern int SendMessage(IntPtr hWnd,
int msg, int wParam, int lParam);
}
In terms of performance, this code does a pretty good job because it filters out automatically from all windows, those that are from Internet Explorer processes, which is much like filtering the running applications shown in the task bar. But we have an even simpler implementation for that. We are going to use the
native WIN32 protected void FindPopupToKill()
{
IntPtr hParent = IntPtr.Zero;
IntPtr hNext = IntPtr.Zero;
String sClassNameFilter = "IEFrame"; // CLASSNAME of all IE windows
do
{
hNext = NativeWIN32.FindWindowEx(hParent,hNext,
sClassNameFilter,IntPtr.Zero);
// we've got a hwnd to play with
if ( !hNext.Equals(IntPtr.Zero) )
{
// get window caption
NativeWIN32.STRINGBUFFER sLimitedLengthWindowTitle;
NativeWIN32.GetWindowText(hNext, out
sLimitedLengthWindowTitle, 256);
String sWindowTitle = sLimitedLengthWindowTitle.szText;
if (sWindowTitle.Length>0)
{
// find this caption in the list of banned captions
foreach (ListViewItem item in listView1.Items)
{
if ( sWindowTitle.StartsWith(item.Text) )
NativeWIN32.SendMessage(hNext,
NativeWIN32.WM_SYSCOMMAND,
NativeWIN32.SC_CLOSE,
IntPtr.Zero); // try soft kill
}
}
}
}
while (!hNext.Equals(IntPtr.Zero));
}
public class NativeWIN32
{
[DllImport("user32.dll", CharSet=CharSet.Auto)]
public static extern IntPtr FindWindowEx(IntPtr parent /*HWND*/,
IntPtr next /*HWND*/,
string sClassName,
IntPtr sWindowTitle);
}
Registering a Windows HotkeyWindows hot keys ease a lot, the use of applications like Popup Killer. A
single registered key combination, Ctrl+Shift+J by default, allows Popup Killer
to figure out the active window and add it to the list of banned windows. This
avoids manual adding and editing of window names (also implemented in the The registered hot key can be changed on-the-fly with the system tray context menu, thanks to an one-shot modal dialog. The registered hot key is also saved in the XML file along with the list of banned window names. Now about implementation, again, we have to use native Win32 API calls,
namely public void SetHotKey(Keys c, bool bCtrl, bool bShift,
bool bAlt, bool bWindows)
{
m_hotkey = c;
m_ctrlhotkey = bCtrl;
m_shifthotkey = bShift;
m_althotkey = bAlt;
m_winhotkey = bWindows;
// update hotkey
NativeWIN32.KeyModifiers modifiers =
NativeWIN32.KeyModifiers.None;
if (m_ctrlhotkey)
modifiers |= NativeWIN32.KeyModifiers.Control;
if (m_shifthotkey)
modifiers |= NativeWIN32.KeyModifiers.Shift;
if (m_althotkey)
modifiers |= NativeWIN32.KeyModifiers.Alt;
if (m_winhotkey)
modifiers |= NativeWIN32.KeyModifiers.Windows;
//Keys.J);
NativeWIN32.RegisterHotKey(Handle, 100, modifiers, m_hotkey);
}
Using a hot key in an application requires a few steps, which are listed below : /* ------- using HOTKEYs in a C# application -------
-- code snippet by James J Thompson --
in form load : Ctrl+Shift+J
bool success = RegisterHotKey(Handle,
100,
KeyModifiers.Control | KeyModifiers.Shift,
Keys.J);
in form closing :
UnregisterHotKey(Handle, 100);
handling a hot key just pressed :
protected override void WndProc( ref Message m )
{
const int WM_HOTKEY = 0x0312;
switch(m.Msg)
{
case WM_HOTKEY:
MessageBox.Show("Hotkey pressed");
ProcessHotkey();
break;
}
base.WndProc(ref m );
}
public class NativeWIN32
{
[DllImport("user32.dll", SetLastError=true)]
public static extern bool RegisterHotKey(
IntPtr hWnd, // handle to window
int id, // hot key identifier
KeyModifiers fsModifiers, // key-modifier options
Keys vk // virtual-key code
);
[DllImport("user32.dll", SetLastError=true)]
public static extern bool UnregisterHotKey(
IntPtr hWnd, // handle to window
int id // hot key identifier
);
[Flags()]
public enum KeyModifiers
{
None = 0,
Alt = 1,
Control = 2,
Shift = 4,
Windows = 8
}
}
------- using HOTKEYs in a C# application ------- */
When the hot key is pressed, the workflow is as follows: we get the active
window, thanks to the native Win32 API protected void ProcessHotkey()
{
IntPtr hwnd = NativeWIN32.GetForegroundWindow();
if (!hwnd.Equals(IntPtr.Zero))
{
NativeWIN32.STRINGBUFFER sWindowTitle;
NativeWIN32.GetWindowText(hwnd, out sWindowTitle, 256);
if (sWindowTitle.szText.Length>0)
// add to the ListView (Form)
AddWindowTitle( sWindowTitle.szText );
}
}
History
| ||||||||||||||||||||