背景
我的工作中经常手工运行一个windows程序(密码生成工具),获取该程序的计算结果,手工填到登录表单的中。该程序非常久远,已无人维护。根据凡是重复2次以上的工作都应该自动化原则,那么我来写个自动化脚本,减轻手工工作。
关键点是如何运行外部exe、获取句柄及一些操作。
***喜欢此文档的朋友,请点个赞吧!***
步骤
1. 启动应用程序,采用非阻塞式启动应用程序。 `subprocess.Popen([name])`
2. 关闭应用程序 `os.system(r'taskkill /F /IM {}'.format(name))`
3. 遍历窗体。 `hwnd = win32gui.FindWindow(None, maintitlename) # 获取主窗口句柄`
4. 遍历控件 `win32gui.EnumChildWindows(hwnd, lambda hwnd, param: param.append(hwnd), hwnd_child_list)`
5. 触发按钮。 `win32gui.SendMessage(btngenerate, win32con.WM_LBUTTONDOWN, win32con.MK_LBUTTON, 0)
win32gui.SendMessage(btngenerate, win32con.WM_LBUTTONUP, win32con.MK_LBUTTON, 0)`
6. 获取文本框内容。 `win32gui.SendMessage(edit_hwnd, win32con.WM_GETTEXT, len_text, buffer) # 发送消息,获取控件内容`
环境
1.程序使用到了Spy++ 工具。
代码
import time
import logging
import win32gui
import win32con
import os
import subprocess
import psutil
from timeloop import Timeloop
from datetime import timedelta
logger = logging.getLogger('DevicePassword')
logger.setLevel(logging.DEBUG)
ch_file = logging.FileHandler('logger.log', encoding='utf-8')
ch_file.setLevel(logging.INFO)
ch_Terminal = logging.StreamHandler()
ch_Terminal.setLevel(logging.INFO)
formater = logging.Formatter('%(asctime)s - %(name)s - %(levelname)s - %(message)s')
ch_file.setFormatter(formater)
ch_Terminal.setFormatter(formater)
logger.addHandler(ch_file)
logger.addHandler(ch_Terminal)
appname = 'app.exe'
titlename = 'PasswordGen'
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):
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:
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])
password = get_edit(handle[4])
print("密码:{}".format(password))
time.sleep(3)
sync_cb_device_password(password)
closeapp(appname)
logger.info('今天的密码是:{}'.format(password))