如何检查表单是否已关闭?

时间:2011-01-27 10:18:34

标签: delphi forms vcl

我看到的唯一方法是为此添加标志,但这是最好的方法吗?

当表单被销毁时,我检查是否(Assigned(form2))结果是否为真?为什么呢?

这样做的方法是什么?

5 个答案:

答案 0 :(得分:8)

您可以使用Form1.Showing查看表单是否已关闭。

除非您在Action := caFree事件中设置OnClose,否则关闭表单不会释放它。默认值为caHide

答案 1 :(得分:4)

哇,从过去的爆炸:)

Assigned()的工作方式是它基本上对指针进行无效检查。如果你破坏了form2,那么仍然会有一个form2指向的内存地址。

自从我完成任何Delphi以来,我已经很长时间了,但是从内存中,你需要在它被销毁时手动将form2 var设置为nil。如果你有一个中心位置(例如表格经纪人?),你创建&破坏形式,这应该很容易。

答案 2 :(得分:4)

如果使用Form1.FreeForm1.Destroy,Delphi将销毁对象,但不会将对象引用设置为nil。因此,请使用FreeAndNil

有关更多信息,请查看this link

中的Andreas Rejbrand答案

答案 3 :(得分:1)

在关闭应用程序时执行一些例程时面临同样的问题。在这种情况下,所有表单都会在舞台后面销毁,但指针不会设置为nil。这段代码让我感到高兴:

procedure TMyForm.FormDestroy(Sender: TObject);
begin
  MyForm:=nil;
end;

因此指针变为零,我可以使用Assigned进行检查,或者与nil进行比较。

答案 4 :(得分:-1)

正如提示一样,在某些特殊情况下执行此操作的正确方法是创建一个定时器,将nil分配给变量。

我会解释它(不知何故它很复杂),如果你在自己的代码MyForm:=TMyForm.Create中创建表单并且你有一个MyFrom.Close就很容易,只需添加一个MyForm:=nil或者更好MyForm.FreeAndNil ...但有时参考不是任何意思。

示例:你在一个程序里面创建一个循环很多同一个表单的副本(或者只是一个),让表单打开并结束那个程序,现在对打开的表单的引用是无处的,所以你可以不以正常方式指定nil或做freeandnil等。

对于那种情况,诀窍是使用一个定时器(只有一个毫秒),这个定时器需要引用,所以你必须存储在像Self这样的全局上,所有这些都可以在关闭事件。

执行free(在没有任何引用的地方)的最简单方法是在主窗体上创建一个TObjectList,这样它将保存所有需要释放的表单引用,并定义一个定时器(一毫秒)通过该列表做freeandnil;然后在onlcose上添加Self到该列表并启用该计时器。

现在另一部分,你有一个在开始时自动创建的普通表单,但是你需要将它设置为nil并在你自己的代码上重新创建它。

那个案例有一个全局指向那个表单,所以你只需要释放它就可以了,但是不要(我说得很响亮)在自己的表单代码中的任何部分,你必须这样做(我说如果(大声)表格代码。

有些时候你需要释放表单,当用户关闭它时,它没有以模态显示,这种情况很复杂,但同样的技巧是有效的,在onclose事件上你启用了一个计时器(即超出那种形式,通常在主要形式上)和计时器将自由和零。该计时器间隔可以设置为一个毫秒,它将不会运行,直到窗体完全关闭(请记住不使用Application.ProcessMessages,这通常是一个非常糟糕的主意)。

如果你将Self设置为nil,free或者自己的形式,你可以破坏你的应用程序内存(这样做完全不安全,更不用说它可以吃ram)。

释放表单的唯一方法(并且没有它的引用),一个未显示为模态的表单,并且是关闭它的用户,是编写一个触发器,在表单完全关闭后执行该操作。

我知道设置动作是免费的,但要把它设置为零,没有其他安全的方法。

必须说明:如果您在主表单上使用计时器,请在Onclose事件中对所有计时器运行Enabled:=False ...否则可能会发生奇怪的事情(不是总是,但有时......竞争条件销毁应用程序并在该计时器上运行代码),以及如果启用某个程序正确地终止它或中止它等等。

你的问题是复杂的事情之一......免费和无法通过代码而非用户操作关闭的表单。

对于所有其他人:想想,如果应用程序同时打开了很多表单,并且所有表单都可以同时与用户交互(任何人都是模态的),并且您的代码可以从其他...您需要知道用户已关闭任何表单以避免从代码访问该表单。除非你使用计时器,否则这不是一件容易的事。

如果你有一个中心'表单(如MDI应用程序)您可以将该计时器放在主MDI表单上,因此任何已关闭的子表单都可以释放并且为零,该技巧也是该主表单上的计时器。

只有当你确定你可以释放所有不可见的形式时,你可以在主表单上有一个通过所有表单的计时器,如果Visible是假的,那么调用FreeAndNil,我认为这种方式很容易错误,因为如果你在未来添加一个不能被释放但可以保持隐藏的表单......这段代码将无效。

总而言之,如果用户是onw谁关闭必须释放的表单并且nil,则代码无法检测和执行,没有事件启动(表单完全关闭后)和表单之前是完全关闭你甚至不能尝试释放它或没有它的参考,可能会发生奇怪的事情(如果主板有多个插槽,更容易发生,如果您的应用程序使用线程等,也更多)。

因此,对于线程应用程序(也没有线程化),我使用另一种方法,它工作得很好,不需要计时器,但需要在每个ThatForm.*之前进行双重检查,诀窍是定义一个Form bolean公共变量就像表格上公共部分的PleaseFreeAndNilMe一样,然后在onclose(作为最后一行)将其设置为True,而在OnCreate上将其设置为False。

通过这种方式,您将知道该表单是已关闭还是仅隐藏(隐藏表单从不调用close,只需调用hide)。

所以编码看起来像(你可以使用它作为warper,而不是定义表格,因为TForm将它们定义为TMyform,或者更好,使用像type TForm=class(Forms.TForm)而不是TMyForm=class(TForm)这样的黑客来将该变量添加到所有表单中):

TMyForm=class(TForm)
...
public
      PleaseFreeAndNilMe:=Boolean;
...
procedure TMyForm.FormCreate(Sender: TObject);
begin
     PleaseFreeAndNilMe:=False;
     ...
end;
procedure TMyForm.FormClose(Sender: TObject; var Action: TCloseAction);
begin
     ...
     PleaseFreeAndNilMe:=True;
end;

如果您遇到黑客攻击版本:

TForm=class(Froms.TForm)
public
      PleaseFreeAndNilMe:=Boolean;
end;
procedure TForm.FormCreate(Sender:TObject);
begin
     inherited Create(Sender);
     PleaseFreeAndNilMe:=False;
end;
procedure TForm.FormClose(Sender:TObject;var Action:TCloseAction);
begin
     PleaseFreeAndNilMe:=True;
     inherited FormClose(Sender,Action);
end;

但正如我所说,在访问任何会员之前(或者只是在你的比赛中进行比较),只需拨打全球'函数传递引用(无论它是否为零),编码为:

function IsNilTheForm(var TheForm: TMyForm);
begin
     if nil=TheForm
     then begin // The form was freed and nil
               IsNilTheForm:=True; // Return value
          end
     else begin // The form refence is not nil, but we do not know is it has been freed or not
               try
                  if TheForm.PleaseFreeAndNilMe
                  then begin // The form is not freed but wants to
                            try
                               TheForm.Free;
                            except
                            end;
                            try
                               TheForm:=Nil;
                            except
                            end;
                            IsNilTheForm:=True; // Return value
                       end
                  else begin // The form is not nil, not freed and do not want to be freed
                            IsNilTheForm:=False; // Return value
                       end; 
               except // The form was freed but not set to nil
                    TheForm:=Nil; // Set it to nil since it had beed freed
                    IsNilTheForm:=True; // Return value
               end;
          end;         
end;

因此,if nil=MyForm then ...您可以if (IsNilTheForm(MyForm)) then ...进行操作。

就是这样。

更好的计时器解决方案,因为表单尽快释放(使用较少的ram),使用PleaseFreeAndNilMe技巧,表单在调用IsNilTheForm之前不会被释放(如果不是在任何其他地方释放它。)

IsNilTheForm是如此复杂,因为它正在考虑所有状态(对于多插槽主板和线程应用程序)并让代码免费/在其他任何地方使用它。

当然,必须在主线程和原子排除中调用该函数。

释放一个Form并且Nil它的指针不是一件小事,大多数时候用户可以随时关闭它(因为没有触发表单中的代码)。

最大的问题是:当用户关闭表单时,无法使用该表单触发的事件处理程序以及表单结束它正在执行的所有操作之后。

Imaigne现在编码器已经放了很多Application.ProcessMessages;在应用程序的每个位置,也在该表格上等等......并且没有注意竞争条件...在用户要求关闭后尝试释放并且没有这样的表格...这是一场噩梦,但可以使用被破坏的TForm版本来解决,该版本具有一个变量,该变量告诉表单尚未被释放但是想要它。

现在想象你使用被黑客攻击的TForm并想要一个普通的TForm,只需将其定义为...= class(Forms.TForm),这样它现在就会有额外的变量。所以调用IsNilTheForm将作为nil的比较。

希望这有助于VCL编码人员修复这样的事情,例如当一个对象被破坏,释放,被玷污,隐藏等等时,从该对象的代码中提出一个事件,比如在主窗体上,等等。让生活变得更容易......或者只是修复它......关闭和自由意味着设置为Nil所有指向它的refences。

还有另一件事可以做(但我总是试图避免它):有多个变量指向完全相同的形式(不是复制它),这很容易出现很多错误,你自由一个并且需要为所有这些等等所有......我展示的代码也与之兼容。

我知道代码是comples ...但Free和Nil表单比我的代码更复杂。