How to pin .lnk files to taskbar in PowerShell?

5,081

Solution 1

Minimum system requirement

I don't know, I am using PowerShell 7.2 preview 2 x64 on Windows 10 20H2, I have tested the script and confirmed it is working, and found a minor bug and fixed it; But I have tested my code on PowerShell 5.1.19041.610 and confirmed my code won't work on it, so maybe you need at least PowerShell Core 6? I don't really know, but it is advised to use the latest version of PowerShell.


I solved it via:

  1. Save as pintotaskbar.ps1:
    param(
      [parameter(mandatory=$true)] [validatenotnullorempty()]$execs
    )
    
    if (!(test-path $home\pinnedshortcuts)) {new-item $home\pinnedshortcuts -type "directory" > $null}
    $execs  = $execs -split "\|"
    
    foreach ($exec in $execs) {
      $path = ($exec -split "::")[0]
      $name = ($exec -split "::")[1]
    
      if ($path -notmatch "[C-Zc-z]:(\\[^(<>:`"\/\\|?*\x00-\x1f\x7f)]+)+") {$path=where.exe $path}
    
      $shortcutpath         = "$home\desktop\$name.lnk"
      $wshshell             = new-object -comobject wscript.shell
      $shortcut             = $wshshell.createshortcut($shortcutpath)
      $shortcut.targetpath  = $path
      $shortcut.save()
    
      $bytes                = [system.io.file]::readallbytes($shortcutpath)
      $bytes[0x15]          = $bytes[0x15] -bor 0x20
      [system.io.file]::writeallbytes($shortcutpath,$bytes)
    
      copy-item -path $shortcutpath -destination $home\pinnedshortcuts
    }
    
    $template         = get-content "$psscriptroot\template.xml"
    $pinnedshortcuts  = (get-childitem -path $home\pinnedshortcuts -filter "*.lnk").fullname | %{"`t`t<taskbar:DesktopApp DesktopApplicationLinkPath=`"{0}`" />" -f $_}
    $template         = $template | % {$_;if ($_ -match "<taskbar:taskbarpinlist>") {$pinnedshortcuts}}
    
    $template | out-file  -path "$home\pinnedshortcuts\pinnedshortcuts.xml"
    import-startlayout    -layoutpath $home\pinnedshortcuts\pinnedshortcuts.xml -mountpath c:\
    get-process           -name "explorer" | stop-process & explorer.exe
    

  2. Save template.xml in the same directory as pintotaskbar.ps1:
    <?xml version="1.0" encoding="utf-8"?>
    <LayoutModificationTemplate
        xmlns="http://schemas.microsoft.com/Start/2014/LayoutModification"
        xmlns:defaultlayout="http://schemas.microsoft.com/Start/2014/FullDefaultLayout"
        xmlns:start="http://schemas.microsoft.com/Start/2014/StartLayout"
        xmlns:taskbar="http://schemas.microsoft.com/Start/2014/TaskbarLayout"
        Version="1">
      <CustomTaskbarLayoutCollection>
        <defaultlayout:TaskbarLayout>
          <taskbar:TaskbarPinList>
          </taskbar:TaskbarPinList>
        </defaultlayout:TaskbarLayout>
      </CustomTaskbarLayoutCollection>
    </LayoutModificationTemplate>
    

  3. # Accepts a string like:
      # cmd::Command Prompt|pwsh::PowerShell 7|python::Python
    
    .\pintotaskbar.ps1 "cmd::Command Prompt|pwsh::PowerShell 7|python::Python"
    

I have found a strange issue, using the above example will generate a working shortcut named "Command Prompt pwsh" that points to %comspec%, I got rid of this by deleting [string] type specifier of $execs and it magically went away, I still have no idea how it happened...

Solution 2

There are scripts for Win10 for taskbar pinning to use as is, learn form, tweak as needed.

PinToTaskBar1903.ps1 script version 1

Example use - PIN/UNPIN are case sensitive:

powershell -noprofile -ExecutionPolicy Bypass -file D:\Scripts\PinToTaskBar1903.ps1 "C:\Windows\Notepad.exe" PIN

powershell -noprofile -ExecutionPolicy Bypass -file C:\Scripts\PinToTaskBar1903.ps1 "C:\Windows\Notepad.exe" UNPIN

Update as to your downvote comment.

"The script linked masquerades PEB, and I have not seen the verbs PIN and UNPIN "

Did you even read the script. This PIN/UNPIN is directly called out in the code of the script:

if (($args[0] -eq "/?") -Or ($args[0] -eq "-h") -Or ($args[0] -eq "--h") -Or ($args[0] -eq "-help") -Or ($args[0] -eq "--help")){
    write-host "This script needs to be run with two arguments."`r`n
    write-host "1 - Full path to the file you wish to pin (surround in quotes)."
    write-host "2 - Either PIN or UNPIN (case insensitive)."
    write-host "Example:-"
    write-host 'powershell -noprofile -ExecutionPolicy Bypass -file PinToTaskBar1903.ps1 "C:\Windows\Notepad.exe" PIN'`r`n
    Break
}
Share:
5,081

Related videos on Youtube

Ξένη Γήινος
Author by

Ξένη Γήινος

Updated on September 18, 2022

Comments

  • Ξένη Γήινος
    Ξένη Γήινος over 1 year

    I have seen many similar questions posted on stackoverflow.com, but this is different, and before you are wondering, why don't I post the question on stackoverflow.com? Because I am currently banned from asking questions there, and I currently don't have enough time to edit all those questions.

    A brief description of what I want to do: I want to pin elevated cmd, powershell, pwsh, python etc. to taskbar programmatically, and of course I know how to create shortcuts using explorer.exe and pin them, but I consider the GUI method to be tiresome and tedious, having to click many times to get a simple job done, and I want to pin many programs...

    I have found this:

    How to create a Run As Administrator shortcut using Powershell

    An example:

    $WshShell = New-Object -comObject WScript.Shell
    $Shortcut = $WshShell.CreateShortcut("$Home\Desktop\PowerShell.lnk")
    $Shortcut.TargetPath = "C:\Windows\System32\WindowsPowerShell\v1.0\PowerShell.exe"
    $Shortcut.Save()
    $bytes = [System.IO.File]::ReadAllBytes("$Home\Desktop\PowerShell.lnk")
    $bytes[0x15] = $bytes[0x15] -bor 0x20
    [System.IO.File]::WriteAllBytes("$Home\Desktop\PowerShell.lnk", $bytes)
    

    The example will create a shortcut to default PowerShell named "PowerShell" on desktop with admin rights.

    Now, to wrap it in a function:

    function admin-shortcut {
      PARAM (
        [Parameter(ValueFromPipeline=$true, Mandatory=$true, Position=0)] [system.string] $path,
        [Parameter(ValueFromPipeline=$true, Mandatory=$true, Position=1)] [system.string] $name
        )
        $WshShell = New-Object -comObject WScript.Shell
        $Shortcut = $WshShell.CreateShortcut("$Home\Desktop\$name.lnk")
        $Shortcut.TargetPath = $path
        $Shortcut.Save()
        $bytes = [System.IO.File]::ReadAllBytes("$Home\Desktop\$name.lnk")
        $bytes[0x15] = $bytes[0x15] -bor 0x20
        [System.IO.File]::WriteAllBytes("$Home\Desktop\$name.lnk", $bytes)
    }
    

    I have seen many posts on stackoverflow.com about methods to pin programs directly to taskbar, but none of them solves my problem as I want to give the pinned shortcuts Administrator privileges, none of the methods I have seen does it.

    So I Googled how to create an admin shortcut using PowerShell, and found the method mentioned above, now I only need to figure out how to pin the .lnk files to taskbar, unfortunately all Google search results of "how to pin shortcuts to taskbar" talks about pinning the program directly, none of them involves .lnk files, and I already explained why they won't work in this case.

    So do you have any ideas? Any help will be appreciated.


    Outdated methods that no longer work on Windows 10:

    Method #1

    $shell = new-object -com "Shell.Application"  
    $folder = $shell.Namespace((Join-Path $env:SystemRoot System32\WindowsPowerShell\v1.0))
    $item = $folder.Parsename('powershell_ise.exe')
    $item.invokeverb('taskbarpin');
    

    Method #2

    $shell = new-object -com "Shell.Application"  
    $folder = $shell.Namespace('C:\Windows')    
    $item = $folder.Parsename('notepad.exe')
    $verb = $item.Verbs() | ? {$_.Name -eq 'Pin to Tas&kbar'}
    if ($verb) {$verb.DoIt()}
    

    Method #3

    $sa = new-object -c shell.application
    $pn = $sa.namespace($env:windir).parsename('notepad.exe')
    $pn.invokeverb('taskbarpin')
    

    I pasted all these codes into PowerShell, no error messages showed up, but these commands literally do nothing, I had even restarted explorer.exe, still no change observed...


    But no, wait! All is not lost, I can drag the resultant shortcut to taskbar to pin it to taskbar, and I can right click and pin to start, but imagine dragging 2 dozens of them... If I can figure out what exactly these actions do, I can replicate them with commands...


    I have opened %Appdata%\Microsoft\Internet Explorer\Quick Launch\User Pinned\TaskBar

    Folder and found some (non-UWP) app icons that I have pinned are indeed there,

    and I have found some app icons that I have added to start menu here:

    %AppData%\Microsoft\Internet Explorer\Quick Launch\User Pinned\StartMenu

    However, I have confirmed simply copying the shortcuts there wouldn't work.

    Found this:https://docs.microsoft.com/en-us/windows/configuration/configure-windows-10-taskbar, it is useful.


    I have found a method to do this, since all I want is to add lines to the xml file, I can use this:

    [string[]]$xmlconfig=get-content $template | % {
        $_
        if ($_ -match $pattern) {$line}
    }
    

    I know this is stupid, that's why I didn't post it as an answer, but it does get the job done, don't laugh at me as I am still learning, I am currently researching how to properly manipulate .xml files...

    I tried:

    [xml]$template=Get-Content $templatefile
    $template.SelectNodes("//DesktopApp")
    

    But it doesn't work, it also doesn't work on xml objects converted from .admx files, however .ChildNodes works, and it is not shown by Get-Member... I think treating it like plain text file is the best method now, otherwise I have to use something like this:

    $template.LayoutModificationTemplate.CustomTaskbarLayoutCollection.TaskbarLayout.TaskbarPinList.DesktopApp.DesktopApplicationLinkPath
    

    I have already solved it, I will post it as an answer now, I didn't pursue the registry key, and I no longer want to pin programs to start menu as I don't normally use it...

    • SimonS
      SimonS over 3 years
      from reddit.com/r/PowerShell/comments/6d8jo5/…: The method you are looking for is Pin to Tas&kbar, but has been removed. MSFT's official word is that it was been removed so that programs can not pin unwanted items to the taskbar. [...] They also said that they would continue to make changes that will eventually prevent any application from pinning to a user's taskbar . Maybe you're on a lost case here. But maybe you can see if there is an xml for startmenu and taskbar. search for LayoutModificationTemplate
    • Balthazar
      Balthazar over 3 years
      I would also say that a LayoutModificationTemplate could be the way to go here docs.microsoft.com/en-us/windows/configuration/…
    • JW0914
      JW0914 over 3 years
      When writing a long[er] question/answer, formatting matters... please consider using bullets and/or numbered lists. A lack of formatting makes a long[er] question/answer more taxing to digest (e.g. it's not efficient), which is is a common issue with almost all of your questions/answers. (FYI: text editors like VS Code offer not only markdown language display, but also markdown preview, which is often more efficient to use when crafting long[er] questions/answers than the text box on the site.)
  • Ξένη Γήινος
    Ξένη Γήινος over 3 years
    Sorry for the downvote, but it isn't useful.
  • JW0914
    JW0914 over 3 years
    @XeнεiΞэnвϵς It helps to explain what isn't useful so the author can improve their answer if they wish.
  • Ξένη Γήινος
    Ξένη Γήινος over 3 years
    The script linked masquerades PEB, and I have not seen the verbs PIN and UNPIN present in the script. Even if it somehow does pin to taskbar, it would have the same effect as the methods that no longer works should they work, which pins the progran directly to taskbar without involving .lnk files, and as I have already explained in the question body, won't have my intended effect because the resultant shortcuts won't have admin rights, but I have already found a method to make the .lnk files to have admin rights...
  • postanote
    postanote over 3 years
    Did you actually read the script? It is in there and specifically calls this out at lines 346-353. Again, tweak the code to do the additional settings you are after.
  • Ξένη Γήινος
    Ξένη Γήινος over 3 years
    Are you sure the link you provided is correct one? github.com/FuzzySecurity/PowerShell-Suite/blob/master/… Did you even click the link you posted? And the script only has 311 lines in Notepad++... And I am only stating objective facts.
  • postanote
    postanote over 3 years
    @Xeнεi Ξэnвϵς --- Link updated. We are all human, mistakes happen. There's, no real reason for the attitude. I have been using this since 2019, of course, modified/refactored it for my own needs.
  • Ξένη Γήινος
    Ξένη Γήινος over 3 years
    I have found the script to be indeed relevant, so I upvoted your answer, seriously though, next time when you post answers be sure to click the links you have provided.
  • postanote
    postanote over 3 years
    Again mistakes happen and btw, this is the first time I've ever made a link mistake in the entirety of my contributions on SU. Even with links, they change over time. This was not the case here, but just saying. Again the pushback is/was unwarranted (because links can change /disappear altogether, even if they were correct on the first post, but need to be used in many cases, since code can be too large to post. Catch22 all-in-all). We all are pro-bono types, just trying to help others, when and where possible. Sometimes it works out, other times not.
  • Ξένη Γήινος
    Ξένη Γήινος over 3 years
    True, but we can always use crawler snapshots like Wayback Machine to view the links after they are dead.