2

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.

In particular, I'd like a tool that can highlight the control that currently has the focus (or tell me that no control currently has the focus). Also I've read somewhere that using a remote debugger may assist in this kind of bugs. Any idea how to do that?

UPDATE: Jeffrey Richter wrote an article in 1997 which contains a tool for finding the focus among other things: http://www.microsoft.com/msj/0397/Win32/Win320397.aspx

Eldad Mor
  • 5,405
  • 3
  • 34
  • 46

3 Answers3

4

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;
    }
}
Community
  • 1
  • 1
arx
  • 16,686
  • 2
  • 44
  • 61
  • That's very cool, thanks! Have you seen the link I've added to Richter's tool? – Eldad Mor Jan 27 '12 at 13:52
  • Cheers. I saw the tool, but having using spy++ I know it can be a chore to map window handles to actual windows so I did the overlay. The Inspect tool suggested by BrendanMcK looks like it might have the best of both worlds, though I haven't tried it yet. – arx Jan 27 '12 at 14:40
  • Fantastic idea. Never occurred to me, but turned what had been torture to figure out my focus issues into a 30 second task. – Doug Porter Aug 29 '18 at 16:55
3

The Inspect tool, part of the Windows SDK download, can be useful here. It's designed to test out the two Accessibility-related APIs - MSAA and UI Automation - and one of the things that both those APIs to is allow accessibility and test tools to track the focus.

The simplest way to use it for tracking focus is to put it into MSAA mode, check the options to follow only focus changes (ie. turn off following the mouse pointer), then turn on the yellow highlight rectangle. Now as focus changes, you can see the rectangle move. As a bonus, if focus goes to something that's hidden or offscreen, you won't see a rectangle, but the name and Win32 class of the control will be displayed in the window.

Note that Inspect shows a superset of focus events: you get not just HWND focus changes, but also notifications when focus moves within certain controls - such as between items in a list box. Automated test and accessibility need these, but for your purposes you should be ok to just ignore these; it's extra information, but shouldn't get in the way too much.

BrendanMcK
  • 14,252
  • 45
  • 54
  • This is great! I wish there was a log though, since I can't tell who's stealing the focus after a mouse click sets it (there's not enough delay for it to be picked up on). – Cameron Feb 28 '19 at 20:54
0

I think you can use Spy++ which are part oh Visual Studio or Windows SDK. It tells you lot of other information, you can also capture messages sent to selected window.

http://msdn.microsoft.com/en-us/library/dd460760.aspx

rkosegi
  • 14,165
  • 5
  • 50
  • 83