title: P/Invoke to dynamically loaded library on Mono
body: <p>I'm writing a cross-platform .NET library that uses some unmanaged code.  In the static constructor of my class, the platform is detected and the appropriate unmanaged library is extracted from an embedded resource and saved to a temp directory, similar to the code given in <a href="https://stackoverflow.com/a/768429/358336">another stackoverflow answer</a>.  </p>
  
  <p>So that the library can be found when it isn't in the PATH, I explicitly load it after it is saved to the temp file. On windows, this works fine with <code>LoadLibrary</code> from kernel32.dll.  I'm trying to do the same with <code>dlopen</code> on Linux, but I get a <code>DllNotFoundException</code> when it comes to loading the P/Invoke methods later on.</p>
  
  <p>I have verified that the library "libindexfile.so" is successfully saved to the temp directory and that the call to <code>dlopen</code> succeeds.  I delved into the <a href="https://github.com/mono/mono/blob/1429f06a9e3baf56245ec2433dcdb77e76eba684/mono/metadata/loader.c#L565" rel="nofollow noreferrer">mono source</a> to try figure out what is going on, and I think it might boil down to whether or not a subsequent call to <code>dlopen</code> will just reuse a previously loaded library.  (Of course assuming that my naïve swoop through the mono source drew the correct conclusions).</p>
  
  <p>Here is the shape of what I'm trying to do:</p>
  
  <pre><code>// actual function that we're going to p/invoke to
  [DllImport("indexfile")]
  private static extern IntPtr openIndex(string pathname);
  
  const int RTLD_NOW = 2; // for dlopen's flags
  const int RTLD_GLOBAL = 8;
  
  // its okay to have imports for the wrong platforms here
  // because nothing will complain until I try to use the
  // function
  [DllImport("libdl.so")]
  static extern IntPtr dlopen(string filename, int flags);
  
  [DllImport("kernel32.dll")]
  static extern IntPtr LoadLibrary(string filename);
  
  
  static IndexFile()
  {
      string libName = "";
  
      if (IsLinux)
          libName += "libindexfile.so";
      else
          libName += "indexfile.dll";
  
      // [snip] -- save embedded resource to temp dir
  
      IntPtr handle = IntPtr.Zero;
  
      if (IsLinux)
          handle = dlopen(libPath, RTLD_NOW|RTLD_GLOBAL);
      else
          handle = LoadLibrary(libPath);
  
      if (handle == IntPtr.Zero)
          throw new InvalidOperationException("Couldn't load the unmanaged library");
  }
  
  
  public IndexFile(String path)
  {
      // P/Invoke to the unmanaged function
      // currently on Linux this throws a DllNotFoundException
      // works on Windows
      IntPtr ptr = openIndex(path);
  }
  </code></pre>
  
  <hr>
  
  <p><strong>Update:</strong></p>
  
  <p>It would appear that subsequent calls to <code>LoadLibrary</code> on windows look to see if a dll of the same name has already been loaded, and then uses that path.  For example, in the following code, both calls to <code>LoadLibrary</code> will return a valid handle:</p>
  
  <pre><code>int _tmain(int argc, _TCHAR* argv[])
  {
      LPCTSTR libpath = L"D:\\some\\path\\to\\library.dll";
  
      HMODULE handle1 = LoadLibrary(libpath);
      printf("Handle: %x\n", handle1);
  
      HMODULE handle2 = LoadLibrary(L"library.dll");
      printf("Handle: %x\n", handle2);
  
      return 0;
  }
  </code></pre>
  
  <p>If the same is attempted with <code>dlopen</code> on Linux, the second call will fail, as it doesn't assume that a library with the same name will be at the same path.    Is there any way round this?</p>