ventana pestaña navegador evitar evento ejemplo ejecutar cierre cerrar capturar antes alerta delphi firemonkey

delphi - pestaña - ¿Cómo obtener el evento de cierre de Windows en el proyecto Fmx como WM_QUERYENDSESSION y WM_ENDSESSION en un proyecto de VCL?



onbeforeunload (2)

Necesito interceptar el cierre de Windows y ejecutar alguna consulta DB, antes de que se cierre mi aplicación. Estoy usando Delphi XE10 en Windows 10 en un proyecto de FMX

Lo que probé es el siguiente código, pero no funciona

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;

Solo la aplicación de cierre normal funcionará correctamente en el evento FormCloseQuery, pero cuando Windows se cierre, Mi aplicación se cerrará sin guardar ningún dato


Se han producido una serie de cambios en esta área en Windows en versiones (relativamente) recientes, es decir, volviendo a Windows XP. Además, la forma en que las ventanas de Delphi son administradas por la aplicación han sido modificadas para comportarse mejor con respecto a otros cambios del sistema operativo y las cosas son diferentes nuevamente en FMX.

WM_QUERYENDSESSION ahora solo se envía a las ventanas de nivel superior. Si su aplicación es una aplicación VCL y tiene MainFormOnTaskbar configurado como TRUE , su formulario principal de aplicación es una ventana de nivel superior y debería recibir el mensaje. Si MainFormOnTaskbar se establece FALSE , o si su formulario no es el principal (a pesar del nombre), entonces no es una ventana de nivel superior y no recibirá el mensaje.

Si su aplicación usa FMX, tendrá que buscar dentro de FMX.Platform.Win WindowService para determinar exactamente cómo se determina la crianza de su formulario principal. Según una inspección de la fuente [XE4] FMX, parece que las cosas han ido hacia atrás en esta área (en relación con el VCL) y aquí hay algunos olores desagradables.

Los problemas que causan los detalles más finos en esta área son que desde Vista en adelante, WM_QUERYENDSESSION ya no se envía a aplicaciones sin ventanas visibles de nivel superior. Incluso si su formulario principal es una ventana de nivel superior, si no está visible en el punto en que Windows se está cerrando, entonces podría ser por eso que no está recibiendo el mensaje.

Si el problema es que su ventana no es la ventana de nivel superior en su aplicación, entonces debería haber suficiente información aquí para que pueda al menos descubrir por qué.

En una aplicación de VCL, hacer que su Formulario principal sea la ventana de la barra de tareas debería resolver el problema. No sé si hay un medio similar para abordar el problema en una aplicación FMX.

Si tiene una ventana de nivel superior válida y el problema es que su ventana de nivel superior es (a veces) no visible, entonces deberá encontrar algún otro mecanismo para enganchar en el proceso de apagado, pero debe tener en cuenta que cualquier comportamiento que dependa en otros procesos debe tenerse en cuenta el hecho de que esos otros procesos pueden cerrarse y pueden no estar disponibles.

Por supuesto, todo esto es altamente específico para las notificaciones de apagado de Windows . Si tiene la intención de admitir otras plataformas con su aplicación FMX, tendrá que lidiar con el comportamiento de apagado de forma diferente allí, asumiendo que FMX no proporciona una solución de notificación de apagado multiplataforma (de lo contrario, estaría usando eso, ¿no?).

(Y si en realidad solo está apuntando a Windows, ¿por qué en la Tierra está usando FMX?)


FormCloseQuery funciona porque está expuesto por el marco. Su aplicación no guarda ningún dato cuando Windows se cierra porque sus manejadores de mensajes nunca son llamados. El manejo de mensajes solo está disponible para las aplicaciones de VCL, las aplicaciones de fmx tienen un mecanismo diferente para la mensajería según lo documentado .

Breve explicación aquí implica que es posible recibir notificaciones del sistema operativo en el marco de fmx. Sin embargo, no sé si esto incluye las notificaciones de cierre y si es posible establecer su devolución, ya que la documentación menciona que el objeto de mensaje debe ser de solo lectura.

Hasta que descubra cómo funciona el mecanismo de mensajería de fmx y si cumple con los requisitos, puede crear una subclase de la ventana de su formulario por medios convencionales. El ejemplo siguiente usa 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}