Introduction
In the recent releases of Microsoft* Visual Studio 2012 and Windows 8* with Windows Runtime as
a common foundation, there are many new things to investigate. This article demonstrates how to implement
interfaces with equal set of methods and properties in various languages. This can be valuable in
iterative development or just helpful in better understanding interfaces in Windows Runtime.
Background
In order to estimate syntax complexity and details of interfaces implementations with further
comparison of results, a small task, solving a small problem, was taken since small things with
equal functionality are easy to compare. Moreover, the results of compilation into MSIL are also included.
Using the code
A given sample idea stems from a Hybrid JavaScript and C++ online Visual Studio sample. It contains
a UI JavaScript project and 3 worker projects. The worker projects are respectively implemented in
C#, C++/CX and C++/WRL and the UI project is in JavaScript. Each worker project interlaces the picture with
its own method and returns the calculation time through its elapsedTime property.
From JavaScript code the functionality looks as follows:
var myObject = new ImagingNamespace.ImageFilter();
myObject.interlaceImage(srcFile.path, dstImgPath);
var calcTime = myObject.elapsedTime;
Interfaces
First, this is an IDL interface for C++/WRL. It looks almost as a regular COM interface with a few new details:
[uuid(c350a862-0132-4da5-86b2-db0b47bc6913),
version(COMPONENT_VERSION)]
interface IImageCustomProcessing : IInspectable
{
HRESULT InterlaceImage([in] HSTRING inFile, [in] HSTRING outFile);
[propget] HRESULT ElapsedTime([out, retval] DOUBLE* pVal);
}
This is the result of compilation of the interface into IL:
.class interface public auto ansi abstract flag(4000) ImagingCppNative.IImageCustomProcessing
{
.custom instance void [Windows.Foundation]Windows.Foundation.Metadata.VersionAttribute::.ctor(uint32) = (
01 00 00 00 00 01 00 00
)
.custom instance void [Windows.Foundation]Windows.Foundation.Metadata.GuidAttribute::.ctor(
uint32, uint16, uint16, uint8, uint8, uint8, uint8, uint8, uint8, uint8, uint8) = (
01 00 62 a8 50 c3 32 01 a5 4d 86 b2 db 0b 47 bc
69 13 00 00
)
.property instance float64 ElapsedTime()
{
.get instance float64 ImagingCppNative.IImageCustomProcessing::get_ElapsedTime()
{
}
}
.method public hidebysig newslot abstract virtual
instance void InterlaceImage (
[in] string inFile,
[in] string outFile
) cil managed
{
}
}
C++/CX interface is implemented in the following way:
public interface class IImageCustomProcessing
{
void InterlaceImage(String^ inFile, String^ outFile);
property double ElapsedTime { double get(); }
};
The corresponding IL is below:
.class interface public auto ansi abstract flag(4200) ImagingCppCX.IImageCustomProcessing
{
.custom instance void [Windows]Windows.Foundation.Metadata.VersionAttribute::.ctor(uint32) = (
01 00 01 00 00 00 00 00
)
.custom instance void [Windows]Windows.Foundation.Metadata.GuidAttribute::.ctor(uint32,
uint16, uint16, uint8, uint8, uint8, uint8, uint8, uint8, uint8, uint8) = (
01 00 d4 3e d0 d8 70 23 11 3b be 25 82 40 3e 81
01 52 00 00
)
.property instance float64 ElapsedTime()
{
.get instance float64 ImagingCppCX.IImageCustomProcessing::get_ElapsedTime()
{
}
}
.method public hidebysig newslot abstract virtual
instance void InterlaceImage (
string inFile,
string outFile
) cil managed
{
}
}
The C# interface looks as usual:
public interface IImageCustomProcessing
{
void InterlaceImage(String inFile, String outFile);
double ElapsedTime { get; }
}
IL for this interface:
.class interface public auto ansi abstract flag(4000) ImagingCS.IImageCustomProcessing
{
.custom instance void [Windows]Windows.Foundation.Metadata.VersionAttribute::.ctor(uint32) = (
01 00 00 00 00 01 00 00
)
.custom instance void [Windows]Windows.Foundation.Metadata.GuidAttribute::.ctor(uint32,
uint16, uint16, uint8, uint8, uint8, uint8, uint8, uint8, uint8, uint8) = (
01 00 72 2c c0 d1 59 00 d9 50 63 5f 66 f4 3e 7f
60 91 00 00
)
.property instance float64 ElapsedTime()
{
.get instance float64 ImagingCS.IImageCustomProcessing::get_ElapsedTime()
{
}
}
.method public hidebysig newslot abstract virtual
instance void InterlaceImage (
[in] string inFile,
[in] string outFile
) cil managed
{
}
}
It's not hard to notice that the resulting MSIL for all languages is almost completely equal, no matter which language has been used.
Classes
This section describes how to bring these interfaces to life and to make them actually work.
The IDL interface in C++/WRL is implemented in the following manner:
class ImageFilter : public RuntimeClass<::ABI::ImagingCppNative::IImageCustomProcessing>
{
InspectableClass(RuntimeClass_ImagingCppNative_WinRTImageProcComponent, TrustLevel::BaseTrust);
...
public:
IFACEMETHOD(InterlaceImage)(_In_ HSTRING inFile, _In_ HSTRING outFile);
IFACEMETHOD(get_ElapsedTime)(DOUBLE *time);
...
}
ActivatableClass(ImageFilter);
C++/CX implementation looks like this:
public ref class ImageFilter sealed : IImageCustomProcessing
{
...
public:
virtual void InterlaceImage(String^ inFile, String^ outFile);
virtual property double ElapsedTime
{
double get() { return elapsedTime; } }
...
}
The C# code is the simplest:
public sealed class ImageFilter : IImageCustomProcessing
{
...
public void InterlaceImage(String inFile, String outFile)
{ ... }
public double ElapsedTime
{
get { return elapsedTime; }
}
...
}
C# implementation remark
C# in .NET 4.5 targeting Metro UI (or Microsoft design language, or with WinMD output) is made in
such a way that all IO operations are asynchronous. Therefore, the special trick has been done to make
loading and unloading synchronous and align the code idea with C++ pieces.
The following code snippet runs an asynchronous piece of code in a synchronous method.
int SyncMethod()
{
var asyncTask = new TaskFactory().StartNew(async () =>
{
return 0;
});
var syncTask = asyncTask.ContinueWith(tt =>
{
return tt.Result.Result;
});
syncTask.Wait();
int taskResult = syncTask.Result
return taskResult;
}
Please notice that the lambda function in StartNew
constructor is asynchronous and on
the contrary the lambda function in ContinueWith
is regularly synchronous. In this case,
taskResult
will be assigned only after asyncTask
and syncTask
both
finish.
Conclusion
As a result, interfaces with completely equal functionality may be created in different languages and notably
resulting MSIL code for different interfaces looks equal, no matter which tool--IDL, CL or CSC--has been used.
History
December 15, 2012 - Initial publishing.
Anton possess a great hands-on 10+ year development experience in a number of areas from assembler to web development.
Recently he relocated to Bay Area as a consultant to help companies bring their ideas to life.