哪种是终止Delphi应用程序的正确方式?

28

我想终止一个Delphi应用程序而不执行任何其他代码行,并且我想知道正确的做法是什么。此外,我想知道目前我正在做的事情是否有问题。 基本上,我的代码看起来像这样:

//Freeing all objects (Obj1.Free, etc..)
Application.Terminate;
Halt;

这样停止 Delphi 应用程序的方式正确吗?还是应该用其他方法?


3
关闭主窗体是正确的方式。如果你愿意,可以直接使用 Application.MainForm.Close; 进行关闭。 - Sir Rufo
1
关闭 MainForm 只需调用 Application.Terminate,因此在需要时可以直接调用它。但是,请注意它只能在主线程的上下文中调用,而不能在工作线程中调用。 - Remy Lebeau
4
当调用Application.MainForm.Close时,不仅会调用Application.Terminate,而且还会处理主窗体的OnCloseQueryOnClose事件(如果已分配)。您甚至可以通过将OnCloseQuery事件的CanClose变量设置为false来防止应用程序关闭。但是,调用Application.Terminate不会处理这些事件。 - SilverWarior
为了防止当前过程中的其他代码执行,请在 Application.Terminate; 后使用 Exit; 而不是 Halt; - crefird
@SilverWarior - 是的,Close 方法首先将调用 DoClose(),然后再调用 Terminate。 - Gabriel
Exit仅仅是离开当前的函数或过程,它不会有任何其他操作。顺便说一下,它不应该与C标准库中的exit函数混淆。 - undefined
6个回答

43
Application.Terminate()会中断在TApplication.Run()TForm.ShowModal()中的消息循环,使主线程能够正常退出、执行必要的清理等操作。

Vcl.Forms.TApplication.Terminate

结束应用程序的执行。

调用Terminate来以编程方式结束应用程序。通过调用Terminate而不是释放应用程序对象,可以允许应用程序有序地关闭

Terminate调用Windows API的PostQuitMessage函数来执行应用程序的有序关闭。Terminate不是立即生效的。Terminate在收到WM_QUIT消息和主窗体关闭时会自动调用。

Halt()则是一种立即异常终止的方法。基本上,它会将进程从内存中强制移除。只有在没有其他选项可用的极端情况下才使用它。

System.Halt

启动程序的异常终止。

Halt执行程序的异常终止,并返回到操作系统。

要对Delphi应用程序进行正常关闭,请调用全局Application对象上的Terminate方法。如果应用程序不使用提供Application对象的单元,请从主Program块调用Exit过程。


在这种特定情况下,我的目标是停止应用程序而不执行任何其他代码行(我只想释放所有内容并终止应用程序)。由于Application.Terminate不是立即执行的,我应该使用Halt吗?或者在使用Halt时是否存在可能的问题? - Hwau
3
“Halt”并不释放太多内存,但它会尝试进行一些内部系统清理。如果您的目标只是立即退出进程,您可以使用“Halt()”,但如果这仍然需要执行太多代码,则必须使用Win32 API的“TerminateProcess()”函数。 - Remy Lebeau
感谢您的澄清,我想Halt已经足够满足我的需求了。您的信息非常有帮助,+1并接受。 - Hwau

14

我想要终止一个 Delphi 应用程序,而不执行任何其他代码。

Application.TerminateHalt 都无法达到这个目的。前者会执行有序终止,许多代码将被执行。调用 Halt 更有希望。那是一种异常终止。但是单元终结代码会被执行。

如果您希望尽可能快地退出,并在此过程中执行最少量的代码,请调用 ExitProcess。那是 Halt 的最后一步,通过直接调用 ExitProcess,您可以避免 Halt 在调用 ExitProcess 之前执行的所有步骤。


6
ExitProcess会执行代码,例如DllMain。应该使用TerminateProcess。 - Alex
2
@Alex TerminateProcess 确实更加残酷。 - David Heffernan
TerminateProcess 曾经是我必须使用的邪恶方法。我们需要的第三方库在退出时会引发异常。使用 TerminateProcess 后,异常消失了。 :-) - nurettin
1
如果您不知道进程中所有线程的状态,最好调用 TerminateProcess 而不是 ExitProcess - Ian Boyd

2

我在使用Application.Terminate时遇到了一些问题,因为我需要启动窗体关闭程序,所以我只做了以下操作:

Form1.Close;

我在 .dproj 文件中发现了一个新的解决方案

begin
  ReportMemoryLeaksOnShutdown := True;
  Application.Initialize;
  Application.CreateForm(TFormMain, FormMain);
  if Not(VerifyCode()) then
  begin
      ShowMessage('Software unregistered!');
      Application.Terminate;
  end
  else
  Application.Run;
end.

首先检查VerifyCode是否正确,只有在正确的情况下才创建主窗体,这样不是更好吗?Application.Terminate会离开事件循环,而Application.Run则进入事件循环。换句话说,Application.Terminate应该从某个事件处理程序中调用,而不能直接从主过程中调用。 - undefined

1

如果代码必须放在主表单的OnCreate事件中,请尝试以下代码。但是,它并不能按预期工作,主表单会显示,然后应用程序就会结束。

要能够看到效果,请添加另一个窗体并在其创建时加入一个长循环。

似乎所有主项目源文件中的Application.CreateForm都被执行了。

示例代码:

procedure TMyMainForm.FormCreate(Sender: TObject);
begin
     ShowMessage('[1] This must allways be shown');
     if mrOK=MessageDlg('Exit?',mtConfirmation,[mbOK,mbCancel],0)
     then begin
               Application.Terminate;
               Exit;
          end;
     ShowMessage('[2] This must not allways be shown');
end;
procedure TMyOtherForm.FormCreate(Sender: TObject);
begin
     ShowMessage('[3] This must not allways be shown');
end;

使用该代码消息[1]和[3]总是显示。

仅通过调用Halt来不显示[3]。

注意:为什么在MainForm OnCreate上编写这样的代码?简单的答案可能是,exe检查是否满足运行条件并且发现它们没有满足(缺少文件等)。粗鲁的答案(抱歉),只是因为我想要/需要这样做。


Application.ShowMainForm := False; - Nashev
我的意思是,当在主窗体上创建时调用Application.Terminate,它几乎什么也不做,因为主窗体的创建是在项目文件“.dpr”中的主应用程序源代码中的Application.Run调用之前处理的。Terminate会中断主循环,但它仍未启动!而Application.Run在启动主消息处理循环之前会显示先前创建的MainForm。但有解决方案:它有一个特殊标志来不显示窗体。将Application.ShowMainForm:= False;设置为false,主窗体将根本不会显示。 - Nashev
但是[3]你仍然会看到,它是表单构造函数的一部分。从构造函数退出并不会中断对象的构建。它可以通过引发异常来中断/例如,您可以调用Abort,但必须处理某些事情。 - Nashev

0

我知道这是一个旧的帖子,但如果还有人在关注,我会很感激对这个帖子的补充意见。

相当长一段时间以来,我一直调用Application.Terminate,然后是ExitProcess(0)。我的理论是Application.Terminate可以进行优雅的清理,而ExitProcess则可以防止任何其他代码执行。它似乎有效,并且我没有收到内存泄漏或其他不良影响的报告。代码可能如下:

Procedure (KillTheApp);
begin
    Application.Terminate;
    Application.ProcessMessages;
    ExitProcess(0);
end;

-1

关于dll

TerminateProcess(OpenProcess(PROCESS_ALL_ACCESS,False,GetPIDbyProcessName(ExtractFileName(ParamStr(0)))),0);

感谢您为Stack Overflow社区做出的贡献。这可能是一个正确的答案,但如果您能提供代码的额外解释,那将非常有用,这样开发人员就可以理解您的推理过程。对于不太熟悉语法或难以理解概念的新开发人员来说,这尤其有用。为了造福社区,您能否编辑您的答案并包含更多细节呢? - Jeremy Caney

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