With the release of Windows 8, a new class library, Windows Runtime, has become available to developers. WinRT components can be used in Windows Store and
desktop applications ; in unmanaged C / C ++ code, in JavaScript, as well as in C # and Visual Basic.
Windows Runtime Metadata
Internally, WinRT components are COM (Component Object Model) components, which now use metadata to describe their APIs. These metadata are stored in files with the * .winmd extension and represent an updated version of .NET metadata that is encoded in accordance with the rules of Section 2 (Metadata Definition and Semantics) of the
ECMA-335 standard. Since the usual .NET Framework assemblies are encoded using the same standard, it means that you can use familiar tools (such as
ildasm.exe , the Object Browser) to view the contents of these files.
For the most part, viewing the WinMD file using the
ildasm.exe utility is very similar to viewing a standard managed assembly. There are several differences that can be seen - first of all, that the WinMD files, in general, do not contain any Intermediate Language (IL) instructions. Instead, these files describe the API provided by the Windows Runtime. The implementation of these interfaces can be completely separated from their definition, and in essence, can be written in native code. However, for developers of managed applications, the details of the WinRT API implementation are irrelevant, because managed code must see only the API definitions it calls. Behind the scenes, the Common Language Runtime (CLR) and the Windows operating system connect API definitions and implementations for you.
For example, in the Windows.Foundation.winmd metadata file (located in the% WinDir% \ System32 \ WinMetadata directory) you can find the following type of
Windows.Foundation.Collections.PropertySet , whose constructor does not contain a body, because the type is implemented in the native code.

')
However, the metadata that describes this type allows the CLR to get an instance of an implementation when calling a class constructor.
When viewing the Windows Runtime metadata, you can also notice that the type and assembly definitions use the new WindowsRuntime keyword.

This keyword is context-sensitive and is interpreted differently depending on where it is applied. For example, if a keyword marks a type definition (TypeDef), then this type is subject to the rules of the Windows Runtime type system and a call of this type should be treated as a call to the WinRT API.
CLR interaction with WinRT components
CRL supports interaction with COM components via
Callable Wrapper (RCW) and
COM Callable Wrapper (CCW) wrappers . Thus, in the CLR, the reference to the WinRT object is a reference to the
RCW , which in turn contains a reference to the WinRT object. Accordingly, the managed code interacts with the
RCW , which is essentially the interface between your code and the WinRT object.

Similarly, in the Windows Runtime, a CLR object reference is a reference to a
CCW , which in turn contains a reference to a CLR object. The Windows Runtime then interacts with the
CCW to access the functionality of the managed object.
WinRT types and managed code
Despite the fact that the Windows Runtime type system is similar to the CLR type system, you can notice that some of the types used in the API definitions do not match the types used in managed code. In order for .NET developers to create applications using familiar technologies, the CLR hides some types of WinRT and provides access to them through others. In general, there are three types of types in Windows Runtime and some of them look different in managed code:
- Basic data types that are encoded in metadata using the same ELEMENT_TYPE enumeration as in the managed assemblies. The exception is SByte , which is not supported in WinRT.
- Projected types (mapped types) , which are encoded in WinMD files as one type, but appear in managed code as their .NET equivalents. For example, when the CLR reads the Windows.Foundation.Uri type in the metadata, it uses the System.Uri type instead. That is, the CLR hides the WinRT type and provides access to it through another type. In addition, the CLR marshals the type between managed and unmanaged code, which allows you to transfer the System.Uri object to the Windows Runtime as Windows.Foundation.Uri . A complete list of WinRT types that CLR projects on FCL types can be found here .
- All other types . The vast majority of WinRT API types are provided to .NET developers as is. For example, if you use the Windows.UI.Xaml.Controls.Button class, the CLR does not provide a special view or marshaling of this type. Types for which the CLR provides extension methods are also included in this category. For example, to use an object that implements the Windows.Storage.IInputStream WinRT interface, with a .NET Framework class that needs a type derived from System.IO.Stream , you should use extension methods that are defined in the System.IO.WindowsRuntimeStreamExtensions class of the System assembly. Runtime.WindowsRuntime.dll.
Interestingly, there is still a small category of types that appear in irrelevant places in the Windows metadata encodings. These are the .NET Framework types, which are used simply to describe the WinRT types. For example, the delegates of the Windows Runtime are encoded with the base
System.MulticastDelegate type, but this does not mean that all the delegates of the Windows Runtime are derived from managed code. Instead, the base type is simply used as a metadata marker to indicate that the type is a delegate type.

Projection Types
When the CLR projects (maps) WinRT types, it does two things:
- The CLR defines the Windows Runtime type as private, not public. This prevents the original type from being visible to the managed code. Those. in terms of managed code, the only type that exists is the .NET Framework type, which is the target of the mapping.
- All references to the source type are converted to references to the target .NET Framework type.
Thus, the CLR presents the
IVector <T> interface as an
IList <T> , the
IVR type definition of the CLR is considered as a type definition with a private scope, rather than a public one. Similarly, the
Windows.UI.Xaml.Controls.UIElementCollection type that implements the IVector <UIElement> has been updated by the CLR. The
IVector <T> interface implementation is redirected to the
IList <T> so that the
UIElementCollection in the managed code implements the IList <UIElement> interface.
Usually
ildasm.exe shows the raw view of the WinMD file without including any redirects. To view the contents of a WinMD file with redirections enabled, you must specify the / project parameter. This option allows you to see how the CLR displays the metadata on disk.
Base type
The WinRT components do not have a common base class, but all Windows runtime classes must implement the
IInspectable interface, which in turn inherits from the
IUnknown interface (which is not surprising). However, for .NET developers, all WinRT types look like types derived from
System.Object and, accordingly, inherit methods such as
Equals ,
GetHashCode , etc. This is made possible by the fact that the CLR marshals objects at runtime to convert types between WinRT and .NET views.
Structures
The WinRT structures, in contrast to the significant CLR types, can contain only open fields of one of the basic types or they can be another WinRT structure. Thus, the following code will generate an error at compile time:
public struct MyStruct { // 'MyStruct' contains non-public field 'MyStruct.i'. // Windows Runtime structures can contain only public fields. private Int32 i; //Windows Runtime structures can contain only fields. public MyStruct(Int32 i) { this.i = i; } //Windows Runtime structures can contain only fields. public void MyFunc() { } }
In addition, WinRT structures cannot define constructors or contain helper methods. However, some CLR structures, for convenience, project on their own, thereby providing developers with helper methods and constructors. These include, for example, the structure of
Windows.Foundation.Point ,
Windows.Foundation.Size and
Windows.Foundation.Rect .
Strings
The
System.String type in WinRT is represented as
HSTRING . When you call a Windows runtime method, the CLR converts any .NET Framework string to
HSTRING before calling the method. Similarly, the CLR converts any strings returned from the runtime method to the
System.String type. There is one more feature - the WinRT type system does not allow strings to be null. Instead of null, use
String.Empty to pass in an empty string. When trying to pass null as a string in the WinRT function, the CLR will throw an
ArgumentNullException . In the same way, you will never see that the WinRT function can return a null string; this can only be an empty string.
Null compatible types
The WinRT API uses the
Windows.Foundation.IReference <T> interface to define a null-compatible meaningful type, which the CLR projects onto its own
System.Nullable <T> . For example, if a method in the WinMD file has the following signature:
IReference<bool> Method(IReference<int> i);
then in managed code, this method will look like this:
Nullable<bool> Method(Nullable<int> i);
Delegates
Only WinRT compatible types can be used as the parameter type or return value of a WinRT delegate. Also, delegates with a global (public) scope cannot be declared as nested (in fact, these are general rules for the Windows runtime as a whole). When you pass a delegate object to the Windows Runtime component, the object is wrapped in a
CCW wrapper, which is not destroyed by the garbage collector until it is released by the component that uses it. Another interesting fact is that the WinRT delegates do not have the BeginInvoke and EndInvoke methods.
Developments
WinRT components can define events using only WinRT delegate types. There is a
Windows.Foundation.EventHandler <T> delegate type that the CLR projects to the .NET Framework
System.EventHandler <TEventArgs> delegate type. When you define a member event:
public event EventHandler<RoutedEventArgs> MyEvent;
then when compiling this line of code, the compiler turns it into the following instructions:
private EventRegistrationTokenTable<EventHandler<RoutedEventArgs>> MyEvent; public EventRegistrationToken add_MyEvent(EventHandler<RoutedEventArgs> handler) { return EventRegistrationTokenTable<EventHandler<RoutedEventArgs>> .GetOrCreateEventRegistrationTokenTable(ref MyEvent) .AddEventHandler(handler); } public void remove_MyEvent(EventRegistrationToken token) { EventRegistrationTokenTable<EventHandler<RoutedEventArgs>> .GetOrCreateEventRegistrationTokenTable(ref MyEvent) .RemoveEventHandler(token); }
As before, the compiler creates a private field and two accessor methods for registering and not subscribing to an event. However, the type of the field and the content of these methods differ from what we are used to (
Delegate.Combine and
Delegate.Remove ). The field type is a generic
EventRegistrationTokenTable <T> class, whose type argument is the corresponding delegate type. This class is responsible for storing the chain of delegates that represent event handlers. When adding a new handler, the
EventRegistrationToken token is
returned , which can be used later to remove the event handler.
public void RaiseEvent() { var list = EventRegistrationTokenTable<EventHandler<RoutedEventArgs>> .GetOrCreateEventRegistrationTokenTable(ref MyEvent) .InvocationList; if (list != null) list(this, new RoutedEventArgs()); } public void Main() { var myClass = new MyClass(); var token = myClass.add_MyEvent(Handler); myClass.RaiseEvent(); myClass.remove_MyEvent(token); myClass.RaiseEvent(); } private void Handler(object sender, RoutedEventArgs args) { Debug.WriteLine("event handling"); }
In order to raise an event, use the
InvocationList property, which returns a delegate whose call list includes all delegates added as event handlers.
Time and date
In WinRT, time and date are represented in UTC by the
Windows.Foundation.DateTime structure. The CLR projects this type on the
System.DateTimeOffset structure, not on
System.DateTime . It is worth noting that
DateTime does not contain information about the time zone. Therefore, the date and time returned by the WinRT functions in UTC format, the CLR converts to local time. Conversely, when the
DateTimeOffset structure is transferred to the WinRT function, the date and time are converted to the UTC format.
Arrays
WinRT API supports only one-dimensional arrays. Accordingly, the following code will cause a compile-time error:
// Arrays in Windows Runtime method signatures must be one dimensional. public int[,] MyFunc() { return new int[5, 5]; }
In managed code, arrays are passed by reference, and changes to the elements of the array will be visible to any code that has a reference to an instance of this array. However, for WinRT this is not always the case, because the contents of the array are marshaled only in the direction that the API defines in its signature using
System.Runtime.InteropServices.InAttribute and
System.Runtime.InteropServices.OutAttribute . Both attributes apply to method parameters or return values ​​and determine the marshaling direction between managed and unmanaged memory at run time. In the Windows Runtime, parameters can be either read only [InAttribute] or write only [OutAttribute] and cannot be marked for reading and writing at the same time [InAttribute], [OutAttribute]. This means that the contents of the array passed to the method, as well as the array itself, should be designed to be read or written. So, the contents of the array, which is marked with the [InAttribute] attribute, are copied to the method being called, so all changes that the method applies to the array are not visible to the calling object. Similarly, the contents of the array, which is marked with the [OutAttribute] attribute, are set by the called method and copied to the caller, so the called method should not make any assumptions about the contents of the original array.
Collections
When sending a collection, the CLR wraps the collection object in a
CCW wrapper and passes the reference to it to the WinRT API. In this case, calls through the wrapper intersect the interaction boundary, which negatively affects the performance. However, unlike arrays, it is possible to perform operations without copying the elements.
Conclusion
Summing up, I note that due to changes in the CLR, developers of managed code can easily adapt to the new Windows Runtime API using familiar technologies. In this article, I described far from all the details of the WinRT and CLR interaction. However, this can serve as a basis for further study and a deeper understanding of the Windows Runtime.