Hiding a Powershell window using VBscript: whitespace in the -file filepath - powershell

User JPBlanc and others have given a nice solution to such a problem with the solution:
Set Args = Wscript.Arguments
'MsgBox "Chemin LDAP: " & Args(0)
'MsgBox "Classe: " & Args(1)
Set objShell = CreateObject("Wscript.Shell")
objShell.Run "c:\Windows\System32\WindowsPowerShell\v1.0\powershell.exe -nologo -Noninteractive -file c:\SlxRH\RhModif.ps1 " & chr(34) & Args(0) & chr(34) , 0
I have tested such a solution out and it works fine. However, my problem concerns the situation when the file path identified as the argument for the "-file" option has white space in it; as example, if the code example above had a file path of:
c:\Slx RH\RhModif.PS1
Observe the white space in the file path. The question is this: how to escape the file path correctly. I haven't found a way to do this (to make it work).
I have searched on this site and the general "interweb" and have learned (I think) that there are two levels of script interpretation going on: VBscript and the Wscript. Another very good example appears here (link to the full page with this title):
Run scheduled tasks with WinForm GUI in PowerShell
Dim objShell
Set objShell=CreateObject("WScript.Shell")
strExpression="get-date | add-content c:\temp\test.txt"
strCMD="powershell -sta -noProfile -NonInteractive -nologo -command " & Chr(34) & "&{" & strExpression &"}" & Chr(34)
objShell.Run strCMD,0
I have tested this and it works for me. While not using the "-file" option, this example gives (what you would think) would be a generic method of putting a "-command" or "-file" input for the strCMD variable. However, when I use the following code, it does not work:
Dim objShell
Set objShell=CreateObject("WScript.Shell")
strExpression="C:\aP_RDB\Set Up\SimpleInsert.PS1"
strCMD="powershell -noprofile -executionpolicy Unrestricted -NonInteractive -file " & Chr(34) & "&{" & strExpression &"}" & Chr(34)
objShell.Run strCMD,0
In my example above, I essentially copied the code reference above (and from the link listed), replaced the PS command with my file path (which includes white space) for the variable strExpression, and updated the strCMD definition by replacing the "-command" argument with the "-file" argument. However, the error message points to an unrecognized argument "c:\aP_RDB\Set" that is, it does not see the whole path "C:\aP_RDB\Set Up\SimpleInsert.PS1". It appears that the escaping of the quotes (or something) is not working... better said, I am not doing it correctly.
Any thoughtful advice would be greatly appreciated. FYI: I have tried the -WindowStyle Hidden option in Powershell; I like many have found that not to be a consistent approach across Windows platforms (more specifically for me: I could not get it to work).

Thanks for the reference, here are two ways to start scripts with white space in the name :
1) using double " ("")
set Args = Wscript.Arguments
MsgBox "Chemin LDAP: " & Args(0)
'MsgBox "Classe: " & Args(1)
Set objShell = CreateObject("Wscript.Shell")
objShell.Run "c:\Windows\System32\WindowsPowerShell\v1.0\powershell.exe -nologo -Noninteractive -file ""c:\temp\test arg.ps1"" " & chr(34) & Args(0) & chr(34) , 0
2) Consist in writing your own PowerShell.exe with no console, as I explain at the end of this article ""PowerShell.exe" ne sait pas démarrer sans afficher une fenêtre"(sorry in french)

Related

Powershell via VBScript - Environmental Variable path terminated when username contains spaces [duplicate]

This question already has answers here:
How can I run a PowerShell script with white spaces in the path from the command line?
(5 answers)
Closed 1 year ago.
I have been working on a Powershell Winforms app that requires the console window to be hidden. To do this, I am calling a .ps1 script from a .vbs file (as starting via another .ps1 script and using "-WindowStyle Hidden" still briefly shows the console window upon opening the script). I am using the following code:
Dim shell,command
command = "powershell.exe -WindowStyle Hidden -ExecutionPolicy Bypass -command ""%localappdata%\test\test.ps1"""
Set shell = CreateObject("WScript.Shell")
shell.Run command,0
This works with no issues when using an account name with no spaces (e.g. "TESTUSER" will resolve to "C:\USERS\TESTUSER\AppData\Local"). However, when any part of the path generated by the "%localappdata%" environmental variable contains spaces (in this case, using something like "TEST USER 1"), Powershell will terminate the command at "C:\Users\TEST" with an error stating: "The term "C:\Users\TEST" is not recognised as the name of a cmdlet, function, script file or operable program."
I am aware that any strings with spaces in VBScript need to use two sets of surrounding double quotes (""path with spaces""), but this doesn't work in this case - I have tried every combination that has been suggested and the %localappdata% path still has the same issue.
Things I have tried:
Two quotes surrounding path (Working with no spaces):
command = "powershell.exe -WindowStyle Hidden -ExecutionPolicy Bypass -command ""%localappdata%\test\test.ps1"""
Two quotes surrounding entire argument:
command = ""powershell.exe -WindowStyle Hidden -ExecutionPolicy Bypass -command ""%localappdata%\test\test.ps1""""
Single quotes surrounding both:
command = "powershell.exe -WindowStyle Hidden -ExecutionPolicy Bypass -command "%localappdata%\test\test.ps1""
Expanding %localappdata% variable:
Dim shell,path,command
Set shell.CreateObject("WScript.Shell")
path = shell.ExpandEnvironmentStrings("%localappdata%")
command = "powershell.exe -WindowStyle Hidden -ExecutionPolicy Bypass -command " & path &"\test\test.ps1"
shell.Run command,0
Adding "Chr(34)" to replace spaces:
command = "powershell.exe -WindowStyle Hidden -ExecutionPolicy Bypass -command ""%localappdata%\test\test.ps1""" & Chr(34)
Using:
"$env:localappdata" or "$env:username"
(both inside and out of "command" quotes) to replace
"%localappdata%" or "C:\Users\%username%\AppData\Local"
I have also tried various solutions provided here and here, but nothing works in this case.
I also tried replacing %localappdata% with the absolute file path ("C:\Users\TEST USER 1\AppData\Local\test\test.ps1"), but this also gives the same error.
Any help would be greatly appreciated! Thanks in advance.
Since you're invoking a script file by path rather than passing PowerShell statements to PowerShell's CLI, use the -File parameter, not -Command, which implicitly solves your quoting problems:
' Note the use of -File instead of -Command
command = "powershell.exe -WindowStyle Hidden -ExecutionPolicy Bypass -File ""%localappdata%\test\test.ps1"""
The reason that -Command didn't work in your case is that its argument(s) are subject to another round of interpretation, namely as PowerShell code, after stripping syntactic " chars. during command-line parsing. This means that a path with spaces is then seen unquoted by PowerShell, causing the invocation to fail; you'd need additional quoting -either escaped " quotes (\"...\", i.e. \""...\"" from inside a VBScript string) or single quotes ('...'), which in turn would necessitate use of &, the call operator:
' With -Command: note the embedded '...' quoting and the need to call with `&`
' However, there's usually no need for -Command to invoke scripts with arguments.
command = "powershell.exe -WindowStyle Hidden -ExecutionPolicy Bypass -Command ""& '%localappdata%\test\test.ps1'"""
See this answer for more information.

How to pass a string array to powershell through vbscript?

I'm trying to pass a string array to powershell through vbscript.
I got the params in the file.ps1 script:
param (
[string[]]$target
)
I'm trying to call file.ps1 through vbscript.
I want $target = #('c:\dev','d:\lib') to be passed in.
I currently have in my vbscript :-
target1 = """c:\dev"", ""d:\lib"""
Set objShell = CreateObject("Wscript.Shell")
objShell.run("powershell.exe -noexit -ExecutionPolicy Unrestricted -file .\file.ps1 -target1 """ & target & """")
which returns:
c:\dev, d:\lib and which isn't the powershell string array format "c:\dev", "d:\lib"
Update - Thanks mklement0 and alex-dl for your answers!
alex-dl's helpful answer contains an effective solution, as long as the array's elements don't need quoting, but let me try to break it down conceptually in more detail:
The -File parameter of PowerShell's CLI, powershell.exe (pwsh in PowerShell (Core) 7+) fundamentally does not support passing arrays to PowerShell code, because all arguments are interpreted strictly as whitespace-separated, with verbatim content.
E.g., "c:\dev", "d:\lib" on the command line is parsed as two arguments:
c:\dev, (note the trailing ,, syntactic " quotes stripped)
d:\lib
You must use the -Command (-c) option in order to pass arrays.
-Command fundamentally changes how the arguments are parsed:
All arguments are stripped of syntactic (non \"-escaped) " quotes.
The resulting tokens are space-concatenated to form a single string.
The resulting string is then interpreted as PowerShell code, i.e. as if you had submitted it from inside a PowerShell session.
This therefore enables all of PowerShell's features, such as passing arrays with ,, '...' (single-quoting), ..., and notably also means that references to PowerShell variables (e.g. $HOME) are recognized (unlike with -File).
See this answer for more guidance on when to use -File vs. -Command (-c).
Therefore, the best approach in your case is:
target = "'c:\dev', 'd:\lib'" ' Note the embedded '...'-quoting
Set objShell = CreateObject("Wscript.Shell")
' Note the use of -c (-command) instead of -file
objShell.Run("powershell.exe -noexit -ExecutionPolicy Unrestricted -c .\file.ps1 -target " & target)
Using embedded '...'-quoting simplifies passing the array elements in a way that PowerShell sees them as individually quoted (with the values at hand, this isn't strictly necessary, but it may be in other cases).
Doing this with embedded "..."-quoting gets unwieldy, because each embedded " must then be escaped as \"" (sic):
"" to escape the " inside the VBScript string,
and \ to ensure that \" is ultimately passed on the command line, which PowerShell requires for escaping " on the command line[1] (whereas PowerShell-internally, it is `" or (alternatively, inside a double-quoted string) "").
target = "\""c:\dev\"", \""d:\lib\"""
Set objShell = CreateObject("Wscript.Shell")
objShell.Run("powershell.exe -noexit -ExecutionPolicy Unrestricted -c .\file.ps1 -target " & target)
[1] While in Windows PowerShell you can situationally get away with "" instead of \", it can break, especially if the overall -c argument is enclosed in "...".
This problem has been fixed in PowerShell (Core) 7+, where you can now use "" and \" interchangeably.
E.g., when invoked from cmd.exe,
powershell -c " ""ab c"".length " breaks, whereas
pwsh -c " ""ab c"".length " works.
You could use the following vbscript:
target1 = """c:\dev, d:\lib"""
Set objShell = CreateObject("Wscript.Shell")
objShell.run("powershell.exe -Command .\file.ps1 -target " & target1)
And this powershell script:
param (
[string[]]$target
)
foreach ($t in $target) {
write-output $t
}
Read-Host -Prompt "Press Enter to exit"
Without -Command the argument is always interpreted as a single string.

passing quoted arguments from batch file to `powershell start` - self-elevation on demand

I am writing a Windows batch file that automatically escalates itself to administrative permissions, provided the user clicks "Yes" on the User Access Control dialog that appears.
I am using a technique I learned here to detect whether we already have admin rights and another from here to escalate. When appropriate, the following script, let's call it foo.bat, re-launches itself via a powershell-mediated call to runas:
#echo off
net session >NUL 2>NUL
if %ERRORLEVEL% NEQ 0 (
powershell start -wait -verb runas "%~dpfx0" -ArgumentList '%*'
goto :eof
)
echo Now we are running with admin rights
echo First argument is "%~1"
echo Second argument is "%~2"
pause
My problem is with escaping quotes in the -ArgumentList. The code above works fine if I call foo.bat one two from the command prompt, but not if one of the arguments contains a space, for example as in foo.bat one "two three" (where the second argument should be two words, "two three").
If I could even just get the appropriate behavior when I replace %* with static arguments:
powershell start -wait -verb runas "%~dpfx0" -ArgumentList 'one "two three"'
then I could add some lines in foo.bat that compose an appropriately-escaped substitute for %*. However, even on that static example, every escape pattern I have tried so far has either failed (I see Second argument is "two" rather than Second argument is "two three") or caused an error (typically Start-Process: A positional parameter cannot be found that accepts argument 'two'). Drawing on the docs for powershell's Start-Process I have tried all manner of ridiculous combinations of quotes, carets, doubled and tripled quotes, backticks, and commas, but there's some unholy interaction going on between batch-file quoting and powershell quoting, and nothing has worked.
Is this even possible?
You've run into a perfect storm of two quoting hells (cmd and PowerShell), garnished with a PowerShell bug (as of PowerShell Core 6.2.0).
To work around the bug, the batch file cannot be reinvoked directly and must instead be reinvoked via cmd /c.
LotPings' helpful answer, which takes that into account, typically works, but not in the following edge cases:
If the batch file's full path contains spaces (e.g., c:\path\to\my batch file.cmd)
If the arguments happen to contain any of the following cmd metacharacters (even inside "..."): & | < > ^; e.g., one "two & three"
If the reinvoked-with-admin-privileges batch file relies on executing in the same working directory it was originally called from.
The following solution addresses all these edge cases. While it is far from trivial, it should be reusable as-is:
#echo off
setlocal
:: Test whether this invocation is elevated (`net session` only works with elevation).
:: If already running elevated (as admin), continue below.
net session >NUL 2>NUL && goto :elevated
:: If not, reinvoke with elevation.
set args=%*
if defined args set args=%args:^=^^%
if defined args set args=%args:<=^<%
if defined args set args=%args:>=^>%
if defined args set args=%args:&=^&%
if defined args set args=%args:|=^|%
if defined args set "args=%args:"=\"\"%"
powershell -NoProfile -ExecutionPolicy Bypass -Command ^
" Start-Process -Wait -Verb RunAs -FilePath cmd -ArgumentList \"/c \"\" cd /d \"\"%CD%\"\" ^&^& \"\"%~f0\"\" %args% \"\" \" "
exit /b
:elevated
:: =====================================================
:: Now we are running elevated, in the same working dir., with args passed through.
:: YOUR CODE GOES HERE.
echo First argument is "%~1"
echo Second argument is "%~2"
pause
This is my batch for that purpose:
::ElevateMe.cmd::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::
#echo off & setlocal EnableExtensions DisableDelayedExpansion
Set "Args=%*"
net file 1>nul 2>&1 || (powershell -ex unrestricted -Command ^
Start-Process -Verb RunAs -FilePath '%comspec%' -ArgumentList '/c %~f0 %Args:"=\""%'
goto :eof)
:: Put code here that needs elevation
Echo:%*
Echo:%1
Echo:%2
Pause
Sample output:
one "two three"
one
"two three"
Drücken Sie eine beliebige Taste . . .
If you want the elevated cmd to stay open, use -ArgumentList '/k %~f0 %Args:"=\""%
The only approved way to elevate is to use a manifest. This emulates Unix's SUDO.EXE.
To run a command and stay elevated
RunAsAdminconsole <Command to run>
To elevate current cmd window or create a new elevated one
RunAsAdminconsole
From https://pastebin.com/KYUgEKQv
REM Three files follow
REM RunAsAdminConsole.bat
REM This file compiles RunAsAdminconsole.vb to RunAsAdminconsole.exe using the system VB.NET compiler.
REM Runs a command elevated using a manifest
C:\Windows\Microsoft.NET\Framework\v4.0.30319\vbc "%~dp0\RunAsAdminconsole.vb" /win32manifest:"%~dp0\RunAsAdmin.manifest" /out:"%~dp0\RunAsAdminConsole.exe" /target:exe
REM To use
rem RunAsAdminconsole <Command to run>
pause
RunAsAdmin.manifest
<?xml version="1.0" encoding="UTF-8" standalone="yes"?>
<assembly xmlns="urn:schemas-microsoft-com:asm.v1" manifestVersion="1.0">
<assemblyIdentity
version="1.0.0.0"
processorArchitecture="*"
name="Color Management"
type="win32"
/>
<description>Serenity's Editor</description>
<trustInfo xmlns="urn:schemas-microsoft-com:asm.v2">
<security>
<requestedPrivileges>
<requestedExecutionLevel level="requireAdministrator" uiAccess="false"/>
</requestedPrivileges>
</security>
</trustInfo>
</assembly>
'RunAsAdminConsole.vb
'Change cmd /k to cmd /c to elevate and run command then exit elevation
imports System.Runtime.InteropServices
Public Module MyApplication
Public Sub Main ()
Dim wshshell as object
WshShell = CreateObject("WScript.Shell")
Shell("cmd /k " & Command())
End Sub
End Module
----------------------------------------

Vbscript valid path

I'm trying to have a scheduler to run a Vbscript, which it will run a Powershell script.
Basically here is my Vbscript
command = "powershell.exe -nologo -command C:\Users\someuser\Desktop\appAPIMonitor.ps1"
set shell = CreateObject("WScript.Shell")
shell.Run command,0
When I run above script, it worked. But when I place the script at my preferred path, the script wont run as before. So it must be the path not valid. Here is my new script
command = "powershell.exe -nologo -command C:\Users\someuser\My Work\App\Project\My.API.App\Scripts\appAPIMonitor.ps1"
set shell = CreateObject("WScript.Shell")
shell.Run command,0
Can somebody point what is wrong with my path? Is it the whitespace in My Work? Or the dot in My.Api.App?
This is the solution I managed to get.
command = "powershell.exe -nologo -file ""C:\Users\someuser\My Work\App\Project\My.API.App\Scripts\appAPIMonitor.ps1"" "
set shell = CreateObject("WScript.Shell")
shell.Run command,0
Give the last quote some white space and change -command to -file should do the trick. I solve this in my case. Hopefully help anybody in future. By the way, thanks to those who spent their precious time to answer my question.

How can I execute a powershell command as admin from VBS?

I currently have a VBScript that allows me to call arbitrary powershell commands, which includes entire powershell scripts. When I call them I cannot set the execution policy due to registry restrictions. Meaning, powershell isn't running as administrator.
How can I change this?
I believe the following is the section of the VBScript that is calling powershell.exe
cmd = "C:\Windows\System32\WindowsPowerShell\v1.0\powershell.exe -OutputFormat text -EncodedCommand " & b64 & " > " & logstd & " 2> " & logerr
There are various ways you can make a process run elevated but for this use case, I think you should just specify the execution policy in the command line:
cmd = "C:\Windows\System32\WindowsPowerShell\v1.0\powershell.exe -ExecutionPolicy unrestricted -OutputFormat text -EncodedCommand " & b64 & " > " & logstd & " 2> " & logerr