Ich habe das Problem beschrieben in this message board post.Wie kann ich ein Ereignis über AppDomains abonnieren (object.Event + = Handler;)
Ich habe ein Objekt, das in seiner eigenen AppDomain gehostet wird.
public class MyObject : MarshalByRefObject
{
public event EventHandler TheEvent;
...
...
}
Ich möchte zu diesem Ereignis einen Handler hinzufügen. Der Handler wird in einer anderen AppDomain ausgeführt. Mein Verständnis ist, dass alles gut ist, Ereignisse werden über diese Grenze magisch mit .NET Remoting geliefert.
Aber, wenn ich dies tun:
// instance is an instance of an object that runs in a separate AppDomain
instance.TheEvent += this.Handler ;
... es kompiliert gut, aber zur Laufzeit nicht mit:
System.Runtime.Remoting.RemotingException:
Remoting cannot find field 'TheEvent' on type 'MyObject'.
Warum?
EDIT: Quellcode der Arbeits App, die das Problem veranschaulicht:
// EventAcrossAppDomain.cs
// ------------------------------------------------------------------
//
// demonstrate an exception that occurs when trying to use events across AppDomains.
//
// The exception is:
// System.Runtime.Remoting.RemotingException:
// Remoting cannot find field 'TimerExpired' on type 'Cheeso.Tests.EventAcrossAppDomain.MyObject'.
//
// compile with:
// c:\.net3.5\csc.exe /t:exe /debug:full /out:EventAcrossAppDomain.exe EventAcrossAppDomain.cs
//
using System;
using System.Threading;
using System.Reflection;
namespace Cheeso.Tests.EventAcrossAppDomain
{
public class MyObject : MarshalByRefObject
{
public event EventHandler TimerExpired;
public EventHandler TimerExpired2;
public MyObject() { }
public void Go(int seconds)
{
_timeToSleep = seconds;
ThreadPool.QueueUserWorkItem(Delay);
}
private void Delay(Object stateInfo)
{
System.Threading.Thread.Sleep(_timeToSleep * 1000);
OnExpiration();
}
private void OnExpiration()
{
Console.WriteLine("OnExpiration (threadid={0})",
Thread.CurrentThread.ManagedThreadId);
if (TimerExpired!=null)
TimerExpired(this, EventArgs.Empty);
if (TimerExpired2!=null)
TimerExpired2(this, EventArgs.Empty);
}
private void ChildObjectTimerExpired(Object source, System.EventArgs e)
{
Console.WriteLine("ChildObjectTimerExpired (threadid={0})",
Thread.CurrentThread.ManagedThreadId);
_foreignObjectTimerExpired.Set();
}
public void Run(bool demonstrateProblem)
{
try
{
Console.WriteLine("\nRun()...({0})",
(demonstrateProblem)
? "will demonstrate the problem"
: "will avoid the problem");
int delaySeconds = 4;
AppDomain appDomain = AppDomain.CreateDomain("appDomain2");
string exeAssembly = Assembly.GetEntryAssembly().FullName;
MyObject o = (MyObject) appDomain.CreateInstanceAndUnwrap(exeAssembly,
typeof(MyObject).FullName);
if (demonstrateProblem)
{
// the exception occurs HERE
o.TimerExpired += ChildObjectTimerExpired;
}
else
{
// workaround: don't use an event
o.TimerExpired2 = ChildObjectTimerExpired;
}
_foreignObjectTimerExpired = new ManualResetEvent(false);
o.Go(delaySeconds);
Console.WriteLine("Run(): hosted object will Wait {0} seconds...(threadid={1})",
delaySeconds,
Thread.CurrentThread.ManagedThreadId);
_foreignObjectTimerExpired.WaitOne();
Console.WriteLine("Run(): Done.");
}
catch (System.Exception exc1)
{
Console.WriteLine("In Run(),\n{0}", exc1.ToString());
}
}
public static void Main(string[] args)
{
try
{
var o = new MyObject();
o.Run(true);
o.Run(false);
}
catch (System.Exception exc1)
{
Console.WriteLine("In Main(),\n{0}", exc1.ToString());
}
}
// private fields
private int _timeToSleep;
private ManualResetEvent _foreignObjectTimerExpired;
}
}
Ausgezeichnet! ~ Danke für die Erklärung. – Cheeso