From 440ad8ccf2e90ca59c49353a78f7c2af7b74b1c3 Mon Sep 17 00:00:00 2001 From: Eric Pouech Date: Wed, 8 Sep 2004 01:25:05 +0000 Subject: [PATCH] - ctrl-c enabling flag is now inherited - fixed console-related fields in RTL_USER_PROCESS_PARAMETERS - various clean-up in kernel32.SetConsoleCtrlHandler - only send a console event once to a process and not to all the process' threads --- dlls/kernel/console.c | 99 +++++++++++++++++++++++++++++---------------- dlls/kernel/tests/console.c | 50 ++++++++++++++++++++--- dlls/ntdll/env.c | 2 +- include/winternl.h | 4 +- server/console.c | 13 +++--- server/trace.c | 4 +- 6 files changed, 119 insertions(+), 53 deletions(-) diff --git a/dlls/kernel/console.c b/dlls/kernel/console.c index 454d9b4b2cf..96b9ceb4a7b 100644 --- a/dlls/kernel/console.c +++ b/dlls/kernel/console.c @@ -1,11 +1,11 @@ /* - * Win32 kernel functions + * Win32 console functions * * Copyright 1995 Martin von Loewis and Cameron Heide * Copyright 1997 Karl Garrison * Copyright 1998 John Richardson * Copyright 1998 Marcus Meissner - * Copyright 2001,2002 Eric Pouech + * Copyright 2001,2002,2004 Eric Pouech * Copyright 2001 Alexandre Julliard * * This library is free software; you can redistribute it and/or @@ -52,6 +52,7 @@ #include "excpt.h" #include "console_private.h" #include "kernel_private.h" +#include "thread.h" WINE_DEFAULT_DEBUG_CHANNEL(console); @@ -1419,20 +1420,14 @@ static BOOL WINAPI CONSOLE_DefaultHandler(DWORD dwCtrlType) * RETURNS * Success: TRUE * Failure: FALSE - * - * CHANGED - * James Sutherland (JamesSutherland@gmx.de) - * Added global variables console_ignore_ctrl_c and handlers[] - * Does not yet do any error checking, or set LastError if failed. - * This doesn't yet matter, since these handlers are not yet called...! */ -struct ConsoleHandler { +struct ConsoleHandler +{ PHANDLER_ROUTINE handler; struct ConsoleHandler* next; }; -static unsigned int CONSOLE_IgnoreCtrlC = 0; /* FIXME: this should be inherited somehow */ static struct ConsoleHandler CONSOLE_DefaultConsoleHandler = {CONSOLE_DefaultHandler, NULL}; static struct ConsoleHandler* CONSOLE_Handlers = &CONSOLE_DefaultConsoleHandler; @@ -1447,15 +1442,23 @@ static CRITICAL_SECTION CONSOLE_CritSect = { &critsect_debug, -1, 0, 0, 0, 0 }; /*****************************************************************************/ +/****************************************************************** + * SetConsoleCtrlHandler (KERNEL32.@) + */ BOOL WINAPI SetConsoleCtrlHandler(PHANDLER_ROUTINE func, BOOL add) { BOOL ret = TRUE; - FIXME("(%p,%i) - no error checking or testing yet\n", func, add); + TRACE("(%p,%i)\n", func, add); if (!func) { - CONSOLE_IgnoreCtrlC = add; + RtlEnterCriticalSection(&CONSOLE_CritSect); + if (add) + NtCurrentTeb()->Peb->ProcessParameters->ConsoleFlags |= 1; + else + NtCurrentTeb()->Peb->ProcessParameters->ConsoleFlags &= ~1; + RtlLeaveCriticalSection(&CONSOLE_CritSect); } else if (add) { @@ -1472,7 +1475,7 @@ BOOL WINAPI SetConsoleCtrlHandler(PHANDLER_ROUTINE func, BOOL add) { struct ConsoleHandler** ch; RtlEnterCriticalSection(&CONSOLE_CritSect); - for (ch = &CONSOLE_Handlers; *ch; *ch = (*ch)->next) + for (ch = &CONSOLE_Handlers; *ch; ch = &(*ch)->next) { if ((*ch)->handler == func) break; } @@ -1484,18 +1487,19 @@ BOOL WINAPI SetConsoleCtrlHandler(PHANDLER_ROUTINE func, BOOL add) if (rch == &CONSOLE_DefaultConsoleHandler) { ERR("Who's trying to remove default handler???\n"); + SetLastError(ERROR_INVALID_PARAMETER); ret = FALSE; } else { - rch = *ch; - *ch = (*ch)->next; + *ch = rch->next; HeapFree(GetProcessHeap(), 0, rch); } } else { WARN("Attempt to remove non-installed CtrlHandler %p\n", func); + SetLastError(ERROR_INVALID_PARAMETER); ret = FALSE; } RtlLeaveCriticalSection(&CONSOLE_CritSect); @@ -1509,18 +1513,42 @@ static WINE_EXCEPTION_FILTER(CONSOLE_CtrlEventHandler) return EXCEPTION_EXECUTE_HANDLER; } -static DWORD WINAPI CONSOLE_HandleCtrlCEntry(void* pmt) +/****************************************************************** + * CONSOLE_SendEventThread + * + * Internal helper to pass an event to the list on installed handlers + */ +static DWORD WINAPI CONSOLE_SendEventThread(void* pmt) { - struct ConsoleHandler* ch; + DWORD event = (DWORD)pmt; + struct ConsoleHandler* ch; + if (event == CTRL_C_EVENT) + { + BOOL caught_by_dbg = TRUE; + /* First, try to pass the ctrl-C event to the debugger (if any) + * If it continues, there's nothing more to do + * Otherwise, we need to send the ctrl-C event to the handlers + */ + __TRY + { + RaiseException( DBG_CONTROL_C, 0, 0, NULL ); + } + __EXCEPT(CONSOLE_CtrlEventHandler) + { + caught_by_dbg = FALSE; + } + __ENDTRY; + if (caught_by_dbg) return 0; + /* the debugger didn't continue... so, pass to ctrl handlers */ + } RtlEnterCriticalSection(&CONSOLE_CritSect); - /* the debugger didn't continue... so, pass to ctrl handlers */ for (ch = CONSOLE_Handlers; ch; ch = ch->next) { - if (ch->handler((DWORD)pmt)) break; + if (ch->handler(event)) break; } RtlLeaveCriticalSection(&CONSOLE_CritSect); - return 0; + return 1; } /****************************************************************** @@ -1533,24 +1561,21 @@ int CONSOLE_HandleCtrlC(unsigned sig) /* FIXME: better test whether a console is attached to this process ??? */ extern unsigned CONSOLE_GetNumHistoryEntries(void); if (CONSOLE_GetNumHistoryEntries() == (unsigned)-1) return 0; - if (CONSOLE_IgnoreCtrlC) return 1; - /* try to pass the exception to the debugger - * if it continues, there's nothing more to do - * otherwise, we need to send the ctrl-event to the handlers - */ - __TRY - { - RaiseException( DBG_CONTROL_C, 0, 0, NULL ); - } - __EXCEPT(CONSOLE_CtrlEventHandler) - { - /* Create a separate thread to signal all the events. This would allow to - * synchronize between setting the handlers and actually calling them + /* check if we have to ignore ctrl-C events */ + if (!(NtCurrentTeb()->Peb->ProcessParameters->ConsoleFlags & 1)) + { + /* Create a separate thread to signal all the events. + * This is needed because: + * - this function can be called in an Unix signal handler (hence on an + * different stack than the thread that's running). This breaks the + * Win32 exception mechanisms (where the thread's stack is checked). + * - since the current thread, while processing the signal, can hold the + * console critical section, we need another execution environment where + * we can wait on this critical section */ - CreateThread(NULL, 0, CONSOLE_HandleCtrlCEntry, (void*)CTRL_C_EVENT, 0, NULL); + CreateThread(NULL, 0, CONSOLE_SendEventThread, (void*)CTRL_C_EVENT, 0, NULL); } - __ENDTRY; return 1; } @@ -1586,6 +1611,10 @@ BOOL WINAPI GenerateConsoleCtrlEvent(DWORD dwCtrlEvent, } SERVER_END_REQ; + /* FIXME: shall this function be synchronous, ie only return when all events + * have been handled by all processes in the given group ? + * As of today, we don't wait... + */ return ret; } diff --git a/dlls/kernel/tests/console.c b/dlls/kernel/tests/console.c index 67e7e428618..cf1a7a554cc 100644 --- a/dlls/kernel/tests/console.c +++ b/dlls/kernel/tests/console.c @@ -1,7 +1,7 @@ /* * Unit tests for console API * - * Copyright (c) 2003 Eric Pouech + * Copyright (c) 2003,2004 Eric Pouech * * This library is free software; you can redistribute it and/or * modify it under the terms of the GNU Lesser General Public @@ -360,7 +360,6 @@ static void testWrite(HANDLE hCon, COORD sbSize) testWriteWrappedProcessed(hCon, sbSize); } -#if 0 static void testScroll(HANDLE hCon, COORD sbSize) { SMALL_RECT scroll, clip; @@ -477,6 +476,7 @@ static void testScroll(HANDLE hCon, COORD sbSize) } } +#if 0 /* clipping, src & dst rect do overlap */ resetContent(hCon, sbSize, TRUE); @@ -511,8 +511,46 @@ static void testScroll(HANDLE hCon, COORD sbSize) else okCHAR(hCon, c, CONTENT(c), DEFAULT_ATTRIB); } } -} #endif +} + +static int mch_count; +/* we need the event as Wine console event generation isn't synchronous + * (ie GenerateConsoleCtrlEvent returns before all ctrl-handlers in all + * processes have been called). + */ +static HANDLE mch_event; +static BOOL WINAPI mch(DWORD event) +{ + mch_count++; + SetEvent(mch_event); + return TRUE; +} + +static void testCtrlHandler(void) +{ + ok(!SetConsoleCtrlHandler(mch, FALSE), "Shouldn't succeed\n"); + ok(GetLastError() == ERROR_INVALID_PARAMETER, "Bad error %lu\n", GetLastError()); + ok(SetConsoleCtrlHandler(mch, TRUE), "Couldn't set handler\n"); + /* wine requires the event for the test, as we cannot insure, so far, that event + * are processed synchronously in GenerateConsoleCtrlEvent() + */ + mch_event = CreateEventA(NULL, TRUE, FALSE, NULL); + mch_count = 0; + ok(GenerateConsoleCtrlEvent(CTRL_C_EVENT, 0), "Couldn't send ctrl-c event\n"); + todo_wine ok(mch_count == 1, "Event isn't synchronous\n"); + ok(WaitForSingleObject(mch_event, 3000) == WAIT_OBJECT_0, "event sending didn't work\n"); + CloseHandle(mch_event); + ok(SetConsoleCtrlHandler(NULL, TRUE), "Couldn't turn off ctrl-c handling\n"); + mch_event = CreateEventA(NULL, TRUE, FALSE, NULL); + mch_count = 0; + ok(GenerateConsoleCtrlEvent(CTRL_C_EVENT, 0), "Couldn't send ctrl-c event\n"); + ok(WaitForSingleObject(mch_event, 3000) == WAIT_TIMEOUT && mch_count == 0, "Event shouldn't have been sent\n"); + CloseHandle(mch_event); + ok(SetConsoleCtrlHandler(mch, FALSE), "Couldn't remove handler\n"); + ok(!SetConsoleCtrlHandler(mch, FALSE), "Shouldn't succeed\n"); + ok(GetLastError() == ERROR_INVALID_PARAMETER, "Bad error %lu\n", GetLastError()); +} START_TEST(console) { @@ -553,9 +591,9 @@ START_TEST(console) /* testBottomScroll(); */ /* will test all the scrolling operations */ /* this one is disabled for now, Wine's result are way too bad */ - /* testScroll(hCon, sbi.dwSize); */ + testScroll(hConOut, sbi.dwSize); /* will test sb creation / modification... */ /* testScreenBuffer() */ - - /* still to be done: events generation, access rights & access on objects */ + testCtrlHandler(); + /* still to be done: access rights & access on objects */ } diff --git a/dlls/ntdll/env.c b/dlls/ntdll/env.c index 29e77863bc4..a0ceb1d6baa 100644 --- a/dlls/ntdll/env.c +++ b/dlls/ntdll/env.c @@ -453,7 +453,7 @@ NTSTATUS WINAPI RtlCreateProcessParameters( RTL_USER_PROCESS_PARAMETERS **result params->AllocationSize = total_size; params->Size = size; params->Flags = PROCESS_PARAMS_FLAG_NORMALIZED; - params->ProcessGroup = cur_params->ProcessGroup; + params->ConsoleFlags = cur_params->ConsoleFlags; params->Environment = Environment; /* all other fields are zero */ diff --git a/include/winternl.h b/include/winternl.h index 9aab5797303..3b5d65c556b 100644 --- a/include/winternl.h +++ b/include/winternl.h @@ -101,8 +101,8 @@ typedef struct _RTL_USER_PROCESS_PARAMETERS ULONG Size; ULONG Flags; ULONG DebugFlags; - HANDLE hConsole; - ULONG ProcessGroup; + HANDLE ConsoleHandle; + ULONG ConsoleFlags; HANDLE hStdInput; HANDLE hStdOutput; HANDLE hStdError; diff --git a/server/console.c b/server/console.c index b9da5a6dd6f..9312f4242df 100644 --- a/server/console.c +++ b/server/console.c @@ -386,7 +386,8 @@ static struct console_input* console_input_get( obj_handle_t handle, unsigned ac return console; } -struct console_signal_info { +struct console_signal_info +{ struct console_input *console; process_id_t group; int signal; @@ -399,13 +400,11 @@ static int propagate_console_signal_cb(struct process *process, void *user) if (process->console == csi->console && process->running_threads && (!csi->group || process->group_id == csi->group)) { - struct thread *thread = process->thread_list; - - while (thread) + /* find a suitable thread to signal */ + struct thread *thread; + for (thread = process->thread_list; thread; thread = thread->proc_next) { - struct thread *next = thread->proc_next; - send_thread_signal( thread, csi->signal ); - thread = next; + if (send_thread_signal( thread, csi->signal )) break; } } return FALSE; diff --git a/server/trace.c b/server/trace.c index 5ff521325b1..fecc0bf2304 100644 --- a/server/trace.c +++ b/server/trace.c @@ -324,8 +324,8 @@ static void dump_varargs_startup_info( size_t size ) fprintf( stderr, "Size=%lx,", params.Size ); fprintf( stderr, "Flags=%lx,", params.Flags ); fprintf( stderr, "DebugFlags=%lx,", params.DebugFlags ); - fprintf( stderr, "Console=%p,", params.hConsole ); - fprintf( stderr, "ProcessGroup=%lx,", params.ProcessGroup ); + fprintf( stderr, "ConsoleHandle=%p,", params.ConsoleHandle ); + fprintf( stderr, "ConsoleFlags=%lx,", params.ConsoleFlags ); fprintf( stderr, "hStdInput=%p,", params.hStdInput ); fprintf( stderr, "hStdOutput=%p,", params.hStdOutput ); fprintf( stderr, "hStdError=%p,", params.hStdError ); -- 2.11.4.GIT