Click here to Skip to main content
15,879,535 members
Articles / Programming Languages / C++

Implementing WinRT Interfaces Defined Using WRL/C++ with WRL/C++, C++/CX and C#

Rate me:
Please Sign up or sign in to vote.
5.00/5 (5 votes)
8 Jan 2013CPOL9 min read 49.3K   357   10   6
This article demonstrates how to implement interfaces defined for a WinRT component using WRL and C++.

Introduction

This 3rd article explores the most native way of building WinRT components with Visual Studio 2012. The lowest level of implementation of WinRT components is a COM based technology that has been updated for Windows 8 modern style. In COM you were using ATL (ActiveX Template Library) to develop components, in WinRT the lowest framework provided by Microsoft is WRL (Windows Runtime Library). WRL is what ATL was to COM, a C++ template library that provides classes to develop WinRT components directly in C++:

In the previous article I defined interfaces in C++/CX and then implemented those interfaces with C++/CX and C#. Let's now revisit the creation of those interfaces and use WRL/C++ to define them and implement them. Then we'll implement them with C++/CX and C#. This will give three different ways of implementing the same interfaces, two native technologies that are WRL/C++ and C++/CX, and one managed technology which is C#. As C# is based on the CLR, any other .NET language can be use and the code will be very similar.

Once again while writing this code I discovered issues that might be bugs, but so far I don't have any confirmation from Microsoft, so what I'm writing in this article is valid only at the time of the publication. Stay tuned for any update!

Background

I suggest that before going through this article you first read the first and the second article I previously wrote. As I just mentioned, as WinRT is fully based on the COM technology introduced in Windows 3.11 for OLE (Object Linking and Embedding) you can also read some COM reference book or tutorial to know about this technology if you're not yet familiar with it.

The Interfaces

In the previous articles I defined three simple interfaces IPerson, ICitizen and IAddress to illustrate some fundamentals of interface/component development. In C++/CX and C# the compilers are generating interfaces with the public part of the implementation of the components. In COM an object only exists through its interfaces, so it is necessary to design those interfaces and build your architecture with them.

With WRL interfaces must be defined using MIDL (Microsoft Interface Description Language). This MIDL although similar to the one in COM seems to have some major differences.

The interfaces are defines as follows.

C#
namespace WRLCompV1
{
    interface IPerson;
    interface IAddress;
    interface ICitizen;
    interface ISaveable;
    runtimeclass PersonClass;
    runtimeclass AddressClass;
    runtimeclass CitizenClass;

    [uuid(0d585932-fbc4-4b0a-90b5-ccf34aefd4c6)] 
    [version(COMPONENT_VERSION)] 
    interface IPerson : IInspectable
    {
        [propget] HRESULT Name([out, retval] HSTRING* value);
	[propput] HRESULT Name([in] HSTRING value);

	[propget] HRESULT Surname([out, retval] HSTRING* value);
	[propput] HRESULT Surname([in] HSTRING value);
    }

    [uuid(497783FC-D66D-4DF6-AAFC-C4D879AB22F1)] 
    [version(COMPONENT_VERSION)]
    interface IAddress : IInspectable
    {
	[propget] HRESULT Street([out, retval] HSTRING* value);
	[propput] HRESULT Street([in] HSTRING value);

	[propget] HRESULT City([out, retval] HSTRING* value);
	[propput] HRESULT City([in] HSTRING value);
    }

    [uuid(C3F9CEA3-B897-4A79-BF6C-02B5DF4DB77D)]
    [version(COMPONENT_VERSION)]
    interface ISaveable : IInspectable
    {
	HRESULT CanSave([out, retval] boolean *value);
    }

    [uuid(863571FC-4CBB-47E8-8BD3-2709D5CB7D0D)]
    [version(COMPONENT_VERSION)]
    interface ICitizen : IInspectable 
    {
	[propget] HRESULT Name([out, retval] HSTRING* value);
	[propput] HRESULT Name([in] HSTRING value);

	[propget] HRESULT Surname([out, retval] HSTRING* value);
	[propput] HRESULT Surname([in] HSTRING value);

	[propget] HRESULT Address([out, retval] IAddress** value);
	[propput] HRESULT Address([in] IAddress* value);
    }

    [version(COMPONENT_VERSION)] 
    [activatable(COMPONENT_VERSION)]
    runtimeclass PersonClass
    {
        [default] interface IPerson;
	interface ISaveable;
    }

    [version(COMPONENT_VERSION)] 
    [activatable(COMPONENT_VERSION)]
    runtimeclass AddressClass
    {
        [default] interface IAddress;
	interface ISaveable;
    }

    [version(COMPONENT_VERSION)] 
    [activatable(COMPONENT_VERSION)]
    runtimeclass CitizenClass
    {
        [default] interface ICitizen;
	interface IPerson;
	interface ISaveable;    
    }
}

This MIDL description of the interfaces when compiled with C++ will generate a .h header file that contains all the C and C++ definition of the interfaces. All interfaces have to inherit from IInspectable. So far I discovered that unlike the MIDL of COM it is not possible to use inheritance of anything else than IInspectable, the compiler prevents it even if the base interface you are using already inherits from IInspectable. You can't neither inherit from IInspectable and an other interface. I don't have any confirmation from Microsoft if it is a limitation or a bug. In my opinion this is a serious limitation and I don't really understand why they did that. However, it is still possible for a component to implement several distinct interfaces.

When you create your MIDL code you must first declare the interfaces and the runtime classes in that order, then the interfaces must be defined and then the runtime classes. If you define a runtime class that uses an interface that hasn't been defined yet it won't compile, even if you defined it afterward in the same file.

When you compile the MIDL, the compiler generates the class headers for the interfaces and it defines as an external the runtime class name strings.

The C++ code is created under the namespace ABI and you then find your own namespace. This is a Microsoft design decision to put all the WRL code under the namespace ABI.

There are few differences with the legacy COM components, the component that you create are not registered globally in the system registry. When the application is distributed through the Windows Store all component must be included in the package and cannot be shared with other Apps. Only if you distribute your application within a corporation you will be able to share components with different application. This restriction implies that you basically cannot build an architecture with a plug-in mechanism where you can freely extend an application distributed with the Windows Store.

Visual Studio 2012 doesn't come with a project template for WRL development in C++, you have to get a rudimentary one from the Online templates and import it. The template name is WRLClassLibrary. This template won't generate a lot of code for you and there is no wizard to add method or property to a class, you'll have to do everything manually. This project template is in fact less powerful than the one provided in Visual C++ to develop COM components with ATL but the framework is now simpler than the original ATL.

Let's have a look at the implementation of those interfaces in C++.

Implementing the interfaces with C++ and WRL

When you create the project the template comes with a sample implementation class, any other class has to be done manually. Fortunately the necessary code to implement a WinRT component is quite light. The class needs to inherit from the RuntimeClass template class. There are several templates provided depending the number of interfaces your component has to implement.

C++
namespace ABI { namespace WRLCompV1
{
    /**
	WRL component class for the IPerson interface. Also implements ISaveable as 
	a seperate interface
     */
    class PersonClass: public RuntimeClass<IPerson, ISaveable>, protected SaveableHelper
    {
        InspectableClass(L"WRLCompV1.PersonClass", BaseTrust)

    private:
	std::wstring m_name;
	std::wstring m_surname;

    public:
	PersonClass()
	{
	}

    public:
	// IPerson
	IFACEMETHODIMP get_Name(_Out_ HSTRING* value);
	IFACEMETHODIMP put_Name(_In_ HSTRING value);
	IFACEMETHODIMP get_Surname(_Out_ HSTRING* value);
	IFACEMETHODIMP put_Surname(_In_ HSTRING value);

	// ISaveable
	IFACEMETHODIMP CanSave(_Out_ boolean* value);

    protected:
	virtual boolean CanSaveImpl();
    };

    ActivatableClass(PersonClass);

    // Implementation of IPerson
    StringProperty(PersonClass, Name, m_name);
    StringProperty(PersonClass, Surname, m_surname);

    // Implementation of ISaveable
    boolean PersonClass::CanSaveImpl()
    {
	return m_name.length() > 0 && m_surname.length() > 0;
    }

    CanSaveMethod(PersonClass);
}}

A WinRT runtime class will always have to inherit from the template class RuntimeClass. A macro is provided to implement the IInspectable interface (and the IUnknown at the same time) and another macro has to be used in the implementation part of the class to make the class activatable.

I used some predifined macros such as IFACEMETHODIMP, STDMETHODIMP to declare and implement the property methods of the class and I created a simple macro StringProperty to implement a the methods of a get/set property. If you're not familiar with C++, macros are very popular with C++ developer and are a powerful mean to simplify the code.

To implement the ISaveable interface which is to be implemented by the three components I have tried to use a base class but I couldn't achieve exactly what I wanted to do because of some pattern used by the RuntimeClass. However with a simple macro I made it almost like it should be.

While implementing the ICitizen class I ran into a strange issue and I couldn't use the ComPtr class. The ICitizen object has a composition with an IAddress object. Normally the IAddress pointer should be managed with a ComPtr class, but it appears that the release sequence of the IAddress pointer has some issue if the get Address property of ICitzen is called.

To workaround this problem I used the raw reference counting of COM calling manually AddRef() and Release() of IUnknown. My guess is that the problem occurs when the ComPtr is used as an instance member of the runtime class as in other cases it works perfectly.

The code of the CitizenClass is the given there (I removed some parts that don't relate to the ComPtr issue)

C++
class CitizenClass: public RuntimeClass<ICitizen, IPerson, ISaveable>, protected SaveableHelper
{
    InspectableClass(L"WRLCompV1.CitizenClass", BaseTrust)

private:
    std::wstring m_name;
    std::wstring m_surname;
    ABI::WRLCompV1::IAddress* m_pAddress;

public:
    CitizenClass()
    {
	m_pAddress = nullptr;
	ComPtr<ABI::WRLCompV1::IAddress> pAddress;
	HStringReference activableClassId(L"WRLCompV1.AddressClass");
	HRESULT hr = ActivateInstance<ComPtr<ABI::WRLCompV1::IAddress>>(activableClassId.Get(), &pAddress);

	// Detach the pointer from the ComPtr class and manually call AddRef()
	m_pAddress = pAddress.Detach();
	m_pAddress->AddRef();
    }

    ~CitizenClass()
    {
	// When the class is destroyed, release the reference of IAddress
	if (m_pAddress != nullptr)
	{
	    m_pAddress->Release();
	}
    }

public:
    // IPerson, ICitizen
    ...
};

// Implementation of ICitizen
STDMETHODIMP CitizenClass::get_Address(ABI::WRLCompV1::IAddress** value)
{
    HRESULT hr = E_POINTER;

    if (value != nullptr)
    {
        *value = m_pAddress;
	hr = S_OK;
    }

    return hr;
}

STDMETHODIMP CitizenClass::put_Address(ABI::WRLCompV1::IAddress* value)
{
    HRESULT hr = E_POINTER;

    if (value != nullptr)
    {
	// Release the previously used IAddress
	if (m_pAddress != nullptr)
	{
	    m_pAddress->Release();
	}

	// Set the given one and increase the reference count
	m_pAddress = value;
	m_pAddress->AddRef();
	hr = S_OK;
    }

    return hr;
}

// Implementation of ISaveable
boolean CitizenClass::CanSaveImpl()
{
    boolean canSave = true;
    ComPtr<ABI::WRLCompV1::ISaveable> pSaveable;
    ComPtr<ABI::WRLCompV1::IAddress> pAddress = m_pAddress;
    HRESULT hr = pAddress.As(&pSaveable);
    if (SUCCEEDED(hr))
    {
	hr = pSaveable->CanSave(&canSave);
    }

    return m_name.length() > 0 && m_surname.length() > 0 && canSave;
}

Implementing the interfaces with C++/CX and C#

Implementing the given three interfaces with C++/CX and C# is very simple, you simply need to add a reference to the WRL DLL to the project and use the namespace where the interfaces are declared. Note that when you use the namespace in C++/CX or C# the root namespace ABI doesn't exist, it is only necessary in C++ with WRL. In fact when you import the DLL in your project it is through the windows metadata file and this file doesn't contain any reference to ABI.

In C++/CX you don't need to include any .h for your component as anything the compiler needs is provided when referencing the DLL to the project. You can eventually use the using namespace statement. The following is the code of the C# implementation. The C++/CX is very similar and basically the same as an implementation given in the first article.

C#
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;

using WRLCompV1;

namespace WinRTCompCsharpWRLItf
{
    /// <summary>
    /// The class Citizen implements in C# the following interfaces of WRLCompV1 namespace:
    ///     - ICitizen
    ///     - IPerson
    ///     - ISaveable
    /// </summary>
    public sealed class Citizen : ICitizen, IPerson, ISaveable
    {
        private IPerson person;
        private IAddress address;

        public Citizen()
        {
            person = new Person();
            address = new Address();
        }

        #region Explicit implementation of IPerson

        string IPerson.Name
        {
            get
            {
                return person.Name;
            }
            set
            {
                person.Name = value;
            }
        }

        string IPerson.Surname
        {
            get
            {
                return person.Surname;
            }
            set
            {
                person.Surname = value;
            }
        }

        #endregion

        #region Explicit implementation of ICitizen

        IAddress ICitizen.Address
        {
            get
            {
                return address;
            }
            set
            {
                address = value;
            }
        }

        string ICitizen.Name
        {
            get
            {
                return ((IPerson) this).Name;
            }
            set
            {
                ((IPerson)this).Name = value;
            }
        }

        string ICitizen.Surname
        {
            get
            {
                return ((IPerson)this).Surname;
            }
            set
            {
                ((IPerson)this).Surname = value; ;
            }
        }

        #endregion

        #region Explicit implementation of ISaveable

        public bool CanSave()
        {
            return
                ((ISaveable)person).CanSave() &&
                ((ISaveable)address).CanSave();
        }

        #endregion
    }
}

Like in the previous article about C# implementation of a C++/CX interface, you need to explicitly implement the given interfaces to avoid a bug of the C# compiler.

The Unit Test using WRL and C++

I wrote some simple unit test to demonstrate the creation of the different components using WRL/C++. This is the only way that you can use to create a WinRT component the old COM fashion way. You don't need to use GUID, WRL provides some more friendly way to create the components.

Although only one set of component is implemented in C++ with WRL, they can all be loaded by C++ code. The creation of an instance of a WinRT component with WRL is done using the method Windows::Foundation::ActivateInstance(). The flollowing code shows how to use this static method.

C++
template<typename T>
void CreateComponent(const wchar_t* activableClassId, 
                     ::Microsoft::WRL::Details::ComPtrRef<ComPtr<T>> pInstance)
{
    HStringReference runtimeClass(activableClassId);

    HRESULT hr = Windows::Foundation::ActivateInstance< ComPtr<T>>( runtimeClass.Get(), pInstance );
    Assert::IsTrue(SUCCEEDED(hr));
}

With this method it is possible to create an instance of an object by its name, like you would have done using COM anc CoCreateInstance(). This way you could implement a pseudo plug-in mechanism where you could list the name of the component in an XML file for example, giving you the ability to add new components without recompiling the code as long as they implement expected interfaces. However, you will need to distribute all the components with the package of the application, they can't be delivered separately except if your application is an enterprise application not deployed through the Windows store.

The unit test also demonstrate how implementations with different technologies can be mixed without any problem. The code of the test method is there.

C++
void TestMixCitizenAddress(ComPtr<ABI::WRLCompV1::ICitizen> pICitizen, 
	const wchar_t* wsName,
	const wchar_t* wsSurname,
	ComPtr<ABI::WRLCompV1::IAddress> pIAddress,
	const wchar_t* wsStreet,
	const wchar_t* wsCity,
	const wchar_t* wsStreet2,
	const wchar_t* wsCity2)
{
    // Get the IPerson interface of ICitizen
    ComPtr<ABI::WRLCompV1::IPerson> pIPerson;
    HRESULT hr = pICitizen.As(&pIPerson);
    Assert::IsTrue(SUCCEEDED(hr));

    // Test the IPerson (of ICitizen...)
    TestIPerson(pIPerson, wsName, wsSurname);

    ComPtr<ABI::WRLCompV1::IAddress> pCitizenAddress;
    hr = pICitizen->get_Address(&pCitizenAddress);
    Assert::IsTrue(SUCCEEDED(hr));

    // Test the integrated address
    TestIAddress(pCitizenAddress,  wsStreet, wsCity);

    // Test the separate address
    TestIAddress(pIAddress, wsStreet2, wsCity2);

    // Set Address of Citizen with the separate address
    hr = pICitizen->put_Address(pIAddress.Get());
    Assert::IsTrue(SUCCEEDED(hr));

    // Get the Address of Citizen
    hr = pICitizen->get_Address(&pCitizenAddress);
    Assert::IsTrue(SUCCEEDED(hr));

    HSTRING hStreet;
    HSTRING hCity;

    // Get the Street and the City
    TestGetProperty(pCitizenAddress, Street, hStreet);
    TestGetProperty(pCitizenAddress, City, hCity);

    HString street;
    HString city;
    street.Set(hStreet);
    city.Set(hCity);

    // Validate the test
    Assert::AreEqual(wsStreet2, street.GetRawBuffer(nullptr));
    Assert::AreEqual(wsCity2, city.GetRawBuffer(nullptr));
}

Those of you who have experience with COM will recognize the structure of the code as WRL is very similar to ATL. The complete code of the components and of the unit test is given in the attached solution.

Points of Interest

Like the previous articles the class hierarchy that I used is very simple but it illustrate many possibilities of WinRT components and WRL. I ran into many issues, some that I could solve easily and some that are bugs of the compilers or of the framework, but fortunately they currently all have a workaround.

WinRT components also have many limitations compared to legacy COM components and I find some of those limitations quite frustrating specially if you are a COM power user.

Currently WRL and the new technologies like type projection are only available for Windows store application. It would be very useful if Microsoft could provide the modern features like type projection to the legacy COM technology for the regular windows platform.

License

This article, along with any associated source code and files, is licensed under The Code Project Open License (CPOL)


Written By
Architect Connect In Private
Singapore Singapore
Software Architect, COM, .NET and Smartcard based security specialist.

I've been working in the software industry since I graduated in Electrical and Electronics Engineering. I chose software because I preferred digital to analog.

I started to program with 6802 machine code and evolved to the current .NET technologies... that was a long way.

For more than 20 years I have always worked in technical positions as I simply like to get my hands dirty and crack my brain when things don't go right!

After 12 years in the smart card industry I can claim a strong knowledge in security solutions based on those really small computers!
I've been back into business to design the licensing system for the enterprise solution for Consistel using a .NET smart card (yes they can run .NET CLR!)

I'm currently designing a micro-payment solution using the NXP DESFire EV1 with the ACSO6 SAM of ACS. I can then add a full proficient expertise on those systems and NFC payments.
This technology being under strict NDA by NXP I cannot publish any related article about it, however I can provide professional consulting for it.

You can contact me for professional matter by using the forum or via my LinkedIn profile.

Comments and Discussions

 
QuestionWRL newbie questions Pin
umeca7429-Oct-16 6:37
umeca7429-Oct-16 6:37 
QuestionLoading WRT class inside C# (also WRT) Pin
reichel20-Mar-15 11:03
reichel20-Mar-15 11:03 
AnswerRe: Loading WRT class inside C# (also WRT) Pin
orouit22-Mar-15 15:24
professionalorouit22-Mar-15 15:24 
Sorry I haven't used WinRT API since quite some time and I have no idea right now, I'd have to test that myself and I'm a bit busy with several other projects at this time.

I suggest you place a request with MS, I have done it successfully when I was experimenting WinRTThumbs Up | :thumbsup:
Software Architect, COM, .NET and Smartcard security specialist.

QuestionMix C++/CX and WRL in the same DLL? Pin
__PPS__9-Oct-13 19:40
__PPS__9-Oct-13 19:40 
AnswerRe: Mix C++/CX and WRL in the same DLL? Pin
orouit14-Oct-13 16:39
professionalorouit14-Oct-13 16:39 
GeneralRe: Mix C++/CX and WRL in the same DLL? Pin
__PPS__5-Nov-13 10:20
__PPS__5-Nov-13 10:20 

General General    News News    Suggestion Suggestion    Question Question    Bug Bug    Answer Answer    Joke Joke    Praise Praise    Rant Rant    Admin Admin   

Use Ctrl+Left/Right to switch messages, Ctrl+Up/Down to switch threads, Ctrl+Shift+Left/Right to switch pages.