.NET: Problem mit auslösen und behandeln von Ereignissen mithilfe von AppDomains
Hier ist der Grundtenor meines Problems:
- Mein Hauptfenster-Klasse erzeugt ein Objekt der Klasse A.
- Klasse A instanziiert die Klasse B in einer sekundäre AppDomain.
- Der Klasse B, ein Ereignis auslöst, und der Klasse A behandelt die Veranstaltung erfolgreich.
- Klasse A wirft ein Ereignis seine eigenen.
Problem: In Schritt 4, wenn die Klasse Ein, wirft seine eigenen Ereignis aus der Event-handler-Methode, die gefangen-Klasse B-Ereignis, das Ereignis ausgelöst wird; jedoch der abonnieren handler in der Window-Klasse nie aufgerufen wird.
Es gibt auch keine exceptions geworfen. Wenn ich entfernen Sie die sekundäre AppDomain, in der das Ereignis gehandhabt werden soll, ohne ein problem.
Weiß jemand warum das nicht funktioniert? Gibt es einen anderen Weg um diese Arbeit zu machen, ohne einen Rückruf?
Ich würde denken, wenn überhaupt, das problem würde auftreten, in Schritt 3 anstelle von Schritt 4.
Hier ist ein code Beispiel um das problem zu veranschaulichen:
Class Window1
Private WithEvents _prog As DangerousProgram
Private Sub Button1_Click(ByVal sender As System.Object, ByVal e As System.Windows.RoutedEventArgs) Handles Button1.Click
_prog = New DangerousProgram()
_prog.Name = "Bad Program"
End Sub
Private Sub MyEventHandler(ByVal sender As Object, ByVal e As NameChangedEventArgs) Handles _prog.NameChanged
TextBox1.Text = "Program's name is now: " & e.Name
End Sub
End Class
<Serializable()> _
Public Class DangerousProgram
Private _appDomain As AppDomain
Private WithEvents _dangerousProgram As Program
Public Event NameChanged(ByVal sender As Object, ByVal e As NameChangedEventArgs)
Public Sub New()
//DangerousPrograms are created inside their own AppDomain for security.
_appDomain = AppDomain.CreateDomain("AppDomain")
Dim assembly As String = System.Reflection.Assembly.GetEntryAssembly().FullName
_dangerousProgram = CType( _
_appDomain.CreateInstanceAndUnwrap(assembly, _
GetType(Program).FullName), Program)
End Sub
Public Property Name() As String
Get
Return _dangerousProgram.Name
End Get
Set(ByVal value As String)
_dangerousProgram.Name = value
End Set
End Property
Public Sub NameChangedHandler(ByVal sender As Object, ByVal e As NameChangedEventArgs) Handles _dangerousProgram.NameChanged
Debug.WriteLine(String.Format("Caught event in DangerousProgram. Program name is {0}.", e.Name))
Debug.WriteLine("Re-raising event...")
RaiseEvent NameChanged(Me, New NameChangedEventArgs(e.Name))
End Sub
End Class
<Serializable()> _
Public Class Program
Inherits MarshalByRefObject
Private _name As String
Public Event NameChanged(ByVal sender As Object, ByVal e As NameChangedEventArgs)
Public Property Name() As String
Get
Return _name
End Get
Set(ByVal value As String)
_name = value
RaiseEvent NameChanged(Me, New NameChangedEventArgs(_name))
End Set
End Property
End Class
<Serializable()> _
Public Class NameChangedEventArgs
Inherits EventArgs
Public Name As String
Public Sub New(ByVal newName As String)
Name = newName
End Sub
End Class
Du musst angemeldet sein, um einen Kommentar abzugeben.
Die Magie .NET Ereignisse verbirgt sich die Tatsache, dass, wenn Sie ein Abonnement für ein Ereignis in einer Instanz von B durch eine Instanz von A, A gesendet wird, über in B ' s appdomain. Wenn du nicht MarshalByRef, dann ein value-Kopie Eines gesendet wird. Jetzt hast du zwei separate Instanzen von A, die ist, warum Sie erlebte die unerwartete Verhaltensweisen.
Wenn jemand ist eine harte Zeit zu verstehen, wie dies geschieht, schlage ich die folgende Problemumgehung, die macht es offensichtlich, warum die Ereignisse sich so benehmen.
Zu erhöhen, um "Ereignisse", die in B (in appdomain 2), und behandeln diese in Einer (in appdomain 1) ohne Verwendung von realen Ereignissen, die wir benötigen, um erstellen Sie ein zweites Objekt übersetzt, die Methode Aufrufe (die Grenzen überschreiten, ohne viel Lärm) auf events (die sich nicht so Verhalten, wie man erwarten könnte). Diese Klasse, nennen wir Sie X, die instanziiert werden können in appdomain 1, und seine proxy gesendet werden in appdomain 2. Hier ist der code:
Den pseudocode würde so etwas:
Damit B Feuer ein Ereignis zurück in AD1, es muss nicht nur die Methode, sondern auch eine Instanz, um das Feuer, die Methode auf. Deshalb haben wir uns zu senden, einen proxy X in AD2. Dies ist auch der Grund, warum cross-domain Veranstaltungen bedürfen der event-handler zu gemarshallt werden über die domänengrenze! Ein Ereignis ist nur ein schicker wrapper um eine Methode der Ausführung. Und zu tun, müssen Sie nicht nur die Methode, sondern auch die Instanz ausführen auf.
Die Faustregel muss sein, dass, wenn Sie möchten, um Ereignisse zu behandeln, die über eine Anwendungsdomäne, die Grenze, die beide Typen-der eine Freilegung der Veranstaltung und der Umgang mit it--erweitern müssen MarshalByRefObject abgeleitet.
RealProxy
von X abonnieren X. MyEvent von AD1. Dies zu tun erfordert, dass Ein's event-handler gemarshallt werden nun die übergeben wird AD2. Wenn das Ereignis ausgelöst wird, AD2 verwendet nun dieTransparentProxy
des event-handlers. Wirklich, das ist nur die inverse von Ihrer Lösung, so könnte es nützlich sein, je nachdem, wer (welche app domain) muss selbst dieRealProxy
von der Veranstaltung.Bei meinem ersten Versuch, bei der Lösung dieses Problems habe ich entfernt, Klasse B's Erbe
MarshalByRefObject
und markiert als serialisierbar statt. Das Ergebnis war das Objekt wurde nach dem Wert gemarshallt und ich habe gerade eine Kopie von Klasse C, die ausgeführt wird, in den host-AppDomain. Dies ist nicht das, was ich wollte.Die wirkliche Lösung, die ich fand, war, dass Klasse B (
DangerousProgram
im Beispiel) sollten auch Erben vonMarshalByRefObject
so, dass die call-back auch verwendet einen proxy,, um den übergang des Faden zurück zu der Standard-AppDomain.Durch die Art und Weise, hier ist ein toller Artikel fand ich von Eric Lippert erklärt, dass Marschall von ref vs. marshal-by-value in ein sehr cleverer Weg.