Windows窗体数据抓取详解
最近在客户项目上刚好遇到一个问题,项目需求是要获取某台机床的实时状态,问题点刚好就在于该机床不是传统意义上的数控机床,也不是PLC控制器,只有一个上传下载程序文件的应用程序,上面刚好有几个按钮可以大概判断当前工作状态,转眼一想,是否可以实时获取几个按钮的状态,从而简单分析下就确定机床加工状态。
说干就干,开始拿起放下已久的Win32API来试试。思路大概如下:
- 首先,我们知道的是应用程序的进程名称如:notepad.exe
- 然后,就要通过进程名获取窗口句柄(HWND)
- 其次,通过窗口句柄遍历子窗口句柄,通过其获取相关数据,比如:Button是否被可用、Button的Text、CheckButton是否被选中等等一些列想要的操作。此处我们就抓取记事本内容吧(内容在Edit控件中)
- 最后,就是实时更新、存储数据即可,进行后期逻辑处理
获取进程ID
首先当我们知道进程名,通过进程名获取进程ID,我们需要用到一个Win32的进程快照模块:CreateToolhelp32Snapshot 可以通过获取进程信息为指定的进程、进程使用的堆[HEAP]、模块[MODULE]、线程建立一个快照。
HANDLE WINAPI CreateToolhelp32Snapshot(
__in DWORD dwFlags,
__in DWORD th32ProcessID
);
参数:
- dwFlags 用来指定“快照”中需要返回的对象,指定快照中包含的系统内容,这个参数能够使用下列数值(常量)中的一个或多个。
- TH32CS_INHERIT :声明快照句柄是可继承的。
- TH32CS_SNAPALL : 在快照中包含系统中所有的进程和线程。
- TH32CS_SNAPHEAPLIST : 在快照中包含在th32ProcessID中指定的进程的所有的堆。
- TH32CS_SNAPMODULE : 在快照中包含在th32ProcessID中指定的进程的所有的模块。
- TH32CS_SNAPPROCESS : 在快照中包含系统中所有的进程。
- TH32CS_SNAPTHREAD : 在快照中包含系统中所有的线程。
- th32ProcessID 指定将要快照的进程ID。如果该参数为0表示快照当前进程。该参数只有在设置了TH32CS_SNAPHEAPLIST或者TH32CS_SNAPMODULE后才有效,在其他情况下该参数被忽略,所有的进程都会被快照。
返回值: 调用成功,返回快照的句柄,调用失败,返回INVALID_HANDLE_VALUE 。
废话不多说,直接上代码:
DWORD GetProcessIdFromProcessName(std::string processname)
{
DWORD dwRet = 0;
PROCESSENTRY32 pe32;
pe32.dwSize = sizeof(pe32);
HANDLE hProcessSnap = ::CreateToolhelp32Snapshot(TH32CS_SNAPPROCESS, 0);
if (hProcessSnap != INVALID_HANDLE_VALUE)
{
BOOL bMore = ::Process32First(hProcessSnap, &pe32);
while (bMore)
{
if (boost::iequals(pe32.szExeFile, processname))
{
dwRet = pe32.th32ProcessID;
}
bMore = ::Process32Next(hProcessSnap, &pe32);
}
::CloseHandle(hProcessSnap);
}
return dwRet;
}
调用测试:
std::string str1 = "notepad.exe";
DWORD dwProcessId = GetProcessIdFromProcessName(str1);
std::cout << dwProcessId << std::endl; //

获取到了进程,那就进入下一节,获取窗口句柄。
获取窗口句柄HWND
通过进程ID获取窗口句柄,那么就需要遍历窗口了,找到符合我们需求的进程所对应的窗口句柄了,这个地方就会用到一个函数:EnumWindows 枚举所有屏幕上的顶层窗口,并将窗口句柄传送给应用程序定义的回调函数。
BOOL EnumWindows(
WNDENUMPROC lpEnumFunc,
LPARAM lParam
);
参数:
- lpEnumFunc:指向一个应用程序定义的回调函数指针,请参看EnumWindowsProc。
- lPararm:指定一个传递给回调函数的应用程序定义值。
返回值:如果函数成功,返回值为非零;如果函数失败,返回值为零。若想获得更多错误信息,请调用GetLastError函数。
回调函数:
BOOL CALLBACK EnumWindowsProc(
HWND hwnd,
LPARAM lParam
);
参数:
- hwnd:顶层窗口的句柄
- lparam:应用程序定义的一个值(即EnumWindows中lParam)
返回值:TRUE继续遍历,FALSE停止遍历
注: EnumWindows函数不列举子窗口。
那么接下来就是开始获取窗口句柄了。
首先定一个结构体
该机构体由于标记进程和窗口句柄:
typedef struct tagHWNDINFO
{
DWORD dwProcessId;
HWND hWnd;
} HWNDINFO, *LPHWNDINFO;
枚举所有窗口
BOOL CALLBACK EnumWindowProc(HWND hWnd, LPARAM lParam)
{
DWORD dwProcId;
GetWindowThreadProcessId(hWnd, &dwProcId);
LPHWNDINFO pInfo = (LPHWNDINFO)lParam;
if (dwProcId == pInfo->dwProcessId)
{
if (GetParent(hWnd) == NULL && IsWindowVisible(hWnd)) //判断是否顶层窗口并且可见
{
pInfo->hWnd = hWnd;
return FALSE;
}
else
return TRUE;
}
return TRUE;
}
获取指定进程ID窗口句柄
HWND GetProcessMainWnd(DWORD dwProcessId)//获取给定进程ID的窗口HWND
{
HWNDINFO wi;
wi.dwProcessId = dwProcessId;
wi.hWnd = NULL;
EnumWindows(EnumWindowProc, (LPARAM)&wi);
return wi.hWnd;
}
调用测试:
std::string processname = "notepad.exe";
DWORD dwProcessId = GetProcessIdFromProcessName(processname);
std::cout << dwProcessId << std::endl;
if (dwProcessId != 0)
{
HWND hwnd = GetProcessMainWnd(dwProcessId);
if (hwnd != NULL)
{
char WindowTitle[100] = { 0 };
::GetWindowText(hwnd, WindowTitle, 100);
std::cout << WindowTitle << std::endl;
}
}
结果输出:
11712
无标题 - 记事本

现在已经获取了记事本主窗口句柄了,下一步就是遍历子窗口了。
遍历子窗口
我们的目标是抓取窗体中信息,这时候介绍一个工具,相当的好用Spy++(具体怎么用,就自己百度了)

现在我们要获取的就是下面的Edit框内容。此处我们又需要遍历子窗口,需用到一个方法EnumChildWindows 枚举一个父窗口的所有子窗口。
BOOL EnumChildWindows(
HWND hWndParent,
WNDENUMPROC lpEnumFunc,
LPARAM lParam
);
参数:
- hWndParent: 父窗口句柄
- lpEnumFunc: 回调函数的地址
- lParam: 自定义的参数
回调函数如下:
BOOL CALLBACK EnumChildProc(
HWND hwnd,
LPARAM lParam
);
参数:
- hwnd:父窗口指定的一个子窗口句柄
- lParam:EnumChidWindows指定的参数
返回值:如果返回TRUE,则枚举继续直到枚举完成;如果返回FALSE,则将会中止枚举。
直接亮代码:
BOOL CALLBACK EnumNotepadChildWindowsProc(HWND hWnd, LPARAM lParam)
{
char szTitle[100] = { 0 };
::GetWindowText(hWnd, szTitle, 100);
long lStyle = GetWindowLong(hWnd, GWL_STYLE);
if (lStyle & ES_MULTILINE)
{
long lineCount = SendMessage(hWnd, EM_GETLINECOUNT, 0,0);
for (int i = 0; i < lineCount; i++)
{
//long chCount = SendMessage(hWnd, EM_LINELENGTH, (WPARAM)i, 0);
char szContent[200] = { 0 };
szContent[0] = 200; //此处注意下,如果不设置EM_GETLINE无法获取内容
long ret = SendMessage(hWnd, EM_GETLINE, (WPARAM)i, (LPARAM)(LPCSTR)szContent);
if (ret > 0)
{
szContent[ret] = '\0';
std::cout << "line: " << i << ", Content: " << szContent << std::endl;
}
}
}
else
{
std::string title(szTitle);
if (!title.empty())
std::cout << title << std::endl;
}
return true;
}
调用如下:
std::string processname = "notepad.exe";
DWORD dwProcessId = GetProcessIdFromProcessName(processname);
std::cout << dwProcessId << std::endl;
if (dwProcessId != 0)
{
HWND hwnd = GetProcessMainWnd(dwProcessId);
if (hwnd != NULL)
{
char WindowTitle[100] = { 0 };
::GetWindowText(hwnd, WindowTitle, 100);
std::cout << WindowTitle << std::endl;
EnumChildWindows(hwnd, EnumNotepadChildWindowsProc, NULL);
}
}
结果如下:
line: 0, Content: 第一行 iceman
line: 1, Content: 第二行 Hello
line: 2, Content: 第三行 World
到此,就完成既定目标了
Windows窗体数据抓取详解的更多相关文章
- wireshark在windows下无法抓取localhost数据包
在调试SSL时要抓包,通过tcpview和minisniffer等工具明明看到tcp连接已经建立并开始收发数据了,但wireshark却总是无法抓到相应的数据包. 今天早上,HQ的高工告诉我“wire ...
- Python爬虫工程师必学——App数据抓取实战 ✌✌
Python爬虫工程师必学——App数据抓取实战 (一个人学习或许会很枯燥,但是寻找更多志同道合的朋友一起,学习将会变得更加有意义✌✌) 爬虫分为几大方向,WEB网页数据抓取.APP数据抓取.软件系统 ...
- 吴裕雄--天生自然python学习笔记:WEB数据抓取与分析
Web 数据抓取技术具有非常巨大的应用需求及价值, 用 Python 在网页上收集数据,不仅抓取数据的操作简单, 而且其数据分析功能也十分强大. 通过 Python 的时lib 组件中的 urlpar ...
- Phantomjs+Nodejs+Mysql数据抓取(2.抓取图片)
概要 这篇博客是在上一篇博客Phantomjs+Nodejs+Mysql数据抓取(1.抓取数据) http://blog.csdn.net/jokerkon/article/details/50868 ...
- Phantomjs+Nodejs+Mysql数据抓取(1.数据抓取)
概要: 这篇博文主要讲一下如何使用Phantomjs进行数据抓取,这里面抓的网站是太平洋电脑网估价的内容.主要是对电脑笔记本以及他们的属性进行抓取,然后在使用nodejs进行下载图片和插入数据库操作. ...
- Java实现多种方式的http数据抓取
前言: 时下互联网第一波的浪潮已消逝,随着而来的基于万千数据的物联网时代,因而数据成为企业的重要战略资源之一.基于数据抓取技术,本文介绍了java相关抓取工具,并附上demo源码供感兴趣的朋友测试! ...
- python3爬虫再探之豆瓣影评数据抓取
一个关于豆瓣影评的爬虫,涉及:模拟登陆,翻页抓取.直接上代码: import re import time import requests import xlsxwriter from bs4 imp ...
- 数据抓取的艺术(一):Selenium+Phantomjs数据抓取环境配置
数据抓取的艺术(一):Selenium+Phantomjs数据抓取环境配置 2013-05-15 15:08:14 分类: Python/Ruby 数据抓取是一门艺术,和其他软件不同,世界上 ...
- python爬虫数据抓取方法汇总
概要:利用python进行web数据抓取方法和实现. 1.python进行网页数据抓取有两种方式:一种是直接依据url链接来拼接使用get方法得到内容,一种是构建post请求改变对应参数来获得web返 ...
随机推荐
- TDSQL“相似查询工具MSQL+”入选VLDB论文
欢迎大家前往腾讯云+社区,获取更多腾讯海量技术实践干货哦~ 本文由腾讯云数据库 TencentDB发表于云+社区专栏 作者介绍:王晓宇,腾讯数据库TDSQL团队成员,目前参与TDSQL数据库内核研发工 ...
- C++ STL 学习
/* algorithm-算法 */ .copy() //此函数用在vector中只做拷贝使用,它不能让vector有自动扩充作用.如果vector的容量小于它拷贝的数据量将会报错. /* itera ...
- Eclipse 处理 Console 打印信息自动删除
开发中,特识是需要项目运行打印日志很长的时候需要查看打印的日志, Eclipse没经过设定的话,会自动80000行之前的日志记录. 想要日志一直打印下去处理方法: Preferences --> ...
- 深入出不来nodejs源码-timer模块(JS篇)
鸽了好久,最近沉迷游戏,继续写点什么吧,也不知道有没有人看. 其实这个node的源码也不知道该怎么写了,很多模块涉及的东西比较深,JS和C++两头看,中间被工作耽搁回来就一脸懵逼了,所以还是挑一些简单 ...
- 自己写一个java的mvc框架吧(四)
自己写一个mvc框架吧(四) 写一个请求的入口,以及初始化框架 上一章写了获取方法的入参,并根据入参的参数类型进行数据转换.这时候,我们已经具备了通过反射调用方法的一切必要条件.现在我们缺少一个htt ...
- 利用JavaMail发送邮件:smtp.163.com
一.利用JavaMail发送邮件案例: 1.maven项目结构: 2.先在pom.xml里边加入Javamail依赖,系统会根据坐标自动下载mail包(前提是配置好了maven): 3.配置email ...
- 小程序通过用户授权获取手机号之getPhoneNumber
小程序有一个获取用户很便捷的api,就是通过getPhoneNumber获取用户的已经绑定微信的手机号码.有一点要大家注意,现在微信和注重用户体验,有些方法都是需要用户主动去触发才能调用的,比如get ...
- ThinkPHP5下自己写日志
1.首先在common.php公共函数文件下写需要的公共函数(appalication/common.php文件下),在此文件下写的函数可以在项目任意页面直接调用 /** * 打印log日志 * @p ...
- python网络爬虫抓取动态网页并将数据存入数据库MySQL
简述以下的代码是使用python实现的网络爬虫,抓取动态网页 http://hb.qq.com/baoliao/ .此网页中的最新.精华下面的内容是由JavaScript动态生成的.审查网页元素与网页 ...
- bzoj 2406: 矩阵 ——solution
对于100%的数据满足N,M<=200,0<=L<=R<=1000,0<=Aij<=1000 http://www.lydsy.com/JudgeOnline/pr ...