3

Thank you for your help.
I use Powershell in the Windows 10 command prompt and try to execute commands with administrative rights.
When I try the following three, a, b and c, I cannot start with "RunAs" as I think.
Does anyone know how to solve this problem?

(a) It can be executed successfully. The arguments are also passed correctly.
powershell.exe start-process -FilePath 'TestEnv.cmd' -ArgumentList '\"a\" \"B C\" \"d\"' -Verb Open

(b) Does not start after UAC confirmation.
powershell.exe start-process -FilePath 'TestEnv.cmd' -ArgumentList '\"a\" \"B C\" \"d\"' -Verb RunAs

(c) It is possible to invoke it, but the "B C" argument is not reflected in the "double quotes" argument, and "B C" is recognized separately.
powershell.exe start-process -FilePath 'TestEnv.cmd' -ArgumentList '"a" "B C" "d"' -Verb RunAs

Translated with www.DeepL.com/Translator (free version)

mklement0
  • 382,024
  • 64
  • 607
  • 775
aroma
  • 33
  • 2

1 Answers1

1

tl;dr

REM From cmd.exe (Command Prompt) / a batch file:
powershell.exe -c Start-Process cmd.exe -Verb RunAs ('/k {0} \"a\" \"B C\" \"d\"' -f ((Convert-Path 'TestEnv.cmd') -replace ' ', '^^ '))
  • Replace /k with /c to make the new window close automatically when the batch file exits.

  • The working directory for the batch file is C:\Windows\System32.

If you also want to ensure that the batch file executes in the caller's current directory:

REM From cmd.exe (Command Prompt) / a batch file:
powershell.exe -c Start-Process cmd.exe -Verb RunAs ('/k cd \"{0}\" ^&^& {1} \"a\" \"B C\" \"d\"' -f $PWD.ProviderPath, ((Convert-Path 'TestEnv.cmd') -replace ' ', '^^ '))

Read on for an explanation.


The main problem is:

  • When you pass a batch-file path directly - as the executable - to Start-Process -Verb RunAs, there seems to be a problem with passing an argument list that contains double quotes (") - I don't know the reason, but the problem occurs in a layer below PowerShell, either in the underlying System.Diagnostics.ProcessStartInfo .NET API or possibly even in the underlying ShellExecuteExe WinAPI function.

  • (If you either need no arguments or none of them require ", you can pass the batch-file path directly as the executable; by default, because the underlying executable is cmd.exe, which is located in C:\Windows\System32, C:\Windows\System32 becomes the working directory, which you can override with -WorkingDirectory, but note that you must then refer to the batch file with a (possibly relative) path, if it is located elsewhere.)

Workaround:

  • Instead of using the batch-file path directly as the executable to launch, use cmd.exe as the executable, and pass the batch-file path and all its arguments as arguments to the /c option (or /k, if you want to keep the new window open).

  • Unfortunately, with -Verb Runas, cmd.exe invariably uses C:\Windows\System32 as the new process' working directory - passing a startup directory with the -WorkingDirectory parameter is then ignored.

    • The same applies to powershell.exe, but, curiously, not to pwsh.exe (PowerShell [Core] v6+) and .NET-based executables, which (a) preserve the caller's working directory by default and (b) do honor a -WorkingDirectory argument.
  • Therefore, if your TestEnv.cmd batch file is located in the current directory - as opposed to a directory listed in the PATH environment variable ($env:PATH) - you must pass the batch file's full path to cmd.exe, which you can determine with Convert-Path.

    • Note: If you batch file is in the PATH, so to speak, this step isn't necessary.

    • Unfortunately, another workaround is then needed, in case the batch file's full path contains spaces: Because "..."-enclosing the batch-file path inexplicably fails, the individual spaces must be ^-escaped instead.

From inside PowerShell this means your command must look like this (note that I'm using /k to keep the new window open, for diagnostic purposes; use /c to auto-close the window when the batch file exits):

# From inside PowerShell:
Start-Process cmd.exe -Verb RunAs (
  '/k {0} "a" "B C" "d"' -f ((Convert-Path 'TestEnv.cmd') -replace ' ', '^ ')
)

Caveat: Since cmd.exe is being invoked, C:\Windows\System32 is the working directory.
If you also want to ensure that the batch file executes in the caller's current directory, you must prepend a cd command:

# From inside PowerShell:
Start-Process cmd.exe -Verb RunAs (
  '/k cd "{0}" && {1} "a" "B C" "d"' -f $PWD.ProviderPath, ((Convert-Path 'TestEnv.cmd') -replace ' ', '^ ')
)

Calling this from cmd.exe (Command Prompt) / a batch file, via Windows PowerShell's CLI, powershell.exe (note that in PowerShell [Core] v6+ it is now pwsh), adds additional complexity:

  • The " chars. must be escaped as \" so that PowerShell recognizes as them as part of the command to execute rather than as syntactic quoting around the CLI arguments.

  • ^ must be escaped as ^^ and, in the second command below, & as ^& to prevent cmd.exe from interpreting these characters (up front).

REM From cmd.exe (Command Prompt) / a batch file:
powershell.exe -c Start-Process cmd.exe -Verb RunAs ('/k {0} \"a\" \"B C\" \"d\"' -f ((Convert-Path 'TestEnv.cmd') -replace ' ', '^^ '))

If you also want to ensure that the batch file executes in the caller's current directory:

REM From cmd.exe (Command Prompt) / a batch file:
powershell.exe -c Start-Process cmd.exe -Verb RunAs ('/k cd \"{0}\" ^&^& {1} \"a\" \"B C\" \"d\"' -f $PWD.ProviderPath, ((Convert-Path 'TestEnv.cmd') -replace ' ', '^^ '))
mklement0
  • 382,024
  • 64
  • 607
  • 775
  • Good one. I gave it a shot and I still don't understand why the behaviour changes with RunAs (talking about the quotes here), it's like it's suddenly impossible to have spaces in the argument list for no obvious reason. Good job on the workaround. – PMental Nov 15 '20 at 16:20
  • Thanks, @PMental. Yes, the behavior definitely sounds buggy. – mklement0 Nov 15 '20 at 16:47
  • Sounds buggy to me too. I raised an issue https://github.com/PowerShell/PowerShell/issues/14099 – Martin Brown Nov 15 '20 at 21:23
  • I even looked at the source code for Start-Process and still can't see the issue. – Martin Brown Nov 15 '20 at 21:23
  • 1
    Thank you. I knew it was a bug. I knew how to relay another method, such as CMD. I just didn't know about it and was wondering if I could get around it by using Powershell's options and escape sequences. Thank you. – aroma Nov 16 '20 at 05:02
  • Thanks, @MartinBrown, I've added information that the bug comes from a layer _below_ PowerShell to the answer, so we can clean up our comments here. – mklement0 Nov 17 '20 at 13:54
  • My Issue got closed (quite reasonably) as a duplicate of this: https://github.com/PowerShell/PowerShell/issues/5576 – Martin Brown Nov 17 '20 at 19:12
  • @MartinBrown: That's a separate issue that occurs irrespective of `-Verb RunAs`. The bug at hand is: using a _single_ `-ArgumentList` string with _embedded_ quoting generally works, but with `-Verb RunAs` it doesn't. I have not tried to report it anywhere. – mklement0 Nov 17 '20 at 19:17
  • 1
    Thank you, mklement0, everyone. The answer was approved. In addition to Powershell, the ShellExecute function of WSH seems to have the same problem. I have been running Powershell with administrative privileges from the ShellExecute function to solve this problem in JSCRIPT. I then run the desired commands and arguments in Powershell to get around the problem. The substitution method you showed me, mklement0, was very informative. Thank you very much. – aroma Nov 17 '20 at 23:42