-
-
Save lalibi/3762289efc5805f8cfcf to your computer and use it in GitHub Desktop.
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 | |
} | |
} |
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
Modify the code so that BEFORE you minimize it, you store info about the process (PID or ProcessName). That way you can reference it.
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.
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
?
@chaoscreater RESTORE should work after you minimize a window. It doesn't work when you use HIDE, where MainWindowHandle
becomes 0
.
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
)
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 canHIDE
and thenSHOW
a window.The implementation is not that elegant at the moment, it pollutes the global space with a variable (
MainWindowHandle
)
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
Or like this:
Get-Process notepad | Set-WindowState -State Hide
Get-Process notepad | Set-WindowState -State Show
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
It's good, but although it sets the correct window state on the correct handle, what is still missing in certain $State
s is a command to bring the window to the foreground, e.g. $Win32ShowWindowAsync::SetForegroundWindow($handle) | Out-Null
@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
@Nagidal added a flag for that too -SetForegroundWindow
Get-Process someprocess| Set-WindowState -State Show -SetForegroundWindow
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?
Hmm. When I do this, the program gets hidden, but it does not show again
Get-Process program | Set-WindowState -State Hide -SuppressErrors
thenGet-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
.
MainWindowHandle
s 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.
.json
now includes a timestamp for each entry and invalidates them, if a threshold is reached (default is 24 hours).
Wow, two years later, you still added a feature. That's awesome!
And I just realized I didn't respond, sorry about that lol.
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"