采用侧并排组件加载DLL的64位或X32版本 [英] Using Side-by-Side assemblies to load the x64 or x32 version of a DLL

查看:215
本文介绍了采用侧并排组件加载DLL的64位或X32版本的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

我们有两个版本,一个托管C ++组件,一个是86,一个是64位。此组件调用.NET应用程序遵守了值为anycpu。我们通过一个文件拷贝部署我们的code安装,并希望继续这样做。

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

解决方案

我创建了一个简单的解决方案,它能够从编译成值为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#类库psented $ P $:

  //文件'library.cs在C:\ TEMP \ CrossPlatformTest
命名空间Cross.Platform.Library
{
公共静态类工人
{
公共静态无效的run()
{
的System.Console.WriteLine(工人正在运行);
的System.Console.WriteLine((Enter继续));
System.Console.ReadLine();
}
}
}
 

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

 (CMD.EXE从注2)
MKDIR平台\ X86
中信建投/out:platform\x86\library.dll /目标:库/平台:86 library.cs
MKDIR平台\ AMD64
中信建投/out:platform\amd64\library.dll /目标:库/平台:64 library.cs
 

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

  //文件'bootstrapper.cs在C:\ TEMP \ CrossPlatformTest
命名空间Cross.Platform.Program
{
公共静态类引导程序
{
公共静态无效的主要()
{
System.AppDomain.CurrentDomain.AssemblyResolve + = CustomResolve;
App.Run();
}

私有静态System.Reflection.Assembly CustomResolve(
对象发件人,
System.ResolveEventArgs参数)
{
如果(args.Name.StartsWith(库))
{
字符串文件名= System.IO.Path.GetFullPath(
					平台\\
+ System.Environment.GetEnvironmentVariable(PROCESSOR_ARCHITECTURE)
+\\ library.dll);
的System.Console.WriteLine(文件名);
如果(System.IO.File.Exists(文件名))
{
返回System.Reflection.Assembly.LoadFile(文件名);
}
}
返回null;
}
}
}
 

程序是真正的执行的应用程序(注意App.Run在Bootstrapper.Main的端调用的):

  //文件Program.cs的'在C:\ TEMP \ CrossPlatformTest
命名空间Cross.Platform.Program
{
公共静态类应用
{
公共静态无效的run()
{
Cross.Platform.Library.Worker.Run();
}
}
}
 

第四步:编译命令行上的主要应用:

 (CMD.EXE从注2)
中信建投/reference:platform\x86\library.dll /out:program.exe Program.cs中bootstrapper.cs
 

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

 (C:\ TEMP \ CrossPlatformTest,根目录)
    平台(DIR)
        AMD64(DIR)
            library.dll
        86(DIR)
            library.dll
    PROGRAM.EXE
    *的.cs(源文件)
 

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

当PROGRAM.EXE运行时,我们的自定义程序集解析器附加到当前应用程序域。当.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.

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

解决方案

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. Make sure default .NET assembly loading mechanism ("Fusion" engine) can't find either x86 or x64 version of the platform-specific assembly
  2. Before the main application attempts loading the platform-specific assembly, install a custom assembly resolver in the current AppDomain
  3. Now when the main application needs the platform-specific assembly, Fusion engine will give up (because of step 1) and call our custom resolver (because of step 2); in the custom resolver we determine current platform and use directory-based lookup to load appropriate DLL.

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

Note 1: "csc.exe" is a C-sharp compiler. This tutorial assumes it is in your path (my tests were using "C:\WINDOWS\Microsoft.NET\Framework\v3.5\csc.exe")

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 \TEMP\CrossPlatformTest
cd \TEMP\CrossPlatformTest

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

// 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();
		}
	}
}

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

(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

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:\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;
		}
	}
}

"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:\TEMP\CrossPlatformTest
namespace Cross.Platform.Program
{
	public static class App
	{
		public static void Run()
		{
			Cross.Platform.Library.Worker.Run();
		}
	}
}

Step 4: Compile the main application on command line:

(cmd.exe from Note 2)
csc /reference:platform\x86\library.dll /out:program.exe program.cs bootstrapper.cs

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

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

If you now run program.exe on a 32bit platform, platform\x86\library.dll will be loaded; if you run program.exe on a 64bit platform, platform\amd64\library.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.

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.

这篇关于采用侧并排组件加载DLL的64位或X32版本的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆