Component Development

The Modern C++ Library provides a Standard C++ language projection for the Windows Runtime. That’s great for writing apps, but what about component development? It would be ideal if the same general approach could be used for developing components as well. With Modern you get the best of both worlds! No need to use C++/CX or WRL when Modern can extend its Standard C++ language projection for your own components as well. You get the convenience of modern C++ and with no overhead compared to using the Windows API directly. Whether you’re on the Windows team developing OS components or an external tools developer this is the solution for you.

Start by defining your component with IDL. Here’s a simple activatable Hen class.

namespace Component
{
    runtimeclass Hen;

    [exclusiveto(Component.Hen)]
    [uuid(0d1b9cfc-1a2a-4f22-b235-cf0fd6e9b05b)]
    [version(1.0)]
    interface IHen : IInspectable
    {
        HRESULT Cluck();
    }

    [activatable(1.0)]
    [version(1.0)]
    runtimeclass Hen
    {
        [default] interface IHen;
    }
}

Simply tell the Modern compiler that you’d like to create a new component:

> Modern.exe /c Component.idl

And it will generate the supporting C++ source files for you:

C:\temp> dir /b
Component.Abi.h
Component.Exports.cpp
Component.Exports.def
Component.idl
Component.Implementation.cpp
Component.Modern.h
Component.winmd

It generates everything you need to build the component with the Visual C++ compiler and the .winmd file is ready should you need to use the component from C#. Now open up Component.Implementation.cpp and you can start writing the component’s implementation right away. Here’s what you’ll find:

#include "Component.Modern.h"

namespace Modern { namespace Component { namespace Implementation
{
    class Hen : public HenAbi<Hen>
    {
    public:

        void Cluck()
        {
        }
    };

    class HenFactory : public HenFactoryAbi<HenFactory>
    {
    public:

    };

}}}

There’s no low-level COM plumbing to deal with. You can simply use modern C++ and focus on the component’s implementation right away. Having shipped your component to millions of eager developers you realize that your Hen is really a layer and wants to tell the neighborhood of its egg-laying capabilities. Of course, COM interfaces are immutable contracts so you can’t very well change the IHen interface defined in IDL. Instead, you’ll need to add a new interface to your IDL source file:

[exclusiveto(Component.Hen)]
[uuid(0396c57e-1048-4be8-b311-4371b001b9e7)]
[version(1.0)]
interface ILayer : IInspectable
{
    HRESULT ProudCluck([out] [retval] INT32 * eggs);
}

[activatable(1.0)]
[version(1.0)]
runtimeclass Hen
{
    [default] interface IHen;
    interface ILayer;
}

Now simply rerun the Modern compiler to update the supporting source files:

> Modern /c Component.idl

And then you can simply update your Hen’s implementation by adding the ProudCluck method:

class Hen : public HenAbi<Hen>
{
public:

    void Cluck()
    {
    }

    int ProudCluck()
    {
        return 123;
    }
};

Now the hen can proudly declare how many eggs it hopes to lay this week (hens aren’t very smart). Perhaps we’d be better off initializing the hen with some more realistic data. What the Hen class needs is something other than its default constructor. So I’ll add an IHenFactory interface to the component’s IDL file:

[exclusiveto(Component.Hen)]
[uuid(62b62e01-f0f1-4d9d-86f7-d69c3f6199d8)]
[version(1.0)]
interface IHenFactory : IInspectable
{
    HRESULT CreateHen([in] INT32 eggs, [out] [retval] Hen ** instance);
}

And I’ll just update the runtimeclass to note the new factory interface:

[activatable(1.0)]
[activatable(IHenFactory, 1.0)]
[version(1.0)]
runtimeclass Hen
{
    [default] interface IHen;
    interface ILayer;
}

Again, I’ll rerun the Modern compiler to update the supporting source files:

> Modern /c Component.idl

Now I can simply add the matching constructor to the implementation’s Hen class:

class Hen : public HenAbi<Hen>
{
    int m_eggs = 0;

public:

    Hen(int eggs = 0) : m_eggs(eggs)
    {
    }

    void Cluck()
    {
    }

    int ProudCluck()
    {
        return m_eggs;
    }
};

Notice that I’m careful to preserve a default constructor since the Hen class was originally default activatable. How about a static method? Let’s add the ability to check how many layers are actually roaming around the yard. It’s back to IDL with a new interface to house static members:

[exclusiveto(Component.Hen)]
[uuid(49b79c8a-fd3d-4799-b01c-1f15c630e95a)]
[version(1.0)]
interface IHenStatics : IInspectable
{
    [propget] HRESULT Layers([out] [retval] INT32 * count);
}

And I’ll just update the runtime class to reflect this new static interface:

[activatable(1.0)]
[activatable(IHenFactory, 1.0)]
[static(IHenStatics, 1.0)]
[version(1.0)]
runtimeclass Hen
{
    [default] interface IHen;
    interface ILayer;
}

Again, I’ll rerun the Modern compiler to update the supporting source files but this time I need to update the implementation’s HenFactory class since static members are actually implemented by the class’s activation factory:

class HenFactory : public HenFactoryAbi<HenFactory>
{
public:

    int Layers()
    {
        return 123;
    }
};

The HenFactory is also responsible for implementing the Hen constructors but Modern provides a default implementation for all constructors so you needn’t think about this. Occasionally you might want to perform some custom construction step. In that case you can certainly override any one of the construction methods:

class HenFactory : public HenFactoryAbi<HenFactory>
{
public:

    IInspectable ActivateInstance()
    {
        return Make<Hen>();
    }

    IHen CreateHen(int eggs)
    {
        return Make<Hen>(eggs + 1000);
    }

    int Layers()
    {
        return 123;
    }
};

What about error handling? Well, you might allocate a bit too much memory and the hen catastrophically fails to cluck:

void Cluck()
{
    throw std::bad_alloc();
}

Modern seamlessly transports error information across the ABI boundary (in both directions). So if you happened to call Cluck from a C# app here’s what it looks like:

What about reporting your own error information? Perhaps some crazy developer asks your hen to lay a ridiculous amount of eggs:

Hen(int eggs = 0) : m_eggs(eggs)
{
    if (eggs > 100)
    {
        throw Exception(E_INVALIDARG, L"Too many eggs!");
    }
}

Modern will again transport this information across the ABI boundary so that the app developer will get the needed information:

The Modern C++ Library unleashes the Windows Runtime for Standard C++ developers with incredible convenience and the power tools you need.