0

First off, what I wanted to accomplish: intercepting (via a low level mouse hook) WM_MOUSEHWHEEL messages to go back and forward in Windows' file explorer history, or at least to the parent window if access to the history weren't possible.

That was what started it all, not that I had any idea if it'd be possible or not, specially with a touchpad, but that was my intention. So I started looking at the more usual WM_MOUSEWHEEL (vertical scrolling), only to find out that those messages aren't being generated much of the time!

As per the MSDN docs on WM_MOUSEWHEEL:

Sent to the focus window when the mouse wheel is rotated. The DefWindowProc function propagates the message to the window's parent. There should be no internal forwarding of the message, since DefWindowProc propagates it up the parent chain until it finds a window that processes it.

Okay, looks good, so when the mouse wheel is rotated that message is sent to the window that has the focus. Let's see if that's the case with the help of Spy++ from the Visual Studio pack of utilities, made a recording of it and uploaded it to YouTube, embedding doesn't seem to be allowed in this Stack Exchange: https://youtu.be/YJvjw_BPJf0

Summing it up though, there's no WM_MOUSEWHEEL being generated for all of the programs tested. Explorer was the first one I tried to look at, focusing on the different sub-windows a window had and made sure it was scrollable.

I could see some related messages being received by the scrollbar section of it, but that's it, no sign of that message anywhere (however, VM_VSCROLL was posted to one of the windows of the scrollbar).

Next up was Notepad3, nada, its SysListView32 doesn't see anything and the parent window gets some notification, activation messages and the like but nothing scrolling related.

Everything's SysListView32 received some LVS_SCROLL messages but it didn't act on them (its message loop is probably looking for the darn WM_MOUSEWHEELs instead).

Finally a browser, nothing at all, the "view" didn't register anything at all while the parent window captured the mouse by the look of the events.

NOTE: For those of you who see the video, note that while Spy++ was on top it didn't have focus during all tests, it was merely pinned so I could see the messages.


I almost gave up, starting from the default example Win32 application they give you in Visual Studio I added a hook procedure and if that message was received it'd print a message box, just in case it was the shell the one getting the messages somehow.

There was my surprise when I ran the code directly in VS that I see message boxes when I scroll through the code. WTF?!

Fire Spy++ again (also 64-bit because it was the 2022 Preview I was running) and let's see, there they are!

https://youtu.be/ekrWKP7HiTE

The code editor sub-window is receiving those messages whenever I scroll vertically with the trackpad using two fingers. So, what is going on?

I mean, I even tried to run that sample code elevated to be at a higher integrity level and see if that'd make those messages appear, but to no avail.

The code is as simple as it gets, most of it was autogenerated as a sample Win32 application, but the hook and procedure are as follows:

// The callback procedure:
LRESULT CALLBACK mouseHookProc(int nCode, WPARAM wParam, LPARAM lParam) {
  if (nCode == 0) {
    if (wParam == WM_MOUSEWHEEL)
      MessageBox(NULL, L"We got it!", L"Yay!", MB_OK);
  }
  return CallNextHookEx(NULL, nCode, wParam, lParam);
}

...

// Setting the mouse hook (at the application's entry point):
HHOOK mouseHook = SetWindowsHookEx(WH_MOUSE_LL, mouseHookProc, hInstance, 0);
if (!mouseHook) {
  LPWSTR errorMessage = NULL;
  if (FormatMessage(FORMAT_MESSAGE_ALLOCATE_BUFFER | FORMAT_MESSAGE_FROM_SYSTEM, NULL, GetLastError(), 0, (LPWSTR)&errorMessage, 0, NULL) == 0) {
    MessageBox(NULL, L"Couldn't format the error message...", L"Error", MB_OK | MB_ICONERROR);
  }
  MessageBox(NULL, errorMessage, L"Damn", MB_OK | MB_ICONERROR);
  LocalFree(errorMessage);
  return FALSE;
}

Although I tagged it as C, it really applies to any language that runs in Windows and can access low level mouse events, be it managed or not.

PS. By the way, although the intention I mentioned at the beginning was the motivation that started it all, I reckon it's impossible to do. If vertical scroll messages aren't always generated even in scrollable controls, I don't see horizontal ones being generated when there's nothing to scroll. It'd need to be implemented in the File Explorer itself most likely.

James Russell
  • 339
  • 1
  • 3
  • 12
  • Do you have an actual mouse you could test with? I'm not exactly sure how it works but I think a trackpad might generate gesture messages initially; and probably these get converted to WM_MOUSEWHEEL by DefWndProc. – Jonathan Potter Sep 02 '21 at 01:03
  • 1
    @JonathanPotter you're on point! I didn't have access to one yesterday, but tried with a cabled one today and indeed the messages appear (even if the control underneath doesn't scroll). Your comment led me to the docs again, searching for gestures this time and I found another page ( https://learn.microsoft.com/en-us/windows/win32/wintouch/windows-touch-gestures-overview ) where they explain what you said before about the default gesture handler. – James Russell Sep 02 '21 at 08:07
  • 1
    However, regarding legacy support it reads that panning (scrolling) makes the default gesture handler generate WM_VSCROLL or WM_HSCROLL messages, and I've only seen that in 1 case: the File Explorer and only if the view is long enough to make the scrollbar visible. So as it sometimes happens, it seems to be inconsistent... *sigh* – James Russell Sep 02 '21 at 08:19
  • It says the **default** gesture handler generates `WM_VSCROLL` and `WM_HSCROLL` messages, so if an application processes the [WM_GESTURE messages](https://learn.microsoft.com/en-us/windows/win32/wintouch/wm-gesture) itself (not forward them to `DefWindowProc`), this will not happen. – ssbssa Sep 03 '21 at 13:26
  • But that `WM_GESTURE` message is not being sent or posted to the apps either (or I can't see it with Spy++). I looked at what I had logged in those videos and it didn't show up for any of the _problematic_ programs. It's not one of the messages I could catch with a LL mouse hook though and I don't really want to be hooking into applications' memory and the like, so end of the line. I wonder what's going on though, there must be some other conditions not documented for those messages to be generated. – James Russell Sep 11 '21 at 17:30

0 Answers0