Здравствуйте, silart, Вы писали:
S>Если ты в каком-то потоке создашь GIT, сохранишь указатель на него и будешь его использовать из другого потока, то работать не будет. По крайней мере у меня так не работало. GIT нужно создавать в тот моент когда он становится нужен.
Вероятно, Вы что-то не так сделали. Стандартный GIT — объект с нейтральной моделью, то есть получив его интерфейс в одном апартаменте, этот интерфейс затем можно использовать в любом другом апартаменте. Фактически метод RegisterInterfaceInGlobal создаёт стрим на HGlobal, маршалирует в него интерфейс с помощью CoMarshalInterface, и сохраняет HGlobal в своём внутреннем списке. Метод GetInterfaceFromGlobal по куку ищет HGlobal, опять из него создаёт стрим и вызывает CoUnmarshalInterface. Так что никаких препятствий для использования одной ссылки на IGlobalInterfaceTable из всех апартаментов нет.
Здравствуйте, Мизантроп, Вы писали:
М>Здравствуйте, silart, Вы писали:
S>>Если ты в каком-то потоке создашь GIT, сохранишь указатель на него и будешь его использовать из другого потока, то работать не будет. По крайней мере у меня так не работало. GIT нужно создавать в тот моент когда он становится нужен.
М>Вероятно, Вы что-то не так сделали. Стандартный GIT — объект с нейтральной моделью, то есть получив его интерфейс в одном апартаменте, этот интерфейс затем можно использовать в любом другом апартаменте. Фактически метод RegisterInterfaceInGlobal создаёт стрим на HGlobal, маршалирует в него интерфейс с помощью CoMarshalInterface, и сохраняет HGlobal в своём внутреннем списке. Метод GetInterfaceFromGlobal по куку ищет HGlobal, опять из него создаёт стрим и вызывает CoUnmarshalInterface. Так что никаких препятствий для использования одной ссылки на IGlobalInterfaceTable из всех апартаментов нет.
Я пробовал и инициализировать новый указатель на GIT в каждом апартменте, и использовать один, инициализированный в одном — результат одинаков. Что-то не так — это что? Может быть, я перечислю, что есть, а Вы сможете подсказать, где там может быть ошибка?
Есть DLL, внутри которой крутится поток. На вход эта DLL принимает интерфейс IMyEvents, дергая его методы тогда, когда мне нужны события. Реализовано всё руками и всё можно переделать. Интерфейс этот содержит три метода OnStart (перед стартом цикла прослушивания сообщений), OnMessage (если пришло сообщение) и OnFinish (после завершения цикла прослушивания сообщений). Все эти события вызываются в моем доп потоке DLL.
Далее есть .exe КОМ сервер, который цепляет эту ДЛЛ. Он реализует метод OnStart — добавляя туда CoInitializeEx(NULL, MultiThreaded..), реализует метод OnFinish, добавляя туда CoUninitialize(); , и реализует метод OnMessage, добавляя туда обертку Fire_OnMessage уже своего событийного интерфейса (чтобы отрабатывалось событие).
Все методы IMyEvents выполняются в дополнительном потоке... сначала там выполнится CoInitializeEx(...), потом события, достающиеся из GIT (это зашито в Fire_OnMessage) потом CoUninitialize().
Что тут может быть не так? Правильно я понимаю, что клиент на VBA должен цеплять эти события?
Здравствуйте, Аркадий, Вы писали:
S>>>Если ты в каком-то потоке создашь GIT, сохранишь указатель на него и будешь его использовать из другого потока, то работать не будет. По крайней мере у меня так не работало. GIT нужно создавать в тот моент когда он становится нужен.
А>Есть DLL, внутри которой крутится поток. На вход эта DLL принимает интерфейс IMyEvents, дергая его методы тогда, когда мне нужны события. Реализовано всё руками и всё можно переделать. Интерфейс этот содержит три метода OnStart (перед стартом цикла прослушивания сообщений), OnMessage (если пришло сообщение) и OnFinish (после завершения цикла прослушивания сообщений). Все эти события вызываются в моем доп потоке DLL.
А>Далее есть .exe КОМ сервер, который цепляет эту ДЛЛ. Он реализует метод OnStart — добавляя туда CoInitializeEx(NULL, MultiThreaded..), реализует метод OnFinish, добавляя туда CoUninitialize(); , и реализует метод OnMessage, добавляя туда обертку Fire_OnMessage уже своего событийного интерфейса (чтобы отрабатывалось событие).
А>Все методы IMyEvents выполняются в дополнительном потоке... сначала там выполнится CoInitializeEx(...), потом события, достающиеся из GIT (это зашито в Fire_OnMessage) потом CoUninitialize().
Ну вроде в описании всё понятно, кроме нескольких вещей. Во-первых, это загадочная фраза
добавляя туда обертку Fire_OnMessage уже своего событийного интерфейса (чтобы отрабатывалось событие).
Что Вы имели в виду?
Далее, какие апартаментные модели поддерживает объект, реализующий IMyEvents? Кто и как создаёт этот объект, в какой апартамент входит поток, в котором создаётся этот объект?
И самое главное, а что не получается-то?
На всякий случай:
добавляя туда CoInitializeEx(NULL, MultiThreaded..)
То есть вызвавший этот метод поток подключается к MTA. MTA всегда один на процесс, все потоки данного процесса, вызвавшие так CoInitializeEx, являются членами одного апартамента. Маршалинга между членами одного апартамента не требуется, и переключение потоков внутри апартамента не производится, методs работают в том потоке, в котором были вызваны.
М>Ну вроде в описании всё понятно, кроме нескольких вещей. Во-первых, это загадочная фраза
М>[q]
М>добавляя туда обертку Fire_OnMessage уже своего событийного интерфейса (чтобы отрабатывалось событие).
М>Что Вы имели в виду?[/q]
Я имел ввиду, что есть интерфейс событий DLL — IMyEvents, а есть интерфейс событий сервера (.exe), который называется IUpEvents. С методом OnMessage.
Этот метод оборачивается вызовом Fire_OnMessage (где интерфейс извлекается из GIT и используется его метод).
Этот вызов Fire_OnMessage я помещаю в метод OnMessage интерфейса IMyEvents. Таким образом добиваясь, чтобы из потока dll, где дергается метод OnMessage интерфейса IMyEvents, через него дергался Fire_OnMessage.
М>Далее, какие апартаментные модели поддерживает объект, реализующий IMyEvents? Кто и как создаёт этот объект, в какой апартамент входит поток, в котором создаётся этот объект?
М>И самое главное, а что не получается-то?
1) объект, реализующий IMyEvents — обычный внутренний объект сервера, который создается .exe сервером на этапе инициализации сервера и передается в DLL. Как можно настроить ему апартментовую модель?
2) Поток, в котором создается этот объект — основной поток сервера, его потоковая модель настраивается наличием такого макроса внутри объекта
DECLARE_THREADING_MODEL(otFree);
и следующей директивы
#define ATL_FREE_THREADED
В опциях проекта (CBuilder6) установлено
Instancing: multiple use
OLE initialization COINIT_XXX_Flag: MULTITHREADED
Threading Model: Free
Это, надо понимать, распространяется не на все объекты сервера...
3) Не получается следующее: Если вызов Fire_OnMessage находится в основном потоке процесса, то они корректно отрабатываются. А помещенный в объект, реализующий IMyEvent, и вызываемый там в другом потоке — не дает никакого эффекта, т.е. полностью дебаггер проходит по телу Fire_OnMessage и не дает никакого результата.
М>На всякий случай: М>
М>добавляя туда CoInitializeEx(NULL, MultiThreaded..)
М>То есть вызвавший этот метод поток подключается к MTA. MTA всегда один на процесс, все потоки данного процесса, вызвавшие так CoInitializeEx, являются членами одного апартамента. Маршалинга между членами одного апартамента не требуется, и переключение потоков внутри апартамента не производится, методs работают в том потоке, в котором были вызваны.
Т.е. теоретически вообще всё должно отрабатывать просто при простой передаче указателя на IUpEvents? А у меня не работает даже если я его передаю через GIT..
М>Ну а лучше всего, вероятно:
=)) Там очень много методов, возможно, влияющих, может быть лучше я вышлю Вам на почту части кода, которые этим занимаются, а после того, как удастся понять, в чем дело — тогда уже сюда ту самую проблемную часть кода можно будет выложить с объяснением "для потомков"?
Здравствуйте, Аркадий, Вы писали:
А>1) объект, реализующий IMyEvents — обычный внутренний объект сервера, который создается .exe сервером на этапе инициализации сервера и передается в DLL. Как можно настроить ему апартментовую модель?
Никак, да и не нужно. Модель влияет только при создании объекта через сервис CoCreateInstance(Ex).
А>3) Не получается следующее: Если вызов Fire_OnMessage находится в основном потоке процесса, то они корректно отрабатываются. А помещенный в объект, реализующий IMyEvent, и вызываемый там в другом потоке — не дает никакого эффекта, т.е. полностью дебаггер проходит по телу Fire_OnMessage и не дает никакого результата.
А>Т.е. теоретически вообще всё должно отрабатывать просто при простой передаче указателя на IUpEvents? А у меня не работает даже если я его передаю через GIT..
Остаётся не прояснённым вопрос, откуда берётся внезапно объявившийся IUpEvents Кто его реализует, как Вы его получаете, ну и тд, всё то-же, что уже спрашивалось про IMyEvent. Нужен ли для него маршаллинг, зависит от этого. А так — да, если все объекты созданы в одном апартаменте, и этот апартамент не противоречит их потоковой модели, то всё должно работать напрямую.
Дело в том, что так — "дебаггер проходит по телу Fire_OnMessage и не дает никакого результата" — просто не бывает. Если вызов метода реально присутствует в скомпилённом коде, и выполнение до него доходит, то метод просто физически не может быть не вызван. А вот что происходит внутри этого метода, это уже другой вопрос. Может там что-нибудь типа
if (GetCurrentThreadId() != InitThreadId) return 0;
вот и получится, что при вызове этого метода из "неправильного" потока "ничего не происходит". Ну и определённая разница может быть от того, вызываете ли Вы метод самого объекта, или-же Ваш код имеет дело с прокси.
А>=)) Там очень много методов, возможно, влияющих, может быть лучше я вышлю Вам на почту части кода, которые этим занимаются, а после того, как удастся понять, в чем дело — тогда уже сюда ту самую проблемную часть кода можно будет выложить с объяснением "для потомков"?
М>Остаётся не прояснённым вопрос, откуда берётся внезапно объявившийся IUpEvents :) Кто его реализует, как Вы его получаете, ну и тд, всё то-же, что уже спрашивалось про IMyEvent. Нужен ли для него маршаллинг, зависит от этого. А так — да, если все объекты созданы в одном апартаменте, и этот апартамент не противоречит их потоковой модели, то всё должно работать напрямую.
IUpEvent объявлен уже в ..._TLB.h следующим образом:
Я его получаю при создании объекта (Up) через Wizard Builder-а, указав, что объекту нужны события.
Так выглядит автоматически создаваемая при этом голова объекта Up:
class ATL_NO_VTABLE TUpImpl :
public CComObjectRootEx<CComMultiThreadModel>,
public CComCoClass<TUpImpl, &CLSID_Up>,
public IConnectionPointContainerImpl<TUpImpl>,
// public TEvents_Up<TUpImpl>,
public TEventsWrapper<TUpTaskImpl>,
public IDispatchImpl<IUpTask, &IID_IUpTask, &LIBID_UpiterObj>
{
Замечу при этом, что TEvents_Up — это обертка интерфейса, позволяющая "дергать" методы интерфейса, который реализовал клиент и передал через advise(...). Я её переписал с использованием GIT, назвал TEventsWrapper. Работает так же, только сохраняет подключившиеся интерфейсы в GIT, а не в свой массив интерфейсов, это должно было помочь вызывать событие из другого потока, но не помогло.
М>Дело в том, что так — "дебаггер проходит по телу Fire_OnMessage и не дает никакого результата" — просто не бывает. Если вызов метода реально присутствует в скомпилённом коде, и выполнение до него доходит, то метод просто физически не может быть не вызван. А вот что происходит внутри этого метода, это уже другой вопрос. Может там что-нибудь типа
М>
М>вот и получится, что при вызове этого метода из "неправильного" потока "ничего не происходит". Ну и определённая разница может быть от того, вызываете ли Вы метод самого объекта, или-же Ваш код имеет дело с прокси.
Да, но доступ до тела Fire_OnMessage у меня есть, и я по нему "хожу" дебаггером и смотрю, что по всем своим вызовам он проходится. Один из вызовов в Fire_OnMessage — это OnMessage() интерфейса IUpEvents, который отрабатывается, не давая никакой реакции. Но доступа до тела IUpEvents у меня нет, т.к. он передавался от клиента, через advise().
При этом если Fire_OnMessage выполняется в основном потоке сервера, то OnMessage интерфейса IUpEvents отрабатывается.
Вы поймите, код Вашего приложения видите Вы, но его не видят те, кто читает Ваши посты на форуме. Ваше последнее сообщение порождает опять больше вопросов, чем даёт ответов. Например, откуда у Вас взялся файл с секретным именем "..._TLB.h"? Вам его кто-то подарил? Вы импортировали чью-то библиотеку типов? Вы сами создали библиотеку типов?
А>Да, но доступ до тела Fire_OnMessage у меня есть, и я по нему "хожу" дебаггером и смотрю, что по всем своим вызовам он проходится. Один из вызовов в Fire_OnMessage — это OnMessage() интерфейса IUpEvents, который отрабатывается, не давая никакой реакции. Но доступа до тела IUpEvents у меня нет, т.к. он передавался от клиента, через advise().
А>При этом если Fire_OnMessage выполняется в основном потоке сервера, то OnMessage интерфейса IUpEvents отрабатывается.
Если метод вызывается, значит он вызывается. Точка. Всё остальное зависит от реализации этого метода, и если у Вас нет доступа к этой реализации, то поделать Вы ничего не сможете. Остаётся, правда, не ясно — проверяете ли Вы результат вызова функций, в частности функций маршалинга и демаршалинга IUpEvents, и поддерживает ли этот интерфейс маршалинг вообще.
Я, например, понятия не имею, какие обёртки генерит билдер, и что, например, будет делать эта обёртка при отсутствии у неё реальной ссылки на внешний интерфейс.
А>Я его получаю при создании объекта (Up) через Wizard Builder-а, указав, что объекту нужны события.
Кого — его? Через какой визард? Создания объекта с поддержкой событий? Или создания обёртки для чужого событийного интерфейса?
В общем, в таком духе. Старайтесь сами себе задавать подобные вопросы, когда описываете проблему, тем более что спрашивал-то я не это, а кто, где и как создаёт инстанс, реализующий IUpEvents.
Давайте я попробую изложить то, что мне подсказывает телепатия, а Вы поправите там, где я неправ.
У Вас есть eхе-приложение. В нём определён некий COM-объект с поддержкой событий через интерфейс то-ли IUpEvents, то-ли IUpTaskEvents. Внешний клиент создаёт экзэмпляр этого объекта и подписывается на его события, передав реализованный этим клиентом интерфейс IUpEvents (или IUpTaskEvents?). В ответ Ваш COM объект загружает DLL, которой передаёт свой интерфейс IMyEvents. DLL создаёт доп. поток и из этого потока вызывает методы интерфейса IMyEvents. Метод IMyEvents.OnMessage Вашего COM-объекта вызывает функцию Fire_OnMessage, которая, в свою очередь, должна вызвать
IUpEvents.OnMessage.
Так? В методе Advise Вы маршалируете пришедший извне IUpEvents с помощью GIT. В Fire_OnMessage Вы его демаршалируете и пытаетесь вызвать его метод. Так? Если Advise вызывается в потоке-члене MTA (а именно так и должно быть судя по Вашим словам), и Fire_OnMessage вызывается членом MTA, то маршалинг между ними не нужен, всё и так обязано работать. Но и помешать он тоже не может, при демаршалинге Вы должны получить тот самый экзэмпляр интерфейсной ссылки, который маршалировали. В этом можно убедиться, просто сравнив эти ссылки друг с другом как целые или указатели.
Прежде всего убедитесь, что Вы проверяете результат всех вызываемых Вами функций, прежде всего тех, которые возвращают HResult. Далее убедитесь, что все переменные инициализируются валидными значениями. Например, если Вы намерены получить от функции интерфейс, то перед Вызовом запишите в переменную NULL, а после убедитесь, что она изменила своё значение.
Потому, что чудес не бывает, и "Если метод вызывается, значит он вызывается. Точка".