[JNI]用JAVA实现全局快捷键


基本思路:使用WIN API实现一个底层键盘钩子,监听按键事件。如果需要的快捷键被触发,则弹出相应的窗口。
找到了http://www.jotschi.de/?p=90

这个代码基本上实现了我的要求。可惜一运行老崩溃。

更改后的代码如下:

src_media_SysHook.h

/* DO NOT EDIT THIS FILE - it is machine generated */
#include <jni.h>
/* Header for class src_media_SysHook */

#ifndef _Included_src_media_SysHook
#define _Included_src_media_SysHook
#ifdef __cplusplus
extern "C" {
#endif

/*
 * Class:     src_media_SysHook
 * Method:    registerHook
 * Signature: (Lsrc/media/GlobalEventListener;)V
 */
JNIEXPORT void JNICALL Java_src_media_SysHook_registerHookl
  (JNIEnv *, jobject, jobject);

/*
 * Class:     src_media_SysHook
 * Method:    unRegisterHook
 * Signature: ()V
 */
JNIEXPORT void JNICALL Java_src_media_SysHook_unRegisterHookl
  (JNIEnv *, jobject);

#ifdef __cplusplus
}
#endif
#endif


dllmain.cpp

// dllmain.cpp : Defines the entry point for the DLL application.
#include "stdafx.h"


#include <windows.h>

HMODULE hInst = NULL;

BOOL APIENTRY DllMain( HMODULE hModule,
                       DWORD  ul_reason_for_call,
                       LPVOID lpReserved
					 )
{
	switch (ul_reason_for_call)
	{
	case DLL_PROCESS_ATTACH:
		
		hInst = hModule;
		break;
	case DLL_THREAD_ATTACH:
	case DLL_THREAD_DETACH:
	case DLL_PROCESS_DETACH:
		break;
	}
	return TRUE;
}


SysHook.cpp

// SysHook.cpp : Defines the exported functions for the DLL application.
//

#include "stdafx.h"
#include "src_media_SysHook.h"

#include <windows.h>
extern HMODULE hInst ;


JavaVM * jvm = NULL;
jobject hookObj_kb = NULL;

jobject g_kl = NULL;

jmethodID processKeyID_kb = NULL;

DWORD hookThreadId = 0;


LONG	g_mouseLocX = -1;	// x-location of mouse position
LONG	g_mouseLocY = -1;	// y-location of mouse position


extern "C" 

LRESULT CALLBACK LowLevelKeyboardProc(int nCode, WPARAM wParam, LPARAM lParam) 
{
	JNIEnv * env;
	KBDLLHOOKSTRUCT * p = (KBDLLHOOKSTRUCT *)lParam;

	if (jvm->AttachCurrentThread((void **)&env, NULL) >= 0) 
	{
		switch (wParam) 
		{
		case WM_KEYDOWN:
		case WM_SYSKEYDOWN:
			env->CallVoidMethod(hookObj_kb, processKeyID_kb, (jboolean)TRUE, (jint)(p->vkCode),g_kl);
			break;
		case WM_KEYUP:
		case WM_SYSKEYUP:
			env->CallVoidMethod(hookObj_kb, processKeyID_kb, (jboolean)FALSE, (jint)(p->vkCode),g_kl);
			break;
		default:
			break;
		}
	}
	else 
	{
		printf("C++: LowLevelKeyboardProc - Error on the attach current thread.\n");
	}

	return CallNextHookEx(NULL, nCode, wParam, lParam);
}

void MsgLoop() 
{
	MSG message;
	BOOL bRet;

	while ((bRet = GetMessage(&message, NULL, 0, 0))!=0) 
	{
		if(bRet == -1){

		}
		else
		{
			TranslateMessage(&message);
			DispatchMessage(&message);
		}
	}
}



JNIEXPORT void JNICALL Java_src_media_SysHook_registerHookl(JNIEnv * env, jobject obj,jobject kl) 
{
	HHOOK hookHandle_kb = SetWindowsHookEx(WH_KEYBOARD_LL, LowLevelKeyboardProc, hInst, 0);

	g_kl = kl;

	if (hookHandle_kb == NULL) 
	{
		printf("C++: Java_SysHook_registerKeyHook - Hook failed!\n");
		return;
	}
	else 
	{
		printf("C++: Java_SysHook_registerKeyHook - Hook successful\n");
	}

	hookObj_kb = env->NewGlobalRef(obj);
	jclass cls_kb = env->GetObjectClass(hookObj_kb);
	processKeyID_kb = env->GetMethodID(cls_kb,"processKey","(ZILsrc/media/GlobalEventListener;)V");

	env->GetJavaVM(&jvm);

	hookThreadId = GetCurrentThreadId();

	MsgLoop();

	if (!UnhookWindowsHookEx(hookHandle_kb))
	{
		printf("C++: Java_SysHook_registerKeyHook - Unhook failed\n");
	}
	else
	{
		printf("C++: Java_SysHook_registerKeyHook - Unhook successful\n");
	}


}

JNIEXPORT void JNICALL Java_src_media_SysHook_unRegisterHookl(JNIEnv *env, jobject object) 
{
	if (hookThreadId == 0)
		return;

	printf("C++: Java_SysHook_unRegisterKeyHook - call PostThreadMessage.\n");
	PostThreadMessage(hookThreadId, WM_QUIT, 0, 0L);
}

 

这个JNI有两个给C调用的接口。有两处调用C的地方。

原来的代码导致程序死掉的地方:
processKeyID_kb = env->GetMethodID(cls_kb, "processKey", "(ZILGlobalEventListener;)V");
这句代码是调用JAVA代码中的processKey这个函数,参数和返回值是(ZILGlobalEventListener;)V
这个根据我们代码的实际情况需要更改的。例如我改成了:
processKeyID_kb = env->GetMethodID(cls_kb,"processKey","(ZILsrc/media/GlobalEventListener;)V");
括号里的是参数,方法的签名,可以自动生成的。
使用cmd工具,切换到编译后的文件路径使用下面命令生成:
Javap –s 包名.类名

GlobalEventListener.java

 

package src.media;

/*
 * To change this template, choose Tools | Templates
 * and open the template in the editor.
 */



/**
 *
 * @author issuser
 */
public class GlobalEventListener
{
	PoolHook pt;
	public GlobalEventListener()
	{
		pt = new PoolHook(this);
		pt.start();
                
        }

	protected javax.swing.event.EventListenerList listenerList = new javax.swing.event.EventListenerList();

	public void addKeyboardEventListener(KeyboardEventListener listener)
	{
		listenerList.add( KeyboardEventListener.class, listener );
	}

	public void removeKeyboardEventListener(KeyboardEventListener listener)
	{
		listenerList.remove( KeyboardEventListener.class, listener );
	}


	void keyPressed(KeyboardEvent event)
	{
		Object[] listeners = listenerList.getListenerList();
		for ( int i = 0; i < listeners.length; i += 2 )
		{
			if ( listeners[ i ] == KeyboardEventListener.class )
			{
				( (KeyboardEventListener)listeners[i + 1] ).GlobalKeyPressed( event );
			}
		}
	}

	void keyReleased(KeyboardEvent event)
	{
		Object[] listeners = listenerList.getListenerList();
		for ( int i = 0; i < listeners.length; i += 2 )
		{
			if ( listeners[ i ] == KeyboardEventListener.class )
			{
				( (KeyboardEventListener)listeners[i + 1] ).GlobalKeyReleased( event );
			}
		}
	}
}


KeyCtrl.java

package src.media;


import javafx.reflect.FXClassType;
import javafx.reflect.FXLocal;
import javafx.reflect.FXLocal.Context;
import javafx.reflect.FXLocal.ObjectValue;
import src.SCInterface;
import src.media.GlobalEventListener;

/*
 * To change this template, choose Tools | Templates
 * and open the template in the editor.
 */

/**
 *
 * @author issuser
 */


public class KeyCtrl implements KeyboardEventListener{

    static GlobalEventListener gl;
    Boolean ctrlPress = false;
    Boolean altPress = false;
    public static void globalKeyEvent() throws Exception  {
        KeyCtrl inst = new KeyCtrl();
        gl = new GlobalEventListener();
	gl.addKeyboardEventListener(inst);
    }

    public void shortCutPress(String cmd){
            Context context = FXLocal.getContext();
            FXClassType instance = context.findClass("src.ShortCutInterface");
            ObjectValue obj = (ObjectValue)instance.newInstance();
            SCInterface ji = (SCInterface)obj.asObject();
            ji.shortCutInterface(cmd);
   }

    public String shortCut(Boolean press,long code){
        if(code == 162){
            if(press == true){
                ctrlPress = true;
            }else{
                ctrlPress = false;
            }
        }

        if(ctrlPress == true){
            if(code == 164){
                if(press == true){
                    altPress = true;
                }else{
                    altPress = false;
                }
            }
        }

        if(ctrlPress == true && altPress == true){
             if(code >=48 && code <= 57 ){
                 long num = code - 48;
                 return "ctrl alt " + num;
             }
             if(code >=96 && code <=105 ){
                 long num = code - 96;
                 return "ctrl alt " + num;
             }
             if(code == 191 ){
                 return "ctrl alt ?";
             }
             if(code == 114 ) {
                 return "ctrl alt F3";
             }
             if(code == 115 ){
                 return "ctrl alt F4";
             }
             if(code == 117 ){
                 return "ctrl alt F6";
             }
             if(code == 118 ){
                 return "ctrl alt F7";
             }
             if(code == 119 ){
                 return "ctrl alt F8";
             }
        }

        return "";
    }

    @Override
    public void GlobalKeyPressed(KeyboardEvent event) {
        //System.out.println( "Key Pressed: " + event.getVirtualKeyCode() );
        String cmd = shortCut(true,event.getVirtualKeyCode());
        if(!cmd.equals("")){
            shortCutPress(cmd);
        }
        throw new UnsupportedOperationException("Not supported yet.");
    }

    @Override
    public void GlobalKeyReleased(KeyboardEvent event) {
        shortCut(false,event.getVirtualKeyCode());
        throw new UnsupportedOperationException("Not supported yet.");
    }

}


KeyboardEventListener.java

package src.media;

/*
 * To change this template, choose Tools | Templates
 * and open the template in the editor.
 */
import java.util.*;

/**
 *
 * @author issuser
 */
public interface KeyboardEventListener extends EventListener {

    public void GlobalKeyPressed(KeyboardEvent event);
    public void GlobalKeyReleased(KeyboardEvent event);
}

class KeyboardEvent extends EventObject {

    private static final long serialVersionUID = 2341653211621224652L;
    boolean ts, ap, ek;
    int vk;

    public KeyboardEvent(Object source, boolean ts, int vk, boolean ap, boolean ek) {
        super(source);
        this.ts = ts;
        this.vk = vk;
        this.ap = ap;
        this.ek = ek;
    }

    public long getVirtualKeyCode() {
        return vk;
    }
}


SysHook.java

package src.media;

import src.media.GlobalEventListener;

/*
 * To change this template, choose Tools | Templates
 * and open the template in the editor.
 */
/**
 *
 * @author issuser
 */
class PoolHook extends Thread {
    SysHook hook;
    GlobalEventListener g_gl;

    PoolHook(GlobalEventListener gl) {
        g_gl = gl;
    }

    public void run() {
        hook = new SysHook();
        hook.rgHook(g_gl);
    }
}

class SysHook {

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

    void processKey(boolean ts, int vk, GlobalEventListener gl) {
        KeyboardEvent event = new KeyboardEvent(this, ts, vk, false, false);
        if (ts == true) {
            gl.keyPressed(event);
        } else {
            gl.keyReleased(event);
        }
    }

    public void rgHook(GlobalEventListener gl) {
        registerHookl(gl);
    }

    public void unRgHook() {
        unRegisterHookl();
    }

    private native void registerHookl(GlobalEventListener gl);
    private native void unRegisterHookl();
}


 

  • 1
    点赞
  • 3
    收藏
    觉得还不错? 一键收藏
  • 1
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值