Jump to content
Excelsior Forums
threadhead

Accessing String arguments in Java From C Main via JNI Invocation API Fails

Recommended Posts

Hi. I ran your sample, specifically from C:\JET\samples\Invocation\cMain and all was well. Then I decided to pass some arguments and make sure that worked. Unfortunately, it did not. I modified your test code so that 'foo' accepts 3 strings. Every thing compiles nicely and I inspect the jobjectArray before passing it in arg#4 of the JNI API method CallStaticVoidMethod(). The results? The strings are all null inside Java (Null Pointer Exception):

C:\JET\samples\Invocation\cMainModified>test_args

MyDll.dll loaded

JET RT initialized

Class MyClassInDll found

object of 'MyClassInDll' created

instance method 'ifoo' called

Allocated String Array

SetObjectArrayElements

        Allocated value=> arg one

        Allocated value=> arg two

        Allocated value=> arg three

In Java: static method 'foo' called

Exception in thread "main" java.lang.NullPointerException

        at java.lang.String.charAt(String.java:576)

        at MyClassInDll.foo(MyClassInDll.java:16)

===============

Is it my code or yours? My additions to the sample are in red.

Here is the Java code:

public class MyClassInDll {

    public MyClassInDll() {

        System.out.println("object of 'MyClassInDll' created");

    }

    public void ifoo() {

        System.out.println("instance method 'ifoo' called");

    }

    public static void foo( String one, String two, String three ) {

       

System.out.println("In Java: static method 'foo' called");

System.out.println("ARG one charAt(0) => " + one.charAt(0) );

System.out.println("ARG two => " + two);

System.out.println("ARG three => " + three );

    }

}

Here is the modified c code:

#include <jni.h>

#ifdef __linux__

    #include <dlfcn.h>

    #define HANDLE void*

    #define LoadLibrary(x) dlopen(x, RTLD_LAZY)

    #define GetProcAddress(x,y) dlsym(x,y)

#else

    #include <windows.h>

#endif

char dllname[] =

    #ifdef __linux__

        "./libMyDll.so";

    #else

        "MyDll.dll";

    #endif

/*

* Load dll.

*/

HANDLE loadDll(char* name)

{

    HANDLE hDll = LoadLibrary (name);

    if (!hDll) {

        printf ("Unable to load %s\n", name);

        exit(1);

    }

    printf ("%s loaded\n", name);

    return hDll;

}

jint (JNICALL * JNI_GetDefaultJavaVMInitArgs_func) (void *args);

jint (JNICALL * JNI_CreateJavaVM_func) (JavaVM **pvm, void **penv, void *args);

/*

* Initialize JET run-time.

*/

void initJavaRT(HANDLE myDllHandle, JavaVM** pjvm, JNIEnv** penv)

{

    int            result;

    JavaVMInitArgs args;

    JNI_GetDefaultJavaVMInitArgs_func =

            (jint (JNICALL *) (void *args))

            GetProcAddress (myDllHandle, "JNI_GetDefaultJavaVMInitArgs");

    JNI_CreateJavaVM_func =

            (jint (JNICALL *) (JavaVM **pvm, void **penv, void *args))

            GetProcAddress (myDllHandle, "JNI_CreateJavaVM");

    if(!JNI_GetDefaultJavaVMInitArgs_func) {

        printf ("%s doesn't contain public JNI_GetDefaultJavaVMInitArgs\n", dllname);

        exit (1);

    }

    if(!JNI_CreateJavaVM_func) {

        printf ("%s doesn't contain public JNI_CreateJavaVM\n", dllname);

        exit (1);

    }

    memset (&args, 0, sizeof(args));

    args.version = JNI_VERSION_1_2;

    result = JNI_GetDefaultJavaVMInitArgs_func(&args);

    if (result != JNI_OK) {

        printf ("JNI_GetDefaultJavaVMInitArgs() failed with result %d\n", result);

        exit(1);

    }

 

    /*

    * NOTE: no JVM is actually created

    * this call to JNI_CreateJavaVM is intended for JET RT initialization

    */

    result = JNI_CreateJavaVM_func (pjvm, (void **)penv, &args);

    if (result != JNI_OK) {

        printf ("JNI_CreateJavaVM() failed with result %d\n", result);

        exit(1);

    }

    printf ("JET RT initialized\n");

}

/*

* Look for class.

*/

jclass lookForClass (JNIEnv* env, char* name)

{

    jclass clazz = (*env)->FindClass (env, name);

    if (!clazz) {

        printf("Unable to find class %s\n", name);

        exit(1);

    }

    printf ("Class %s found\n", name);

    return clazz;

}

/*

* Create an object and invoke the "ifoo" instance method

*/

void invokeInstanceMethod (JNIEnv* env, jclass myClassInDll)

{

    jmethodID MID_init, MID_ifoo;

    jobject obj;

    MID_init = (*env)->GetMethodID (env, myClassInDll, "<init>", "()V");

    if (!MID_init) {

        printf("Error: MyClassInDll.<init>() not found\n");

        return;

    }

    obj = (*env)->NewObject(env, myClassInDll, MID_init);

    if (!obj) {

        printf("Error: failed to allocate an object\n");

        return;

    }

    MID_ifoo = (*env)->GetMethodID (env, myClassInDll, "ifoo", "()V");

    if (!MID_ifoo) {

        printf("Error: MyClassInDll.ifoo() not found\n");

        return;

    }

    (*env)->CallVoidMethod (env, obj, MID_ifoo);

}

/*

* Invoke the "foo" static method

*/

void invokeStaticMethod( JNIEnv* env, jclass myClassInDll )

{

    jmethodID MID_foo;

   

    jobjectArray jObjArray;

    jstring jArgOne;

    jstring jArgTwo;

    jstring jArgThree;

    jstring jStrVar;

    int i;

    MID_foo = (*env)->GetStaticMethodID(env, myClassInDll, "foo", "(Ljava/lang/String;Ljava/lang/String;Ljava/lang/String;)V" );

    if (!MID_foo) {

        printf("\nError: MyClassInDll.foo() not found\n");

        return;

    }

   

jObjArray = (*env)->NewObjectArray( env, 3, (*env)->FindClass( env, "java/lang/String" ), NULL ); 

if( NULL == jObjArray )

{

printf( "\nError: Could not allocate NewObjectArray." );

return ;

}

printf( "\nAllocated String Array\n" );   

jArgOne = (*env)->NewStringUTF( env, "arg one" ); 

if( NULL == jArgOne )

{

printf( "\nError: FAILED to create jArgOne." );

                  return ;

}

(*env)->SetObjectArrayElement( env, jObjArray, 0, jArgOne );

jArgTwo = (*env)->NewStringUTF( env, "arg two" ); 

if( NULL == jArgOne )

{

printf( "\nError: FAILED to create jArgTwo." );

        return ;

}

(*env)->SetObjectArrayElement( env, jObjArray, 1, jArgTwo );

jArgThree = (*env)->NewStringUTF( env, "arg three" ); 

if( NULL == jArgThree )

{

printf( "\nError: FAILED to create jArgThree." );

        return ;

}

(*env)->SetObjectArrayElement( env, jObjArray, 2, jArgThree );

printf( "SetObjectArrayElements\n" );

for( i = 0; i < 3; i++ )

{

jStrVar = (*env)->GetObjectArrayElement( env, jObjArray, i );

printf( "\tAllocated value=> %s\n", (*env)->GetStringUTFChars( env, jStrVar, JNI_FALSE ) );

}

    (*env)->CallStaticVoidMethod( env, myClassInDll, MID_foo, jObjArray );

if( (*env)->ExceptionOccurred( env ) )

{

(*env)->ExceptionDescribe( env );

}

}

void finalizeJavaRT (JavaVM* jvm)

{

    (*jvm)->DestroyJavaVM (jvm);

}

int main()

{

    HANDLE myDllHandle;

    JNIEnv *env;

    JavaVM *jvm;

    jclass  myClassInDll;

    /*

    * First of all, load required component.

    * By the time of JET initialization, all components should be loaded.

    */

    myDllHandle = loadDll (dllname);

    /*

    * Initialize JET run-time.

    * The handle of loaded component is used to retrieve Invocation API.

    */

    initJavaRT (myDllHandle, &jvm, &env);

    /*

    * Look for class.

    */

    myClassInDll = lookForClass(env, "MyClassInDll");

    /*

    * Create an object and invoke instance method.

    */

    invokeInstanceMethod(env, myClassInDll);

    /*

    * Invoke static method.

    */

    invokeStaticMethod(env, myClassInDll);

    /*

    * Finalize JET run-time.

    */

    finalizeJavaRT (jvm);

    return 0;

}

Share this post


Link to post
Share on other sites

In short, you have misused JNI. Just check jni.h or the JNI documentation.

Instead of

    (*env)->CallStaticVoidMethod( env, myClassInDll, MID_foo, jObjArray );

you should have to wrirte

    (*env)->CallStaticVoidMethod( env, myClassInDll, MID_foo, jArgOne, jArgTwo, jArgThree );

as the function proto contains "..." (variable-length argument list).

Another flavor of the function called "CallStaticVoidMethodA" takes an array of arguments but that should be array of the "jvalue" union not a Java array.

-------------

What you wrote resembles a usage pattern of Reflection API that requires Object[] to pass arguments to a method.

Share this post


Link to post
Share on other sites

Create an account or sign in to comment

You need to be a member in order to leave a comment

Create an account

Sign up for a new account in our community. It's easy!

Register a new account

Sign in

Already have an account? Sign in here.

Sign In Now

×