tags:

views:

2009

answers:

5

I have a unmanaged DLL (the scilexer.dll of Scintilla code editor, used by Scintilla.Net from CodePlex) that is loaded from a managed application trough the Scintilla.Net component. The windows managed application runs without problem on both 32 and 64 bit environments, but I need to create different installations that uses the 64 or the 32 scilexer.dll.

Is there a way to distribute both DLLs in 32 and 64 bit format so that the DLL loader of the .Net framework loads the unmanaged DLL in the 32 or 64 bit format depending on some .config option or some "path name magic" stuff?

+1  A: 

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.

Igal Serban
+1  A: 

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");
        }
    }
massimogentilini
A: 

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

Joel Lucsy
+4  A: 

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.

norheim.se
Watch out! I have done exactly the same thing with my application, and I still get problems on 32-bit systems from time to time. Because you can't control how the JIT works, you cannot be sure your application won't try loading the 64-bit DLL on 32-bit systems, and vice-versa.
Martin Plante
I have been using this in production for one and a half year, and it has worked well for me so far.
norheim.se
+3  A: 

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 + ".");
void
Used this approach, worked like a charm. Approved response.
massimogentilini