Disappearing Window Contents while dragging the mouse

We have heard a couple reports of strange behavior in Visual FoxPro if you click on a thumb button in a scrollbar, drag it up or down a little, then pause for 6 seconds or so.

The contents of the window would disappear, leaving a blank editing window.

When you release the mouse, the contents would reappear.

This sounds like a strange behavior, but what’s even stranger is that it was very hard to duplicate the conditions to reproduce it.

Eventually, we could reproduce it sporadically, but if we tried with a debug version of FoxPro or with a debugger attached, it would not reproduce.

So how can we track this behavior down?

We could modify the source code and put a hard coded DebugBreak function at various places, such as the routine to erase the window or the scrollbar routine. When the breakpoint would hit, a dialog would appear asking to attach a debugger.

However, guessing where to put such a breakpoint so that it would be hit only once at the right time didn’t lead very far.

I decided to use an old friend, Spy++ , which comes with Visual Studio. It can display all the messages that are sent to a particular window.

You can try it: From your Start menu, choose All Programs->Microsoft Visual Studio .NET-> Visual Studio.NET Tools->Spy++

Choose Spy->Log Messages from the menu, click on the Finder Tool, drag it on top of any window (like the VFP command window) and release. You’ll see dozens of messages like this:

<00429> 000A1498 S WM_SETFOCUS hwndLoseFocus:(null)

<00430> 000A1498 R WM_SETFOCUS

<00431> 000A1498 P WM_MOUSEMOVE fwKeys:MK_LBUTTON xPos:402 yPos:1006

<00432> 000A1498 P WM_MOUSEMOVE fwKeys:MK_LBUTTON xPos:403 yPos:1007

<00433> 000A1498 P WM_MOUSEMOVE fwKeys:MK_LBUTTON xPos:404 yPos:1007

<00434> 000A1498 P WM_MOUSEMOVE fwKeys:MK_LBUTTON xPos:406 yPos:1009

<00435> 000A1498 P WM_MOUSEMOVE fwKeys:MK_LBUTTON xPos:407 yPos:1010

<00436> 000A1498 P WM_MOUSEMOVE fwKeys:MK_LBUTTON xPos:408 yPos:1010

<00437> 000A1498 P WM_MOUSEMOVE fwKeys:MK_LBUTTON xPos:410 yPos:1011

<00438> 000A1498 P WM_MOUSEMOVE fwKeys:MK_LBUTTON xPos:411 yPos:1012

<00439> 000A1498 P WM_MOUSEMOVE fwKeys:MK_LBUTTON xPos:412 yPos:1013

<00440> 000A1498 P WM_MOUSEMOVE fwKeys:MK_LBUTTON xPos:413 yPos:1013

<00441> 000A1498 P WM_MOUSEMOVE fwKeys:MK_LBUTTON xPos:413 yPos:1014

<00442> 000A1498 P WM_MOUSEMOVE fwKeys:MK_LBUTTON xPos:414 yPos:1014

<00443> 000A1498 P WM_MOUSEMOVE fwKeys:MK_LBUTTON xPos:415 yPos:1014

<00444> 000A1498 P WM_MOUSEMOVE fwKeys:MK_LBUTTON xPos:416 yPos:1015

<00445> 000A1498 P WM_MOUSEMOVE fwKeys:MK_LBUTTON xPos:416 yPos:1016

<00446> 000A1498 P WM_MOUSEMOVE fwKeys:MK_LBUTTON xPos:417 yPos:1016

<00447> 000A1498 P WM_MOUSEMOVE fwKeys:MK_LBUTTON xPos:418 yPos:1016

<00448> 000A1498 P WM_MOUSEMOVE fwKeys:MK_LBUTTON xPos:418 yPos:1017

<00449> 000A1498 P WM_MOUSEMOVE fwKeys:MK_LBUTTON xPos:419 yPos:1017

<00450> 000A1498 S WM_CANCELMODE

<00451> 000A1498 S WM_CAPTURECHANGED hwndNewCapture:00000000

<00452> 000A1498 R WM_CAPTURECHANGED

<00453> 000A1498 R WM_CANCELMODE

<00454> 000A1498 S WM_KILLFOCUS hwndGetFocus:(null)

<00455> 000A1498 R WM_KILLFOCUS

<00456> 000A1498 S WM_SETFOCUS hwndLoseFocus:(null)

<00457> 000A1498 R WM_SETFOCUS

<00458> 000A1498 S WM_NCPAINT hrgn:00000001

<00459> 000A1498 R WM_NCPAINT

<00460> 000A1498 S WM_ERASEBKGND hdc:79010CC2

<00461> 000A1498 R WM_ERASEBKGND fErased:True

<00462> 000A1498 S WM_NCPAINT hrgn:00000001

<00463> 000A1498 R WM_NCPAINT

<00464> 000A1498 S WM_ERASEBKGND hdc:79010CC2

<00465> 000A1498 R WM_ERASEBKGND fErased:True

<00466> 000A1498 S WM_KILLFOCUS hwndGetFocus:(null)

<00467> 000A1498 R WM_KILLFOCUS

<00468> 000A1498 P WM_PAINT hdc:00000000

From this log, I saw that a message called WM_CANCELMODE was being sent after I had dragged the thumb a little and I was holding the mouse still with the button down.

Internally, FoxPro was doing this:

SetCapture

WHILE the mouse is still down {

            Do some stuff

}

ReleaseCapture

The SetCapture channels all mouse events to the specified window. This allows the user to move the mouse off the window, release the mouse, and the window still gets the mouse up message.

Looking up WM_CANCELMODE, I saw that it can be sent is sent to cancel certain modes, such as mouse capture.

Remarks

When the WM_CANCELMODE message is sent, the DefWindowProc function cancels internal processing of standard scroll bar input, cancels internal menu processing, and releases the mouse capture.

This looked suspicious, and I immediately checked to see what we do with this message, which was to ignore it: just call DefWindProc (the equivalent of DoDefault()).

Thus, ReleaseCapture was being done from under our noses, and we were still in the WHILE loop.

Simple fix: if we receive a WM_CANCELMODE message, break out of the loop.