PowerShell icon indicating copy to clipboard operation
PowerShell copied to clipboard

Powershell -WindowStyle Hidden still shows a window briefly

Open Ciantic opened this issue 9 years ago • 117 comments

Steps to reproduce

In Windows Run dialog type this: PowerShell.exe -WindowStyle Hidden -Command ping www.microsoft.com

Expected behavior

There should be no window, right now you can't start powershell without window flashing, making it rather useless e.g. for scheduled tasks.

Note I think this is intended behavior, but it's confusing and new option is probably required. If you search how to run a powershell in scheduled task, the go-to workaround is to do a vbs script of all things! Such as:

Dim shell,command
command = "powershell.exe -nologo -File D:\myscript.ps1"
Set shell = CreateObject("WScript.Shell")
shell.Run command,0

This is not a good thing, powershell needs this feature in the shell itself, scheduled tasks are important feature and having a window flash on scheduled task is a really bad experience.

Actual behavior

It flashes the powershell window briefly.

Environment data

> $PSVersionTable
Name                           Value
----                           -----
PSVersion                      5.1.14393.693
PSEdition                      Desktop
PSCompatibleVersions           {1.0, 2.0, 3.0, 4.0...}
BuildVersion                   10.0.14393.693
CLRVersion                     4.0.30319.42000
WSManStackVersion              3.0
PSRemotingProtocolVersion      2.3
SerializationVersion           1.1.0.1

Ciantic avatar Jan 22 '17 18:01 Ciantic

powershell.exe is a console application. The console window is automatically created by the OS when the process starts. The powershell.exe code that processes -WindowStyle Hidden is therefore executed after the console window is opened hence the flash. To fix this, we would need the equivalent of wscript i.e. a win32 host application instead of a console host application. 

BrucePay avatar Jan 25 '17 19:01 BrucePay

Given there's no code change we can do in powershell.exe to address this, I've changed this issue to a feature request to have a wscript-like host to support this type of scenario

SteveL-MSFT avatar Feb 02 '17 21:02 SteveL-MSFT

http://www.f2ko.de/en/p2e.php and Power Shell Studio has a custom host too

ghost avatar Feb 02 '17 21:02 ghost

You could have a powershellw.exe which would be a GUI application that doesn't show a console window. Same as what javaw.exe and pythonw.exe do.

rednoah avatar Jun 26 '17 18:06 rednoah

Can pwsh.exe get support for this or get pwshw.exe as suggested above? With this pwsh exe being new it seems like a great time to change the behaviour of -windowstyle hidden. Nobody has ever used hidden and thought "yep that's what I wanted, flash some screen for a second".

It makes sense that powershell.exe can't be changed after all this time and its legacy.

Jackbennett avatar Feb 16 '18 12:02 Jackbennett

I would support a community contribution to add pwshw.exe

SteveL-MSFT avatar Feb 21 '18 00:02 SteveL-MSFT

Agree .. this would be consistant with other language executables and solve me having to currently wrap my powershell scripts in vbs scripts.

gtalton avatar Feb 21 '18 00:02 gtalton

Technically we could do like https://github.com/AvaloniaUI/Avalonia/wiki/Hide-console-window-for-self-contained-.NET-Core-application

editbin.exe /subsystem:windows yourapp.exe

But I wonder - if PowerShell Core is portable what is expected behavior on Unix? Can we be unified on all platforms? Related discussion https://github.com/dotnet/cli/issues/296 It also mentions that we could use GUI subsystem.

Maybe @mklement0 have any thoughts?

iSazonov avatar Feb 21 '18 14:02 iSazonov

@iSazonov -WindowStyle isn't supported on non-Windows

SteveL-MSFT avatar Feb 21 '18 21:02 SteveL-MSFT

Yes, I meant - have we the same behavior on Unix by creating a console? Have we scenarios where we don't want to create the console on Unix?

iSazonov avatar Feb 22 '18 03:02 iSazonov

@iSazonov: I haven't really looked into this; the only thing I can tell you, from personal experience, is that invoking pwsh invisibly works fine from utilities such as Alfred 3 and TextExpander

mklement0 avatar Feb 22 '18 04:02 mklement0

What I've been using so far is a shortcut named PS with the Target: C:\Windows\System32\WindowsPowerShell\v1.0\powershell.exe and the option Run: Minimized Like this: C:\Windows\PS Start-Process . The taskbar does flicker but no more console.

brice-ruppen avatar Mar 03 '18 15:03 brice-ruppen

Seems like the -WindowStyle Hidden has to be the first parameter of your command line.

aresowj avatar Aug 07 '18 17:08 aresowj

So, we are starting to support Windows PowerShell now??

Just a friendly reminder, if this is a PowerShell Core issue? Please provide the PowerShell Core version as is required when submitting an issues.

Otherwise, Windows PowerShell need to go thru UserVoice at: https://windowsserver.uservoice.com/forums/301869-powershell

For more information see: https://github.com/PowerShell/PowerShell#windows-powershell-vs-powershell-core

:)

MaximoTrinidad avatar Aug 07 '18 18:08 MaximoTrinidad

@aresowj: While the placement of arguments does matter when calling PowerShell's CLI (given that anything following -Command is interpreted as part of the command to execute), (a) the OP's attempt already places it before -Command and (b) that doesn't prevent the flashing, for the reasons explained in @BrucePay's earlier comment.

@MaximoTrinidad: While this issue may have started out as focused on Windows PowerShell only, it has long since morphed into a PS Core feature request (which also deserves back-porting to Windows PowerShell).

To recap: In order to get fully invisible invocations via the PowerShell CLI:

  • On Windows, a new, separate PowerShell executable (pwshw.exe / powershellw.exe) is needed that is a GUI application, following the model of the python.exe / pythonw.exe pair

  • On macOS and Linux, where the problem doesn't exist, I presume there is no need for a separate executable. For symmetry with Windows, a simple symlink could be implemented that simply points to the regular pwsh executable.

mklement0 avatar Aug 07 '18 23:08 mklement0

Thanks @mklement0! :)

MaximoTrinidad avatar Aug 08 '18 15:08 MaximoTrinidad

Sorry guys, mistook the two Powershells and thanks for your clarification. :)

aresowj avatar Aug 12 '18 00:08 aresowj

What about initially starting Powershell in the back ground with a non visible console, then checking for any console window arguments -WindowStyle and implementing them. But, if none are found it could then start a visible console window.

zero77 avatar Sep 03 '18 09:09 zero77

@zero77 I think we mean https://social.msdn.microsoft.com/Forums/en-US/b7a14400-6d72-4fbf-9927-0966f69ef4a2/how-to-open-console-window-in-windows-apllication?forum=csharplanguage

iSazonov avatar Sep 03 '18 10:09 iSazonov

I created an issue in the windows console team issue repo here: https://github.com/Microsoft/console/issues/249

Jawz84 avatar Sep 03 '18 10:09 Jawz84

@zero77:

pwsh.exe must remain a console-subsystem application to ensure synchronous, standard-streams-connected execution from an existing console window.

We therefore need the previously discussed separate, GUI-subsystem executable, pwshw.exe. If we want that executable to support conditional creation of a(n invariably new) console window, @iSazonov's link is a helpful starting point.

@Jawz84: I don't think the console team can help here:

  • A console-subsystem application such as pwsh.exe invariably creates a new console window, which happens before the application sees any of its arguments.

  • To only way to hide or prevent creation of this console window is to have a GUI-subsystem application as the entry point. In the simplest case this GUI-subsystem application can be a stub that relays arguments to the console application, but starts it hidden.

mklement0 avatar Sep 03 '18 12:09 mklement0

I'll leave the console issue open, see what they think. I see your point, and i know it may be totally impossible. Yet then again, it may be something they would like to consider.

[Edit:] I've got the answer that this is not currently feasable to make amends for in console. A separate pwshw.exe would be the way to go for now.

Jawz84 avatar Sep 03 '18 13:09 Jawz84

Yikes! Open for nearly two years? This is quite an important feature for SysAdmins.

Chiramisudo avatar Sep 06 '18 03:09 Chiramisudo

@Chiramisu Feel free to offer PR - proposal in my comment above.

iSazonov avatar Sep 06 '18 04:09 iSazonov

I concur with this feature request, the default behavior of Powershell when run as a command is befuddling

ibex-are-goats avatar Oct 11 '18 11:10 ibex-are-goats

I start seeing this after applying Windows updates. something changed recently?

LNGU avatar Oct 30 '18 23:10 LNGU

Has anyone looked at what this would require in practice?

I'm looking at the powershell.exe, and it seems rather simple:

https://github.com/PowerShell/PowerShell/blob/master/src/powershell/Program.cs

Now, to make it without console, is it just a darn Project setting like changing Output type to "Windows application" instead of console application?

Ciantic avatar Oct 31 '18 10:10 Ciantic

There's hasn't been a decision to make pwshw.exe and backport powershellw.exe which seems like the only reasonable choice after the console teams feedback above.

This wouldn't be that unusual given C:\windows\System32\taskhostw.exe exists. There seems to be a few items using this pattern in windows searching *w.exe in C:\windows\System32\

Personally I'd have thought just changing the pwsh.exe to fix -windowstyle without backporting to powershell.exe is acceptable since its new but nothing is as simple as it seems.

Jackbennett avatar Oct 31 '18 11:10 Jackbennett

@Ciantic the work should be to replicate powershell-win-core and update the .csproj file so that Assembly is pwshw and OutputType is winexe. Then changes in build.psm1 so that we build both.

SteveL-MSFT avatar Oct 31 '18 17:10 SteveL-MSFT

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 or pwshw.exe with the usual parameters and it won't 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 :) EDIT: found a solution for that problem

SeidChr avatar Nov 17 '18 20:11 SeidChr

Compile a release and I will try it sometime! Really great work Vittel :) 👍 💯

ibex-are-goats avatar Nov 17 '18 20:11 ibex-are-goats

Compile a release and I will try it sometime! Really great work Vittel :) 👍 💯

good idea. did so.

SeidChr avatar Nov 17 '18 21:11 SeidChr

For that specific PowerShell issue (occurred on my scheduled tasks) I had ended up using https://github.com/stbrenner/SilentCMD (also C#), I'll give RunHiddenConsole a try...

C-Duv avatar Nov 17 '18 23:11 C-Duv

@Vittel Thank you for the project! You need to pay attention to input/output/error redirection and perhaps argument escaping.

If we will make new pwshw project we should think about defaults: perhaps -Noprofile should be.

iSazonov avatar Nov 18 '18 12:11 iSazonov

Simply changing OutputType to WinExe isn't sufficient as netcoreapp2x doesn't support this currently. Per https://github.com/dotnet/core-setup/issues/196#issuecomment-394786860 looks like we need to wait for netcoreapp30.

As @iSazonov alluded, simply building as winexe instead of exe will not be a complete solution. Some of the existing parameters allowed by pwsh.exe wouldn't work correctly under pwshw.exe (like -NoExit as a console window will never show up, it's only for automation). So things like -NoProfile by default and other defaults specific to automation vs interactive would make sense to consider.

SteveL-MSFT avatar Nov 18 '18 19:11 SteveL-MSFT

Parameter Status
-File
-Command
-ConfigurationName
-EncodedCommand
-ExecutionPolicy
-InputFormat
-Interactive Remove (Not used)
-NoExit Remove (Not used)
-NoLogo Remove (Not used)
-NonInteractive Remove (By default)
-NoProfile Remove (By default)
-OutputFormat Remove (Not used)
-Version Remove
-WindowStyle Remove (Not used)
-WorkingDirectory

If pwshw.exe is for no-console scenario what exe will be for GUI?

iSazonov avatar Nov 19 '18 03:11 iSazonov

For reference. From https://github.com/dotnet/core-setup/pull/3888:

Ability for an apphost to be renamed.

but I don't found docs how to rename.

iSazonov avatar Nov 19 '18 04:11 iSazonov

Thanks for the helpful parameters table, @iSazonov.

I think -NonInteractive and-WindowStyle can be removed too, as they only have meaning in the context of a console window - which the GUI-subsystem executable by definition won't have.

mklement0 avatar Nov 19 '18 13:11 mklement0

If pwshw.exe is for no-console scenario what exe will be for GUI?

pwshw.exe can do double duty:

  • For automation (run hidden from a scheduled task, for instance)
  • For launching GUI-user-interaction-only scripts without an unwanted console window (e.g., scripts that create WinForms UIs).

mklement0 avatar Nov 19 '18 14:11 mklement0

For launching GUI-user-interaction-only scripts …

Should we keep -WindowStyle for them?

And what about GUI console?

iSazonov avatar Nov 19 '18 14:11 iSazonov

Should we keep -WindowStyle for them?

I don't think that's useful, because there's no telling in advance at what point and through what mechanism a GUI window - if any - will be created, and the code that creates it would have to query PowerShell for its startup parameters somehow in order to respect the value.

What do you mean by GUI console?

mklement0 avatar Nov 19 '18 17:11 mklement0

@iSazonov pwshw by definition won't have an interactive console. -WindowStyle is specifically for the console window. Scripts that leverage WinForms/WPF is independent of the pwshw host.

SteveL-MSFT avatar Nov 19 '18 18:11 SteveL-MSFT

Thanks! Table above was updated.

iSazonov avatar Nov 20 '18 03:11 iSazonov

I'd rather avoid creating a new host for this, and I'd much prefer to solve this with PTYs where they're available.

joeyaiello avatar Mar 18 '19 20:03 joeyaiello

Here's a workaround for now - the only thing that appears is a PowerShell instance in the taskbar that quickly disappears - no more conhost.exe flashing on the screen.

$WshShell = New-Object -ComObject 'WScript.Shell'
$ShortcutPath = Join-Path -Path $ENV:Temp       -ChildPath 'Temp.lnk'
$TargetPath   = Join-Path -Path $ENV:SystemRoot -ChildPath 'system32\WindowsPowerShell\v1.0\powershell.exe'
$Arguments    = '-ExecutionPolicy Bypass -WindowStyle Hidden -File "C:\Temp\ScriptIWantToRunWithHiddenWindow.ps1"'

$Shortcut = $WshShell.CreateShortcut($ShortcutPath)
$Shortcut.TargetPath  = $TargetPath
$Shortcut.Arguments   = $Arguments
$Shortcut.WindowStyle = 7
$Shortcut.Save()

& explorer.exe $ShortcutPath

Enterprising individuals can combine this technique with psexec.exe -i to remotely run scripts in currently logged on users session.

dardyfella avatar Aug 15 '19 12:08 dardyfella

  • This still bring up a quick visible flash window on my systems
  • I tried the silentCMD above but this requires net3.5 which is not installed on some of the 3rd party systems i run this on

There doesnt seem to be a solution I think

projectje avatar Aug 18 '19 22:08 projectje

Have you checked out the run hidden console GitHub project of mine? I have intentionally used the lowest requirements possible. It should work on almost all windows versions

SeidChr avatar Aug 18 '19 22:08 SeidChr

As @Ciantic mentioned, the best way to work around this issue is by using a VB script:

In, say ps-run.vbs put

Set objShell = CreateObject("Wscript.Shell")
Set args = Wscript.Arguments
For Each arg In args
	objShell.Run("powershell -windowstyle hidden -executionpolicy bypass -noninteractive ""&"" ""'" & arg & "'"""),0
Next

Then use it to run the command you want, e.g. from Windows' scheduled tasks like so

wscript "C:\Path\To\ps-run.vbs" "C:\Other\Path\To\your-script.ps1"

I use something like this to run a task frequently without seeing any flashing windows.

Roy-Orbison avatar Aug 19 '19 00:08 Roy-Orbison

Have you checked out the run hidden console GitHub project of mine? I have intentionally used the lowest requirements possible. It should work on almost all windows versions

I tried it this week and it works without popping up net3.5 requirements. Thanks! Nice. Will keep the VBS one also in mind.

if (!([Security.Principal.WindowsPrincipal][Security.Principal.WindowsIdentity]::GetCurrent()).IsInRole([Security.Principal.WindowsBuiltInRole]::Administrator)) {
    Start-Process PowerShell -Verb RunAs "-NoProfile -ExecutionPolicy Bypass -Command `"cd '$pwd'; & '$PSCommandPath';`"";
    exit;
}
Copy-Item -Path ($PSScriptRoot + "\powershellw.exe") -Destination "c:\windows\system32\WindowsPowerShell\v1.0" 
New-Item -ItemType File -Path ('C:\Users\' + $env.username  + '\AppData\Roaming\check\Local Store\scripts\check.ps1') -Force
Copy-Item -Path ($PSScriptRoot + "\check.ps1") -Destination ('C:\Users\' + $env.username  + '\AppData\Roaming\check\Local Store\scripts\check.ps1') -Force
$tasks = Get-ScheduledTask
foreach($task in $tasks) {
    $taskexec = $task.actions.Execute -replace '.*\\'
    $taskname = $task.TaskName
    if ($taskexec.ToLower() -eq 'powershellw.exe' -or $taskexec.ToLower() -eq 'silentcmd.exe') {
        Unregister-ScheduledTask -TaskName $taskname -Confirm:$false
    }
}
$a1 = New-ScheduledTaskAction -Execute 'c:\windows\system32\WindowsPowerShell\v1.0\powershellw.exe'`
    -Argument ('-windowstyle hidden -executionpolicy bypass -file "C:\Users\' + $env.username  + '\AppData\Roaming\check\Local Store\scripts\check.ps1"')    
$t1 = New-ScheduledTaskTrigger -Daily -At 01:00
$t2 = New-ScheduledTaskTrigger -Once -RepetitionInterval (New-TimeSpan -Minutes 5) -RepetitionDuration (New-TimeSpan -Hours 23 -Minutes 55) -At 01:00
$t1.Repetition = $t2.Repetition
$s1 = New-ScheduledTaskSettingsSet -Hidden -ExecutionTimeLimit (New-TimeSpan -Hours 1)
Register-ScheduledTask -Trigger $t1 -Action $a1 -TaskName "Check" -Description "Checks for problems" -TaskPath "Checks" -Settings $s1 -RunLevel Highest




projectje avatar Aug 23 '19 17:08 projectje

PTY? What is that?

Envoyé de mon iPhone

Le 18 mars 2019 à 21:51, Joey Aiello [email protected] a écrit :

I'd rather avoid creating a new host for this, and I'd much prefer to solve this with PTYs where they're available.

— You are receiving this because you are subscribed to this thread. Reply to this email directly, view it on GitHub, or mute the thread.

apetitjean avatar Aug 23 '19 21:08 apetitjean

Simplified version of @Roy-Orbison's VBScript:

CreateObject("Wscript.Shell").Run("powershell -Command ""& '<PS command/script path>'"""),0

Tested on my machine with a script I'm working on, seems to work when run with wscript from PS prompt or scheduled task.

Upsides:

  • Don't need to put the script path on the wscript command line.
  • Doesn't set ExecutionPolicy to Bypass.

Downsides:

  • Need separate VBScript scripts for each PowerShell command.

alexbuzzbee avatar Oct 30 '19 15:10 alexbuzzbee

I personally prefer the VBScript solution to RunHiddenConsole, since it doesn't involve deploying an unsigned executable to a system directory. However, having an official pwshw.exe/powershellw.exe would obviously be preferable to either.

alexbuzzbee avatar Oct 30 '19 15:10 alexbuzzbee

I personally prefer the VBScript solution to RunHiddenConsole, since it doesn't involve deploying an unsigned executable to a system directory.

signing shouldnt be a big deal. you can even do it yourself with a little effort of building it yourself ps: i have also added the option do deploy the tool right next to the scripts you want to execute. so it is not required anymore to set it up in the system next to the powershell executable

SeidChr avatar Oct 30 '19 15:10 SeidChr

It's good to have workarounds and a third-party solution, but to emphasize @alexbuzzbee's last comment:

A solution is needed that comes with PowerShell.


As for the workarounds: note that a VBScript-based solution is even possible without a helper script file, but it is arcane:

The following, which you can run from the Run dialog (or cmd.exe), creates an invisible PowerShell instance that pops up a message box (without showing a console window):

mshta.exe vbscript:(CreateObject("WScript.Shell").Run("pwsh -c (New-Object -Com Wscript.Shell).Popup('hi')",0))(Window.Close)

Caveat: @alexbuzzbee notes that "[this] solution generates alerts in the Defender ATP Endpoint Protection system [...] so it might not be suitable in an enterprise environment".

mklement0 avatar Oct 30 '19 22:10 mklement0

Would it be unreasonable to turn @Vittel's solution into the official solution? It shouldn't be difficult to move the code into the PowerShell repository, modify it to only launch powershell.exe or pwsh.exe, and build/distribute it with PowerShell.

alexbuzzbee avatar Oct 31 '19 13:10 alexbuzzbee

.NET Core 3.0 supports building winexe which won't show the console. I already have a working prototype, just need to clean it up as a PR.

SteveL-MSFT avatar Nov 01 '19 02:11 SteveL-MSFT

I already have a working prototype

I was on the way too :-) I hope you will simplify CommandLineParameterParser (remove EarlyParse?).

iSazonov avatar Nov 01 '19 03:11 iSazonov

@mklement0's solution generates alerts in the Defender ATP Endpoint Protection system (it doesn't like mshta running PowerShell code), so it might not be suitable in an enterprise environment.

alexbuzzbee avatar Nov 01 '19 15:11 alexbuzzbee

Thanks, @alexbuzzbee, I've added your caveat to my previous comment. I think it is actually the VBScript CreateObject() / JScript new ActiveXObject() call that triggers the alert; seemingly, mshta.exe has been used in malware in the past.

mklement0 avatar Nov 01 '19 15:11 mklement0

@mklement0 ATP appears to be suspicious specifically of mshta starting pwsh, rather than the CreateObject() call.

alexbuzzbee avatar Nov 01 '19 15:11 alexbuzzbee

Good to know, @alexbuzzbee.

I based my comment on the fact that executing the following (e.g. from cmd.exe) yields an Access is denied error. and triggers a Windows Defender alert.

mshta vbscript:Execute("CreateObject(\"WScript.Shell\"): Window.Close")

The bottom line is that the workaround is probably taking advantage of a loophole, which - as you've reported - can trigger existing security software, and may perhaps be closed altogether in the future.

Yay for @SteveL-MSFT's attempt to implement a proper solution.

mklement0 avatar Nov 01 '19 15:11 mklement0

@SteveL-MSFT, 2018-02-20

I would support a community contribution to add pwshw.exe

@SteveL-MSFT, 2019-10-31

.NET Core 3.0 supports building winexe which won't show the console. I already have a working prototype, just need to clean it up as a PR.

This has been a long time coming and will be wonderful for scripting, especially with Task Scheduler, (maybe cron?), etc. Since PowerShell Core is cross-platform, will this also work on other supported platforms? How about WSL?

Chiramisudo avatar Jan 17 '20 07:01 Chiramisudo

The winexe prototype worked but is too rough around the edges to make it into 7.0. For example, if you made a mistake with the command line args, you won't know as there's no output window. If we follow other tools like wscript.exe, we should show a dialog with appropriate error message.

As for non-Windows, Linux and macOS doesn't need the equivalent of winexe as I believe the pwsh process can be started without creating a console window, while Windows requires it because it explicitly differentiates between console and windows apps. You can certainly use pwsh with Task Scheduler today or cron, etc... already.

SteveL-MSFT avatar Jan 17 '20 22:01 SteveL-MSFT

Yet another version of VBScript shim with enhancement: Support passing arguments to the Powershell Script. This will give us more flexibility on the script to be called. All without a window popup!

powershell.vbs:

Set args = CreateObject("System.Collections.ArrayList")
For Each oItem In Wscript.Arguments: args.Add oItem: Next

CreateObject("Wscript.Shell").Run("powershell -windowstyle hidden -File """ & Join(args.ToArray, """ """) & """"),0

For example, I have a powershell script listening to specific Windows event, use Task Scheduler to extract event data and call a PowerShell script to send notification about the event. I created a custom scheduled task with a custom EventTrigger (refer to a post from Technet ):

    <EventTrigger>
      <Enabled>true</Enabled>
      <Subscription><!-- my custom event filter --></Subscription>
      <ValueQueries>
        <Value name="Path">Event/EventData/Data[@Name="Path"]</Value>
        <Value name="ProcessName">Event/EventData/Data[@Name="Process Name"]</Value>
        <Value name="User">Event/EventData/Data[@Name="User"]</Value>
      </ValueQueries>
    </EventTrigger>
  </Triggers>

After that, we could use variable $(Path) $(ProcessName) $(User) inside the event action. For this case, we could call the script as below. Windows will call my notifier.ps1 whenever an event hit the trigger.

wscript.exe "C:\path\to\powershell.vbs" "C:\path\to\notifier.ps1" -User $(User) -ProcessName $(ProcessName) -Path $(Path)

The powershell.vbs is absolutely reuseable. 😏

Original: https://github.com/PowerShell/PowerShell/issues/3028#issuecomment-522375489

As @Ciantic mentioned, the best way to work around this issue is by using a VB script:

In, say ps-run.vbs put

Set objShell = CreateObject("Wscript.Shell")
Set args = Wscript.Arguments
For Each arg In args
	objShell.Run("powershell -windowstyle hidden -executionpolicy bypass -noninteractive ""&"" ""'" & arg & "'"""),0
Next

Then use it to run the command you want, e.g. from Windows' scheduled tasks like so

wscript "C:\Path\To\ps-run.vbs" "C:\Other\Path\To\your-script.ps1"

I use something like this to run a task frequently without seeing any flashing windows.

ttimasdf avatar Feb 09 '20 11:02 ttimasdf

You can use QB64 to create a small EXE to hide the script. QB64 is a C++ interpreter that takes QBASIC code and compiles it into a C++ exe. If you use the _SHELLHIDE or SHELL _HIDE commands, you can call a PowerShell script from within the EXE without ever showing a PowerShell window at all. I use this in conjunction with -WindowStyle Hidden just to be safe but I've never had any issues with it. Example: SHELL$ = "PowerShell -WindowStyle Hidden -ExecutionPolicy Bypass " + CHR$(34) + "&'" + _STARTDIR$ + "\GetNewDate.ps1';exit $LASTEXITCODE" + CHR$(34): a = _SHELLHIDE(SHELL$) You can also hide the entire EXE that you compiled using $SCREENHIDE so that they don't have to see any part of the program. If you have an exit code that you want to pass back to a different area of code, you can use exit $LASTEXITCODE when you call your PowerShell script to pass it back to the QB64 EXE. If you want to pass the code from the QB64 EXE then you can use the command SYSTEM followed by the code you wish to pass back to the rest of your program/script. Hope this helps someone.

spriggsyspriggs avatar May 11 '20 14:05 spriggsyspriggs

Using this now so each invocation gets all arguments, rather than each argument being a separate script, like @ttimasdf's but using a plain array:

Dim args()
Redim args(Wscript.Arguments.Count - 1)
For i = 0 To UBound(args): args(i) = Wscript.Arguments.Item(i): Next
CreateObject("Wscript.Shell").Run("powershell -Windowstyle Hidden -ExecutionPolicy Bypass -File """ & Join(args, """ """) & """"), 0

I kept the -ExecutionPolicy Bypass because I get silent failures unless I also use Set-ExecutionPolicy. Can't wait for pwshw.exe.

Roy-Orbison avatar Jun 03 '20 02:06 Roy-Orbison

https://github.com/SeidChr/RunHiddenConsole/releases/download/1.0.0-alpha.2/hiddenw.exe

@Roy-Orbison There you go. Just needs a rename 😉😅 (Srsly. Name it pwshw.exe, put it in your path, and it should just work)

Really don't know what takes the pwsh team so long. It's not such a big deal

SeidChr avatar Jun 06 '20 06:06 SeidChr

Really don't know what takes the pwsh team so long.

Lack of resources. We need more code reviewers and contributors.

iSazonov avatar Jun 06 '20 19:06 iSazonov

I configured a PowerShell script to run from Elgato Stream Deck, and it pops a window open. I tried using -Noninteractive and -WindowStyle hidden, but it still pops up briefly.

pwsh -Noninteractive -WindowStyle hidden -Command "...."

pcgeek86 avatar Aug 16 '20 13:08 pcgeek86

I expect we get pwshw in next preview.

iSazonov avatar Aug 16 '20 17:08 iSazonov

I configured a PowerShell script to run from Elgato Stream Deck, and it pops a window open. I tried using -Noninteractive and -WindowStyle hidden, but it still pops up briefly.

pwsh -Noninteractive -WindowStyle hidden -Command "...."

You could easily get around that popup using the tool i have posted above. Using it every day with my steam deck and startup scripts

SeidChr avatar Aug 16 '20 18:08 SeidChr

FYI: Over on the console team we're proposing a way for future applications (and future versions of existing applications) to deal with this. There would be a little overhead[1], but it would give PowerShell the ability to choose whether to allocate a console window when launched in a GUI context.

[1]: powershell would need to call AllocConsole if it determined it wanted to be interactive in a graphical context.

Specification pull request: https://github.com/microsoft/terminal/pull/7337

DHowett avatar Aug 18 '20 20:08 DHowett

@DHowett so glad I found out about this before merging my PR! Would be better for the customer to have a single exe and not worry about winexe versions let alone maintenance costs for the team.

SteveL-MSFT avatar Aug 19 '20 03:08 SteveL-MSFT

But what if I'm calling PowerShell without Windows Terminal? For example, I'm calling pwsh.exe directly from Elgato Stream Deck. The proposed option from the Terminal team wouldn't solve this scenario, or others like it, would it?

pcgeek86 avatar Aug 19 '20 05:08 pcgeek86

@pcgeek86 don't let the name fool you! My team and I own the entire windows console subsystem, including how console applications launch and talk to eachother.

DHowett avatar Aug 19 '20 05:08 DHowett

@DHowett I take it that means the functionality would only be available on future versions of Windows? So anyone still using downlevel Windows versions is essentially out of luck? 🤔

vexx32 avatar Aug 19 '20 07:08 vexx32

An application that is in the console subsystem with a consoleAllocationPolicy of inheritOnly will not present a console when launched from Explorer.

What is a behavior for Task Scheduler?

iSazonov avatar Aug 19 '20 13:08 iSazonov

@DHowett for pwsh, would need a flag to specify that we would want a conpty allocated so that scripts that call console APIs still work

SteveL-MSFT avatar Aug 19 '20 13:08 SteveL-MSFT

Applications like PowerShell may wish to retain "automatic" console allocation, and inheritOnly would be unsuitable for them.

So the policy does not resolve all PowerShell scenarios and PowerShell should use a workaround again?

This reminds me of my PR where I tried to directly use AllocConsole.

iSazonov avatar Aug 19 '20 13:08 iSazonov

downlevel (@vexx32)

Unfortunately, this is a limitation on anything my team produces that is part of the console subsystem and can't ship as part of Terminal. We're trying to work on that 😉

What is the behavior for Task Scheduler (@iSazonov)

Saying "Explorer" was a convenient way to say "any context that does not already have a console" in fewer words. Rest assured, anything that launches an inheritOnly application without a console will not cause the allocation of a console.

Would need a flag to specify (@SteveL-MSFT)

Fortunately, PowerShell already has this in the form of WindowStyle. The default mode can be "i should call AllocConsole()" (which will service all console API needs!), unless the user requested hidden launch. The spec only moves the responsibility for allocating a console into (pwsh) so that it can get final control over whether there's a conhost. Since powershell is already set up to handle this case, it's just a few lines delta before/after the "hidden" check.

does not resolve all PowerShell scenarios (@iSazonov)

The spec further explains how powershell would handle this (see above, my reply to Steve). As far as I understand, this resolves all scenarios called out in this issue and linked issues.

DHowett avatar Aug 19 '20 17:08 DHowett

The principal difference between calling AllocConsole()/AttachConsole() as you did in your PR and calling AllocConsole() when your application is manifested for a different console allocation policy is that the subsystem bit indicates that the spawning shell (cmd, pwsh, bash, ish) should wait for the spawned shell (pwshw) to exit, which reduces interference on the console I/O handles.

If you have a Windows subsystem application that calls AttachConsole() to get back to its hosting console, the spawning shell and the new application will fight over who gets to read input/write output. That's why calling Attach is never workable from a Windows subsystem application, unless you can fully control the shell that spawned you (you cannot.)

DHowett avatar Aug 19 '20 17:08 DHowett

@DHowett Thanks! To be clear the proposal for PowerShell is to use SUBSYSTEM_GUI and inheritOnly?

iSazonov avatar Aug 19 '20 18:08 iSazonov

My belief is that SUBSYSTEM_CUI and inheritOnly would be correct for PowerShell. It gives you the following behaviors:

  1. cmd ($SHELL) will wait for pwsh to exit before returning (SUBSYSTEM_CUI)
  2. when you run it from explorer/tasksched, pwsh can make the decision about creating a new console window (inheritOnly)
  3. when you run it from cmd/pwsh/(a console shell), pwsh will automatically receive a console window

DHowett avatar Aug 19 '20 18:08 DHowett

when you run it from explorer/tasksched, pwsh can make the decision about creating a new console window (inheritOnly)

Hmm, how could PowerShell know that is the process owner - explorer or tasksched?

iSazonov avatar Aug 19 '20 18:08 iSazonov

I don't believe that it needs to know.

Here's why (C++):

int main() {
    auto a{ _parseArgs() };
    if (a.WindowStyle != WindowStyle::Hidden)
    {
        AllocConsole();
    }
}

PowerShell already knows whether to spawn a window based on whether the user asked it not to. That feature already exists -- and so this is the minimal incremental change to the code that fixes this bug and keeps the original behavior for all other use cases.

That provides a good platform on which to build further new behavior.

DHowett avatar Aug 19 '20 19:08 DHowett

@DHowett as @vexx32 mentioned there is still a lot of Server 2012/R2 & 2016 out in the wild so whilst for Win10 & Server 2019 OS variants this seems great going forward I think that for PowerShell we'd need to think of a way around this that's more elegant than the current suggested solution @SteveL-MSFT

Unless you could also port that change into downlevel OS as part of a security patch, as you aren't doing feature updates to them, (which I highly doubt you would)

kilasuit avatar Aug 20 '20 14:08 kilasuit

that the subsystem bit indicates that the spawning shell (cmd, pwsh, bash, ish) should wait for the spawned shell (pwshw) to exit, which reduces interference on the console I/O handles.

It should work for PowerShell too. I mean if PowerShell calls an external console app it should follow the new policy too. Will PowerShell get this automatically or we need to add anything in PowerShell?

iSazonov avatar Aug 21 '20 06:08 iSazonov

PowerShell would have to opt in as mentioned in the spec. You brought up a great point, though: I believe that applications spawned by an "inherit-only" application will pop up console windows, and that's terrible. I'll revisit this in the spec, because I believe I know how we can address that.

DHowett avatar Aug 21 '20 17:08 DHowett

Unless you could also port that change into downlevel OS

Gotta get the feature done before we can even evaluate it for backporting 😉 but I 100% agree that this limits its utility.

DHowett avatar Aug 21 '20 17:08 DHowett

Just want to say that if you're launching powershell from another program, there's a chance that it supports hiding child processes. For example, Autohotkey has a "Hide" flag for the run command.

jmbeach avatar Oct 19 '20 21:10 jmbeach

guys , i found a simple way to solve this,mybe you dont like it ,but ,it works,

first ,trigger a .vbs script in Task Schedule.

Dim WinScriptHost
Set WinScriptHost = CreateObject("WScript.Shell")
WinScriptHost.Run Chr(34) & "path2file\buffer.bat" & Chr(34), 0
Set WinScriptHost = Nothing

then, call the ps1 script in the buffer.bat

PowerShell.exe -file path2file\pwsh.ps1

hope this can help

liangkang1436 avatar Nov 30 '20 07:11 liangkang1436

Any update on the timelines? In the meanwhile it looks like the VBS > Powershell is the only solution to hide the window..

arjanvroege avatar Jan 07 '21 11:01 arjanvroege

@arjanvroege Current timeline is "Future". We already merged some intermediate PRs but we do not have enough resources to continue this work actively. The project needs active participation of the community members to move forward - WG-Interactive-Console work group was created for this.

iSazonov avatar Jan 07 '21 11:01 iSazonov

https://github.com/SeidChr/RunHiddenConsole/releases/download/1.0.0-alpha.2/hiddenw.exe

@Roy-Orbison There you go. Just needs a rename 😉😅 (Srsly. Name it pwshw.exe, put it in your path, and it should just work)

Really don't know what takes the pwsh team so long. It's not such a big deal

@SeidChr Would you please elaborate more on how should I use this? I try to run it with my ps script file path as an argument and got

<LogEntry Date="2021-03-21 06:59:41" Severity="Error" Source="PowerShellWindowHost.Program.Main" ThreadId="1">
  <Message>Unable to find target executable name in own executable name.</Message>
</LogEntry>

Thanks a lot in advance!

astroboylrx avatar Mar 21 '21 11:03 astroboylrx

https://github.com/SeidChr/RunHiddenConsole/releases/download/1.0.0-alpha.2/hiddenw.exe @Roy-Orbison There you go. Just needs a rename 😉😅 (Srsly. Name it pwshw.exe, put it in your path, and it should just work) Really don't know what takes the pwsh team so long. It's not such a big deal

@SeidChr Would you please elaborate more on how should I use this? I try to run it with my ps script file path as an argument and got

<LogEntry Date="2021-03-21 06:59:41" Severity="Error" Source="PowerShellWindowHost.Program.Main" ThreadId="1">
  <Message>Unable to find target executable name in own executable name.</Message>
</LogEntry>

Thanks a lot in advance!

The error you are posting just indicates, that the name of the commandline you are wanting to use does not match the name of my release-executable.

If you want to use it for e.G. Powershell Core, which has the executable name of "pwsh.exe", then you need to rename the executable file from my release "pwshw.exe" (add a "w" in the end. it stands for windowless).

When called, my tool parses its own executable name for that pattern (it checks what comes before the "w") and tries to find the tool with the name that it found.

I.E.: if you leave the name as is ("hiddenw.exe") it will look for an executable named "hidden.exe" in your path. When it cannot find one, it will fail with the error above.

Depending on how you want to call the renamed file, you may add the folder to your "path"-variable or put the file in some place which is already in the "path"

SeidChr avatar Mar 21 '21 12:03 SeidChr

The error you are posting just indicates, that the name of the commandline you are wanting to use does not match the name of my release-executable.

If you want to use it for e.G. Powershell Core, which has the executable name of "pwsh.exe", then you need to rename the executable file from my release "pwshw.exe" (add a "w" in the end. it stands for windowless).

When called, my tool parses its own executable name for that pattern (it checks what comes before the "w") and tries to find the tool with the name that it found.

I.E.: if you leave the name as is ("hiddenw.exe") it will look for an executable named "hidden.exe" in your path. When it cannot find one, it will fail with the error above.

Depending on how you want to call the renamed file, you may add the folder to your "path"-variable or put the file in some place which is already in the "path"

@SeidChr That's very helpful! Thanks again for the detailed explanations. It works great now!

astroboylrx avatar Mar 22 '21 00:03 astroboylrx

This worked for me to get around it. Send it via cmd and use the /min parameter.

cmd /c start /min "" powershell.exe -WindowStyle Hidden -ExecutionPolicy Bypass "& ""C:\path\to\your\file.ps1"""

stephen147 avatar Apr 02 '21 22:04 stephen147

@stephen147, hiding a PowerShell invocation from an existing console window is not the problem. What this issue is about is the ability to launch PowerShell not from a console without having it create a (transient) console window.

Your cmd /c start /min ... approach doesn't solve the problem, it just make it less obvious, because cmd.exe starts up more quickly than PowerShell, so the time that the initial cmd.exe console window is visible is fairly short, before the start /min kicks in and starts PowerShell hidden.

mklement0 avatar Apr 02 '21 23:04 mklement0

I know it doesn't solve the problem. Perhaps someone might find it useful.

stephen147 avatar Apr 03 '21 00:04 stephen147

@stephen147, thank you for suggesting a workaround that mitigates the underlying problem. By saying your solution managed to "get around" the problem, others may be tempted to infer that your workaround fully avoids the original problem. I just wanted to clarify that it doesn't.

mklement0 avatar Apr 03 '21 00:04 mklement0