Enable or disable shortcut elevation: Set-ShortcutElevation

Discussion in 'Scripting' started by GodHand, Jan 5, 2019.

  1. GodHand

    GodHand MDL Senior Member

    Jul 15, 2016
    436
    498
    10
    Set-ShortcutElevation allows you to enable or disable elevation on one or multiple shortcuts using the .NET classes "ReadAllBytes" and "WriteAllBytes," which are more efficient and quicker than using the FileStream and BinaryReader.

    For single shortcut .lnk files:

    For multiple shortcut .lnk files in a specific folder or directory:
    Code:
    Function Set-ShortcutElevation
    {
        <#
        .SYNOPSIS
            Sets or removes elevation on shortcuts.
        
        .DESCRIPTION
            Sets or removes elevation on either a single shortcut .lnk file or multiple shortcut .lnk files in a specific folder or directory.
        
        .PARAMETER Path
            The full path to the shortcut, or the directory containing multiple shortcuts.
        
        .PARAMETER Disable
            Disables elevation.
        
        .EXAMPLE
            PS C:\> Set-ShortcutElevation -Path "C:\My Shortcut.lnk"
            PS C:\> "C:\My Shortcut.lnk" | Set-ShortcutElevation -Disable
            PS C:\> Get-ChildItem -Path "C:\My Custom Files" -Filter *.lnk | ForEach { Set-ShortcutElevation -Path $_.FullName }
        #>
        [CmdletBinding()]
        Param
        (
            [Parameter(Mandatory = $true,
                ValueFromPipeline = $true,
                ValueFromPipelineByPropertyName = $true,
                Position = 0,
                HelpMessage = 'The full path to the shortcut, or the directory containing multiple shortcuts.')]
            [ValidateScript( { Test-Path $(Resolve-Path -Path $_) })]
            [string]$Path,
            [Parameter(HelpMessage = 'Disables elevation.')]
            [switch]$Disable
        )
        Begin
        {
            $Offset = 0x15
        }
        Process
        {
            If ((Get-Item -Path $Path).Extension.Equals('.lnk'))
            {
                Try
                {
                    $Bytes = [System.IO.File]::ReadAllBytes($Path)
                    $Bytes[$Offset] = @(0x00, 0x20)[!$Disable]
                    [System.IO.File]::WriteAllBytes($Path, $Bytes)
                }
                Catch
                {
                    $PSCmdlet.ThrowTerminatingError($_)
                }
            }
        }
    }
    
     
  2. Thomas Dubreuil

    Thomas Dubreuil MDL Senior Member

    Aug 29, 2017
    272
    457
    10
    Nice!!
    Would it be possible to filter by "target path"??? I struggle with this since long time :)

    let's say I want to change many (.bat) uninstallers links located in different folders in the start menu, but all pointing to the same folder in programdata..
    If I make this:
    Code:
    Get-ChildItem -Path "$env:programdata\Microsoft\Windows\Start Menu\Programs\" -recurse | Where-Object {$_.Name -like "*lnk*" -and $_.Name -like "*Uninstall*"} | Set-ShortcutElevation  -enable
    it will change every other installer...Is it possible to filter lnk by their "target path"? (for ex: change only those pointing to programdata/test)
     
    Stop hovering to collapse... Click to collapse... Hover to expand... Click to expand...
  3. GodHand

    GodHand MDL Senior Member

    Jul 15, 2016
    436
    498
    10
    For what you're asking, you would simply add all the paths to an array, and then loop through it, piping each output to the Set-ShortcutElevation function.

    Code:
    $Paths = @("C:\Derp", "C:\Derp1", "C:\Derp2")
    ForEach ($Path In $Paths)
    {
        Get-ChildItem -Path $Path -Filter *.lnk | Where-Object Name -ILike *Uninstall* | ForEach-Object { Set-ShortcutElevation -Path $_.FullName  }
    }
    
    Also, the function does not require an -Enable switch. It only requires -Disable if you instead want to disable elevation.
     
  4. Thomas Dubreuil

    Thomas Dubreuil MDL Senior Member

    Aug 29, 2017
    272
    457
    10
    Not really the expected answer...How do I know the folders ? and what if they are many links and mixed with other uninstallers ???
    btw this is a bit better, filtering .bat targets only and optionally adding a string/text that is found inside that bat

    Code:
    $source = "$env:programdata\Microsoft\Windows\Start Menu\Programs"
    
    $ws = New-Object -ComObject Wscript.Shell
    
    Get-ChildItem $source -recurse -Filter *.lnk | Foreach {
       $tp = Get-Item $ws.CreateShortcut($_.fullname).TargetPath -Ea 0 | Where {$_.Extension -eq ".bat" -and $_.Name -like "*uninst*"} | Select-String anystringyouwant
       if($tp)
       { Set-ShortcutElevation -Path $_.FullName }
       
    }
    
    
    still I would like to filter by target path :)
     
    Stop hovering to collapse... Click to collapse... Hover to expand... Click to expand...