基于截图和模拟点击的自动化压测工具开发(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

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.mzph.cn/diannao/24180.shtml

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

微服务框架下,因发送端与消费端的vhost不一致,导致rabbitmq出现严重的消息堆积

一、背景 在生产环境下&#xff0c;rabbitmq机器出现磁盘空间不足的报警&#xff0c;发现是某个队列的消息只有生产&#xff0c;迟迟没有消费。 可以得到的信息是&#xff1a; 队列queue是data_center_file_change_queue队列绑定的交换机是resourceChangeExchange&#xff0c…

PLC通过Profinet转Modbus网关与流量计通讯案例

1、案例背景 在工业自动化系统中&#xff0c;PLC(可编程逻辑控制器)与流量计之间的通信是保证以后设备生产数据准确传输和实现控制功能的关键。但是&#xff0c;由于PLC和流量计可能使用不同的通信协议(如Profinet和Modbus)&#xff0c;因此需要一种转换机制来实现它们之间的通…

中介子方程四

X$XFX$XEXyXEX$XFX$XEXyXEX$XαXηXtXαX$XWXyX$XyXWX$XpXαXqXηX$XeXαXhX$XdX$XpX$XdX$XyXeXαX$XEXyXEX$XαXeXyX$XdX$XpX$XdX$XhXαXeX$XηXqXαXpX$XWXyX$XyXWX$XαXtXηXαXpX$XEX$XZX$XpXαXηXtXαX$XWXyX$XyXWX$XpXαXqXηX$XeXαXhX$XdX$XpX$XdX$XyXeXαX$XEXyXEX$X…

uniapp uni-popup内容被隐藏问题

今天开发新需求的时候发现uni-popup 过一会就被隐藏掉只留下遮罩(css被更改了)&#xff0c;作者进行了如下调试。 1.讲uni-popup放入其他节点内 失败&#xff01; 2.在生成dom后在打开 失败&#xff01; 3.uni-popup将该节点在包裹一层 然后将统计设置样式&#xff0c;v-if v-s…

监视一个进程,当它停止响应时结束任务然后重启

最近写了个服务器端的socket程序&#xff0c;有时会锁死&#xff0c;由于有些引用的组件看不到源码&#xff0c;不知道哪里出了问题&#xff0c;杀掉进程重启就可以用了。而且不影响客户端的重连。我打算写个程序来做这个事 网上找了一些资料&#xff0c;有些事linux下的用pyh…

SpringCloud 网关配置websocket

一、nginx https://域名.com location /websocket/ { proxy_pass http://172.1.1.173:8181/; #内网网关IP proxy_http_version 1.1; proxy_read_timeout 360s; proxy_redirect off; proxy_set_header Upgrade $http_upgrade; …

算法题--华为od机试考试(围棋的气、用连续自然数之和来表达整数、亲子游戏)

目录 围棋的气 题目描述 输入描述 示例1 输入 输出 解析 答案 用连续自然数之和来表达整数 题目描述 输入描述 输出描述 示例1 输入 输出 说明 示例2 输入 输出 解析 答案 亲子游戏 题目描述 输入描述 输出描述 示例1 输入 输出 说明 示例2 输入…

开发人员必备的常用工具合集-lombok

Project Lombok 是一个 java 库&#xff0c;它会自动插入您的编辑器和构建工具&#xff0c;为您的 Java 增添趣味。 再也不用编写另一个 getter 或 equals 方法了&#xff0c;只需一个注释&#xff0c;您的类就拥有了一个功能齐全的构建器&#xff0c;自动化了您的日志记录变量…

2024河南高考作文ChatGPT

阅读下面的材料&#xff0c;根据要求写作。&#xff08;60分&#xff09; 随着互联网的普及、人工智能的应用&#xff0c;越来越多的问题能很快得到答案。那么&#xff0c;我们的问题是否会越来越少&#xff1f; 以上材料引发了你怎样的联想和思考&#xff1f;请写一篇文章。 要…

低代码:加速企业数字化转型的利器

随着企业数字化转型步伐的加快&#xff0c;低代码开发平台迅速成为市场的焦点。凭借其能简化开发流程、缩短交付时间和降低成本等优势&#xff0c;低代码已经赢得了企业和开发人员的广泛认可&#xff0c;已成为推动企业数字化转型、提高企业创新效率、竞争力的关键工具。本文将…

R语言中的列表list

基础 在R语言中的最常用的向量有两种&#xff1a; 第一种&#xff0c;原子向量 像字符型向量&#xff0c;数值型向量&#xff0c;逻辑型向量这些&#xff0c;它们共有的一个特点是&#xff0c;向量里面的值是同质的。当你用数值型向量时&#xff0c;里面所有值都是数值型的。…

Node启动前端项目问题解决方案

Node.js不同版本下载地址 如何降低npm版本 https://blog.csdn.net/bobay/article/details/114262933 报错文件下载地址

红酒:如何选择适合的红酒储存容器

选择适合的红酒储存容器对于保持雷盛红酒的品质和风味至关重要。不同的容器具有不同的优缺点&#xff0c;因此应根据个人需求和条件进行选择。以下是一些常见的红酒储存容器的特点和适用场景&#xff1a; 玻璃瓶&#xff1a;玻璃瓶是常见的红酒储存容器。它具有良好的密封性能、…

粘性代理 vs 轮换代理: 特点、优势与选择指南

在网络领域&#xff0c;代理服务器是一种常见的工具&#xff0c;用于隐藏真实IP地址并提供更安全和匿名的网络体验。 粘性代理和轮换代理是两种常见的代理类型&#xff0c;它们在IP持久性和变更频率等方面有所不同。 本文将介绍粘性代理和轮换代理的区别&#xff0c;并分析在…

wordpress里面嵌入哔哩哔哩视频的方法

我们正常如果从blibli获取视频分享链接然后在wordpress里面视频URL插入&#xff0c;发现是播放不了的 而视频嵌入代码直接粘贴呢窗口又非常的小 非常的难受&#xff0c;就需要更改一下代码。你可以在在allowfullscreen"true"的后面&#xff0c;留1个空格&#xff…

GWT 与 Python App Engine 集成

将 Google Web Toolkit (GWT) 与 Python App Engine 集成可以实现强大的 Web 应用程序开发。这种集成允许你使用 GWT 的 Java 客户端技术构建丰富的用户界面&#xff0c;并将其与 Python 后端结合在一起&#xff0c;后端可以运行在 Google App Engine 上。 1、问题背景 在 Pyt…

从零开始手把手Vue3+TypeScript+ElementPlus管理后台项目实战四(引入Axios,并调用第一个接口)

RealWorld接口综述 本项目调用的是RealWorld项目的开放接口。 接口文档如下&#xff1a; https://main--realworld-docs.netlify.app/docs/specs/backend-specs/endpoints https://main--realworld-docs.netlify.app/docs/specs/frontend-specs/swagger RealWorld 是一个适…

C++多线程同步总结

C多线程同步总结 关于C多线程同步 一、C11规范下的线程库 1、C11 线程库的基本用法&#xff1a;创建线程、分离线程 #include<iostream> #include<thread> #include<windows.h> using namespace std; void threadProc() {cout<<"this is in t…

AI产品经理岗位需求量大吗?好找工作吗?

前言 在当今这个科技日新月异的时代&#xff0c;人工智能&#xff08;AI&#xff09;已不再仅仅是一个遥远的概念&#xff0c;而是深深嵌入到我们生活的方方面面&#xff0c;从日常的语音助手到复杂的自动驾驶系统&#xff0c;AI的触角无处不在。随着AI技术的广泛应用和持续进…