views:

66

answers:

3

I am using several P/Invokes under .NET. However, I want my library to work both in Windows and Linux, preferably with the same binaries.

Since the native library I depend on is available on multiple platforms, I was hoping to just have them along with my managed library's binaries.

Right now I'm using something like this:

[DllImport("/usr/lib/libMYLIBNAME.so.1")]

But this obviously only works for Linux. I was considering that I could possibly copy that binary from /usr/lib and distribute along with my application, so I could reduce the above to:

[DllImport("libMYLIBNAME.so")]

But this still is Linux-only.

Is there anyway to change the library name string so it'd look for libMYLIBNAME.so under Linux and MYLIBNAME.dll on Windows, or something very similar?

I would like to avoid anything that requires recompilation for each supported platform...

(Note: even better would be a solution that'd look for MYLIBNAME.dll on Windows and /usr/lib/libMYLIBNAME.so.1 on Linux, but this improvement is optional)

+1  A: 

One solution I've seen is to create an abstract wrapper class around your P/Invokes and to generate the appropriate one based on environment.

public abstract class Wrapper
{
   public void SomeMethod()
   {
      WrappedMethod();
   }

   public static Wrapper GetWrapper()
   {
      //TODO: write some method to determine OS
      return IsLinux() ? new LinuxWrapper() : new WindowsWrapper();
   }

   public abstract void WrappedMethod();
}

public class WindowsWrapper : Wrapper
{
  //windows dll imports go here

  public override void WrappedMethod()
  {
      //p/invokes go here
  }
}

public class LinuxWrapper : Wrapper
{
  //linux dll imports go here
  public override void WrappedMethod()
  {
     //p/invokes go here
  }
}
Steve Danner
+5  A: 

Two things

1- DllImport without the extension This is supported on Windows, Linux and MAC and will import the appropriate library for the target platform.

[DllImport("libMYLIBNAME")] - 

2- The preffered option is to use the <dllmap/> which allows you to map an import library name to the target platform library name. So if on Windows you have a dll called mylib.dll and the corresponding Linux so is mylinuxlib.so.3.6.1 you can import this using the windows DLL name

[DllImport("mylib.dll")]

And add a configuration to the config to map this name to the Linux library name

<configuration>
  <dllmap dll="mylib.dll" target="mylinuxlib.so.3.6.1" />
</configuration>

Read more Here

Chris Taylor
Leaving the extension off doesn't work, I tried that.
Hans Passant
I'm going with the dllmap option. I knew about it but I didn't know it could be specified per-assembly.
luiscubal
@Hans, that is strange, I tested it and it worked for me. Did you have the `.so` in the same directory as the `.exe`? I tested with Mono 2.4.4 on Ubuntu 10.04.
Chris Taylor
I tested on Windows.
Hans Passant
+2  A: 

Windows isn't picky about the filename extension for a DLL. Changing them isn't unusual, .ocx for ActiveX controls, .scr for screen savers for example. But still a regular DLL. The Windows loader verifies the identity of the file from the content, the PE32 header is what makes it a true DLL.

So just rename your Window version of the .dll to .so. Change the linker's Output name setting or just rename the file.

Hans Passant
If I rename the Windows DLL to *.so, then I won't be able to ship a Linux version in the same package...
luiscubal
Not sure what a "package" means. Another directory jumps to mind.
Hans Passant
I meant the same ZIP file I could distribute my assembly in.
luiscubal