使用侧面并排组件加载DLL的64或32倍的版本使用侧面并排组件加载DLL的64或32倍的版本(Usi

2019-05-17 07:44发布

我们有一个托管C ++组件的两个版本,一个用于x86,一个用于x64的。 该组件由符合了AnyCPU .NET应用程序调用。 我们通过一个文件副本部署我们的代码安装,并希望继续这样做。

是否有可能使用一个侧面并排集清单分别加载x86或x64组件当应用程序动态地选择它的处理器架构? 还是有另一种方式来得到这个文件复制完成部署(例如不使用GAC)?

Answer 1:

我创建了一个简单的解决方案,其能够从编译为AnyCPU可执行加载平台特定组件。 所使用的技术可以被概括如下:

  1. 确保默认.NET程序集加载机制(“融合”引擎)无法找到特定于平台的组件的x86或x64版本
  2. 主要的应用程序试图加载特定于平台的组装之前,在当前的AppDomain安装自定义程序集解析器
  3. 现在,当主应用程序需要特定于平台的组装,融合引擎将放弃(因为第1步),并调用(因为第2步),我们自定义的解析器; 在定制解析器我们确定当前平台和使用基于目录查找加载相应的DLL。

为了证明这种技术,我附上短,基于命令行的教程。 我测试在Windows XP x86的结果二进制文件,然后Vista SP1的64位(通过在复制二进制文件,就像您的部署)。

注1: “CSC.EXE”是指C-尖锐编译器。 本教程假定它是在你的路径(我的测试中,使用“C:\ WINDOWS \ Microsoft.NET \框架\ v3.5版本\ CSC.EXE”)

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

(cmd.exe)
C:
mkdir \TEMP\CrossPlatformTest
cd \TEMP\CrossPlatformTest

步骤1:特定于平台的组件通过一个简单的C#类库表示:

// file 'library.cs' in C:\TEMP\CrossPlatformTest
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 platform\x86
csc /out:platform\x86\library.dll /target:library /platform:x86 library.cs
mkdir platform\amd64
csc /out:platform\amd64\library.dll /target:library /platform:x64 library.cs

步骤3:主程序被分成两个部分。 “引导程序”包含主入口点的可执行文件和它注册在当前的AppDomain自定义程序集解析器:

// file 'bootstrapper.cs' in C:\TEMP\CrossPlatformTest
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;
        }
    }
}

“方案”是“真正的”执行的应用程序(注意App.Run在Bootstrapper.Main结束调用的):

// file 'program.cs' in C:\TEMP\CrossPlatformTest
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:platform\x86\library.dll /out:program.exe program.cs bootstrapper.cs

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

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

如果你现在在32位平台上运行的Program.exe,平台\ X86 \ library.dll将被载入; 如果你在64位平台上运行的Program.exe,平台\ AMD64 \ library.dll将被载入。 请注意,我在Worker.Run方法的末尾添加到Console.ReadLine(),这样就可以使用任务管理器/进程资源管理调查加载的DLL,或者您可以使用Visual Studio / Windows调试程序连接到进程看调用堆栈等。

当运行的Program.exe,我们的自定义程序集解析器附加到当前应用程序域。 只要.NET开始加载程序类,它看到的“库”装配依赖,所以它会试图加载它。 但是,没有这样的组件被找到(因为我们在平台/ *子目录隐藏它)。 幸运的是,我们的自定义解析器知道我们的诡计,并基于当前平台上它试图从适当的平台/ *子目录加载程序集。



Answer 2:

我的版本,类似于@Milan,但有几个重要的变化:

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

  • Environment.Is64BitProcess代替PROCESSOR_ARCHITECTURE ,因为我们不应该依赖于OS是什么,这个过程,而如何开始的-它可能是在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;
    }
}


Answer 3:

看一看SetDllDirectory会。 我用它围绕两个x64和x86的IBM SPSS组件的动态负载。 这也解决了在我的情况下,组件加载非组装支持DLL的路径是与SPSS DLL的情况。

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



Answer 4:

您可以使用corflags实用程序来强制AnyCPU exe文件加载为x86或x64可执行文件,但这并不能完全满足该文件的副本部署要求,除非你选择复制基于目标其EXE。



Answer 5:

该解决方案可对非托管程序正常工作。 我创建了类似于米兰GARDIAN的很好的例子,一个简单的例子。 我创建的示例动态加载托管C ++ DLL到编译为任何CPU平台上的C#DLL。 该解决方案利用了InjectModuleInitializer NuGet包被装载在组件的依赖关系之前订阅AssemblyResolve事件。

https://github.com/kevin-marshall/Managed.AnyCPU.git



文章来源: Using Side-by-Side assemblies to load the x64 or x32 version of a DLL
标签: c# .net 64bit