What are JNI and this guide
Java Native Interface (JNI) is a simple tool that allows calling native functions from Java code. In addition, a native function which is called by Java may be able to call back Java functions but we shall not discuss this functionality in this tutorial.
There are plenty of existing guides (some of them are listed below), and I am not competing with them.
Rather, my purpose is to complement them with my experiences: when I tried to use JNI myself and followed these tutorials, I found a few difficulties which were not covered in them, and I wanted explain how I dealt with these difficulties which included packages and mangling conventions. This step-by-step guide explains exactly how I dealt with the difficulties I encountered.
I have been writing code that was supposed to work on Windows while running myself a Linux Ubuntu with Eclipse Java development environment.
Of course, I needed a Windows machine to compile and run Windows code, and for this purpose I had access to a Windows XP 32-bit computer on which I ran Eclipse 4.3.2, both for Java and for C/C++ native code; for C/C++ I used MinGW C/C++ toolchain.
Note that the dll I produced on the 32 bit computer, cannot be called from 64-bit code. Practically, this means that if I run 32-bit JRE, whether on 32-bit computer or an AMD64 one, I can call this dll from it; however, if I run 64-bit JRE (this can be done only on an AMD64 computer), I cannot call this library.
A side note: if you need to cover both 32-bit and 64-bit JRE, you need to compile two different dlls, write Java code that determines whether your JRE is 32-bit or 64-bit, and load the appropriate one of the two, e.g.:
load your 64-bit dll
load your 32-bit dll
First, read the existing tutorials
I suggest starting with reading a nice introduction and a step-by-step guide written in 2004 by Govind Seshadri. We shall follow the same steps below, although I shall add some details of mine. If you want, you can complement it with the Wikipedia article.
How to run javah to create a C header for JNI functions
This is the first step in implementing JNI.
Create a java file with a native function declaration (as explained in the above tutorials), and compile it.
Open a terminal.
If your java files were created by Eclipse, go to the
bin/ directory of the project (i.e.,
cd to this directory).
Otherwise, go to the directory where the package tree of the class files starts (the
bin/ directory of an Eclipse project is a particular case of it).
MyClass is declared in the package
MyClass.class lies in
cd in your terminal to
After that run javah:
This creates your header file:
It is placed in the same directory,
This file contains the C/C++ declarations of the native functions to be called from Java. It starts with the lines
/* DO NOT EDIT THIS FILE - it is machine generated */
The header file referenced here,
jni.h , is provided by java; I have found it in the directory
<Java installation directory>/jdk1.7.0_51/include
On my Windows computer, this is
This directory needs to be added to the include path, together with its subdirectory win32
<Java installation directory>/jdk1.7.0_51/include/win32
that contains another header file,
jni_md.h which is
#include ‘d by
jdk1.7.0_51 in the directory path reflects the fact that I was running jdk 1.7.0 Release 51; if you update your jdk to a later version, while doing that you change the directory name
jdk1.7.0_51 accordingly and want to build your dll again, you would have to update your include path.)
Still, Eclipse with MinGW C/C++ compliler could not find this include even though it could display
jni.h in its include list. The only thing that helped, was to replace the line
(Regretfully, I had to disregard the warning not to edit this file.)
After that the file successfully compiled.
Calling this code from java
The next step is to create a .c file that implements the declarations in this header (assuming you want to implement it in C as I did).
Compile it to create a Windows dll; if its name is hello.dll then do the following.
Place hello.dll on the Java build path of this project; in Eclipse, open Project Properties (by right-clicking on the project in the project explorer) → Java build path → Source → native library location → Edit, and make sure that the folder containing hello.dll is included.
Insert the following code in the class that calls the native function:
System.loadLibrary("hello"); // Load native library at runtime
// hello.dll (Windows) or libhello.so (Unixes)
Note that this code must appear before you actually call your native function; usually this code is put in the same java class in which you have declared your native function in java. For an example, see the Wikipedia article referenced above.
If you want your program to deal with the case the library cannot be loaded, embed the
System.loadLibrary call in a
try/catch block. (The alternative approach that I followed, was to package the dll together with the Java class files and deal with all problems related to this packaging – including with the possibility that the native library cannot be loaded – manually during debugging.)
Still, as I tried to run my program, I got a exception of the type
It was thrown on the line on which I called my native function; Java could not find it.
dumpbin /exports MyDLLname.dll
(according to the description of dumbin) and found that
@8 was added to the name of my exported function, which prevented it from being found.
As I studied it further, I found out the following.
The C/C++ header file that was created by javah (see above), defined my function with
JNICALL (see an example in the Wikipedia article). As I looked for the definition of
JNICALL , I found it in the header file
jni_md.h which is
include ‘d in the header file
jni.h (both mentioned above). The header
__stdcall . This is a calling convention and according to Microsoft documentation
__stdcall prefixes an underscore _ to the name and suffixes it with @ followed by the decimal number of bytes in the argument list.
So the conclusion is that
@8 is correct (8 being the number of bytes in the two arguments) but the leading underscore was missing.
I have found a solution to add the underscore _ before
Java_... in the name of the C function that implements the native Java call. This needs to be done in the header file which was generated by javah, Pkg1_Pkg2_Pkg3_MyClass.h – see above – and in the .c file that implements this function.
This solution worked successfully.
The remaining question is, whether MinGW compiler operates legitimately or not; this question is not entirely academic since if this is a bug of MinGW then some next version of MinGW may be expected to fix it, and at this point the workaround I implemented, would break down.
The Wikipedia article on Name mangling says: “The mangling scheme was established by Microsoft, and has been informally followed by other compilers including Digital Mars, Borland, and GNU GCC, when compiling code for the Windows platforms.” This means that, on one hand, we cannot complain to MinGW that this is a bug in the technical sense, but we can expect MinGW to fix it eventually.
©Baruch Youssin 2014