Register-EngineEvent -SourceIdentifier Powershell.Exiting -Action { Set-Content -Value "Hello there." -Path "$env:TEMP\General Kenobi.txt" }
exit
Instead of exit, the user may also close the Powershell with the x-Button.
I expect that Powershell executes every command in the -Action parameter before it exits. In this case, I expected that a file General Kenobi.txt would have been written to $env:TEMP.
Powershell does not execute the action in any case. Not when exiting via exit command and not when exiting by pressing the x-button of the powershell console window.
This not only happens in PS6 but also in PS5.
> $PSVersionTable
Name Value
---- -----
PSVersion 6.1.0
PSEdition Core
GitCommitId 6.1.0
OS Microsoft Windows 10.0.17763
Platform Win32NT
PSCompatibleVersions {1.0, 2.0, 3.0, 4.0...}
PSRemotingProtocolVersion 2.3
SerializationVersion 1.1.0.1
WSManStackVersion 3.0
This may be related to #6862, perhaps?
Any news on this? I am still experiencing this issue with the current preview.
> $PSVersionTable
Name Value
---- -----
PSVersion 6.2.0-preview.3
PSEdition Core
GitCommitId 6.2.0-preview.3
OS Microsoft Windows 10.0.15063
Platform Win32NT
PSCompatibleVersions {1.0, 2.0, 3.0, 4.0...}
PSRemotingProtocolVersion 2.3
SerializationVersion 1.1.0.1
WSManStackVersion 3.0
Oddly enough, the following code works. But only if PS is exited via exit command. Closing by pressing the x-button does not trigger the event.
Register-EngineEvent -SourceIdentifier PowerShell.Exiting -Action {
"PowerShell exited at {0}" -f (Get-Date) |
Out-File -FilePath "$env:TEMP\powershell.log" -Append
}
@vexx32 #6862 was fixed and merged. I just built from source (master) and the issue still exists.
There are several factors at play here:
The event _never_ kicks in if PowerShell is not in control of its own termination: Thus, closing the window / quitting the terminal emulator will _not_ run the event handler.
When PowerShell does exit by itself (whether normally or via a script-terminating error generated with throw), the handler does run, but attempts to use of much of PowerShell's regular functionality _fail quietly_, which is what you saw:
When your code is run _in-process in a PowerShell session_, it does work for me on exit-ing that session.
By contrast, if I run it _via the CLI_ (pwsh -c ...) - whether from PowerShell or cmd.exe / Bash - it fails (see test command below). What _seems_ to happen (I'm speculating) is that at the time the script block executes, all modules except Microsoft.PowerShell.Utility have already been unloaded, and attempts to use their commands fail silently and abort execution of the script block instantly.
Set-Content is part of module Microsoft.PowerShell.Management, and therefore unavailable; if I substitute Out-File in your command, which comes with Microsoft.PowerShell.Utility, the script block works as expected.In either invocation scenario you also cannot use Write-Output or implicit output to send output to the _display_ (redirection to a file works, though) - only Write-Host is effective.
At the very least, the documentation needs to be improved to clarify the constraints: I've asked for that in https://github.com/MicrosoftDocs/PowerShell-Docs/issues/4061.
Here's a test command via the CLI that demonstrates that the handler _is_ invoked, but fails when Set-Content is called:
# Call from PowerShell itself or Bash.
pwsh -nop -c '$null = Register-EngineEvent -SourceIdentifier Powershell.Exiting -Action { write-host before; try { Set-Content -ErrorAction stop -Value (Get-Date) -Path ~/t.txt } catch { write-host $_.ToString() }; write-host after }'
# Call from cmd.exe.
pwsh -nop -c "$null = Register-EngineEvent -SourceIdentifier Powershell.Exiting -Action { write-host before; try { Set-Content -ErrorAction stop -Value (Get-Date) -Path ~/t.txt } catch { write-host $_.ToString() }; write-host after }"
The above yields:
before
The 'Set-Content' command was found in the module 'Microsoft.PowerShell.Management', but the module could not be loaded. For more information, run 'Import-Module Microsoft.PowerShell.Management'.
after
@mklement0 Michael, thanks for addressing this. Your explanations make sense. I'm not sure what to do with this issue, though.
On one hand, this is actually by design and I probably should close my ticket.
On the other hand, the design should be improved in my opinion. The exiting Register-EngineEvent should be fired before anything is unloaded. The PowerShell would greatly benefit from that. The use-case where I actually found out about this event was to disconnect from all open Office 365 sessions when you forgot to explicitly disconnect before closing the Window. So you don't end up being locked out until the stale sessions expire. Which doesn't work when the modules are already unloaded when the event fires.
Another improvement would be to pass the close-event from the window to the Powershell, so it fires when you close by ALT-F4 or pressing the X. Most PowerShell users are using it on Windows and not on Linux, where you would actually have to run the exit command explicitly. Should I open another issue for that, or is it better to discuss this here? The context is already there.
All good points, @Borkason.
I suggest we get started _here_ by asking for feedback from a subject-matter expert about the original design intent, what could be improved, and what cannot be changed for technical reasons.
@SteveL-MSFT, can you help?
I'm having the same issue, and looking forward to see any progress on enhancement.
The Win32 Console api supports notifying a console application that it is closing and PowerShell is supposed to receive that notifcation here.
I know this mechanism works because PSReadLine uses it to avoid a ~6s delay when you click the X to exit. It's possible PSReadLine is getting in the way of PowerShell receiving the notification and hence not firing the event - so I'd test without PSReadLine loaded.
I tested with Register-EngineEvent -SourceIdentifier PowerShell.Exiting -Action {Set-Content $env:TEMP/test.txt "PowerShell died."} (deleting the file when it appeared) in PowerShell 5.1.18362.145 and PowerShell 6.2.1 on Windows and a variety of exit types, and I put together a table to summarize the results.
Version|exit|End of -Command|throw 'fatal' (via -Command)|Close button
-|-|-|-|-
5.1|Works|Works|Works|Broken
6.2.1|Broken|Broken|Broken|Broken
Remove-Module PSReadLine seemed to have no effect.
I tried this (explicit exit command) on Windows and macOS and both the exit event triggers the scriptblock and the file is created. Used 6.2.1 and 7-Preview.2.
That's strange. What code did you use? Edited my previous comment to note that it was only on Windows.
I used your example above: Register-EngineEvent -SourceIdentifier PowerShell.Exiting -Action {Set-Content $env:TEMP/test.txt "PowerShell died."}
I only checked with explicit exit, but the file was created with that content consistently.
I can confirm that with PowerShell 7-Preview.2 the issue is fixed, when using exit.
鈿狅笍 But not when using the close button.
I even tried a command that takes considerably more time.
Register-EngineEvent -SourceIdentifier Powershell.Exiting -Action { Set-Content -Value (get-module -ListAvailable -All) -Path "$env:TEMP\General Kenobi.txt" }
exit
@SteveL-MSFT any update on this? It would be great if we would see this working when PowerShell 7 is released. This would be a great benefit for automatically gracefully terminating any forgotten open PSSession before the window is closed. Especially helpful when working with Exchange Online or other Office 365 services that block you out if you forgot to close the sessions.
Ps, happy belated birthday to this issue! 馃巶 馃巵
I also just noticed that this is labeled as a discussion, but shouldn't it be a bug if the Exiting event does not trigger when closing the PowerShell with the X button?
Looks like a bug to me; the code that is supposed to handle this scenario is there but it is not working.
For console close button scenario - in debugger i see that ConsoleBreakSignal.Close event is received by ConsoleHost, but (unlike exit scenario) the action in EventManager is not invoked. This needs more investigation.
PSReadLine does not seem to have any effect; I've tried the scenario with and without it - result is the same for close button scenario.
Any updates so far ?
Most helpful comment
Looks like a bug to me; the code that is supposed to handle this scenario is there but it is not working.
For console
close buttonscenario - in debugger i see thatConsoleBreakSignal.Closeevent is received byConsoleHost, but (unlikeexitscenario) the action inEventManageris not invoked. This needs more investigation.PSReadLinedoes not seem to have any effect; I've tried the scenario with and without it - result is the same forclose buttonscenario.