因工作需要,需要获取一个windos客户端软件控件的值,使用win32api.SendMessage获取不到。
使用spy++观察发现标题和内容全是None,
晚上搜了下,发现这种属于Powerbuilder语言开发出的数据窗口,也叫pb数据窗口
求指点怎么用python怎么获取这个窗口的数据吗? 卡了好久, 平台最高悬赏只有0.5k,如果能解决,愿意另外有偿感谢
补充:我不确定是Powerbuilder语言开发的
https://bbs.csdn.net/topics/70097737
这个链接的问题和我一样,但是八九年以前的了,而且也没有解决
一、原理
PB数据窗口有一个导出文件的函数,通过调用API向数据窗口传递特定的消息,它就乖乖的弹出一个导出数据文件的窗口了。
二、实现步骤
1、获取数据窗口的句柄。使用spy++工具获取,或者使用工具SpyLite24(http://www.asanscape.com/可以免费下载)
2、调用让数据窗口导出文件的消息。SendMessage(pbdw_handle, 1305, 0, 0)
三、C#代码
public class Win32API
{
[DllImport("user32.dll", CharSet=CharSet.Auto)]
public static extern int SendMessage(IntPtr hWnd, int msg, int wParam, int lParam);
}
调用示例:
private void button_Click(object sender, EventArgs e)
{
try
{
IntPtr mwh = (IntPtr)Convert.ToInt32(tbPBDWHandle.Text);
int result = Win32API.SendMessage(mwh, 1305, 0, 0);
if (result == 0)
{
MessageBox.Show("操作失败!请确定数据窗口句柄是否正确!");
}
}
catch (Exception ex)
{
MessageBox.Show(ex.ToString());
}
}
四、PBDW的其他函数和查看资料
Win32 API不能解决问题的话,那么思路应该是从进程空间的固定位置读取到这个值。控件中的值,肯定不会放在栈上,一般是在全局变量里,保证了地址是不会变的。
下面这篇仔细阅读,抄作业就是了!
https://www.cnblogs.com/predator-wang/p/4788643.html
最后,用PYTHON调用C,完成整个事情。
你试没试过用win32gui来获取
发私信给你个工具试试
看看这个能不能给你点帮助:外部程序读取Powerbuilder的数据窗口数据的方法_火山码哥的博客-CSDN博客
有两个点不知道你试过没:1)是否不同的控件需要使用不同的方法获取文本,比如,标签、按钮等的文本获取方式与下拉列表的文本获取方式不同;2)是否可能获取到的控件本身就是空的,而你想获取的其实在下一层
可以试试 绝对值的方式,直接操作坐标
参考一下这一个https://www.cnblogs.com/yarightok/p/6800540.html
程序涉密嘛?可以发给示例程序给我本地实验不?
如果现有方案无效的话,可以尝试两个思路:
试一试c#的代码可不可以
public class Win32API
{
[DllImport("user32.dll", CharSet=CharSet.Auto)]
public static extern int SendMessage(IntPtr hWnd, int msg, int wParam, int lParam);
}
调用示例:
private void button_Click(object sender, EventArgs e)
{
try
{
IntPtr mwh = (IntPtr)Convert.ToInt32(tbPBDWHandle.Text);
int result = Win32API.SendMessage(mwh, 1305, 0, 0);
if (result == 0)
{
MessageBox.Show("操作失败!请确定数据窗口句柄是否正确!");
}
}
catch (Exception ex)
{
MessageBox.Show(ex.ToString());
}
}
当然您也可以试一试这一个:
Function integer SndMsg(long hWnd, long uMsg, long wp, &
ref string filename) library "user32.dll" Alias For "SendMessageA"
long ll_dw_handle
ll_dw_handle = handle(dw_1)
string s
s="c:\111.txt" 这里把数据窗口的内容存到了文本文件111.txt
SndMsg(ll_dw_handle, 1305, 2, s)
可以转换下思路,尝试下使用截图,然后定位获取数据。
这边可以帮您看看,有过这方面的逆向经验,已私信楼主。
目前来看
一般就是三种方法,
但是按照你目前的回复来说,
至于其他的方法,如果方便的话,需要发一下具体的程序和需要获取的文本截图到网盘,
需要本地测试才能看看是否有具体的方法可行。
另外
如果没有更好的解决方法的话
个人觉得可能继续你原先的OCR方法可能更好点或者说可行性更高,
1、通过Python脚本或者其他脚本来进行具体的截图
2、然后通过OCR API来识别获取到具体值
这些大概就是你原先的OCR步骤,
你可能需要做的就是
挑选一些更加准确的OCR工具或者方法,免费的或者一些大公司的收费的OCR解决方法
注:是否选择收费的API接口看你自己需求
浏览器搜索关键词OCR API
查找
https://github.com/PaddlePaddle/PaddleOCR/blob/release/2.2/ppstructure/README_ch.md
如有问题及时沟通
使用ocr是最简单的办法,有一款可以识别数字的免费ocr识别软件,tesseract,可以先用图像处理opencv获取窗口位置,简单的阈值分割就可以得到弹出窗口,由于窗口句柄和值是固定位置,得到对应像素位置的crop数值对应的空格,调用tesseract识别数字即可获得。实在搞不定可以私信我可以发代码给你。
long ll_dw_handle
ll_dw_handle = handle(dw_1)
string s
s="c:\111.txt"
SndMsg(ll_dw_handle, 1305, 2, s)
如果无法理解可以参考C#这个链接详情比较详细,逻辑原理都是一样,只是语法不同。
这种需求确实用C#更好实现
要先确定应用程序的的backend是 是什么。
不过win上就支持两种,你现在用的应该是win32,换uia试一下。
不行的话,python该自动化思路无法实现。
app = Application(backend='uia')
spy++检查不到可以换其它工具https://github.com/blackrosezy/gui-inspect-tool
使用pyqt
可以试试这个
#include<windows.h>
#include<conio.h>
#include<cstdio>
BOOL CALLBACK EnumChildProc(HWND hwndChild, LPARAM lParam) {
LPRECT rcParent;
int i, idChild;
idChild = GetWindowLong(hwndChild, GWL_ID);
char ClassName[200], Title[200],Text[200];
GetWindowText(hwndChild, Title, 200);
GetClassName(hwndChild, ClassName, 200);
printf("Title:%s\n", Title);
GetWindowText(hwndChild,Text,200);
printf("内容:%s\n", Text);
printf("----------------\n");
HINSTANCE childHS = (HINSTANCE)GetWindowLong(hwndChild, GWL_HINSTANCE);
WNDCLASSEX cls1;
GetClassInfoEx(childHS, ClassName, &cls1);
printf("结构体大小:%d\n", cls1.cbSize);
printf("窗口类样式:%d\n", cls1.style);
printf("窗口处理函数:0X%X\n", cls1.lpfnWndProc);
printf("窗口类的额外信息:%d\n", cls1.cbClsExtra);
printf("窗口实例的额外信息:%d\n", cls1.cbWndExtra);
printf("本模块的实例句柄:0X%X\n", cls1.hInstance);
printf("窗口类图标:0X%X\n", cls1.hIcon);
printf("窗口类鼠标:0X%X\n", cls1.hCursor);
printf("窗口类的背景刷:0X%X\n", cls1.hbrBackground);
printf("窗口类的菜单名:%s\n", cls1.lpszMenuName);
printf("窗口类名字:%s\n", cls1.lpszClassName);
printf( "窗口类小图标:%s\n", cls1.hIconSm);
printf("控件ID:%d\n",GetDlgCtrlID(hwndChild));
printf("----------------\n");
return TRUE;
}
int main() {
HWND hwnd;
char Title[200]="Test";
hwnd = FindWindow(NULL,Title);
EnumChildWindows(hwnd, EnumChildProc, NULL);
return 0;
}
我可以准确的告诉你,如果是自绘出来的,或者只要不是win平台的控件,是根本获取不到控件句柄的,所以建议你获取到整个窗口句柄,然后进行模拟操作吧,比如可以发送消息点击固定的位置的按钮,或者按照窗口句柄,区域性的截取窗口,然后进行识别图片进行操作,这都是可以的,并不是说操作控件就非要知道控件句柄,没有控件句柄一样可以操作
楼主可以尝试下这个工具inspect.exe,可以在Windows SDK文件夹下找到该inspect.exe工具,该文件夹通常是C:\Program Files (x86)\Windows Kits\10\bin\x86
试试这个工具
获取数据窗口的句柄。使用spy++工具获取,或者使用工具SpyLite24
http://www.asanscape.com/
望采纳谢谢啦
我觉得可以有两种方案解决你的问题:
1、第一种方式通过窗口句柄和坐标获取到控件位置和控件绘制信息,从绘制的图片信息中ocr解析得到内容,这种方式因为是反向识别因此效率较低,准确率也不能完全保证,是比较被动的方式;
2、第二种方式就是在pb中开发,通过pb获取到窗口和控件,能够直接得到控件的值并进行操作和修改,这种方式需要在pb中开发并且运行,可以直接运用pb的内部方法直接获取,不会有效率和准确性的问题,应能较好满足你的需求。
从你的需求角度,个人建议采用第二种方式实现,只需简单掌握pb开发就能实现。有帮助请采纳谢谢!
如果随便能获取,那操作系统层面岂不是有重大漏洞。
这个用 QTP 应该可以做到。我原先就用QTP获取并定位windows桌面上的窗体和内容。 python与qtp结合用。
1、第一种方式通过窗口句柄和坐标获取到控件位置和控件绘制信息,从绘制的图片信息中ocr解析得到内容,这种方式因为是反向识别因此效率较低,准确率也不能完全保证,是比较被动的方式;
2、第二种方式就是在pb中开发,通过pb获取到窗口和控件,能够直接得到控件的值并进行操作和修改,这种方式需要在pb中开发并且运行,可以直接运用pb的内部方法直接获取,不会有效率和准确性的问题,应能较好满足你的需求。
从你的需求角度,个人建议采用第二种方式实现,只需简单掌握pb开发就能实现
能有个Powerbuilder 实例程序就方便了, 没有测试对象, 空谈理论 都不好使: 也可以考虑一下绕过Powerbuilder 直接从Powerbuilder 的数据源获取原始数据嘛,
1、不是所有程序都可以通过系统来获取其句柄,进而拿到程序进程的数据,跨进程的通信其实是很复杂的
2、win32提供的api可以访问的窗口组件,以及可以获取的窗口组件属性有限,其实很多时候不能满足全部需求。
3、控件的句柄会作为回调函数的参数传进去。"属性"的话,只能通过API获取。主要使用 "SendMessage",因为SDK中程序和控件的交互基本都是通过 windows 消息实现的。没有办法简单地获得一个控件的 "所有属性值",只能通过一个一个的方式按需读取。
4、尝试一下这款工具 "Control Spy" , 但是这个工具只能快速了解原生组件,对于少见的第三方组件,没有太大的作用。
一、原理
PB数据窗口有一个导出文件的函数,通过调用API向数据窗口传递特定的消息,它就乖乖的弹出一个导出数据文件的窗口了。
二、实现步骤
1、获取数据窗口的句柄。使用spy++工具获取,或者使用工具SpyLite24(http://www.asanscape.com/可以免费下载)
2、调用让数据窗口导出文件的消息。SendMessage(pbdw_handle, 1305, 0, 0)
三、C#代码
public class Win32API
{
[DllImport("user32.dll", CharSet=CharSet.Auto)]
public static extern int SendMessage(IntPtr hWnd, int msg, int wParam, int lParam);
}
调用示例:
private void button_Click(object sender, EventArgs e)
{
try
{
IntPtr mwh = (IntPtr)Convert.ToInt32(tbPBDWHandle.Text);
int result = Win32API.SendMessage(mwh, 1305, 0, 0);
if (result == 0)
{
MessageBox.Show("操作失败!请确定数据窗口句柄是否正确!");
}
}
catch (Exception ex)
{
MessageBox.Show(ex.ToString());
}
}