JNI "env->GetStaticMethodID()" crashed program
I'm trying to call a Java function from C++. This is my code so far:
#include <jni.h>
typedef struct JavaVMCreationResult {
JavaVM* jvm;
JNIEnv* env;
} JVMCreationResult;
JVMCreationResult* CreateJavaVM() {
JavaVM* jvm;
JNIEnv* env;
JavaVMInitArgs args;
JavaVMOption opts[1];
opts[0].optionString = "-Djava.class.path=C:\\MyJavaClasses";
args.version = JNI_VERSION_1_6;
args.nOptions = 1;
args.options 开发者_如何学JAVA= opts;
args.ignoreUnrecognized = JNI_TRUE;
JNI_GetDefaultJavaVMInitArgs(&args);
JNI_CreateJavaVM(&jvm, (void **) &env, &args);
JavaVMCreationResult* cres;
cres->jvm = jvm;
cres->env = env;
return cres;
}
int main() {
JVMCreationResult* cres = CreateJavaVM();
JavaVM* jvm = cres->jvm;
JNIEnv* env = cres->env;
jclass cls = env->FindClass("Main");
jmethodID mid = env->GetStaticMethodID(cls, "main", "([Ljava/lang/String;)V"); // the evil line
}
I'm using Code::Blocks with MinGW GCC on Windows 7.
The last line in the main() function crashes the Program, but the compiler does not complain about anything. (Commenting out thejmethodID mid = env->GetSta...
line makes the program "not crashing")
I have uses javap -s Main
to obtain the right method signature, also the class is a valid Java class.
Can you tell me why the Program crashes ? This example is just shown everywhere on the Internet but it doesn't work for me. :(
This is the Java class:
public class Main {
public static void main(String[] args) {
System.out.println("This is from Java !");
}
}
SOLUTION
I wouldn't have thought it, it seems unlogic to me that the program wasn't crashing earlier when the struct wasn't initialized. But this was really the issue.
This is the full and working code !#include <jni.h>
#ifndef null
#define null NULL
#endif
typedef struct JavaVMCreationResult {
JavaVM* jvm;
JNIEnv* env;
} JVMCreationResult;
JVMCreationResult* CreateJavaVM() {
JavaVM* jvm;
JNIEnv* env;
JavaVMInitArgs args;
JavaVMOption opts[1];
opts[0].optionString = "-Djava.class.path=C:\\Users\\Claudia\\Desktop";
args.version = JNI_VERSION_1_6;
args.nOptions = 1;
args.options = opts;
args.ignoreUnrecognized = JNI_TRUE;
JNI_GetDefaultJavaVMInitArgs(&args);
JNI_CreateJavaVM(&jvm, (void **) &env, &args);
JVMCreationResult* cres = new JVMCreationResult();
cres->jvm = jvm;
cres->env = env;
return cres;
}
int main() {
JVMCreationResult* cres = CreateJavaVM();
JavaVM* jvm = cres->jvm;
JNIEnv* env = cres->env;
jclass cls = env->FindClass("Main");
if (cls) {
printf("Yes !\n");
}
else {
printf("No !\n");
}
jmethodID mid = env->GetStaticMethodID(cls, "main", "([Ljava/lang/String;)V");
env->CallStaticVoidMethod(cls, mid);
printf("At end of Program.");
}
Your variable "cres" is a point in the CreateJavaVM call that is never initialized, so you're probably dereferencing a null or otherwise invalid pointer at that point.
One solution is to define cres (not a pointer to cres) in main, and pass a pointer to that to CreateJavaVM as a parameter, and then use the parameter inside CreateJavaVM to return the result.
Also it's a good idea to check that jvm and env get non-null values after the JNI_CreateJavaVM call, and that cls and mid are likewise non-null after the calls to FindClass and GetStaticMethodID, respectively
cls is probably invalid. I presume your program would have crashed earlier if 'cres' was null.
int main() {
JVMCreationResult* cres = CreateJavaVM();
if(!cres) return -1;
JavaVM* jvm = cres->jvm;
JNIEnv* env = cres->env;
jclass cls = env->FindClass("Main");
if(env->ExceptionCheck()) { // ClassNotFoundException ?
env->ExceptionDescribe();
env->ExceptionClear();
}
if(!cls) return -2; // this I think is your problem
jmethodID mid = env->GetStaticMethodID(cls, "main", "([Ljava/lang/String;)V"); // the evil line
}
Are you sure your classpath has been specified correctly ? Hopefully FindClass("Main") will find a default package class. Anyhow tell us the return value if your C/C++ main() now.
It is possible for the "JavaVM* jvm = cres->jvm;" to be optimized away, since "jvm" is never referenced and the statement "cres->jvm" has no side effects. Some commenters state it should crash on this, hmm yes maybe, if code was generated and then executed. But a decent compiler might see it is a no operation.
However the statement "JNIEnv* env = cres->env;" can not be optimized away, since the variable "env" is used later on. So we can only claim that if cres==0 then it would crash at or before this point in execution. Since "env" is used for FindClass() call then we know for sure that env!=0 and therefore cres!=0.
I would guess you have a Class Path setup issue, FindClass() is not finding your class at runtime which is causing "cls==0" to be true. Which is my answer here.
EDITED: I see what the others are claiming over 'cres' however that does not change my original diagnosis, but you still have a bug regarding 'cres', change the line to:
JavaVMCreationResult* cres = new JavaVMCreationResult;
I think you are lucky that cres is pointing somewhere (probably on the stack), you then copied the values into the local main() stack and used the values. But that doesn't make the technique correct as the initial memory that 'cres' is pointing to is random, so you are lucky that no crash occured but you did scribble on memory you should not have. By using the "cres = new JavaVMCreationResult;" this causes the pointer to be set to a known valid block of memory.
If you want compiler assistance with this problem (i.e. it should show up a warning) try with MinGW "-Wall" and "-O2" options during compile. It should warn about uninitialized use of the variable 'cres'.
精彩评论