I need to intercept Windows shutdown, and execute some DB query, before that my application will close. I'm using Delphi XE10 under Windows 10 on a FMX project
What I tried is the code below, but it doesn't work
private
{ Private declarations }
{$IFDEF MSWINDOWS}
procedure WMQueryEndSession(var Msg: TWMQueryEndSession); message WM_QUERYENDSESSION;
procedure WMEndSession(var Msg : TWMQueryEndSession); message WM_ENDSESSION ;
{$ENDIF}
end;
procedure TfMain.WMQueryEndSession(var Msg: TWMQueryEndSession);
var
lista:TStringList;
begin
{$IFDEF MSWINDOWS}
try
lista:=TStringList.Create;
lista.Add(FOrmatDateTime('DD/MM/YYYY HH:NN:SS',now)+' event WMQueryEndSession');
Lista.SaveToFile(froot+formatdatetime('YYMMDDHHNNSSZZZ',now)+'.log');
SincroClose();
lista.Add(FOrmatDateTime('DD/MM/YYYY HH:NN:SS',now)+' Done');
Lista.SaveToFile(froot+formatdatetime('YYMMDDHHNNSSZZZ',now)+'.log');
finally
lista.Free;
end;
{$ENDIF}
inherited;
end;
procedure TfMain.WMEndSession(var Msg: TWMQueryEndSession);
var
lista:TStringList;
begin
{$IFDEF MSWINDOWS}
try
lista:=TStringList.Create;
lista.Add(FOrmatDateTime('DD/MM/YYYY HH:NN:SS',now)+' WMEndSession');
Lista.SaveToFile(froot+formatdatetime('YYMMDDHHNNSSZZZ',now)+'.log');
SincroClose();
lista.Add(FOrmatDateTime('DD/MM/YYYY HH:NN:SS',now)+' Done');
Lista.SaveToFile(froot+formatdatetime('YYMMDDHHNNSSZZZ',now)+'.log');
finally
lista.Free;
end;
{$ENDIF}
inherited;
end;
procedure TfMain.FormCloseQuery(Sender: TObject; var CanClose: Boolean);
var lista:TStringList;
begin
{$IFDEF MSWINDOWS}
CanClose:=false;
try
lista:=TStringList.Create;
lista.Add(FOrmatDateTime('DD/MM/YYYY HH:NN:SS',now)+' FormCloseQuery');
Lista.SaveToFile(froot+formatdatetime('YYMMDDHHNNSSZZZ',now)+'.log');
SincroClose();
lista.Add(FOrmatDateTime('DD/MM/YYYY HH:NN:SS',now)+' Done');
Lista.SaveToFile(froot+formatdatetime('YYMMDDHHNNSSZZZ',now)+'.log');
CanClose:=true;
finally
lista.Free;
end;
{$ENDIF}
end;
Only normal closing application, will work fine ,under FormCloseQuery event, but when Windows is shutting down, My application will close without saving any data
FormCloseQuery
works because it's exposed by the framework. Your application does not save any data when Windows is shutting down because your message handlers are never called. Message handling is only available to VCL applications, fmx applications have a different mechanism for messaging as documented.
Brief explanation here implies that it is possible to receive notifications from the OS in fmx framework. However I don't know if this includes shutdown notifications and if it is possible to set your return, as the documentation mentions the message object to be read only.
Until you find out how fmx messaging mechanism works and if it meets the requirements, you can subclass your form's window by conventional means. Below example uses SetWindowSubclass
.
...
protected
{$IFDEF MSWINDOWS}
procedure CreateHandle; override;
procedure DestroyHandle; override;
procedure WMQueryEndSession(var Msg: TWMQueryEndSession); message WM_QUERYENDSESSION;
procedure WMEndSession(var Msg: TWMEndSession); message WM_ENDSESSION;
{$ENDIF}
...
implementation
{$IFDEF MSWINDOWS}
uses
FMX.Platform.Win, Winapi.Commctrl;
function SubclassProc(Wnd: HWND; Msg: UINT; wParam: WPARAM; lParam: LPARAM;
uIdSubclass: UINT_PTR; RefData: DWORD_PTR): LRESULT; stdcall;
var
Self: TfMain;
Message: TMessage;
begin
Result := DefSubclassProc(Wnd, Msg, wParam, lParam);
case Msg of
WM_QUERYENDSESSION, WM_ENDSESSION:
begin
Self := TfMain(RefData);
Message.Msg := Msg;
Message.WParam := wParam;
Message.LParam := lParam;
Message.Result := Result;
Self.Dispatch(Message);
Result := Message.Result;
end;
end;
end;
procedure TfMain.CreateHandle;
var
Wnd: HWND;
begin
inherited;
Wnd := WindowHandleToPlatform(Self.Handle).Wnd;
SetWindowSubclass(Wnd, SubclassProc, 1, DWORD_PTR(Self));
end;
procedure TfMain.DestroyHandle;
var
Wnd: HWND;
begin
Wnd := WindowHandleToPlatform(Self.Handle).Wnd;
RemoveWindowSubclass(Wnd, SubclassProc, 1);
inherited;
end;
procedure TfMain.WMQueryEndSession(var Msg: TWMQueryEndSession);
begin
// do not call inherited here, there's no inherited handling
end;
procedure TfMain.WMEndSession(var Msg: TWMEndSession);
begin
// do not call inherited here, there's no inherited handling
end;
var
ICC: TInitCommonControlsEx;
initialization
ICC.dwSize := SizeOf(ICC);
ICC.dwICC := ICC_STANDARD_CLASSES;
InitCommonControlsEx(ICC);
{$ENDIF}