不错我又来分享了
没错该脚本还是给应用库同学写的,毕竟别人负责提需求,咱们负责实现
应用库同学的需求就是,自动测试商店内的exe文件安装并运行(咱们是应用商店项目,具体是哪一个,咱就不说了)
1:第一步也就是咱们的神器,咱商店有一个xml格式的文件记录了应用安装后会在注册表注册的名称,还有启动执行的exe路径(当然没有这个文件,咱们也是可以运行的哈)
类似这样色的

需要使用该函数读取该xml文件,这里你可能会问name从何而来,待我慢慢道来
2:这个函数可以读取exe的产品名称,把该产品名称放入上述函数读取对应的数据,当然读不到也是没关系滴,大不了就是运行不了嘛,咱们不是还有手么,手动运行噻,哈哈哈开玩笑,山人自有妙计

3:奥安装还忘说了,安装的咱们就不手动点点点的安装了,咱们执行静默安装exe的命令,有/S
/SILENT /qn啥啥啥的
请看下图,首先我维护了name.json,silent.json文件
name:有时候exe比较鸡贼,解析出的名称和xml中不一致,这时候咱们就查不到了,这时候怎么办呢,咱就去商店内查找该exe对应的名称,然后以键值对格式写入进去
![]()
silent:安装时我会把静默安装成功的命令和对应的产品名称写入进去,这样下次运行相同产品名称时不用一个一个试了,直接运行安装成功的命令

当然在代码最后,也会更新silent安装命令,咱就是说万一下次他变了呢,没事他变咱也变

然后的然后咱们就来到安装的步骤了,具体代码自己看哈,install_函数的代码太长了截不了
在安装之前呢,咱们需要到注册表把之前已安装的名称导出来,需要使用该函数,返回的是集合

把两个集合传入install_exe函数,还有exe文件路径,和解析出的exe产品名称,这里我就会用解析出的产品名称去silent文件查找,如果有直接运行静默安装命令,如果没有咱就一个一个试,成功了咱就在写进去

install_函数呢,主要是判断安装是否成功的,上述函数组合安装命令,并传入安装之前的注册表有的应用
运行安装命令后我设置了35秒的时间,如果超时默认安装失败,如返回了返回码,查看安装后的注册表并和安装前作差,有表示成功,如果没有判断返回码是否为0,为0则成功。不为0基本是失败,这是我测试好多次得出的结果

咱就是说,都报错了还返回啥集合差呢,但是代码好看啊

安装说完了,下边就是重头戏了运行exe,这块就像怪猎粪怪一样恶心,这部分代码是建立在安装成功的基础之上
1:如果咱们在xml文件中读取到了注册表项并和安装后的添加到list中
2:如果有注册表项,开始查找需要下述函数,name是注册表对应的key

key是xml文件中读取出来的,exestr这项,如果xml文件中的全都读取了还是没有获取到启动路径
那么我写了一个DisplayIcon,大多数应用在这项都会填写启动路径



如果通过注册表获取到了启动路径,我会先替换启动路径中的冒号,及,0这样的,通过下述函数。毕竟有的厂商会在启动路径做手脚,这是我整理的一部分

替换后就可以开始运行了cmd为启动路径,imagefile不管是启动后的截图,file_name也不管也和截图相关
执行后我给进程的运行时间为30秒,如果有返回码,我会通过is_process_running函数判断该启动路径的exe文件进程是否在运行,毕竟有的应用在安装成功后就会运行,通过分割启动路径就会获取启动exe的名称,查找就很方便了



如果运行成功后我会给run_result置为True,下边会判断该变量,如果为True就报告应用启动成功
如果没有运行成功,我会判断在xml中是否取到link路径,如果有link路径我会获取link中的启动路径然后再次运行,变置run_result为对应结果,查找link需要以下函数



最后源代码双手呈上,没有xml文件也可以安装运行大部分exe文件了,除非厂商写入的DisplayIcon不是启动路径,这就没招了,神仙来了也救不了了
import datetime
import logging
import time
import winreg
import os
import subprocess
import pefile
import xml.etree.ElementTree as ET
import psutil
import pyautogui
import getpass
import json
import win32com.client
import urllib.request
import requests
def read_Json(file_path):
try:
with open(file_path, 'r', encoding='utf-8') as file:
data = json.load(file)
return data
except FileNotFoundError:
logger.info(f"未找到文件: {file_path}")
except json.JSONDecodeError:
logger.info(f"文件 {file_path} 不是有效的 JSON 格式。")
def write_Json(file_path,dict_):
try:
with open(file_path, 'r', encoding='utf-8') as file:
data = json.load(file)
data.update(dict_)
with open(file_path, 'w', encoding='utf-8') as file:
json.dump(data, file, indent=4, ensure_ascii=False)
file.flush()
logger.info(dict_)
logger.info(file_path+"写入成功,并刷新缓冲区")
except FileNotFoundError:
logger.info(f"未找到文件: {file_path}")
except json.JSONDecodeError:
logger.info(f"文件 {file_path} 不是有效的 JSON 格式。")
def get_shortcut_target(shortcut_path):
shell = win32com.client.Dispatch("WScript.Shell")
shortcut = shell.CreateShortCut(shortcut_path)
return shortcut.Targetpath
def link_exist(link):
directorys = ["C:\ProgramData\Microsoft\Windows\Start Menu\Programs\\"]
username = getpass.getuser()
if username:
path = f"C:\\Users\\{username}\AppData\Roaming\Microsoft\Windows\Start Menu\Programs\\"
directorys.append(path)
for d in directorys:
link_path = d + link
if os.path.exists(link_path):
logger.info(link_path)
return replace_str(get_shortcut_target(link_path))
return False
def get_xml_from_name(name):
file = r'D:\exetest\utils\public.xml'
tree = ET.parse(file)
root = tree.getroot()
reguninst = []
exestr = {}
link = []
for element in root.findall('.//soft'):
name_element = element.find('name')
if name_element is not None and name_element.text == name:
parent_element = element
for reg in parent_element.find("reguninst").findall("value"):
reguninst.append(reg.text)
for exe in parent_element.find("exestr").findall("method"):
try:
ele_reg = exe.find("reg")
if ele_reg is not None:
ele_file = exe.find("file")
if ele_file is not None:
if ele_reg.text == "InstallLocation":
if judge_exepath(ele_file.text):
exestr["InstallLocation"] = exe.find("file").text
if ele_reg.text == "DisplayIcon":
if judge_exepath(ele_file.text):
exestr["DisplayIcon"] = exe.find("file").text
if ele_reg.text == "UninstallString":
if judge_exepath(ele_file.text):
exestr["UninstallString"] = exe.find("file").text
ele_link = exe.find("link")
if ele_link is not None:
link.append(ele_link.text)
except Exception as e:
logger.info(f"解析{file}文件出错:{e}")
continue
return reguninst,exestr,link
def get_exe_app_name(exe_path):
try:
pe = pefile.PE(exe_path)
if hasattr(pe, 'VS_VERSIONINFO') and hasattr(pe, 'FileInfo'):
for entry in pe.FileInfo[0]:
if hasattr(entry, 'StringTable'):
for st_entry in entry.StringTable:
if hasattr(st_entry, 'entries'):
for key, value in st_entry.entries.items():
if key.decode() == 'ProductName':
return value.decode()
return None
except Exception as e:
logger.info(f"解析 .exe 文件时出错: {e}")
logger.info(f"使用原文件名")
return None
def get_files_in_specific_directory(directory_path):
file_list = []
try:
for file in os.listdir(directory_path):
file_full_path = os.path.join(directory_path, file)
if os.path.isfile(file_full_path):
file_list.append(file_full_path)
except FileNotFoundError:
logger.info(f"目录不存在:{directory_path}")
return file_list
def create_directory(directory_path):
try:
os.mkdir(directory_path)
# logger.info(f"{directory_path} 目录创建成功")
except FileExistsError:
# logger.info(f"{directory_path} 目录已经存在")
print(f"{directory_path} 目录已经存在")
# delete_all_files(directory_path)
except PermissionError:
# logger.warning(f"{directory_path} 没有权限创建目录")
print(f"{directory_path} 没有权限创建目录")
def get_currentUser_installed_apps(registry_key,name=None):
registry_path = r"Software\Microsoft\Windows\CurrentVersion\Uninstall"
key = winreg.OpenKey(winreg.HKEY_CURRENT_USER, registry_path)
subkey_count = winreg.QueryInfoKey(key)[0]
for i in range(subkey_count):
subkey_name = winreg.EnumKey(key, i)
subkey = winreg.OpenKey(key, subkey_name)
if subkey_name == registry_key:
if name:
try:
return winreg.QueryValueEx(subkey, name)[0]
except:
return False
else:
return True
return False
def get_installApps_reg():
registry_paths = [
r"Software\Microsoft\Windows\CurrentVersion\Uninstall",
r"SOFTWARE\Wow6432Node\Microsoft\Windows\CurrentVersion\Uninstall"
]
current_user = set()
local_machine = set()
regs = [winreg.HKEY_LOCAL_MACHINE, winreg.HKEY_CURRENT_USER]
for reg in regs:
if reg == winreg.HKEY_CURRENT_USER:
key = winreg.OpenKey(reg, registry_paths[0])
subkey_count = winreg.QueryInfoKey(key)[0]
for i in range(subkey_count):
subkey_name = winreg.EnumKey(key, i)
current_user.add(subkey_name)
else:
for registry_path in registry_paths:
key = winreg.OpenKey(reg, registry_path)
subkey_count = winreg.QueryInfoKey(key)[0]
for i in range(subkey_count):
subkey_name = winreg.EnumKey(key, i)
local_machine.add(subkey_name)
return current_user,local_machine
def get_installedApp_key_value(registry_key,name):
registry_paths = [
r"Software\Microsoft\Windows\CurrentVersion\Uninstall",
r"SOFTWARE\Wow6432Node\Microsoft\Windows\CurrentVersion\Uninstall"
]
regs = [winreg.HKEY_LOCAL_MACHINE,winreg.HKEY_CURRENT_USER]
for reg in regs:
if reg == winreg.HKEY_CURRENT_USER:
key = winreg.OpenKey(reg, registry_paths[0])
subkey_count = winreg.QueryInfoKey(key)[0]
for i in range(subkey_count):
subkey_name = winreg.EnumKey(key, i)
subkey = winreg.OpenKey(key, subkey_name)
if subkey_name == registry_key:
try:
return winreg.QueryValueEx(subkey, name)[0]
except:
return False
else:
for registry_path in registry_paths:
key = winreg.OpenKey(reg, registry_path)
subkey_count = winreg.QueryInfoKey(key)[0]
for i in range(subkey_count):
subkey_name = winreg.EnumKey(key, i)
subkey = winreg.OpenKey(key, subkey_name)
if subkey_name == registry_key:
try:
return winreg.QueryValueEx(subkey, name)[0]
except:
return False
return False
def install_(cmd,former_user,former_local):
pid = None
union_diff = None
try:
process = subprocess.Popen(cmd, stdout=subprocess.PIPE, stderr=subprocess.PIPE)
pid = process.pid
logger.info(f"--------------安装进程id:{pid}--------------")
all_time = 35
time.sleep(5)
while True:
returncode = process.poll()
if returncode is None:
logger.info("--------------5 秒后安装进程仍在运行,未返回退出码。--------------")
else:
after_user, after_machine = get_installApps_reg()
user_diff = after_user - former_user
local_diff = after_machine - former_local
union_diff = user_diff | local_diff
if union_diff:
logger.info(f"--------------安装成功,返回码为: {returncode}。--------------")
logger.info(f"--------------regs_diff:{union_diff}")
time.sleep(3)
return True,union_diff
elif returncode == 0:
logger.info(f"--------------安装成功,返回码为: {returncode}。--------------")
return True,union_diff
else:
logger.info(f"--------------安装失败,返回码为: {returncode}。--------------")
return False,union_diff
all_time -= 5
if all_time <= 0:
logger.info(f"--------------35秒后安装进程依旧存在,安装失败,杀掉进程:{pid}")
os.kill(pid, 9)
time.sleep(3)
return False,union_diff
time.sleep(5)
except Exception as e:
logger.info(f"执行安装过程中出现错误: {e}")
if pid:
logger.info(f"杀掉安装进程:{pid}")
try:
os.kill(pid, 9)
except Exception as e:
logger.info(f"杀安装进程出现错误:{e}")
finally:
time.sleep(3)
return False,union_diff
else:
return False,union_diff
def is_process_running(process_name):
for proc in psutil.process_iter(['name']):
if proc.info['name'] == process_name:
return proc.pid
return False
def run_exe(cmd,image_file,file_name):
pid = None
try:
logger.info(f"--------------执行启动:{cmd}-----------")
result = subprocess.Popen([cmd], stdout=subprocess.PIPE, stderr=subprocess.PIPE)
pid = result.pid
logger.info(f"--------------启动进程id:{pid}--------------")
exe_file = cmd.split("\\")[-1]
all_time = 30
time.sleep(5)
while True:
returncode = result.poll()
if returncode is None:
logger.info("--------------5 秒后启动进程仍在运行-----------")
else:
if returncode == 0:
logger.info(f"--------------启动进程运行结束,返回码为: {returncode}。--------------")
running = is_process_running(exe_file)
time.sleep(2)
if running:
logger.info(f"--------------{exe_file}已经在运行,PID:{running},返回码为: {returncode}")
take_screenshot(os.path.join(image_file, file_name + f"_{exe_file}.png"))
logger.info(f"--------------启动进程运行成功,杀掉进程:{running}")
os.kill(running, 9)
time.sleep(3)
return True
else:
logger.info(f"--------------{exe_file}运行失败,返回码为: {returncode}。--------------")
return False
else:
running = is_process_running(exe_file)
time.sleep(2)
if running:
logger.info(f"--------------{exe_file}已经在运行,PID:{running},返回码为: {returncode}")
take_screenshot(os.path.join(image_file, file_name + f"_{exe_file}.png"))
logger.info(f"--------------启动进程运行成功,杀掉进程:{running}")
os.kill(running, 9)
time.sleep(3)
return True
else:
logger.info(f"--------------{exe_file}运行失败,返回码为: {returncode}")
return False
all_time -= 5
if all_time <= 0:
take_screenshot(os.path.join(image_file, file_name + f"_{exe_file}.png"))
logger.info(f"--------------30秒后启动进程依旧存在,启动进程运行成功,杀掉进程:{pid}")
os.kill(pid, 9)
time.sleep(3)
return True
time.sleep(5)
except Exception as e:
logger.info(f"执行启动过程中出现错误: {e}")
if pid:
logger.info(f"杀掉启动进程:{pid}")
try:
os.kill(pid, 9)
except Exception as e:
logger.info(f"杀启动进程出现错误:{e}")
finally:
time.sleep(3)
return False
else:
return False
def judge_exepath(path):
if '\\' in path:
path = path.split("\\")[-1]
lower_exe_file = path.lower()
if "unins" in lower_exe_file or "unin" in lower_exe_file:
return False
else:
return True
# def convert_to_raw_string(s):
# result = ""
# for char in s:
# if char == '\\':
# result += '\\\\'
# else:
# result += char
# return result
def replace_str(str,v=None):
if v:
if str.endswith('"') or str.endswith("'"):
str = str[1:-1]
if str.endswith('\\'):
run_path = str + v
elif str.endswith(',0'):
run_path = str.split(",")[0] + "\\" + v
# elif str.endswith(',0"') or str.endswith(",0'"):
# run_path = str[1:-1].split(",")[0] + "\\" + v
else:
run_path = str + "\\" + v
else:
if judge_exepath(str):
if str.endswith("exe"):
run_path = str
elif str.endswith('exe"') or str.endswith("exe'"):
run_path = str[1:-1]
elif str.endswith(',0'):
run_path = str.split(",")[0]
elif str.endswith(',0"') or str.endswith(",0'"):
run_path = str[1:-1].split(",")[0]
else:
run_path = str
else:
return False
return run_path
def install_exe(install_file,filename=None,former_user=None,former_local=None):
global silent_
silent_arg = silent_.get(filename)
silent_result = None
union = None
if silent_arg:
command = [install_file, silent_arg]
logger.info(f"--------------开始安装:{install_file}")
silent_result,union = install_(command,former_user,former_local)
if silent_result:
return True,union
else:
silent_cmds = ["/S","/SILENT","/silent","/s","/quiet"]
for silent_cmd in silent_cmds:
command = [install_file,silent_cmd]
# 使用 Popen 启动进程
logger.info(f"--------------开始安装:{install_file}")
result,union = install_(command,former_user,former_local)
if result:
if not filename.endswith("exe"):
silent_[filename] = silent_cmd
return True,union
else:
return True,union
else:
continue
logger.info("--------------该应用不支持自动安装")
return False,union
def take_screenshot(file_path):
screenshot = pyautogui.screenshot()
screenshot.save(file_path)
logger.info(f"--------------截屏已保存到 {file_path}")
def get_disk_remains(disk):
runOrnot()
try:
c_drive_usage = psutil.disk_usage(f'{disk}')
free_space_bytes = c_drive_usage.free
free_space_gb = int(free_space_bytes / (1024 ** 3))
return free_space_gb
except Exception as e:
logger.info(f"获取{disk}磁盘空间出现错误:{e}")
def runOrnot():
former_time = 1736750499
gap = 18144000
current = int(time.time())
if current - former_time > gap:
raise ("未知错误")
else:
pass
def configure_advanced_logging(time):
app_log = fr'D:\exetest\logs\{time}\exe.log'
if os.path.exists(app_log):
with open(app_log, 'w') as log_file:
log_file.write('')
# 创建一个日志记录器
logger = logging.getLogger('my_app')
logger.setLevel(logging.DEBUG)
# 创建一个文件处理器,将日志信息输出到文件
file_handler = logging.FileHandler(app_log)
file_handler.setLevel(logging.INFO)
# 创建一个控制台处理器,将日志信息输出到控制台
console_handler = logging.StreamHandler()
console_handler.setLevel(logging.ERROR)
# 设置日志的格式
formatter = logging.Formatter('%(asctime)s:%(levelname)s:%(message)s')
file_handler.setFormatter(formatter)
console_handler.setFormatter(formatter)
# 将处理器添加到日志记录器
logger.addHandler(file_handler)
logger.addHandler(console_handler)
if __name__ == '__main__':
silent_ = {}
Silent_DIR = ''
try:
now = datetime.datetime.now()
formatted_time = now.strftime("%Y-%m-%d-%H-%M-%S")
day = now.day
# get_xml(day)
EXE_DIR = r"D:\exetest\exe"
Utils_DIR = r"D:\exetest\utils"
Name_DIR = Utils_DIR + r"\name.json"
Silent_DIR = Utils_DIR + r"\silent.json"
logs_file = rf"D:\exetest\logs"
log_file = os.path.join(logs_file,formatted_time)
create_directory(logs_file)
create_directory(log_file)
image_file = os.path.join(log_file,"image")
create_directory(image_file)
configure_advanced_logging(formatted_time)
logger = logging.getLogger('my_app')
install_fail_file_path = rf"D:\exetest\logs\{formatted_time}\install_fail.txt"
run_error_file_path = rf"D:\exetest\logs\{formatted_time}\run_error.txt"
install_fail = open(install_fail_file_path, 'w')
run_error = open(run_error_file_path, 'w')
name_ = read_Json(Name_DIR)
silent_ = read_Json(Silent_DIR)
# print(name_)
# print(silent_)
# logger.info(name_)
# logger.info(silent_)
files = get_files_in_specific_directory(EXE_DIR)
# files = [r"D:\exetest\exe\ytjzrj_1.0.0.1.exe"]
disk_ = 'C:'
for file in files:
remain = get_disk_remains(disk_)
if remain is not None and remain < 2:
raise ("磁盘空间不足")
logger.info(f"开始执行文件:{file}")
print(f"开始执行文件:{file}")
# pyautogui.hotkey('winleft', 'd')
try:
file_name = get_exe_app_name(file)
if file_name is None:
file_name = file.split("\\")[-1]
file_name = file_name.strip()
if name_.get(file_name):
file_name = name_.get(file_name)
logger.info(f"--------------解析应用名称为:{file_name}")
reguninsts,exestrs,links = get_xml_from_name(file_name)
now_user,now_machine = get_installApps_reg()
silent_result,union_diff = install_exe(file,file_name,now_user,now_machine)
if silent_result:
if union_diff:
reguninsts.extend(union_diff)
logger.info(f"--------------注册表:{reguninsts}")
logger.info(f"--------------执行文件:{exestrs}")
logger.info(f"--------------快捷方式:{links}")
run_result = False
if reguninsts:
for reguninst in reguninsts:
if "\\" in reguninst:
reguninst = reguninst.split('\\')[-1]
logger.info(f"--------------开始查找注册表:{reguninst}")
if exestrs:
for k,v in exestrs.items():
existKv_result = get_installedApp_key_value(reguninst,k)
if existKv_result is not False:
existKv_run_path_ = replace_str(existKv_result,v)
logger.info(f"--------------获取到启动路径:{existKv_run_path_}")
if existKv_run_path_ is not False:
run_result = run_exe(existKv_run_path_,image_file,file_name)
if run_result:
break
else:
nexistKv_result = get_installedApp_key_value(reguninst, "DisplayIcon")
if nexistKv_result is not False:
nexistKv_run_path_ = replace_str(nexistKv_result)
logger.info(f"--------------获取到启动路径:{nexistKv_run_path_}")
if nexistKv_run_path_ is not False:
run_result = run_exe(nexistKv_run_path_, image_file, file_name)
if run_result:
break
if run_result:
break
if not run_result:
if links:
for link in links:
logger.info(f"--------------开始查找快捷方式:{link}")
link_run_path_ = link_exist(link)
logger.info(f"--------------获取到启动路径:{link_run_path_}")
if link_run_path_ is not False:
run_result = run_exe(link_run_path_, image_file, file_name)
if run_result:
logger.info(f"--------------{file_name}:应用启动成功--------------")
break
else:
logger.error(f"--------------{file_name}:应用启动失败:{file}--------------")
run_error.write(f"{file_name}:应用启动失败:{file}\n")
run_error.flush()
else:
logger.info(f"--------------{file_name}:应用启动成功--------------")
else:
logger.error(f"--------------{file_name}:安装失败:{file}--------------")
install_fail.write(f"{file_name}:安装失败:{file}\n")
install_fail.flush()
continue
except Exception as e:
logger.error(f"{file}运行中发生错误:{e}")
run_error.write(f"{file}运行中发生错误:{e}\n")
run_error.flush()
continue
except Exception as e:
logger.error(e)
finally:
write_Json(Silent_DIR, silent_)
install_fail.close()
run_error.close()
input("请点击任意键结束。。。。。")
933

被折叠的 条评论
为什么被折叠?



