Click here to Skip to main content
Click here to Skip to main content
Go to top

Windows Service which Kills Unwanted Windows

, 8 Jan 2008
Rate this:
Please Sign up or sign in to vote.
Written in C# Windows service which monitors all Windows and kills unwanted ones.

Introduction

The main goal of this project is to create a Windows service in C# which will monitor all appearing Windows and kill unwanted ones.

This sample project is an illustration for two topics:

  1. Use of PInvoke, and
  2. Writing a Windows service in C#

Background

Some time ago, I had two phone interviews and one on-site interview with Google. That was fun (you know those crazy puzzles they like to ask at Google interviews!) and I enjoyed that, but when I was denied without any reasonable explanation, I became so mad that I decided to write this service to kill any Internet browser on my PC as soon as user dialed google.com in the address bar. Now I think this project could be useful for those who want to automatically kill some unwanted popups on their computers. For example, this approach could be used to kill some Windows ToolTips after some timeout because they can stay on the desktop forever if there is no user activity.

Using the Code

There are two stages if you want to kill some window:

  1. Find a handle to this window, and
  2. Close this window

It looks like to find a window there is an API call FindWindowEx and that was my first approach, but very soon I figured out that it doesn't work well for all kind of windows, for example, it doesn't work for tooltip popups because their title is their content. So, I've tried another approach - using another API call EnumWindows. This API call requires two parameters - pointer to callback function and some structure - to be passed. In the code snippet below, we use member m_hwndFound to return the result of a search:

CallBackPtr callBackPtr = new CallBackPtr(ReportWindow);
m_hwndFound = IntPtr.Zero;
EnumWindows(callBackPtr, new PopupWinow(classToKill, titleToKill));
if (m_hwndFound != IntPtr.Zero)
{... do something if window is found ...}

This code uses class PopupWindow...

[StructLayout(LayoutKind.Sequential, Pack = 1, CharSet = CharSet.Unicode)]
public class PopupWinow
{
  public string wClass = "tooltips_class32";
  public string wTitle = "";

  public PopupWinow(string wclass, string wtitle)
  {
    wClass = wclass;
    wTitle = wtitle;
  }
}

... and callback method ReportWindow which must return true to continue enumeration and false to stop it if a window is found:

private IntPtr m_hwndFound = IntPtr.Zero;
private bool ReportWindow(IntPtr hwnd, PopupWinow lparam)
{
  // Set the user interface to display in the
  // same culture as that set in Control Panel.
  Thread.CurrentThread.CurrentUICulture = Thread.CurrentThread.CurrentCulture;

  string classToKill = lparam.wClass;
  string titleToKill = lparam.wTitle;

  // Get the window title
  StringBuilder sbTitle = new StringBuilder(1024, 1024);
  IntPtr nbytes = SendMessage(hwnd, /*WM_GETTEXT = 
            */0x000D, new IntPtr(sbTitle.Capacity), sbTitle);
  string wTitle = sbTitle.ToString();

  //Get the window class name
  StringBuilder sbClass = new StringBuilder(100);
  int res = GetClassName(hwnd, sbClass, sbClass.Capacity);
  if (res == 0) return true; // just continue enumeration
  string wClass = sbClass.ToString();

  // Check if requested window is found
  if ((wTitle == "" && titleToKill == "") || (wTitle != "" && wClass == classToKill))
  {
    if (wTitle.IndexOf(titleToKill) != -1)
    {
      m_hwndFound = hwnd;
      return false; // stop enumeration
    }
  }
  return true; // continue enumeration
}

After the window handle is found we can close it. Note that for different classes of Windows, we have to use different "close window" calls:

switch (popupType)
{
  case PopupWindowType.Tooltip:
    // tooltip window found --> gently close it 
    // (if use WM_CLOSE message the balloon will be closed forever!)
    nbytes = SendMessage(m_hwndFound, /*WM_LBUTTONDOWN = */0x0201, 0, IntPtr.Zero);
    return;

  case PopupWindowType.Edit:
  case PopupWindowType.IEFrame:
  case PopupWindowType.MozillaUIWindowClass:
  case PopupWindowType.MessageBox:
    // message box window found --> close it
    nbytes = SendMessage(m_hwndFound, /*WM_CLOSE = */0x0010, 0, IntPtr.Zero);
    return;

  default:
  return;
} 

If you want to kill some Windows other than default for this solution just change call to KillPopupWindow:

m_windowFinder.KillPopupWindow(<some word from window title>, <some PopupWindowType>);

To find out a window title and class, you can use Microsoft Spy++ utility which comes with SDK.

To install this service, use regular InstallUtil.exe utility which is a part of the .NET Framework.

Service Startup Parameters

Recently I used this service in my practice. I opened some website in one of the tabs in Internet Explorer and was faced with a lot of error message boxes with 'Yes' / 'No' choices. I was tired of clicking on the 'No' button and did not want to kill Internet Explorer because I did not want to lose many other opened tabs there. So, I decided to use this service to kill all those error Windows automatically. The trick is that it's not possible to close Yes/No Windows just sending them WM_CLOSE message. To close them, we need to find their 'Yes' or 'No' child button window and send it WM_LBUTTONUP message. Here is how to do that:

switch (popupType)
{
  ...
  case PopupWindowType.MessageBox:
    // message box window found --> close it
    if (m_buttonToClick == "")
    {
      nbytes = SendMessage(m_hwndFound, /*WM_CLOSE = */0x0010, 0, IntPtr.Zero);
    }
    else
    {
      // Find child button window and click it!
      IntPtr button = FindWindowEx(m_hwndFound, IntPtr.Zero, "Button", m_buttonToClick);
      if (button != IntPtr.Zero)
      {
        nbytes = SendMessage(button, /*WM_LBUTTONDOWN = */0x0201, 0, IntPtr.Zero);
        nbytes = SendMessage(button, /*WM_LBUTTONUP = */0x0202, 0, IntPtr.Zero);
      }
    }
    return;
  ...
} 

I also modified the OnStart method to parse parameters to make it possible to start the service to kill different types of Windows. For details, see the downloaded solution. Here is the example of service startup parameters if you want to kill popups with title "Error" and buttons "Yes" and "No" (500 is polling interval in ms):

Error #32770 500 &No

Make Service Self-Installable

For me, it's very painful to install/uninstall services using InstallUtil application. To avoid this, we can use another set of API functions to control Service Control Manager. I added a special class ServiceInstaller to the solution to manage that. This class is a helpful wrapper for API calls to advapi32.dll to perform install, uninstall, start and stop service.

Here is the most complicated method InstallService:

public static bool InstallService(string svcPath, string svcName, 
        string svcDispName, string svcDescription, bool bRun)
{
  // Open Service Control Manager
  IntPtr hSCM = OpenSCManager(
    null, // lpMachineName
    null, // lpSCDB
    (uint)Win32Methods.SCM_ACCESS_TYPE.SC_MANAGER_CREATE_SERVICE // scParameter
    );
  if (hSCM == IntPtr.Zero) return false;

  // Create the service
  IntPtr hService = CreateService(
    hSCM,       // SC_HANDLE
    svcName,    // lpSvcName
    svcDispName,// lpDisplayName
    Win32Methods.SERVICE_ALL_ACCESS,                           // dwDesiredAccess
    (uint)Win32Methods.SERVICE_TYPE.SERVICE_WIN32_OWN_PROCESS, // dwServiceType
    (uint)Win32Methods.SERVICE_START_TYPE.SERVICE_AUTO_START,  // dwStartType
    (uint)Win32Methods.SERVICE_ERROR_CODE.SERVICE_ERROR_NORMAL,// dwErrorControl
    svcPath,    // lpPathName
    null,       // lpLoadOrderGroup
    IntPtr.Zero,// lpdwTagId
    null,       // lpDependencies
    null,       // lpServiceStartName
    null        // lpPassword
    );
  if (hService == IntPtr.Zero)
  {
    CloseServiceHandle(hSCM);
    return false;
  }

  // Change service description
  if (!ChangeServiceConfig2(
    hService,                  // handle to service
    SERVICE_INFO_LEVEL.SERVICE_CONFIG_DESCRIPTION, // change: description
    ref svcDescription          // value: new description
    ))
  {
    CloseServiceHandle(hSCM);
    return false;
  }
  CloseServiceHandle(hSCM);

  // Set check box "Allow service to interact with desktop" 
  // checked to allow service show popup messages.
  OperatingSystem os = Environment.OSVersion;
  if (os.Version.Major < 6) // 6 is Vista: service cannot interact with desktop on Vista
  {
    System.ServiceProcess.ServiceController serviceController = 
            new System.ServiceProcess.ServiceController();
    serviceController.ServiceName = svcName;
    ConnectionOptions coOptions = new ConnectionOptions();
    coOptions.Impersonation = ImpersonationLevel.Impersonate;

    ManagementScope mgmtScope = 
        new System.Management.ManagementScope(@"root\CIMV2", coOptions);
    mgmtScope.Connect();

    ManagementObject wmiService = new ManagementObject
                    ("Win32_Service.Name='" + svcName + "'");
    ManagementBaseObject InParam = wmiService.GetMethodParameters("Change");
    InParam["DesktopInteract"] = true;

    ManagementBaseObject OutParam = wmiService.InvokeMethod("Change", InParam, null);

    serviceController.Start();
  }

  // Run service
  if (bRun) return RunService(svcName);
  return true;
}

This code uses calls to advapi32.dll functions implemented in Win32Methods class (see downloaded solution for details).

Now you can install/uninstall this service calling it from the command line like this...

WindowKiller -a

... where the parameter -a means "auto" install/uninstall depending on the current service status. To get help on other command line parameters, you can call the service with -h parameter.

License

This article, along with any associated source code and files, is licensed under The Code Project Open License (CPOL)

Share

About the Author

Victuar
Software Developer (Senior) Intel Corp.
United States United States
No Biography provided

Comments and Discussions

 
Generaldoesn't work :( Pinmemberashex_14321-Jul-08 3:00 
GeneralRe: doesn't work :( PinmemberVictuar7-Sep-08 21:37 
GeneralRe: doesn't work :( PinmemberVictuar9-Sep-08 7:51 
JokeGreat, also for other usages Pinmemberprotix11-Jan-08 2:48 
JokeTake that google! PinmemberBen Daniel8-Jan-08 20:23 
GeneralExcellent PinmemberVyskubov7-Jan-08 7:05 
Generalthe smell of C++ PinmemberThanks for all the fish2-Jan-08 10:36 

General General    News News    Suggestion Suggestion    Question Question    Bug Bug    Answer Answer    Joke Joke    Rant Rant    Admin Admin   

Use Ctrl+Left/Right to switch messages, Ctrl+Up/Down to switch threads, Ctrl+Shift+Left/Right to switch pages.

| Advertise | Privacy | Mobile
Web04 | 2.8.140926.1 | Last Updated 8 Jan 2008
Article Copyright 2007 by Victuar
Everything else Copyright © CodeProject, 1999-2014
Terms of Service
Layout: fixed | fluid