Форум: "Основная";
Текущий архив: 2002.10.03;
Скачать: [xml.tar.bz2];
ВнизВыгрузка DLL с модально поднятой формой Найти похожие ветки
← →
Юрий Зотов (2002-09-23 11:12) [0]Программа однопоточная, с run-time пакетами (включая пакеты VCL). Есть BPL c объектами O1, O2 и формой F1. Загружена DLL с формой F2 - наследником F1. Метод ShowModal в F1 и в F2 не перекрыт (поскольку пока не ясно, нужно ли это делать и что в нем следует писать).
И есть цепочка вызовов:
O1.Method1-O2.Method2-F2.ShowModal
после которой вся эта цепочка, естественно, оказывается в стеке вызовов, а программа крутится во внутреннем цикле выборки сообщений ShowModal.
В этот момент иногда необходимо выгрузить DLL с F2. Само по себе это не сложно и после соответствующих мер доступность ранее показанных форм нормально восстанавливается, а программа нормально работает (правда, вызов ShowModal не завершен и его результат не получен, но в данном случае это неважно). Однако при завершении программы она делает попытку выйти из стека вызовов, а DLL уже выгружена. С соответствующими Access Violation и прочими чудесами.
Вопрос - есть ли у кого какие-то мысли по этому скорбному поводу? Разумеется, кроме банального гашения в try-except и мыслей типа "а на фига это нужно".
Дополнительная информация.
1. Допускаются любые изменения в BPL, но изменения в DLL весьма нежелательны (потому что таких DLL - порядка полусотни и перепахивать их все как-то очень не хочется).
2. Замена ShowModal на Show решает проблему в корне, но при обычной работе (без выгрузки DLL) программе нужен результат вызова. То есть, функция, а не процедура.
← →
Юрий Зотов (2002-09-23 11:17) [1]P.S.
> То есть, функция, а не процедура.
Или эмуляция вызова функции.
← →
kerk (2002-09-23 11:24) [2]Обычно при выгрузки ДЛЛ должны уничтожатся все связанные с ней обекты, тогда точно не будет Access Violation.
← →
Старый Паскалист (2002-09-23 11:49) [3]На сколько я понимаю, АВ возникает при попытке вызвать
оконную ф-ю - а она к этому времени уже выгружена из памяти.
Поэтому нужно насильно закрыть форму при выгрузке ДЛЛ.
Просто уничтожить её в деинициализационном коде ДЛЛ - она при уничтожении (при Close, точнее) поставит ModalResult := mrCancel и выйдет из ShowModal.
← →
Старый Паскалист (2002-09-23 12:09) [4]Прошу прощения, не совсем врубился в вопрос.
Но всё равно при выгрузке ДЛЛ (точнее, при уничтожении формы) ShowModal должно возвращать mrCancel и её результат должен быть получен...
← →
Polevi (2002-09-23 12:31) [5]да, скорее всего придется Dll переписывать или хранить хендлы открытых модально окон
← →
Polevi (2002-09-23 12:33) [6]то есть указатели
← →
Старый Паскалист (2002-09-23 13:38) [7]ShowModal на уничтожение формы плохо отреагирует.
До самого выхода из ShowModal форма должна существовать.
(А освобождаете ДЛЛ Вы, видимо, где-то вннутри
ц.об.сообщ. ShowModal, а ShowModal обращается к полям формы
уже после выхода из цикла).
Может быть, поможет что-то вроде следующего трюка
(это сырая идея, как задержать освобождение ДЛЛ до
выхода из ShowModal за счёт ретрансляции сообщений)
TMainForm.WMFreeLib(var Msg: TMessage); message WM_FREELIB;
begin
//wParam - флаг
//lParam - HDll
if Msg.wParam = 0 then
PostMessage(Handle, WMFreeLib, 1, Msg.lParam)
else
if Msg.wParam = 1 then
FreeLibrary(Msg.lParam);
end;
procedure FreeLib(FormThatShownModal: TForm {возможно - глоб. перем.}, HDll: HMODULE);
begin
FormThatShownModal.ModalResult := mrOk;
PostMessage(MainForm.Handle, WM_FREELIB, 0, HDll);
// Сначала MainForm получит WM_FREELIB с wParam = 0
// Затем закроется FormThatShownModal
// Затем MainForm получит WM_FREELIB с wParam = 1 и освободит библиотеку
end;
← →
Shaman_Naydak (2002-09-23 13:52) [8]У меня есть такая мысль..
Надо сделать выгрузку через систему сообщений виндов:
То есть, когда нужно выгружать DLL, ты просто кладешь в очередь сообщений (через POST) свое сообщение WU_DLL_Deatach (в нем передаешь handle dll, нуждающейся в выгрузке)
Таперича в BPL (в той, где описывается форма F1, делаешь обработчик message, и если DLL handle = твоей Dll handle, то ModalResult:=mrCancel)
Кроме того, в главной программе тоже ставишь обработчик (на Application, в котором после того как отработают все (так сказать, событие пройдется по цепочке вниз), выгружаешь DLL
Я надеюсь, я сумел донести мысль
← →
Shaman_Naydak (2002-09-23 13:54) [9]> Старый Паскалист
Ну ежкин кот, пока писал свою мессагу, оказалось меня опередили :)
← →
Юрий Зотов (2002-09-23 14:10) [10]Перед выгрузкой форма, естественно, уничтожается. Вот более подробная структура кода
O1.Method1-O2.Method2-F2.ShowModal-<Далее>
Стантартный ShowModal:
- префикс
- цикл выборки сообщений (из которого и вызывается это <Далее>)
- постфикс (здесь ставим BreakPoint1)
Код выгрузки (в этом <Далее>, точнее в некоей форме F3 из BPL):
F2.ModalResult := mrCancel;
// Это НЕ приводит к мгновенному выходу из цикла,
// поскольку возврата в ShowModal пока еще нет.
F2.Release; // Запрос на уничтожение F2.
PostMessage(F3.Handle, My_Msg, ...); // Запрос на выгрузку DLL
Application.ProcessMessages; // Теперь F2 убита, DLL выгружена
F3.MyMsg:
-префикс
-FreeLibrary(...)
-постфикс
ОК, все это работает. Ставим BreakPoint2 в деструкторе F2 и запускаем программу. Выгружаем DLL - и приходим BreakPoint2, МИНУЯ BreakPoint1. В стеке вызовов видим:
O1.Method1-O2.Method2-TForm.ShowModal-<что-то еще>
То есть, возврат в ShowModal, выход из цикла и приход на BreakPoint1 при уничтожении F2 НЕ происходит (мы возвращаемся в это <что-то еще> и в нем программа работает дальше. А вот потом, при ее завершении приходим на BreakPoint1 (идет обратный проход по стеку) и, поскольку в постфиксе ShowModal есть обращения к полям формы (уже убитой), получаем AV.
Нужен возврат в ShowModal при уничтожении формы. Если бы вся программа была на Ассемблере можно было бы манипулировать подменой адресов возврата. Не подарок, но можно. А тут-то львиная доля кода - это VCL.
> Polevi
Список форм уже имеется. Он используется для разруливания доступностью форм при убиении модальной (ну и еще кое для чего, не относящегося к теме).
← →
Старый Паскалист (2002-09-23 14:25) [11]Я то имел ввиду, что у F2 Action = caFree, поэтому
я сразу делал ModalResult := mrOk.
Очередь потока тогда будет выглядеть так:
MainForm, WM_FREELIB, 0, hDll
...
F2, CM_RELEASE, ...
...
MainForm, WM_FREELIB, 1, hDll.
Но это не важно.
Главная мысль подхода - сформировать
сообщения в очереди потока таким образом,
чтобы библиотека освобождалась после окончания всех работ с
формой.
Так как в процессе этой работы форме F2 могут пересылать
сообщения через PostMessage, нужно будет организовать
цепочку ПостМессаджей, чтобы добиться правильного порядка сообщений в очереди.
И, в певую очередь, нужно просто выйти из ц.об.с"а ShowModal
и войти в основной ц.об.с.
Одного ПостМессаджа может быть мало.
← →
Alx2 (2002-09-23 14:43) [12]Проработать Detach в DLL?
← →
Старый Паскалист (2002-09-23 14:49) [13]Я полагаю, не надо внутри обработчика делать
ProgressMessages, поскольку Вы ещё не вышли из
цикла обраб. сообщ. ShowModal.
Нужно пустить всё как оно должно идти,
т.е. сначала, по выходе из вашей процедуры,
в ц.об.с ShowModal будет проверен ModalResult,
после этого Вы выйдете из ц.об.с. ShowModal,
после этого ShowModal вернёт результат там, где его ждут,
после этого все последующие сообщения в очереди будут извлечены
уже из глобального ц.об.с.
И там всё должно сработать нормально.
← →
Старый Паскалист (2002-09-23 14:53) [14]Вызывая ProcessMessages, Вы обрабатываете сообщения до
выполнения постфикса. И в этом вся проблема.
← →
Polevi (2002-09-23 15:34) [15]>F2.ModalResult := mrCancel;
>// Это НЕ приводит к мгновенному выходу из цикла,
>// поскольку возврата в ShowModal пока еще нет.
F2.ModalResult:=mrCancel
PostMessage(F2.Handle,WM_USER,0,0); - будим цикл и переходим по стеку к вызову ShowModal
2Старый Паскалист (23.09.02 14:25)
мне хватило одного :-)
← →
Старый Паскалист (2002-09-23 15:43) [16]Polevi © (23.09.02 15:34)
F2.ModalResult:=mrCancel
PostMessage(F2.Handle,WM_USER,0,0); - будим цикл и переходим по стеку к вызову ShowModal
Не надо никого будить, никто не заснул. И цепочку PostMessag"eй не надо.
Нужно просто убрать ProcessMessages.
Два PostMessage нужно, когда в OnClose Action = caFree.
Потому что уже после первого сообщения форме будет послано
CM_RELEASE.
Оно будет стоять в очереди после первого пользовательского сообщения, но перед вторым.
← →
Polevi (2002-09-23 15:57) [17]как же не уснул ?
procedure ShowModal
...
....
//вот этот цикл засыпает на WaitMessage
//нам надо присвоить ModalResult<>0 и разбудить цикл, чтобы
//отработал until
repeat
Application.HandleMessage;
if Application.FTerminate then ModalResult := mrCancel else
if ModalResult <> 0 then CloseModal;
until ModalResult <> 0;
...
...
procedure TApplication.HandleMessage;
var
Msg: TMsg;
begin
if not ProcessMessage(Msg) then Idle(Msg);
end;
procedure TApplication.Idle(const Msg: TMsg);
var
Control: TControl;
Done: Boolean;
begin
Control := DoMouseIdle;
if FShowHint and (FMouseControl = nil) then
CancelHint;
Application.Hint := GetLongHint(GetHint(Control));
Done := True;
try
if Assigned(FOnIdle) then FOnIdle(Self, Done);
if Done then DoActionIdle;
except
HandleException(Self);
end;
if Done then WaitMessage; //ВОТ ТУТ ЗАСЫПАЕМ !!!!!!!!!!!!!!!
end;
← →
Старый Паскалист (2002-09-23 16:12) [18]Polevi © (23.09.02 15:57)
Хорошо, уснул, не спорю.
Но в коде всё равно достаточно PostMessag"ей, что бы не ставить ещё один специально для побудки. Проблема-то не в этом.
← →
Polevi (2002-09-23 16:29) [19]гм, видимо я тогда не понял чегото..
мне казалось что нужно выйти из ShowModal до того как библиотека будет выгружена из памяти..
← →
Старый Паскалист (2002-09-23 16:58) [20]2Polevi © (23.09.02 15:57)
>как же не уснул ?
Ты тормознул, да и я у тебя на поводу.
Как же может поток заснуть, когда он код выполняет???
:-))))
← →
Polevi (2002-09-23 18:02) [21]да, только сейчас дошло что все в одном потоке крутится :-)))
← →
Юрий Зотов (2002-09-23 18:52) [22]Победа!
Все, как и всегда, оказалось элементарно. Лишь 2 правки.
PostMessage(F2.Handle, WM_USER+1, mrCancel, 0); // !!!
F2.Release;
PostMessage(F3.Handle, WM_USER+2, DllHandle, 0);
И СТРОГО никаких ProcessMessages!!!
В F2.WMUserPlus1: ModalResult := WParam;
В F3.WMUserPlus2: FreeLibrary(WParam);
В итоге структура очереди:
1. WM_USER + 1
2. CM_RELEASE
3. WM_USER + 2
Первое сообщение выбирается внутренним циклом ShowModal. Из него вызывается обработчик (устанавливается ModalResult), а из обработчика возвращаемся в тот же цикл - И ПО UNTIL ПРОИСХОДИТ ВЫХОД ИЗ ЦИКЛА, а за ним - из ShowModal и из стека его вызовов.
Сообщения 2 и 3 выбираются уже глобальным циклом, убивается форма и выгружается DLL. И все тип-топ.
Блуждал в трех соснах, чувствовал, что хожу где-то рядом, но зацепиться не мог. Вот и решил во-первых, сформулировать проблему словами (часто помогает), во-вторых привлечь людей со свежим взглядом и в-третьих, устроить мозговой штурм.
ОГРОМНОЕ СПАСИБО ВСЕМ, ОСОБЕННО СТАРОМУ (И МУДРОМУ) ПАСКАЛИСТУ.
Страницы: 1 вся ветка
Форум: "Основная";
Текущий архив: 2002.10.03;
Скачать: [xml.tar.bz2];
Память: 0.51 MB
Время: 0.008 c