delphi - 使用运行时包编译的应用程序的单元最终确定顺序?

标签 delphi delphi-2010 package finalization

我需要在 SysUtils 单元完成后执行我的代码。

我已将代码放在单独的单元中,并首先将其包含在 dpr 文件的 use 子句中,如下所示:

project Project1;

uses
  MyUnit,    // <- my separate unit
  SysUtils,
  Classes,
  SomeOtherUnits;

procedure Test;
begin
  //
end;

begin
  SetProc(Test);
end.

MyUnit 看起来像这样:

unit MyUnit;

interface

procedure SetProc(AProc: TProcedure);

implementation

var
  Test: TProcedure;

procedure SetProc(AProc: TProcedure);
begin
  Test := AProc;
end;

initialization

finalization
  Test;
end.

请注意,MyUnit 没有任何用途。

这是常见的 Windows exe,没有控制台,没有表单,并使用默认运行时包编译。 MyUnit 不属于任何包(但我也尝试从包中使用它)。

我希望 MyUnit 的终结部分将在 SysUtils 的终结部分之后执行。这就是 Delphi 的帮助告诉我的。

但是,情况并非总是如此。

我有 2 个测试应用程序,它们在测试例程/dpr 文件和单元中的代码略有不同,在用途中列出。然而,在所有情况下,MyUnit 都首先列出。

一个应用程序按预期运行:Halt0 -> FinalizeUnits -> ...其他单元... -> SysUtils 的最终确定 -> MyUnit 的最终确定 -> ...其他单元...

但第二个不是。 MyUnit 的终结在 SysUtils 的终结之前调用。实际的调用链如下所示: Halt0 -> FinalizeUnits -> ...其他单位... -> SysUtils 的终结(跳过) -> MyUnit 的终结 -> ...其他单位... -> SysUtils 的终结(已执行)

两个项目都有非常相似的设置。我尝试了很多方法来消除/最小化它们的差异,但我仍然没有看到这种行为的原因。

我尝试对此进行调试并发现:似乎每个单元都有某种引用计数。而且 InitTable 似乎包含对同一单元的多次引用。当第一次调用 SysUtils 的终结部分时 - 它会更改引用计数器并且不执行任何操作。然后执行MyUnit的终结。然后再次调用 SysUtils,但这次引用计数达到零并执行终结部分:

Finalization: // SysUtils' finalization
5003B3F0 55               push ebp          // here and below is some form of stub
5003B3F1 8BEC             mov ebp,esp
5003B3F3 33C0             xor eax,eax
5003B3F5 55               push ebp
5003B3F6 688EB50350       push $5003b58e
5003B3FB 64FF30           push dword ptr fs:[eax]
5003B3FE 648920           mov fs:[eax],esp
5003B401 FF05DCAD1150     inc dword ptr [$5011addc] // here: some sort of reference counter
5003B407 0F8573010000     jnz $5003b580     // <- this jump skips execution of finalization for first call
5003B40D B8CC4D0350       mov eax,$50034dcc // here and below is actual SysUtils' finalization section
...

任何人都可以破解这个问题吗?我错过了什么吗?

最佳答案

单元以与初始化相反的顺序完成。初始化的顺序由单元使用图的非循环(即永远不会下降到已访问的单元)后序遍历确定,从主使用子句(在程序或库中)开始。 SysInit 通常是第一个被初始化的单元,然后是 System。

包的动态加载使事情变得复杂,因为主 EXE 或 DLL 需要指定主镜像使用的单元的初始化顺序。因此,当动态加载包时,它将运行它认为应该是初始化的顺序,但已经初始化的单元将被跳过;当动态卸载包时,情况相反。

一般规则:

  • 较低级别的事物应在较高级别的事物之前初始化
  • 终结应该按照初始化的相反顺序

这些规则几乎总是有意义的。上级单元的初始化通常依赖于下级单元提供的服务。例如,如果没有SysUtils,Delphi中就没有异常支持。出于同样的原因,逆序终结也有意义:高级终结依赖于低级单元提供的服务,因此它们必须在低级单元终结之前运行。

总而言之,就您的问题而言,如果您所说的是真的,那么听起来编译器或 RTL 中的某个地方可能存在错误:主 EXE 首先使用 MyUnitMyUnit 在其接口(interface)或实现中不使用其他单元,并且动态加载的包不会发生任何有趣的事情。我所能建议的就是继续削减具有奇怪行为的项目,直到获得最小的可复制样本;到那时,应该清楚到底是什么导致了问题。

关于delphi - 使用运行时包编译的应用程序的单元最终确定顺序?,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/2628859/

相关文章:

delphi - 有没有办法让 TRadioButton 透明?

delphi - 如何在 Delphi 中用稳定排序替换 StringList.Sort?

mysql - 使用自增主键插入记录

Delphi - 让外部应用程序最小化以触发另一个应用程序最小化过程

delphi - 使用 TTime 作为 TDictionary 的键

rust - 无法安装Racer : "pub(restricted) syntax is experimental (see issue #32409)"

delphi - 是什么原因导致 EVariantBadVarTypeError 异常?

mysql - 使用 Delphi 2010 将 Unicode 字符插入 MySQL

python - 如何使用 find_packages() 打包子目录中的所有文件

r - 如何在不构建或安装包的情况下运行 R 包测试?