Главная страница
Top.Mail.Ru    Яндекс.Метрика
Текущий архив: 2002.10.03;
Скачать: CL | DM;

Вниз

Выгрузка 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;
Скачать: CL | DM;

Наверх




Память: 0.54 MB
Время: 0.02 c
1-8047
Metotrone
2002-09-22 18:03
2002.10.03
Размещение texta


7-8181
Tolic-F
2002-07-22 09:12
2002.10.03
CD-ROM Sony CDU5221


4-8233
Lamer86
2002-08-16 11:07
2002.10.03
Как убрать программу с Toolbar


1-7958
Lamooooooooooooo
2002-09-24 10:52
2002.10.03
В чем разница?


3-7771
AM
2002-09-11 12:15
2002.10.03
Выполнение хран. процедуры идет как одна транзакция?