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

StackOverflow https://stackoverflow.com/questions/1618778

  •  06-07-2019
  •  | 
  •  

Вопрос

Это всего лишь вопрос о том, как написать мой код для импорта COM.

Мое понимание правильной реализации интерфейсов взаимодействия состоит в том, что основные критерии таковы:

<Ол>
  • Все сигнатуры методов должны совпадать совместимым образом
  • Методы должны отображаться в том же порядке в интерфейсе .Net, что и в неуправляемом интерфейсе.
  • Когда неуправляемый интерфейс наследует от другого неуправляемого интерфейса, управляемая реализация должна сначала объявить членов интерфейса базового уровня, начиная с самого базового интерфейса.
  • Мой вопрос Что мне делать в том порядке, в котором отображаются элементы, если импортируемый мной интерфейс наследуется от другого интерфейса и переопределяет / скрывает один или несколько элементов в базовом интерфейсе? Куда идет объявление члена интерфейса? Во-первых, где базовый интерфейс это объявил? Или удален из своей исходной позиции и помещен туда, где его объявляет производный интерфейс?

    [uuid(31d1c294-1dd2-11b2-be3a-c79230dca297)]
    interface BaseComInterface
    {
        void method1();
        void method2();
        void method3();
    }
    
    [uuid(fab51c92-95c3-4468-b317-7de4d7588254)]
    interface DerivedComInterface : BaseComInterface
    {
        void method1();
        void method4();
        void method5();
    }
    

    Теперь для кода C #:

    [Guid("fab51c92-95c3-4468-b317-7de4d7588254"), ComImport, InterfaceType(ComInterfaceType.InterfaceIsIUnknown)]
    public interface IDerivedComInterface
    {
        void method1(); // do I remove this one?
        void method2();
        void method3();
        void method1(); // or this one?
        void method4();
        void method5();
    }
    
    Это было полезно?

    Решение

    У вас есть пример того, как кто-то на самом деле это делает? Хотя в COM вполне законно иметь одно и то же имя для метода в интерфейсе и в другом производном от него интерфейсе, это делает работу с производным интерфейсом сложной или невозможной практически во всех средах разработки. Это включает в себя реализацию, а также вызов любых COM-объектов на основе интерфейса.

    В интерфейсе COM нет такой вещи, как переопределение или скрытие. Интерфейс COM - это просто контракт на то, как двоичный интерфейс объекта будет размещен в памяти, имена методов в определении интерфейса не имеют смысла, за исключением инструментов. В вашем случае BaseComInterface обещает «vtable» с шестью методами, первые три соответствуют сигнатурам методов IUnknown, а следующие три соответствуют сигнатурам трех методов, которые вы дали, все в правильном порядке. С другой стороны, DerivedComInterface обещает «vtable», который включает девять методов, опять же первые три сигнатуры соответствуют методам IUnknown, следующие три соответствуют сигнатурам методов BaseComInterface, а последние три соответствуют трем методам, уникальным для DerivedComInterface. Имена этих методов не имеют значения, за исключением вашей IDE, инструментов и компилятора, которым нужны имена для поиска указателей 'vtable'.

    Таким образом, в основном в C #, а также в C, C ++ и некоторых других языках для реализации DerivedComInterface одно из имен методов должно быть оформлено, чтобы отличить его слот «vtable» от другого.

    Чтобы ответить на ваш вопрос - вы не удалите ни один из методов, поскольку они оба должны быть там, чтобы выполнить контракт COM:

    [Guid("fab51c92-95c3-4468-b317-7de4d7588254"), ComImport,  InterfaceType(ComInterfaceType.InterfaceIsIUnknown)]
    public interface IDerivedComInterface
    {
        void method1(); 
        void method2();
        void method3();
        void method1_2(); //Decorated to distinguish from base interface method.
        void method4();
        void method5();
    }
    

    Это игнорирует то, что произойдет, если эти интерфейсы будут производными от IDispatch, а не от IUnknown, и я не хотел бы в них вмешиваться. Также помните, что если ваш idl действительно определяет методы как возвращающие void, вы должны декорировать ваши методы C # атрибутом PreserveSig.

    scroll top