如何在不访问应用程序目录的情况下将程序集从字节数组加载到非默认AppDomain中?

问题描述 投票:2回答:1

我有一个COFF格式的内存中程序集,该程序集在我的主程序集MyApp.exe中使用:

byte[] assemblyData = GetAssemblyDataFromSomewhere();

((为了进行测试,GetAssemblyDataFromSomewhere方法只能对现有的程序集文件进行File.ReadAllBytes,但是在我的实际应用中没有文件。)

让我们将此程序集称为MyAssembly。它仅具有.NET Framework引用,并且不依赖于任何其他用户代码。

我可以将此程序集加载到当前(默认)AppDomain

Assembly.Load(assemblyData);

// this works
var obj = Activator.CreateInstance("MyAssembly", "MyNamespace.MyType").Unwrap();

现在,我想将此程序集加载到另一个AppDomain中并在那里实例化该类。 MyNamespace.MyType源自MarshalByRefObject,因此我可以在应用程序域之间共享实例。

var newAppDomain = AppDomain.CreateDomain("DifferentAppDomain");

// this doesn't really work...
newAppDomain.Load(assemblyData);

// ...because this throws a FileNotFoundException
var obj = newAppDomain.CreateInstanceAndUnwrap("MyAssembly", "MyNamespace.MyType");

是,我知道AppDomain.Load docs中有一个音符:

此方法仅应用于将程序集加载到当前应用程序域中。

是,它应该用于此,但是...

如果当前的AppDomain.Load对象表示应用程序域A,并且从应用程序域B调用了AppDomain方法,则程序集将加载到两个应用程序域中。

我可以忍受。对于我来说,将程序集同时加载到两个应用程序域中都没有问题(因为无论如何我实际上都将其加载到默认应用程序域中)。

我可以看到该程序集已加载到新的应用程序域中。种类。

Load

这给了我:

var assemblies = newAppDomain.GetAssemblies().Select(a => a.GetName().Name);
Console.WriteLine(string.Join("\r\n", assemblies));

但是尝试实例化该类始终会导致一个mscorlib MyAssembly ,因为CLR试图从文件中加载程序集(尽管它已经加载,至少根据FileNotFoundException而言)。

我可以在AppDomain.GetAssemblies中执行此操作:

MyApp.exe

这有效,但是这会导致第二个应用程序域从文件加载调用程序集(newAppDomain.AssemblyResolve += CustomResolver; private static Assembly CustomResolver(object sender, ResolveEventArgs e) { byte[] assemblyData = GetAssemblyDataFromSomewhere(); return Assembly.Load(assemblyData); } )。发生这种情况是因为该应用程序域现在需要来自调用程序集的代码(MyApp.exe方法)。

我可以将应用程序域创建逻辑和事件处理程序移至其他程序集中,例如CustomResolver,因此新的应用程序域将加载该程序集,而不是MyAppServices.dll

但是,我想避免不惜一切代价避免文件系统访问我的应用程序目录:新的应用程序域不得从文件中加载任何用户程序集。

我也尝试过MyApp.exe,但这也不起作用,因为返回值的类型AppDomain.DefineDynamicAssembly既不是System.Reflection.Emit.AssemblyBuilder也不是用MarshalByRefObject标记的。

是否有任何方法可以将字节数组中的程序集加载到非默认[Serializable]中,而无需将调用的程序集从文件中加载到该应用程序域中?实际上,没有任何文件系统可以访问我的应用程序的目录吗?

c# .net .net-assembly appdomain assembly-loading
1个回答
0
投票

我不太确定您要达到什么目标,但是我会尝试以下方法。

通常,您的方法似乎还可以。您必须确保正确设置了辅助appdomain的探测路径(尤其是appbase路径)。否则,.NET Fusion会在这些位置探测依赖关系,并且您将尝试避免进行不需要的文件系统访问。 (那么,至少要确保将这些路径配置为某些临时文件夹,且未设置任何实际权限)。

提议的解决方案

无论如何,您都可以尝试向您的dynamic添加(这是我应该如何称呼它?)程序集的入口点(例如,某些AppDomain类中的Main方法),并且将程序集加载到辅助AppDomain中后,尝试调用Bootstrap

我将您的AppDomain.ExecuteAssemblyByName方法添加到Bootstrap类中,并且在CustomResolver方法中,我将订阅Main

这样,当调用AssemblyResolve方法时(希望它能按预期工作),对AppDomain的Main的预订不会触发融合。

我没有测试此解决方案,可能会花很长时间,但是尝试得更糟。

© www.soinside.com 2019 - 2024. All rights reserved.