本文介绍了使用并行程序集加载 x64 或 x32 版本的 DLL的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

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

We have two versions of a managed C++ assembly, one for x86 and one for x64. This assembly is called by a .net application complied for AnyCPU. We are deploying our code via a file copy install, and would like to continue to do so.

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

Is it possible to use a Side-by-Side assembly manifest to loading a x86 or x64 assembly respectively when an application is dynamically selecting it's processor architecture? Or is there another way to get this done in a file copy deployment (e.g. not using the GAC)?

推荐答案

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

I created a simple solution that is able to load platform-specific assembly from an executable compiled as AnyCPU. The technique used can be summarized as follows:

  1. 确保默认的 .NET 程序集加载机制(Fusion"引擎)找不到特定于平台的程序集的 x86 或 x64 版本
  2. 在主应用程序尝试加载特定于平台的程序集之前,在当前 AppDomain 中安装自定义程序集解析器
  3. 现在当主应用程序需要特定于平台的程序集时,Fusion 引擎将放弃(因为第 1 步)并调用我们的自定义解析器(因为第 2 步);在自定义解析器中,我们确定当前平台并使用基于目录的查找来加载适当的 DLL.

为了演示这种技术,我附上了一个简短的、基于命令行的教程.我在 Windows XP x86 和 Vista SP1 x64 上测试了生成的二进制文件(通过复制二进制文件,就像您的部署一样).

To demonstrate this technique, I am attaching a short, command-line based tutorial. I tested the resulting binaries on Windows XP x86 and then Vista SP1 x64 (by copying the binaries over, just like your deployment).

注意 1:csc.exe"是 C-sharp 编译器.本教程假定它在您的路径中(我的测试使用的是C:WINDOWSMicrosoft.NETFrameworkv3.5csc.exe")

Note 1: "csc.exe" is a C-sharp compiler. This tutorial assumes it is in your path (my tests were using "C:WINDOWSMicrosoft.NETFrameworkv3.5csc.exe")

注意 2:我建议您为测试创建一个临时文件夹并运行命令行(或 powershell),其当前工作目录设置为此位置,例如

Note 2: I recommend you create a temporary folder for the tests and run command line (or powershell) whose current working directory is set to this location, e.g.

(cmd.exe)
C:
mkdir TEMPCrossPlatformTest
cd TEMPCrossPlatformTest

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

Step 1: The platform-specific assembly is represented by a simple C# class library:

// 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 步:我们使用简单的命令行命令编译特定于平台的程序集:

Step 2: We compile platform-specific assemblies using simple command-line commands:

(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

第三步:主程序分为两部分.Bootstrapper"包含可执行文件的主要入口点,并在当前应用程序域中注册自定义程序集解析器:

Step 3: Main program is split into two parts. "Bootstrapper" contains main entry point for the executable and it registers a custom assembly resolver in current 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 的末尾调用的):

"Program" is the "real" implementation of the application (note that App.Run was invoked at the end of 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 步:在命令行上编译主应用程序:

Step 4: Compile the main application on command line:

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

第 5 步:我们现在完成了.我们创建的目录结构应该是这样的:

Step 5: We're now finished. The structure of the directory we created should be as follows:

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

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

If you now run program.exe on a 32bit platform, platformx86library.dll will be loaded; if you run program.exe on a 64bit platform, platformamd64library.dll will be loaded. Note that I added Console.ReadLine() at the end of the Worker.Run method so that you can use task manager/process explorer to investigate loaded DLLs, or you can use Visual Studio/Windows Debugger to attach to the process to see the call stack etc.

当 program.exe 运行时,我们的自定义程序集解析器附加到当前应用程序域.一旦 .NET 开始加载 Program 类,它就会看到对库"程序集的依赖,因此它会尝试加载它.但是,没有找到这样的程序集(因为我们将它隐藏在 platform/* 子目录中).幸运的是,我们的自定义解析器知道我们的诡计,并根据当前平台尝试从适当的 platform/* 子目录加载程序集.

When program.exe is run, our custom assembly resolver is attached to current appdomain. As soon as .NET starts loading the Program class, it sees a dependency on 'library' assembly, so it tries loading it. However, no such assembly is found (because we've hidden it in platform/* subdirectories). Luckily, our custom resolver knows our trickery and based on the current platform it tries loading the assembly from appropriate platform/* subdirectory.

这篇关于使用并行程序集加载 x64 或 x32 版本的 DLL的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持!

08-31 08:45