📜 ⬆️ ⬇️

Inherited class for the WinRT component written using WRL

I was interested in the topic of creating a class that could be inherited in another WinRT component / application. The C ++ / CX extension allows you to create such a class only if it inherits another unprinted class. In any other case, the compilation fails. Using WRL allows you to bypass this limitation and makes it possible to write an unprinted class.

Interface Description


First you need to describe the interface object and factory:

import "inspectable.idl"; namespace DataBinding { interface INumber; interface INumberOverrides; interface INumberFactory; runtimeclass Number; } namespace DataBinding { [exclusiveto(Number)] [uuid(5b197688-2f57-4d01-92cd-a888f10dcd90)] [version(0x00000001)] interface INumber : IInspectable { [propget] HRESULT Value([out, retval] INT32* value); [propput] HRESULT Value([in] INT32 value); } [exclusiveto(Number)] [uuid(12b0eeee-76ed-47af-8247-610025184b58)] [version(0x00000001)] interface INumberOverrides : IInspectable { HRESULT GetValue([out, retval] INT32* value); } [exclusiveto(Number)] [uuid(29f9bd09-d452-49bf-99f9-59f328103cbd)] [version(0x00000001)] interface INumberFactory : IInspectable { [overload("CreateInstance")] HRESULT CreateInstance0( [in] IInspectable* outer, [out] IInspectable** inner, [out, retval] Number** result); [overload("CreateInstance")] HRESULT CreateInstance1( [in] int value, [in] IInspectable* outer, [out] IInspectable** inner, [out, retval] Number** result); } [composable(DataBinding.INumberFactory, public, 1.0)] [marshaling_behavior(agile)] [threading(both)] [version(0x00000001)] runtimeclass Number { [default] interface INumber; [overridable][version(0x00000001)] interface INumberOverrides; } } 

In the description you can see some interesting details:

The MIDL compiler based on this code will create a header file that will need to be included in the code file.

Interface implementation in C ++ code


The next task is to implement the specified interfaces in the code. For the MIDL compiler, the settings for creating * .h files using the% (Filename) _h.h pattern were specified, and the / ns_prefix option was specified (which adds an ABI prefix to the generated code). The interfaces have been defined in the DataBinding.idl file, so the header DataBinding_h.h file is included.
So, the interface implementation code:

 #include <wrl.h> #include <wrl/wrappers/corewrappers.h> #include "DataBinding_h.h" using ABI::DataBinding::INumber; using ABI::DataBinding::INumberOverrides; using ABI::DataBinding::INumberFactory; using Microsoft::WRL::RuntimeClassFlags; using Microsoft::WRL::RuntimeClassType; using Microsoft::WRL::EventSource; using Microsoft::WRL::Make; using Microsoft::WRL::MakeAndInitialize; using Microsoft::WRL::RuntimeClass; using Microsoft::WRL::ActivationFactory; using Microsoft::WRL::ComPtr; using Microsoft::WRL::Wrappers::HStringReference; class Number : public RuntimeClass < RuntimeClassFlags<RuntimeClassType::WinRt>, INumber, INumberOverrides > { InspectableClass(RuntimeClass_DataBinding_Number, BaseTrust); private: INT32 _value; public: Number() : _value(0) { } Number(INT32 value) : _value(value) { } virtual HRESULT STDMETHODCALLTYPE get_Value(INT32* value) override { *value = _value; return S_OK; } virtual HRESULT STDMETHODCALLTYPE put_Value(INT32 value) override { _value = value; return S_OK; } virtual HRESULT STDMETHODCALLTYPE GetValue(INT32* value) override { *value = _value; return S_OK; } }; class NumberFactory : public ActivationFactory < INumberFactory > { InspectableClassStatic(RuntimeClass_DataBinding_Number, BaseTrust); public: virtual HRESULT STDMETHODCALLTYPE CreateInstance0( IInspectable* outer, IInspectable** inner, INumber** result) override { .... } virtual HRESULT STDMETHODCALLTYPE CreateInstance1( INT32 value, IInspectable* outer, IInspectable** inner, INumber** result) override { .... } }; ActivatableClassWithFactory(Number, NumberFactory); 

I'll tell you about CreateInstance0 and CreateInstance1. These methods are responsible for "inheritance".
Unfortunately, I could not find in the documentation recommendations on the implementation of factory methods of this type. Therefore, we had to experimentally investigate the purpose of the parameters:

 IInspectable* outer, IInspectable** inner, INumber** result 

To do this, connect the component to an application written in C #. In the metadata * .winmd, the unprinted class Number was defined. Exactly what I was trying to achieve. It only remained to understand how to implement the methods. For this I used the following code:

 private class LocalNumber : Number { public LocalNumber() { } public LocalNumber(int value) : base(value) { } } ..... { var items = new List<Number> { new Number(), new Number(1), new LocalNumber(), new LocalNumber(1), }; } 

After several passes of debugging, I came to the following variant of the implementation of factory methods:

 virtual HRESULT STDMETHODCALLTYPE CreateInstance0( IInspectable* outer, IInspectable** inner, INumber** result) override { auto pnumber = Make<Number>().Detach(); if (nullptr != outer && S_OK != outer->QueryInterface(ABI::DataBinding::IID_INumber, reinterpret_cast<void**>(result))) { *inner = reinterpret_cast<IInspectable*>(pnumber); } else { *result = pnumber; } return S_OK; } virtual HRESULT STDMETHODCALLTYPE CreateInstance1( INT32 value, IInspectable* outer, IInspectable** inner, INumber** result) override { auto pnumber = Make<Number>(value).Detach(); if (nullptr != outer && S_OK != outer->QueryInterface(ABI::DataBinding::IID_INumber, reinterpret_cast<void**>(result))) { *inner = reinterpret_cast<IInspectable*>(pnumber); } else { *result = pnumber; } return S_OK; } 

First, create an object. Then use the condition to initialize the returned values. Expression:

 outer->QueryInterface(ABI::DataBinding::IID_INumber, reinterpret_cast<void**>(result)) 

It polls the object for the implementation of the INumber interface, and a pointer to the result parameter is passed as the return value. In case of successful execution, we initialize the inner parameter using the expression:

 *inner = reinterpret_cast<IInspectable*>(pnumber); 

In any other case, simply initialize the result parameter.

PS


This article is for informational purposes only. The main goal was to demonstrate the possibility of writing an "inherited" class using WRL.

Source: https://habr.com/ru/post/248071/


All Articles