2016-03-03 70 views
2

Sitation孩子的AppDomain收到回調:傳遞對象從過程中創建的默認的AppDomain

  1. 我從我的過程中創建子應用程序域加載程序集。
  2. 我可以撥打這個AppDomain。
  3. 我想從我的默認進程AppDomain傳遞一個對象到這個新創建的AppDomain,以接收從新AppDomain加載到我的默認AppDomain的程序集的回調。

我發現的一種方法是使用AppDomain.DoCallback方法,但不知道如何獲取我的子AppDomain中的主機AppDomain?

任何機構有任何想法來實現它?

回答

2

一般的想法是將新類型的實例傳遞給一個從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方法是在一個新的領域執行但在原來的回調。現在註釋2行: MarshalByRefObject並再次運行該程序。這一次Listener會按值傳遞到新的領域,其結果將是:

+0

感謝米哈爾和taffer! – Abby

+0

這簡化了很多東西! – Abby

+0

@abby - 如果我的回答很有幫助,您可以接受並投票。 –

1

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

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(); 
相關問題