想法是通过自动化的操作其他界面中的控件,减少工作量
处理一下EasyBuilder_Pro中的查找与替换功能
import ctypes
from ctypes import wintypes
import os
import sys
import re
import csv
import struct
import time
import copy
import traceback
import logging
from pathlib import Path
import collections
import tkinter
from tkinter import filedialog
from typing import List, Dict
from datetime import datetime
import commctrl
import win32con
import win32gui
import win32ui
import win32process
import win32api
import win32clipboard
from commctrl import (LVM_GETITEMCOUNT, LVM_GETITEMTEXT, LVM_GETNEXTITEM, LVNI_SELECTED)
from win32gui import (FindWindow, FindWindowEx, SendMessage, GetCursorPos)
from win32process import (GetWindowThreadProcessId, ReadProcessMemory, VirtualAllocEx, VirtualFreeEx, WriteProcessMemory)
import pywintypes
from pywintypes import HANDLEType
from ctypes import wintypes, CFUNCTYPE, WINFUNCTYPE
from win32con import WH_KEYBOARD, WH_KEYBOARD_LL, WH_MOUSE, WH_MOUSE_LL, HC_ACTION
from win32con import WM_KEYDOWN, WM_KEYUP
from win32con import KF_EXTENDED, KF_ALTDOWN, KF_REPEAT, KF_UP
user32 = ctypes.windll.user32
kernel32 = ctypes.windll.kernel32
CallNextHookEx = user32.CallNextHookEx
UnhookWindowsHookEx = user32.UnhookWindowsHookEx
SetWindowsHookEx = user32.SetWindowsHookExW
LRESULT = wintypes.LPARAM
Hookproc_ptr = WINFUNCTYPE(LRESULT, wintypes.INT, wintypes.WPARAM, wintypes.LPARAM)
SetWindowsHookEx.argtypes = (wintypes.INT, Hookproc_ptr, wintypes.HINSTANCE, wintypes.DWORD)
SetWindowsHookEx.restype = wintypes.HHOOK
CallNextHookEx.argtypes = (wintypes.HHOOK, wintypes.INT, wintypes.WPARAM, wintypes.LPARAM)
CallNextHookEx.restype = LRESULT
SendMessageA = user32.SendMessageA
SendMessageA.restype = LRESULT
user32.WindowFromPoint.argtypes = [wintypes.POINT]
user32.WindowFromPoint.restype = wintypes.HWND
user32.GetComboBoxInfo.argtypes = [wintypes.HWND, wintypes.HWND]
user32.GetComboBoxInfo.restype = wintypes.BOOL
# ctypes.windll是一个用于加载动态链接库(DLL)的对象,它提供了一种使用Python调用这些库中函数的方式。除了ctypes.windll.user32和ctypes.windll.kernel32,ctypes.windll还有其他一些成员可以使用。以下是一些常见的ctypes.windll成员:
# ctypes.windll.gdi32:用于访问Windows图形设备接口(GDI)库中的函数,用于绘制图形和处理图像。
# ctypes.windll.advapi32:用于访问Windows高级程序接口(API)库中的函数,用于处理系统配置、注册表和安全等。
# ctypes.windll.comctl32:用于访问Windows公共控件库中的函数,用于创建和管理常见的用户界面控件。
# ctypes.windll.ole32:用于访问Windows对象链接和嵌入(OLE)库中的函数,用于处理COM对象和组件。
# ctypes.windll.shell32:用于访问Windows Shell库中的函数,用于处理文件和文件夹操作、系统托盘和快捷方式等。
# ctypes.windll.winspool:用于访问Windows打印机库中的函数,用于管理打印机和打印作业。
class Structure_walk:
'''遍历结构体'''
def __init__(self, struct_obj):
self.struct_obj = struct_obj
self.stack = []
self.dump = {}
def walk_struct(self, struct_obj):
for field_name, field_type, *field_other in struct_obj._fields_:
field_value = getattr(struct_obj, field_name)
self.stack.append(field_name)
if isinstance(field_value, (ctypes.Structure, ctypes.Union)):
self.walk_struct(field_value)
else:
self.dump['.'.join(self.stack)] = field_value
self.stack.pop()
def __str__(self):
self.stack.clear()
self.dump.clear()
self.walk_struct(self.struct_obj)
return str(self.dump)
class Structure_tool:
def __str__(self) -> str:
return str(Structure_walk(self))
__repr__ = __str__
class C32:
UINT = ctypes.c_uint32
INT = ctypes.c_int32
LPARAM = ctypes.c_int32
WPARAM = ctypes.c_uint32
LONG = ctypes.c_int32
ULONG = ctypes.c_uint32
LPSTR = ctypes.c_uint32
PUINT = ctypes.c_uint32
PINT = ctypes.c_uint32
class POINT(wintypes.POINT, Structure_tool):
pass
class C32_LVITEMA(ctypes.Structure):
_fields_ = [
("mask", C32.UINT),
("iItem", C32.INT),
("iSubItem", C32.INT),
("state", C32.UINT),
("stateMask", C32.UINT),
("pszText", C32.LPSTR),
("cchTextMax", C32.INT),
("iImage", C32.INT),
("lParam", C32.LPARAM),
("iIndent", C32.INT),
("iGroupId", C32.INT),
("cColumns", C32.UINT),
("puColumns", C32.PUINT),
("piColFmt", C32.PINT),
("iGroup", C32.INT),
]
class COMBOBOXINFO(ctypes.Structure):
_fields_ = [
('cbSize', wintypes.DWORD),
('rcItem', wintypes.RECT),
('rcButton', wintypes.RECT),
('stateButton', wintypes.DWORD),
('hwndCombo', wintypes.HWND),
('hwndItem', wintypes.HWND),
('hwndList', wintypes.HWND),
]
# 64位有问题,计算长度变成88
# class LV_ITEM(ctypes.Structure):
# _fields_ = [
# ("mask", wintypes.UINT),
# ("iItem", wintypes.INT),
# ("iSubItem", wintypes.INT),
# ("state", wintypes.UINT),
# ("stateMask", wintypes.UINT),
# # 这个指针12字节??
# ("pszText", wintypes.LPVOID),
# # ("cchTextMax", wintypes.INT),
# # ("iImage", wintypes.INT),
# # ("lParam", ctypes.c_long),
# # ("iIndent", wintypes.INT),
# # ("iGroupId", wintypes.INT),
# # ("cColumns", wintypes.UINT),
# # ("puColumns", wintypes.PUINT),
# # ("piColFmt", wintypes.PINT),
# # ("iGroup", wintypes.INT),
# ]
class WalkHwndEx:
'''遍历获取所有窗口数据'''
def __init__(self, start=0):
self.start = start
self.stack = []
self.dump = []
self.route = []
def hwnd_iter(self, start):
child_hwnd = 0
while True:
try:
child_hwnd = win32gui.FindWindowEx(start, child_hwnd, None, None)
if child_hwnd == 0:
return
else:
yield child_hwnd
except:
return
def walk_hwnd(self, hwnd):
x_iter = self.hwnd_iter(hwnd)
for chwnd in x_iter:
self.stack.append(chwnd)
if self.route:
try:
para = None
_func = None
for _func in self.route:
para = _func(self, para)
except:
# print('error', chwnd, _func)
pass
self.dump.append(copy.deepcopy(self.stack))
self.walk_hwnd(chwnd)
self.stack.pop()
def run(self):
self.stack.clear()
self.dump.clear()
self.walk_hwnd(self.start)
return self
SendMessageA = user32.SendMessageA
SendMessageA.restype = wintypes.LONG
def MAKEWPARAM(h, l):
return ((h & 0x0000_FFFF) << 16) | (l & 0x0000_FFFF)
class C32_RAM:
def __init__(self, hProcess: HANDLEType, dwSize: int, flAllocationType=win32con.MEM_COMMIT, flProtect=win32con.PAGE_READWRITE):
pass
self.hProcess = hProcess
self.ptr = VirtualAllocEx(hProcess, False, dwSize, flAllocationType, flProtect)
self.size = dwSize
@property
def data(self) -> bytes:
return ReadProcessMemory(self.hProcess, self.ptr, self.size)
@data.setter
def data(self, _value):
if isinstance(_value, (bytes, bytearray)):
d_size = len(_value)
if d_size <= self.size:
bytes_written = WriteProcessMemory(self.hProcess, self.ptr, bytes(_value))
else:
self.release()
C32_RAM.__init__(self, self.hProcess, d_size)
def release(self):
VirtualFreeEx(self.hProcess, self.ptr, 0, win32con.MEM_RELEASE)
def __del__(self):
try:
self.release()
except:
pass
class C32_RAM_STR(C32_RAM):
def __init__(self, hProcess: HANDLEType, init: bytes | int | str, encoding='gbk'):
if isinstance(init, (bytes, bytearray)):
C32_RAM.__init__(self, hProcess, len(init))
init_data = bytes(init)
elif isinstance(init, int):
C32_RAM.__init__(self, hProcess, init)
# init_data = bytes(init)
elif isinstance(init, str):
init_data = init.encode(encoding) + b'\0'
C32_RAM.__init__(self, hProcess, len(init_data))
else:
raise TypeError(init)
if isinstance(init, (bytes, bytearray, str)):
bytes_written = WriteProcessMemory(self.hProcess, self.ptr, init_data)
self.encoding = encoding
@property
def text(self) -> str:
return self.read_text_from(self.ptr)
@text.setter
def text(self, _value):
if isinstance(_value, str):
data = _value.encode(self.encoding) + b'\0'
self.data = data
raise ValueError(_value)
def read_text_from(self, address):
ret: bytes = ReadProcessMemory(self.hProcess, address, self.size)
ret_text = ret.decode(encoding=self.encoding, errors='replace').rstrip('\0')
return ret_text
def int_to_hex(integer):
hex_str = hex(integer)[2:] # Convert integer to hexadecimal string
hex_str = hex_str.zfill(8) # Pad the string with zeros to 8 characters
return hex_str
def GetSpecifyItemString(MMEProcessHandle: int, hwnd: int, row: int = 0, column: int = 0, bufferSize=512) -> str:
'''获取字符串内容\n
HANDLE MMEProcessHandle :进程句柄, \n
HWND hwnd :控件句柄, \n
int row,\n
int column, \n
int bufferSize
'''
test_LVITEM = C32_LVITEMA()
size = ctypes.sizeof(C32_LVITEMA)
# 在表格对象中创建一个内存空间
RAM_LVITEM = C32_RAM_STR(MMEProcessHandle, init=size)
RAM_stringBuffer = C32_RAM_STR(MMEProcessHandle, init=bufferSize, encoding='gbk')
before_ptr = RAM_stringBuffer.ptr
# 定义当前选中的表格元素
test_LVITEM.cchTextMax = bufferSize
test_LVITEM.iSubItem = column
test_LVITEM.pszText = RAM_stringBuffer.ptr
buffer = bytes(test_LVITEM)
# 按照指定的初始结构初始化申请的内存空间
RAM_LVITEM.data = buffer
# # 从指定行获取 LVITEM 对象 ,存放至申请的空间
SendMessageA(hwnd, LVM_GETITEMTEXT, row, RAM_LVITEM.ptr)
# 从对象中获取字符串数据,
test_LVITEM2 = C32_LVITEMA.from_buffer_copy(RAM_LVITEM.data)
if test_LVITEM2.pszText == test_LVITEM.pszText:
ret_text = RAM_stringBuffer.text
return ret_text
elif test_LVITEM2.pszText > 0:
return RAM_stringBuffer.read_text_from(test_LVITEM2.pszText)
else:
return ''
def SetItemState(MMEProcessHandle: int, hwnd: int, row: int = 0):
'''将某一行设置为选中状态'''
test_LVITEM = C32_LVITEMA()
size = ctypes.sizeof(C32_LVITEMA)
# 在表格对象中创建一个内存空间
RAM_LVITEM = C32_RAM_STR(MMEProcessHandle, size)
# 定义当前选中的表格元素
test_LVITEM.iItem = row
test_LVITEM.state = commctrl.LVIS_SELECTED
test_LVITEM.stateMask = commctrl.LVIS_SELECTED
buffer = bytes(test_LVITEM)
# 按照指定的初始结构初始化申请的内存空间
RAM_LVITEM.data = buffer
# # 从指定行获取 LVITEM 对象 ,存放至申请的空间
SendMessageA(hwnd, commctrl.LVM_SETITEMSTATE, row, RAM_LVITEM.ptr)
# 从对象中获取字符串数据
return test_LVITEM
def ComboBox_GetLBText(hwndCtl: int, index: int = 0) -> str:
text_len = SendMessageA(hwndCtl, win32con.CB_GETLBTEXTLEN, index, 0)
buffer = ctypes.create_string_buffer(text_len + 1)
ret = SendMessageA(hwndCtl, win32con.CB_GETLBTEXT, index, buffer)
lret = ctypes.c_int32(ret).value
if lret >= 0:
ret_text = buffer.value.decode('gbk')
return ret_text
else:
return ''
def ComboBox_SetText(hwndCtl: int, text: str, encoding='gbk') -> str:
buffer = ctypes.create_string_buffer(text.encode(encoding=encoding))
SendMessageA(hwndCtl, win32con.WM_SETTEXT, 0, buffer)
ComboBox_FreshText(hwndCtl)
def ComboBox_SetClipboardText(hwndCtl: int, text: str) -> str:
win32clipboard.OpenClipboard(0)
win32clipboard.EmptyClipboard()
win32clipboard.SetClipboardText(text)
try:
win32clipboard.CloseClipboard()
pass
except pywintypes.error:
logging.exception('Exception occurred')
SendMessageA(hwndCtl, win32con.WM_PASTE, 0, 0)
ComboBox_FreshText(hwndCtl)
def ComboBox_SendUnicode(hwndCtl: int, text: str) -> str:
for c in text:
SendMessage(hwndCtl, win32con.WM_IME_CHAR, ord(c), 1)
ComboBox_FreshText(hwndCtl)
# def ComboBox_SELECTSTRING():
# # 可以修改内容 但是在进行替换操作时还是获取 最后交互时的设置
# # buffer = ctypes.create_string_buffer("整个工程文件".encode(encoding='gbk'))
# SendMessageA(dlg.Cbo_窗口选择, win32con.WM_LBUTTONDOWN, 0, 0)
# SendMessageA(dlg.Cbo_窗口选择, win32con.WM_LBUTTONUP, 0, 0)
# buffer = ctypes.create_string_buffer("窗口 116".encode(encoding='gbk'))
# ret = SendMessageA(dlg.Cbo_窗口选择, win32con.CB_SELECTSTRING, -1, buffer)
def ComboBox_FreshItem(hwndCtl: int):
cb_id = win32gui.GetWindowLong(hwndCtl, win32con.GWL_ID)
ret = SendMessage(win32gui.GetParent(hwndCtl), win32con.WM_COMMAND, MAKEWPARAM(win32con.CBN_SELCHANGE, cb_id), hwndCtl)
return ret
def ComboBox_FreshText(hwndCtl: int):
cb_id = win32gui.GetWindowLong(hwndCtl, win32con.GWL_ID)
ret = SendMessage(win32gui.GetParent(hwndCtl), win32con.WM_COMMAND, MAKEWPARAM(win32con.CBN_EDITCHANGE, cb_id), hwndCtl)
return ret
def ComboBox_FreshText(hwndCtl: int):
cb_id = win32gui.GetWindowLong(hwndCtl, win32con.GWL_ID)
ret = SendMessage(win32gui.GetParent(hwndCtl), win32con.WM_COMMAND, MAKEWPARAM(win32con.CBN_EDITCHANGE, cb_id), hwndCtl)
return ret
def ComboBox_SetCurSel(hwndCtl: int, index):
SendMessageA(hwndCtl, win32con.CB_SETCURSEL, index, 0)
ComboBox_FreshItem(hwndCtl)
def Button_Click(hwndCtl: int):
btn_id = win32gui.GetWindowLong(hwndCtl, win32con.GWL_ID)
ret = SendMessage(win32gui.GetParent(hwndCtl), win32con.WM_COMMAND, MAKEWPARAM(win32con.BN_CLICKED, btn_id), hwndCtl)
return ret
def get_combobox_text(hwnd) -> List[str]:
'''获取组合框控件的可选项内容'''
MMECombox = hwnd
threadId, processId = MMEComboxPid = GetWindowThreadProcessId(MMECombox)
MMEProcessHandle = win32api.OpenProcess(win32con.PROCESS_ALL_ACCESS, False, processId)
# 获取组合框项目数量
count = SendMessage(MMECombox, win32con.CB_GETCOUNT, 0, 0)
print('当前组合框的总行元素量', count)
cb_list = []
# 获取组合框每一项文本
if count > 0:
for i in range(0, count):
窗口名 = ComboBox_GetLBText(MMECombox, i)
# print(窗口名)
cb_list.append(窗口名)
else:
print('列表无数据')
return cb_list
def get_search_result():
'''获取当前的搜索结果'''
try:
# //1、首先获取窗口句柄
MMEWindow = FindWindow("#32770", "寻找/替换")
if MMEWindow == 0:
raise
# //2、通过父窗口句柄获取SysListView32控件的句柄
地址页 = FindWindowEx(MMEWindow, None, "#32770", None)
MMEListView = FindWindowEx(地址页, None, "SysListView32", "")
# //3、获取SysListView32控件pid
threadId, processId = MMEListViewPid = GetWindowThreadProcessId(MMEListView)
except:
print('未找到窗口')
return
# //4、打开进程,返回进程句柄
MMEListViewProcess = None
try:
MMEListViewProcess = win32api.OpenProcess(win32con.PROCESS_ALL_ACCESS, False, processId)
# //5、获取表格总的数量
count = SendMessage(MMEListView, LVM_GETITEMCOUNT, 0, 0)
print('当前列表的总行元素量', count)
# //6、获取当前被选中的行号,未选择时为 4294967295 不是 -1,行号从0开始
selected = SendMessage(MMEListView, LVM_GETNEXTITEM, -1, LVNI_SELECTED)
# 将所有的字符串输出
alldata = []
if count > 0:
for i in range(0, count):
位置 = GetSpecifyItemString(MMEListViewProcess, MMEListView, i, 0)
名称 = GetSpecifyItemString(MMEListViewProcess, MMEListView, i, 1)
地址 = GetSpecifyItemString(MMEListViewProcess, MMEListView, i, 2)
# print(f"{i:>05d}\t{位置}\t{名称}\t{地址}")
新值 = ''
忽略 = 0
alldata.append({
"位置": 位置,
"名称": 名称,
"原始地址": 地址,
"地址": 地址,
"新值": 新值,
"忽略": 忽略
})
csvpath = r'D:\eb_replace.csv'
fieldnames = [
"位置",
"名称",
"原始地址",
"地址",
"新值",
"忽略",
]
with open(file=csvpath, mode='w+', encoding='gbk', errors='replace') as csvfile:
writer = csv.DictWriter(csvfile, fieldnames=fieldnames, lineterminator='\n')
# 写表头
writer.writeheader()
# 写数据
writer.writerows(alldata)
else:
print('列表无数据')
except:
traceback.print_exception(*sys.exc_info())
print('输出数据异常')
finally:
if MMEListViewProcess:
win32api.CloseHandle(MMEListViewProcess)
def get_search_selection():
'''获取可选的搜索位置'''
try:
MMEWindow = FindWindow("#32770", "寻找/替换")
if MMEWindow == 0:
raise
# 组合框处理
MMECombox = FindWindowEx(MMEWindow, None, "ComboBox", None)
# //3、获取SysListView32控件pid
threadId, processId = MMEComboxPid = GetWindowThreadProcessId(MMECombox)
except:
print('未找到窗口')
return
try:
# //4、打开进程,返回进程句柄
MMEComboxProcess = None
MMEComboxProcess = win32api.OpenProcess(win32con.PROCESS_ALL_ACCESS, False, processId)
print(MMEComboxProcess)
# 获取组合框项目数量
count = SendMessage(MMECombox, win32con.CB_GETCOUNT, 0, 0)
print('当前组合框的总行元素量', count)
# 设置新的选择序号 指定要选择的字符串的从零开始的索引。 如果此参数为 -1,则删除列表中的任何当前选定内容并清除编辑控件。
ComboBox_SetCurSel(MMECombox, 0)
# 获取组合框的信息
# cb_info = get_combobox_info(MMECombox)
cb_list = []
# 获取组合框每一项文本
if count > 0:
for i in range(0, count):
窗口名 = ComboBox_GetLBText(MMECombox, i)
# print(窗口名)
cb_list.append(窗口名)
else:
print('列表无数据')
return cb_list
except:
traceback.print_exception(*sys.exc_info())
print('输出数据异常')
finally:
if MMEComboxProcess:
win32api.CloseHandle(MMEComboxProcess)
def replace_as_csv(use_win=False, gen_mode=0):
'''根据CSV表格内容进行替换'''
try:
# //1、首先获取窗口句柄
MMEWindow = FindWindow("#32770", "寻找/替换")
if MMEWindow == 0:
raise
# 组合框处理
MMECombox = FindWindowEx(MMEWindow, None, "ComboBox", None)
# //2、通过父窗口句柄获取 控件的句柄
地址页 = FindWindowEx(MMEWindow, None, "#32770", None)
pass
前标签 = FindWindowEx(地址页, None, "Static", "双击鼠标, 可检视元件")
# 这里使用的组合框会根据是否存在设备而变化
找寻框 = FindWindowEx(地址页, 前标签, "ComboBox", None)
替换框 = FindWindowEx(地址页, 找寻框, "ComboBox", None)
# 找寻框_输入 = FindWindowEx(找寻框, None, "Edit", None)
# 替换框_输入 = FindWindowEx(替换框, None, "Edit", None)
寻找按钮 = FindWindowEx(地址页, None, "Button", "寻找")
替换按钮 = FindWindowEx(地址页, None, "Button", "替换")
全部替换按钮 = FindWindowEx(地址页, None, "Button", "全部替换")
# //3、获取 控件pid
threadId, processId = MMEWindowPid = GetWindowThreadProcessId(MMEWindow)
pass
except:
print('未找到窗口')
return
# 输入找寻内容
# 从csv文件中载入数据
rootfile = filedialog.askopenfilename()
if len(rootfile) > 0:
csvpath = rootfile
else:
# raise Exception('文件未选择')
return
try:
with open(file=csvpath, mode='r', encoding='gbk', errors='replace') as csvfile:
reader = csv.DictReader(csvfile)
head = reader.fieldnames
alltext = list(reader)
except:
print('表格文件打开失败')
return
try:
if use_win:
# 获取可选的窗口列表
eb_windows = get_search_selection()
keyword = []
for title in eb_windows:
keyword.append(title.split(' : ')[0])
pass
# 对可以执行的选项进行记录,在一个窗口字典中,不再进行已使用的搜索参数
pass
used_para = dict()
for i, action in enumerate(alltext, start=0):
if gen_mode == 0:
if not action["位置"].startswith('窗口'):
print('跳过非窗口的替换值')
action['忽略'] = '1'
continue
if len(action['新值']) == 0:
print('未设置替换值:', action['地址'], '因此跳过')
action['忽略'] = '1'
continue
if use_win:
search_key = action['位置'] + '____' + action['地址']
else:
search_key = '____' + action['地址']
if search_key in used_para:
print('曾经执行过参数:', search_key, '因此跳过')
action['忽略'] = '1'
continue
else:
used_para[search_key] = action['新值']
if action['忽略'] != '0':
print(f'忽略已替换项{i}')
continue
ComboBox_SetText(找寻框, action['地址'])
ComboBox_SetText(替换框, action['新值'])
if use_win:
try:
# 设置新的选择序号 指定要选择的字符串的从零开始的索引。 如果此参数为 -1,则删除列表中的任何当前选定内容并清除编辑控件。
eb_index = keyword.index(action['位置'].replace('_', ' '))
ComboBox_SetCurSel(MMECombox, eb_index)
except:
# 无法选择时指定全局
ComboBox_SetCurSel(MMECombox, 0)
elif gen_mode == 1:
# 自动生成替换列表
return
# 单击寻找按钮
ret = SendMessageA(寻找按钮, win32con.BM_CLICK, 0, 0)
# print(ret)
time.sleep(0.4)
# 检测能否全部替换
# ret = SendMessageA(全部替换按钮,win32con.BM_GETSTATE,0,0)
ret = win32gui.IsWindowEnabled(全部替换按钮)
# print(ret)
if ret == 1:
# 按下全部替换
ret = SendMessageA(全部替换按钮, win32con.BM_CLICK, 0, 0)
print(f'第{i}项替换完成,余下{len(alltext)-i-1}')
else:
print(f'第{i}项无替换选择,余下{len(alltext)-i-1}')
time.sleep(0.2)
# 更新记录
action['忽略'] = '1'
x, y = GetCursorPos()
if x < 10 or y < 10:
time.sleep(2)
x, y = GetCursorPos()
if x < 10 or y < 10:
print('特殊结束')
return
except:
print('异常')
finally:
try:
with open(file=csvpath, mode='w+', encoding='gbk', errors='replace') as csvfile:
writer = csv.DictWriter(csvfile, fieldnames=head, lineterminator='\n')
# 写表头
writer.writeheader()
# 写数据
writer.writerows(alltext)
except:
print('保存处理数据失败')
def replace_as_csv2(use_win=False, gen_mode=0):
'''根据CSV表格内容进行替换'''
try:
# //1、首先获取窗口句柄
MMEWindow = FindWindow("#32770", "寻找/替换")
if MMEWindow == 0:
raise
# 组合框处理
MMECombox = FindWindowEx(MMEWindow, None, "ComboBox", None)
# //2、通过父窗口句柄获取 控件的句柄
地址页 = FindWindowEx(MMEWindow, None, "#32770", None)
pass
前标签 = FindWindowEx(地址页, None, "Static", "双击鼠标, 可检视元件")
# 这里使用的组合框会根据是否存在设备而变化
找寻框 = FindWindowEx(地址页, 前标签, "ComboBox", None)
替换框 = FindWindowEx(地址页, 找寻框, "ComboBox", None)
# 找寻框_输入 = FindWindowEx(找寻框, None, "Edit", None)
# 替换框_输入 = FindWindowEx(替换框, None, "Edit", None)
寻找按钮 = FindWindowEx(地址页, None, "Button", "寻找")
替换按钮 = FindWindowEx(地址页, None, "Button", "替换")
全部替换按钮 = FindWindowEx(地址页, None, "Button", "全部替换")
# //3、获取 控件pid
threadId, processId = MMEWindowPid = GetWindowThreadProcessId(MMEWindow)
pass
except:
print('未找到窗口')
return
# 输入找寻内容
# 从csv文件中载入数据
rootfile = filedialog.askopenfilename()
if len(rootfile) > 0:
csvpath = rootfile
else:
# raise Exception('文件未选择')
return
try:
with open(file=csvpath, mode='r', encoding='gbk', errors='replace') as csvfile:
reader = csv.DictReader(csvfile)
head = reader.fieldnames
alltext = list(reader)
except:
print('表格文件打开失败')
return
try:
for i, action in enumerate(alltext, start=0):
if gen_mode == 0:
if len(action['新值']) == 0:
print('未设置替换值:', action['地址'], '因此跳过')
action['忽略'] = '1'
continue
if action['忽略'] != '0':
print(f'忽略已替换项{i}')
continue
ComboBox_SetText(找寻框, action['地址'])
ComboBox_SetText(替换框, action['新值'])
# # 指定全局
# SendMessageA(int(MMECombox), CB_SETCURSEL, 0, 0)
elif gen_mode == 1:
# 自动生成替换列表
pass
# 单击寻找按钮
ret = SendMessageA(寻找按钮, win32con.BM_CLICK, 0, 0)
# print(ret)
time.sleep(0.15)
# 检测能否全部替换
# ret = SendMessageA(全部替换按钮,win32con.BM_GETSTATE,0,0)
ret = win32gui.IsWindowEnabled(全部替换按钮)
# print(ret)
if ret == 1:
# 按下全部替换
ret = SendMessageA(全部替换按钮, win32con.BM_CLICK, 0, 0)
print(f'第{i}项替换完成,余下{len(alltext)-i-1}')
time.sleep(0.15)
# 更新记录
action['忽略'] = '1'
else:
print(f'第{i}项无替换选择,余下{len(alltext)-i-1}')
x, y = GetCursorPos()
if x < 10 or y < 10:
time.sleep(2)
x, y = GetCursorPos()
if x < 10 or y < 10:
print('特殊结束')
return
except:
print('异常')
finally:
try:
with open(file=csvpath, mode='w+', encoding='gbk', errors='replace') as csvfile:
writer = csv.DictWriter(csvfile, fieldnames=head, lineterminator='\n')
# 写表头
writer.writeheader()
# 写数据
writer.writerows(alltext)
except:
print('保存处理数据失败')
def init_data_invo():
'''初始化替换列表,为汇川做准备'''
# 输入找寻内容
# 从csv文件中载入数据
rootfile = filedialog.askopenfilename()
if len(rootfile) > 0:
csvpath = rootfile
else:
# raise Exception('文件未选择')
return
try:
with open(file=csvpath, mode='r', encoding='gbk', errors='replace') as csvfile:
reader = csv.DictReader(csvfile)
head = reader.fieldnames
alltext = list(reader)
except:
print('表格文件打开失败')
return
def repl_func(objMatch: re.Match) -> str:
# temp_text = objMatch.re.sub(string=objMatch.group(0), repl=_repl),
aa = objMatch.groupdict()['aa']
num = objMatch.groupdict()['num'].strip('[]')
num = int(num[3:]) - 1
logging.info(f"{objMatch.groupdict()['num']} -> [{num}]")
return f'{aa}[{num}]'
for i, action in enumerate(alltext, start=0):
sText = action['地址']
objPattern = re.compile(r'''(?P<aa>\w+)(?P<num>\[\d{5}\])''', re.M | re.I)
sText = objPattern.sub(repl=repl_func, string=sText)
objPattern = re.compile(r'''(?P<full>VAR://)''', re.M)
sText = objPattern.sub(repl=r'Application.GVL.', string=sText)
action['新值'] = sText
# 回写数据
fieldnames = [
"位置",
"名称",
"原始地址",
"地址",
"新值",
"忽略",
]
with open(file=csvpath, mode='w+', encoding='gbk', errors='replace') as csvfile:
writer = csv.DictWriter(csvfile, fieldnames=fieldnames, lineterminator='\n')
# 写表头
writer.writeheader()
# 写数据
writer.writerows(alltext)
def init_data_invo_fast():
'''初始化替换列表,为汇川做准备'''
# 输入找寻内容
# 从csv文件中载入数据
rootfile = filedialog.askopenfilename()
if len(rootfile) > 0:
csvpath = rootfile
else:
# raise Exception('文件未选择')
return
try:
with open(file=csvpath, mode='r', encoding='gbk', errors='replace') as csvfile:
reader = csv.DictReader(csvfile)
head = reader.fieldnames
alltext = list(reader)
except:
print('表格文件打开失败')
return
修改记录 = dict()
# ST_Control的替换
for i, action in enumerate(alltext, start=0):
# 储存
sText = action['地址']
objPattern = re.compile(r'''(?P<full>(?P<aa>VAR:.+ST_Control\[\d+\]\.)ini)''', re.M | re.I)
objMatch = objPattern.search(string=sText)
if objMatch is not None:
aa = objMatch.groupdict()['aa']
修改记录[objMatch.group(0)] = f'{aa}init'
# 重新生成替换用的字典组
for i, action in enumerate(alltext, start=0):
# 储存
sText = action['地址']
objPattern = re.compile(r'''(?P<aa>\w+)(?P<num>\[\d{5}\])''', re.M | re.I)
objMatch = objPattern.search(string=sText)
if objMatch is not None:
aa = objMatch.groupdict()['aa']
num = objMatch.groupdict()['num'].strip('[]')
num = int(num[3:]) - 1
logging.info(f"{objMatch.groupdict()['num']} -> [{num}]")
修改记录[objMatch.group(0)] = f'{aa}[{num}]'
修改记录['VAR://'] = f'Application.GVL.'
修改记录['sSendData_AryByte'] = f'bySendData'
修改记录['sReadData_AryByte'] = f'byReadData'
alltext.clear()
for key, value in 修改记录.items():
temp = dict()
temp['位置'] = '整个工程文件'
temp['地址'] = str(key)
temp['新值'] = str(value)
temp['忽略'] = '0'
alltext.append(temp)
# 回写数据
fieldnames = [
"位置",
"名称",
"地址",
"新值",
"忽略",
]
with open(file=csvpath, mode='w+', encoding='gbk', errors='replace') as csvfile:
writer = csv.DictWriter(csvfile, fieldnames=fieldnames, lineterminator='\n')
# 写表头
writer.writeheader()
# 写数据
writer.writerows(alltext)
def program1():
code = 0
while True:
code = input("""\
--------请输入操作--------
1:获取当前找到的列表值并保存至 D:\\eb_replace.csv
2:打开替换用的数据表格并开始替换(鼠标移动至屏幕左上角停止流程)
3:同上操作,新增按照表格中的 窗口值 替换
4:初始化替换列表,为汇川做准备
5:初始化替换列表,为汇川做准备(简化快捷版本)
6:简易替换
9:退出
>> """)
match code:
case '1':
get_search_result()
case '2':
replace_as_csv()
case '3':
replace_as_csv(use_win=True)
case '4':
init_data_invo()
case '5':
init_data_invo_fast()
case '6':
replace_as_csv2()
case '9':
break
case _:
pass
continue
print('程序结束')
def program2():
'''获取当前的搜索结果'''
# win32ui.EnableControlContainer()
try:
MMEWindow = FindWindow("#32770", "寻找/替换")
if MMEWindow == 0:
raise
# 组合框处理
MMECombox = FindWindowEx(MMEWindow, None, "ComboBox", None)
# //3、获取SysListView32控件pid
threadId, processId = MMEComboxPid = GetWindowThreadProcessId(MMECombox)
except:
print('未找到窗口')
return
CWMMEWindow = win32ui.FindWindow("#32770", "寻找/替换")
#
# comboxx = win32com.client.Dispatch("Forms.ComboBox.1")
# 这个顺序不行
# CWMMECombox = win32ui.FindWindowEx(CWMMEWindow, None, "ComboBox", None)
# ppp= win32ui.CreateWindowFromHandle(MMECombox)
# 反过来可以
ppp = win32ui.CreateWindowFromHandle(MMECombox)
CWMMECombox = win32ui.FindWindowEx(CWMMEWindow, None, "ComboBox", None)
ppp.GetCount()
ppp.GetLBText(0)
# t1 = win32ui.FindWindowEx(CWMMEWindow, None, "Static", '寻找 :')
# t2 = win32ui.FindWindowEx(CWMMEWindow, None, "Button", '关闭')
# t3 = win32ui.FindWindowEx(CWMMEWindow, None, "#32770", None)
# t4 = win32ui.FindWindowEx(t3, None, "ComboBox", None)
# t5 = win32ui.FindWindowEx(t3, t4, None, None)
# comboCtrl = win32ui.CreateControl('Forms.ComboBox.1',
# '',
# win32con.CBS_SIMPLE | win32con.WS_VISIBLE,
# (10, 10, 20, 20),
# CWMMEWindow,
# 0,
# 0,
# 0,
# '')
# [x for x in dir(win32ui) if str(x).find('box')>-1]
pass
def program3() -> None:
'''函数功能'''
MMEWindow = win32gui.FindWindow("#32770", "寻找/替换")
ret_hwnds = WalkHwndEx(MMEWindow)
ret_hwnds.detail = []
def finder(obj: WalkHwndEx, para=None):
'''获取窗口句柄'''
stack = obj.stack
child_hwnd = stack[-1]
temp_info = dict()
temp_info['hwnd'] = child_hwnd
temp_info['ClassName'] = win32gui.GetClassName(child_hwnd)
temp_info['WindowText'] = win32gui.GetWindowText(child_hwnd)
if len(stack) == 1:
temp_info['parent'] = obj.start
else:
temp_info['parent'] = stack[-2]
ret_hwnds.detail.append(temp_info)
return None
ret_hwnds.route.append(finder)
ret_hwnds.run()
if __name__ == "__main__":
DEFAULTOUT = sys.stdout
try:
with open(file=r'D:\替换.log', mode='a+', encoding='utf-8') as logfile:
# sys.stdout = logfile
# program3()
program1()
pass
except:
traceback.print_exception(*sys.exc_info())
print('log异常')
finally:
sys.stdout = DEFAULTOUT