A Short, Practical Review of Foreign Function Calls in Java, C#, C++

Android is a popular platform for smartphones, with ten of thousands of applications developed for it every year. For coding an Android app, there are a number of programming languages, but Java is the most popular. However, Xamarin provides tools for writing applications in C# if you prefer. If you use Xamarin, but sometimes want to use an open-source library written in Java, you still can. The state of currently used, practical Foreign Function Interfaces (FFI) on most platforms is similar. Of course, the difficulty is in the details.

Generally, there are two ways for code in one language to call another: use a native function to call the foreign function; or, use reflection to call the foreign function. A bindings interface offers native functions to the whole foreign API.

Java Native Interface (JNI) provides a native interface to call non-Java code from Java. It also provides an API for use in C++ to invoke Java code, structured like Java’s reflection API.

There are a number of tutorials on how to use JNI, but they are somewhat out of date. This article brings it forward–if only to a small degree, with “how to” explanations.

Java Native Interface on Windows

Java to C# Call Using JNI

JNI provides a native interface for C++ functions in Java. For Java to call C#, a C++/CLI wrapper layer must be created containing calls to C# code.

  1. Create a Java file (e.g., Program.java) with a class.
  • Add native methods (e.g., static native void greet()) to the class corresponding to entry points in a C++/CLI DLL. These methods have no body in the Java code; the bindings to the C++/CLI code are assigned at runtime when the class is created.
  • Add a static initializer (e.g., { System.load(“…”); }) in the class to load the C++/CLI DLL defined in step 5. The purpose of the initializer is to load the C++/CLI DLL code and bind them to the native methods in the class. The C++/CLI DLL contains entry points that JNI will bind with the native methods in this class.

Program.java

public class Program
{
    static {
    	System.load("c:/Users/Ken/SkyDrive/JNI-Examples/Project1/x64/Debug/ClassLibrary2.dll");
    }

   public native void greet();

   public static void main(String[] args)
   {
       Program test = new Program();
       test.greet();
   }
}
  1. Compile the Java class, e.g., javac Program.java.
  2. Generate a C-style header file from the Java class, e.g., javah Program.

Program.h

/* DO NOT EDIT THIS FILE - it is machine generated */
#include <jni.h>
/* Header for class Program */

#ifndef _Included_Program
#define _Included_Program
#ifdef __cplusplus
extern "C" {
#endif
/*
 * Class:     Program
 * Method:    greet
 * Signature: ()V
 */
JNIEXPORT void JNICALL Java_Program_greet
  (JNIEnv *, jobject);

#ifdef __cplusplus
}
#endif
#endif
  1. Create a C# DLL project, e.g., ClassLibrary1.
  • In the C# library, add a class, e.g., Class1.

Class1.cs

using System;

namespace ClassLibrary1
{
    public class Class1
    {
        public Class1() { }

        public static void greet()
        {
            if (IntPtr.Size == 4)
                System.Console.WriteLine("Hello world, from JNI DLL 32-bit C# ...");
            else if (IntPtr.Size == 8)
                System.Console.WriteLine("Hello world, from JNI DLL 64-bit C# ...");
        }
    }
}
  1. Create a C++/CLI DLL project, e.g., ClassLIbrary2. This library functions as the “glue” between JNI and C#.
  • In the C++/CLI project, add or modify a C++/CLI source file, e.g., Entries.cpp.
  • In the source file, add #include <jni.h>, #include <>.
  • Add a #include directive for the JNI-generated (via javah) C-style header in the source file, e.g., Program.h.
  • Add code to adjust search path for DLLs, otherwise when the Java program is ran, the program will crash.
  • Add code for entry points defined in the generated header file.

Entries.cpp

#include "stdafx.h"
#include <jni.h>
#include <win32/jni_md.h>
#include "../x64/Debug/Program.h"
#include <stdio.h>
#include <stdlib.h>

using namespace System;

#using "C:\\Users\\Ken\\SkyDrive\\JNI-Examples\\Project1\\x64\\Debug\\ClassLibrary1.dll"

#ifdef __cplusplus
extern "C" {
#endif
    static bool initialized = false;
	using namespace System;
	using namespace System::Reflection;

	/***
	This is procedure is always needed when the .NET dll's arent in the actual directory of the calling exe!!!
	It loads the needed assembly from a predefined path, if found in the directory and returns the assembly.
	*/

	Assembly ^OnAssemblyResolve(Object ^obj, ResolveEventArgs ^args)
	{
		//System::Console::WriteLine("In OnAssemblyResolve");
#ifdef _DEBUG
		/// Change to your .NET DLL paths here
		String ^path = gcnew String("C:\\Users\\Ken\\SkyDrive\\JNI-Examples\\Project1\\x64\\Debug");
#endif
		array<String^>^ assemblies =
			System::IO::Directory::GetFiles(path, "*.dll");
		for (long ii = 0; ii < assemblies->Length; ii++) {
			AssemblyName ^name = AssemblyName::GetAssemblyName(assemblies[ii]);
			if (AssemblyName::ReferenceMatchesDefinition(gcnew AssemblyName(args->Name), name)) {
				//  System::Console::WriteLine("Try to resolve "+ name);
				Assembly ^a = Assembly::Load(name);
				//System::Console::WriteLine("Resolved "+ name);
				return a;
			}
		}
		return nullptr;
	}

	/**
	This procedure adds the Assembly resolve event handler
	*/
	void AddResolveEvent()
	{
		AppDomain::CurrentDomain->AssemblyResolve +=
			gcnew ResolveEventHandler(OnAssemblyResolve);
	}

	void greet()
	{
		ClassLibrary1::Class1 ^ xx = gcnew ClassLibrary1::Class1();
		xx->greet();
	}

	JNIEXPORT void JNICALL Java_Program_greet(JNIEnv *env, jobject obj)
	{
		if (! initialized)
		{
			AddResolveEvent();
			initialized = true;
		}
		greet();
	}

	// C++ Code

#ifdef __cplusplus

}
#endif
  1. Compile, build, and execute.

For a basic introduction on JNI Java to C++ calling, see http://www.codeproject.com/Articles/13093/C-method-calls-within-a-Java-program. However, the introduction is out of date and the example will not compile, e.g., the keyword __gc is not valid in the Visual C++ compiler since 2005!

For another introduction, see the following article in Software Practice and Experience: http://onlinelibrary.wiley.com/doi/10.1002/cpe.858/abstract

C# to Java Call Using JNI

JNI provides an interface for C++ to call Java code.  However, the interface is not native for C#. Instead, JNI is a reflection API for use in C++. A C++/CLI library must act as an intermediary between C# and JNI.

  1. Create a Windows C# application. Insert calls to a wrapper function containing the JNI code.

CSharpProgram.cs

using ClassLibrary3;

namespace CSharpApplication
{
    class CSharpProgram
    {
        static void Main(string[] args)
        {
            ClassLibrary3.Class1.Greet();
        }
    }
}
  1. Create a C++/CLI DLL project for wrapping the calls to JNI/Java.
  • In the C++/CLI project, add or modify a C++/CLI source file for code that will contain the JNI calls, e.g., Wrapper.cpp.
  • Add a #include directives for JNI, e.g., #include <jni.h>, #include <win32/jni_md.h>.
  • Add code for JNI_CreateJavaVM. This function call loads the Java Virtual Machine (JVM) and returns handle to it.
  • For each function you want to call in your Java API, add code for FindClass, GetStaticMethodID, CallStaticVoidMethod, etc. These functions get handles to JNI objects, and call the Java API.

Wrapper.cpp

#include "stdafx.h"
#include "ClassLibrary3.h"
#include <jni.h>
#include <win32/jni_md.h>
#include <stdio.h>
#include <stdlib.h>
#include <string>

namespace ClassLibrary3
{

    void Class1::Greet()
	{
		JavaVM *vm;
		JNIEnv *env;
		JavaVMInitArgs vm_args;
		vm_args.version = JNI_VERSION_1_2;
		vm_args.ignoreUnrecognized = 1;
		JavaVMOption jvmOptions[3];
		std::string classPath;
		classPath = "-Djava.class.path=C:\\Users\\Ken\\SkyDrive\\JNI-Examples\\Project1\\x64\\Debug";
		jvmOptions[0].optionString = (char *)classPath.c_str();
		vm_args.options = jvmOptions;
		vm_args.nOptions = 1;

		// Construct a VM
		jint res = JNI_CreateJavaVM(&vm, (void **)&env, &vm_args);

		// First get the class that contains the method you need to call
		jclass example = env->FindClass("com/domemtech/examples/Example");

		// Get the method that you want to call
		jmethodID greet = env->GetStaticMethodID(example, "greet", "()V");

		env->CallStaticVoidMethod(example, greet, 0);

		// Shutdown the VM.
		vm->DestroyJavaVM();
	}

}

  1. Create a .JAR file or directory structure with the Java API you want to implement. Make sure to set up the class in a package with the appropriate directory structure for the JAR, otherwise, JNI will not be able to find the API.

com/domemtech/examples/Example.java

package com.domemtech.examples;

public class Example
{
    public static void greet()
    {
    	System.out.println("Hi from Java");
    }
}
  1. Compile, build, and execute.

For another introduction on C++ calling Java, see http://www.codeproject.com/Articles/993067/Calling-Java-from-Cplusplus-with-JNI.

FFI in Xamarin

C# to Java Calling using a Binding Library

The details of how to create a Java Binding Library for a Java Jar library is given by Xamarin (https://developer.xamarin.com/guides/android/advanced_topics/java_integration_overview/binding-a-java-library/binding-a-jar/). As outlined there, you must create a library that belongs to a package, such as “com.domemtech.examples.” Otherwise, this won’t work. To do that, create your source in a directory tree corresponding to the package (e.g., “com/domemtech/examples/Example.java”). Compile the java code (“javac com”), then create a Jar file (“jar cvf example.jar com”).

C# to Java Calling using JNI

The details of how to use JNI is given by Xamarin (https://developer.xamarin.com/guides/android/advanced_topics/java_integration_overview/working_with_jni/). There doesn’t seem to be a discussion of where to place the JAR files, but it works if you create a binding library that embeds the jar.

Generating Interfaces

JNI4Net

JNI4Net is an open-source tool that generates native interfaces for Java and C#. After generating the proxy code, you have to add functionality to work as a wrapper. A good introduction is here: http://www.c-sharpcorner.com/UploadFile/ajyadav123/invoking-java-code-in-C-Sharp-net/

Simplified Wrapper and Interface Generator (SWIG)

SWIG is an open-source tool that generates native interfaces from a description specification. Complete functionality of the wrapper must be added by the user.