Delphi单元初始化并不总是被调用

11

我在.bpl文件中有一个单元,而我需要为我编写的新函数创建一个字符串列表。我希望这个字符串列表在应用程序的生命周期内持续存在,以便每次调用都可以基于前一次的结果进行构建。

因此,它在单元内被全局声明,并在初始化部分中进行了初始化,如下所示:

var
  ProductLookup : TStrings;  
...

function foo : boolean;
begin
  result := (ProductLookup.IndexOfName('bar') >=0); //blow up here. It's nil. Why?
end;
....

initialization
  ProductLookup := TStringList.Create;  // This should get run, but doesn't.

finalization
  FreeAndNil(ProductLookup);

end.

在进行单元测试时,一切都很正常。但是当从主应用程序运行时,我遇到了访问冲突,因为字符串列表是nil。所以现在我正在尝试检查foo函数中的nil并在需要时创建。但是我不知道为什么初始化对我没有起作用。我在Initialization中放了一个调试消息,但在将其加载为BPL时它不会运行,但如果直接编译到我的dUnit exe中,则会运行。有任何想法吗? Delphi2005。


4个回答

28
Darian 提醒我之前已经回答过这个问题:(链接)

如果操作系统在加载相关EXE时作为BPL的一部分加载,那么并不会调用所有初始化部分。只会调用程序中其他东西显式使用的单元中的部分。

如果初始化部分中的代码注册了一个类,然后您只间接地引用该类,比如通过在列表中按名称查找,那么单元的初始化部分可能不会被调用。将该单元添加到程序中任何“uses”子句中应该可以解决该问题。

要解决这个问题,可以通过调用 SysUtils 单元中的 InitializePackage 函数自己初始化包中的单元。它需要一个模块句柄,您可以通过调用 GetModuleHandle API 函数来获取它。该函数仅调用尚未初始化的单元的初始化部分。这是我的观察结果。

如果调用了InitializePackage,则还应该调用FinalizePackage。当您的包卸载时,将为所有已自动初始化的单元调用终结部分。

如果操作系统不会自动加载您的包,则使用LoadPackage函数加载它。它将为您初始化包中的所有单元,因此您无需自己调用InitializePackage。同样,UnloadPackage将为您完成所有终结工作。


3

请注意,QualityCentral现已关闭,因此您无法再访问qc.embarcadero.com链接。如果您需要访问旧的QC数据,请查看QCScraper - Remy Lebeau

1
在某些情况下,BPL 中的每个单元都不一定会被初始化。如果我要猜的话,我会说这个 BPL 是在加载时与您的程序链接的,而不是后来动态加载的?尝试将您正在使用的单元名称放入 DPR 中程序的 uses 列表中。那应该就可以解决了。

这里的复杂之处在于大约有100个不同的EXE和.BPL使用了这个特定的.BPL,而我不被允许重新构建它们。因此,明智的方法并不总是奏效。虽然这通常是一个好技巧,但还是给你加1分。 - Chris Thornton

-1
你是如何加载bpl的?你是让Delphi自动加载还是手动加载bpl?如果你是手动加载bpl,那么你是将其作为“直接”dll加载还是使用LoadPackage将其作为delphi包加载?我认为要么让vcl通过requires处理加载它,要么使用LoadPackage才能使初始化部分由vcl运行...

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