Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

How WinRT events are interoperate with .NET

In the latest video by Rx team Bart De Smet: Rx Update - .NET 4.5, Async, WinRT I saw that WinRT events exposed to .NET by some really strange metadata, more preciesly - add_/remove_ pair methods signature:

EventRegistrationToken add_MyEvent(EventHandler<MyEventArgs> handler) { … }
void remove_MyEvent(EventRegistrationToken registrationToken) { … }

It looks really great, allowing unsubscribing from event by "disposing" the registration token (Rx does the same kind of thing, returning IDisposable instance from Subscribe() method). So it's became possible to easily unsubscribe lamba-expressions from events, but...

So how does C# allows for working with this kind of events? In .NET it's possible to subscribe an method (static and instance) with one instance on delegate and unsubscribe with completely another delegate instance pointed to the same method. So if I using an WinRT event and just do unsubscribing of some delegate type instance in C#... where did compiler get the correct EventRegistrationToken? How all this magic works?

-- update --

Actually EventRegistrationToken doesn't allows to unsubscribe simply by calling some kind of Dispose() method, that is really sadly:

public struct EventRegistrationToken
{
    internal ulong Value { get; }
    internal EventRegistrationToken(ulong value)
    public static bool operator ==(EventRegistrationToken left, EventRegistrationToken right)
    public static bool operator !=(EventRegistrationToken left, EventRegistrationToken right)
    public override bool Equals(object obj)
    public override int GetHashCode()
}

-- update2 --

WinRT interoperability actually uses global table of registration tokens when subscribing WinRT events with managed objets. For example, interop code for removing handlers looks like this:

internal static void RemoveEventHandler<T>(Action<EventRegistrationToken> removeMethod, T handler)
{
  object target = removeMethod.Target;
  var eventRegistrationTokenTable = WindowsRuntimeMarshal.ManagedEventRegistrationImpl.GetEventRegistrationTokenTable(target, removeMethod);
  EventRegistrationToken obj2;
  lock (eventRegistrationTokenTable)
  {
    List<EventRegistrationToken> list;
    if (!eventRegistrationTokenTable.TryGetValue(handler, out list)) return;
    if (list == null || list.Count == 0) return;
    int index = list.Count - 1;
    obj2 = list[index];
    list.RemoveAt(index);
  }
  removeMethod(obj2);
}

That is really sadly.

like image 789
controlflow Avatar asked Oct 14 '11 23:10

controlflow


People also ask

Is WinRT based on Win32?

WinRT represents a new application execution environment with semantics that are very different than Win32. Unlike Win32, which was designed with C in mind, the WinRT APIs are written in C++ and designed from the beginning to be object oriented.

What is WinRT used for?

WinRT allows developers to create safe "sandboxed" touchscreen applications available from the Microsoft Store. WinRT apps support both the x86 and ARM architectures and multiple programming languages, including C/C++, C#, Visual Basic and JavaScript. WinRT was augmented by the Universal Windows Platform (see UWP).

What is C# WinRT?

C#/WinRT is a NuGet-packaged toolkit that provides Windows Runtime (WinRT) projection support for the C# language. A projection assembly is an interop assembly, which enables programming WinRT APIs in a natural and familiar way for the target language.


2 Answers

When you add or remove a delegate to an WinRT event, like this:

this.Loaded += MainPage_Loaded;

this.Loaded -= MainPage_Loaded;

It looks just like you were working with normal .Net events. But this code actually compiles to something like this (Reflector seems to have some trouble decompiling WinRT code, but I think this is what the code actually does):

WindowsRuntimeMarshal.AddEventHandler<RoutedEventHandler>(
    new Func<RoutedEventHandler, EventRegistrationToken>(this.add_Loaded),
    new Action<EventRegistrationToken>(remove_Loaded),
    new RoutedEventHandler(this.MainPage_Loaded));

WindowsRuntimeMarshal.RemoveEventHandler<RoutedEventHandler>(
    new Action<EventRegistrationToken>(this.remove_Loaded),
    new RoutedEventHandler(this.MainPage_Loaded));

This code won't actually compile, because you can't access the add_ and remove_ methods from C#. But you can that in IL, and that's exactly what the compiler does.

It looks like WindosRuntimeMarshal keeps all those EventRegistrationTokens and uses them to unsubscribe when necessary.

like image 140
svick Avatar answered Sep 23 '22 19:09

svick


Would you accept "there are some amazingly clever people working on the C# language projection" as an answer?

More seriously, what you've discovered is the low level ABI (binary) implementation of the event pattern, the C# language projection knows this pattern and knows how to expose it as C# events. There are classes inside the CLR that implement this mapping.

like image 45
ReinstateMonica Larry Osterman Avatar answered Sep 23 '22 19:09

ReinstateMonica Larry Osterman