Конференция "KOL" » корректное завершение [Delphi, Windows]
 
  • matroskin66 (11.09.07 19:41) [0]
    здравствуйте. использую 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 но всё равно ошибка остаётся. Пожалуйста подскажите как быть.
  • mdw © (11.09.07 22:08) [1]
    Никак. Проблема давно висит, и пожалуй решить (если возникнет желание) может только автор. Долго копался, но проблема, насколько понял, в том что нужно создавать новый Applet, т.е. еще один обработчик событий. Не выходит создать полностью адекватную форму из Dll. Если закрыть основное приложение до закрытия формы из Dll, то Applet из Dll остается жить своей жизнью, а в результате приложение остается висеть в памяти.
  • mdw © (11.09.07 22:09) [2]
    Перечитал еще раз [1] немного не в тему ответил, но ноги все равно от туда растут...
  • matroskin66 (11.09.07 23:49) [3]
    Владимир Кладов, вся надежда только на вас :-[ проясните ситуацию.
  • matroskin66 (23.09.07 23:17) [4]
    Владимир Кладов, вся надежда только на вас :-[ проясните ситуацию.

    Так есть решение проблемы или нет? Скажите плз.
  • thaddy (24.09.07 03:13) [5]
    You could use my interceptors to trap unloadlibrary:
    http://thaddy.co.uk/koldetours.zip

    But that technique is not recommended (although it works!)
  • Vladimir Kladov © (24.09.07 18:04) [6]
    Я проблемы не вижу. Выгружать dll, не завершив работу с её функциями, абсолютно неверно. Это не только KOL касается. Сам недавно напарывался: если неожиданно выгрузить либу (кажется, это была opengl), то программа падает с большим грохотом при попытке обратиться к ее функциям. И это понятно: по тем адресам больше нет исполняемого кода. Вообще ничего нет. А адрес функции остался.
  • Dimaxx © (26.09.07 10:17) [7]
    Давно что-то не использовал формы из dll, поэтому хочу спросить на будущее: это относится к динамически загружаемым dll или к статическим тоже? Давно как-то делал приложение, в котором статически вызывалась ф-ция с формой. Так вот все нормально открывалось и закрывалось. Но если хотя бы один раз открыть-закрыть форму из dll - основное приложение падало при закрытии себя с ошибкой 216. Вроде все делал как в примере.

    Кстати эта ошибка 216 лезла раньше изо всех щелей.
  • Delphuk © (08.12.07 21:37) [8]
    Столкнулся с полностью идентичной проблемой. Использую KOL+MCK для написания плагина для RnQ (хост приложение). Вызываю окно плагина (не модально) точно так же, как в демке:
    procedure ShowSettings;
    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;
    end;



    Использую вот такой код для вызова окна моего плагина:

    begin
    if CanShow
    then
      ShowSettings
    else
      SettingsForm.Form.Show;
    end;



    Проблема в том, что если отключать плагин при открытой форме, то вылетает ошибка и хост приложение "умирает". А если сначала "вручную" закрыть форму и только потом отключать плагин, то всё норм. Логично, что так и должно быть и перед отключением нужно закрывать форму, но...
    Перед отключением генерируется событие, что сейчас плагин будет отключен. Тут я проверяю, открыта ли форма плагина и если открыта, то закрываю её:
    procedure OnFinalize;
    begin
    if not CanShow
    then
      begin
       ShowMessage('off....');
       SettingsForm.Form.Close;
      end
    else
      MessageBox(0, 'Отключено успешно', 'Отключение', MB_OK);
    end;



    Как и положено, если форма закрыта, то выводится соотв. сообщение и плагин благополучно отключается. А если форма открыта, то выводится другое сообщение, но после выполнения SettingsForm.Form.Close; вылетает ошибка и хост приложение вылетает.

    Ещё раз хочу акцентировать внимание на том, что если ручками закрыть форму, то никаких проблем нет. Владимир Кладов, пожалуйста помогите, ошибка точно где то со стороны Апплета.
  • Delphuk © (08.12.07 21:38) [9]
    Удалено модератором
  • Vladimir Kladov © (08.12.07 23:25) [10]
    Я как помогу, я же с плагинами не работаю. Вот я пример у себя (с полгода как или больше) положил. Там есть проблема? (А ведь там даже есть вариант с вызывальщиком на C).

    PAS_VERSION - пробовали? Отлаживать, что вылетает - пробовали? Можно ведь включить пошаговую отладку со стороны DLL.
  • Delphuk © (09.12.07 10:40) [11]
    Отлаживать dll не получается. Прописываю хост приложение, включаю все необходимые опции проекта, указываю необходимые пути для исходников и тд. При запуске пошаговой отладки, через несколько секунд вылетает окошко с ошибкой "External exception EEFFACE" после чего дэльфи можно выключить только убив сам процес дэльфи.
  • Vladimir Kladov © (09.12.07 11:10) [12]
    Попробуйте другую версию Delphi. И это серьёзный совет. То, что не идёт на D7, вполне может сработать на D6, например. Кстати, включили поддержку debug dcu + информации для TurboDebuger'а (в закладке Linker)? Помогает.

    Ещё одни вариант: отладка  под MS VC++. Тяжеловато лазить по дизасм-коду, но если очень хочется, то можно.

    И последний вариант - логи.
  • Delphuk © (09.12.07 11:41) [13]
    Пишу под Delphi 6.

    > Кстати, включили поддержку debug dcu + информации для TurboDebuger'а
    > (в закладке Linker)? Помогает.

    Да, конечно это включаю, но результата нет :( Мне вот что интересно, ведь если ручками закрывать форму, то всё норм. Там кнопка с полностью идентичным кодом SettingsForm.Form.Close; А вот если просто вызывать этот метод, то вылетает хост-приложение. Я уже даже попробовал извратиться и программно нажать на кнопку закрытия формы, но результат тот же :(
  • Vladimir Kladov © (09.12.07 12:46) [14]
    Сильно подозреваю, что происходит выгрузка dll в тот момент, когда ещё есть в стеке код, который сам из этой dll. Вы пытаетесь закрыть форму и выгрузить dll из самой dll?
  • Delphuk © (09.12.07 12:54) [15]
    саму dll я не выгружаю (не могу т.к. хост-приложение не моё) я только реагирую на событие перед выгрузкой dll, и dll выгружается только после того, как закончится обработка данного события. Сделаю небольшую демку на VLC и посмотрю, какая будет реакция в подобной же ситуации, потом сообщу. Но всё же, мне кажется, что проблема с Апплетом.
  • Delphuk © (09.12.07 13:11) [16]
    Владимир Кладов, я проверил, с VLC таких проблем нет, никакие ошибки не вылетают т.е. код в стеке норм весь выполняется и только потом уже происходит выгрузка библиотеки. Вот основной код моей демки на VLC:

    //процедура выполняется при инициализации плагина
    procedure OnInitialize;
    begin
    Form1 := TForm1.Create(nil);
    Form1.Show;
    Called := true;
    end;

    //процедура вызывается в момент получения сообщения, что сейчас плагин будет отключен
    procedure OnFinalize;
    begin
    if Called
    then
      begin
       Form1.Close;
       Form1.Free;
       MessageBox(0, 'good', 'msg', 0);
       Called := false;
      end;
    end;



    Всё прекрасно работает, а вот подобный код в KOL (чуть выше я уже писал код) приводит к ошибке и вылету хост-приложения. Владимир, пожалуйста обратите на это внимание.
  • Vladimir Kladov © (09.12.07 13:54) [17]
    Тогда я не понимаю вот это ваше утверждение (жирным - я выделил):

    А теперь суть проблемы. Если закрыть форму, и выгрузить dll, всё будет хорошо. Но если выгружать dll не закрывая KOL форму, то
    вылетает ошибка и программа вырубается :( Пробовал перед выгрузкой использовать метод MyForm.Form.Close но всё равно ошибка
    остаётся. Пожалуйста подскажите как быть.

    Я думаю, что надо сделать всё правильно. И как минимум дождаться завершения цикла обработки сообщений (условие AppletTerminated = true).
  • Delphuk © (09.12.07 14:13) [18]
    Под "выгружать dll не закрывая KOL форму" я имел ввиду, что если из хост-приложения отключать плагин (в плагин посылается сообщение, что он сейчас будет отключен. В результате чего выполняется процедура OnFinalize, в которой я проверяю, открыта или нет форма и если нужно, то закрываю форму. dll'ка не выгружается, пока весь код процедуры OnFinalize не будет выполнен), то в случае с VCL никаких проблем нет, а с KOL происходит ошибка. Попробую поэксперементировать с AppletTerminated.
  • Vladimir Kladov © (09.12.07 15:07) [19]
    А разница-то какая, закрываете вы форму или нет. Результат тот же - в стеке адрес возврата на цикл обработки сообщений 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 © (11.12.07 12:49) [20]
    Понятно. Правда в асме я не силён и вставок сделать не смогу. Да к тому же интересно, как можно из самой же библиотеки заставить программу выгрузить эту же библиотеку? Попутно возник вопрос, а как можно вообще безболезненно завершить этот цикл обработки Run(Applet) ?
  • Vladimir Kladov © (11.12.07 16:06) [21]
    Есть вариант и без асма. Запустить в потоке, пусть там крутится обработчик сообщений вместе с апплетом И тогда сразу вернётся. Надо только учесть при взаимодействии с хост-приложением, если такое есть. Ну и поток правильно завершать.

    Честно если, я не знаю, как можно без минимального знания асма что-то серьёзное сделать. Надо же хотя бы теоретически представлять, в какие команды код с Паскаля превратится. Ведь Паскаль - это просто макроассемблер, просто навороченный.
  • Delphuk © (13.12.07 10:31) [22]
    Хорошо, сразу после сдачи сессии попробую работу с потоками, потом отпишусь :)

    P.S.
    Самым минимум знания асма конечно же есть (как то даже проигрываени песенки "кузнечик" писал на чистом асме)
  • Delphuk © (23.12.07 02:23) [23]
    Владимир Кладов, так ничего и не получается :( Пожалуйста, помогите справиться с этой ситуацией. Только из-за этого бага не выпускаю плагин в паблик.
  • Vladimir Kladov © (23.12.07 03:31) [24]
    Не могу сейчас, нет Delphi под рукой. (Смешно даже).
  • Vladimir Kladov © (23.12.07 06:54) [25]
    Вообще, я так понимаю, проблема корректного завершения потока? Думаю, надо послать потоку WM_QUIT, затем сделать thread.Wait;, после чего ему можено говорить Free, и возвращаться в хост-программу.
  • Delphuk © (23.12.07 09:48) [26]
    Владимир, смотрите как я делаю:


    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 © (23.12.07 10:24) [27]
    Ладно посмотрю, но мне сейчас срочно надо по магазинам с семьёй - искать новогодние прибамбасы для сына типа костюма, для ёлки типа гирлянды...

    Сразу: я сказал, каак правильно поток завершать. Так у вас как минимум лик ресурсов может случиться. TerminateThread - это "последний выстрел", в том числе на API если поток делается. Поток должен сам себя закончить, это единственный правильный способ.
  • Delphuk © (23.12.07 11:20) [28]
    Владимир, заменил строчку TerminateThread(HThread,0); на HThread := 0; (поток и так норм зам закрывается, в конце процедурки написано же ExitThread(0); ) В общем вроде бы сейчас всё нормально, глюков и вылетов пока не заметил. Но правильный ли подход?
  • Delphuk © (23.12.07 14:08) [29]
    Всё же оказалось не так всё гладко, как хотелось бы. Теперь после обработки события ОнДаблКлик в ListView, который находится на форме (которая вызывается в потоке) происходит ошибка, хост приложение валится или ругается на нехватку ресурсов. Возвращаю всё как было (без потоков), работает на ура :(
  • Vladimir Kladov © (23.12.07 15:53) [30]
    а что вы делаете в ondblclick?
  • Delphuk © (23.12.07 16:05) [31]
    Вызываю процедуру из плагинного апи хост приложения (вызов окна с некоторой информацией). Там ошибок точно нет.
  • Delphuk © (23.12.07 16:14) [32]
    Вот обработчик по дабл клику:

    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)
  • Vladimir Kladov © (23.12.07 16:47) [33]
    Как я и подумал: вы пытаетесь напрямую вызывать callback в хостовом приложении. Но из другого потока этого делать не следует - последствия непредсказуемы. Вариант с потоком подойдёт только для случая, когда между формой в dll и хостовым приложением нет прямых call'ов до самого закрытия формы.
  • Delphuk © (23.12.07 16:53) [34]
    Тогда нужно искать другой выход, не через потоки. т.к. я просто не могу обойтись без call'ов. Остаётся только самый первый вариант, при котором всё отлично работает, но необходимо ручками закрывать окно. Пришли к тому, с чего начали :) Владимир, у вас есть ещё какие-нибудь предложения?
  • Vladimir Kladov © (23.12.07 17:29) [35]
    А я уже предлагал. Асм-вставкой сделать, помните?
  • Delphuk © (23.12.07 17:56) [36]
    Помню, но к сожалению мне это не по силам :(
 
Конференция "KOL" » корректное завершение [Delphi, Windows]
Есть новые Нет новых   [134431   +10][b:0][p:0.003]