- 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 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)
{
DWORD event = (DWORD)pmt;
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 */
}
RtlEnterCriticalSection(&CONSOLE_CritSect);
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
/* check if we have to ignore ctrl-C events */
if (!(NtCurrentTeb()->Peb->ProcessParameters->ConsoleFlags & 1))
{
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
/* 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;
}

View File

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

View File

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

View File

@ -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;

View File

@ -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;

View File

@ -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 );