In this tutorial I will use Eclipse with CDT (using MinGW/gcc) to create a simple hello world example. Therefore we will create a Java program, that calls native code from a shared library. Settings for linux and windows differ in some details, so these differences will be explicitely marked.
Source code for this tutorial is available on github as a single zip archive, as a Team Project Set or you can browse the files online.
Step 1: Creating a Java Project
First create a new Java Project called "com.codeandme.jni". Create a class "com.codeandme.jni.JNITest" with following content:
package com.codeandme.jni; public class JNITest { static { // load library if (System.getProperty("os.name").startsWith("Windows")) // windows System.loadLibrary("libJNI_Library_windows"); else // linux System.loadLibrary("JNI_Library_linux"); } public static void main(final String[] args) { new JNITest().hello("world"); } // native method signature public native void hello(String name); }Line 9/13 will trigger loading of the shared library.
Line 21 declares an external method which is implemented in the native library.
Create a new folder called resources under your project root. This will be the location for our JNI library.
Step 2: Create C++ Project
Now we need to create the library code. Therefore create a new C++ Project called "JNI_Library". As Project Type select Shared Library/Empty Project. This will provide reasonable defaults for our build. Create a source folder src within that project.
The header file for the shared functions needs to follow a very specific coding. This is generated by javah, a helper program of your JDK. We will do this by using an external tool helper.
Use Run -> External Tools -> External Tools Configurations... and create a new Program launcher:
Location: C:\Program Files\Java\jdk1.8.0_31\bin\javah.exe
Working Directory: ${workspace_loc:/com.codeandme.jni/bin}
Arguments: -jni -d "${workspace_loc:/JNI_Library_windows/src}" com.codeandme.jni.JNITest
This launcher will create a header file within the src folder of our C++ Project. On linux you will have to change the Location to /usr/bin/javah. On the refresh tab you can enable refreshing of the target folder. Otherwise refresh manually after file creation.
Open the project properties of JNI_Library and go to C/C++ General/Paths and Symbols. Select [All Configurations] from the Configuration combo at the top of the dialog.
On the Includes tab add following folders from your JDK to the include paths for GNU C++.
Windows:
- include
- include/win32
- include
- include/linux
Now we can add our implementation in C++.
Create a new source file within src folder named com_codeandme_jni_JNITest.cpp. Add following content to the file
#include <iostream> #include "com_codeandme_jni_JNITest.h" using namespace std; JNIEXPORT void JNICALL Java_com_codeandme_jni_JNITest_hello (JNIEnv *env, jobject jthis, jstring data) { jboolean iscopy; const char *charData = env->GetStringUTFChars(data, &iscopy); cout << "Hello " << charData << endl; env->ReleaseStringUTFChars(data, charData); }
Finally we need to adjust our build settings a bit. Open the project properties and go to C/C++ Build/Settings. Switch to the Tool Settings tab:
Windows:
- GCC C++ Compiler/Miscellaneous: enable Position independent code (-fPIC)
- GCC C++ Linker/Shared Library Settings: enable Shared (-shared)
- MinGW C++ Linker/Miscellaneous: add -Wl,--add-stdcall-alias
- GCC C++ Compiler/Miscellaneous: enable Position independent code (-fPIC)
- GCC C++ Linker/Shared Library Settings: enable Shared (-shared)
Windows:
- xcopy "${BuildArtifactFilePrefix}${BuildArtifactFileName}" "${workspace_loc:/com.codeandme.jni/resources/}" /Y
- cp "${BuildArtifactFilePrefix}${BuildArtifactFileName}" "${workspace_loc:/com.codeandme.jni/resources/}"
This will automatically copy our build product to the Java Project we created at step 1.
Save all settings and build your library. It should be copied over to your Java Project. Refresh your workspace (F5) to see the newly created file.
Step 3: Put it all together
We are almost done. As a final step we need to add the resource folder to the library path of our run target.
Launch your Java program to create a launch target for it. You will get an Exception, don't worry, we'll fix that immediately. Go to Run -> Run Configurations... and find your Java run target (typically named JNITest). On the Arguments tab add
-Djava.library.path="${workspace_loc:/com.codeandme.jni/resources/}"to VM arguments.
Now launch your Java program and you should get a "Hello world" on your console view.
Further reading
It is also possible to have asynchronous callbacks from C to Java. An example how to do this is available on github.
Thanks a lot for this detailed instruction.
ReplyDeleteIt was a pain for me to get JNI running with CDT and MinGW. There are much tutorials and instructions out there on the web, but most of them are out of date. And there are many explanations about this ******* java.lang.UnsatisfiedLinkError - too much caused by too much several reasons....
I hate the heterogeneous C++ build process that is more complicated to me than programming itself.
Excellent Article ! But there should be one fix associated with that,
ReplyDelete/* CODE */
package com.example.jni;
public class JNITest {
static {
// load library
System.loadLibrary("resources/libJNI Library");
}
public static void main(final String[] args) {
new JNITest().hello("world");
}
// native method signature
public native void hello(String name);
}
Actually this is a windows/linux issue (don't know how this is on mac):
DeleteOn windows a dll called libJNI.dll would be accessed with System.loadLibrary("libJNI");
On linux libJNI.so would be accessed with ystem.loadLibrary("JNI");
so I mixed windows and linux stuff a bit up there.
Dear Christian,
ReplyDeleteExcellent, succinct, article. I'm determined to get it to work, but I can't figure out WHICH file is can't find when its building JNI Library (in the console log in full below the error is reported as "g++: CreateProcess: No such file or directory)"
CDT Build Console log:
---------------------------------------------------------------------------------
13:51:09 **** Incremental Build of configuration Debug for project JNI Library ****
Info: Internal Builder is used for build
g++ "-IC:\\Program Files\\Java\\jdk1.7.0_06\\include\\win32" "-IC:\\Program Files\\Java\\jdk1.7.0_06\\include" "-IC:\\MinGW\\lib\\gcc\\mingw32\\4.7.0\\include\\c++" "-IC:\\MinGW\\lib\\gcc\\mingw32\\4.7.0\\include\\c++\\backward" "-IC:\\MinGW\\lib\\gcc\\mingw32\\4.7.0\\include\\c++\\mingw32" "-IC:\\MinGW\\include" -O0 -g3 -Wall -c -fmessage-length=0 -o "src\\com_example_jni_JNITest.o" "..\\src\\com_example_jni_JNITest.cpp"
g++: CreateProcess: No such file or directory
g++ -Wl,--add-stdcall-alias -shared -o "libJNI Library.dll" "src\\com_example_jni_JNITest.o"
g++: src\com_example_jni_JNITest.o: No such file or directory
xcopy "libJNI Library.dll" /Y
File not found - libJNI Library.dll
0 File(s) copied
13:51:09 Build Finished (took 288ms)
----------------------------------------------------------------------------------
Thanks for taking a look,
Peter Schwenn
Woowwwwwwwwwwwwwwwwwwww!!!. Awesome... i was ruining from last two days to execute a sample hello world program..visual studio dll creation sucks since i dunno to create properly and no proper tutorial for that.. this tutorial made it easy with eclipse itself.. thanks to the author...
ReplyDeletelove yaaaaa!
Great Tutorial, thank you! Just a side note: in my case (windows & mingw) I had to add the addional
ReplyDelete-static-libgcc -static-libstdc++
as linker flags in order to avoid an
"UnsatisfiedLinkError: Can't find dependent libraries"
Thank you! I'm using (windows & mingw) and I also had to use that.
DeleteSo for anyone else in the same situation the full list is:
-Wl,--add-stdcall-alias, -static-libgcc, -static-libstdc++
Many thanks for the tutorial!
ReplyDeleteGreat tutorial Thank you!! :)
ReplyDeleteBTW:
In the Java file you have:
System.loadLibrary("JNI Library");
However the library that is copied into the Java resource file was name libJNI Test.dll, and thus the correct line should be:
System.loadLibrary("libJNI Library");
depending on the operating system you are using, you may be right. Please see my response to the 2nd post
DeleteOh my bad, I didn't realized you mentioned that already. Thanks again! This was by far the best Eclipse + JNI tutorial that I found.
DeleteHey, thank you for your tutorial. But I have a problem.
ReplyDeletexcopy "${BuildArtifactFilePrefix}${BuildArtifactFileName}" "${workspace_loc:/JNI Test/resources/}" /Y
This command is not copy our build project. How can I fix that.
hi, its a simple doc command, check that all files and target locations are there
DeleteWhat he means is that the xcopy fails because the resources folder is not created.
Deletefor linux??
ReplyDeleteThis comment has been removed by the author.
ReplyDeleteThis comment has been removed by the author.
ReplyDeleteAfter another try ... getting these errors...
ReplyDeleteThe Os Is WINDOWS----calling native libraray
Exception in thread "main" java.lang.UnsatisfiedLinkError: C:\Users\Lenovo\WorkSpaceForJNI\com.codeandme.jni\lib\libJNI_Library.dll: Can't load IA 32-bit .dll on a AMD 64-bit platform
at java.lang.ClassLoader$NativeLibrary.load(Native Method)
at java.lang.ClassLoader.loadLibrary0(Unknown Source)
at java.lang.ClassLoader.loadLibrary(Unknown Source)
at java.lang.Runtime.loadLibrary0(Unknown Source)
at java.lang.System.loadLibrary(Unknown Source)
at com.codeandme.jni.JNITest.(JNITest.java:9)
Why this is comming?
the error message is clear: you are building a 32 bit library and tr to run it on a 64 bit environment. you need to adjust your library build settings to 64 bit. How to do this is beyond this tutorial
DeleteHi Christian..
DeleteSorry for the late reply...
And Yes, you were right, I simply replaced the MinGW32 to MinGW64 before going for the build... and it worked fine...
Thanks for that wonderful piece of code..
Hope you can add something about "How to run a piece of code in a remote server through eclipse installed in a local desktop"... Thanks in advance...
Where to set pls for this issue for windows ?
Deletehow to fix error :
ReplyDelete# A fatal error has been detected by the Java Runtime Environment:
#
# EXCEPTION_ACCESS_VIOLATION (0xc0000005) at pc=0x00000003f79fd55d, pid=7040, tid=7064
#
# JRE version: Java(TM) SE Runtime Environment (7.0_79-b15) (build 1.7.0_79-b15)
# Java VM: Java HotSpot(TM) 64-Bit Server VM (24.79-b02 mixed mode windows-amd64 compressed oops)
# Problematic frame:
# C [cygstdc++-6.dll+0x6d55d]
your dll did something wrong, sorry but I cant help you here
DeleteUseful article, thank you for sharing the article!!!
ReplyDeleteWebsite bloggiaidap247.com và website blogcothebanchuabiet.com giúp bạn giải đáp mọi thắc mắc.