WM_MOUSEWHEEL doesn't seem to be _always_ generated, what gives? - c

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.

Related

Why does my window only get shown after I use SDL_PollEvent()?

I'm using SDL2 in C and wanted to show something to the screen without the need for taking in inputs. But when I ran the code to present a black screen the window would not open. I proceeded to do what I knew would make it pop up and added a SDL_Event variable and used SDL_PollEvent(). I would like to know why I had to do this, and if there would be a way for me to use a SDL_Window without polling any events.
(For example: something like an animation).
The code in question is as follows:
#include <stdio.h>
#include <stdlib.h>
#include <SDL2/SDL.h>
int main()
{
if (SDL_Init(SDL_INIT_VIDEO))
{
fprintf(stderr, "Error while Initalizing SDL2: %s\n", SDL_GetError());
return EXIT_FAILURE;
}
SDL_Window *window = SDL_CreateWindow("Connect Four", 100, 100, 500, 500, SDL_WINDOW_SHOWN);
if (!window)
{
fprintf(stderr, "Error while Initalizing window: %s\n", SDL_GetError());
return EXIT_FAILURE;
}
SDL_Renderer *renderer = SDL_CreateRenderer(window, -1, SDL_RENDERER_ACCELERATED | SDL_RENDERER_PRESENTVSYNC);
if (!renderer)
{
SDL_DestroyWindow(window);
fprintf(stderr, "Error while Initalizing renderer: %s\n", SDL_GetError());
return EXIT_FAILURE;
}
SDL_Event events; //without these two lines
SDL_PollEvent(&events); //the window will not open on screen
SDL_SetRenderDrawColor(renderer, 0, 0, 0, 255);
SDL_RenderClear(renderer);
SDL_RenderPresent(renderer);
SDL_Delay(3000);
SDL_DestroyWindow(window);
SDL_Quit();
return EXIT_SUCCESS;
}
It may be confusing because some SDL examples (including SDL doc wiki) uses "draw&delay" code without event processing, but that is not a reliable way to display things on screen. That examples are overly simplified and was written long time ago when it kind of worked, but only for simplest things, as 3 seconds is probably not long enough for window manager to kill unresponsive program, or for user to notice window image collapses if it is minimised or covered by other windows.
Short answer to your question is no, your program have to communicate with window manager (side note - it may not be necessary on some operating/graphics systems); you have to get events and react to special window events (e.g. your window becoming visible or user requesting resize). In SDL, this is done with SDL_PumpEvents call, which internally does this communication and generates event queue that you can inspect later, so you need to call that frequently, either directly or indirectly via SDL_PollEvent or SDL_WaitEvent. If you don't, window manager probably will not be nice to you - depending on window manager, you may get "application not responding, let's kill it" dialog, greyed out window or, as in your case, no window at all (that particular case is because you've presented your rendering result before "your window is now visible" event was received, so your image is discarded; that's why processing events before rendering changed how things go).
But even so, processing events once is not correct either. What you have is still optimistic "let's hope nothing breaking will happen in 3 seconds". If you do animation, process events on every frame; but if you have static display code - it may be good idea to treat it like it is animated too. Basically you need rendering loop, with event processing at start, and redraw code to re-generate your image when you have to. This "have to" may be either unconditional (full redraw on every iteration), or, if you really don't want to (e.g. for processing cost reasons - most GUI programs don't do full redraw when idle) you still have to redraw when window manager says your previous image is no longer valid - SDL notifies you of that via SDL_WindowEvent of SDL_WINDOWEVENT_EXPOSED type. In that case you may want to use blocking SDL_WaitEvent to avoid unnecessary iterations.

WM_MOUSEMOVE issues with position

I am working on a program that is supposed to simulate basic mouse input for a program while it stays in the background (meaning, I want to do other things in other windows with the actual mouse and keyboard while the target receives input). One thing I need to be able to do is move the mouse to a specific (x,y) point.
It seems to work for the most part, but in a certain region of the screen the message only works correctly some of the time. Other times, it moves to a consistent but wrong other point within the region. I am reading that sometimes it is relative, but if I spam the message repeatedly, it does seem to work consistently. Also, reading in Spy++, the messages that are sent by me actually moving my mouse are using what seem to be absolute coordinates.
My function is here:
void mouseMove(short x, short y) {
PostMessage(wnd, WM_MOUSEMOVE, 0, MAKELPARAM(x, y));
}
Preferably, I'd like for any (x,y) to be absolute so I can have the region treated as any other. But it would also be fine to set the position to a coordinate relative to the top left corner of the region.
I have tried just about all of the messages I've seen sent to the window in Spy++ before the WM_MOUSEMOVE but nothing is helping.
How might I approach this?
Edit, since details might not prove enough info:
I am sending these messages in this order:
WM_ACTIVATE 2 0
WM_MOUSEMOVE 0 MAKELPARAM(x,y)
WM_LBUTTONDOWN MK_LBUTTON MAKELPARAM(x,y)
WM_LBUTTONUP 0 MAKELPARAM(x,y)
I think what I initially thought (bad WM_SETCURSOR area) is wrong because Spy++ consistently shows HTCLIENT being used.
I have found a lot of people trying to do this (background input) and most seem to be told it's impossible. It's not impossible, and I think if someone can answer how to do this correctly once and for all it would be doing quite a few people a big favor.
Synthesizes keystrokes, mouse motions, and button clicks.
UINT WINAPI SendInput(
_In_ UINT nInputs,
_In_ LPINPUT pInputs,
_In_ int cbSize
);
PostMessage function. Places (posts) a message in the message queue
associated with the thread that created the specified window and
returns without waiting for the thread to process the message.
For PostMessage to work you need to be in the process as the target that you can achieve with SetWindowHook but the these links will do the jobs:
https://msdn.microsoft.com/en-us/library/windows/desktop/ms646310(v=vs.85).aspx
https://msdn.microsoft.com/en-us/library/ms171542(v=vs.110).aspx

parts drawn with DrawThemeBackground on Windows 10 are not correct

Problem description
I want to create a Windows API app in C which renders the menu and the caption buttons in the same non-client area, similar to Firefox
In order to do this, I've determined the solution needs to:
be type WS_POPUP, in order for the menu to be aligned to the top
take ownership of the non-client area (where the menu is rendered)
manually render the minimize/maximize/close buttons
The solution needs to work on Windows 7, 8, and 10 (and ideally future versions too).
How it looks now
I have a test program available on GitHub.
In my app, I've overridden the appropriate events:
WM_NCCALCSIZE, WM_NCHITTEST, WM_NCLBUTTONDOWN, WM_NCLBUTTONUP, WM_NCMOUSEMOVE, WM_NCPAINT
And then I repaint non-client areas on these events:
WM_NCACTIVATE, WM_SETTEXT
Here's an example of how I'm doing the rendering:
// globals set elsewhere
RECT customAreaRect, minRect, maxRect, closeRect, coverMenuRect;
BOOL maximized;
// ...
LRESULT OnPaintNCA(HWND hWnd, WPARAM wParam, LPARAM lParam) {
RECT windowRect;
HRGN hRgn = NULL;
GetWindowRect(hWnd, &windowRect);
if (wParam == 1) {
hRgn = CreateRectRgnIndirect(&windowRect);
} else {
hRgn = (HRGN)wParam;
}
if (hRgn) {
// Carve out the area for custom content
HRGN captionButtonRgn = CreateRectRgnIndirect(&customAreaRect);
CombineRgn(hRgn, hRgn, captionButtonRgn, RGN_XOR);
DeleteObject(captionButtonRgn);
// Force default painting for non-client area
LRESULT ret = DefWindowProc(hWnd, WM_NCPAINT, (WPARAM)hRgn, 0);
// black background covering part of menu, behind buttons
HDC hDC = GetWindowDC(hWnd);
FillRect(hDC, &coverMenuRect, (HBRUSH)GetStockObject(BLACK_BRUSH));
HTHEME hTheme = OpenThemeData(hWnd, TEXT("WINDOW"));
DrawThemeBackground(hTheme, hDC, WP_MINBUTTON, partState, minRect, NULL);
DrawThemeBackground(hTheme, hDC, maximized ? WP_RESTOREBUTTON : WP_MAXBUTTON, partState, maxRect, NULL);
DrawThemeBackground(hTheme, hDC, WP_CLOSEBUTTON, partState, closeRect, NULL);
CloseThemeData(hTheme);
}
}
The rendered result looks like this:
Unfortunately, the styles used for the parts (minimize, maximize/restore, close) look like the styles for Windows 7/8, and not the native Windows 10 controls. I've been searching for a way to do this for several days without luck. I need help understanding how to render these buttons for Windows 10 using the Windows API.
Current status (and what I've tried so far)
My first hunch was that I need to properly enable Visual Styles.
Per this article, calls checking the OS version will get Windows 8 unless you specifically target Windows 10 via a manifest. Click here to view my manifest. This does work:
Before GetVersionEx returned major=6, minor=2, build=9200
Now GetVersionEx returns major=10, minor=0, build=10586
Per the official "Enabling Visual Styles" article, I made sure to use Common Controls version 6.
Added linker input for Comctl32.lib
Call is made to InitCommonControls on program start
Added dependency for version 6 to the application manifest
Here are some screenshots of relevant project settings that I have tried:
Other ideas
I'm running low on things to try. Before throwing in the towel, there were some things I was going to try:
Idea 1: using GetThemeStream which allows you to retrieve the size/bitmap for controls.
Load aero msstyles file like so:
HMODULE themeFile = LoadLibraryEx(TEXT("C:\\Windows\\Resources\\Themes\\aero\\aero.msstyles"), NULL, LOAD_LIBRARY_AS_DATAFILE);
Get the bitmap for the part (minimize button, maximize button, etc) like so (passing the loaded theme file):
GetThemeStream(h, WP_MAXBUTTON, MAXBS_NORMAL, TMT_DISKSTREAM, (void**)&buffer, &bufferSize, themeFile);
Load the bitmap; it appears to be in PNG format (I haven't gotten this far)
Draw the bitmap
Idea 2: copy the non-client area from a hidden window which has the caption area (and minimize, maximize, close buttons).
Create a window which has the caption and min/max buttons, never activating it.
In the non-client paint, get the DC for that Window and capture the pixels for the min/max/close button
Render them using bitblt
I think the issue comes from trying to use WM_NCPAINT on OS version >= Win Vista.
Since Vista all NC rendering is controlled by DWM (desktop window manager). If you still dare to handle WM_NCPAINT, DWM rendering will be turned off and you get "old-school" look:
From the Shell Revealed Blog:
The DWM doesnt have any legacy worries because applications cannot
draw inside the glass frame, since its rendered and managed by a
totally different process. If an application tries to do it, Windows
will detect it and remove the glass frame entirely (and therefore
revert to the Basic frame), so that the application can draw what it
wants to draw.
To get proper results, you have to do it the "DWM way" (specifically the sections "Removing the Standard Frame" and "Drawing in the Extended Frame Window"). This works by letting DWM render the frame within the client area, so you can paint over it. Also with this solution you don't have to draw the caption buttons on your own. This answer summarizes the required steps (under "Aero supported solution").
The caveat is that you propably have to draw the menu on your own and you can't use most of GDI API for that, because GDI ignores the alpha channel and things will look ugly if the frame is translucent (Vista and Win 7 by default, Win8+ with extensions). BitBlt() works if the source is a memory DC that contains a 32bpp bitmap with an alpha channel. GDI+ works aswell.

Rendering issue in WPF window while dragging

I have WPF window in which my Direct3D engine render. WPF Window I start from my engine, which I develop on C++/CLI.
I have 2 displays and when I move window between them, part of the window became white and not update until I am not stop dragging it (like on screen shot).
Does somebody know how to fix this issue?
It will be hard to post code here as it is a lot of code, but I will try:
I use managed C++ and unmanaged in one project:
So, I add .Net dll`s to have access to the WPF window:
Window^ wnd = gcnew Window();
wnd->Width = 1280;
wnd->Height = 720;
wnd->Show();
WindowInteropHelper^ helper = gcnew WindowInteropHelper(wnd);
HWND WPFHwnd = (HWND)helper->Handle.ToPointer();
HINSTANCE hinstance = (HINSTANCE)Marshal::GetHINSTANCE(this->GetType()->Module).ToPointer();
engine->SetHisnstanceHWND(WPFHwnd, hinstance);
engine->InitializeSystem(wnd->ActualWidth, wnd->ActualHeight);
engine - this is unmanaged class which incapsulates all work with rendering.
Further it is a stardard d3d rendering loop. Nothing special.
It render OK, but when I resizing or move window, it draw a part of it like this (with white). When I stop resizing or moving - all OK again.
My question is: is it possible to get rid of this effect or not?
UPDATE:
I hooked the wndProc if the WPF window and try to use UpdateLayout() when size changing, but this has no effect. Seems window is not react on any intervention.
Well, it sounds like your engine is receiving WM_ERASEBKGND and it clears the window. After you release the window, it sends WM_PAINT and everything is clear again. That's what I believe might be happening.
In order to test this theory, you can handle that message and return true. Put that to your engine message handling routine:
case WM_ERASEBKGND:
{
PAINTSTRUCT pss;
BeginPaint(hWnd,&pss);
EndPaint(hWnd,&pss);
return true;
}
You can use WinSpy++ to check out what messages are delivered to underneath window. If you want to see why the background is whited out, you can do it through ProcessMonitor and see what API calls are being issued. You're probably interested in API calls that include the color white.

Why does handling WM_NCCALCSIZE cause my window to jump?

I have a WPF app which snaps to screen edges (I just set the .Top or .Left of the window if you're within 20 pixels of the screen edge), but I recently added some code provided by the WPF SDK Team to "mess" with the window chrome, and although it's working great (screenshot), it's causing the "snapto" to move the window unexpectedly (e.g.: it jumps to the left when it should be snapping straight down to the bottom)
I've narrowed it down to their handling of the WM_NCCALCSIZE ... which is really odd because they basically don't do anything, they just say they handle it, and return 0.
According to the documentation of WM_NCCALCSIZE, this should just result in the whole window being treated as client (having no non-client edge), but somehow it also means that whenever my snap-to code moves the window down to the bottom of the screen, it also moves left about 134 pixels ... (moving to the other edges has similar side effects) and as long as I hold the mouse to drag it, it flickers back and forth from where it's supposed to be. If I comment the WM_NCCALCSIZE handling out, the snap-to works the way it should (but the form doesn't look right).
I've tried everything I can thing of in the WM_NCCALCSIZE handler, but I can't stop it from jumping left ... and of course, WM_NCCALCSIZE only gets called when the window size changes, so I don't understand how it causes this in the first place!
P.S. If you want to actually see the code, it's already on CodePlex, in two files, look for _HandleNCCalcSize and OnWindowLocationChanged
The reason this happens is that handling the WM_NCCALCSIZE changes the overall size of the window ... but if you're moving the window, changing your position during WM_MOVE or WM_WINDOWPOSCHANGED (which corresponds to the WPF WindowPositionChanged event) causes another WM_NCCALCSIZE message ...
Making changes during WM_NCCALCSIZE (even just asserting that you handled the message) causes another call to WM_MOVE ... which puts you into a loop where the "FROM" part of the positionchanged message stays the same (making the window "jump" from where it started to the position you adjust it to during WM_MOVE over and over as it changes back after WM_NCCALCSIZE).
The Correct Way
What you have to do is to obey Raymond Chen and handle WM_WINDOWPOSCHANGING instead. It happens before these other messages, and that way they do not interfere with each other!
The wParam always seems to be TRUE (1) and lParam is a NCCALCSIZE_PARAMS ...
The intent is to do exactly what you said: to force the whole window to be "client" and then use the Vista DWM apis to extend the frame into the client area. I just don't see why it's moving so far to the left...
If I trace or breakpoint the HandleNCCalcSize method, when I resize the window (while it's on the edge so the snap-to fires), the NCCalcSize gets called twice: once where it should be, and then off to the left, where it ends up.

Resources