Login Register






[Tut] Java Native Interface basics filter_list
Author
Message
[Tut] Java Native Interface basics #1
Introduction To The Java Native Interface 


Introduction
Hi, I'm NzFrog, a newbie to these forums, but I'm not a newbie to coding  Cool
This is also my first tutorial, so any feedback is much appreciated. 


Anyway, in this tutorial I will attempt to show you how to call native code (ie. a C function) from Java using the Java Native Interface (JNI). Why on earth would you want to do that? Well there are a few reasons, you might want to use a library written in C from your Java code, or maybe you want to use some low level native code to accomplish something that would be hard or impossible to do in Java.


I'll say this now, this is just intended as a simple introduction to get you started. You will definitely want to check out the links in the further reading section at the end to gain a proper and much deeper understanding of JNI. You will not learn anything by eating copypasta, you need to understand how things work to use them effectively. 

We will be writing a function in C that will call a Win32 API function (just for an example, you can do whatever you want in your C code), which will then be put into a DLL. This function will then be called just like any Java function from a Java class which we will be writing.

What you will need:

* Knowledge of C and Java

* The Java JDK (I am using version 1.6.0_03)

* A C compiler (I am using GCC 3.4.5, but any C compiler should do the trick)
* Any OS capable of running the above, I will be using Win32 specific code, but *nix guys can easily replace it with their platforms counterparts

Okay lets do this shit

So what native code are we gunna call? Well in this tutorial I'm going to go with a function that returns the username of the user running the process, and before you flame me, I know you can do this in Java, it is just an example!!! To get the username I'm going to use the Win32 API function GetUserName(), *nix guys be creative here! 



First of all lets break everything into steps to give us a clearer view of what we need to do. If it seems like the first two steps are backwards, you will see why shortly  :funny:



1. Write our Java class, which needs to

* Load our DLL

* Call our C function from the loaded DLL



2. Create our DLL, which involves

* Using the JDK to generate our header file (You'll see why we don't write this ourselves, although you could if you like wasting time)

* Write the actual C implementation of our function

* Write a .def file for creating the DLL

* Compile and link



3. Test it



4. ???


5. Profit!






This step is pretty simple, theres only a few tricky bits (but even those aren't that hard  :hurr: ) Lets call our class GetUserName, so fire up your favourite text editor and make a basic class shell and add in a main function:


Code:

Code:
[align=center]class GetUserName {[/align] [align=center]    public static void main(String[] args) {[/align] [align=center]    }[/align] [align=center]}[/align]

Of course, this is going to be saved as GetUserName.java. So looking back up at the list, we need to load our DLL and then call a function in it. Here we're going to assume our DLL is called gun.dll and our function is gunna be called getUserName(), which will take no parameters and return a string.

Loading the DLL is pretty simple, all we require is a call to System.loadLibrary(), passing it the name of our DLL, in this case gun (Java automatically adds the extension, be it DLL on Windows or SO on *nix). The only tricky bit here is we're going to load the library in a static block, not inside a function. This ensures that the library will only get loaded once for each class. Although you can load the library wherever you want. So now our class will look like this:

Code:
Code:
[align=center]class GetUserName {[/align] [align=center]    // Load the library in a static block[/align] [align=center]    static {[/align] [align=center]        System.loadLibrary("gun");[/align] [align=center]    }[/align] [align=center]    [/align] [align=center]    public static void main(String[] args) {[/align] [align=center]    }[/align] [align=center]}[/align]


Well now the library will be loaded, all we've gotta do here is call the function. But wait, how is Java gunna know the function exists? We will have to define it in our class, seeing as functions cannot exist outside of classes in Java. The only differences between this and a normal function is that we provide no implementation in the Java code, and we need to declare it as native. Declaring it as native tells Java it's a native function, not a Java one. Check out the code below to see how we do this:

Code:
Code:
[align=center]class GetUserName {[/align] [align=center]    static {[/align] [align=center]        System.loadLibrary("gun");[/align] [align=center]    }[/align] [align=center]    [/align] [align=center]    // Our native function definition[/align] [align=center]    public native String getUserName();[/align] [align=center]    [/align] [align=center]    public static void main(String[] args) {[/align] [align=center]    }[/align] [align=center]}[/align]



Ok, that's all the JNI related Java code we have to write, the rest is just standard Java code to fill out our main method, so I'm just going to dump it on you below, it should be pretty self explanatory:

Code:
Code:
[align=center]class GetUserName {[/align] [align=center]    static {[/align] [align=center]        System.loadLibrary("gun");[/align] [align=center]    }[/align] [align=center]    [/align] [align=center]    public native String getUserName();[/align] [align=center]    [/align] [align=center]    public static void main(String[] args) {[/align] [align=center]        // Create an instance of our class[/align] [align=center]        GetUserName inst = new GetUserName();[/align] [align=center]        [/align] [align=center]        // Call our native function and save the result in uname[/align] [align=center]        String uname = inst.getUserName();[/align] [align=center]        [/align] [align=center]        // Spit uname out to standard output[/align] [align=center]        System.out.println("User name: " + uname);        [/align] [align=center]    }[/align] [align=center]}[/align]

That's all the Java stuff done! Grab a drink and move on to step 2 when you're ready  :funny:

Step 2 - Creating the DLL

Well so far it's been quite a walk in the park, but don't worry, it's about to get alot more brutal down in the guts of JNI  :tongue:

Once again lets look up to our task list for this step, and we see that the first step is generating a header file. Why generate it and not write it? Well, the C definition of our function can get a bit messy with JNI stuff, and the JDK provides a tool to generate it for us, so why not save some time and use it?

To generate the header file we use the javah program, passing it the name of the class we want a header for, like so:

Code:
Code:
javah GetUserName


This goes through our class and picks out all the native functions and makes a file (in this case GetUserName.h) which will contain all C definitions of our native functions. Here is what you should have got: 

Code:

Code:
[align=center]/* DO NOT EDIT THIS FILE - it is machine generated */[/align] [align=center]#include <jni.h>[/align] [align=center]/* Header for class GetUserName */[/align] [align=center][/align] [align=center]#ifndef _Included_GetUserName[/align] [align=center]#define _Included_GetUserName[/align] [align=center]#ifdef __cplusplus[/align] [align=center]extern "C" {[/align] [align=center]#endif[/align] [align=center]/*[/align] [align=center]* Class:     GetUserName[/align] [align=center]* Method:    getUserName[/align] [align=center]* Signature: ()Ljava/lang/String;[/align] [align=center]*/[/align] [align=center]JNIEXPORT jstring JNICALL Java_GetUserName_getUserName[/align] [align=center]  (JNIEnv *, jobject);[/align] [align=center][/align] [align=center]#ifdef __cplusplus[/align] [align=center]}[/align] [align=center]#endif[/align] [align=center]#endif[/align]

Lets take a closer look at the actual function definition:

Code:

Code:
[align=center]JNIEXPORT jstring JNICALL Java_GetUserName_getUserName[/align] [align=center]  (JNIEnv *, jobject);[/align]

Ok lets break this down even further, peice by peice. JNIEXPORT is a #define (in jni_md.h) that tells your compiler to export the function to a shared library. The return type, jstring, is a C representation of the Java String object. As Java types may not relate directly to C types there is one of these for every type, eg. jint, jbyte etc. See the further reading section at the end of this tutorial for more info on these. JNICALL is the calling convention that Java wants you to use, it is also a #define in jni_md.h. Now check out the function name, looks pretty mangled right? It basically goes like this Java_<Java Class Function Is In>_<Function Name>, so in our case we defined the function as getUserName in the Java class GetUserName, see how that works? Ok last thing here, and you probably noticed this already, whats with the two parameters? I thought our function didn't take any? Yeah but all JNI functions get passed these two parameters no matter what. The first one, the JNIEnv pointer, is a pointer to the Java VM running our Java code. This is pretty important, we use it for converting C types to their Java representations. Once again, for more info on this see the further reading section. The second parameter, the jobject, is a reference to the object that called our function. Any parameters your function takes will appear after these two.


Whew, I think I need another drink after that lol. 

Back to the task list... next we've gotta write the implementation! This is mostly just like writing any other function in C, so I'll just dump the code below and explain any JNI related parts in more detail afterwards. Create a file called GetUserName.c for this code.

Code:

Code:
[align=center]#include <windows.h>        // For GetUserName()[/align] [align=center]#include <jni.h>            // Need this for any JNI related code[/align] [align=center][/align] [align=center]// The function definition from our header, except we named the parameters[/align] [align=center]JNIEXPORT jstring JNICALL Java_GetUserName_getUserName[/align] [align=center]  (JNIEnv *pJEnv, jobject pJObj) {[/align] [align=center]    char uname[100];        // This will hold the username we get back from GetUserName()[/align] [align=center]    int nameLen = 100;        // The length of our string, once GetUserName() returns will contain the length of the username[/align] [align=center]    [/align] [align=center]    // Call GetUserName()[/align] [align=center]    GetUserName((LPTSTR)uname, (LPDWORD)&nameLen);[/align] [align=center][/align] [align=center]    // Return the username string, this line will be explained below :D[/align] [align=center]    return (*pJEnv)->NewStringUTF(pJEnv, uname);[/align] [align=center]}[/align]

Pretty straight forward right? Maybe, apart from this line:

Code:

Code:
return (*pJEnv)->NewStringUTF(pJEnv, uname);

Remeber how I said we would use the JNIEnv parameter to convert between C types and Java types? Thats exactly what this line is doing. The NewStringUTF function converts a char array into a jstring object, which we promised Java our function would return. There are a whole bunch of these functions for all sorts of different type conversions, yet again check the further reading links for more info on these functions. Remember though, these functions are essential. Java types are not compatible with C types.

Sweet, we're almost ready for compilation, just one more little task.


To make our DLL complete, we need to write a linker definition file, which just says what functions we want exporting. It is rediculously simple so once again I'm just gunna dump it and leave it at that. Save to GetUserName.def. 

Code:

Code:
[align=center]EXPORTS[/align] [align=center]Java_GetUserName_getUserName[/align]

Now we are finally ready to compile and link our code  Cool I wrote a batch file to do this for me, to save my fingers.

We compile the Java class just like any other:

Code:

Code:
javac GetUserName.java

Then generate our header file:

Code:

Code:
javah GetUserName

Sweet, now we need to build our DLL. The following commands are for GCC, but it should be pretty easy to come up with the commands for any other compiler. Remember, you will have to change the include directories to wherever you installed the JDK, the ones here are the defaults for my OS and JDK version.

Code:

Code:
gcc -c GetUserName.c -o gun.o -I"C:\Program Files\Java\jdk1.6.0_03\include" -I"C:\Program Files\Java\jdk1.6.0_03\include\win32"

Now link it...

Code:

Code:
gcc -shared -o gun.dll gun.o GetUserName.def

If all went well you'll have a gun.dll and a GetUserName.class  :funny:
Pat yourself on the back if you've made it this far successfully. All we have to do now is test it!

Step -Testing

This is so easy it barely deserves its own section. You can run the program the same was as you would run any Java class:

Code:

Code:
java GetUserName

Note here, if your DLL file is in a different directory to your class file, you will have to pass the path to Java like so:

Code:

Code:
java -Djava.library.path=\path\to\library GetUserName

Steps 4 and 5 - ??? and Profit
Do I really need to explain how to ??? and Profit lol...

Conclusion
Well I hoped my little tutorial helped someone. I haven't provided a download with all the code and stuff simply because all the code is in the tutorial itself. Please take a look at the further reading section below though!Thank you for reading  :funny: 

Further Reading

Java Native Interface documentation:

Sun Java Native Interface Guide:



Will proof read and edit for errors soon. Thanks
(This post was last modified: 11-09-2015, 11:12 AM by Reaper.)

Reply

RE: [Tut] Java Native Interface basics #2
nice tut,thanks so much

Reply

RE: [Tut] Java Native Interface basics #3
(11-09-2015, 05:13 AM)jimmynguyen Wrote: nice tut,thanks so much

Glad you liked it Smile
(This post was last modified: 11-09-2015, 05:20 AM by Reaper.)

Reply