【Python】Spy++使用

2023-05-16

SPY++的使用和Python操作

  • 1、spy++的基本操作
    • 1.1 窗口属性查找
    • 1.2 窗口spy++定位
  • 2、python与spy++
    • Source Code
    • Use

  • 下载 Spy++:文末公众号回复 210127 获取压缩包

1、spy++的基本操作

在这里插入图片描述

1.1 窗口属性查找

在这里插入图片描述
拖住中间的“寻找工具”放到想要定位的软件上,然后松开
在这里插入图片描述

  • 以微信为例,我们会得到“微信”这个窗口的句柄,为“00031510”,注意这个句柄是“十六进制”,即“0x31510”。
    在这里插入图片描述
  • 点击ok我们会看到更详细的属性信息
    在这里插入图片描述

1.2 窗口spy++定位

在这里插入图片描述

  • 同理拖放到“微信”上,获取到“微信”的界面
    在这里插入图片描述
  • 点击ok,会直接定位到“微信”
    在这里插入图片描述
  • 在这里我们会看到一条信息
  • 00031510 “微信” WeChatMainWndForPC
    – 00031510:代表十六进制的窗口句柄
    – 微信:代表窗口标题
    – WeChatMainWndForPC:代表窗口的类名

2、python与spy++

Source Code

#!/usr/bin/python
# -*- coding: utf-8 -*-
# @Time   : 2021/1/27 10:50
# @Author : SandQuant

import collections
from win32 import win32gui
import pyautogui
import sys


class PySpy(object):
    Window = collections.namedtuple('Window', ['caption', 'class_name', 'hwnd_py', 'hwnd_spy'])

    def __init__(self):
        pass

    @classmethod
    def window_attr(cls, hwnd_py):
        """
        显示窗口的属性
        :param hwnd_py: 窗口句柄(十进制)
        :return: Window
        """
        return cls.Window(
            caption=win32gui.GetWindowText(hwnd_py),
            class_name=win32gui.GetClassName(hwnd_py),
            hwnd_py=hwnd_py,
            hwnd_spy=hex(hwnd_py),
        )

    @classmethod
    def show_top_windows(cls):
        """
        列出所有的顶级窗口及属性
        :return: 全部的顶层窗口及对应属性
        """
        containers = []
        win32gui.EnumWindows(lambda hwnd, param: param.append(cls.window_attr(hwnd)), containers)
        return containers

    @classmethod
    def find_top_window(cls, caption) -> list:
        """
        查找窗体
        :param caption: 窗口标题部分文字
        :return:
        """
        return [w for w in cls.show_top_windows() if caption in w.caption]

    @classmethod
    def find_sub_windows(cls, hwnd_py=None, class_name=None, caption=None, index=None):
        """
        返回窗体下全部的子窗体,默认主窗体下的窗体
        :param hwnd_py: 句柄 十进制
        :param class_name: 窗口类名,返回特定类名
        :param caption: 窗口标题,返回特定标题
        :param index: 位置,返回特定位置的窗口
        :return: 包含属性的全部子窗口
        """
        num = 0
        handle = 0
        sub_windows = []
        while True:
            # find next handle, return HwndPy
            handle = win32gui.FindWindowEx(hwnd_py, handle, class_name, caption)
            if handle == 0:
                # no more handle
                break
            # get handle attribution
            attr = cls.window_attr(handle)
            # append to list
            sub_windows.append(tuple(list(attr) + [num]))
            num += 1
        if index is not None:
            return sub_windows[index]
        else:
            return sub_windows

    @classmethod
    def show_all_windows(cls, window=None, handle_list=None, handle_dict=None):
        """
        生成窗口全部对应的关系
        :param window: 目标父窗口
        :param handle_list: 默认为[[None]]
        :param handle_dict: 用于存放对应关系
        :return: 返回目标窗口下全部子父窗口的字典
        """
        if not handle_list and not handle_dict:
            handle_list = [[None]]
            handle_dict = dict()
        sys.setrecursionlimit(1000000)
        if window:
            handle_list[-1][0] = window
            handles = cls.find_sub_windows(handle_list[-1][0][2])
        else:
            handles = cls.find_sub_windows()
        for handle in handles:
            handle_dict[handle] = window
        # 这个根节点已经遍历完,删除
        del handle_list[-1][0]
        # 如果有叶节点,非空,则加入新的叶节点
        if handles:
            handle_list.append(handles)
        # 删除已被清空的根
        handle_list = [HandleGroup for HandleGroup in handle_list if HandleGroup]
        # 如果还有根就继续遍历,否则输出树
        if handle_list:
            return cls.show_all_windows(window=handle_list[-1][0], handle_list=handle_list, handle_dict=handle_dict)
        else:
            return handle_dict

    @classmethod
    def find_handle_path(cls, hwnd_spy, num):
        """
        寻找特定窗口的寻找路径
        找到全部层级的对应关系,然后反向搜索
        :param hwnd_spy: 窗口句柄(十六进制)
        :param num: 窗口所属index,在spy++内查看
        :return:
        parent_window:顶层窗口
        target_path:路径的index
        """
        all_path = cls.show_all_windows()
        key = tuple(list(cls.window_attr(int(hwnd_spy))) + [num])
        handle_path = [key]
        while True:
            key = all_path[key]
            if not key:
                handle_path = handle_path[::-1]
                parent_window = handle_path[0]
                target_path = [(i[-1]) for i in handle_path[1:]]
                return parent_window, target_path
            handle_path.append(key)

    @classmethod
    def find_target_handle(cls, window, path):
        """
        递归寻找子窗口的句柄
        :param window: 祖父窗口的完整句柄 (WindowName, ClassName, HwndPy, HwndSpy)
        :param path: 子窗口列表
        :return: 目标窗口的完整属性
        """
        for i in range(len(path)):
            window = cls.find_sub_windows(window[2], index=path[i])
        return window

Use

1、获取窗体属性

# 获取句柄1902690的属性
window = PySpy.window_attr(hwnd_py=1902690)
print(window)
Window(caption='百度云', class_name='ATL:011386F8', hwnd_py=1902690, hwnd_spy='0x1d0862')

2、获取全部顶层窗体

# 获取全部顶层窗体
windows = PySpy.show_top_windows()[:5]
for w in windows:
    print(w)
Window(caption='', class_name='ForegroundStaging', hwnd_py=66032, hwnd_spy='0x101f0')
Window(caption='', class_name='ForegroundStaging', hwnd_py=65982, hwnd_spy='0x101be')
Window(caption='', class_name='tooltips_class32', hwnd_py=65836, hwnd_spy='0x1012c')
Window(caption='', class_name='tooltips_class32', hwnd_py=65844, hwnd_spy='0x10134')
Window(caption='', class_name='tooltips_class32', hwnd_py=65856, hwnd_spy='0x10140')

3、查找指定的顶层窗体

# 查找顶层窗体
result = PySpy.find_top_window(caption='spy++')
print(result)
[Window(caption='spy++', class_name='CabinetWClass', hwnd_py=9571640, hwnd_spy='0x920d38')]

4、查找特定父窗体下全部子窗体

# 查找特定句柄下全部子窗体
window = PySpy.find_sub_windows(hwnd_py=9571640)
for w in windows:
    print(w)
Window(caption='', class_name='ForegroundStaging', hwnd_py=66032, hwnd_spy='0x101f0')
Window(caption='', class_name='ForegroundStaging', hwnd_py=65982, hwnd_spy='0x101be')
Window(caption='', class_name='tooltips_class32', hwnd_py=65836, hwnd_spy='0x1012c')
Window(caption='', class_name='tooltips_class32', hwnd_py=65844, hwnd_spy='0x10134')
Window(caption='', class_name='tooltips_class32', hwnd_py=65856, hwnd_spy='0x10140')

5、获取全部窗体父子对应关系

# 生成窗口全部对应的关系
windows = PySpy.show_all_windows(window=('spy++', 'CabinetWClass', 9571640, '0x920d38'))
for w in windows.items():
    print(w)
(('UIRibbonDockLeft', 'UIRibbonCommandBarDock', 1838390, '0x1c0d36', 0), ('spy++', 'CabinetWClass', 9571640, '0x920d38'))
(('UIRibbonDockRight', 'UIRibbonCommandBarDock', 1773536, '0x1b0fe0', 1), ('spy++', 'CabinetWClass', 9571640, '0x920d38'))
(('UIRibbonDockTop', 'UIRibbonCommandBarDock', 2756588, '0x2a0fec', 2), ('spy++', 'CabinetWClass', 9571640, '0x920d38'))
...
(('', 'ScrollBar', 52956100, '0x3280bc4', 0), ('', 'CtrlNotifySink', 3607746, '0x370cc2', 1))
(('', 'ReBarWindow32', 11603876, '0xb10fa4', 0), ('', 'WorkerW', 2755884, '0x2a0d2c', 1))
(('', 'ToolbarWindow32', 2427436, '0x250a2c', 0), ('', 'ReBarWindow32', 11603876, '0xb10fa4', 0))

6、获取特定窗体的查找路径

# 寻找特定窗口的寻找路径
parent, pth = PySpy.find_handle_path(hwnd_spy=0xb10fa4, num=0)
print(parent, pth)
('spy++', 'CabinetWClass', 9571640, '0x920d38', 232) [5, 1, 0]

7、根据查找路径获取到句柄信息(6的逆过程)

# 根据顶层窗体和路径 寻找子窗口的句柄
window = PySpy.find_target_handle(window=parent, path=pth)
print(window)
('', 'ReBarWindow32', 11603876, '0xb10fa4', 0)

8、根据窗体句柄,找到窗体位置

# 根据句柄定位窗体
x, y, m, n = win32gui.GetWindowRect(9571640)
pyautogui.moveTo((x + m) / 2, (y + n) / 2)


欢迎关注~ SandQuant 专注于全球金融数据和量化投资策略

本文内容由网友自发贡献,版权归原作者所有,本站不承担相应法律责任。如您发现有涉嫌抄袭侵权的内容,请联系:hwhale#tublm.com(使用前将#替换为@)

【Python】Spy++使用 的相关文章

  • ODFPy 文档 [关闭]

    Closed 这个问题正在寻求书籍 工具 软件库等的推荐 不满足堆栈溢出指南 help closed questions 目前不接受答案 我需要操作ODF文件格式 开放文档格式 开放办公室的内部格式 我需要用Python来完成 ODFPy
  • 如何使用 lstm 执行多类多输出分类

    I have multiclass multioutput classification see https scikit learn org stable modules multiclass html https scikit lear
  • 如果值已经是字符串,我是否应该避免转换为字符串?

    有时您必须使用列表理解将所有内容转换为字符串 包括字符串本身 b str a for a in l 但我必须这样做 b a if type a str else str a for a in l 我想知道是否str在字符串上已经足够优化no
  • 使用 matplotlib 在图像数据之上对线网格进行像素精确定位

    我试图在 python 库 matplotlib 显示的图像网格顶部精确地覆盖 1 像素宽线的网格 不幸的是 我似乎无法对结果进行足够精细的控制 以实现线网格与数据网格的正确对齐 如下面的代码所示 结果似乎总是很接近 但并不完全正确 我尝试
  • BeautifulSoup 不适用于某些网站

    我有这个脚本 import urrlib2 from bs4 import BeautifulSoup url http www shoptop ru page urllib2 urlopen url read soup Beautiful
  • 在python中浏览ftp目录

    我正在尝试使用 ftplib 使用 Python 3 从 ftp 服务器下载多个文件夹 我有一个文件夹名称列表 它们都位于文件夹 root 中 问题是我不知道如何浏览它们 当我使用cwd我可以进入更深的目录 但是如何再次起来呢 我正在尝试得
  • 类型错误:无法连接“str”和“instance”对象(python urllib)

    写一个python程序 我在使用时遇到了这个错误urllib urlopen功能 Traceback most recent call last File ChurchScraper py line 58 in
  • 如何让MagicMock返回多个值

    我想模拟一个图书馆 matplotlib对于它的价值 并且遇到一个问题 当调用模拟并期望返回元组时 它会失败 有一个更好的方法吗 Python 3 7 2 default Jan 13 2019 12 50 15 Clang 10 0 0
  • 如何在 PySide/PyQt 中制作一个位于屏幕中央的小部件?

    这段代码有效 但我想知道是否有更简单的方法 def center self qr self frameGeometry cp gui QDesktopWidget availableGeometry center qr moveCenter
  • 如何使用 Python Flask-Security 使用 bcrypt 加密密码?

    我正在尝试使用 Flask Security 文档中的标准基本示例 并使其正常工作 除了密码以明文形式存储之外 我知道这一行 user datastore create user email email protected cdn cgi
  • 在 PyQt 中使用 Windows 7 任务栏功能

    我正在寻找有关将一些新的 Windows 7 任务栏功能集成到我的 PyQt 应用程序中的信息 具体来说 如果已经存在使用新进度指示器的可能性 see here http www petri co il wp content uploads
  • 仅打印字符串中的元音

    我是Python新手 我正在尝试打印字符串中的所有元音 因此 如果有人输入 嘿 一切都好吗 所有元音都需要打印 但我不知道怎么做 所以这不是计算元音 而是打印元音 现在我已经得到了这个 sentence input Enter your s
  • Django 中同一个模型的多个多对多关系

    给定以下具有两个多对多关系的模型 class Child models Model name models CharField max length 80 class Foo models Model bar models ManyToMa
  • 使用 pyinstaller 制作的可执行文件出现运行时错误

    所以我使用 Pygame 制作了一个游戏 现在我想用它制作一个可执行文件 首选独立可执行文件 所以我用它来制作可执行文件 pyinstaller onefile main py 编译顺利 但运行时出现错误 这是错误 Traceback mo
  • Django 1.6:如何在视图中访问静态文件

    我已经尝试过解决方案here https stackoverflow com questions 11721818 django get the static files url in view这对我不起作用 我正在为 Python 创建一
  • 如何下载和使用对象检测数据集(例如 coco 或 pascal)

    我对物体检测领域非常陌生 我想知道是否有人可以帮助我下载和使用对象检测数据集 例如 coco 或 pascal 当我下载数据集后访问他们的网站时 我觉得我不知道应该如何处理它们 我知道这个问题很愚蠢 但是开始的提示可能非常有用 谢谢 我正在
  • Python 中的“finally”总是执行吗?

    对于Python中任何可能的try finally块 是否保证finally块总是会被执行吗 例如 假设我在except block try 1 0 except ZeroDivisionError return finally print
  • FileAllowed 不显示错误消息

    我正在使用 WTForms 我正在对文件上传应用验证 并将其限制为仅 jpg png 和 pdf 格式 但是 如果我输入不正确 则不会出现错误消息 我按照这个教程https flask wtf readthedocs io en stabl
  • Mac 上的 PythonXY?

    如何在 Mac OS X Lion 上安装 Python 我开始了 它应该能够通过 macports 但无论如何我找不到 mac ports 网站上所述的端口 pythonXY 我对 MAC 和 pythonXY 都不太了解 但在 pyth
  • Python 线程与 Linux 中的多处理

    基于此question https stackoverflow com questions 807506 threads vs processes in linux我假设创建新流程应该几乎和创造新线程在Linux中 然而 很少的测试显示出截

随机推荐

  • 能赢吗?

    Description 有一堆石子 xff0c 总共有n枚 xff0c 两人轮流拿 xff0c 最少拿一枚 xff0c 最多拿k枚 xff0c 拿到最后一枚的人获胜 先手拿的人可以保证自己必胜吗 xff1f Input 第一行输入一个整数T
  • python e指数函数,常用的e指数代码

    在 python中 xff0c 有一种函数叫做e指数函数 xff08 exponential function xff09 xff0c 它的名称非常的直接 xff0c 是我们在进行数值计算时经常用到的一种函数 下面就让我们一起来学习一下这种
  • 栈的概念及性质

    栈的基本概念 栈的定义 栈是一种只能在一端进行插入或删除的线性表 其中插入被称作进栈 xff0c 删除被称作出栈 允许进行插入或删除操作的一端被称为栈顶 xff0c 另一段被称为栈底 xff0c 栈底固定不变 其中 xff0c 栈顶由一个称
  • python requests post 使用方法

    使用python模拟浏览器发送post请求 span class token keyword import span requests 1 格式request post xff1a request span class token punc
  • 各类Python项目的项目结构及代码组织最佳实践

    1 了解Python项目文件组织结构非常重要 为什么要掌握pythob项目结构 xff1f 优秀的程序员都使用规范的项目代码结构 xff0c 了解这些好的习惯方式 xff0c 能帮助你快速读懂代码如果项目是几个人合作开发 xff0c 好的代
  • Python简单的位运算

    位运算 程序中的数在计算机内存中都是以二进制的形式存在的 xff0c 位运算就是直接对整数在内存中对应的二进制位进行操作 位运算分为 6 种如下 xff1a 1 按位与 按位与运算符 xff1a 参与运算的两个值 如果两个相应位都为1 则该
  • 【Linux】WLAN接口桥接

    一 内核补丁 因为Linux内核会在注册特定设备时将会将dev gt priv flags置为IFF DONT BRIDGE xff0c 所以现还不支持sta p2p client adhoc等无线接口加入到桥接中去的 xff0c 所以要支
  • Python学习小记-爬虫基础例子之抓取热门游戏排行榜-2020-3-2

    span class token keyword import span urllib span class token punctuation span request span class token keyword import sp
  • 《A Survey on Aspect-Based Sentiment Analysis: Tasks, Methods, and Challenges》阅读笔记

    忙活了一阵子后 xff0c 现在终于有空研究一下目前如火如荼的ABSA了 xff0c 当然 xff0c 还是先从综述出发 A Survey on Aspect Based Sentiment Analysis Tasks Methods a
  • A problem has occurred and the system can‘t recover问题的解决

    A problem has occurred and the system can 39 t recover问题的解决 问题描述解决方法参考博客 问题描述 启动后无法进入图形界面 xff0c 出现如下报错内容 按 ctrl 43 alt 4
  • 安装:WSL2(Ubuntu18.04)+miniconda3+mysql数据库+windows pycharm连接wsl

    一 WSL2 xff08 Ubuntu18 04安装 xff09 1 开启 适用于Linux的Windows子系统 找到控制面板 程序和功能 启用或关闭Windows功能 xff0c 选中 适用于Linux的Windows子系统 和 虚拟机
  • 【Python入门】:字典与集合

    Problems span class token number 1 span span class token punctuation span 创建一个通讯录 xff0c 步骤如下 xff0c 请根据步骤完成以下操作 xff1a spa
  • 【Python入门】:函数1

    Problems span class token number 1 span span class token punctuation span 编写函数showMsg span class token punctuation span
  • 【Python】@property私有属性的控制和保护

    64 property的使用 xff1a 对属性的控制和保护 一 保护变量 xff0c 防止被修改 64 property的首要目的是在访问私有变量时 xff0c 保护变量不被随意修改 span class token keyword cl
  • 【Python入门】:函数2

    Problems Source Code Output span class token number 318 span span class token number 321 span span class token number 19
  • 由浅入深介绍 Python Websocket 编程

    目录 1 为什么使用 Websocket 1 1 websocket 协议简介1 2 基本原理 2 如何用 Python 搭建 Websocket 服务2 1 安装websockets包2 2 编写 server 端代码 3 Python
  • 【Python入门】:文件与异常

    Problems Source Code Output 慈母手中线 xff0c 游子身上衣 临行密密缝 xff0c 意恐迟迟归 谁言寸草心 xff0c 报得三春晖 span class token number 7 795 span spa
  • 【Python】ESC服务器通过SMTP收发邮件

    解决方法 xff1a 1 25端口基本都不能用 xff0c 所以要用SSL xff0c qq用465 2 需要添加安全组 3 关闭防火墙 4 邮箱密码是授权码 Step1 xff1a 开通邮箱smtp服务 授权码作为后续的登录密码 Step
  • 【Python 量化交易】什么是择时策略

    量化金融 xff1a 什么是择时策略 xff1f 什么是市场择时 xff1f 市场择时概要择时成本损失机会的代价交易成本的代价真实例子 什么是市场择时 xff1f 市场择时 xff0c 也可以叫做市场选时 xff0c 是一种投资或者交易的策
  • 【Python】Spy++使用

    SPY 43 43 的使用和Python操作 1 spy 43 43 的基本操作1 1 窗口属性查找1 2 窗口spy 43 43 定位 2 python与spy 43 43 Source CodeUse 下载 Spy 43 43 xff1