You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

Windows任务栏关闭最小化FMX窗体时程序锁死问题求助

问题描述

我开发的FMX程序实现了关闭前确认逻辑:用户尝试关闭窗体时,程序会弹出模态对话框询问是否确认关闭。但存在以下异常:

  • 窗体未最小化时,流程正常,对话框正常显示,用户可选择是否关闭
  • 窗体最小化后从任务栏点击关闭按钮,程序直接锁死,只能通过任务管理器结束进程
  • 排查发现模态确认对话框已创建但不可见,用户无法操作,即使恢复窗体也无法正常关闭程序

已知对比情况:

  • 使用TApplication.MessageBox的VCL程序无此问题,任务栏关闭时消息框正常弹出
  • Delphi 10开发的同类FMX程序也无此问题

已尝试无效方案:

  • 在显示消息框前强制将窗体WindowState设为wsNormal并调用Application.ProcessMessages

需求:必须使用同步消息框,需等待用户点击"Yes"后才能关闭程序,防止未保存工作丢失。


相关代码

DFM文件

object Form1: TForm1
  Left = 0
  Top = 0
  Caption = 'Form1'
  ClientHeight = 347
  ClientWidth = 437
  FormFactor.Width = 320
  FormFactor.Height = 480
  FormFactor.Devices = [Desktop]
  OnClose = FormClose
  DesignerMasterStyle = 0
end

单元代码

unit Unit1;

interface

uses
  System.SysUtils, System.Types, System.UITypes, System.Classes, System.Variants,
  FMX.Types, FMX.Controls, FMX.Forms, FMX.Graphics, FMX.Dialogs;

type
  TForm1 = class(TForm)
    procedure FormClose(Sender: TObject; var Action: TCloseAction);
  private
    { Private declarations }
  public
    { Public declarations }
  end;

var
  Form1: TForm1;

implementation

{$R *.fmx}

uses
  FMX.DialogService.Sync;

procedure TForm1.FormClose(Sender: TObject; var Action: TCloseAction);
begin
  if TDialogServiceSync.MessageDialog(
           'Are you sure you want to close?', TMsgDlgType.mtWarning,
           [TMsgDlgBtn.mbYes, TMsgDlgBtn.mbNo],
           TMsgDlgBtn.mbYes, 0) = mrNo then
    Action := TCloseAction.caNone;
end;

end.

解决方案

方案1:改用Windows原生API MessageBox

直接调用Windows API的MessageBox替代TDialogServiceSync.MessageDialog,确保对话框在任何窗体状态下都能正常显示,和VCL程序行为一致:

修改FormClose方法,需在uses中添加Winapi.Windows单元:

procedure TForm1.FormClose(Sender: TObject; var Action: TCloseAction);
var
  Result: Integer;
begin
  // 可选:恢复窗体状态提升用户体验
  if WindowState = TWindowState.wsMinimized then
    WindowState := TWindowState.wsNormal;
  
  // 指定当前窗体为父窗口,确保对话框正常显示
  Result := MessageBox(Handle, 'Are you sure you want to close?', 'Warning', MB_YESNO or MB_ICONWARNING);
  
  if Result = IDNO then
    Action := TCloseAction.caNone;
end;

方案2:为TDialogServiceSync指定明确父窗口

如果坚持使用TDialogServiceSync,可手动指定父窗口为当前窗体,并确保窗体激活:

procedure TForm1.FormClose(Sender: TObject; var Action: TCloseAction);
var
  DialogResult: TModalResult;
begin
  if WindowState = TWindowState.wsMinimized then
  begin
    WindowState := TWindowState.wsNormal;
    SetForegroundWindow(Handle); // 强制激活窗体
    Application.ProcessMessages;
  end;

  // 手动传入当前窗体作为对话框Owner
  DialogResult := TDialogServiceSync.MessageDialog(
    'Are you sure you want to close?', TMsgDlgType.mtWarning,
    [TMsgDlgBtn.mbYes, TMsgDlgBtn.mbNo], TMsgDlgBtn.mbYes, 0, Self);
  
  if DialogResult = mrNo then
    Action := TCloseAction.caNone;
end;

方案3:在FormCloseQuery中提前拦截关闭请求

将确认逻辑移到FormCloseQuery事件中,更早拦截关闭请求,避免窗体状态导致的对话框隐藏:

  1. 为窗体添加OnCloseQuery事件
  2. 实现事件处理:
procedure TForm1.FormCloseQuery(Sender: TObject; var CanClose: Boolean);
var
  Result: Integer;
begin
  if WindowState = TWindowState.wsMinimized then
    WindowState := TWindowState.wsNormal;

  Result := MessageBox(Handle, 'Are you sure you want to close?', 'Warning', MB_YESNO or MB_ICONWARNING);
  CanClose := (Result = IDYES);
end;

内容的提问来源于stack exchange,提问作者XylemFlow

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.07.22 05:37:22