passing JNIEnv to VB, then back to C++ (standard DLL)

I have 2 methods I expose in my C++ DLL to my VB app:

1. init_JVM( ) which I want to return a JNIEnv reference. This method is called once from my VB app.

2. convert_Image( ) which passes the JNIEnv object as a parameter back to the C++ DLL. This method is called many times from VB.

So, essentially, my VB code I have:

Declare Function init_JVM Lib "convert.dll" () As Object

Declare Sub convert_Image Lib "convert.dll" (ByRef lpJNIEnv As Any, ByVal strTiffSourcePath As String, ByVal strError As String)

Sub Main( )

Dim oEnv As Object

'*** Initialization - create JVM

Set oEnv = init_JVM()

For iNum = 0 to nCount

convert_Image(oEnv)

Next

End Sub

This is what my C++ looks like:

extern "C" JNIEXPORT JNIEnv *JNICALL init_JVM()

{

JNIEnv *env;

// code that creates JVM...

return env;

}

extern "C" JNIEXPORT void JNICALL convert_Image(JNIEnv *env)

{

jclass convertClass;

// BLOWS UP ON THIS CALL

convertClass = env->FindClass("TConvert");

// the rest of the code

}

So, TConvert, is a java class. It blows up with an "Unhandled exception" error the first time it hits that line. Everything used to worked well if I didn't pass the env (when everything was in 1 method). But I had to split into 2 methods so that in my VB, I want to be able to only create the JVM once and then call the convert_Image as many times as I need to.

Please help. I think this is just a matter of being able to pass the JNIEnv correctly. What should the object types be in the declare statements in VB?

Thanks in advance.

Ya-Tin

[1728 byte] By [yatingga] at [2007-10-3 1:20:42]
# 1

1- Don't pass JNIEnv * at all.

2- Use JVM * as a global in C++ module.

3- Use the global JVM * to retrieve JNIEnv * each time you need it in C++ functions.

Here it is://Global to be used in each C++ function call

JavaVM * jvm = NULL;

// Returns a value != 0 on error.

extern "C" JNIEXPORT jint JNICALL init_JVM()

{

JNIEnv * env;

JavaVMInitArgs vm_args;

...

return JNI_CreateJavaVM(&jvm, (void **)&env, &vm_args);

}

extern "C" JNIEXPORT void JNICALL convert_Image()

{

JNIEnv * env;

// This is the way to retrieve the env value

if (jvm->AttachCurrentThread((void **)&env, NULL) < 0)

{

// some error handling

return;

}

jclass convertClass;

convertClass = env->FindClass("TConvert");

...

}

// Also don't forget to call this at the end of your VB program:

extern "C" JNIEXPORT void JNICALL destroy_JVM()

{

if (jvm != NULL)

{

jvm->DetachCurrentThread();

jvm->DestroyJavaVM();

}

jvm = NULL;

}

In your VB code it sould be:

Sub Main( )

'*** Initialization - create JVM

If init_JVM() != 0 Then

'*** Some error handling

Exit Sub

End If

For iNum = 0 to nCount

convert_Image()

Next

'*** Destroy the JVM

destroy_JVM()

End Sub

Regards

jfbrierea at 2007-7-14 18:17:53 > top of Java-index,Java HotSpot Virtual Machine,Specifications...
# 2
Thank you so much for your response. It works great, EXCEPT, as soon as destroy_JVM is called, I can't create a JVM again or do anything else.Is that expected?Ya-Tin
yatingga at 2007-7-14 18:17:53 > top of Java-index,Java HotSpot Virtual Machine,Specifications...
# 3

You should call destroy_JVM only once when the VB program ends.

You should also call init_JVM only once or add a check at the beginning of the C++ function:extern "C" JNIEXPORT jint JNICALL init_JVM()

{

// check if the JVM is already created

if (jvm != NULL)

return 0;

JNIEnv * env;

JavaVMInitArgs vm_args;

...

return JNI_CreateJavaVM(&jvm, (void **)&env, &vm_args);

}

Regards

jfbrierea at 2007-7-14 18:17:53 > top of Java-index,Java HotSpot Virtual Machine,Specifications...
# 4

There is documentation that it is a known bug that JNI_CreateJavaVM cannot be called after DestroyJavaVM.

So, now I call JNI_GetCreatedJavaVMs to get any existing instances loaded if one has been created already.

// see if there are loaded VMs. If so, use it instead of creating a new one

res = JNI_GetCreatedJavaVMs(&vms, 1, &vmsCount);

// if no errors and 0 VMs are loaded

if(res == 0 && vmsCount == 0)

{

// Create the Java VM

res = JNI_CreateJavaVM(&jvm, (void **)&env, &vm_args);

}

But I see a lot of threads saying that DestroyJavaVM doesn't work and shouldn't be called. If that's the case, how would the JVM be unloaded?

yatingga at 2007-7-14 18:17:53 > top of Java-index,Java HotSpot Virtual Machine,Specifications...