CurrentDomain.AssemblyResolve не запускается, когда сборка используется в качестве подкласса - PullRequest
8 голосов
/ 22 мая 2011

Я пытаюсь использовать событие CurrentDomain.AssemblyResolve для загрузки библиотеки DLL, помеченной как встроенный ресурс. В частности, моя проблема связана с тем, что я пытаюсь использовать сборку в качестве подкласса, например:

#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;
        }
    }
}

Класс испытаний SubClass определяется как:

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

Если в первой строке #define BROKEN оставить комментарий без комментария, событие CurrentDomain.AssemblyResolve никогда не сработает, и возникнет исключение System.IO.FileNotFoundException, и мне сообщают, что SubClass не может быть найдено. Если первая строка будет удалена или закомментирована, то это вызовет событие (но я не могу использовать его таким образом).

Я также пытался переместить Main в его собственный класс вместо того, чтобы тот же класс создал свой экземпляр, но я получаю точно такое же исключение.

Итак, как правильно настроить это событие, чтобы оно загружало эту сборку в этих условиях?


Скомпилировано в VS 2010 .NET 4, если это кому-то важно. Также для тех, кто пытается воссоздать это. Подкласс находится в собственном проекте. Добавьте SubClass в качестве ссылки на TestConsole и пометьте его как Copy Local = False. Я где-то читал, что это может быть не ссылка на проект, а прямая ссылка на DLL. Затем добавьте файл DLL в проект TestConsole и отметьте его как встроенный ресурс, а не по умолчанию для содержимого.

1 Ответ

20 голосов
/ 22 мая 2011

Подумайте о порядке загрузки ... Чтобы выполнить JIT и вызвать Main, он должен понимать Программу. Он не может понять Программу без загрузки базового класса, который требует специальной обработки. Событие не вызывается , потому что оно еще не зарегистрировано (потому что Main еще не запущено).

Это не может работать. Единственный способ сделать это - иметь точку входа, которая не зависит ни от чего другого. Обратите внимание, что JIT выполняется до запуска метода, поэтому Main также не может включать ничего неизвестного. Например, вы можете сделать:

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();
    }
}

Обратите внимание, что нам нужно 2 метода выше, поскольку он не может JIT Main, если он не может полностью разрешить программу. С учетом вышесказанного событие должно сработать непосредственно перед вызовом MainCore () (т. Е. Во время JIT для MainCore).

...