郁金香VC++初级和中级篇, 最全的手记及琢字翻译

2019-04-01 17:53

郁金香VC++教程及手记

初级篇1.1.1教学目标:模拟鼠标操作 1.1.1、游戏数据分析(SPY++) a、取得窗口相对坐标

b、读出游戏窗口信息GetWindowRect c、移动鼠标指针SetCursorPos HWND FindWindow(

LPCTSTR lpClassName, //窗口类名 LPCTSTR lpWindowName //窗口标题 );

教学过程:

取游戏标题:QQ游戏 - 连连看角色版

取开局所在坐标:x=655;y=577 //lparam 0x0241028f 拦截消息:WM_LBUTTONDOWN,WM_LBUTTONUP

API-FindWindow(NULL,\游戏 - 连连看角色版\

打开VC,新建-工程-MFC EXE-工程名是LLKWG,然后选择基本对话框,完成即可 [attachment=535]

还要在编辑框中关联变量 ,建立类向导 [attachment=536]

在游戏开局按钮输入代码

// The system calls this to obtain the cursor to display while the user drags // the minimized window.

HCURSOR CLlk_wgDlg::OnQueryDragIcon() {

return (HCURSOR) m_hIcon; }

HWND gameh;//游戏窗口句柄

RECT r1; // RECT结构表示一个矩形区域 void CLlk_wgDlg::*****tartGame() {

// TODO: Add your control notification handler code here

gameh=::FindWindow(NULL,\游戏 - 连连看角色版\获取游戏窗口句柄 ::GetWindowRect(gameh,&r1); //这里取坐标, 双冒号是全局的意思

this->m_x=r1.left;this->m_y=r1.top;//读出窗口左上角坐标, this是关联变量 UpdateData(false); //显示到编辑框

//设置鼠标指针位置 取开局所在坐标:x=655;y=577 //lparam 0x0241028f SetCursorPos(655+r1.left,577+r1.top);//当前窗口坐标+开局按钮坐标 }

1.1.2 用VC++写个最简单的外挂(实现游戏开局) a、鼠拟鼠标单击mouse_event b、鼠标指针移动还原

c、集成到startgame函数里 教学过程:

//模拟鼠标的单击(鼠标按下/鼠标抬起) //鼠标在当前位置按下

mouse_event(MOUSEEVENTF_LEFTDOWN,0,0,0,0); //鼠标在当前位置抬起

mouse_event(MOUSEEVENTF_LEFTUP,0,0,0,0); 小结:

mouse_event,Sleep,SetCursorPos

这次用到了鼠标点击的函数mouse_event鼠标硬件模拟,如果调用不成功则延时一下Sleep (200),然后再将鼠标移回原位SetCursorPos这个是位置设置函数

调用成功后,将函数放在一个.h头文件里,方便以后调用.新建 c/c++ Header File,文件名GameProc #include \//游戏 功能函数 HWND gameh; RECT r1;

POINT p;//x,y void startGame() {

// TODO: Add your control notification handler code here //获取游戏窗口句柄

gameh=::FindWindow(NULL,\游戏 - 连连看角色版\ ::GetWindowRect(gameh,&r1); //保存当前鼠标指针 //取得当前鼠标位置 GetCursorPos(&p);

1

//设置鼠标指针位置 取开局所在坐标:x=655;y=577 //lparam 0x0241028f SetCursorPos(655+r1.left,577+r1.top); //模拟鼠标的单击(鼠标按下/鼠标抬起)

mouse_event(MOUSEEVENTF_LEFTUP,0,0,0,0); //鼠标在当前位置按下 mouse_event(MOUSEEVENTF_LEFTDOWN,0,0,0,0); //鼠标在当前位置抬起 mouse_event(MOUSEEVENTF_LEFTUP,0,0,0,0); Sleep(200);//过一段时间 再执行后边的代码 SetCursorPos(p.x,p.y); //还原鼠标位置 }

当然还要将这个.h文件包涵进主函数里 #include \#include \#include \#include \然后这样调用

void CLlk_wgDlg::*****tartGame() {

startGame(); }

1.2.1、CE中的数据类型

a、数据类型:Bit,Byte,Word,Dword,float,double b、用CE查找出坐位号; c、保存分析数据 教学过程:

a、数据类型:Bit,Byte,Word,Dword,float,double C++数据类型

bit/位 1位 取值范围:0..1

byte(字节)=0..11111111(2进制)=0..255(10进制)=0..FF (16进制) WORD(单字)=2Byte=0..65535(10进制) =0..FFFF (16进制) DWORD(双字)=2WORD=4Byte=0..4294967295 =0..FFFFFFFF float(浮点数) double(双浮点数)

int(4字节),long(4字节),WORD,DWORD(4字节),float(4字节),double(8字节) b、用CE查找出坐位号; 打开 QQ游戏 连连看 猜测 0..5,1..6,顺时针 0..7

坐位号地址:0x00B8D8E0

因为游戏当中不同的座位号数据不一样,所以首先要找到座位号.

用CE搜索游戏是字节型来查找游戏的座位号,一桌是6个位置,猜测是从0到5,上面是0,顺时针方向加1.所以我们更换座位号后再继续用CE搜索相应的位置号,注意附加的进程可不要出错哦.QQ连连看的进程名是KYODAI~1.EXE,可以用任务管理器找出. 找到后将CE的数据保存一下,方便下次调用. 1.2.2、编程读出坐位号;

a、远程读取进程数据 b、打开远程进程 c、读取远程进程数据 教学过程: API函数介绍

1、FindWindow //获取窗口句柄 2、GetWindowThreadProcessId //获取窗口进程ID 3、OpenProcess //打开指定进程

4、ReadProcessMemory //读指定进程内存数据 游戏进程名:KYODAI~1.EXE

游戏窗口标题:\游戏 - 连连看角色版\ HWND FindWindow(

LPCTSTR lpClassName, // NULL 忽略 LPCTSTR lpWindowName // 窗口标题 );

BOOL ReadProcessMemory(

HANDLE hProcess, // 进程句柄 LPCVOID lpBaseAddress,

// 基址0x00B8D8E0 LPVOID lpBuffer, // 存放数据缓冲区 DWORD nSize, // 要读取数据的字节数 LPDWORD lpNumberOfBytesRead

// 实际读取的字节数 );

这次需要几个函数来取得窗口/句柄/进程/内存等信息

2

几个函数要联合运用,前一个函数的返回值就是后一个函数的参数

在VC代码里添加座位号的变量m_Num 类型是UINT,新增一个按钮,添加如下代码 [attachment=537]

c*****t PCHAR gameCaption=\游戏 - 连连看角色版\void CLlk_wgDlg::OnButton2() {

// 游戏窗口标题:\游戏 - 连连看角色版\ // 1、FindWindow //获取窗口句柄 //2、GetWindowThreadProcessId //获取窗口进程ID //3、OpenProcess //打开指定进程

//4、ReadProcessMemory //读指定进程内存数据 //获取窗口句柄

HWND gameh=::FindWindow(NULL,gameCaption); //获取窗口进程ID DWORD processid;

::GetWindowThreadProcessId(gameh,&processid); //打开指定进程

HANDLE processH=::OpenProcess(PROCESS_ALL_ACCESS,false,processid); //读指定进程内存数据 DWORD byread;

LPCVOID pbase=(LPCVOID)0x00B8D8E0;//读取当前的指针,强制转换为LPCVOID指针 LPVOID nbuffer=(LPVOID)&m_num; //保存当前的指针,强制转换为LPVOID指针 ::ReadProcessMemory(processH,pbase,nbuffer,4,&byread); UpdateData(false); //更新变量的值到 编辑框 }

1.2.3、用CE查出棋盘基址; a、找棋盘数据基址 b、分析棋盘数据结构 19宽*11高 :数组 byte a[19][11] byte 0..255 // 00..FF

0x0012A508 //棋盘数组基址

db 地址 // 以字节方式 显示指定地址内存里的数据 前面座位号的基址已经找出了,所以这次要找出棋盘的数据,

要先查一下棋盘的2维排列,是19宽*11高,用QQ截图查找一下长和宽,然后计算出每一格的大小.猜测棋盘的棋子也是字节的,我们查找左上角第一棋子的数据.

用CE查找,如果有棋子就是大于0,变化了就再搜索更改的数值,没有棋子就是0,多次查找就找到第一棋子的地址了.自己找个座位坐下来,加个密码不让别人进,多按几次\练习\按钮,这样棋盘变化就方便找棋子数据了. 老师也查找出错了,再来一次吧. 这次找到了..

找到后用OD加载一下看看,这样的数据排序看的比较清楚 1.2.4、读出当前棋盘数据

a、编程读出棋盘数据

b、棋盘数据显示出来 参考章节:1.2.3,1.2.2

19宽*11高 :数组 byte a[11][19] // a[y][x] byte 0..255 // 00..FF

0x0012A508 //棋盘数组基址

db 地址 // 以字节方式 显示指定地址内存里的数据 itoa(要转换的整数,存放字符数组,要转换的字符进制) for (int y=0;y<=10;y++) //y++ y:=Y+1;

添加了一个编辑框用来显示棋盘数据,再关联一个变量m_Chessdata Cstring类型 [attachment=538]

增加”更新棋盘数据”按钮,添加代码如下: byte chessdata[11][19];//a[y][x] void CLlk_wgDlg::OnBtnReadchess() {

// TODO: Add your control notification handler code here //获取窗口句柄

HWND gameh=::FindWindow(NULL,gameCaption); //获取窗口进程ID DWORD processid;

::GetWindowThreadProcessId(gameh,&processid); //打开指定进程

HANDLE processH=::OpenProcess(PROCESS_ALL_ACCESS,false,processid); //读指定进程内存数据 DWORD byread;

LPCVOID pbase=(LPCVOID)0x0012A508; //棋盘数据基址 LPVOID nbuffer=(LPVOID)&chessdata; //存放棋盘数据

3

::ReadProcessMemory(processH,pbase,nbuffer,11*19,&byread); char buf[11]; ///显示棋盘数据 m_chessdata=\先清空编辑

for (int y=0;y<=10;y++)//一列一列的读,FOR循环:Y=0是循环的起始值,Y<=10是终止值,Y++是每次Y+1 {

for (int x=0;x<=18;x++) //一行一行的读 {

itoa(chessdata[y][x],buf,16); //itoa整型转换成字串 m_chessdata+=buf; m_chessdata+=\ }

m_chessdata+=\换行 }

UpdateData(false); //更新数据 }

这节课讲了VC编程,注意细节,数据是按照[Y][X]排序的,注意棋盘数据是2位数的,要将char buf改成char buf[11]. 1.3.1 分析棋子与棋盘坐标关系

a、鼠标软件模拟,函数SendMessage b、分析窗口内棋子相对坐标X,Y

c、软件模拟点击棋盘坐标x,y处的棋子 1、SendMessage;

SendMessage(hwnd,WM_LBUTTOMDOWN,0,YX);//hwnd=FindWindow(NULL,游戏标题); SendMessage(hwnd,WM_LBUTTOMUP,0,YX); //PostMessage/mouse_event 2、获取棋盘左上角棋盘第一格坐标. 棋盘第一格 坐标 x=21,y=192 int x=22,y=187;

hwnd=FindWindow(NULL,游戏标题);

SendMessage(hwnd,WM_LBUTTONDOWN,0,(y<<16)+x);// SendMessage(hwnd,WM_LBUTTONUP,0,(y<<16)+x); // 3、计算棋盘的 宽度*高度 589*385 棋盘第一格 坐标 x=21,y=192

31*35 棋子宽度,高度

SendMessage(hwnd,WM_LBUTTONDOWN,0,(y<<16)+x+31*2);// SendMessage(hwnd,WM_LBUTTONUP,0,(y<<16)+x); //

//SendMessage 鼠标模拟,//WM_LBUTTONDOWN 鼠标左键按下 //WM_LBUTTONUP 鼠标左键抬起 //<< 左移指令

前面都是直接移动了鼠标,这次要改发送鼠标消息了,这样鼠标不移动也会点击游戏的开始按钮.SendMessage的参数是相对坐标, mouse_event的参数是绝对坐标

再次打开SPY++,找到棋盘第一格的位置,X=21,Y=187

新增一个按钮”点击棋盘第一格”方便测试,添加代码如下:

void CLlk_wgDlg::OnButton3() //按钮函数 {

int x=22,y=187; //定义座标点 HWND hwnd=::FindWindow(NULL,gameCaption); //查找窗口 int lparam; //定义座标点变量

lparam=(y<<16)+x+31*2; //表示指定格,Y<<16是左移16位,发消息用的Y座标点 ::SendMessage(hwnd,WM_LBUTTONDOWN,0,lparam);//鼠标按下消息 ::SendMessage(hwnd,WM_LBUTTONUP,0,lparam); //鼠标抬起消息 }

用QQ抓图,查找棋子格子数大小,31*35的大小,以便计算出所有格子的座标点. 1.3.2 消掉一对棋子的算法框架

a、遍历棋盘同类型棋子配对 b、构建算法框架 //遍历整个 棋盘 找相同一对棋子 //检测这一对棋子是否 可以消除

//如果 可以消除 ,则模拟鼠标点击这2点

最多一条 三条 连线 能够形式一条 路线 就表示可消除 更改”点击棋盘第一格”代码如下: void ClearPiar() //消除一对棋子 {

//读出棋盘数据至chessdata 11,19 updatdChess();

//遍历整个棋盘 找出相同类型 一对棋子 POINT p1,p2;//定义两个点,座标型 int x1,y1,x2,y2;//定义座标点

4

for (y1=0;y1<11;y1++)//点1循环,从Y列开始 for (x1=0;x1<19;x1++)//点2循环,从X行开始

{ for (y2=y1;y2<11;y2++)//这是点2的Y循环,由Y1开始 for (x2=x1;x2<19;x2++)//这是点2的X循环,由X1开始

// 棋子1与棋子2 类型是否相同, 要求点1与点2 相等则假,就是座标不能相同.

if ((chessdata[y1][x1]==chessdata[y2][x2]) &&(!((x1==x2)&&(y1==y2))) ) {

p1.x=x1;p1.y=y1; p2.x=x2;p2.y=y2;

//检测 相同的2个棋子是否可消掉

if ( check2p(p1,p2))//如果可消除 则返回真 {

//click2p 鼠标模拟 点击 p1,p2 click2p(p1,p2); } } } }

1.3.3 (Check2p)大致框架(算法核心)

a、在这一对棋子间找相通路径的原理 b、(Check2p函数)框架代码

c、(CheckLine函数)检测2点是否有连通

LineNull(p1,p2); //是否在棋盘上 的2个点之前是否有一条全为0的直线,如有true,否则false 1、剪贴游戏图;

Y坐标相同的情况下 p1,p2

lineNull(p1.right,p2.left) //可消除 X坐标相同的情况下 p1,p2

LineNull(p1.down,p2.up) //可消除 X与Y坐标都不相情况下 p1,p2

lineNll(p1.down,pa),LineNull(p2.down,pb),LineNull(pa,pb) //可消除

现在就要来分析游戏了,连连看大家都知道,是需要判断两点间是否可以连接的,比如有直连,有一折后的连接,最多是二折后的连接.

如果是直连的话,就要判断中间是否所有的数据都为0,判断的思路很主要,一定要搞清楚.而有折的连接则需要多条直线,最多是三条直线,这个需要遍历,不断的向下判断.

将VC代码打开,插入一个类Generic Class,名称为CChessPoint 添加代码如下:

class CChessPoint {

public:

POINT p;//临时点 POINT up;//上点 POINT down;//下点 POINT left;//左点 POINT right;//右点

CChessPoint(POINT pxy); //构造函数 virtual ~CChessPoint(); };

还要在Cchesspoint.cpp实现部分修改代码 CChessPoint::CChessPoint(POINT pxy)

{ up=pxy;down=pxy;left=pxy;right=pxy;//将座标初始化 //向上下左右扩展 p=pxy;

up.y=pxy.y-1; down.y=pxy.y+1; left.x=pxy.x-1; right.x=pxy.x+1;

//这样处理完之后每个棋子就包涵了上下左右中五个点的属性 }

CChessPoint::~CChessPoint() { }

在按钮部分增加代码如下:

bool lineNull(POINT p1,POINT p2) {

return true;//先写个空的,下节课继续. }

bool check2p(POINT p1,POINT p2)

5


郁金香VC++初级和中级篇, 最全的手记及琢字翻译.doc 将本文的Word文档下载到电脑 下载失败或者文档不完整,请联系客服人员解决!

下一篇:组织行为学重点

相关阅读
本类排行
× 注册会员免费下载(下载后可以自由复制和排版)

马上注册会员

注:下载文档有可能“只有目录或者内容不全”等情况,请下载之前注意辨别,如果您已付费且无法下载或内容有问题,请联系我们协助你处理。
微信: QQ: