首頁
編程語言
C語言|JAVA編程
Python編程
網頁編程
ASP編程|PHP編程
JSP編程
數據庫
MYSQL數據庫|SqlServer數據庫
Oracle數據庫|DB2數據庫
您现在的位置: 程式碼之家 >> 編程語言 >  >> 更多程式語言 >> 組合語言

Windows ToolTip 控件深度解析:從 TOOLINFO 結構到子類化訊息轉發的完整實作

     編輯:組合語言

什麼是 ToolTip (工具提示)?

ToolTip 是當滑鼠游標在特定區域停留時,自動彈出的小矩形視窗,顯示開發者預設的提示文字。它與狀態列功能相似,但 ToolTip 會在點擊或移出區域時自動消失。若需在工具列以外的任意視窗/控件顯示提示,必須自行建立與管理 ToolTip 控件。

建立 ToolTip 的四大步驟

  1. CreateWindowEx 建立 ToolTip 控件 (類別名 Tooltips_class32)。
  2. 定義 TOOLINFO 結構,指定監控區域 (「工具」)、提示文字、顯示行為旗標。
  3. TTM_ADDTOOL 將工具註冊給 ToolTip 控件。
  4. 訊息轉發:將目標視窗的滑鼠訊息轉發給 ToolTip (手動 TTM_RELAYEVENT 或自動 TTF_SUBCLASS 子類化)。

核心結構:TOOLINFO 詳細欄位解析

TOOLINFO STRUCT
    cbSize      DWORD ?      ; 結構大小 (必填,否則行為不可預期)
    uFlags      DWORD ?      ; 行為旗標組合
    hWnd        DWORD ?      ; 擁有工具的視窗代碼
    uId         DWORD ?      ; 工具 ID 或視窗代碼 (視 uFlags)
    rect        RECT  <>     ; 工具矩形區域 (相對 hWnd 客戶區)
    hInst       DWORD ?      ; 字串資源所在模組代碼
    lpszText    DWORD ?      ; 提示文字指標 / 資源 ID / LPSTR_TEXTCALLBACK
    lParam      LPARAM ?     ; 應用程式自訂資料
TOOLINFO ENDS

關鍵旗標

  • TTF_IDISHWND:uId 存放視窗代碼,工具覆蓋整個客戶區 (適用按鈕、編輯框)。
  • TTF_SUBCLASS:強烈建議開啟。ToolTip 會自動子類化目標視窗攔截滑鼠訊息,免除手動轉發麻煩。
  • TTF_CENTERTIP:提示顯示於工具區域正下方,而非滑鼠右下角。
  • TTF_RTLREADING:右到左閱讀順序 (阿拉伯/希伯來語環境)。

lpszText 三種用法

  1. 靜態字串指標:直接指向記憶體字串 (高位非 0)。
  2. 字串資源 ID:低位為資源 ID,高位為 0,需配合 hInst。
  3. LPSTR_TEXTCALLBACK:動態回調。ToolTip 需要時發送 TTN_NEEDTEXT 通知至 hWnd 取得文字,適合即時變動內容。

完整範例:對話框四象限 + 子控件自動註冊

此範例建立一個對話框,客戶區劃分為左上、右上、左下、右下四個「工具」,並自動列舉對話框上的按鈕控件註冊為工具 (使用 TTF_IDISHWND 覆蓋整個按鈕區域)。

核心程式碼片段

.data
ToolTipsClassName db "Tooltips_class32",0
MainDialogText1 db "This is the upper left area of the dialog",0
MainDialogText2 db "This is the upper right area of the dialog",0
MainDialogText3 db "This is the lower left area of the dialog",0
MainDialogText4 db "This is the lower right area of the dialog",0

.data?
hwndTool dd ?
hInstance dd ?

.code
DlgProc proc hDlg:DWORD, uMsg:DWORD, wParam:DWORD, lParam:DWORD
    LOCAL ti:TOOLINFO
    LOCAL id:DWORD
    LOCAL rect:RECT

    .if uMsg == WM_INITDIALOG
        invoke InitCommonControls
        ; 1. 建立 ToolTip 控件
        invoke CreateWindowEx, NULL, ADDR ToolTipsClassName, NULL, \
            TTS_ALWAYSTIP, CW_USEDEFAULT, CW_USEDEFAULT, CW_USEDEFAULT, CW_USEDEFAULT, \
            NULL, NULL, hInstance, NULL
        mov hwndTool, eax

        ; 2. 初始化通用 TOOLINFO
        mov id, 0
        mov ti.cbSize, sizeof TOOLINFO
        mov ti.uFlags, TTF_SUBCLASS      ; 關鍵:自動子類化攔截訊息
        push hDlg
        pop ti.hWnd
        invoke GetWindowRect, hDlg, addr rect

        ; 3. 註冊四個象限工具
        invoke SetDlgToolArea, hDlg, addr ti, addr MainDialogText1, id, addr rect
        inc id
        invoke SetDlgToolArea, hDlg, addr ti, addr MainDialogText2, id, addr rect
        inc id
        invoke SetDlgToolArea, hDlg, addr ti, addr MainDialogText3, id, addr rect
        inc id
        invoke SetDlgToolArea, hDlg, addr ti, addr MainDialogText4, id, addr rect

        ; 4. 列舉子控件 (按鈕等) 並自動註冊
        invoke EnumChildWindows, hDlg, addr EnumChild, addr ti

    .elseif uMsg == WM_CLOSE
        invoke EndDialog, hDlg, NULL
    .else
        mov eax, FALSE
        ret
    .endif
    mov eax, TRUE
    ret
DlgProc endp

;--------------------------------------------------
; 計算象限矩形並註冊工具
;--------------------------------------------------
SetDlgToolArea proc uses edi esi hDlg:DWORD, lpti:DWORD, lpText:DWORD, id:DWORD, lprect:DWORD
    mov edi, lpti
    mov esi, lprect
    assume esi:ptr RECT
    assume edi:ptr TOOLINFO

    ; 根據 id (0-3) 計算四個象限矩形 (左、上、右、下)
    .if id == 0        ; 左上
        mov [edi].rect.left, 0
        mov [edi].rect.top, 0
        mov eax, [esi].right
        sub eax, [esi].left
        shr eax, 1
        mov [edi].rect.right, eax
        mov eax, [esi].bottom
        sub eax, [esi].top
        shr eax, 1
        mov [edi].rect.bottom, eax
    .elseif id == 1    ; 右上
        ; ... (略) 計算右半部 ...
    .elseif id == 2    ; 左下
        ; ... (略) 計算下半部左側 ...
    .else              ; 右下
        ; ... (略) 計算右下 ...
    .endif

    push lpText
    pop [edi].lpszText
    invoke SendMessage, hwndTool, TTM_ADDTOOL, NULL, lpti
    assume edi:nothing
    assume esi:nothing
    ret
SetDlgToolArea endp

;--------------------------------------------------
; EnumChild 回呼:為每個子控件註冊工具 (使用 TTF_IDISHWND)
;--------------------------------------------------
EnumChild proc uses edi hwndChild:DWORD, lParam:DWORD
    LOCAL buffer[256]:BYTE
    mov edi, lParam
    assume edi:ptr TOOLINFO
    push hwndChild
    pop [edi].uId              ; uId = 子控件代碼
    or [edi].uFlags, TTF_IDISHWND ; 標記:工具 = 整個子控件客戶區
    invoke GetWindowText, hwndChild, addr buffer, 255
    lea eax, buffer
    mov [edi].lpszText, eax    ; 使用控件標題文字作為提示
    invoke SendMessage, hwndTool, TTM_ADDTOOL, NULL, edi
    assume edi:nothing
    ret
EnumChild endp

進階控制訊息速查表

訊息用途
TTM_ACTIVATE啟用/停用 ToolTip (wParam=TRUE/FALSE)
TTM_DELTOOL移除已註冊工具
TTM_GETTOOLINFO / TTM_SETTOOLINFO查詢/修改工具屬性
TTM_NEWTOOLRECT僅更新工具矩形區域
TTM_UPDATETIPTEXT僅更新提示文字
TTM_SETDELAYTIME設定顯示延遲、重新顯示延遲、自動關閉時間

避坑指南

  • cbSize 未填:最常見錯誤,導致 ToolTip 不顯示或亂碼。
  • 忘記 InitCommonControls:未載入 comctl32.dll,CreateWindowEx 會失敗。
  • 手動轉發訊息漏掉類型:若不使用 TTF_SUBCLASS,必須在目標視窗處理 WM_MOUSEMOVE, WM_LBUTTONDOWN, WM_LBUTTONUP, WM_RBUTTONDOWN, WM_RBUTTONUP, WM_MBUTTONDOWN, WM_MBUTTONUP 並轉發 TTM_RELAYEVENT。
  • 緩衝區生命週期:若 lpszText 指向區域變數緩衝區,確保 ToolTip 顯示期間緩衝區有效。
  1. 上一篇文章:
  2. 下一篇文章:
Copyright © 程式碼之家 All Rights Reserved