Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Including custom dll and dylib in MonoMac app

Background: My MonoMac app uses a custom build of sqlite3.0.8.6.dylib.

I need the exact steps to have MyApp.app use this dylib.

Here are some steps I took:

  1. Copied the dylib to MyApp.app/Contents/SharedSupport. (Related question: is this the preferred location for 3rd party dylibs or is MyApp.app/Contents/Frameworks preferred?)

  2. Changed the installed name for the library so that it matches its new location.

    MyApp.app/Contents/SharedSupport> otool -L libsqlite3.0.8.6.dylib 
    libsqlite3.0.8.6.dylib:
        @executable_path/../SharedSupport/libsqlite3.0.8.6.dylib (compatibility version 9.0.0, current version 9.6.0)
        /usr/lib/libSystem.B.dylib (compatibility version 1.0.0, current version 159.1.0)
    

Problems

  1. MyApp.app/Contents/MacOS/MyApp does not reference the dylib directly, so I can't use install_name_tool to point to the new library location. I believe that the library is referenced by System.Data.Sqlite.dll.
  2. I thought of overriding DYLD_FALLBACK_LIBRARY_PATH in the launcher script but MonoMac now uses a binary launcher (MyApp.app/Contents/MacOS/MyApp), not a script, so I'm out of luck there.

Would the MonoMac gods please help with what must be a simple solution? I've spent a couple of months, on and off, trying to get this to work.

And please provide exact steps - this problem is all about the details.

like image 641
bright Avatar asked Jan 26 '13 12:01

bright


2 Answers

Have a look at my answer to this question: Setting path of the Native Library for DllImport on Mono for Mac

The binary launcher comes from monodevelop/main/build/MacOSX/monostub.m.

You can use either MyApp.app/Contents/Frameworks or some other path, the important part is not to use any path names in your [DllImport] but instead add the <dllmap> using @executable_path to your app.config like I explained in that other answer.

There's a also a link to a test app on github in there.

Detailed Instructions

  1. Pick a path inside the MyApp.app to install your native dll, for instance Contents/SharedSupport/sqlite3.0.8.6.dylib.

  2. Compute the relative path from the directory where the managed assembly is located to the native .dll and prepend @executable_path to it.

    For instance, if your managed assembly is in Contents/MonoBundle/MyApp.exe and the native dll in Contents/SharedSupport/sqlite3.0.8.6.dylib, then it's @executable_path/../SharedSupport/sqlite3.0.8.6.dylib.

  3. Change the installed name of the library to this relative path using install_name_tool.

  4. Add a new MyApp.exe.config file to your project, containing

    <configuration>
      <dllmap dll="sqlite" target="@executable_path/../SharedSupport/sqlite3.0.8.6.dylib" />
    </configuration>
    

    Use the path that you computed in step 2. for the target field. Right-click the file in MonoDevelop, select "Quick Properties" from the context menu and enable "Copy to Output directory". This will copy the file into the Contents/MonoBundle directory, so it sits right next to your MyApp.exe.

  5. Use [DllImport ("sqlite")] to reference this in your code.

When another library references it

When another library, for instance Mono.Data.Sqlite.dll references it, it get a little bit more complicated.

Use the same steps as above, but you need to figure out which name that other library is using in its [DllImport] to reference the native library and put that into the <dllimport dll="..." />. You can either look for the [DllImport] statements in the source code or run monodis on the assembly and search for pinvokeimpl, for instance:

// method line 679
.method assembly static hidebysig pinvokeimpl ("sqlite3" as "sqlite3_create_function" cdecl )
       default int32 sqlite3_create_function (native int db, unsigned int8[] strName, int32 nArgs, int32 nType, native int pvUser, class Mono.Data.Sqlite.SQLiteCallback func, class Mono.Data.Sqlite.SQLiteCallback fstep, class Mono.Data.Sqlite.SQLiteFinalCallback ffinal)  cil managed preservesig 
{
    // Method begins at RVA 0x0
} // end of method UnsafeNativeMethods::sqlite3_create_function

So Mono.Data.Sqlite.dll is using "sqlite3" to reference the native dll, so your MyApp.exe.config file will look like this:

    <configuration>
      <dllmap dll="sqlite3" target="@executable_path/../SharedSupport/sqlite3.0.8.6.dylib" />
    </configuration>
like image 60
Martin Baulig Avatar answered Sep 21 '22 13:09

Martin Baulig


Be careful if you're using mono. @executable_path will return the path to the mono binary, instead of the actual .EXE executable. This should work correctly with Xamarin though.

like image 40
Ilya Suzdalnitski Avatar answered Sep 19 '22 13:09

Ilya Suzdalnitski