I am trying to call a Java method from the code. C code listens to either Escape
, Shift
, Ctrl
key press, then it calls the Java method telling which key was pressed. Following are the snippets that play a role in this.
C Snippet:
mid = (*env)->GetMethodID(env,cls,"callBack","(Ljava/lang/String;)V");
Env = env;
if(called)
switch(param) {
case VK_CONTROL:
printf("Control pressed !\n");
(*Env)->CallVoidMethodA(Env,Obj,mid,"11"); // calling the java method
break;
case VK_SHIFT:
printf("Shift pressed !\n");
(*Env)->CallVoidMethodA(Env,Obj,mid,"10"); // calling the java method
break;
case VK_ESCAPE:
printf("Escape pressed !\n");
(*Env)->CallVoidMethodA(Env,Obj,mid,"1B"); // calling the java method
break;
default:
printf("The default case\n");
break;
}
Java Snippet:
public void callBack(String key) {
String x = KeyEvent.getKeyText(Integer.parseInt(key, 16));
System.out.println(x);
}
When I run the program and press the Escape
key I get this on the console:
Escape pressed !
#
# A fatal error has been detected by the Java Runtime Environment:
#
# EXCEPTION_ACCESS_VIOLATION (0xc0000005) at pc=0x5c8b809a, pid=7588, tid=8088
#
# JRE version: 7.0
# Java VM: Java HotSpot(TM) Client VM (20.0-b01 mixed mode, sharing windows-x86 )
# Problematic frame:
# V [jvm.dll+0x19809a]
#
# An error report file with more information is saved as:
# W:\UnderTest\NetbeansCurrent\KeyLoggerTester\build\classes\hs_err_pid7588.log
#
# If you would like to submit a bug report, please visit:
# http://java.sun.com/webapps/bugreport/crash.jsp
#
I know I am calling the Java function the wrong way, but I don't know where I am wrong. As from the output, it satisfies the case when I press the Escape
key and then an unexpected error occurs.
EDIT:
After the answer by mavroprovato I still get the same errors.
I edited this way:
(*Env)->CallVoidMethodA(Env,Obj,mid,(*Env)->NewStringUTF(Env,"1B"));
EDIT:
The JVM is crashing because the
JNIEnv
that is used is not a valid one. There are other issues with the code as well.The Sun JNI documentation is providing very good information regarding threads.
Here comes some parts that are obvious:
Create a
JNI_OnLoad
function in your code. It will be called when the library is loaded. Then cache theJavaVM
pointer because that is valid across threads. An alternative is to call(*env)->GetJavaVM
in theinitializeJNIVars
function but I prefer the first one.In your
initializeJNIVars
you can save theobj
reference by callingObj = (*env)->NewGlobalRef(obj)
.In the
LowLevelKeyboardProc
you will have to get theenv
pointer:AttachCurrentThread(JavaVM *jvm, JNIEnv &env, NULL);
Edit
OK, here are the code that you should add to get it working, I have tried it myself and it works. NB: I have not analyzed what your code is actually doing so I just did some fixes to get it working.
Add these variables among your other global variables:
You can remove your
cls
,mid
,Env
andObj
variables and use mine instead.Create the JNI_OnLoad method where you cache the JavaVM pointer:
Alter your
initializeJNIVars
to look like the following:And finally in your
LowLoevelKeyboardProc
code you will have to add the following:In your
unregisterWinHook
you should delete the global reference so that objects can be GC'd.And that's it.