Здравствуйте, 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;