admin管理员组文章数量:1387396
I’m messing with PowerShell scripting and wanted to know if this was possible. I know when you use try
/ finally
the code in the finally
block will execute even when Ctrl+C is typed. However, how can you run code it someone terminates it by closing the window? When I try try
/ finally
the code in the finally block never executes when exiting this way. Is this even possible?
I’m messing with PowerShell scripting and wanted to know if this was possible. I know when you use try
/ finally
the code in the finally
block will execute even when Ctrl+C is typed. However, how can you run code it someone terminates it by closing the window? When I try try
/ finally
the code in the finally block never executes when exiting this way. Is this even possible?
2 Answers
Reset to default 3Adding this as a new answer that can complement my previous one and doesn't require an orchestration script.
Thanks to zett42's research, it seems that SetConsoleCtrlHandler
function can be used to invoke handler when the PowerShell console is closed, you can see his attempt in this gist using a compiled handler. However, note, this approach does not work if the process is killed, i.e.: via Process.Kill()
or Stop-Process
.
After some testing it does seem to also work correctly passing a script block as your HandlerRoutine
callback function, however it requires a workaround using a new runspace, if you try to invoke the script block itself you'd get an exception stating that there is no available runspace. So, I've decided to add this cmdlet that you can compile ad-hoc with Add-Type
to set an exit handler:
using System.Collections.Generic;
using System.Management.Automation;
using System.Runtime.InteropServices;
public enum ConsoleCtrlEvent : uint
{
// A CTRL+C signal was received, typically from the user pressing Ctrl+C.
CTRL_C_EVENT = 0,
// A CTRL+BREAK signal was received, typically from the user pressing Ctrl+Break.
CTRL_BREAK_EVENT = 1,
// A signal that the console window is being closed.
CTRL_CLOSE_EVENT = 2,
// A signal that the user is logging off the system.
CTRL_LOGOFF_EVENT = 5,
// A signal that the system is shutting down.
CTRL_SHUTDOWN_EVENT = 6
}
[Cmdlet(VerbsCommon.Add, "ConsoleCtrlHandler")]
[OutputType(typeof(bool))]
public class ConsoleHelper : PSCmdlet
{
[DllImport("Kernel32")]
private static extern bool SetConsoleCtrlHandler(HandlerRoutine handler, bool add);
private delegate bool HandlerRoutine(ConsoleCtrlEvent ctrlType);
private static Dictionary<PowerShell, HandlerRoutine> s_handlers;
[Parameter(Mandatory = true, Position = 0)]
public ScriptBlock ScriptBlock { get; set; }
protected override void EndProcessing()
{
if (s_handlers == null)
{
s_handlers = new Dictionary<PowerShell, HandlerRoutine>();
}
PowerShell powershell = PowerShell
.Create(RunspaceMode.NewRunspace)
.AddScript(ScriptBlock.ToString());
s_handlers[powershell] = new HandlerRoutine(eventType =>
{
powershell.AddArgument(eventType).Invoke();
return false;
});
WriteObject(SetConsoleCtrlHandler(s_handlers[powershell], true));
}
}
The way to use it, if using Add-Type
versus pre-compile it:
- Store the code in a file or inline it in your PowerShell script itself, e.g.:
$code = @'...'@
. Add-Type
the code with-PassThru
that you can then pass-in toImport-Module -Assembly
.- Set the exit handler using the
Add-ConsoleCtrlHandler { ... }
cmdlet.
In summary, in this example assumes the C# code is stored in a file:
# `-Raw` is important here, don't miss it!
$code = Get-Content -Path path\to\myCmdlet.cs -Raw
# compile it and import it as a module
Add-Type $code -PassThru | Import-Module -Assembly { $_.Assembly }
# set the handler
Add-ConsoleCtrlHandler {
param([ConsoleCtrlEvent] $ctrlType)
# here goes the code that should execute before powershell exits
}
# the actual code for your script goes here
# ....
What you're looking for is possible, but does require pinvoke, see this answer for details.
As a workaround without it, you could have one orchestration script that starts your actual script in a new process that you can subscribe to its Exited
event.
$script = 'C:\path\to\theScript.ps1'
$proc = Start-Process powershell -ArgumentList '-NoProfile', '-File', $script -PassThru
$null = Register-ObjectEvent -InputObject $proc -EventName Exited -Action {
# here goes the code that should execute before powershell exits
}
$proc | Wait-Process
Then you should start this orchestration script using -WindowStyle Hidden
and -NoProfile
, e.g.:
powershell -WindowStyle Hidden -NoProfile -File myOrchestration.ps1
本文标签: powershellIs there a way to run code if someone tries exiting the programStack Overflow
版权声明:本文标题:powershell - Is there a way to run code if someone tries exiting the program? - Stack Overflow 内容由网友自发贡献,该文观点仅代表作者本人, 转载请联系作者并注明出处:http://www.betaflare.com/web/1744529389a2610939.html, 本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如发现本站有涉嫌抄袭侵权/违法违规的内容,一经查实,本站将立刻删除。
Process.Exited
event of the started script and that might work. – Santiago Squarzon Commented Mar 18 at 1:37PowerShell.Exiting
event does work for me from within the PowerShell session that is shutting down, but only in Windows Terminal, not in "plain" powershell.exe or pwsh.exe consoles. What does seem to work though is setting a console handler by p/invokingSetConsoleCtrlHandler
. This answer shows a workaround for a possible GC issue, when usingSetConsoleCtrlHandler
. – zett42 Commented Mar 18 at 12:19