Search code examples
mfcpositionwindowscreendisplay

Lock window position on display change


I have an application which has several windows and I want that some of them (which I will call CMyLockedFrameWndEx, because they derive from CFrameWndEx) stay placed where they were after changing system display area. The Parent of all windows of my application is NULL.

I've managed to already catch the WM_DISPLAYCHANGE message when I drag the position of the 2nd monitor relatively to the first; and I also had come to catch the WM_DEVICECHANGE when I connect or disconnect the 2nd monitor's HDMI cable. I've intercepted them both at CMyLockedFrameWndEx::WindowProc.

The automatic window repositioning occurs after that. I noticed that bacause I've put breakpoints on CMyLockedFrameWndEx::OnWindowPosChanging and CMyLockedFrameWndEx::OnWindowPosChanged and they stop after the events I catched on WindowProc. This workflow seems to be unrelated to the catching of events I described as my WindowProc method is:

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);
}

and when passing in OnWindowPosChanging or OnWindowPosChanged, the flow doesn't come from the specific cases of WindowProc handled by me. And that is a problem.

I tried to follow the call stack to see what window sent the WM_WINDOWPOSCHANGING or the WM_WINDOWPOSCHANGED message, but I didn't succeed. I have even tried to use Spy++64 to detect who was the the sender of the message, but I didn't succeed. The whole idea of seeing who was the sender was: if it is associated to a system's display change is to detect it beforehand and impeach the auto repositioning to even happen.

As i didn't succeed yet, what can I do to make the window immune to a system's display change?

Thanks.


Solution

  • The unhappy news are I can not do this on a preventive way, because the system moves the windows to the primary screen even after sending any useful messages.

    The good news are I can react to the move by adding a

    ON_WM_WINDOWPOSCHANGED()

    line on the class's message map and supply it with its respective handler function:

    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;
            }
        }
    }´
    

    Note, that it helps me that I was persisting the position where the window was in the m_Top and m_Left variables.