Każdy jest innym i nikt sobą samym.


The first step is to write the Java code declaring a native method and its arguments: class ShowMsgBox {
public static void main(String [] args) {
ShowMsgBox app = new ShowMsgBox();
app.ShowMessage("Generated with JNI");
788
Thinking in Java
www.BruceEckel.com
}
private native void ShowMessage(String msg);
static {
System.loadLibrary("MsgImpl");
}
}
The native method declaration is followed by a static block that calls System.loadLibrary( ) (which you could call at any time, but this style is more appropriate).
System.loadLibrary( ) loads a DLL in memory and links to it. The DLL must be in your system path or in the directory containing the Java class file. The file name extension is automatically added by the JVM depending on the platform.
The C header file generator: javah
Now compile your Java source file and run javah on the resulting .class file. Javah was present in version 1.0, but since you are using Java 1.1 JNI you must specify the –jni switch:
javah –jni ShowMsgBox
Javah reads the Java class file and for each native method declaration it generates a function prototype in a C or C++ header file. Here’s the output: the ShowMsgBox.h source file (edited slightly to fit into the book):
/* DO NOT EDIT THIS FILE
- it is machine generated */
#include <jni.h>
/* Header for class ShowMsgBox */
#ifndef _Included_ShowMsgBox
#define _Included_ShowMsgBox
#ifdef __cplusplus
extern "C" {
#endif
/*
* Class: ShowMsgBox
* Method: ShowMessage
* Signature: (Ljava/lang/String;)V
*/
JNIEXPORT void JNICALL
Java_ShowMsgBox_ShowMessage
(JNIEnv *, jobject, jstring);
#ifdef __cplusplus
}
#endif
#endif
As you can see by the #ifdef __cplusplus preprocessor directive, this file can be compiled either by a C or a C++ compiler. The first #include directive includes jni.h, a header file that, among other things, defines the types that you can see used in the rest of the file.
JNIEXPORT and JNICALL are macros that expand to match platform-specific directives; JNIEnv, jobject and jstring are JNI data type definitions.
Appendix A: Using Non-Java Code
789
Name mangling and function signatures
JNI imposes a naming convention (called name mangling) on native methods; this is important, since it’s part of the mechanism by which the virtual machine links Java calls to native methods. Basically, all native methods start with the word “Java,” followed by the name of the class in which the Java native declaration appears, followed by the name of the Java method; the underscore character is used as a separator. If the Java native method is overloaded, then the function signature is appended to the name as well; you can see the native signature in the comments preceding the prototype. For more information about name mangling and native method signatures, please refer to the JNI documentation.
Implementing your DLL
At this point, all you have to do is write a C or C++ source file that includes the javah-generated header file and implements the native method, then compile it and generate a dynamic link library. This part is platform-dependent, and I’ll assume that you know how to create a DLL. The code below implements the native method by calling a Win32 API. It is then compiled and linked into a file called MsgImpl.dll (for “Message Implementation”).
#include <windows.h>
#include "ShowMsgBox.h"
BOOL APIENTRY DllMain(HANDLE hModule,
DWORD dwReason, void** lpReserved) {
return TRUE;
}
JNIEXPORT void JNICALL
Java_ShowMsgBox_ShowMessage(JNIEnv * jEnv,
jobject this, jstring jMsg) {
const char * msg;
msg = (*jEnv)->GetStringUTFChars(jEnv, jMsg,0);
MessageBox(HWND_DESKTOP, msg,
"Thinking in Java: JNI",
MB_OK | MB_ICONEXCLAMATION);
(*jEnv)->ReleaseStringUTFChars(jEnv, jMsg,msg);
}
If you have no interest in Win32, just skip the MessageBox( ) call; the interesting part is the surrounding code. The arguments that are passed into the native method are the gateway back into Java. The first, of type JNIEnv, contains all the hooks that allow you to call back into the JVM. (We’ll look at this in the next section.) The second argument has a different meaning depending on the type of method. For non-static methods like the example above (also called instance methods), the second argument is the equivalent of the “this” pointer in C++ and similar to this in Java: it’s a reference to the object that called the native method.
For static methods, it’s a reference to the Class object where the method is implemented.
The remaining arguments represent the Java objects passed into the native method call.
Primitives are also passed in this way, but they come in by value.