Trying not to need two separate solutions for x86 and x64 programs

I have a program that should work both in x86 and in x64 environment. It uses Oracle ODBC drivers. I have a link to Oracle.DataAccess.DLL. This DLL differs depending on whether the system is x64 or x86.

I currently have two separate solutions, and I support code for both. This is terrible. I was wondering what is the right decision?

I have the "Any processor" platform installed. and I understand that VS has to compile the DLL into a proxy language, so it doesn't matter if I use x86 or x64 version. However, if I try to use the x64 DLL, I get the error message "Failed to load the file or assembly Oracle.DataAccess, Version = 2.102.3.2, Culture = neutral, PublicKeyToken = 89b483f429c47342" or one of its dependencies. to download a program with the wrong format. "

I am running on a 32-bit machine, so the error message makes sense, but I'm not interested in how I should effectively develop this program when it needs to work on x64.

Thanks.

+7
source share
5 answers

This is just a deployment problem, you will never have to support different projects. This is inconvenient, but, and be on Oracle, so as not to worry about it yourself. Another consideration is that this assembly really needs to be pointed out on the target machine. Some options

  • Create two installers: one for x64 and one for x86. The client selects the correct one based on the operating system that it uses. Simple enough, you just copy the desired file.
  • Expand both assemblies in the GAC. Now it automatically, .NET selects the correct option on any type of machine. Large companies should almost always use the GAC so that they can deploy security updates without knowing why Oracle is not doing this.
  • Expand assemblies in the x86 and x64 subdirectory of the installation directory. You will need to write an AppDomain.AssemblyResolve event handler that selects the correct directory based on the IntPtr.Size value.
  • Change the target platform in the EXE project to x86. Given that your code should run on a 32-bit machine, as well as on a 64-bit machine, there should not be a reason to build for AnyCPU.
+3
source

This is a working solution for your problem:

Add 2 DLLs (x86 and x64) to your solution in a subfolder. Make them "Copy if new"

Specify the correct DLL that you use for development to debug from the 2 DLLs you added. Copy it Local = false.

What does this mean that when the application starts, the DLL does not load automatically. It will not load until you use the Type from this assembly. Once this happens, the event will be fired in .Net, which will ask where it can find your assembly.

Therefore, before using this assembly for the first time, make sure that you join this event.

AppDomain.CurrentDomain.AssemblyResolve += CurrentDomain_AssemblyResolve; 

In the contents of the handler, make sure that you load the DLL (x86 or x64) when it requests it.

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

Voila. Now you can run the application in both 32-bit and 64-bit.

Alternatively, to add DLLs to a subfolder, you can make them as Embedded Resources, and then load them as follows:

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

This does not work for all builds. Some "hybrid" builds tend to fail if they are not loaded from disk (they can be resolved by writing them to disk before loading).

+3
source

If you are running on a 32-bit machine, you need to download the 32-bit version of the Oracle DLL. A 32-bit program cannot reference a 64-bit DLL. And the 64-bit program cannot reference the 32-bit DLL.

"Any processor" is the right goal if you have several versions of an external DLL. The trick is that the proper Oracle DLL is located and loaded. It is best to find the 64-bit version of the DLL on a 32-bit system and rename it so that the runtime cannot find it.

+2
source

Using AnyCPU with your own early connections simply will not work, for this you need two separate solutions and assemblies, as you saw. You must get a 64-bit system for developing or at least testing compiled x64 libraries.

However, with late bindings, you can use the AnyCPU and System properties to find out which architecture you are using and refer to the correct dll if you save the name as Oracle.DataAccess.x86.dll. If they are installed in the GAC, it’s even easier, you can bind yourself without even bothering to check the architecture, but I believe that you still have to be late.

Please note that VMware can run a 64-bit guest system on a 32-bit host if you really do not need to reinstall Windows.

0
source

You can configure the same solution to build x86 / x64 versions separately. You may also need to add post-build steps to copy the correct version of the DLL to the appropriate output folders ...

At least if you need to build 2 solutions - use the same source (add files as a link to the second solution, and do not copy to the second solution).

0
source

All Articles