java 操作 User32 的一次小尝试

翻阅资料的时候发现了java 居然也可以操作桌面系统。
但是其中确遇到了很多的问题,这里记录一下。这里用QQ游戏举一下例子,获取窗体。

首先 项目刚开始就遇到了问题,利用jna jar包 中的user32 获取屏幕窗口 方法如下
首先创建一个接口类 User32

// 提供 获取窗口句柄  获取窗口矩形坐标 鼠标控制 
 public interface User32 extends StdCallLibrary{

    User32 INSTANCE = (User32)Native.loadLibrary("User32",User32.class);
    
    int PostMessageA(int a,int b,int c,int d);
    
    int FindWindowA(String a,String b);
    
    int GetWindowRect(int hwnd,Rect r);
    
}

首先 要获取“QQ游戏 - 连连看角色版本” ,

// 取游戏窗口句柄
int hwnd = user32.FindWindowA(null, "QQ游戏 - 连连看角色版");

但是一直获取不到窗口的句柄,尝试获取 QQ 窗口 发现成功。怀疑是编码的问题
于是 cmd 输入 chcp 返回936 说明系统是 GBK 编码 ,所以尝试对项目 设置字符集为 GBK 。 成功获取到窗口

// 获取窗口位置
Rect r = new Rect();
user32.GetWindowRect(hwnd, r);

获取到窗口以后,是对窗口进行一个快照截图保存在本地生成一个.bmp 的图片格式。

//窗口截图
public static BufferedImage getImage(int hwnd){
    Rect r = Window.getRect(hwnd);
    Rectangle rg = new Rectangle(r.left, r.top, r.right-r.left, r.bottom-r.top);
    try {
        return new Robot().createScreenCapture(rg);
    } catch (Exception e) {
        e.printStackTrace();
        return null;
    }
}

bmp

说到bmp了,完全不了解。于是开始查看相应的资料
关于bmp 的资料请自行百度 。 将.bmp文件转换成二进制开始读取图片信息。 其中比较关键的几个点。二进制数据中,第18-21 是描述图片的宽度 单位为像素
第22-24 描述的是图片的高度 单位为像素 所以可以取出 这几位 算出图片的宽高。也就是所谓的分辨率。 因为我们这边是三原色图片,所以一个像素占3个字节 ,r,g,b
其次 前54 个字节的信息都是文件的描述 信息 ,从55个字节开始 是具体的图片信息。另外bmp文件要求每行为4个字节,所以数据总数为4的倍数,不足以0补足。

bmmp 图的几个特点
1.bmp位图确实是按照从下到上,从左到右的顺序进行存储的,并且会对不满8字节的进行补零操作。
2.bmp位图的存储确实是按 blue,green,red的顺序进行存取的,也就是和我们常见的颜色通道的顺序是相反的。
3.bmp位图前的信息存取,低位地址存的是低位数据,高位地址存的是高位数据。

&0xff

另外一个需要注意的点,在操作二进制文件的时候需要将 byte 转 Int 如果是正数,直接转换就好了,但是如果byte 为负数 则存在问题。所以这里引进一个 &0xff

& 符号 表示 与操作 与操作 1&0 =0 1&1 =1 只有两者都为1的时候为1 其余情况都为 0
0xff 表示的二进制数 11111111
所以 &0xff 一般用作截取8位 通常与 >> << 位移符联合使用。

当我们只关心二进制的机器数而不关注十进制的值,那么byte &0xff只是对其最低8位的复制 但不保证十进制数不变 如下

public static void main(String[] args) {
byte b = -127;//10000001
int a = b;
System.out.println(a);
a = b&0xff;
System.out.println(a);
}
//输出结果-127,129

首先在计算机系统中,数值一律用补码来表示和存储。原因在于,使用补码,可以将符号位和数值域统一处理;同时,加法和减法也可以统一处理。
此外,补码与原码相互转换,其运算过程是相同的,不需要额外的硬件电路。

正数 因为 补码,反码都是本身 所以不受影响。

负数的补码是在原码的基础上除符号位外其余位取反后+1

-127 1111 1111 原码

  1000 0000  符号位不变其余取反 
+ 0000 0001  +1
  1000 0001  最终的补码

-127补码表示为 1000 0001

int a = b;将byte 类型提升为int时候,b的补码提升为 32位,补码的高位补1,也就是

1111 1111 1111 1111 1111 1111 1000 0001

负数的补码转为原码,符号位不变,其他位取反,在加1,

结果是 1000 0000 0000 0000 0000 0000 0111 1111表示为十进制 也是 -127

也就是 当 byte -> int 能保证十进制数不变,但是有些时候比如文件流转为byte数组时候,

我们不是关心的是十进制数有没有变,而是补码有没有变,这时候需要&上0xff

本例子中,将byte转为int 高24位必将补1,此时补码显然发生变化,在与上0xff,将高24重新置0,

这样能保证补码的一致性,当然由于符号位发生变化,表示的十进制数就会变了

1111 1111 1111 1111 1111 1111 1000 0001

&

0000 0000 0000 0000 0000 0000 1111 1111

结果是

0000 0000 0000 0000 0000 0000 1000 0001

和原来的补码 一致,但是显然符号位变化了,表示的十进制数发生变化,变为129

结论:

java中基本类型从小扩展到大的数据类型时候,正数因为符号位是0,无论如何都是补零扩展,但是负数补零扩展和补符号位扩展完全不同,

负数补符号位扩展,保证十进制数不变

例如 byte>>>int -127自动按照补符号位扩展,在高24位补符号位1,表示的十进制数不变

补零扩展,保证补码的一致性,但是表示的十进制发生变化

所以为了保证数据一致所以 采用了 &0xff

剩下关于搜索算法。

首先创建一个

int[][] data = new int[11][19]; 

来保存所有点位的特征值

首先我们是采用将窗口截图保存为bmp文件再转换为二进制,进行byte读取,这里可以先将图片 放入到ps 中 ,查看具体的像素坐标。这里我们可以得出
我们的初始坐标点,以及每个格子所占的大小。然后就可以将每个格子进行分割,算出具体的rgb值进行存储,这里的采样可以自行考虑每个格子中取多少像素点。
我们这里创建一个byte[] 数组 ,每个格子截取15 * 15 ,截取以后我们创建一个List 进行保存,每次保存进行查重,查看List 中是否已存在,若存在则返回
在list中的位置 存入 二维数组 data 中;若不存在 则存入List 中 并 将 当前 List.size()存入 data 中。依次循环。

数据处理完以后,在二维数组中 相同点的 int 值相同。

后面就是搜索可消除点的算法,搜索算法这块可以采用广度优先搜索,或者深度优先搜索。分别搜索上下左右四个方向,遇到边界 或者 被其他元素 阻挡 则返回上一节点继续搜索,直到搜索到目标。需注意的是搜索路径不能超过2个拐点。找到可消除点后,记录下消除点坐标,模拟鼠标点击目标点和消除点,然后将两个点位的值赋值为0,进行下一次搜索。

//模拟鼠标左键单击
	public static void click(int hwnd, int x, int y) {
		int v = x + (y << 16);
		user32.PostMessageA(hwnd, 513, 1, v); // 按下
		user32.PostMessageA(hwnd, 514, 0, v); // 松开
	}

在这里插入图片描述

以上就是这次阅读代码的收获了。记录一下,加深一下记忆。

评论 3
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值