Подтвердить что ты не робот

Вызов COM видимого управляемого компонента из управляемого кода через COM-обертку

У меня есть сторонний компонент, можно сказать, что FIPreviewHandler обрабатывает предварительный просмотр, который реализует IPreviewHandler. FIPreviewHandler реализуется как управляемый компонент и использует интерфейс IPreviewHandler и связанные с ним интерфейсы с помощью взаимодействия. FIPreviewHandler регистрируется с помощью regasm.exe как COM.

У меня есть клиентское приложение, которое также управляется. Я хочу создать экземпляр FIPreviewHandler в качестве COM-компонента в моем приложении.

У меня есть сборка interop, которая определяет IPreviewHandler и связанные с ним интерфейсы.

Когда я создаю экземпляр FIPreviewHandler, используя Activator.CreateInstance(), для типа, возвращаемого GetTypeByCLSID(), который использует правильный CLSID для FIPreviewHandler, он возвращает мне управляемый экземпляр, поскольку он имеет доступную фактическую сборку, и пропускает COM. Когда я пытаюсь выполнить QI/передать этот экземпляр как любой из интерфейсов, например, IPreviewHandler, он возвращает null, потому что он загружается как управляемый объект, и хотя интерфейс IPreviewHandler, реализованный FIPreviewHandler, представляет собой тот же интерфейс, что и в моем взаимодействии, но его в пространстве имен/сборке различий, следовательно, null. Если бы он возвращал мне экземпляр COM/RCW (System.__ ComObject), он не учитывал бы пространство имен и выдавал бы штраф, а возвращал бы действительный экземпляр.

FIPreviewHandler - это 32-битный компонент и на 64-битной машине Win7, если я скомпилирую свое клиентское приложение как "любой процессор", Activator.CreateInstance() возвращает экземпляр COM/RCW (System.__ ComObject), поскольку он cudnt find 64-битная реализация FIPreviewHandler, следовательно, возвращает прокси. В этом случае приложение работает нормально. Но когда я компилирую его для x86, он получает 32-битную реализацию и возвращает управляемый экземпляр фактического управляемого класса, а не экземпляр COM, поэтому терпит неудачу.

Я не могу использовать интерфейсы, определенные в сборке FIPreviewHandler, поскольку я должен написать общий клиент для IPreviewHandler, и мое приложение будет работать с любым компонентом, реализующим IPreviewHandler, который отлично подходит для клиентов на С++, обращающихся к FIPreviewHandler как COM-объект, но не работает для управляемых клиентов.

Надеюсь, у меня есть смысл, и я был бы очень благодарен за любую помощь.

4b9b3361

Ответ 1

Так ясно, что неудача в .NET-части, поскольку я нахожу, что нет способа использовать оболочку COM вокруг управляемого COM-объекта.

"Решение" (и я использую этот термин очень слабо) - это использование PIA или "Первичной Interop Assembly". PIA предоставляет единую сильную именованную сборку, импортированную с TlbImp.exe, которая зарегистрирована GAC. В принципе, я предполагаю, что тогда мы должны полагаться на политики издателя GAC, чтобы заставить клиентов к правильной сборке интерфейса.

см. также

http://social.msdn.microsoft.com/Forums/en/csharpgeneral/thread/b11a0f90-fcc5-487a-b057-632f5415bfc2

http://www.codeproject.com/KB/COM/BuildCOMServersInDotNet.aspx

Ответ 2

Человек, если бы я был вами, я бы просто сделал обертку самостоятельно, только когда тип НЕ является COM-типом. Чтобы узнать, создан ли тип типа COM, используйте IsCOMObject из Type объекта:

myObject.GetType().IsCOMObject

Если это FALSE, создайте обертку, которая использует отражение для вызова управляемого типа. Отражение происходит медленно, но вы можете кэшировать объекты MethodInfo, которые вы получаете... в противном случае вы можете генерировать IL-код и создавать обертку, которая вообще не будет отражать.

Конечно, есть и другие способы сделать это... это зависит от вас.

Поскольку я влюблен в динамическое генерация IL во время выполнения, я могу предоставить вам код, который сделает это... если вы заинтересованы!

Ответ 3

Мой опыт в том, что Microsoft сделала это по-своему. Они не хотят, чтобы ваши две управляемые кодовые сборки обменивались данными через COM. Если вы попытаетесь добавить сборку, которая поддерживает COM как ссылку на COM в вашем проекте, ошибка говорит так же.

Если интерфейсы COM - это единственный способ получить необходимую функциональность, то обманщик не управляется. Напишите новый COM-сервер в С++ или VB6 (или любой не управляемый язык, который является COM-совместимым), который обертывает ваш сторонний управляемый COM-сервер. Затем добавьте эту новую DLL-оболочку в проект управляемого кода как COM-сервер.

Ответ 4

Я пытался сделать подобную вещь без успеха. (В моем случае существующий определенный COM-интерфейс имел ошибку, которая означала, что он не может быть реализован в .Net, поэтому я сам переопределил интерфейс COM в отдельном пространстве имен и внедрил этот интерфейс. Результирующий объект реализовал правильный COM интерфейсы, однако я не смог удержать оболочку COM, чтобы отбросить исходный сломанный интерфейс).

Насколько мне известно, есть только 2 решения:

  • Объявить все COM-интерфейсы в Первичная сборка взаимодействия, либо используя TLBimp или вручную), чтобы все интерфейсы были определены в общем пространстве имен. Обратите внимание, что эта сборка обычно не содержит никакой реализации и поэтому не должна ссылаться на другие сборки (если эти другие сборки также не являются сборками interop, которые объявляют зависимые COM-интерфейсы).

  • Создайте обертку (например, в Managed С++), которая выполняет вызовы через "традиционный" COM, а не через .Net COM interop.

Вариант 1. определенно поражает меня как лучший вариант - обратите внимание на то, что нет необходимости регистрировать эту сборку или размещать ее в GAC, и реализация может быть в совершенно отдельной сборке, если ссылка на общую сборку interop.

Я не могу придумать много законных ситуаций, когда сборка первичного взаимодействия недоступна.

Ответ 5

Если я читаю это право, вам нужно заставить его использовать COM. Две идеи:

  • Изменяется ли поведение Activator.CreateInstance(), если вы получаете тип через GetTypeFromProgID() вместо GetTypeByCLSID()?

  • Как работает Microsoft.VisualBasic.Interaction.CreateObject()? Да, я ненавижу вносить VB в это, но мне любопытно, возвращает ли он COM-объект вместо .NET-класса.

Ответ 6

Похоже, есть две проблемы:

  • Доступ к интерфейсам
  • Показатель компонента.

Во-первых, зачем ссылаться на него как на COM-объект, если вы знаете, что он управляется? Почему бы не прямо ссылаться на DLL, а не на interop, и таким образом вы должны иметь доступ к интерфейсам так же, как библиотека имеет к ним доступ. Таким образом, вам не нужно будет звонить Activator.CreateInstance. Вместо этого вы бы назвали var foo = new FIPreviewHandler();. Тем не менее, это не решает проблему битов, поэтому точка спорная. Вместо того, чтобы...

Следуя второй проблеме, одним из решений является включение COM-компонента в серверное приложение COM+. Приложения COM + определяют свою битту, когда они загружаются впервые. Если все библиотеки в приложении COM + 32-разрядные, когда он загружен, он загружается в WOW32, и вы можете вызывать его из 64-разрядного приложения. Я использовал этот трюк для библиотек COM, которые существовали только в 32-битной форме, но мне нужно было использовать их на 64-битном сервере. Недостатком является то, что вы загружаете библиотеку в отдельный процесс и несете затраты на сортировку из-за того, что выполнение не выполняется в вашем приложении, но после создания экземпляра оно должно выполняться достаточно хорошо.

Ответ 7

Используйте PInvoke для вызова COM-функции CoCreateInstance (Ex) и передайте ему CLSID, определенный в вашей сборке interop и IID IPreviewHandler. Таким образом,.NET не имеет возможности вмешиваться.

Вы говорите, что у вас есть сборка interop. Лучше всего, если это PIA, распространяемая автором сторонней сборки, но это прекрасно, если вам нужно ее самостоятельно создать. (PIA не нужно регистрировать в GAC, хотя они часто бывают.) Вы можете получить CLSID, загрузив сборку interop в дизассемблере IL и посмотрев атрибут System.Runtime.InteropServices.GuidAttribute в классе.