The procedure for final configuration of the module for the application compiled with runtime packages?

I need to execute my code after finalizing the SysUtils block.

I put my code in a separate block and first included it in the uses dpr sentence of the file, for example:

project Project1; uses MyUnit, // <- my separate unit SysUtils, Classes, SomeOtherUnits; procedure Test; begin // end; begin SetProc(Test); end. 

MyUnit looks like this:

 unit MyUnit; interface procedure SetProc(AProc: TProcedure); implementation var Test: TProcedure; procedure SetProc(AProc: TProcedure); begin Test := AProc; end; initialization finalization Test; end. 

Please note that MyUnit does not have any uses.

This is a regular Windows exe, without a console, without forms, and compiled with default runtime packages. MyUnit is not part of any package (but I also tried to use it from the package).

I expect the MyUnit completion section to be executed after the SysUtils completion section. This is what Delphi helps me.

However, this is not always the case.

I have 2 test applications that differ slightly in code in the test procedure / dpr file and the units listed in the applications. However, MyUnit is listed first in all cases.

One application starts as expected: Halt0 β†’ FinalizeUnits β†’ ... other units ... β†’ Finalize SysUtils β†’ Finalize MyUnit β†’ ... other units ...

But the second is not. The finalization of MyUnit is called before the completion of SysUtils. The actual call chain is as follows: Halt0 β†’ FinalizeUnits β†’ ... other units ... β†’ Finalize SysUtils (skipped) β†’ Finalize MyUnit β†’ ... other units ... β†’ Final SysUtils (in progress)

Both projects have very similar settings. I tried a lot to remove / minimize their differences, but I still see no reason for this behavior.

I tried to debug this and found out that: it seems that every unit has some kind of reference count. And it looks like InitTable contains multiple references to the same unit. When the SysUtils completion section is called for the first time, it changes the reference count and does nothing. Then MyUnit is finalized. And then SysUtils is called again, but this time ref-count reaches zero, and the completion section ends:

 Finalization: // SysUtils' finalization 5003B3F0 55 push ebp // here and below is some form of stub 5003B3F1 8BEC mov ebp,esp 5003B3F3 33C0 xor eax,eax 5003B3F5 55 push ebp 5003B3F6 688EB50350 push $5003b58e 5003B3FB 64FF30 push dword ptr fs:[eax] 5003B3FE 648920 mov fs:[eax],esp 5003B401 FF05DCAD1150 inc dword ptr [$5011addc] // here: some sort of reference counter 5003B407 0F8573010000 jnz $5003b580 // <- this jump skips execution of finalization for first call 5003B40D B8CC4D0350 mov eax,$50034dcc // here and below is actual SysUtils' finalization section ... 

Can anyone highlight this issue? Am I missing something?

+6
delphi delphi-2010 packages finalization
source share
4 answers

I could find the reason, and now I feel a little stupid :)

My second test application has a static link to a DLL that was compiled with RTL.bpl (it is empty except for references to SysUtils and has 1 simple procedure). So, since the DLL is statically linked, it is initialized before any exe code has a chance of running.

What is it:

DLL System β†’ DLL SysUtils β†’ exe System (skipped) β†’ MyUnit β†’ exe SysUtils (skipped) β†’ etc

Finalization is performed in the reverse order, which leads to the execution of MyUnit before SysUtils.

Solution: you must first include MyUnit in all projects.

(oh, how I want the time machine to come back in time and force someone to add the OnBeforeMMShutdown: D event)

+1
source share

Units are completed in the reverse order of initialization. The initialization order is determined by non-cyclic (i.e., it never falls into an already visited block). Postoperative block traversal uses the graph, starting with the main use (in a program or library). SysInit is usually the first module to be initialized, and then the system.

Dynamic package loading complicates the situation, because the main EXE or DLL receives the task of initializing the units used by the main image. Therefore, when a package is dynamically loaded, it will run what, in its opinion, should be the order of initialization, but units that have already been initialized will be skipped; when a package is dynamically unloaded, it happens in the reverse order.

General rules:

  • lower level things must be initialized before higher level things.
  • finalization should be in reverse order of initialization

These rules almost always make sense. The initialization of higher-level units often depends on the services provided by lower-level units. For example, without SysUtils, Delphi lacks exception support. Finalizing in the reverse order makes sense for the same reason: high-level finalization depends on the services provided by lower-level blocks, so they must be completed before finalizing lower-level blocks.

All that has been said regarding your problem, it looks like there might be a mistake in the compiler or RTL if you tell the truth: the main EXE uses MyUnit in the first place, and MyUnit uses no other units in its interface or implementation, and there is no funny thing things going with dynamically loaded packages. All I can offer is to keep the project breeding with odd behavior until you have a minimal playback example; at this point, it should be clear what exactly is causing the problem.

+10
source share

You have not missed anything. This is exactly what is happening.

When your program downloads a package, it will initialize all units used in this package. When it unloads the packet, it must complete all blocks. But finalization often involves releasing variables. Remember that double-frees are Bad Thing, especially if they happen during finalization, when some exception handling functions can be unloaded, which makes debugging difficult. Thus, he puts a control counter on the initialization of the block so that they are not finalized until everything that used them has been executed with them.

Is there any special reason why your MyUnit should exit after SysUtils?

0
source share

I'm not sure, but is there still no old old ExitProc variable known as Turbo / BorlandPascal? If so, this may solve your problem.

0
source share

All Articles