描述
我有一个Delphi XE2应用程序,其中一种forms跨越两个显示器。 当我lockingWindows,等到屏幕保护程序被激活,然后解锁窗口,我的应用程序的所有窗体将被调整/重新定位,以适应每个显示器(这显然是默认的Windows行为,并适用于大多数应用程序)。
意向
无论何时发生这种locking情况,我要么恢复我的表单位置,要么防止我的表单被事先resize。
重现步骤
这些步骤适用于Windows 7 x64。
1分钟后,我正在设置一个空白屏幕保护程序。 我打开我的应用程序和适当的拉伸forms。 我lock
我的帐户,并等待屏幕保护程序popup。 login后,我可以看到resize的窗体。
在其他机器上locking足以重现行为。 在一些机器上,激活的屏幕保护程序就足够了。
附加信息
我到目前为止所做的和观察到的:
Spy++
我已经看到我的应用程序接收到WM_SETTINGCHANGE
消息SPI_SETWORKAREA
= SPI_SETWORKAREA
。 在这一点上我的表单已经有了新的大小。 WM_SETTINGCHANGE
,表单大小已被更改并缩小到一个监视器。 SetWindowPos
。 wsNormal
。 我在两个监视器上方以编程方式拉伸窗体,但不要触摸窗体状态。 WM_WTSSession_Change
解锁消息旧的(内部保存)的位置/大小,我试图调用 SetWindowPos(Handle, HWND_NOTOPMOST, FFormSizePos.Left, FFormSizePos.Top, FFormSizePos.Width, FFormSizePos.Height, SWP_NOACTIVATE or SWP_NOMOVE);
Self.Left := FFormSizePos.Left;
任何人都可以帮助解决我的意图吗?
我发现一个解决方案,并发布演示代码(XE2)作为这个问题的Delphi解决方案。
这里的答案是delphiDabbler的解决方案1的组合 。
基本上我正在注册Windows会话状态更改事件( WM_WTSSESSION_CHANGE
)。 在提供的示例中(基于裸VCL表单应用程序),我使用WM_EXITSIZEPOS
消息来保存当前的表单sizepos。
Windows在位置更改消息被触发的时刻显示出不同的行为。 这就是为什么我必须修改我的初稿,现在使用两个变量。 我正在阻止会话锁定时的位置更改,并阻止会话解锁后的第一个位置更改。 位置更改是使用WM_WINDOWPOSCHANGING
消息拦截的。
但是为了不恢复正常的位置,如果窗体是最大化我正在使用FRestoreNormalRect字段。
unit Unit1; interface uses Winapi.Windows, Winapi.Messages, Vcl.Forms; type TForm1 = class(TForm) private FSessionIsLocked: Boolean; FSessionWasUnlocked: Boolean; FRestoreNormalRect: Boolean; FLeft: Integer; FTop: Integer; FWidth: Integer; FHeight: Integer; procedure WMWTSSessionChange(var Msg: TMessage); message WM_WTSSESSION_CHANGE; protected procedure CreateWnd; override; procedure DestroyWnd; override; procedure WMExitSizeMove(var Msg: TMessage); message WM_EXITSIZEMOVE; procedure WMPosChanging(var Msg: TWmWindowPosChanging); message WM_WINDOWPOSCHANGING; procedure WMSize(var Msg: TWMSize); message WM_SIZE; end; var Form1: TForm1; implementation {$R *.dfm} //-------------------------------------------------------------------------------------------------- procedure TForm1.CreateWnd; begin inherited; WTSRegisterSessionNotification(WindowHandle, NOTIFY_FOR_THIS_SESSION); end; //-------------------------------------------------------------------------------------------------- procedure TForm1.DestroyWnd; begin WTSUnRegisterSessionNotification(WindowHandle); inherited; end; //-------------------------------------------------------------------------------------------------- procedure TForm1.WMExitSizeMove(var Msg: TMessage); var WP: TWindowPlacement; NormalRect: TRect; begin WP.Length := SizeOf(TWindowPlacement); GetWindowPlacement(Self.Handle, @WP); NormalRect := WP.rcNormalPosition; FLeft := NormalRect.Left; FTop := NormalRect.Top; FWidth := NormalRect.Right - NormalRect.Left; FHeight := NormalRect.Bottom - NormalRect.Top; end; //-------------------------------------------------------------------------------------------------- procedure TForm1.WMPosChanging(var Msg: TWmWindowPosChanging); begin { Sizepos changes might occur due to locks or unlocks. We need do prohibit both. While the session is locked we ignore all position changes. When the session has been unlocked we will ignore the next PosChanging message. } if FSessionIsLocked or FSessionWasUnlocked then begin { overwrite with the old settings } if FRestoreNormalRect then begin Msg.WindowPos.x := FLeft; Msg.WindowPos.y := FTop; Msg.WindowPos.cx := FWidth; Msg.WindowPos.cy := FHeight; Msg.Result := 0; end; { reset the variable, otherwise a manual resize would not be possible } if FSessionWasUnlocked then FSessionWasUnlocked := False; end; end; //-------------------------------------------------------------------------------------------------- procedure TiQForm.WMSize(var Msg: TWMSize); begin inherited; { We need to restore our normal rect only if the form is not maximized. Because if it is maximized it only positioned on one monitor and will not be moved by windows. If we do not repsect this case we would be restoring the normal rect unintentionally in WMPosChanging for every maximized form. } FRestoreNormalRect := not (Msg.SizeType = SIZE_MAXIMIZED); end; //-------------------------------------------------------------------------------------------------- procedure TForm1.WMWTSSessionChange(var Msg: TMessage); begin case Message.WParam of WTS_CONSOLE_DISCONNECT, WTS_REMOTE_DISCONNECT, WTS_SESSION_LOCK, WTS_SESSION_LOGOFF: begin FSessionIsLocked := True; end; WTS_CONSOLE_CONNECT, WTS_REMOTE_CONNECT, WTS_SESSION_UNLOCK, WTS_SESSION_LOGON: begin FSessionIsLocked := False; FSessionWasUnlocked := True; end; end; inherited; end; //-------------------------------------------------------------------------------------------------- end.