【问题标题】:Loading an assembly into an AppDomain outsite of applicationBase C#将程序集加载到 applicationBase C# 的 AppDomain 外部
【发布时间】:2013-10-10 10:56:44
【问题描述】:

所以最近我一直在从事一个项目,其中应用程序(或可执行文件,无论你想怎么称呼它)需要能够加载和卸载在可执行文件的文件夹中找不到的程序集全部。 (甚至可能是另一个驱动器)

举个例子,我希望能够将我的应用程序放在 D:\AAA\theAppFolder ,并将 DLL 文件的程序集放在 C:\BBB\组件

彻底看,我发现 AppDomain 允许卸载自己和任何附加的程序集的能力,所以我想我会试一试,但是几个小时后似乎出现了问题值得尝试:AppDomains 无法查看应用程序库之外的任何地方。

根据 AppDomain 的纪录片(和我自己的经验),您不能在 ApplicationBase 之外设置 PrivateBinPath ,如果我将 ApplicationBase 设置在应用程序所在的驱动器之外(通过 AppDomainSetup),我会得到 System.IO。 FileNotFoundException 抱怨找不到应用程序本身。

因此,我什至无法达到可以使用 AssemblyResolve ResolveEventHandler 尝试使用 MarhsalByRefObject 继承类来获取程序集的阶段...

这里有一些与我目前正在尝试的代码相关的 sn-ps 代码

    internal class RemoteDomain : MarshalByRefObject
    {
        public override object InitializeLifetimeService() //there's apparently an error for marshalbyref objects where they get removed after a while without this
        {
            return null;
        }
        public Assembly GetAssembly(byte[] assembly)
        {
            try
            {
                return Assembly.Load(assembly);
            }
            catch (Exception e)
            {
                Console.WriteLine(e);
            }
            return null;
        }
        public Assembly GetAssembly(string filepath)
        {
            try
            {
                return Assembly.LoadFrom(filepath);
            }
            catch (Exception e)
            {
                Console.WriteLine(e);
            }
            return null;
        }
    }

    public static Assembly LoadAssembly(string modName, BinBuffer bb)
    {
        string assembly = pathDirTemp+"/"+modName+".dll";
        File.WriteAllBytes(assembly, bb.ReadBytes(bb.BytesLeft()));
        RemoteDomain loader = (RemoteDomain)modsDomain.CreateInstanceAndUnwrap(typeof(RemoteDomain).Assembly.FullName, typeof(RemoteDomain).FullName);
        return loader.GetAssembly(assembly);
    }

尽可能具体:有什么方法可以让可卸载的 AppDomain 加载不在应用程序基础文件夹中的程序集?

【问题讨论】:

  • 如何创建AppDomain
  • 我有多种方法,目前的方法是 modsDomain = AppDomain.CreateDomain("randomname",null,new AppDomainSetup() { PrivateBinPath = AssembliesFolderPath });我知道路径是有效的,因为 Directory.CreateDirectory(在代码的另一部分调用)创建文件夹就好了。
  • 使用 Assembly.Load(byte[]) 是自找麻烦。通过将程序集存储在临时目录中并将类型解包到主应用程序域中来复合。

标签: c# .net-assembly appdomain marshalbyrefobject


【解决方案1】:

每个AppDomain 都有自己的基目录,并且完全不受主应用程序基目录的限制(除非它是应用程序的主 AppDomain)。因此,您可以使用 AppDomains 实现您想要的。

您的方法不起作用的原因是您在 AppDomain 之间传递 Assembly 对象。当您调用任何 GetAssembly 方法时,程序集将加载到子 AppDomain 中,但是当方法返回时,主 AppDomain 也会尝试加载程序集。当然,程序集不会被解析,因为它不在主 AppDomain 的 base dir私有路径GAC 中。

所以一般来说,您永远不应该在AppDomains 之间传递TypeAssembly 对象。

this answer 中可以找到一种加载程序集而不会将它们泄漏到主 AppDomain 的简单方法。

当然,要使您的应用程序与加载在子 AppDomain 中的程序集一起工作,您必须使 MarshalByRefObject 派生类成为 AppDomain 之间的访问点。

【讨论】:

  • 您的回答解释了我观察到的情况。我将类型(位于父域而不是子域)从父域传递到子域,然后它尝试从子域加载类型,然后抛出文件未找到异常。你的回答节省了我的时间。谢谢帕诺斯!
【解决方案2】:

也许您需要使用全局变量,所以如果您使用全局变量来解决问题,您可以声明只读全局变量,例如:

public static string a = "Moosaie";

转换成

public static readonly a = "Moosaie";

无论如何,您不能将全局动态值变量用于 CLR 程序集。

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2021-07-11
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多