显示更改时锁定 window 位置
Lock window position on display change
我有一个包含多个 windows 的应用程序,我希望其中一些(我将其称为 CMyLockedFrameWndEx
,因为它们派生自 CFrameWndEx
)保留在原处更改系统显示区域后。
我的应用程序的所有 windows 的父项都是 NULL。
当我将第二台显示器的位置相对于第一台显示器拖动时,我已经设法捕捉到了 WM_DISPLAYCHANGE
消息;当我连接或断开第二台显示器的 HDMI 电缆时,我也赶上了 WM_DEVICECHANGE
。我在 CMyLockedFrameWndEx::WindowProc
.
截获了它们
自动 window 重新定位之后发生。我注意到因为我在 CMyLockedFrameWndEx::OnWindowPosChanging
和 CMyLockedFrameWndEx::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);
}
并且在传入OnWindowPosChanging
或OnWindowPosChanged
时,流量不是来自我处理的WindowProc
的具体案例。这是一个问题。
我试图跟踪调用堆栈以查看 window 发送了 WM_WINDOWPOSCHANGING
或 WM_WINDOWPOSCHANGED
消息,但没有成功。我什至尝试过使用 Spy++64 来检测谁是消息的发送者,但我没有成功。查看谁是发件人的整个想法是:如果它与系统的显示更改相关联,那就是事先检测到它并阻止自动重新定位甚至发生。
由于我还没有成功,我该怎么做才能使 window 不受系统显示变化的影响?
谢谢。
好消息是:显然,您可以控制自己的位置 window。
坏消息 - Windows 将首先 将您的 window 移动到新位置,然后 发送你是 WM_DISPLAYCHANGE
和 WM_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_Top
和 m_Left
变量中的位置对我有帮助。
我有一个包含多个 windows 的应用程序,我希望其中一些(我将其称为 CMyLockedFrameWndEx
,因为它们派生自 CFrameWndEx
)保留在原处更改系统显示区域后。
我的应用程序的所有 windows 的父项都是 NULL。
当我将第二台显示器的位置相对于第一台显示器拖动时,我已经设法捕捉到了 WM_DISPLAYCHANGE
消息;当我连接或断开第二台显示器的 HDMI 电缆时,我也赶上了 WM_DEVICECHANGE
。我在 CMyLockedFrameWndEx::WindowProc
.
自动 window 重新定位之后发生。我注意到因为我在 CMyLockedFrameWndEx::OnWindowPosChanging
和 CMyLockedFrameWndEx::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);
}
并且在传入OnWindowPosChanging
或OnWindowPosChanged
时,流量不是来自我处理的WindowProc
的具体案例。这是一个问题。
我试图跟踪调用堆栈以查看 window 发送了 WM_WINDOWPOSCHANGING
或 WM_WINDOWPOSCHANGED
消息,但没有成功。我什至尝试过使用 Spy++64 来检测谁是消息的发送者,但我没有成功。查看谁是发件人的整个想法是:如果它与系统的显示更改相关联,那就是事先检测到它并阻止自动重新定位甚至发生。
由于我还没有成功,我该怎么做才能使 window 不受系统显示变化的影响?
谢谢。
好消息是:显然,您可以控制自己的位置 window。
坏消息 - Windows 将首先 将您的 window 移动到新位置,然后 发送你是 WM_DISPLAYCHANGE
和 WM_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_Top
和 m_Left
变量中的位置对我有帮助。