Prevent generation of WM_MOUSEMOVE after popup window is shown/hidden - winapi

I have encountered an annoying problem. When the mouse pointer is positioned over my main window and the owning popup window is shown (see example below) or is made invisible a WM_MOUSEMOVE message is generated each time even if the mouse has not been moved. For several reasons it can't be tolerated in my case.
hWnd = CreateWindowEx(0, wcx.lpszClassName, L"Demo", WS_OVERLAPPEDWINDOW, CW_USEDEFAULT, CW_USEDEFAULT, CW_USEDEFAULT, CW_USEDEFAULT, nullptr, 0, hInstance, nullptr);
HWND hWndPopupTest = CreateWindowEx(WS_EX_NOACTIVATE | WS_EX_TOPMOST, L"Static", L"DemoPopup", WS_POPUP | WS_VISIBLE, 10, 10, 100, 100, hWnd, 0, hInstance, nullptr);
ShowWindow(hWnd, SW_SHOW);
ShowWindow(hWndPopup, SW_SHOWNOACTIVATE);
Sleep(1000);
ShowWindow(hWndPopup, SW_HIDE);
The same behavior occurs when ReleaseCapture is called. Is this a feature that can be disabled? Is it a known "problem" or is there a workaround?
Edit: Dirty Workaround
In (main) window procedure we could test if the mouse position has changed since last WM_MOUSEMOVE. If the position has not changed it must be because wither a popup window was shown/hidden or a some window capture was released.

Based on the information provided here (thank you #IInspectable) my general solution would be to detect if the given point is a real point by looking in mouse position history with GetMouseMovePointsEx. If no point is found it means no valid movement occurred.
POINT CurrentPoint{GET_X_LPARAM(lParam), GET_Y_LPARAM(lParam)};
MapWindowPoints(hWnd, nullptr, &CurrentPoint, 1);
MOUSEMOVEPOINT mmpi = {
CurrentPoint.x, CurrentPoint.y, GetTickCount(), 0
};
MOUSEMOVEPOINT mmpo = {0};
if (GetMouseMovePointsEx(sizeof(mmpi), &mmpi, &mmpo, 1, GMMP_USE_DISPLAY_POINTS) > 0) {
MyInstance->HandleMouseMove(POINT{GET_X_LPARAM(lParam), GET_Y_LPARAM(lParam)}, wParam);
} else {
// No mouse point found in history, so couldn't be a valid point
}

Related

Going Fullscreen in win32 without 2 WM_SIZE message

So I am creating a fullscreen function in win32 c++ doing:
uint8_t isFullscreen = 0;
RECT winRect; //Current Window Rect
RECT nonFullScreenRect; //Rect Not In Full Screen Position (used to restore window to not full screen position when coming out of fullscreen)
uint32_t screen_width = DEFAULT_SCREEN_WIDTH;
uint32_t screen_height = DEFAULT_SCREEN_HEIGHT;
void Fullscreen( HWND WindowHandle )
{
isFullscreen = isFullscreen ^ 1;
if( isFullscreen )
{
//saving off current window rect
nonFullScreenRect.left = winRect.left;
nonFullScreenRect.right = winRect.right;
nonFullScreenRect.bottom = winRect.bottom;
nonFullScreenRect.top = winRect.top;
SetWindowLongPtr( WindowHandle, GWL_STYLE, WS_POPUP | WS_CLIPCHILDREN | WS_CLIPSIBLINGS | WS_VISIBLE ); //causes a resize msg
HMONITOR hmon = MonitorFromWindow(WindowHandle, MONITOR_DEFAULTTONEAREST);
MONITORINFO mi = { sizeof( mi ) };
GetMonitorInfo( hmon, &mi );
screen_width = mi.rcMonitor.right - mi.rcMonitor.left;
screen_height = mi.rcMonitor.bottom - mi.rcMonitor.top;
MoveWindow( WindowHandle, mi.rcMonitor.left, mi.rcMonitor.top, (int32_t)screen_width, (int32_t)screen_height, FALSE );
}
else
{
SetWindowLongPtr( WindowHandle, GWL_STYLE, WS_OVERLAPPEDWINDOW | WS_VISIBLE );
screen_width = nonFullScreenRect.right - nonFullScreenRect.left;
screen_height = nonFullScreenRect.bottom - nonFullScreenRect.top;
MoveWindow( WindowHandle, nonFullScreenRect.left, nonFullScreenRect.top, (int32_t)screen_width, (int32_t)screen_height, FALSE );
}
}
However when it goes fullscreen, the function generates 2 WM_SIZE messages. While when it goes windowed, it generates only 1.
Why is that the case? And how can I make it generate only 1 WM_SIZE message for the proper full screen size?
How can I update an HWND's style and position atomically? asks about it but no one answers it
The reasons I need this is because I am using DirectX12 and on WM_SIZE I wait for all the signals at the end of the command queues before resizing all the swap chain back buffers. And I don't want to have to resize the swap chain twice when switching to fullscreen mode.
case WM_SIZE:
{
screen_width = LOWORD( LParam );
screen_height = HIWORD( LParam );
//DirectX stuff here
}break;
Thanks in advance!
Updated Answer:
The Win32 API allows you to modify parameters of the window one at a time. When a parameter is modified, the API may or may not update the window and trigger a WM_SIZE that will be the size of the window given the current parameters.
Since to have a complete full screen window you need to make at least 2 calls, one to update GWL_STYLE and another to update GWL_EXSTYLE, you have a big chance of getting 2 WM_SIZE calls. One of them will give you the window size without the menu, and the other the full screen window size. It depends in which order you call SetWindowLongPtr, but you'll probably get 2 WM_SIZE and only the second one is "correct", i.e. the one you want in the end.
The more reliable solution to your problem is to use a variable at the top of Main.cpp:
int isTogglingFullScreen = false;
Then inside your full screen toggle code (note where isTogglingFullScreen is being set):
case WM_SYSKEYDOWN:
if (wParam == VK_RETURN && (lParam & 0x60000000) == 0x20000000)
{
// Implements the classic ALT+ENTER fullscreen toggle
if (s_fullscreen)
{
isTogglingFullScreen = true;
SetWindowLongPtr(hWnd, GWL_STYLE, WS_OVERLAPPEDWINDOW);
isTogglingFullScreen = false;
SetWindowLongPtr(hWnd, GWL_EXSTYLE, 0);
int width = 800;
int height = 600;
if (game)
game->GetDefaultSize(width, height);
SetWindowPos(hWnd, HWND_TOP, 0, 0, width, height, SWP_NOMOVE | SWP_NOZORDER | SWP_FRAMECHANGED);
ShowWindow(hWnd, SW_SHOWNORMAL);
}
else
{
isTogglingFullScreen = true;
SetWindowLongPtr(hWnd, GWL_EXSTYLE, WS_EX_TOPMOST);
SetWindowLongPtr(hWnd, GWL_STYLE, WS_POPUP);
SetWindowPos(hWnd, HWND_TOP, 0, 0, 0, 0, SWP_NOMOVE | SWP_NOSIZE | SWP_NOZORDER | SWP_FRAMECHANGED);
isTogglingFullScreen = false;
ShowWindow(hWnd, SW_SHOWMAXIMIZED);
}
s_fullscreen = !s_fullscreen;
}
break;
Finally, inside WM_SIZE, change
else if (!s_in_sizemove && game)
{
game->OnWindowSizeChanged(LOWORD(lParam), HIWORD(lParam));
}
to
else if (!s_in_sizemove && game && !isTogglingFullScreen)
{
game->OnWindowSizeChanged(LOWORD(lParam), HIWORD(lParam));
}
This will give you a single call to OnWindowSizeChanged() when you toggle full screen, and the call will be with the correct final size.
--
Old Answer:
If you only want a single WM_SIZE to trigger, when you switch to full screen then you should go for something like this:
SetWindowLongPtr(hWnd, GWL_EXSTYLE, WS_EX_TOPMOST);
SetWindowPos(hWnd, HWND_TOP, 0, 0, 0, 0, SWP_NOMOVE | SWP_NOSIZE | SWP_NOZORDER | SWP_FRAMECHANGED);
ShowWindow(hWnd, SW_SHOWMAXIMIZED);
Any SetWindowLongPtr call to GWL_STYLE will trigger a WM_SIZE, so make sure it's only called with GWL_EXSTYLE. For example, if you both set GWL_EXSTYLE to what you want, and reset GWL_STYLE to 0, you'll trigger WM_SIZE twice.
To make it clearer:
Don't use GWL_STYLE in SetWindowLongPtr because it triggers a useless WM_SIZE
ShowWindow will trigger WM_SIZE
The above code will ultimately only trigger WM_SIZE once.
It turns out that YMMV. It's entirely possible that the first time you switch fullscreen, you'll get 2 WM_SIZE. One will be with the original size, and the other with the new size. Subsequent calls will trigger only one WM_SIZE.
Hence, the really bulletproof solution (which I was using anyway before playing around with the SetWindowLongPtr to answer this question, is to validate that the window size has actually changed. Because one thing that I can guarantee in the above call is that you'll never get more than 1 call with the new size. At most you'll get a WM_SIZE call with the old size, which you'll discard by checking that it's the same as the current size.
If you use the DevicesResources template for DX12, you'll see that there's a check in OnWindowSizeChanged() that does nothing if the size hasn't changed.

Owner and Owned window Activation issue

In an experimental code, when creating three top level windows with hierarchical ownership I am seeing weird behavior when dismissing them in reverse order.
Code:
#include <Windows.h>
LRESULT CALLBACK WndProc(HWND, UINT, WPARAM, LPARAM);
int WINAPI WinMain(HINSTANCE hInstance, HINSTANCE hPrevInstance, PSTR szCmdLine, int iCmdShow)
{
MSG msg;
WNDCLASS wndClass;
WCHAR className[] = L"OwnedWindowsWeirdness";
wndClass.cbClsExtra = 0;
wndClass.cbWndExtra = 0;
wndClass.lpszMenuName = NULL;
wndClass.hbrBackground = (HBRUSH) GetStockObject(WHITE_BRUSH);
wndClass.hCursor = LoadCursor(NULL, IDC_ARROW);
wndClass.hIcon = LoadIcon(NULL, IDI_APPLICATION);
wndClass.hInstance = hInstance;
wndClass.lpfnWndProc = WndProc;
wndClass.lpszClassName = className;
wndClass.style = CS_HREDRAW | CS_VREDRAW;
if(!RegisterClassW(&wndClass))
{
MessageBoxW(0, L"Unable to register class...Exiting!", className, MB_OK);
return -1;
}
HWND hwnd1 = CreateWindowW(className, L"Main Window", WS_OVERLAPPEDWINDOW,
CW_USEDEFAULT, CW_USEDEFAULT, 500, 400,
NULL, 0, hInstance, 0);
HWND hwnd2 = CreateWindowW(className, L"Main Window > Window 2", WS_OVERLAPPEDWINDOW,
CW_USEDEFAULT, CW_USEDEFAULT, 400, 300,
hwnd1, 0, hInstance, 0);
HWND hwnd3 = CreateWindowW(className, L"Main Window > Window 2 > Window 3", WS_OVERLAPPEDWINDOW,
CW_USEDEFAULT, CW_USEDEFAULT, 300, 200,
hwnd2, 0, hInstance, 0);
ShowWindow(hwnd1, SW_SHOWNORMAL);
UpdateWindow(hwnd1);
ShowWindow(hwnd2, SW_SHOWNORMAL);
UpdateWindow(hwnd2);
ShowWindow(hwnd3, SW_SHOWNORMAL);
UpdateWindow(hwnd3);
while(GetMessage(&msg, 0,0,0))
{
TranslateMessage(&msg);
DispatchMessage(&msg);
}
return msg.wParam;
}
LRESULT CALLBACK WndProc(HWND hwnd, UINT iMsg, WPARAM wParam, LPARAM lParam)
{
// Subdeveloper: Purposefully not complicating the code by calling PostQuitMessage/etc!
// In absence of which, this test application will need to be closed using
// task manager
return DefWindowProc(hwnd, iMsg, wParam, lParam);
}
The code above does this:
Creates a top level window Main Window
Creates another top level window Window 2 and assigns its owner as Main Window
Creates yet another top level window Window 3 and assigns its owner as Window 2
All are non-modal if you observe closely, but with correct ownership
Now when this application is run (Release built, run on Windows 10 x64) and we close the windows in reverse order, after closing Window 2 activation goes away to existing Notepad window.
The behavior could be seen in following screen capture:
I am wondering what is going on. Generally this kind of behavior occurs when we miss setting correct ownership!
Secondly, when hunting around I did see that focus goes to Default IME window sometimes (i.e. Windows Input Method Editor). I think a default window is assigned for IME to every application with UI? If so maybe as soon as I create the Main Window, an IME window is created, and then on my next calls to CreateWindowW, the other 2 owned windows are created, thus changing the siblings in top level windows list? This is just a speculation for now.
Can someone explain this, and whats the "no-hack" workaround for this?
Adding additional WS_POPUP style to Window 2 (Or use WS_CAPTION | WS_POPUPWINDOW replace WS_OVERLAPPEDWINDOW.) solves the issue for me.
With WS_POPUP, the ownership will be picked, you will see the behavior you expected. Without WS_POPUP, system will find the next window to activate, this is undocumented.

Why is my capture window code not working?

I am newbie to winapi. I have seen an example to capture desktop excluding some windows at codeproject
There a child window is created and it is captured.
hwndMag = CreateWindow(WC_MAGNIFIER, TEXT("MagnifierWindow"),
WS_CHILD | MS_SHOWMAGNIFIEDCURSOR | WS_VISIBLE,
0, 0, m_ScreenX, m_ScreenY,
hostDlg->GetSafeHwnd(), NULL, hInstance, NULL );
Instead of creating a child window, I want to create a parent window.
I have tried with this code.
hwndMag = CreateWindow(WC_MAGNIFIER, TEXT("MagnifierWindow"),
MS_SHOWMAGNIFIEDCURSOR | WS_VISIBLE,
0, 0, m_ScreenX, m_ScreenY,
NULL , NULL, hInstance, NULL );
A new window is visible with black screen. And even when I click the capture button the window is stucked.
Why is this happening and How can I make that work with a new parent window?
Thanks
The magnifier window should be a child window. It therefore needs a host parent window. The example code on MSDN shows how to do it:
BOOL CreateMagnifier(HINSTANCE hInstance)
{
// Register the host window class.
WNDCLASSEX wcex = {};
wcex.cbSize = sizeof(WNDCLASSEX);
wcex.style = 0;
wcex.lpfnWndProc = HostWndProc;
wcex.hInstance = hInstance;
wcex.hCursor = LoadCursor(NULL, IDC_ARROW);
wcex.hbrBackground = (HBRUSH)(1 + COLOR_BTNFACE);
wcex.lpszClassName = WindowClassName;
if (RegisterClassEx(&wcex) == 0)
return FALSE;
// Create the host window.
hwndHost = CreateWindowEx(WS_EX_TOPMOST | WS_EX_LAYERED | WS_EX_TRANSPARENT,
WindowClassName, WindowTitle,
WS_CLIPCHILDREN,
0, 0, 0, 0,
NULL, NULL, hInstance, NULL);
if (!hwndHost)
{
return FALSE;
}
// Make the window opaque.
SetLayeredWindowAttributes(hwndHost, 0, 255, LWA_ALPHA);
// Create a magnifier control that fills the client area.
hwndMag = CreateWindow(WC_MAGNIFIER, TEXT("MagnifierWindow"),
WS_CHILD | MS_SHOWMAGNIFIEDCURSOR | WS_VISIBLE,
0, 0,
LENS_WIDTH,
LENS_HEIGHT,
hwndHost, NULL, hInstance, NULL );
if (!hwndMag)
{
return FALSE;
}
return TRUE;
}
This same documentation also says:
The magnifier control must be hosted in a window created with the WS_EX_LAYERED extended style. After creating the host window, call SetLayeredWindowAttributes to set the opacity of the host window. The host window is typically set to full opacity to prevent the underlying screen content from showing though. The following example shows how to set the host window to full opacity:
SetLayeredWindowAttributes(hwndHost, NULL, 255, LWA_ALPHA);
If you apply the WS_EX_TRANSPARENT style to the host window, mouse clicks are passed to whatever object is behind the host window at the location of the mouse cursor. Be aware that, because the host window does not process mouse clicks, the user will not be able to move or resize the magnification window by using the mouse.
The MSDN example above illustrates this. And the CodeProject article that you link to also adheres to these rules. You must do likewise.
in case it's of interest, I created a sample app which uses the magnification API a while back called "Windows 7 UI Automation Client API C# sample (focus tracking)", available at https://code.msdn.microsoft.com/Windows-7-UI-Automation-6390614a. The app tracks where keyboard focus is, and then shows the element with focus in a magnification window, (and inverts the colors using the magnification API). This is a C# app, so it uses interop to access the magnification API.
A screenshot of the results is shown below.
Thanks,
Guy

Is it possible to add control to another process's window?

I've created a MFC window and, from another project, I wanted to add a button to that window via its handle (using FindWindow). The handle is correct, but nothing happens. Is this unachievable or am I doing something wrong?
Here's my code:
HWND hWnd = FindWindow(NULL, "MFCtest");
if(hWnd)
{
printf("Found it\n");
HWND hwndButton = CreateWindow(
"BUTTON",
"OK", // Button text
WS_TABSTOP | WS_VISIBLE | WS_CHILD | BS_DEFPUSHBUTTON, // Styles
0, // x position
0, // y position
100, // Button width
100, // Button height
hWnd, // Parent window
NULL, // No menu.
(HINSTANCE)GetWindowLong(hWnd, GWL_HINSTANCE),
NULL); // Pointer not needed.
if(!hwndButton)
printf("GetLastError: %d\n", GetLastError());
}
Yes. BUT!
The problem is that a window always belongs to a thread that creates it. So the messages for such a control messages will arrive in the thread that creates such a window.
This might work, but because messages form the parent window will be sent to the child will take the long way through the message queue and may cause locks if the message can't be retrieved and handled directly.
So at the end I would suggest to: Don't do it!

Windows Tooltips with style TTS_BALLOON not shown on some installations

I maintain a Win32 desktop application that shows tooltips. This so far works pretty well on many XP And Windows 7 installations.
We now get reports from a few customers that they do not see our tool tips. The See a rectangular tooltip (that does not have the TTS_BALLOON attribute. But those created with TTS_BALLOON are not visible. The log files sent by a customer report that CreateWindowEx returns a valid windows handle as well as the coordinates and string contained are correct.
The machine concerned runs Windows XP and is updated regularly.
Has anybody encountered a similar behavior?
How can we solve this problem?
Source Code:
gHintInfo.hwnd = CreateWindowEx(NULL, TOOLTIPS_CLASS, NULL,
WS_POPUP | TTS_NOPREFIX | TTS_ALWAYSTIP | TTS_BALLOON,
CW_USEDEFAULT, CW_USEDEFAULT,
CW_USEDEFAULT, CW_USEDEFAULT,
NULL, NULL,
(HINSTANCE)xvt_vobj_get_attr(TASK_WIN, ATTR_WIN_INSTANCE),
NULL);
Trace(1, "\n### DrawHint %d, hwnd = %08x, Text =\n%s\n###\n\n", __LINE__, gHintInfo.hwnd, tx);
if (gHintInfo.hwnd != NULL)
{
TOOLINFO ti;
ti.cbSize = sizeof (ti);
ti.uFlags = TTF_TRANSPARENT | TTF_ABSOLUTE;
ti.hwnd = hwndParent;
ti.uId = 0;
ti.hinst = NULL;
ti.lpszText = (char *) tx;
GetClientRect (hwndParent, &ti.rect);
dbgrct(ti.rect);
dbgpnt(gHintInfo.LastHintLoc);
SendMessage(gHintInfo.hwnd, TTM_TRACKPOSITION,0, MAKELONG(gHintInfo.LastHintLoc.v, gHintInfo.LastHintLoc.h));
SendMessage (gHintInfo.hwnd, TTM_ADDTOOL, 0, (long) &ti);
SendMessage (gHintInfo.hwnd, TTM_SETDELAYTIME, TTDT_AUTOMATIC, -1);
SendMessage (gHintInfo.hwnd, TTM_SETMAXTIPWIDTH, 0, 500);
SendMessage (gHintInfo.hwnd, TTM_TRACKACTIVATE, TRUE, (long) &ti);
}
The log output created by this code on the machine that does not diesplay the tooltips is:
### DrawHint 474, hwnd = 00090112, Text =
Some text with
multiple lines
###
ti.rect left = 0, top = 0, right = 1280, bottom = 978
gHintInfo.LastHintLoc h = 295, v = 539
(We set ti.rect to the coordiantes of the whole screen, as windows resizes the tool tip to the containing text anyway.)
EDIT:
We actually added a configuration property to our Software that does nothing more than adding or removing the TTS_BALLOON attribute. This solves the problem on the machines concerned.
The best solution can be found here.
To disable the tooltip balloon, set EnableBalloonTips to 1

Resources