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

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.06.02 07:27:26