我试图从代码中调用Java方法。 C代码监听Escape
,Shift
,Ctrl
按键,然后调用Java方法告知按下了哪个键。以下是在此中发挥作用的片段。
C片段:
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代码段:
public void callBack(String key) {
String x = KeyEvent.getKeyText(Integer.parseInt(key, 16));
System.out.println(x);
}
当我运行程序并按下Escape
键时,我会在控制台上看到它:
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
#
我知道我以错误的方式调用Java函数,但我不知道我错在哪里。从输出开始,它满足按Escape
键然后发生意外错误的情况。
修改
在mavroprovato回答之后,我仍然会遇到同样的错误。
我这样编辑:
(*Env)->CallVoidMethodA(Env,Obj,mid,(*Env)->NewStringUTF(Env,"1B"));
修改
答案 0 :(得分:4)
JVM崩溃,因为使用的JNIEnv
不是有效的。代码还有其他问题。
Sun JNI documentation提供了有关线程的非常好的信息。
以下是一些显而易见的部分:
在代码中创建JNI_OnLoad
函数。加载库时将调用它。然后缓存JavaVM
指针,因为它在线程间有效。另一种方法是在(*env)->GetJavaVM
函数中调用initializeJNIVars
,但我更喜欢第一个。
在您的initializeJNIVars
中,您可以通过致电obj
来保存Obj = (*env)->NewGlobalRef(obj)
引用。
在LowLevelKeyboardProc
中,您必须获得env
指针:
AttachCurrentThread(JavaVM *jvm, JNIEnv &env, NULL);
修改强>
好的,这是您应该添加的代码以使其正常工作,我已经自己尝试了它并且它有效。 NB :我还没有分析您的代码实际上在做什么,所以我只是做了一些修复以使其正常工作。
在您的其他全局变量中添加这些变量:
static JavaVM *javaVM = NULL;
static jmethodID callbackMethod = NULL;
static jobject callbackObject = NULL;
您可以删除cls
,mid
,Env
和Obj
个变量,然后使用我的变量。
创建用于缓存JavaVM指针的JNI_OnLoad方法:
JNIEXPORT jint JNICALL JNI_OnLoad(JavaVM *jvm, void *reserved) {
JNIEnv *env = 0;
if ((*jvm)->GetEnv(jvm, (void**)&env, JNI_VERSION_1_4)) {
return JNI_ERR;
}
javaVM = jvm;
return JNI_VERSION_1_4;
}
将initializeJNIVars
更改为以下内容:
void Java_keylogger_TestKeys_initializeJNIVars(JNIEnv *env, jobject obj) {
jclass cls = (*env)->GetObjectClass(env,obj);
callbackMethod = (*env)->GetMethodID(env, cls, "callBack", "(Ljava/lang/String;)V");
callbackObject = (*env)->NewGlobalRef(env, obj);
if(cls == NULL || callbackMethod == NULL) {
printf("One of them is null \n");
}
called = TRUE;
}
最后在您的LowLoevelKeyboardProc
代码中,您必须添加以下内容:
...
WPARAM param = kbhook->vkCode;
JNIEnv *env;
jint rs = (*javaVM)->AttachCurrentThread(javaVM, (void**)&env, NULL);
if (rs != JNI_OK) {
return NULL; // Or something appropriate...
}
...
case VK_ESCAPE:
printf("Escape pressed !\n");
jstring message = (*env)->NewStringUTF(env, "1B");
(*env)->CallVoidMethod(env, callbackObject, callbackMethod, message);
break;
...
在unregisterWinHook
中,您应该删除全局引用,以便可以对象进行GC。
...
(*env)->DeleteGlobalRef(env, callbackObject);
就是这样。
答案 1 :(得分:3)
我相信你不能调用一个带有String参数的java方法并将其传递给char*
。您应该先致电NewStringUTF。
答案 2 :(得分:0)
我认为这是由于您的操作系统启用了UAC功能。这是Java 6的一个错误。请阅读this以供进一步参考。
我这样说的原因是因为转义事件的事件被正确触发,只有在完成对java方法的调用后问题才会开始。