|
|
/*++
Copyright (c) 1985 - 1999, Microsoft Corporation
Module Name:
ctrlc.c
Abstract:
This module implements ctrl-c handling
Author:
Therese Stowell (thereses) 1-Mar-1991
Revision History:
--*/
#include "precomp.h"
#pragma hdrstop
#if !defined(BUILD_WOW64)
#define LIST_INCREMENT 2 // amount to grow handler list
#define INITIAL_LIST_SIZE 1 // initial length of handler list
PHANDLER_ROUTINE SingleHandler[INITIAL_LIST_SIZE]; // initial handler list
ULONG HandlerListLength; // used length of handler list
ULONG AllocatedHandlerListLength; // allocated length of handler list
PHANDLER_ROUTINE *HandlerList; // pointer to handler list
#define NUMBER_OF_CTRL_EVENTS 7 // number of ctrl events
#define SYSTEM_CLOSE_EVENT 4
BOOL LastConsoleEventActive;
BOOL DefaultHandler( IN ULONG CtrlType )
/*++
This is the default ctrl handler.
Parameters:
CtrlType - type of ctrl event (ctrl-c, ctrl-break).
Return Value:
none.
--*/
{ ExitProcess((DWORD)CONTROL_C_EXIT); return TRUE; UNREFERENCED_PARAMETER(CtrlType); }
NTSTATUS InitializeCtrlHandling( VOID )
/*++
This routine initializes ctrl handling. It is called by AllocConsole and the dll initialization code.
Parameters:
none.
Return Value:
none.
--*/
{ AllocatedHandlerListLength = HandlerListLength = INITIAL_LIST_SIZE; HandlerList = SingleHandler; SingleHandler[0] = DefaultHandler; return STATUS_SUCCESS; }
DWORD CtrlRoutine( IN LPVOID lpThreadParameter )
/*++
Routine Description:
This thread is created when ctrl-c or ctrl-break is entered, or when close is selected. it calls the appropriate handlers.
Arguments:
lpThreadParameter - what type of event happened.
Return Value:
STATUS_SUCCESS
--*/
{ ULONG i; ULONG EventNumber,OriginalEventNumber; DWORD fNoExit; DWORD dwExitCode; EXCEPTION_RECORD ExceptionRecord;
SetThreadPriority(NtCurrentThread(), THREAD_PRIORITY_HIGHEST); OriginalEventNumber = EventNumber = PtrToUlong(lpThreadParameter);
//
// If this bit is set, it means we don't want to cause this process
// to exit itself if it is a logoff or shutdown event.
//
fNoExit = 0x80000000 & EventNumber; EventNumber &= ~0x80000000;
//
// the ctrl_close event is set when the user selects the window
// close option from the system menu, or EndTask, or Settings-Terminate.
// the system close event is used when another ctrl-thread times out.
//
switch (EventNumber) { default: ASSERT (EventNumber < NUMBER_OF_CTRL_EVENTS); if (EventNumber >= NUMBER_OF_CTRL_EVENTS) return (DWORD)STATUS_UNSUCCESSFUL; break;
case CTRL_C_EVENT: case CTRL_BREAK_EVENT: //
// If the process is being debugged, give the debugger
// a shot. If the debugger handles the exception, then
// go back and wait.
//
if (!IsDebuggerPresent()) break;
if ( EventNumber == CTRL_C_EVENT ) { ExceptionRecord.ExceptionCode = DBG_CONTROL_C; } else { ExceptionRecord.ExceptionCode = DBG_CONTROL_BREAK; } ExceptionRecord.ExceptionFlags = 0; ExceptionRecord.ExceptionRecord = NULL; ExceptionRecord.ExceptionAddress = (PVOID)DefaultHandler; ExceptionRecord.NumberParameters = 0;
try { RtlRaiseException(&ExceptionRecord); } except (EXCEPTION_EXECUTE_HANDLER) { LockDll(); try { if (EventNumber != CTRL_C_EVENT || (NtCurrentPeb()->ProcessParameters->ConsoleFlags & CONSOLE_IGNORE_CTRL_C) == 0) { for (i=HandlerListLength;i>0;i--) { if ((HandlerList[i-1])(EventNumber)) { break; } } } } finally { UnlockDll(); } } ExitThread(0); break;
case SYSTEM_CLOSE_EVENT: ExitProcess((DWORD)CONTROL_C_EXIT); break;
case SYSTEM_ROOT_CONSOLE_EVENT: if (!LastConsoleEventActive) ExitThread(0); break;
case CTRL_CLOSE_EVENT: case CTRL_LOGOFF_EVENT: case CTRL_SHUTDOWN_EVENT: //if (LastConsoleEventActive)
//EventNumber = SYSTEM_ROOT_CONSOLE_EVENT;
break; }
LockDll(); dwExitCode = 0; try { if (EventNumber != CTRL_C_EVENT || (NtCurrentPeb()->ProcessParameters->ConsoleFlags & CONSOLE_IGNORE_CTRL_C) == 0) { for (i=HandlerListLength;i>0;i--) {
//
// Don't call the last handler (the default one which calls
// ExitProcess() if this process isn't supposed to exit (system
// process are not supposed to exit because of shutdown or
// logoff event notification).
//
if ((i-1) == 0 && fNoExit) { if (EventNumber == CTRL_LOGOFF_EVENT || EventNumber == CTRL_SHUTDOWN_EVENT) { break; } }
if ((HandlerList[i-1])(EventNumber)) { switch (EventNumber) { case CTRL_CLOSE_EVENT: case CTRL_LOGOFF_EVENT: case CTRL_SHUTDOWN_EVENT: case SYSTEM_ROOT_CONSOLE_EVENT: dwExitCode = OriginalEventNumber; break; } break; } } } } finally { UnlockDll(); } ExitThread(dwExitCode); return STATUS_SUCCESS; }
#endif //!defined(BUILD_WOW64)
#if !defined(BUILD_WOW6432)
VOID APIENTRY SetLastConsoleEventActiveInternal( VOID )
/*++
Routine Description:
Sends a ConsolepNotifyLastClose command to the server.
Arguments:
none.
Return Value:
None.
--*/
{ CONSOLE_API_MSG m; PCONSOLE_NOTIFYLASTCLOSE_MSG a = &m.u.SetLastConsoleEventActive;
a->ConsoleHandle = GET_CONSOLE_HANDLE; CsrClientCallServer( (PCSR_API_MSG)&m, NULL, CSR_MAKE_API_NUMBER( CONSRV_SERVERDLL_INDEX, ConsolepNotifyLastClose ), sizeof( *a ) ); }
#endif //!defined(BUILD_WOW6432)
#if !defined(BUILD_WOW64)
VOID APIENTRY SetLastConsoleEventActive( VOID ) // private api
{
LastConsoleEventActive = TRUE; SetLastConsoleEventActiveInternal(); }
BOOL SetCtrlHandler( IN PHANDLER_ROUTINE HandlerRoutine )
/*++
Routine Description:
This routine adds a ctrl handler to the process's list.
Arguments:
HandlerRoutine - pointer to ctrl handler.
Return Value:
TRUE - success.
--*/
{ PHANDLER_ROUTINE *NewHandlerList;
//
// NULL handler routine is not stored in table. It is
// used to temporarily inhibit ^C event handling
//
if (!HandlerRoutine) { NtCurrentPeb()->ProcessParameters->ConsoleFlags |= CONSOLE_IGNORE_CTRL_C; return TRUE; }
if (HandlerListLength == AllocatedHandlerListLength) {
//
// grow list
//
NewHandlerList = (PHANDLER_ROUTINE *) RtlAllocateHeap( RtlProcessHeap(), 0, sizeof(PHANDLER_ROUTINE) * (HandlerListLength + LIST_INCREMENT)); if (!NewHandlerList) { SET_LAST_ERROR(ERROR_NOT_ENOUGH_MEMORY); return FALSE; }
//
// copy list
//
RtlCopyMemory(NewHandlerList,HandlerList,sizeof(PHANDLER_ROUTINE) * HandlerListLength);
if (HandlerList != SingleHandler) {
//
// free old list
//
RtlFreeHeap(RtlProcessHeap(), 0, HandlerList); } HandlerList = NewHandlerList; AllocatedHandlerListLength += LIST_INCREMENT; } ASSERT (HandlerListLength < AllocatedHandlerListLength);
HandlerList[HandlerListLength] = HandlerRoutine; HandlerListLength++; return TRUE; }
BOOL RemoveCtrlHandler( IN PHANDLER_ROUTINE HandlerRoutine )
/*++
Routine Description:
This routine removes a ctrl handler from the process's list.
Arguments:
HandlerRoutine - pointer to ctrl handler.
Return Value:
TRUE - success.
--*/
{ ULONG i;
//
// NULL handler routine is not stored in table. It is
// used to temporarily inhibit ^C event handling. Removing
// this handler allows normal processing to occur
//
if ( !HandlerRoutine ) { NtCurrentPeb()->ProcessParameters->ConsoleFlags = 0; return TRUE; }
for (i=0;i<HandlerListLength;i++) { if (*(HandlerList+i) == HandlerRoutine) { if (i < (HandlerListLength-1)) { memmove(&HandlerList[i],&HandlerList[i+1],sizeof(PHANDLER_ROUTINE) * (HandlerListLength - i - 1)); } HandlerListLength -= 1; return TRUE; } } SET_LAST_ERROR(ERROR_INVALID_PARAMETER); return FALSE; }
BOOL APIENTRY SetConsoleCtrlHandler( IN PHANDLER_ROUTINE HandlerRoutine, IN BOOL Add // add or delete
)
/*++
Routine Description:
This routine adds or removes a ctrl handler from the process's list.
Arguments:
HandlerRoutine - pointer to ctrl handler.
Add - if TRUE, add handler. else remove.
Return Value:
TRUE - success.
--*/
{ BOOL Success;
LockDll(); if (Add) { Success = SetCtrlHandler(HandlerRoutine); } else { Success = RemoveCtrlHandler(HandlerRoutine); } UnlockDll(); return Success; }
#endif //!defined(BUILD_WOW64)
|