Using a 32bit or 64bit dll in C# DllImport

2019-01-10 05:01发布

Here is the situation, I'm using a C based dll in my dot.net application. There are 2 dlls, one is 32bit called MyDll32.dll and the other is a 64bit version called MyDll64.dll.

There is a static variable holding the DLL file name: string DLL_FILE_NAME.

and it is used in the following way:

[DllImport(DLL_FILE_NAME, CallingConvention=CallingConvention.Cdecl, EntryPoint=Func1")]
private static extern int is_Func1(int var1, int var2);

Simple so far.

As you can imagine, the software is compiled with "Any CPU" turned on.

I also have the following code to determine if the system should use the 64bit file or the 32bit file.

#if WIN64
        public const string DLL_FILE_NAME = "MyDll64.dll";
#else
        public const string DLL_FILE_NAME = "MyDll32.dll";        
#endif

By now you should see the problem.. DLL_FILE_NAME is defined in compilation time and not in execution time so the right dll isn't loaded according to the execution context.

What would be the correct way to deal with this issue? I do not want two execution files (one for 32bit and the other for 64bit)? How can I set DLL_FILE_NAME before it is used in the DllImport statement?

7条回答
时光不老,我们不散
2楼-- · 2019-01-10 05:36

What you describe is known as "side-by-side assembly" (two versions of the same assembly, one 32 and the other 64 bit)... I think you will find these helpful:

Here you can find a walkthrough for exactly your scenario (.NET DLL wrapping C++/CLI DLL referencing a native DLL).

RECOMMENDATION:

Just build it as x86 and be done with it... or have 2 builds (one x86 and one x64)... as the above techniques are rather complicated...

查看更多
成全新的幸福
3楼-- · 2019-01-10 05:40

In this case, i should do like this (make 2 folders, x64 and x86 + put the corresponding dll, WITH THE SAME NAME, in both folders):

using System;
using System.Runtime.InteropServices;
using System.Reflection;
using System.IO;

class Program {
    static void Main(string[] args) {
        var path = Path.GetDirectoryName(Assembly.GetEntryAssembly().Location);
        path = Path.Combine(path, IntPtr.Size == 8 ? "x64" : "x86");
        bool ok = SetDllDirectory(path);
        if (!ok) throw new System.ComponentModel.Win32Exception();
    }
    [DllImport("kernel32.dll", CharSet = CharSet.Auto, SetLastError = true)]
    private static extern bool SetDllDirectory(string path);
}
查看更多
干净又极端
4楼-- · 2019-01-10 05:43

There is a static variable holding the DLL file name

It is not a static variable. It's a constant, at compile time. You can't change a compile time constant at runtime.

What would be the correct way to deal with this issue?

Honestly I would recommend just targeting x86 and forgetting the 64-bit version all together, and letting your application run on WOW64, unless your application has a compelling need to run as x64.

If there is a need for x64, you could:

  • Change the DLLs to have the same name, such as MyDll.dll, and at install / deploy time, put the right one in place. (If the OS is x64, deploy the 64-bit version of the DLL, otherwise the x86 version).

  • Have two separate builds altogether, one for x86 and one for x64.

查看更多
We Are One
5楼-- · 2019-01-10 05:48

I have used one of the approaches meantioned by vcsjones:

"Change the DLLs to have the same name, such as MyDll.dll, and at install / deploy time, put the right one in place."

This approach requires maintaining two build platforms though see this link for more details: https://stackoverflow.com/a/6446638/38368

查看更多
Animai°情兽
6楼-- · 2019-01-10 05:49

Here is another alternative that requires that the two DLLs have the same name and are placed in different folders. For instance:

  • win32/MyDll.dll
  • win64/MyDll.dll

The trick is to manually load the DLL with LoadLibrary before the CLR does it. It will then see that a MyDll.dll is already loaded and use it.

This can be done easily in the static constructor of the parent class.

static class MyDll
{
    static MyDll()
    {            
        var myPath = new Uri(typeof(MyDll).Assembly.CodeBase).LocalPath;
        var myFolder = Path.GetDirectoryName(myPath);

        var is64 = IntPtr.Size == 8;
        var subfolder = is64 ? "\\win64\\" : "\\win32\\";

        LoadLibrary(myFolder + subfolder + "MyDll.dll");
    }

    [DllImport("kernel32.dll")]
    private static extern IntPtr LoadLibrary(string dllToLoad);

    [DllImport("MyDll.dll")]
    public static extern int MyFunction(int var1, int var2);
}

EDIT 2017/02/01: Use Assembly.CodeBase so that it works even if Shadow Copying is enabled.

查看更多
Juvenile、少年°
7楼-- · 2019-01-10 05:52

an alternative approach may be

public static class Sample
{
    public Sample()
    {

        string StartupDirEndingWithSlash = System.IO.Path.GetDirectoryName(System.Diagnostics.Process.GetCurrentProcess().MainModule.FileName) + "\\";
        string ResolvedDomainTimeFileName = StartupDirEndingWithSlash + "ABCLib_Resolved.dll";
        if (!File.Exists(ResolvedDomainTimeFileName))
        {
            if (Environment.Is64BitProcess)
            {
                if (File.Exists(StartupDirEndingWithSlash + "ABCLib_64.dll"))
                    File.Copy(StartupDirEndingWithSlash + "ABCLib_64.dll", ResolvedDomainTimeFileName);
            }
            else
            {
                if (File.Exists(StartupDirEndingWithSlash + "ABCLib_32.dll"))
                    File.Copy(StartupDirEndingWithSlash + "ABCLib_32.dll", ResolvedDomainTimeFileName);
            }
        }
    }

    [DllImport("ABCLib__Resolved.dll")]
    private static extern bool SomeFunctionName(ref int FT);
}
查看更多
登录 后发表回答