0
点赞
收藏
分享

微信扫一扫

Python调用外部EXE程序,遍历窗体及控件,并获取控件信息。

骑在牛背上看书 2022-02-07 阅读 120
python

背景:
我工作中经常运行一个windows程序,获取并使用该程序的计算结果,但是该程序又不能满足我们的自动化要求,又无法破解其算法,怎么办?那就用Python做个批处理任务吧。关键点是如何运行外部exe、获取句柄及一些操作。

关键步骤:
1. 启动和关闭应用程序,我采用非阻塞式启动应用程序。
2. 遍历窗体。
3. 触发按钮。
4. 获取文本框内容。

环境:
1.程序使用到了Spy++ 工具。

# _*_ encoding:utf-8 _*_
import time
import logging
import win32gui
import win32con
import os
import subprocess
import psutil
from timeloop import Timeloop
from datetime import timedelta

# 创建记录器logger
logger = logging.getLogger('DevicePassword')
logger.setLevel(logging.DEBUG)
# 创建文件处理器FileHandler
ch_file = logging.FileHandler('logger.log', encoding='utf-8')  #  输出到文件
ch_file.setLevel(logging.INFO)
ch_Terminal = logging.StreamHandler()  # 输出到屏幕
ch_Terminal.setLevel(logging.INFO)

# 创建格式器Formatter
formater = logging.Formatter('%(asctime)s - %(name)s - %(levelname)s - %(message)s')
# 配置logger
ch_file.setFormatter(formater)
ch_Terminal.setFormatter(formater)
logger.addHandler(ch_file)
logger.addHandler(ch_Terminal)

appname = 'app.exe'  #  你的程序文件名称
titlename = 'PasswordGen'   # 程序主窗口的标题名称,如果不知道,可通过Spy++获取。


def startapp(name):
    """
    启动外部EXE程序
    :param name: 程序名称
    :return: None
    """
    if exe_is_active(name):  # 若外部程序已存在,则终止外部程序。
        os.system(r'taskkill /F /IM {}'.format(name))
        time.sleep(2)

    if os.path.exists(name):  # 非阻塞启动外部EXE程序。
        subprocess.Popen([name])
    else:
        print("软件不存在,请将{}复制到程序相同目录下。".format(appname))


def closeapp(name):
    """
    关闭外部EXE程序
    :param name:
    :return:
    """
    if exe_is_active(name):  # 若外部程序已存在,则终止外部程序。
        os.system(r'taskkill /F /IM {}'.format(name))
        time.sleep(2)


def exe_is_active(name):
    """
    判断外部EXE程序是否已经启动
    :param name: 外部程序名
    :return: TRUE  or  FALSE
    """
    processes_name = []
    pids = psutil.pids()
    for pid in pids:
        p = psutil.Process(pid)
        processes_name.append(p.name())
    if name in processes_name:
        print('{} 已存在'.format(name))
        return True
    else:
        print('{} 不存在'.format(name))
        return False


def click_generate(btngenerate):
    """
    点击Generate按钮,生成密码
    :param btngenerate: 按钮句柄
    :return:None
    """
    win32gui.SendMessage(btngenerate, win32con.WM_LBUTTONDOWN, win32con.MK_LBUTTON, 0)
    win32gui.SendMessage(btngenerate, win32con.WM_LBUTTONUP, win32con.MK_LBUTTON, 0)


def find_control(maintitlename):
    hwnd = win32gui.FindWindow(None, maintitlename)  # 获取主窗口句柄
    hwnd_child_list = []   #  子控件数组,存储子控件
    if hwnd > 0:
        win32gui.EnumChildWindows(hwnd, lambda hwnd, param: param.append(hwnd), hwnd_child_list)
    return hwnd_child_list  #  返回子控件数组


def get_edit(edit_hwnd):
    """
    获取密码控件内容
    :param edit_hwnd: 密码控件句柄
    :return: 控件内容text
    """
    len_text = win32gui.SendMessage(edit_hwnd, win32con.WM_GETTEXTLENGTH) + 1  # 获取文本内容长度
    buffer = win32gui.PyMakeBuffer(len_text)  # 初始化缓存
    win32gui.SendMessage(edit_hwnd, win32con.WM_GETTEXT, len_text, buffer)  # 发送消息,获取控件内容
    address, length = win32gui.PyGetBufferAddressAndLen(buffer)  # 转换缓存内容
    text = win32gui.PyGetString(address, length - 1)
    return text


def sync_cb_device_password(pwd):   # 将获取的密码更新到密码表中,供设备使用。这样就不用我每次手动更新了。
    sql = 'UPDATE device set DeviceUserName= %s , DevicePassword = %s'
    params = ('admin', pwd)
    with DBMysql(log_time=False) as db:  # DBMysql自定义数据库类,执行SQL。
        db.cursor.execute(sql, params)
        data = db.cursor.rowcount
        print('同步设备密码-sync_device_password:{},更改记录数量:{}'.format(pwd, data))
        if not data or data is None or data == 0 or data > 1:
            isset = False
        else:
            isset = True
    return isset


def main():
    startapp(appname)
    time.sleep(5)
    # 获取窗体控件
    handle = find_control(titlename)
    # 点击按钮,生成密码
    click_generate(handle[5])   #  handle[5] 是按钮的句柄,通过Spy++获取。  这里应该可以通过控件名称获取,以后优化。
    # 获取密码内容
    password = get_edit(handle[4])  #  handle[4] 是按钮的句柄,通过Spy++获取。  这里应该可以通过控件名称或类名获取,以后优化。
    print("密码:{}".format(password))

    time.sleep(3)
    sync_cb_device_password(password)
    closeapp(appname)
    logger.info('今天的密码是:{}'.format(password))


举报

相关推荐

0 条评论