Click here to Skip to main content
15,867,308 members
Articles / Programming Languages / ASM
Article

KillTT: No More Tooltips!

Rate me:
Please Sign up or sign in to vote.
3.76/5 (11 votes)
27 Sep 20055 min read 92.8K   630   22   40
Disabling tooltips system-wide - efficiently!

Introduction

I wrote this program some time back, and finally decided to post it here and see how it is received. Please be gentle, this is my first article! :)

I routinely help people with Windows-related problems on annoyances.org. Several people there wanted to be rid of those wonderful little yellow boxes called tooltips. While their larger cousins, balloon tips, can be disabled via a registry modification, no known way exists to disable all tooltips. This program solves that problem. I will give you a bare-bones overview of how it works and let you play around with it yourself.

MSDN's Tooltip documentation, which I used throughout.

Warning: This code breaks on Windows 98, and I have no clue why, nor do I care at this point. Currently, this code is only debugged on Windows XP.

Background

To help show how I arrived at my current implementation, let me explain the initial implementation. First, I searched MSDN and found the TTM_ACTIVATE message. This appeared to be exactly what I needed:

  • TTM_ACTIVATE has no pointer parameters, and thus may be sent cross-process.
  • In most cases, the application will have no need to send this message.
  • This message doesn’t appear to have any side effects. When the control is disabled, the application thinks that the user just hasn’t hovered the mouse over the right spot.

In the end, this message was indeed the means I chose to use to disable tooltip controls.

The problem

Enumerating windows and disabling all tooltip controls is only the beginning. As new applications are started, new tooltip controls are created. In addition, Windows Explorer also sends a TTM_ACTIVATE message to its control whenever a new window’s button is added to the task bar. Hence the problem: Intercept the creation of windows and the sending of TTM_ACTIVATE messages. I rejected polling immediately as effective, but too resource-hungry. A system has hundreds of windows, and copying the class name of a window across process boundaries is quite expensive. Thus I settled on this approach: A system-wide hook function can be registered via SetWindowsHookEx. This hook can intercept a number of events, including window creation and every sent or posted message. I set up two hooks:

  • A WH_CBT hook to intercept window creation.
  • A WH_CALLWNDPROC hook to intercept TTM_ACTIVATE and, if the parameter is TRUE, post another message with a parameter of FALSE.

This system worked all right, but also suffered from performance problems.

The solution

Let’s walk through the current implementation from the beginning. The user interface is an icon in the system "tray" which, when clicked, either enables or disables all blocking. When the application is started, all windows are enumerated, and tooltip windows are sent a TTM_ACTIVATE (FALSE).

BOOL CALLBACK KillTT_EnableTooltips(HWND hwnd,LPARAM lParam)
{
    CHAR buf[256];
    GetClassName(hwnd,buf,256);
    if(lstrcmp(buf,TEXT("tooltips_class32"))==0)
    {
        PostMessage(hwnd,TTM_ACTIVATE,lParam,0);
    }
    EnumChildWindows(hwnd,&KillTT_EnableTooltips,lParam);
    return TRUE;
}

// ...

    KillTT_Hook();

// ...

    KillTT_SetBlock(kill);
    if(kill)
        EnumWindows(&KillTT_EnableTooltips,0);

KillTT_Hook simply registers a system-wide WH_CBT hook. Now, when a window is created, the hook DLL is loaded into the process that is creating the window, and the hook function CBTHook is called. First, however, when the DLL is loaded,

extern "C" BOOL WINAPI _DllMainCRTStartup(
                               HANDLE  hDllHandle,
                               DWORD   dwReason,
                               LPVOID  lpreserved)
{
    if(dwReason == DLL_PROCESS_ATTACH)
    {
        if(hInstance == NULL)
            hInstance = (HINSTANCE)hDllHandle;

        // Allocate a flag that determines if 
        // this DLL is still loaded
        bDLLPresent = (BOOL*)
            HeapAlloc(GetProcessHeap(), 0, sizeof(BOOL));
        *bDLLPresent = TRUE;

        DWORD dwThreadID;

        // Load ourselves.
        HMODULE hmSelf = LoadLibrary("killtt_helper.dll");

        // Create a monitor thread to unload us.
        CreateThread(NULL, 1024, &WaitForUnload, 
                                  hmSelf, 0, &dwThreadID);
    }
    if(dwReason == DLL_PROCESS_DETACH)
    {
        *bDLLPresent = FALSE;
    }

    return TRUE;
}

Several important notes:

  • When a hook is called, the DLL is loaded, the function run, and the DLL is unloaded again. As you might imagine, this can have some nasty consequences if code from the DLL was running at the time! Thus, I make sure the DLL remains in memory until I’m ready to unload it.
  • There must be a flag to tell whether the DLL is present, but it cannot be part of the data segment of the DLL, or it will be unloaded with the DLL. Thus, it is allocated on the heap.

When the hook function is called, it checks its parameters. If a tooltip window is being created, it uses standard subclassing techniques with a twist: Rather than using a function from the DLL, a stub, originally written in assembly, is created on the stack and the window procedure is set to that stub. The stub makes sure the DLL is present, and then that blocking is enabled. If both are true, the message and its parameters are checked. If the message is TTM_ACTIVATE, TTM_POPUP, TTM_TRACKACTIVATE, TTM_TRACKPOSITION, or TTM_RELAYEVENT, it is discarded. Otherwise, it is passed on to the original Windows procedure. (Note that throughout, I use placeholder values for addresses. Also note that I am not an assembly guru. I hate assembly, and write it poorly.)

ASM
    // lParam
    // wParam
    // uMsg
    // hwnd
    // Return Address
    // Old EBP             <-EBP
        push ebp

        mov eax, 0xFFFFFFFF // DllPresent flag
        cmp dword ptr[eax], 0
        je retold
        mov eax, 0x88888888 // Hooking flag
        cmp dword ptr[eax], 0
        je retold

        mov ebp, esp
        mov eax, dword ptr[ebp + 12]

        cmp eax, TTM_POPUP
        je ret0
        cmp eax, TTM_TRACKACTIVATE
        je ret0
        cmp eax, TTM_TRACKPOSITION
        je ret0
        cmp eax, TTM_RELAYEVENT
        je ret0
        cmp eax, TTM_ACTIVATE
        je ret0

retold:
        pop ebp
        pop eax
        push 0xAAAAAAAA    // Old window proc
        push eax
        mov eax, 0xBBBBBBBB  // CallWindowProc
        jmp eax

ret0:
        xor eax, eax
        pop ebp
        ret 16

I quickly realized a small flaw. As multiple tooltip Windows are created and destroyed, a small chunk of memory is left behind on the heap. If many tooltip Windows are created and destroyed, this small resource leak could become a big problem. Thus, the code included in ttproc.asm shows the full source of the assembler function, which includes a branch that deletes the procedure upon return from processing of WM_NCDESTROY, the last message sent to a window.

What I learned

  • Hooks set via SetWindowsHooksEx have a major performance impact if used incorrectly.
  • Hook DLL’s are loaded only for the duration of the hook function. Don’t rely on their presence at any other time!
  • Comments! I need more comments in my code!
  • If you’re packing code into a structure:
    • Test it! Step through in a debugger. Byte order may be different than you expect, resulting in nonsense.
    • Don’t forget #pragma pack! Padding added in between opcodes really messes things up.
    • Always call FlushInstructionCache, or bad things may happen! One machine exhibited sporadic crashes without this call. This took me hours to debug.
    • Get it right the first time! Code in structures is hard to rework.

TODO

  • Fix code for Win98.
  • Optimize assembly.
  • Fix DLL unloading mechanism.

Comments or questions appreciated! This is a fairly un-maintained project, but I will update it if something warrants the change.

License

This article has no explicit license attached to it but may contain usage terms in the article text or the download files themselves. If in doubt please contact the author via the discussion board below.

A list of licenses authors might use can be found here


Written By
Web Developer
United States United States
I've worked with C++ for about 9 years for personal work. Only recently have I begun to learn the true power of things such as templates and operator overloading.
I've also done work in a few other languages, and am proficient with Java, shell scripts and batch files, PHP, and HTML. I do CSS and .NET sometimes, but nothing advanced.
I am 18 years old and live in Ohio, USA. I enrolled in college when I was 16, and plan on finishing in September, 2006.

Comments and Discussions

 
QuestionKillit Pin
Member 139372073-Aug-18 8:22
Member 139372073-Aug-18 8:22 
QuestionKillTT Pin
Member 958801013-Nov-12 5:06
Member 958801013-Nov-12 5:06 
QuestionDisable Balloons Pin
DednDave11-Apr-12 2:22
DednDave11-Apr-12 2:22 
GeneralWhy , oh why kill those precious, precious tool tips? *snark* Pin
leturner4-Feb-09 4:31
leturner4-Feb-09 4:31 
GeneralJust that icon! Pin
mitche32330-Dec-08 17:56
mitche32330-Dec-08 17:56 
QuestionWorks on Vista business BUT adds yet another systray icon? Pin
AngelaB17-Nov-08 5:24
AngelaB17-Nov-08 5:24 
GeneralKillTT Pin
ELMocombo20-Aug-08 20:21
ELMocombo20-Aug-08 20:21 
GeneralHooking window Creation / Destruction Pin
Ohmu3-Jan-08 16:56
Ohmu3-Jan-08 16:56 
GeneralKill all tooltips Pin
johnjerrysue10-Dec-07 9:23
johnjerrysue10-Dec-07 9:23 
Questionmaximum count of windows in system? Pin
yph200401074-Dec-07 20:46
yph200401074-Dec-07 20:46 
GeneralKillTT Pin
turner862-Oct-07 20:47
turner862-Oct-07 20:47 
GeneralWilling to Pay ... Pin
glubrani16-Sep-07 10:49
glubrani16-Sep-07 10:49 
GeneralKillTT Pin
gymnp24-Aug-07 14:53
gymnp24-Aug-07 14:53 
QuestionLove it, but... [modified] Pin
BjarneMJ21-Mar-07 8:30
BjarneMJ21-Mar-07 8:30 
QuestionHow can kill end task app? Pin
manguonden5-Sep-06 0:52
manguonden5-Sep-06 0:52 
GeneralHung My Computer, but worked awesome until it did Pin
lyndarou23-Jun-06 8:39
lyndarou23-Jun-06 8:39 
GeneralRe: Hung My Computer, but worked awesome until it did Pin
Peregrine Falcon23-Jun-06 8:47
Peregrine Falcon23-Jun-06 8:47 
GeneralRe: Hung My Computer, but worked awesome until it did Pin
lyndarou23-Jun-06 9:15
lyndarou23-Jun-06 9:15 
GeneralRe: Hung My Computer, but worked awesome until it did Pin
Peregrine Falcon23-Jun-06 9:31
Peregrine Falcon23-Jun-06 9:31 
GeneralRe: Hung My Computer, but worked awesome until it did Pin
sam898837829-Jun-06 17:23
sam898837829-Jun-06 17:23 
GeneralRe: Hung My Computer, but worked awesome until it did Pin
I.R.Brainiac21-Mar-07 14:05
I.R.Brainiac21-Mar-07 14:05 
GeneralAwesome ! Pin
filouk28-Apr-06 6:05
filouk28-Apr-06 6:05 
GeneralCrashed my System Pin
gfilter7-Apr-06 10:58
gfilter7-Apr-06 10:58 
GeneralRe: Crashed my System Pin
sam898837829-Jun-06 18:07
sam898837829-Jun-06 18:07 
QuestionDoesn't Work with Notification Area nor 'Start' button Pin
Raptorikus14-Mar-06 8:51
Raptorikus14-Mar-06 8:51 
Thanks for writing such a cool application! Is there any way to also trap tooltips for the 'Start' button and notification area (where the clock and little icons on the right of the Taskbar reside)?

The problem seems to be because Explorer starts before KillTT, so there are already are Tooltip windows created? If I kill all instances of explorer.exe and then restart it, no tooltips appear!

Perhaps the program could be a DLL that starts with windows, prior to explorer? Or perhaps there is some other way to start it up?

-Raptor

General General    News News    Suggestion Suggestion    Question Question    Bug Bug    Answer Answer    Joke Joke    Praise Praise    Rant Rant    Admin Admin   

Use Ctrl+Left/Right to switch messages, Ctrl+Up/Down to switch threads, Ctrl+Shift+Left/Right to switch pages.