2

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.

TheLethalCoder
  • 6,668
  • 6
  • 34
  • 69
  • 2
    have you tried using the `typeof(MyClass).AssemblyQualifiedName` instead of the `.FullName`? full name is not usually enough. Without the AQN there is *some* attempt at discovery (caller assembly, or mscorlib) - usually just enough of an attempt to cause confusion when it works for some types but not others, but: it works much more reliably with an AQN – Marc Gravell Mar 26 '18 at 10:07
  • @MarcGravell Just tried it and that too returns null. I just found [this answer](https://stackoverflow.com/a/6377763) after a bit more looking around that says it returns null if you call from within the same Assembly as I am here but it has no evidence to back up the claim. Any idea if this is actually the case? – TheLethalCoder Mar 26 '18 at 10:13
  • that sounds like complete garbage to me; I added a comment to that post – Marc Gravell Mar 26 '18 at 10:17
  • @MarcGravell Me too but there is apparently something different when calling the same method from inside the same assembly compared to when it's in a different one. – TheLethalCoder Mar 26 '18 at 10:20
  • Does the class you are creating an instance of have a default constructor or one that requires some parameters? – JayV Mar 26 '18 at 10:20
  • @JayV Default constructor. – TheLethalCoder Mar 26 '18 at 10:21
  • Try to do the steps `Activator` does by yourself: get the type, get the constructor, invoke it. – thehennyy Mar 26 '18 at 11:50

1 Answers1

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);
JayV
  • 3,238
  • 2
  • 9
  • 14