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

求助:Tracking Tooltip无法正常显示的原因排查

跟踪式Tooltip无法显示的问题修复

你的代码存在几个关键问题,导致跟踪Tooltip无法正常工作:

问题分析

  • 局部内存失效:WM_MOUSEMOVE中定义的coords是栈上的局部变量,当你通过TTM_SETTOOLINFO把它的地址传给Tooltip后,函数返回时该内存会被释放,Tooltip读取时会访问无效内存,导致显示异常或不显示。
  • TOOLINFO结构大小错误:不同Windows版本的TOOLINFO结构大小不同,直接使用sizeof(TOOLINFO)可能导致Tooltip控件无法正确解析参数,尤其在Windows 10等新版本系统中。
  • 不必要的TTF_IDISHWND标志:跟踪Tooltip不需要绑定到特定窗口句柄,该标志会干扰跟踪逻辑。

修正后的代码

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

HWND g_hwndTrackingTT;
TOOLINFO g_toolItem;
BOOL g_TrackingMouse = FALSE;
// 使用静态缓冲区存储Tooltip文本,避免局部内存释放问题
static char g_tooltipText[15];

HWND CreateTrackingToolTip(HWND hDlg)
{
    HWND hwndTT = CreateWindowEx(WS_EX_TOPMOST, TOOLTIPS_CLASS, NULL, 
                                 WS_POPUP | TTS_NOPREFIX | TTS_ALWAYSTIP, 
                                 CW_USEDEFAULT, CW_USEDEFAULT, CW_USEDEFAULT, CW_USEDEFAULT, 
                                 hDlg, NULL, NULL,NULL);
    if (!hwndTT)
      return NULL;
    
    // 使用TTTOOLINFO_V2_SIZE确保结构大小兼容新系统
    g_toolItem.cbSize   = TTTOOLINFO_V2_SIZE;
    // 移除TTF_IDISHWND,仅保留跟踪和绝对定位标志
    g_toolItem.uFlags   = TTF_TRACK | TTF_ABSOLUTE;
    g_toolItem.hwnd     = hDlg;
    g_toolItem.hinst    = NULL;
    g_toolItem.lpszText = g_tooltipText;
    // 跟踪模式下uId可以设为0,不需要绑定窗口
    g_toolItem.uId      = 0;
    // 设置Tooltip的矩形区域(跟踪模式下仅需占位)
    g_toolItem.rect.left = g_toolItem.rect.top = g_toolItem.rect.right = g_toolItem.rect.bottom = 0;
    
    SendMessage(hwndTT, TTM_ADDTOOL, 0, (LPARAM)(LPTOOLINFO)&g_toolItem);
    // 设置Tooltip最大宽度,确保文本正常显示
    SendMessage(hwndTT, TTM_SETMAXTIPWIDTH, 0, 200);
    return hwndTT;
}

LRESULT CALLBACK WndProc(HWND hwnd, UINT Message, WPARAM wParam, LPARAM lParam) 
{
    static int oldX, oldY;
    int newX, newY;

    switch(Message) 
    {
        case WM_CREATE:
            g_hwndTrackingTT = CreateTrackingToolTip(hwnd);
            return 0;

        case WM_MOUSELEAVE:
            SendMessage(g_hwndTrackingTT, TTM_TRACKACTIVATE, (WPARAM)FALSE, (LPARAM)&g_toolItem);
            g_TrackingMouse = FALSE;
            return 0;
    
        case WM_MOUSEMOVE:
            if (!g_TrackingMouse)
            {
                TRACKMOUSEEVENT tme = { sizeof(TRACKMOUSEEVENT) };
                tme.hwndTrack       = hwnd;
                tme.dwFlags         = TME_LEAVE;
                TrackMouseEvent(&tme);
                SendMessage(g_hwndTrackingTT, TTM_TRACKACTIVATE, (WPARAM)TRUE, (LPARAM)&g_toolItem);
                g_TrackingMouse = TRUE;
            }
    
            newX = GET_X_LPARAM(lParam);
            newY = GET_Y_LPARAM(lParam);
        
            if ((newX != oldX) || (newY != oldY))
            {
                oldX = newX;
                oldY = newY;
                
                snprintf(g_tooltipText, sizeof(g_tooltipText), "%d, %d", newX, newY);
            
                SendMessage(g_hwndTrackingTT, TTM_UPDATETIPTEXT, 0, (LPARAM)&g_toolItem);
    
                POINT pt = { newX, newY }; 
                ClientToScreen(hwnd, &pt);
                SendMessage(g_hwndTrackingTT, TTM_TRACKPOSITION, 0, (LPARAM)MAKELONG(pt.x + 10, pt.y - 20));
                
                SetWindowTextA(hwnd, g_tooltipText);
            }
            // 让DefWindowProc处理默认行为,避免影响其他鼠标逻辑
            return DefWindowProc(hwnd, Message, wParam, lParam);
            
        case WM_DESTROY: 
            PostQuitMessage(0);
            break;
            
        default:
            return DefWindowProc(hwnd, Message, wParam, lParam);
    }
    return 0;
}

int WINAPI WinMain(HINSTANCE hInstance, HINSTANCE hPrevInstance, LPSTR lpCmdLine, int nCmdShow)
{
    WNDCLASSEX wc;
    HWND hwnd;
    MSG msg;
    INITCOMMONCONTROLSEX icc;

    icc.dwSize = sizeof(INITCOMMONCONTROLSEX);
    icc.dwICC = ICC_WIN95_CLASSES | ICC_STANDARD_CLASSES;
    InitCommonControlsEx(&icc);

    memset(&wc, 0, sizeof(wc));
    wc.cbSize        = sizeof(WNDCLASSEX);
    wc.lpfnWndProc   = WndProc;
    wc.hInstance     = hInstance;
    wc.hCursor       = LoadCursor(NULL, IDC_ARROW);
    wc.hbrBackground = (HBRUSH)COLOR_WINDOW;
    wc.lpszClassName = "WindowClass";

    RegisterClassEx(&wc);

    hwnd = CreateWindowEx(WS_EX_CLIENTEDGE, "WindowClass", "Caption", WS_VISIBLE|WS_OVERLAPPEDWINDOW,
        CW_USEDEFAULT, CW_USEDEFAULT, 640, 480,
        NULL, NULL, hInstance, NULL);

    while(GetMessage(&msg, NULL, 0, 0) > 0)
    {
        TranslateMessage(&msg);
        DispatchMessage(&msg);
    }
    return msg.wParam;
}

修改说明

  1. 静态文本缓冲区:将coords替换为全局静态缓冲区g_tooltipText,确保Tooltip始终能访问到有效内存。
  2. 修正TOOLINFO结构大小:使用TTTOOLINFO_V2_SIZE替代sizeof(TOOLINFO),兼容Windows Vista及以后的系统。
  3. 调整uFlags:移除TTF_IDISHWND标志,跟踪模式下不需要绑定特定窗口。
  4. 使用TTM_UPDATETIPTEXT:替代TTM_SETTOOLINFO更高效地更新Tooltip文本。
  5. 修正返回值:WM_MOUSEMOVE中返回DefWindowProc的结果,确保系统默认鼠标行为正常执行。
  6. 添加TTM_SETMAXTIPWIDTH:确保Tooltip文本不会被截断。

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.06.15 04:50:59