[KERNEL32][CONSRV]
authorHermès Bélusca-Maïto <hermes.belusca-maito@reactos.org>
Mon, 21 Apr 2014 01:22:17 +0000 (01:22 +0000)
committerHermès Bélusca-Maïto <hermes.belusca-maito@reactos.org>
Mon, 21 Apr 2014 01:22:17 +0000 (01:22 +0000)
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

reactos/dll/win32/kernel32/client/console/console.c
reactos/include/psdk/wincon.h
reactos/include/reactos/subsys/win/conmsg.h
reactos/win32ss/user/winsrv/consrv/condrv/console.c
reactos/win32ss/user/winsrv/consrv/console.c
reactos/win32ss/user/winsrv/consrv/handle.c
reactos/win32ss/user/winsrv/consrv/include/conio.h

index f65838c..9376a7d 100644 (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 */
index b36712c..c44e478 100644 (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
index 70bf665..4d117a7 100644 (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;
index b4b1111..3ec35f2 100644 (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);
index 8ecd567..81a39c5 100644 (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;
 }
 
 
index b79fe52..81fa41d 100644 (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);
index 30a70ea..6dda157 100644 (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 */