Вопрос в следующем:
мне нужно написать объект, который бы перехватывал обычные event'ы через IConnectionPoint и запускал бы их на переданном мне IDispatch. Я загружаю tlb, вычитываю оттуда нужный мне интерфейс, выполняю advise на объекте-источнике событий. Но возникает проблема: если посмотреть в код метода advise в ATL реализации, то там есть такой кусок:
hRes = pUnkSink->QueryInterface(iid, (void**)&p);
if (SUCCEEDED(hRes))
{
pT->Lock();
*pdwCookie = m_vec.Add(p);
hRes = (*pdwCookie != NULL) ? S_OK : CONNECT_E_ADVISELIMIT;
pT->Unlock();
if (hRes != S_OK)
p->Release();
}
else if (hRes == E_NOINTERFACE)
hRes = CONNECT_E_CANNOTCONNECT;
if (FAILED(hRes))
*pdwCookie = 0;
где iid — CLSID интерфейса-источника событий (для определенности дальше я буду его называть IID_Event), для которого я говорю advise.
Т.е получается, что ATL делает дополнительную проверку и проверяет, действительно ли переданный ему указатель на интерфейс поддерживает интерфейс-источник событий (IID_Event).
И вроде бы все хорошо, но я ( и другие языки, поддерживающие позднее связывание) не могу в QueryInterface ответить, что я поддерживаю на двоичном уровне IID_Event. Я могу честно ответить, что я поддерживаю IUnknown и IDispatch.
Дальше если посмотреть исходники запуска событий в ATL будет все равно видно, что события вызываются через IDispatch.
Т.е. я могу формально подправить QueryInterface и добавить туда строчку, что я поддерживаю IID_Event, но если вдруг объект-источник событий решит вызвать через указатель методы IID_Event напрямую, не через IDispatch, то тут же получит ошибку доступа к памяти.
Я поискал по интернету, но наткнулся лишь на одну статью с borland'a, где они решают похожую задачу, так там они действительно в QueryInterface объявляют о поддержке IID_Event, но возвращают просто указатель на IDispatch.
Может быть есть какие-нибудь соглашения о вызове методов на [default,source] интерфейсах?
Или нужна какая-нибудь дополнительная обработка?
Re: [default.source] дуальные интерфейсы и позднее связывани
Это большой недостаток коннекшн поинтов. Обычно используется диспинтерфейс.
Обойти можно, но есть много телодвижений.
Обычно все исходящие интерфейсы являются наследниками от IDispatch.
Если есть диспатч — работай с ним. Если нет — работай напрямую.
Все это можно выяснить из конкретного интерфейса.
Обычно эта ответственность лежит на сервере.
IE, например, проверяет на IDispatch.
Ну и нужно делать дуальный интерфейс, если в IID_Event засовываешь в QueryInterface.
SNN>Вопрос в следующем: SNN>Т.е. я могу формально подправить QueryInterface и добавить туда строчку, что я поддерживаю IID_Event, но если вдруг объект-источник событий решит вызвать через указатель методы IID_Event напрямую, не через IDispatch, то тут же получит ошибку доступа к памяти.
SNN>Я поискал по интернету, но наткнулся лишь на одну статью с borland'a, где они решают похожую задачу, так там они действительно в QueryInterface объявляют о поддержке IID_Event, но возвращают просто указатель на IDispatch.
SNN>Может быть есть какие-нибудь соглашения о вызове методов на [default,source] интерфейсах? SNN>Или нужна какая-нибудь дополнительная обработка?
Re: [default.source] дуальные интерфейсы и позднее связывани
На сколько мне известно, default source интерфейсы обычно объявляются как dispinterface. В этом случае можно смело возвращать S_OK на запрос IID_Event.
Вроде бы есть соглашение, что методы source интерфейса [применительно к ActiveX объектам] вызываются только через IDispatch. В принципе, можно поэкспериментировать на VB (VBA): создать объект с дуальным source интерфейсом и генерировать события напрямую. Сам не проверял, но полагаю, что VB поплохеет.
Чтобы хорошо работать, надо получать от этого удовольствие! (c) Michael Schumacher
Re[2]: [default.source] дуальные интерфейсы и позднее связыв
Здравствуйте, AlexanderK, Вы писали:
AK>Здравствуйте, SloNN, Вы писали:
AK>На сколько мне известно, default source интерфейсы обычно объявляются как dispinterface. В этом случае можно смело возвращать S_OK на запрос IID_Event.
AK>Вроде бы есть соглашение, что методы source интерфейса [применительно к ActiveX объектам] вызываются только через IDispatch. В принципе, можно поэкспериментировать на VB (VBA): создать объект с дуальным source интерфейсом и генерировать события напрямую. Сам не проверял, но полагаю, что VB поплохеет.
Я проверял, падает, и IE падает тоже .
Хорошо, тогда буду честно заявлять о поддержке IID_Event.
Спасибо.
Re[3]: [default.source] дуальные интерфейсы и позднее связыв
Да, я забыл сказать с самого начала, что интерфейс, на котором я перехватываю события — дуальный.
Когда я загружаю tlb, я проверяю на то, что интерфейс, на котором нужно перехватывать события — дуальный, только тогда начинаю делать advise на нем.
PE>Это большой недостаток коннекшн поинтов. Обычно используется диспинтерфейс. PE>Обойти можно, но есть много телодвижений.
Если не секрет — то как? У меня был только один вариант, что сделать перехват native вызовов к интерфейсу типа того, как сделано в ATL _QIThunk( ставя свои обработчики на первые 1024 метода интерфейса) и переделывает его в вызовы диспатча, но мне показалось это каким-то корявым.
PE>Обычно все исходящие интерфейсы являются наследниками от IDispatch. PE>Если есть диспатч — работай с ним. Если нет — работай напрямую. PE>Все это можно выяснить из конкретного интерфейса.
PE>Обычно эта ответственность лежит на сервере. PE>IE, например, проверяет на IDispatch.
Да, я видел, но только не совсем понял зачем он это делает? Был у меня, конечно, вариант, что он просто делает запрос к IDispatch и отвечает в QueryInterface S_OK на IID_Event, если IID_Event дуальный интерфейс и я поддержал IDispatch.
PE>Ну и нужно делать дуальный интерфейс, если в IID_Event засовываешь в QueryInterface.
Re: [default.source] дуальные интерфейсы и позднее связывани
1. Если я правильно тебя понял, то проблемма у тебя с этим IID_Event. Обьесни пожалуйсто что означает это по подробнее:
И вроде бы все хорошо, но я ( и другие языки, поддерживающие позднее связывание) не могу в QueryInterface ответить, что я поддерживаю на двоичном уровне IID_Event.
2. Для конекшнпоинтов который производные от IDispatch можно пользовать IID_IDispatch. Обычно работает. Так что этого вполне достаточно:
Я могу честно ответить, что я поддерживаю IUnknown и IDispatch.
... << RSDN@Home 1.0 beta 6a >>
Народная мудрось
всем все никому ничего(с).
Re: [default.source] дуальные интерфейсы и позднее связывани
SNN>Может быть есть какие-нибудь соглашения о вызове методов на [default,source] интерфейсах?
SNN>Или нужна какая-нибудь дополнительная обработка?
Соглашений, отличных от существующих в СОМе, нет. Хорошая дискуссия была ранее в этоим форуме (и как раз по дуальному событийному интерфейсу).
1 момент. Событийный интерфейс — это обычный СОМ интерфейс, в котором сервер вызывает объект клиента, т.е. меняется местами: сервер становится клиентом, а клиент — сервером.
2 момент. Сервер (на то он и сервер) уже написан, если рассматривать со стороны клиента, который имеет желание подцепиться к серверу. Поэтому однозначно сервер использует либо диспатч-вызовы (причем сразу по Invoke, без GetIDsOfNames) либо vtable-вызовы.
Если сервер написан с диспатч-вызовами, то Скрипты подсоединяются к событиям через запрос IProvideClassInfo или IProvideClassInfo2 на объекте. Через который они получают информацию о IID событийного интерфейса, который и удовлетворяют в QueryInterface. Неправда, что они это не умеют. Они прекрасно запрашивают QueryInterface на нужные интерфейсы и тогда, когда им надо. Юзеру они не дают таких средств, но это другое дело.
Если Скрипт определяет, что событийный интерфейс — это дуальный или custom, то вполне возможно, что Скрипт отвергнет такое подсоединение, если не может реализовать свой синк. Что и делает, например, VB для дуального интерфейса. Или же попытается подсоединиться через свой диспинтерфейс синка с последующим вылетом.
Поэтому, что заставило тебя использовать дуальные событийные интерфейсы? Можно ли их перевести в обычные дисп-интерфейсы?
Если ты позиционируешь свой объект между сервером событий и получателем, то ты и должен реализовать как свое подключение к существующему серверу, так и клиентское подключение к своему объекту. Если я правильно понял твою модель.
SNN>Может быть есть какие-нибудь соглашения о вызове методов на [default,source] интерфейсах?
SNN>Или нужна какая-нибудь дополнительная обработка?
Vi2>Если сервер написан с диспатч-вызовами, то Скрипты подсоединяются к событиям через запрос IProvideClassInfo или IProvideClassInfo2 на объекте. Через который они получают информацию о IID событийного интерфейса, который и удовлетворяют в QueryInterface. Неправда, что они это не умеют. Они прекрасно запрашивают QueryInterface на нужные интерфейсы и тогда, когда им надо. Юзеру они не дают таких средств, но это другое дело.
Я не совсем понял выделенного текста.
Я имел в виду, что у меня есть модель:
То есть у меня есть мой объект, реализующий IActiveScript, при этом сразу оговорюсь, что вносить туда изменения, чтобы добавить туда поддержку ActiveX-event'ов нельзя.
Насколько я знаю, нет способа из такого jscript файла ловить event'ы на объектах, созданных через new ActiveXObject().
Поэтому нужен некоторый объект-прокси, который будет жить между IActiveScript и внешним объектом и маршалить event'ы от ActiveX в jscript.
На рисунке я изобразил примерную диаграмму того, что должно быть: т.е сначала я получаю IUnknown(IDispatch) на объект, чьи event'ы мне надо перехватывать, после этого я опрашиваю QueryInterface( IProvideClassInfo2 ) от этого объекта, чтобы получить ссылку на ITypeLib из него и вытащить [default,source] дуальный интерфейс и в конце говорю объекту Advise( proxy ) на этот интерфейс, он мне отвечает QueryInterface( IID_Event ). Вот должен ли я в этом QueryInterface на запрос IID_Event отвечать S_OK или E_NOINTERFACE?
При этом объекты, которые нужно создать из скрипта могут быть любые.
Vi2>Если Скрипт определяет, что событийный интерфейс — это дуальный или custom, то вполне возможно, что Скрипт отвергнет такое подсоединение, если не может реализовать свой синк. Что и делает, например, VB для дуального интерфейса. Или же попытается подсоединиться через свой диспинтерфейс синка с последующим вылетом.
Vi2>Поэтому, что заставило тебя использовать дуальные событийные интерфейсы? Можно ли их перевести в обычные дисп-интерфейсы?
Нет, не могу, это чужие компоненты.
Vi2>Если ты позиционируешь свой объект между сервером событий и получателем, то ты и должен реализовать как свое подключение к существующему серверу, так и клиентское подключение к своему объекту. Если я правильно понял твою модель.
SNN>То есть у меня есть мой объект, реализующий IActiveScript, при этом сразу оговорюсь, что вносить туда изменения, чтобы добавить туда поддержку ActiveX-event'ов нельзя.
SNN>Насколько я знаю, нет способа из такого jscript файла ловить event'ы на объектах, созданных через new ActiveXObject().
SNN>Поэтому нужен некоторый объект-прокси, который будет жить между IActiveScript и внешним объектом и маршалить event'ы от ActiveX в jscript.
Я имел в виду IActiveScript::AddNamedItem("имя", ...|SCRIPTITEM_ISSOURCE ) с подключением объекта с именем "имя" через хост IActiveScriptSite::GetItemInfo. Как скрипт будет выбирать подпрограммы события (скорее всего, стратегия такая — перебирая методы, определенные у себя, и найденные как "имя_fff" или "имя::fff" и т.п. будет обработчиком) или ему нужно помогать в этом через IActiveScriptParse::AddScriptlet — я точно не знаю.
SNN>На рисунке я изобразил примерную диаграмму того, что должно быть: т.е сначала я получаю IUnknown(IDispatch) на объект, чьи event'ы мне надо перехватывать, после этого я опрашиваю QueryInterface( IProvideClassInfo2 ) от этого объекта, чтобы получить ссылку на ITypeLib из него и вытащить [default,source] дуальный интерфейс и в конце говорю объекту Advise( proxy ) на этот интерфейс, он мне отвечает QueryInterface( IID_Event ). Вот должен ли я в этом QueryInterface на запрос IID_Event отвечать S_OK или E_NOINTERFACE?
SNN>При этом объекты, которые нужно создать из скрипта могут быть любые.
Подсоединиться к событиям можно только по определенному IID интерфейса, который сервер объявил своим событийным интерфейсом. Так что ты просто обязан не просто ответить S_OK, но и сделать sink, который правильно обработает любые запросы по этому интерфейсу. Впрочем, ты можешь его ограничить только диспатч-частью, рискуя сознательно.