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)
{
STDMETHOD(QueryInterface)(THIS_ REFIID riid,
LPVOID FAR* ppvObj) PURE;
STDMETHOD_(ULONG,AddRef)(THIS) PURE;
STDMETHOD_(ULONG,Release)(THIS) PURE;
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)
{
STDMETHOD(QueryInterface)(THIS_ REFIID riid, LPVOID FAR* ppvObj) PURE;
STDMETHOD_(ULONG,AddRef)(THIS) PURE;
STDMETHOD_(ULONG,Release)(THIS) PURE;
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)
{
STDMETHOD(QueryInterface)(THIS_ REFIID riid, LPVOID FAR* ppvObj) PURE;
STDMETHOD_(ULONG,AddRef)(THIS) PURE;
STDMETHOD_(ULONG,Release)(THIS) PURE;
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)
{
STDMETHOD(QueryInterface)(THIS_ REFIID riid, LPVOID FAR* ppvObj) PURE;
STDMETHOD_(ULONG,AddRef)(THIS) PURE;
STDMETHOD_(ULONG,Release)(THIS) PURE;
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;
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.
DEFINE_GUID(IID_IUIElement,
0xEC6FE84F,0xDC14,0x4FBB,0x88,0x9F,0xEA,0x50,0xFE,0x27,0xFE,0x0F);
DEFINE_GUID(IID_IUICommand,
0x4026DFB9,0x7691,0x4142,0xB7,0x1C,0xDC,0xF0,0x8E,0xA4,0xDD,0x9C);
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.