Как обрабатывать событие закрытия окна PowerShell, если пользователь нажимает кнопку «Закрыть» («X») - PullRequest
8 голосов
/ 02 декабря 2011

Я хочу запустить некоторый код до закрытия окна PowerShell 2.0. Для этого я попытался:

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

Работает нормально, если пользователь закрывает окно PowerShell с помощью команды выхода (т.е. выхода). Но это не работает, если пользователь закрывает его, нажимая кнопку Закрыть ('X') в правом верхнем углу.

Я не мог найти способ справиться с этим. Я также попытался сделать это следующим образом, но это тоже не работает:

PS> $ query = "Выбрать * из __InstanceDeletionEvent В ТЕЧЕНИЕ 5 ГДЕ TargetInstance ISA 'Win32_Process' AND TargetInstance.Name = 'powershell.exe'"

PS> Register-WmiEvent -Query $ query -Action {get-process | вне файла c: \ work \ powershellexiteventcalled.txt}

Пожалуйста, объясните, как мне справиться с этой задачей.


ОБНОВЛЕНИЕ: С некоторой полезной информацией от полезного профессионала онлайн я также попробовал следующее:

$ appCurrentDomain = [System.AppDomain] :: CurrentDomain Register-ObjectEvent -Action {get-process | исходящий файл c: \ work \ powershellexiteventcalled.txt} -InputObject $ appCurrentDomain -EventName DomainUnload

Но опять же, это не работает. Согласно Microsoft «Событие DomainUnload наступает, когда AppDomain собирается быть выгруженным». Но это не работает, когда я закрываю окно (или даже в этом случае пишу exit).

UPDATE:

С некоторой помощью других онлайн-профессионалов и небольшим усилием я мог бы добиться этого с помощью следующего кода.

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()      

Но есть проблема, с которой я сталкиваюсь .... Если я запустил какой-либо командлет на консоли после регистрации этого обработчика (например, get-date, get-process). Тогда приложение будет аварийно завершать работу всякий раз, когда происходит событие (например, Ctrl + C, закрытие). Может кто-нибудь помочь мне с этим?

Ответы [ 3 ]

2 голосов
/ 08 декабря 2011

К сожалению, вы не можете этого сделать.Единственное время, когда вызывается событие выхода, - это если вы набираете «exit» в приглашении PowerShell.

Вот как я подключаю свое событие выхода:

 $null = Register-EngineEvent -SourceIdentifier `
     ([System.Management.Automation.PsEngineEvent]::Exiting) -Action { # Put code to run here }
1 голос
/ 22 апреля 2012

Произошло необработанное исключение, потому что сборщик мусора уже утилизировал ваш обработчик к моменту вызова неуправляемого метода. Вы можете обойти это, сохранив его в статическом поле:

private static HandlerRoutine s_rou;

public static void SetHandler()
{
    if (s_rou == null)
    {
        s_rou = new HandlerRoutine(ConsoleCtrlCheck);
        SetConsoleCtrlHandler(s_rou, true);
    }
}
0 голосов
/ 08 декабря 2011

Когда вы нажимаете X, чтобы закрыть, вы закрываете приложение, в котором находится PowerShell.Это приложение должно обрабатывать ситуацию выхода.Я считаю, что хост по умолчанию PS - это консоль Windows, которая явно не выполняет то, что вам нужно.Вы можете разместить PowerShell на пользовательском хосте и обрабатывать события выхода.Я сейчас на Mac, но, возможно, работа под PS ISE справится с этим для вас?

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...