如何在Windows会话解锁后阻止表单位置大小更改?

时间:2014-03-07 11:42:13

标签: windows delphi multiple-monitors

描述

我有一个Delphi XE2应用程序,其中一个表单跨两个监视器。当我锁定Windows时,等到屏幕保护程序被激活然后解锁窗口,我的所有应用程序表单都将调整大小/重新定位以适合每个监视器(这显然是一种默认的Windows行为并适用于大多数应用程序)。

意图

每当发生此锁定方案时,我要么要恢复我的表单位置,要么阻止我的表单事先调整大小。

重现的步骤

这些步骤适用于Windows 7 x64 我设置了一个空白屏幕保护程序,1分钟后激活。我打开我的应用程序和适当的拉伸形式。我lock我的帐户并等待屏幕保护程序弹出。登录后,我可以看到表单已调整大小。

在其他机器上,锁定足以重现行为。在某些机器上激活的屏幕保护程序就足够了。

其他信息

到目前为止我所做的和所观察到的:

  • 使用Spy++我看到我的应用收到WM_SETTINGCHANGE = WParam的{​​{1}}条消息。此时我的表格已经有了新的尺寸。
  • 我已注册会话通知以对会话锁定,解锁,注销等作出反应 锁定时接收会话更改,我的表单大小似乎没问题。收到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);
  • 一样手动设置尺寸属性

有人可以帮我解决我的意图吗?

2 个答案:

答案 0 :(得分:2)

我找到了一个解决方案,并发布了演示代码(XE2)作为解决此问题的Delphi解决方案。

它是来自answer heredelphiDabbler和解决方案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.

答案 1 :(得分:0)

您可以在TMemoryStream.WriteComponent事件中使用OnDeactivate内部的TMemoryStream.ReadComponent方法和OnActivate方法。作为参数,您要提供表格。

请查看this post,以获取更多详细信息。