InOnIt.com
Cygwin/Java
FAQ
Calling C from Java
Starting a JVM from C
Overview
Building an import library
Creating a JVM from C
Invoking the class's main() method
Compiling and executing
MLS Playoff Odds
World Football Rankings & World Cup Odds
NCAA Basketball Ratings
Hearts
Personal Home Pages
David's Company

Launching the Java Program

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.