[KERNEL32][CONSRV]

Implement (undocumented) SetLastConsoleEventActive API.
Patch by Alexander Andrejevic, with a minor modification by me concerning the addition of a NotifiedLastCloseProcess member to the CONSOLE structure and a check that makes sure that only the app that asked for the notification, receives it (and so that we don't call the console control dispatcher for nothing).
This API is used by ntvdm to be sure that it gets killed when all other console apps attached to the ntvdm's console are away.
CORE-7250

svn path=/trunk/; revision=62847
This commit is contained in:
Hermès Bélusca-Maïto 2014-04-21 01:22:17 +00:00
parent 38be5bceff
commit 286ee4b8b1
7 changed files with 114 additions and 32 deletions

View file

@ -30,6 +30,7 @@ PHANDLER_ROUTINE InitialHandler[1];
PHANDLER_ROUTINE* CtrlHandlers;
ULONG NrCtrlHandlers;
ULONG NrAllocatedHandlers;
BOOL LastCloseNotify = FALSE;
HANDLE InputWaitHandle = INVALID_HANDLE_VALUE;
@ -129,8 +130,14 @@ ConsoleControlDispatcher(IN LPVOID lpThreadParameter)
case CTRL_SHUTDOWN_EVENT:
break;
case 3:
ExitThread(0);
case CTRL_LAST_CLOSE_EVENT:
/*
* In case the console app hasn't register for last close notification,
* just kill this console handler thread. We don't want that such apps
* get killed for unexpected reasons. On the contrary apps that registered
* can be killed because they expect to be.
*/
if (!LastCloseNotify) ExitThread(0);
break;
case 4:
@ -2595,14 +2602,26 @@ BOOL WINAPI GetConsoleKeyboardLayoutNameW(LPWSTR name)
}
/*
* @unimplemented
* @implemented
*/
BOOL
DWORD
WINAPI
SetLastConsoleEventActive(VOID)
{
STUB;
return FALSE;
CONSOLE_API_MESSAGE ApiMessage;
PCONSOLE_NOTIFYLASTCLOSE NotifyLastCloseRequest = &ApiMessage.Data.NotifyLastCloseRequest;
/* Set the flag used by the console control dispatcher */
LastCloseNotify = TRUE;
/* Set up the input arguments */
NotifyLastCloseRequest->ConsoleHandle = NtCurrentPeb()->ProcessParameters->ConsoleHandle;
/* Call CSRSS; just return the NTSTATUS cast to DWORD */
return CsrClientCallServer((PCSR_API_MESSAGE)&ApiMessage,
NULL,
CSR_CREATE_API_NUMBER(CONSRV_SERVERDLL_INDEX, ConsolepNotifyLastClose),
sizeof(*NotifyLastCloseRequest));
}
/* EOF */

View file

@ -65,11 +65,12 @@ extern "C" {
/*
* Control handler codes
*/
#define CTRL_C_EVENT 0
#define CTRL_BREAK_EVENT 1
#define CTRL_CLOSE_EVENT 2
#define CTRL_LOGOFF_EVENT 5
#define CTRL_SHUTDOWN_EVENT 6
#define CTRL_C_EVENT 0
#define CTRL_BREAK_EVENT 1
#define CTRL_CLOSE_EVENT 2
#define CTRL_LAST_CLOSE_EVENT 3 /* Undocumented */
#define CTRL_LOGOFF_EVENT 5
#define CTRL_SHUTDOWN_EVENT 6
/*
* Input mode flags

View file

@ -202,6 +202,20 @@ typedef struct
PDWORD ProcessIdsList;
} CONSOLE_GETPROCESSLIST, *PCONSOLE_GETPROCESSLIST;
typedef struct
{
HANDLE ConsoleHandle;
DWORD CtrlEvent;
DWORD ProcessGroupId;
} CONSOLE_GENERATECTRLEVENT, *PCONSOLE_GENERATECTRLEVENT;
typedef struct
{
HANDLE ConsoleHandle;
} CONSOLE_NOTIFYLASTCLOSE, *PCONSOLE_NOTIFYLASTCLOSE;
typedef struct
{
HANDLE OutputHandle;
@ -349,6 +363,7 @@ typedef struct
} CONSOLE_GETSETHWSTATE, *PCONSOLE_GETSETHWSTATE;
typedef struct
{
HANDLE ConsoleHandle;
@ -526,6 +541,15 @@ typedef struct
BOOL AppendToEnd;
} CONSOLE_WRITEINPUT, *PCONSOLE_WRITEINPUT;
typedef struct
{
HANDLE ConsoleHandle;
HANDLE InputHandle;
DWORD NumberOfEvents;
} CONSOLE_GETNUMINPUTEVENTS, *PCONSOLE_GETNUMINPUTEVENTS;
typedef struct
{
HANDLE ConsoleHandle;
@ -584,6 +608,7 @@ typedef struct
} CONSOLE_OPENCONSOLE, *PCONSOLE_OPENCONSOLE;
typedef struct
{
HANDLE ConsoleHandle;
@ -700,20 +725,6 @@ typedef struct
typedef struct
{
HANDLE ConsoleHandle;
DWORD CtrlEvent;
DWORD ProcessGroupId;
} CONSOLE_GENERATECTRLEVENT, *PCONSOLE_GENERATECTRLEVENT;
typedef struct
{
HANDLE ConsoleHandle;
HANDLE InputHandle;
DWORD NumberOfEvents;
} CONSOLE_GETNUMINPUTEVENTS, *PCONSOLE_GETNUMINPUTEVENTS;
typedef struct
{
HANDLE ConsoleHandle;
@ -757,8 +768,10 @@ typedef struct _CONSOLE_API_MESSAGE
CONSOLE_ATTACHCONSOLE AttachConsoleRequest;
CONSOLE_FREECONSOLE FreeConsoleRequest;
/* Process list */
/* Processes */
CONSOLE_GETPROCESSLIST GetProcessListRequest;
CONSOLE_GENERATECTRLEVENT GenerateCtrlEventRequest;
CONSOLE_NOTIFYLASTCLOSE NotifyLastCloseRequest;
/* Handles */
CONSOLE_OPENCONSOLE OpenConsoleRequest;
@ -806,6 +819,7 @@ typedef struct _CONSOLE_API_MESSAGE
CONSOLE_GETINPUT GetInputRequest; // SrvGetConsoleInput / PeekConsoleInput & ReadConsoleInput
CONSOLE_READOUTPUT ReadOutputRequest; // SrvReadConsoleOutput / ReadConsoleOutput
CONSOLE_READOUTPUTCODE ReadOutputCodeRequest; // SrvReadConsoleOutputString / ReadConsoleOutputAttribute & ReadConsoleOutputCharacter
CONSOLE_GETNUMINPUTEVENTS GetNumInputEventsRequest;
/* Write */
CONSOLE_WRITECONSOLE WriteConsoleRequest; // SrvWriteConsole / WriteConsole
@ -830,9 +844,6 @@ typedef struct _CONSOLE_API_MESSAGE
CONSOLE_SETHISTORYNUMBERCOMMANDS SetHistoryNumberCommandsRequest;
CONSOLE_GETSETHISTORYINFO HistoryInfoRequest;
CONSOLE_GENERATECTRLEVENT GenerateCtrlEventRequest;
CONSOLE_GETNUMINPUTEVENTS GetNumInputEventsRequest;
/* Input and Output Code Pages */
CONSOLE_GETINPUTOUTPUTCP GetConsoleCPRequest;
CONSOLE_SETINPUTOUTPUTCP SetConsoleCPRequest;

View file

@ -244,7 +244,7 @@ ConDrvConsoleCtrlEventTimeout(IN ULONG CtrlEvent,
return Status;
}
static NTSTATUS
NTSTATUS
ConDrvConsoleCtrlEvent(IN ULONG CtrlEvent,
IN PCONSOLE_PROCESS_DATA ProcessData)
{
@ -535,6 +535,8 @@ ConDrvInitConsole(OUT PHANDLE NewConsoleHandle,
Console->ReferenceCount = 0;
InitializeCriticalSection(&Console->Lock);
InitializeListHead(&Console->ProcessList);
Console->NotifiedLastCloseProcess = NULL;
Console->NotifyLastClose = FALSE;
/* Initialize the frontend interface */
ResetFrontEnd(Console);

View file

@ -641,8 +641,27 @@ CSR_API(SrvGenerateConsoleCtrlEvent)
CSR_API(SrvConsoleNotifyLastClose)
{
DPRINT1("%s not yet implemented\n", __FUNCTION__);
return STATUS_NOT_IMPLEMENTED;
NTSTATUS Status;
PCONSOLE_PROCESS_DATA ProcessData = ConsoleGetPerProcessData(CsrGetClientThread()->Process);
PCONSOLE Console;
Status = ConSrvGetConsole(ProcessData, &Console, TRUE);
if (!NT_SUCCESS(Status)) return Status;
/* Only one process is allowed to be registered for last close notification */
if (!Console->NotifyLastClose)
{
Console->NotifiedLastCloseProcess = ProcessData;
Console->NotifyLastClose = TRUE;
Status = STATUS_SUCCESS;
}
else
{
Status = STATUS_ACCESS_DENIED;
}
ConSrvReleaseConsole(Console, TRUE);
return Status;
}

View file

@ -633,6 +633,10 @@ Quit:
return Status;
}
NTSTATUS
ConDrvConsoleCtrlEvent(IN ULONG CtrlEvent,
IN PCONSOLE_PROCESS_DATA ProcessData);
VOID
FASTCALL
ConSrvRemoveConsole(PCONSOLE_PROCESS_DATA ProcessData)
@ -662,6 +666,30 @@ ConSrvRemoveConsole(PCONSOLE_PROCESS_DATA ProcessData)
/* Update the internal info of the terminal */
TermRefreshInternalInfo(Console);
/*
* Check if there is only one process still attached to the console,
* and that the console should send a control event in this case.
*/
if ((Console->ProcessList.Flink != &Console->ProcessList) &&
(Console->ProcessList.Flink->Flink == &Console->ProcessList) &&
// (Console->ProcessList.Flink == Console->ProcessList.Blink) &&
Console->NotifyLastClose)
{
PCONSOLE_PROCESS_DATA LastProcess = CONTAINING_RECORD(Console->ProcessList.Flink,
CONSOLE_PROCESS_DATA,
ConsoleLink);
/* If the remaining process is the one that wanted the notification... */
if (LastProcess == Console->NotifiedLastCloseProcess)
{
/* ... notify it that it's the only one remaining on the console */
ConDrvConsoleCtrlEvent(CTRL_LAST_CLOSE_EVENT, LastProcess);
}
/* In any case reset the pointer and the flag */
Console->NotifiedLastCloseProcess = NULL;
Console->NotifyLastClose = FALSE;
}
/* Release the console */
DPRINT("ConSrvRemoveConsole - Decrement Console->ReferenceCount = %lu\n", Console->ReferenceCount);
ConDrvReleaseConsole(Console, TRUE);

View file

@ -282,6 +282,8 @@ typedef struct _CONSOLE
CONSOLE_STATE State; /* State of the console */
LIST_ENTRY ProcessList; /* List of processes owning the console. The first one is the so-called "Console Leader Process" */
PCONSOLE_PROCESS_DATA NotifiedLastCloseProcess; /* Pointer to the unique process that needs to be notified when all the other processes have been detached from the console */
BOOLEAN NotifyLastClose; /* TRUE if the console should send a control event to the last attached process after all the others detached, if it wanted to be notified */
FRONTEND TermIFace; /* Frontend-specific interface */