我有一个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]
中,而无需将调用的程序集从文件中加载到该应用程序域中?实际上,没有任何文件系统可以访问我的应用程序的目录吗?
我不太确定您要达到什么目标,但是我会尝试以下方法。
通常,您的方法似乎还可以。您必须确保正确设置了辅助appdomain的探测路径(尤其是appbase路径)。否则,.NET Fusion会在这些位置探测依赖关系,并且您将尝试避免进行不需要的文件系统访问。 (那么,至少要确保将这些路径配置为某些临时文件夹,且未设置任何实际权限)。
提议的解决方案
无论如何,您都可以尝试向您的dynamic添加(这是我应该如何称呼它?)程序集的入口点(例如,某些AppDomain
类中的Main
方法),并且将程序集加载到辅助AppDomain中后,尝试调用Bootstrap
。
我将您的AppDomain.ExecuteAssemblyByName方法添加到Bootstrap
类中,并且在CustomResolver
方法中,我将订阅Main
。
这样,当调用AssemblyResolve
方法时(希望它能按预期工作),对AppDomain的Main
的预订不会触发融合。
我没有测试此解决方案,可能会花很长时间,但是尝试得更糟。