Как использовать SetConsoleHandler () для блокировки выходных вызовов - PullRequest
3 голосов
/ 10 февраля 2009

Я знаю, что я должен использовать setconsolehandler(), если я хочу, чтобы управлять консолью закрытия события.

Я не знаю, как блокировать CTRL_CLOSE_EVENT. Я пытался возвращение ложным / верно, если он ловит это событие, но не успех

Вот что я до сих пор (спасибо Антон Гоголев!)

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

public delegate bool HandlerRoutine(CtrlTypes CtrlType);

public enum CtrlTypes{
    CTRL_C_EVENT = 0,
    CTRL_BREAK_EVENT,
    CTRL_CLOSE_EVENT,
    CTRL_LOGOFF_EVENT = 5,
    CTRL_SHUTDOWN_EVENT
}

private static bool ConsoleCtrlCheck(CtrlTypes ctrlType)
{ 
    if(ctrlType == CtrlTypes.CTRL_CLOSE_EVENT)
        return false;// I have tried true and false and viceversa with the return   
                     // true/false but I cant seem to get it right.
    return true;
}


//and then I use this to call it
SetConsoleCtrlHandler(new HandlerRoutine(ConsoleCtrlCheck), true);

Также возможно запустить новый поток для контроля, если консоль закрывается и блок, который закрывается, если основной поток находится в середине что-то делать?

Ответы [ 3 ]

6 голосов
/ 10 февраля 2009

Документация для SetConsoleCtrlHandler() гласит:

Система генерирует сигналы CTRL_CLOSE_EVENT, CTRL_LOGOFF_EVENT и CTRL_SHUTDOWN_EVENT, когда пользователь закрывает консоль, выходит из системы или выключает систему, чтобы у процесса была возможность выполнить очистку перед завершением.

Это означает, что в отличие от обработки событий CTRL + C или CTRL + BREAK, ваш процесс не получает возможности отменить закрытие, выход из системы или завершение работы.

1 голос
/ 26 августа 2013

Я обнаружил возможный «взлом», который может помешать закрытию приложения, в то же время аккуратно подчиняясь запросу на закрытие консоли. В частности, это подходит, я думаю, для приложений с графическим интерфейсом, которые создали консоль как «дополнительную». Из документации MSDN, в точке, где обработчик Ctrl называется , создается новый поток , чтобы вызвать обработчик. Мое решение, таким образом, состоит в том, чтобы уничтожить атакующий поток вторжения до того, как обработчик по умолчанию сможет вызвать ExitProcess. В подпрограмме обработчика (код на C ++):

// Detach Console:
FreeConsole();
// Prevent closing:
ExitThread(0);
return TRUE; // Not reached

РЕДАКТИРОВАТЬ: Кажется, это вызывает некоторые проблемы, как и следовало ожидать, я полагаю. Последующий вызов AllocConsole () зависает на неопределенное время, поэтому я подозреваю, что преждевременный выход из потока не сможет очистить должным образом.

РЕДАКТИРОВАТЬ 2:

Чтобы уточнить выше, я не обнаружил прямых проблем с продолжением запуска программы. Но имейте в виду, что мы принудительно прервали поток, созданный kernel32, поэтому любые ресурсы в kernel32 могут находиться в неопределенном состоянии. Это может вызвать непредвиденные проблемы при продолжении работы программы.

В основном, я думаю, эти проблемы будут связаны с консольным API. Как уже упоминалось, AllocConsole не может работать с этого момента (зависает приложение), поэтому программа не может открыть новую консоль. Вполне возможно, что другие функции консоли также не будут работать. По сути, все, что вы делаете с этого момента и каким-либо образом (прямо или косвенно) вызывает ядро32, подвержено неопределенному поведению, но я подозреваю, что на практике не будет никаких проблем вне функций консоли.

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

1 голос
/ 14 июля 2012

На самом деле вы можете заблокировать его (я воспроизвел это по крайней мере на Windows XP). Например, если в вашем обработчике у вас есть бесконечный цикл while со сном, это не позволит этому процессу завершиться навсегда (или, по крайней мере, на долгое время, или пока пользователь не убьет процесс через диспетчер задач).

Если вам действительно нужно запустить поток, вы можете использовать условие ожидания (AutoResetEvent в C #) и запустить свой поток (хотя новый поток, вероятно, не требуется в большинстве случаев), а затем уведомить условие ожидания, когда ваш нить закончена. Однако в большинстве случаев достаточно просто выполнить очистку в обработчике.

Если в худшем случае вы ждали вечно, процесс останется запущенным, и вы сможете увидеть его при повторном входе в систему (по крайней мере, в Windows XP). Однако это приводит к тому, что рабочий стол приостанавливается примерно на 20 секунд, прежде чем перейти к экрану выхода из системы (пока он ожидает выхода вашего приложения), а затем снова делает паузу на экране выхода из системы (я полагаю, что при второй попытке) , Конечно, я настоятельно советую не ждать вечно; для любых длительных вещей вы должны действительно поместить это в сервис.

...