![]() |
|
![]() ![]() ![]() |
|
ivs4 |
|
||||||||||
Новичок Профиль Группа: Участник Сообщений: 27 Регистрация: 19.7.2004 Репутация: нет Всего: нет |
Добрый день. Столкнулся с нетривиальной задачей. Делаю обертку над сторонней библиотекой в виде COM объекта в ATL. В этой библиотеке есть некай ф-ция результат выполнения которой можно узнать через асинхронно через Callback ф-цию. Задача же стоит в вызове метода объекта при наступлении Callback. Сначала подготовил свой объект для вызова ивентов.
Делаю первый метод ком объекта без параметров, он отвечает за назначение callback. Сначала хотел сделать CallBack методом класса ком объекта через такой изврат:
При таком раскладе вызов Callback происходит но проблема в вызове метода определенного мною метода события, а конкретно ошибка происходит в этом месте функции события:
те как я понял при таком назначениию callback я не могу корректно обращаться к членам родителя моего класса к таким как IConnectionPointImpl. Хорошо, решил уйти от callback как члена класса и сделал его статическим методом класса. Кроме того ввел в класс статический указатель на самого себя:
вроде все хорошо, callback вызывается но опять проблема с вызовом событийного события Fire_CallBackDone. Выполнение в этом методе ф-ции
Возвращало ошибку -2147221008 (800401F0) Не был произведен вызов CoInitialize. переработал Callback
после этого тот же вызов Invoke выдал ошибку -2147417842 (8001010E) Приложение обратилось к интерфейсу, относящемуся к другому потоку. На сегодняшний момент прихожу к выводу, что невозможно вызвать из callback событие в рамках COM идеология. Думаю уходить от этого решения. Последняя надежда на ваше мнение. Если вдохнете надежду, то буду продолжать. Это сообщение отредактировал(а) ivs4 - 1.7.2009, 16:23 |
||||||||||
|
|||||||||||
xvr |
|
|||
Эксперт ![]() ![]() ![]() ![]() Профиль Группа: Комодератор Сообщений: 7046 Регистрация: 28.8.2007 Где: Дублин, Ирландия Репутация: 8 Всего: 223 |
Вызвать можно, но не просто
![]() Для начала мелкое замечание - CoInitialize нужно вызывать в начале каждого потока, где планируется работать с COM объектами. Для вызова объектов между разными потоками (т.е. создан он был в одном, а вызывать его надо в другом) нужно передать сам объект в тот поток, где его планируется вызывать (см CoMarshalInterThreadInterfaceInStream и CoGetInterfaceAndReleaseStream). И еще очень желательно, что бы этот поток крутил цикл обработки очереди сообщений Windows (иначе половина COM инфраструктуры отвалится) |
|||
|
||||
ivs4 |
|
|||
Новичок Профиль Группа: Участник Сообщений: 27 Регистрация: 19.7.2004 Репутация: нет Всего: нет |
Здравствуйте, xvr.
Как я думаю, Callback вызывается в своем потоке, не в том, где создается COM объект и соответственно в своем апартменте. Тогда понятно природа ошибок и нужность маршалинга. Но возникают вопросы: Каким образом осуществить маршалинг. Т.е. вы предлагаете в статической переменной вместо того как у меня хранения указателя на класс CMyClass. хранить IStream из которого потом в CallBack доставать указатель на интерфейс CMyClass.
Правильно я Вас понял? |
|||
|
||||
xvr |
|
|||
Эксперт ![]() ![]() ![]() ![]() Профиль Группа: Комодератор Сообщений: 7046 Регистрация: 28.8.2007 Где: Дублин, Ирландия Репутация: 8 Всего: 223 |
В общем так. Вся прелесть в деталях
![]() В том потоке, где был созданн COM объект надо записать его в стрим (с помощью CoMarshalInterThreadInterfaceInStream) и записать полученный стрим в глобал. Затем, в потоке, где надо объект вызывать, делается CoGetInterfaceAndReleaseStream. Полученный объект сохраняется опять же в глобале. Затем все вызовы делаются с этим объектом. Вызывать пару CoInitialize + CoUninitialize на каждый callback не стоит - это довольно дорогая операция Главный вопрос как синхронизировать создание стрима с объектом и его использование, т.к. это должно делаться в разных потоках. Для этого можно воспользоваться сообщениями Windows (т.к. поток-приемник все равно должен крутить цикл обработки сообщений, что бы COM нормально работал). Можно применить и другие методы. Callback'и вызываются в заранее известном потоке, или о их месте вызова ничего неизвестно? |
|||
|
||||
jonie |
|
|||
Эксперт ![]() ![]() ![]() ![]() Профиль Группа: Завсегдатай Сообщений: 5613 Регистрация: 21.8.2005 Где: Владимир Репутация: 5 Всего: 118 |
можно без сериализации в IStream жить между потоками используя GIT. http://forum.sources.ru/index.php?showtopic=40322 вот примерное объяснение. Это несколько (?) быстрее чем марашалить как делаете вы, но имеет ряд ограничений.
-------------------- Что-то не поняли? -> Напейтесь до зеленых человечков... эта сверхцивилизация Вам поможет... |
|||
|
||||
ivs4 |
|
||||||||||||||||
Новичок Профиль Группа: Участник Сообщений: 27 Регистрация: 19.7.2004 Репутация: нет Всего: нет |
Давайте еще раз обрисуем ситуацию. Вот описание CMyCllass:
обратите внимание на наследование от CProxy_IMyClassEvents. Он отвечает за вызова рукотворных событий. Вот он
Так вот как раз это метод я и вызываю из моего CallBack
1. Как я уже говорил, сначала была проблема с назначением calback в методе SetCallback. Извратился и сделал с помощью typedef и приведения адреса ф-ции к типу long. 2. Потом встала проблема с вызовом из callback метода отвечающего за событие. Это я сделал через статический указатель на сам класс CMyClass. Считаю это не совсем корректным для многопользовательского режима, но там посмотрим. Пока оставил так. 3. Далее спотыкания пошли на Fire_CallbackDone. Не хотелась выполняться строка
Если посмотреть на ф-цию Fire_CallbackDone, то виден цикл, который пробегает по всем членам динамического массива IUnknown интерфейсов m_vec. Там хранятся точки в которых от нас ждут вызова события, например в VBA
По вашему совету, добавил CMyClass укfзатель на IStream. в методе SetCallBack добавил маршпалинг диспатч интерфейса, который используется в Fire_CallbackDone
в сам CallBack добавил
где ptr - глобальный указатель на IDispatch. И уже через него вызываю Invoke
все проходит, hres=0 НО! ивент в бейсике все равно не отрабатывает. В общем я грешу на хранение указателя класса в статической переменной. Иначе в статическом методе до него не добраться. А как ваше мнение? |
||||||||||||||||
|
|||||||||||||||||
xvr |
|
|||
Эксперт ![]() ![]() ![]() ![]() Профиль Группа: Комодератор Сообщений: 7046 Регистрация: 28.8.2007 Где: Дублин, Ирландия Репутация: 8 Всего: 223 |
Сработает только один раз. Функция CoGetInterfaceAndReleaseStream извлекает из стрима объект и УНИЧТОЖАЕТ стрим. Т.е. при ее втором вызове (а он будет, т.к. она вставленна в CallBack и значит будет вызываться при каждом его срабатывании) ей уже будет неоткуда извлекать объект.
При таком раскладе вариант jonie с GIT выглядит предпочтительней |
|||
|
||||
ivs4 |
|
|||
Новичок Профиль Группа: Участник Сообщений: 27 Регистрация: 19.7.2004 Репутация: нет Всего: нет |
Попробовал маршилинг через GIT - то же самое. Вот эта строка в Fire_CallBackDone
выполняется без ошибок и hres=0, НО ивент из VBA не срабатывает. Подумал про цикл сообщений для доступа к маршалируемому интерфейсу, но где в ATL проекте его прицепить не пойму. |
|||
|
||||
xvr |
|
||||
Эксперт ![]() ![]() ![]() ![]() Профиль Группа: Комодератор Сообщений: 7046 Регистрация: 28.8.2007 Где: Дублин, Ирландия Репутация: 8 Всего: 223 |
Без него работать не будет.
Прицеплять надо там, где VBA создает твой объект. Хотя он там и так есть. Надо обеспечить, что бы он работал, т.е. не мешать ему своими объектами (например создать для их жизнедеятельности отдельную нить) |
||||
|
|||||
ivs4 |
|
|||
Новичок Профиль Группа: Участник Сообщений: 27 Регистрация: 19.7.2004 Репутация: нет Всего: нет |
Уважаемый, xvr, Можно об этом чуть по-подробнее. |
|||
|
||||
xvr |
|
|||
Эксперт ![]() ![]() ![]() ![]() Профиль Группа: Комодератор Сообщений: 7046 Регистрация: 28.8.2007 Где: Дублин, Ирландия Репутация: 8 Всего: 223 |
Поподробнее можно.
Это означает, что если методы вашего класса вызываются из VBA (да и не только) вы не имеете права запускать внутри них длительную обработку. Вся работа подсистемы COM по доставке сообщений и вызовов будет заморожена, пока вы не вернетесь из вызванного метода обратно в VBA Так же это означает, что если вы создаете свои нити (или кто то создает их от вашего имени - с точки зрения VBA), то в этих нитях должен работать цикл обработки сообщений, если вы собираетесь пользоваться в них инфраструктурой COM |
|||
|
||||
ivs4 |
|
|||
Новичок Профиль Группа: Участник Сообщений: 27 Регистрация: 19.7.2004 Репутация: нет Всего: нет |
Т.е. в потоке сторонней библиотеки к которой принадлежит CallBack я должен сделать цикл? Но этот поток мне доступен только когда библиотека библиотека активирует CallBack. Т.е. в этом CallBack я должен сделать цикл?
А он должен быть до или после вызова Fire_CallBackDone? Добавлено через 8 минут и 59 секунд И еще вопрос. Вечный цикл не поставишь, тогда на какое сообщение надо выходить? |
|||
|
||||
xvr |
|
|||
Эксперт ![]() ![]() ![]() ![]() Профиль Группа: Комодератор Сообщений: 7046 Регистрация: 28.8.2007 Где: Дублин, Ирландия Репутация: 8 Всего: 223 |
Цикл должен крутится со стороны VBS, там где клиенты подсоединяются к вашему CProxy_IMyClassEvents
(кстати, у вас маршалинг CProxy_IMyClassEvents расчитан только на 1 подсоединение) Кроме того, event может вообще не сработать, т.к. вы маршалите к себе чужой интерфейс, а он может быть на это не рассчитан Я бы вообще отказался от прямого вызова Fire_CallbackDone из CMyClass::Callback, а сделал бы вызов через передачу Виндового оконного сообщения |
|||
|
||||
ivs4 |
|
|||
Новичок Профиль Группа: Участник Сообщений: 27 Регистрация: 19.7.2004 Репутация: нет Всего: нет |
Спасибо за ответы, xvr.
Вы писали
Попробую расписать ваше предложение. Вы говорите о передаче оконного сообщения. Хорошо, я регистрирую новое сообщение. Затем в конструкторе класса CProxy_IMyClassEvents запускаю некий новый поток с циклом приема оконных сообщений и в случае получения им моего сообщения вызываю Fire_CallbackDone. В самом Fire_CallbackDone производить демаршалинг интерфейсов и вызывать через них ивенты в VBA. 1. Правильно ли я развернул Вашу мысль? 2. Какому HWND посылать мое сообщение о вызове CallBack? |
|||
|
||||
ivs4 |
|
|||
Новичок Профиль Группа: Участник Сообщений: 27 Регистрация: 19.7.2004 Репутация: нет Всего: нет |
Может быть сделать не ожидание сообщения, а ожидания виндового события с таймером, а между тиками проверки события обрабатывать сообщения?
|
|||
|
||||
![]() ![]() ![]() |
0 Пользователей читают эту тему (0 Гостей и 0 Скрытых Пользователей) | |
0 Пользователей: | |
« Предыдущая тема | C/C++: COM/DCOM/ActiveX/ATL/CORBA | Следующая тема » |
|
По вопросам размещения рекламы пишите на vladimir(sobaka)vingrad.ru
Отказ от ответственности Powered by Invision Power Board(R) 1.3 © 2003 IPS, Inc. |