Могу ли я гарантировать выполнение пользовательского кода завершения ПОСЛЕ уничтожения формы?
У меня есть многопоточное приложение со многими формами, но мне нужно создать экземпляры некоторых классов и вызвать некоторые вещи для инициализации перед созданием форм. Конечно, я должен выполнить соответствующий код завершения.
Вот упрощенный пример файла.dpr:
begin // .dpr project file
LoadDlls;
try
Config := TConfig.Create;
try
Application.Initialize;
Application.Title := 'Foo';
Application.CreateForm(TMainForm, MainForm);
Application.CreateForm(TOtherForm, OtherForm);
//...other forms...
Application.Run;
finally
Config.Free;
end;
finally
UnloadDlls;
end;
end;
Проблема здесь в том, что код внутри finally
блоки исполняются ДО OnDestroy
/ destructor
с моей формы. Это становится ясно, глядя на finalization
раздел Form
единица измерения:
finalization
if Application <> nil then DoneApplication;
А также DoneApplication
звонки Application.DestroyComponents
который эффективно освобождает все Application
Форм.
Итак, формы, созданные с Application.CreateForm
будет уничтожен после любого кода внутри основного begin..end
блок.
То, что я хочу, это то, что после Application.Run
все формы уничтожены, так что их OnDestroy
обработчики событий могут видеть Config
объект и внешние функции, определенные в моих библиотеках. То же самое, если возникло исключение. Но я также хочу иметь обработку исключений стандартного приложения, если Config.Free
или же UnlodDlls
поднять (приложение должно еще существовать).
Обратите внимание, что:
- Я бы предпочел не использовать
finalization
блок (возможно ли в.dpr?) сделать код более понятным и отлаживаемым; - На данный момент я предпочитаю не менять слишком много кода (например, динамически создавать формы)
Я думаю, что самое простое решение заключается в явном Application.DestroyComponents
после Application.Run
, Как вы думаете, есть ли недостатки? Есть ли более элегантное решение?
Спасибо
2 ответа
Самый чистый способ достичь того, чего вы хотите, - это контролировать разрушение форм.
Единственная форма, которая должна принадлежать Application
это ваша основная форма. Это должно быть так, потому что первая форма, созданная путем вызова Application.CreateForm
обозначается как основная форма. Итак, мой совет, что вы должны сделать один звонок, и один звонок только Application.CreateForm
, чтобы создать основную форму. Для всех остальных форм создайте их, вызывая их конструкторы. Пусть другие формы будут принадлежать основной форме. Когда пришло время завершить работу, уничтожьте основную форму и дайте ей принять все принадлежащие ей формы.
Вы можете написать свой код.dpr следующим образом:
begin
LoadDlls;
try
Config := TConfig.Create;
try
Application.Initialize;
Application.Title := 'Foo';
Application.CreateForm(TMainForm, MainForm);
try
OtherForm := TOtherForm.Create(MainForm);
YetAnotherForm := TYetAnotherForm.Create(MainForm);
Application.Run;
finally
FreeAndNil(MainForm);
// will destroy the other forms since they are owned by the main form
end;
finally
Config.Free;
end;
finally
UnloadDlls;
end;
end;
Еще одно замечание: возможно, вам не нужно выгружать библиотеки DLL. Поскольку это явно исполняемый файл, система все равно их выгружает. Зачем тебе это нужно?
Другой вариант - не позволяйте вашим формам неявно ссылаться на глобальный конфиг.
Сделайте зависимость явной, дав каждой форме свою ссылку на IConfig
интерфейс.
Когда RefCount ссылочного экземпляра падает до нуля (после того, как все формы, которые его используют, были уничтожены), он может самоуничтожиться.
Явная зависимость вашей формы (и других объектов) от конфигурации даст другие преимущества.
- Это будет намного проще для тестирования.
- Формы, которые не нуждаются в IConfig, не будут иметь зависимости и не будут заботиться ни о чем другом.
- Поэтому эти формы будут легко (и очевидно) перемещаться в другие приложения с немного другой структурой.