I can't get this batch script to work, even though I tried a lot of things, I'm now stuck.
The intention is for the batch script to re-run itself elevated, using PowerShell and Start-Process, if the admin check fails.
As long as the passed argument(s), do not contain spaces it works fine. However I need to forward the original batch arguments, (file paths), and can't figure out how to handle the case where they contain spaces.
Here is a small example of what I'm trying to achieve, in this example the echo
doesn't correctly print out Arg 1
and Arg 2
:
@echo off
setlocal enabledelayedexpansion
echo %1
echo %2
net session >nul 2>&1
if %ERRORLEVEL% neq 0 (
echo Need to fix association but missing admin rights...
echo Elevating privileges...
REM Once it work, 'Arg 1' and 'Arg 2' will be replaced by %1 and %2
REM Which are gonna be path enclosed with double quote and containing space ie : "C:\Path to\A folder\"
powershell -Command "$args = 'Arg 1', 'Arg 2'; Start-Process -Verb RunAs -FilePath '%~dpnx0' -ArgumentList $args"
pause
exit /b
)
pause
endlocal
There are multiple challenges:
When using the -Commmand
(-c
) parameter of powershell.exe
, the Windows PowerShell CLI, any pass-through "
characters, i.e. those that should be seen as part of the command for PowerShell to execute, must be escaped as \"
.
A long-standing bug in Start-Process
requires that elements of an array passed to -ArgumentList
that contain spaces be manually enclosed in embedded "..."
in order to be passed correctly - see GitHub issue #5576
Note that the bug will not be fixed, so as not to break backward compatibility; a new parameter that effects the proper behavior has been proposed, but the discussion has stalled.
In light of the bug, it is usually conceptually clearer to use a single string with -ArgumentList
that encodes all arguments, with embedded quoting as necessary, as shown below; see this answer for background information:
There are additional complications relating to batch files, specifically, which - in short - require calling your batch file indirectly, via cmd /c
, plus an extra layer of embedded double-quoting.
Therefore, use the following (note that I'm omitting enabledelayedexpansion
, as it isn't necessary here, and can cause problems with what should be verbatim !
characters):
@echo off
setlocal
net session >nul 2>&1 || (
echo Need to fix association but missing admin rights...
echo Elevating privileges...
powershell -noprofile -Command $argStr = '\"%~1\" \"%~2\"'; Start-Process -Verb RunAs cmd.exe '/c', \"`\"`\"%~f0`\" $argStr`\"\"
pause
exit /b
)
echo Now running with elevation; arguments received:
echo [%1]
echo [%2]
pause
endlocal
Note:
%~f0
is a shorter alternative to %~dpnx0
for obtaining the running batch file's full file path.
%~1
and %~2
ensures that enclosing double quotes, if present, are stripped from the values of %1
and %2
, so that these values can be double-quoted predictably as part of the PowerShell command, with the required \
-escaping.
The fact that '...'
is used around the value assigned to $argStr
assumes that the value itself, specifically the values of %~1
and %~2
, do not contain '
; handling this case would require a bit more work.