Столкнулся со следующей проблемой. Имеется inproc ActiveX компонент. Он генерирует события в отдельном потоке, причем с довольно большой частотой.
Событийный поток занимается обслуживанием очереди событий. Я привожу код полностью процедуры потока.
void EventManager::Routine()
{
HRESULT hr = E_FAIL;
tstring name;
vector<ml::com::Variant> args;
com_ptr<IDispatch, IID_IDispatch> pIEvent;
com_ptr<IEnumConnections, IID_IEnumConnections> pEnumConnections;
// Блокировка потока, если список стал пустымif ( m_EventList.empty() )
{
mutex::scoped_lock lock(monitor);
m_NewEvent.wait(lock);
}
// Предотвращаем одновременную генерацию событий из разных потоков.
// (При использовании нескольких экземпляров компоненты в одном процессе)
ExternalLock ext_lock;
mutex::scoped_lock cs_InvokeEvent(m_InvokeEventMutex);
if ( !ExtractEvent(name, args) )
return;
DispParams dp(args);
cs_InvokeEvent.unlock();
hr = pIConnectionPoint->EnumConnections( &pEnumConnections );
cs_InvokeEvent.lock();
if (hr == RPC_E_CALL_CANCELED)
return;
ML_ASSERT( SUCCEEDED(hr) );
CONNECTDATA cd;
cs_InvokeEvent.unlock();
hr = pEnumConnections->Next(1, &cd, NULL);
cs_InvokeEvent.lock();
if (hr == RPC_E_CALL_CANCELED)
return;
ML_ASSERT( SUCCEEDED(hr) );
cs_InvokeEvent.unlock();
hr = cd.pUnk->QueryInterface(IID_IDispatch, (void**)&pIEvent);
cs_InvokeEvent.lock();
if (hr == RPC_E_CALL_CANCELED)
return;
ML_ASSERT( SUCCEEDED(hr) );
BStr memberName( TtoW::from(name).c_str() );
DISPID dispid;
cs_InvokeEvent.unlock();
hr = pITypeInfo->GetIDsOfNames( memberName, 1, &dispid);
cs_InvokeEvent.lock();
if (hr == RPC_E_CALL_CANCELED)
return;
ML_ASSERT( SUCCEEDED(hr) );
cs_InvokeEvent.unlock();
hr = pIEvent->Invoke(dispid, IID_NULL,
GetUserDefaultLCID(), DISPATCH_METHOD,
dp.get(), NULL, NULL, NULL);
cs_InvokeEvent.lock();
if (hr == RPC_E_CALL_CANCELED)
return;
ML_ASSERT( SUCCEEDED(hr) );
}
При старте событийный поток вызывает CoInitialize(NULL).
Если клиент, работает только с одним экземпляром COM объекта, то все отлично.
Как только клиент использует несколько экземпляров, все падает в тот момент, когда COM объекты начинают генерирование событий. Причем такой эффект не наблюдался на 2-х компьютерах, на которых велась разработка, он проявился на компьютере заказчика уже на объекте, где не представляется возможным подключиться отладчиком.
Долгие размышления привели меня к мысли использовать синглетный мьютекс в процедуре потока:
// Предотвращаем одновременную генерацию событий из разных потоков.
// (При использовании нескольких экземпляров компоненты в одном процессе)
ExternalLock ext_lock;
т. е. мьютекс запрещает одновременное обращение к интерфейсу IDispatch из разных потоков.
Таким образом все работает, но мне непонятно почему проблема не проявилась сразу на компах разработчиков.
И вообще, правильно ли такое решение? Может в COM имеется более изящный подход к подобным проблемам?
не обращайте внимания. Они нужны для отмены события при выгрузки компоненты.
Т. е. они нужны для того, чтобы завершающий поток мог отменить вызов лишь в тот момент, когда событийный поток совершает вызов COM-метод. эти вызовы не имеют прямого отношения к этой теме.
Здравствуйте, silart, Вы писали:
S>Может эта проблема связана с апартментами?
Может быть, если Вы передаёте в свои потоки событийные интерфейсы без маршалинга. Но по идее оно должно проявляться на всех компьютерах. Правда тут есть некоторые нюансы, связанные с тем, работаете ли Вы с объектом напрямую или через прокси. В общем, в COM есть правило — между апартаментами интерфейсные ссылки должы передаваться только через маршалинг, и это требование необходимо неукоснительно соблюдать. То есть в некоторых случаях это можно обойти, но это чистой воды хак. Я могу рекомендовать воспользоваться Global Interface table (IGlobalInterfaceTable), очень удобно для внутрипроцессного маршалинга.
Здравствуйте, Мизантроп, Вы писали:
М>Может быть, если Вы передаёте в свои потоки событийные интерфейсы без маршалинга. Но по идее оно должно проявляться на всех компьютерах. Правда тут есть некоторые нюансы, связанные с тем, работаете ли Вы с объектом напрямую или через прокси. В общем, в COM есть правило — между апартаментами интерфейсные ссылки должы передаваться только через маршалинг, и это требование необходимо неукоснительно соблюдать. То есть в некоторых случаях это можно обойти, но это чистой воды хак. Я могу рекомендовать воспользоваться Global Interface table (IGlobalInterfaceTable), очень удобно для внутрипроцессного маршалинга.
Про IGlobalInterfaceTable я в курсе. Я его использую. У меня генерируются события из внутреннего потока внутри компоненты. Это все работает если клиент загружает один экземпляр компонента. Все начинает падать, когда клиент пытается загрузить несколько экземпляров компонента. Так как у нас inproc-сервер, компонент — это dll и все находится в одном адресном пространстве клиента. Как только несколько экземпляров компонента начинают генерировать события одновременно, все падает. Я поставил синглетный мьютекс как раз в то место, чтобы предотвратить генерацию событий разными потоками одновременно. Сейчас разные потоки генерируют события поочередно.
Хотелось бы выяснить в чем проблема и решается ли она средствами COM.
Здравствуйте, silart, Вы писали:
S>Хотелось бы выяснить в чем проблема и решается ли она средствами COM.
Ну если у Вас нигде нет прямых передач интерфейсов без маршалинга и используется STA, то боюсь, что проблема не в СOM, а просто имеют место ошибки в Вашем коде. Дело в том, что вызовы в ST апартамент и так сериализованы, то есть поступают последовательно, и обработка нового вызова в данном апартаменте не начнётся, пока не завершится предыдущий. А потоки, из которых эти вызовы пришли (если они тоже STA), будут крутить свой внутренний Message loop для обеспечения реентерабельности, либо, в случае MTA, будут просто заблокированы, если правильно помню. Судя по тому, что добавление Вами явной синхронизации проблему снимает, то скорее всего имеет место всё-таки какая-то путаница с потоковыми моделями, а из Вашего изложения реализованная Вами схема выглядит весьма туманно, извините.
Разное же проявление на разных машинах может быть связано с разным количеством процессоров или ядер на этих машинах.
реализованная Вами схема выглядит весьма туманно, извините.
Внесем ясность.
Вот потоковая функция, которая посылает события (Теперь я из нее убрал все лишнее).
void EventManager::Routine()
{
HRESULT hr = E_FAIL;
tstring name;
vector<ml::com::Variant> args;
com_ptr<IDispatch, IID_IDispatch> pIEvent;
com_ptr<IEnumConnections, IID_IEnumConnections> pEnumConnections;
// Блокировка потока, если список стал пустымif ( m_EventList.empty() )
{
mutex::scoped_lock lock(monitor);
m_NewEvent.wait(lock);
}
// Предотвращаем одновременную генерацию событий из разных потоков.
// (При использовании нескольких экземпляров компоненты в одном процессе)
ExternalLock ext_lock;
if ( !ExtractEvent(name, args) )
return;
DispParams dp(args);
hr = pIConnectionPoint->EnumConnections( &pEnumConnections );
ML_ASSERT( SUCCEEDED(hr) );
CONNECTDATA cd;
hr = pEnumConnections->Next(1, &cd, NULL);
ML_ASSERT( SUCCEEDED(hr) );
hr = cd.pUnk->QueryInterface(IID_IDispatch, (void**)&pIEvent);
ML_ASSERT( SUCCEEDED(hr) );
BStr memberName( TtoW::from(name).c_str() );
DISPID dispid;
hr = pITypeInfo->GetIDsOfNames( memberName, 1, &dispid); // &lpolestrName
ML_ASSERT( SUCCEEDED(hr) );
hr = pIEvent->Invoke(dispid, IID_NULL,
GetUserDefaultLCID(), DISPATCH_METHOD,
dp.get(), NULL, NULL, NULL);
ML_ASSERT( SUCCEEDED(hr) );
cd.pUnk->Release();
}
Вот конструктор, основная цель которого — получить pITypeInfo, который будет использоваться другим потоком.
EventManager::EventManager(IID lib_id, IID event_id)
: m_EventId(event_id)
{
HRESULT hr;
com_ptr<ITypeLib, IID_ITypeLib> pITypeLib;
hr = ::LoadRegTypeLib(lib_id, 1, 0, 0x00, &pITypeLib);
ML_ASSERT( SUCCEEDED(hr) );
// Get type information for the interface of the object.
hr = pITypeLib->GetTypeInfoOfGuid(event_id, &pITypeInfo);
ML_ASSERT( SUCCEEDED(hr) );
pIConnectionPoint.reset( new ConnectionPoint(this, m_EventId) );
}
Понятно, что конструктор вызывается другим (по отношению к отправляющему события) потоком.
Таким образом, pITypeInfo получается в конструкторе одним потоком, а используется в потоковой функции другим потоком. Нужно ли здесь использовать маршалинг? Ведь по идее должно было все обрушиться и при одном компоненте, а все работает.
Событийный поток при старте вызывает CoInitialize()
Мизантроп, если речь идет о компоненте без событий, то с STA все понятно. А как задать STA, если речь идет о генерации событий? Как задать STA именно для событийного потока явно?
А могут быть проблемы от "кривости" клиента? Клиент написан на Borland C++ Builder.
Здравствуйте, silart, Вы писали:
S>Внесем ясность.
Удачная мысль
S>Понятно, что конструктор вызывается другим (по отношению к отправляющему события) потоком. S>Таким образом, pITypeInfo получается в конструкторе одним потоком, а используется в потоковой функции другим потоком. Нужно ли здесь использовать маршалинг? Ведь по идее должно было все обрушиться и при одном компоненте, а все работает.
Ну а с чего бы ему обрушаться Когда Вы просто передаёте куда-то интерфейс, без маршалинга, Вы фактически передаёте адрес таблицы в памяти, содержащей адреса методов, реаслизующих этот интерфейс. При этом вызов метода интерфейса — это просто самый обыкновенный вызов функции по адресу. Вы же не пишите в реализации каждого метода проверку валидности текущего апартамента и не генерите по этому поводу исключений, откуда же возьмётся обрушение? И никто этого не делает, потому что это не задача объекта. Это задача для прокси, и у него есть для этого необходимые средства. Когда Вы попытаетесь вызвать метод прокси из чужого для него апартамента, тот обратится за RPC буфером к менеджеру канала, а тот в ответ вернёт WRONG_THREAD. В случае же прямого вызова такой проверки сделать просто некому.
S>Мизантроп, если речь идет о компоненте без событий, то с STA все понятно. А как задать STA, если речь идет о генерации событий? Как задать STA именно для событийного потока явно?
Апартаментная модель для потока всегда задаётся одинаково — вызовом CoInitialize(Ex).
Потоковая модель клиента — это одно, а поддерживаемая сервером — другое, и они друг от друга не зависят. Каждый из них выбирает ту модель, которую считает для себя наиболее подходящей, и маршалинг как раз и служит для согласования этих моделей.
От наличия событий тут тоже мало что зависит, только что общая логика усложняется. И любой событийный интерфейс, и любой "инфраструктурный" интерфейс поддержки событий — это самые обыкновенные интерфейсы, и к ним применимы все те-же правила, что и к любым другим. А правило простое — любой интерфейс должен пердаваться в другой апартамент только через маршалинг.
Например, Вы похоже pIConnectionPoint передаёте без маршалинга, что уже является ошибкой.
Но вообще мне, честно говоря, не очень нравится Ваша схема. Я бы сразу при подписке маршалировал событийный интерфейс в GIT, а в потоке один раз их демаршалировал. Разумеется, это потребует реализации собственного механизма подписки — IConnectionPointContainer для этого не слишком удачный выбор, но оно того стоит, по-моему. Если конечно вообще оправдана генерация событий в отдельном потоке. Может лучше чтобы доп. поток извещал главный о завершении обработки, а тот уже генерировал бы события?
S>А могут быть проблемы от "кривости" клиента? Клиент написан на Borland C++ Builder.
Здравствуйте, Vi2, Вы писали:
Vi2>Здравствуйте, silart, Вы писали:
S>>Событийный поток при старте вызывает CoInitialize()
Vi2>А какая у тебя уверенность, что все методы объекта EventManager вызываются в том же потоке, что и конструктор?
Конструктор вызывается в одном потоке, функция потока отправки событий — в другом.
Здравствуйте, Мизантроп, Вы писали:
S>>Понятно, что конструктор вызывается другим (по отношению к отправляющему события) потоком. S>>Таким образом, pITypeInfo получается в конструкторе одним потоком, а используется в потоковой функции другим потоком. Нужно ли здесь использовать маршалинг? Ведь по идее должно было все обрушиться и при одном компоненте, а все работает.
М>Ну а с чего бы ему обрушаться Когда Вы просто передаёте куда-то интерфейс, без маршалинга, Вы фактически передаёте адрес таблицы в памяти, содержащей адреса методов, реаслизующих этот интерфейс. При этом вызов метода интерфейса — это просто самый обыкновенный вызов функции по адресу. Вы же не пишите в реализации каждого метода проверку валидности текущего апартамента и не генерите по этому поводу исключений, откуда же возьмётся обрушение?
Мезантроп, ну как же? У нас указатель получается в конструкторе (один поток), а используется в функции потока (другой поток). Вроде же разные апартменты? В этом месте я вас немного не понял. Поясните пожалуйста свою мысль.
М>Апартаментная модель для потока всегда задаётся одинаково — вызовом CoInitialize(Ex). М>Потоковая модель клиента — это одно, а поддерживаемая сервером — другое, и они друг от друга не зависят. Каждый из них выбирает ту модель, которую считает для себя наиболее подходящей, и маршалинг как раз и служит для согласования этих моделей.
М>От наличия событий тут тоже мало что зависит, только что общая логика усложняется. И любой событийный интерфейс, и любой "инфраструктурный" интерфейс поддержки событий — это самые обыкновенные интерфейсы, и к ним применимы все те-же правила, что и к любым другим. А правило простое — любой интерфейс должен пердаваться в другой апартамент только через маршалинг. М>Например, Вы похоже pIConnectionPoint передаёте без маршалинга, что уже является ошибкой.
Да, вы правы. Та же история. pIConnectionPoint создается в конструкторе (один поток), а используется в функции потока (другой поток). Опять возникает вопрос, почему все не обрушивется, когда имеется всего один экземпляр компонента? Ведь в этом случае присутствуют оба потока: и поток клиена, который создает компонент (вызов конструктора) и событийный поток, который отправляет события (функция Routine()). Мезантроп, если требуется маршалинг, подскажите как его лучше сделать?
М>Но вообще мне, честно говоря, не очень нравится Ваша схема. Я бы сразу при подписке маршалировал событийный интерфейс в GIT, а в потоке один раз их демаршалировал. Разумеется, это потребует реализации собственного механизма подписки — IConnectionPointContainer для этого не слишком удачный выбор, но оно того стоит, по-моему.
Мезантроп, GIT у мена реализован в ConnectionPoint:
М>Если конечно вообще оправдана генерация событий в отдельном потоке. Может лучше чтобы доп. поток извещал главный о завершении обработки, а тот уже генерировал бы события?
Мезантроп, у меня все работает следующим образом:
Внутри компонента есть рабочий поток, который генерирует данные. Этот поток нельзя затормаживать отправкой событий, поскольку скорость их обработки целиком возложена на клиента. Для рабочего потока быстродействие критично, поэтому он формирует данные в специальные пакеты и ставит в очередь событий. Есть событийный поток, который обслуживает эту очередь: то есть занимается непосредственным отправлением. Событийный поток нужен для асинхронной генерации событий.
Может у вас будут каки-нибудь мысли по модернизации моей модели?
Здравствуйте, silart, Вы писали:
S>Мезантроп, ну как же? У нас указатель получается в конструкторе (один поток), а используется в функции потока (другой поток). Вроде же разные апартменты? В этом месте я вас немного не понял. Поясните пожалуйста свою мысль.
Честно говоря, я даже не знаю, что ещё сказать Ну а отчего здесь что-то должно обрушиться-то? Попробуйте сформулировать прричину обрушения, как Вы её видите но не в терминах "один поток, другой поток", а более конкретно, типа, вот мы вызвали функцию, произошло то-то, из-за этого такой-то механизм или такой-то код возбудил исключение...или что Вы подразумеваете под обрушением? В интерфейсах никакой магии нет, всё вполне приземлённо, и работает только тот код, который имеется вналичии. Я же уже писал, интерфейс — только лишь адрес таблицы. Или можно сказать так, интерфейс == абстрактный класс. Вот Вы объявили переменную типа такого абстрактного класса, потом записали в неё адрес потомка этого класса, а затем начали из другого потока вызывать методы через эту переменную. Что здесь может "обрушиться" и главное кем?
Откровенно говоря, я сегодня сильно устал, и особо вникать мне не хочется, извините Я у Вас вижу маршалинг в GIT, а демаршалинг где? Иными словами, кто и как реализует IEnumConnections? Маршалировать ведь недостаточно, нужно ещё демаршалировать в том потоке, где интерфейс будет использоваться.
Здравствуйте, silart, Вы писали:.
S>Может у вас будут каки-нибудь мысли по модернизации моей модели?
Для построения оптимальной модели нужно знать множество аспектов, присущих конкретно Вашему приложению. Как относительно обобщённый вариант можно предложить следующее:
Контейнер событий реализуете в виде отдельного объекта (КС). Сам COM-объект (O) при создании запускает доп. поток и ждёт окончания его инициализации, например с помощью Event'a или семафора. Доп. поток создаёт КС, маршалирует его интерфейс IConnectionPointContainer и извещает О взводом Event'а о завершении своей инициализации. Объект О демаршалирует IConnectionPointContainer и в дальнейшем по запросу клиента возвращает именно его, для этого можно просто переопределить реализацию QueryInterface. Получается, что О делегирует реализацию IConnectionPointContainer объекту КС. Как результат вся работа с событийными интерфейсами будет автоматически перенесена в доп. поток и никаких дополнительных действий по маршалингу — демаршалингу событийных интерфейсов не потребуется.
Вы не знаете из-за чего может виснуть вызов Release() ?
Я вызываю Release() на интерфейс, полученный из GIT (это Event Sink) и в некоторых случаях он зависает. Т. е. поток в него входит и не может выйти.
Такая же ситуация с зависанием CoUninitialize().
CoUninitialize() вызывается в процедуре завершения потока, т. е. это последнее что вызывает поток.
Причем в если получение Event Sink из GIT поместить в одно место (процедура инициализации потока) то CoUninitialize() зависает постоянно. Указатель на Event Sink хранится все время, пока живет объект.
А если получение Event Sink из GIT поместить в процедуру потока, зависания не происходит, т. е. указатель на интерфейс Event Sink не будет храниться в переменной, а получаться в момент вызова события.
Я понимаю, это просто слова... Но хотелось бы узнать почему может зависнуть CoUninitialize() и Release(). Интересуют причины этого.
Есть компонент, inproc-сервер. У него есть методы и события. Внутри компонента есть рабочий поток, генерирующий данные. События по идее он и должен генерировать, но так как обработка событий ложится на клиента, а поток критичный по времени, этого допустить нельзя. Иначе клиент будет затормаживать работу компонента. Значит нужно создать другой поток, который будет генерировать события из данных, предоставляемых ему рабочим потоком.
Но событийный поток должен кто-то запускать и останавливать. Для этого есть 2 метода у компонента: Init() и Done(). Метод Init() вызывается в конструкторе окна клиента, Done() соответственно — в деструкторе.
Здесь и кроется проблема.
Проблема состоит в следующем: Если метод Done() будет вызван во время обработки события клиентом (ну то есть в обработчике события), произойдет взаимная блокировка. Событийный поток ждет обработкт события, поток клиента ждет когда выгрузится компонента и завершатся ее все потоки. А потоки не могут все завершится, потому что событийный поток ждет обработки события.
Для того чтобы она не происходила, я завершаю вызов события принудительно:
Хотел бы узнать, правильный подход к такой проблеме? Ситуация ведь довольно распространенная. Каким образом принято строить компоненты, которые генерируют события из отдельного потока? Как принято организовывать управление событийным потоком?
После того, как происходит принудительный выход, умные указатели начинают вызывать Release(). в этот момент происходит зависание потока.
Такое же зависание происходит когда поток пытается вызвать Counitialize(). Причем, если принудительную отмену вызова делать не пришлось (то есть если событийный поток в момент сигнала к остановке занимался не вызовом очередного события), зависания не происходит. Оно произойдет, если событийный поток будет принудительно отменен.
Может ли быть связанно зависание с тем, что EventSink в клиенте уже уничтожился к тому времени, когда я вызвал Release()? Ведь завершение потока происходит во время закрытия окна клиента. А если не вызывать Release(), тогда виснет Counitialize().
Хотел бы уяснить для себя следующие вопросы:
1. Правильный ли подход к организации асинхронных событий я использую?
2. Из-за чего происходит зависание Counitialize() и Release()?
Прочитав этот Ваш пример, я сделал то же самое. Переопределил методы Advise и Unadvise, добавив в них занесение интерфейса в GIT.
Действительно, интерфейс регистрируется в GIT, hr = S_OK, а потом удачно дерегистрируется.
И интерфейс достается из GIT без проблем. Но при отработке ничего не происходит. Почему это может быть, Вы, скорее всего, уже обошли этот вопрос.
Вот код:
template <class T>
STDMETHODIMP TEvents_UpTask<T>::Advise(IUnknown* pUnkSink,
DWORD* pdwCookie)
{
T* pT = static_cast<T*>(this);
IUnknown* p;
IID iid;
GetConnectionInterface(&iid);
HRESULT hRes = pUnkSink->QueryInterface(iid, (void**)&p);
if (SUCCEEDED(hRes))
{
pT->Lock();
DWORD myCookie;
gp_GIT->RegisterInterfaceInGlobal(p, IID_IDispatch, &myCookie);//это std::map<DWORD, DWORD> для хранения всех Cookie
m_GitPointers[*pdwCookie] = myCookie;
pT->Unlock();
}
return hRes;
}
template <class T>
STDMETHODIMP TEvents_UpTask<T>::Unadvise(DWORD dwCookie)
{
...
//Arkady Add
gp_GIT->RevokeInterfaceFromGlobal(m_GitPointers[dwCookie]);
...
}
//метод, который должен вызываться при старте потока, генерирующего данные (такого же, как у Вас).template <class T> HRESULT
TEvents_UpTask<T>::Fire_OnListenStart(void)
{
DWORD dw;
T * pT = (T*)this;
pT->Lock();
IUnknown ** pp = m_vec.begin();
while (pp < m_vec.end())
{
if (*pp != NULL)
{
dw = (DWORD)pp;
IDispatch* dp;
HRESULT hr = gp_GIT->GetInterfaceFromGlobal(m_GitPointers[dw], IID_IDispatch, (void**)&dp);
if (SUCCEEDED(hr))
{
IUpTaskEvents* Event;
hr = dp->QueryInterface(DIID_IUpTaskEvents, (void**)&Event);
if (SUCCEEDED(hr))
{
Event->OnListenStart();
}
}
}
pp++;
}
pT->Unlock();
}
Так вот, сервер попадает в Event->OnListenStart(); и выполняет этот метод, но при этом клиентский обработчик об этом почему-то не знает.
Когда то же самое событие вызывается в основном потоке сервера, клиентская реализация интерфейса отрабатывает. А тут — нет.
Не знаете, почему?
Функция потока, вызывается периодически потоком, генерирует события.
void EventManager::Routine()
{
HRESULT hr = E_FAIL;
tstring name;
vector<ml::com::Variant> args;
DISPID dispid;
// Блокировка потока, если список стал пустымif ( m_eventList.empty() )
{
mutex::scoped_lock lock(monitor);
m_NewEvent.wait(lock);
}
// Берем новое событие из очередиif ( !ExtractEvent(name, args) )
return;
// Готовим для него все необходимое
BStr memberName( TtoW::from(name).c_str() );
DispParams dp(args);
hr = m_typeInfo->GetIDsOfNames( memberName, 1, &dispid);
ML_ASSERT( SUCCEEDED(hr) );
// Вызываем.
hr = m_eventSink->Invoke(dispid, IID_NULL,
GetUserDefaultLCID(), DISPATCH_METHOD,
dp.get(), NULL, NULL, NULL);
ML_ASSERT( SUCCEEDED(hr) );
}
Т. е. у меня поток в начальный момент времени при старте получает Event Sink, сохраняет его в переменной внутри класса, при этом полученный интерфейс Event Sink'а используется напрямую.
Здравствуйте, silart, Вы писали:
S>Т. е. у меня поток в начальный момент времени при старте получает Event Sink, сохраняет его в переменной внутри класса, при этом полученный интерфейс Event Sink'а используется напрямую.
Сделал — то же самое :( Причем попробовал сохранять в GIT и извлекать, используя напрямую, как IDispatch, так и сам IUpTaskEvents.
В случае, если FireEvent вызывается в основном потоке — событие исполняется, всё как и должно быть. В случае, если в слушающем потоке — не работает.
Может быть дело в том, что клиентом моему серверу выступает VBA приложение? =(
Здравствуйте, Аркадий, Вы писали:
А>Сделал — то же самое Причем попробовал сохранять в GIT и извлекать, используя напрямую, как IDispatch, так и сам IUpTaskEvents. А>В случае, если FireEvent вызывается в основном потоке — событие исполняется, всё как и должно быть. В случае, если в слушающем потоке — не работает.
Аркадий, а каким образом вы используете GIT?
У меня с этим были тоже проблемы. Нужно делать так:
1. Тот поток, который вызывает Advise() должен создать GIT, поместить в него указатель, освободить указатель на GIT.
2. Поток, который генерирует события, тоже должен создать GIT, получить интерфейс по куки, сохранить полученный интерфейс, освободить указатель на GIT.
Если ты в каком-то потоке создашь GIT, сохранишь указатель на него и будешь его использовать из другого потока, то работать не будет. По крайней мере у меня так не работало. GIT нужно создавать в тот моент когда он становится нужен.