Comment obtenir Windows shutdown event dans le projet Fmx comme WM QUERYENDSESSION et WM ENDSESSION sur un projet VCL?

je dois intercepter Windows shutdown, et exécuter une requête DB, avant que mon application se ferme. J'utilise Delphi XE10 sous Windows 10 <!-Sur un projet FMX

ce que j'ai essayé est le code ci-dessous, mais il ne fonctionne pas

  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;

seule l'application de fermeture normale, fonctionnera très bien, sous L'événement FormCloseQuery, mais quand Windows s'éteint, mon application se fermera sans enregistrer de données

13
demandé sur Sebastian Z 2016-10-01 16:45:50

2 réponses

FormCloseQuery fonctionne parce qu'il est exposé par le cadre. Votre application ne sauve aucune donnée lorsque Windows se ferme parce que vos gestionnaires de messages ne sont jamais appelés. Le traitement de messages n'est disponible que pour les applications VCL, les applications fmx ont un mécanisme de messagerie différent comme documentée.

Brève explication ici implique qu'il est possible de recevoir des notifications de l'OS dans fmx cadre. Cependant, je ne sais pas si c' inclut les notifications d'arrêt et s'il est possible de configurer votre retour, car la documentation mentionne l'objet message à lire seulement.

Jusqu'à ce que vous trouviez comment fonctionne le mécanisme de messagerie fmx et s'il répond aux exigences, vous pouvez sous-classer la fenêtre de votre formulaire par des moyens conventionnels. Exemple ci-dessous utilise 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}
14
répondu Sertac Akyuz 2016-10-03 19:24:07

il y a eu un certain nombre de changements dans cette zone de Windows dans les versions (relativement) récentes, c'est - à-dire pour revenir à Windows XP. En outre, la façon dont Delphi windows sont gérés par L'Application ont été modifiés pour mieux se comporter par rapport à D'autres changements de système D'exploitation et les choses sont différentes à nouveau dans FMX.

WM_QUERYENDSESSION n'est désormais envoyé qu'aux fenêtres de haut niveau. Si votre application est une application VCL et a MainFormOnTaskbar set TRUE puis votre le formulaire principal de demande est une fenêtre de haut niveau et devrait recevoir le message. Si MainFormOnTaskbar est FALSE, ou si votre formulaire n'est pas le formulaire principal (malgré le nom) alors il n'est pas une fenêtre de haut niveau et ne recevra pas le message.

si votre application utilise FMX alors vous aurez besoin de creuser autour de l'intérieur de la FMX.Platform.Win WindowService pour déterminer exactement comment la parentalité de votre forme principale est déterminée. D'après une inspection de [XE4] FMX source, les choses semblent avoir je suis allé en arrière dans cette zone (par rapport à la VCL) et il y a des odeurs de code moches ici.

Les problèmes que les détails les plus fins dans ce domaine sont responsables qu'à partir de Vista ou ultérieur WM_QUERYENDSESSION n'est plus envoyé aux demandes visible fenêtres de niveau supérieur. Même si votre formulaire principal est une fenêtre de haut niveau, s'il n'est pas visible au moment où Windows se ferme, alors ce pourrait être la raison pour laquelle vous ne recevez pas le message.

si la le problème est que votre fenêtre n'est pas la fenêtre de haut niveau dans votre application alors il devrait y avoir assez d'informations ici pour vous permettre au moins de comprendre pourquoi.

dans une application VCL, faire de votre formulaire principal la fenêtre de la barre des tâches devrait résoudre le problème. Je ne sais pas s'il existe un moyen similaire de traiter la question dans une demande de FMX.

si vous avez une fenêtre de haut niveau valide et que le problème est que votre fenêtre de haut niveau n'est (parfois) pas visible alors vous devrez trouver un autre mécanisme pour vous connecter au processus d'arrêt, mais vous devez être conscient que tout comportement qui dépend d'autres processus doit tenir compte du fait que ces autres processus eux-mêmes sont en train de s'arrêter et peuvent ne pas être disponibles.

bien sûr, tout cela est très spécifique à Windows notifications d'arrêt. Si vous avez l'intention de soutenir d'autres plates-formes avec votre application FMX, alors vous aurez besoin de traiter le comportement d'arrêt autrement dit, en supposant que FMX n'offre pas de solution de notification d'arrêt multiplateformes (autrement, vous utiliseriez cette solution, non?).

(et si vous ne visez en fait que Windows, pourquoi diable utilisez-vous FMX ?)

1
répondu Deltics 2016-10-01 23:10:46