Again, here's the code for our C program:
#include <stdio.h>
#include <jni.h>
JNIEnv* create_vm() {
JavaVM* jvm;
JNIEnv* env;
JavaVMInitArgs args;
JavaVMOption options[1];
/* There is a new JNI_VERSION_1_4, but it doesn't add anything for the purposes of our example. */
args.version = JNI_VERSION_1_2;
args.nOptions = 1;
options[0].optionString = "-Djava.class.path=c:\\projects\\local\\inonit\\classes";
args.options = options;
args.ignoreUnrecognized = JNI_FALSE;
JNI_CreateJavaVM(&jvm, (void **)&env, &args);
return env;
}
void invoke_class(JNIEnv* env) {
jclass helloWorldClass;
jmethodID mainMethod;
jobjectArray applicationArgs;
jstring applicationArg0;
helloWorldClass = (*env)->FindClass(env, "example/jni/InvocationHelloWorld");
mainMethod = (*env)->GetStaticMethodID(env, helloWorldClass, "main", "([Ljava/lang/String;)V");
applicationArgs = (*env)->NewObjectArray(env, 1, (*env)->FindClass(env, "java/lang/String"), NULL);
applicationArg0 = (*env)->NewStringUTF(env, "From-C-program");
(*env)->SetObjectArrayElement(env, applicationArgs, 0, applicationArg0);
(*env)->CallStaticVoidMethod(env, helloWorldClass, mainMethod, applicationArgs);
}
int main(int argc, char **argv) {
JNIEnv* env = create_vm();
invoke_class( env );
}
In this section, we'll look at the invoke_class(JNIEnv*) function.
The JNIEnv pointer contains most of the functions for interacting with Java programs from the native
side. A complete (as of JDK 1.1) list of JNIEnv functions is available in the
JNI specification; there are
some JDK 1.2 enhancements described in the JDK 1.2
JNI Enhancements document and even newer
enhancements in the JDK 1.4 JNI Enhancements document.
(Yes, they could stand to rewrite and consolidate these; you currently have to read them all to know everything that's
available.)
The process for invoking the main method of our InvocationHelloWorld class is similar to
using Java reflection.
Our first task is to get a reference to the class we want to execute. We can do this via the FindClass
function. Note that an extra level of indirection is necessary when using C; in C++, we could code the
FindClass
call as env->FindClass("example/jni/InvocationHelloWorld");. Note also that we use VM-style syntax
for indicating the class name -- using slashes rather than periods for delimiting packages, etc.
Next, we want to get a reference to the main method with the signature
static void main(java.lang.String[]). (Note that JNI does not use access modifiers like
public, private, etc., so JNI breaks Java encapsulation.) In order to do that, we need to translate
the method signature into the VM's mangled language. The messy details on this are also in the
JNI specification (as well as the
VM specification). In any case, void [method name](String[]) becomes ([Ljava/lang/String;)V.
Using the method's name and signature as arguments, we can retrieve a jmethodID representing the method
using GetStaticMethodID.
Now, we need to create a String array to pass as an argument to the main method. An
array (of objects; primitive arrays have their own functions) can be instantiated using the
NewObjectArray function and specifying the array's type
(we get a reference to the String class using FindClass again) and length (the last
argument specifies the "default value" for the array -- that is, the value to which all the elements will be set upon
initialization).
To specify our argument -- the string "From-C-Program" -- we need to create a String object and then
set the array's element to point to it. Mercifully, JNI includes some string manipulation functions to make the
conversion between normal C strings and Java Unicode Strings easier. One such function is the
NewStringUTF function, which allows the creation of a Java String given a UTF-8 string.
We create our String using it and then set our array's element using SetObjectArrayElement.
Finally, we invoke the main method using our class reference, our method reference, and our arguments
array.