Почему код в каком-либо разделе финализации пакета не выполняется при завершении работы? - PullRequest
4 голосов
/ 27 сентября 2011

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

finalization
  ShowMessage('Goodbye');
end.

При закрытии Delphi отображается сообщение, но не когда приложение закрываетсявниз.Это становится более странным, если я ставлю точку останова на 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.

Если я включаю модуль в приложение, которое не использует пакеты, все выполняется правильно.

У кого-нибудь есть идеи, что может быть причиной этого?

РЕДАКТИРОВАТЬ:

Благодаря комментарию Аллена Бауэра, указывающего в правильном направлении, мне удалось выделить проблему.Кажется, проблема возникает, если приложение создается с пакетом времени выполнения, а затем динамически загружает другой пакет, который также ссылается на этот пакет и модуль.

Я создал тестовый проект, который демонстрирует проблему: TestFinalization

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

1 Ответ

8 голосов
/ 28 сентября 2011

Убедитесь, что вы вызываете UnloadPackage для каждого динамически загружаемого пакета перед выключением.Если вы просто вызываете UnloadLibrary (или просто полагаетесь на ОС, чтобы их выгружать), то финализация для этого модуля в этом пакете и всех модулей из других пакетов не вызывается.Инициализация и финализация выполняются с использованием системы подсчета ссылок, потому что, несмотря на наличие загруженных с помощью dyanmically пакетов, нет способа узнать, какие единицы будут инициализированы и когда.Только после того, как вы уравняли вызовы завершения с вызовами инициализации, последний вызов завершения фактически выполнит блок кода в разделе финализации.Аналогично, только первый вызов в секции инициализации фактически выполнит блок кода.

Инициализация / финализация выполняются с использованием таблицы, сгенерированной компилятором для данного модуля.Когда вы создаете exe или dll, связанные с пакетами, эта таблица содержит ссылки на все модули, которые фактически используются, даже те из связанных пакетов.Обратите внимание, что только единицы, на которые фактически ссылаются, фактически инициализированы.Итак, если у вас есть 100 модулей в PackageA и exe-файл ссылается только на один из них, то будет инициализирован только этот модуль и все используемые им модули.

Для динамически загружаемых пакетов действительно нет способа узнать, чтомодули будут фактически использоваться, поэтому компилятор генерирует таблицу init / finit, как если бы каждый модуль был инициализирован.Эта таблица не обрабатывается при загрузке пакета во время вызова LoadLibrary, а обрабатывается путем вызова специального экспорта, называемого Initialize ().Функция LoadPackage гарантирует, что эта функция вызывается.Эта таблица только гарантирует, что все единицы в загрузочном пакете инициализированы.Инициализируются только единицы , фактически затронутые в любом другом пакете, аналогично случаю exe / dll, который я упоминал выше.UnloadPackge делает обратное и вызывает специальный экспорт Finalize () перед вызовом UnloadLibrary ().

Наконец, если вы внесли изменения в списки использования любых упакованных модулей и только перестроили пакет, вы можете запутатьсяслучаи, когда инициализации / финализации могут не вызываться, даже если ваши юниты в данном пакете должным образом «используют» друг друга.Это связано с тем, что init / finit управляется загрузочным модулем , а не изнутри.Только в случае, когда пакет явно загружен с использованием LoadPackage, каждый модуль в этом пакете (и только этот пакет) будет инициализирован / завершен.

...