Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

The curious problem of the missing WM_NCLBUTTONUP message when a window isn't maximised

Tags:

c++

winapi

mfc

I've got a window that I handle WM_NCLBUTTONUP messages, in order to handle clicks on custom buttons in the caption bar. This works great when the window is maximised, but when it's not, the WM_NCLBUTTONUP message never arrives! I do get a WM_NCLBUTTONDOWN message though. Strangely WM_NCLBUTTONUP does arrive if I click on the right of the menu bar, but anywhere along the caption bar / window frame, the message never arrives.

After a while of debugging I discovered that if I set a breakpoint on CMainFrame::OnNcLButtonDown(), clicked the caption bar, but keep the mouse button held down, let the debugger break in the function, hit F5 to continue debugging, then release the mouse button - magically WM_NCLBUTTONUP is sent!!

My question is two-fold, (1) what the hell is going on? (2) how do I get around this "problem".

I also note that there are several other people on the internet who have the same issue (a quick Google reveals lots of other people with the same issue, but no solution).

Edit
Thanks for the first two replies, I've tried calling ReleaseCapture in NCLButtonDown, but it has no effect (in fact, it returns NULL, indicating a capture is not in place). I can only assume that the base class (def window proc) functionality may set a capture. I shall investigate on Monday...

like image 286
Mark Ingram Avatar asked Feb 06 '09 17:02

Mark Ingram


2 Answers

I've had this same problem. The issue is indeed that a left button click on the window caption starts a drag, and thus mouse capture, which prevents WM_NCLBUTTONUP from arriving.

The solution is to override WM_NCHITTEST:

LRESULT CALLBACK WndProc(HWND hWnd, UINT nMsg, WPARAM wParam, LPARAM lParam)
{
    switch (nMsg)
    {
        ...
        case WM_NCHITTEST:
            Point p(GET_X_LPARAM(lParam), GET_Y_LPARAM(lParam);
            ScreenToClient(p);
            if (myButtonRect.Contains(p))
            {
                return HTBORDER;
            }
            break;
    }
    return DefWindowProc(hWnd, nMsg, wParam, lParam);
}

So essentially you inform Windows that the area occupied by your button is not part of the window caption, but a non-specific part of the non-client area (HTBORDER).

Footnote: If you have called SetCapture() and not yet called ReleaseCapture() when you expect the WM_NCLBUTTONDOWN message to come in, it won't arrive even with the above change. This can be irritating since it's normal to capture the mouse during interaction with such custom buttons so that you can cancel the click/highlight if the mouse leaves the window. However, as an alternative to using capture, you might consider SetTimer()/KillTimer() with a short (eg. 100 ms) interval, which won't cause WM_NCLBUTTONUP messages to vanish.

like image 101
El Zorko Avatar answered Sep 22 '22 12:09

El Zorko


A wild guess - some code is capturing the mouse, probably to facilitate the window move when you grab the title. That would explain also why breaking in the debugger would cause the message to show up - the debugger interaction is clearing the mouse capture.

I would suggest you run Spy++ on that window and it's children and try to figure out who gets the button up message.

As to how to fix it - can't help you there without looking at the actual code. You'll have to figure out who the culprit is and look at their code.

like image 26
Franci Penov Avatar answered Sep 19 '22 12:09

Franci Penov