Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

CurrentDomain.AssemblyResolve does not fire when assembly is used as a subclass

I am trying to use the CurrentDomain.AssemblyResolve event to load a DLL that is marked as an Embedded Resource. My problem, specifically, comes from the fact that I am trying to use the assembly as a subclass, like so:

#define BROKEN
using System;
using System.Reflection;
using TestCompanyInc;

namespace TestConsole
{
#if BROKEN
    // This is how I NEED to use it
    class Program : SubClass
#else
    // This is only here as a test to make sure I wired 
    // CurrentDomain.AssemblyResolve correctly
    class Program
#endif
    {
        static int Main(string[] args)
        {
            AppDomain.CurrentDomain.AssemblyResolve += (sender, eventArgs) =>
            {
                string resourceName = Assembly.GetExecutingAssembly()
                       .GetName().Name 
                       + "." + new AssemblyName(eventArgs.Name).Name + ".dll";
                Console.WriteLine("About to lookup {0}", resourceName);

                using (var stream = Assembly.GetExecutingAssembly()
                       .GetManifestResourceStream(resourceName))
                {
                    byte[] assemblyData = new byte[stream.Length];
                    stream.Read(assemblyData, 0, assemblyData.Length);
                    return Assembly.Load(assemblyData);
                }
            };

            Program p = new Program(args);
            return p.Run();
        }

        public Program(string[] args)
        {
        }

        public int Run()
        {
#if BROKEN
            // This is how I NEED to use it
            Console.WriteLine(TestProperty);
#else
            // This is only here as a test to make sure I wired 
            // CurrentDomain.AssemblyResolve correctly
            SubClass sc = new SubClass();
            Console.WriteLine(sc.TestProperty);
#endif
            Console.ReadKey();
            return 0;
        }
    }
}

The test class SubClass is defined as:

namespace TestCompanyInc
{
    public class SubClass
    {
        public SubClass()
        {
            TestProperty = "Init'd";
        }
        public string TestProperty { get; set; }
    }
}

If the first line #define BROKEN is left uncommented, then the CurrentDomain.AssemblyResolve event will never fire and System.IO.FileNotFoundException is thrown and I am told that SubClass can not be found. If the first line is removed or commented out, then it will fire the event (but I can't use it this way).

I have also tried moving Main into its own class instead of having the same class create an instance of itself, but I get the exact same exception.

So, how do I get this event wired up correctly so it will load this assembly under these conditions?


Compiled in VS 2010 .NET 4, if that makes a difference to anyone. Also, for anyone trying to recreate this. SubClass is in its own project. Add SubClass as a Reference to TestConsole and mark it as Copy Local = False. I have read, somewhere, that this can not be a Project Reference, but a direct reference to the DLL. Then, add the DLL file to the TestConsole project and mark it as an Embedded Resource, not the default of Content.

like image 823
Jim Avatar asked May 22 '11 15:05

Jim


1 Answers

Think about load-order... In order to JIT and invoke Main, it must understand Program. It can't understand Program without loading the base-class, which needs special handling. The event doesn't fire because it hasn't registered yet (because Main hasn't started).

That cannot work. The only way you can do this is to have an entry-point that doesn't depend on anything else. Note that JIT is done before the method starts, so the Main also can't involve anything that isn't known. For example, you might do:

class Loader {
    static void Main()
    {
         // not shown: register assemy-load here
         MainCore();
    }
    [MethodImpl(MethodImplOptions.NoInlining)]
    static void MainCore()
    {   // your class as shown is Program
        Program.Init();
    }
}

Note we need 2 methods above since it can't JIT Main unless it can fully resolve Program. With the above, the event should fire just before MainCore() is invoked (I.e. During JIT for MainCore).

like image 176
Marc Gravell Avatar answered Oct 18 '22 02:10

Marc Gravell