I'm having the problem described in this message board post.
I have an object that is hosted in its own AppDomain.
public class MyObject : MarshalByRefObject
{
public event EventHandler TheEvent;
...
...
}
I'd like to add a handler to that event. The handler will run in a different AppDomain. My understanding is this is all good, events get delivered across that boundary magically, with .NET Remoting.
But, when I do this:
// instance is an instance of an object that runs in a separate AppDomain
instance.TheEvent += this.Handler ;
...it compiles fine but fails at runtime with:
System.Runtime.Remoting.RemotingException:
Remoting cannot find field 'TheEvent' on type 'MyObject'.
Why?
EDIT: source code of working app that demonstrates the problem:
// 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;
}
}