Dot-Net

.NET:使用 AppDomains 引發和處理事件的問題

  • May 27, 2011

這是我的問題的基本要點:

  1. 我的主視窗類實例化了 A 類。
  2. A 類在輔助 AppDomain中實例化 B 類。
  3. B 類引發事件,A 類成功處理該事件。
  4. A 類引發了它自己的事件。

**問題:**在第4步中,當A類從擷取B類事件的事件處理方法中引發*自己的事件時,該事件被引發;***但是,**從不呼叫 Window 類中的訂閱處理程序。

沒有拋出異常。如果我刪除輔助 AppDomain,則事件將毫無問題地得到處理。

有誰知道為什麼這不起作用?有沒有另一種方法可以在不使用回調的情況下完成這項工作?

我認為,如果有的話,問題將出現在第 3 步而不是第 4 步。

這是一個真實的程式碼範例來說明問題:

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

在我第一次嘗試解決這個問題時,我刪除了Class B的繼承MarshalByRefObject並將其標記為可序列化。結果是對象按值編組,我剛剛獲得了在主機 AppDomain 中執行的**C 類的副本。**這不是我想要的。

我發現真正的解決方案是Class BDangerousProgram在範例中)也應該繼承自,MarshalByRefObject以便回調也使用代理將執行緒轉換回預設的 AppDomain。

順便說一句,我在 Eric Lippert 找到了一篇很棒的文章,它以非常聰明的方式解釋了 marshal by ref 與 marshal by value。

.NET 事件的魔力隱藏了這樣一個事實,即當您通過 A 的實例訂閱 B 的實例中的事件時,A 會被發送到 B 的 appdomain。如果 A 不是 MarshalByRef,則發送 A 的值副本。現在您有兩個單獨的 A 實例,這就是您遇到意外行為的原因。

如果有人很難理解這是如何發生的,我建議使用以下解決方法,這可以讓事件以這種方式執行的原因變得顯而易見。

為了在 B(在 appdomain 2 內)中引發“事件”並在 A(在 appdomain 1 內)處理它們而不使用真實事件,我們需要創建第二個對象來轉換方法呼叫(無需太多麻煩就可以跨越邊界)事件(其行為與您的預期不同)。這個類,我們稱之為 X,將在 appdomain 1 中實例化,其代理將被發送到 appdomain 2。程式碼如下:

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

虛擬碼將類似於:

  1. AAD1中創建一個新的 appdomain。稱之為AD2
  2. A在****AD2上呼叫 CreateInstanceAndUnwrap 。 B現在存在於AD2中,B (代理)存在於AD1中。
  3. A創建X的一個實例。
  4. A將X交給B代理)
  5. AD2中,B現在有一個**X (代理)**實例(XMBRO
  6. AD1中,A向****X.MyEvent註冊了一個事件處理程序
  7. AD2中,B呼叫X (proxy) .FireEvent()
  8. AD1中,FireEvent在X上執行,這會觸發MyEvent
  9. A 的FireEvent 事件處理程序執行。

為了讓B在****AD1中觸發事件,它不僅必須具有方法,而且還必須具有觸發該方法的實例。這就是我們必須將X的代理髮送到AD2的原因。這也是為什麼跨域事件需要跨域邊界編組事件處理程序的原因! 事件只是方法執行的精美包裝。為此,您不僅需要方法,還需要執行它的實例。

經驗法則必須是,如果您希望跨應用程序域邊界處理事件,這兩種類型(一種公開事件和一種處理事件)都必須擴展 MarshalByRefObject。

引用自:https://stackoverflow.com/questions/1277346