Завершение единицы заказывает для приложения, скомпилированного с пакетами во время выполнения?

Я должен выполнить свой код после завершения единицы SysUtils.

Я поместил свой код в отдельную единицу и включал его сначала в пункт использования dpr-файла, как это:

project Project1;

uses
  MyUnit,    // <- my separate unit
  SysUtils,
  Classes,
  SomeOtherUnits;

procedure Test;
begin
  //
end;

begin
  SetProc(Test);
end.

MyUnit похож на это:

unit MyUnit;

interface

procedure SetProc(AProc: TProcedure);

implementation

var
  Test: TProcedure;

procedure SetProc(AProc: TProcedure);
begin
  Test := AProc;
end;

initialization

finalization
  Test;
end.

Обратите внимание, что MyUnit не имеет никакого использования.

Это - обычный Windows exe, никакая консоль, без форм и скомпилированный с пакетами во время выполнения по умолчанию. MyUnit не является частью никакого пакета (но я попытался использовать его от пакета также).

Я ожидаю, что раздел завершения MyUnit будет выполняться после раздела завершения SysUtils. Это - то, что справка Delphi говорит мне.

Однако это не всегда имеет место.

У меня есть 2 тестовых приложения, который отличается немного кодом в Тестовой задаче/dpr-file и единицах, перечисленных в использовании. MyUnit, однако, перечислен сначала во всех случаях.

Одно приложение запущено как ожидалось: Halt0-> FinalizeUnits->... другие единицы...-> завершение SysUtils-> завершение MyUnit->... другие единицы...

Но второе не. Завершение MyUnit вызывается перед завершением SysUtils. Цепочка фактического вызова похожа на это: Halt0-> FinalizeUnits->... другие единицы...-> (пропущенное) завершение SysUtils-> завершение MyUnit->... другие единицы...-> (выполняемое) завершение SysUtils

Оба проекта имеют очень похожие настройки. Я попробовал много для удаления/минимизирования их различий, но я все еще не вижу оснований для этого поведения.

Я попытался отладить это и узнал что: кажется, что каждая единица имеет некоторый подсчет ссылок. И кажется, что InitTable содержит, умножают ссылки на ту же единицу. Когда раздел завершения SysUtils называют в первый раз, когда - он изменяется, ссылка противостоят и ничего не делают. Затем завершение MyUnit выполняется. И затем SysUtils называют снова, но на этот раз касательно количества достигает нуля, и раздел завершения выполняется:

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
...

Может кто-либо действительно ли уничтожить может осветить по этой проблеме? Я пропускаю что-то?

6
задан Alex 13 April 2010 в 10:45
поделиться

4 ответа

Я смог найти причину и теперь чувствую себя немного глупо :)

У моего второго тестового приложения есть статическая ссылка на DLL, которая была скомпилирована с помощью RTL.bpl (она пуста, за исключением ссылок на SysUtils и имеет 1 простую процедуру). Итак, поскольку DLL статически связана, она инициализируется до того, как любой код из exe сможет запустить.

Вот и все:

DLL System -> DLL SysUtils -> EXE System (пропущено) -> MyUnit - > EXE SysUtils (пропущено) -> и т. д.

Завершение выполняется в обратном порядке, что приводит к выполнению MyUnit перед SysUtils.

Решение: сначала необходимо включить MyUnit во все проекты.

(о, как я хочу иметь машину времени, чтобы путешествовать во времени и заставлять кого-нибудь добавить событие OnBeforeMMShutdown: D)

1
ответ дан 9 December 2019 в 22:31
поделиться

Модули финализируются в порядке, обратном инициализации. Порядок инициализации определяется нециклическим (т. Е. Никогда не спускается в уже посещенный блок) пост-порядковым обходом блока с использованием графа, начиная с предложения main uses (в программе или библиотеке). SysInit обычно является первым инициализируемым модулем, за которым следует System.

Динамическая загрузка пакетов усложняет ситуацию, потому что основной EXE или DLL может указать порядок инициализации модулей, используемых основным образом. Таким образом, когда пакет загружается динамически, он будет выполнять то, что, по его мнению, должно быть порядком инициализации, но уже инициализированные блоки будут пропущены; когда пакет динамически выгружается, это происходит в обратном порядке.

Общие правила:

  • объекты нижнего уровня должны быть инициализированы до того, как объекты более высокого уровня
  • завершение должно быть в порядке, обратном инициализации

Эти правила почти всегда имеют смысл. Инициализация модулей более высокого уровня часто зависит от услуг, предоставляемых модулями более низкого уровня. Например, без SysUtils в Delphi нет поддержки исключений. Завершение в обратном порядке имеет смысл по той же причине: завершение на высоком уровне зависит от услуг, предоставляемых подразделениями более низкого уровня, поэтому они должны выполняться до завершения выполнения модулей более низкого уровня.

Все, что было сказано относительно вашей проблемы, похоже, может быть ошибка где-то в компиляторе или RTL, если то, что вы говорите, правда: что основной EXE сначала использует MyUnit , и MyUnit не использует никаких других модулей в своем интерфейсе или реализации, и с динамически загружаемыми пакетами не происходит никаких забавных дел. Все, что я могу предложить, - это продолжать сокращать проект со странным поведением до тех пор, пока у вас не будет минимального воспроизводимого образца; в этот момент должно быть ясно, что именно вызывает проблему.

10
ответ дан 9 December 2019 в 22:31
поделиться

Вы ничего не упускаете. Именно это и происходит.

Когда ваша программа загружает пакет, она инициализирует все модули, используемые в этом пакете. Когда он выгружает пакет, он должен завершить работу над всеми модулями. Но финализация часто включает освобождение переменных. Помните, что двойное освобождение - это плохо, особенно если они происходят во время финализации, когда некоторые функции обработки исключений могли быть выгружены, что очень затрудняет отладку. Таким образом, он устанавливает счетчик ссылок на инициализации модулей, чтобы они не были завершены до тех пор, пока все , которые их использовали, не будет выполнено с ними.

Есть ли какая-то конкретная причина, по которой ваш MyUnit необходимо завершить после SysUtils?

0
ответ дан 9 December 2019 в 22:31
поделиться

Я не уверен, но разве нет старой доброй глобальной переменной ExitProc, известной как Turbo / BorlandPascal? Если да, это может решить вашу проблему.

0
ответ дан 9 December 2019 в 22:31
поделиться
Другие вопросы по тегам:

Похожие вопросы: