From 286ee4b8b144b03382ac07ad1ef08cd9595a7c11 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Herm=C3=A8s=20B=C3=A9lusca-Ma=C3=AFto?= Date: Mon, 21 Apr 2014 01:22:17 +0000 Subject: [PATCH] [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 --- .../win32/kernel32/client/console/console.c | 31 +++++++++--- reactos/include/psdk/wincon.h | 11 +++-- reactos/include/reactos/subsys/win/conmsg.h | 47 ++++++++++++------- .../user/winsrv/consrv/condrv/console.c | 4 +- reactos/win32ss/user/winsrv/consrv/console.c | 23 ++++++++- reactos/win32ss/user/winsrv/consrv/handle.c | 28 +++++++++++ .../user/winsrv/consrv/include/conio.h | 2 + 7 files changed, 114 insertions(+), 32 deletions(-) diff --git a/reactos/dll/win32/kernel32/client/console/console.c b/reactos/dll/win32/kernel32/client/console/console.c index f65838c4c06..9376a7d66d9 100644 --- a/reactos/dll/win32/kernel32/client/console/console.c +++ b/reactos/dll/win32/kernel32/client/console/console.c @@ -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 */ diff --git a/reactos/include/psdk/wincon.h b/reactos/include/psdk/wincon.h index b36712c7911..c44e478dab0 100644 --- a/reactos/include/psdk/wincon.h +++ b/reactos/include/psdk/wincon.h @@ -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 diff --git a/reactos/include/reactos/subsys/win/conmsg.h b/reactos/include/reactos/subsys/win/conmsg.h index 70bf665af7d..4d117a717b6 100644 --- a/reactos/include/reactos/subsys/win/conmsg.h +++ b/reactos/include/reactos/subsys/win/conmsg.h @@ -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; diff --git a/reactos/win32ss/user/winsrv/consrv/condrv/console.c b/reactos/win32ss/user/winsrv/consrv/condrv/console.c index b4b1111de48..3ec35f220af 100644 --- a/reactos/win32ss/user/winsrv/consrv/condrv/console.c +++ b/reactos/win32ss/user/winsrv/consrv/condrv/console.c @@ -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); diff --git a/reactos/win32ss/user/winsrv/consrv/console.c b/reactos/win32ss/user/winsrv/consrv/console.c index 8ecd567fa7b..81a39c54e7f 100644 --- a/reactos/win32ss/user/winsrv/consrv/console.c +++ b/reactos/win32ss/user/winsrv/consrv/console.c @@ -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; } diff --git a/reactos/win32ss/user/winsrv/consrv/handle.c b/reactos/win32ss/user/winsrv/consrv/handle.c index b79fe527e7a..81fa41d2dce 100644 --- a/reactos/win32ss/user/winsrv/consrv/handle.c +++ b/reactos/win32ss/user/winsrv/consrv/handle.c @@ -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); diff --git a/reactos/win32ss/user/winsrv/consrv/include/conio.h b/reactos/win32ss/user/winsrv/consrv/include/conio.h index 30a70eaae60..6dda15759fe 100644 --- a/reactos/win32ss/user/winsrv/consrv/include/conio.h +++ b/reactos/win32ss/user/winsrv/consrv/include/conio.h @@ -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 */