使用java写个简单游戏脚本(二):JNI

Posted by cabeza on September 9, 2015

上一次的小程序只是演示了一下如何用robot类控制鼠标键盘操作

这种方法有很大的弊端:1、不能后台运行。2、坐标代码写死,窗口甚至都不可移动。3、某些游戏会屏蔽该类 下面讲述下如何用JNI技术实现游戏内脚本后台执行

什么是JNI

简单讲就是实现了java和其他语言的通信,下面要用到是c或c++

JNI自行阅读,不赘述

另:不到万不得已不要使用JNI,一方面它需要你掌握更多的知识才可以驾驭,一方面使用了JNI你的程序就会丧失可移植性

为什么要用JNI

java本身无法获得windows的接口,如窗口句柄等,需要借助c或c++

java代码


public class TowerUtil {
	public static native void keyPress(int hwnd, int key);
	public static native void mouseClick(int hwnd,int x,int y);
	static{
		System.loadLibrary("towerutil");
	}
	public static void main(String[] args) {
        int hwnd=1050990;
		TowerUtil.keyPress(hwnd, 0x42); 
	}
}

注意keyPress和mouseClick函数的声明,它有一个关键字native,表明这个方法使用java以外的语言实现。方法不包括实现,因为我们要用c/c++语言实现它

System.loadLibrary(“towerutil”)这句代码,在静态初始化块中定义的,用来装载towerutil.dll动态链接库

编译生成头文件

使用命令javah:

javah TowerUtil.java

文件内容如下:


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

#ifndef _Included_TowerUtil
#define _Included_TowerUtil
#ifdef __cplusplus
extern "C" {
#endif
/*
 * Class:     TowerUtil
 * Method:    keyPress
 * Signature: (II)V
 */
JNIEXPORT void JNICALL Java_TowerUtil_keyPress
  (JNIEnv *, jclass, jint, jint);

/*
 * Class:     TowerUtil
 * Method:    mouseClick
 * Signature: (III)V
 */
JNIEXPORT void JNICALL Java_TowerUtil_mouseClick
  (JNIEnv *, jclass, jint, jint, jint);

#ifdef __cplusplus
}
#endif
#endif

用c/c++实现函数体

第三部里定义了keyPress和mouseClick两个函数,接下来要用c语言实现它,注意观察上一步生成的头文件里的函数名和参数,按照其来写 如:


#include "stdafx.h"
#include "jni.h"
#include "TowerUtil.h"

BOOL APIENTRY DllMain( HANDLE hModule, 
                       DWORD  ul_reason_for_call, 
                       LPVOID lpReserved
					 )
{
    return TRUE;
}
JNIEXPORT void JNICALL Java_TowerUtil_mouseClick
(JNIEnv *env, jobject obj, jint hwnd, jint x, jint y){
	LPARAM lparam = MAKELPARAM(x,y);
	SendMessage((HWND)hwnd, WM_LBUTTONDOWN, MK_LBUTTON, lparam);
	sleep(10);
	SendMessage((HWND)hwnd, WM_LBUTTONUP, MK_LBUTTON, lparam);
	return;
}
JNIEXPORT void JNICALL Java_TowerUtil_keyPress
  (JNIEnv *env, jobject obj, jint hwnd, jint key){
	SendMessage((HWND)hwnd,WM_KEYDOWN,key,0);
	sleep(100);
	SendMessage((HWND)hwnd,WM_KEYUP,key,0);
	return;
}

关于代码里出现的SendMessage函数:SendMessage 函数参数大全 的 注意在开头引入jni.h和前面生成的TowerUtil.h

生成dll文件

下面生成前面提到的 towerutil.dll,windows可以下载MinGW进行编译:

gcc -shared TowerUtil.cpp -o towerutil.dll -I (.h头文件目录)

运行java程序

把上面生成的dll文件加入到java.library.path内

main函数里写上一行:

TowerUtil.keyPress(hwnd, 0x42);

解释下, hwnd是我玩的完美国际当前的句柄值,0x42是键盘“b”的asiic值,游戏内b键打开背包 执行后可以看到游戏里的背包界面打开 大功告成! 再结合上一篇文章写到的脚本解析函数的思路,就可以写出一个略微简陋的游戏脚本执行器了 使用JNI的好处上面已提到,脚本编写好后,你可以把游戏最小化,也不影响执行 如果能够再获得游戏内属性基址,就能写出一个功能更丰富的??了