Contents
Introduction
As we saw in my previous article on jump lists, Windows 7 lets an application
customize its jump list using the IApplicationDestinations
and ICustomDestinationList
interfaces. In this article, we'll go beyond the built-in categories, and see how you can customize a jump list
by adding custom tasks, and how to handle those tasks in your program.
As with the other articles in this series, the sample code is built with Visual Studio 2008, WTL
8.0, and the beta
Windows 7 SDK.
Tasks in Jump Lists
In the previous article, we saw that jump lists can have two types of entries: destinations and tasks.
This article will cover tasks, which are commands that your app can carry out, such as "Create a new document."
A task is represented internally as an IShellLink
, so a task should be a command that can be communicated
to your app via the command line.
Recall that the user can pin your app to the Taskbar, and bring up the jump list from the pinned shortcut. That
means that your app doesn't have to be running before the user invokes a task. Therefore, tasks must be actions
that can be carried out at any time. For example, IE8 has a task to open a new InPrivate browser window, which
can be done regardless of whether IE is already running. Microsoft recommends that the list of tasks should be
static, and not change based on the current state of the app.
Tasks are shown in their own category, similar to the Recent and Frequent categories that we saw
last time. The Tasks category is always the last category (nearest the bottom) in the list, but its items
are always shown as long as there is room in the list. This may potentially leave less room for other categories,
so when deciding what tasks to put in the list, consider that having a lot of tasks will leave less room for recently-used
or frequently-used files, if you include those categories in your jump list.
There is no documented maximum number of tasks, but the maximum is constrained by screen space and usability
concerns. The beta build of Windows 7 didn't appear to enforce a maximum (I was able to create a jump list that
went off the edge of my 1920x1200 screen), while the RC build has a maximum of 16. You shouldn't rely on this number
to stay the same in the future, since Microsoft may change the internal workings of jump lists as the result of
design or usability decisions. If you add too many tasks, the jump list will show as many tasks as it can, and
the remainder will not be visible.
The sample project for this article is a simple clock whose jump list has tasks for running the app with various
color schemes:
The app also associates a command line switch with each task:
/r
: Red text
/y
: Yellow text
/g
: Green text
/z
: Random colors
If the app is run without switches, it will use your preferences for window color and text color:
But running it with one of the four switches will use a different color scheme:
Setting Up a Jump List
Using tasks requires creating a custom jump list, so the procedure is:
- Create an
DestinationList
COM object and get an ICustomDestinationList
interface.
- Call
SetAppID()
to tell the jump list your application's AppID.
- Call
BeginList()
to create a new, empty jump list.
- Create an
EnumerableObjectCollection
object.
- Create your tasks and add them to the object collection.
- Call
AddUserTasks()
to add the tasks to the jump list.
- Call
CommitList()
to save the jump list.
The demo app sets up its jump list in WinMain()
, so that work is done every time the program is
run. (This means that the customized jump list won't appear until you've run the program once.) Creating a jump
list is not a time-consuming operation, so this won't delay startup by an appreciable amount.
Creating a new jump list
In the sample project, all the jump list-related code is in SetUpJumpList()
. We start by creating
a new jump list:
bool SetUpJumpList()
{
CComPtr<ICustomDestinationList> pDestList;
pDestList.CoCreateInstance ( CLSID_DestinationList );
pDestList->SetAppID ( g_wszAppID );
UINT cMaxSlots;
CComPtr<IObjectArray> pRemovedItems;
pDestList->BeginList ( &cMaxSlots, IID_PPV_ARGS(&pRemovedItems) );
Next, we create an object collection to hold our tasks. We do this by creating a COM object with CLSID CLSID_EnumerableObjectCollection
,
and requesting an IObjectCollection
interface:
CComPtr<IObjectCollection> pObjColl;
pObjColl.CoCreateInstance ( CLSID_EnumerableObjectCollection );
We then call another function, AddJumpListTasks()
, to populate the collection. We'll see the code
for AddJumpListTasks()
later.
AddJumpListTasks ( pObjColl );
Now that the tasks are in the collection, we query the collection for an IObjectArray
interface,
pass that interface to AddUserTasks()
, and then save the jump list.
CComQIPtr<IObjectArray> pTasksArray = pObjColl;
pDestList->AddUserTasks ( pTasksArray );
pDestList->CommitList();
}
Creating tasks
The AddJumpListTasks()
function in the sample project creates four tasks, as shown in the screen
shot at the beginning of the article. Each task is created in the same way, by creating a ShellLink
object and querying for an IShellLink
interface, and then calling the appropriate IShellLink
methods. Each task has five properties, four of which are set via IShellLink
methods:
SetPath()
: The full path to the executable (required).
SetArguments()
: Command line arguments to pass to the executable (optional, but you will usually
set arguments).
SetIconLocation()
: An icon to show next to the task (optional).
SetDescription()
: A description of the task that appears as a tooltip (optional).
The last property, the text to show in the jump list item, is set by querying the ShellLink
object
for an IPropertyStore
interface, and setting the PKEY_Title
property.
Let's look at an example of how to create a task. This should be pretty familiar if you've ever dealt with shortcuts,
which also use IShellLink
. For simplicity, all values are hard-coded here in this snippet, but the
sample project's AddJumpListTasks()
queries for some common data (like the executable path) and calls
AddJumpListTask()
once for each task.
bool AddJumpListTask ( IObjectCollection* pObjColl )
{
CComPtr<IShellLink> pLink;
pLink.CoCreateInstance ( CLSID_ShellLink );
TCHAR szExePath[MAX_PATH];
GetModuleFileName ( NULL, szExePath, _countof(szExePath) );
pLink->SetPath ( szExePath );
pLink->SetArguments ( _T("/r") );
pLink->SetIconLocation ( szExePath, -IDI_RED );
pLink->SetDescription ( _T("Run the clock with red text") );
Next we get the IPropertyStore
interface and set the PKEY_Title
property:
CComQIPtr<IPropertyStore> pPropStore = pLink;
PROPVARIANT pv;
InitPropVariantFromString ( L"Red Text", &pv );
pPropStore->SetValue ( PKEY_Title, pv );
PropVariantClear ( &pv );
pPropStore->Commit();
Now that we've set up the link, we add it to the object collection that was created earlier in SetUpJumpList()
and passed to this function:
pObjColl->AddObject ( pLink );
}
Here's how the jump list looks with just the Red Text task:
Creating separators
One more thing you can put in the task list is a separator. You can see a separator between the Green Text
and Random Colors items here:
In the Windows 7 beta build, separators are very small, and don't take up a full-height slot in the jump list.
In RC builds, however, the line is wider and there is more vertical space around the separator:
A separator is basically a task whose ShellLink
has a property that marks it as being a separator.
All other aspects are the same as regular tasks, in that you create a ShellLink
object and add it
to an object collection. To create a separator, you only have to do one thing: Set the PKEY_AppUserModel_IsDestListSeparator
property of the ShellLink to TRUE
:
bool AddJumpListSeparator ( IObjectCollection* pObjColl )
{
CComPtr<IShellLink> pLink;
pLink.CoCreateInstance ( CLSID_ShellLink );
CComQIPtr<IPropertyStore> pPropStore = pLink;
InitPropVariantFromBoolean ( TRUE, &pv );
pPropStore->SetValue ( PKEY_AppUserModel_IsDestListSeparator, pv );
PropVariantClear ( &pv );
pPropStore->Commit();
pObjColl->AddObject ( pLink );
}
That's it! Note that Windows will check for separators at the beginning and end of the task list and remove
them. It will also collapse multiple consecutive separators into one.
There is one more thing you can do with jump lists: create a custom category that shows destinations that you
define. We'll take a look at how to do that next time.
Revision History
June 28, 2009: Article first published