![]() |
| ||
|
|
Новости |
Поиск |
FAQ |
Статьи |
Книги |
Новости VCL
| | Форумы ЧАТ |
Орешник |
|
| |||
| |||
« предыдущая ветвь | форум | следующая ветвь »
корректное завершение [Delphi, Windows]
matroskin66 © (2007-09-11 19:41:00)здравствуйте. использую KOL для написания dll. в программе вызываю процедурку, показывающую форму из dll. форма отображается не модально. использую следующий код:
procedure ShowForm;
begin
if Applet <> nil then Applet.Free;
Applet := nil;
AppletTerminated := false;
Applet := NewApplet('');
NewMyForm(MyForm, Applet);
MyForm.Form.Show;
Applet.Visible := false;
Run(Applet);
Applet.Free;
Applet := nil;
AppletTerminated := false;
end;
А теперь суть проблемы. Если закрыть форму, и выгрузить dll, всё будет хорошо. Но если выгружать dll не закрывая KOL форму, то вылетает ошибка и программа вырубается :( Пробовал перед выгрузкой использовать метод MyForm.Form.Close но всё равно ошибка остаётся. Пожалуйста подскажите как быть.
Delphuk (2007-12-23 17:56:00) [1]Помню, но к сожалению мне это не по силам :(
Vladimir Kladov (2007-12-23 17:29:00) [2]А я уже предлагал. Асм-вставкой сделать, помните?
Delphuk (2007-12-23 16:53:00) [3]Тогда нужно искать другой выход, не через потоки. т.к. я просто не могу обойтись без call'ов. Остаётся только самый первый вариант, при котором всё отлично работает, но необходимо ручками закрывать окно. Пришли к тому, с чего начали :) Владимир, у вас есть ещё какие-нибудь предложения?
Vladimir Kladov (2007-12-23 16:47:00) [4]Как я и подумал: вы пытаетесь напрямую вызывать callback в хостовом приложении. Но из другого потока этого делать не следует - последствия непредсказуемы. Вариант с потоком подойдёт только для случая, когда между формой в dll и хостовым приложением нет прямых call'ов до самого закрытия формы.
Delphuk (2007-12-23 16:14:00) [5]Вот обработчик по дабл клику:
procedure TSettingsForm.LVMouseDblClk(Sender: PControl;
var Mouse: TMouseEventData);
begin
if LV.LVCount < 1
then
exit;
if (LV.LVCurItem = LV.LVFocusItem)
then
RQ_OpenContactInfo(Int2Str(LV.LVItemData[LV.LVFocusItem]));
end;
А вот сама процедура из апи хост приложения:
procedure RQ_OpenContactInfo(uin: String);
begin
callStr(char(PM_CMD) + char(PC_SHOWINFO) + _istring(uin));
end;
P.S.
В ListView находится один столбец с данными вида: Name(string)/Uin(integer)
Delphuk (2007-12-23 16:05:00) [6]Вызываю процедуру из плагинного апи хост приложения (вызов окна с некоторой информацией). Там ошибок точно нет.
Vladimir Kladov (2007-12-23 15:53:00) [7]а что вы делаете в ondblclick?
Delphuk (2007-12-23 14:08:00) [8]Всё же оказалось не так всё гладко, как хотелось бы. Теперь после обработки события ОнДаблКлик в ListView, который находится на форме (которая вызывается в потоке) происходит ошибка, хост приложение валится или ругается на нехватку ресурсов. Возвращаю всё как было (без потоков), работает на ура :(
Delphuk (2007-12-23 11:20:00) [9]Владимир, заменил строчку TerminateThread(HThread,0); на HThread := 0; (поток и так норм зам закрывается, в конце процедурки написано же ExitThread(0); ) В общем вроде бы сейчас всё нормально, глюков и вылетов пока не заметил. Но правильный ли подход?
Vladimir Kladov (2007-12-23 10:24:00) [10]Ладно посмотрю, но мне сейчас срочно надо по магазинам с семьёй - искать новогодние прибамбасы для сына типа костюма, для ёлки типа гирлянды...
Сразу: я сказал, каак правильно поток завершать. Так у вас как минимум лик ресурсов может случиться. TerminateThread - это "последний выстрел", в том числе на API если поток делается. Поток должен сам себя закончить, это единственный правильный способ.
Delphuk (2007-12-23 09:48:00) [11]Владимир, смотрите как я делаю:
var
SettingsForm {$IFDEF KOL_MCK} : PSettingsForm {$ELSE} : TSettingsForm {$ENDIF} ;
CanShow: Boolean = true;
AnswerList: PStrList;
// переменные для потока
thID: DWORD;
HThread: THandle;
procedure ShowSettings; stdcall;
.............................................
//процедура создания и показа моего окна
procedure ShowSettings; stdcall;
begin
CanShow := false;
if Applet <> nil then Applet.Free;
Applet := nil;
AppletTerminated := false;
Applet := NewApplet('');
NewSettingsForm(SettingsForm, Applet);
SettingsForm.Form.Show;
Applet.Visible := false;
Run(Applet);
Applet.Free;
Applet := nil;
AppletTerminated := false;
CanShow := true;
ExitThread(0);
end;
.......................................
//это процедурка, обработчик нажатия кнопки вызова формы плагина
procedure OnButtonLeft(Handle: Integer; X,Y: Integer);
begin
if CanShow
then
HThread:= CreateThread(nil,0,@ShowSettings,nil,0,thID); //запускаем в потоке процедуру создания и показа формы
else
SettingsForm.Form.Show; //если форма открыта, то передаём ей фокус
end;
..................
//процедура которая вызывается при получении уведомления об отключении плагина
procedure OnFinalize;
begin
if HThread <> 0
then
begin
if not CanShow
then
SettingsForm.Form.Close;
TerminateThread(HThread,0);
MessageBox(0,'unload','dfdf',MB_ICONINFORMATION);
end;
end;
Владимир, пожалуйста подскажите что изменить или покажите пример, как можно сделать по-другому.
Vladimir Kladov (2007-12-23 06:54:00) [12]Вообще, я так понимаю, проблема корректного завершения потока? Думаю, надо послать потоку WM_QUIT, затем сделать thread.Wait;, после чего ему можено говорить Free, и возвращаться в хост-программу.
Vladimir Kladov (2007-12-23 03:31:00) [13]Не могу сейчас, нет Delphi под рукой. (Смешно даже).
Delphuk (2007-12-23 02:23:00) [14]Владимир Кладов, так ничего и не получается :( Пожалуйста, помогите справиться с этой ситуацией. Только из-за этого бага не выпускаю плагин в паблик.
Delphuk (2007-12-13 10:31:00) [15]Хорошо, сразу после сдачи сессии попробую работу с потоками, потом отпишусь :)
P.S.
Самым минимум знания асма конечно же есть (как то даже проигрываени песенки "кузнечик" писал на чистом асме)
Vladimir Kladov (2007-12-11 16:06:00) [16]Есть вариант и без асма. Запустить в потоке, пусть там крутится обработчик сообщений вместе с апплетом И тогда сразу вернётся. Надо только учесть при взаимодействии с хост-приложением, если такое есть. Ну и поток правильно завершать.
Честно если, я не знаю, как можно без минимального знания асма что-то серьёзное сделать. Надо же хотя бы теоретически представлять, в какие команды код с Паскаля превратится. Ведь Паскаль - это просто макроассемблер, просто навороченный.
Delphuk (2007-12-11 12:49:00) [17]Понятно. Правда в асме я не силён и вставок сделать не смогу. Да к тому же интересно, как можно из самой же библиотеки заставить программу выгрузить эту же библиотеку? Попутно возник вопрос, а как можно вообще безболезненно завершить этот цикл обработки Run(Applet) ?
Vladimir Kladov (2007-12-09 15:07:00) [18]А разница-то какая, закрываете вы форму или нет. Результат тот же - в стеке адрес возврата на цикл обработки сообщений Run. В общем, вы просто не пытаетесь представить себе, что происходит, кто кого и когда вызывает. Смотрите:
Главный цикл обработки сообщений в хост-приложении что-то взывал после чего загрузилась dll, сработал Run(Applet), и
начал работать цикл обработки сообщени в процедуре Run. То, что форма немодальная означает лишь, что DispatchMessage может вызывать отсюда обработчики окон хост-приложения, но они все вызываются из этого цикла. В том числе тот код, который решил выгрузить DLL.
Если такой обработчик выгрузит DLL, пусть даже вы и закроете форму, управление всё равно вернётся на код, который есть только DLL. А раз DLL выгружена, то по этим адресам уже никакого кода нет.
И ожидание AppletTermunated в OnFinalize ничего не даст, возвращаться надо всё равно в код из DLL.
Возможное решение: Не дать выгружать DLL из хоста (например, ещё раз вызвать LoadLibrary). Пи желании выгрузиться самим можно после Run( Applet ), вызвав FreeLibrary дважды (Дважды - потому что и еслиLoadLibrary вызвана дважды). Но второй раз - только через асм, т.к. возвращаться из FreeLibrary можно будет только в хост-приложение. Обычная команда CALL неприменима, нужно в стек загнать правильный адрес возврата, и сделать Jmp. Без асм-вставки тут не обойтись.
Наверное, можно было избежать всего это, не создавая цикл обработки сообщений (не запуская Applet) в DLL, но так почему-то не получилось N лет назад. Может сейчас получится, если попробовать, не знаю.
Delphuk (2007-12-09 14:13:00) [19]Под "выгружать dll не закрывая KOL форму" я имел ввиду, что если из хост-приложения отключать плагин (в плагин посылается сообщение, что он сейчас будет отключен. В результате чего выполняется процедура OnFinalize, в которой я проверяю, открыта или нет форма и если нужно, то закрываю форму. dll'ка не выгружается, пока весь код процедуры OnFinalize не будет выполнен), то в случае с VCL никаких проблем нет, а с KOL происходит ошибка. Попробую поэксперементировать с AppletTerminated.
Vladimir Kladov (2007-12-09 13:54:00) [20]Тогда я не понимаю вот это ваше утверждение (жирным - я выделил):
А теперь суть проблемы. Если закрыть форму, и выгрузить dll, всё будет хорошо. Но если выгружать dll не закрывая KOL форму, то
вылетает ошибка и программа вырубается :( Пробовал перед выгрузкой использовать метод MyForm.Form.Close но всё равно ошибка
остаётся. Пожалуйста подскажите как быть.
Я думаю, что надо сделать всё правильно. И как минимум дождаться завершения цикла обработки сообщений (условие AppletTerminated = true).