Создание компонента WinRT со статическими методами в C ++ / WRL

Я хочу создать компонент WinRT, используя C ++ и WRL (библиотека шаблонов C ++ среды выполнения Windows) для использования в управляемом коде через вызов статического метода C #.

int sum = Math.FastAdd(5,6);

Ниже приведена реализация, которая у меня не работает.
Что здесь может быть не так?

  1. В файле IDL создайте класс Math. Это будет хост для статических методов на управляемой стороне. Создайте интерфейс IMathStatics с помощью метода FastAdd. Этот просто содержит кучу статических методов. Пометьте класс Math атрибутом static с параметром IMathStatics.

    import "inspectable.idl";
    #define COMPONENT_VERSION 1.0
    namespace WRLNativeComponent
    {
        runtimeclass Math;
        [uuid(EFA9D613-BA8F-4F61-B9E7-C6BE7B7765DD)]
        [exclusiveto(WRLNativeComponent.Math)]
        [version(COMPONENT_VERSION)]
        interface IMathStatics : IInspectable
        {
            HRESULT FastAdd([in] int a, [in] int b, [out, retval] int* value);
        }
        [uuid(650438BA-C401-49E1-8F06-58DCD5A4B685), version(COMPONENT_VERSION)] 
        interface IMath : IInspectable
        {
            HRESULT InstanceMethod(void);
        }
        [static(WRLNativeComponent.IMathStatics, COMPONENT_VERSION)]
        [version(COMPONENT_VERSION), activatable(COMPONENT_VERSION)]
        runtimeclass Math
        {
            [default] interface IMath;
        }
    }
  1. Создайте класс C ++ MathStatics. Пусть макрос InspectableClassStatic указывает на строковый идентификатор IMathStatics. Добавьте макрос ActivatableStaticOnlyFactory, чтобы указать на реализацию класса MathStatics.

    #pragma once
    #include <wrl.h>
    #include "MyMath_h.h" // generated from IDL
    using namespace Microsoft::WRL;
    namespace WRLNativeComponent {
    class Math : public Microsoft::WRL::RuntimeClass,
        ABI::WRLNativeComponent::IMath>
    {
        InspectableClass(RuntimeClass_WRLNativeComponent_Math, BaseTrust);
    public:
        Math(void) {}
        ~Math(void) {}
        STDMETHODIMP InstanceMethod() override
        {
            return S_OK;
        }
    };
    class MathStatics : public Microsoft::WRL::ActivationFactory
    {
        InspectableClassStatic(InterfaceName_WRLNativeComponent_IMathStatics, BaseTrust);
    public:
        MathStatics(void) {}
        ~MathStatics(void) {}
        STDMETHODIMP FastAdd(_In_ int a, _In_ int b, _Out_ int* value) override
        {
            if (value == nullptr) return E_POINTER;
            *value = a + b;
            return S_OK;
        }
    };
    ActivatableClass(Math);
    ActivatableStaticOnlyFactory(MathStatics);
    }
  1. После компиляции создается файл WRLNativeComponent.winmd. Я вижу класс Math с помощью общедоступного статического метода FastAdd.

  2. Создайте клиент C # для вызова статического метода. При вызове выдается System.InvalidCastException. Ожидается, что это будет работать правильно.


person tivadj    schedule 24.09.2012    source источник


Ответы (1)


Класс среды выполнения может иметь не более одной фабрики активации. Каждое использование одного из макросов Activatable регистрирует фабрику активации для типа среды выполнения. Следовательно, следующий код из вашей библиотеки

ActivatableClass(Math);
ActivatableStaticOnlyFactory(MathStatics);

пытается зарегистрировать две фабрики активации: первая регистрирует простую фабрику активации для класса Math, а вторая регистрирует другую простую фабрику активации, которая на самом деле непригодна для использования (сейчас мы поймем, почему).

Поскольку первая простая фабрика активации связана с классом Math, она возвращается, когда компонент C # пытается вызвать статическую функцию-член. Затем компонент C # пытается преобразовать этот указатель интерфейса в интерфейс IMathStatics, который фабрика простой активации не реализует, поэтому преобразование завершается неудачно, и вы получаете InvalidCastException.


Поскольку для данного класса среды выполнения может быть только одна фабрика активации, ваш MathStatics класс должен реализовать как интерфейс статических членов IMathStatics, так и интерфейс IActivationFactory, который используется для построения по умолчанию (это необходимо, поскольку вы объявили свой тип Math как конструктивный по умолчанию , используя атрибут activatable без имени заводского интерфейса).

Ваша фабрика активации должна быть реализована следующим образом:

class MathStatics : public ActivationFactory<IMathStatics>
{
    InspectableClassStatic(RuntimeClass_WRLNativeComponent_Math, BaseTrust);

public:

    MathStatics() {}
    ~MathStatics() {}

    STDMETHODIMP ActivateInstance(_Outptr_result_nullonfailure_ IInspectable** ppvObject) override
    {
        return MakeAndInitialize<Math>(ppvObject);
    }

    STDMETHODIMP FastAdd(_In_ int a, _In_ int b, _Out_ int* value) override
    {
        if (value == nullptr) return E_POINTER;
        *value = a + b;
        return S_OK;
    }
};

ActivatableClassWithFactory(Math, MathStatics);

Шаблон базового класса ActivationFactory обеспечивает реализацию интерфейса IActivationFactory по умолчанию. Эта реализация по умолчанию просто возвращает E_NOTIMPL, когда клиент пытается по умолчанию создать экземпляр типа Math, поэтому нам нужно переопределить эту функцию-член, чтобы фактически по умолчанию создать объект Math.

Обратите внимание, что при использовании InspectableClassStatic для завершения реализации IInspectable для фабрики активации имя класса должно быть именем класса среды выполнения (в данном случае RuntimeClass_WRLNativeComponent_Math), а не именем интерфейса статики. Активация выполняется по имени типа, и именно это имя используется инфраструктурой WRL для поиска фабрики активации для типа среды выполнения по его имени.

ActivatableClassWithFactory используется для регистрации класса среды выполнения в связанной фабрике активации.

person James McNellis    schedule 24.09.2012