当程序集用作子类时,CurrentDomain.AssemblyResolve 不会触发

发布于 2024-11-08 19:43:15 字数 2654 浏览 0 评论 0原文

我正在尝试使用 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< /code> 未注释,则 CurrentDomain.AssemblyResolve 事件将永远不会触发,并且会引发 System.IO.FileNotFoundException,并且我被告知无法找到 SubClass。如果第一行被删除或注释掉,那么它将触发该事件(但我不能以这种方式使用它)。

我还尝试将 Main 移动到其自己的类中,而不是让同一个类创建其自身的实例,但我得到了完全相同异常。

那么,如何正确连接此事件,以便在这些条件下加载此程序集?


在 VS 2010 .NET 4 中编译,如果这对任何人都有影响的话。另外,对于任何尝试重新创建此内容的人。子类在它自己的项目中。添加子类作为 TestConsole 的引用并将其标记为 Copy Local = False。我在某处读到,这不能是项目引用,而是对 DLL 的直接引用。然后,将 DLL 文件 添加到 TestConsole 项目并将其标记为嵌入式资源,而不是默认的内容。

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.

如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。

扫码二维码加入Web技术交流群

发布评论

需要 登录 才能够评论, 你可以免费 注册 一个本站的账号。

评论(1

苍白女子 2024-11-15 19:43:15

考虑加载顺序...为了 JIT 并调用 Main,它必须了解 Program。如果不加载基类,它就无法理解 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() 之前触发(即在 MainCore 的 JIT 期间)。

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).

~没有更多了~
我们使用 Cookies 和其他技术来定制您的体验包括您的登录状态等。通过阅读我们的 隐私政策 了解更多相关信息。 单击 接受 或继续使用网站,即表示您同意使用 Cookies 和您的相关数据。
原文