求助: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; }
修改说明
- 静态文本缓冲区:将
coords替换为全局静态缓冲区g_tooltipText,确保Tooltip始终能访问到有效内存。 - 修正TOOLINFO结构大小:使用
TTTOOLINFO_V2_SIZE替代sizeof(TOOLINFO),兼容Windows Vista及以后的系统。 - 调整uFlags:移除
TTF_IDISHWND标志,跟踪模式下不需要绑定特定窗口。 - 使用TTM_UPDATETIPTEXT:替代
TTM_SETTOOLINFO更高效地更新Tooltip文本。 - 修正返回值:
WM_MOUSEMOVE中返回DefWindowProc的结果,确保系统默认鼠标行为正常执行。 - 添加TTM_SETMAXTIPWIDTH:确保Tooltip文本不会被截断。
内容的提问来源于stack exchange,提问作者odo
相关产品推荐
相关产品推荐

