Using accessibility to monitor windows as they come and go

Today's Little Program monitors windows as they come and go. When people contemplate doing this, they come up with ideas like installing a WH_CBT hook or a WH_SHELL hook, but one of the major problems with those types of hooks is that they are injected hooks. Injection is bad for a number of reasons.

  • It forces the hook to be in a DLL so it can be injected.
  • Hook activities need to be marshaled back to the main program.
  • Your DLL will capture events only in processes of the same bitness, because you cannot load a 32-bit DLL into a 64-bit process or vice versa.

  • You can inject into an elevated process only if your process is also elevated. If your process is non-elevated, then you will not capture events for windows belonging to elevated processes.

This is where accessibility comes in handy, because accessibility lets you specify whether you want your hook to be an injected or non-injected one. And if you're non-injected, then the programming model is much simpler because everything happens in your process (indeed, on a single thread).

Take the scratch program and make the following changes:

#include <strsafe.h>

OnCreate(HWND hwnd, LPCREATESTRUCT lpcs)
 g_hwndChild = CreateWindow(TEXT("listbox"), NULL,
     0, 0, 0, 0, hwnd, NULL, g_hinst, 0);
 if (!g_hwndChild) return FALSE;
 return TRUE;

void CALLBACK WinEventProc(
    HWINEVENTHOOK hWinEventHook,
    DWORD event,
    HWND hwnd,
    LONG idObject,
    LONG idChild,
    DWORD dwEventThread,
    DWORD dwmsEventTime
 if (hwnd &&
     idObject == OBJID_WINDOW &&
     idChild == CHILDID_SELF)
  PCTSTR pszAction = NULL;
  TCHAR szBuf[80];
  switch (event) {
   pszAction = TEXT("created");
   pszAction = TEXT("destroyed");
  if (pszAction) {
   TCHAR szClass[80];
   TCHAR szName[80];
   szClass[0] = TEXT('\0');
   szName[0] = TEXT('\0');
   if (IsWindow(hwnd)) {
    GetClassName(hwnd, szClass, ARRAYSIZE(szClass));
    GetWindowText(hwnd, szName, ARRAYSIZE(szName));
   TCHAR szBuf[80];
   StringCchPrintf(szBuf, ARRAYSIZE(szBuf),
                   TEXT("%p %s \"%s\" (%s)"), hwnd, pszAction,
                   szName, szClass);
   ListBox_AddString(g_hwndChild, szBuf);

int WINAPI WinMain(HINSTANCE hinst, HINSTANCE hinstPrev,
                   LPSTR lpCmdLine, int nShowCmd)
  ShowWindow(hwnd, nShowCmd);

 HWINEVENTHOOK hWinEventHook = SetWinEventHook(
     NULL, WinEventProc, 0, 0,

  while (GetMessage(&msg, NULL, 0, 0)) {

  if (hWinEventHook) UnhookWinEvent(hWinEventHook);

This is a generalization of our earlier program which waits for a specific window to be destroyed, except that we now are watching all windows for creation and destruction.

When you run this program, you see that there is a lot of window activity, but maybe you are interested only in windows when they are shown and hidden. No problem, that's a small change:

  switch (event) {
   pszAction = TEXT("shown");
   pszAction = TEXT("hidden");

 HWINEVENTHOOK hWinEventHook = SetWinEventHook(
     NULL, WinEventProc, 0, 0,

Notice that these notifications are received for windows from both 32-bit and 64-bit processes, and that they are received even for windows belonging to elevated processes. You can't do that with an injected hook.

Comments (9)
  1. AC says:

    The whole concept of injecting is just horrible.

    With this around you can never be sure your application works as expected, because any random bug could appear caused by 3rd party injected DLLs.

  2. Danny says:

    Really? Even if I am a freaking malware non-elevated I can watch the all mighty admin windows creation? OAW!!! That's awesome, now I can take over the world (heh, or only about 85% of it since rest is non-Windows). And you mean I can be notified also about the UAC asking user window creation? Or when the Antivirus is starting to hunt me by creating it's scheduler windows? Uber-super, lemme start creating the next Stuxnet :D:D.

  3. ChuckOp says:

    One of the things I like about this blog, indeed Raymond in general, is that he shows developers how useful the accessibility-related technology can be outside of traditional uses.  Raymond and the original MSAA developers were key to ensuring that WinEvents has uses beyond empowering accessibility aids.

    @Danny, while you can get information about window creation, you can't really do much with the information.  You are still bound by the rules of uiAccess privilege do to anything meaningful.

  4. @AC says:

    Doesn't injection require administrative rights to occur? If your giving a 3rd party program administrative rights, you better be darn sure what that 3rd party program does or just not care.

    And if you're running in an environment as the administrator, or disabling UAC, you are just asking for this behavior.

    There are plenty of valid scenarios for injection, debugging comes to mind, fixing others broken code in a product that is no longer supported, intrusive monitoring applications, etc. Just because a concept has been abused or used poorly doesn't mean its horrible, it just means that those programmers did not take great care with a powerful tool (and should be named and shamed in my opinion).

    @ChuckOp I think Danny was joking, mocking those that think this ability is some type of security exploit.

  5. Danny says:

    @@AC the ":D:D" I put at the end of my statement was precisely for that purpose but I think ChuckOp was too busy to read them :).

  6. not my real name says:

    Danny, your jokes may have been just a little too close to the truth, too. Or at least a little too close to the truth as perceived by the pinheads who write some antimalware software.

    People who write accessibility software NEED these interfaces. But people in the "security" industry go off half-cocked and regularly flag accessibility software as malware because they make similar wrong assumptions about what these interfaces can and can't do. The problem is, they don't think they're joking.

  7. @not my real name says:

    They actually have an "Internet Law" for that: Poe's Law.

    From TOW:

    [Poe's Law] is an Internet adage reflecting the idea that without a clear indication of the author's intent, it is difficult or impossible to tell the difference between sincere extremism and an exaggerated parody of extremism

    I agree with you, my job requires me to maintain an Automation Framework that relies heavily on these Interfaces (and some other dark-evil-magic that I prefer not to mention such as Injection) and I am a huge fan of any Raymond Chen posts which dive into anything remotely related to what I'm working on.

  8. ChuckOp says:

    @Danny, apologies, I did not pick up on the sarcasm.  TIL today:  Poe's Law.  

  9. The downside of this approach is the context switching from being out-of-process. It turns out that that extra cost can be meaningful in some regards. Also, with winevents, the payload isn't usually helpful. You have to make another roundtrip talk to the application (via AccessibleObjectFromEvent) to get anything useful (and deal with reentrancy if you're on an STA).

Comments are closed.