CurrentDomain.AssemblyResolve не запускается, когда assembly используется в качестве подкласса

#c# #assemblies #embedded-resource

#c# #сборки #встроенный ресурс

Вопрос:

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

 #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, если для кого-то это имеет значение. Также, для всех, кто пытается воссоздать это. ПодКласс находится в его собственном проекте. Добавьте подкласс в качестве ссылки на TestConsole и пометьте его как Copy Local = False. Я где-то читал, что это может быть не ссылка на проект, а прямая ссылка на DLL. Затем добавьте файл DLL в проект TestConsole и пометьте его как встроенный ресурс, а не как содержимое по умолчанию.

Ответ №1:

Подумайте о порядке загрузки… Для JIT и вызова Main он должен понимать Program. Он не может понять программу без загрузки базового класса, который требует специальной обработки. Событие не запускается, потому что оно еще не зарегистрировано (поскольку 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, если он не может полностью разрешить Program. С учетом вышесказанного, событие должно сработать непосредственно перед вызовом mainCore() (т.е. во время JIT для mainCore).

Комментарии:

1. Вау, я был почти на месте. Я просто не подумал о переносе выполнения Program в другой метод. Теперь, можете ли вы сказать мне, что [MethodImpl(MethodImplOptions.NoInlining)] делает для меня? Я прокомментировал это, просто чтобы посмотреть, что произойдет, и это все еще работало. Спасибо

2. @Jim, который просто пытается убедиться, что он не пытается объединить Main и mainCore во время JIT Main. Точно предсказать, когда он решит встроить метод, сложно.

3. Ах, хорошо. И я полагаю, что частично это могло быть, если бы это была неоптимизированная отладочная сборка по сравнению с оптимизированной выпускной сборкой.