问题
I use the following method to load a new Assembly and create an instance of a class into a new AppDomain.
private static object CreateInstanceFromBinary(AppDomain appDomain, string typeName)
{
Assembly entryAssembly = Assembly.GetEntryAssembly();
byte[] assemblyBinary = LoadAssemblyBinary();
Assembly loadedAssembly = appDomain.Load(assemblyBinary);
if (loadedAssembly != null)
{
return loadedAssembly.CreateInstance(typeName);
}
return null;
}
Which get's called like so.
AppDomain appDomain = AppDomain.CreateDomain(domainName);
appDomainHelper = CreateInstanceFromBinary(appDomain, typeof(MyClass).FullName) as MyClass;
Looking into the loadedAssembly
I can see that MyClass
exists inside of the DefinedTypes
and it's name matches typeName
. However, when the code runs
loadedAssembly.CreateInstance(typeName)
it returns null.
This code was working, however, I recently moved this class into the same dll as the one that calls it and now it has started returning null.
Any ideas on how to fix this?
For some short(ish) reproducible code you can use the following. In this code ClassLibrary1
has CopyLocal set to false and then included as an EmbeddedResource to mimic what I have in my live project in case that matters.
Inside of ConsoleApplication1
I have Program.
using ClassLibrary1;
using System;
using System.Collections.Generic;
using System.IO;
using System.Linq;
namespace ConsoleApplication1
{
class Program
{
static Program()
{
AppDomain.CurrentDomain.AssemblyResolve += Resolve;
}
static void Main(string[] args)
{
WorkerClass workerClass = new WorkerClass();
workerClass.DoWork();
Console.WriteLine("\r\nPress enter to exit...");
Console.ReadLine();
}
static System.Reflection.Assembly Resolve(object sender, ResolveEventArgs args)
{
if (!args.Name.Contains(","))
{
return null;
}
List<string> rn = System.Reflection.Assembly.GetEntryAssembly().GetManifestResourceNames()
.Where(r => r.EndsWith(".dll", StringComparison.OrdinalIgnoreCase))
.ToList();
string assemblyName = rn.FirstOrDefault(r => r.EndsWith(args.Name.Substring(0, args.Name.IndexOf(",")) + ".dll"));
if (!String.IsNullOrEmpty(assemblyName))
{
using (Stream stream = System.Reflection.Assembly.GetEntryAssembly().GetManifestResourceStream(assemblyName))
{
byte[] assemblyBinary = new byte[stream.Length];
stream.Read(assemblyBinary, 0, assemblyBinary.Length);
System.Reflection.Assembly assembly = System.Reflection.Assembly.Load(assemblyBinary);
if (Environment.UserInteractive)
{
Console.WriteLine("Loaded Assembly: " + assembly.FullName);
}
return assembly;
}
}
if (Environment.UserInteractive)
{
Console.WriteLine($"** Failed to find an assembly with name: {args.Name} ** ");
}
return null;
}
}
}
Inside of ClassLibrary1
there is WorkerClass.
using System;
namespace ClassLibrary1
{
public class WorkerClass
{
public void DoWork()
{
try
{
HelperClass hc = HelperClass.Create("Name");
Console.WriteLine("Created");
}
catch (Exception ex)
{
Console.WriteLine("Failed to create: " + ex.ToString());
}
}
}
}
and HelperClass.
using System;
using System.Collections.Generic;
using System.IO;
using System.Linq;
using System.Reflection;
using System.Security.Permissions;
namespace ClassLibrary1
{
[Serializable]
public class HelperClass : MarshalByRefObject
{
public AppDomain Domain { get; private set; }
public HelperClass()
{
}
[SecurityPermission(SecurityAction.Demand, Flags = SecurityPermissionFlag.Infrastructure)]
public override object InitializeLifetimeService()
{
return null;
}
public static HelperClass Create(string domainName)
{
AppDomain appDomain = AppDomain.CreateDomain(domainName);
HelperClass helperClass = CreateInstanceFromBinary(appDomain, typeof(HelperClass).AssemblyQualifiedName) as HelperClass;
if (helperClass == null)
{
throw new Exception("Unable to create instance from binary resource.");
}
helperClass.Domain = appDomain;
return helperClass;
}
private static object CreateInstanceFromBinary(AppDomain appDomain, string typeName)
{
Assembly entryAssembly = Assembly.GetEntryAssembly();
IList<string> rn = entryAssembly.GetManifestResourceNames().Where(r => r.EndsWith(".dll")).ToList();
string assembly = rn.FirstOrDefault(r => r.EndsWith($"{typeof(HelperClass).Assembly.GetName().Name}.dll"));
if (!String.IsNullOrEmpty(assembly))
{
using (Stream stream = entryAssembly.GetManifestResourceStream(assembly))
{
byte[] assemblyBinary = new byte[stream.Length];
stream.Read(assemblyBinary, 0, assemblyBinary.Length);
Assembly loadedAssembly = appDomain.Load(assemblyBinary);
if (loadedAssembly != null)
{
return loadedAssembly.CreateInstance(typeName);
}
}
}
return null;
}
}
}
Where it is return loadedAssembly.CreateInstance(typeName);
that returns null.
回答1:
In your function public static HelperClass Create(string domainName)
you are passing the AssemblyQualifiedName as the type of the class to create.
I think you just want to pass the type name, ie: ClassLibrary1.HelperClass
//HelperClass helperClass = CreateInstanceFromBinary(appDomain, typeof(HelperClass).AssemblyQualifiedName) as HelperClass;
HelperClass helperClass = CreateInstanceFromBinary(appDomain, typeof(HelperClass).ToString()) as HelperClass;
I tried some variations, each time passing in the Assembly Qualified Name failed, just the type name worked as expected.
Variations tried, and failed:
// Do not work
var x = loadedAssembly.CreateInstance(typeName); //AssemblyQualifiedName
var loadedType = loadedAssembly.GetType(typeName); //AssemblyQualifiedName
// Work
var x = Activator.CreateInstance(typeof(HelperClass)); // Works
var x = loadedAssembly.CreateInstance("ClassLibrary1.HelperClass");
var loadedType = loadedAssembly.GetType("ClassLibrary1.HelperClass");
var x = Activator.CreateInstance(loadedType);
来源:https://stackoverflow.com/questions/49488782/assembly-createinstance-returning-null-even-though-i-can-see-the-class-in-define