.NET: Problem beim Aufrufen und Behandeln von Ereignissen mit AppDomains

8

Hier ist der grundlegende Kern meines Problems:

  1. Meine Hauptfensterklasse instanziiert Klasse A.
  2. Klasse A instanziiert Klasse B in einer sekundären Anwendungsdomäne .
  3. Klasse B löst ein Ereignis aus und Klasse A behandelt das Ereignis erfolgreich.
  4. Klasse A löst ein eigenes Ereignis aus.

Problem: Wenn in Klasse 4 das eigene -Ereignis von der Ereignisbehandlungsmethode ausgelöst wird, die das Ereignis der Klasse B erfasst hat, wird das Ereignis ausgelöst. Der abonnierende Handler in der Window-Klasse wird jedoch nie aufgerufen.

Es werden keine Ausnahmen ausgelöst. Wenn ich die sekundäre AppDomain entferne, wird das Ereignis problemlos behandelt.

Weiß jemand, warum das nicht funktioniert? Gibt es eine andere Möglichkeit, dies ohne Rückruf auszuführen?

Ich würde denken, dass das Problem in Schritt 3 statt in Schritt 4 auftreten würde.

Hier ist ein echtes Codebeispiel zur Veranschaulichung des Problems:

%Vor%     
Rob Sobers 14.08.2009, 11:21
quelle

2 Antworten

5

Bei meinem ersten Versuch, dieses Problem zu lösen, habe ich die Vererbung von Klasse B MarshalByRefObject entfernt und stattdessen als serialisierbar gekennzeichnet. Das Ergebnis war, dass das Objekt nach Wert gemarshallt wurde und ich gerade eine Kopie von Klasse C erhalten habe, die in der Host-AppDomain ausgeführt wird. Das wollte ich nicht.

Die wirkliche Lösung, die ich gefunden habe, war, dass Klasse B ( DangerousProgram im Beispiel) auch von MarshalByRefObject erben sollte, damit der Rückruf auch einen Proxy verwendet strong>, um den Thread wieder in die Standard-AppDomain zu übertragen.

By the way, hier ist ein großartiger Artikel , den ich von Eric Lippert gefunden habe das erklärt Marshal durch ref vs. Marshal nach Wert auf sehr clevere Weise.

    
Rob Sobers 15.08.2009, 11:30
quelle
31

Die Magie von .NET-Ereignissen verbirgt die Tatsache, dass, wenn Sie ein Ereignis in einer Instanz von B durch eine Instanz von A abonnieren, A in die App-Domäne von B gesendet wird. Wenn A nicht MarshalByRef ist, wird eine Wertkopie von A gesendet. Jetzt haben Sie zwei separate Instanzen von A, weshalb Sie das unerwartete Verhalten erfahren haben.

Wenn es jemandem schwer fällt zu verstehen, wie dies geschieht, schlage ich die folgende Problemumgehung vor, die es offensichtlich macht, warum sich Ereignisse auf diese Weise verhalten.

Um "Ereignisse" in B (innerhalb der Anwendungsdomäne 2) zu erzeugen und sie in A (innerhalb der Anwendungsdomäne 1) zu behandeln, ohne reale Ereignisse zu verwenden, müssen wir ein zweites Objekt erstellen, das Methodenaufrufe übersetzt viel zu) zu Ereignissen (die sich nicht wie erwartet verhalten). Diese Klasse, nennen wir sie X, wird in Appdomain 1 instanziiert, und ihr Proxy wird in Appdomain 2 gesendet. Hier ist der Code:

%Vor%

Der Pseudocode würde etwa so aussehen:

  1. A erstellt in AD1 eine neue App-Domain. Nennen Sie es AD2 .
  2. A ruft CreateInstanceAndUnwrap auf AD2 auf. B ist jetzt in AD2 vorhanden und B (Proxy) ist in AD1 vorhanden / li>
  3. A erstellt eine Instanz von X .
  4. A übergibt X an B (proxy)
  5. In AD2 hat B jetzt eine Instanz von X (proxy) ( X <) / strong> ist MBRO )
  6. In AD1 registriert A einen Ereignishandler mit X.MyEvent
  7. In AD2 ruft B X (Proxy) .FireEvent () auf
  8. In AD1 wird FireEvent auf X ausgeführt, wodurch MyEvent ausgelöst wird
  9. A -Ereignishandler für FireEvent wird ausgeführt.

Damit B ein Ereignis in AD1 zurückfeuern kann, muss es nicht nur die Methode, sondern auch eine Instanz haben, um diese Methode zu aktivieren. Aus diesem Grund müssen wir einen Proxy von X in AD2 senden. Das ist auch, warum domainübergreifende Ereignisse erfordern, dass der Ereignishandler über die Domänengrenze verteilt wird! Ein Ereignis ist nur ein fantastischer Wrapper um eine Methodenausführung. Und dazu brauchen Sie nicht nur die Methode, sondern auch die Instanz, um sie auszuführen.

Als Faustregel gilt: Wenn Sie Ereignisse über eine Grenze einer Anwendungsdomäne hinweg behandeln wollen, müssen beide Typen - dasjenige, das das Ereignis und das, das es behandelt - das MarshalByRefObject erweitern.

    
Will 24.08.2009 14:11
quelle