显示更改时锁定 window 位置

Lock window position on display change

我有一个包含多个 windows 的应用程序,我希望其中一些(我将其称为 CMyLockedFrameWndEx,因为它们派生自 CFrameWndEx)保留在原处更改系统显示区域后。 我的应用程序的所有 windows 的父项都是 NULL。

当我将第二台显示器的位置相对于第一台显示器拖动时,我已经设法捕捉到了 WM_DISPLAYCHANGE 消息;当我连接或断开第二台显示器的 HDMI 电缆时,我也赶上了 WM_DEVICECHANGE。我在 CMyLockedFrameWndEx::WindowProc.

截获了它们

自动 window 重新定位之后发生。我注意到因为我在 CMyLockedFrameWndEx::OnWindowPosChangingCMyLockedFrameWndEx::OnWindowPosChanged 上设置了断点,它们在我捕捉到 WindowProc 上的事件后停止。这个工作流程似乎与我描述的事件捕获无关,因为我的 WindowProc 方法是:

LRESULT CMyLockedFrameWndEx::WindowProc(UINT message, WPARAM wParam, LPARAM lParam)
{
    if (message == WM_DISPLAYCHANGE)
    {
        TRACE(_T("DISPLAY CHANGE"));
        return 0L;
    }

    if (message == WM_SYSCOMMAND)
    {
        TRACE(_T("SYSCOMMAND"));

        if (wParam == SC_MOVE)
        {
            return 0L;
        }
    }

    if (message == WM_WININICHANGE)
    {
        TRACE(_T("WININICHANGE"));

        if (wParam == SPI_SETWORKAREA)
        {
            return 0L;
        }
    }



    return __super::WindowProc( message, wParam, lParam);
}

并且在传入OnWindowPosChangingOnWindowPosChanged时,流量不是来自我处理的WindowProc的具体案例。这是一个问题。

我试图跟踪调用堆栈以查看 window 发送了 WM_WINDOWPOSCHANGINGWM_WINDOWPOSCHANGED 消息,但没有成功。我什至尝试过使用 Spy++64 来检测谁是消息的发送者,但我没有成功。查看谁是发件人的整个想法是:如果它与系统的显示更改相关联,那就是事先检测到它并阻止自动重新定位甚至发生。

由于我还没有成功,我该怎么做才能使 window 不受系统显示变化的影响?

谢谢。

好消息是:显然,您可以控制自己的位置 window。

坏消息 - Windows 将首先 将您的 window 移动到新位置,然后 发送你是 WM_DISPLAYCHANGEWM_SETTINGCHANGE,如这个 Spy++ 的日志所示:

S WM_WINDOWPOSCHANGING lpwp:003CF9AC
S WM_GETMINMAXINFO lpmmi:003CF624
R WM_GETMINMAXINFO lpmmi:003CF624
R WM_WINDOWPOSCHANGING
S WM_WINDOWPOSCHANGED lpwp:003CF9AC
S WM_MOVE xPos:278 yPos:450
R WM_MOVE
R WM_WINDOWPOSCHANGED
S WM_SETTINGCHANGE wFlag:SPI_ICONVERTICALSPACING pszMetrics:0026E018
R WM_SETTINGCHANGE
S WM_DISPLAYCHANGE cBitsPerPixel:32 cxScreen:2560 cyScreen:1440
R WM_DISPLAYCHANGE
S WM_SETTINGCHANGE wFlag:SPI_SETWORKAREA pszMetrics:0026E018
R WM_SETTINGCHANGE

所以 - 你必须自己测试位置更改到另一个屏幕。就像在这个简化示例中一样,其中 2560 是我的屏幕宽度:

case WM_WINDOWPOSCHANGING:
{
    WINDOWPOS* pWP = (WINDOWPOS*)lParam;
    if ((pWP->flags & SWP_NOMOVE) == 0) // it's a move
    {
        if (pWP->x < 2560)
            pWP->flags |= SWP_NOMOVE;
    }
    return 0;
}

不幸的消息是我无法以预防的方式执行此操作,因为即使在发送任何有用的消息后,系统也会将 windows 移至主屏幕。

好消息是我可以通过添加

来对移动做出反应

ON_WM_WINDOWPOSCHANGED()

行在 class 的消息映射上,并为其提供相应的处理函数:

CMyLockedFrameWndEx::OnWindowPosChanged(WINDOWPOS* lpwndpos)
{
    __super::OnWindowPosChanged(lpwndpos);

    CFrameWndEx* pFrame=(CFrameWndEx*)::AfxGetMainWnd();
    VALIDATE_FPTR(pFrame);

    CMyDoc* pDoc=(CMyDoc*)pFrame->GetActiveDocument();
    VALIDATE_FPTR(pDoc);

    POSITION p= pDoc->GetFirstViewPosition();

    while(p)
    {
        CMyLockedView* pLockedView= dynamic_cast<CLockedView*>(pDoc->GetNextView(p));
        if(!pLockedView)
            continue;

        if(pLockedView->GetParentFrame() == this)
        {
            this->SetWindowPos(NULL, m_Top, m_Left, 0, 0, SWP_NOSIZE);

            break;
        }
    }
}´

请注意,我坚持 window 在 m_Topm_Left 变量中的位置对我有帮助。