根据平台加载x64或x86 DLL?

我有一个构建为“任何CPU”的应用程序,并且有两个针对x86和x64的同一个库的第三方Dll。 我想在运行时包含其中一个库,具体取决于它将在客户端计算机上运行的平台。 最好的方法是什么?

如果我们讨论的是非托管DLL,请像这样声明p / invokes:

[DllImport("DllName.dll")] static extern foo(); 

请注意,我们没有指定DLL的路径,只是它的名称,我认为它对于32位和64位版本都是相同的。

然后,在调用任何p / invokes之前,将库加载到您的进程中。 通过p /调用LoadLibrary API函数来做到这一点。 此时,您将确定您的进程是32位还是64位,并相应地构建DLL的完整路径。 完整路径是您传递给LoadLibrary路径。

现在,当您调用库的p / invokes时,它们将由刚刚加载的模块解析。

对于托管程序集,您可以使用Assembly.LoadFile指定程序集的路径。 这可能是一个有点棘手的编排,但这篇优秀的文章向您展示了如何: 自动选择32或64位混合模式DLL 。 有很多与混合模式和本机DLL依赖关系相关的细节可能与您无关。 关键是AppDomain.CurrentDomain.AssemblyResolve事件处理程序。

我对我的问题的完整解决方案是使用David Heffernan提供的第二个链接。 我做的是1.在项目中引用了一个虚拟dll。 2.指定两个预构建事件

 xcopy /y "$(SolutionDir)\Assemblies\Lib\x86\(Assembly name)*" "$(TargetDir)" xcopy /y "$(SolutionDir)\Assemblies\Lib\x64\(Assemble name)*" "$(TargetDir)" 

3.并且在程序集解析事件中启动应用程序时,根据平台更改了相应的程序集。

  var currentDomain = AppDomain.CurrentDomain; var location = Assembly.GetExecutingAssembly().Location; var assemblyDir = Path.GetDirectoryName(location); if (assemblyDir != null && (File.Exists(Path.Combine(assemblyDir, "(Assembly name).proxy.dll")) || !File.Exists(Path.Combine(assemblyDir, "(Assembly name).x86.dll")) || !File.Exists(Path.Combine(assemblyDir, "(Assembly name).x64.dll")))) { throw new InvalidOperationException("Found (Assembly name).proxy.dll which cannot exist. " + "Must instead have (Assembly name).x86.dll and (Assembly name).x64.dll. Check your build settings."); } currentDomain.AssemblyResolve += (sender, arg) => { if (arg.Name.StartsWith("(Assembly name),", StringComparison.OrdinalIgnoreCase)) { string fileName = Path.Combine(assemblyDir, string.Format("(Assembly).{0}.dll", (IntPtr.Size == 4) ? "x86" : "x64")); return Assembly.LoadFile(fileName); } return null; }; 

我实际上对这个话题很有经验,所以我想我会根据我在Pencil.Gaming中使用的方式发布答案。 首先,你必须“ DllImport ”两个函数,一个来自32位dll,一个来自64位dll(左右,或dylib,无论你的平台使用什么)。

 static class Foo32 { [DllImport("32bitdll.dll")] internal static extern void Foo(); } static class Foo64 { [DllImport("64bitdll.dll")] internal static extern void Foo(); } 

然后你需要一个包含委托的中间类,并根据IntPtr的大小从32或64位互​​操作中导入它们(我不使用Environment.Is64BitProcess ,因为这是一个.NET 4函数):

 internal delegate void FooDelegate(); static class FooDelegates { internal static FooDelegate Foo; static FooDelegates() { Type interop = (IntPtr.Size == 8) ? typeof(Foo64) : typeof(Foo32); FieldInfo[] fields = typeof(FooDelegates).GetFields(BindingFlags.Static | BindingFlags.NonPublic | BindingFlags.Public); foreach (FieldInfo fi in fields) { MethodInfo mi = glfwInterop.GetMethod(fi.Name, BindingFlags.Static | BindingFlags.NonPublic | BindingFlags.Public); Delegate function = Delegate.CreateDelegate(fi.FieldType, mi); fi.SetValue(null, function); } } } 

然后我通常使用一个“真正的”类,包含你导入的函数(虽然这在技术上并不需要):

 public static class FooApi { public static void Foo() { FooDelegates.Foo(); } } 

如果您只需要一个或两个函数,这是一个真正的痛苦,但导入委托的方式对于更大规模的库/应用程序非常有效。 您可能想在github上查看Pencil.Gaming,因为它非常广泛地使用这种方法( 这里是一个很常用的例子)。

这种方法的另一个好处是它是100%跨平台的,并且不依赖于任何WinAPIfunction。