Winapi AnimateWindow()在高dpi(Win10)上错误地绘制背景

Winapi AnimateWindow()在高dpi(Win10)上错误地绘制背景,winapi,mfc,windows-10,highdpi,animatewindow,Winapi,Mfc,Windows 10,Highdpi,Animatewindow,我正在创建一个简单的Win32/MFC应用程序,其中包含一个主窗口和一个子窗口,该子窗口使用AnimateWindow()来显示(向上滑动)和隐藏(向下滑动)窗口。当在100%dpi缩放上运行应用程序时,一切正常 我已经覆盖了WM_ERASEBKGND以呈现随机的红色,以演示效果。当窗口在动画的每个“步骤”上向下滑动(隐藏)时,背景的“更新矩形”被重新绘制,正好在子窗口消失的地方,背景将再次可见 但是,当通过Windows设置更改dpi缩放时(在本例中为125%),将重新绘制不正确的区域。看

我正在创建一个简单的Win32/MFC应用程序,其中包含一个主窗口和一个子窗口,该子窗口使用
AnimateWindow()
来显示(向上滑动)和隐藏(向下滑动)窗口。当在100%dpi缩放上运行应用程序时,一切正常

我已经覆盖了WM_ERASEBKGND以呈现随机的红色,以演示效果。当窗口在动画的每个“步骤”上向下滑动(隐藏)时,背景的“更新矩形”被重新绘制,正好在子窗口消失的地方,背景将再次可见

但是,当通过Windows设置更改dpi缩放时(在本例中为125%),将重新绘制不正确的区域。看起来好像传入
OnEraseBkgnd()
的区域仍在使用100%dpi缩放大小,但位置也已关闭:看起来好像左上角的x/y位置是在屏幕空间而不是客户端空间中传递的。因此,重绘区域看起来不同,这取决于窗口在屏幕上的位置

白色区域是子窗口的实际位置,也是背景重画实际发生的位置

我已经在Win10(1803和1809)和Win8.1上确认了这种影响 这是操作系统中的一个bug,还是除了不使用
AnimateWindow()
,我还能做些什么来避免这个问题<代码>显示窗口()(使用
SW\u SHOW
SW\u HIDE
)工作正常,顺便说一句

更新:添加完整的源代码以重现问题。 在使用完全不支持dpi的清单时会出现问题,但在使用
true


我刚刚在Win7虚拟机上进行了快速测试<代码>动画窗口()可与dpi缩放一起正确工作!您是否可以创建并添加您为应用程序声明的DPI感知类型?当您将
CDialogEx
替换为
CDialog
时,是否可以复制?这不是第一次出现此类错误。只需尝试使用
CDialog
而不是
CDialogEx
。同样的效果。问题出在AnimateWindow()中。这是一个执行动画的阻塞函数,在执行此操作时,它会生成有缺陷的
WM_ERASEBKGND
消息,并传递错误的更新矩形。奇怪的是,我在应用程序中使用
AnimateWindow
自定义下拉菜单,但我从未注意到这个问题。我猜无效的更新矩形无关紧要,因为DWM将每个窗口缓存在自己的纹理中,并且可以在不涉及应用程序的情况下重新绘制未覆盖的部分。因此,如果
AnimateWindow
遗漏了一些部分,DWM将仅从纹理中绘制其余部分。我刚刚在Win7虚拟机中进行了快速测试<代码>动画窗口()可与dpi缩放一起正确工作!您是否可以创建并添加您为应用程序声明的DPI感知类型?当您将
CDialogEx
替换为
CDialog
时,是否可以复制?这不是第一次出现此类错误。只需尝试使用
CDialog
而不是
CDialogEx
。同样的效果。问题出在AnimateWindow()中。这是一个执行动画的阻塞函数,在执行此操作时,它会生成有缺陷的
WM_ERASEBKGND
消息,并传递错误的更新矩形。奇怪的是,我在应用程序中使用
AnimateWindow
自定义下拉菜单,但我从未注意到这个问题。我猜无效的更新矩形无关紧要,因为DWM将每个窗口缓存在自己的纹理中,并且可以在不涉及应用程序的情况下重新绘制未覆盖的部分。因此,如果
AnimateWindow
遗漏了某些部分,DWM将仅从纹理中绘制其余部分。
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);
}