Как я могу создать интерфейс класса COM в общем случае

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

Это исходный код, который у меня был:

        if (WebConfigSettings.ComPartition == null && HttpContext.Current != null)
            Nses = new NSession();
        else
            Nses = (INSession)Marshal.BindToMoniker(string.Format("partition:{0}/new:NuntioServer.NSession", WebConfigSettings.ComPartition));

И

        if (WebConfigSettings.ComPartition == null && HttpContext.Current != null)
            apses.Wses = new WSession();
        else
            apses.Wses = (IWSession)Marshal.BindToMoniker(string.Format("partition:{0}/new:NuntioServer.WSession", WebConfigSettings.ComPartition));  

И вот как я пытаюсь его реорганизовать:
(Да, в C# вы можете в интерфейс.)

    public static TInterface Get<TSubInterface, TInterface>() where TSubInterface: TInterface
    {
        <snip></snip>
        if (!useComPartitions)
            return Activator.CreateInstance<TSubInterface>(); // --> this is not cooperating

        return (TInterface)Marshal.BindToMoniker(.....);
    }

Вот что я уже пробовал:

  1. Я попытался указать ограничение new (), а затем выполнить «новый TSubInterface ()»: это приводит к ошибке сборки: «.. должен быть неабстрактный тип с общедоступным конструктором без параметров, чтобы использовать его в качестве параметра» TSubInterface ' в универсальном типе или методе.."

  2. когда я использую Activator.CreateInstance, я получаю исключение времени выполнения: «Невозможно создать экземпляр интерфейса»

  3. когда я использую Activator.CreateComInstanceFrom("someAssemblyName", "typeName"), я получаю ошибку компиляции: "Не удается преобразовать тип выражения "System.Runtime.Remoting.ObjectHandle" для возврата типа TInterface"

[edit] Я смог выполнить эту компиляцию, добавив 'where TSubInterface : class, но я не уверен, что это имеет смысл, поскольку TSubInterface является интерфейсом.
Использование CreateComInstanceFrom также не имеет смысла' не работает, потому что пытается найти ту сборку, которая указана в каталоге, где этой dll нет и быть не должно.

Могу ли я как-то заставить это скомпилировать и запустить?


person TweeZz    schedule 28.09.2012    source источник
comment
Какой код вы используете этот метод?   -  person cuongle    schedule 28.09.2012
comment
nses = COMUtils.Get‹NSession, INSession›() И NSession, и INSession являются интерфейсами.   -  person TweeZz    schedule 28.09.2012
comment
Вы хотите, чтобы это работало только потому, что вы можете или собираетесь использовать это в рабочем коде? Если другое, то вам было бы лучше с некоторой инверсией контейнера управления, чтобы добиться аналогичного результата.   -  person Rafal    schedule 28.09.2012
comment
Я не удивлюсь, если любая магия, позволяющая вам создавать экземпляр интерфейса через C# (т.е. в ваших ссылках), не применяется при работе через отражение. Я подозреваю, что компилятор переводит связанный код в стандартный IL, создающий экземпляр класса, но у отражения нет этой опции.   -  person Rawling    schedule 28.09.2012
comment
Меня смущает Both NSession and INSession are interface, а у вас ограничение NSession : INSession. Действительно ли это два интерфейса, один из которых наследует другой? Если да, то почему имя интерфейса в шаблоне TClass.   -  person Zdeslav Vojkovic    schedule 28.09.2012
comment
@ZdeslavVojkovic Я согласен с вами, это сбивает с толку :) Оба являются интерфейсами, и NSession реализует INSession (я не могу изменить имена этих интерфейсов). Так что да, я должен изменить TClass на TSubInterface или что-то подобное.   -  person TweeZz    schedule 28.09.2012
comment
@ Роулинг, я тоже так думаю. Но я пока не хочу сдаваться :) Должен же быть способ ;)   -  person TweeZz    schedule 28.09.2012
comment
Ну, вы могли бы прочитать имя класса из атрибута «CoClass» и вместо этого создать экземпляр одного из них?   -  person Rawling    schedule 28.09.2012
comment
Я боюсь, что это не сработает без чтения метаданных и создания экземпляра класса, так как большинство перегрузок CreateInstance используют конструкторы указанного типа без поиска класса «псевдоним». Тем не менее, меня интересует предыстория вашего решения сделать это таким образом. Мне кажется это очень плохой идеей.   -  person Zdeslav Vojkovic    schedule 28.09.2012
comment
хорошо, я добавлю немного кода, чтобы вы могли видеть, с чего я начал..   -  person TweeZz    schedule 28.09.2012
comment
@ZdeslavVojkovic кажется ты прав..   -  person TweeZz    schedule 28.09.2012
comment
В тех случаях, когда кажется, что есть некоторая магия компилятора, часто стоит открыть рассматриваемый код в рефлекторе, чтобы посмотреть, как это делается.   -  person Yaur    schedule 28.09.2012


Ответы (2)


Вам нужно будет сосредоточиться на кажущейся магии возможности создать объект класса из имени интерфейса. Давайте выберем пример, который может попробовать каждый. Создайте новое консольное приложение и используйте Project + Add Reference, вкладку Browse и выберите c:\windows\system32\shell32.dll.

Взгляните на библиотеку взаимодействия, которая создается с помощью обозревателя объектов. Обратите внимание, что тип оболочки является типом интерфейса. Теперь напишите этот код:

class Program {
    static void Main(string[] args) {
        var shl = new Shell32.Shell();
    }
}

Скомпилируйте и запустите ildasm.exe в файле .exe. Вот увидишь:

.method private hidebysig static void  Main(string[] args) cil managed
{
  .entrypoint
  // Code size       8 (0x8)
  .maxstack  1
  .locals init ([0] class [Interop.Shell32]Shell32.Shell 'shl')
  IL_0000:  nop
  IL_0001:  newobj     instance void [Interop.Shell32]Shell32.ShellClass::.ctor()
  IL_0006:  stloc.0
  IL_0007:  ret
} // end of method Program::Main

Обратите внимание, как имя типа было заменено из Shell в ShellClass. Импортер библиотеки типов создал этот класс, он использует исходное имя совместного класса и добавляет «класс» к имени. Компилятор делает эту замену.

В чем ключ, Activator.CreateInstance() не может сделать такую ​​же замену. Я не вижу очевидного способа заставить дженерики сделать такую ​​же замену, кроме прямого использования имени IFooClass вместо имени интерфейса. Технически вы можете получить атрибут [CoClass], который импортер библиотеки типов применил к типу интерфейса.

person Hans Passant    schedule 28.09.2012
comment
Пометил ваш как правильный, потому что он предлагает решение (используйте атрибут «CoClass») и объясняет некоторые причины, по которым это не так просто, как я надеялся. - person TweeZz; 01.10.2012

Это можно сделать, выяснив, что такое coClass этого интерфейса, и создав его экземпляр:

var coClassAttribute = type.GetCustomAttribute<CoClassAttribute>(); // our extension method
return (TSubInterface)Activator.CreateInstance(coClassAttribute.CoClass);

Меня это не устраивает, но это работает. (не будет отмечать это как правильный ответ)

person TweeZz    schedule 28.09.2012