154

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 :)

Peter Mortensen
  • 28,342
  • 21
  • 95
  • 123
Thomas Bratt
  • 40,822
  • 34
  • 113
  • 133
  • Checkout this question if you are interested in learning: http://stackoverflow.com/questions/573623/powershell-vs-unix-shells – Thomas Bratt Nov 26 '09 at 12:21
  • This solution works for Task Scheduler as well: https://stackoverflow.com/a/51007810/571591 – Hilydrow May 15 '20 at 05:25

18 Answers18

149

You can either run it like this (but this shows a windows 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 source and exe file 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.

stej
  • 26,575
  • 11
  • 65
  • 98
  • 8
    I compiled the PsRun, however, if I add it to the scheduled task, it also flashes a window... – Ciantic Dec 12 '15 at 17:33
  • 4
    Same here, also does not work because the window still pops up to run the script. It exits quickly but we're trying to run this in the background without interruption. – Nathan McKaskle Aug 23 '16 at 15:30
  • 2
    Yes, that's why there is "but this shows a windows for a while" in the response. – stej Aug 24 '16 at 06:04
  • 29
    @ThomasBratt For scheduled tasks, I've found PowerShell scripts run silently without `-WindowStyle Hidden` if the task is set to `Run whether user is logged on or not` on the General tab under "Security Options" section. – adam Dec 02 '16 at 14:26
  • This is a nice option, but be aware that if you have a "wait for input" line at the end of your script, the window being hidden will make you unable to complete that wait line, causing the process to stay around forever. So if you know you're going to sometimes be adding the "wait for input" line after the tasks are done (for inspection/debugging), the other approaches (@AndyLowry @gavraham) are better as you can easily "re-show" the console window at that point. (just call `ShowWindow(..., 1)`) – Venryx Dec 08 '18 at 02:23
  • 2
    Using User Account to SYSTEM also avoids a window being displayed. – Jon Eldridge Dec 10 '18 at 16:43
  • 3
    -windowstyle hidden doesn't completely work. The window will at least flash. – js2010 Apr 20 '20 at 13:52
  • PowerShell.exe -windowstyle hidden -command yourscript.ps1 I use this as scheduled task to backup some folder on my machine so it does not jump into my face when it works – McVitas Jul 21 '20 at 15:02
  • 1
    For those interested: The powershell.exe executable has so far always been linked to use the character-mode (i.e. console) subsystem, so even if PowerShell was to hide its console window at the earliest possible time, a console window would always show from the start of the PowerShell process until it hides it. I only see two ways around that: either convince Microsoft to release, along with powershell.exe & powershell_ise.exe, an official, GUI-linked, and console-optional build (unlikely); or you/someone to fork the PowerShell open-source project and create a patch to make such build possible – johnwait Aug 19 '20 at 04:36
  • 1
    @johnwait: See [GitHub issue #3028](https://github.com/PowerShell/PowerShell/issues/3028). – mklement0 Nov 18 '20 at 23:30
  • You can at least get around the window flashing with calling cmd with `cmd /c start /min "" powershell -WindowStyle Hidden -ExecutionPolicy Bypass -File "C:\Users\username\Desktop\test.ps1"`. – Ste Apr 28 '21 at 12:03
44

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.

Yusha
  • 1,066
  • 2
  • 10
  • 26
  • 4
    Best solution without depending on a third-party extension, executable, or wrapper script. – invert Nov 29 '18 at 05:33
  • 2
    Note that this option requires the user to have the "Log on as Batch Job" privilege – gb96 Sep 11 '19 at 02:28
  • 3
    This solution makes my script [not to run properly](https://superuser.com/q/1563600/149754). – Martin Argerami Jun 24 '20 at 20:31
  • Not a solution if your script involves displaying any kind of gui. – letmaik Nov 14 '20 at 08:31
  • This solution will prevent you from displaying notifications to the current user. Hiding the code does not mean that it will be useful at all for the person who is working now. – Garric Dec 21 '20 at 17:50
17

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/

(Via this forum thread.)

ax.
  • 53,672
  • 7
  • 72
  • 66
14

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)
Andy Lowry
  • 725
  • 6
  • 11
12

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.

Adam Taylor
  • 4,313
  • 1
  • 34
  • 36
  • 1
    In most cases, running Powershell.exe in the logged on users context will either show a full window or flash briefly if you use -windowstyle hidden. To totally remove window you can do one of two things: 1: Run in a different user's context such as admin account (won't display any windows to the logged on user). Or 2: Use a vbscript with objshell.run with a hidden window flag to launch cmd.exe /c powershel.exe -file c:\script.ps1. When powershell is called from cmd it will run in the existing cmd window which is already hidden by wscript.exe //b /nologo c:\launcher.vbs. – Iconiu Mar 26 '19 at 21:59
  • wow, Suddenly. I did not even notice your answer at first. I answered with a very similar script. It's nice to see a knowledgeable person who gives a real answer. – Garric Dec 15 '19 at 16:16
  • 1
    This should be the accepted answer, it is the one method that seems to work in every situation (including inside the task scheduler, which was my problem). – Martin Argerami Jun 24 '20 at 20:40
  • Caveat: The exit code from the PowerShell script gets lost here and is always 0. – letmaik Nov 06 '20 at 21:17
7

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")
Garric
  • 195
  • 1
  • 5
  • It works as "Target:" in a link as well. I wish this answer was higher up on the page, would have saved me a lot of time. Thank you! – FHC Apr 24 '21 at 14:50
6

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
};
Vincent K
  • 1,248
  • 10
  • 18
jmc
  • 883
  • 8
  • 19
6

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.

gavraham
  • 109
  • 1
  • 3
  • 1
    For me the console still displays for about a second. – thdoan May 02 '20 at 03:19
  • This worked wonderfully for me when scheduling a powershell in a scheduled task created by a GPO. For kicks, I combined with the "-windowstyle hidden" option. No pop up window at all. – Chris Apr 01 '21 at 12:34
3

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"
js2010
  • 13,551
  • 2
  • 28
  • 40
2

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 :)

Chris
  • 514
  • 2
  • 11
  • 1
    This is the best answer if you want to run powershell scripts using `MessageBox` without **any** flash of the window on startup (requiring an EXE compiled as a Winexe, not a console app, and requiring Task Scheduler to be set to "Run only when user is logged on" so dialogs show in the current desktop session.) Thanks for implementing this, powershellw.exe has been on my wishlist for years! – Carl Walsh Jan 11 '19 at 16:48
  • PS: i have included the solution for "waiting for input" a while ago (as well as a couple of bugfixes)! – Chris Jul 12 '19 at 10:25
  • @CarlWalsh i assume you mean that its not an console app, but a win forms app instead, which is correct. just it doesnt include any windows. but the project type should be defined in the csproj file and you should not need to set a specific output type after opening it with visual studio – Chris Jul 12 '19 at 10:28
2

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.

make sure to upvote if this worked for you!

neobihli
  • 21
  • 1
2

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.
Leathan
  • 405
  • 3
  • 6
  • Why can't you just use the .bat file in scheduled task? Why do you need to use .vbs to call .bat? I just tested using the .BAT file in scheduled task and it works fine without any popups – shadowz1337 Apr 22 '21 at 22:29
1

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.

1

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.ps1'; -Arg1 'Hello' -Arg2 ' World'"

3. Call a file with a function and arguments

cmd /c start /min "" powershell -WindowStyle Hidden -ExecutionPolicy Bypass -Command ". 'C:\Users\username\Desktop\test.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.

enter image description here

Ste
  • 752
  • 1
  • 5
  • 18
0
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
Garric
  • 195
  • 1
  • 5
  • Interesting method, but how can you capture the output from PS (StdOut) into a variable to use in the vbs script without using a temporary file? – Safwan Mar 13 '21 at 10:59
  • As I recall, This is my an extremely simplified method presented here in my other comment, which returns StdOut result. You should not demand from this simple code that for which it is not intended. – Garric Mar 14 '21 at 15:30
  • I went through the longer code in your other comment, it does capture StdOut but it also relaunches the script in a hidden console, effectively hiding a lot of other things, I was simply looking for a way to just hide the PS window that is started by the script, but thanks anyway for your replay, cheers. – Safwan Mar 15 '21 at 19:05
0

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
Garric
  • 195
  • 1
  • 5
0
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.

Suraj Rao
  • 28,186
  • 10
  • 88
  • 94
Jehson
  • 1
-1

All these solutions suck and are unnecessary complicated. Just create a .BAT file and do this:

powershell.exe -ExecutionPolicy ByPass -WindowStyle Hidden -File C:\Users\blah\example.ps1

And then add that BAT file into your Task Scheduler and done. No popups.

shadowz1337
  • 691
  • 1
  • 8
  • 15