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- 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
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
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?