WinAPI client area not being redrawn after SetWindowPos() - winapi

I am trying to resize the window of another application (an ipoker client window), using this code:
SetWindowPos(hwnd, HWND_NOTOPMOST, x, y, width, height, SWP_ASYNCWINDOWPOS | SWP_NOACTIVATE | SWP_NOOWNERZORDER | SWP_NOZORDER);
Now this resizes the window properly, but the client area is not getting redrawn (meaning its just clipped to fit the new window area). This is how it should look:
how the window looks properly
But this is how it actually looks: client area is just being clipped
So I've inspected the messages the window receives upon resizing it by mouse (which works fine) with Spy++ and tried to just send all of them to the window using SendMessage(..), but without any luck as well.
WINDOWPOS wpos;
wpos.hwnd = hWnd;
wpos.x = x;
wpos.y = y;
wpos.cx = nWidth;
wpos.cy = nHeight;
wpos.flags = SWP_NOZORDER | SWP_NOACTIVATE | SWP_NOMOVE | 1000;
NCCALCSIZE_PARAMS params;
params.lppos = &wpos;
params.rgrc[0] = { x, y, width, height};
params.rgrc[1] = rcWindow; // rect of the window before resize
params.rgrc[2] = rcClient; // rect of the client before resize
SendMessage(hWnd, WM_WINDOWPOSCHANGED, 0, LPARAM(&wpos));
wpos.flags = SWP_NOZORDER | SWP_NOACTIVATE;
SendMessage(hwnd, WM_SIZE, SIZE_RESTORED, MAKELPARAM(nWidth, nHeight));
SendMessage(hwnd, WM_WINDOWPOSCHANGING, 0, LPARAM(&wpos));
wpos.flags = SWP_NOZORDER | SWP_NOACTIVATE | SWP_NOMOVE | 1800;
SendMessage(hwnd, WM_NCCALCSIZE, WPARAM(TRUE), LPARAM(&params));
Do you have any idea why all of this is not working? Is there no way around subclassing the window (I dont have any experience with that..)? I've also tried to resize the window using placemint (a window positioning tool), but it turns out to have the same problem. The only way to force it to redraw I've found is resize it manually.

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.

Disable window messages (No WNDPROC call)

So i'm trying to write a toggle fullscreen function for my game.
It works.. kinda, problem is the first SetWindowLongPtr() call causes a WM_SIZE message to be queued and sent to my WNDPROC (on MSDN i read changes to the window style are only cached and have to be applied using SetWindowPos() though).
SetWindowLongPtr(m_hWnd, GWL_STYLE, dwStyle);
SetWindowLongPtr(m_hWnd, GWL_EXSTYLE, dwExStyle);
SetWindowPos(m_hWnd, NULL, fullscreen ? 0 : window.left, fullscreen ? 0 : window.top, window.right, window.bottom, SWP_NOZORDER | SWP_FRAMECHANGED);
ShowWindow(m_hWnd, SW_SHOW);
When changing from windowed to fullscreen mode the client area gets resized to the window rect and updated like that (so now 'contains' the bars etc.).
Apart from causing my program to destroy the Vulkan context twice, this makes me save the wrong window dimensions for reverting to windowed mode later on (this is done when WM_SIZE occurs and when the game is in fullscreen mode).
Afterwards it's sized to the correct dimensions by SetWindowPos().
I mean i could certainly just hack in a bool to discard the WM_SIZE message when it should be, but i'm looking for a better and maybe less bodged way.
Is there a function to temporarily disable window messages or just the user def WNDPROC?
This is the function that I use to toggle fullscreen on and off.
I got this function from Raymond Chen.
I don't know if it messes with Vulkan though but it works with OpenGL.
WINDOWPLACEMENT GlobalWindowPosition = {sizeof(GlobalWindowPosition)};
void ToggleFullscreen(HWND WindowHandle)
{
DWORD Style = GetWindowLong(WindowHandle, GWL_STYLE);
if(Style & WS_OVERLAPPEDWINDOW)
{
MONITORINFO MonitorInfo = {sizeof(MonitorInfo)};
if(GetWindowPlacement(WindowHandle, &GlobalWindowPosition) &&
GetMonitorInfo(MonitorFromWindow(WindowHandle, MONITOR_DEFAULTTOPRIMARY),
&MonitorInfo))
{
SetWindowLong(WindowHandle, GWL_STYLE,
Style & ~WS_OVERLAPPEDWINDOW);
SetWindowPos(WindowHandle, HWND_TOP,
MonitorInfo.rcMonitor.left, MonitorInfo.rcMonitor.top,
MonitorInfo.rcMonitor.right - MonitorInfo.rcMonitor.left,
MonitorInfo.rcMonitor.bottom - MonitorInfo.rcMonitor.top,
SWP_NOOWNERZORDER | SWP_FRAMECHANGED);
}
}
else
{
SetWindowLong(WindowHandle, GWL_STYLE, Style | WS_OVERLAPPEDWINDOW);
SetWindowPlacement(WindowHandle, &GlobalWindowPosition);
SetWindowPos(WindowHandle, NULL, 0, 0, 0, 0,
SWP_NOMOVE | SWP_NOSIZE | SWP_NOZORDER |
SWP_NOOWNERZORDER | SWP_FRAMECHANGED);
}
}

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

How to show a swf and set background transparent by ActiveX?

My code as follow, but the background doesn't transparent also.. What should i do ?
QAxWidget *flash = new QAxWidget(0,0);
flash->resize(200,200);
flash->setControl(QString::fromUtf8("{d27cdb6e-ae6d-11cf-96b8-444553540000}"));
flash->dynamicCall("LoadMovie(long,string)",0,"D:/test.swf");
flash->dynamicCall("WMode", "transparent");
flash->show();
By the way,,In Qt , is there a another way to show swf ? Thank you....
add
flash->setAutoFillBackground(true)
if the movie has a transparent background, you should see widget's original backgorund
try this code
QAxWidget *flash = new QAxWidget(0, 0);
flash->resize(500, 400);
HWND hWnd = (HWND)flash->winId();
LONG lStyle = ::GetWindowLong(hWnd, GWL_STYLE);
lStyle &= ~(WS_CAPTION | WS_THICKFRAME | WS_MINIMIZE | WS_MAXIMIZE | WS_SYSMENU);
::SetWindowLong(hWnd, GWL_STYLE, lStyle);
LONG lExStyle = ::GetWindowLong(hWnd, GWL_EXSTYLE);
::SetWindowLong(hWnd, GWL_EXSTYLE, lExStyle|WS_EX_LAYERED|WS_EX_TOPMOST|WS_EX_TRANSPARENT);
typedef int (WINAPI* LPFUNC)(HWND, COLORREF , BYTE, DWORD);
HINSTANCE hins = ::LoadLibraryW(L"User32.DLL");
if(!hins)
return ;
LPFUNC func2 = (LPFUNC)GetProcAddress(hins,"SetLayeredWindowAttributes");
if(!func2)
return ;
COLORREF clrMask = RGB(255,255,255);
func2(hWnd, clrMask, 0, LWA_COLORKEY);
FreeLibrary(hins);
flash->setControl(QString::fromUtf8("{d27cdb6e-ae6d-11cf-96b8-444553540000}"));
flash->dynamicCall("LoadMovie(long,string)", 0, "d:/9023.swf");
flash->dynamicCall("WMode", "transparent");
flash->show();
show activex with transparent background.

Go to windowed mode in Direct3D 9

I'm making a Direct3D app, and I can easily go from Windowed to Fullscreen mode using IDirect3DDevice9::Reset with new presentation parameters. However, when I use the same trick to go from fullscreen to windowed mode, the window has now lost its borders.
If I try doing SetWindowLong to set the window style to WS_OVERLAPPED | WS_CAPTION | WS_SYSMENU (and then SetWindowPos with SWP_FRAMECHANGED), the window now gets its border, but the direct3d device no longer works. Resetting the device again doesn't work, and instead of Reset(), doing Release() then SetWindowLong() then CreateDevice() again, of course fails, as my managed resources are dependent on my device.
How do I make IDirect3DDevice9::Reset to go back to windowed mode, while creating a bordered window?
First, you need to change the properties of the window:
SetWindowLongPtr(hWnd, GWL_STYLE, WS_OVERLAPPEDWINDOW);
if (new_pos_size)
{
// if you want new position (pos_x, pos_y) and size (width, height)
UINT flags = SWP_FRAMECHANGED | SWP_SHOWWINDOW;
SetWindowPos(hWnd, HWND_NOTOPMOST, pos_x, pos_y, width, height, flags);
}
else
{
UINT flags = SWP_NOMOVE | SWP_NOSIZE | SWP_FRAMECHANGED | SWP_SHOWWINDOW;
SetWindowPos(hWnd, 0, 0, 0, 0, 0, flags);
}
Next you have to release any resources you created in default pool - D3DPOOL_DEFAULT (it's better to use D3DPOOL_MANAGED if possible). If you don't, IDirect3DDevice9::Reset will fail.
Then you can reset the device and finally recreate any resources if needed. Make sure you set up D3DPRESENT_PARAMETERS for IDirect3DDevice9::Reset correctly.

Resources