Home > OS >  C# Load different assemblies depending on bitness in runtime
C# Load different assemblies depending on bitness in runtime

Time:07-21

So my problem is that my company wants to switch from x86 to x64 though some of our customers still want to use the x86 version, we had the idea to load the x86 assemblies instead of the x64 if the customer starts the program as a x86 process. So we did that:

        var baseDirectory = AppDomain.CurrentDomain.BaseDirectory;

        if (!Environment.Is64BitProcess)
        {

            Assembly.LoadFrom($"{baseDirectory}\\Assemblies32\\Bibliothek.dll");

        }

The problem now is that the CRL still wants to load the x64 .dll which clearly isn't possible. Is there a way to tell the CLR to use a different .DLL instead of the original one?

CodePudding user response:

One method I have seen use is to use NativeLibrary.SetDllImport. Note that this is only available in .Net core 3 .

...
NativeLibrary.SetDllImportResolver(Assembly.GetExecutingAssembly(), DllImportResolver);
...
private static IntPtr DllImportResolver(string libraryName, Assembly assembly, DllImportSearchPath? searchPath)
    {
        return libraryName != "myLibrary"
            ? IntPtr.Zero
            : NativeLibrary.Load(
                        Environment.Is64BitProcess ? "myLibrary-x64" : "myLibrary-x86", 
                        assembly, 
                        DllImportSearchPath.AssemblyDirectory);
    }

If that is not feasible I would probably just create separate x86 and x64 builds, and use build scripts to copy the dll with the correct bittness to the build folder. That way it should be clear to your customer what version they are running.

I would ask your customers why they want to use x86. The only valid reason I can think of would be interoperability with some other x86 software. It might be that they are using some ancient computers with an x86 OS, but in that case you should ask yourself how old system you want to support.

CodePudding user response:

We have a situation where we are using a 3rd-party DLL which is available in 32-bit and 64-bit versions. Our application could run as either 32-bit or 64-bit, so we had to make it load the correct version of the DLL.

The problem we had was that the 3rd-party DLL is a UI control, so you have to access it using the Windows Forms designer - which means you can't easily just use Assembly.LoadFrom() to load it.

To fix this, we did the following:

  1. Work with the 32-bit DLL in the designer. (We had to do this because Visual Studio 2019 is 32-bit, so it couldn't load the 64-bit one.)
  2. Invent new filenames for the 32-bit and the 64-bit versions of the DLL. For example, "SomeDll32.dll" and "SomeDll64.dll". The important thing here is to ensure that these names are DIFFERENT from the DLL that you referenced in the designer.
  3. The installed application includes the renamed DLLs ("SomeDll32.dll" and "SomeDll64.dll" but NOT the one that was referenced in the designer.

If you were to try to run the application now, it would fail because it wouldn't be able to resolve the 3rd-party DLL that's referenced from the forms.

Here comes the actual fix to make it run the correct version:

  1. At the start of Main() attach to AppDomain.CurrentDomain.AssemblyResolve.
  2. In the handler for AssemblyResolve check if the name of the DLL its looking for is the one that you have 32-bit and 64-bit versions of.
  3. If it is, substitute the correct name, depending on whether the app is running 32-bit or 64-bit.

For example here's what our code looks like for resolving a 3rd-party DLL used to display PDF files (using "PDFNet11").

In Main():

AppDomain.CurrentDomain.AssemblyResolve  = pdfFNetResolveEventHandler;

Then in the event handler:

static Assembly? pdfFNetResolveEventHandler(object sender, ResolveEventArgs args)
{
    if (!args.Name.StartsWith("PDFNet,"))
        return null;

    var dllPath = Path.Combine(
        AppDomain.CurrentDomain.BaseDirectory, 
        Environment.Is64BitProcess 
            ? "PDFNet64.dll" 
            : "PDFNet32.dll");

    return Assembly.LoadFrom(dllPath);
}

It should be pretty obvious how this works. You should hopefully be able to do a similar thing.

  • Related