Introduction
Windows 8 has been released
last month and Visual Studio 2012 a few months ago. You can find a lot of
articles and blogs that demonstrate how to implement a WinRT component
with Windows 8. I installed Windows 8 on an old machine as well as a
version of Visual Studio 2012 Premium and I tried to develop some
patterns that I have been implementing many times with this new
technology. Doing that I bumped into some issues and while looking for
a solution on various blogs, I couldn't find any that was showing what
I'm demonstrating in this short article.
Background
Although Microsoft presented
WinRT as a new technology, it is in fact a quite old and proven
technology that WinRT is using behind the scenes. Those who have been
coding in the Microsoft ecosystem between 1995 to early 2000 have
certainly already discovered that WinRT is in fact based off the good
old COM technology made famous by Don Box in chronicles in the MSDN
journal. So if like me you are a veteran in software you will find a
lot of familiar things in WinRT and this article.
The power of COM
COM stands for Component Object
Model, a lot has been written about COM and I won't repeat every
detail of this technology using my own words here. For those who
really want to understand this technology and its philosophy I would
advise you to read the book Essential COM written by Don Box.
First of all, COM is not .NET
and COM is technically very different from .NET. COM and as a matter of
fact WinRT which is an evolution of COM, are native technologies. .NET
(C#) and Java are using a virtual machine to execute their code, this
virtual machine sits on top of the machine language to abstract the
execution of the program and makes it independent of the system APIs
that are written in native code. Native code is code that can be
executed directly by the microprocessor of your computer. During the 10
to 15 last years the fashion has been to use managed languages that are
high level languages that generate code for virtual machines and free
the developer from many responsibilities he would have writing native,
also know as unmanaged code.
Languages like C++ are
relatively static compared to modern languages like C# or Java and lack
the ability of exposing objects that can be shared and dynamically
loaded by other environment and languages like VB, Java, Python,
JavaScript, etc... Those who have used the native interop of C# know
that they can use methods from the native Win32 API, but they can't use
directly a class that has been developed in native C++ (not C++/CLI!).
COM has this ability to expose the native interfaces implemented by a
binary object to other languages, even JavaScript and this since a long
long time. I wrote in 2000 a retail application using a COM
architecture used by HTMLA (HTML for application) where the programming
language was MS JavaScript and the execution engine a particular
version of Internet Explorer. Basically a desktop application that was
using HTML as GUI, JavaScript as programming language and COM
components for the core services. Doesn't this sound familiar to you?
What is one of the programming model of windows 8 metro application?
HTML5 with JavaScript and WinRT components. Hey Mr. Microsoft, looks
like you are reviving a 10 to 12 year old technology available with IE5
and IE5.5!!
One of the main interest of COM
is that you can easily build applications that support a plug-in
architecture or what I would call a component based architecture where
you can support new features or type of data without touching the code
of the main application. This is possible thanks to the ability of a
COM object to expose as many interfaces you want and to be dynamically
loaded into memory from a central repository using a factory mechanism.
Once loaded in memory by its name, the application will ask the
component if he can produce the interfaces the application requires
from it to execute some features. This is also known as late
binding.
What I'm going to demonstrate
in that article is how to implement the same interface with different
WinRT components. I also present 2 different architectures to implement
COM/WinRT components with C++/CX.
You certainly have read that a
WinRT component cannot be inherited, this article will also gives a
clear understanding of why this is not possible.
Interface and implementation
classes
A WinRT interface which is also a COM interface used the binary
structure of a C++ vtable which makes C++ the natural language to
implement WinRT components. However Microsoft has done a great job to
make COM more usable in the 21
st century and it is also possible to
write WinRT components with .NET and C# for example, like it was
possible to write COM component with .NET.
Every COM object must implement the IUnknown
interface, this interface provides 3 very important methods:
AddRef
:
Increment the reference count of the object Release
:
Decrement the reference count of the object QueryInterface
:
Allow the caller to query for a given interface if implemented by the
object.
WinRT has added a new interface which is
IInspectable
and basically brings a reflection mechanism to COM. I haven't yet
explore this interface and its usage so I won't talk much about it.
Simply know that a WinRT always implements this interface as well and
you don't have to do it, the compiler extensions do it for you.
What I'm going to do in this article is to define four simple interfaces
and give two different implementations of them in C++/CX. Then we are
going to see, if both architecture are interoperable with each others
and with a robust client technology like .NET.
The following diagram gives a simple view of the possible architectures.
Let me detail a bit this diagram:
- COM interfaces are the
interfaces exposed by the objects to the outside world.
- COM classes are the
implementation classes of the components. Within C++/CX those classes
are public ref classes. They are used to instantiate the components.
- The C++ implementation
classes are internal classes used to create an object hierarchy of the
implementation.
If you look at the interfaces you can see that
they represent a
hierarchy where
ICitizen
inherits from
IPerson
and has an association with
IAddress
.
In the COM classes layer there is no hierarchy of classes, only
an
association between Citizen
and Address
.
Then in the C++ class layer we have again a full hierarchy and
association diagram.
This pattern is one of the pattern I'm going to implement in this
article. The idea of using such a pattern is to benefit of the full
object orientation of C++ and use inheritance, polymorphism and all the
features available to this modern language.
The second pattern is also represented in the above diagram, you just
need remove the C++ class implementation to get it. Each COM class will
be implemented without using a C++ class internal object model. As
there is no inheritance of implementation this may lead to some code
duplication. However in many cases it is not the case and a kind of
reuse is possible.
Implementation with
composition classes
The pattern is quite simple. The object model is created in C++ using
all object oriented concepts at you disposal and it is then mapped with
COM classes to be exposed to the outside boundaries. This model has
some important implication because we are mapping 2 worlds with
different concepts. Let me explain what I mean by that.
COM/WinRT components are created by a factory and they life cycle
depends on a reference counting. Every time a reference to that object
is passed to a method, it's reference counter is increased and when the
method, or the object doesn't use it any more the reference counter
must be decreased. When this counter becomes 0, the COM components is
deleted from the memory. All references to COM component point to the
same instance, this is similar to a reference in C# or Java.
In the C++ world we are using pointers, class reference and value
classes that present a distinct life cycle management. In the composite
model, each COM implementation is having a C++ class that represents
the object and the associations that exist in the outside boundary are
also mapped in the C++ class model. This means that the consistency
between the 2 models has to be maintained in term of instances.
When you work on a reference of a COM component you work in fact on the
internal instance of the composite class. The biggest problem is that
when you create a COM component and associate it to another COM
component, the association must also be created at the composition
class level. We will see that this has complex implications!
There is a very important concept that you must understand with COM.
Once the object is instantiated the only way to access it, are the
public interfaces that the object exposes to the outside world. The
implementation class with its protected, internal and private members
is not accessible to the outside world. Even if when you create an
instance of this object with C# or C++/CX you use the name of the COM
class, the pointer you get is not
a pointer to that class, but a
pointer to the default interface.
However when you are creating this object in C++ and that you know that
you are using a particular implementation of an in-process COM, then
you can cast the interface to the implementation class, and gain access
to internal members. Internal are in fact public members of the
implementation class that are not exported by the DLL.
The following code extract illustrate the most critical aspect if that
pattern.
void Citizen::Address::set(IAddress^ value)
{
WinRTCompV2::Address^ refAddress = nullptr;
try
{
refAddress = (WinRTCompV2::Address^) value;
}
catch(InvalidCastException^ ex)
{
OutputDebugString(std::wstring().append(
L"Address::set raised exception: " , ex->Message->Data()).c_str());
}
if (refAddress == nullptr)
{
refAddress = ref new WinRTCompV2::Address(value->Street, value->ZipCode, value->City);
}
CAddress* ptrAddress = &refAddress->GetAddress();
m_ptrCitizen->SetAddress(ptrAddress, false);
m_address = refAddress;
The set property of Citizen is
of type IAddress^
.
This means that when you set the address you pass a
new instance of the COM Address
to the COM Citizen
.
The difficulty is to create the
association between the CCitizen
and the CAddress
objects at the same
time this association is modified between the COM components. The
method assumes that the IAddress
interface is implemented by the
WinRTCompV2::Address
object which implies that we have access to the
internal methods of that object and in particular the GetAddress()
method that returns a pointer to the internal CAddress
object of
the WinRTCompV2::Address
implementation.
The code checks that
the cast could be done properly and in the case the implementation is
not the one expected, create a new instance of Address
and create the
association in the composition class layer. The consequence is that the
Address reference is not the same as the one passed by the calling
program which means that if you modify the content of the passed
Address
object after the set, this change won't be reflected in the
Citizen
object. This is illustrated in one of the unit tests I provide with the
code of the WinRT components.
Another interesting aspect of that pattern is that pone must be very
careful with the deletion of the different objects. I haven't the time
to implement it (this could
be another article) but I would use a smart pointer with reference
counting in order to manage the different pointers of the
implementation as the same pointer to a composition object may be used
in a COM class and in a composition class.
Direct implementation within
the body of the WinRT component
This is were I had some
difficulties with the new C++/CX compiler! In the early times of COM I
would have defined the interface and coded the 2 implementations of
this interface in the same DLL. But at this time you needed to declare
your interface in an IDL file and give it some GUID and then write the
implementation of the classes using ATL. Now it is much more easy, you
don't even have to explicitly create an interface if you don't need to
implement it in other objects. But there is a little problem if you try
to have the interface and both implementation in the same DLL. The
compiler is not happy because of the generation of the .winmd file. I
couldn't find a solution to put both implementations in the same DLL
but fortunately I could put them in a second DLL where I referenced the
one that is declaring the interfaces.
I haven't managed to find the
explanation from Microsoft but it has to do with the namespace
mechanism and the name of the metadata file. This looks a lot like Java
that asks you to have a source file with the same name as the class and
match the namespace hierarchy with the structure of the directories, if
I remember well my Java moments!
Having say that WinRT
introduces more rules and restrictions than the original COM
technology, and this is one of them.
The second implementation is
done directly in the body of the COM classes, there are no composition
classes and if you remember another limitation of those classes, you
cannot inherit from another class, you can only inherit (I prefer
implement) from interfaces.
That means that the class
Citizen
cannot inherit from the class Person and that would lead to
code duplication. However the original COM addresses this problem in two
ways. One is known as COM aggregation and the other is dynamic
composition. I don't know how to do COM aggregation with the C++/CX but
the composition is very simple to achieve.
The Citizen
object creates an
instance of a Person
object and wraps the methods/properties of the
Person interface in its own implementation.
namespace WinRTCompV3
{
Citizen::Citizen(void) :
m_person(ref new Person()),
m_iAddress(ref new WinRTCompV3::Address())
{
}
Citizen::Citizen(String^ name, String^ surname) :
m_person(ref new Person(name, surname)),
m_iAddress(ref new WinRTCompV3::Address())
{
}
Citizen::Citizen(String^ name, String^ surname, WinRTCompV3::Address^ address) :
m_person(ref new Person(name, surname)),
m_iAddress(address)
{
}
String^ Citizen::Name::get()
{
return m_person->Name;
}
void Citizen::Name::set(String^ value)
{
m_person->Name = value;
}
String^ Citizen::Surname::get()
{
return m_person->Surname;
}
void Citizen::Surname::set(String^ value)
{
m_person->Surname = value;
}
WinRTCompV2::IAddress^ Citizen::Address::get()
{
return m_iAddress;
}
void Citizen::Address::set(IAddress^ value)
{
m_iAddress = value;
}
bool Citizen::CanSave()
{
return m_person->CanSave() && m_iAddress->CanSave();
}
}
The above implementation
illustrates the simplicity of that pattern. In the case of this example it has proved much easier to implement the Citizen
, Person
,
Address
hierarchy directly without a set of composition classes. In some most complex cases it may be more interesting to use the composition
classes. This is the pattern I used more than 10 years ago to implement a more complex hierarchy of COM objects using ATL. Using ATL also opens
more possibility in term of source reuse and templates because COM classes are implemented directly in C++.
The C++/CX model introduces
more simplicity in writing WinRT/COM component but for those like me
who have used intensively ATL it is obvious that we loose a lot of
power because we don't have any more the control of the framework.
However I have barely scratched the surface of that new way of
implementing the WinRT/COM components and there are some great benefits
like the projection that eliminates the headache of the SAFEARRAY
.
Those who have used them know what I'm talking about!
A last interesting aspect of that little test is to mix both implementations of the interfaces I defined.
Mixing the two implementations
Mixing both implementation of the
ICitizen
and
IAddress
interfaces illustrates perfectly the
boundaries of a WinRT
component and why you have to be extremely careful when you
create them. In an interface based architecture, you must
not assume that you can know what class is implementing an interface.
With mechanism like reflection many developers have built patterns that
are not advisable with component technology like COM. When you design a
COM base architecture if you intend to publish your components and make
them available for other languages or technologies than the one you
have used to develop them, then you must not assume that you can ask an
interface to give you a given implementation class. It is better to
build the whole architecture assuming that the only way to access the
object is the interfaces that it publishes.
The following codes illustrates the wrong behavior when mixing with an
implementation which is only compatible with itself.
public void UnitTestMixingWinRTCompV2withWinRTCompV3()
{
WinRTCompV2.IAddress addressV2 =
new WinRTCompV2.Address(TestData.TEXT_STR1, TestData.TEXT_ZIP1, TestData.TEXT_CTY1);
WinRTCompV2.IAddress addressV3 =
new WinRTCompV3.Address(TestData.TEXT_STR2, TestData.TEXT_ZIP2, TestData.TEXT_CTY2);
WinRTCompV2.ICitizen citizenV2 =
new WinRTCompV2.Citizen(TestData.TEXT_NAME1, TestData.TEXT_SURNAME1);
WinRTCompV2.ICitizen citizenV3 =
new WinRTCompV3.Citizen(TestData.TEXT_NAME2, TestData.TEXT_SURNAME2);
citizenV2.Address = addressV2;
addressV2.ZipCode = TestData.TEXT_ZIP2;
string zip2 = citizenV2.Address.ZipCode;
Assert.AreEqual(TestData.TEXT_ZIP2, zip2);
citizenV3.Address = addressV3;
addressV3.ZipCode = TestData.TEXT_ZIP1;
string zip3 = citizenV3.Address.ZipCode;
Assert.AreEqual(TestData.TEXT_ZIP1, zip3);
citizenV2.Address = addressV3;
addressV3.Street = TestData.TEXT_STR1;
string street2 = citizenV2.Address.Street;
Assert.IsFalse(TestData.TEXT_STR1 == citizenV2.Address.Street);
citizenV3.Address = addressV2;br> addressV2.City = TestData.TEXT_CTY2;
string city3 = citizenV3.Address.City;
Assert.IsTrue(TestData.TEXT_CTY2 == citizenV3.Address.City);
}
I had to use IsTrue
and IsFalse
because of a reason I don't know although the strings are the same,
AreEqual
returns false for this test.
The interesting case is when you set the
IAddress
property of the WinRT
component using the composition classes. Because the implementation of
the property fails to get the
CAddress
object of the
Adrress
WinRT
component, it cannot create the association with that object. A new
reference of the right
Address
type is created internally
and the
association is established in the composition classes, that's why the
Street
which is updated in the
Address
after it is set
to
Citizen
is not showing in the
Address
of the
Citizen
that was created internally.
In the second case, using an Address
V2 with a Citizen
V3 works because
the association is directly between the COM instances and an individual
Address
V2 object perfectly works.
Implementing the interfaces in a second set of components in the
same DLL
I originally tried to implement the second set of components in the
same DLL using different a namespace but with the same implementation
class names. This method failed and I had to create two separate DLLs to
achieve this goal. I was a bit frustrated not to be able to do it so I
tried to think if there would be a way to do it.
In the original implementation of COM, you could create many components
in the same DLL that would inherit from the same interface, I did it
many times. One of the differences is that the component and interfaces were
identified by GUID and there was no use of namespaces.
This is how I found the solution: if you want to create several
implementations of the same interface in the same DLL, you need to put
the interface and the components in the
same namespace.
#pragma once
#pragma warning( disable: 4251 )
#include "../CompRTV2/CompRTV2.h"
#include <string>
using namespace Platform;
namespace WinRTCompV2
{
namespace WUXD = Windows::UI::Xaml::Data;
[WUXD::Bindable]
public ref class Address2 sealed : IAddress
{
private:
std::wstring m_street;
std::wstring m_zipCode;
std::wstring m_city;
public:
Address2(void);
Address2(String^ street, String^ zip, String^ city);
public:
virtual property String^ Street {
String^ get();
void set(String^ value);
}
virtual property String^ ZipCode {
String^ get();
void set(String^ value);
}
virtual property String^ City {
String^ get();
void set(String^ value);
}
virtual bool CanSave();
};
}
This is the code of the declaration of the class Address2
which
implements the interface IAddress
. As this is just an example, I used
the same implementation as the one in the other DLL.
I also corrected a bug in catch((InvalidCastException^ ex)
of the
set method of WinRTCompV2::Citizen
. The new code looks like this:
try
{
refAddress = (WinRTCompV2::Address^) value;
}
catch(InvalidCastException^ ex)
{
std::wstring msg = L"Address::set raised exception: ";
msg.append(ex->Message->Data());
OutputDebugString(msg.c_str());
}
WinRT has the following rules when it comes to implement an interface with several components:
- In a single DLL, the interface and its implementations must
appear in the same namespace.
- If an interface is implemented by components with different
namespace, then one DLL must be used for each namespace.
Points of Interest
I hope that this article and
the code that comes with it will help you understand the challenges of
implementing WinRT components and creating software architecture with
them.
As a conclusion I would say
that even if the composition pattern could help reduce the code
footprint in some cases, it introduces complexity and risks that must
be considered. In the case of my example in fact the direct COM class
implementation is smaller and safer because there is no code
duplication as using the dynamic composition of objects to achieve the
inheritance of Person
by Citizen
works.
In a component technology like
WinRT/COM you cannot override a method or a property, and you cannot
access protected or internal members of the implementation of COM
that you encapsulate in another one, even if it is the implementation of
your base interface. But you can inherit the behavior and you can
create a new version of a method. There are obvious limitations with
this technology but there are also many advantages.
I have a background of electronics. Imagine that you use some
transistors to build another component. You cannot take a PIN of the
transistor and replace it by a new PIN, but you can use the signal on
that PIN, process it, and expose the result of that composition as a new
component with its own characteristics. This is what WinRT/COM
components are about. You can assemble them to create new components or
a complete new system.
Like a transistor you can replace a component implementation by another as long as it respects the contract described for its interface.