Почему .NET-библиотека .NET не может использоваться через COM в .NET?

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

Мне не важно, на каком языке написана COM-библиотека, так почему это важно?

Для справки, это то, что происходит, когда вы используете tlbimp в файле .tlb, сгенерированном из библиотеки .NET:

C:\dev>tlbimp test.tlb
Microsoft (R) .NET Framework Type Library to Assembly Converter 4.0.30319.1
Copyright (C) Microsoft Corporation.  All rights reserved.

TlbImp : error TI1029 : Type library 'test' was exported from
a CLR assembly and cannot be re-imported as a CLR assembly.

Кроме того, моя тестовая COM-библиотека использует IUnknown, поддерживая только ранний интерфейс COM.

Ответ 1

tlbimp - это только начало проблемы.

Каждый COM-видимый объект .NET реализует IManagedObject. Каждый раз, когда вы пытаетесь вызвать объект через COM-интерфейс,.NET выполняет QueryInterface для IManagedObject, и если ему удастся, объект будет развернут и доступен напрямую. Устранение вызова interop таким образом считается оптимизацией производительности.

Это серьезная проблема в сценариях взаимодействия на основе COM, где на .NET-языках могут быть реализованы несколько компонентов. Если каждый компонент .NET реализует собственную версию COM-интерфейса, генерируемую tlbimp, они не смогут связываться друг с другом с помощью этого интерфейса. Несмотря на то, что каждая копия интерфейса Tlbimp'а имеет один и тот же COM-GUID,.NET рассматривает их отдельные интерфейсы. "Правильным" решением этой проблемы является определение COM-интерфейса в первичной сборке interop, и каждый компонент, внедренный .NET, использует эту же копию интерфейса, но если нет никакой координации между разработчиками компонентов, которые вряд ли произойдут.

Эта проблема была однажды подчеркнута разработчиком Microsoft в http://blogs.msdn.com/b/jmstall/archive/2009/07/09/icustomqueryinterface-and-clr-v4.aspx вместе с решением для .NET 4, но она была основана на предварительная версия и не работает в финале. Я не знаю нигде, что он был признан Microsoft.

Одно из решений - отказаться от интерфейса в сценарии .NET-.NET и вместо этого использовать отражение. Это может работать во многих случаях, но, конечно, не очень впечатляет. Возможно, лучшим решением является использование неуправляемого кода для агрегирования каждого из ваших управляемых компонентов и отклонения попыток QueryInterface для IManagedObject. Это похоже на то, что описано в приведенной выше записи в блоге, но не полагается на функции .NET, которые больше не работают, как описано в этом блоге.

Взаимодействие .NET, IMO, довольно ужасное и явно нарушает правила COM (тот же интерфейс IID == тот же интерфейс и не должен заботиться о том, на каком языке реализуется объект). Но .NET вел себя так с самого начала, и никакая обратная связь от разработчиков, укушенных этим поведением, не изменила умов никого в команде .NET.

Ответ 2

Tlbimp может видеть из библиотеки типов, что он был создан из сборки .NET. Это просто препятствует тому, что вы пытаетесь сделать. Что не имеет смысла, если вы хотите использовать сборку .NET, просто добавьте ссылку на нее.

Вы можете обмануть машину, используя поздний интерфейс COM, наиболее легко выполняемый с помощью динамического ключевого слова в С#. Это все еще не обманывает среду CLR, она может видеть, что она перехватывает управляемую сборку и фактически не создаст COM-вызываемую оболочку. Обычно вы делаете это, чтобы написать тестовую программу для тестирования вашего COM-сервера. Подразумевается, что ваш тест на самом деле не проверяет, как ваш COM-сервер будет использоваться на практике. Очень простые вещи, такие как преобразование вариантов в объекты и обратно, просто не будут протестированы вообще. И вполне может дать вам очень неприятные сюрпризы, когда вы используете сборку в производстве.