How to get parent AppDomain?

2019-04-11 02:11发布

问题:

I need to do something like this in c# (pseudo):

static var ns = new Non_Serializable_Nor_Marshal()

var app = new AppDomain();
app.execute(foo)

void foo()
{
    var host = AppDomain.Current.Parent; //e.g. the original one
    host.execute(bar)
}

void bar()
{
    ns.Something();
}

IOW I have a non serializeable nor marshal object in one appdomain. I want to create a second domain and execute foo(). From within that second domain I want to execute bar() on the original domain.

How do I pass the original domain to the child one?

回答1:

If you don't want to use interop, you can also use a little trick using AppDomainManager. You can basically automatically 'wire' the 'primary' domain into any domains automatically - albiet the way I do it means you discard your real primary domain.

Here is the class that does all the magic:

/// <summary>
/// Represents a <see cref="AppDomainManager"/> that is
/// aware of the primary application AppDomain.
/// </summary>
public class PrimaryAppDomainManager : AppDomainManager
{
    private static AppDomain _primaryDomain;

    /// <summary>
    /// Gets the primary domain.
    /// </summary>
    /// <value>The primary domain.</value>
    public static AppDomain PrimaryDomain
    {
        get
        {
            return _primaryDomain;
        }
    }

    /// <summary>
    /// Sets the primary domain.
    /// </summary>
    /// <param name="primaryDomain">The primary domain.</param>
    private void SetPrimaryDomain(AppDomain primaryDomain)
    {
        _primaryDomain = primaryDomain;
    }

    /// <summary>
    /// Sets the primary domain to self.
    /// </summary>
    private void SetPrimaryDomainToSelf()
    {
        _primaryDomain = AppDomain.CurrentDomain;
    }

    /// <summary>
    /// Determines whether this is the primary domain.
    /// </summary>
    /// <value>
    ///     <see langword="true"/> if this instance is the primary domain; otherwise, <see langword="false"/>.
    /// </value>
    public static bool IsPrimaryDomain
    {
        get
        {
            return _primaryDomain == AppDomain.CurrentDomain;
        }
    }

    /// <summary>
    /// Creates the initial domain.
    /// </summary>
    /// <param name="friendlyName">Name of the friendly.</param>
    /// <param name="securityInfo">The security info.</param>
    /// <param name="appDomainInfo">The AppDomain setup info.</param>
    /// <returns></returns>
    public static AppDomain CreateInitialDomain(string friendlyName, Evidence securityInfo, AppDomainSetup appDomainInfo)
    {
        if (AppDomain.CurrentDomain.DomainManager is PrimaryAppDomainManager)
            return null;

        appDomainInfo = appDomainInfo ?? new AppDomainSetup();
        appDomainInfo.AppDomainManagerAssembly = typeof(PrimaryAppDomainManager).Assembly.FullName;
        appDomainInfo.AppDomainManagerType = typeof(PrimaryAppDomainManager).FullName;

        var appDomain = AppDomainManager.CreateDomainHelper(friendlyName, securityInfo, appDomainInfo);
        ((PrimaryAppDomainManager)appDomain.DomainManager).SetPrimaryDomainToSelf();
        _primaryDomain = appDomain;
        return appDomain;
    }

    /// <summary>
    /// Returns a new or existing application domain.
    /// </summary>
    /// <param name="friendlyName">The friendly name of the domain.</param>
    /// <param name="securityInfo">An object that contains evidence mapped through the security policy to establish a top-of-stack permission set.</param>
    /// <param name="appDomainInfo">An object that contains application domain initialization information.</param>
    /// <returns>A new or existing application domain.</returns>
    /// <PermissionSet>
    ///     <IPermission class="System.Security.Permissions.SecurityPermission, mscorlib, Version=2.0.3600.0, Culture=neutral, PublicKeyToken=b77a5c561934e089" version="1" Flags="ControlEvidence, ControlAppDomain, Infrastructure"/>
    /// </PermissionSet>
    public override AppDomain CreateDomain(string friendlyName, Evidence securityInfo, AppDomainSetup appDomainInfo)
    {
        appDomainInfo = appDomainInfo ?? new AppDomainSetup();
        appDomainInfo.AppDomainManagerAssembly = typeof(PrimaryAppDomainManager).Assembly.FullName;
        appDomainInfo.AppDomainManagerType = typeof(PrimaryAppDomainManager).FullName;

        var appDomain = base.CreateDomain(friendlyName, securityInfo, appDomainInfo);
        ((PrimaryAppDomainManager)appDomain.DomainManager).SetPrimaryDomain(_primaryDomain);

        return appDomain;
    }
}

And you need to alter your Main() (application entry) slightly:

/// <summary>
/// The main entry point for the application.
/// </summary>
static void Main(string[] args)
{
    new Program().Run(args);
}

void Run(string[] args)
{
    var domain = PrimaryAppDomainManager.CreateInitialDomain("PrimaryDomain", null, null);
    if (domain == null)
    {
        // Original Main() code here.
    }
    else
    {
        domain.CreateInstanceAndUnwrap<Program>().Run(args);
    }
}

Now at any point you can get PrimaryAppDomainManager.PrimaryDomain to get a reference to the primary domain, just remember that it isn't the inital domain created by the .Net runtime - it's one we create immediately.

You can look at the comments in my blog post for an way to get the .Net runtime to hook this in for you automatically using the app.config.

Edit: I forgot to add the extension method I use, here it is:

/// <summary>
/// Creates a new instance of the specified type.
/// </summary>
/// <typeparam name="T">The type of object to create.</typeparam>
/// <param name="appDomain">The app domain.</param>
/// <returns>A proxy for the new object.</returns>
public static T CreateInstanceAndUnwrap<T>(this AppDomain appDomain)
{
    var res = (T)appDomain.CreateInstanceAndUnwrap(typeof(T));
    return res;
}


回答2:

You could try referencing mscoree and then using its methods. I have used this in one of my projects. mscoree will keep track of your AppDomains without any input.

    /// <summary>
    /// Returns the primary application domain.
    /// </summary>
    /// <returns>The primary application domain.</returns>
    public static AppDomain GetPrimaryAppDomain()
    {
        return GetAppDomain(Process.GetCurrentProcess().MainModule.ModuleName);
    }

    /// <summary>
    /// Returns the application domain with the given friendly name.
    /// </summary>
    /// <param name="friendlyName">The friendly name of the application domain.</param>
    /// <returns>The application domain with the given friendly name.</returns>
    /// <exception cref="System.ArgumentNullException">Thrown if friendlyName is null.</exception>
    public static AppDomain GetAppDomain(string friendlyName)
    {
        if (friendlyName == null)
        {
            throw new ArgumentNullException("friendlyName");
        }
        IntPtr handle = IntPtr.Zero;
        CorRuntimeHostClass host = new CorRuntimeHostClass();
        try
        {
            host.EnumDomains(out handle);
            object domain = null;
            while (true)
            {
                host.NextDomain(handle, out domain);
                if (domain == null)
                {
                    return null;
                }
                AppDomain appDomain = (AppDomain)domain;
                if (appDomain.FriendlyName == friendlyName)
                {
                    return appDomain;
                }
            }
        }
        finally
        {
            host.CloseEnum(handle);
            Marshal.ReleaseComObject(host);
            host = null;
        }
    }

(Adapted from http://www.dolittle.com/blogs/einar/archive/2007/05/18/cross-appdomain-singleton.aspx)