Click here to Skip to main content
Click here to Skip to main content

Fun with Common Controls Bitmaps and Toolbars

By , 29 May 2004
 

Sample screenshot

Introduction

I've been lurking around The Code Project for years now, but this is my first contribution. It's a mix-in class to help a CFrameWindowImpl derived class build a toolbar using the standard WIN32 common controls bitmaps. This class can be used to avoid weighing that lean-and-mean WTL app down with bitmap resources for such commonplace toolbar buttons as print, save, etc. While this may seem like an awful lot of trouble for nothing, I'm a WTL newbie and I wanted to see if I could make my WTL app a little bit leaner. Also, for a variety of other reasons, I like using system resources and controls instead of custom objects. For instance, if a resource is updated when a new version of Windows comes out, an application that inherits from the system automatically inherits the new look, whereas an application that defines its own resource must often be rebuilt just to avoid looking dated.

Background

The common controls bitmap constants and the indices to the individual images within those bitmaps are documented in the Platform SDK in various locations depending on the SDK version. Two locations I know of are:

  1. User Interface Services/Shell and Common Controls/Common Controls/Toolbar Controls/Toolbar Control Reference/Toolbar Standard Button Image Index Values
  2. User Interface Services/Windows Controls/Individual Control Information/Toolbar Controls/Toolbar Controls Reference/Constants/Toolbar Standard Button Image Index Values

In some cases, these constants are incorrectly documented. They're defined in the Platform SDK's <commctrl.h> file, which is also where the TBBUTTON structure type is defined:

typedef struct _TBBUTTON {
    int iBitmap;
    int idCommand;
    BYTE fsState;
    BYTE fsStyle;
#ifdef _WIN64
    BYTE bReserved[6];          // padding for alignment
#elif defined(_WIN32)
    BYTE bReserved[2];          // padding for alignment
#endif
    DWORD_PTR dwData;
    INT_PTR iString;
} TBBUTTON, NEAR* PTBBUTTON, *LPTBBUTTON;

It includes some undocumented padding which varies in size depending on the target platform. To simplify TBBUTTON array initialization statements, I define the TBBPADDING macro in SysToolbarCtrl.h, and macros for buttons corresponding to the toolbar icons.

In order to make the demo a little more interesting than a simple frame window with a do-nothing toolbar, I've stolen (well, OK, "borrowed" may be a gentler way of putting it) some other peoples' ideas:

  • Leon Finker's IShellBrowser article
  • Ed Gadziemski's Pixview sample application, which sadly doesn't seem to be available on Code Project anymore. I used his CPix class, which uses IPicture to load and render image files. I guess there are plenty of other examples of how to use IPicture.

Using the code

There are four simple steps to using this code:

  1. This probably doesn't need to be said, but I'll say it anyway: include the header file SysToolbarCtrl.h in your mainframe class precompiler directives:
    #include "SysToolbarCtrl.h"
  2. Include CFrameWindowImpl and CSysToolbarCtrl in your mainframe's inheritance list:
    class CMainFrame : public CFrameWindowImpl<CMainFrame>,
            public CSysToolbarCtrl<CMainFrame>,
            ...
  3. Instead of calling CreateSimpleToolBarCtrl in your mainframe class' OnCreate method (or whatever technique you normally use to create your toolbar), declare and initialize a TBBUTTON array, then call CreateSysToolbarCtrl as follows:
    // init toolbar button array, including separators, if any.
    TBBUTTON tbb[] =
    {
        // File|New
        STBB_STD_FILENEW,
        // File|Save
        STBB_STD_FILESAVE,
        // separator
        STBB_SEPARATOR,
        // View|Parent folder
        STBB_VIEW_PARENTFOLDER(ID_VIEW_PARENTFOLDER),
        // separator
        STBB_SEPARATOR,
        // Help|About
        STBB(STD_HELP, ID_APP_ABOUT, IDB_STD_SMALL_COLOR)
    };
    
        // create the toolbar control
        HWND hWndToolBar = CreateSysToolbarCtrl(m_hWnd, tbb,
            sizeof(tbb)/sizeof(TBBUTTON), &m_CmdBar);

    The code uses some macros I defined to specify the toolbar buttons. The macros come in three basic forms, which all use STBB (System Tool Bar Button) as a prefix:

    • STBB_IMG_ID, where IMG_ID is the button's image identifier as defined in <commctrl.h>. This form assigns a default command ID to the button using the command IDs defined in <atlres.h>;
    • STBB_IMG_ID(CMD_ID), where IMG_ID is as described above, and CMD_ID is the command ID that should be associated with the toolbar button. This is used when there is no standard command ID in the <atlres.h> file that corresponds to the button image, as in the case of VIEW_PARENTFOLDER;
    • STBB(IMG_ID, CMD_ID, BITMAP_ID), where IMG_ID and CMD_ID are as described previously, and BITMAP_ID is the bitmap ID defined in <commctrl.h> that contains the button image identified by IMG_ID. This is the one to use when you want to override the default command ID for a particular toolbar button, as I do in the demo for Help|About.

    Use your favorite resource editor to define your menus as you normally would. You can include the command IDs defined by your resource editor in the system toolbar. If <atlres.h> pre-defines any command IDs you could use in your menus, those can be used as well. Needless to say, the specified command ID should have an associated handler somewhere in the application's message map, and may have a corresponding menu item as well. In the demo, I've only provided a handler for two of the buttons (View|Parent folder, and Help|About) but it should be enough to give you a general idea of how it all works.

    You can also include a pointer to a CCommandBarCtrl as a parameter, and CreateSysToolbarCtrl will put the bitmaps on the appropriate menu items. If you aren't using a command bar, passing NULL (the default value) will cause CreateSysToolbarCtrl to ignore this parameter.

  4. Last, but not least, don't forget to remove any references to the toolbar bitmap you no longer want, from your resource script (*.rc file). You might want to do this manually using a bare-bones text editor instead of Visual C++. Simply commenting out the unwanted line should work:
    ///////////////////////////////////////////////////////////////////////
    //
    // Bitmap
    //
    
    // IDR_MAINFRAME           BITMAP  MOVEABLE PURE   "res\\Toolbar.bmp"

    Now if you try to open the toolbar in the Visual C++ IDE, it will squawk at you and ask you if you want to create a bitmap for the toolbar. JUST SAY NO!

Points of Interest

  • The STD_HELP image is the arrow-question-mark traditionally used for context help. Where's the classic yellow question mark?
  • There is no image for VIEW_VIEWMENU on any system I've looked at, yet it's in <commctrl.h>. What gives?
  • For the enterprising reader, there are other system bitmaps available to make hot/cold image toolbars. Of course, the code would have to be modified somewhat, and that's beyond the scope of this article.
  • Yes, I know, I might be cheating when my IShellBrowser implementation gets a selected item's PIDL from the IShellView window. That's because I don't know the "right" way to do it.
  • I found a way to synch a tree control with the shell folder view, but it's an undocumented hack and I didn't include it in this article. It's really just a brute-force method, but if anybody's interested, email me and I'll send you the code.

License

This article, along with any associated source code and files, is licensed under The Code Project Open License (CPOL)

About the Author

Jon Feider
Web Developer
United States United States
Member
A geek-of-all-trades who's masqueraded as a software engineer, metrologist, production engineer, BB stacker, and translator, just to name a few.

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.
Search this forum  
    Spacing  Noise  Layout  Per page   
Questioncan you give me more information about the application?The detailed stepsmemberskyfreedoms3 Jun '09 - 5:42 
HI,
can you give me more information about the application?The detailed steps
 
thanks!
 
The best post to my email
AnswerRe: can you give me more information about the application?The detailed stepsmemberJon Feider3 Jun '09 - 6:50 
Sure, what kind of information would you like? All of the code is in the download package, so feel free to inspect it and experiment with it at your leisure.
GeneralCombining standard and propietary bitmapsmemberPablo Aliskevicius23 Mar '04 - 2:01 
Great stuff!
 
I tried to apply it to a small utility of mine, which has functionality slightly beyond what is defined in the standard buttons, so I'd like, if possible, to combine 'standard' with 'propietary' bitmaps. The code, as far as I have seen, does not provide for that option. Have I missed something?
Do you know of anyone who has done it?
 
Thank you for your article,
 
Pablo.
 

 
Pablo_A
GeneralRe: Combining standard and propietary bitmapsmemberJon Feider30 Mar '04 - 14:17 
You're welcom, Pablo, and sorry for taking so long to answer this one. I was planning a sequel to this article to address this very issue, and I thought I had some code somewhere to back it up. I've rummaged through my drawers and swept aside the cobwebs, but I can't seem to find either the article or the code. I'm rather swamped with projects right now, but I hope to post the follow-up article by the end of April. In the meantime, all I can say is that the answer involves creating a toolbar button in the array that includes a local resource ID as the bitmap ID, and passing that ID as a parameter to the static CreateSysToolbarCtrl method. I didn't provide an example of it because I haven't actually finished and tested the code for the special case in the CreateSysToolbarCtrl logic. Hope this helps!
 
Jon.
Generalnice work .....memberhero24_m8 Nov '03 - 20:33 
Thanks for this very useful article.
 
Please give me the link to Platform SDK October 2002 update .
 
Thanks ....

GeneralRe: nice work .....memberJon Feider8 Nov '03 - 22:34 
The Platform SDK currently available is the February 2003 update, available at
http://www.microsoft.com/msdownload/platformsdk/sdkupdate/psdk-full.htm
It may be a rather convoluted process, so if you'd rather order the CD (around $10 I think), you can go to http://www.microsoft.com/msdownload/platformsdk/sdkupdate/.
Note that these sites are set up NOT to work with any other browser besides IE. Go figure!

GeneralRe: nice work .....memberhero24_m11 Nov '03 - 4:18 
Thank you very much for help !!!
 
My best regards

QuestionAnybody get this to work under XP?memberJon Feider3 Nov '03 - 23:01 
I've had reports that this doesn't work under XP, but I only have win2k and win9x boxes, so I can't verify them myself. Is this true? Has anybody tried downloading the executable and running it under XP?

AnswerRe: Anybody get this to work under XP?membera visitor11 Nov '03 - 12:31 
For the very quick look the problem (I have no idea about the initial reasons of it) looks like the following: the "LISTVIEW" window (the child of "SHELLDLL_DefView" aka your m_hWndShellView) is not shown (it has not VISIBLE flag). So as a quick "fix" (of course it is not correct, but just to "get it working") I've put ::ShowWindow(hWndListView,SW_NORMAL); as the last string of CMainFrame::BrowseObject() method (before return NOERROR; Smile | :) ).
It's been checked on XP+SP1.
Let us know about the proper fix.
GeneralRe: Anybody get this to work under XP?memberJon Feider12 Nov '03 - 22:52 
Thanks! I still don't have access to an XP machine for coding, so it may be a while before I get a chance to play around with it Frown | :(
GeneralI got it to build...memberToby Jacob Rhodes3 Nov '03 - 5:08 
Do a search for ILCombine on this site. There is an article that has the definitions for the IL... stuff.
 
Add the following to PIDL.cpp:
 
// dupicate an idlist
typedef WINSHELLAPI LPITEMIDLIST (WINAPI *fnILClone)(LPCITEMIDLIST pidl);
extern fnILClone ILClone;
 
typedef WINSHELLAPI LPITEMIDLIST (WINAPI *fnILCombine)(LPCITEMIDLIST pidl, LPCITEMIDLIST pidlsub);
extern fnILCombine ILCombine;
 
typedef WINSHELLAPI LPITEMIDLIST (WINAPI *fnILFindLastID)(LPCITEMIDLIST);
extern fnILFindLastID ILFindLastID;
 
// free an idlist using the shell allocater
// (how is this func diffrent from SHFree?
typedef WINSHELLAPI void (WINAPI *fnILFree)(LPITEMIDLIST pidl);
extern fnILFree ILFree;
 
// Creates a new list with the last tiem removed.
typedef WINSHELLAPI BOOL (WINAPI *fnILRemoveLastID)(LPCITEMIDLIST);
extern fnILRemoveLastID ILRemoveLastID;
 
static HINSTANCE g_hShell32;
 
-------------
Add the following to mainfrm.cpp:
 
// dupicate an idlist
typedef WINSHELLAPI LPITEMIDLIST (WINAPI *fnILClone)(LPCITEMIDLIST pidl);
extern fnILClone ILClone;
 
typedef WINSHELLAPI LPITEMIDLIST (WINAPI *fnILCombine)(LPCITEMIDLIST pidl, LPCITEMIDLIST pidlsub);
extern fnILCombine ILCombine;
 
typedef WINSHELLAPI LPITEMIDLIST (WINAPI *fnILFindLastID)(LPCITEMIDLIST);
extern fnILFindLastID ILFindLastID;
 
// free an idlist using the shell allocater
// (how is this func diffrent from SHFree?
typedef WINSHELLAPI void (WINAPI *fnILFree)(LPITEMIDLIST pidl);
extern fnILFree ILFree;
 
// Creates a new list with the last tiem removed.
typedef WINSHELLAPI BOOL (WINAPI *fnILRemoveLastID)(LPCITEMIDLIST);
extern fnILRemoveLastID ILRemoveLastID;
 
static HINSTANCE g_hShell32;
fnILClone ILClone;
fnILFree ILFree;
fnILCombine ILCombine;
fnILFindLastID ILFindLastID;
fnILRemoveLastID ILRemoveLastID;
 
-----------------
In OnCreate() of Mainfrm.cpp add:
 
g_hShell32 = ::LoadLibrary("shell32.dll");
ILClone = (fnILClone)::GetProcAddress(g_hShell32, (const char *)18);
ILFree = (fnILFree)::GetProcAddress(g_hShell32, (const char *)155);
ILCombine = (fnILCombine)::GetProcAddress(g_hShell32, (const char *)25);
ILFindLastID = (fnILFindLastID)::GetProcAddress(g_hShell32, (const char *)16);
ILRemoveLastID = (fnILRemoveLastID)::GetProcAddress(g_hShell32, (const char *)17);
 
That should be it.
GeneralRe: I got it to build...memberJon Feider3 Nov '03 - 6:09 
Actually, if you have a recent enough version of the Platform SDK, none of that should be necessary. The declarations are in <shlobj.h>, which is part of the Platform SDK. I'm not sure when it was included, but it's there in mine, which is the October 2002 update. <shlobj.h> is included by <atldlgs.h>, which is included in my stdafx.h file. If this works for those who don't have a new SDK though, it looks like you've done an awful lot of work. Thanks!
GeneralRe: I got it to build...memberJon Feider3 Nov '03 - 6:17 
Another article people might want to look at:
http://www.codeproject.com/shell/shlext.asp
The source files even include some declarations which might work too. I haven't tried them, but if anybody can, I'd like to know whether they work.
GeneralCannot buildsussStanislav Panasik2 Nov '03 - 22:51 
Hi !
 
Where is declarations of ILFree and other IL** ?
I have PSDK July 2002 and WTL 7, but cannot build
your project.
 
Best regards,
Stas
GeneralRe: Cannot buildmemberJon Feider3 Nov '03 - 6:06 
The declarations are in <shlobj.h>, which is part of the Platform SDK. I'm not sure when it was included, but it's there in mine, which is the October 2002 update. <shlobj.h> is included by <atldlgs.h>, which is included in my stdafx.h file.
GeneralRe: Cannot buildmemberStanislav Panasik3 Nov '03 - 7:02 
Hi !
 
OK, i see.
Thanks for answer, will try posted solution.
This is very good that the WTL used more widely,
so i read closely each article where it used,
because i think WTL demands higher class of programming
than other libraries.
 
When i build, will rate (i think very high)
Smile | :)
 
Best regards,
Stas
GeneralRe: Cannot buildmemberJon Feider3 Nov '03 - 7:11 
Good luck! Sorry about the "roadblocks". I'd forgotten about some of the intricacies of building this stuff, because this article is a small chunk I broke off from a much larger project I've been working on for some time. I initially went through some of the same gyrations as you and others are going through now trying to build this project, but I'd forgotten some of them. This is proving to be a good refresher course.
Generalatlres.hmemberbruno leclerc2 Nov '03 - 21:05 
Hi,
While building the project: "cannot open include file 'atlres.h'"
Thanks.
GeneralRe: atlres.hmemberJon Feider2 Nov '03 - 21:20 
It's a part of WTL. You have to install WTL before you can build this project. For more info, check this out:
http://www.codeproject.com/cpp/cppforumfaq.asp#cl_wtldownload
GeneralRe: atlres.hmemberbruno leclerc2 Nov '03 - 21:29 
Thanks.
For information, in this article about Wtl, the link "WTL 3.1" seems to be no longer available...

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

Permalink | Advertise | Privacy | Mobile
Web03 | 2.6.130523.1 | Last Updated 30 May 2004
Article Copyright 2003 by Jon Feider
Everything else Copyright © CodeProject, 1999-2013
Terms of Use
Layout: fixed | fluid