From Light/Dark Theme Switching in Windows 11 – Snippets, I had a PowerShell script (*.ps1
) that was scheduled (in Task Scheduler) to run periodically. One thing I noticed was that there was a brief pop-up of a Terminal window when the script ran. It would pop-up, seemingly blank, then quickly closed itself.
In some cases, these brief pop-ups are just annoying. In other cases, like when I’m gaming full screen, these are just unacceptable because they also steal the focus momentarily.
Unfortunately, none of the following parameters passed to powershell.exe
work:
-NonInteractive
— this just didn’t do anything-WindowStyle Hidden
— this caused the windows to “animate” as if it were minimized (same as-WindowStyle Minimized
).
After some searching, and with Copilot’s help — sorta, I found a way to get around that using a Visual Basic script that in turn runs powershell.exe
.
VBScript to The Rescue
So apparently, Task Scheduler can be scheduled to run a Visual Basic script (*.vbs
) with wscript.exe
. And wscript.exe
, unlike powershell.exe
, can be invoked silently without a disruptive pop-up terminal window.
wscript.exe vs. cscript.exe
There are two ways to run a VBScript: cscript.exe
and wscript.exe
. Despite the documentation, using cscript.exe
to run the script (even with “/b /nologo
“) will also trigger a flickering pop-up window because it has to do that in order to show potential “console outputs” of the script.
Therefore, in the case when we want to run a script silently, use wscript.exe
(and don’t open any GUI dialogs in the script). It does mean that debugging may get tricky if we want to print something out. The easiest way is to temporarily use cscript.exe
to run the script during developing/debugging (to be able to print to console and see the output) but to use wscript.exe
for the final running (to get the silent running behavior).
Here’s the simple wrapper VBScript code to run the PowerShell script silently:
Dim shell
Set shell = CreateObject("WScript.Shell")
shell.Run "powershell -ExecutionPolicy Bypass -File ""C:\projects\myscript.ps1""", 0, False
- The first parameter is the command to run in the shell, including the parameters passed to the PowerShell script. Note the use of
""
to escape the"
character in the string. - The second parameter is the KEY here: 0 means to hide the window. THIS IS THE REASON we are doing all this.
- The last parameter indicates whether to wait for the PowerShell script to finish. Passing False here is saying don’t wait for the script to complete (i.e. run the script asynchronously).
To get the result I want, then, I just create a VBScript file next to the PowerShell script, modify the VBScript to run my PowerShell script, and modify the Task Scheduler to now run my VBScript instead of the PowerShell script.
Pass-through Parameters
The problem with someone like me is that, from experience, I know that the VBScript above works but can be modified to be more reusable.
And that is the bane of the somewhat mature programmer. I miss the days when I would’ve just left things as they are above and go live my life. Unfortunately, as tiresome as this may sound, we should modify the VBScript so that it can:
- run any PowerShell script instead of hard coding the PowerShell script
- accept and pass parameters through to the PowerShell script
VBScript Arguments
So now we need to add capability to the VBScript to accept some parameters to become more reusable:
- the first parameter would be the path to the PowerShell script to run
- additional parameters to pass to the PowerShell script
Fortunately, I now can use Copilot to generate this. Here’s the prompt:
Generate a VBScript that:
- Accepts a variable number of parameters
- The first parameter is the full path to a PowerShell script to run
- The remaining parameters are passed to the PowerShell script
- Run the PowerShell script provided with the parameters
Here’s the script generated:
' VBScript: RunPowerShellWithArgs.vbs
Option Explicit
Dim shell, args, psScript, psArgs, i
Set shell = CreateObject("WScript.Shell")
Set args = WScript.Arguments
If args.Count < 1 Then
WScript.Echo "Usage: cscript RunPowerShellWithArgs.vbs <PowerShellScriptPath> [arg1 arg2 ...]"
WScript.Quit 1
End If
' First argument is the PowerShell script path
psScript = args(0)
' Build the remaining arguments string
psArgs = ""
For i = 1 To args.Count - 1
psArgs = psArgs & " """ & args(i) & """"
Next
' Run PowerShell with passed arguments
shell.Run "powershell.exe -ExecutionPolicy Bypass -File """ & psScript & """" & psArgs, 0, True
Amazingly, that generated script RunPowerShellWithArgs.vbs
works for the most part. I just needed to tweak the usage message to use wscript
and pass False
as that last parameter to shell.Run
. Now I can use that VBScript to run any other PowerShell script with different parameters as needed.
Yes. Gripe all you want about AI. It was certainly faster to have Copilot generate that last script than to struggle to learn (or remember) Visual Basic.