32 or 64 bit DLL loading from .Net managed code

半腔热情 提交于 2019-11-28 19:56:07

P/Invoke uses LoadLibrary to load DLLs, and if there is already a library loaded with a given name, LoadLibrary will return it. So if you can give both versions of the DLL the same name, but put them in different directories, you can do something like this just once before your first call to a function from scilexer.dll, without needing to duplicate your extern declarations:

    string platform = IntPtr.Size == 4 ? "x86" : "x64";
    string dll = installDir + @"\lib-" + platform + @"\scilexer.dll";
    if (LoadLibrary(dll) == IntPtr.Zero)
        throw new IOException("Unable to load " + dll + ".");

Unfortunately, I don't know anything about this particular DLL. However, when you do the P/Invoke yourself, and you can cope with a little duplication, it's possible to create one proxy for each platform.

For instance, suppose that you have the following interface, that should be implemented by either a 32 or 64 bit DLL:

public interface ICodec {
    int Decode(IntPtr input, IntPtr output, long inputLength);
}

You create the proxies:

public class CodecX86 : ICodec {
    private const string dllFileName = @"Codec.x86.dll";

    [DllImport(dllFileName)]
    static extern int decode(IntPtr input, IntPtr output, long inputLength);

    public int Decode(IntPtr input, IntPtr output, long inputLength) {
        return decode(input, output, inputLength);
    }
}

and

public class CodecX64 : ICodec {
    private const string dllFileName = @"Codec.x64.dll";

    [DllImport(dllFileName)]
    static extern int decode(IntPtr input, IntPtr output, long inputLength);

    public int Decode(IntPtr input, IntPtr output, long inputLength) {
        return decode(input, output, inputLength);
    }
}

And finally make a factory that picks the right one for you:

public class CodecFactory {
    ICodec instance = null;

    public ICodec GetCodec() {
        if (instance == null) {
            if (IntPtr.Size == 4) {
                instance = new CodecX86();
            } else if (IntPtr.Size == 8) {
                instance = new CodecX64();
            } else {
                throw new NotSupportedException("Unknown platform");
            }
        }
        return instance;
    }
}

As the DLLs are loaded lazily the first time they are being invoked, this actually works, despite each platform only being able to load the version that is native to it. See this article for a more detailed explanation.

The best I've come up with is the following:

  • Distribute my application with two DLLs named 64 or 32
  • In the main startup code include the following:
    
    File.Delete(Application.StartupPath + @"\scilexer.dll");
    {
      // Check for 64 bit and copy the proper scilexer dll
        if (IntPtr.Size == 4)
        {
          File.Copy(Application.StartupPath + @"\scilexer32.dll",
            Application.StartupPath + @"\scilexer.dll");
        }
        else
        {
          File.Copy(Application.StartupPath + @"\scilexer64.dll",
            Application.StartupPath + @"\scilexer.dll");
        }
    }

You can put the dll in system32. The 32 bit in syswow64 and the 64 bit in the real system32. For 32 bit application, when thay access system32 they are redirected to Syswow64.

You can create an entry in the registry. The software key has a subkey named Wow6432Node that 32 bit application see as the software key.

Here is what powershell installer does.

Unmanaged dlls can be installed into the GAC side-by-side with their managed counterparts. This article should explain how it works.

标签
易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!