Click here to Skip to main content
15,868,141 members
Articles / Desktop Programming / MFC
Article

Fixing the toolbar button position bug for MFC CToolBar

Rate me:
Please Sign up or sign in to vote.
4.71/5 (5 votes)
16 Jul 20072 min read 84.5K   1.5K   20   4
An article on a bug of MFC CToolBar that may cause incorrect tooltips to be shown for a toolbar button

Screenshot - ScreenShot.png

Introduction

There is a bug in MFC's CToolBar class while showing tooltips for toolbar buttons. When calculating which button is to show the tooltip, the positions for toolbar buttons are moved 1 pixel to the right. As a result, when the mouse cursor is pointed to the left-most pixel of a toolbar button, the tooltip and status bar tip for the button on its left are actually shown!

It is quite easy to reproduce this bug. Create an MFC project, either SDI or MDI, which by default includes a toolbar. Leave all other project settings at their default values. Run the program and move the mouse cursor carefully to the left-most pixel of the "Open" toolbar button. CToolBar will show the tooltip for the "New" button, as demonstrated in the image above. The bug is quite misleading.

A number of well-known, MFC-based pieces of software are affected by this bug, including Spy++ and Dependency Walker.

Background

After a dig into the CToolBar source codes, I found the cause of this bug. When a tooltip may be needed for the toolbar, the framework calls CToolBar::OnToolHitTest() to determine which button the cursor is on. The source code of this method is shown below, copied from VC++ 6.0. The VC++ 8.0 code is nearly unchanged except that the return type int is changed into INT_PTR:

C++
int CToolBar::OnToolHitTest(CPoint point, TOOLINFO* pTI) const
{
    ASSERT_VALID(this);
    ASSERT(::IsWindow(m_hWnd));

    // check child windows first by calling CControlBar
    int nHit = CControlBar::OnToolHitTest(point, pTI);
    if (nHit != -1)
        return nHit;

    // now hit test against CToolBar buttons
    CToolBar* pBar = (CToolBar*)this;
    int nButtons = (int)pBar->DefWindowProc(TB_BUTTONCOUNT, 0, 0);
    for (int i = 0; i < nButtons; i++)
    {
        CRect rect;
        TBBUTTON button;
        if (pBar->DefWindowProc(TB_GETITEMRECT, i, (LPARAM)&rect))
        {
            ++rect.bottom;    // Buggy line
            ++rect.right;    // Buggy Line
            if (rect.PtInRect(point) &&
                pBar->DefWindowProc(TB_GETBUTTON, i, (LPARAM)&button) &&
                !(button.fsStyle & TBSTYLE_SEP))
            {
                int nHit = GetItemID(i);
                if (pTI != NULL && pTI->cbSize >= sizeof(AFX_OLDTOOLINFO))
                {
                    pTI->hwnd = m_hWnd;
                    pTI->rect = rect;
                    pTI->uId = nHit;
                    pTI->lpszText = LPSTR_TEXTCALLBACK;
                }
                // found matching rect, return the ID of the button
                return nHit != 0 ? nHit : -1;
            }
        }
    }
    return -1;
}

The lines involved with the bug are commented "Buggy Line." There seems to be no reason to increase rect.bottom and rect.right by 1 pixel, and MFC provided no comments for doing so. These lines cause incorrect button RECT to be retrieved and so wrong tooltips are shown. :(

Using the code

To fix the bug, simply create a class derived from CToolBar and overwrite the CToolBar::OnToolHitTest() method. Copy the original implementation of CToolBar::OnToolHitTest() and comment out the two buggy lines. Then replace CToolBar with the new class everywhere it is used in your project. The attached CFixedToolBar is an example.

There are still some things to do. If you compile the new class you will now get an error saying that AFX_OLDTOOLINFO is undefined. It is actually defined in <afximpl.h>. The code containing that is to make sure that a compatible version of TOOLINFO is passed in. So, instead of providing a definition of AFX_OLDTOOLINFO, it is sufficient to simply change sizeof(AFX_OLDTOOLINFO) to 40, i.e. the size of the AFX_OLDTOOLINFO structure. Now everything is OK. Enjoy. :)

History

  • 16 July, 2007 -- Original version posted

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
China China
This member has not yet provided a Biography. Assume it's interesting and varied, and probably something to do with programming.

Comments and Discussions

 
Questionwhen you fount it? Pin
flyingxu8-Aug-07 21:37
flyingxu8-Aug-07 21:37 
AnswerRe: when you fount it? Pin
Mingliang Zhu23-Aug-07 20:56
Mingliang Zhu23-Aug-07 20:56 
GeneralCool find, I never noticed it, but your solution... Pin
Panic2k328-Jul-07 20:53
Panic2k328-Jul-07 20:53 
GeneralRe: Cool find, I never noticed it, but your solution... Pin
Mingliang Zhu29-Jul-07 1:13
Mingliang Zhu29-Jul-07 1:13 

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.