4

我在 Delphi 中有一个单元(可以选择)提供单个全局对象:

var
   InternalThemeParkServices: TThemeParkServices;

function ThemeParkServices: TThemeParkServices;
begin
   if InternalThemeParkServices= nil then
      InternalThemeParkServices := TThemeParkServices.Create();
   Result := InternalThemeParkServices ;
end;

...

initialization
finalization
   FreeAndNil(InternalThemeServices);
end.

我在进程关闭期间摧毁了自己。

注意:另一个代码变体是:

var
   InternalThemeParkServices: IThemeParkServices;

function ThemeParkServices: TThemeParkServices;
begin
   if InternalThemeParkServices= nil then
      InternalThemeParkServices := TThemeParkServices.Create();
   Result := InternalThemeParkServices ;
end;

当程序关闭期间接口变量的引用计数变为零时,接口变量被隐式销毁

当我的对象不再使用时(即在其期间destructor),我调用各种 WinAPI 函数。

问题是,如果有人从 DLL 中使用我的类(我无法控制的东西),那么在以下过程中会调用任何东西:

finalization

是 的德尔福道德等价物DLL_PROCESS_DETACH进程终止时(例如) ,我不应该做各种事情。 DLL_PROCESS_DETACH CoCreateInstance

我知道 Embarcadero 使用:

initialization 
   if not IsLibrary then
   begin
      ...

我也许可以适应,改变我的代码:

var
   InternalThemeParkServices: IThemeParkServices;

(使用隐式清理),以:

var
   InternalThemeParkServices: IThemeParkServices;
...
finalization
   if IsLibrary then
      Pointer(InternalThemeParkServices) := nil; //defeat reference counting and let the object leak
end;

让它泄漏。

但这是最好的解决方案吗?我认为这意味着如果运行我的代码的 dll 被卸载(但不是在进程关闭期间),我将泄漏内存。如果 dll 被附加和分离,我每次都会泄漏。

真正想要的是 Delphi在/之前finalization运行它的块。这可能吗? ExitProcessDllMain(DLL_PROCESS_DETACH)

奖金喋喋不休

@pa破译了 Delphi 应用程序关闭方案

关机的层次结构如下

  Application.Terminate()
    performs some unidentified housekeeping of application
    calls Halt()

  Halt()
    calls ExitProc if set
    alerts the user in case of runtime error
    get rid of PackageLoad call contexts that might be pending
    finalize all units
    clear all exception handlers
    call ExitprocessProc if set
    and finally, call ExitProcess() from 'kernel32.dll'

  ExitProcess() 
    unloads all DLLs
    uses TerminateProcess() to kill the process

在调用卸载 DLL ExitProcess- 因为 Windows 是这样做的。

4

2 回答 2

6

要判断是否在调用 ExitProcess 后的 DLL_PROCESS_DETACH 期间调用了您,您可以为您的库编写初始化代码,以便在FreeLibrary从主程序调用时执行您的代码。ExitProcess如果已经调用了“lpReserved”参数,则为“1” ,否则为“0”:

..
var
  SaveDllProcEx: TDllProcEx;

procedure DllMainEx(Reason: Integer; Reserved: Integer);
begin
  if (Reason = DLL_PROCESS_DETACH) and (Reserved = 0) then
    // Main app is still running, cleanup.

  if Assigned(SaveDllProcEx) then
    SaveDllProcEx(Reason, Reserved);
end;

initialization

if IsLibrary then begin
  SaveDllProcEx := DllProcEx;
  DllProcEx := @DllMainEx;
end;


DllMain 入口点

lpReserved 参数指示是否由于 FreeLibrary 调用、加载失败或进程终止而卸载 DLL。

于 2012-04-27T17:27:55.143 回答
2

真正想要的是 Delphifinalization之前运行它的块DllMain(DLL_PROCESS_DETACH)。这可能吗?

不,这是不可能的。

如果您需要执行在此期间无法完成的关闭操作,DllMain(DLL_PROCESS_DETACH)则需要将导出的函数添加到执行最终确定的 DLL 中。然后,您应该要求 DLL 的客户端在卸载 DLL 之前调用此函数。CoInitialize这与/的模式相同CoUninitialize

于 2012-04-27T15:46:29.120 回答