Вызов видимого управляемого компонента COM из управляемого кода через оболочку COM - PullRequest
16 голосов
/ 25 июня 2010

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

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

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

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

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

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

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

Ответы [ 7 ]

1 голос
/ 24 апреля 2011

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

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

1 голос
/ 27 апреля 2011

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

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

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

  2. Создание оболочки (например, в Managed C ++), которая выполняет вызовы через «традиционный» COM, а не через .Net COM-взаимодействие.

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

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

1 голос
/ 21 апреля 2011

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

myObject.GetType().IsCOMObject

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

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

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

1 голос
/ 19 апреля 2011

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

«Решением» (и я использую этот термин очень свободно) является использование PIA или «Первичной сборки взаимодействия».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

0 голосов
/ 27 апреля 2011

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

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

0 голосов
/ 21 апреля 2011

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

  1. Доступ к интерфейсам
  2. Битность компонента.

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

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

0 голосов
/ 18 апреля 2011

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

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

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

...