Главная страница
    Top.Mail.Ru    Яндекс.Метрика
Форум: "Основная";
Текущий архив: 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
7-8184
harismatik
2002-07-25 15:36
2002.10.03
Как сделать?


1-7999
sasw
2002-09-21 12:32
2002.10.03
Как удалить файлик?


3-7846
alexts
2002-09-11 12:57
2002.10.03
Фильтр в ADO


8-8070
BaLoo
2002-05-30 03:22
2002.10.03
Подскажите, пожалуйста, как написать МР3/WMA плэер,


4-8234
Stany
2002-08-17 15:32
2002.10.03
Заколебавший ListView





Afrikaans Albanian Arabic Armenian Azerbaijani Basque Belarusian Bulgarian Catalan Chinese (Simplified) Chinese (Traditional) Croatian Czech Danish Dutch English Estonian Filipino Finnish French
Galician Georgian German Greek Haitian Creole Hebrew Hindi Hungarian Icelandic Indonesian Irish Italian Japanese Korean Latvian Lithuanian Macedonian Malay Maltese Norwegian
Persian Polish Portuguese Romanian Russian Serbian Slovak Slovenian Spanish Swahili Swedish Thai Turkish Ukrainian Urdu Vietnamese Welsh Yiddish Bengali Bosnian
Cebuano Esperanto Gujarati Hausa Hmong Igbo Javanese Kannada Khmer Lao Latin Maori Marathi Mongolian Nepali Punjabi Somali Tamil Telugu Yoruba
Zulu
Английский Французский Немецкий Итальянский Португальский Русский Испанский