Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
4 changes: 4 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -91,6 +91,10 @@ Get-Help .\Windows-Path-Repair-Full.ps1 -Detailed

Double-click `Run-PATH-Repair-Admin.bat` to open an elevated PowerShell session with a **preview** of the canonical script. It does not apply changes automatically. Review the result, then rerun the command in that elevated window with `-Apply` if it is correct.

The batch file hands the script location to [`Start-PATH-Repair-Elevated.ps1`](Start-PATH-Repair-Elevated.ps1) as a bound `-ScriptPath` parameter, and that helper passes the path to the elevated process as a single argument instead of inserting it into PowerShell command text. This keeps the launcher working when the repository folder contains spaces, apostrophes, ampersands, parentheses, or non-ASCII characters, for example `C:\Qoder Test\O'Brien & 工具 (x64)`. Keep `Start-PATH-Repair-Elevated.ps1` in the same folder as the batch file.

If the canonical script is missing or the elevated session cannot be started, such as when the UAC prompt is dismissed, the launcher reports the reason and exits with a non-zero exit code.

## Backups and recovery

Every apply operation creates a timestamped JSON backup under `%USERPROFILE%\PathBackups` before modifying `PATH`. Keep the backup until you have opened a new terminal and confirmed that your required commands resolve correctly. The backup contains the exact original User and Machine values, and the script automatically rolls back changes from the current run if a later write or read-back verification fails.
Expand Down
13 changes: 11 additions & 2 deletions Run-PATH-Repair-Admin.bat
Original file line number Diff line number Diff line change
Expand Up @@ -3,9 +3,18 @@ setlocal DisableDelayedExpansion
echo Windows PATH Repair Tool
echo Opening an elevated preview. No PATH changes will be made unless you rerun with -Apply.
set "repair_script=%~dp0Windows-Path-Repair-Full.ps1"
set "launcher_script=%~dp0Start-PATH-Repair-Elevated.ps1"
if not exist "%repair_script%" (
echo ERROR: Canonical repair script was not found.
exit /b 1
)
PowerShell -NoProfile -Command "$scriptPath = $args[0]; $arguments = '-NoProfile -NoExit -File \"{0}\"' -f $scriptPath; Start-Process -FilePath 'PowerShell.exe' -Verb RunAs -ArgumentList $arguments" "%repair_script%"
endlocal
if not exist "%launcher_script%" (
echo ERROR: Elevation launcher script was not found.
exit /b 1
)
PowerShell -NoProfile -ExecutionPolicy Bypass -File "%launcher_script%" -ScriptPath "%repair_script%"
set "launch_exit=%ERRORLEVEL%"
if not "%launch_exit%"=="0" (
echo ERROR: The elevated preview could not be started.
)
endlocal & exit /b %launch_exit%
122 changes: 122 additions & 0 deletions Start-PATH-Repair-Elevated.ps1
Original file line number Diff line number Diff line change
@@ -0,0 +1,122 @@
<#
.SYNOPSIS
Start an elevated, preview-only run of Windows-Path-Repair-Full.ps1.

.DESCRIPTION
Receives the canonical script location as a bound -ScriptPath parameter and
forwards it to the elevated PowerShell.exe process as a discrete argv token.
The path is never concatenated into PowerShell command text, so a repository
directory containing spaces, apostrophes, ampersands, parentheses, or
non-ASCII characters is passed through unchanged.

Elevation uses -Verb RunAs so the standard UAC prompt stays visible, and the
elevated session opens the canonical script in preview mode. -Apply is never
added automatically.
#>

[CmdletBinding()]
param(
[Parameter(Mandatory = $true)]
[ValidateNotNullOrEmpty()]
[string]$ScriptPath,

[scriptblock]$StartProcessAction = {
param($FilePath, $ArgumentList)
Start-Process -FilePath $FilePath -ArgumentList $ArgumentList -Verb RunAs | Out-Null
},

[switch]$NoExecute
)

Set-StrictMode -Version Latest
$ErrorActionPreference = 'Stop'

function ConvertTo-ArgumentToken {
<#
Quotes one value using the Windows CommandLineToArgvW rules so the child
process recovers the original string exactly.
#>
[CmdletBinding()]
param([Parameter(Mandatory = $true)][AllowEmptyString()][string]$Value)

if ($Value.Contains('"')) {
throw "A Windows file path cannot contain a double quote character: $Value"
}

# Backslashes are literal unless they immediately precede the closing quote,
# where each one must be doubled.
$trailingBackslashes = 0
for ($i = $Value.Length - 1; $i -ge 0 -and $Value[$i] -eq '\'; $i--) {
$trailingBackslashes++
}

return '"' + $Value + ('\' * $trailingBackslashes) + '"'
}

function Get-ElevatedLaunchArgumentLine {
<#
Builds the argv line for the elevated PowerShell.exe process. The result
contains only fixed switches plus the quoted script path; it is never
evaluated as PowerShell command text.
#>
[CmdletBinding()]
param([Parameter(Mandatory = $true)][ValidateNotNullOrEmpty()][string]$ScriptPath)

$tokens = @('-NoProfile', '-NoExit', '-File', (ConvertTo-ArgumentToken -Value $ScriptPath))
return ($tokens -join ' ')
}

function Get-ElevatedPowerShellPath {
[CmdletBinding()]
param()

$candidate = Join-Path -Path $PSHOME -ChildPath 'PowerShell.exe'
if (Test-Path -LiteralPath $candidate -PathType Leaf) {
return $candidate
}

return 'PowerShell.exe'
}

function Start-ElevatedPathRepair {
[CmdletBinding()]
param(
[Parameter(Mandatory = $true)][ValidateNotNullOrEmpty()][string]$ScriptPath,
[Parameter(Mandatory = $true)][scriptblock]$StartProcessAction
)

if (-not (Test-Path -LiteralPath $ScriptPath -PathType Leaf)) {
throw "Canonical repair script was not found: $ScriptPath"
}

$resolvedPath = (Get-Item -LiteralPath $ScriptPath).FullName
$filePath = Get-ElevatedPowerShellPath
$argumentLine = Get-ElevatedLaunchArgumentLine -ScriptPath $resolvedPath

try {
& $StartProcessAction $filePath $argumentLine
}
catch {
throw "Could not start the elevated PowerShell session: $($_.Exception.Message)"
}

return [pscustomobject]@{
FilePath = $filePath
ArgumentList = $argumentLine
ScriptPath = $resolvedPath
}
}

if ($NoExecute) {
return
}

try {
$launch = Start-ElevatedPathRepair -ScriptPath $ScriptPath -StartProcessAction $StartProcessAction
Write-Host "Elevated preview requested for: $($launch.ScriptPath)" -ForegroundColor Cyan
exit 0
}
catch {
Write-Host "ERROR: $($_.Exception.Message)" -ForegroundColor Red
exit 1
}
Loading