Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

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

I have a program which needs to function in both an x86 and an x64 environment. It is using Oracle's ODBC drivers. I have a reference to Oracle.DataAccess.DLL. This DLL is different depending on whether the system is x64 or x86, though.

Currently, I have two separate solutions and I am maintaining the code on both. This is atrocious. I was wondering what the proper solution is?

I have my platform set to "Any CPU." and it is my understanding that VS should compile the DLL to an intermediary language such that it should not matter if I use the x86 or x64 version. Yet, if I attempt to use the x64 DLL I receive the error "Could not load file or assembly 'Oracle.DataAccess, Version=2.102.3.2, Culture=neutral, PublicKeyToken=89b483f429c47342' or one of its dependencies. An attempt was made to load a program with an incorrect format."

I am running on a 32 bit machine, so the error message makes sense, but it leaves me wondering how I am supposed to efficiently develop this program when it needs to work on x64.

Thanks.

like image 280
Sean Anderson Avatar asked Jan 05 '11 00:01

Sean Anderson


People also ask

How do I choose between x86 and x64?

In the right pane, look at the System Type entry. For a 32-bit version operating system, it will say X86-based PC. For a 64-bit version, you'll see X64-based PC.

Should I use x64 or x86 installer?

x64 processors work more efficiently than an x86 processor when dealing a large amount of data If you are using a 64-bit Windows PC, you can find a folder named Program Files (x86) on the C drive.

Can x64 programs run on x86?

Yes it is possible & it will run properly. Instruction Set Architecture is always backwards compatible.

Why is x86 32 bit and x64 64 bit?

Probably because the x86 line became synonymous with 32 bit processors for quite some time, while x64 was specifically a designation for 64 bit as applications and operating systems were transitioned over, and now there are software applications that require the 64 bit designation in order to run (like some VM software ...


3 Answers

This is purely a deployment problem, you should never have to maintain different projects. It is an awkward one though, and boo on Oracle for not taking care of this themselves. Another consideration is that this assembly really should be ngen-ed on the target machine. Some options

  • Create two installers, one for x64 and one for x86. The customer picks the right one, based on the operating system she uses. Simple enough, you just copy the right file.
  • Deploy both assemblies to the GAC. Now it is automatic, .NET picks the right one on either type of machine. Big companies should almost always use the GAC so they can deploy security updates, not sure why Oracle doesn't do this.
  • Deploy the assemblies to a x86 and x64 subdirectory of the install directory. You'll need to write an AppDomain.AssemblyResolve event handler that, based on the value of IntPtr.Size, picks the right directory.
  • Change the target platform on your EXE project to x86. Given that your code needs to work on a 32-bit machine as well as on a 64-bit machine, there isn't/shouldn't be a reason to build for AnyCPU.
like image 158
Hans Passant Avatar answered Nov 14 '22 20:11

Hans Passant


This is a working solution for your problem:

Add the 2 DLL's (x86 and x64) to your solution in a subfolder. Make them "Copy if newer"

Reference the correct DLL you use for development for debugging from the 2 DLL's you added. Make it Copy Local=false.

What this does is that when you app starts the DLL is not autoloaded. It will not be loaded until you use a Type from that assembly. Once that happens an event will be triggered in .Net that asks where it can find your assembly.

So sometime before the first use of that assembly make sure you attach yourself to that event.

AppDomain.CurrentDomain.AssemblyResolve += CurrentDomain_AssemblyResolve;

In the content of the handler make sure you load the DLL (x86 or x64) when it asks for 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. You can now run your app as both 32 bit and 64 bit.

Alternatively to adding the DLLs in a subfolder, you can make them as Embedded Resources, and then load them like this:

    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 assemblies. Some "hybrid" assemblies tends to fail unless they are loaded from disk (can be solved by writing them to disk just before loading).

like image 22
Wolf5 Avatar answered Nov 14 '22 20:11

Wolf5


If you're running on a 32-bit machine, then you have to load the 32-bit version of the Oracle DLL. A 32-bit program can't reference a 64-bit DLL. And, a 64-bit program can't reference a 32-bit DLL.

"Any CPU" is the correct target if you have multiple versions of the external DLL. The trick is making sure that the proper Oracle DLL is located and loaded. Your best bet is to locate the 64-bit version of the DLL on your 32-bit system and rename it so that the runtime can't find it.

like image 22
Jim Mischel Avatar answered Nov 14 '22 21:11

Jim Mischel