Windows SetWindowPlacement触发的WM\u dpichange不正确

Windows SetWindowPlacement触发的WM\u dpichange不正确,windows,winapi,dpi,Windows,Winapi,Dpi,我正在使用Windows 10上的GetWindowPlacement/SetWindowPlacement保存/恢复窗口位置。我的应用程序支持DPI。当SetWindowPlacement调整窗口大小并将窗口从具有一个DPI的监视器#1移动到具有不同DPI的监视器#2时,会出现此问题。坐标已保存为WINDOWPLACEMENT结构中监视器#2的正确大小 当窗口仍在监视器#1上时,首先在SetWindowPlacement期间调整窗口的大小。然后将窗口移动到监视器#2,这会引发WM#U DPIC

我正在使用Windows 10上的GetWindowPlacement/SetWindowPlacement保存/恢复窗口位置。我的应用程序支持DPI。当SetWindowPlacement调整窗口大小并将窗口从具有一个DPI的监视器#1移动到具有不同DPI的监视器#2时,会出现此问题。坐标已保存为WINDOWPLACEMENT结构中监视器#2的正确大小

当窗口仍在监视器#1上时,首先在SetWindowPlacement期间调整窗口的大小。然后将窗口移动到监视器#2,这会引发WM#U DPICHANGED消息,表示应更改窗口大小。建议的大小不正确,因为它正在更改窗口的大小,而该窗口已经是监视器2的正确大小。 解决这个问题的正确方法是什么?我是否应该在SetWindowPlacement之前设置一个标志来忽略WM_DPICHANGED消息,直到调用完成?在某些情况下,这会导致我错过一条我不应该忽略的信息吗? 谢谢

编辑:附上@SongZhu MSFT的复印件。 在这个测试用例中,我使用Surface Studio 2作为我的主监视器,以4500x3000运行,使用175%的缩放。在该监视器的右侧,与底部对齐的是一个1920x1080监视器,设置为100%缩放。此代码试图以设置的大小打开右侧监视器上的监视器,但是在SetWindowPlacement()调用期间会出现一条DPICHANGE消息,这会导致大小调整不正确,除非我手动避免。示例代码编辑自:

#ifndef UNICODE
#定义UNICODE
#恩迪夫
#包括
LRESULT回调WindowProc(HWND-HWND、UINT-uMsg、WPARAM-WPARAM、LPARAM-LPARAM);
int WINAPI wWinMain(HINSTANCE HINSTANCE、HINSTANCE、PWSTR pCmdLine、int nCmdShow)
{
//注册窗口类。
常量wchar_t CLASS_NAME[]=L“示例窗口类”;
WNDCLASS wc={};
wc.lpfnWndProc=WindowProc;
wc.hInstance=hInstance;
wc.lpszClassName=类名称;
注册类(&wc);
//创建窗口。
HWND HWND=CreateWindowEx(
0,//可选的窗口样式。
类\名称,//窗口类
L“学习编程窗口”,//窗口文本
WS\u重叠窗口,//窗口样式
//大小和位置
CW_USEDEFAULT,CW_USEDEFAULT,1280720,
NULL,//父窗口
NULL,//菜单
hInstance,//实例句柄
NULL//其他应用程序数据
);
if(hwnd==NULL)
{
返回0;
}
WINDOWPLACEMENT wp={};
wp.length=sizeof(wp);
wp.showCmd=1;
wp.ptMaxPosition.x=-1;
wp.ptMaxPosition.y=-1;
wp.ptMinPosition.x=-1;
wp.ptMinPosition.y=-1;
wp.rcNormalPosition.left=4510;
wp.rcNormalPosition.top=2320;
wp.rcNormalPosition.right=wp.rcNormalPosition.left+1850;
wp.rcNormalPosition.bottom=2909;
::SetWindowPlacement((HWND)HWND和wp);
显示窗口(hwnd、nCmdShow);
//运行消息循环。
MSG={};
while(GetMessage(&msg,NULL,0,0))
{
翻译信息(&msg);
发送消息(&msg);
}
返回0;
}
LRESULT回调WindowProc(HWND HWND、UINT uMsg、WPARAM WPARAM、LPARAM LPARAM)
{
开关(uMsg)
{
案例WM_销毁:
PostQuitMessage(0);
返回0;
案例WM_dpichange:
{
int dpi=HIWORD(wParam);
{
RECT*const prcNewWindow=(RECT*)LPRAM;
设置窗口位置(hwnd,
无效的
PRCNEWINDOW->左,
PRCNEWINDOW->顶部,
PRCNewindow->右-PRCNewindow->左,
PRCNEWINDOW->底部-PRCNEWINDOW->顶部,
SWP_NOZORDER | SWP_NOACTIVATE);
}
返回0;
}
案例WM_油漆:
{
PAINTSTRUCT-ps;
HDC HDC=开始喷漆(hwnd和ps);
//所有的绘画都发生在这里,从开始到结束。
FillRect(hdc和ps.rcPaint,(HBRUSH)(颜色窗口+1));
端漆(hwnd和ps);
}
返回0;
}
返回DefWindowProc(hwnd、uMsg、wParam、lParam);
}
还有我用的清单

<?xml version="1.0" encoding="UTF-8" standalone="yes"?>
<assembly xmlns="urn:schemas-microsoft-com:asm.v1" manifestVersion="1.0">
<application xmlns="urn:schemas-microsoft-com:asm.v3">
    <windowsSettings>
        <dpiAwareness xmlns="http://schemas.microsoft.com/SMI/2016/WindowsSettings">PerMonitorV2,permonitor</dpiAwareness>
        <dpiAware xmlns="http://schemas.microsoft.com/SMI/2005/WindowsSettings">true/pm</dpiAware>
    </windowsSettings>
</application>
</assembly>

PerMonitorV2,permonitor
对/下午

有两种方法可以解决此问题:

  • 在调用
    SetWindowPlacement
    之前,将类似
    s_isindewindowmove
    的标志设置为
    true
    ,如果它是在触发
    WM_DPICHANGED
    时设置的,请不要按照建议调整窗口大小。
    SetWindowPlacement
    返回后,将标志设置回
    false
  • 计算传递给
    SetWindowPlacement
    的大小,就像您将其放置在显示器上时使用的DPI与当前窗口所在的显示器相同一样。例如,如果要将窗口从DPI
    144
    移动到DPI
    192
    监视器,并且希望最终结果大小为
    800x600
    ,请询问
    SetWindowPlacement
    以获得600x450的大小

  • 我们使用第一个选项,因为它更容易推理和实现。

    您能在不显示私人信息的情况下显示吗?当您应用
    设置WindowPlacement
    时,窗口是否可见(显示)?@DanielSęk我尝试了显示和不显示,没有区别。@SongZhu MSFT Ok,我将尝试合并一个。
    建议的大小不正确,因为它正在更改窗口的大小,该窗口已经是监视器2的正确大小<?xml version="1.0" encoding="UTF-8" standalone="yes"?>
    <assembly xmlns="urn:schemas-microsoft-com:asm.v1" manifestVersion="1.0">
    <application xmlns="urn:schemas-microsoft-com:asm.v3">
        <windowsSettings>
            <dpiAwareness xmlns="http://schemas.microsoft.com/SMI/2016/WindowsSettings">PerMonitorV2,permonitor</dpiAwareness>
            <dpiAware xmlns="http://schemas.microsoft.com/SMI/2005/WindowsSettings">true/pm</dpiAware>
        </windowsSettings>
    </application>
    </assembly>