Rich Zwaap

Where there's a will, there's a hack


Leave a comment

P/Invoke with Xamarin and Android

I’ve recently been playing around a bit with Xamarin’s tools for building cross-platform apps using C# and Visual Studio (commonly known to .NET developers as “the Holy Grail”).  Coding things up in managed code and sharing across platforms with Universal projects and good MVVM practices is all super easy, which is awesome and a great leap forward for we .NET folks.  But I’ve also been keenly interested in bringing pure native (i.e. C++) libraries into the mix, and here things get a little murky.  Xamarin provides some documentation indicating that this can be done via standard .NET PInvoke, but is light on details.  Digging around online, I found a bit more information (see here and here, for instance), but nothing that pulled everything together and, more importantly, provided a working example.  After a fair bit of banging my head on the nearest wall, I managed to get something working with Android, so I thought I’d share.  Hopefully I’ll be able to get this wired up with iOS as well and follow up with another post.

If you just want the code, go here.  For more details, read on.

The Setup

So what’s in the repo?  There are essentially two projects – one for the native C++ Android library, and one for the Xamarin library and app.  All the C++ stuff is under src\hello-int, while the Xamarin code is under src\HelloIntPInvoke.  You’ll need the Android NDK to build the Android library, and of course Xamarin and Visual Studio for the Xamarin library and app.  I also highly recommend using Genymotion as your Android emulator; it’s light years faster than those provided with the Android SDK.

To build the Android library, fire up a command prompt, navigate to the src\hello-int\jni directory, and run the ndk-build command that’s included in the root Android NDK directory.  Personally, I’ve setup the NDK root path as an environment variable for convenience, but typing out the entire path will work just as well.  To build the Xamarin library and app, just pop open the HelloIntPInvoke solution (under src\HelloIntPInvoke) in Visual Studio and build.

The Source

So taking a quick look at the code, it’s your typically trivial sample stuff.  The native library consists of one file, containing a whopping nine lines of C++:

int m_value;

extern "C" int hello_int_get_value()
{
   return m_value;
}

extern "C" void hello_int_set_value(int val)
{
   m_value = val;
}

To be clear, this is bad C++.  It makes use of a globally declared data member, which should of course always be avoided in practice.  But for a minimalist proof of concept, it works.  The methods are global too, but those need to be for P/Invoke – so in practice make sure to use long, ugly names that are almost certain to be unique.

On the C# side of things, we’ve got a Xamarin Android class library with a single class, HelloInt, that includes the static P/Invoke declarations needed to expose the C++ members to managed code:

[DllImport("libhello_int")]
private static extern int hello_int_get_value();

[DllImport("libhello_int")]
private static extern void hello_int_set_value(int val);

… and also contains a couple instance members that invoke the imported C++ members:

public int GetValue()
{
    return hello_int_get_value();
}

public void SetValue(int val)
{
    hello_int_set_value(val);
}

Then we’ve got the Xamarin Android app, which is just the default click-counting sample with the Click handler modified to pass the updated value down through the native setter and getter:

button.Click += delegate 
{
    count++;
    m_helloInt.SetValue(count);
    var i = m_helloInt.GetValue();
    button.Text = string.Format("{0} clicks!", i); 
};

The Rub

That’s all so simple, right?  Well there are a few wrinkles I stumbled over enroute to such a glorious outcome.

First, it turns out this only works with shared libaries (.so) and not static libraries (.a).  Now why was I trying static libraries to begin with?  Why not!  Being a relative C++ neophyte, and not at all familiar with Unix library types (i.e. .so and .a instead of .dll and .lib), I really had no idea the difference.  And is this documented anywhere?  Of course not!  Eventually, after much head-scratching and bad-word uttering, I came across this Xamarin forum post, which is to date the only mention I’ve seen about this limitation.  So make sure you $(BUILD_SHARED_LIBRARY) in your Android.mk, mmkay?

Second, the Android library must be prefixed with “lib” in order to be deployed with the app.  If you’re using the Android NDK, this isn’t a problem as it’s done automatically.  But originally I was using some compilation tools that weren’t Android-specific, and I had no clue about this sensitivity.  I never did find any documentation on this, but talking to my emulator via the Android Debugging Bridge (“shell ls” is your friend) finally let me see that my little library wasn’t anywhere to be found.  So I pulled in some other 3rd party native libraries, saw those were being deployed, and eventually sniffed out the fact that it was dependent on the name of the library itself.  Weird.

Last but not least, make sure you include the extern "C" bit on the C++ methods you want to make callable from C#, and don’t include the __declspec(dllexport) or __stdcall export directives that are typically included when making native methods P/Invokable on Windows platforms.  The entry point to call the methods from managed code won’t be found without extern "C", and you won’t get things compiling for Android with the Windows export directives. Nor do you need to include the JNIEXPORT or JNICALL directives that seem commonplace on C++ code that’s meant for Java interop. Basically, if it’s an export directive, and it’s not extern "C", just leave it out.

Some bonus gotchas that are doc’d, but easy to foul up nonetheless – in Visual Studio make sure you set the build action on your Android C++ libraries to EmbeddedNativeLibrary when including them in a class library project, and AndroidNativeLibrary when including them in an application project.  And make sure you put the libraries inside a folder structure of lib\<architecture> in your project, where <architecture> is aremeabi, armeabi-v7a, or x86.  And of course make sure that your libraries’ target architectures match the folder you put them in.

The End

So there you have it – a functional example of P/Invoking with Xamarin on Android, with many of the gory details called out.  I’ll admit that hacking around to get it all sorted out was quite the fun time, but I’m betting that those of you out there looking to do the same could do with a little less “fun” in getting up and running.  Hope you find it useful!

Advertisements