Вопрос управления уничтожением объектов в среде Delphi, особенно когда речь идет о библиотеках, является сложной и важной задачей. Проблема заключается в том, что при использовании объектов из DLL, их уничтожение происходит в момент, когда Delphi эквивалентно событию DLL_PROCESS_DETACH. Это может быть критично, так как в этот момент не рекомендуется выполнять определенные операции, например, вызовы некоторых функций WinAPI.
Описание проблемы
Рассмотрим ситуацию, когда в вашем проекте на Delphi присутствует едиственный глобальный объект, который создается при первом обращении к нему и уничтожается во время завершения работы процесса. Если такой объект используется из DLL, то его уничтожение будет происходить в момент, когда выполняется финализация в Delphi, что эквивалентно DLL_PROCESS_DETACH. В этом контексте возникают ограничения на выполнение определенных операций, например, вызов CoCreateInstance.
Пример кода
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;
...
Адаптация кода Embarcadero: Можно адаптировать подход, используемый Embarcadero, и изменить код так, чтобы он явно приводил к утечке памяти, если объект используется в библиотеке.
if IsLibrary then
Pointer(InternalThemeParkServices) := nil; // деактивация подсчета ссылок и намеренная утечка
Подтвержденное решение
Для контроля за вызовом во время DLL_PROCESS_DETACH после вызова ExitProcess, можно использовать следующий подход:
var
SaveDllProcEx: TDllProcEx;
procedure DllMainEx(Reason: Integer; Reserved: Integer);
begin
if (Reason = DLL_PROCESS_DETACH) and (Reserved = 0) then
// Основное приложение все еще работает, выполняем очистку.
if Assigned(SaveDllProcEx) then
SaveDllProcEx(Reason, Reserved);
end;
initialization
if IsLibrary then begin
SaveDllProcEx := DllProcEx;
DllProcEx := @DllMainEx;
end;
Этот метод позволяет определить, была ли уже вызвана ExitProcess, и соответствующим образом выполнить необходимые действия.
Альтернативный ответ
Если необходимо выполнить действия по завершению работы до DLL_PROCESS_DETACH, следует экспортировать функцию из DLL, которая будет выполнять финализацию. Клиенты DLL должны вызывать эту функцию перед ее разгрузкой. Это похоже на паттерн CoInitialize/CoUninitialize.
Заключение
Управление уничтожением объектов в Delphi требует тщательного планирования и учета особенностей работы с библиотеками. Разработчикам необходимо быть осведомленными о возможных проблемах и иметь в арсенале различные решения для их устранения.
Управление уничтожением объектов в Delphi, особенно при работе с библиотеками, связано с проблемами, связанными с временем их автоматического освобождения, что может приводить к ограничениям на выполнение определенных операций, таких как вызов некоторых
Комментарии и вопросы
Получайте свежие новости и обновления по Object Pascal, Delphi и Lazarus прямо в свой смартфон. Подпишитесь на наш Telegram-канал delphi_kansoftware и будьте в курсе последних тенденций в разработке под Linux, Windows, Android и iOS