I found a solution and am posting demo code (XE2) as a Delphi solution to this problem.
It is a combination of the answer here and solution 1 from delphiDabbler.
Basically I am registering for Windows session state change events (WM_WTSSESSION_CHANGE
). In the provided example (based on a naked VCL Form Application) I am using the WM_EXITSIZEPOS
message to save the current form sizepos.
Windows was showing a varying behavior regarding the moment the position change message was fired. This is why I had to revise my first draft and am now using two variables. I am preventing position changes when the session is locked and prevent the first position change after the session is unlocked. The position changes are intercepted using the WM_WINDOWPOSCHANGING
message.
But to not restore the normal position if the form is maximized I am using the FRestoreNormalRect field.
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.