The Cause of Spurious Mouse Move Messages/Events when task manager is open

Posted in: , by . 1 Comment

Recently while programming in JavaScript I noticed that my mousemove event keeps firing without my mouse moving, as long as my mouse is over the relevant object. It seemed strange so I searched for this behavior online and apparently quite a few others encountered it and thought it was strange: [1], [2], [3], [4].

The specification says “A user agent must dispatch this event when a pointing device is moved while it is over an element (emphasis is mine), confirming that this behavior isn’t originated in the standard. Luckily for me, the first link from above helped me find the source of this behavior: the Task Manager was open in the background.

At the OS level

First I wanted to check what’s happening at the OS level, Applications mostly handle input by listening to Windows Messages and a tool for observing them is Spy++. Because my version of Chrome is 32-bit, I opened the 32-bit version of Spy++ (spyxx.exe rather than spyxx_amd64.exe). After locating Chrome’s window using the crosshair from the Search –> Find Window dialog we get information about the window:
Clicking Synchronize will highlight the window on the main windows tree. By right clicking the window entry in the tree and clicking Messages we can capture windows messages targeted at this window:
When holding the mouse over the browser window without moving it the following messages keep repeating:
One of the repeating messages is WM_MOUSEMOVE which not surprisingly represents mouse movement, and apparently Chrome doesn’t check whether the mouse has actually moved and passes the event to the web application. Actually Firefox and IE11 behave differently in this case (the mousemove event doesn’t keep firing), probably because they do check whether the mouse has moved each time.

That showed that the underlying issue is the continuing arrival of WM_MOUSEMOVE messages, and not a web specific issue.

Why does it happen?

I wondered why would a background application (Task Manager) affect in such a way other applications (Although this shouldn’t cause bugs because mouse move handling is usually idempotent), so I wanted to debug this issue.

The assumption is that there’s a piece of code in Task Manager that causes it and the goal is finding it. Since Task Manager is a Win32 GUI application much of its code is powered by Windows Messages, and our code of interest is likely run in response to a Windows Message.

Since the WM_MOUSEMOVE messages seem to be arriving approximately every 1 second, we can look for similar periodical windows messages with Spy++ (This time with the 64-bit version), After Similarly locating the Task Manager main window we can capture messages targeted at this window:image

To our expectation, there’s a user defined Windows Message arriving every 1 second, so we can debug its handling code and likely find the root cause. After attaching WinDBG we can switch context to the message pumping thread (with ~0s) and look at the call stack:

0:000> k
Child-SP RetAddr Call Site
000000eb`dad2f618 00007ffc`2b4013ed ntdll!NtWaitForMultipleObjects+0xa
000000eb`dad2f620 00007ffc`2ded3100 KERNELBASE!WaitForMultipleObjectsEx+0xe1
000000eb`dad2f900 00007ffc`265311ca USER32!RealMsgWaitForMultipleObjectsEx+0x100
000000eb`dad2f9b0 00007ffc`2653134b DUser!CoreSC::xwProcessNL+0x2d6
000000eb`dad2fa40 00007ffc`2ded2822 DUser!MphProcessMessage+0xb3
000000eb`dad2faa0 00007ffc`2e213034 USER32!_ClientGetMessageMPH+0x52
000000eb`dad2fb30 00007ffc`2ded26ca ntdll!KiUserCallbackDispatcherContinue
000000eb`dad2fba8 00007ffc`2ded2695 USER32!NtUserGetMessage+0xa
000000eb`dad2fbb0 00007ff6`ba4d6d6a USER32!GetMessageW+0x25
000000eb`dad2fbe0 00007ff6`ba4e2144 Taskmgr!wWinMain+0x2aa
000000eb`dad2fcc0 00007ffc`2b8113d2 Taskmgr!__wmainCRTStartup+0x1d4
000000eb`dad2fd90 00007ffc`2e195444 KERNEL32!BaseThreadInitThunk+0x22
000000eb`dad2fdc0 00000000`00000000 ntdll!RtlUserThreadStart+0x34

The interesting frame is highlighted above, that’s the message loop frame and there we can start debugging the message handling code. First we can disassemble around it to see where is the current message stored:

0:000> u Taskmgr!wWinMain+0x270 l30
00007ff6`ba4d6d30 488b0dc1d60700 mov rcx,qword ptr [Taskmgr!g_MainWindow (00007ff6`ba5543f8)]
00007ff6`ba4d6d37 4c8d4510 lea r8,[rbp+10h]
00007ff6`ba4d6d3b 488bd3 mov rdx,rbx
00007ff6`ba4d6d3e ff1544c30800 call qword ptr [Taskmgr!_imp_TranslateAcceleratorW (00007ff6`ba563088)]
00007ff6`ba4d6d44 488d4d10 lea rcx,[rbp+10h]
00007ff6`ba4d6d48 ff15eac20800 call qword ptr [Taskmgr!_imp_TranslateMessage (00007ff6`ba563038)]
00007ff6`ba4d6d4e 488d4d10 lea rcx,[rbp+10h]
00007ff6`ba4d6d52 ff15e8c20800 call qword ptr [Taskmgr!_imp_DispatchMessageW (00007ff6`ba563040)]
00007ff6`ba4d6d58 488d4d10 lea rcx,[rbp+10h]
00007ff6`ba4d6d5c 4533c9 xor r9d,r9d
00007ff6`ba4d6d5f 4533c0 xor r8d,r8d
00007ff6`ba4d6d62 33d2 xor edx,edx
00007ff6`ba4d6d64 ff1516c30800 call qword ptr [Taskmgr!_imp_GetMessageW (00007ff6`ba563080)]
00007ff6`ba4d6d6a 85c0 test eax,eax

The first parameter to GetMessage is the message (passed in the rcx register), so It’s stored at [rbp+10]. Now we can add a conditional breakpoint for the user defined message with bp Taskmgr!wWinMain+0x2aa "j poi(@rbp+18)=4df '';'gc'"  (rbp+18 is used because the message number is the second parameter of the MSG structure, 4df is the user defined message code from the above Spy++ output).

After we run the app and the breakpoint fires it’s just a matter of tracing until finding the code of interest. A good method for that kind of search is jumping over function calls and seeing how the application reacts, until we jump over the function that causes the suspicious behavior to disappear (sort of binary search). To save time, I’ll skip the search process and show that function, it’s a call to SendMessage at Taskmgr!WdcDataMonitor::ListUpdate+0x45f:

0:000>  u Taskmgr!WdcDataMonitor::ListUpdate+0x45f
00007ff6`ba505d0f ff1583d10500 call qword ptr [Taskmgr!_imp_SendMessageW (00007ff6`ba562e98)]
00007ff6`ba505d15 418bc4 mov eax,r12d
00007ff6`ba505d18 488b8d90010000 mov rcx,qword ptr [rbp+190h]
00007ff6`ba505d1f 4833cc xor rcx,rsp
00007ff6`ba505d22 e8f9c4fdff call Taskmgr!_security_check_cookie (00007ff6`ba4e2220)
00007ff6`ba505d27 488b9c24f0020000 mov rbx,qword ptr [rsp+2F0h]
00007ff6`ba505d2f 4881c4a0020000 add rsp,2A0h
00007ff6`ba505d36 415f pop r15

The trick for constantly jumping over a function call (like a patch) is a breakpoint that changes the value of rip (the instruction pointer in x64) when it fires. In our case, because the code doesn’t check the return value we can advance it to the next instruction with that command: bp Taskmgr!WdcDataMonitor::ListUpdate+0x45f "r @rip=00007ff6`ba505d15;gc". Now after the application is run we can see that the spurious WM_MOUSEMOVE messages stop arriving.

The Windows message that is being sent – WM_SETREDRAW

The call we patched is a SendMessage call that sends a WM_SETREDRAW with the false parameter. That prevents a window from redrawing until WM_SETREDRAW is sent with the true parameter, it is targeted at the processes list view control in the Details tab and as evident by the stack trace it is sent while it’s being sorted:

0:000> k
Child-SP RetAddr Call Site
000000eb`dad2f520 00007ff6`ba508488 Taskmgr!WdcDataMonitor::ListUpdate+0x45f
000000eb`dad2f800 00007ff6`ba50643a Taskmgr!WdcProcessMonitor::ListUpdate+0x38
000000eb`dad2f840 00007ff6`ba503dba Taskmgr!WdcDataMonitor::SetSortDirty+0x5e
000000eb`dad2f880 00007ff6`ba502dc5 Taskmgr!WdcListView::Sort+0xaa
000000eb`dad2f8c0 00007ff6`ba4bbe84 Taskmgr!WdcListView::Render+0x115
000000eb`dad2f940 00007ff6`ba4f0c5c Taskmgr!WdcDataPortal::Render+0xe8
000000eb`dad2f970 00007ff6`ba4c6431 Taskmgr!WdcMonitor::Render+0x2a71c
000000eb`dad2f9c0 00007ff6`ba4c6016 Taskmgr!TmTraceControl::ForceRenderUI+0x2d
000000eb`dad2fa00 00007ffc`2ded250d Taskmgr!TmWindowProc+0xf6
000000eb`dad2fa90 00007ffc`2ded2367 USER32!UserCallWinProcCheckWow+0x149
000000eb`dad2fb60 00007ff6`ba4d6d58 USER32!DispatchMessageWorker+0x1a7
000000eb`dad2fbe0 00007ff6`ba4e2144 Taskmgr!wWinMain+0x298

That message is used to prevent numerous window redraws while many updates are being made to it (sorting could surely cause many updates). The issue is how it is implemented by default, and that’s by making the window invisible, which along with the WM_SETREDRAW with the true parameter that is being sent later, causes Windows to send WM_MOUSEMOVE messages, and the puzzle is complete.

In conclusion, as I wrote above, I don’t think this behavior is very problematic, as the extra messages shouldn’t usually change application’s behavior, and if they do, the application should be fixed anyway because Windows doesn’t guarantee sending them on actual mouse movement (the above linked post explains why), and even the web standard is somewhat ambiguous about the mousemove event.

Posted in: , by . 1 Comment

The permalink

One Response to The Cause of Spurious Mouse Move Messages/Events when task manager is open

  1. Superb post. Really helped me out thanks.