Sunday, 28 March 2010

JNI Example - Eclipse, Dev-Cpp

I wanted to test JNI by preparing simple example. I did had a lot of problems with it. Especially with creating proper DLL :( That is why I wanted to live something behind - maybe someone will have less problems. This is a simple example not tutorial about JNI.


Software:


  • Eclipse GALILEO - Java IDE - here

  • Dev-Cpp - C++ IDE - here



Plan:


  • Java part.

    • Create java project.

    • Create java class with native methods.

    • Generate cpp header.

    • Create java class which will load library and use our native class



  • C++ part.

    • Create c++ project.

    • Create c++ class and implement it.






Java part


After opening eclipse IDE create new Java project (File->New->Other->Java Project). Name: jni_hello_world. Create new class (File->New->Class) in default package (empty package) named MyFirstWrapper.



public class MyFirstWrapper {
native public String first(String parameter);
}

Create cpp directory in main project. Using eclipse External Tools Configuration (on the main toolbar there is play icon with red toolbox - running external tools) we add new configuration. Name: generate JNI header, location: c:\Program Files\Java\jdk1.7.0\bin\javah.exe, working directory: ${workspace_loc:/jni_hello_world/cpp} (use browse workspace directory), arguments: -jni MyFirstWrapper. After running it refresh the project (F5 on project). There should be created header file MyFirstWrapper.h in cpp directory.



/* DO NOT EDIT THIS FILE - it is machine generated */
#include
/* Header for class MyFirstWrapper */

#ifndef _Included_MyFirstWrapper
#define _Included_MyFirstWrapper
#ifdef __cplusplus
extern "C" {
#endif
/*
* Class: MyFirstWrapper
* Method: first
* Signature: (Ljava/lang/String;)Ljava/lang/String;
*/
JNIEXPORT jstring JNICALL Java_MyFirstWrapper_first
(JNIEnv *, jobject, jstring);

#ifdef __cplusplus
}
#endif
#endif

Now lets create new class named Runner in default package.



public class Runner {
static public void main(String[] args) throws Exception {
System.loadLibrary("MyFirst");
MyFirstWrapper wrapper = new MyFirstWrapper();
System.out.println(wrapper.first("world!"));
}
}

Using eclipse Run Configuration (on the main toolbar there is play icon) we add new configuration. On the main tab: name: Runner, project: jni_hello_world, main class: Runner. On the arguments tab: vm arguments: -Djava.library.path=cpp. Apply.


Now we are missing the dll so we go to the second part in dev-cpp.



C++ part


Open dev-cpp and create new project (File->New->Project). Choose on tab basic: Dynamic DLL, name: MyFirst, language C++, then OK and save it in your workspace in cpp directory (e.g. c:\workspaces\playground\jni_hello_world\cpp\MyFirst.dev). Now lets close without saving all generated tabs (right click on tab and close all). Add header file (right click on project and add to project - choose MyFirstWrapper.h). Create new file and save it as MyFirstWrapper.cpp (right click on project and new file then CTRL+S).



#include
#include
#include
#include
#include
#include "MyFirstWrapper.h"

JNIEXPORT jstring JNICALL Java_MyFirstWrapper_first
(JNIEnv *env, jobject obj, jstring parameter) {

jboolean isCopy;
const char *param = env->GetStringUTFChars(parameter, JNI_FALSE);
char temp[127];

sprintf(temp, "Hello %s!", param);
jstring result = env->NewStringUTF(temp);

env->ReleaseStringUTFChars(parameter, param);

return result;
}

Now we have to setup the project options. Open project properties (right click on project and project options). Tab Directories, subtab Include Directories add C:\Program Files\Java\jdk1.7.0\include and C:\Program Files\Java\jdk1.7.0\include\win32 (click the folder icon, choose folder and add button).


Hit in menu Execute->Rebuild All and there should be MyFirst.dll created.


Finally refresh the project in Eclipse and hit the earlier prepared runner configuration. The End. If you found it usefull live me a note ;) Maybe I will write something more when I will play around.

13 comments:

ReYvaJ said...

Great tutorial!! It helped me a lot. I had no problems at all, and it saved me a big headache...
I can't be thankfull enough.

Rajesh said...

Thank You

GopAL said...

I use eclipse Helios on Windows 7, I did exactly as you told, but I get "error: cannot access MyFirstWrapper, class file for MyFirstWrapper not found, javadoc: error - Class MyFirstWrapper not found. Error: No classes were specified on the command line. Try -help."
1)my MyFirstWrapper.class is created in bin folder of jni_hello_world folder in eclipse.
2)i have set environment variable "Variable Name : JAVA_HOME, Variable Value : C:\Program Files\Java\jdk1.6.0_23"
3)How do I give Arguments, -jni MyFirstWrapper, how do I tell eclipse to look for my class file in ".....jni_hello_world\bin" folder of my eclipse workspace.

GopAL said...

Your help will be appreciated, thankyou

GopAL said...

Okay, now, I changed, working Directory to "....\jni_hello_world\bin" and then ran. It successfully created the header file, but it created in the bin folder.
How do I make eclipse search for the class file from "....\jni_hello_world\bin" and store the header file created in "....\jni_hello_world\cpp"

vishwa said...

Hi while trying out your example in eclipse Helios windows xp am getting this error in eclipse console

"error: cannot access MyFirstWrapper, class file for MyFirstWrapper not found, javadoc: error - Class MyFirstWrapper not found. Error: No classes were specified on the command line. Try -help."

what i have to do..???
waiting for your reply

GopAL said...

Hi vishwa, I exactly got the same error, you can read the comments in this post, But I tried something, and I was able to create the header file, but changing the working directory...

vishwa said...

Hi Gopal May i know exactly what u changed...i didnt get you

GopAL said...

In Run Tools, External Tools Configurations, Name : give whatever name you want, 1)Location : c:\Program Files\Java\jdk1.7.0\bin\javah.exe 2) WrokingDirectory : GIVE THE BIN FOLDER OF YOUR ANDROID_TEST_PROJECT(jni_hello_world). 3) -jni MyFirstWrapper

What Working Directory actually means is, where our MyFirstWrapper.class file is located. previously we used to give /jni_hello_world/cpp. But that is not where our MyFirstWrapper.class is created. I guess for us it is created in some folder "bin" in the folder jni_hello_world, (check your filesystem once).

Probably that Wendro guy might have done some previous setting to get the MyFirstWrapper.class created in cpp directory. So he mentioned jni_hello_world/cpp in "Working Directory"

Then once you do this, your jni header file will also be created in bin folder, now copy this header file and paste it in jni_hello_world/cpp and proceed...


Hope it works, or you can get back to me...

vishwa said...

Hi Thanks i did like this i got a jni header file

In Eclipse Run Tools, External Tools Configurations,

Name:Jni

Location:My javah.exe location
D:\ProgramFiles\Java\jdk1.6.0_17\bin\javah.exe

Working Directory: my javah file location
${workspace_loc:/jni_hello_world/cpp}

And the most important point is inside Arguments we have to give first as our .class location then jni command like below

-classpath E:\Workspace\JNI\jni_hello_world\bin
-jni MyFirstWrapper

Lukman said...

hi,
I have tried this example, but I got an error.java.lang.NoClassDefFoundError: Djava/library/path=cpp.
Actually what have to set as the VM arguments in Run Configuration.
In this document it is given as Djava/library/path=cpp....
Pls reply anyone............
Advance thanks......

Lukman said...

hi,
I have tried this example, but I got an error.java.lang.NoClassDefFoundError: Djava/library/path=cpp.
Actually what have to set as the VM arguments in Run Configuration.
In this document it is given as Djava/library/path=cpp....
Pls reply anyone............
Advance thanks......

Christophe Gerard said...

posted a working example here:
http://stackoverflow.com/questions/17199904/why-do-i-get-unsatisfiedlinkerror-when-calling-a-mingw-compiled-function-not-lo/18523019#18523019