This tutorial has three parts-
- What these band objects are?
- How to implement them? and
- Sample. This is a modified version of MSJ and MSDN samples.
What are band objects?
Did you ever push "Search" toolbar button of IE? Yeah...then you must be familiar with band objects. Clicking on search button opens a window in the left side of IE. This window is 'Web Search Assistant'. This is a perfect example of a band object.
There are three main categories of band objects: Explorer bars( Info and command bands), desk bands, and tool bands. Info bands are vertical explorer bars and Command bands are horizontal explorer bars. There are two types of explorer bars. Vertical explorer bars or sometimes called Info bands and horizontal explorer bars, called command bands. Implementation of both is same. The Internet Explorer History, Favorites, and Search windows are all info bands.
Band objects were introduced in IE 4.0, Shell version 4.71. They are COM objects contained by IE or shell.
Explorer Bars
An explorer bar is a child window within the main IE window. Explorer bars can be vertical or horizontal. Horizontal explorer bars cover bottom area of IE's main window.

Explorer bars can be called from View/Explorer bars menu or on clicking of a toolbar button.
Desk Bands
Desk bands are dockable windows on the desktop. You can select it by right-clicking the taskbar and selecting it from the Toolbars submenu. You can even drag it to the desktop, and it will appear as a normal window.
Tool Bands
Radio control in this image is a perfect example of tool band. By creating a tool band, you can add a band to that rebar control.

Implementation of band objects?
A band object implements 6 interfaces. Three of them are optional. Here is a list of these interfaces
- IDeskBand
- IObjectWithSite
- IPersistStream
- IInputObject (optional)
- IContextMenu (optional)
- ICommandTarget (optional)
Before discussing any thing else, we should see little bit about these interfaces.
IDeskBand
IDeskBand is used to obtain information about a band object. This interface is used by the browser to obtain display information for a band. IDeskBand has only one method, GetBandInfo. HRESULT GetBandInfo( DWORD dwBandID, DWORD dwViewMode, DESKBANDINFO* pdbi );
This interface is derived from IDockingWindow. IDockingWindow interface provides docking feature to band objects so they can be docked inside a Windows Explorer window. This interface has three methods. You will see these three methods in sample example. These methods are
| Methods |
Description |
| CloseDW |
Notifies the docking window object that it is about to be removed |
| ResizeBorderDW |
Notifies the docking window object that the frame's border space has changed |
| ShowDW |
Instructs the docking window object to show or hide itself. |
IObjectWithSite
This interface provides communication between a band object and its container, a browser. It has two methods:
| Methods |
Description |
| SetSite |
Provides the site's IUnknown pointer to the object being managed. |
| GetSite |
Retrieves the last site set with IObjectWithSite::SetSite. |
IPersistStream
The IPersistStreamInit replaces IPersistStream and class IPersistStreamInitImpl provides a default implementation of this interface. You will see IPersistStreamInitImpl in our sample.
IInputObject
This interface is used by the browser. The IInputObject interface is used to notify the object of UI activation change and translate keyboard accelerators. Do not worry about it :). If a band object accepts user input then this interface must be implemented.
IContextMenu
This interface is used to create a context menu for a band object. This interface must be implemented if you want to create context menus. Otherwise there is no need to implement this interface. We will create a menu on right mouse click of the explorer bar. IContextMenu has three functions.
| Methods |
Description |
| GetCommandString |
Retrieves a command's text. |
| QueryContextMenu |
Adds commands to a context menu. |
| InvokeCommand |
Carries out the command associated with a context menu item. |
Band Objects and registry
Band objects are COM objects. Their CLSID must be registered. Besides CLSID, they should be registered in a category. This CATID tells the browser that what kind of band object is it. Here is a table listed with categories.
| Type |
Category |
| Vertical Explorer Bar |
CATID_InfoBand |
| Horizontal Explorer Bar |
CATID_CommBand |
| Desk Band |
CATID_DeskBand |
Registry entries required for an Explorer Bar
- Create a GUID in using guiden.exe. Say this GUID is MyGUID
- Create a new key, with the GUID as the name, in the registry under:
HKEY_CLASSES_ROOT\CLSID\MyGUID
The default value of the key should be set to the name of the Explorer Bar in the View menu.
- Create a new key, Implemented Categories, under:
HKEY_CLASSES_ROOT\CLSID\<MyGUID>\Implemented Categories
- Create a new key using the CATID of the type of Explorer Bar you are creating as the name of the key. This can be one of the following values:
| CATID |
Description |
| {00021494-0000-0000-C000-000000000046} |
Defines a horizontal Explorer Bar. |
| {00021493-0000-0000-C000-000000000046} |
Defines a vertical Explorer Bar. |
The result should look like:
//for a horizontal Explorer Bar
HKEY_CLASSES_ROOT\CLSID\<MyGUID>\
Implemented Categories\{00021494-0000-0000-C000-000000000046}
//for a vertical Explorer Bar
HKEY_CLASSES_ROOT\CLSID\<MyGUID>\
Implemented Categories\{00021493-0000-0000-C000-000000000046}
- Create a new key, InProcServer32, under:
HKEY_CLASSES_ROOT\CLSID\MyGUID\InProcServer32
Set the default value of the key to the full path of the Shdocvw.dll for HTML pages or the .dll file of the
Explorer Bar.
- Create a new string value, ThreadingModel, under:
HKEY_CLASSES_ROOT\CLSID\MyGUID\InProcServer32\ThreadingModel
Set the value of ThreadingModel to "Apartment".
- Create a new key, Instance, under:
HKEY_CLASSES_ROOT\CLSID\MyGUID\Instance
- Required for Explorer Bars that display an HTML page, but optional otherwise. Create a new string value,
CLSID, under:
HKEY_CLASSES_ROOT\CLSID\MyGUID\Instance\CLSID
Set the value of CLSID to {4D5C8C2A-D075-11d0-B416-00C04FB90376}.
- Create a new key, InitPropertyBag, under:
HKEY_CLASSES_ROOT\CLSID\MyGUID\Instance\InitPropertyBag
- Required for Explorer Bars that display an HTML page, but optional otherwise. Create a new string value,
Url, under:
HKEY_CLASSES_ROOT\CLSID\MyGUID\Instance\InitPropertyBag\Url
Set the value of Url to the location of the HTML file that contains the content for the Explorer Bar.
- Remove the following registry entries:
HKEY_CLASSES_ROOT\Component Categories\
{00021493-0000-0000-C000-000000000046}\Enum
HKEY_CLASSES_ROOT\Component Categories\
{00021494-0000-0000-C000-000000000046}\Enum
Implementing interfaces
The IObjectWithSite Interface
When the user selects an Explorer Bar, the container calls the corresponding band object's IObjectWithSite::SetSite method. The punkSite parameter will be set to the site's IUnknown pointer.
In general, a SetSite implementation should perform the following steps:
- Release any site pointer that is currently being held.
- If the pointer passed to SetSite is set to NULL, the band is being removed. SetSite can return S_OK.
- If the pointer passed to SetSite is non-NULL, a new site is being set. SetSite should do the following:
- Call QueryInterface on the site for its IOleWindow interface.
- Call IOleWindow::GetWindow to obtain the parent window's handle, and save it for future use. Release IOleWindow if it is no longer needed.
- Create the band object's window as a child of the window obtained in the previous step. Do not create it as a visible window.
- If the band object implements IInputObject, call QueryInterface on the site for its IInputObjectSite interface. Store the pointer to this interface for use later.
- If all steps are successful, return S_OK. If not, return the OLE-defined error code indicating what failed.
Here is SetSite and GetSite methods from my example. Here m_hwndParent is the parent window's handle.
STDMETHODIMP CCHTM::SetSite(IUnknown* pUnkSite)
{
// If punkSite is not NULL, a new site is being set.
if (pUnkSite)
{
// If a site is being held, release it and
// release the in-place object and frame interfaces.
//
if (_pSite)
{
_pSite->Release();
_pSite = NULL;
}
// Get the parent window.
IOleWindow* pOleWindow;
if (SUCCEEDED(pUnkSite->QueryInterface(IID_IOleWindow, (LPVOID*)&pOleWindow)))
{
pOleWindow->GetWindow(&_hwndParent);
pOleWindow->Release();
}
_ASSERT(_hwndParent);
if (!_hwndParent)
return E_FAIL;
RECT rc;
::GetClientRect(_hwndParent, &rc);
Create(_hwndParent, rc);
// Get and keep the IInputObjectSite pointer.
HRESULT hr = pUnkSite->QueryInterface(IID_IInputObjectSite, (LPVOID*)&_pSite);
_ASSERT(SUCCEEDED(hr));
// Get the IWebBrowser2 interface of Internet Explorer
// This is so we can do such things as navigate in the main
// window and write to the status bar.
IOleCommandTarget* pCmdTarget;
hr = pUnkSite->QueryInterface(IID_IOleCommandTarget, (LPVOID*)&pCmdTarget);
if (SUCCEEDED(hr))
{
IServiceProvider* pSP;
hr = pCmdTarget->QueryInterface(IID_IServiceProvider, (LPVOID*)&pSP);
pCmdTarget->Release();
if (SUCCEEDED(hr))
{
if (s_pFrameWB)
{s_pFrameWB->Release();
s_pFrameWB = NULL;
}
hr = pSP->QueryService(SID_SWebBrowserApp, IID_IWebBrowser2, (LPVOID*)&s_pFrameWB);
_ASSERT(s_pFrameWB);
pSP->Release();
}
}
/* // char * name = "_kBand";
// _bstr_t bstr = name;
BSTR bstr;
char * wndName = "sdfasf";
bstr = _bstr_t(wndName);
IHTMLWindow2Ptr pHTMLWnd;
IHTMLDocument2Ptr pDoc(__uuidof(HTMLDocument));
pDoc->get_parentWindow(&pHTMLWnd);
pHTMLWnd->get_name( &bstr);
::MessageBox(NULL,LPCTSTR((char*)bstr),"Title",MB_OK);
*/
// pHTMLWnd->put_name( bstr );
}
return S_OK;
}
STDMETHODIMP CCHTM::GetSite(REFIID riid, void **ppvSite)
{
*ppvSite = NULL;
if(_pSite)
return _pSite->QueryInterface(riid,ppvSite);
return E_FAIL;
}
Window creation is handled by OnCreate method. I am using CAxWindow to create a window which hosts a web
browser control. SetWindowLong is used to set window styles.
LRESULT OnCreate(UINT /*uMsg*/, WPARAM /*wParam*/, LPARAM /*lParam*/, BOOL& /*bHandled*/)
{
CAxWindow wnd(m_hWnd);
HRESULT hr = wnd.CreateControl(IDH_CHTM);
if (SUCCEEDED(hr))
{
hr = wnd.SetExternalDispatch(static_cast<ICHTMUI*>(this));
long dwStyle = wnd.GetWindowLong(GWL_STYLE);
wnd.SetWindowLong(GWL_STYLE,dwStyle | WS_HSCROLL | WS_VSCROLL);
}
if (SUCCEEDED(hr))
hr = wnd.QueryControl(IID_IWebBrowser2, (void**)&m_spBrowser);
return SUCCEEDED(hr) ? 0 : -1;
}
NOTE: If you dont want to host a web browser inside an Explorer bar then you can create just window using CreateChildWindow function given below. This is same as in MSDN. The window creation is dome by using CreateWindowEx. If the window does not exist, this method creates the Explorer Bar's window. The child window's handle is stored in m_hwnd.
BOOL CreateChildWindow(void)
{
//If the window doesn't exist yet, create it now.
if(!m_hWnd)
{
//Can't create a child window without a parent.
if(!m_hwndParent)
{
return FALSE;
}
//If the window class has not been registered, then do so.
WNDCLASS wc;
if(!GetClassInfo(g_hInst, EB_CLASS_NAME, &wc))
{
ZeroMemory(&wc, sizeof(wc));
wc.style = CS_HREDRAW | CS_VREDRAW | CS_GLOBALCLASS;
wc.lpfnWndProc = (WNDPROC)WndProc;
wc.cbClsExtra = 0;
wc.cbWndExtra = 0;
wc.hInstance = g_hInst;
wc.hIcon = NULL;
wc.hCursor = LoadCursor(NULL, IDC_ARROW);
wc.hbrBackground = (HBRUSH)CreateSolidBrush(RGB(0, 0, 192));
wc.lpszMenuName = NULL;
wc.lpszClassName = EB_CLASS_NAME;
if(!RegisterClass(&wc))
{
//If RegisterClass fails, CreateWindow below will fail.
}
}
RECT rc;
GetClientRect(m_hwndParent, &rc);
//Create the window. The WndProc will set m_hWnd.
CreateWindowEx( 0,
EB_CLASS_NAME,
NULL,
WS_CHILD | WS_CLIPSIBLINGS | WS_BORDER,
rc.left,
rc.top,
rc.right - rc.left,
rc.bottom - rc.top,
m_hwndParent,
NULL,
g_hInst,
(LPVOID)this);
}
return (NULL != m_hWnd);
}
Creating controls on an Explorer Bar
You can create your controls on this window too. Here is how to create a button on an Explorer bar. Write this code after CreateWindowEx.
::GetClientRect(m_hWnd, &rc);
HWND btnHandle = CreateWindow(
"mcbButton", // predefined class
"Button", // button text
WS_VISIBLE | WS_CHILD | BS_PUSHBUTTON, // styles
rc.left, // starting x position
rc.top, // starting y position
rc.right - rc.left, // button width
20, // button height
m_hWnd, // parent window
NULL, // No menu
(HINSTANCE) ::GetWindowLong(m_hWnd, GWL_HINSTANCE),
NULL); // pointer not needed
The IDeskBand Interface
There are two IOleWindow methods: GetWindow and ContextSensitiveHelp. GetWindow returns the band's child window handle, m_hwnd. Context-sensitive help is not implemented, so ContextSensitiveHelp returns E_NOTIMPL.
All three interfaces of IDockingWindow are also here.
STDMETHODIMP GetWindow(HWND* phwnd)
{
*phwnd = m_hWnd;
return S_OK;
}
// The ShowDW method either shows or hides depending on the value of its parameter. STDMETHODIMP
howDW(BOOL fShow)
{
if(m_hWnd)
{
if(fShow)
{
::ShowWindow(m_hWnd,SW_SHOW);
}
else
{
::ShowWindow(m_hWnd,SW_HIDE);
}
}
return S_OK;
}
// CloseDW Calls DestroyWindow
STDMETHODIMP CloseDW(unsigned long dwReserved)
{
ShowDW(FALSE);
if(::IsWindow(m_hWnd))
::DestroyWindow(m_hWnd);
m_hWnd = NULL;
return S_OK;
}
// This method is not implemented. Returns E_NOTIMPL;
STDMETHODIMP ResizeBorderDW(const RECT* prcBorder,
IUnknown* punkToolbarSite,
BOOL fReserved)
{
return E_NOTIMPL;
}
Here is GetBandInfo method of IDeskBand.
STDMETHODIMP CCHTM::GetBandInfo(DWORD dwBandID, DWORD dwViewMode,DESKBANDINFO* pdbi)
{
if(pdbi)
{
m_dwBandID = dwBandID;
m_dwViewMode = dwViewMode;
if(pdbi->dwMask & DBIM_MINSIZE)
{
pdbi->ptMinSize.x = MIN_SIZE_X;
pdbi->ptMinSize.y = MIN_SIZE_Y;
}
if(pdbi->dwMask & DBIM_MAXSIZE)
{
pdbi->ptMaxSize.x = -1;
pdbi->ptMaxSize.y = -1;
}
if(pdbi->dwMask & DBIM_INTEGRAL)
{
pdbi->ptIntegral.x = 1;
pdbi->ptIntegral.y = 1;
}
if(pdbi->dwMask & DBIM_ACTUAL)
{
pdbi->ptActual.x = 0;
pdbi->ptActual.y = 0;
}
if(pdbi->dwMask & DBIM_TITLE)
{
lstrcpyW(pdbi->wszTitle,L"Kruse Band");
}
if(pdbi->dwMask & DBIM_MODEFLAGS)
{
pdbi->dwModeFlags = DBIMF_VARIABLEHEIGHT;
}
if(pdbi->dwMask & DBIM_BKCOLOR)
{
pdbi->dwMask &= ~DBIM_BKCOLOR;
}
return S_OK;
}
return E_INVALIDARG;
}
Two more functions OnHScroll anbd OnVScroll are not implemented yet.
Hosting a web browser inside an explorer bar?
Project kBand.zip is attached with this article. This DLL implements a vertical explorer bar, called Kruse Band. Download this zip, compile it and call Explorer Bar from View\Explorer Bars menu's Kruse Band submenu. It Looks like in the above example. If you type URL (including http), click go, it opens that URL inside the Explorer bar.
See above about how it works.
References:
MSDN Library - April 2000
TO DO #1: Scroll bars doesn't work in this sample. You can provide that functionality and can resubmit the
code.
TO DO #2: View in main window doesn't work.
TO DO #3: Remove html and create a dialog with buttons on it. Open URL on dialog's button click.