How can I be notified when a new window is created on Win32?
Solution 1:
Use SetWindowsHookEx
to set up a WH_SHELL
hook and look for the HSHELL_WINDOWCREATED
event.
Solution 2:
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...
Solution 3:
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;
}