Skip to content

Instantly share code, notes, and snippets.

@lalibi
Forked from Nora-Ballard/Set-WindowState.ps1
Last active October 8, 2025 22:13
Show Gist options
  • Select an option

  • Save lalibi/3762289efc5805f8cfcf to your computer and use it in GitHub Desktop.

Select an option

Save lalibi/3762289efc5805f8cfcf to your computer and use it in GitHub Desktop.
Hide, Show, Minimize, Maximize, etc window from Powershell.
function Set-WindowState {
<#
.SYNOPSIS
Set the state of a window.
.DESCRIPTION
Set the state of a window using the `ShowWindowAsync` function from `user32.dll`.
.PARAMETER InputObject
The process object(s) to set the state of. Can be piped from `Get-Process`.
.PARAMETER State
The state to set the window to. Default is 'SHOW'.
.PARAMETER SuppressErrors
Suppress errors when the main window handle is '0'.
.PARAMETER SetForegroundWindow
Set the window to the foreground
.PARAMETER ThresholdHours
The number of hours to keep the window handle in memory. Default is 24.
.EXAMPLE
Get-Process notepad | Set-WindowState -State HIDE -SuppressErrors
.EXAMPLE
Get-Process notepad | Set-WindowState -State SHOW -SuppressErrors
.LINK
https://gist.github.com/lalibi/3762289efc5805f8cfcf
.NOTES
Original idea from https://gist.github.com/Nora-Ballard/11240204
#>
[CmdletBinding(DefaultParameterSetName = 'InputObject')]
param(
[Parameter(Position = 0, Mandatory = $true, ValueFromPipeline = $true)]
[Object[]] $InputObject,
[Parameter(Position = 1)]
[ValidateSet(
'FORCEMINIMIZE', 'HIDE', 'MAXIMIZE', 'MINIMIZE', 'RESTORE',
'SHOW', 'SHOWDEFAULT', 'SHOWMAXIMIZED', 'SHOWMINIMIZED',
'SHOWMINNOACTIVE', 'SHOWNA', 'SHOWNOACTIVATE', 'SHOWNORMAL'
)]
[string] $State = 'SHOW',
[switch] $SuppressErrors = $false,
[switch] $SetForegroundWindow = $false,
[int] $ThresholdHours = 24
)
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);
[DllImport("user32.dll", SetLastError = true)]
public static extern bool SetForegroundWindow(IntPtr hWnd);
'@ -Name "Win32ShowWindowAsync" -Namespace Win32Functions -PassThru
$handlesFilePath = "$env:APPDATA\WindowHandles.json"
$global:MainWindowHandles = @{}
if (Test-Path $handlesFilePath) {
$json = Get-Content $handlesFilePath -Raw
$data = $json | ConvertFrom-Json
$currentTime = Get-Date
foreach ($key in $data.PSObject.Properties.Name) {
$handleData = $data.$key
if ($handleData -and $handleData.Timestamp) {
try {
$timestamp = [datetime] $handleData.Timestamp
if ($currentTime - $timestamp -lt (New-TimeSpan -Hours $ThresholdHours)) {
$global:MainWindowHandles[[int] $key] = $handleData
}
} catch {
Write-Verbose "Skipping invalid timestamp for handle $key"
}
} else {
Write-Verbose "Skipping entry for handle $key due to missing data"
}
}
}
}
Process {
foreach ($process in $InputObject) {
$handle = $process.MainWindowHandle
if ($handle -eq 0 -and $global:MainWindowHandles.ContainsKey($process.Id)) {
$handle = [int] $global:MainWindowHandles[$process.Id].Handle
}
if ($handle -eq 0) {
if (-not $SuppressErrors) {
Write-Error "Main Window handle is '0'"
} else {
Write-Verbose ("Skipping '{0}' with id '{1}', because Main Window handle is '0'" -f $process.ProcessName, $process.Id)
}
continue
}
Write-Verbose ("Processing '{0}' with id '{1}' and handle '{2}'" -f $process.ProcessName, $process.Id, $handle)
$global:MainWindowHandles[$process.Id] = @{
Handle = $handle.ToString()
Timestamp = (Get-Date).ToString("o")
}
$Win32ShowWindowAsync::ShowWindowAsync($handle, $WindowStates[$State]) | Out-Null
if ($SetForegroundWindow) {
$Win32ShowWindowAsync::SetForegroundWindow($handle) | Out-Null
}
Write-Verbose ("» Set Window State '{1}' on '{0}'" -f $handle, $State)
}
}
End {
$data = [ordered] @{}
foreach ($key in $global:MainWindowHandles.Keys) {
if ($global:MainWindowHandles[$key].Handle -ne 0) {
$data["$key"] = $global:MainWindowHandles[$key]
}
}
$json = $data | ConvertTo-Json
Set-Content -Path $handlesFilePath -Value $json
}
}
@lalibi

lalibi commented Oct 27, 2015

Copy link
Copy Markdown
Author

Corrected issues with illegal characters and added pipeline support.

@CDWilliams05

Copy link
Copy Markdown

I'm having an issue with using your inputobject. It's telling me that its null when I run this from a piped "Get-Process"

@ed1chandler

Copy link
Copy Markdown

Question: Once you "HIDE" a window, its MainWindowHandle becomes 0, ... so how do you make it visible again?
Start-Process 'notepad.exe' # Start notepad.
$hwndBefore = (Get-Process -Name 'notepad').MainWindowHandle # Get main window handle.
Set-WindowStyle -MainWindowHandle $hwndBefore -Style HIDE # Hide it.
$hwndAfter = (Get-Process -Name 'notepad').MainWindowHandle # Handle now = 0
Set-WindowStyle -MainWindowHandle $hwndAfter -Style SHOW # No effect

@Inventologist

Copy link
Copy Markdown

@ed1chandler

Modify the code so that BEFORE you minimize it, you store info about the process (PID or ProcessName). That way you can reference it.

@dearZander

Copy link
Copy Markdown

Thanks, Dude, working like a charm on Windows 8, I need the alt - tab simulation but I was able to solve my issue minimizing the unwanted app and bring to the front the desired app.

@chaoscreater

chaoscreater commented Aug 30, 2019

Copy link
Copy Markdown

I can't seem to restore a minimized window. Can someone post an example code on how to achieve this?

After the window is minimized, if I do this:
$hwndAfter = (Get-Process -Name 'notepad').MainWindowHandle

I can get the mainwindowhandle just fine. So why Can't I just do this?
Set-WindowStyle -MainWindowHandle $hwndAfter -Style RESTORE

OR this:
Set-WindowStyle -MainWindowHandle $hwndAfter -Style SHOW

?

@lalibi

lalibi commented Nov 17, 2019

Copy link
Copy Markdown
Author

@chaoscreater RESTORE should work after you minimize a window. It doesn't work when you use HIDE, where MainWindowHandle becomes 0.

@lalibi

lalibi commented Nov 18, 2019

Copy link
Copy Markdown
Author

Updated the code so that it keeps the MainWindowHandle of the processes (for the current session) it handles. So now (in the same session) you can HIDE and then SHOW a window.

The implementation is not that elegant at the moment, it pollutes the global space with a variable (MainWindowHandle)

@chaoscreater

chaoscreater commented Dec 3, 2019

Copy link
Copy Markdown

Updated the code so that it keeps the MainWindowHandle of the processes (for the current session) it handles. So now (in the same session) you can HIDE and then SHOW a window.

The implementation is not that elegant at the moment, it pollutes the global space with a variable (MainWindowHandle)

@lalibi

Hi, thanks for updating the script. I can't figure out how to use it, I'm getting the following error:

Cannot convert argument "hWnd", with value: "", for "ShowWindowAsync" to type "System.IntPtr": "Cannot convert null to type "System.IntPtr"."

Could you provide an example of how to restore a minimized window please?

Update:

Nevermind, I got it.

$inputobject = get-process -name 'notepad'
Set-WindowState -inputObject $inputobject -State RESTORE

@lalibi

lalibi commented Dec 21, 2019

Copy link
Copy Markdown
Author

Or like this:

Get-Process notepad | Set-WindowState -State Hide
Get-Process notepad | Set-WindowState -State Show

@Duoquadragesimal

Copy link
Copy Markdown

Keep getting error "Main Window handle is '0' " when trying to run this on spotify, works Fine on notepad though. Maybe something to do with how spotify seems to be made up of 3 processes in task manager.
Image attached to show what I mean.
https://i.ibb.co/71nqXhx/Capture69.jpg

@fleetingbytes

Copy link
Copy Markdown

It's good, but although it sets the correct window state on the correct handle, what is still missing in certain $States is a command to bring the window to the foreground, e.g. $Win32ShowWindowAsync::SetForegroundWindow($handle) | Out-Null

@lalibi

lalibi commented Dec 27, 2021

Copy link
Copy Markdown
Author

@Duoquadragesimal, yes it's because of the multiple processes, it should work though. You get an error for each process that doesn't have a main window. I added a -SuppressErrors flag, so you can call it like this:

Get-Process spotify | Set-WindowState -State Show -SuppressErrors
Get-Process spotify | Set-WindowState -State Hide -SuppressErrors

@lalibi

lalibi commented Dec 27, 2021

Copy link
Copy Markdown
Author

@Nagidal added a flag for that too -SetForegroundWindow

Get-Process someprocess| Set-WindowState -State Show -SetForegroundWindow 

@adambasis

Copy link
Copy Markdown

Hmm. When I do this, the program gets hidden, but it does not show again
Get-Process program | Set-WindowState -State Hide -SuppressErrors
then
Get-Process program | Set-WindowState -State Show -SuppressErrors

Any idea?

@lalibi

lalibi commented Jun 14, 2022

Copy link
Copy Markdown
Author

Hmm. When I do this, the program gets hidden, but it does not show again Get-Process program | Set-WindowState -State Hide -SuppressErrors then Get-Process program | Set-WindowState -State Show -SuppressErrors

Any idea?

You need to execute those two commands in the same session. Otherwise it won't work, since a "hidden" application doesn't have a MainWindowHandle.

@lalibi

lalibi commented May 24, 2024

Copy link
Copy Markdown
Author

MainWindowHandles are now stored in a .json file, in "$env:APPDATA\WindowHandles.json", and restored them every time the script is run. At this point there is no automatic way to clear this file, only manually.

@lalibi

lalibi commented May 24, 2024

Copy link
Copy Markdown
Author

.json now includes a timestamp for each entry and invalidates them, if a threshold is reached (default is 24 hours).

@adambasis

Copy link
Copy Markdown

Wow, two years later, you still added a feature. That's awesome!

And I just realized I didn't respond, sorry about that lol.

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