C#

我们有两个版本的托管C ++程序集,一个用于x86,一个用于x64。 该程序集由AnyCPU编译的.net应用程序调用。 我们正在通过文件复制安装部署我们的代码,并希望继续这样做。

当应用程序动态选择它的处理器体系结构时,是否可以使用并行程序集清单分别加载x86或x64程序集? 或者还有另一种方法可以在文件复制部署中完成此操作(例如,不使用GAC)?


我创建了一个简单的解决方案,它能够从编译为AnyCPU的可执行文件加载平台特定的程序集。 所使用的技术可以总结如下:

  • 确保默认的.NET程序集加载机制(“Fusion”引擎)找不到x86或x64版本的特定于平台的程序集
  • 在主应用程序尝试加载平台特定的程序集之前,在当前的AppDomain中安装一个自定义程序集解析器
  • 现在,当主应用程序需要平台特定的程序集时,Fusion引擎将放弃(因为步骤1)并调用我们的自定义解析器(因为步骤2); 在自定义解析器中,我们确定当前平台并使用基于目录的查找来加载适当的DLL。
  • 为了演示这种技术,我附上了一个简短的基于命令行的教程。 我测试了在Windows XP x86和Vista SP1 x64上产生的二进制文件(通过复制二进制文件,就像部署一样)。

    注1 :“csc.exe”是一个C-sharp编译器。 本教程假设它在您的路径中(我的测试使用“C: WINDOWS Microsoft.NET Framework v3.5 csc.exe”)

    注2 :我建议你为当前工作目录设置到这个位置的测试和运行命令行(或PowerShell)创建一个临时文件夹,例如

    (cmd.exe)
    C:
    mkdir TEMPCrossPlatformTest
    cd TEMPCrossPlatformTest
    

    第1步 :平台特定的程序集由一个简单的C#类库表示:

    // file 'library.cs' in C:TEMPCrossPlatformTest
    namespace Cross.Platform.Library
    {
        public static class Worker
        {
            public static void Run()
            {
                System.Console.WriteLine("Worker is running");
                System.Console.WriteLine("(Enter to continue)");
                System.Console.ReadLine();
            }
        }
    }
    

    第2步 :我们使用简单的命令行命令编译特定于平台的程序集:

    (cmd.exe from Note 2)
    mkdir platformx86
    csc /out:platformx86library.dll /target:library /platform:x86 library.cs
    mkdir platformamd64
    csc /out:platformamd64library.dll /target:library /platform:x64 library.cs
    

    第3步 :主程序分为两部分。 “Bootstrapper”包含可执行文件的主要入口点,并在当前的appdomain中注册一个自定义程序集解析器:

    // file 'bootstrapper.cs' in C:TEMPCrossPlatformTest
    namespace Cross.Platform.Program
    {
        public static class Bootstrapper
        {
            public static void Main()
            {
                System.AppDomain.CurrentDomain.AssemblyResolve += CustomResolve;
                App.Run();
            }
    
            private static System.Reflection.Assembly CustomResolve(
                object sender,
                System.ResolveEventArgs args)
            {
                if (args.Name.StartsWith("library"))
                {
                    string fileName = System.IO.Path.GetFullPath(
                        "platform"
                        + System.Environment.GetEnvironmentVariable("PROCESSOR_ARCHITECTURE")
                        + "library.dll");
                    System.Console.WriteLine(fileName);
                    if (System.IO.File.Exists(fileName))
                    {
                        return System.Reflection.Assembly.LoadFile(fileName);
                    }
                }
                return null;
            }
        }
    }
    

    “Program”是应用程序的“真实”实现(请注意,App.Run在Bootstrapper.Main的末尾被调用):

    // file 'program.cs' in C:TEMPCrossPlatformTest
    namespace Cross.Platform.Program
    {
        public static class App
        {
            public static void Run()
            {
                Cross.Platform.Library.Worker.Run();
            }
        }
    }
    

    第4步 :在命令行上编译主应用程序:

    (cmd.exe from Note 2)
    csc /reference:platformx86library.dll /out:program.exe program.cs bootstrapper.cs
    

    第5步 :我们现在完成了。 我们创建的目录结构应如下所示:

    (C:TEMPCrossPlatformTest, root dir)
        platform (dir)
            amd64 (dir)
                library.dll
            x86 (dir)
                library.dll
        program.exe
        *.cs (source files)
    

    如果您现在在32位平台上运行program.exe,platform x86 library.dll将被加载; 如果您在64位平台上运行program.exe,则将加载platform amd64 library.dll。 请注意,我在Worker.Run方法的末尾添加了Console.ReadLine(),以便您可以使用任务管理器/进程资源管理器来调查加载的DLL,或者可以使用Visual Studio / Windows调试器附加到进程以查看调用堆栈等

    当program.exe运行时,我们的自定义程序集解析器被附加到当前的appdomain。 一旦.NET开始加载Program类,它就会看到对'library'程序集的依赖,所以它会尝试加载它。 但是,没有找到这样的程序集(因为我们已经将它隐藏在platform / *子目录中)。 幸运的是,我们的自定义解析器知道我们的诡计,并基于当前平台尝试从适当的平台/ *子目录加载程序集。


    我的版本与@Milan相似,但有几个重要的改变:

  • 适用于所有未找到的DLL
  • 可以打开和关闭
  • AppDomain.CurrentDomain.SetupInformation.ApplicationBase代替Path.GetFullPath()因为当前目录可能不同,例如,在托管方案中,Excel可能会加载您的插件,但当前目录不会设置为您的DLL。

  • Environment.Is64BitProcess而不是PROCESSOR_ARCHITECTURE ,因为我们不应该依赖于操作系统,而是如何启动此进程 - 它可能是x64操作系统上的x86进程。 在.NET 4之前,请改用IntPtr.Size == 8

  • 在其他所有主要类的静态构造函数中调用此代码。

    public static class MultiplatformDllLoader
    {
        private static bool _isEnabled;
    
        public static bool Enable
        {
            get { return _isEnabled; }
            set
            {
                lock (typeof (MultiplatformDllLoader))
                {
                    if (_isEnabled != value)
                    {
                        if (value)
                            AppDomain.CurrentDomain.AssemblyResolve += Resolver;
                        else
                            AppDomain.CurrentDomain.AssemblyResolve -= Resolver;
                        _isEnabled = value;
                    }
                }
            }
        }
    
        /// Will attempt to load missing assembly from either x86 or x64 subdir
        private static Assembly Resolver(object sender, ResolveEventArgs args)
        {
            string assemblyName = args.Name.Split(new[] {','}, 2)[0] + ".dll";
            string archSpecificPath = Path.Combine(AppDomain.CurrentDomain.SetupInformation.ApplicationBase,
                                                   Environment.Is64BitProcess ? "x64" : "x86",
                                                   assemblyName);
    
            return File.Exists(archSpecificPath)
                       ? Assembly.LoadFile(archSpecificPath)
                       : null;
        }
    }
    

    看看SetDllDirectory。 我用它来动态加载用于x64和x86的IBM spss程序集。 它也解决了在我的情况下,非程序集支持dll由程序集加载的路径与spss dll的情况。

    http://msdn.microsoft.com/en-us/library/ms686203%28VS.85%29.aspx

    链接地址: http://www.djcxy.com/p/60305.html

    上一篇: c#

    下一篇: Encountering error during context initialization in spring