SetWindowPos with scaled-up dialog on Laptop - c++

Below is a very old function that has worked on numerous computers, never seen a bug, but now this laptop is experiencing problems. According to the tester, "Seems to be fine until I reboot and don’t have another monitor plugged in." It seems Windows 10 scales dialog content in some situations making the actual size differ from the designed size.
void ResizeComponent::SetWidth( int width /*= DEF_WIDTH*/ )
{
CRect rect;
this->GetWindowRect(rect);
this->SetWindowPos(NULL, 0,0, width, rect.Height(), /*resize only*/SWP_NOZORDER|SWP_NOMOVE);
}
Okay, usage info:
const static int WIDTH_PANEL4 = 585;
SetWidth(WIDTH_PANEL4);
According to a screenshot from that laptop, in one example the window is 581 wide, and when I run it on a development computer, it's also 581 wide. BUT: The laptop dialog is scaled larger, and so 581 is no longer the correct size.
I don't know how to deal correctly with this situation.

Because dialogs are laid out in "dialog units", I do not use hard pixel counts in my source. I base my dynamic size/position calculations based on the rendered size of the dialog and/or its controls. If your customer changes the system text size (Control Panel/Display Settings of 100% 125% 150% etc.), then you will definitely see issues if you code "hard 100% rendering" pixel values.
I am guessing that your laptop may be doing this type of "translation" when rendering with monitors that do not match the "native resolution" of the built-in laptop monitor.
Here is an example where I reposition OK/Cancel buttons based on the rendered positions (i.e. after chainback call to CDialog::OnInitDialog)
BOOL CSetupDlg::OnInitDialog()
{
CDialog::OnInitDialog();
if (m_bShowCancel)
{
// show/enable Cancel button and re-position the OK/CANCEL buttons (default is OK button is centered and cancel is hidden/disabled)
CWnd *pWndOK = GetDlgItem(IDOK);
CWnd *pWndCancel = GetDlgItem(IDCANCEL);
if (pWndOK->GetSafeHwnd() && pWndCancel->GetSafeHwnd())
{
CRect rOKOriginal;
pWndOK->GetWindowRect(&rOKOriginal);
this->ScreenToClient(rOKOriginal);
// move Cancel button to the immediate right of the centered OK button
pWndCancel->SetWindowPos(NULL, rOKOriginal.right, rOKOriginal.top, 0, 0, SWP_NOACTIVATE | SWP_NOSIZE | SWP_NOZORDER | SWP_SHOWWINDOW);
pWndCancel->EnableWindow(true);
// pWndCancel->ShowWindow(SW_SHOW);
// move OK button to the immediate left of its original/centered position
pWndOK->SetWindowPos(NULL, rOKOriginal.left - rOKOriginal.Width(), rOKOriginal.top, 0, 0, SWP_NOACTIVATE | SWP_NOSIZE | SWP_NOZORDER);
}
}
return TRUE; // return TRUE unless you set the focus to a control
// EXCEPTION: OCX Property Pages should return FALSE
}

There are several possibilities:
The dialog is specified in "dialog units" for a font that's different than the font the code is actually using.
The border style changes between Windows versions weren't accounted for in the design of the dialog, and thus you're losing a few pixels.
The screen is high-DPI and the program isn't properly accounting for high-DPI, or it is but it hasn't told the OS that it knows how to (via manifest or SetProcessDPIAware or SetProcessDPIAwareness).
There isn't enough information in the question to know for sure the exact problem. I suspect #3, based on the fact that the behavior changes depending upon whether an external monitor is connected and on the fact that Windows 10 introduced more nuanced concepts of DPI awareness, like PROCESS_PER_MONITOR_DPI_AWARE.

Related

Problems with per-monitor DPI aware MFC MDI app

I'm working on making an MFC app display properly on multiple monitor environments with different DPI scaling. There is one issue I cannot explain which occurs when the primary and secondary monitors are running with different DPIs and the app is on the secondary monitor.
If the primary monitor has 100% DPI scaling (96) and the secondary monitor has the same 100% DPI scaling, all is fine.
If the primary monitor has 100% DPI scaling (96) and the secondary monitor has 125% scaling (120 DPI) or 150% scaling (144 DPI) or any other higher value, when child windows are maximized, part of the child window system bar is visible, as seen here:
125% scaling:
150% scaling:
If you look carefully, its 7 pixels for 125% and 14 for 150%. Given that the system bar is 29 pixels at 100% scaling, 36 and 125%, and 43 at 150%, those 7 and 14 pixels is the height difference between the bar size at 125% and 150% respectively, compared to the 100% baseline.
Therefore, it appears that the position and size of the bar is computed by the system as it was run on the primary monitor.
When you maximize the child window, there is a series of Windows messages that are sent to the window: WM_GETMINMAXINFO > WM_WINDOWPOSCHANGING > WM_GETMINMAXINFO > WM_NCCALSIZE > WM_WINDOWSPOSCHANGED > WM_MOVE > WM_SIZE. WM_GETMINMAXINFO is sent when the size or position of the window is about to change so that an app can override, for instance, the window's default maximized size and position. There is a note about this:
For systems with multiple monitors, the ptMaxSize and ptMaxPosition
members describe the maximized size and position of the window on the
primary monitor, even if the window ultimately maximizes onto a
secondary monitor. In that case, the window manager adjusts these
values to compensate for differences between the primary monitor and
the monitor that displays the window. Thus, if the user leaves
ptMaxSize untouched, a window on a monitor larger than the primary
monitor maximizes to the size of the larger monitor.
There is an article by Raymond Chan, explaining this: How does the window manager adjust ptMaxSize and ptMaxPosition for multiple monitors?.
So the ptMaxSize should be filled with the dimensions of the primary monitor. My primary monitor is 2560x1440 pixels and the size of the secondary monitor is 1920x1200. However, the value of the size I get here is 1757x1023 and 1761x1027 (at consecutive calls). This is neither the size of the primary nor the secondary monitor.
I tried to do a dirty trick and handled the WM_NCCALCSIZE message and set the position (left, top) at 0 (relative to the parent).
void CMyMDIChildWnd::OnNcCalcSize(BOOL bCalcValidRects, NCCALCSIZE_PARAMS* lpncsp)
{
CMDIChildWnd::OnNcCalcSize(bCalcValidRects, lpncsp);
if (condition)
{
lpncsp->rgrc[0].left = 0;
lpncsp->rgrc[0].top = 0;
}
}
Works fine as long as the child window has the focus. If I click on another window and it loses the focus, then the bar is redrawn and shows up at the previous position. This trick is only saying where the client area starts so when the non-client is redrawn I get back to the original problem.
My question is what could the root of this problem be and how can I try to fix it?
I came across possibly the same phenomenon, albeit from a different route, and my solution was very similar to yours, with a little extra, maybe this might help you?
void CFixedFrame::OnNcCalcSize ( BOOL bCalcValidRects, NCCALCSIZE_PARAMS* lpncsp )
{
MDIBASEWND::OnNcCalcSize ( bCalcValidRects, lpncsp ) ;
if ( bCalcValidRects )
{
RECT& rcNew = lpncsp->rgrc[0];
RECT& rcOld = lpncsp->rgrc[1];
RECT& rcClient = lpncsp->rgrc[2];
// My problem arose because of the Aero bug (hardwired border widths)
// And also problems with Theming
const CNonClientMetrics ncm;
rcNew.top = ncm.iCaptionHeight + Aero related stuff not relevant to your problem
}
}
Where CNonClientMetrics is...
class CNonClientMetrics : public NONCLIENTMETRICS
{
public:
CNonClientMetrics ( )
{
cbSize = sizeof ( NONCLIENTMETRICS ) - sizeof ( this->iPaddedBorderWidth ) ;
SystemParametersInfo ( SPI_GETNONCLIENTMETRICS, sizeof ( NONCLIENTMETRICS ), this, 0 ) ;
}
} ;
[In a DPI-Aware way] This allowed me to get rid of that annoying border of blue (with buttons). As I understand it, you can no longer disable the DWM that draws that section.
I can't now find my original references, but in my notes for this problem, it only occurs for MFC's MDI Frame Windows.
Still, this link might also be useful?

ShowWindow() SW_HIDE window instantly, without effect

In my C++ application, I have the following code:
ShowWindow(hDlg, SW_HIDE);
MakeScreenshot();
ShowWindow(hDlg, SW_SHOW);
This should capture screenshot of screen WITHOUT the current application window. However there is a problem. The SW_HIDE takes some time because my windows 8.1 is configured to use Animation effects. So the hiding of window takes about 400 miliseconds and if screenshot is captured during this interval (which it is), it will contain also the window of the app itself, which I do not like.
Is there any way to hide the current window instantly, so it won't be included in the create screenshot function, which is called immediately after it? If not, is there any other preferred way how to take screenshot of windows desktop excluding the application itself? Adding a delay before the MakeScreenshot is not any good solution. Thank you.
You could use MoveWindow (or SetWindowsPos) to move the undesired window outside the visible region of the virtual desktop, and then move it back.
You might need to enumerate the monitors to find a coordinate beyond the reach of all the monitors, which would be a little bit of work. Presumably your screenshot code is computing the coordinates to snapshot, so you could re-use that calculation to find a safe place to park the window.
What worked for me:
this->ModifyStyleEx(0, WS_EX_LAYERED | WS_EX_TOPMOST); //just a backup
COLORREF c;
BYTE b;
DWORD flags;
this->GetLayeredWindowAttributes(&c, &b, &flags); //just a backup
this->SetLayeredWindowAttributes(0, 0, LWA_ALPHA);
//CODE TO TAKE A SCREENSHOT
this->SetLayeredWindowAttributes(c, b, flags); //just a restore
this->ModifyStyleEx(WS_EX_LAYERED | WS_EX_TOPMOST, 0); //just a restore

Detect Split Screen mode in windows 8

How to detect split screen mode in windows 8. I have a wim32 desktop application(written in MFC) and i need to provide some functionality in case of split screen mode
FYI - In split screen mode both desktop and metro mode come side by side
From your comments, the reason you're getting the screen size is because that's what you're asking for. Passing SM_CXSCREEN and SM_CYSCREEN to GetSystemMetrics() will return, as the name suggests, the width and height of the primary display.
There are a number of solutions, each with their pro's and con's, the simplest of which is probably:
RECT rcDesktop;
BOOL ok = GetWindowRect(GetDesktopWindow(), &rcDesktop);
This will return the size of the desktop window of the primary monitor. If you wanted just the "useable" area (taking into account the taskbar):
RECT rc;
BOOL ok = SystemParametersInfo(SPI_GETWORKAREA, 0, &rc, 0);
In the case of having a Modern-UI app docked to the side of the screen, both of those should return what you want, depending on whether you want to cover the taskbar with your program or not.
Note that those examples will only return information for the primary monitor on multi-monitor systems. You can get information about a specific monitor, such as the monitor that your current window is located on, by doing the following:
MONITORINFO mon_info;
mon_info.cbSize = sizeof(MONITORINFO);
BOOL ok = GetMonitorInfo(MonitorFromWindow(hWnd, MONITOR_DEFAULTTONEAREST), &mon_info);
The MONITORINFO structure contains the size (and position - don't assume it's 0,0) of the requested monitor, including the work area:
Caveat: I'm not at home on my Windows8 system, so I can't check that all of these will return the correct information, but in theory checking the work area should do what you want, unless you specifically want your program to be full-screen.

minimize fullscreen Xlib OpenGL Window

I'm currently trying to enable alt-tabbing out of my fullscreen Xlib OpenGL window, but am having some difficulties. I've tried XUnmapWindow(..), which kindof works, but the resolution does not reset (unless I should be doing that manually?) and my Xlib window does not appear as a minimized window (i.e. I can't alt-tab back into the window, even though the app still seems to be running in the background).
The next thing I tried was changing my window from fullscreen to windowed mode (i.e. re-creating the window over again in windowed mode), but obviously, I'd rather not have to do that.
I'm listening to FocusOut and FocusIn events, and the FocusOut seems to be called when I alt-tab, but I'm just not sure how to get my app to minimize properly. If I don't do anything in my code when a FocusOut event is called, my app doesn't do anything (i.e. I can't minimize the window).
Any help would be appreciated!
Edit: Unfortunately, I've been unable to get X Windows to properly minimize a fullscreen window. So, to work around this problem I've decided to destroy() the fullscreen window and then create() a new window in windowed mode. Seems to work well.
XUnmapWindow() completely removes the window from the display. Minimizing a Window happens through EMWH ICCCM state, so that the window manager knows, that the window is still there in some form. And like you already assumed you're responsible for resetting the screen resolution. This is BTW the very same in Windows.
EDIT:
Minimizing a Window in Xlib is done with XIconifyWindow, which will take care to set the right ICCCM properties, and unmaps the window. Both must be done to interact properly with the WM. However X11 only defines the methods, not the policy, so when unmapping a fullscreen window you're also responsible to reset the screen resolution, like I already wrote above.
On a side note: I suggest you don't change the resolution at all, but instead, if such is available, render to a Framebuffer Object of the target size, and map the final result to the full, native screen size. If you combine this with native resolution text/HUD overlays (I assume this is for a game or similar), you get much higher percieved quality and save the resolution switching. You may even combine this with taking a screenshot of the desktop and gradually fading to your content.
EDIT 2 for reference
:
XIconifyWindow is just a helper/convenience function, it's source code is
/*
* This function instructs the window manager to change this window from
* NormalState to IconicState.
*/
Status XIconifyWindow(Display *dpy, Window w, int screen)
{
XClientMessageEvent ev;
Atom prop;
prop = XInternAtom(dpy, "WM_CHANGE_STATE", False);
if(prop == None)
return False;
ev.type = ClientMessage;
ev.window = w;
ev.message_type = prop;
ev.format = 32;
ev.data.l[0] = IconicState;
return XSendEvent(dpy, RootWindow(dpy, screen), False,
SubstructureRedirectMask|SubstructureNotifyMask,
(XEvent *)&ev);
}
You can try to do it like this :
XEvent xev;
Atom wm_state = XInternAtom(dpy, "_NET_WM_STATE", False);
Atom wm_hide_win = XInternAtom(dpy, "_NET_WM_STATE_HIDDEN", False);
memset(&xev, 0, sizeof(xev));
xev.type = ClientMessage;
xev.xclient.window = win;
xev.xclient.message_type = wm_state;
xev.xclient.format = 32;
xev.xclient.data.l[0] = _NET_WM_STATE_ADD;
xev.xclient.data.l[1] = wm_hide_win;
XSendEvent(dpy, DefaultRootWindow(dpy), False, SubstructureNotifyMask, &xev);
EDIT
If you have access to gnome API, you can use wnck_window_minimize(), or take a look into the source for that function.

How do I force windows NOT to redraw anything in my dialog when the user is resizing my dialog?

When the user grabs a corner of a resizable window, and then moves it, windows first moves the contents of the window around, then issues a WM_SIZE to the window being resized.
Thus, in a dialog where I want to control the movement of various child controls, and I want to eliminate flickering, the user first sees what windows OS thinks the window will look like (because, AFAICT, the OS uses a bitblt approach to moving things around inside the window before sending the WM_SIZE) - and only then does my dialog get to handle moving its child controls around, or resize them, etc., after which it must force things to repaint, which now causes flicker (at the very least).
My main question is: Is there a way to force windows NOT to do this stupid bitblt thing? Its definitely going to be wrong in the case of a window with controls that move as the window is resized, or that resize themselves as their parent is resized. Either way, having the OS do a pre-paint just screws the works.
I thought for a time that it might be related to CS_HREDRAW and CSVREDRAW class flags. However, the reality is that I don't want the OS to ask me to erase the window - I just want to do the repainting myself without the OS first changing the contents of my window (i.e. I want the display to be what it was before the user started resizing - without any bitblit'ing from the OS). And I don't want the OS to tell every control that it needs to be redrawn either (unless it happened to be one that was in fact obscured or revealed by the resize.
What I really want:
To move & resize child controls before anything gets updated onscreen.
Draw all of the moved or resized child controls completely so that they appear without artifacts at their new size & location.
Draw the spaces inbetween the child controls without impacting the child controls themselves.
NOTE: Steps 2 and 3 could be reversed.
The above three things appear to happen correctly when I use DeferSetWindowPos() in combination with the dialog resource marked as WS_CLIPCHILDREN.
I'd get an additional small benefit if I could do the above to a memory DC, and then only do a single bitblt at the end of the WM_SIZE handler.
I have played with this for a while now, and I cannot escape two things:
I still am unable to suppress Windows from doing a 'predictive bitblt'. Answer: See below for a solution that overrides WM_NCCALCSIZE to disable this behavior.
I cannot see how one can build a dialog where its child controls draw to a double buffer. Answer: See John's answer (marked as answer) below for how to ask Windows OS to double buffer your dialog (note: this disallows any GetDC() in-between paint operations, according to the docs).
My Final Solution (Thank you everyone who contributed, esp. John K.):
After much sweat and tears, I have found that the following technique works flawlessly, both in Aero and in XP or with Aero disabled. Flicking is non-existent(1).
Hook the dialog proc.
Override WM_NCCALCSIZE to force Windows to validate the entire client area, and not bitblt anything.
Override WM_SIZE to do all of your moves & resizes using BeginDeferWindowPos/DeferWindowPos/EndDeferWindowPos for all visible windows.
Ensure that the dialog window has the WS_CLIPCHILDREN style.
Do NOT use CS_HREDRAW|CS_VREDRAW (dialogs don't, so generally not an issue).
The layout code is up to you - its easy enough to find examples on CodeGuru or CodeProject of layout managers, or to roll your own.
Here are some code excerpts that should get you most of the way:
LRESULT ResizeManager::WinProc(HWND hwnd, UINT msg, WPARAM wparam, LPARAM lparam)
{
switch (msg)
{
case WM_ENTERSIZEMOVE:
m_bResizeOrMove = true;
break;
case WM_NCCALCSIZE:
// The WM_NCCALCSIZE idea was given to me by John Knoeller:
// see: http://stackoverflow.com/questions/2165759/how-do-i-force-windows-not-to-redraw-anything-in-my-dialog-when-the-user-is-resiz
//
// The default implementation is to simply return zero (0).
//
// The MSDN docs indicate that this causes Windows to automatically move all of the child controls to follow the client's origin
// and experience shows that it bitblts the window's contents before we get a WM_SIZE.
// Hence, our child controls have been moved, everything has been painted at its new position, then we get a WM_SIZE.
//
// Instead, we calculate the correct client rect for our new size or position, and simply tell windows to preserve this (don't repaint it)
// and then we execute a new layout of our child controls during the WM_SIZE handler, using DeferWindowPos to ensure that everything
// is moved, sized, and drawn in one go, minimizing any potential flicker (it has to be drawn once, over the top at its new layout, at a minimum).
//
// It is important to note that we must move all controls. We short-circuit the normal Windows logic that moves our child controls for us.
//
// Other notes:
// Simply zeroing out the source and destination client rectangles (rgrc[1] and rgrc[2]) simply causes Windows
// to invalidate the entire client area, exacerbating the flicker problem.
//
// If we return anything but zero (0), we absolutely must have set up rgrc[0] to be the correct client rect for the new size / location
// otherwise Windows sees our client rect as being equal to our proposed window rect, and from that point forward we're missing our non-client frame
// only override this if we're handling a resize or move (I am currently unaware of how to distinguish between them)
// though it may be adequate to test for wparam != 0, as we are
if (bool bCalcValidRects = wparam && m_bResizeOrMove)
{
NCCALCSIZE_PARAMS * nccs_params = (NCCALCSIZE_PARAMS *)lparam;
// ask the base implementation to compute the client coordinates from the window coordinates (destination rect)
m_ResizeHook.BaseProc(hwnd, msg, FALSE, (LPARAM)&nccs_params->rgrc[0]);
// make the source & target the same (don't bitblt anything)
// NOTE: we need the target to be the entire new client rectangle, because we want windows to perceive it as being valid (not in need of painting)
nccs_params->rgrc[1] = nccs_params->rgrc[2];
// we need to ensure that we tell windows to preserve the client area we specified
// if I read the docs correctly, then no bitblt should occur (at the very least, its a benign bitblt since it is from/to the same place)
return WVR_ALIGNLEFT|WVR_ALIGNTOP;
}
break;
case WM_SIZE:
ASSERT(m_bResizeOrMove);
Resize(hwnd, LOWORD(lparam), HIWORD(lparam));
break;
case WM_EXITSIZEMOVE:
m_bResizeOrMove = false;
break;
}
return m_ResizeHook.BaseProc(hwnd, msg, wparam, lparam);
}
The resizing is really done by the Resize() member, like so:
// execute the resizing of all controls
void ResizeManager::Resize(HWND hwnd, long cx, long cy)
{
// defer the moves & resizes for all visible controls
HDWP hdwp = BeginDeferWindowPos(m_resizables.size());
ASSERT(hdwp);
// reposition everything without doing any drawing!
for (ResizeAgentVector::const_iterator it = m_resizables.begin(), end = m_resizables.end(); it != end; ++it)
VERIFY(hdwp == it->Reposition(hdwp, cx, cy));
// now, do all of the moves & resizes at once
VERIFY(EndDeferWindowPos(hdwp));
}
And perhaps the final tricky bit can be seen in the ResizeAgent's Reposition() handler:
HDWP ResizeManager::ResizeAgent::Reposition(HDWP hdwp, long cx, long cy) const
{
// can't very well move things that no longer exist
if (!IsWindow(hwndControl))
return hdwp;
// calculate our new rect
const long left = IsFloatLeft() ? cx - offset.left : offset.left;
const long right = IsFloatRight() ? cx - offset.right : offset.right;
const long top = IsFloatTop() ? cy - offset.top : offset.top;
const long bottom = IsFloatBottom() ? cy - offset.bottom : offset.bottom;
// compute height & width
const long width = right - left;
const long height = bottom - top;
// we can defer it only if it is visible
if (IsWindowVisible(hwndControl))
return ::DeferWindowPos(hdwp, hwndControl, NULL, left, top, width, height, SWP_NOZORDER|SWP_NOACTIVATE);
// do it immediately for an invisible window
MoveWindow(hwndControl, left, top, width, height, FALSE);
// indicate that the defer operation should still be valid
return hdwp;
}
The 'tricky' being that we avoid trying to mess with any windows that have been destroyed, and we don't try to defer a SetWindowPos against a window that is not visible (as this is documented as "will fail".
I've tested the above in a real project that hides some controls, and makes use of fairly complex layouts with excellent success. There is zero flickering(1) even without Aero, even when you resize using the upper left corner of the dialog window (most resizable windows will show the most flickering and problems when you grab that handle - IE, FireFox, etc.).
If there is interest enough, I could be persuaded to edit my findings with a real example implementation for CodeProject.com or somewhere similar. Message me.
(1) Please note that it is impossible to avoid one draw over the top of whatever used to be there. For every part of the dialog that has not changed, the user can see nothing (no flicker whatsoever). But where things have changed, there is a change visible to the user - this is impossible to avoid, and is a 100% solution.
You can't prevent painting during resizing, but you can (with care) prevent repainting which is where flicker comes from. first, the bitblt.
There a two ways to stop the bitblt thing.
If you own the class of the top level window, then just register it with the CS_HREDRAW | CS_VREDRAW styles. This will cause a resize of your window to invalidate the entire client area, rather than trying to guess which bits are not going to change and bitblting.
If you don't own the class, but do have the ability to control message handling (true for most dialog boxes). The default processing of WM_NCCALCSIZE is where the class styles CS_HREDRAW and CS_VREDRAW are handled, The default behavior is to return WVR_HREDRAW | WVR_VREDRAW from processing WM_NCCALCSIZE when the class has CS_HREDRAW | CS_VREDRAW.
So if you can intercept WM_NCCALCSIZE, you can force the return of these values after calling DefWindowProc to do the other normal processing.
You can listen to WM_ENTERSIZEMOVE and WM_EXITSIZEMOVE to know when resizing of your window starts and stops, and use that to temporarily disable or modify the way your drawing and/or layout code works to minimize the flashing. What exactly you want to do to modify this code will depend on what your normal code normally does in WM_SIZE WM_PAINT and WM_ERASEBKGND.
When you paint the background of your dialog box, you need to not paint behind any of the child windows. making sure that the dialog has WS_CLIPCHILDREN solves this, so you have this handled already.
When you do move the child windows, Make sure that you use BeginDeferWindowPos / EndDefwindowPos so that all of the repainting happens at once. Otherwise you will get a bunch of flashing as each window redraws their nonclient area on each SetWindowPos call.
If I understood the question properly, it's exactly the question Raymond addressed today.
Here's a 2018 update, since I just ran through the very same gauntlet as you.
The "final solution" in your question, and the related answers, that mention tricks with WM_NCCALCSIZE and CS_HREDRAW|CS_VREDRAW are good for preventing Windows XP/Vista/7 from doing the BitBlt that molests your client area during resizing. It might even be useful to mention a similar trick: you can intercept WM_WINDOWPOSCHANGING (first passing it onto DefWindowProc) and set WINDOWPOS.flags |= SWP_NOCOPYBITS, which disables the BitBlt inside the internal call to SetWindowPos() that Windows makes during window resizing. This has the same eventual effect of skipping the BitBlt.
And some people mentioned that your WM_NCCALCSIZE trick no longer works in Windows 10. I think that might be because the code you wrote returns WVR_ALIGNLEFT|WVR_ALIGNTOP when it should be returning WVR_VALIDRECTS in order for the two rectangles you constructed (nccs_params->rgrc[1] and nccs_params->rgrc[2]) to be used by Windows, at least according to the very skimpy dox in the MSDN pages for WM_NCCALCSIZE and NCCALCSIZE_PARAMS. It's possible that Windows 10 is more strict about that return value; I would try it out.
However, even if we assume that we can convince Windows 10 not to do BitBlt inside SetWindowPos(), it turns out there's a new problem...
Windows 10 (and possibly also Windows 8) adds another layer of client area molestation on top of the old legacy molestation from XP/Vista/7.
Under Windows 10, apps do not draw directly to the framebuffer, but instead draw into offscreen buffers that the Aero Window manager (DWM.exe) composites.
It turns out that DWM will sometimes decide to "help" you by drawing its own content over your client area (sort of like a BitBlt but even more perverse and even further out of your control).
So in order to be free of client area molestation, we still need to get WM_NCCALCSIZE under control but we also need to prevent DWM from messing with your pixels.
I was fighting with exactly the same problem and created a roundup Question/Answer which brings together 10 years of posts on this topic and offers some new insights (too long to paste the content here in this question). The BitBlt mentioned above is no longer the only problem, as of Windows Vista. Enjoy:
How to smooth ugly jitter/flicker/jumping when resizing windows, especially dragging left/top border (Win 7-10; bg, bitblt and DWM)?
For some controls, you can use WM_PRINT message to make the control draw into a DC. But that doesn't really solve your primary problem, which is that you want Windows to NOT draw anything during resize, but to let you do it all.
And the answer is that you just can't do what you want as long as you have child windows.
The way I ended up solving this eventually in my own code is to switch to using Windowless Controls. Since they have no window of their own, they always draw at the same time (and into the same DC) as their parent window. This allows me to use simple double buffering to completely remove flicker. I can even trivially suppress painting of the children when I need to just by not calling their draw routine inside the parent's draw routine.
This is the only way I know of to completely get rid of flicker and tearing during resize operations.
If you can find a place to plug it in, CWnd::LockWindowUpdates() will prevent any drawing from occuring until after you unlock the updates.
But keep in mind this is a hack, and a fairly ugly one at that. Your window will look terrible during resizes. If the problem you are having is flickering during resizes, then the best thing to do is diagnose the flickering, rather than hiding the flickering by blocking paints.
One thing to look for are redraw commands that get called too often during the resize. If you r window's controls are calling RedrawWindow() with the RDW_UPDATENOW flag specified, it is going to repaint then and there. But you can strip out that flag and specify RDW_INVALIDATE instead, which tells the control to invalidate the window without repainting. It will repaint at idle time, keeping the display fresh without spazzing out.
There are various approaches, but I found the only one that can be used generally is double buffering: draw to an offscreen buffer, then blit the entire buffer to screen.
That comes for free in Vista Aero and above, so your pain might be shortlived.
I am not aware of a general double-buffering implementation for windows and system controls under XP, However, here are some things to explore:
Keith Rule's CMemDC for double-buffering anything you draw yourself with GDI
WS_EX_COMPOSITED Window style (see the remarks section, and something here on stackoverflow)
there is only one way to effectively diagnose repainting problems - remote debugging.
Get a 2nd PC. Install MSVSMON on it. Add a post build step or utility project that copies your build products to the remote PC.
Now you should be able to place breakpoints in WM_PAINT handlers, WM_SIZE handlers and so on and actually trace through your dialog code as it performs the size and redraw. If you download symbols from the MS symbol servers you will be able to see full call stacks.
Some well placed breakpoints - in your WM_PAINT, WM_ERAGEBKGND handlers and you should have a good idea of why your window is being synchronously repainted early during the WM_SIZE cycle.
There are a LOT of windows in the system that consist of a parent window with layered child controls - explorer windows are massivly complicated with listviews, treeviews preview panels etc. Explorer does not have a flicker problem on resizing, so It is celarly possible to get flicker free resizing of parent windows :- what you need to do is catch the repaints, figure out what caused them, and, well, ensure that the cause is removed.
What appears to work:
Use the WS_CLIPCHILDREN on the parent dialog (can be set in WM_INITDIALOG)
During WM_SIZE, loop through the child controls moving and resizing them using DeferSetWindowPos().
This is very close to perfect, in my testing under Windows 7 with Aero.