Skip to content

Instantly share code, notes, and snippets.

@Nora-Ballard
Last active January 21, 2024 09:25
Show Gist options
  • Star 28 You must be signed in to star a gist
  • Fork 9 You must be signed in to fork a gist
  • Save Nora-Ballard/11240204 to your computer and use it in GitHub Desktop.
Save Nora-Ballard/11240204 to your computer and use it in GitHub Desktop.
Hide, Show, Minimize, Maximize, etc window from Powershell.
function Set-WindowState {
param(
[Parameter()]
[ValidateSet('FORCEMINIMIZE', 'HIDE', 'MAXIMIZE', 'MINIMIZE', 'RESTORE',
'SHOW', 'SHOWDEFAULT', 'SHOWMAXIMIZED', 'SHOWMINIMIZED',
'SHOWMINNOACTIVE', 'SHOWNA', 'SHOWNOACTIVATE', 'SHOWNORMAL')]
[Alias('Style')]
[String] $State = 'SHOW',
[Parameter(ValueFromPipelineByPropertyname='True')]
[System.IntPtr] $MainWindowHandle = (Get-Process –id $pid).MainWindowHandle,
[Parameter()]
[switch] $PassThru
)
BEGIN
{
$WindowStates = @{
'FORCEMINIMIZE' = 11
'HIDE' = 0
'MAXIMIZE' = 3
'MINIMIZE' = 6
'RESTORE' = 9
'SHOW' = 5
'SHOWDEFAULT' = 10
'SHOWMAXIMIZED' = 3
'SHOWMINIMIZED' = 2
'SHOWMINNOACTIVE' = 7
'SHOWNA' = 8
'SHOWNOACTIVATE' = 4
'SHOWNORMAL' = 1
}
$Win32ShowWindowAsync = Add-Type –memberDefinition @”
[DllImport("user32.dll")]
public static extern bool ShowWindowAsync(IntPtr hWnd, int nCmdShow);
“@ -name “Win32ShowWindowAsync” -namespace Win32Functions –passThru
}
PROCESS
{
$Win32ShowWindowAsync::ShowWindowAsync($MainWindowHandle, $WindowStates[$State]) | Out-Null
Write-Verbose ("Set Window State on '{0}' to '{1}' " -f $MainWindowHandle, $State)
if ($PassThru)
{
Write-Output $MainWindowHandle
}
}
END
{
}
}
Set-Alias -Name 'Set-WindowStyle' -Value 'Set-WindowState'
@OneSpecialPeg
Copy link

Rename the $State parameter to $Style to get this to work.

@Paluan
Copy link

Paluan commented Dec 16, 2014

thanks for this amazing script, just one thing to add
" “@ -name “Win32ShowWindowAsync” -namespace Win32Functions –passThru"
my powershell ask to remove spaces at beginig of this line

@flamingtoast13
Copy link

Perhaps this is outdated, but anyway, this script works really well and it would be cool if it accepted pipeline input

@lalibi
Copy link

lalibi commented Oct 27, 2015

Check my fork, I added pipeline support

@ed1chandler
Copy link

Very useful, but isn't this really setting the window's "state", not its "style"?
State - https://msdn.microsoft.com/en-us/library/windows/desktop/ms633548(v=vs.85).aspx
Style - https://msdn.microsoft.com/en-us/library/windows/desktop/ms632600(v=vs.85).aspx
Any ideas on how the actual "style" can be set?
Thanks.

@nspb001
Copy link

nspb001 commented Jan 8, 2018

Any idea how to minimize/hide a window that doesn't have a MainWindowHandle property? For example, starting printui.exe you will receive a MainWindowHandle=0. The window doesn't have buttons to minimize/maximize either. However, using win+d to minimize the desktop, minimizes the printui.exe process as well.

@Nora-Ballard
Copy link
Author

Thank you everyone for the feedback. I have made some updates to address everyone's feedback. I hope you find it useful.

@ed1chandler is correct that state is probably more accurate, I wrote this originally for hiding the PS console and named it off of the parameter on there (-WindowsStyle) which sets these properties. I have updated it to reflect that, and added aliases for 'style' to keep compatibility, but you can easily disable those if you need.

I fixed the bug @Paluan reported, that was my typo when I was trying to clean up the formatting.

I have updated the MainWindowHandle parameter to accept pipeline input by property name, so it will accept a process object piped into it now.

I have update the script to output the window handle object if the -PassThru parameter is provided.

@nspb001 I'm not sure. Perhaps starting it with Start-Process or System.Diagnostics.Process would have it on that process object, or perhaps it is a permissions issue with it running elevated?

@dazlab
Copy link

dazlab commented Oct 20, 2020

Is there any way to get this to open up in fullscreen?

@Nora-Ballard
Copy link
Author

Is there any way to get this to open up in fullscreen?

I don’t think so, at least not with the API call that is being used currently. We are just calling an existing Windows API function and giving it values that it accepts.

@Offertorium
Copy link

Hi, what is the best way to minimize a window if the process name has a period. The process looks like this name_1.0 But the script runs very well otherwise. Thank you very much for that

@muraii
Copy link

muraii commented Oct 11, 2022

I am going to ask the dumb question: how should this be invoked? I've tried

> Get-Process *PowerShell* | Set-WindowState -State "SHOW"

and nothing happens. I've got three or four hidden PowerShell windows, but no dice. I then pulled a specific PID (say, 3372) and passed it like

> Get-Process -id 3372 | Set-WindowState -State "SHOW"

and still didn't see any response. I know the script's doing something because if I pass -PassThru I get a value of MainWindowHandle. I am tempted to think I'm getting the MainWindowHandle value for the window in which I'm invoking it. But I just don't grok PowerShell script parameters I guess.

Is it possibly that MainWindowHandle is set depending on the way in which the shell was invoked? Like, I've got a non-zero integer value for one of my PowerShell processes, but all the others have a value of zero.

@McArren
Copy link

McArren commented Nov 7, 2023

Hi All,
I'm going to necropost this one, since there's a bunch of links leading here from StackOverflow, and search engines also hit here.

First of all, thank you to @Nora-Ballard - this has been a huge help, as I needed this specific funcionality for one of my scripts.

Now, at least on my install - there's a couple of issues with the script as it stands, which I assume might just be due to newer versions of powershell.

  1. It happily accepts process ID via pipeline if no other parameter is specified, e.g.: >
    1. Get-Process -id ID | Set-WindowState works
    2. Get-Process -id ID | Set-WindowState -State "MINIMIZED" doesn't. It defaults to the $pid of the powershell window itself which is what I assume is happening to @muraii. Which is a really weird symptom. That and hidden windows may or may not have MainWindowHandle, think they are not supposed to, but behaviour seems inconsistent.
  2. If you use it to minimize the window that you've just started with Start-Process the $MainWindowHandle value will be zero and nothing useful will happen e.g.:
    1. (Start-Process Notepad -passthru).ID | Set-WindowState -State "MINIMIZE". Will have $MainWindowHandle == 0 and nothing will happen, If the #1 is not fixed - it will minimize powershell window itself.

In light of that - i did a couple adjustments.:
For #1 - processing of pipe input has been moved to PROCESS section out of BEGIN section.
For #2 - I've used WaitForInputIdle() to wait until the process has initialized and has MainWindowHande at all. Still doesn't work with some programms that launch some sort of startup .exe that then closes and leaves the main app open (e.g. steam, thunderbird). This can be resolved by adding a recursive function to go over the child processes. And then when calling Set-Windowstate - we can iterate over those. But I've not integrated that yet

And the actual full code that I've used to address the issues I've encountered

function Set-Windowstate {
param(
    [Parameter()]
    [ValidateSet('FORCEMINIMIZE', 'HIDE', 'MAXIMIZE', 'MINIMIZE', 'RESTORE',
                 'SHOW', 'SHOWDEFAULT', 'SHOWMAXIMIZED', 'SHOWMINIMIZED',
                 'SHOWMINNOACTIVE', 'SHOWNA', 'SHOWNOACTIVATE', 'SHOWNORMAL')]
    [String] $State = 'SHOW',
    
    [Parameter(ValueFromPipeline = $true)]
    [System.IntPtr] $id = $pid,#default to $pid as before, but we get the handle later. 
    #Also since for issue #2 MainWindowHandle might not exist - I pass process ID instead of Process Object instead
    
    [Parameter()]
    [switch] $PassThru
       
    
    
)
BEGIN{
    $WindowStates = @{
        'FORCEMINIMIZE'   = 11
        'HIDE'            = 0
        'MAXIMIZE'        = 3
        'MINIMIZE'        = 6
        'RESTORE'         = 9
        'SHOW'            = 5
        'SHOWDEFAULT'     = 10
        'SHOWMAXIMIZED'   = 3
        'SHOWMINIMIZED'   = 2
        'SHOWMINNOACTIVE' = 7
        'SHOWNA'          = 8
        'SHOWNOACTIVATE'  = 4
        'SHOWNORMAL'      = 1
    }


}
PROCESS{

    #if the below line is in BEGIN - it breaks in a manner I've described in #1, hence the rest is in here as well.
    $targetProcess = Get-Process -id $id

    #waiting for the process to finish initializing
    do
     {
       if ($targetProcess.HasExited -eq $true)
       {
         return
         break
       }
     } while ($targetProcess.WaitForInputIdle(1000) -ne $true)
    #we can now get $MainWindowHandle
    $MainWindowHandle = $targetProcess.MainWindowHandle
    


$Win32ShowWindowAsync = Add-Type -memberDefinition @"
[DllImport("user32.dll")] public static extern bool ShowWindowAsync(IntPtr hWnd, int nCmdShow);
"@ -name "Win32ShowWindowAsync" -namespace Win32Functions -passThru

    Write-Output ("Set Window State on {0} to {1}" -f $MainWindowHandle, $State)
    $Win32ShowWindowAsync::ShowWindowAsync($MainWindowHandle, $WindowStates[$State]) | Out-Null
        
        
        
}
END{
    #seems to better fit here
    if($PassThru){
    Write-Output $MainWindowHandle
    }
}
}

I've also removed the aliases while investigating the issues I had, but that should be easy to add back if one wants them.

@surfaceowl
Copy link

hey Nora - really great work here!

I ran into a few issues getting the gist to run on my Win 11 setup. To make it easier than finding minor changes in this thread, I posted a new repo here for your consideration. https://github.com/surfaceowl/Set-WindowState.ps1

There are three files there to help see the changes in steps-

  1. Set-WindowState.ps1 -- directly from Nora's gist, for reference / diffs
  2. Set-WindowState-v2-basic-fixes.ps1 -- corrects quote and dash chars that cause powershell errors on my install
  3. Set-WindowState-v3-style-suggestions.ps1 -- building on v2, some minor order/style changes

Using Set-WindowState-v2-basic-fixes.ps1 as an example, once this function is referenced in your powershell profile you can run from your terminal anywhere with something like: Get-Process *Evernote* | Set-WindowState -State "MINIMIZE" ...that just finds all the instances of a process and then minimizes

This is super helpful for managing annoying recurring tasks in Task Scheduler you can put this in a script file (e.g. paste cmd above into a file named minimize_evernote.ps1 and setup a new task in Task Scheduler. The cmd below will run the script silently, importantly avoiding a quick pop-up or flash of a powershell window that some may find annoying or worrisome (especially if run at startup). Configure the task with:
Program/script: cmd
Add arguments:

/c start /min "" Powershell -WindowStyle Hidden -ExecutionPolicy Bypass -File "C:\Users\chris\dev\powershell_scripts\minimize_evernote.ps1"

(of course, change the line above to match whatever path and filename used on your system)

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment