WinAPI程序中按钮Tooltip无法显示的问题排查
WinAPI按钮Tooltip无法显示的问题修复
问题分析与修复步骤
1. 通用控件初始化标志缺失
初始化INITCOMMONCONTROLSEX时,未包含Tooltip控件所需的初始化标志。当前代码仅指定了ICC_BAR_CLASSES | ICC_TAB_CLASSES,但Tooltip属于ICC_WIN95_CLASSES(兼容Win95及以上系统)范畴,必须添加该标志才能正确注册Tooltip控件类。
修复代码:
icex.dwICC = ICC_BAR_CLASSES | ICC_TAB_CLASSES | ICC_WIN95_CLASSES;
2. Tooltip窗口父窗口设置错误
创建Tooltip窗口时,错误地将父窗口设为按钮窗口hButton。Tooltip的父窗口必须是拥有该按钮的主窗口(即hMainWnd),否则Tooltip无法正常接收鼠标消息,无法触发显示逻辑。
修复代码:
HWND hwndTip = CreateWindowEx(WS_EX_TOPMOST, TOOLTIPS_CLASS, NULL, TTS_ALWAYSTIP, CW_USEDEFAULT, CW_USEDEFAULT, CW_USEDEFAULT, CW_USEDEFAULT, hMainWnd, NULL, // 将原hButton替换为hMainWnd hInstance, NULL);
3. WndProc存在语法错误
WM_COMMAND分支中存在多余的*/注释符号,会导致编译失败,需直接删除该符号。
4. 可选优化:兼容不同系统的TOOLINFO大小
部分系统版本中,直接使用sizeof(toolInfo)可能存在兼容性问题,可通过TTM_GETTOOLINFO_SIZE消息获取正确的结构体大小,替换原有的toolInfo.cbSize赋值:
toolInfo.cbSize = SendMessage(hwndTip, TTM_GETTOOLINFO_SIZE, 0, 0);
修复后的完整代码
#pragma once #define WIN32_LEAN_AND_MEAN #include <windows.h> #include <stdlib.h> #include <malloc.h> #include <memory.h> #include <tchar.h> #include "assert.h" #include "commctrl.h" #pragma comment( lib, "comctl32" ) #define MAX_LOADSTRING 100 #define ID_MY_BUTTON1 11 HINSTANCE hInst; WCHAR szTitle[MAX_LOADSTRING] = L"Window title"; WCHAR szWindowClass[MAX_LOADSTRING] = L"WindowClass"; ATOM MyRegisterClass(HINSTANCE hInstance); BOOL InitInstance(HINSTANCE, int); LRESULT CALLBACK WndProc(HWND, UINT, WPARAM, LPARAM); int APIENTRY wWinMain(_In_ HINSTANCE hInstance, _In_opt_ HINSTANCE hPrevInstance, _In_ LPWSTR lpCmdLine, _In_ int nCmdShow) { INITCOMMONCONTROLSEX icex; icex.dwSize = sizeof(INITCOMMONCONTROLSEX); // 添加ICC_WIN95_CLASSES以初始化Tooltip控件类 icex.dwICC = ICC_BAR_CLASSES | ICC_TAB_CLASSES | ICC_WIN95_CLASSES; bool ok = InitCommonControlsEx(&icex); assert(ok == TRUE); UNREFERENCED_PARAMETER(hPrevInstance); UNREFERENCED_PARAMETER(lpCmdLine); MyRegisterClass(hInstance); if (!InitInstance(hInstance, nCmdShow)) return FALSE; MSG msg; while (GetMessage(&msg, nullptr, 0, 0)) { TranslateMessage(&msg); DispatchMessage(&msg); } return (int)msg.wParam; } ATOM MyRegisterClass(HINSTANCE hInstance) { WNDCLASSEXW wcex; wcex.cbSize = sizeof(WNDCLASSEX); wcex.style = CS_HREDRAW | CS_VREDRAW; wcex.lpfnWndProc = WndProc; wcex.cbClsExtra = 0; wcex.cbWndExtra = 0; wcex.hInstance = hInstance; wcex.hIcon = NULL; wcex.hCursor = LoadCursor(nullptr, IDC_ARROW); wcex.hbrBackground = (HBRUSH)(COLOR_WINDOW + 1); wcex.lpszMenuName = NULL; wcex.lpszClassName = szWindowClass; wcex.hIconSm = NULL; return RegisterClassExW(&wcex); } bool CreateButton(HINSTANCE hInstance, HWND hMainWnd) { bool ok = true; int StartX = 0; const int ButtonWidth = 120; RECT rect; HMENU ButtonID = (HMENU) 1; HWND hButton = CreateWindow(L"BUTTON", L"My button 1", WS_VISIBLE | WS_CHILD, StartX, 0, ButtonWidth, 30, hMainWnd, ButtonID, hInst, NULL); // 创建Tooltip,父窗口改为主窗口hMainWnd HWND hwndTip = CreateWindowEx(WS_EX_TOPMOST, TOOLTIPS_CLASS, NULL, TTS_ALWAYSTIP, CW_USEDEFAULT, CW_USEDEFAULT, CW_USEDEFAULT, CW_USEDEFAULT, hMainWnd, NULL, hInstance, NULL); assert(hwndTip != NULL); // 关联Tooltip与按钮 TOOLINFO toolInfo = { 0 }; // 可选:使用TTM_GETTOOLINFO_SIZE获取兼容的大小 // toolInfo.cbSize = SendMessage(hwndTip, TTM_GETTOOLINFO_SIZE, 0, 0); toolInfo.cbSize = sizeof(toolInfo); toolInfo.uFlags = TTF_IDISHWND | TTF_SUBCLASS; toolInfo.hwnd = hMainWnd; toolInfo.uId = (UINT_PTR) hButton; toolInfo.lpszText = (LPWSTR)L"tool tip"; toolInfo.hinst = hInstance; auto res = SendMessage(hwndTip, TTM_ADDTOOL, 0, (LPARAM)&toolInfo); return true; } BOOL InitInstance(HINSTANCE hInstance, int nCmdShow) { hInst = hInstance; HWND hWnd = CreateWindowW(szWindowClass, szTitle, WS_OVERLAPPEDWINDOW, CW_USEDEFAULT, 0, CW_USEDEFAULT, 0, nullptr, nullptr, hInstance, nullptr); assert(hWnd != NULL); CreateButton(hInst, hWnd); ShowWindow(hWnd, nCmdShow); UpdateWindow(hWnd); return TRUE; } LRESULT CALLBACK WndProc(HWND hWnd, UINT message, WPARAM wParam, LPARAM lParam) { switch (message) { case WM_COMMAND: { int wmId = LOWORD(wParam); // 处理菜单选择: switch (wmId) { case ID_MY_BUTTON1: { break; } default: return DefWindowProc(hWnd, message, wParam, lParam); } } break; case WM_PAINT: { PAINTSTRUCT ps; HDC hdc = BeginPaint(hWnd, &ps); EndPaint(hWnd, &ps); } break; case WM_DESTROY: PostQuitMessage(0); break; default: return DefWindowProc(hWnd, message, wParam, lParam); } return 0; }
内容的提问来源于stack exchange,提问作者LUN
相关产品推荐
相关产品推荐

