我们有两个版本的管理C++会,一个针对x86和一64.这会是由一个.网应用程序的遵守对于AnyCPU.我们部署我们的代码,通过一个文件的副本的安装,并将继续这样做。

是否有可能使用边组件清单载x86或64大会分别当应用程序动态地选择它处理建筑?或是有另一种方式得到这个工作文件中复制的部署(例如使用的不同)?

有帮助吗?

解决方案

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

  1. 确保默认的.NET程序集加载机制(“Fusion”引擎)找不到特定于平台的程序集的x86或x64版本
  2. 在主应用程序尝试加载特定于平台的程序集之前,在当前AppDomain中安装自定义程序集解析程序
  3. 现在当主应用程序需要特定于平台的程序集时,Fusion引擎将放弃(因为步骤1)并调用我们的自定义解析器(因为步骤2);在自定义解析器中,我们确定当前平台并使用基于目录的查找来加载适当的DLL。
  4. 为了演示这种技术,我附上了一个简短的基于命令行的教程。我在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 \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;
            }
        }
    }
    

    "程序"是“真实的”应用程序的实现(请注意,在Bootstrapper.Main结束时调用了App.Run):

    // 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,将加载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 / *子目录中)。幸运的是,我们的自定义解析器知道我们的诡计,并且基于当前平台,它尝试从适当的平台/ *子目录加载程序集。

其他提示

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

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

  • Environment.Is64BitProcess 是用来代替 PROCESSOR_ARCHITECTURE, 正如我们不应该依赖于什么样的系统,而这个过程是如何开始-这可能已经x86进程64的操作系统。之前。净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 /库/ ms686203%28VS.85%29.aspx

您可以使用 corflags 实用程序强制AnyCPU exe作为x86或x64可执行文件加载,但这并不完全符合文件复制部署要求,除非您根据目标选择要复制的exe。

此解决方案也适用于非托管程序集。我创建了一个类似米兰Gardian的典范的简单例子。我创建的示例动态地将Managed C ++ dll加载到为Any CPU平台编译的C#dll中。该解决方案利用InjectModuleInitializer nuget包在加载程序集的依赖项之前订阅AssemblyResolve事件。

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

许可以下: CC-BY-SA归因
不隶属于 StackOverflow
scroll top