1

I am creating a simple Win32/MFC application with a main window, and a child-window that uses AnimateWindow() to show (slide up) and hide (slide down) the window. When running the application on 100% dpi scaling, everything behaves normally.

Child window shown

I have overridden WM_ERASEBKGND to render a random red color, to demonstrate the effect. As the window slides-down (hide) on every "step" of the animation the "update rectangle" of the background is repainted, exactly where the child-window disappeared, and the background is to become visible again.

Child window retracted with the background re-drawn with random red colors

However, when changing the dpi-scaling via Windows Settings (in this case to 125%), an incorrect area is redrawn. It appears as if the area passed into OnEraseBkgnd() is still using the 100% dpi-scaling size, but also the position is off: it appears as if the x/y position of the upper-left corner is passed in screen-space instead of client-space. So the redrawn area looks different, depending on where the on the screen the window is positioned.

Update-rectangle with wrong size and position

The white area is where the child window was actually positioned, and where the redraw of the background should actually have happened.

I have confirmed this effect on Win10 (1803 and 1809) and on Win8.1 Is this a bug in the OS, or is there something I can do to avoid the problem - other than not using AnimateWindow()? ShowWindow() (with SW_SHOW or SW_HIDE) works just fine, btw.

Update: added the full source-code to reproduce the issue. The problem occurs when using no dpi-aware manifest at all, but it also occurs when using <gdiScaling xmlns="http://schemas.microsoft.com/SMI/2017/WindowsSettings">true</gdiScaling>

class CDialogTestApp : public CWinApp
{
    virtual BOOL InitInstance();
};

CDialogTestApp theApp;

class CAboutDlg : public CDialogEx
{
public:
    CAboutDlg() : CDialogEx(IDD_ABOUTBOX) {}
};

class CDialogTestDlg : public CDialogEx
{
public:
    CDialogTestDlg(CWnd* pParent = nullptr) : CDialogEx(IDD_DIALOGTEST_DIALOG, pParent) {}

protected:
    virtual BOOL OnInitDialog();
    afx_msg BOOL OnEraseBkgnd(CDC* pDC);
    afx_msg void OnLButtonUp(UINT nFlags, CPoint point);

    DECLARE_MESSAGE_MAP()

private:
    CAboutDlg mDialog;
};

BEGIN_MESSAGE_MAP(CDialogTestDlg, CDialogEx)
    ON_WM_ERASEBKGND()
    ON_WM_LBUTTONUP()
END_MESSAGE_MAP()

BOOL CDialogTestApp::InitInstance()
{
    CWinApp::InitInstance();

    CDialogTestDlg dlg;
    m_pMainWnd = &dlg;
    INT_PTR nResponse = dlg.DoModal();
    return FALSE;
}

BOOL CDialogTestDlg::OnInitDialog()
{
    CDialogEx::OnInitDialog();
    mDialog.Create(IDD_ABOUTBOX, this);
    return TRUE;
}

BOOL CDialogTestDlg::OnEraseBkgnd(CDC* pDC)
{
    COLORREF color = RGB(rand() & 255, 20, 40);

    CRect rect;
    GetClipBox(pDC->m_hDC, &rect);     // retrieve the update-rectangle

    CBrush brush(color);
    FillRect(pDC->m_hDC, &rect, (HBRUSH)brush.m_hObject);

    return TRUE;
}

void CDialogTestDlg::OnLButtonUp(UINT nFlags, CPoint point)
{
    if (mDialog.IsWindowVisible())
    {
        mDialog.AnimateWindow(200, AW_HIDE | AW_SLIDE | AW_VER_POSITIVE);
    }
    else
    {
        mDialog.SetWindowPos(&CWnd::wndTop, 0, 50, 0, 0, SWP_NOSIZE);
        mDialog.AnimateWindow(200, AW_ACTIVATE | AW_SLIDE | AW_VER_NEGATIVE);
    }

    CDialogEx::OnLButtonUp(nFlags, point);
}
  • I just had a quick test in a Win7 virtual-machine. `AnimateWindow()` works correctly with dpi-scaling! – Erwin Kloibhofer Mar 09 '19 at 20:03
  • 1
    Can you please create a [mcve] and also add which kind of DPI awareness you have declared for your application? – zett42 Mar 10 '19 at 20:38
  • Can you reproduce when you replace `CDialogEx` by `CDialog`? It wouldn't be the first time that bugs of this class manifest themselves. – zett42 Mar 11 '19 at 21:53
  • Just tried with `CDialog` instead Of `CDialogEx`. Same effect. The problem lies within AnimateWindow(). It's a blocking function doing the animation, and while doing so it produces flawed `WM_ERASEBKGND` messages passing an incorrect update-rectangle. – Erwin Kloibhofer Mar 11 '19 at 23:40
  • Strange, I'm using `AnimateWindow` in my application for a custom drop-down menu, still I never noticed this problem. Guess the invalid update rectangle doesn't matter much, because DWM caches each window in its own texture and can redraw uncovered parts without involving the application. So if `AnimateWindow` misses some parts, DWM will just draw the remaining parts from the texture. – zett42 Mar 12 '19 at 08:36
  • I am using `AnimateWindow()` for custom drop-down lists too - same problem there. Which DPI Awareness manifest do you use? The problem does not occur with `permonitorv2,permonitor` – Erwin Kloibhofer Mar 12 '19 at 11:28
  • I'm using `true `. – zett42 Mar 12 '19 at 13:45
  • Okay, that's the fallback declaration for Windows 7 and 8, and essentially means the same as `permonitorv2,permonitor`. It basically means that your application is in control of scaling. Try the `gdiScaling` declaration instead, and you should experience the same problem as I do. – Erwin Kloibhofer Mar 12 '19 at 22:09

0 Answers0