Может быть, не сразу смогу внятно объяснить т.к. тема для меня новая и я в боевом режиме изучаю использование интерфейсов. Проблема в следующем: накатал dll, которая поставляет нужный мне интерфейс. В нем осуществляется вызов обычной VCL-формы. В стороннем приложении вызов моего интерфейса производится по CreateObject("bla-bla"). Затем вызов метода интерфейса приводит к созданию формы (немодальной), осуществляющей некоторые действия, и которая может некоторое время висеть сама по себе. Но проблема в том, что вызывающее приложение может завершиться, пока моя форма присутствует. В этом случае вываливается AV, впрочем, понятно почему. Непонятно, как с этим бороться? т.к. я еще не слишком глубоко постиг механизм всей этой кухни...
P.S. Мой интерфейс — наследник от IDispatch, инкапсулируется классом так:
А> 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: Корректно отработать завершение своего интерфейса
Здравствуйте, Аноним, Вы писали:
А>Может быть, не сразу смогу внятно объяснить т.к. тема для меня новая и я в боевом режиме изучаю использование интерфейсов. Проблема в следующем: накатал dll, которая поставляет нужный мне интерфейс. В нем осуществляется вызов обычной VCL-формы. В стороннем приложении вызов моего интерфейса производится по CreateObject("bla-bla"). Затем вызов метода интерфейса приводит к созданию формы (немодальной), осуществляющей некоторые действия, и которая может некоторое время висеть сама по себе. Но проблема в том, что вызывающее приложение может завершиться, пока моя форма присутствует. В этом случае вываливается AV, впрочем, понятно почему. Непонятно, как с этим бороться? т.к. я еще не слишком глубоко постиг механизм всей этой кухни...
А>P.S. Мой интерфейс — наследник от IDispatch, инкапсулируется классом так:
А>Что, где проверить\отловить надо, чтобы успеть уничтожить форму в момент завершения вызывающего приложения?
Ответ на твой вопрос заключается в том, какой доступ к форме (немодальной) она позволяет. Если экземпляр доступен по указателю, то определи метод завершения CloseControlWindow и вызывай при закрытии клиента. В VB (судя по коду в другом посте) есть события на закрытие формы — QueryUnload и Unload, в которых можно оповестить этот экземпляр. Если экземпляр не доступен, то его как бы фактически нет, и поэтому то, что он жив, является простым недоразумением.
Здравствуйте, 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]: Корректно отработать завершение своего интерфейса
Здравствуйте, Voxifer, Вы писали:
V> Вкратце, происходит следующее: внешнее приложение вызывает мой интерфейс, что приводит к созданию формы. Форма порождает дополнительный поток, который уже запускает другой интерфейс вызвавшего форму внешнего приложения и работает с ним, периодически посылая информацию форме на отображение. При освобождении моего интерфейса форма закрывается, в её деструкторе вызывается Terminate для потока и вот тут происходят его зависания (попытки выяснить с помощью MessageBox'ов, где конкретно — привели меня к CoUninitialize). Принудительный TerminateThread помогает частично, форма закрывается, но вызывающее приложение все равно зависает до тех пор, пока я не попытаюсь вызвать его системное меню на панели задач (тогда, видимо, происходит выход из какого-то дедлока). Причем вставка MessageBox'а сразу после команды Terminate почему-то сразу снимает всю проблему — абсолютно все отрабатывает корректно, сколько я ни дергал это окно. Есть подозрение, что я что-то напутал с потоковой моделью COM, но что именно — не знаю. Между потоками интерфейсы не передаю, поэтому маршаллинг тут не использую. (Де)Инициализировать COM пытался где можно и не можно. Надоело тыкаться, как слепому котенку, уже мозг и гугл сломал, может кто подскажет где я налажал?
"Вставка MessageBox'а ... снимает всю проблему" — MessageBox крутит цикл выборки сообщений, твой же код, видимо, нет.
V> Вот краткие выдержки из моего кода:
Здравствуйте, 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.