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 */