Introduction
In a previous article I showed how to create WinRT components in
C++/CX that implement a set of interfaces. For demonstration I
provided several implementations of the same interface in different
components. This article extends the exercise and will show you how to
create WinRT components in C#, i.e., .NET that implement interfaces that
have been defined in a C++/CX Windows store DLL.
What I'm showing in this article is valid at the time I'm writing it
because while creating those components with Visual Studio 2012, I
discovered
some bugs in the compilers and hopefully those bugs will soon be fixed
by Microsoft.
Background
I suggest that before going through this article, you first read the
previous
one as the interfaces I'm using are created there. As I
mentioned before even if Microsoft is trying to hide it, WinRT is in
fact fully based on the COM technology introduced in Windows 3.11 for
OLE (Object Linking and Embedding). So if you have a COM knowledge you
will find this very simple.
The interfaces
In the previous article I have defined with C++/CX three very simple interfaces
IPerson
,
ICitizen
, and
IAddress
,
and I have given few different implementation in C++. C++/CX makes it very easy to create components for the Windows store, so easy that it even hides the fact
that those components can only exist if they have an interface, so they
generate a default one for you.
However if you create your own component architecture, you will design
your own interfaces that are the only way to interact with components.
Unlike in a class based design, in a component design, objects can only
be accessed by their interfaces that expose behaviors and
properties. A component like an interface only has public members,
there is no protected, internal or even private members in a component.
There is its interfaces and the implementation which you don't have
access.
The interfaces are defines as follows.
namespace WinRTCompV2
{
public interface class ISaveable
{
bool CanSave();
};
public interface class IAddress : ISaveable
{
property String^ Street;
property String^ ZipCode;
property String^ City;
};
public interface class IPerson : ISaveable
{
property String^ Name;
property String^ Surname;
};
public interface class ICitizen : IPerson
{
property IAddress^ Address;
};
}
C++/CX defines the keyword interface as an extension to the C++
language. This interface when you create WinRT component is not like in
C# a reference to a managed object, it is really pointing to a physical
address in memory. WinRT components are native. Of course when you're
going to implement it in C# the code is going to be executed in the
CLR, but the pointer to this object that you would get when creating it
in a C++ program is a pointer to a kind of bootstrap that is calling
the C# implementation.
One characteristic of COM was that you could implement a COM
component with different languages like C++ or VB and later C# in .NET.
WinRT is not different and that's why we can also implement WinRT
components in C#. In fact I suspect that the code behind the scene when
you implement a WinRT component in C# is very similar to the one of a
COM component implemented in C# as well!
Implementing the interfaces in a Windows store C# DLL
In Visual Studio if you select C# and Windows store, you are given few
project templates. The one we are interested in now is Windows Runtime
Component. In a Windows Runtime component all classes that are marked
public will be exported as WinRT components, which means that they will
be modern COM objects!
There are few rules that you have to follow when you create a WinRT
components with a C# class.
- The class must be marked sealed
- The class cannot inherit from any other class
- The class can implement one or several interfaces
The first implementation I did of the interface IPerson
was the
following:
public sealed class Person : IPerson
{
#region Fields
private string name;
private string surname;
#endregion
#region Constructors
public Person()
{
}
public Person(string name, string surname)
{
this.name = name;
this.surname = surname;
}
#endregion
public string Name
{
get { return name; }
set { name = value; }
}
public string Surname
{
get { return surname; }
set { surname = value; }
}
public bool CanSave()
{
return name.Length > 0 && surname.Length > 0;
}
}
At first sight this code looks correct and it should because it is
what the compiler generates for you when you ask it to implement the
interface.
But in fact this code doesn't compile because there is currently a
bug with the C# compiler. If you try to compile this code you will get
the following error for both the Name
and Surname
properties:
Method
'WinRTCompNetDll.Person.WinRTCompV2.IPerson.set_Name(System.String)
'
has a parameter named '' whereas the corresponding parameter in the
implemented interface method
'WinRTCompV2.IPerson.Name.set(System.String)
' is named '__param0
'.
Please make sure that the names are identical.
This problem is known by the Visual Studio product group of
Microsoft and
they kindly gave me two workarounds to be able to implement an
interface defined in C++/CX DLL in a C# component.
The first and most efficient solution to create a C# component is to
simply implement the interface explicitly.
The code that can now compile looks like this:
public sealed class Person : IPerson
{
#region Fields
private string name;
private string surname;
#endregion
#region Constructors
public Person()
{
}
public Person(string name, string surname)
{
this.name = name;
this.surname = surname;
}
#endregion
string IPerson.Name
{
get { return name; }
set { name = value; }
}
string IPerson.Surname
{
get { return surname; }
set { surname = value; }
}
public bool CanSave()
{
return name.Length > 0 && surname.Length > 0;
}
}
This solution has a little side effect in the way you can use the
component in your program. In their quest to simplification of COM the
architects of WinRT have decided that the compiler will automatically
generate an interface from the public members of the implementation
class. Unlike pure COM you don't need to always have an interface to
use the component, you can directly use the class if you only need the
default interface of your component. The problem is that now the
properties of our class Person are private and can only be used when
you
explicitly get the interface. If you try to use directly the
implementation class, you will only see the method CanSave()
.
This
aspect is demonstrated in the unit test of the project.
The second solution they gave me is to implement the components in a
Windows store application.
Implementing the interfaces in a Windows store C# application
In fact this is the first solution that a very helpful support of
Microsoft gave me and when I tried it I found few issues that I want to
share with the readers of CodeProject.
In a Windows store component DLL if you try to create a public class
that is not sealed you will get a compilation error. However when I
tried to implement my interfaces in a Windows store App I discovered
that I can create a public class that implements a C++/CX interface
that is not sealed... This is breaking the rule that all Windows store
component classes must be sealed.
Then because I was able to create that non sealed classes I tried to
create a public sealed class that inherits from that previous class and
implements one of my C++/CX interfaces. This is breaking another rule
of a WinRT component class, which is that you cannot inherit from
another class.
Here is the code of those classes.
namespace WinRTCompNet
{
public class Person : IPerson
{
protected string display = string.Empty;
private string name = string.Empty;
private string surname = string.Empty;
public Person()
{
}
public Person(string name, string surname)
{
this.name = name;
this.surname = surname;
FormatDisplay();
}
public string Name
{
get { return name; }
set
{
name = value;
FormatDisplay();
}
}
public string Surname
{
get { return surname; }
set
{
surname = value;
FormatDisplay();
}
}
public bool CanSave()
{
return Name.Length > 0 && Surname.Length > 0;
}
private void FormatDisplay()
{
display = string.Format("{0} {1}", name, surname);
}
}
}
namespace WinRTCompNet
{
public sealed class Citizen : Person, ICitizen
{
public Citizen() :
base()
{
this.Address = new Address();
}
public Citizen(string name, string surname, Address address) :
base(name, surname)
{
this.Address = address;
}
public IAddress Address
{
get;
set;
}
public string FormatDisplay()
{
return display;
}
public bool CanSave()
{
return base.CanSave() && Address.CanSave();
}
}
}
There is more than just the sealed and inheritance issue to consider. I
declared a protected member in the class Person
as well
as a private
method with the only purpose to set the value of the protected member. Then in the inherited class I created a public method with the same
name as the private of the base class and which returns the value of
the protected member. All this compile and perfectly work!
You can also notice that in the Windows store app, the public
implementation of the interface IPerson
works while it
was giving a
compilation error in the DLL.
I have tested this implementation in a C# unit test and it works. In
this test I declare a variable of IPerson
type and I
assign to this
interface either the C++/CX implementation or the C# implementation
from the Windows store application. I have also used the component
implemented in the Exe in a very simple app. Those two tests seem to
prove that those illegitimate components can be use properly. However I
wanted to be 100% sure that they really are WinRT components by using
them in a C++ unit test.
The Exe can be referenced in the project and intellisense sees the
namespace and the components, so I wrote a simple test but
unfortunately the DLL cannot compile. The weird stuff is that the
compiler simply cannot find the namespace... while intellisense can. I
have submitted those few issues to Microsoft and I'm expecting an
explanation or a solution any time soon.
Points of Interest
The example I give is very simple, but as it illustrates few issues
that you will encounter if you try to implement in C# interfaces that
you have defined in a C++/CX component, I hope that you will find it
useful. I personally searched this issue on the internet and because I
couldn't find any solution to this problem, I wrote a post on a
Microsoft forum and I got a fast answer from them as well as a very
good follow-up on that problem.
WinRT even if it's been built on top of a proved technology like
COM still looks like in the early stage of maturation. It is obvious
that the support in Visual Studio of WinRT still needs to be polished
by the VS product group of Microsoft. But it is an exciting evolution
of Windows app development that puts C++ and component architecture
development back into business.