Windows 7 has added many neat enhancements to Explorer, and specifically to the Taskbar. One prominent new feature is the jump list. This is a context-menu-like UI element that is associated with an application, and is accessed by right-clicking the application's Taskbar button. This article introduces jump lists, shows how to access the jump list from C++, and demonstrates some simple customizations you can do to your app's jump list.
The sample code is built with Visual Studio 2008, WTL 8.0, and the beta Windows 7 SDK. As with my earlier Vista Goodies articles, I've classified this article as Intermediate because I won't be covering the basics of Win32 and WTL. See my series of articles on WTL if you need to get up to speed on WTL. I also won't be going step-by-step through the Visual Studio wizards, which were also covered in the WTL series. (Those articles show the wizards in VC 2003, but the wizards in 2008 are similar.)
Some important points to note right off the bat:
- "Jump list" is a relatively recent term. During development, Microsoft used the term "destination list," and that is the term that appears in various places in header files (for example the
- In order for an app's jump list to work correctly, it must be a registered handler for a file type. That doesn't necessarily mean it has to be the default handler, just a handler. We'll see how to register as file type handlers later.
- Registering as a file type can be done in
HKEY_CLASSES_ROOT (for machine-wide associations), or in
HKEY_CURRENT_USER\Software\Classes (for per-user associations). For the sake of brevity, this article will refer to
HKEY_CLASSES_ROOT, but downloadable the sample code uses
HKEY_CURRENT_USER. This is done because writing to
HKEY_CLASSES_ROOT requires elevation, and I wanted the sample programs to run without needing elevation.
What are Jump Lists?
As mentioned above, you bring up an app's jump list by right-clicking its Taskbar button. If the app is present in the Start menu's list of apps - either by being pinned there, or because it's been used recently - the jump list is also accessible through the Start menu item.
The jump list has two categories of items: destinations and tasks. Destinations are items (usually files) that the application can operate on. This is similar to MRU file lists that some apps maintain on their own. However, with destinations, the app and Windows can work together to manage one central list of files. Also, an app doesn't have to do anything in order to get destinations; Windows will try to figure out on its own which files the app is operating on and use that information to build a jump list. The app can also add files to this list if it wants to.
Tasks are commands, such as "create a new document" or "play an album of MP3 files." A task is represented by an instance of
IShellLink, so a task must be an operation that can be invoked via the command line. When writing an app, you will plan out in advance what your command line arguments will be, and which operations you want to add to your app's jump list. Because tasks are inherently application-specific, Windows does not provide any default tasks. The bottom of the jump list contains a few commands for managing windows and pinning/unpinning the app to the Taskbar, but those are not considered tasks. Those are simply window-management controls.
Default Jump Lists for Older Applications
Let's start out by seeing what Windows 7 does for older apps that have no knowledge of jump lists. The first sample program is a bare-bones file viewer that's associated with the
.aly extension. The app, NaiveAlyViewer, doesn't actually do anything with the files, it just demonstrates the various ways that Windows determines what files should appear in the app's jump list.
Ever since Windows 95, Windows has had an algorithm for filling in the Recent Documents list in the Start menu. This is based around the
SHAddToRecentDocs() API. There are three situations where
SHAddToRecentDocs() is called:
- The app uses the common file open dialog (
GetOpenFileName() or IFileOpenDialog). When the user selects a file with these dialogs, the dialog calls
- The user double-clicks a file in Explorer, Explorer runs the app associated with the file's extension, and calls
SHAddToRecentDocs() for you.
- An app calls
SHAddToRecentDocs() itself. This is usually done when the app accesses a file in a way that Explorer does not automatically detect.
For a naïve app like this one, Windows 7 uses the same algorithm to determine which files the app is operating on. The difference with jump lists is that Recent Documents shows all the recently-used files in one list, while a jump list only shows the files associated with one particular app.
To see this viewer in action, first click Register as Handler to associate the app with the
.aly extension. You must do this before any jump list features will work. The app's jump list is initially empty:
To add files to the jump list, open any file with the
.aly extension, and the full path to the file will appear in the dialog. As part of the registration process, the app creates a few
.aly files in your My Documents directory for you to test with. You can also create your own test files if you want - just create a file with the extension
.aly (the file's contents are unimportant). After you open a file, right-click the Taskbar button, and that file will appear in the Recent category in the jump list:
You can also try other ways of opening files, such as double-clicking a file in Explorer, or dragging a file into the NaiveAlyViewer window. Explorer automatically provides a tooltip and a context menu for jump list items, as well as the ability to pin, unpin, and delete them. When you click a file in the jump list, Explorer uses the file association information to build a command line. Normally, this will run the app and pass the full path to the file on the command line.
Using Basic Jump List Features
Picking and using an AppID
Now that we've seen how jump lists work for older programs, let's start adding some knowledge of jump lists to our code. The sample app for this section is also a viewer for
.aly files, but it can change its jump list in some basic ways.
One important concept that's used in jump lists is the application user model ID. This is abbreviated "AppID," but it is not related to the AppIDs that are used in COM. An AppID is how the new Taskbar identifies processes and windows for the purposes of organizing Taskbar buttons.
If you don't assign your app an AppID - as was the case with the naïve viewer - then Windows will create an AppID for you. This has the disadvantage that separate copies of the viewer will be treated separately by the Taskbar. For instance, if you build a debug and release version of the viewer, the two executables will not share a Taskbar button and will have separate jump lists. Assigning an AppID fixes that problem.
Our first step is to pick an AppID. MSDN recommends using a string in the form "Company.Product.SubProduct.Version". This is similar to how ProgIDs are made, and in fact we will need to have a ProgID as well, so let's pick an AppID and a ProgID now.
We won't be using multiple versions of our app, so we can leave off the version field. Now that we have these IDs, we need to tell Windows three things:
- The AppID of our application
- The ProgID of the
.aly file association
- How to find the AppID from the ProgID
The first part can be done in a few ways, but the simplest is to call
SetCurrentProcessExplicitAppUserModelID() like this:
LPCWSTR wszAppID = L"MDunn.CodeProjectSamples.SimpleAlyViewer";
HRESULT hr = SetCurrentProcessExplicitAppUserModelID ( wszAppID );
This must be done in the app's initialization code, before it creates any windows or manipulates any jump lists.
The next step is to add our app's ProgID to the
.aly file association info. We do this by adding a key called
HKEY_CLASSES_ROOT\.aly and setting a string value whose name is our ProgID. We then create a key for our ProgID under
HKEY_CLASSES_ROOT, just as in COM. Since our viewer is also the default handler for
.aly files, the information in the ProgID key determines what happens if you double-click an
.aly file (as in the previous example), as well as what should appear in the jump list. Things get more interesting when your app is not the default handler for a file type; we will see an example of this situation in the next section.
The last piece of info in the ProgID key is the
AppUserModelID value. This tells Explorer the AppID of the app that handles
.aly files. Once Explorer knows the AppID of the handler, it can properly manage Taskbar buttons and update its internal data structures related to jump lists.
The other important place where we specify our AppID is in calls to
SHAddToRecentDocs(). Instead of just passing a file path, we use some new parameters that were added to
SHAddToRecentDocs() for Windows 7. We fill in a
SHARDAPPIDINFO struct that holds our AppID and an
IShellItem interface on the file. Creating the
IShellItem is easy: the new
SHCreateItemFromParsingName() function takes a file path and returns an
LPCWSTR szFilePath = ;
hr = SHCreateItemFromParsingName ( szFilePath, NULL,
if ( SUCCEEDED(hr) )
info.psi = pItem;
info.pszAppID = g_wszAppID;
SHAddToRecentDocs ( SHARD_APPIDINFO, &info );
Clearing the list of files
Now that we have our AppID set up, we can do useful things to our jump list. The easiest thing to do is clear the list of files, leaving only the window management commands. We use the
IApplicationDestinations interface to operate on the built-in features of the jump list.
hr = pDests.CoCreateInstance ( CLSID_ApplicationDestinations,
NULL, CLSCTX_INPROC_SERVER );
if ( SUCCEEDED(hr) )
hr = pDests->SetAppID ( g_wszAppID );
if ( SUCCEEDED(hr) )
The first method we call is
SetAppID() to indicate which jump list we want to operate on. This must be called before any other methods. Then we call
RemoveAllDestinations() to clear the list of recently-used files. That's it!
Switching between Recent and Frequent categories
Another modification we can do is change the jump list to show frequently-used files. There are two known categories in jump lists, Recent and Frequent. By default, a jump list shows the Recent category, but we can change that by creating a new jump list.
All jump list modifications follow this general pattern:
- Create an
SetAppID() just as we did when using
BeginList(), which creates a new jump list that we can modify.
- Make modifications to the jump list.
CommitList() to save the jump list.
Note that jump lists are never modified in-place; you always create an entirely new jump list and then tell Explorer to use the new list by calling
CommitList(). This isn't a big deal for us now, since we're only using built-in jump list features, but it's something to keep in mind when adding your own custom items to jump lists. If your app keeps any state that affects the contents of the jump list, that state must be stored in a persistent location so it can be read when building a new jump list.
The SimpleAlyViewer dialog has two buttons that change the category that's shown in the jump list. Both button handlers call the helper function
ShowCategory() and pass in the category ID:
bool CMainDlg::ShowCategory ( KNOWNDESTCATEGORY category )
hr = pDestList.CoCreateInstance ( CLSID_DestinationList,
NULL, CLSCTX_INPROC_SERVER );
if ( FAILED(hr) )
hr = pDestList->SetAppID ( g_wszAppID );
if ( FAILED(hr) )
hr = pDestList->BeginList ( &cMaxSlots, IID_PPV_ARGS(&pRemovedItems) );
if ( FAILED(hr) )
hr = pDestList->AppendKnownCategory ( category );
if ( FAILED(hr) )
return SUCCEEDED( pDestList->CommitList() );
As before, we create the necessary COM object, query for
ICustomDestinationList, and call
SetAppID(). We call
BeginList() to create a new jump list.
BeginList() has output parameters that indicate the maximum size of the jump list, and an array of custom items that the user has removed from the list. We don't need to worry about these parameters, since we're not adding any custom items to the list. We then call
AppendKnownCategory() to add the appropriate category to the list, and save it with
Using Jump Lists with a Non-default Viewer
Our final example will be a bit different, a graphics viewer:
The difference this time is that the app doesn't have its own file type; instead, it registers as a handler for existing file types. (The commands for registering and unregistering are in the Jump List menu.) Here are this app's AppID and ProgID:
LPCWSTR g_wszAppID = L"MDunn.CodeProjectSamples.JumpListGraphicsViewer";
LPCWSTR g_wszProgID = L"MDunn.CodeProjectSamples.JumpListGraphicsViewerProgID";
Our viewer will handle BMP, JPG, PNG, GIF, and TIFF files by registering under the
OpenWithProgIDs key for each extension. For example, in the
HKCR\.jpg\OpenWithProgIDs key, we create a string value whose name is our ProgID.
Under our own ProgID key, we add two values,
FriendlyTypeName is used as the file type description when you bring up the properties dialog for a file in the jump list. The string in
FriendlyTypeName overrides the description that you see in Explorer, as shown below. The first screen shot is the property page as invoked from Explorer, while the second is the property page as invoked from a jump list item. Notice that the Type of file string is different. (The icon is also different; that is controlled by the
DefaultIcon key described below.)
AppUserModelID works as described in the previous sample app. There are also some other registry entries that round out our file type association:
DefaultIcon key specifies the icon to use in our app's jump list.
CurVer key that holds the current version of our ProgID. As before, we aren't using versioning, so we leave off the version field in the ProgID.
shell\open\command key that holds the command line to use when the user selects a file from our jump list.
With these registry entries in place, our viewer's jump list works as you'd expect, and it also appears in the
Open With submenu of all the file types that we register under.
As an additional little detail, the viewer demonstrates how to tell that it is being run from an
Open With menu or a jump list item. The command line written to the
command key is
JumpListGfxViewer.exe /v "%1" (
/v for "viewer mode"). When the app sees the
/v switch and a file path, it adds a "viewer mode" string to the window.
Jump List Issues
Note: The information in this section is based on the beta release of Windows 7 (build 7000). The behavior may be different in later builds.
- Pinning an app sometimes causes a second Taskbar button to be added. During development of the sample apps, I've seen this happen with one of the apps but not the others. The samples in the Windows 7 beta SDK show this problem as well.
- If you add a known category to a jump list multiple times, the jump list will show some white space at the bottom.
May 19, 2009: Article first published