在新的AppDomain下执行dll程序集

时间:2014-09-26 21:32:25

标签: c# .net .net-assembly appdomain

我现在已经研究了几天了。我试图在它自己的AppDomain下的外部程序集中正确创建一个类的实例。

我已经能够在新的AppDomain下加载外部程序集,但是它的所有依赖项似乎都加载在父AppDomain中,这违背了目的,因为我想稍后将dll卸载到释放锁定(插件系统)。知道为什么会这样做吗?

public MyCustomObject BindAssembly()
{
    string currentAssemblyPath = @"C:\PathToMy\Assembly";
    string currentAssemblyFile = @"C:\PathToMy\Assembly\MyAssembly.dll";
    AssemblyName currentAssemblyName = AssemblyName.GetAssemblyName(currentAssemblyFile);
    AppDomain domain = AppDomain.CurrentDomain;
    domain.AssemblyResolve += domain_AssemblyResolve;
    AppDomainSetup setup = new AppDomainSetup()
    {
        PrivateBinPath = currentAssemblyPath,
        ApplicationBase = domain.BaseDirectory,
        DynamicBase = domain.SetupInformation.DynamicBase,
        ShadowCopyFiles = domain.SetupInformation.ShadowCopyFiles,
        CachePath = domain.SetupInformation.CachePath,
        AppDomainManagerAssembly = domain.SetupInformation.AppDomainManagerAssembly,
        AppDomainManagerType = domain.SetupInformation.AppDomainManagerType
    };
    AppDomain newDomain = AppDomain.CreateDomain("NewDomain", AppDomain.CurrentDomain.Evidence, setup);
    newDomain.AssemblyResolve += newDomain_AssemblyResolve;

    currentAssembly = newDomain.Load(currentAssemblyName);
    // tried this too
    //var obj = domain.CreateInstanceFromAndUnwrap(currentAssemblyFile, className);

    // list all of the assemblies inside the custom app domain
    var newDomainAssemblies = newDomain.GetAssemblies(); // (contains my loaded assembly, but not dependencies)

    // list all of the assemblies inside the parent app domain
    var appAssemblies = AppDomain.CurrentDomain.GetAssemblies(); // (contains my loaded assembly as well as all dependencies)

    return obj as MyCustomObject;
}

// resolve dependencies in custom domain
Assembly newDomain_AssemblyResolve(object sender, ResolveEventArgs args)
{
    // this never fires
    AppDomain domain = (AppDomain)sender;
}

// resolve dependencies in parent domain
Assembly domain_AssemblyResolve(object sender, ResolveEventArgs args)
{
    AppDomain domain = (AppDomain)sender;
    string assemblyDependencyPath = String.Format(@"{0}", Path.GetDirectoryName(Uri.UnescapeDataString(new UriBuilder(args.RequestingAssembly.CodeBase).Path)));
    string[] files = Directory.GetFiles(assemblyDependencyPath, "*.dll", SearchOption.AllDirectories);
    foreach (string file in files)
    {
        // if we found it, load the assembly and cache it.

        AssemblyName aname = AssemblyName.GetAssemblyName(file);
        if (aname.FullName.Equals(args.Name))
        {
            Assembly assembly = domain.Load(aname);
            //Assembly assembly = Assembly.LoadFrom(file);  // also tried this, same result
            return assembly;
        }
    }
}

1 个答案:

答案 0 :(得分:1)

我不确定这个(或者一般的跨域应用程序)如何与抽象类的不同实现一起工作,因为您可能会发现在主AppDomain中使用子类仍然需要依赖项,但最近我不得不做类似的事情,发现使用与MyCustomObject在同一个程序集中的Serializable Loader类是保持分离的最佳方法。

我们的想法是在当前的AppDomain中创建Loader,然后编组到新的AppDomain,并要求加载并实例化那里所需的程序集和类型。

请注意,Loader和从MyCustomObject继承的任何内容都必须为Serializable或从MarshalByRefObject继承,因为它们将在AppDomain之间传递

public MyCustomObject BindAssembly()
{
    string currentAssemblyPath = @"C:\PathToMy\Assembly";
    string currentAssemblyFile = @"C:\PathToMy\Assembly\MyAssembly.dll";
    AssemblyName currentAssemblyName = AssemblyName.GetAssemblyName(currentAssemblyFile);
    AppDomain domain = AppDomain.CurrentDomain;
    domain.AssemblyResolve += domain_AssemblyResolve;
    AppDomainSetup setup = new AppDomainSetup()
    {
        PrivateBinPath = currentAssemblyPath,
        ApplicationBase = domain.BaseDirectory,
        DynamicBase = domain.SetupInformation.DynamicBase,
        ShadowCopyFiles = domain.SetupInformation.ShadowCopyFiles,
        CachePath = domain.SetupInformation.CachePath,
        AppDomainManagerAssembly = domain.SetupInformation.AppDomainManagerAssembly,
        AppDomainManagerType = domain.SetupInformation.AppDomainManagerType
    };

    AppDomain newDomain = AppDomain.CreateDomain("NewDomain", AppDomain.CurrentDomain.Evidence, setup);

    newDomain.Load(typeof(Loader).Assembly.GetName());

    Loader loader = (Loader)newDomain.CreateInstanceAndUnwrap(
        typeof(Loader).Assembly.FullName, typeof(Loader).FullName);

    // load the assembly containing MyCustomObject into the remote domain
    loader.LoadAssembly(currentAssemblyFile);

    // ask the Loader to create the object instance for us
    MyCustomObject obj = loader.CreateCustomObject();

    return obj;
}

public class Loader : MarshalByRefObject
{
    /// <summary>Stores the assembly containing the task class.</summary>
    private Assembly assembly;

    /// <summary>Retrieves the current lifetime service object that controls the lifetime policy for this instance.</summary>
    /// <returns>This always returns null.</returns>
    public override object InitializeLifetimeService()
    {
        return null;
    }

    /// <summary>Loads the assembly containing the task class.</summary>
    /// <param name="path">The full path to the assembly DLL containing the task class.</param>
    public void LoadAssembly(string path)
    {
        this.assembly = Assembly.Load(AssemblyName.GetAssemblyName(path));
    }

    /// <summary>Instantiates the required object.</summary>
    /// <param name="classFullName">The full name (namespace + class name) of the task class.</param>
    /// <returns>The new object.</returns>
    public MyCustomObject CreateCustomObject()
    {
        MyCustomObject instance = new MyCustomObject();
        // do whatever you want with the instance here
        return instance;
    }
}