基于截图和模拟点击的自动化压测工具开发(MFC)
1.背景
想对一个MFC程序做自动压测功能,根据判断程序界面某块区域是否达到预定状态,来自动执行鼠标点击或者键盘输入的操作,以解决测试人员需要重复手动压测问题。
1.涉及的技术
串口控制,基于MFC橡皮筋类(CRectTracker)做一个简单的截图对话框,GDI,模拟鼠标点击(mouse_event),模拟键盘输入(keybd_event),MD5等
2.串口控制继电器上下电
如何你要压测的程序需要通过控制USB上下电,可用串口控制继电器来达到此目的。
- 以下是串口初始化的代码
bool CComTest::InitialCom(int iComID, int iComPort, DWORD iBaudRate)
{if (iComPort < 1 || iComPort > 255){return FALSE;}if (INVALID_HANDLE_VALUE != m_hCom){CloseCom(iComID);}if (INVALID_HANDLE_VALUE != m_hCom){CloseHandle(m_hCom);}m_iComPort = iComPort; //串口通信端口m_iBaudRate = iBaudRate; //串口通信速率DCB Dcb;CString str;COMMTIMEOUTS TimeOut;int Data = 8;int Stop = 0;int Parity = 0;str.Format(_T("COM%d"), iComPort);m_hCom = CreateFile(str, GENERIC_READ | GENERIC_WRITE, 0, NULL, OPEN_EXISTING, NULL, NULL);if (m_hCom == INVALID_HANDLE_VALUE){return FALSE;}GetCommState(m_hCom, &Dcb);Dcb.BaudRate = iBaudRate;Dcb.ByteSize = Data;Dcb.StopBits = Stop;Dcb.Parity = Parity;if (!SetCommState(m_hCom, &Dcb)){CloseHandle(m_hCom);m_hCom[iComID] = INVALID_HANDLE_VALUE;return FALSE;}memset(&TimeOut, 0, sizeof(TimeOut));TimeOut.ReadIntervalTimeout = MAXDWORD;SetCommTimeouts(m_hCom[iComID], &TimeOut);SetupComm(m_hCom, 1024, 1024);PurgeComm(m_hCom, PURGE_TXABORT | PURGE_RXABORT | PURGE_TXCLEAR | PURGE_RXCLEAR);return TRUE;
}
- 读取串口:
DWORD CComTest::ReadCom(int ComNo, BYTE *pBuff, int nCount)
{if (m_hCom == INVALID_HANDLE_VALUE){return 0;}DWORD read = 0;ReadFile(m_hCom, pBuff, nCount, &read, NULL);return read;
}
写串口
BOOL CComTest::WriteCom(int ComNo, BYTE *pBuff, int nCount)
{if (m_hCom == INVALID_HANDLE_VALUE){return FALSE;}DWORD written = 0;BOOL ret = WriteFile(m_hCom, pBuff, nCount, &written, NULL);return ret;
}
发串口命令示例
BYTE pData[6] = { 0x01, 0x02, 0x03, 0x04, 0x05, 0x06 };WriteCom(m_iComID, pData, 6);
继电器控制命令需参考具体供应商提供的使用文档。
3.程序界面状态监控(截图对话框的实现)
想要通过检测某块区域是否达到预设的状态来自动执行被压测程序的下一步动作,可在压测前将被压测程序执行到预定状态,然后截取该区域的位图并保存,在压测时不停的检测该区域位图,和前面保存的位图做对比(MD5),如果MD5一致则执行模拟点击或模拟输入动作。
- 截图对话框的OnInitDialog函数代码如下
BOOL CCaptureDlg::OnInitDialog()
{CDialog::OnInitDialog();copyScreenToBitmap(m_ScreenBmp); //将屏幕内容拷贝到Bitmap类型的对象中//获取屏幕当前分辨率的宽度和高度(以像素为单位,传递参数不同X,Y)int screenWidth = GetSystemMetrics(SM_CXSCREEN);int screenHeight = GetSystemMetrics(SM_CYSCREEN);//调用MoveWindow或者SetWindowPos将当前的窗口设置成与屏幕大小相同//使用两个,软件写好后要使用SetWindowPos,但使用SetWindowPos设置成顶层窗口就不能调试了,使用MoveWindow进行调试MoveWindow(-3, -3, screenWidth + 6, screenHeight + 6); //比屏幕膜大3个像素不然白边出现,好看一些//SetWindowPos(&wndTopMost, -3, -3, screenWidth + 6, screenHeight + 6, SWP_SHOWWINDOW);//橡皮筋类的操作m_rectTracker.m_nStyle = CRectTracker::resizeOutside | CRectTracker::dottedLine; //矩形框虚线m_rectTracker.m_rect.SetRect(0, 0, 0, 0); //初始化矩形大小return TRUE; // return TRUE unless you set the focus to a control
}
m_ScreenBmp会保存截图对话框显示前的屏幕实时位图。
- 截图对话框背景显示原来屏幕的背景:
BOOL CCaptureDlg::OnEraseBkgnd(CDC* pDC)
{//将bitmap对象作为背景画到对话框上//创建内存DCCDC memDC;memDC.CreateCompatibleDC(pDC); //使内存DC与pDC兼容memDC.SelectObject(&m_ScreenBmp); //选入设备环境//将内容从内存DC拷贝到pDC中(本模态对话框窗口的DC)CRect rect;GetClientRect(&rect); //获取对话框大小,在初始化时设置了pDC->BitBlt(0, 0, rect.Width(), rect.Height(), &memDC, 0, 0, SRCCOPY); //将内容从内存DC拷贝到pDC中memDC.DeleteDC(); //释放return TRUE; //直接在此返回,不进行下一步操作,否则就画不上了return CDialog::OnEraseBkgnd(pDC);
}
- 鼠标消息处理,使橡皮筋对象矩形可操作:
void CCaptureDlg::OnLButtonDown(UINT nFlags, CPoint point)
{//如果点击绘制橡皮筋区域的外部,重新构建一个可拖拽的区域if (m_rectTracker.HitTest(point) == CRectTracker::hitNothing){m_rectTracker.TrackRubberBand(this, point, TRUE);}else{//点击在了区域内部,允许用户大小调整进行区域描画m_rectTracker.Track(this, point, TRUE);m_rectTracker.m_rect.NormalizeRect(); //NormalizeRect可以进行左右上下值调整,从右下向左上框柱}Invalidate(TRUE); //更新,使WM_PAINT描画消息触发CDialog::OnLButtonDown(nFlags, point);
}
- 修改橡皮筋框类光标的形态:
BOOL CCaptureDlg::OnSetCursor(CWnd* pWnd, UINT nHitTest, UINT message)
{//如果传递的是本窗口,SetCursor成功了if (pWnd == this && m_rectTracker.SetCursor(this, nHitTest)){SetCursor(LoadCursor(NULL, IDC_CROSS));return TRUE;}else{return CDialog::OnSetCursor(pWnd, nHitTest, message);}
}
- 在OnPaint中实时显示橡皮筋框
void CCaptureDlg::OnPaint()
{//CPaintDC只适合OnPaint里面,所以使用GetDC来获取DC,进行描绘CDC *pDC = GetDC();m_rectTracker.Draw(pDC);ReleaseDC(pDC); //get与release成对使用
}
- 双击时将橡皮筋对话框坐标和大小保存,并此区域内的对话框未显示前的屏幕位图保存为图片
void CCaptureDlg::OnLButtonDblClk(UINT nFlags, CPoint point)
{// 保存双击时坐标m_startPointX = point.x;m_startPointY = point.y;//如果双击在了矩形区域的内部就进行保存工作if (m_rectTracker.HitTest(point) != CRectTracker::hitMiddle){ MessageBox(_T("截图失败,请重新截图!"));return;}CDC *pDC = GetDC();CDC memDC;memDC.CreateCompatibleDC(pDC);memDC.SelectObject(&m_ScreenBmp);CRect rect;rect = m_rectTracker.m_rect;CBitmap mBmp, *pOldBmp = NULL;mBmp.CreateCompatibleBitmap(pDC, rect.Width(), rect.Height());m_rectInfo.m_Top1 = rect.top;m_rectInfo.m_Bottom1 = rect.bottom;m_rectInfo.m_Left1 = rect.left;m_rectInfo.m_Right1 = rect.right;CDC dstDC;dstDC.CreateCompatibleDC(pDC);pOldBmp = dstDC.SelectObject(&mBmp);//内容拷贝到目标,目标肯定比屏幕小,所以坐标0,0,宽高用户选择的,rect.left, rect.top是源缓冲区的坐标dstDC.BitBlt(0, 0, rect.Width(), rect.Height(), &memDC, rect.left, rect.top, SRCCOPY);CImage Img;Img.Attach(mBmp); //关联CString strModuleCapturePath;CString strCapturePath = "你想要保存的路径\\capture1.jpg";Img.Save(strCapturePath ); //保存地址mBmp.DeleteObject();memDC.DeleteDC();dstDC.DeleteDC();ReleaseDC(pDC);//保存完了后,说明本次截图操作完成了,要把当前显示的模态对话框,全屏的对话框关闭CDialog::OnCancel();
}
- 以上步骤可实现压测前某块区域的位图、区域和双击坐标的保存,以下函数可截取屏幕某个区域的位图并保存到本地:
void CCaptureDlg::Screen(CRect cRect, int iNumber)
{CRect rect = cRect;CDC *pDC;//屏幕DCpDC = CDC::FromHandle(::GetDC(NULL));//获取当前整个屏幕DCint BitPerPixel = pDC->GetDeviceCaps(BITSPIXEL);//获得颜色模式CDC memDC;//内存DCmemDC.CreateCompatibleDC(pDC);CBitmap memBitmap, *oldmemBitmap;//建立和屏幕兼容的bitmapmemBitmap.CreateCompatibleBitmap(pDC, rect.Width(), rect.Height());oldmemBitmap = memDC.SelectObject(&memBitmap);//将memBitmap选入内存DCmemDC.BitBlt(0, 0, rect.Width(), rect.Height(), pDC, rect.left, rect.top, SRCCOPY);//备注CImage Img;Img.Attach(memBitmap); //关联CString strCapturePath = _T("你想要保存的路径\\capture1.jpg");memDC.SelectObject(oldmemBitmap);memDC.DeleteDC(); ::ReleaseDC(NULL, pDC->m_hDC);
}
4.模拟鼠标点击
可通过以下代码来实现:
SetCursorPos(m_strPointX, m_strPointY);
mouse_event(MOUSEEVENTF_LEFTDOWN, m_strPointX, m_strPointY, 0, 0);
mouse_event(MOUSEEVENTF_LEFTUP, m_strPointX, m_strPointY, 0, 0);
5.模拟键盘输入
比如想在某个编辑框中自动输入字符串,可使用前面叙述的截图对话框截取编辑框区域,并通过模拟鼠标点击将光标焦点移动到编辑框内(SetCursorPos的坐标使用双击截图对话框时保存的坐标)
// 截取编辑框时,双击的坐标
SetCursorPos(m_strPointX, m_strPointY);
mouse_event(MOUSEEVENTF_LEFTDOWN, m_strPointX, m_strPointY, 0, 0);
mouse_event(MOUSEEVENTF_LEFTUP, m_strPointX, m_strPointY, 0, 0);
char szImput[10] = "123456789";
keybd_event(szImput[0], 0, 0, 0);
...
keybd_event(szImput[9], 0, 0, 0相关文章:
基于截图和模拟点击的自动化压测工具开发(MFC)
1.背景 想对一个MFC程序做自动压测功能,根据判断程序界面某块区域是否达到预定状态,来自动执行鼠标点击或者键盘输入的操作,以解决测试人员需要重复手动压测问题。 1.涉及的技术 串口控制,基于MFC橡皮筋类(CRectTracker)做一个…...
力扣每日一题 6/10
881.救生艇[中等] 题目: 给定数组 people 。people[i]表示第 i 个人的体重 ,船的数量不限,每艘船可以承载的最大重量为 limit。 每艘船最多可同时载两人,但条件是这些人的重量之和最多为 limit。 返回 承载所有人所需的最小船…...
[知识点] 内存顺序属性的用途和行为
C标准库中定义了以下几种内存顺序属性: std::memory_order_relaxedstd::memory_order_consumestd::memory_order_acquirestd::memory_order_releasestd::memory_order_acq_relstd::memory_order_seq_cst 1. std::memory_order_relaxed 定义:不提供同步…...
JAVA Mongodb 深入学习(二)索引的创建和优化
一、常用索引类型 1、单个索引 单个索引的创建 db.你的表名.createIndex({"你的字段名":1}) 单个索引的创建且是唯一索引 db.你的表名.createIndex({"你的字段名":1}),{ unique: true }) 2、复合索引 将多个过滤的字段,做成索引,…...
转让北京劳务分包地基基础施工资质条件和流程
地基基础资质转让流程是怎样的?对于企业来说,资质证书不仅是实力的证明,更是获得工程承包的前提。而在有了资质证书后,企业才可以安心的准备工程投标,进而在工程竣工后获得收益。而对于从事地基基础工程施工的企业,需…...
Python基础——字符串
一、Python的字符串简介 Python中的字符串是一种计算机程序中常用的数据类型【可将字符串看作是一个由字母、数字、符号组成的序列容器】,字符串可以用来表示文本数据。 通常使用一对英文的单引号()或者双引号(")…...
AP的数据库性能到底重要吗?
先说结论:没那么重要。甚至可能不重要。 我用我的经历和分析给大家说说。诸位看看如何。 不重要的观点是不是不能接受? 因为这些是站在我们角度觉得的。而实际上使用者(业务或者用户),真的不太在乎我们所在乎的。 …...
Vue3【二】 VSCode需要安装的Vue语法插件
VSCode需要安装的 适配Vue3的插件 Vue-Official插件安装...
设置路径别名
一、描述 如果想要给路径设置为别名,就是常见的有些项目前面的引入文件通过开头的,也就是替换了一些固定的文件路径,怎么配置。 二、配置 import { defineConfig } from vite import react from vitejs/plugin-react import path from path…...
人事信息管理系统(Java+MySQL)
一、项目背景 在现代企业中,管理大量员工的工作信息、薪资、请假、离职等事务是一项非常繁琐和复杂的任务。传统的手工管理方式不仅效率低下,而且容易出错。为了提高人事管理的效率,减少人工操作带来的错误,企业迫切需要一个高效…...
Python 中生成器与普通函数的区别
在Python中,生成器和普通函数有一些区别。 生成器使用 yield 语句从函数中返回一个值,而不是使用 return 语句。当生成器函数被调用时,它会返回一个迭代器对象,而非立即执行函数体内的代码。 生成器函数可以通过多次调用 yield 语…...
最小栈、栈的弹出(C++)
1.最小栈 思路分析: 代码: class MinStack { public:MinStack() {}void push(int val) {st.push(val);//两种情况需要更新最小值//1.最小栈为空(就是存最小值的那个栈)//2.插入的值小于或等于最小栈的栈顶元素if(minstack.empty()||minstack.top()>…...
20240607每日通信--------VUE3前端引入scoket-io,后端引入Netty-SocketIO,我成功了,希望一起交流沟通
无语 前置: VUE3 前端集成scoket-io socket.io-client Sringboot 3.0JDK17集成Netty-SocketIO Netty-SocketIO 失败原因一: 前期决定要写demo时候,单独了解了,后端引入Netty-SocketIO注意事项,详见我先头写的博客 前…...
Tomcat源码解析(八):一个请求的执行流程(附Tomcat整体总结)
Tomcat源码系列文章 Tomcat源码解析(一):Tomcat整体架构 Tomcat源码解析(二):Bootstrap和Catalina Tomcat源码解析(三):LifeCycle生命周期管理 Tomcat源码解析(四):StandardServer和StandardService Tomcat源码解析(五)&…...
python使用gdb进行堆栈查看与调试
以ubuntu示例,先安装gdb与python-dbg,dbg按照python版本安装 apt install -y gdb python3.10-dbg 使用top查看python进程,使用gdb操作python进程 gdb python3 6618 加载环境 source /usr/share/gdb/auto-load/usr/bin/python3.10-gdb.py…...
【DevOps】路由与路由器详细介绍:原理、功能、类型及应用场景
目录 一、路由详细介绍 1、什么是路由? 2、路由的基本原理 3、 路由协议 静态路由 动态路由 4、 路由表 5、 路由算法 6、路由的优缺点 优点 缺点 7、 路由应用场景 二、路由器详细介绍 1、什么是路由器? 2、 路由器的工作原理 3、路由器…...
【WP|9】深入解析WordPress [add_shortcode]函数
add_shortcode 是 WordPress 中一个非常强大的函数,用于创建自定义的短代码(shortcodes)。短代码是一种简洁的方式,允许用户在内容中插入动态的、可重用的功能。通过 add_shortcode,开发者可以定义自己的短代码&#x…...
Qt QStackedWidget类详细分析
一.定义 QStackedWidget类是一个容器控件,它提供了一个堆叠的页面布局方式,每个页面可以包含一个子部件。在QStackedWidget中,只有当前活动的页面是可见的,其他页面会被隐藏起来。 QStackedWidget类的常用方法包括: a…...
Java数据结构与算法(leetcode热题881. 救生艇)
前言 救生艇属于贪心算法,解题之前条件一定要归纳好。题目中存在3个要求: 1.一艘船最多坐2人 2.船数要求最小 3.每艘船重量小于limit 意味着体重较轻的两人可以同乘一艘救生艇。 . - 力扣(LeetCode) 实现原理 1.重量大的有…...
react+wijmo所遇问题
1.官网地址:https://demo.mescius/wijmo/demos/Grid/Overview/react 别进中文地址,注意后缀mescius有没有.cn有的话删掉,那个没有触发方法和各类API,组件也不全 2.中文地址:(不太好用)&#x…...
装饰模式(Decorator Pattern)重构java邮件发奖系统实战
前言 现在我们有个如下的需求,设计一个邮件发奖的小系统, 需求 1.数据验证 → 2. 敏感信息加密 → 3. 日志记录 → 4. 实际发送邮件 装饰器模式(Decorator Pattern)允许向一个现有的对象添加新的功能,同时又不改变其…...
React Native在HarmonyOS 5.0阅读类应用开发中的实践
一、技术选型背景 随着HarmonyOS 5.0对Web兼容层的增强,React Native作为跨平台框架可通过重新编译ArkTS组件实现85%以上的代码复用率。阅读类应用具有UI复杂度低、数据流清晰的特点。 二、核心实现方案 1. 环境配置 (1)使用React Native…...
Java入门学习详细版(一)
大家好,Java 学习是一个系统学习的过程,核心原则就是“理论 实践 坚持”,并且需循序渐进,不可过于着急,本篇文章推出的这份详细入门学习资料将带大家从零基础开始,逐步掌握 Java 的核心概念和编程技能。 …...
汇编常见指令
汇编常见指令 一、数据传送指令 指令功能示例说明MOV数据传送MOV EAX, 10将立即数 10 送入 EAXMOV [EBX], EAX将 EAX 值存入 EBX 指向的内存LEA加载有效地址LEA EAX, [EBX4]将 EBX4 的地址存入 EAX(不访问内存)XCHG交换数据XCHG EAX, EBX交换 EAX 和 EB…...
是否存在路径(FIFOBB算法)
题目描述 一个具有 n 个顶点e条边的无向图,该图顶点的编号依次为0到n-1且不存在顶点与自身相连的边。请使用FIFOBB算法编写程序,确定是否存在从顶点 source到顶点 destination的路径。 输入 第一行两个整数,分别表示n 和 e 的值(1…...
MySQL账号权限管理指南:安全创建账户与精细授权技巧
在MySQL数据库管理中,合理创建用户账号并分配精确权限是保障数据安全的核心环节。直接使用root账号进行所有操作不仅危险且难以审计操作行为。今天我们来全面解析MySQL账号创建与权限分配的专业方法。 一、为何需要创建独立账号? 最小权限原则…...
深度学习习题2
1.如果增加神经网络的宽度,精确度会增加到一个特定阈值后,便开始降低。造成这一现象的可能原因是什么? A、即使增加卷积核的数量,只有少部分的核会被用作预测 B、当卷积核数量增加时,神经网络的预测能力会降低 C、当卷…...
PAN/FPN
import torch import torch.nn as nn import torch.nn.functional as F import mathclass LowResQueryHighResKVAttention(nn.Module):"""方案 1: 低分辨率特征 (Query) 查询高分辨率特征 (Key, Value).输出分辨率与低分辨率输入相同。"""def __…...
人工智能--安全大模型训练计划:基于Fine-tuning + LLM Agent
安全大模型训练计划:基于Fine-tuning LLM Agent 1. 构建高质量安全数据集 目标:为安全大模型创建高质量、去偏、符合伦理的训练数据集,涵盖安全相关任务(如有害内容检测、隐私保护、道德推理等)。 1.1 数据收集 描…...
嵌入式学习之系统编程(九)OSI模型、TCP/IP模型、UDP协议网络相关编程(6.3)
目录 一、网络编程--OSI模型 二、网络编程--TCP/IP模型 三、网络接口 四、UDP网络相关编程及主要函数 编辑编辑 UDP的特征 socke函数 bind函数 recvfrom函数(接收函数) sendto函数(发送函数) 五、网络编程之 UDP 用…...
