Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Setting path of the Native Library for DllImport on Mono for Mac

The code I am porting to Mono for Mac, calls into a unmanaged C++ library. On the Mac I have ported our unmanaged library into a Framework(unfortunately our Build process only allows it to be compiled as a Framework and not as a dylib). In Mono I have set up a post build step in my project to copy the Framework file next to the built app.

But when a P/Invoke into this dll I get a DllNotFound exception. I read through the Mono Interop Wiki and it seems P/Invoke on Mono will only look at the locations pointed to by the DYLD_* environment variables and the current directory is not in the search path. When I copied the Framework to /Library/Frameworks, my P/Invoke calls worked fine, but I would prefer my Framework files to be present next to the App and not in /Library/Frameworks.

I tried adding the current working directory to the DYLD_FRAMEWORK_PATH environment variable in my code before I do any P/Invoke calls, but the System.Environment namespace has limited functionality on Mono for Mac and Getting or Setting environment variables is not supported.

Is there anyway I can keep my Framework files next to the App and still be abel to P/Invoke?

like image 928
Shivaprasad Avatar asked Oct 11 '12 07:10

Shivaprasad


1 Answers

Let's split this into two questions: how to set environment variables and how to bundle native frameworks in a MonoMac application.

Setting Environment Variables

You can set environment variables in the LSEnvironment section of your application's Info.plist, like this:

    <?xml version="1.0" encoding="UTF-8"?>
    <!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
    <plist version="1.0">
            <dict>
            <key>CFBundleIdentifier</key>
            <string>com.yourcompany.TableViewTest</string>
            <key>CFBundleName</key>
            <string>TableViewTest2</string>
            <key>CFBundleVersion</key>
            <string>1</string>
            <key>LSMinimumSystemVersion</key>
            <string>10.6</string>
            <key>NSMainNibFile</key>
            <string>MainMenu</string>
            <key>NSPrincipalClass</key>
            <string>NSApplication</string>
            <key>LSEnvironment</key>
            <dict>
                    <key>Foo</key>
                    <string>Bar</string>
            </dict>
    </dict>
    </plist>

It seems like to have to manually edit that file once and add at least one environment variable.

The file is automatically created by MonoDevelop, so all you have to do is to add the LSEnvironment section.

After that, you can edit them in MonoDevelop: go to project options, "Mac OS X Application", "Advanced".

Bundling Native Frameworks in a MonoMac Application

You don't need to set any environment variables to bundle a native framework in a MonoMac Application, there's a much easier and cleaner way to do it, which is also similar to how things work in Objective C.

I created a small test applications, which bundles a framework both in a native Objective C application and in a MonoMac one.

The first thing you need to do is bundle your framework with the app. There currently is no way of doing this automatically in MonoDevelop, so you need to manually copy the files or use some post-build script (see copy-framework.sh in my example).

I would recommend to put the framework into YourApp.app/Contents/Frameworks/YourFramework.framework as that's how XCode handles it; see also Apple's Documentation.

To reference a library inside your application bundle, you can use `@executable_path' (see the dyld man page).

I would recommend to create an app.config file using <dllmap>, so you don't need to put any pathnames into your code, thus making it easier to change framework versions. For instance:

    <configuration>
       <dllmap dll="TestFramework" target="@executable_path/../Frameworks/TestFramework.framework/TestFramework" />
     </configuration>

If the actual library inside your framework starts with lib or ends with .so / .dylib, then you must specify that name (the above dllmap won't file TestFramework.framework/libTestFramework.dylib, for instance). This is a bug in Mono, which I just fixed.

like image 196
Martin Baulig Avatar answered Nov 18 '22 21:11

Martin Baulig