- 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
This commit is contained in:
Eric Pouech 2004-09-08 01:25:05 +00:00 committed by Alexandre Julliard
parent fe442b21f7
commit 440ad8ccf2
6 changed files with 118 additions and 52 deletions

View File

@ -1,11 +1,11 @@
/* /*
* Win32 kernel functions * Win32 console functions
* *
* Copyright 1995 Martin von Loewis and Cameron Heide * Copyright 1995 Martin von Loewis and Cameron Heide
* Copyright 1997 Karl Garrison * Copyright 1997 Karl Garrison
* Copyright 1998 John Richardson * Copyright 1998 John Richardson
* Copyright 1998 Marcus Meissner * Copyright 1998 Marcus Meissner
* Copyright 2001,2002 Eric Pouech * Copyright 2001,2002,2004 Eric Pouech
* Copyright 2001 Alexandre Julliard * Copyright 2001 Alexandre Julliard
* *
* This library is free software; you can redistribute it and/or * This library is free software; you can redistribute it and/or
@ -52,6 +52,7 @@
#include "excpt.h" #include "excpt.h"
#include "console_private.h" #include "console_private.h"
#include "kernel_private.h" #include "kernel_private.h"
#include "thread.h"
WINE_DEFAULT_DEBUG_CHANNEL(console); WINE_DEFAULT_DEBUG_CHANNEL(console);
@ -1419,20 +1420,14 @@ static BOOL WINAPI CONSOLE_DefaultHandler(DWORD dwCtrlType)
* RETURNS * RETURNS
* Success: TRUE * Success: TRUE
* Failure: FALSE * 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; PHANDLER_ROUTINE handler;
struct ConsoleHandler* next; 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_DefaultConsoleHandler = {CONSOLE_DefaultHandler, NULL};
static struct ConsoleHandler* CONSOLE_Handlers = &CONSOLE_DefaultConsoleHandler; 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 WINAPI SetConsoleCtrlHandler(PHANDLER_ROUTINE func, BOOL add)
{ {
BOOL ret = TRUE; BOOL ret = TRUE;
FIXME("(%p,%i) - no error checking or testing yet\n", func, add); TRACE("(%p,%i)\n", func, add);
if (!func) 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) else if (add)
{ {
@ -1472,7 +1475,7 @@ BOOL WINAPI SetConsoleCtrlHandler(PHANDLER_ROUTINE func, BOOL add)
{ {
struct ConsoleHandler** ch; struct ConsoleHandler** ch;
RtlEnterCriticalSection(&CONSOLE_CritSect); RtlEnterCriticalSection(&CONSOLE_CritSect);
for (ch = &CONSOLE_Handlers; *ch; *ch = (*ch)->next) for (ch = &CONSOLE_Handlers; *ch; ch = &(*ch)->next)
{ {
if ((*ch)->handler == func) break; if ((*ch)->handler == func) break;
} }
@ -1484,18 +1487,19 @@ BOOL WINAPI SetConsoleCtrlHandler(PHANDLER_ROUTINE func, BOOL add)
if (rch == &CONSOLE_DefaultConsoleHandler) if (rch == &CONSOLE_DefaultConsoleHandler)
{ {
ERR("Who's trying to remove default handler???\n"); ERR("Who's trying to remove default handler???\n");
SetLastError(ERROR_INVALID_PARAMETER);
ret = FALSE; ret = FALSE;
} }
else else
{ {
rch = *ch; *ch = rch->next;
*ch = (*ch)->next;
HeapFree(GetProcessHeap(), 0, rch); HeapFree(GetProcessHeap(), 0, rch);
} }
} }
else else
{ {
WARN("Attempt to remove non-installed CtrlHandler %p\n", func); WARN("Attempt to remove non-installed CtrlHandler %p\n", func);
SetLastError(ERROR_INVALID_PARAMETER);
ret = FALSE; ret = FALSE;
} }
RtlLeaveCriticalSection(&CONSOLE_CritSect); RtlLeaveCriticalSection(&CONSOLE_CritSect);
@ -1509,18 +1513,42 @@ static WINE_EXCEPTION_FILTER(CONSOLE_CtrlEventHandler)
return EXCEPTION_EXECUTE_HANDLER; 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)
{ {
DWORD event = (DWORD)pmt;
struct ConsoleHandler* ch; struct ConsoleHandler* ch;
RtlEnterCriticalSection(&CONSOLE_CritSect); 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 */ /* the debugger didn't continue... so, pass to ctrl handlers */
}
RtlEnterCriticalSection(&CONSOLE_CritSect);
for (ch = CONSOLE_Handlers; ch; ch = ch->next) for (ch = CONSOLE_Handlers; ch; ch = ch->next)
{ {
if (ch->handler((DWORD)pmt)) break; if (ch->handler(event)) break;
} }
RtlLeaveCriticalSection(&CONSOLE_CritSect); 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 ??? */ /* FIXME: better test whether a console is attached to this process ??? */
extern unsigned CONSOLE_GetNumHistoryEntries(void); extern unsigned CONSOLE_GetNumHistoryEntries(void);
if (CONSOLE_GetNumHistoryEntries() == (unsigned)-1) return 0; if (CONSOLE_GetNumHistoryEntries() == (unsigned)-1) return 0;
if (CONSOLE_IgnoreCtrlC) return 1;
/* try to pass the exception to the debugger /* check if we have to ignore ctrl-C events */
* if it continues, there's nothing more to do if (!(NtCurrentTeb()->Peb->ProcessParameters->ConsoleFlags & 1))
* otherwise, we need to send the ctrl-event to the handlers
*/
__TRY
{ {
RaiseException( DBG_CONTROL_C, 0, 0, NULL ); /* Create a separate thread to signal all the events.
} * This is needed because:
__EXCEPT(CONSOLE_CtrlEventHandler) * - this function can be called in an Unix signal handler (hence on an
{ * different stack than the thread that's running). This breaks the
/* Create a separate thread to signal all the events. This would allow to * Win32 exception mechanisms (where the thread's stack is checked).
* synchronize between setting the handlers and actually calling them * - 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; return 1;
} }
@ -1586,6 +1611,10 @@ BOOL WINAPI GenerateConsoleCtrlEvent(DWORD dwCtrlEvent,
} }
SERVER_END_REQ; 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; return ret;
} }

View File

@ -1,7 +1,7 @@
/* /*
* Unit tests for console API * 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 * This library is free software; you can redistribute it and/or
* modify it under the terms of the GNU Lesser General Public * 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); testWriteWrappedProcessed(hCon, sbSize);
} }
#if 0
static void testScroll(HANDLE hCon, COORD sbSize) static void testScroll(HANDLE hCon, COORD sbSize)
{ {
SMALL_RECT scroll, clip; SMALL_RECT scroll, clip;
@ -477,6 +476,7 @@ static void testScroll(HANDLE hCon, COORD sbSize)
} }
} }
#if 0
/* clipping, src & dst rect do overlap */ /* clipping, src & dst rect do overlap */
resetContent(hCon, sbSize, TRUE); resetContent(hCon, sbSize, TRUE);
@ -511,8 +511,46 @@ static void testScroll(HANDLE hCon, COORD sbSize)
else okCHAR(hCon, c, CONTENT(c), DEFAULT_ATTRIB); else okCHAR(hCon, c, CONTENT(c), DEFAULT_ATTRIB);
} }
} }
}
#endif #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) START_TEST(console)
{ {
@ -553,9 +591,9 @@ START_TEST(console)
/* testBottomScroll(); */ /* testBottomScroll(); */
/* will test all the scrolling operations */ /* will test all the scrolling operations */
/* this one is disabled for now, Wine's result are way too bad */ /* 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... */ /* will test sb creation / modification... */
/* testScreenBuffer() */ /* testScreenBuffer() */
testCtrlHandler();
/* still to be done: events generation, access rights & access on objects */ /* still to be done: access rights & access on objects */
} }

View File

@ -453,7 +453,7 @@ NTSTATUS WINAPI RtlCreateProcessParameters( RTL_USER_PROCESS_PARAMETERS **result
params->AllocationSize = total_size; params->AllocationSize = total_size;
params->Size = size; params->Size = size;
params->Flags = PROCESS_PARAMS_FLAG_NORMALIZED; params->Flags = PROCESS_PARAMS_FLAG_NORMALIZED;
params->ProcessGroup = cur_params->ProcessGroup; params->ConsoleFlags = cur_params->ConsoleFlags;
params->Environment = Environment; params->Environment = Environment;
/* all other fields are zero */ /* all other fields are zero */

View File

@ -101,8 +101,8 @@ typedef struct _RTL_USER_PROCESS_PARAMETERS
ULONG Size; ULONG Size;
ULONG Flags; ULONG Flags;
ULONG DebugFlags; ULONG DebugFlags;
HANDLE hConsole; HANDLE ConsoleHandle;
ULONG ProcessGroup; ULONG ConsoleFlags;
HANDLE hStdInput; HANDLE hStdInput;
HANDLE hStdOutput; HANDLE hStdOutput;
HANDLE hStdError; HANDLE hStdError;

View File

@ -386,7 +386,8 @@ static struct console_input* console_input_get( obj_handle_t handle, unsigned ac
return console; return console;
} }
struct console_signal_info { struct console_signal_info
{
struct console_input *console; struct console_input *console;
process_id_t group; process_id_t group;
int signal; 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 && if (process->console == csi->console && process->running_threads &&
(!csi->group || process->group_id == csi->group)) (!csi->group || process->group_id == csi->group))
{ {
struct thread *thread = process->thread_list; /* find a suitable thread to signal */
struct thread *thread;
while (thread) for (thread = process->thread_list; thread; thread = thread->proc_next)
{ {
struct thread *next = thread->proc_next; if (send_thread_signal( thread, csi->signal )) break;
send_thread_signal( thread, csi->signal );
thread = next;
} }
} }
return FALSE; return FALSE;

View File

@ -324,8 +324,8 @@ static void dump_varargs_startup_info( size_t size )
fprintf( stderr, "Size=%lx,", params.Size ); fprintf( stderr, "Size=%lx,", params.Size );
fprintf( stderr, "Flags=%lx,", params.Flags ); fprintf( stderr, "Flags=%lx,", params.Flags );
fprintf( stderr, "DebugFlags=%lx,", params.DebugFlags ); fprintf( stderr, "DebugFlags=%lx,", params.DebugFlags );
fprintf( stderr, "Console=%p,", params.hConsole ); fprintf( stderr, "ConsoleHandle=%p,", params.ConsoleHandle );
fprintf( stderr, "ProcessGroup=%lx,", params.ProcessGroup ); fprintf( stderr, "ConsoleFlags=%lx,", params.ConsoleFlags );
fprintf( stderr, "hStdInput=%p,", params.hStdInput ); fprintf( stderr, "hStdInput=%p,", params.hStdInput );
fprintf( stderr, "hStdOutput=%p,", params.hStdOutput ); fprintf( stderr, "hStdOutput=%p,", params.hStdOutput );
fprintf( stderr, "hStdError=%p,", params.hStdError ); fprintf( stderr, "hStdError=%p,", params.hStdError );