Call PowerShell script from VBScript but windows close - powershell

This VBScript runs a few PowerShell scripts. I put the invocation of notepad.exe there to make sure it ran as an admin.
My PowerShell scripts open consoles but then they close immediately.
I would like to have them stay open.
What is wrong?
Here is my VBScript:
RunAsAdmin()
Set shell = WScript.CreateObject("WScript.shell")
shell.Run("powershell.exe -noexit -executionpolicy bypass -file .\Source\RemoveWindows10Apps-2.0.ps1"), 1 , True
Set shell = WScript.CreateObject("WScript.shell")
shell.Run("powershell.exe -noexit -executionpolicy bypass -file .\Source\ChangeWin10StartLayout.ps1"), 1 , True
Set shell = WScript.CreateObject("WScript.shell")
shell.Run("powershell.exe -noexit -executionpolicy bypass -file .\Source\NewFolder.ps1"), 1 , True
Set shell = WScript.CreateObject("WScript.shell")
shell.Run("notepad.exe"), 1 , True
Function RunAsAdmin()
Dim objAPP
If WScript.Arguments.length = 0 Then
Set objAPP = CreateObject("Shell.Application")
objAPP.ShellExecute "wscript.exe", """" & _
WScript.ScriptFullName & """" & " RunAsAdministrator",,"runas", 1
WScript.Quit
End If
End Function

I guess it is because you use relative paths so when your script will call itself as admin your current directory will change to ...\WINDOWS\system32.
Try it with building absolute paths and pass them to PowerShell like so
RunAsAdmin()
strScriptPath = Wscript.ScriptFullName
Set objFSO = CreateObject("Scripting.FileSystemObject")
Set objFile = objFSO.GetFile(strScriptPath )
strScriptFolder = objFSO.GetParentFolderName(objFile)
Set shell = WScript.CreateObject("WScript.shell")
shell.Run("powershell.exe -noexit -executionpolicy bypass -file " & strScriptFolder & "\Source\RemoveWindows10Apps-2.0.ps1"), 1 , True
...

Related

How do I run a .ps1 file via a .vbs file in the same folder?

The following files are in the same folder.
Testing.cmd
Toast notification.ps1
Run [Toast notification].vbs
When I run Toast notification.ps1, it works. However, when I run Run [Toast notification].vbs, the .ps1 file is not run.
The following is the PowerShell script:
[reflection.assembly]::loadwithpartialname("System.Windows.Forms")
[reflection.assembly]::loadwithpartialname("System.Drawing")
# notify.icon type: Information, Warning or Error.
$notify = new-object system.windows.forms.notifyicon
$notify.icon = [System.Drawing.SystemIcons]::Information
$notify.visible = $true
$notify.showballoontip(10,"", "The CPU is hot.",
[system.windows.forms.tooltipicon]::None)
$notify.dispose()
The following is the VBScript:
Path = split(wscript.scriptFullName, wscript.scriptname)(0)
Item1 = Path & "Toast notification.ps1"
Item2 = Path & "Testing.cmd"
Set Action = CreateObject("Wscript.shell")
Action.run ("powershell -executionpolicy bypass -file ""& Item1 &""")
Action.run ("""" & Item2 & ""), 0
When I double-click on the VBScript file, the .ps1 file is not run. The "Path" can be used to run Testing.cmd, but I cannot make it work with a .ps1 file. How can I fix the problem? The following is the Testing.cmd file:
(ncpa.cpl)
When you run the PowerShell script it should look more like this:
Option Explicit
Dim Path : Path = split(wscript.scriptFullName, wscript.scriptname)(0)
Dim Item1 : Item1 = Path & "Toast notification.ps1"
Dim Item2 : Item2 = Path & "Testing.cmd"
Dim Action : Set Action = CreateObject("Wscript.shell")
Action.run "powershell -executionpolicy bypass -file " & chr(34) & Item1 & chr(34), 0, true
Action.run "cmd /c " & chr(34) & Item2 & chr(34), 0, true
Set Action = Nothing
The chr(34) represents quotes in case your path contains spaces. I'd also recommend more error checking, and checking that the files exist etc. The above example hides the PowerShell and CMD windows too...
You don't need 3 files to execute those commands, just, create one vbscript file.
So, refer to this,
You can do something like this :
Option Explicit
Dim Ws,Ret,ByPassPSFile,PSFile
Set Ws = CreateObject("wscript.Shell")
ByPassPSFile = "cmd /c PowerShell.exe -ExecutionPolicy bypass -noprofile -file "
Call WritePSFile("Warning","10","'The CPU is hot !'","'The CPU is hot '","'Warning'","10")
Ret = Ws.run(ByPassPSFile & PSFile,0,True)
Ret = Ws.run("cmd /c ncpa.cpl",0,True)
'------------------------------------------------------------------------------------------------------------
Sub WritePSFile(notifyicon,time,title,text,icon,Timeout)
Const ForWriting = 2
Dim fso,ts,strText
PSFile = Left(Wscript.ScriptFullName, InstrRev(Wscript.ScriptFullName, ".")) & "ps1"
Set fso = CreateObject("Scripting.FileSystemObject")
Set ts = fso.OpenTextFile(PSFile,ForWriting,True)
strText = strText & "[reflection.assembly]::loadwithpartialname('System.Windows.Forms') | Out-Null;" & VbCrlF
strText = strText & "[reflection.assembly]::loadwithpartialname('System.Drawing') | Out-Null;" & VbCrlF
strText = strText & "$notify = new-object system.windows.forms.notifyicon;" & VbCrlF
strText = strText & "$notify.icon = [System.Drawing.SystemIcons]::"& notifyicon &";" & VbCrlF
strText = strText & "$notify.visible = $true;"
strText = strText & "$notify.showballoontip("& time &","& title &","& text &","& icon &");" & VbCrlF
strText = strText & "Start-Sleep -s " & Timeout &";" & VbCrlF
strText = strText & "$notify.Dispose()"
ts.WriteLine strText
End Sub
'-----------------------------------------------------------------------------------------------------------
There's also this nifty trick to start any PowerShell script with a generic batch (CMD). The trick is to name the batch like the PowerShell script, e.g. MyPS_Script.cmd and MyPS_Script.ps1. It even allows to pass arguments to the script via the batch
Batch:
#ECHO OFF
REM *****
REM * Wrapper CMD to start the PowerShell script of the same name
REM * I.e. if the script is named ServiceRestart.ps1, then the
REM * CMD needs to be named ServiceRestart.cmd
REM *****
PowerShell.exe -Command "& '%~dpn0.ps1' %1 %2 %3 %4 %5 %6 %7"
PAUSE
PS Script demonstration
ForEach ($Arg In $Args) {
Write-Host "Arguments from cmd" $Arg
}

Self Elevating Script + Execution Policy

I'm trying to use the following code from th question "PowerShell: Running a command as Administrator" to not only self elevate my script to run automatically in an Administrator-level PowerShell, but also for the Administrator-level PowerShell session to be run with an ExecutionPolicy level of RemoteSigned. I'm assuming that I need to use something like -ExecutionPolicy RemoteSigned in $newProcess.Arguments but am completely lost as to if this is the case, and if it is then what the syntax do I use to create the the multiple arguments?
# Get the ID and security principal of the current user account
$myWindowsID = [System.Security.Principal.WindowsIdentity]::GetCurrent();
$myWindowsPrincipal = New-Object System.Security.Principal.WindowsPrincipal($myWindowsID);
# Get the security principal for the administrator role
$adminRole = [System.Security.Principal.WindowsBuiltInRole]::Administrator;
# Check to see if we are currently running as an administrator
if ($myWindowsPrincipal.IsInRole($adminRole)) {
# We are running as an administrator, so change the title and background colour to indicate this
$Host.UI.RawUI.WindowTitle = $myInvocation.MyCommand.Definition + "(Elevated)";
$Host.UI.RawUI.BackgroundColor = "DarkBlue";
Clear-Host;
} else {
# We are not running as an administrator, so relaunch as administrator
# Create a new process object that starts PowerShell
$newProcess = New-Object System.Diagnostics.ProcessStartInfo "PowerShell";
# Specify the current script path and name as a parameter with added scope and support for scripts with spaces in it's path
$newProcess.Arguments = "& '" + $script:MyInvocation.MyCommand.Path + "'"
# Indicate that the process should be elevated
$newProcess.Verb = "runas";
# Start the new process
[System.Diagnostics.Process]::Start($newProcess);
# Exit from the current, unelevated, process
Exit;
}
# Run your code that needs to be elevated here...
Write-Host -NoNewLine "Press any key to continue...";
$null = $Host.UI.RawUI.ReadKey("NoEcho,IncludeKeyDown");
$newProcess.Arguments is indeed where you add the relevant parameters. However, you may want to run the script via the parameter -File instead of using the call operator (&) in an implicit -Command parameter.
$newProcess = New-Object Diagnostics.ProcessStartInfo 'powershell.exe'
$newProcess.Arguments = '-ExecutionPolicy RemoteSigned -File "' +
$script:MyInvocation.MyCommand.Path + '"'
$newProcess.Verb = 'runas'
[Diagnostics.Process]::Start($newProcess)

Powershell write to vbscript console(cscript.exe)

I have a requirement of running powershell script in silent mode through vbscript(cscript.exe).
Basic steps for script are as follow.
vbscript
WScript.StdOut.WriteLine "Welcome..."
WScript.StdOut.WriteLine "First Step..."
WScript.Sleep Int(2000)
Set objShell = CreateObject("Wscript.Shell"): objShell.Run "powershell -nologo -file D:\basic\child.ps1" ,0,true
WScript.StdOut.WriteLine "Script Completed."
WScript.Sleep Int(5000)
powershell script
Write-Host "Some Text Printed"
Start-Sleep -s 2
Atthis point, I like the powershell script to write to vbscript(cscript.exe) console.
I am running vb script as follow.
cscript d:\basic\script.vbs
Is there any work around for this requirement.
As explained in the answer to the question that I linked as a possible duplicate, you can do something like this:
WScript.StdOut.WriteLine "Welcome..."
WScript.StdOut.WriteLine "First Step..."
WScript.Sleep Int(2000)
res = getCommandOutput("powershell -nologo -file D:\basic\child.ps1")
WScript.StdOut.Write res
WScript.StdOut.WriteLine "Script Completed."
WScript.Sleep Int(5000)
Function getCommandOutput(theCommand)
Dim objShell, objCmdExec
Set objShell = CreateObject("WScript.Shell")
Set objCmdExec = objshell.exec(thecommand)
getCommandOutput = objCmdExec.StdOut.ReadAll
end Function

Always on top batch script

I have created a simple batch script to pseudo-lock a computer using the following code:
#ECHO OFF & setlocal ENABLEDELAYEDEXPANSION
setlocal EnableDelayedExpansion
color a
TITLE Lock
if not "%1" == "max" (
powershell -command "& { $x = New-Object -ComObject Shell.Application; $x.minimizeall() }"
start /MAX cmd /c %0 max & exit/b
)
:Lock
echo Please enter a password to lock your computer . . .
powershell -Command $pword = read-host "Enter password" -AsSecureString ; $BSTR=[System.Runtime.InteropServices.Marshal]::SecureStringToBSTR($pword) ; [System.Runtime.InteropServices.Marshal]::PtrToStringAuto($BSTR) > EOFlock.txt & set /p Pass1=<EOFlock.txt & del EOFlock.txt
TITLE Lock
taskkill /IM explorer.exe /F >nul
cls
echo Please type the password to unlock the computer . . .
:Locked
set /p Pass2=
:Unlock
if !Pass1! == !Pass2! (goto End)
goto Locked
:End
start explorer.exe
echo This Computer is unlocked.
I want this window to stay on top, and preferably be unclosable until it has reached the end of the file. However, I did not find a way to do this yet.
You can call into PowerShell which in turn can call into the WinAPI... at least on Windows 8+ (7 might work too, previous versions probably not).
It's relatively straightforward:
Call PowerShell
Tell it to run independent of context
Use SetWindowPos to bring a window to the front
Use GetConsoleWindow to find out which window to act on
It all fits pretty neatly into a single command:
#powershell -ExecutionPolicy UnRestricted -Command "(Add-Type -memberDefinition \"[DllImport(\"\"user32.dll\"\")] public static extern bool SetWindowPos(IntPtr hWnd, IntPtr hWndInsertAfter, int x,int y,int cx, int xy, uint flagsw);\" -name \"Win32SetWindowPos\" -passThru )::SetWindowPos((Add-Type -memberDefinition \"[DllImport(\"\"Kernel32.dll\"\")] public static extern IntPtr GetConsoleWindow();\" -name \"Win32GetConsoleWindow\" -passThru )::GetConsoleWindow(),-1,0,0,0,0,67)"

vbscript using Wscript to run a powershell script - need the return from powershell

We are using vbscript in a Classic ASP page and in that vbscript I am calling Powershell using Wscript. I would like to check the return as it is meant to tell me whether the Powershell finished successfully of not. I have a return value in the Powershell script. I've tried both objShell.Run and objShell.Exec and neither one is allowing the Powershell return value through to my ASP page.
My question: how can I get the return values from Powershell?
VBScript follows:
'call PowerShell script with filename and printername and scriptname
strScript = Application("EnvSvcsPSScript")
Set objShell = CreateObject("Wscript.Shell")
dim strCommand
strCommand = "powershell.exe -file " & strScript & " " & strFileName & " " & strPrinterName
Set strPSReturn = objShell.Run(strCommand, 0, true)
response.Write("return from shell: " & strPSReturn.StdOut.ReadAll & "<br>")
response.Write("return from shell: " & strPSReturn.StdErr.ReadAll & "<br>")
Powershell script:
$FileName = $args[0]
$PrinterName = $args[1]
$strReturn = "0^Successful"
"Filename: " + $FileName
"Printer: " + $PrinterName
try
{
get-content $FileName | out-printer -name $PrinterName
[gc]::collect()
[gc]::WaitForPendingFinalizers()
}
catch
{
$strReturn = "1^Error attempting to print report."
}
finally
{
}
return $strReturn
THANK YOU!
You can check whether your PowerShell script succeeded. Check out this example.
Powershell script:
$exitcode = 0
try
{
# Do some stuff here
}
catch
{
# Deal with errors here
$exitcode = 1
}
finally
{
# Final work here
exit $exitcode
}
VB Script:
Dim oShell
Set oShell = WScript.CreateObject ("WScript.Shell")
Dim ret
ret = oShell.Run("powershell.exe -ep bypass .\check.ps1", 0, true)
WScript.Echo ret
Set oShell = Nothing
Now if you run the VB script you'll get 0 if the PowerShell script succeeded and 1 otherwise.
However this approach won't let you get exit codes other than 0 or 1.