Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Proper binding of a native library in Xamarin

Given this method in a c++ library (Gifflen, animated gif maker):

JNIEXPORT jint JNICALL Java_org_jiggawatt_giffle_Giffle_Init(JNIEnv *ioEnv, jobject ioThis, 
jstring gifName, jint w, jint h, jint numColors, jint quality, jint frameDelay) { ... }

And this signature to access it:

extern "C"
{
    JNIEXPORT jint JNICALL Java_org_jiggawatt_giffle_Giffle_Init(JNIEnv *ioEnv,
    jobject ioThis, jstring gifName, jint w, jint h, jint numColors, jint quality, 
    jint frameDelay);
}

Shouldn't the p/invoke be similar to that?

[DllImport("libgifflen.so", CharSet = CharSet.Ansi)]
public static extern int Java_org_jiggawatt_giffle_Giffle_Init(string gifName, int w, 
int h, int numColors, int quality, int frameDelay);

I have a program that basically just tries to access that Init methods.

I'm loading the library successfully: Java.Lang.JavaSystem.LoadLibrary("gifflen");

Then it seems I'm pointing to the correct method, but it crashes as soon as it enters in the method.

I'm calling Init with same values as in the java demo application (and with basically the same values as in all my other implementations of this gif encoder since this is the same code-base for all libraries):

var pathForResult = "/storage/sdcard0/giftest/giffleResult.gif";
int optCol = 256, optQuality = 100, optDelay = 4;
Giffle.Init (pathForResult , 256, 256, optCol, optQuality, optDelay);

Then I'm getting this crash:

[mono-rt]   at <unknown> <0xffffffff>
[mono-rt]   at (wrapper managed-to-native) org.jiggawatt.giffle.Giffle.Java_org_jiggawatt_giffle_Giffle_Init (string,int,int,int,int,int) <IL 0x00046, 0xffffffff>
[mono-rt]   at org.jiggawatt.giffle.Giffle.Init (string,int,int,int,int,int) [0x00001] in c:\Users\user.name\Projects\Giffle\Giffle\Giffle.cs:20
[mono-rt] 
[mono-rt]   Attempting native Android stacktrace:
[mono-rt] 
[mono-rt]   at Java_org_jiggawatt_giffle_Giffle_Init+19 [0x6433c83c]
[mono-rt]   at ???+12272 [0x6467fff0]
[libc] Fatal signal 11 (SIGSEGV) at 0x6f7475d3 (code=1), thread 29670 (myapp.someapp)

I'm I using native libraries incorrectly in my Xamarin application?

Code for bindings

using System;
using System.Runtime.InteropServices;

namespace org.jiggawatt.giffle
{
    public class Giffle {

        public const string GIFFLE_DLL_NAME = "libgifflen.so";

        [DllImport(GIFFLE_DLL_NAME, CharSet = CharSet.Ansi)]
        public static extern int Java_org_jiggawatt_giffle_Giffle_Init(string gifName, int w, int h, int numColors, int quality, int frameDelay);

        [DllImport(GIFFLE_DLL_NAME, CharSet = CharSet.Ansi)]
        public static extern void Java_org_jiggawatt_giffle_Giffle_Close();

        [DllImport(GIFFLE_DLL_NAME, CharSet = CharSet.Ansi)]
        public static extern int Java_org_jiggawatt_giffle_Giffle_AddFrame(int[] inArray);
    }
}

Code for test

var external = Android.OS.Environment.ExternalStorageDirectory.AbsolutePath;
var appPath = System.IO.Path.Combine (external, "giftest");
if (!Directory.Exists (appPath)) {
    Directory.CreateDirectory (appPath);
}

var t = "/storage/sdcard0/giftest/result3.gif";

File.Delete (t);
int optCol = 256, optQuality = 100, optDelay = 4;

Giffle.Init (t, 256, 256, optCol, optQuality, optDelay);
var image = Bitmap.CreateBitmap (256, 256, Bitmap.Config.Argb8888);
for (int i = 0; i < 50; i++) {
    image = Bitmap.CreateBitmap (256, 256, Bitmap.Config.Argb8888);
    image.EraseColor (Color.Argb (255, 255 - i * 4, i * 4, i * 4));

    int[] pixelsCopy = new int[image.Width * image.Height];
    image.LockPixels ();
    image.GetPixels(pixelsCopy, 0, image.Width, 0, 0, image.Width, image.Height);
    image.UnlockPixels ();
    image.Recycle ();
    Giffle.Java_org_jiggawatt_giffle_Giffle_AddFrame (pixelsCopy);
}

Giffle.Java_org_jiggawatt_giffle_Giffle_Close ();
like image 380
Léon Pelletier Avatar asked Oct 09 '15 03:10

Léon Pelletier


1 Answers

You can only skip this arguments "JNIEnv *ioEnv, jobject ioThis" like you do in java if your c++ code does nothing with this arguments, otherwise you should also include this arguments in your function definition. And you should not use C# types, use java types instead.

[DllImport("libgifflen.so", EntryPoint = "Java_org_jiggawatt_giffle_Giffle_Init", CharSet = CharSet.Ansi)]
public static extern int Init(IntPtr env, IntPtr thiz, IntPtr gifName, IntPtr w, 
IntPtr h, IntPtr numColors, IntPtr quality, IntPtr frameDelay);

Call your function like this:

Init(JNIEnv.Handle, System.IntPtr.Zero, (new Java.Lang.String("Your gif name")).Handle, (new Java.Lang.Integer(w)).Handle,
    (new Java.Lang.Integer(h)).Handle, (new Java.Lang.Integer(numColors)).Handle, (new Java.Lang.Integer(quality)).Handle, (new Java.Lang.Integer(frameDelay)).Handle);

Check also this answer:

Load .so file in Xamarin.Android

like image 86
Led Machine Avatar answered Oct 07 '22 20:10

Led Machine