Преобразование экспортированных членов

В данном разделе описывается преобразование следующих членов процедурой экспорта:

  • Методы

  • Свойства

  • События

Методы

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

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

Преобразование HRESULT

Преобразование управляемой сигнатуры в неуправляемую осуществляется путем замены управляемого возвращаемого значения на параметр [out, retval] и замены типа неуправляемого возвращаемого значения на HRESULT. Например, метод DoSomething может иметь следующие сигнатуры:

Управляемая сигнатура

short DoSomething(short i);

Неуправляемая сигнатура

HRESULT DoSomething([in] short i, [out, retval] short *rv);

Обратите внимание на то, что сигнатура COM возвращает значение HRESULT и имеет дополнительный параметр out для возвращаемого значения. Возвращаемое значение в управляемой реализации всегда представлено параметром [out, retval], добавляемым к концу неуправляемой сигнатуры, в то время как неуправляемая сигнатура всегда возвращает значение HRESULT. Если управляемый метод возвращает значение void, в среде выполнения параметр [out, retval] опускается. Пример.

Управляемая сигнатура

void DoSomething(short i);

Неуправляемая сигнатура

HRESULT DoSomething([in] short i);

В некоторых случаях предпочтительно оставить управляемую сигнатуру без изменений. Для этого можно использовать атрибут PreserveSigAttribute. Пример.

Управляемая сигнатура

[PreserveSig] short DoSomething(short i);

Неуправляемая сигнатура

short DoSomething ([in] short i);

Наличие двух отдельных сигнатур метода упрощает автоматическое использование класса из клиентов COM и .NET. Кроме того, клиенты COM и .NET могут одновременно использовать класс .NET. Создатель класса реализует только управляемую сигнатуру. При использовании программы Tlbexp.exe (или эквивалентного интерфейса API) выполняется автоматический экспорт сигнатуры в библиотеку типов, созданную для класса.

Перегруженные методы

Хотя платформа .NET поддерживает перегруженные методы, интерфейс IDispatch использует для связывания только имя метода, а не его полную сигнатуру. Таким образом, он не поддерживает перегруженные методы. Но в целях обеспечения доступа к перегруженным методам типа программа Tlbexp.exe добавляет к именам перегруженных методов порядковый номер, чтобы сделать каждое имя метода уникальным.

Приведенные ниже управляемая и неуправляемая сигнатуры показывают добавление номеров:

Управляемая сигнатура

interface INew {
public:
    void DoSomething();
    void DoSomething(short s);
    void DoSomething(short l);
    void DoSomething(float f);
    void DoSomething(double d);
}

Неуправляемая сигнатура

interface INew {
    void DoSomething();
    void DoSomething_2(short s);
    void DoSomething_3(short l);
    void DoSomething_4(float f);
    void DoSomething_5(double d);
}

Сигнатура COM для методов представлена как один метод DoSomething, за которым следует ряд методов с расширенным именем DoSomething_x, где x начинается с 2 и последовательно увеличивается для каждой следующей перегруженной формы метода. Необходимо отметить, что некоторые перегруженные методы могут наследоваться от базового типа. Однако не существует гарантии, что перегруженные методы сохранят свой номер при изменения версии типа.

Хотя клиенты .NET могут использовать перегруженную форму метода, COM-клиенты должны работать с методами с расширенными именами. Обозреватели объектов отображают все формы метода с расширенным именем вместе с сигнатурой метода, позволяя разработчику выбрать нужный метод. Клиент с поздним связыванием также может выполнить вызов IDispatch::GetIdsOfNames, передав расширенное имя, чтобы получить идентификатор DispID любого перегруженного метода.

Свойства

Управляемые классы и интерфейсы могут содержать свойства. Управляемое свойство относится к специальному типу данных, с которым могут использоваться связанные методы get и set. Эти методы определяются отдельно, как и любой другой метод. В следующем примере кода показан интерфейс, содержащий свойство Height. Классы, реализующие этот интерфейс, должны предоставить для свойства методы get и set.

    interface IMammal {
 
    IMammal Mother{get;set;}
    IMammal Father{get;set;}
    int     Height{get;set;}
    int     Weight{get;set;}

    }

    class Human : IMammal
    {
        int weight;
        int height;
        IMammal father;
        IMammal mother;

        public IMammal Mother { get { return mother; } set { mother = value; } }
        public IMammal Father { get { return father; } set { father = value; } }
        public int Height { get { return height; } set { height = value; } }
        public int Weight { get { return weight; } set { weight = value; } }
    }

Во время экспорта программа Tlbexp.exe преобразует метод set свойства в [propput], а метод get — в [propget]. Имя свойства в COM остается таким же, как и имя управляемого свойства. Это правило имеет следующие исключения:

  • Если тип свойства (исключая типы значения) является классом или интерфейсом, метод set свойства преобразуется в [propputref], что обеспечивает дополнительный уровень косвенного обращения для параметров.

  • Если у свойства нет метода get или set, программа Tlbexp.exe не включает это свойство в библиотеку типов.

Управляемые поля, как и свойства, экспортируются в библиотеку типов. Служба маршалинга времени выполнения автоматически создает методы get и set для всех открытых полей. Во время преобразования программа Tlbexp.exe создает функцию [propput] (или [propputref]) и функцию [propget] для каждого поля, как показано в следующем представлении библиотеки типов.

Представление библиотеки типов

interface IMammal : IDispatch {
         [propget]    HRESULT Mother([out, retval] IMammal** pRetVal);
         [propputref] HRESULT Mother([in] IMammal* pRetVal);
         [propget]    HRESULT Father([out, retval] IMammal** pRetVal);
         [propputref] HRESULT Father([in] IMammal* pRetVal);
         [propget]    HRESULT Height([out, retval] long* pRetVal);
         [propput]    HRESULT Height([in] long pRetVal);
         [propget]    HRESULT Weight([out, retval] long* pRetVal);
         [propput]    HRESULT Weight([in] long pRetVal);
         [propget]    HRESULT Age([out, retval] long* pRetVal);
         [propput]    HRESULT Age([in] long pRetVal);    
};

События

Модель событий в COM-взаимодействии см. в разделе управляемые и неуправляемые события. Управляемые типы реализуют события с помощью модели событий на основе делегатов. Например, интерфейс Class1Events в следующем примере кода вызывает событие Click.

    Public Delegate Sub ClickDelegate()
    <GuidAttribute("1A585C4D-3371-48dc-AF8A-AFFECC1B0967"), _
    InterfaceTypeAttribute(ComInterfaceType.InterfaceIsIDispatch)>

    Public Interface Class1Event
        Sub Click ()
    End Interface
<ComSourceInterfaces("Class1Event, EventSrc")> _
    Public Class Class1
        Public Event Click As ClickDelegate
   End Class
    public delegate void Click();

    public interface Class1Event
    {
        void Click();
    }
   [ComSourceInterfaces("Class1Event, EventSrc")]
    public class Class1
    {
        public event ClickDelegate Click;
    }

Во время экспорта программа Tlbexp.exe помечает интерфейс событий как источник в соответствующем компонентном классе. Как показано в следующем представлении библиотеки типов, экспортированный интерфейс ComClass1Events помечается как интерфейс источника.

Представление библиотеки типов

    disinterface Class1Event {
        properties:
        methods:
        [id(0x60020000)]
        HRESULT Click();
    };

    coclass Class1
    {
    …
    [default, source] Class1Event;
    };

См. также

Основные понятия

Преобразование экспортированной сборки

Преобразование экспортированного модуля

Преобразование экспортированного типа

Преобразование экспортированного параметра

Другие ресурсы

Резюме преобразования сборки в библиотеку типов