[JNI] globeler KeyboardHook

darkeye2

Bekanntes Mitglied
Hallo,

hab vor längerer zeit mal nach einer möglichkeit gesucht, aus einem inaktieven fenster heraus tastatureingaben abzufangen, um bei einer besimmten tastenkombi das fenster zu maximieren und den focus drauf zu setzen.

Nun hab ich zwar nix wirklich sinnvolles gefunden, aber da das prog nur auf meinen xp rechner laufen soll, hab ich mich dann entschlossen das beste zu nehmen, was ich gefunden habe:
Java – Global (low level) Keyboard / Mouse Hook – JNI kSquared.de – Blog
Nun lässt sich bei mir der c(++) code nicht kompilieren, obwohl ich eigentlich alles was wichtig war abgändert habe, gibt er komische meldungen aus, bei dennen ich den fehler einfach nicht finde:

abgeänderte header datei der java classe (KeyControll.h)
Code:
#include <jni.h>

/* Header for class KeyboardHook */
#ifndef _Included_natives_KeyboardHook
#define _Included_natives_KeyboardHook
#ifdef __cplusplus
extern "C" {
#endif

/*
 * Class:     KeyboardHook
 * Method:    registerHook
 * Signature: (LGlobalEventListener;)V
 */
JNIEXPORT void JNICALL Java_natives_KeyboardHook_registerHook(JNIEnv *,jobject thisObj,jobject listenerObj);
/*
 * Class:     KeyboardHook
 * Method:    unregisterHook
 * Signature: ()V
 */
JNIEXPORT void JNICALL Java_natives_KeyboardHook_unregisterHook(JNIEnv *env,jobject thisObj);

#ifdef __cplusplus
}
#endif
#endif

JKHook.dll (methodennamen angepasst)
Code:
#include <windows.h>
#include <jni.h>
#include "KeyControll.h"

#ifdef DEBUG
#define DEBUG_PRINT(x) printf x
#else
#define DEBUG_PRINT(x) do {} while (0)
#endif 

HINSTANCE hInst = NULL;

JavaVM * jvm = NULL;
DWORD hookThreadId = 0;

jobject keyboardHookObject = NULL;
jobject globalKeyListenerObject = NULL;
jmethodID processKeyMethod = NULL;

//extern "C"
BOOL APIENTRY DllMain(HINSTANCE _hInst,DWORD reason,LPVOID reserved)  {
    switch(reason) {
        case DLL_PROCESS_ATTACH:
            DEBUG_PRINT(("NATIVE: DllMain - DLL_PROCESS_ATTACH.n"));
            hInst = _hInst;
            break;
        default:
            break;
    }
    return TRUE;
}

LRESULT CALLBACK LowLevelKeyboardProc(int nCode,WPARAM wParam,LPARAM lParam)  {
    JNIEnv* env;
    KBDLLHOOKSTRUCT* p = (KBDLLHOOKSTRUCT *)lParam;
    if(jvm->AttachCurrentThread((void **)&env, NULL)>=0) {
        jboolean transitionState = (jboolean)FALSE;
        switch(wParam)  {
            case WM_KEYDOWN: case WM_SYSKEYDOWN:
                transitionState = (jboolean)TRUE;
            case WM_KEYUP: case WM_SYSKEYUP:
                env->CallVoidMethod(keyboardHookObject,processKeyMethod,transitionState,p->vkCode,globalKeyListenerObject);
                break;
            default:
                break;
        }
    }   else DEBUG_PRINT(("NATIVE: LowLevelKeyboardProc - Error on the attach current thread.n"));
    return CallNextHookEx(NULL,nCode,wParam,lParam);
}

JNIEXPORT void JNICALL  Java_natives_KeyboardHook_registerHook(JNIEnv * env,jobject obj,jobject _globalKeyListenerObject) {
    DEBUG_PRINT(("NATIVE: Java_de_ksquared_system_keyboard_KeyboardHook_registerHook - Hooking started!n"));

    HHOOK hookHandle = SetWindowsHookEx(WH_KEYBOARD_LL,LowLevelKeyboardProc,hInst,0);
    globalKeyListenerObject = _globalKeyListenerObject;

    if(hookHandle==NULL) {
        DEBUG_PRINT(("NATIVE: Java_de_ksquared_system_keyboard_KeyboardHook_registerHook - Hook failed!n"));
        return;
    } else DEBUG_PRINT(("NATIVE: Java_de_ksquared_system_keyboard_KeyboardHook_registerHook - Hook successfuln"));

    keyboardHookObject = env->NewGlobalRef(obj);
    jclass cls = env->GetObjectClass(keyboardHookObject);
    processKeyMethod = env->GetMethodID(cls,"processKey","(ZILnative/GlobalKeyListener;)V");

    env->GetJavaVM(&jvm);
    hookThreadId = GetCurrentThreadId();

    MSG message;
    while(GetMessage(&message,NULL,0,0)) {
        TranslateMessage(&message);
        DispatchMessage(&message);
    }

    DEBUG_PRINT(((!UnhookWindowsHookEx(hookHandle))?("NATIVE: Java_de_ksquared_system_keyboard_KeyboardHook_registerHook - Unhook failedn")
                                                    :"NATIVE: Java_de_ksquared_system_keyboard_KeyboardHook_registerHook - Unhook successfuln"));
}

JNIEXPORT void JNICALL Java_natives_KeyboardHook_unregisterHook(JNIEnv *env,jobject object) {
    if(hookThreadId==0) return;
    DEBUG_PRINT(("NATIVE: Java_de_ksquared_system_keyboard_KeyboardHook_unregisterHook - call PostThreadMessage.n"));
    PostThreadMessage(hookThreadId,WM_QUIT,0,0L);
}

da kriege ich dann eine ganze liste mit fehlern:
Code:
D:\....\C\KHook\JKHook.c(36): error #2112: Left operand of '->' has incompatible type 'JavaVM *'.
D:\....\C\KHook\JKHook.c(36): error #2068: Expected a function but found 'JavaVM *'.
D:\....\C\KHook\JKHook.c(36): error #2168: Operands of '>=' have incompatible types 'void' and 'int'.
D:\....\C\KHook\JKHook.c(42): error #2112: Left operand of '->' has incompatible type 'JNIEnv *'.
D:\....\C\KHook\JKHook.c(42): error #2068: Expected a function but found 'JNIEnv *'.
D:\....\C\KHook\JKHook.c(62): error #2112: Left operand of '->' has incompatible type 'JNIEnv *'.
D:\....\C\KHook\JKHook.c(62): error #2068: Expected a function but found 'JNIEnv *'.
D:\....\C\KHook\JKHook.c(62): error #2168: Operands of '=' have incompatible types 'jcharArray' and 'void'.
D:\....\C\KHook\JKHook.c(63): error #2112: Left operand of '->' has incompatible type 'JNIEnv *'.
D:\....\C\KHook\JKHook.c(63): error #2068: Expected a function but found 'JNIEnv *'.
D:\....\C\KHook\JKHook.c(63): error #2168: Operands of '=' have incompatible types 'jcharArray' and 'void'.
D:\....\C\KHook\JKHook.c(64): error #2112: Left operand of '->' has incompatible type 'JNIEnv *'.
D:\....\C\KHook\JKHook.c(64): error #2068: Expected a function but found 'JNIEnv *'.
D:\....\C\KHook\JKHook.c(64): error #2168: Operands of '=' have incompatible types 'jmethodID' and 'void'.
D:\....\C\KHook\JKHook.c(66): error #2112: Left operand of '->' has incompatible type 'JNIEnv *'.
D:\....\C\KHook\JKHook.c(66): error #2068: Expected a function but found 'JNIEnv *'.
*** Error code: 1 ***
 
Zuletzt bearbeitet:

Marco13

Top Contributor
Beim ersten Draufschauen: Das ist eine C-Datei, die C++-Features verwendet - bist du sicher, dass sie mit einem C++-Compiler compiliert wird? Ggf. mal nach C++ umbenennen (sollte z.B. bei VS schon reichen...)
 

darkeye2

Bekanntes Mitglied
ok, blöder fehler, du hattest natürlich recht.

Hab es nun compiliert, gab allerdings einen fehler, aber die dll ist trotzdem da.
Fehler: C:\Dev-Cpp\c++ utput-lib: No such file or directory.
sagt mir jetzt absolut gar nix, aber ich dachte, da es die dll jetzt gibt, ist es net so wichtig, aber wenn ich den code jetzt auführe, (in eclipse) läuft das prog solange ich keine taste drücke, und sobald ein tastendruck erfolgt gibt mir eclipse folgendes aus:
(mir ist übrigens auch klar, dass es bedetet, dass das prog abstürzt, mit einem kritischen fehler ich will nur den fehler finden)
Code:
# A fatal error has been detected by the Java Runtime Environment:
#
#  EXCEPTION_ACCESS_VIOLATION (0xc0000005) at pc=0x6d8888ca, pid=4812, tid=3124
#
# JRE version: 6.0_26-b03
# Java VM: Java HotSpot(TM) Client VM (20.1-b02 mixed mode, sharing windows-x86 )
# Problematic frame:
# V  [jvm.dll+0x988ca]
#
# An error report file with more information is saved as:
# D:\...\hs_err_pid4812.log

in der entsprechenden datei steht zwar viel drin, kann damit leider nur nix anfangen, da steht, war im stack war, was im heap, ... aber kein grund, wieso es abgestürzt ist.

Weiß jemand, woran das liegen könnte?
 

Marco13

Top Contributor
Da passiert erstmal zu viel Windows-spezifisches, von dem ich keine Ahnung habe, deswegen ist das zumindest für mich schwer einzuschätzen. Die JNI-Funktionen (AttachCurrentThread & Co) haben auch einiges Fehlerpotential, eigentlich sollte JNI-Code zum größten Teil aus
Code:
if (env->ExceptionCheck())
{
    printf("Uh-hu...\n");
    return; // So schnell wie möglich zurück zu Java
}
Blöcken bestehen.

In ther hs_err-Datei sollte man zumindest erkennen, wo der Fehler aufgetreten ist, da steht irgendwas von "Current Stack Frames" oder so.

Ansonsten vielleicht erstmal GANZ elementar, in der "obersten" nativen Methode NUR ein
printf("Hello World\n");
oder so...

Würde mich übrigens fast wundern, wenn es sowas nicht schon als fertige Bibliothek gäbe....
 

darkeye2

Bekanntes Mitglied
also hab mir jetzt überral alles ausgeben lassen, was da nur geht, und innerhalb der dll wird jedes stück code ausgeführt, allerdings wird im java code beim tastendruck keine entsprechende methode aufgerufen, also müsste der fehler in der methode hier liegen:

Code:
LRESULT CALLBACK LowLevelKeyboardProc(int nCode,WPARAM wParam,LPARAM lParam)  {
    JNIEnv* env;
    KBDLLHOOKSTRUCT* p = (KBDLLHOOKSTRUCT *)lParam;
    if(jvm->AttachCurrentThread((void **)&env, NULL)>=0) {
        jboolean transitionState = (jboolean)FALSE;
        switch(wParam)  {
            case WM_KEYDOWN: case WM_SYSKEYDOWN:
                transitionState = (jboolean)TRUE;
            case WM_KEYUP: case WM_SYSKEYUP:
                env->CallVoidMethod(keyboardHookObject,processKeyMethod,transitionState,p->vkCode,globalKeyListenerObject);
                break;
            default:
                break;
        }
    }   else DEBUG_PRINT(("NATIVE: LowLevelKeyboardProc - Error on the attach current thread.n"));
    return CallNextHookEx(NULL,nCode,wParam,lParam);
}

um genau zu sein, in der zeile hier:
Code:
env->CallVoidMethod(keyboardHookObject,processKeyMethod,transitionState,p->vkCode,globalKeyListenerObject);
ich glaube es könnte an der processKeyMethod liegen, das vllt die id nicht richtig ermittelt wird oder so:
Code:
processKeyMethod = env->GetMethodID(cls,"processKey","(ZILnative/GlobalKeyListener;)V");


die hs_err-Datei:
Code:
#
# A fatal error has been detected by the Java Runtime Environment:
#
#  EXCEPTION_ACCESS_VIOLATION (0xc0000005) at pc=0x6d8888ca, pid=5064, tid=4156
#
# JRE version: 6.0_26-b03
# Java VM: Java HotSpot(TM) Client VM (20.1-b02 mixed mode, sharing windows-x86 )
# Problematic frame:
# V  [jvm.dll+0x988ca]
#
# If you would like to submit a bug report, please visit:
#   http://java.sun.com/webapps/bugreport/crash.jsp
#

---------------  T H R E A D  ---------------

Current thread (0x03f56000):  JavaThread "Thread-0" daemon [_thread_in_vm, id=4156, stack(0x04440000,0x04490000)]

siginfo: ExceptionCode=0xc0000005, reading address 0x00000000

Registers:
EAX=0x00000000, EBX=0x03f56128, ECX=0x03f56000, EDX=0x0000050a
ESP=0x0448fb48, EBP=0x0448fb90, ESI=0x03f56000, EDI=0x00000000
EIP=0x6d8888ca, EFLAGS=0x00010246

Top of Stack: (sp=0x0448fb48)
0x0448fb48:   0448fc68 000d0000 7ef98800 0448fc68
0x0448fb58:   0448fb64 77bcb6ad 77bf3b50 0448fb70
0x0448fb68:   77bccac6 00000011 0448fbb0 77bd2825
0x0448fb78:   00000001 77bf1cc8 03f56000 03f34644
0x0448fb88:   0000050a 6da173b4 0448fbb0 61581afc
0x0448fb98:   03f56128 03f2fc48 00000000 0448fbc4
0x0448fba8:   77ba4770 0448fbc4 0448fbe0 615812f8
0x0448fbb8:   03f56128 03f2fc48 00000000 00000001 

Instructions: (pc=0x6d8888ca)
0x6d8888aa:   ff 8b 46 04 83 c4 08 85 c0 89 75 f0 c7 45 f4 00
0x6d8888ba:   00 00 00 74 08 8d 4d f0 e8 e9 8d 09 00 8b 7d 10
0x6d8888ca:   8b 07 c7 45 e0 0e 00 00 00 8b 48 08 0f b7 51 2a
0x6d8888da:   8b 40 0c 8b 4c 90 28 51 56 8d 4d c4 e8 85 77 07 


Register to memory mapping:

EAX=0x00000000 is an unknown value
EBX=0x03f56128 is an unknown value
ECX=0x03f56000 is a thread
EDX=0x0000050a is an unknown value
ESP=0x0448fb48 is pointing into the stack for thread: 0x03f56000
EBP=0x0448fb90 is pointing into the stack for thread: 0x03f56000
ESI=0x03f56000 is a thread
EDI=0x00000000 is an unknown value


Stack: [0x04440000,0x04490000],  sp=0x0448fb48,  free space=318k
Native frames: (J=compiled Java code, j=interpreted, Vv=VM code, C=native code)
V  [jvm.dll+0x988ca]
C  [JKHook.dll+0x1afc]  Java_natives_KeyboardHook_unregisterHook+0x686
C  [JKHook.dll+0x12f8]
C  [USER32.dll+0x37f5a]  GetDlgItemTextW+0xd1
C  [USER32.dll+0x5ce04]  DdeConnectList+0xad1
C  [ntdll.dll+0x1ea0e]  KiUserCallbackDispatcher+0x2e
C  [JKHook.dll+0x144d]  Java_natives_KeyboardHook_registerHook+0x121
j  natives.KeyboardHook.registerHook(Lnatives/GlobalKeyListener;)V+0
j  natives.PoolHook.run()V+19
v  ~StubRoutines::call_stub
V  [jvm.dll+0xfae4b]
V  [jvm.dll+0x18c531]
V  [jvm.dll+0xfaff1]
V  [jvm.dll+0xfb04b]
V  [jvm.dll+0xb56b9]
V  [jvm.dll+0x1190d4]
V  [jvm.dll+0x1411ac]
C  [msvcr71.dll+0x9565]  endthreadex+0xa0
C  [kernel32.dll+0x1fe21]  FlsSetValue+0x136

Java frames: (J=compiled Java code, j=interpreted, Vv=VM code)
j  natives.KeyboardHook.registerHook(Lnatives/GlobalKeyListener;)V+0
j  natives.PoolHook.run()V+19
v  ~StubRoutines::call_stub

---------------  P R O C E S S  ---------------

Java Threads: ( => current thread )
=>0x03f56000 JavaThread "Thread-0" daemon [_thread_in_vm, id=4156, stack(0x04440000,0x04490000)]
  0x03f30400 JavaThread "Low Memory Detector" daemon [_thread_blocked, id=4564, stack(0x04320000,0x04370000)]
  0x03f21800 JavaThread "C1 CompilerThread0" daemon [_thread_blocked, id=5044, stack(0x04290000,0x042e0000)]
  0x03f20000 JavaThread "Attach Listener" daemon [_thread_blocked, id=4044, stack(0x04200000,0x04250000)]
  0x03f1ec00 JavaThread "Signal Dispatcher" daemon [_thread_blocked, id=1492, stack(0x04170000,0x041c0000)]
  0x03f18000 JavaThread "Finalizer" daemon [_thread_blocked, id=4792, stack(0x040e0000,0x04130000)]
  0x03f16800 JavaThread "Reference Handler" daemon [_thread_blocked, id=4604, stack(0x04050000,0x040a0000)]
  0x01c16800 JavaThread "main" [_thread_blocked, id=2908, stack(0x01c20000,0x01c70000)]

Other Threads:
  0x03eda400 VMThread [stack: 0x03fc0000,0x04010000] [id=4952]
  0x03f32400 WatcherThread [stack: 0x043b0000,0x04400000] [id=3976]

VM state:not at safepoint (normal execution)

VM Mutex/Monitor currently owned by a thread: None

Heap
 def new generation   total 4928K, used 369K [0x24020000, 0x24570000, 0x29570000)
  eden space 4416K,   8% used [0x24020000, 0x2407c6d8, 0x24470000)
  from space 512K,   0% used [0x24470000, 0x24470000, 0x244f0000)
  to   space 512K,   0% used [0x244f0000, 0x244f0000, 0x24570000)
 tenured generation   total 10944K, used 0K [0x29570000, 0x2a020000, 0x34020000)
   the space 10944K,   0% used [0x29570000, 0x29570000, 0x29570200, 0x2a020000)
 compacting perm gen  total 12288K, used 45K [0x34020000, 0x34c20000, 0x38020000)
   the space 12288K,   0% used [0x34020000, 0x3402b4e8, 0x3402b600, 0x34c20000)
    ro space 10240K,  51% used [0x38020000, 0x3854dff8, 0x3854e000, 0x38a20000)
    rw space 12288K,  55% used [0x38a20000, 0x390bc208, 0x390bc400, 0x39620000)

Code Cache  [0x01ce0000, 0x01d50000, 0x03ce0000)
 total_blobs=126 nmethods=5 adapters=58 free_code_cache=33104640 largest_free_block=0
 

Marco13

Top Contributor
@faetzminator: Stimmt, da war doch was :oops:

@Topic: Wie schon angedeutet sollte der überwiegende Teil des JNI-Codes aus Fehlerbehandlungen bestehen. Das sollte kein Witz und keine Übertreibung sein. Wenn man das RICHTIG machen will, kommt man aus den Fehlerchecks kaum noch raus... Tendenziell halten sich die Leute da nicht dran, und ob man wirklich damit rechnen sollte, dass man in der Klasse java.lang.Object die Methode 'hashCode' NICHT findet, weiß ich nicht, aber zumindest bei eigenen Methoden sollte man bei sowas wie
processKeyMethod = env->GetMethodID(cls,"processKey","(ZILnative/GlobalKeyListener;)V");
zumindest am Anfang, während es noch im entstehen ist (und eigentlich auch danach noch) sowas schreiben wie
Code:
if (processKeyMethod == NULL)
{
    printf("Wah...\n");
    return; // So schnell wie möglich zurück nach Java - vielleicht sogar vorher einen NoSuchMethodError werfen...
}
 

darkeye2

Bekanntes Mitglied
ok, danke an euch beide, dank den ganzen ausgaben hab ich den fehler jetzt doch gefunden, war an einer völlig anderen stelle und war "blos" ein schreibfehler (buchstabe vergessen).
Die ausgabe der gedrückten Tasten kommt zwar immer noch nicht, aber immerhin kommt der keycode bis zum java teil, und da werde ich den fehler auch noch finden (gibt keine fehlermeldung oder so, aber sollte trotzdem recht leicht zu finden sein).

Sollte ich das jetzt doch nicht hinkriegen, werde ich den code von der angegebenen seite verwenden.

Vielen dank für eure Hilfe.
 

Neue Themen


Oben