Я не знаю, есть ли API, который делает это возможным, или я должен был бы свернуть свой собственный. Вот что я пытаюсь сделать.
У меня есть приложение, которое подключается к службе NT, чтобы начать сеанс с другим COM-сервером.
На сервере сеансов будет запущен экземпляр для каждого экземпляра приложения, который подключается к службе NT. Приложение может запросить, чтобы сервер сеансов загрузил библиотеки DLL библиотеки COM, а также разместил объекты и службы из этих библиотек на сервере сеансов. DLL регистрируются через активацию без регистрации.
Создание объектов с сервера сеансов и передача их обратно в приложение работает нормально, если они являются производными от IDispatch, что является требованием всей системы, поскольку ожидается, что языки сценариев могут использовать это, и это запрашиваемый интерфейс. Приложение C ++ также может использовать объекты, размещенные на сервере сеансов. Но IDispatch — это слишком подробный интерфейс для работы с C ++.
У меня вопрос такой:
Учитывая, что размещаемые библиотеки DLL имеют два пользовательских интерфейса, о которых приложение знает, и информация о типах этих интерфейсов может быть прочитана приложением через ITypeInfo; Есть ли API, который во время выполнения создаст прокси для имитации оригинального пользовательского интерфейса, если я смогу предоставить ему интерфейс IDispatch, который также несет информацию ITypeInfo. Все, что нужно прокси-серверу, — это вызвать интерфейс IDispatch, но для C ++ он выглядит как пользовательский интерфейс. Более оптимальным решением было бы использовать тот же прокси-сервер, прокси-сервер OLE Automation по умолчанию, который DLL-библиотеки зарегистрировали в своем манифесте.
Я не могу зарегистрировать прокси / заглушки для библиотек DLL, так как несколько приложений могут иметь одинаковые модули, но разные по версии, поэтому используется активация без регистрации.
Один из вариантов — отказаться от жесткого двойного интерфейса и использовать IDispatch
-только дисперинтерфейс через жестко типизированные, сгенерированные во время компиляции интеллектуальные оболочки-указатели. Вы бы использовали VC ++ #import
с raw_dispinterfaces
и / или no_dual_interfaces
варианты для генерации тех.
Маршалер COM не нуждается в библиотеке типов для маршала IDispatch::Invoke
звонки. Однако вам нужно будет скомпилировать версию библиотеки типов / DLL, которую вы будете запускать бок о бок. Или, по крайней мере, убедитесь, что DISPID и сигнатуры методов остаются одинаковыми во всех версиях COM DLL. AFAIR, сгенерированные умные указатели не используют IDispatch::GetIdsOfNames
поэтому DISPID жестко запрограммированы.
IDispatch::Invoke
производительность может быть необязательной по сравнению с прямыми вызовами двойного интерфейса, но я не думаю, что это имеет значение, учитывая сценарий межпроцессного вызова, который вы описали. Выделенный COM-вызов из-под-процесса намного дороже, чем внутрипроцессный IDispatch::Invoke
вызов.
любой [oleautomation]
интерфейс (и любой [dual]
интерфейс) описанный в библиотеке типов может использовать маршалер библиотеки типов.
Здесь вы решаете проблему поиска заглушки-посредника с библиотекой типов. Итак, вы объявляете интерфейс и библиотеку типов в манифесте вашей сборки (прямо под assembly
элемент) вот так:
<comInterfaceExternalProxyStub name="IFooBar"iid="{IIIIIIII-IIII-IIII-IIII-IIIIIIIIIIII}"proxyStubClsid32="{00020424-0000-0000-C000-000000000046}"tlbid="{TTTTTTTT-TTTT-TTTT-TTTT-TTTTTTTTTTTT}" />
<!-- This also works for a type library embedded in a DLL -->
<file name="FooBar.tlb">
<!-- If you have multiple embedded type libraries, use the resourceid attribute -->
<typelib tlbid="{TTTTTTTT-TTTT-TTTT-TTTT-TTTTTTTTTTTT}"version="1.0" />
</file>
Если бы ваш интерфейс не был [oleautomation]
интерфейс, и вы бы хотели изолировать DLL-заглушку прокси, вы бы использовали что-то вроде этого:
<file name="FooBarPS.dll">
<comInterfaceProxyStub name="IFooBar"iid="{IIIIIIII-IIII-IIII-IIII-IIIIIIIIIIII}"proxyStubClsid32="{PPPPPPPP-PPPP-PPPP-PPPP-PPPPPPPPPPPP}"threadingModel="Both" />
</file>
comInterfaceProxyStub
очень похоже на comClass
, но сосредоточен на прокси / заглушках, и это связано с интерфейсом.
Вы можете достичь того же эффекта с парой comInterfaceExternalProxyStub
(под assembly
уровень элемента) и comClass
(под file
элемент), в случае, если вы хотите протестировать с изолированной прокси / заглушкой DLL и без нее, (не) комментируя прокси / заглушку file
раздел:
<comInterfaceExternalProxyStub name="IFooBar"iid="{IIIIIIII-IIII-IIII-IIII-IIIIIIIIIIII}"proxyStubClsid32="{PPPPPPPP-PPPP-PPPP-PPPP-PPPPPPPPPPPP}" />
<file name="FooBarPS.dll">
<comClass description="PSFactoryBuffer"clsid="{PPPPPPPP-PPPP-PPPP-PPPP-PPPPPPPPPPPP}"threadingModel="Both" />
</file>
Я не уверен, но если ваша стандартная DLL прокси / заглушки используется для более чем одного интерфейса, вы должны также использовать этот подход.
РЕДАКТИРОВАТЬ: Кажется, ничего из этого не является новым для вас. Ваша проблема в том, что в сервисе сеанса, даже если вы активируете манифест динамически загружаемых библиотек, это состояние остается только в текущем потоке. Таким образом, рабочие потоки COM (например, потоки RPC) не будут знать о ваших интерфейсах, коклассах и прокси / заглушках.
Ошибка, которую вы видите в сервисе брокера (REGDB_E_IIDNOTREG
) может происходить при маршалинге обратно из службы сеанса. Вы не получите эту ошибку в сервисе сеанса, потому что это происходит после ваши методы возвращаются.
Однако это может происходить в брокерской службе, поскольку это естественно: она не загружает библиотеки, тем более их манифесты.
Подход, который я предлагаю вам сделать, заключается в том, чтобы сервис сеанса и брокерский сервис имели манифесты, которые зависят от сборок, в которых вы объявляете COM-информацию без регистрации. Таким образом, все это будет частью дефолт контекст активации, и вам не нужно ничего делать в отношении контекстов активации.
Помните, что вы не можете контролировать контекст активации потока, которым вы не владеете, кроме того, что контекст активации по умолчанию включает в себя то, что вам нужно заранее.
Относительно этой части вашего вопроса:
Я не могу зарегистрировать прокси / заглушки для библиотек DLL, так как несколько приложений могут иметь одинаковые модули, но разные по версии, поэтому используется активация без регистрации.
Мне не понятно, что ты пытаешься сказать. Если ваши модули имеют обратную совместимость, вам не о чем беспокоиться. Если это не так, используйте разные CLSID / ProgID.
Я надеюсь, что вы не имеете в виду, что вы используете одни и те же IID с фактически разными интерфейсами, так как это является нарушением COM. Лучший способ решить эту проблему — не делать этого, точка. Другой способ — иметь выделенные потоки с выделенными контекстами активации, как в сервисе сеанса. а также в брокерском сервисе, который, как вы, вероятно, видели только с сервисом сеансов, это очень хрупкий подход.
На мой взгляд, вам вообще может не понадобиться изоляция COM. Но если вы все еще хотите этого, вам нужно сделать это для обоих сервисов, брокера и сеанса, и делать это по их манифестам, а не во время выполнения.