Click here to Skip to main content
Licence 
First Posted 16 Jul 2007
Views 29,598
Downloads 603
Bookmarked 17 times

Fixing the toolbar button position bug for MFC CToolBar

By | 16 Jul 2007 | Article
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:

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

About the Author

Mingliang Zhu



China China

Member



Sign Up to vote   Poor Excellent
Add a reason or comment to your vote: x
Votes of 3 or less require a comment

Comments and Discussions

 
You must Sign In to use this message board. (secure sign-in)
 
Search this forum  
 FAQ
    Noise  Layout  Per page   
  Refresh
Questionwhen you fount it? Pinmemberflyingxu21:37 8 Aug '07  
AnswerRe: when you fount it? PinmemberMingliang Zhu20:56 23 Aug '07  
GeneralCool find, I never noticed it, but your solution... PinmemberPanic2k320:53 28 Jul '07  
GeneralRe: Cool find, I never noticed it, but your solution... PinmemberMingliang Zhu1:13 29 Jul '07  

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

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

Permalink | Advertise | Privacy | Mobile
Web01 | 2.5.120517.1 | Last Updated 16 Jul 2007
Article Copyright 2007 by Mingliang Zhu
Everything else Copyright © CodeProject, 1999-2012
Terms of Use
Layout: fixed | fluid