将一个对象传递给 Default AppDomain,以从进程中创建的子 AppDomain 接收回调
Passing an object to receive callback from a child AppDomain created within a process to Default AppDomain
位置:
- 我正在从我的进程创建子应用程序域以加载程序集。
- 我可以调用这个 AppDomain。
- 我想将一个对象从我的默认进程 AppDomain 传递到这个新创建的 AppDomain,以接收来自新 AppDomain 中加载到我默认 AppDomain 的程序集的回调。
我找到的一种方法是使用 AppDomain.DoCallback 方法,但是不确定如何在子 AppDomain 中获取主机 AppDomain?
任何人有实现它的想法吗?
总体思路是将派生自 MarshalByRefObject
class 的 class 实例传递给新创建的域。它将保证该对象将通过引用而非值进行编组。这意味着代理将传递给新域而不是原始对象(此代理将由 .NET 框架为您生成)。
稍后当您在此代理上调用方法时,此调用将传回原始域(创建对象的域)。换句话说,一个方法将在原始域中执行。
下面是一段代码,展示了这个想法:
public class Program
{
private static void Main(string[] args)
{
var listener = new Listener();
var otherDomain = AppDomain.CreateDomain("otherDomain");
var instance = (Loader)otherDomain.CreateInstanceAndUnwrap(Assembly.GetExecutingAssembly().FullName, typeof(Loader).FullName);
instance.Init(listener);
}
}
[Serializable]
public class Loader
: MarshalByRefObject
{
public void Init(Listener listener)
{
Console.WriteLine($"[{nameof(Init)}] Hello from {AppDomain.CurrentDomain.FriendlyName} domain");
listener.Callback();
}
}
[Serializable]
public class Listener
: MarshalByRefObject
{
public void Callback()
{
Console.WriteLine($"[{nameof(Callback)}] Hello from {AppDomain.CurrentDomain.FriendlyName} domain");
}
}
当您运行此代码时,您将得到以下结果:
[Init] Hello from otherDomain domain
[Callback] Hello from Sandbox.vshost.exe domain
说明Init方法是在新域中执行的,但在原域中回调。现在再用 : MarshalByRefObject
和 运行 注释 2 行程序。这次 Listener
将按值传递给新域,结果将是:
[Init] Hello from Sandbox.vshost.exe domain
[Callback] Hello from Sandbox.vshost.exe domain
只需在主 AppDomain
中创建远程主机 object 并将其传递给新初始化的 child 域。每当 child 要向主机发送数据时,请使用此远程主机 object。
// This class provides callbacks to the host app domain.
// As it is derived from MarshalByRefObject, it will be a remote object
// when passed to the children.
// if children are not allowed to reference the host, create an IHost interface
public class DomainHost : MarshalByRefObject
{
// send a message to the host
public void SendMessage(IChild sender, string message)
{
Console.WriteLine($"Message from child {sender.Name}: {message}");
}
// sends any object to the host. The object must be serializable
public void SendObject(IChild sender, object package)
{
Console.WriteLine($"Package from child {sender.Name}: {package}");
}
// there is no timeout for host
public override object InitializeLifetimeService()
{
return null;
}
}
我怀疑您创建的 children object 已经实现了一个接口,因此您可以从主域引用它们而无需加载它们的实际类型。在初始化时,您可以将主机 object 传递给它们,因此在初始化之后,您可以从 children.
进行回调
public interface IChild
{
void Initialize(DomainHost host);
void DoSomeChildishJob();
string Name { get; }
}
ChildExample.dll:
internal class MyChild : MarshalByRefObject, IChild
{
private DomainHost host;
public void Initialize(DomainHost host)
{
// store the remote host here so you will able to use it to send feedbacks
this.host = host;
host.SendMessage(this, "I am being initialized.")
}
public string Name { get { return "Dummy child"; } }
public void DoSomeChildishJob()
{
host.SendMessage(this, "Job started.")
host.SendObject(this, 42);
host.SendMessage(this, "Job finished.")
}
}
用法:
var domain = AppDomain.CreateDomain("ChildDomain");
// use the proper assembly and type name.
// child is a remote object here, ChildExample.dll is not loaded into the main domain
IChild child = domain.CreateInstanceAndUnwrap("ChildExample", "ChildNamespace.MyChild") as IChild;
// pass the host to the child
child.Initialize(new DomainHost());
// now child can send feedbacks
child.DoSomeChildishJob();
位置:
- 我正在从我的进程创建子应用程序域以加载程序集。
- 我可以调用这个 AppDomain。
- 我想将一个对象从我的默认进程 AppDomain 传递到这个新创建的 AppDomain,以接收来自新 AppDomain 中加载到我默认 AppDomain 的程序集的回调。
我找到的一种方法是使用 AppDomain.DoCallback 方法,但是不确定如何在子 AppDomain 中获取主机 AppDomain?
任何人有实现它的想法吗?
总体思路是将派生自 MarshalByRefObject
class 的 class 实例传递给新创建的域。它将保证该对象将通过引用而非值进行编组。这意味着代理将传递给新域而不是原始对象(此代理将由 .NET 框架为您生成)。
稍后当您在此代理上调用方法时,此调用将传回原始域(创建对象的域)。换句话说,一个方法将在原始域中执行。
下面是一段代码,展示了这个想法:
public class Program
{
private static void Main(string[] args)
{
var listener = new Listener();
var otherDomain = AppDomain.CreateDomain("otherDomain");
var instance = (Loader)otherDomain.CreateInstanceAndUnwrap(Assembly.GetExecutingAssembly().FullName, typeof(Loader).FullName);
instance.Init(listener);
}
}
[Serializable]
public class Loader
: MarshalByRefObject
{
public void Init(Listener listener)
{
Console.WriteLine($"[{nameof(Init)}] Hello from {AppDomain.CurrentDomain.FriendlyName} domain");
listener.Callback();
}
}
[Serializable]
public class Listener
: MarshalByRefObject
{
public void Callback()
{
Console.WriteLine($"[{nameof(Callback)}] Hello from {AppDomain.CurrentDomain.FriendlyName} domain");
}
}
当您运行此代码时,您将得到以下结果:
[Init] Hello from otherDomain domain
[Callback] Hello from Sandbox.vshost.exe domain
说明Init方法是在新域中执行的,但在原域中回调。现在再用 : MarshalByRefObject
和 运行 注释 2 行程序。这次 Listener
将按值传递给新域,结果将是:
[Init] Hello from Sandbox.vshost.exe domain
[Callback] Hello from Sandbox.vshost.exe domain
只需在主 AppDomain
中创建远程主机 object 并将其传递给新初始化的 child 域。每当 child 要向主机发送数据时,请使用此远程主机 object。
// This class provides callbacks to the host app domain.
// As it is derived from MarshalByRefObject, it will be a remote object
// when passed to the children.
// if children are not allowed to reference the host, create an IHost interface
public class DomainHost : MarshalByRefObject
{
// send a message to the host
public void SendMessage(IChild sender, string message)
{
Console.WriteLine($"Message from child {sender.Name}: {message}");
}
// sends any object to the host. The object must be serializable
public void SendObject(IChild sender, object package)
{
Console.WriteLine($"Package from child {sender.Name}: {package}");
}
// there is no timeout for host
public override object InitializeLifetimeService()
{
return null;
}
}
我怀疑您创建的 children object 已经实现了一个接口,因此您可以从主域引用它们而无需加载它们的实际类型。在初始化时,您可以将主机 object 传递给它们,因此在初始化之后,您可以从 children.
进行回调public interface IChild
{
void Initialize(DomainHost host);
void DoSomeChildishJob();
string Name { get; }
}
ChildExample.dll:
internal class MyChild : MarshalByRefObject, IChild
{
private DomainHost host;
public void Initialize(DomainHost host)
{
// store the remote host here so you will able to use it to send feedbacks
this.host = host;
host.SendMessage(this, "I am being initialized.")
}
public string Name { get { return "Dummy child"; } }
public void DoSomeChildishJob()
{
host.SendMessage(this, "Job started.")
host.SendObject(this, 42);
host.SendMessage(this, "Job finished.")
}
}
用法:
var domain = AppDomain.CreateDomain("ChildDomain");
// use the proper assembly and type name.
// child is a remote object here, ChildExample.dll is not loaded into the main domain
IChild child = domain.CreateInstanceAndUnwrap("ChildExample", "ChildNamespace.MyChild") as IChild;
// pass the host to the child
child.Initialize(new DomainHost());
// now child can send feedbacks
child.DoSomeChildishJob();