如何在主窗体之前创建一个表单?

6

我想创建一个闪屏(在主窗体之前),它将显示x秒钟,但是我不想用x秒钟延迟主窗体的创建。

因此,我创建了闪屏窗体,创建了主窗体,然后在x秒钟后关闭了闪屏窗体。
据我所知,使用CreateForm创建的第一个窗体是主窗体。这是正确的吗?

begin
  Application.Initialize;
  Application.MainFormOnTaskbar := FALSE;
  Application.Title := AppName;
  frmSplash:= TfrmSplash.Create(NIL);         <----- not main form??
  Application.CreateForm(TfrmMain, frmMain);  <----- main form??
  frmMain.LateInitialization;
  frmMain.show;
  Application.Run;
end.

关闭启动画面
启动画面有一个TTimer。此计时器在启动画面中执行一些动画,并在x秒后关闭该画面:
procedure TfrmSplash.CloseSplashForm;
begin
 Timer.Enabled:= FALSE;
 Close;        <-- I do see the program reaching this point
end;

然而,该应用程序在关闭时泄漏内存:

5 - 12 bytes: TMoveArrayManager<System.Classes.TComponent> x 4, Unknown x 2
13 - 20 bytes: TObservers x 1, TList x 3, Unknown x 3
21 - 36 bytes: TComponent.GetObservers$942$ActRec x 1, TPen x 2, TIconImage x 1, TPadding x 1, TBrush x 3, TTouchManager x 2, TMargins x 2, TSizeConstraints x 2, TList<System.Classes.TComponent> x 4, UnicodeString x 3, Unknown x 6
37 - 52 bytes: TDictionary<System.Integer,System.Classes.IInterfaceList> x 1, TPicture x 1, TGlassFrame x 1, TFont x 4
53 - 68 bytes: TIcon x 1
69 - 84 bytes: TControlScrollBar x 2
85 - 100 bytes: TTimer x 1
101 - 116 bytes: TControlCanvas x 2
149 - 164 bytes: Unknown x 2
437 - 484 bytes: TImage x 1
917 - 1012 bytes: TfrmSplash x 1

看起来frmSplash实际上没有被释放。


“CloseSplashForm” 中的 “Close” 不会自动释放该窗体。在关闭时,您是否释放它? - Andreas
3
不确定为什么这里需要计时器。为什么不在主窗体完全初始化后手动释放它(例如在主窗体的OnShow事件中)? - kobik
3个回答

8
将关闭事件添加到启动画面并设置。
Action := caFree;

难道caFree不是默认行为吗?我现在会立即尝试一下。 - Gabriel
是的。它起作用了...部分地...现在如果我在闪屏仍可见的情况下关闭主窗体,就会出现泄漏问题。 - Gabriel
3
默认行为是 _caHide_,但对于 MDI 子窗体则为 caMinimize 或 _caNone_。 - Uwe Raabe
2
@所有人免费使用:将此与将“Application”设置为所有者相结合 - 这样可以消除泄漏,同时尽快释放资源。 - mghie

4

闪屏窗体泄漏是因为没有销毁它。有一些选项:

  • 在创建时将Application对象作为闪屏窗体的所有者传递。
  • 手动销毁它。
  • 忽略泄漏。这并不重要,因为你只会创建一个闪屏窗体,所以未能销毁它不会导致任何大量资源的消耗。

使用第一种选项,闪屏窗体直到应用程序结束才会被销毁。这与故意泄露窗体并没有太大区别。

选择手动销毁窗体则稍微麻烦一些。你需要在关闭闪屏窗体时执行销毁操作,但你不能从窗体自身的事件处理程序中销毁该对象。因此,你可以调用表单的Release方法并排队销毁该窗体。如果你选择这种方式,需要确保即使计时器永远不过期,该窗体也会被销毁。使窗体归属于Application对象将完成这项工作。


第一项(通过应用程序)完成了。谢谢! - Gabriel
2
“忽略泄漏”是一个非常糟糕的建议,无论它有多小。 - kobik
1
@kobik 这是有争议的。为什么它比将应用程序对象作为所有者更糟糕?我个人不会这样做,但有一个观点认为在进程关闭时进行解除分配是浪费资源的。 - David Heffernan
1
@kobik,进程终止时跳过释放内存并不是泄漏。支持整理的论点是,如果现在抑制泄漏检测,代码可能会被修改以将先前无害的内容变成实际泄漏。所以,我不会这样做,但在某些情况下,有人会持不同意见。有些程序在终止时仅释放内存,可能需要花费大量时间。跳过这一步可以提高性能。 - David Heffernan
@kobik,你不同意我在之前评论中的分析吗? - David Heffernan
显示剩余2条评论

3

多年来我一直使用这个结构:

program MyProg;
uses
  Vcl.Forms,
  MainFrm in 'MainFrm.pas' {Zentrale},
  // Your Forms are here
  SplashFrm in 'SplashFrm.pas' {Splash};

{$R *.res}

var
  Sp: TSplash;

begin
  Application.Initialize;
  Application.MainFormOnTaskbar := True;
  Application.Title := 'XXXXXX';

{$IFDEF RELEASE}
  SP := TSplash.Create(nil);
  SP.Show;
  SP.Update;
{$ENDIF}

  Application.CreateForm(TZentrale, Zentrale);
  // ... and more Forms 

{$IFDEF RELEASE}
  SP.Hide;
  SP.free;
{$ENDIF}

  Application.Run;

是的,但我的问题陈述了“我需要显示闪屏x秒钟”。使用您的代码,如果“Zentrale”不是一个过度拥挤的表单,则闪屏将可见几毫秒。 - Gabriel
您可以在启动窗体中添加一个计时器,并在x秒后隐藏它。计时器可以在onshow事件中启用。在计时器事件中,当达到限制时调用hide方法隐藏窗体。其余部分保持不变。 - Christine Ross
“你可以添加一个计时器” - 这就是我所做的。请仔细阅读我的问题,这次注意一下。现在的回答并没有为讨论增添任何内容(它是一个随机回答,根本没有回答我的问题)。 - Gabriel
你的“问题”只包含一个问题“这样正确吗? ”。答案是肯定的。在项目/选项/窗体下,您可以更改创建的窗体的顺序。但第一个窗体是主窗体或主窗体是第一个窗体。有关详细信息,请阅读帮助文档。 - Christine Ross
你错过了一些内容:“如何在主窗体(MainForm)之前创建一个表单(form)?”可能是因为字体太小了 :) :) :) 无论如何,在stackoverflow上,“问题”就是原始问题的全部内容。你不能提取任何你喜欢的短语并将其作为孤立的想法来回答。请再次仔细阅读整个问题。不管怎样,这个问题已经被David和Uwe解决了。你仍然可以添加一些原创的东西来扩展他们的答案,但仍然要保持在上下文中!!! - Gabriel

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接