Assembly.CreateInstance returning null even though I can see the class in DefinedTypes

有些话、适合烂在心里 提交于 2019-12-12 19:36:41

问题


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

易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!