I have an in-memory assembly MyAssembly
(class library) that is used in my main assembly MyApp.exe
:
byte[] assemblyData = GetAssemblyDataFromSomewhere();
(For testing, the GetAssemblyDataFromSomewhere
method can just do File.ReadAllBytes
for an existing assembly file, but in my real app there is no file.)
MyAssembly
has only .NET Framework references and has no dependencies to any other user code.
I can load this assembly into the current (default) AppDomain
:
Assembly.Load(assemblyData);
// this works
var obj = Activator.CreateInstance("MyAssembly", "MyNamespace.MyType").Unwrap();
Now, I want to load this assembly into a different AppDomain
and instantiate the class there. MyNamespace.MyType
is derived from MarshalByRefObject
, so I can share the instance across the app domains.
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");
Yes, I know there is a note in the AppDomain.Load
docs:
This method should be used only to load an assembly into the current application domain.
Yes, it should be used for that, but...
If the current
AppDomain
object represents application domain A, and theLoad
method is called from application domain B, the assembly is loaded into both application domains.
I can live with that. There's no problem for me if the assembly will be loaded into both app domains (because I actually load it into the default app domain anyway).
I can see that assembly loaded into the new app domain. Kind of.
var assemblies = newAppDomain.GetAssemblies().Select(a => a.GetName().Name);
Console.WriteLine(string.Join("\r\n", assemblies));
This gives me:
mscorlib
MyAssembly
But trying to instantiate the class always leads to a FileNotFoundException
, because the CLR tries to load the assembly from file (despite it is already loaded, at least according to AppDomain.GetAssemblies
).
I could do this in MyApp.exe
:
newAppDomain.AssemblyResolve += CustomResolver;
private static Assembly CustomResolver(object sender, ResolveEventArgs e)
{
byte[] assemblyData = GetAssemblyDataFromSomewhere();
return Assembly.Load(assemblyData);
}
This works, but this causes the second app domain to load the calling assembly (MyApp.exe
) from file. It happens because that app domain now needs the code (the CustomResolver
method) form the calling assembly.
I could move the app domain creation logic and the event handler into a different assembly, e.g. MyAppServices.dll
, so the new app domain will load that assembly instead of MyApp.exe
.
However, I want to avoid the file system access to my app's directory at any cost: the new app domain must not load any user assemblies from files.
I also tried AppDomain.DefineDynamicAssembly
, but that did't work either, because the return value's type System.Reflection.Emit.AssemblyBuilder
is neither MarshalByRefObject
nor marked with [Serializable]
.
Is there any way to load an assembly from byte array into a non-default AppDomain
without loading the calling assembly from file into that app domain? Actually, without any file system access to my app's directory?