尝试不需要两个单独的x86和x64程序解决方案

我有一个程序需要在x86和x64环境中运行。 它使用的是Oracle的ODBC驱动程序。 我有一个Oracle.DataAccess.DLL的引用。 但是,根据系统是x64还是x86,此DLL会有所不同。

目前,我有两个独立的解决方案,我维护两者的代码。 这太残忍了。 我想知道什么是正确的解决方案?

我的平台设置为“任何CPU”。 并且我的理解是VS应该将DLL编译为中间语言,这样如果我使用x86或x64版本则无关紧要。 然而,如果我尝试使用x64 DLL,我收到错误“无法加载文件或程序集’Oracle.DataAccess,Version = 2.102.3.2,Culture = neutral,PublicKeyToken = 89b483f429c47342’或其依赖项之一。加载格式不正确的程序。“

我在32位机器上运行,所以错误信息是有道理的,但它让我想知道当我需要在x64上工作时我应该如何有效地开发这个程序。

谢谢。

这纯粹是一个部署问题,您永远不必维护不同的项目。 虽然这是一个尴尬的问题,并且因为没有自己处理这个问题而在Oracle上嘘声。 另一个考虑因素是这个组件确实应该在目标机器上使用。 一些选择

  • 创建两个安装程序,一个用于x64,另一个用于x86。 客户根据她使用的操作系统选择正确的一个。 很简单,你只需复制正确的文件。
  • 将两个程序集部署到GAC。 现在它是自动的,.NET在任何一种类型的机器上选择正确的。 大公司应该几乎总是使用GAC,以便他们可以部署安全更新,而不确定Oracle为什么不这样做。
  • 将程序集部署到安装目录的x86和x64子目录中。 您需要编写一个AppDomain.AssemblyResolve事件处理程序,该处理程序根据IntPtr.Size的值选择正确的目录。
  • 将EXE项目中的目标平台更改为x86。 鉴于您的代码需要在32位计算机上以及在64位计算机上运行,​​因此没有/不应该是为AnyCPU构建的原因。

这是解决您问题的有效方法:

将2 DLL(x86和x64)添加到子文件夹中的解决方案中。 让他们“复制如果更新”

从您添加的2 DLL中引用用于开发调试的正确DLL。 使其复制Local = false。

这样做的是,当你的应用程序启动时,DLL不会自动加载。 在您使用该程序集中的Type之前,不会加载它。 一旦发生这种情况,将在.Net中触发事件,询问它可以在何处找到您的程序集。

所以在第一次使用该程序集之前的某个时候,请确保将自己附加到该事件。

AppDomain.CurrentDomain.AssemblyResolve += CurrentDomain_AssemblyResolve; 

在处理程序的内容中,确保在请求DLL时加载DLL(x86或x64)。

  static System.Reflection.Assembly CurrentDomain_AssemblyResolve(object sender, ResolveEventArgs args) { if (args.Name.Equals("MyFullAssemblyName")) { var path = System.IO.Path.GetDirectoryName(System.Reflection.Assembly.GetExecutingAssembly().Location); if (IntPtr.Size > 4) { var dll = System.IO.Path.Combine(path, @"MySubDir\MyDLL_x64.dll"); return System.Reflection.Assembly.LoadFile(dll); } else { var dll = System.IO.Path.Combine(path, @"MySubDir\MyDLL.dll"); return System.Reflection.Assembly.LoadFile(dll); } } return null; } 

瞧。 您现在可以将应用程序作为32位和64位运行。

或者在子文件夹中添加DLL,您可以将它们作为嵌入式资源,然后像这样加载它们:

  static System.Reflection.Assembly CurrentDomain_AssemblyResolve(object sender, ResolveEventArgs args) { if (args.Name.Equals("MyFullAssemblyName")) { var ass = Assembly.GetExecutingAssembly(); if (IntPtr.Size > 4) { var strm = ass.GetManifestResourceStream("the.resource.name.for.MyDLL_x64.dll"); var data = new byte[strm.Length]; strm.Read(data, 0, data.Length); return Assembly.Load(data); } else { var strm = ass.GetManifestResourceStream("the.resource.name.for.MyDLL.dll"); var data = new byte[strm.Length]; strm.Read(data, 0, data.Length); return Assembly.Load(data); } } return null; } 

这不适用于所有程序集。 一些“混合”程序集往往会失败,除非它们是从磁盘加载的(可以通过在加载之前将它们写入磁盘来解决)。

如果您在32位计算机上运行,​​则必须加载32位版本的Oracle DLL。 32位程序无法引用64位DLL。 并且,64位程序不能引用32位DLL。

如果您有多个版本的外部DLL,“任何CPU”都是正确的目标。 诀窍是确保找到并加载正确的Oracle DLL。 最好的办法是在32位系统上找到64位版本的DLL并重命名,以便运行时找不到它。

使用具有本机早期绑定的AnyCPU是行不通的,因为您需要两个独立的解决方案和构建,如您所见。 你必须得到一个64位系统来开发或至少测试x64编译的dll。

但是,使用后期绑定,您可以使用AnyCPU和System属性来确定您正在运行的体系结构并链接到正确的dll,如果保留命名为Oracle.DataAccess.x86.dll。 如果它们被安装到GAC中,它甚至更容易,你可以绑定甚至没有费心去测试架构,但我相信你还是要迟到绑定。

请注意,如果您真的无法重新安装Windows,VMware可以在32位主机上运行64位guest虚拟机。

您可以配置相同的解决方案来单独构建x86 / x64版本。 您可能还需要添加后期构建步骤以将正确版本的DLL复制到相应的输出文件夹…

至少如果你必须构建2个解决方案 – 使用相同的源(添加文件作为参考第二个解决方案,而不是复制到第二个解决方案)。