5

我正在尝试从代码中调用 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"));

编辑:

完整代码版本 1

完整代码版本 2

4

3 回答 3

4

JVM 正在崩溃,因为JNIEnv使用的是无效的。代码还有其他问题。

Sun JNI 文档提供了关于线程的非常好的信息。

这里有一些显而易见的部分:

在您的代码中创建一个JNI_OnLoad函数。加载库时将调用它。然后缓存JavaVM指针,因为这在线程之间是有效的。另一种方法是调用函数(*env)->GetJavaVMinitializeJNIVars但我更喜欢第一个。

在你的你可以通过调用initializeJNIVars保存参考。objObj = (*env)->NewGlobalRef(obj)

LowLevelKeyboardProc你将不得不得到env指针:

AttachCurrentThread(JavaVM *jvm, JNIEnv &env, NULL);


编辑

好的,这是您应该添加以使其正常工作的代码,我自己尝试过并且可以正常工作。注意:我没有分析你的代码实际上在做什么,所以我只是做了一些修复让它工作。

在其他全局变量中添加这些变量:

static JavaVM *javaVM = NULL;
static jmethodID callbackMethod = NULL;
static jobject callbackObject = NULL;

您可以删除您的cls、和 变量mid,并改用我的。EnvObj

创建缓存 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'd。

...
(*env)->DeleteGlobalRef(env, callbackObject);

就是这样。

于 2012-06-05T11:47:48.553 回答
3

我相信您不能调用带有 String 参数的 java 方法并将其传递给char*. 您应该先调用NewStringUTF

于 2012-06-05T10:50:39.737 回答
0

我认为这是由于您的操作系统上启用了 UAC 功能。这是 Java 6 的一个错误。阅读内容以供进一步参考。

我这样说的原因是因为转义键的事件被正确触发,并且问题只有在对 java 方法的调用完成后才开始。

于 2012-06-05T10:47:51.207 回答