How to pin .lnk files to taskbar in PowerShell?

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...