Introduction

Having recieved a number of requests for a tutorial of sorts on developing Internet Explorer Toolbars with the RBDeskband and CWindowImpl wizards that I created, I have come up with a simple sample toolbar which can be used as a reference when developing your own toolbars or explorer bars. The tutorial will walk you through the stages of developing a toolbar for IE that is very similar to the Address bar that is already present in IE. I wanted to do a tutorial that would provide a realistic sample and would produce an end result that could be used by others after the tutorial was finish. So, the tutorial is going to show you how to develop an IE toolbar to get stock quote information from The Motley Fool website. So with that, let us get started.

Prequisites

This tutorial assumes that you already know how to program in C++ and know some information about ATL and COM. To work through this tutorial, you will need the following installed on your development machine:

The Framework

The IE toolbar consists of a COM component supporting IDeskband and a few other necessary interfaces for which IE looks for when loading registered toolbars, explorer bars and deskbands. The RBDeskband ATL Object Wizard provides most of the framework for this article. What we will need to do is create our project, a new COM object to house our toolbar, and a few CWindowImpl classes using the CWindowImpl ATL Object Wizard. Then connecting these parts together we will produce the IE toolbar in the picture at the top of the article. Visually the toolbar consists of an editbox and a toolbar with one button on it. In actuality the toolbar consists of the fore mentioned and a non visible window that is used to reflect messages to the Toolbar window, which will process or forward messges to itself and the edit box.

Creating The Shell

We will not work through the steps in creating the shell for our toolbar.

Creating The Project

If you have not done so already, start Visual C++6.

Then, from the File menu select New menu item; the New Dialog pops up.

Now our project has the DeskBand implementation that we will modify to produce the toolbar pictured at the top of the article. First we will create the window classes we will need and then come back to the Desbkand object and update it to use our window classes.

Creating The Window Classes

So back in the Framework section we said that we would need three window classes. One for the Edit Box, one for the toolbar, and one for message reflection back to the toolbar. Let us now create these window classes.

The Edit Window

We need to create a derived class from the standard EDIT button window class because we are going to be adding methods to our class to help support functionality of the toolbar. This is one reason why we cannot use a CContainedWindow object directly.

The Toolbar Window

We need to create a derived class from the standard TOOLBARCLASSNAME window class because we are going to be adding methods to our class to help support functionality of the toolbar. It will also be the parent for the edit box and the window which the IE host will request from our DeskBand.

The Reflection Window

We need to create a reflection window. It's just a CWindowImpl window implmented class. We are going to be adding a small bit of functionality just to create the toolbar object and be able to access the toolbar member from our deskband class.

Adding The Details

Now that we have our window classes available we can add our functionality for our toolbar to the appropriate window classes. Let us start with the deepest window class and work our way back out.

The EditQuote Details

For the EditQuote implementation, we need to be able to process keystrokes from the user and let the host that created our deskband object know our edit box has focus. To accomplish the first part, we need to look ahead and see that our DeskBand object will be implementing the IInputObject interface. So the host will query for that interface and know that we want to recieve messages and be given the chance to recieve focus. When the host sends our band messages to process they come through the IInputObject::TranslateAccelerator method. Our DeskBand will implement this method and it is best if our edit box, which will process the message, copy the TranslateAcceleratorIO method definition so our deskband can forward the message easily through a logical method call.

Figure 11. FileView Pane.

In the FileView pane (See Figure 11), double click the EditQuote.h item under Header Files. This will open the header file in the editing area. We now need to define the method definition for TranslateAcceleratorIO. To do this, add below the virtual CEditQuote destructor the following line of code:

STDMETHOD(TranslateAcceleratorIO)(LPMSG lpMsg);

Now Open the EditQuote.cpp source file and add the implementation of TranslateAcceleratorIO to the file

Now our DeskBand implementation can call this message and the edit box will process the key strokes properly. But wait, our edit box should notify the toolbar to load the quote details entered if the key stroke is the enter key. For this, we will need to define a message id and send that message to the parent window to process. In the EditQuote.h header file below the include statement, add the definition of our message id as shown below in bold.

#include<commctrl.h>constint WM_GETQUOTE = WM_USER + 1024;

In our EditQuote.cpp file we will add code to our TranslateAcceleratorIO method to process the enter key. Add the code below in bold to the EditQuote.cpp file.

Now our edit box will notify the parent when the user presses the enter key so that the parent can retrieve the requested ticker symbol details, this part will be implemented when we get to the toolbar details.

The first part of the Edit boxes implementation is finished. Now we need to be able for the edit box to have the deskband notify the host that we have focus or that we don't have focus any longer. To do this we will need to add a method for the deskband to pass us it's address so that we can call a method of the deskband class. These next steps will involve adding code to the CEditQuote class and to our Deskband class implementation.

Open the EditQuote.h file and add a forward reference to the CStockBar class so that we can defined our methods and members in our class header without knowing the implementation details of our deskband class, add the line in bold.

Before we implement the OnSetFocus method, we need to define a method for our deskband to tells of it's address and to retain that address for later use. Add the following lines of code to your EditQuote.h file below the TranslateAcceleratorIO definition.

void SetBand(CStockBar* pBand);
private:
CStockBar* m_pBand;

Now we can move to your EditQuote.cpp source file and implement the message handler, the SetBand method, and update the TranslateAcceleratorIO method for focus change. At the top of the EditQuote.cpp file add the following includes to the include list as shown below in bold.

Now when we can use the methods of the CStockBar class in our code. Add to the end of the constructor, the initalization of m_pBand. Don't forget the colon operator.

CEditQuote::CEditQuote()
: m_pBand(NULL)
{
}

Next we will add the SetBand implementation to our CEditQuote class. Notice that since it is not a com object we don't call AddRef or Release on it. It's just a pointer to the class and when it's destroyed our CEditQuote instance will also be destroyed. We could have also done this inline in our header file.

void CEditQuote::SetBand(CStockBar* pBand)
{
m_pBand = pBand;
}

Next we need to add our message handler for our EN_SETFOCUS message. Add the code below to the end of the EditQuote.cpp source file.

We have one more section of code to add to our CEditQuote implementation then we can move to our CStockBar class to define and implement the FocusChange method. Add the following code to the CEditQuote TranslateAcceleratorIO method as shown in bold. We add this code so the host knows that we are no longer needing messages.

We have finished off the work needed for the edit box to work properly in our toolbar. Now we need to build our toolbar up so that it has a button and contains our edit box. Then we will add the nessecities to our reflection window and update our IDeskBand to provide the correct information to our host. We are almost there. If you were to compile the project and run it, it would except that the band would look like the following in figure X.

The MFToolbar Details

For the implementation of the MFToolbar window, we need to be able to have it do the following things. It must be able to process the WM_GETQUOTE message from the EditQuote window, communicate with the web browser in which the toolbar is located, create the buttons and place the child windows on itself, forward messages to the EditQuote child window and size itself appropriately to the users actions.

So, the first thing we should do since our toolbar is going to contain an instance of CEditQuote is include the header file for the CEditQuote class. We will do this by opening the MFToolbar.h file and inserting the include statement for the CEditQuote class as shown in bold below.

#include<commctrl.h>#include"EditQuote.h"

Next we need to add a member to our toolbar class for the CEditQuote class. We will do this by adding a private section to the end of our class and defining a member variable as shown below in bold.

CMFToolbar();
virtual ~CMFToolbar();
private:
CEditQuote m_EditWnd;

Now that we have our member defined for our EditQuote window, we need to forward window messages to it so that keyboard inputs are processed appropriately. We do this by updating the toolbar message map to chain messages to our member as shown below in bold.

Looking forward, our deskband will need to get the EditQuote member to deterimine if it has focus and also to make it function. We could just expose the EditQuote member directly by having made it a public member instead of private, but by making it private we can expose a method that will expose our member giving us flexibility later to modify the class if the need should arise. So to expose the EditQuote member, we will add a fuction to our toolbar class to return the reference to the EditQuote member. In the toolbar header file, add the method definition and implementation below in bold to it.

Now we will create our toolbar window. Our toolbar consists of the EditQuote box and a button with an icon and text on it. To house the icon, our toolbar will need an image list handle to send to the toolbar window. So we need to add a few things to our toolbar header file before we go and implement the toolbar's creation. The first thing we will add is the member variable for our image list. Add the line in bold below to your toolbar header file.

private:
CEditQuote m_EditWnd;
HIMAGELIST m_hImageList;

Then we will add a message handler to our toolbar's message map and define the message handlers function definition to our header file and the follow lines of code in bold to your header file.

Before we can implement our toolbar's creation, we need to create a icon resource that our toolbar button will use next to its text. So go to the resource view and add a new icon to the project resources. You can do this by right clicking on "MotleyFool resources" and selecting "Insert..." from the context menu. In the Insert Resource dialog box, select Icon from the Resource type list and click the New button. This will insert a blank icon resource into your project. Rename the icon's resource ID by right clicking on the icon resource in the resource view and selecting the properties menu item from the context menu. Change the id to IDI_MOTLEY. Then draw or graciously borrow an icon from The Motley Fool to use on the toolbar. I graciously borrowed the icon from their website and adapted it into the icon.

Now we can implement it our toolbars creation. Open the MFToolbar source file and implement the details of the toolbar creation as described below.

First we need to include the project resource file so we can use the icon ID in our code. Add the line in bold below to our toolbar's source file as shown.

#include"stdafx.h"#include"resource.h"#include"MFToolbar.h"

Next we need to update our constructor implementation. We need to initialize our handle to the image list by setting it to NULL. Don't forget the colon.

CMFToolbar::CMFToolbar()
: m_hImageList(NULL)
{
}

Next we need to update our destructor, it should destroy the image list and destroy the window if it has not yet been destroyed.

Before we can implement our toolbar's creation, we need to add a resource symbol to our project for the toolbar button's ID. We could just use a #define statement at the top of the source file, but for cleanliness and since we are already including the resource.h file, we will add it to our resource file. Go to the "View" menu and select "Resource Symbols" menu item. Click the "New" button on the Resource Symbols dialog. Then enter a name of "IDM_GETQUOTE" and click OK. Then close the Resource Symbols dialog.

Now we can create our toolbar, we defined the OnCreate method in our header file and need to now implement it. Add the following function and its implementation to the end of the toolbar source file.

If you try to compile at this point, you will see that there are unresolved externals for the image list method calls. We need to add a library to the project. To do this select the "Project|Settings" menu item. On the Project Settings dialog, Select All Configurations from the "Settings For" combo box. Then select the "Link" tab and append to the "Object/Library modules" edit box "comctl32.lib". Then click OK. If you compile the project now, it will compile successfully and the image list unresolved externals will disappear.

We still have a few things we need to do to the Toolbar window. It needs to process Command messages, resopnd to WM_GETQUOTE messages, and resize itself. Let's conquer the latter first.

To orgainze the tooblar correctly, we should have the toolbar responsd to WM_SIZE messages. To do this, we will add to our tooblar header file a message handler for the WM_SIZE message and add a function definition for OnSize which WM_SIZE messages will be sent to. Open our toolbar header file and add the lines in bold below to it as shown.

Now we need to implement our OnSize function. Open the toolbar source file and add the function implementation below to the end of the file.

LRESULT CMFToolbar::OnSize(UINT uMsg, WPARAM wParam, LPARAM lParam, BOOL& bHandled)
{
// based on the size of the window area minus the size of the toolbar button,
// indent the toolbar so that we can place the edit box before the toolbar
// button. This will right justify the toolbar button in the toolbar and the
// edit box will use the vaction space to the left of the button but after the
// toolbar text as it's usable space.
RECT wndRect, btnRect;
GetClientRect(&wndRect);
::SendMessage(m_hWnd, TB_GETITEMRECT, 0, (LPARAM)&btnRect);
wndRect.right -= (btnRect.right - btnRect.left);
SendMessage(TB_SETINDENT, wndRect.right - wndRect.left);
// put a small spacing gap between the edit box's right edge and the toolbar button's left edge
wndRect.right -= 3;
m_EditWnd.MoveWindow(&wndRect, FALSE);
return0;
}

We still need to respond to user input for the toolbar button and from the edit box when the user presses the enter key. What we want the toolbar to do is tell the web browser host to navigate to the motely fool website and retrieve the stock quotes requested. First we need for the Deskband object to tell our toolbar window what the web browser instance is so that the toolbar window can communicate with it. To do this, we will add a private member variable and a public method in which the deskband can set the web browser instance. Our window will then use the member variable set to tell the web browser where to navigate and what to retrieve.

To do this, open the toolbar header file and add the lines in bold to the file.

Now, open the toolbar source file. We will update the constructor and initialize our member variable to null. Then we will update the toolbar destructor and release the member variable if it hasn't been. Then we will implement the SetBrowser method.

If you try and compile the project, you will notice that IWebBrowser2 is undefine in our header file. This is because we need to update our stdafx.h header file to include system files that define IWebBrowser2. To do this, open stdafx.h and add the following lines in bold to the file, then recompile.

extern CComModule _Module;
#include<atlcom.h>#include<atlwin.h>//// These are needed for IDeskBand
//#include<shlguid.h>#include<shlobj.h>

Now we can add message handlers for WM_COMMAND and WM_GETQUOTE to our toolbar class to handle the toolbar button being pressed and the enter key being pressed in the edit box by the user. To do this, we will need to add to our toolbar header file message handlers and function definitions for WM_COMMAND and WM_GETQUOTE. We will also need to add a private method which both will call if they need to to preform the same functionality (better than repeating code that does the same thing). So let's add the message handlers to the header file.

If you try to compile, you will notice that _bstr_t is undefined. That is because the class is defined in comdef.h. We need to add this to our stdafx.h header file so that we can use it as well as any other class in our project (which we will need to for IInputObject). Open the stdafx.h header file and add the lines in bold to the file as indicated.

Our implementation of the toolbar window is complete. Now we can move on to the Reflection window which creates our toolbars and forwards command messages to it.

The Reflection Window Details

For the Reflection Window, it's only purpose is to create the toolbar window (which it doesn't need to really do, but by doing so eases message forwarding) and forward messages to it. The reflection window is not visible, it's just a layer added so that message from the toolbar get to the toolbar. If we didn't have this window present, toolbar messages would get sent to the parent window (which we do not control) and we would never get them. This is not good since we need to respond to WM_COMMAND messages from the toolbar. Thus the need for the reflection window. So let's create the toolbar window and the message forwarding for it.

Open the ReflectionWnd.h header file. We will need to include the toolbar header file and add a private member variable to our reflection window to create it and to pass it to the message chain. First things first, add the include statement below so we can use the CMFToolbar class.

#include<commctrl.h>#include"MFToolbar.h"

Next add a private member variable to the end of the reflection window class for the toolbar as shown below.

We will also need a public function for our deskband class to get at our toolbar window. We will do the same as we did with the EditQuote window by providing a function to get at the member variable indirectly. Add the line of code in bold below to the header file as indicated.

Lastly, we need to create the toolbar window and will do so in the WM_CREATE message handler for our reflection window. Add the code below in bold to the reflection window header file. Then we will implement the OnCreate method in the source file.

Finishing Off The Deskband Toolbar

All that's left is for our deskband to create the toolbar window, have the host use the toolbar window, remove the unused IPersistStream implementation, implement IInputObject (for focus control) and do some code tweaking. So lets wrap this toolbar up. Open the StockBar.h header file. Remove the following lines of code that are in bold since we moved them to our stdafx.h for our other classes to also use.

#include"resource.h" // main symbols
//// These are needed for IDeskBand
//#include<shlguid.h>#include<shlobj.h>

There should now be nothing related to IPersist or IPersistStream between the IDockingWindow and IStockBar function declaration sections.

Now we need to remove the IPersist and IPersistStream function implementations from the StockBar.cpp source file. Find the GetClassID, IsDirty, Load, Save, and GetSizeMax function implementations and remove them from the file. They should be directly above the FocusChange Method we added earlier.

Now we can start adding code to our deskband. Open the stockbar.h header file and add the include for the reflection window class as shown below in bold.

#include"resource.h" // main symbols
#include"ReflectionWnd.h"

Next, find the protected section at the end of the file and replace the line

HWND m_hWnd;

with

CReflectionWnd m_ReflectWnd;

If you try to compile, you will find that it will be unsuccessful since we have removed m_hWnd and have not removed all occurances of m_hWnd from the class source file. We will replace all occurances with more appropriate code for our Reflection window and toolbar window. Open the StockBar.cpp source file, Remove the following line from the class constructor

m_hWnd(NULL),

your class constructor should now look as follows with a SetBand call added,

Next, update the RegisterAndCreateWindow function by replacing the temporary m_hWnd construction with the Reflection Window construction. Your RegisterAndCreateWindow implementation should look as follows:

BOOL CStockBar::RegisterAndCreateWindow()
{
RECT rect;
::GetClientRect(m_hWndParent, &rect);
m_ReflectWnd.Create(m_hWndParent, rect, NULL, WS_CHILD);
// The toolbar is the window that the host will be using so it is the window that is important.
return m_ReflectWnd.GetToolBar().IsWindow();
}

As we scroll through the code, we should fix some other things. A toolbar has a default height of 22 so we need to update our GetBandInfo method so that all the y measurements are 22 not 20. We also want our Integral sizing to be non sizeable so we need to set the DBIM_INTEGRAL x and y values to 0. While we are at it, we should fix the title of our toolbar so it says "The Motley Fool". You'll find this constant defined near the top of the source file, update it now.

We will now update the GetWindow call so that the toolbar window handle is returned and not the invalid m_hWnd variable. Update your GetWindow method so it looks as follows,

Now we need to update teh CloseDW method so that it does not destory our window buy hide it. We do this much like the MFC CToolbar class does, the class destructor will destroy the window. Your CloseDW method should look as follows,

Working our way through our class implementation, we will update the next method that needs updating. Update the ShowDW class to show or hide the toolbar window which the host is using. Your ShowDW class should look as follows,

We are almost done modifying the CStockBar class we need to do one last bit of updating. We need to modify the SetSite implementation to release the browser window that the toolbar may be using if we have a IInputObjectSite object and we need to query the OleCommandTarget's ServiceProvider for the IWebBrowser2 interface which we will then set to our toolbar. The modified parts of the SetSite method implementation are below in bold.

If you try to compile and use the toolbar right now, it will function partially. Tabbing and input control will not work correctly since we have yet to implement IInputObject for our deskband. Let's do that now since it's the last bit of code we will write for our simple deskband. You may also notice that the Toolbars context menu and View|Toolbars menus still say CStockBar Class. we will fix this problem in the Finishing Touches section below.

IInputObject Implementation

To get tabbing and input control to work correctly for any deskband is quite simple. You need but to implement IInputObject. The host will query our deskband to see if this interface is implemented and if it is will call the methods to see if we require input focus and let us also process messages from the user through the host. To do this, open the stockbar.h header file. To the stockbar class declaration add the line below in bold,

Our toolbar is functionaly done, compile, run it and see. It works as described and is fairly simple. Let's put some finishing UI touches on it for IE and our users to use.

Finishing Touches

The are only 2 finishing touches to make, One is to fix the context menu text. The other is to add button support to the main IE toolbar. Let's do them in order.

To fix the context menu text problem, open the StockBar.rgs project file and change all occurances of "StockBar Class" to "The Motley Fool Quotes". Compile it, run it, and see. While you only need to change one of them, it's nicer if they all match.

Now let's add button support for our toolbar. Update the stockbar.rgs file contents by appending the text below to it's contents.

The replace the 425 with the id from resource.h of IDI_MOTLEY. Also replace the BandClsid value with the GUID of our toolbar, the above values represent the source code for the article. Now compile the toolbar again. Then start IE and right click on the Standard Buttons toolbar, Select "Customize" from the context menu. Scroll down the Available toolbar buttons listbox and find "The Motley Fool" item, See Figure 12. Select it and click the Add button in the middle of the dialog. The item will move to the right as in Figure 13. Click the Close Button. You'll see the button added as shown in the before figure 14 and after figure 15.

Figure 12. CustomizeToolbar - Available Toolbar Buttons.

Figure 13. Customize Toolbar - Current Toolbar Buttons.

Figure 14. IE Standard Buttons - Before.

Figure 15. IE Standard Buttons - After.

Conclusion

While this tutorial is long hopefully the explaination was clear. From writing this tutorial it is easy to see that the RBDeskband ATL Object Wizard has some room for improvement but provided enough of a base for us to develop our simple example. In the end you can see that the toolbar we created is much like the Address bar. The differences lie in how MS implemented theirs versus how I implemented mine. As always feedback is welcome. Enjoy.

Share

About the Author

Erik lives in Redmond, Washington. He works as a Senior Software Engineer specializing in C++, COM, ATL and the middle-tier and now .NET. When he isn't coding for work, he can be found trying to extend Internet Explorer with yet another Desk band or simplifying his development process with ATL Object Wizards.

He spends his free time snowboarding, mountain biking, and online gaming.