Learn How to Use PowerShell to Configure Jump Lists

Summary: Microsoft PFE, Chris Wu, discusses using Windows PowerShell to configure Jump Lists.

Microsoft Scripting Guy, Ed Wilson, is here. Welcome to the weekend. Today we have a special treat in the form of a guest blogger. The Scripting Wife and I had the good fortune to meet Chris Wu in Montreal, Canada when we were there doing a Windows PowerShell workshop for Microsoft Premier Customers. Here is a bit about Chris…

Chris Wu started his career at Microsoft in 2002, first as a support engineer in the Microsoft Global Technical Support Center in China to support the various components of the base operating system. Now he works as a premier field engineer in Canada, and he specializes in platform and development support. During the course of troubleshooting, performance tuning, and debugging, he has created many utilities to ease and facilitate the process by leveraging various programming languages, like C, C++, and C#. And Windows PowerShell has become his new favorite.

Photo of Chris Wu

Take it away Chris…

One of the many features that I like about Windows 7 is the much improved taskbar. Specifically, I am a big fan of Jump Lists, which is a feature that enables users open favorite documents, pictures, websites, and utilities associated with an application. All this is accessible through a right-click on the application’s taskbar icon—even without the application being started first.

As serious Windows PowerShell users, just like me, you might have been tempted to pin the Windows PowerShell ISE to the taskbar. You would then end up with the disappointment of having nothing on its Jump List, as shown here.

Image of Jump List

Not only the application fails to create shortcuts to Windows PowerShell Console application or “Run as Administrator” mode, but also it won’t populate frequently used script files. (Remember that .ps1 documents are associated with Notepad instead of Windows PowerShell applications, unless another scripting environment is installed.) So it is time to change it—by using Windows PowerShell scripts, of course.

Windows 7 provides Windows Shell APIs that allow applications to alter Jump Lists (and to achieve many other Windows Shell features). Unfortunately, these APIs are written in native code without .NET implementation. Technically, it’s possible to wrap a needed API in C# code and embed it into a Windows PowerShell script, but this approach is not my intention (and it is probably out of my capability). Lucky for .NET programmers and Windows PowerShell scripters, Microsoft has already released Windows API Code Pack for Microsoft .NET Framework, which will make our lives much easier.

As far as a Jump List is concerned, only two precompiled DLLs from the Windows API Code Pack are needed. So download the current release, and then in the Binaries folder, extract Microsoft.WindowsAPICodePack.dll and Microsoft.WindowsAPICodePack.Shell.dll to a folder, for example, C:\Tools. And now it’s time to have fun!

Add-Type -Path “c:\tools\Microsoft.WindowsAPICodePack.dll”

Add-Type -Path “c:\tools\Microsoft.WindowsAPICodePack.Shell.dll”

$JumpList = [Microsoft.WindowsAPICodePack.Taskbar.JumpList]::CreateJumpList()

 

$Link = new-object Microsoft.WindowsAPICodePack.Taskbar.JumpListLink `

   -ArgumentList”powershell.exe”,”PS Console”

 

$JumpList.AddUserTasks($Link)

$JumpList.Refresh()

The following image shows the script and its associated output.

Image of command output

Well, this list might still lack some elements to be called appealing, but it’s indeed an achievement, considering that we made it with merely six lines of code. To get the most out of a Jump List, one needs to dig into the details of the taskbar classes and their members. And there are great resources available on Internet, including:

Within the features that are provided by the Shell APIs, these can be rather easily achieved as follows:

  • Associate an icon to a Jump List item
  • Use a separator
  • Create custom-named categories to organize items

And here is the code snippet:

Add-Type -Path “c:\tools\Microsoft.WindowsAPICodePack.dll”

Add-Type -Path “c:\tools\Microsoft.WindowsAPICodePack.Shell.dll”

$JumpList = [Microsoft.WindowsAPICodePack.Taskbar.JumpList]::CreateJumpList()

 

$Link = new-object Microsoft.WindowsAPICodePack.Taskbar.JumpListLink `

  -ArgumentList “powershell.exe”,”PS Console”

$Link.IconReference = new-object Microsoft.WindowsAPICodePack.Shell.IconReference `

  -ArgumentList “powershell.exe,0”

$Links = ,$Link

 

$Links += New-Object Microsoft.WindowsAPICodePack.Taskbar.JumpListSeparator

 

$Link = new-object Microsoft.WindowsAPICodePack.Taskbar.JumpListLink `

  -ArgumentList “C:\Tools”,”Tools”

$Link.IconReference = new-object Microsoft.WindowsAPICodePack.Shell.IconReference `

  -ArgumentList “shell32.dll,3”

$Links += $Link

 

$JumpList.AddUserTasks($Links)

 

$Category = new-object Microsoft.WindowsAPICodePack.Taskbar.JumpListCustomCategory -ArgumentList “Utilities”

$Link = new-object Microsoft.WindowsAPICodePack.Taskbar.JumpListLink -ArgumentList “notepad.exe”, “Notepad”

$Category.AddJumpListItems(@($Link))

$JumpList.AddCustomCategories($Category)

 

$JumpList.Refresh()

The script and its associated output are shown here:

Image of command output

You must have noticed redundancy in the code snippet. Indeed, this is a golden opportunity to show the power of pipeline processing in Windows PowerShell. While doing so, I also added support for command parameters, document icons, and custom categories. So here we have a function called Set-JumpList:

function Set-JumpList {

  Param (

    [string] $DllFolder = “”

  )#End Param

 

  Begin {

    “Microsoft.WindowsAPICodePack.dll”,”Microsoft.WindowsAPICodePack.Shell.dll” |

      foreach-object {

        if ($DllFolder) { Add-Type -Path “$DllFolder\$_” -ErrorAction Stop }

        else { Add-Type -Path (get-command $_ -TotalCount 1 -ErrorAction Stop).Path -ErrorAction Stop }

      }

 

    $JumpList = [Microsoft.WindowsAPICodePack.Taskbar.JumpList]::CreateJumpList()

   

    $JumpList.ClearAllUserTasks()

    $Category = $null

  }#End Begin

 

  Process {

    $Name = ([string]$_.Name).Trim()

    $Path = [Environment]::ExpandEnvironmentVariables(([string]$_.Path).Trim())

    $Icon = [Environment]::ExpandEnvironmentVariables(([string]$_.Icon).Trim())

    $Parameter = ([string]$_.Parameter).Trim()

 

    if($Path -and ($Name -notmatch “^%%”)) { # Try to resolve Path

      if (Test-Path $Path) { $Path = (Get-Item $Path).FullName }

      else { $Path = (Get-Command $Path -TotalCount 1 -ErrorAction SilentlyContinue).Path }

    }

   

    if (($Name -notmatch “^%%”) -and !$Icon -and $Path) { # Try to locate the Icon reference from registry

      try {

        if ((Get-Item $Path).PSIsContainer) { $Icon = (Get-ItemProperty “Registry::HKEY_CLASSES_ROOT\Folder\DefaultIcon”).”(default)” }

        else { $Icon = (Get-ItemProperty (“Registry::HKEY_CLASSES_ROOT\” + (Get-ItemProperty (“Registry::HKEY_CLASSES_ROOT\”+(Get-Item $Path).Extension)).”(default)” + “\DefaultIcon”)).”(default)” }

 

        if ($Icon -match “^%1”) { $Icon = “$Path,0” }

       

        $Icon = $Icon.Replace(‘”‘,”)

        if ($Icon -notmatch “,”) { $Icon += “,0” }

      } catch {}

    }#End if

 

    if ($Name -eq “%%”) { # Separator

      if ( $Category ) { # Cannot have separator inside a custom category

        $JumpList.AddCustomCategories($Category)

        $Category = $null

      }

      else { $JumpList.AddUserTasks((New-Object Microsoft.WindowsAPICodePack.Taskbar.JumpListSeparator)) } # Add a separator

    }

    elseif ($Name -match “^%%”) { # New Category

      if ( $Category ) { $JumpList.AddCustomCategories($Category) } # Inside a custom category already, registry previous first

      $Category = new-object Microsoft.WindowsAPICodePack.Taskbar.JumpListCustomCategory -ArgumentList $Name.Substring(2)

    }

    elseif ( $Path -and $Name ) { # Add an item

      $Link = new-object Microsoft.WindowsAPICodePack.Taskbar.JumpListLink -ArgumentList $Path, $Name

      if ( $Icon ) { $Link.IconReference = new-object Microsoft.WindowsAPICodePack.Shell.IconReference -ArgumentList $Icon }

      if ( $Parameter ) { $Link.Arguments = $Parameter }

     

      if ( $Category ) { $Category.AddJumpListItems(@($Link)) }

      else { $JumpList.AddUserTasks($Link) }

    }#End if

  }#End Process

 

  End {

    if ($Category ) { $JumpList.AddCustomCategories($Category) }

    $JumpList.Refresh()

  }#End End

}#End function Set-JumpList

This function expects objects from the pipeline that have properties like Name, Path, Icon, and Parameter. These arguments are used to create items in the task list. Special name “%%” is reserved to create a separator in the “Tasks” section, and “%%categoryname” type of expressions can be used to create a custom-named category in the Jump List, and the items that follow will be added to this category.

Personally, I would use the ConvertFrom-Csv cmdlet to create custom objects and pipe them to the function. I’m using “|” as delimiter because the icon definition retrieved from registry sometimes contains a comma.

@”

Notepad|notepad

Calculator|calc

%%

PS Console|powershell

Command Prompt|cmd

%%Files

Config Script|C:\scripts\Config.ps1

ToDo|notepad|C:\scripts\todo.txt

%%Folders

Tools|c:\tools

“@ | ConvertFrom-Csv -Header Name,Path,Parameter,Icon -Delimiter “|” | Set-JumpList -Dll c:\tools

The -DllFolder parameter in the previous code snippet can be omitted if the two DLLs are located in one of the directories listed in the $env:Path environment variable.

So here we have a very well customized Jump List. And did I mention that after it is created, the Jump List is persistent, regardless of the running status of the application itself? Make sure that you pin the Windows PowerShell ISE to the taskbar. Then you can always find this list by right-clicking the application icon from the taskbar—even without ISE running. You can see this in the following image.

Image of command output

Cheers!

~Chris

Thank you, Chris, for sharing a cool script and technique. The entire script can be found on the Scripting Guys Script Repository

Join us tomorrow for a special report about the Scripting Games by Bartek Bielawski.

I invite you to follow me on Twitter and Facebook. If you have any questions, send email to me at scripter@microsoft.com, or post your questions on the Official Scripting Guys Forum. See you tomorrow. Until then, peace.

Ed Wilson, Microsoft Scripting Guy