Warning: file_get_contents(/data/phpspider/zhask/data//catemap/6/asp.net-mvc-3/4.json): failed to open stream: No such file or directory in /data/phpspider/zhask/libs/function.php on line 167

Warning: Invalid argument supplied for foreach() in /data/phpspider/zhask/libs/tag.function.php on line 1116

Notice: Undefined index: in /data/phpspider/zhask/libs/function.php on line 180

Warning: array_chunk() expects parameter 1 to be array, null given in /data/phpspider/zhask/libs/function.php on line 181
Delphi 为什么在关闭时不执行包的任何单元终结部分中的代码?_Delphi_Package_Delphi 2007_Finalization - Fatal编程技术网

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

Delphi 为什么在关闭时不执行包的任何单元终结部分中的代码?,delphi,package,delphi-2007,finalization,Delphi,Package,Delphi 2007,Finalization,我有一个应用程序,它使用静态链接的运行时包以及使用它们的设计时包。出于某种原因,任何单元终结部分中的代码都没有在运行时运行(我不知道这是何时开始发生的) 关闭Delphi down会显示消息,但不会在我的应用程序关闭时显示。更奇怪的是,如果我在ShowMessage上设置一个断点,它会在那里中断,但不会执行该行。如果终结中有多行,调试器将在第一行停止,不执行它,然后跳到末尾 procedure ProcOne; begin SomeObject.Free; // Debugger does

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

关闭Delphi down会显示消息,但不会在我的应用程序关闭时显示。更奇怪的是,如果我在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)的评论指向了正确的方向,我成功地隔离了这个问题。如果应用程序是使用运行时包构建的,然后动态加载另一个也引用该包和单元的包,那么问题似乎就会出现

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


有人知道这样做的原因和/或解决方法吗?在注意到没有清理外部资源之前,您通常不会注意到正在运行终结。请确保在关机之前为每个动态加载的包调用UnloadPackage。如果您只是调用UnloadLibrary(或者只是依赖操作系统卸载它们),则不会调用该包中的单元以及其他包中的所有单元的终结。初始化和终结是使用引用计数系统完成的,因为面对动态加载的包,无法知道什么单元将在何时初始化。只有在平衡了终结调用和初始化调用之后,最后一个终结调用才会实际执行终结部分中的代码块。同样,只有对初始化部分的第一次调用才会实际执行代码块

初始化/终结使用编译器为给定模块生成的表完成。构建与包链接的exe或dll时,此表包含对实际使用的所有单元的引用,即使是链接包中的单元。请注意,只有实际引用的单元才实际初始化。注意,如果PackageA中有100个单元,而exe只引用其中一个,那么只有该单元及其使用的任何单元将被初始化

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


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

根据Allen Bauer的回答,对于与我处于相同情况的任何人:

我有许多单元使用初始化/终结来自我注册。本着这种精神,我只在重要的时候进行注销:

initialization
  RegisterUnit();
finalization
//In debug mode we track memory leaks so properly deregister
//In release mode the app is shutting down; do not waste time
//freeing memory that's going to be freed anyway
{$IFDEF DEBUG}
  UnloadUnit();
{$ENDIF}
我将其中的一部分移到了包中,但正如问题所描述的,这破坏了核心包的定稿

因此,您必须为所有动态加载的包调用
UnloadPackage()
,否则您将无法在核心中获得正确的终结调用

但是我不能再使用这种优化了。我必须痛苦地在完成时取消注册每个包,因为一旦我卸载包DLL,它在核心中注册的对象将变成僵尸

这感觉像是白费力气。卸货应该很快。我想让所有动态加载的包挂起,直到所有东西都被销毁

您可以对所有动态加载的包调用
FinalizePackage()
!这会使参考计数器均匀,同时使包保持加载状态

如果一个包使用了这种“跳过Denit”技巧,那么它的对象将保持活动状态,直到进程被破坏。(软件包的工作是确保可以调用的任何东西都不会损坏)。如果没有,它将完全去初始化,剩下的是一个惰性DLL


当然,这对您计划真正动态卸载的包不起作用。

您确定
ProcOne
ProcTwo
不会引发任何异常吗?是的,任何异常都不会执行。例如MyInt:=5;如果断点到达时是3,在跨过它之后,它仍然是3。另外,在文本编辑器中手动检查.dpk文件,并查找以下内容:
{$WeakPackageUnit ON}
在应用程序执行期间,是否动态加载其他包?这些包是否也引用相同的问题包/单元?@Allen Bauer:是的,引用包含问题单元的包的其他包是d
initialization
  RegisterUnit();
finalization
//In debug mode we track memory leaks so properly deregister
//In release mode the app is shutting down; do not waste time
//freeing memory that's going to be freed anyway
{$IFDEF DEBUG}
  UnloadUnit();
{$ENDIF}