delphi - 为什么包的任何单元终结部分中的代码在关闭时不执行?

标签 delphi package delphi-2007 finalization

我有一个应用程序,它使用静态链接的运行时包以及使用它们的设计时包。由于某种原因,任何单元终结部分中的代码都不会在运行时运行(我无法判断这种情况何时开始发生)。

finalization
  ShowMessage('Goodbye');
end.

关闭 Delphi 会显示该消息,但当我的应用程序关闭时不会显示该消息。更奇怪的是,如果我在 ShowMessage 上放置断点,它会在那里中断,但不会执行该行。如果终结中有多行,调试器会在第一行停止,不执行它,然后跳转到末尾。

procedure ProcOne;
begin
  SomeObject.Free; // Debugger does not enter or stop here
  SomeObject := nil;
end;

finalization
  ProcOne; // Debugger stops here, doesn't execute, jumps to "end."
  ProcTwo; // Every line has a blue dot
  ShowMessage('Bye');
end.

ProcOne 断点上的调用堆栈显示 @Halt0 => FinalizeUnits => MyPackage.MyUnit.Finalization。

如果我将该单元包含在不使用包的应用程序中,则一切都会正常执行。

有人知道是什么原因造成的吗?

编辑:

感谢 Allen Bauer 的评论指出了正确的方向,我已经成功地隔离了问题。如果应用程序是使用运行时包构建的,然后动态加载另一个也引用该包和单元的包,似乎就会出现问题。

我创建了一个测试项目来演示该问题:TestFinalization

有人知道这个问题的原因和/或解决方法吗?您通常可能不会注意到您的终结没有运行,直到您注意到外部资源没有被清理。

最佳答案

确保在关闭之前为每个动态加载的包调用 UnloadPackage。如果您只是调用 UnloadLibrary(或者只是依靠操作系统来卸载它们),则不会调用该包中的单元以及其他包中的所有单元的最终确定。初始化和终结是使用引用计数系统完成的,因为面对动态加载的包,无法知道哪些单元将被初始化以及何时初始化。只有当您平衡了终结调用和初始化调用时,最后一个终结调用才会真正执行终结部分中的代码块。同样,只有第一次调用初始化部分才会真正执行代码块。

初始化/终结是使用给定模块的编译器生成的表来完成的。当您构建与包链接的 exe 或 dll 时,此表包含对实际使用的所有单元的引用,甚至是来自链接包的单元。请注意,只有实际引用的单元才会被实际初始化。 IOW,如果 PackageA 中有 100 个单元,并且 exe 仅引用其中之一,那么只有该单元及其使用的任何单元都会被初始化。

对于动态加载的包,实际上没有办法知道实际将使用哪些单元,因此编译器会生成 init/finit 表,就好像每个单元都已初始化一样。在调用 LoadLibrary 期间加载包时不会处理该表,而是通过调用名为 Initialize() 的特殊导出来处理。 LoadPackage 函数确保调用该函数。该表仅确保加载包中的所有单元都已初始化。只有任何其他包中实际接触的单元才会被初始化,类似于我上面提到的 exe/dll 情况。 UnloadPackge 执行相反的操作,并在调用 UnloadLibrary() 之前调用特殊的导出 Finalize()。

最后,如果您对任何打包单元的使用列表进行了更改并且仅重建了包,您可能会遇到令人困惑的情况,即使给定包中的单元正确“使用”,初始化/终结也可能不会被调用彼此。这是因为 init/finit 是由加载模块控制的,而不是由其自身内部控制的。只有在使用 LoadPackage 显式加载包的情况下,该包中的每个单元(并且仅该包)才会被初始化/最终化。

关于delphi - 为什么包的任何单元终结部分中的代码在关闭时不执行?,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/7566954/

相关文章:

multithreading - Delphi 多线程消息循环

delphi - 用delphi代码验证密码

delphi - DataSnap Rest Server Windows 服务

Delphi XE - TObjectList 排序

android - 火猴 TListView : How to color items at runtime?

R "no visible binding for global variable"在子例程中创建变量并返回环境时的注意事项

package - 在数学包中定义私有(private)函数

json - 设置 package.json 默认值

sql-server - 打开 ADOQuery 时是否可以显示它的记录?

delphi - 将 LogFont 高度转换为以磅为单位的字体大小