You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

如何使用WinSpy++或Spy++检查鼠标移动时自动隐藏的弹窗、工具提示及悬停效果?兼谈Win32 API学习与文本抓取实现方案

解决自动隐藏弹窗/tooltip捕获与Win32窗口解剖、文本抓取问题

我来帮你拆解这两个关联的需求,从工具技巧到API实现再到研究路径一步步说:


一、用Spy++/WinSpy++捕获鼠标悬停时自动隐藏的弹窗、tooltip

这类控件的核心问题是生命周期短,鼠标移开就销毁,普通的窗口捕获根本赶不上,得用工具的延迟/锁定功能:

  • Spy++的延迟捕获技巧:

    1. 打开Spy++,点击工具栏上的「Find Window」按钮(那个瞄准镜图标),弹出捕获窗口。
    2. 点击捕获窗口里的「Delay」按钮,设置2-3秒的延迟时间,给你留出触发tooltip的时间。
    3. 按住捕获窗口里的瞄准镜图标不要松开,快速把鼠标移到要触发tooltip的控件上,保持不动直到延迟结束。
    4. 延迟到期后,Spy++就会锁定那个tooltip窗口,你就能看到它的类名(一般是tooltips_class32)、句柄、风格(肯定包含WS_POPUP)等信息。
  • WinSpy++的快捷锁定:
    WinSpy++支持快捷键锁定当前鼠标下的窗口(默认是Ctrl+Shift+F),你可以先把鼠标移到触发区域上方,按下快捷键的同时快速移到控件上触发tooltip,就能瞬间捕获到它。

  • 提前过滤类名:
    如果你知道目标控件的类名(比如tooltip都是tooltips_class32),可以直接在Spy++的「Search → Find Window」里,在「Class Name」输入框里填这个类名,然后点击「Find Now」,就能列出所有当前存在的tooltip窗口,哪怕是刚出现的。


二、Win32 API层面的窗口解剖与文本抓取

1. 窗口解剖的核心知识点与研究路径

要搞懂窗口的创建、销毁机制,先抓这些核心概念和关键词:

  • 窗口创建的入口是CreateWindowExW(宽字符版),所有窗口(包括popup、tooltip)都是通过这个函数创建的,你可以在调试器里给这个函数下断点,跟踪任何窗口的创建流程。
  • 窗口类(Window Class):每个窗口都属于一个类,类里定义了窗口的默认风格、图标、窗口过程(Window Procedure),用RegisterClassExW注册。比如tooltip对应的类是tooltips_class32,系统已经提前注册好了。
  • 窗口消息:窗口的所有行为(创建、销毁、显示、隐藏)都是通过消息驱动的,比如WM_CREATE是窗口创建时发送的消息,WM_DESTROY是销毁时的消息,你可以通过子类化(Subclassing)或者钩子(Hook)来监听这些消息。
  • 研究工具:除了Spy++,用x64dbg或WinDbg给CreateWindowExW下断点,能看到每个窗口创建时的参数(父窗口、风格、类名等);用Process Explorer可以查看窗口所属的进程,方便定位代码。

2. 抓取tooltip/WS_POPUP窗口文本的具体实现

根据窗口类型的不同,抓取方法略有区别:

针对Tooltip控件(tooltips_class32)

Tooltip是公共控件,不能直接用GetWindowText获取文本,需要发送TTM_GETTEXT消息:

#include <windows.h>
#include <commctrl.h>

wchar_t* GetTooltipText(HWND hwndTooltip) {
    static wchar_t szBuffer[512] = {0};
    TTOOLINFO ti = {0};
    ti.cbSize = sizeof(TTOOLINFO);
    ti.hwnd = hwndTooltip;
    ti.uFlags = TTF_IDISHWND; // 标识uId是窗口句柄
    ti.uId = reinterpret_cast<UINT_PTR>(hwndTooltip);
    ti.lpszText = szBuffer;

    // 发送消息获取文本
    SendMessageW(hwndTooltip, TTM_GETTEXT, sizeof(szBuffer)/sizeof(wchar_t), reinterpret_cast<LPARAM>(&ti));
    return szBuffer;
}

针对普通WS_POPUP窗口

先找到窗口句柄,再用GetWindowText:

#include <windows.h>
#include <vector>

// 枚举所有WS_POPUP窗口的回调函数
BOOL CALLBACK EnumPopupWindowsProc(HWND hwnd, LPARAM lParam) {
    DWORD style = GetWindowLongPtrW(hwnd, GWL_STYLE);
    if (style & WS_POPUP) {
        std::vector<HWND>* pPopupWindows = reinterpret_cast<std::vector<HWND>*>(lParam);
        pPopupWindows->push_back(hwnd);
    }
    return TRUE;
}

// 获取指定WS_POPUP窗口的文本
wchar_t* GetPopupWindowText(HWND hwndPopup) {
    static wchar_t szBuffer[512] = {0};
    GetWindowTextW(hwndPopup, szBuffer, 512);
    return szBuffer;
}

// 使用示例
void EnumerateAndGetPopupTexts() {
    std::vector<HWND> popupWindows;
    EnumWindows(EnumPopupWindowsProc, reinterpret_cast<LPARAM>(&popupWindows));

    for (HWND hwnd : popupWindows) {
        wchar_t szClassName[256] = {0};
        GetClassNameW(hwnd, szClassName, 256);
        wprintf(L"Popup Window Class: %s, Text: %s\n", szClassName, GetPopupWindowText(hwnd));
    }
}

解决你之前MsgBox文本提取失败的问题

MsgBox的顶层窗口类名是#32770,文本其实在它的子静态控件(类名Static)里,所以要先找到子控件:

void GetMsgBoxText(HWND hwndMsgBox) {
    // 找到MsgBox里的静态文本控件
    HWND hwndStaticText = FindWindowExW(hwndMsgBox, NULL, L"Static", NULL);
    if (hwndStaticText) {
        wchar_t szText[512] = {0};
        GetWindowTextW(hwndStaticText, szText, 512);
        wprintf(L"MsgBox Text: %s\n", szText);
    }
}

3. 自主研究的关键词推荐

如果要找更多资料,搜这些关键词准没错:

  • Win32 tooltip text extraction
  • WS_POPUP window enumeration
  • Spy++ capture hidden window
  • Win32 window subclassing
  • TTM_GETTEXT message
  • Win32 CreateWindowEx debugging

内容的提问来源于stack exchange,提问作者Nonlin

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.04.28 12:37:39