As the title says, how can I bring a Powershell GUI window in front of another window after an event has happened, if it is at all possible? As in, I have, for example, Firefox opened and the Powershell GUI is running behind it, after certain event happens inside of the Powershell it pops in front of the Firefox?
1 Answers
On Windows, you can use [Microsoft.VisualBasic.Interaction]::AppActivate()
to reactivate your own process' main window via the process ID, as reflected in the automatic $PID
variable:
# Enable cross-process window activation (see below).
(Add-Type -ErrorAction Stop -PassThru -Namespace "Random.Ns$PID.AllowWindowActivation" -Name WinApiHelper -MemberDefinition @'
[DllImport("user32.dll", EntryPoint="SystemParametersInfo")]
static extern bool SystemParametersInfo_Set_UInt32(uint uiAction, uint uiParam, UInt32 pvParam, uint fWinIni);
public static void AllowWindowActivation()
{
if (! SystemParametersInfo_Set_UInt32(0x2001 /* SPI_SETFOREGROUNDLOCKTIMEOUT */, 0, 0 /* timeout in secs */, 0 /* non-persistent change */)) {
throw new System.ComponentModel.Win32Exception(System.Runtime.InteropServices.Marshal.GetLastWin32Error(), "Unexpected failure calling SystemParametersInfo() with SPI_SETFOREGROUNDLOCKTIMEOUT");
}
}
'@)::AllowWindowActivation()
# Load the required assembly.
Add-Type -AssemblyName Microsoft.VisualBasic
# Launch a sample GUI application that will steal the focus
# (will become the foreground application).
Start-Process notepad.exe
# Wait a little.
Start-Sleep 3
# Now reactivate the main window of the current process.
[Microsoft.VisualBasic.Interaction]::AppActivate($PID)
Note:
General programmatic activation of arbitrary windows across process boundaries is prevented by default:
Instead of the targeted window getting activated, its taskbar button flashes, so as to signal to the user the intent to make the window active.
However, programmatic activation from code running in the current foreground window seems to always be permitted.
The
Add-Type
-MemberDefinition
call above overrides this for the current session using a P/Invoke call to theSystemParametersInfo
WinAPI function, via setting itsSPI_SETFOREGROUNDLOCKTIMEOUT
parameter to0
.This will incur a one-time compilation performance penalty per session.
Cross-process window activation will be enabled for the remainder of the session, for all processes.
[Doesn't work anymore in W10+] The alternative is to persistently configure your user account to allow activation, via the registry:
Set the
ForegroundLockTimeout
per-user registry value inHKEY_CURRENT_USER\Control Panel\Desktop
to0
(the default is200000
msecs., i.e. 3 minutes and 20 secs); requires logging off or rebooting for the change to take effect:Set-ItemProperty 'registry::HKEY_CURRENT_USER\Control Panel\Desktop' ForegroundLockTimeout 0

- 382,024
- 64
- 607
- 775
-
Good alternative to native `SetForegroundWindow()`, so P/Invoke can be avoided. Under the hood it propably calls `SetForegroundWindow` after determining the "main window" of the process, so it actually just flashes the taskbar button instead of actively setting the window to the foreground. – zett42 Sep 11 '22 at 09:22
-
1Thanks man, I found exactly that code prior to posting this question, but couldn't find the way to determine the PID of my program/script. So this does it, thanks again. – raz0shi Sep 11 '22 at 12:54
-
Thanks, @zett42. On my machine I never see the flashing behavior and I don't know why: the `ForegroundLockTimeout` registry value is at its default, but the `SystemParametersInfo()` WinAPI function indeed indicates that activation is _allowed_, but I don't know where that is set (it is _not_ related to Developer Mode, which is off on my machine). Do you have an explanation? – mklement0 Sep 11 '22 at 15:07
-
@zett42, as for `[Microsoft.VisualBasic.Interaction]::AppActivate()`, specifically: [This answer](https://stackoverflow.com/a/19789921/45375) claims that the method uses a hack to unconditionally support activation. If you have a machine where `SetForegroundWindow` fails to activate, can you confirm that `[Microsoft.VisualBasic.Interaction]::AppActivate()` still succeeds? – mklement0 Sep 11 '22 at 15:09
-
@mklement0 No, `AppActivate()` exhibits the same behaviour as `SetForegroundWindow()` on my machine (both flash the taskbar button only), that's why I thought the former is implemented by calling the latter. That link is to a very old answer, so the hack has propably been prevented from working in newer Windows versions. – zett42 Sep 11 '22 at 15:36
-
Thanks, @zett42. Does `$outVal = 0; (Add-Type -PassThru -Name WinApi -Namespace pg -MemberDefinition @' [DllImport("user32.dll")] public static extern bool SystemParametersInfo(uint uiAction, uint uiParam, ref UInt32 pvParam, uint fWinIni); '@)::SystemParametersInfo(0x2000 <# SPI_GETFOREGROUNDLOCKTIMEOUT #>, 0, [ref] $outVal, 0); $outVal` return (`$true` and) a nonzero value for you? – mklement0 Sep 11 '22 at 15:40
-
Yes, `$true` and `20000`. It matches the value from the registry. – zett42 Sep 11 '22 at 15:42
-
Thanks, the mystery is why on my machine it reports `0`, even though the registry also shows `20000` – mklement0 Sep 11 '22 at 15:44
-
1I don't have an explanation for that. You may use [Process Monitor](https://learn.microsoft.com/en-us/sysinternals/downloads/procmon) to determine which registry key(s) are actually queried by `SystemParametersInfo()`. – zett42 Sep 11 '22 at 15:46
-
1Thanks, @zett42: I tried (not too hard) and came up short, and ended up creating a question, https://stackoverflow.com/q/73735129/45375, which contains additional background info, including with respect to Windows 11. – mklement0 Sep 15 '22 at 17:30