pywin32 读取 ListView 控件中的内容

想法是通过自动化的操作其他界面中的控件,减少工作量

处理一下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


评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值