ChatGPT解决这个技术问题 Extra ChatGPT

How to run a PowerShell script without displaying a window?

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

Checkout this question if you are interested in learning: stackoverflow.com/questions/573623/powershell-vs-unix-shells
This solution works for Task Scheduler as well: stackoverflow.com/a/51007810/571591
There are other options, such as a Windows service, if you need to absolutely avoid showing a window.
For anyone interested, the window flashing even with -WindowStyle hidden is a known Windows limitation with CUI applications (it only works as expected with GUI ones -- hence the pythonw.exe / javaw.exe-type solutions implemented by other projects). It is being (rather) actively discussed on GitHub, with several suggestions for potential workarounds / fixes at the PowerShell or OS level. So, it might get patched, "one day".

w
wannaBeTheBestMe

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 compiled the PsRun, however, if I add it to the scheduled task, it also flashes a window...
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.
Yes, that's why there is "but this shows a windows for a while" in the response.
@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.
-windowstyle hidden doesn't completely work. The window will at least flash.
Y
Yusha

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.


Best solution without depending on a third-party extension, executable, or wrapper script.
Note that this option requires the user to have the "Log on as Batch Job" privilege
This solution makes my script not to run properly.
Not a solution if your script involves displaying any kind of gui.
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.
D
Danilo Roascio

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


Is there a NuGet paclage for this so I can run it from Visual Studio?
A
Andy Lowry

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)

Thanks, this was helpful
A
Adam Taylor

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.


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.
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.
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).
Caveat: The exit code from the PowerShell script gets lost here and is always 0.
G
Garric

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

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!
Worked Flawlessly. Just what i was looking for
S
Ste

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.

https://i.stack.imgur.com/RllIS.png


This is interesting, but I actually discovered that you CAN see the window. It just flashes MUCH MUCH quicker than the other times, but if you're paying very close attention, the window still popped up. If you want to see what I'm talking about, then open a windowed window (like explorer) and you'll see it loses focus for a brief moment as the other window gains focus
It's not a perfect workaround but it worked in all of my use cases and I've never seen it flash. It losing focus of File Explorer would happen regardless would it not seeing as calling a PS script would do that anyway?
It can only lose focus on a window if something popped up in front of it (no matter how brief). Of course the focus is restored once it disappears, but the point remains. I did have to look VERY VERY closely to see the flash (it may have only appeared for a few frames) as I couldn't see it if I wasn't looking for it, so it is less of a flash than other (flashy) methods. I ended up using the code from the run-hidden which was actually hidden (no lose focus or anything as well), although I haven't tested the other answers. Your answer was definitely less invasive than other flashes at least
For the "Call a file with arguments" the semicolon seems to be extra as it prevents the script from executing.
@Peter, thanks for pointing that out. I've fixed that and added the ps1 content also for clarity.
g
gavraham

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.


For me the console still displays for about a second.
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.
V
Vincent K

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
};

j
js2010

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"

J
John Stankievich

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.


S
Sandeep Verma

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.


Works +1. Also user run as user "system" works if you need the elevation. Or my answer if you need your user.
Good job soldier !
s
stax76

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

This was actually very helpful, because so many of the other solutions are a bit cumbersome and/or not ideal. This one is FAST. I modified it to run-as admin mode as well since that's what I came here for (no flashing windows + run as admin). github.com/cherryleafroad/run-hidden
This seems not to work when you use "%USERPROFILE%" in one of your arguments.
@jamie I think this is normal behavior! The Process class does not expand env vars and user applications like run-hidden typically don't do this either, I believe.
Uh I see. Thanks for explaining that.
L
Leathan

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.

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
I forgot the reason why I was not using scheduled task, but there are lots of reasons I could think of off the top of my head, none of which are guaranteed to be right as I forgot. I only remember posting my solution because I originally tried the others and they did not work. I do remember scheduled tasks was not what I wanted though maybe because it was too dynamic idk. In fact the answer might be I used the vbs because I could not use scheduled task, anyway its all irrelevant, just submit yours as another answer if its not on here.
@shadowz1337 Figured it out since I needed to do it again. There are various oddities when starting a task hidden from the scheduler that prevent the bat file from actually being hidden. The vbs here specifically fixes this issue, with this you can run a hidden bat as your admin user, else ud have to do system, or run as any user. see superuser.com/questions/478052/… for detailed explanations.
I just use achedule task to call nircmd exec hide to run the .bat file, which then invokes my Powershell script as admin and this is completely hidden to the user from start to end.
Im sure there are other ways, but for me, if I replace the first step it doesnt run hidden, maybe because im not signed on the user im having the script run as? I dont know.
C
Chris

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


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!
PS: i have included the solution for "waiting for input" a while ago (as well as a couple of bugfixes)!
@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
G
Garric

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

N
Nimantha

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.


G
Garric
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

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?
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.
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.
S
Suraj Rao
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.


s
shadowz1337

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


J
JMax

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.