Microsoft & .NETCreating Windows 7 Jump Lists With The API Code Pack and Visual...

Creating Windows 7 Jump Lists With The API Code Pack and Visual Studio 2008

Developer.com content and product recommendations are editorially independent. We may make money when you click on links to our partners. Learn More.

Introduction


Now that Windows 7 is readily available at every computer store, it’s only a matter of time until the new operating system becomes mainstream in both homes and corporations. Especially this will happen once new computers start replacing old ones.


From a developer perspective, Windows 7 brings many possibilities. One of the most visible and prominent new features is the improved taskbar, through which applications are launched and even managed. In this article, the focus is on a feature called jump lists, which can be thought of as being a mini Start menu when right-clicking an icon on the taskbar (Figure 1).

A basic Windows 7 jump list

Jump lists are active both when right-clicking an icon on the taskbar, but also when pointing at an application icon in the Start menu’s initial view (Figure 2). Thus, there are two ways to make jump lists visible; however these menus have a different focus: jump lists on the Start menu are about the recent documents, whereas the taskbar-activated jump list contains more options to manage the application and its windows.

Figure 2. Jump lists in the Start menu.

Programming both instances of jump lists requires only a single implementation. Under the hood, jump lists use a COM based API, just like many other features in the Windows shell. Although it is possible to access these COM interfaces directly from your application, there is an easier way. Microsoft has developed a free package for .NET developers called the Windows API Code Pack (Figure 3), which contains many ready-made classes to access Windows features, such as Windows 7’s jump lists.

The Windows API Code Pack download page.

Next, you are going to see how you can use Visual Studio 2008 and C# to enable custom jump lists in your own .NET applications with a WinForms sample application (Figure 4). A similar method could be used with Visual Studio 2010 Beta 2. With Visual Studio 2010 however, you can also use WPF 4.0’s new support for Windows 7 shell integration. Even so, these classes are out the scope of this article, but the topic will likely be addressed in the near future.

The sample application

Briefly About Jump List Features


Jump lists on Windows 7 can be considered streamlined mini versions of the Windows Start menu. Furthermore, they are tailored for each application. Just like in all Windows versions after Windows 95, you can right-click an icon on the taskbar, and see a popup menu. Jump lists in Windows 7 are an evolution of this menu.


Indeed, even if you don’t do anything special, Windows 7 will display a popup menu for your application when its icon is available on the taskbar (see again Figure 1). If you have pinned an application to the taskbar, the default menu will give you the option to unpin it or launch the application. If the application is running, the default menu contains options to pin the program to the taskbar, close it, or launch another instance.


If an application has associated a file type (file extension) with itself in the registry–for instance, Word with .doc and .docx files–Windows will collect a list of recent files opened with the application. This happens automatically if the application uses Windows common file dialogs to open files, or uses the SHAddToRecentDocs API function to let Windows know that files were opened. Windows will also keep track of which files are used most frequently.


Files opened with an application from two so-called known lists. These are the Recent and Frequent lists. The difference between these two is that Recent will collect the most recently opened files (say, five of them), and the Frequent list will on the other hand collect files that are most commonly used over time. The application can control whether one of these lists is shown in the jump list, or neither.


These known lists are said to contain destinations, or nouns. Destinations are always files, and because of this, a file handler for a given file type (or types) must be registered. Windows will also make sure the files are available before showing them on the list.


Another important part of jump lists, and the focus of this article, is the support for verbs. Verbs allow your application to freely add custom commands to the jump list, and then allow users to quickly interact with your application while it’s running. Unlike nouns, verbs do not require you to associate a file extension with your application.


Verbs are file based commands that point back to your application, and instruct it to run certain commands. For example, if you were writing a sales application, then the jump list verbs could start a new order, find a customer, and so on. Since these lists are customized for each application, every application has its own set of verbs.


Using the Windows API Code Pack


The freely downloadable Windows API Code Pack (see the Links section at the end for the URL) is a set of .NET classes compiled into several different assemblies. You can easily reference these from your own projects, and thus benefit from the classes.


To get started with the code pack, you first need to download it. It comes in a regular zip file, which you should extract to a convenient location, such as under your Visual Studio projects folder. Then, navigate inside the newly extracted folders, and open a solution named Shell.sln with Visual Studio. Build the whole thing, and then note the location of the resulting DLL files, usually under the binDebug folder.


After a successful build, you should see two assemblies, Microsoft.WindowsAPICodePack.Shell.dll and Microsoft.WindowsAPICodePack.dll in the output folder. The latter file is automatically created, as the Shell project depends on a project called Core. The Core project is also part of the code pack.


The next step is to start a new project (or open one, if you already have one) in Visual Studio, and add the two code pack DLLs as references to your project (Figure 5). With the proper references in place, you can start writing code. The assembly filenames already give a hint of the correct namespaces; to use the code pack for jump lists, add the following using statements to your C# code:


  using Microsoft.WindowsAPICodePack.Shell;
using Microsoft.WindowsAPICodePack.Taskbar;

References have been to the sample project

Inside the latter namespace, you can find a class called JumpList, which is a great starting point to begin exploring the features in jump lists. For instance, let’s see how you could add a custom jump list verb to your application’s menu.


The first step is to create an instance of the JumpList class. After this, you need to construct an object supporting the IJumpListTask interface for each custom verb you wish to add to your jump list. This can be done with the help from the JumpListLink class, also part of the Microsoft.WindowsAPICodePack.Taskbar namespace.


For instance, you might want to add commands into your application’s jump list to quickly launch Notepad or Calculator. To do this, you could use the following code:


  JumpList list = JumpList.CreateJumpList();

string systemFolder = Environment.GetFolderPath(
Environment.SpecialFolder.System);

string notepadPath = System.IO.Path.Combine(
systemFolder, “notepad.exe”);
JumpListLink notepadTask = new JumpListLink(
notepadPath, “Open Notepad”);
notepadTask.IconReference = new IconReference(
notepadPath, 0);

string calculatorPath = System.IO.Path.Combine(
systemFolder, “calc.exe”);
JumpListLink calculatorTask = new JumpListLink(
calculatorPath, “Open Calculator”);
calculatorTask.IconReference = new IconReference(
calculatorPath, 0);

list.AddUserTasks(notepadTask, calculatorTask);
list.Refresh();
MessageBox.Show(“Custom tasks have been added!”);



Here you are creating a new jump list object, and then constructing two verbs with the JumpListLink class. The constructor requires a file-based command, and in this case this is the full pathname to Notepad.exe (and Calc.exe) in the WindowsSystem32 directory. The System.IO.Path class helps in the process. Also, menu icons are added using the IconReference class, which takes in a full path and an icon index. Zero means the first icon in the file, which is commonly the main icon.


Finally, the AddUserTasks method of the JumpList class is called to add the new verbs to the menu. Windows 7 will create the commands under the Tasks generic group, and thus the verbs added here can also be called tasks. Note that Windows will keep a record of the tasks you create. Thus, you will only need to create your tasks once.

Pointing back to your own application


Although starting external applications from jump lists
is easy, it doesn’t help much if you want to use jump lists
to command your own application. Currently, a jump list
command must point to a file on disk, i.e. a document file
with a registered handler application, or an executable with
optional command-line parameters.


Technically, this means that to be able to send commands
back to the currently running application instance, you
would have to implement some kind of inter-process
communication (IPC) between the application instance that
shows the jump list (the “server”) and the one that Windows
will launch when a command is selected from the jump list
(the “client”).


For instance, let’s say you would need to implement two
custom commands in your application. For simplicity, these
commands could be named Command A and Command B. To create
jump list tasks for these two, you would need to construct
the full path to your executable, and then append command-
line parameters telling the application which command was
actually selected. For instance, the path could be:
C:MyAppsWin7JumpListDemo.exe


With this path, Windows would start your executable, and
pass in the parameters you specify. The application would
then check the existence of command-line parameters, and if
correct ones were given, it would communicate them with
already running instance and exit immediately.


First, let’s see how you could create jump list tasks
that point back to your own application. The code looks
similar to what you’ve already seen:



JumpList list = JumpList.CreateJumpList();

string selfPath = System.Environment.CommandLine;
// the value comes with quotation marks, strip them out
selfPath = selfPath.Substring(1, selfPath.Length – 3);

JumpListLink selfCommandATask = new JumpListLink(
selfPath, “Command A”);
selfCommandATask.Arguments = “Command-A”;
selfCommandATask.IconReference = new IconReference(
selfPath, 0);

JumpListLink selfCommandBTask = new JumpListLink(
selfPath, “Command B”);
selfCommandBTask.Arguments = “Command-B”;
selfCommandBTask.IconReference = new IconReference(
selfPath, 0);

JumpListSeparator separator = new JumpListSeparator();
list.AddUserTasks(notepadTask, calculatorTask,
separator, selfCommandATask, selfCommandBTask);



With these tasks added, the jump list would look like the
one in Figure 6. The path to the application instance is
read from the System.Environment.CommandLine
property which contains quotation marks around the full
path. These must be removed for the jump list tasks to work
correctly; otherwise Windows will report an error when
trying to launch the application.


The custom tasks have been added to the application's jump list
Click here for larger image

Figure 6. The custom tasks have been added to the application’s jump list.

The other important part is to specify the command-line
parameters using the Arguments property. Note
that at the time of this writing, the Windows API Code Pack
documentation file doesn’t list the Arguments property at
all. Nonetheless, it is available for usage from code.


Now, if the user would select “Command A” from the
application’s jump list, Windows would execute the following
command (with the real runtime path of course):



C:MyAppsWin7JumpListDemo.exe Command-A


When the application starts, some code would be needed in
the Program.cs file to check for the
parameters. In the sample application, the main method of
the application looks like this:




static void Main(string[] args)
{
if (CommandLineParameters.ParametersGiven(args))
{
CommandLineParameters.ProcessCommandLine(args);
}
else
{
Application.EnableVisualStyles();
Application.SetCompatibleTextRenderingDefault(false);
Application.ApplicationExit +=
(obj, evt) =>
{
PipeCommunications.SignalCloseEvent();
};
Application.Run(new MainForm());
}
}


A custom class called CommandLineParameters
is used to check if there are command-line parameters, and
if yes, then the same class can process them. Otherwise, the
application starts normally and shows the user
interface.


Communicating with Pipes


Sending messages between two running applications on the
same machine is a common requirement. Windows itself calls
this inter-process communication, or IPC, and provides
multiple ways to implement this. For instance, you could be
using memory-mapped files, TCP/IP sockets, or named kernel
objects. One option is to use so-called named pipes, which
provide a convenient and straightforward way to send
messages without tweaking with security or firewall
settings. Pipe programming in .NET was also made easier with
.NET 3.5’s new System.IO.Pipes namespace.


In the sample application, pipes are used for
communication as follows. When the sample application is
launched without any parameters, it launches the user
interface normally and starts a thread that listens to pipe
connections.


On the other hand, when the user chooses a command from
the jump list, the application is started with command-line
parameters. In this case, the application opens a pipe
connection to the existing server application pipe, and
sends the command-line parameter to the already running
instance. Then, the server is free to do whatever is
applicable for the command.


The pipe handling is implemented in the custom
PipeCommunications class. Although the focus of
this article is in jump lists and not in pipe
communications, the code is worth walking through briefly,
as IPC is a common requirement when working with jump lists.
When the sample application starts without parameters, the
main window’s constructor runs the following code:




PipeCommunications pipes = new PipeCommunications(
(cmd) =>
{
communicationLogTextBox.Invoke(
new LogMessageToTextBoxDelegate(
LogMessageToTextBox),cmd);
});
pipes.CreateListenPipeThread();


Here, the PipeCommunications class is initialized with a
delegate that does the real processing of the command
received through the pipe. Note that since the pipe
communication is done in a separate thread, the WinForms
control’s Invoke method must be called to
properly access user interface elements without threading
issues. A lambda expression is used to construct the event
handler. The real work is done in the
LogMessageToTextBox method of the main
form:




public delegate void LogMessageToTextBoxDelegate(string cmd);

private void LogMessageToTextBox(string cmd)
{
communicationLogTextBox.Text =
DateTime.Now.ToLongTimeString() +
“: Got command: ” + cmd + “rn” +
communicationLogTextBox.Text;
}


Although this implementation simply logs the received
command on the screen (Figure 7), your own application could
easily do some real work here, such as accessing a database,
printing a document, or opening a dialog box on the screen.


The server application has received commands through the pipe
Click here for larger image

Figure 7. The server application has received commands through the pipe.

The CreateListenPipeThread method called by the main
form’s constructor executes the following code:




using System.IO.Pipes;
using System.Threading;

Action<string> processCommandAction;
private static AutoResetEvent closeApplicationEvent;

internal Thread CreateListenPipeThread()
{
closeApplicationEvent = new AutoResetEvent(false);
Thread serverThread = new Thread(
new ParameterizedThreadStart(
PipeHandlerServerThread));
serverThread.Start(serverThread);
return serverThread;
}


The code creates a new thread which handles the server
end of the pipe communications, and also creates a single
auto-reset event. This event is signaled (set) from the
Program.cs file when the application is about to exit. If no
such event would be used, the main form would close
normally, but the pipe thread would continue running. Thus,
the thread must be properly ended to completely shut down
the application.


The real server-side work of the pipe communication is
done in the following two methods:




private const string PipeName = “win7-jumplist-demopipe”;

internal void PipeHandlerServerThread(object threadObj)
{
Thread currentThread = (Thread)threadObj;
NamedPipeServerStream pipeServer = null;
try
{
while (true)
{
pipeServer = new NamedPipeServerStream(
PipeName, PipeDirection.InOut, 1,
PipeTransmissionMode.Byte,
PipeOptions.Asynchronous);
try
{
IAsyncResult async =
pipeServer.BeginWaitForConnection(
null, null);
int index = WaitHandle.WaitAny(
new WaitHandle[]
{
async.AsyncWaitHandle,
closeApplicationEvent
});
switch (index)
{
case 0:
pipeServer.EndWaitForConnection(async);
ProcessPipeCommand(pipeServer);
break;
case 1:
// exit this thread
return;
default:
pipeServer.Close();
break;
}
}
catch (Exception ex)
{
processCommandAction(“Exception: ” +
ex.Message);
}
}
}
finally
{
pipeServer.Close();
}
}

private void ProcessPipeCommand(
NamedPipeServerStream pipeServer)
{
string command;
using (StreamReader reader =
new StreamReader(pipeServer))
using (StreamWriter writer =
new StreamWriter(pipeServer))
{
command = reader.ReadLine();
processCommandAction(command);
writer.WriteLine(“OK”);
}
if (pipeServer.IsConnected)
{
pipeServer.Disconnect();
}
}



The code creates an instance of the
NamedPipeServerStream class, and starts to wait
for a connection asynchronously. Waiting is done at the same
time for both the pipe connection and the application close
event. Depending on which comes first, the pipe thread
simply exits, or starts to process the pipe command received
(in the ProcessPipeCommand method). Waiting is done using
the WaitHandle class, part of the
System.Threading namespace.

Next, let’s see how the client application behaves. When
the application is started with command-line parameters
(i.e. through the jump list tasks), the following code
runs:




internal static void ProcessCommandLine(string[] args)
{
string command = args[0];
PipeCommunications pipes = new PipeCommunications();
string result = pipes.SendCommandToServer(command);
}


Here, an instance of the same
PipeCommunications class is created, but since
this is the client side implementation of the pipe (data
flows from the application to the already running
application instance), no delegate needs to be given to the
PipeCommunications class.


Sending commands through the pipe is implemented in the
SendCommandToServer method:




internal string SendCommandToServer(string command)
{
try
{
NamedPipeClientStream pipeClient =
new NamedPipeClientStream(
“localhost”, PipeName, PipeDirection.InOut);
try
{
using (StreamReader reader =
new StreamReader(pipeClient))
using (StreamWriter writer =
new StreamWriter(pipeClient))
{
pipeClient.Connect();
writer.WriteLine(command);
writer.Flush();
string response = reader.ReadLine();
return response;
}
}
finally
{
pipeClient.Close();
}
}
catch (Exception ex)
{
System.Windows.Forms.MessageBox.Show(
“Windows 7 Task Bar Demo: Pipe ” +
“communication failure: ” + ex.Message);
return null;
}
}


The command-line parameter (“Command-A” or “Command-B” in
the sample application’s case) is simply sent to the pipe,
and then a response of “OK” from the server acknowledges
that all is well. After this, the client can simply
exit.


Conclusion


In this article, you saw how you can use Visual Studio
2008 and the newly-released Windows API Code Pack to get the
best out of Windows 7’s new features. The sample application
accompanying this article demonstrated how you can use C# to
implement jump list functionality in your own applications,
and along the way let users of Windows 7 benefit from the
operating system’s new features.


The utility classes in the Windows API Code Pack allow
you to easily manipulate the task bar and control your own
application’s menus. However, to write commands that point
back to your own application requires some extra effort, as
you need to implement a communication channel between the
currently running application instance and the one that
Windows 7 starts in response to a jump list selection.


In the sample application, named pipes were used to
handle the communications. This is by no means the only
option available, but with .NET 3.5’s new classes, using
pipes is quite convenient. And once you write such code, you
can easily use it in many different applications.


Jump lists in Windows 7 provide a quick way to let users
access the main features of your application. Although
Windows 7 is new, many applications, like Internet Explorer
and Messenger already take full use of these new operating
system features. So should you.



Happy shell development!

Jani Järvinen


About the Author


Jani Järvinen is a software development trainer and
consultant in Finland. He is a Microsoft C# MVP and a
frequent author and has published three books about software
development. He is the group leader of a Finnish software
development expert group at ITpro.fi and a board member of
the Finnish Visual Studio Team System User Group. His blog
can be found at http://www
.saunalahti.fi/janij/
. You can send him mail by clicking
on his name at the top of the article.


Resources


Windows API Code Pack for Microsoft .NET Framework

Microsoft Windows SDK for
Windows 7


Develop for Windows 7

Get the Free Newsletter!

Subscribe to Developer Insider for top news, trends & analysis

Latest Posts

Related Stories