How is it possible to run a PowerShell script without displaying a window or any other sign to the user?
In other words, the script should run quietly in the background without any sign to the user.
Extra credit for an answer that does not use third party components :)
You can either run it like this (but this shows a window for a while):
PowerShell.exe -WindowStyle hidden { your script.. }
Or you use a helper file I created to avoid the window called PsRun.exe that does exactly that. You can download the source and exe file from Run scheduled tasks with WinForm GUI in PowerShell. I use it for scheduled tasks.
Edited: as Marco noted this -WindowStyle parameter is available only for V2 and above.
I was having this same issue. I found out if you go to the Task in Task Scheduler that is running the powershell.exe script, you can click "Run whether user is logged on or not" and that will never show the powershell window when the task runs.
You can use the PowerShell Community Extensions and do this:
start-process PowerShell.exe -arg $pwd\foo.ps1 -WindowStyle Hidden
You can also do this with VBScript: http://blog.sapien.com/index.php/2006/12/26/more-fun-with-scheduled-powershell/
Schedule Hidden PowerShell Tasks (Internet Archive)
More fun with scheduled PowerShell (Internet Archive)
(Via this forum thread.)
The answer with -WindowStyle Hidden is great but the windows will still flash.
I've never seen a window flash when calling it via cmd /c start /min "".
Your machine or setup may differ but it works well for me.
1. Call a file
cmd /c start /min "" powershell -WindowStyle Hidden -ExecutionPolicy Bypass -File "C:\Users\username\Desktop\test.ps1"
2. Call a file with arguments
cmd /c start /min "" powershell -WindowStyle Hidden -ExecutionPolicy Bypass -Command ". 'C:\Users\username\Desktop\test me.ps1' -Arg1 'Hello' -Arg2 'World'"ps1'; -Arg1 'Hello' -Arg2 ' World'"
Powershell content for 2. Call a file with arguments is:
Param
(
[Parameter(Mandatory = $true, HelpMessage = 'The 1st test string parameter.')]
[String]$Arg1,
[Parameter(Mandatory = $true, HelpMessage = 'The 2nd test string parameter.')]
[String]$Arg2
)
Write-Host $Arg1
Write-Host $Arg2
3. Call a file with a function and arguments
cmd /c start /min "" powershell -WindowStyle Hidden -ExecutionPolicy Bypass -Command ". 'C:\Users\username\Desktop\test me.ps1'; Get-Test -stringTest 'Hello World'"
Powershell content for 3. Call a file with a function and arguments is:
function Get-Test() {
[cmdletbinding()]
Param
(
[Parameter(Mandatory = $true, HelpMessage = 'The test string.')]
[String]$stringTest
)
Write-Host $stringTest
return
}
In case you need to run this in Task Scheduler then call %comspec% as the Program/Script and then code for calling the file above as the argument.
Note: All examples work when the PS1 file has spaces in its path.
Here's an approach that that doesn't require command line args or a separate launcher. It's not completely invisible because a window does show momentarily at startup. But it then quickly vanishes. Where that's OK, this is, I think, the easiest approach if you want to launch your script by double-clicking in explorer, or via a Start menu shortcut (including, of course the Startup submenu). And I like that it's part of the code of the script itself, not something external.
Put this at the front of your script:
$t = '[DllImport("user32.dll")] public static extern bool ShowWindow(int handle, int state);'
add-type -name win -member $t -namespace native
[native.win]::ShowWindow(([System.Diagnostics.Process]::GetCurrentProcess() | Get-Process).MainWindowHandle, 0)
Here's a one-liner:
mshta vbscript:Execute("CreateObject(""Wscript.Shell"").Run ""powershell -NoLogo -Command """"& 'C:\Example Path That Has Spaces\My Script.ps1'"""""", 0 : window.close")
Although it's possible for this to flash a window very briefly, that should be a rare occurrence.
ps1 hidden from the Task Scheduler and shortcut too
mshta vbscript:Execute("CreateObject(""WScript.Shell"").Run ""powershell -ExecutionPolicy Bypass & 'C:\PATH\NAME.ps1'"", 0:close")
I think that the best way to hide the console screen of the PowerShell when your are running a background scripts is this code ("Bluecakes" answer).
I add this code in the beginning of all my PowerShell scripts that I need to run in background.
# .Net methods for hiding/showing the console in the background
Add-Type -Name Window -Namespace Console -MemberDefinition '
[DllImport("Kernel32.dll")]
public static extern IntPtr GetConsoleWindow();
[DllImport("user32.dll")]
public static extern bool ShowWindow(IntPtr hWnd, Int32 nCmdShow);
'
function Hide-Console
{
$consolePtr = [Console.Window]::GetConsoleWindow()
#0 hide
[Console.Window]::ShowWindow($consolePtr, 0)
}
Hide-Console
If this answer was help you, please vote to "Bluecakes" in his answer in this post.
I was having this problem when running from c#, on Windows 7, the "Interactive Services Detection" service was popping up when running a hidden powershell window as the SYSTEM account.
Using the "CreateNoWindow" parameter prevented the ISD service popping up it's warning.
process.StartInfo = new ProcessStartInfo("powershell.exe",
String.Format(#" -NoProfile -ExecutionPolicy unrestricted -encodedCommand ""{0}""",encodedCommand))
{
WorkingDirectory = executablePath,
UseShellExecute = false,
CreateNoWindow = true
};
Here's a fun demo of controlling the various states of the console, including minimize and hidden.
Add-Type -Name ConsoleUtils -Namespace WPIA -MemberDefinition #'
[DllImport("Kernel32.dll")]
public static extern IntPtr GetConsoleWindow();
[DllImport("user32.dll")]
public static extern bool ShowWindow(IntPtr hWnd, Int32 nCmdShow);
'#
$ConsoleMode = #{
HIDDEN = 0;
NORMAL = 1;
MINIMIZED = 2;
MAXIMIZED = 3;
SHOW = 5
RESTORE = 9
}
$hWnd = [WPIA.ConsoleUtils]::GetConsoleWindow()
$a = [WPIA.ConsoleUtils]::ShowWindow($hWnd, $ConsoleMode.MAXIMIZED)
"maximized $a"
Start-Sleep 2
$a = [WPIA.ConsoleUtils]::ShowWindow($hWnd, $ConsoleMode.NORMAL)
"normal $a"
Start-Sleep 2
$a = [WPIA.ConsoleUtils]::ShowWindow($hWnd, $ConsoleMode.MINIMIZED)
"minimized $a"
Start-Sleep 2
$a = [WPIA.ConsoleUtils]::ShowWindow($hWnd, $ConsoleMode.RESTORE)
"restore $a"
Start-Sleep 2
$a = [WPIA.ConsoleUtils]::ShowWindow($hWnd, $ConsoleMode.HIDDEN)
"hidden $a"
Start-Sleep 2
$a = [WPIA.ConsoleUtils]::ShowWindow($hWnd, $ConsoleMode.SHOW)
"show $a"
When you scheduled task, just select "Run whether user is logged on or not" under the "General" tab.
Alternate way is to let the task run as another user.
Create a shortcut that calls the PowerShell script and set the Run option to Minimized. This will prevent a window from flashing although you will still get a momentary blip of the script running on the Task Bar.
For easy command line usage, there is a simple wrapper app:
https://github.com/stax76/run-hidden
Example command line:
run-hidden powershell -command calc.exe
I got really tired of going through answers only to find it did not work as expected.
Solution
Make a vbs script to run a hidden batch file which launches the powershell script. Seems silly to make 3 files for this task but atleast the total size is less than 2KB and it runs perfect from tasker or manually (you dont see anything).
scriptName.vbs
Set WinScriptHost = CreateObject("WScript.Shell")
WinScriptHost.Run Chr(34) & "C:\Users\leathan\Documents\scriptName.bat" & Chr(34), 0
Set WinScriptHost = Nothing
scriptName.bat
powershell.exe -ExecutionPolicy Bypass C:\Users\leathan\Documents\scriptName.ps1
scriptName.ps1
Your magical code here.
I have created a small tool passing the call to any console tool you want to start windowless through to the original file:
https://github.com/Vittel/RunHiddenConsole
After compiling just rename the executable to "<targetExecutableName>w.exe" (append a "w"), and put it next to the original executable.
You can then call e.G. powershellw.exe with the usual parameters and it wont pop up a window.
If someone has an idea how to check whether the created process is waiting for input, ill be happy to include your solution :)
Wait until Powershell is executed and get the result in vbs
This is an improved version of the Omegastripes code Hide command prompt window when using Exec()
Splits the confused responses from cmd.exe into an array instead of putting everything into a hard-to-parse string.
In addition, if an error occurs during the execution of cmd.exe, a message about its occurrence will become known in vbs.
Option Explicit
Sub RunCScriptHidden()
strSignature = Left(CreateObject("Scriptlet.TypeLib").Guid, 38)
GetObject("new:{C08AFD90-F2A1-11D1-8455-00A0C91F3880}").putProperty strSignature, Me
objShell.Run ("""" & Replace(LCase(WScript.FullName), "wscript", "cscript") & """ //nologo """ & WScript.ScriptFullName & """ ""/signature:" & strSignature & """"), 0, True
End Sub
Sub WshShellExecCmd()
For Each objWnd In CreateObject("Shell.Application").Windows
If IsObject(objWnd.getProperty(WScript.Arguments.Named("signature"))) Then Exit For
Next
Set objParent = objWnd.getProperty(WScript.Arguments.Named("signature"))
objWnd.Quit
'objParent.strRes = CreateObject("WScript.Shell").Exec(objParent.strCmd).StdOut.ReadAll() 'simple solution
Set exec = CreateObject("WScript.Shell").Exec(objParent.strCmd)
While exec.Status = WshRunning
WScript.Sleep 20
Wend
Dim err
If exec.ExitCode = WshFailed Then
err = exec.StdErr.ReadAll
Else
output = Split(exec.StdOut.ReadAll,Chr(10))
End If
If err="" Then
objParent.strRes = output(UBound(output)-1) 'array of results, you can: output(0) Join(output) - Usually needed is the last
Else
objParent.wowError = err
End If
WScript.Quit
End Sub
Const WshRunning = 0,WshFailed = 1:Dim i,name,objShell
Dim strCmd, strRes, objWnd, objParent, strSignature, wowError, output, exec
Set objShell = WScript.CreateObject("WScript.Shell"):wowError=False
strCmd = "C:\Windows\System32\WindowsPowerShell\v1.0\powershell.exe -ExecutionPolicy Bypass Write-Host Hello-World."
If WScript.Arguments.Named.Exists("signature") Then WshShellExecCmd
RunCScriptHidden
If wowError=False Then
objShell.popup(strRes)
Else
objShell.popup("Error=" & wowError)
End If
powershell.exe -windowstyle hidden -noexit -ExecutionPolicy Bypass -File <path_to_file>
then set the run: Minimized
should work as expected without added code for hidden window flash
just slightly more delayed execution.
Here is a working solution in windows 10 that does not include any third-party components. It works by wrapping the PowerShell script into VBScript.
Step 1: we need to change some windows features to allow VBScript to run PowerShell and to open .ps1 files with PowerShell by default.
-go to run and type "regedit". Click on ok and then allow it to run.
-paste this path "HKEY_CLASSES_ROOT\Microsoft.PowerShellScript.1\Shell" and press enter.
-now open the entry on the right and change the value to 0.
-open PowerShell as an administrator and type "Set-ExecutionPolicy -ExecutionPolicy RemoteSigned", press enter and confirm the change with "y" and then enter.
Step 2: Now we can start wrapping our script.
-save your Powershell script as a .ps1 file.
-create a new text document and paste this script.
Dim objShell,objFSO,objFile
Set objShell=CreateObject("WScript.Shell")
Set objFSO=CreateObject("Scripting.FileSystemObject")
'enter the path for your PowerShell Script
strPath="c:\your script path\script.ps1"
'verify file exists
If objFSO.FileExists(strPath) Then
'return short path name
set objFile=objFSO.GetFile(strPath)
strCMD="powershell -nologo -command " & Chr(34) & "&{" &_
objFile.ShortPath & "}" & Chr(34)
'Uncomment next line for debugging
'WScript.Echo strCMD
'use 0 to hide window
objShell.Run strCMD,0
Else
'Display error message
WScript.Echo "Failed to find " & strPath
WScript.Quit
End If
-now change the file path to the location of your .ps1 script and save the text document.
-Now right-click on the file and go to rename. Then change the filename extension to .vbs and press enter and then click ok.
DONE! If you now open the .vbs you should see no console window while your script is running in the background.
c="powershell.exe -ExecutionPolicy Bypass (New-Object -ComObject Wscript.Shell).popup('Hello World.',0,'ОК',64)"
s=Left(CreateObject("Scriptlet.TypeLib").Guid,38)
GetObject("new:{C08AFD90-F2A1-11D1-8455-00A0C91F3880}").putProperty s,Me
WScript.CreateObject("WScript.Shell").Run c,0,false
Out of all the solutions I've tried, this is by far the best and easiest to set up. Download hiddenw.exe from here - https://github.com/SeidChr/RunHiddenConsole/releases
Let's say you want to run Powershell v5 consoleless. Simply rename hiddenw.exe to powershellw.exe. If you want to do this for cmd, then rename to cmdw.exe. If you want to do it for Powershell v7 (pwsh), then rename to pwshw.exe. You can create multiple copies of hiddenw.exe and just rename to the actual process with the letter w at the end. Then, simply add the process to your system environmental PATH, so you can call it from anywhere. Or just copy to C:\Windows. Then, just call it, like this:
powershellw .\example.ps1
I found compiling to exe was the easiest way to achieve this. Theres a number of ways to compile a script, but you can try ISE Steroids
Open "Windows PowerShell ISE", install and run ISESteroids:
Install-Module -Name "ISESteroids" -Scope CurrentUser -Repository PSGallery -Force
Start-Steroids
Then go to Tools->Turn code into EXE, select 'Hide Console Window', and then create the application.
You can run this directly from task scheduler without the need for wrappers or 3rd party apps.
What I do is transform the .ps1 file into an invisible .exe file using an awesome app called Ps1 To Exe which you can download here : https://www.majorgeeks.com/files/details/ps1_to_exe.html
Maybe this helps (although I hope after 12 years you have found a suitable solution... 🙂)
In other words, the script should run quietly in the background without any sign to the user.
Extra credit for an answer that does not use third party components :)
I found a way to do this by compiling a PowerShell script to a Windows executable. Third party modules are required to build the executable but not to run it. My end goal was to compile a one line PowerShell script that ejects a DVD on my system:
(New-Object -com "WMPlayer.OCX.7").cdromcollection.item(0).eject()
My target system is running Windows 7. The specific WMF update needed varies based on Windows version:
Download and install the WMF 5.1 package
The required PowerShell modules should be applicable to any Windows version. Here are the exact commands I used to install the necessary modules and compile the exe. You'll need to tweak the drive, directory and filename details for your system:
mkdir i:\tmp\wmf
cd i:\tmp\wmf
pkunzip ..\Win7AndW2K8R2-KB3191566-x64.zip
c:\windows\system32\windowspowershell\v1.0\powershell.exe
Set-ExecutionPolicy RemoteSigned
.\Install-WMF5.1.ps1
<click> "Restart Now"
c:\Windows\System32\WindowsPowerShell\v1.0\powershell -version 3.0
[Net.ServicePointManager]::SecurityProtocol = [Net.SecurityProtocolType]::Tls12
Install-PackageProvider -Name NuGet -MinimumVersion 2.8.5.201 -Force
Install-Module -Name ps2exe -RequiredVersion 1.0.5
ps2exe i:\utils\scripts\ejectDVD.ps1 -noConsole
My Windows Batch shall be started by the user without administrator privileges. At some step, it shall call itself with elevated privileges. I have learned that this is possible using the PowerShell's runas feature (batch.bat ⭢ PowerShell ⭢ batch.bat). This works like a charm.
Unfortunately, I am not able to receive the exit code from the elevated batch execution. I always get 1, although there is not any error message. I have no idea at which return the exit code gets lost, 1st (batch back to PowerShell) or 2nd (PowerShell back to batch).
I believe, I have tried all of the plenty suggested answers from similar questions, but apparently I am unable to get it going. I need advice.
MVE which should indicate that the elevated batch returns 0:
#echo off
echo param=%~1
openfiles /local >nul 2>&1
IF %ERRORLEVEL% EQU 0 (
echo elevated, exit 0
pause
exit 0
) ELSE (
echo not elevated. trying to elevate.
powershell start-process -wait -verb runas '%0' -argumentlist /foo
echo powershell returned %errorlevel%.
)
Nota bene (edited to eliminate misunderstanding): while the non-elevated call (by the user) does not require any parameter, the elevated call introduces an additional parameter '/foo'. This makes things worse for me because I did not find a solution to not lose this parameter. However, this appears to be a rather unusual use case.
To solve the argument problem, you could use
powershell start-process -wait -verb runas '%0' -argumentlist '/additional-arg %*'
The exit code problem:
The first problem is the line
echo powershell returned %errorlevel%.
This can't work, because it's inside a code block and %errorlevel% will be expanded even before powershell will be called and therefore it is always 1 - the result of openfiles /local ...
But even with delayed expansion, I got always 0, probably because it's the exitcode of the successful runas, that it was able to start your batch.
You could use a work around and store the exitcode in a temporary file
#echo off
setlocal EnableDelayedExpansion
echo param=%*
openfiles /local >nul 2>&1
IF %ERRORLEVEL% EQU 0 (
echo elevated, exit 13
pause
echo 13 > "%temp%\_exitcode.tmp"
rem *** using 14 here to show that it doesn't be stored in errorlevel
exit 14
) ELSE (
echo not elevated. trying to elevate.
powershell start-process -wait -verb runas '%0' -argumentlist '/additional-arg %*'
set /p _exitcode= < "%temp%\_exitcode.tmp"
del "%temp%\_exitcode.tmp"
echo powershell returned !_exitcode!, lvl !errorlevel!.
)
You aren't putting the PowerShell commands to execute in quotes, and you would do well to use the full path as well as include any arguments to the script. A generic way to invoke this, so it could be copied across scripts, your PowerShell invocation should look like so:
powershell -c "if([bool]'%*'){ Start-Process -Wait -Verb runas '%~dpnx0' -ArgumentList ('%*' -split '\s+') } else { Start-Process -Wait -Verb runas '%~dpnx0' }"
For your needs above, this could be simplified since you know you have arguments passed into the batch file to process:
powershell -c "Start-Process -Wait -Verb runas '%~dpnx0' -ArgumentList '/foo'
%~dpnx0 - Automatic batch variable, this is the full path to the current script, including the script name
%* - Automatic batch variable, this is all arguments passed into the script.
('%*' -split '\s'): This is a PowerShell expression takes the space-delimited %* variable and splits it on continuous whitespace, returning an array. For simplicity this does have a shortcoming in that it will split on spaces in between double quotes, but the regex can be tuned to account for that if needed.
This answer is worth a read for other automatic batch variables you may find use for in the future.
I am trying to make Task Schedule Task so it is completely invisible that a PowerShell script is running so I created a Task on my Win10 machine configured as follows:
Program/Script:
powershell.exe
Add arguments (optional):
-WindowStyle Hidden -command "& {Out-File 'C:\temp\somefile.txt'}" -NonInteractive -NoLogo -NoProfile
When I run this task the powershell command windows pops up for a split second which I don't want.
You can get around this with an 'Application Host' type wrapper. This is a known issue with powershell as a console-based host.
The most convenient way to do this I've found, is to use WScript.exe and run a VBS script that will invoke the process "invisibly", with no console or task bar flicker.
VBS Code:
On Error Resume Next
ReDim args(WScript.Arguments.Count-1)
For i = 0 To WScript.Arguments.Count-1
If InStr(WScript.Arguments(i), " ") > 0 Then
args(i) = Chr(34) & WScript.Arguments(i) & Chr(34)
Else
args(i) = WScript.Arguments(i)
End If
Next
CreateObject("WScript.Shell").Run Join(args, " "), 0, False
Save the above code in a file with extension '.vbs', and place it somewhere it can be run by the client running the task. This may be in a protected fileshare on the network (if you expect the script it invokes to only run while connected to the network), or locally on the client.
Now when you invoke your console-based script (PowerShell, BAT, CScript, etc.), you invoke this VBS script with WScript explicitly WScript.exe. It also pays to throw on the 'Batch Mode' parameter //B which will suppress script errors & prompts - such as if the wrapper file itself can't be found.
At this point, all you need to do is pass powershell & the command you want powershell to run to this launch sequence:
WScript.exe //B "\\Path\To\Launcher.VBS" powershell.exe -ExecutionPolicy ByPass -file "\\Powershell\Script\To\Run"
I had the same problem, it was resolved at the simple way.
When you create a Task on Windows, just set this configuration:
Open Properties dialog;
Then you check Run whether user is logged on or not;
You can check Do not store password to avoid asking for PC password on Task execution;
In Add arguments (optional): just:
-File 'C:\temp\somefile.txt
This spcript will run without popup the prompt.
Solved in this link below:
https://stackoverflow.com/a/50630717/19926325
I have a batch script that calls a Powershell file in administration mode. I found this code a while ago, and it's worked great ever since:
PowerShell -NoProfile -ExecutionPolicy Bypass -Command
"& {Start-Process PowerShell -ArgumentList '-NoProfile -ExecutionPolicy Bypass -File %PSFile%' -Verb RunAs}";
This time though, I called the batch script from another program. This program says the process worked, but it didn't actually do anything. Examining the logs from echo, I can see the batch script is being called, but it's not calling Powershell. I tried running the batch script manually, and it calls PS fine, so something with how the batch script is being called by the other program is messing with how it calls PS.
This in mind, I tried changing the batch script to directly run my .ps1 file, instead of starting a new admin instance of powershell to start it. My new .bat file looked like this:
Powershell -File %PSFILE% -Verb RunAs
Calling this from the other program sucessfully calls my Powershell script, but I get a bunch of errors from the PS script, since it's not an admin PS session like it needs to be.
How can I change my batch script to call Powershell as an admin, without using Powershell to call itself (which doesn't seem to work with the program that needs to run it)?
EDIT: After trying a bunch of tweaks, I've found I don't even need to be in admin mode to do what this script does. However, I still get access denied errors when running it through the program (admin or not). So something about running it from the program is making it need more permissions than when I run the batch script manually.
This is what I do (inside the .bat file):
If the .bat is NOT running as admin
powershell.exe -Command "powershell.exe 'C:\path\to\script.ps1' -Verb runAs"
If the .bat is running as admin
powershell.exe -ExecutionPolicy Bypass -Command "C:\path\to\script.ps1"
You could use a small utility I wrote called elevate32.exe/elevate64.exe.
elevate64 -- C:\Windows\System32\WindowsPowerShell\v1.0\powershell.exe -NoProfile -ExecutionPolicy Bypass -File "d:\path to script\scriptfile.ps1"
elevate32.exe (32-bit version) and elevate64.exe (64-bit version) basically elevate whatever command line you pass to them.
You can get it here (ElevationToolkit1.zip):
http://www.westmesatech.com/misctools.html
An alternative is to use a short WSH script that, when called, provokes elevation. An example is on Aaron Margosis' blog here:
https://blogs.msdn.microsoft.com/aaron_margosis/2007/07/01/scripting-elevation-on-vista/
Script:
// elevate.js -- runs target command line elevated
if (WScript.Arguments.Length >= 1) {
Application = WScript.Arguments(0);
Arguments = "";
for (Index = 1; Index < WScript.Arguments.Length; Index += 1) {
if (Index > 1) {
Arguments += " ";
}
Arguments += WScript.Arguments(Index);
}
new ActiveXObject("Shell.Application").ShellExecute(Application, Arguments, "", "runas");
}
else {
WScript.Echo("Usage:");
WScript.Echo("elevate Application Arguments");
}
The limitations of this approach is that it relies on the WSH command-line parser and can't wait for the program to terminate. These limits may not be a problem in your scenario.
Looks like I was totally off as to the problem source. This was a permissions error on some folders I was editing. The program I was running the scripts through acts as a separate service. I had to add that with modify permissions to the security groups of all the folders I was editing. No elevation required in the scripts, just modifying permissions.