Namespace Extensions: The Tasks Band Disclosed






4.86/5 (22 votes)
May 29, 2003
6 min read

72163
This article explains how to manipulate the folder tasks shown in your namespace extension, using undocumented COM interfaces.
Introduction
In Windows XP, Explorer has a cool new feature: A pane on the left that contains items like "other places", "Folder Tasks" and "Picture Tasks". But what if you are developing your own namespace extension? Can you put anything you like there? Microsoft will tell you, you can't change anything in it. But fortunately, we figured out a way to do this.
The information disclosed in this article was discovered while developing Whirling Dervishes NSELib, the first commercial product to implement this feature. It can be found here.
The function SHCreateShellFolderView(Ex)
The recommended way to implement your namespace extension is creating a def view object.
You do this by calling SHCreateShellFolderView(Ex)
. This is described in detail in the article
Namespace extensions: The undocumented
Windows Shell.
The article dates from 1999 and is therefore slightly out of date, but the basic workings are still the same in Windows XP. This article assumes you are using these functions to create the view object for your namespace extension. It relies on new callback messages that were introduced in Windows XP.
At this point, it is very well possible that you don't know what I'm talking about. In this case, you will have to read a few other articles on this web site that get you started, and try to implement your first namespace extension. After that you can come back here.
Vocabulary
First let's give everything a name so that we know what we are talking about.
- Tasks band: This is the band on the left side of a folder that we are talking about in this article. It contains items like Folder Tasks and Other places.
- Expando: This is the name Microsoft uses for the controls you find in the tasks band. They contain a title and a subsection that can be expanded or collapsed.
- Folder tasks: This is one of the sections in the tasks band. The title is usually "Folder Tasks" of "File and Folder Tasks". It contains standard items like Copy, Delete.
- Extra tasks: This is an optional section that has a different color for the title, and sometimes an icon next to the title. The title can be for example "Picture Tasks".
- Task Item: This is an individual command that appears in one of the expandos, such as Copy, Delete.
The callback message SFVM_GET_WEBVIEW_CONTENT
This is where it all starts. This new message is defined as follows:
#define SFVM_GET_WEBVIEW_CONTENT 83
This callback message is passed to the callback function or interface you supplied
in SHCreateShellFolderView(Ex)
.
The wParam
in this message can be ignored. The lParam
is a pointer to a SFVM_WEBVIEW_CONTENT_DATA
struct
, defined as follows:
struct SFVM_WEBVIEW_CONTENT_DATA
{
long l1;
long l2;
IUIElement *pUIElement1;
IUIElement *pUIElement2;
IEnumIDList *pEnum;
};
The first 2 members of this structure should be set to 0. It is possible that they can be used for something, but I didn't succeed in finding out what.
The IUIElement
members represent the titles of the task sections:
One for "Picture Tasks" and the like, and one for "Folder Tasks".
The last member is used for the "Related Places" section.
Manipulating the Related Places
This is the easiest part. It is regulated by the pEnum
member.
If you want Explorer to supply some related places for you, set this member to
NULL
. If you want to supply your own links, return an IEnumIDList
interface. You know this interface from the enumeration of your folders.
But this version will behave a bit different: You now enumerate the full PIDLs of
the folders you want to list.
Supplying the title for your expandos
The IUIElement
members of the struct
are used to supply the titles of the expandos.
The first one is used for the extra tasks, the second one for the folder tasks.
Set the corresponding pointer to NULL
for an expando you don't need.
Supply an IUElement
interface for the expandos you do want to show.
Let's take a look at this interface.
The interface IUIElement
This interface is fairly simple. It has functions to supply the title, tooltip and icon. You can let these vary with the selected items: Every time the selection changes, the functions to retrieve these values will be called again.
The interface is defined as follows:
#undef INTERFACE
#define INTERFACE IUIElement
DECLARE_INTERFACE_(IUIElement, IUnknown)
{
// IUnknown methods
STDMETHOD(QueryInterface)(THIS_ REFIID riid,
LPVOID FAR* ppvObj) PURE;
STDMETHOD_(ULONG,AddRef)(THIS) PURE;
STDMETHOD_(ULONG,Release)(THIS) PURE;
// IUIElement methods
STDMETHOD(get_Name)(THIS_ IShellItemArray *pItemArray,
BSTR *bstrName) PURE;
STDMETHOD(get_Icon)(THIS_ IShellItemArray *pItemArray,
BSTR *bstrName) PURE;
STDMETHOD(get_Tooltip)(THIS_ IShellItemArray *pItemArray,
BSTR *bstrName) PURE;
};
typedef IUIElement FAR* LPUIELEMENT;
Looks pretty simple, huh? Of course you return the name in get_Name
.
The only odd thing is how you allocate the string: You allocate it with
CoMemTaskAlloc
.
But what is this IShellItemArray
interface? It's an interface
that lets you retrieve the items that are selected.
The interface IShellItemArray
The interface IShellItemArray
is very similar to the interface
IShellItem
.
It is defined as follows:
#undef INTERFACE
#define INTERFACE IShellItemArray
DECLARE_INTERFACE_(IShellItemArray, IUnknown)
{
// IUnknown methods
STDMETHOD(QueryInterface)(THIS_ REFIID riid, LPVOID FAR* ppvObj) PURE;
STDMETHOD_(ULONG,AddRef)(THIS) PURE;
STDMETHOD_(ULONG,Release)(THIS) PURE;
// IShellItemArray methods
STDMETHOD(BindToHandler)(THIS_ IBindCtx *pbc,
REFGUID rbhid, REFIID riid, void **ppvOut)PURE;
STDMETHOD(GetAttrributes)(int nEnum,
DWORD dwRequested, DWORD *pdwResult) PURE;
STDMETHOD(GetCount)(UINT *pCount) PURE;
STDMETHOD(GetItemAt)(UINT nIndex,
IShellItem **ppItem) PURE;
STDMETHOD(EnumItems)(IEnumShellItems **) PURE;
};
typedef IShellItemArray FAR* LPSHELLITEMARRAY;
The normal way to get the items from this interface is by requesting an
IDataObject
interface. From this interface you can get the
CFSTR_SHELLIDLIST
data, which contains the PIDLs.
You can obtain an IDataObject
interface by calling
BindToHandler(NULL, BHID_DataObject, IID_IDataObject, (LPVOID *)&pDataObject)
.
The GUID BHID_DataObject
is defined as follows:
DEFINE_GUID(BHID_DataObject, 0xb8c0bd9f, 0xed24, 0x455c,
0x83, 0xe6, 0xd5, 0x39, 0x0c, 0x4f, 0xe8, 0xc4);
The callback message SFVM_GET_WEBVIEW_TASKS
Now that we have found a way to define the expandos, we will also need a way to define the task items contained in them.
For this, we need a second callback message: SFVM_GET_WEBVIEW_TASKS
.
This callback message is defined as follows:
#define SFVM_GET_WEBVIEW_TASKS 84
The wParam
can be ignored. The lParam
is a pointer to a
SFVM_WEBVIEW_TASKSECTION_DATA
struct
, defined as follows:
struct SFVM_WEBVIEW_TASKSECTION_DATA
{
IEnumUICommand *pEnum1;
IEnumUICommand *pEnum2;
};
Again, the first member is for the Extra Tasks, the second one for the Folder Tasks.
The requested interfaces are standard enumerators for the type IUICommand
.
This enumerator is defined as follows:
#undef INTERFACE
#define INTERFACE IEnumUICommand
DECLARE_INTERFACE_(IEnumUICommand, IUnknown)
{
// IUnknown methods
STDMETHOD(QueryInterface)(THIS_ REFIID riid, LPVOID FAR* ppvObj) PURE;
STDMETHOD_(ULONG,AddRef)(THIS) PURE;
STDMETHOD_(ULONG,Release)(THIS) PURE;
// *** IEnumUICommand methods ***
STDMETHOD(Next) (THIS_ ULONG celt,
LPUICOMMAND *rgelt,
ULONG *pceltFetched) PURE;
STDMETHOD(Skip) (THIS_ ULONG celt) PURE;
STDMETHOD(Reset) (THIS) PURE;
STDMETHOD(Clone) (THIS_ IEnumUICommand **ppenum) PURE;
};
As you might have guessed, every IUICommand
represents one task item.
Make sure to implement the method Reset
, because your enumerator will be
reused every time the selection changes.
The interface IUICommand
We are almost there now. All we need to do is define one more COM interface.
The interface IUICommand
is an extension of IUIElement
.
It is defined as follows:
#undef INTERFACE
#define INTERFACE IUICommand
DECLARE_INTERFACE_(IUICommand, IUIElement)
{
// IUnknown methods
STDMETHOD(QueryInterface)(THIS_ REFIID riid, LPVOID FAR* ppvObj) PURE;
STDMETHOD_(ULONG,AddRef)(THIS) PURE;
STDMETHOD_(ULONG,Release)(THIS) PURE;
// IUIElement methods
STDMETHOD(get_Name)(THIS_ IShellItemArray *pItemArray,
BSTR *bstrName) PURE;
STDMETHOD(get_Icon)(THIS_ IShellItemArray *pItemArray,
BSTR *bstrName) PURE;
STDMETHOD(get_Tooltip)(THIS_ IShellItemArray *pItemArray,
BSTR *bstrName) PURE;
// IUICommand methods
STDMETHOD(get_CanonicalName)(THIS_ GUID *pGuid) PURE;
STDMETHOD(get_State)(THIS_ IShellItemArray *pItemArray,
int nRequested, enum UISTATE *pState) PURE;
STDMETHOD(Invoke)(THIS_ IShellItemArray *pItemArray,
IBindCtx *pCtx) PURE;
};
typedef IUICommand FAR* LPUICOMMAND;
We already know most things about this interface. IShellItemArray
has already been defined, all the rest is known stuff, except for the UISTATE
enumeration.
I don't know the official values for this enum
. All I know is that 0
means show this verb, and anything else means hide this verb.
Supplying icons
Of course, you supply the icons for the task items in get_Icon
. The only question
is what format they should be in.
Well, very simple: They take the form module.dll,-id
.
Just like all the other strings, this string is allocated with CoTaskMemAlloc
.
Further issues
You should now have a basic understanding of the task items and how to manipulate them. But there may be a few more things that need clarification.
Showing and hiding verbs
In your enumerator, you should always return all the task items.
If an item should not be shown for the selected PIDLs, you should indicate this
in get_State
. If all tasks are hidden, the expando will also be hidden.
Invoking the task
When the user clicks on a task, as you expect, the function Invoke
will be called.
Be careful to retrieve the items using the method I described. This is the only way to make sure the result is correct.
Changing names
You can return a different name depending on the selected items.
Usually, the titles get different names: For example, "Folder Tasks" if no items are selected, "File and Folder Tasks" if at least one item is selected.
The task items themselves usually don't get different names. If you want to provide different tasks depending on the selected items, return all items in your enumerator and hide the ones that are not valid for this set of items.
IID values
You will need these for your QueryInterface
implementation.
// {EC6FE84F-DC14-4FBB-889F-EA50FE27FE0F}
DEFINE_GUID(IID_IUIElement,
0xEC6FE84F,0xDC14,0x4FBB,0x88,0x9F,0xEA,0x50,0xFE,0x27,0xFE,0x0F);
// {4026DFB9-7691-4142-B71C-DCF08EA4DD9C}
DEFINE_GUID(IID_IUICommand,
0x4026DFB9,0x7691,0x4142,0xB7,0x1C,0xDC,0xF0,0x8E,0xA4,0xDD,0x9C);
// {869447DA-9F84-4E2A-B92D-00642DC8A911}
DEFINE_GUID(IID_IEnumUICommand,
0x869447DA,0x9F84,0x4E2A,0xB9,0x2D,0x00,0x64,0x2D,0xC8,0xA9,0x11);
All the definitions
The interfaces, struct
s and other definitions used in this article will be made
available in the new version of shlext.h, which can be found
here.
A Delphi version will be made available here.