.NET: Problem with raising and handling events using AppDomains

前端 未结 2 783
被撕碎了的回忆
被撕碎了的回忆 2020-12-09 06:39

Here is the basic gist of my problem:

  1. My main Window class instantiates Class A.
  2. Class A instantiates Class B in a secondary AppDomain
相关标签:
2条回答
  • 2020-12-09 06:49

    The magic of .NET events hides the fact that, when you subscribe to an event in an instance of B by an instance of A, A gets sent over into B's appdomain. If A isn't MarshalByRef, then a value-copy of A is sent. Now you've got two separate instances of A, which is why you experienced the unexpected behaviors.

    If anyone is having a hard time understanding how this happens, I suggest the following workaround which makes it obvious why events behave this way.

    In order to raise "events" in B (within appdomain 2) and handle them in A (within appdomain 1) without using real events, we'll need to create a second object which translates method calls (which cross boundaries without much ado) to events (which don't behave how you might expect). This class, lets call it X, will be instantiated in appdomain 1, and its proxy will be sent into appdomain 2. Here's the code:

    public class X : MarshalByRefObject
    {
      public event EventHandler MyEvent;
      public void FireEvent(){ MyEvent(this, EventArgs.Empty); }
    }
    

    The pseudocode would go something like:

    1. A, within AD1, creates a new appdomain. Call it AD2.
    2. A calls CreateInstanceAndUnwrap on AD2. B now exists in AD2 and B(proxy) exists in AD1.
    3. A creates an instance of X.
    4. A hands X to B(proxy)
    5. In AD2, B now has an instance of X(proxy) (X is MBRO)
    6. In AD1, A registers an event handler with X.MyEvent
    7. In AD2, B calls X(proxy).FireEvent()
    8. In AD1, FireEvent executes on X, which fires MyEvent
    9. A's event handler for FireEvent executes.

    In order for B to fire an event back in AD1, it not only must have the method but also an instance to fire that method on. That's why we have to send a proxy of X into AD2. This is also why cross-domain events require the event handler to be marshalled across the domain boundary! An event is just a fancy wrapper around a method execution. And to do that you need not only the method but also the instance to execute it on.

    The rule of thumb must be that if you wish to handle events across an application domain boundary, both types--the one exposing the event and the one handling it--must extend MarshalByRefObject.

    0 讨论(0)
  • 2020-12-09 07:01

    In my first attempt at solving this issue, I removed Class B's inheritance of MarshalByRefObject and flagged it as serializable instead. The result was the the object was marshaled by value and I just got a copy of Class C that executes in the host AppDomain. This is not what I wanted.

    The real solution, I found, was that Class B (DangerousProgram in the example) should also inherit from MarshalByRefObject so that the call back also uses a proxy to transition the thread back to the default AppDomain.

    By the way, here's a great article I found by Eric Lippert that explains marshal by ref vs. marshal by value in a very clever way.

    0 讨论(0)
提交回复
热议问题