Reputation: 39625
I have two app domains, one parent creating the child domain. In the child domain, there is a MarshalByRef
object, being communicated with using .NET Remoting. An object running in the parent domain invokes the wrapper for the remote object as part of the application's function:
public class ScanningTask : Task
{
private class Loader : MarshalByRef
{
public void Load(IEnumerable<string> paths)
{
...
}
public event EventHandler<LoadEventArgs> OnLoad;
}
public void RunTask()
{
var domain = AppDomain.CreateDomain("LoadDomain");
var loader = (Loader)domain.CreateInstanceFromAndUnwrap(
typeof(Loader).Assembly.Location,
typeof(Loader).FullName);
loader.Load(...);
AppDomain.Unload(domain);
}
}
Most code removed for brevity.
This Loader
object exposes an OnLoad
event I'd like to capture in the parent domain. If I just add an event handler delegate, it tries to serialize the ScanningTask
into the child domain and throws an exception about it not being serializable.
What I really want is for the event to be communicated across the domains. Any clever suggestions as to how?
Upvotes: 5
Views: 6900
Reputation: 6242
Based on this solution, you could make your Task class task inherit from MarshalByRefObject as well. This would solve the serialization issue as it would pass a cross-AppDomain serialized reference which would be used to attach to the event.
public class ScanningTask : MarshalByRefObject
{
private class Loader : MarshalByRefObject
{
public void Load()
{
if (OnLoad != null)
OnLoad(this, EventArgs.Empty);
}
public event EventHandler OnLoad;
}
public void RunTask()
{
var domain = AppDomain.CreateDomain("LoadDomain");
var loader = (Loader)domain.CreateInstanceFromAndUnwrap(
typeof(Loader).Assembly.Location,
typeof(Loader).FullName);
loader.OnLoad += new EventHandler(loader_OnLoad);
loader.Load();
AppDomain.Unload(domain);
}
void loader_OnLoad(object sender, EventArgs e)
{
Console.Write("load event called");
}
}
If for existing codebase reasons the base class Task cannot be made to inherit from MarshalByRefObject, your solution could be a proxy class that inherits from Loader (therefore being a MarshalByRefObject itself) and forwards calls to an actual unwrapped instance.
public class ScanningTask
{
private class Loader : MarshalByRefObject
{
public virtual void Load()
{
RaiseOnLoad(this);
}
protected void RaiseOnLoad(Loader loader)
{
if (OnLoad != null)
OnLoad(loader, EventArgs.Empty);
}
public event EventHandler OnLoad;
}
private class LoaderProxy : Loader
{
public readonly Loader Instance;
public LoaderProxy(Loader loaderInstance)
{
this.Instance = loaderInstance;
this.Instance.OnLoad += new EventHandler((sender, e) => RaiseOnLoad(this.Instance));
}
public override void Load()
{
this.Instance.Load();
}
}
public void RunTask()
{
var domain = AppDomain.CreateDomain("LoadDomain");
var loader = (Loader)domain.CreateInstanceFromAndUnwrap(
typeof(Loader).Assembly.Location,
typeof(Loader).FullName);
var proxy = new LoaderProxy(loader);
proxy.OnLoad += new EventHandler(loader_OnLoad);
loader.Load(); // same as proxy.Load()
AppDomain.Unload(domain);
}
void loader_OnLoad(object sender, EventArgs e)
{
Console.Write("load event called");
}
}
Upvotes: 6