Корректно отработать завершение своего интерфейса
От: Аноним  
Дата: 05.02.10 22:02
Оценка:
Может быть, не сразу смогу внятно объяснить т.к. тема для меня новая и я в боевом режиме изучаю использование интерфейсов. Проблема в следующем: накатал dll, которая поставляет нужный мне интерфейс. В нем осуществляется вызов обычной VCL-формы. В стороннем приложении вызов моего интерфейса производится по CreateObject("bla-bla"). Затем вызов метода интерфейса приводит к созданию формы (немодальной), осуществляющей некоторые действия, и которая может некоторое время висеть сама по себе. Но проблема в том, что вызывающее приложение может завершиться, пока моя форма присутствует. В этом случае вываливается AV, впрочем, понятно почему. Непонятно, как с этим бороться? т.к. я еще не слишком глубоко постиг механизм всей этой кухни...

P.S. Мой интерфейс — наследник от IDispatch, инкапсулируется классом так:
 IControlWindow = interface(IDispatch)
    ['{67642A6A-7E5E-41E2-A2E3-ECF9D03503C0}']
    procedure ShowControlWindow(iType: Integer; iIndex: Integer); safecall;
  end;
  IControlWindowDisp = dispinterface
    ['{67642A6A-7E5E-41E2-A2E3-ECF9D03503C0}']
    procedure ShowControlWindow(iType: Integer; iIndex: Integer); dispid 201;
  end;
 TControlWindow = class(TAutoObject, IControlWindow)
  procedure ShowControlWindow(iType: Integer; iIndex: Integer); safecall;
 end;
Что, где проверить\отловить надо, чтобы успеть уничтожить форму в момент завершения вызывающего приложения?
Re: Корректно отработать завершение своего интерфейса
От: pashzhel Россия  
Дата: 06.02.10 05:06
Оценка:
Здравствуйте, Аноним, Вы писали:


А> procedure ShowControlWindow(iType: Integer; iIndex: Integer); safecall;

А> end;[/pascal]Что, где проверить\отловить надо, чтобы успеть уничтожить форму в момент завершения вызывающего приложения?

Вопрос, а при FormClose делается уничтожение вашего объекта ?

Например

obj := CreateObject( 'bla-bla' );


и в

FormClose( .. )
begin
obj := null;
end

ну и естественно в деструкторе должно быть закрытие окна которое создано в сом-объекте
или можно в отдельный метод вынести бывает полезно ( закрытие диалога )
Re[2]: Корректно отработать завершение своего интерфейса
От: Аноним  
Дата: 06.02.10 07:00
Оценка:
Здравствуйте, pashzhel, Вы писали:

P>Вопрос, а при FormClose делается уничтожение вашего объекта ?


P>Например


P>obj := CreateObject( 'bla-bla' );



P>и в


P>FormClose( .. )

P>begin
P> obj := null;
P>end

P>ну и естественно в деструкторе должно быть закрытие окна которое создано в сом-объекте

P>или можно в отдельный метод вынести бывает полезно ( закрытие диалога )

Собственно говоря, в вызывающем приложении делается следующее (Visual Basic):

Dim avt as Object
Set avt = CreateObject("bla-bla")
avt.ShowControlWindow 1,1
Set avt = Nothing
Цель, которой я хочу добиться — чтобы мои окна были практически независимы от событий вызывающего приложения, только вызывались из него. Т.е вышеприведенный код вызывается, допустим, при OnClick, а дальше — не его забота.
Видимо, я делаю не совсем правильно, у меня почему-то было ощущение, что при закрытии вызывающего приложения произойдет обнуление количества ссылок на интерфейс и система все там автоматом подчистит. Тогда хотелось бы узнать, как по науке такие вещи делаются. В отдельный метод вынести — это конечно идея, но не хотелось бы держать avt как глобальную переменную.
Re: Корректно отработать завершение своего интерфейса
От: Vi2 Удмуртия http://www.adem.ru
Дата: 06.02.10 07:23
Оценка: 2 (1)
Здравствуйте, Аноним, Вы писали:

А>Может быть, не сразу смогу внятно объяснить т.к. тема для меня новая и я в боевом режиме изучаю использование интерфейсов. Проблема в следующем: накатал dll, которая поставляет нужный мне интерфейс. В нем осуществляется вызов обычной VCL-формы. В стороннем приложении вызов моего интерфейса производится по CreateObject("bla-bla"). Затем вызов метода интерфейса приводит к созданию формы (немодальной), осуществляющей некоторые действия, и которая может некоторое время висеть сама по себе. Но проблема в том, что вызывающее приложение может завершиться, пока моя форма присутствует. В этом случае вываливается AV, впрочем, понятно почему. Непонятно, как с этим бороться? т.к. я еще не слишком глубоко постиг механизм всей этой кухни...


А>P.S. Мой интерфейс — наследник от IDispatch, инкапсулируется классом так:
 IControlWindow = interface(IDispatch)
А>    ['{67642A6A-7E5E-41E2-A2E3-ECF9D03503C0}']
А>    procedure ShowControlWindow(iType: Integer; iIndex: Integer); safecall;
А>  end;
А>  IControlWindowDisp = dispinterface
А>    ['{67642A6A-7E5E-41E2-A2E3-ECF9D03503C0}']
А>    procedure ShowControlWindow(iType: Integer; iIndex: Integer); dispid 201;
А>  end;
А> TControlWindow = class(TAutoObject, IControlWindow)
А>  procedure ShowControlWindow(iType: Integer; iIndex: Integer); safecall;
А> end;

А>Что, где проверить\отловить надо, чтобы успеть уничтожить форму в момент завершения вызывающего приложения?

Ответ на твой вопрос заключается в том, какой доступ к форме (немодальной) она позволяет. Если экземпляр доступен по указателю, то определи метод завершения CloseControlWindow и вызывай при закрытии клиента. В VB (судя по коду в другом посте) есть события на закрытие формы — QueryUnload и Unload, в которых можно оповестить этот экземпляр. Если экземпляр не доступен, то его как бы фактически нет, и поэтому то, что он жив, является простым недоразумением.
Vita
Выше головы не прыгнешь, ниже земли не упадешь, дальше границы не убежишь! © КВН НГУ
Re[2]: Корректно отработать завершение своего интерфейса
От: Voxifer Россия  
Дата: 08.02.10 10:38
Оценка:
Здравствуйте, Vi2, Вы писали:

Vi2>Ответ на твой вопрос заключается в том, какой доступ к форме (немодальной) она позволяет. Если экземпляр доступен по указателю, то определи метод завершения CloseControlWindow и вызывай при закрытии клиента. В VB (судя по коду в другом посте) есть события на закрытие формы — QueryUnload и Unload, в которых можно оповестить этот экземпляр. Если экземпляр не доступен, то его как бы фактически нет, и поэтому то, что он жив, является простым недоразумением.


Да, твоя правда. Действительно, проблема была в том, что я не до конца представлял себе механизм работы собственного компонента и он был как раз "простым недоразумением". Собственно, покопавшись немного глубже, я вышеобозначенную проблему решил. Не так как хотелось бы, но зато понял, что иначе нельзя. Но тут объявилась другая проблема.
Вкратце, происходит следующее: внешнее приложение вызывает мой интерфейс, что приводит к созданию формы. Форма порождает дополнительный поток, который уже запускает другой интерфейс вызвавшего форму внешнего приложения и работает с ним, периодически посылая информацию форме на отображение. При освобождении моего интерфейса форма закрывается, в её деструкторе вызывается Terminate для потока и вот тут происходят его зависания (попытки выяснить с помощью MessageBox'ов, где конкретно — привели меня к CoUninitialize). Принудительный TerminateThread помогает частично, форма закрывается, но вызывающее приложение все равно зависает до тех пор, пока я не попытаюсь вызвать его системное меню на панели задач (тогда, видимо, происходит выход из какого-то дедлока). Причем вставка MessageBox'а сразу после команды Terminate почему-то сразу снимает всю проблему — абсолютно все отрабатывает корректно, сколько я ни дергал это окно. Есть подозрение, что я что-то напутал с потоковой моделью COM, но что именно — не знаю. Между потоками интерфейсы не передаю, поэтому маршаллинг тут не использую. (Де)Инициализировать COM пытался где можно и не можно. Надоело тыкаться, как слепому котенку, уже мозг и гугл сломал, может кто подскажет где я налажал?
Вот краткие выдержки из моего кода:

...
initialization
  TAutoObjectFactory.Create(ComServer, TControlWindow, Class_ControlWindow,
    ciMultiInstance, tmBoth);  // Последние два параметра менял в разных вариациях
...

  TUptateThread = class(TThread)
  ...
  public
    FEvent:THandle;
  ...
  end;
  TfmWin = class(TForm)
    ...
    FUpdateThread:TUptateThread;
    ...
  end;
...
constructor TUptateThread.Create(...);
begin
 inherited Create(true);
 ...
 FreeOnTerminate:=false;
 FEvent:=CreateEvent(nil, TRUE, FALSE, 'MyCustomEvent');
 Resume;
end;

destructor TUptateThread.Destroy;
begin
  CloseHandle(FEvent);
  inherited;
end;

procedure TUptateThread.Execute;
var
 FApplication:OleVariant;
 ...
 hr:HRESULT;
 WValue,LValue: integer;
begin
  hr:=CoInitializeEx(nil,COINIT_MULTITHREADED);  // Пытался и COINIT_APARTMENTTHREADED

  FApplication := GetActiveOleObject('Интерфейс вызвавшего приложения');
  
  ... // Инициализация внутренних переменных на основе методов FApplication 
  { Place thread code here }
  while not Terminated do
   begin
    // Работа с FApplication 
    ...
    if (Условие) then PostMessage(FMainForm,WM_USER+NNN,WValue,LValue);  // Форма только отображает полученные от потока значения, сама с интерфейсом не взаимодействует
    Sleep(50);
   end;
  ... // Деинициализация внутренних переменных
  FApplication:=Unassigned;
  SetEvent(FEvent);          // Вот тут сигнализирую об окончании потока, потому что ждать сам поток бессмысленно, ибо зависает
  SetErrorInfo(0,nil);       // Где-то слышал звон, что может помочь... нифига...
  if (hr=S_OK)or(hr=S_FALSE) then CoUninitialize;   // Как показало вскрытие - вот тут происходит зависание
end;

constructor TfmWin.Create(AIndex:integer);
begin
 inherited Create(nil);
 FUpdateThread:=TUptateThread.Create(...);
end;

destructor TfmWin.Destroy;
begin
 try
  ResetEvent(FUpdateThread.FEvent);
  FUpdateThread.Terminate;         // Вот после этого поток может зависнуть, а может и нет
  // MessageBox(0,'Удаление потока','Ахтунг!',0); // Раскомментирование этой магической строчки снимает проблему, но само является проблемой.
  if WaitForSingleObject(FUpdateThread.FEvent ,5000)<>WAIT_OBJECT_0 then 
    TerminateThread(FUpdateThread.Handle,0);
  FUpdateThread.Free;
 finally
  FUpdateThread:=nil;
 end;
 inherited;
end;
Re[3]: Корректно отработать завершение своего интерфейса
От: Vi2 Удмуртия http://www.adem.ru
Дата: 08.02.10 14:17
Оценка:
Здравствуйте, Voxifer, Вы писали:

V> Вкратце, происходит следующее: внешнее приложение вызывает мой интерфейс, что приводит к созданию формы. Форма порождает дополнительный поток, который уже запускает другой интерфейс вызвавшего форму внешнего приложения и работает с ним, периодически посылая информацию форме на отображение. При освобождении моего интерфейса форма закрывается, в её деструкторе вызывается Terminate для потока и вот тут происходят его зависания (попытки выяснить с помощью MessageBox'ов, где конкретно — привели меня к CoUninitialize). Принудительный TerminateThread помогает частично, форма закрывается, но вызывающее приложение все равно зависает до тех пор, пока я не попытаюсь вызвать его системное меню на панели задач (тогда, видимо, происходит выход из какого-то дедлока). Причем вставка MessageBox'а сразу после команды Terminate почему-то сразу снимает всю проблему — абсолютно все отрабатывает корректно, сколько я ни дергал это окно. Есть подозрение, что я что-то напутал с потоковой моделью COM, но что именно — не знаю. Между потоками интерфейсы не передаю, поэтому маршаллинг тут не использую. (Де)Инициализировать COM пытался где можно и не можно. Надоело тыкаться, как слепому котенку, уже мозг и гугл сломал, может кто подскажет где я налажал?


"Вставка MessageBox'а ... снимает всю проблему" — MessageBox крутит цикл выборки сообщений, твой же код, видимо, нет.

V> Вот краткие выдержки из моего кода:


Боюсь, он мне непонятен.
Vita
Выше головы не прыгнешь, ниже земли не упадешь, дальше границы не убежишь! © КВН НГУ
Re[3]: Корректно отработать завершение своего интерфейса
От: Jolly Roger  
Дата: 15.02.10 10:29
Оценка:
Здравствуйте, Voxifer, Вы писали:

V>Здравствуйте, Vi2, Вы писали:


Vi2>>Ответ на твой вопрос заключается в том, какой доступ к форме (немодальной) она позволяет. Если экземпляр доступен по указателю, то определи метод завершения CloseControlWindow и вызывай при закрытии клиента. В VB (судя по коду в другом посте) есть события на закрытие формы — QueryUnload и Unload, в которых можно оповестить этот экземпляр. Если экземпляр не доступен, то его как бы фактически нет, и поэтому то, что он жив, является простым недоразумением.


V> Собственно, покопавшись немного глубже, я вышеобозначенную проблему решил. Не так как хотелось бы, но зато понял, что иначе нельзя.


Сделать можно всё, что нужно, но для этого нужно хорошо знать и VCL, и COM. Боюсь, Вы выбрали не самую подходящую задачу для "

в боевом режиме изучаю использование интерфейсов

"

V> Вкратце, происходит следующее: внешнее приложение вызывает мой интерфейс, что приводит к созданию формы. Форма порождает дополнительный поток, который уже запускает другой интерфейс вызвавшего форму внешнего приложения и работает с ним, периодически посылая информацию форме на отображение. При освобождении моего интерфейса форма закрывается, в её деструкторе вызывается Terminate для потока и вот тут происходят его зависания (попытки выяснить с помощью MessageBox'ов, где конкретно — привели меня к CoUninitialize). Принудительный TerminateThread помогает частично, форма закрывается, но вызывающее приложение все равно зависает до тех пор, пока я не попытаюсь вызвать его системное меню на панели задач (тогда, видимо, происходит выход из какого-то дедлока). Причем вставка MessageBox'а сразу после команды Terminate почему-то сразу снимает всю проблему — абсолютно все отрабатывает корректно, сколько я ни дергал это окно. Есть подозрение, что я что-то напутал с потоковой моделью COM, но что именно — не знаю. Между потоками интерфейсы не передаю, поэтому маршаллинг тут не использую. (Де)Инициализировать COM пытался где можно и не можно. Надоело тыкаться, как слепому котенку, уже мозг и гугл сломал, может кто подскажет где я налажал?


Факт зависания на CoUninitialize, да ещё при COINIT_MULTITHREADED, вызывает, откровенно говоря, определённые сомнения. Вы уверены, что правильно определили место блокировки?

MessageBox имеет встроенный Message loop, её вызов приводит к обработки очереди вызывающего потока вместо его блокировки при вызове WaitForSingleObject. В связи с чем возникает вопрос — а не назначаете ли Вы обработчик события OnTerminate потока? Если нет, то скорее всего такой цикл требуется объекту хостового приложения, который Вы обозвали 'Интерфейс вызвавшего приложения'. Видимо этот объект работает в STA.

Кстати, смысл в использовании эвента 'MyCustomEvent' у Вас не просматривается, его роль вполне может выполнить хёндл самого потока. И уж по крайней мере нет смысла делать его именованным. А зачем Вам вообще ждать завершения этого потока? По-моему в данном случае вполне сгодится установка FreeOnTerminate in true.
"Нормальные герои всегда идут в обход!"
 
Подождите ...
Wait...
Пока на собственное сообщение не было ответов, его можно удалить.