Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

How to handle close event of PowerShell window if user clicks on Close('X') button

Tags:

powershell

I want to run some code before PowerShell 2.0 window is closed. For this I tried:

PS > register-engineevent PowerShell.Exiting -action {get-process | out-file c:\work\powershellexiteventcalled.txt}

It works fine if user closes PowerShell window using exit command (i.e. exit ). But it does not work if user closes it by clicking on Close ('X') button on top right.

I could not find any way to handle this. I also tried to do it the following way, but this does not work either:

PS > $query = "Select * from __InstanceDeletionEvent WITHIN 5 WHERE TargetInstance ISA 'Win32_Process' AND TargetInstance.Name='powershell.exe'"

PS > Register-WmiEvent -Query $query -Action {get-process | out-file c:\work\powershellexiteventcalled.txt}

Please guide how I can achieve this task.


UPDATE: With some useful input from a helpful professional online I also tried the following:

$appCurrentDomain = [System.AppDomain]::CurrentDomain Register-ObjectEvent -Action {get-process | out-file c:\work\powershellexiteventcalled.txt} -InputObject $appCurrentDomain -EventName DomainUnload

But again, it does not work. As per Microsoft "DomainUnload event occurs when an AppDomain is about to be unloaded.". But it does not work when I close the window ( or even type exit for that matter).

UPDATE:

With some help from other professionals online & a little effort I could achieve this with following code.

PS..> $code = @"

        using System;
        using System.Runtime.InteropServices;
        using System.Management.Automation;
        using System.Management.Automation.Runspaces;

        namespace MyNamespace
        {
            public static class MyClass
            {
                public static void SetHandler()
                {
                    SetConsoleCtrlHandler(new HandlerRoutine(ConsoleCtrlCheck), true);
                }

                private static bool ConsoleCtrlCheck(CtrlTypes ctrlType)
                {
                    switch (ctrlType)
                    {
                        case CtrlTypes.CTRL_C_EVENT:
                            Console.WriteLine("CTRL+C received!");
                            return false;

                        case CtrlTypes.CTRL_CLOSE_EVENT:
                            Console.WriteLine("CTRL_CLOSE_EVENT received!");
                            return true;

                        case CtrlTypes.CTRL_BREAK_EVENT:
                            Console.WriteLine("CTRL+BREAK received!");
                            return false;

                        case CtrlTypes.CTRL_LOGOFF_EVENT:
                            Console.WriteLine("User is logging off!");
                            return false;

                        case CtrlTypes.CTRL_SHUTDOWN_EVENT:
                            Console.WriteLine("User is shutting down!");
                            return false;
                    }
                    return false;
                }

                [DllImport("Kernel32")]
                public static extern bool SetConsoleCtrlHandler(HandlerRoutine Handler, bool Add);

                // A delegate type to be used as the handler routine
                // for SetConsoleCtrlHandler.
                public delegate bool HandlerRoutine(CtrlTypes CtrlType);

                // An enumerated type for the control messages
                // sent to the handler routine.
                public enum CtrlTypes
                {
                    CTRL_C_EVENT = 0,
                    CTRL_BREAK_EVENT,
                    CTRL_CLOSE_EVENT,
                    CTRL_LOGOFF_EVENT = 5,
                    CTRL_SHUTDOWN_EVENT
                }
            }
        }"@

PS..> $text = Add-Type  -TypeDefinition $code -Language CSharp
PS..> $rs = [System.Management.Automation.Runspaces.Runspace]::DefaultRunspace
PS..> [MyNamespace.MyClass]::SetHandler()      

BUT THERES AN ISSUE I AM FACING.... If I run any cmdlet on console after registering this handler (e.g. get-date, get-process). Then the application will crash whenever an event occurs (e.g. Ctrl+C, close). Can someone please help me with this?

like image 574
JST Avatar asked Dec 02 '11 08:12

JST


People also ask

How do I automatically close a PowerShell window?

Open a PowerShell console session, type exit , and press the Enter key. The PowerShell console will immediately close.

How do I stop a Windows PowerShell window from closing?

Using Read-Host to keep PowerShell console openUsing the PowerShell Read-Host command at the end of the script ensure console windows stay open until you press enter button to end the program.

How do I keep the PowerShell window open after running the script?

Global Fix: Change your registry key by adding the -NoExit switch to always leave the PowerShell Console window open after the script finishes running.

How do I interrupt in PowerShell?

You can interrupt and stop a PowerShell command while it is running by pressing Control-C. A script can be stopped with the command exit. This will also close the PowerShell console.


2 Answers

Unfortunately, you can't do this. The only time an exiting event will get called is if you type "exit" at the PowerShell prompt.

This is how I hook up my exiting event:

 $null = Register-EngineEvent -SourceIdentifier `
     ([System.Management.Automation.PsEngineEvent]::Exiting) -Action { # Put code to run here }
like image 192
Aaron Jensen Avatar answered Oct 05 '22 00:10

Aaron Jensen


The unhandled exception occurs because garbage collection has already disposed your handler by the time the unmanaged method is called. You can get around that by storing it in a static field:

private static HandlerRoutine s_rou;

public static void SetHandler()
{
    if (s_rou == null)
    {
        s_rou = new HandlerRoutine(ConsoleCtrlCheck);
        SetConsoleCtrlHandler(s_rou, true);
    }
}
like image 36
JMacia Avatar answered Oct 05 '22 00:10

JMacia