18

Is there a way using Win32, to register for notifications when a new window is created. I'm trying to keep a list of current open windows, but am now just polling the list of current windows using EnumWindows().

Anyone done something similar?

Thanks


I'm not sure if I'm doing this right, but I'm not able to get the SetWindowsHookEx method to fire.

anything come to mind?

here is my snip

[DllImport("user32.dll", SetLastError = true)]
private static extern IntPtr SetWindowsHookEx(HookType hook, HookProc callback, IntPtr hMod, uint dwThreadId);

[DllImport("user32.dll")]
private static extern int CallNextHookEx(IntPtr hhk, int nCode, IntPtr wParam, IntPtr lParam);
const int HSHELL_WINDOWCREATED = 1;

private static HookProc winDelegate = ShellHookProcDelegate;
internal static void RegisterWindowCreatedEvent()
{
    SetWindowsHookEx(HookType.WH_SHELL, winDelegate, IntPtr.Zero, 0);
}

private static int ShellHookProcDelegate(int code, IntPtr wParam, IntPtr lParam)
{
    if (code != HSHELL_WINDOWCREATED)
    {
        return CallNextHookEx(IntPtr.Zero, code, wParam, lParam);
    }

    //App specific code here

    return CallNextHookEx(IntPtr.Zero, code, wParam, lParam);
}
volody
  • 6,946
  • 3
  • 42
  • 54
James Hollister
  • 537
  • 4
  • 9
  • 1
    Since WH_SHELL is an injected hook, your hook must reside in a DLL, and the DLL must be the same bitness as the application raising the event. And since you're writing your hook in managed code, your hook must target the same version of the CLR as the application raising the event. Any of those would prevent your hook from running. Also, your hook runs in the context of the application raising the event, so even if it runs, you may not be able to see the effect since you're in the wrong process. An accessibility hook is probably the best approach here since it avoids all of these problems. – Raymond Chen Nov 26 '11 at 00:22

5 Answers5

17

Use SetWindowsHookEx to set up a WH_SHELL hook and look for the HSHELL_WINDOWCREATED event.

RichieHindle
  • 272,464
  • 47
  • 358
  • 399
5

Here is some code based on UI automation events. It gives window opened, closed, and focused events.

C#

[STAThread]
public static void Main(string[] args)
{
    Automation.AddAutomationEventHandler(WindowPattern.WindowOpenedEvent, AutomationElement.RootElement, TreeScope.Children, (sender, e) =>
    {
        var element = (AutomationElement)sender;
        var name = element.Current.Name;
        Console.WriteLine("open: " + name + " hwnd:" + element.Current.NativeWindowHandle);
        Automation.AddAutomationEventHandler(WindowPattern.WindowClosedEvent, element, TreeScope.Element, (s, e2) =>
        {
            Console.WriteLine("close: " + name + " hwnd:" + element.Current.NativeWindowHandle);
        });
    });

    Automation.AddAutomationFocusChangedEventHandler((sender, e) =>
    {
        var element = (AutomationElement)sender;
        var name = element.Current.Name;
        Console.WriteLine("focused: " + name + " hwnd:" + element.Current.NativeWindowHandle);
    });
    Console.ReadLine();
    Automation.RemoveAllEventHandlers();
}

C++ equivalent:

#include <windows.h>
#include <stdio.h>
#include <uiautomation.h>

// some useful macros
#define WIDEN2(x) L ## x
#define WIDEN(x) WIDEN2(x)
#define __WFILE__ WIDEN(__FILE__)

#define MYTRACE wprintf
#define CHECKHR(expr) {hr=(expr);if(FAILED(hr)){ MYTRACE(L"HR FAILED line:%u file:%s\n", __LINE__, __WFILE__); goto cleanup; } }  
#define CHECKWIN32(expr) {if(!(expr)){hr = HRESULT_FROM_WIN32(GetLastError()); MYTRACE(L"WIN32 FAILED line:%u file:%s\n", __LINE__, __WFILE__); goto cleanup; } }  
#define CHECKARG(expr) {if(!(expr)){ MYTRACE(L"ARG FAILED line:%u file:%s\n", __LINE__, __WFILE__); hr = E_INVALIDARG; goto cleanup; } }  
#define CHECKMEM(expr) {if(!(expr)){ MYTRACE(L"MEM FAILED line:%u file:%s\n", __LINE__, __WFILE__); hr = E_OUTOFMEMORY; goto cleanup; } } 
#define CORELEASE(expr) {if(expr){ expr->Release(); expr = NULL; } } 
#define HR HRESULT hr=S_OK;

class EventHandler :
  public IUIAutomationEventHandler,
  public IUIAutomationFocusChangedEventHandler
{
private:
  LONG _ref;
  IUIAutomation* _automation;
  HWND _hwnd;
  IUIAutomationElement* _sender;

public:
  EventHandler(IUIAutomation* automation, IUIAutomationElement* sender, HWND hwnd) :
    _ref(1),
    _automation(automation),
    _sender(sender),
    _hwnd(hwnd)
  {
    if (sender)
    {
      sender->AddRef();
    }
  }

  ~EventHandler()
  {
    CORELEASE(_sender);
  }

  // IUnknown
  ULONG STDMETHODCALLTYPE AddRef() { ULONG ret = InterlockedIncrement(&_ref); return ret; }
  ULONG STDMETHODCALLTYPE Release() { ULONG ret = InterlockedDecrement(&_ref); if (!ret) { delete this; return 0; } return ret; }
  HRESULT STDMETHODCALLTYPE QueryInterface(REFIID riid, void** ppInterface)
  {
    if (riid == __uuidof(IUnknown))
    {
      *ppInterface = (IUIAutomationEventHandler*)this;
    }
    else if (riid == __uuidof(IUIAutomationEventHandler))
    {
      *ppInterface = (IUIAutomationEventHandler*)this;
    }
    else if (riid == __uuidof(IUIAutomationFocusChangedEventHandler))
    {
      *ppInterface = (IUIAutomationFocusChangedEventHandler*)this;
    }
    else
    {
      *ppInterface = NULL;
      return E_NOINTERFACE;
    }

    AddRef();
    return S_OK;
  }

  // IUIAutomationFocusChangedEventHandler
  HRESULT STDMETHODCALLTYPE HandleFocusChangedEvent(IUIAutomationElement* sender)
  {
    HWND hwnd = NULL;
    sender->get_CurrentNativeWindowHandle((UIA_HWND*)&hwnd);
    wprintf(L"Window focused hwnd:%p'\n", hwnd);
    return S_OK;
  }

  // IUIAutomationEventHandler
  HRESULT STDMETHODCALLTYPE HandleAutomationEvent(IUIAutomationElement* sender, EVENTID eventID)
  {
    HR;
    HWND hwnd = NULL;
    EventHandler* windowHandler;

    switch (eventID)
    {
    case UIA_Window_WindowOpenedEventId:
      sender->get_CurrentNativeWindowHandle((UIA_HWND*)&hwnd);
      wprintf(L"Window opened hwnd:%p\n", hwnd);

      // register for close on this window
      // we build a new handler, this is the only way to remember the hwnd (the close event doesn't have anything)
      windowHandler = new EventHandler(_automation, sender, hwnd); // implicit addref
      CHECKMEM(windowHandler);
      CHECKHR(_automation->AddAutomationEventHandler(UIA_Window_WindowClosedEventId, sender, TreeScope_Element, NULL, windowHandler));
      break;

    case UIA_Window_WindowClosedEventId:
      wprintf(L"Window closed hwnd:%p\n", _hwnd);
      CHECKHR(_automation->RemoveAutomationEventHandler(UIA_Window_WindowClosedEventId, _sender, this));
      Release(); // we release our own reference, 'this' we be deleted sometime when all COM references are gone. don't do 'delete this'!
      break;
    }

  cleanup:
    return hr;
  }
};

int main()
{
  HR;
  IUIAutomationElement* root = NULL;
  EventHandler* handler = NULL;
  IUIAutomation* automation = NULL;

  CoInitializeEx(NULL, COINIT_MULTITHREADED);
  CHECKHR(CoCreateInstance(__uuidof(CUIAutomation), NULL, CLSCTX_INPROC_SERVER, __uuidof(IUIAutomation), (void**)&automation));

  CHECKHR(automation->GetRootElement(&root));

  handler = new EventHandler(automation, NULL, NULL);
  CHECKMEM(handler);

  CHECKHR(automation->AddAutomationEventHandler(UIA_Window_WindowOpenedEventId, root, TreeScope_Subtree, NULL, handler));
  CHECKHR(automation->AddFocusChangedEventHandler(NULL, handler));

  wprintf(L"Press any key to stop listening for events.\n");
  getchar();

cleanup:
  if (automation != NULL)
  {
    automation->RemoveAllEventHandlers();
    CORELEASE(automation);
  }

  CORELEASE(handler);
  CORELEASE(root);
  CoUninitialize();
  return hr;
}
Simon Mourier
  • 132,049
  • 21
  • 248
  • 298
3

Sure - you can write a CBT hook and watch for HCBT_CREATEWND. See also SetWindowsHookEx().


Note that this will allow you to be notified of all window creation, before the windows being created are even fully initialized. If all you need are unowned, top-level windows, RichieHindle's suggestion may work better...

Community
  • 1
  • 1
Shog9
  • 156,901
  • 35
  • 231
  • 235
-1

Detours will permit you to attach hooks to arbitrary Win32 functions. However, polling is probably a more reliable way to approach the problem: you don't have to worry about whether you've missed a particular window-creation method (how many are there in Win32? I bet more than one!), and, of course, you won't be rewriting the machine code for windows functions at runtime.

But, you know, your call.

David Seiler
  • 9,557
  • 2
  • 31
  • 39
  • 6
    James would have to inject his Detours code into every GUI process, including new ones that were started. Windows hooks (via SetWindowsHookEx) are designed to do exactly what he wants, and are an official part of the Windows API. – RichieHindle Jun 21 '09 at 21:21
-1

You could try WinEventHook library for autohotkey. Try modifying the notepad popup blocker example with the following:

HookProc( hWinEventHook, Event, hWnd, idObject, idChild, dwEventThread, dwmsEventTime ) { if Event ; EVENT_SYSTEM_FOREGROUND = 0x3 {
WinGetTitle, title, ahk_id %hWnd% If (title = "your_window_name" msgbox, your window has been created } }

Naveen
  • 5,910
  • 5
  • 30
  • 38