Learn How to Use PowerShell to Configure Jump Lists

Learn How to Use PowerShell to Configure Jump Lists

  • Comments 8
  • Likes

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

Your comment has been posted.   Close
Thank you, your comment requires moderation so it may take a while to appear.   Close
Leave a Comment
  • Great post Chris! Thanks for sharing

  • Hi,

    very informative post.. I'm enrolled in www.wiziq.com/.../46-INTERVIEW-PREPARATION-COURSE-BY-SHIVPRASAD-KOIRALA for.net programming and so was looking around some material that may help me.. thanks

  • It would be handy to know if there is a way to do other things with Native APIs and specifically is there a way to use Native API calls to Manage IE Addons in IE 8 (if not it would seem that there should be).

    We have to periodically install software on a large number of PCs ( we are able to automate the install of the Software) which Hooks into IE8 as an addon. Well, in  a lot of cases those Addons cause problems(specifically slowness to the browser). Usually there is  no way from the Vendor to not install the Addon so we end up having to manually go into IE (as they user in some cases) and then Disable the particular Addon.

    If there was a way to make a Native Call (or other easy way from Powershell if I am making it too complicated ) to Manage IE Addons it would then be easy just pass the name of the Addon and a paramater saying Enable or Disable, that would make life much easier.

    Not sure if others have the need for this, but I would be the do and don't even realize it (what with all the addons that seem to get added by nearly every Browser based application these days).

    Thanks for  your time,

    Jeff

  • @Jeff

    This can all be controlled from Group Policy.  You can set Group Policy to load no add-ons or to load only the ones you specify.

  • @jrv Thanks for the information about Group Policy. I will look into that, but I would still prefer to have an API call to be able to control it post install and not have to go the GP route. If it is my only option, we may have to use it.

  • @jeff - you cannot call native APIs from script.  

    The design intention for manageingment of IE is to use Group POlicy to control how it runs.  This is more secure and more manageable.

    Some things should not be done with script.

    If you rally want to understand how to manage IE under as many circumstances as possible then look into the IEAK.  It will allow you to define a custom installtion of IE set the way you want it to be set at installation.

  • @jeff

    Sorry for the late update. Regarding your question, I agree with jrv that Group Policy is the way to go. I have yet been able to find managed programming interface for managing add-ons, in which case I would try not to accomplish the task using PowerShell script. If you have to script those settings on a standalone computer where Active Directory infrastructure isn’t available, consider putting desired configuration in a registry.pol file and then automating its application using Aaron Margosis’s ImportRegPol utility: blogs.technet.com/.../lgpo-utilities.aspx

  • Thank you Chris. Very interesting. The number of features in Windows not accessible via .NET has always irritated me. Your post is the first time I've seen the "Windows API Code Pack for Microsoft .NET Framework" mentioned. Two questions leap to mind. First, is the code pack compatible with Windows 8 ? And second, does the code pack provide access to the API for long paths ? Thanks again. Eddy.