首页
学习
活动
专区
圈层
工具
发布
社区首页 >问答首页 >如何防止Windows会话解锁后表单位置大小的更改?

如何防止Windows会话解锁后表单位置大小的更改?
EN

Stack Overflow用户
提问于 2014-03-07 11:42:13
回答 2查看 2K关注 0票数 4

描述

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

意图

每当发生这种锁定场景时,我要么希望恢复窗体位置,要么阻止预先调整窗体的大小。

复制的步骤

这些步骤对我来说适用于Windows7 x64。

我正在设置一个空白屏幕保护程序在1分钟后被激活。我打开我的应用程序和适当的拉伸表单。我lock我的帐户,等待屏幕保护程序弹出。登录后,我可以看到表单大小的调整。

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

附加信息

我迄今所做和观察到的:

  • 使用Spy++,我看到我的应用程序收到了WParam = SPI_SETWORKAREAWM_SETTINGCHANGE消息。此时,我的表单已经有了新的大小。
  • 我已经注册了会话通知,以对会话锁、解锁、注销等作出反应。 锁定时重新调整会话更改,我的表单大小似乎没有问题。当稍后接收到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;

有人能帮我解决这个问题吗?

EN

回答 2

Stack Overflow用户

回答已采纳

发布于 2014-03-10 16:24:47

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

它是回答这里和来自delphiDabbler的解决方案1的组合。

基本上,我注册的是Windows状态更改事件(WM_WTSSESSION_CHANGE)。在提供的示例中(基于一个裸VCL表单应用程序),我使用WM_EXITSIZEPOS消息保存当前的表单大小。

Windows在触发位置更改消息时显示了不同的行为。这就是为什么我必须修改我的第一稿,现在使用两个变量。当会话被锁定时,我将防止位置更改,并在会话解锁后防止第一个位置更改。使用WM_WINDOWPOSCHANGING消息截获位置更改。

但是,为了不恢复正常位置,如果表单最大化,我将使用FRestoreNormalRect字段。

代码语言:javascript
复制
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.
票数 2
EN

Stack Overflow用户

发布于 2018-10-26 07:46:20

您可以通过TMemoryStream.WriteComponent事件使用OnDeactivate中的TMemoryStream.ReadComponent方法和TMemoryStream.ReadComponent方法。作为参数,您将给出您的表单。

请查看这个职位以获得更详细的信息。

票数 0
EN
页面原文内容由Stack Overflow提供。腾讯云小微IT领域专用引擎提供翻译支持
原文链接:

https://stackoverflow.com/questions/22249149

复制
相关文章

相似问题

领券
问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档