JST
JST

Reputation: 131

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

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?

Upvotes: 11

Views: 12167

Answers (3)

JMacia
JMacia

Reputation: 11

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);
    }
}

Upvotes: 1

Brian Lyttle
Brian Lyttle

Reputation: 14579

When you click the X to close you are closing the application hosting PowerShell. This application would need to handle the exit situation. I believe the default PS host is the Windows console which is obviously not doing what you need. You could host PowerShell in a custom host and handle the exit events. I'm on a Mac right now but maybe running under the PS ISE would handle this for you?

Upvotes: 0

Aaron Jensen
Aaron Jensen

Reputation: 26749

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 }

Upvotes: 2

Related Questions