为什么在关闭时不执行包的任何单元完成部分中的代码? [英] Why code in any unit finalization section of a package is not executed at shut down?

查看:72
本文介绍了为什么在关闭时不执行包的任何单元完成部分中的代码?的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

我有一个使用静态链接的运行时包以及使用它们的设计时包的应用程序。由于某些原因,任何单元最终确定部分中的代码都不会在运行时运行(我不知道何时开始发生)。

I have an application that uses statically-linked runtime packages as well as designtime packages that use them. For some reason the code in any unit finalization section is not being run at runtime (I can't tell when this started happening).

finalization
  ShowMessage('Goodbye');
end.

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

Shutting Delphi down shows the message, but not when my application shuts down. It gets weirder in that if I put a breakpoint on ShowMessage, it breaks there but does not execute the line. If there are multiple lines in the finalization, the debugger stops on the first line, does not execute it and then jumps to the end.

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。

The call stack on ProcOne breakpoint shows @Halt0 => FinalizeUnits => MyPackage.MyUnit.Finalization.

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

If I include the unit in an application that doesn't use packages, everything executes properly.

有人吗?有一个想法可能是什么原因造成的?

Does anyone have an idea what could be causing this?

编辑:

感谢Allen Bauer在右边的评论方向,我设法隔离了问题。如果应用程序是使用运行时程序包构建的,然后动态加载另一个也引用该程序包和单元的程序包,似乎会出现问题。

Thanks to Allen Bauer's comment pointing in the right direction, I have managed to isolate the problem. It seems the problem arises if an application is built with a runtime package, then dynamically loads another package that also references that package and unit.

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

I have created a test project that demonstrates the problem: TestFinalization

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

Does anyone know the reason for this and/or a workaround? You normally might not notice that your finalization is not being run until you notice that external resources are not being cleaned up.

推荐答案

制作请确保在关机前为每个动态加载的软件包调用UnloadPackage。如果您只是调用UnloadLibrary(或仅依靠操作系统来卸载它们),则不会调用该包中的单元和其他包中的所有单元的终结处理。初始化和完成是使用参考计数系统完成的,因为面对动态加载的包,无法知道什么单元将被初始化以及何时进行初始化。只有在完成了最终调用与初始化调用之间的平衡之后,最后一个最终调用才会真正执行完成部分中的代码块。同样,只有对初始化部分的第一次调用才会实际执行代码块。

Make sure you're calling UnloadPackage for each dynamically loaded package before shutdown. If you're simply calling UnloadLibrary (or simply relying on the OS to unload them), then the finalizations for that the units in that package and all the units from other packages aren't being called. Initializations and finalizations are done using a reference counting system because in the face of dyanmically loaded packages, there is no way to know what units will be initialized and when. Only when you've balanced the finalization calls with the initialization calls will the last finalization call actually execute the code block in the finalization section. Likewise only the first call to the initialization section will actually execute the code block.

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

Initializations/finalizations are done using a compiler-generated table for a given module. When you build an exe or dll linked with packages, this table contains references to all the units that are actually used, even those from the linked packages. Note that only the units actually referenced are actually initialized. IOW, if you have 100 units in PackageA and the exe only references one of them, then only that unit and any units it uses will be initialized.

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

For dynamically loaded packages, there is really no way to know what units will actually be used, so the compiler generates the init/finit table as if every unit were initialized. This table is not processed upon loading of the package during the call to LoadLibrary, but rather is handled by calling a special export called Initialize(). The LoadPackage function ensures that this function is called. This table only ensures that all the units in the loading package are initialized. Only the units actually touched in any other package are initialized, similar to the exe/dll case I mentioned above. UnloadPackge does the reverse, and calls special export Finalize() before calling UnloadLibrary().

最后,如果您更改了使用任何打包单位的列表,并且仅重建软件包时,您可能会陷入混乱的情况,即使给定软件包中的各个单元正确地相互使用,也可能无法调用初始化/完成。这是因为init / finit由 loading模块控制,而不是由其内部控制。仅在使用LoadPackage显式加载软件包的情况下,该软件包中的每个单元(且仅适用于该软件包)都将被初始化/完成。

Finally, if you've made changes to uses lists of any packaged units and only rebuild the package, you can run into confusing cases where initializations/finalizations may not get called even though your units within a given package properly "use" each other. This is because the init/finit is controlled by the loading module and not from within itself. Only in the case where a package is explicitly loaded using LoadPackage will every unit in that package (and that package only) be initialized/finalized.

这篇关于为什么在关闭时不执行包的任何单元完成部分中的代码?的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆