I\'m developing a WIN32/C++ application containing a main window and a lot of child windows, and I\'m looking for tools that could assist me in tracking down focus bugs.
A Spy++ message log will give a complete record of focus changes, but trying to decode what was happening from the log is a chore. Spy++'s nasty UI doesn't help.
Remote debugging is helpful because the debugger won't interfere with your application's activation and focus, but I'm not sure there's a simple way to determine the focused window from the debugger. Here are a couple of articles on configuring it. Hint: if it doesn't work, double-check your DCOM and firewall settings.
Obviously it would be best to find a tool that does exactly what you want but I couldn't and I was bored so I wrote this code. It creates a semi-transparent blue window that sits on top of the focused control. It's transparent to clicks so it shouldn't interfere with using your app.
To initialise it simply create a timer somewhere in your program's initialisation code:
// Check the focus ten times a second
// Change hwndMain to your main application window
// Note that this won't work if you have multiple UI threads
::SetTimer(hwndMain, 1, 100, HighlightTimerProc);
and add the following code:
LRESULT CALLBACK HighlightWndProc(HWND hWnd, UINT message, WPARAM wParam, LPARAM lParam)
{
switch (message)
{
case WM_NCHITTEST:
return HTTRANSPARENT;
default:
return DefWindowProc(hWnd, message, wParam, lParam);
}
return 0;
}
VOID CALLBACK HighlightTimerProc(HWND hwnd, UINT uMsg, UINT_PTR idEvent, DWORD dwTime)
{
// static locals are bad
static bool initialised = false;
static HWND hwndHighlight = 0;
if (!initialised)
{
HINSTANCE hInstance = 0;
WNDCLASSEX wcex;
wcex.cbSize = sizeof(WNDCLASSEX);
wcex.style = CS_HREDRAW | CS_VREDRAW;
wcex.lpfnWndProc = HighlightWndProc;
wcex.cbClsExtra = 0;
wcex.cbWndExtra = 0;
wcex.hInstance = hInstance;
wcex.hIcon = 0;
wcex.hCursor = 0;
wcex.hbrBackground = (HBRUSH)(COLOR_HIGHLIGHTTEXT);
wcex.lpszMenuName = 0;
wcex.lpszClassName = L"HighlightWindowClasss";
wcex.hIconSm = 0;
ATOM atomHighlightClass = RegisterClassEx(&wcex);
hwndHighlight = CreateWindowEx(WS_EX_LAYERED | WS_EX_TOPMOST | WS_EX_NOACTIVATE | WS_EX_TOOLWINDOW,
(LPCTSTR)atomHighlightClass, L"", WS_POPUP,
CW_USEDEFAULT, 0, CW_USEDEFAULT, 0, NULL, NULL, hInstance, NULL);
// Set opacity to 200/255
SetLayeredWindowAttributes(hwndHighlight, 0, 200, LWA_ALPHA);
initialised = true;
}
static HWND hwndCurrentHighlight = 0;
HWND hwndFocus = GetFocus();
if (hwndFocus != hwndCurrentHighlight)
{
if (hwndFocus == 0)
{
ShowWindow(hwndHighlight, SW_HIDE);
}
else
{
RECT rect;
GetWindowRect(hwndFocus, &rect);
MoveWindow(hwndHighlight, rect.left, rect.top, rect.right - rect.left, rect.bottom - rect.top, false);
ShowWindow(hwndHighlight, SW_SHOW);
}
hwndCurrentHighlight = hwndFocus;
}
}