Windows Runtime delegates and object lifetime in C++/CX, redux

Raymond Chen

Raymond

One thing to watch out for when using delegates in C++/CX is that invoking a delegate can raise Platform::Disconnected­Exception. If the delegate is inside a C++/CX event, then the runtime will do the work of catching the Platform::Disconnected­Exception exception, but if you are invoking the delegate manually, then it falls to you to deal with the possibility that the delegate’s object no longer exists.

public delegate void MenuItemInvoked();
ref class CustomMenuItem
{
public:
  CustomMenuItem(MenuItemInvoked^ handler) :
    m_handler(handler) { }

private:
  MenuItemInvoked^ m_handler;

  void NotifyClientThatItemWasInvoked()
  {
     if (m_handler) m_handler();
  }
}

When the item is invoked, we invoke the handler, but it’s possible that the object that was supposed to handle the event has already been destroyed. In that case, the runtime will fail to resolve the weak reference to a strong reference, and it will raise the Platform::Disconnected­Exception. The above code doesn’t handle that exception, so it will crash.

What you should do is catch the Platform::Disconnected­Exception and use that as a signal that the handler is no longer any good and shouldn’t be invoked any more.

  void NotifyClientThatItemWasInvoked()
  {
     try
     {
       if (m_handler) m_handler();
     }
     catch (Platform::DisconnectedException^)
     {
       // Handler is no good.
       // Don't bother invoking it any more.
       m_handler = nullptr;
     }
  }
Raymond Chen
Raymond Chen

Follow Raymond   

4 comments

Comments are closed.

  • Ben Voigt
    Ben Voigt

    This code to cancel a subscription is significantly simpler than it would be in .NET, e.g. C++/CLI. For one thing, it’s not thread-safe, and for another, .NET delegates are multicast so you couldn’t safely null the whole invocation list, you’d have to subtract just the one subscriber that threw (and because of thread-safety issues, you’d have to do that from inside an interlocked-compare-exchange loop).

    I tried to find whether Windows Runtime delegates are also multicast, but the C++/CX documentation wasn’t clear on that point.

    • Raymond Chen
      Raymond Chen

      A Windows Runtime delegate is simply an object with an Invoke method and is therefore unicast. (Events, on the other hand, are multicast.) An app could create a delegate whose Invoke method in turn calls the Invoke method of other delegates, but that’s the app’s choice, and if one of those sub-delegates throws an exception in its Invoke, it’s up to the wrapper delegate to decide what to do. (Remove the sub-delegate from its list and keep going, or propagate the exception out of the wrapper delegate and stop.) C# is the weirdo here: In C#, delegates are mutable and can change their targets dynamically.

      • snarfblam
        snarfblam

        Hmm? I thought c# delegates were immutable. Adding or removing an invocation target creates a new delegate object with its own readonly invocation list, no? C# events are mutable (that they wrap a mutable reference to a delegate). Have things changed since “the old days” of c#?

        • Raymond Chen
          Raymond Chen

          You can += a delegate. Does that mutate the delegate, or does it create a new delegate and retarget the reference? I’m not sure.

          I just tried it. It appears that += creates a new delegate and retargets the reference. So C# delegates are also immutable. How about that.

          So the deal is that C# is the weirdo because it supports the “+” operator on delegates.