将对象以接收来自进程中创建的子应用程序域的回调传递到默认应用程序域

本文关键字:应用程序域 回调 默认 创建 对象 进程 | 更新日期: 2023-09-27 18:37:15

>Sitation:

  1. 我正在从我的进程创建一个子应用程序域以加载程序集。
  2. 我可以调用此应用程序域。
  3. 我想将一个对象从默认进程 AppDomain 传递到这个新创建的 AppDomain,以便从新 AppDomain 中加载的程序集接收到我的默认 AppDomain 的回调。

我发现的一种方法是使用 AppDomain.DoCallback 方法,但不确定如何在我的子 AppDomain 中获取我的主机 AppDomain?

任何机构都有实现它的想法吗?

将对象以接收来自进程中创建的子应用程序域的回调传递到默认应用程序域

一般的想法是将派生自MarshalByRefObject类的类实例传递给新创建的域。它将保证此对象将按引用而不是按值封送。这意味着代理将传递到新域而不是原始对象(此代理将由 .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中创建远程主机对象,并将其传递给新初始化的子域。每当子对象想要将数据发送到主机时,请使用此远程主机对象。

// 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;
    }
}

我怀疑您创建的子对象已经实现了一个接口,因此您可以从主域引用它们而无需加载它们的实际类型。在初始化时,您可以将主机对象传递给它们,以便在初始化后您可以从子对象执行回调。

public interface IChild
{
    void Initialize(DomainHost host);
    void DoSomeChildishJob();
    string Name { get; }
}

子项示例.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();