diff --git a/dlls/kernel32/console.c b/dlls/kernel32/console.c
index 980ebef8eddd78c1e40fe120f0be23a44f95e97a..4e1c15cb7188c7bd5972888af9668ff02ec75d7a 100644
--- a/dlls/kernel32/console.c
+++ b/dlls/kernel32/console.c
@@ -889,151 +889,6 @@ BOOL WINAPI GetNumberOfConsoleMouseButtons(LPDWORD nrofbuttons)
     return TRUE;
 }
 
-/******************************************************************
- *		CONSOLE_DefaultHandler
- *
- * Final control event handler
- */
-static BOOL WINAPI CONSOLE_DefaultHandler(DWORD dwCtrlType)
-{
-    FIXME("Terminating process %x on event %x\n", GetCurrentProcessId(), dwCtrlType);
-    ExitProcess(0);
-    /* should never go here */
-    return TRUE;
-}
-
-/******************************************************************************
- * SetConsoleCtrlHandler [KERNEL32.@]  Adds function to calling process list
- *
- * PARAMS
- *    func [I] Address of handler function
- *    add  [I] Handler to add or remove
- *
- * RETURNS
- *    Success: TRUE
- *    Failure: FALSE
- */
-
-struct ConsoleHandler
-{
-    PHANDLER_ROUTINE            handler;
-    struct ConsoleHandler*      next;
-};
-
-static struct ConsoleHandler    CONSOLE_DefaultConsoleHandler = {CONSOLE_DefaultHandler, NULL};
-static struct ConsoleHandler*   CONSOLE_Handlers = &CONSOLE_DefaultConsoleHandler;
-
-/*****************************************************************************/
-
-/******************************************************************
- *		SetConsoleCtrlHandler (KERNEL32.@)
- */
-BOOL WINAPI SetConsoleCtrlHandler(PHANDLER_ROUTINE func, BOOL add)
-{
-    BOOL        ret = TRUE;
-
-    TRACE("(%p,%i)\n", func, add);
-
-    if (!func)
-    {
-        RtlEnterCriticalSection(&CONSOLE_CritSect);
-        if (add)
-            NtCurrentTeb()->Peb->ProcessParameters->ConsoleFlags |= 1;
-        else
-            NtCurrentTeb()->Peb->ProcessParameters->ConsoleFlags &= ~1;
-        RtlLeaveCriticalSection(&CONSOLE_CritSect);
-    }
-    else if (add)
-    {
-        struct ConsoleHandler*  ch = HeapAlloc(GetProcessHeap(), 0, sizeof(struct ConsoleHandler));
-
-        if (!ch) return FALSE;
-        ch->handler = func;
-        RtlEnterCriticalSection(&CONSOLE_CritSect);
-        ch->next = CONSOLE_Handlers;
-        CONSOLE_Handlers = ch;
-        RtlLeaveCriticalSection(&CONSOLE_CritSect);
-    }
-    else
-    {
-        struct ConsoleHandler**  ch;
-        RtlEnterCriticalSection(&CONSOLE_CritSect);
-        for (ch = &CONSOLE_Handlers; *ch; ch = &(*ch)->next)
-        {
-            if ((*ch)->handler == func) break;
-        }
-        if (*ch)
-        {
-            struct ConsoleHandler*   rch = *ch;
-
-            /* sanity check */
-            if (rch == &CONSOLE_DefaultConsoleHandler)
-            {
-                ERR("Who's trying to remove default handler???\n");
-                SetLastError(ERROR_INVALID_PARAMETER);
-                ret = FALSE;
-            }
-            else
-            {
-                *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);
-    }
-    return ret;
-}
-
-static LONG WINAPI CONSOLE_CtrlEventHandler(EXCEPTION_POINTERS *eptr)
-{
-    TRACE("(%x)\n", eptr->ExceptionRecord->ExceptionCode);
-    return EXCEPTION_EXECUTE_HANDLER;
-}
-
-/******************************************************************
- *		CONSOLE_SendEventThread
- *
- * Internal helper to pass an event to the list on installed handlers
- */
-static DWORD WINAPI CONSOLE_SendEventThread(void* pmt)
-{
-    DWORD_PTR                   event = (DWORD_PTR)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);
-    for (ch = CONSOLE_Handlers; ch; ch = ch->next)
-    {
-        if (ch->handler(event)) break;
-    }
-    RtlLeaveCriticalSection(&CONSOLE_CritSect);
-    return 1;
-}
-
 /******************************************************************
  *		CONSOLE_HandleCtrlC
  *
@@ -1041,6 +896,7 @@ static DWORD WINAPI CONSOLE_SendEventThread(void* pmt)
  */
 int     CONSOLE_HandleCtrlC(unsigned sig)
 {
+    extern DWORD WINAPI CtrlRoutine( void *arg );
     HANDLE thread;
 
     /* FIXME: better test whether a console is attached to this process ??? */
@@ -1059,7 +915,7 @@ int     CONSOLE_HandleCtrlC(unsigned sig)
          *    console critical section, we need another execution environment where
          *    we can wait on this critical section 
          */
-        thread = CreateThread(NULL, 0, CONSOLE_SendEventThread, (void*)CTRL_C_EVENT, 0, NULL);
+        thread = CreateThread(NULL, 0, CtrlRoutine, (void*)CTRL_C_EVENT, 0, NULL);
         if (thread == NULL)
             return 0;
 
diff --git a/dlls/kernel32/kernel32.spec b/dlls/kernel32/kernel32.spec
index 8645d48d7737bd1d0d3dff247d540929d23c7aea..17322bcd904df4b5b432bd638716dc183d504ba3 100644
--- a/dlls/kernel32/kernel32.spec
+++ b/dlls/kernel32/kernel32.spec
@@ -345,7 +345,7 @@
 @ stdcall CreateWaitableTimerExA(ptr str long long)
 @ stdcall -import CreateWaitableTimerExW(ptr wstr long long)
 @ stdcall -import CreateWaitableTimerW(ptr long wstr)
-# @ stub CtrlRoutine
+@ stdcall CtrlRoutine(ptr) kernelbase.CtrlRoutine
 @ stdcall -import DeactivateActCtx(long long)
 @ stdcall -import DebugActiveProcess(long)
 @ stdcall -import DebugActiveProcessStop(long)
@@ -1349,7 +1349,7 @@
 @ stdcall -import SetConsoleCP(long)
 # @ stub SetConsoleHistoryInfo
 @ stub SetConsoleCommandHistoryMode
-@ stdcall SetConsoleCtrlHandler(ptr long)
+@ stdcall -import SetConsoleCtrlHandler(ptr long)
 @ stub SetConsoleCursor
 @ stdcall -import SetConsoleCursorInfo(long ptr)
 @ stub SetConsoleCursorMode
diff --git a/dlls/kernelbase/console.c b/dlls/kernelbase/console.c
index 7dabc91e67c714d858259cadd9bd4c17f1585071..3171933b1221c246d864349ae400e542228cf559 100644
--- a/dlls/kernelbase/console.c
+++ b/dlls/kernelbase/console.c
@@ -55,6 +55,21 @@ static CRITICAL_SECTION console_section = { &critsect_debug, -1, 0, 0, 0, 0 };
 
 static WCHAR input_exe[MAX_PATH + 1];
 
+struct ctrl_handler
+{
+    PHANDLER_ROUTINE     func;
+    struct ctrl_handler *next;
+};
+
+static BOOL WINAPI default_ctrl_handler( DWORD type )
+{
+    FIXME( "Terminating process %x on event %x\n", GetCurrentProcessId(), type );
+    RtlExitUserProcess( 0 );
+    return TRUE;
+}
+
+static struct ctrl_handler default_handler = { default_ctrl_handler, NULL };
+static struct ctrl_handler *ctrl_handlers = &default_handler;
 
 /* map a kernel32 console handle onto a real wineserver handle */
 static inline obj_handle_t console_handle_unmap( HANDLE h )
@@ -239,6 +254,45 @@ HANDLE WINAPI DECLSPEC_HOTPATCH CreateConsoleScreenBuffer( DWORD access, DWORD s
 }
 
 
+/******************************************************************************
+ *	CtrlRoutine   (kernelbase.@)
+ */
+DWORD WINAPI CtrlRoutine( void *arg )
+{
+    DWORD_PTR event = (DWORD_PTR)arg;
+    struct ctrl_handler *handler;
+
+    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_ALL
+        {
+            caught_by_dbg = FALSE;
+        }
+        __ENDTRY
+        if (caught_by_dbg) return 0;
+    }
+
+    if (NtCurrentTeb()->Peb->ProcessParameters->ConsoleFlags & 1) return 0;
+
+    RtlEnterCriticalSection( &console_section );
+    for (handler = ctrl_handlers; handler; handler = handler->next)
+    {
+        if (handler->func( event )) break;
+    }
+    RtlLeaveCriticalSection( &console_section );
+    return 1;
+}
+
+
 /******************************************************************************
  *	FillConsoleOutputAttribute   (kernelbase.@)
  */
@@ -985,6 +1039,57 @@ BOOL WINAPI DECLSPEC_HOTPATCH SetConsoleCP( UINT cp )
 }
 
 
+/******************************************************************************
+ *	SetConsoleCtrlHandler   (kernelbase.@)
+ */
+BOOL WINAPI DECLSPEC_HOTPATCH SetConsoleCtrlHandler( PHANDLER_ROUTINE func, BOOL add )
+{
+    struct ctrl_handler *handler;
+    BOOL ret = FALSE;
+
+    TRACE( "(%p,%d)\n", func, add );
+
+    RtlEnterCriticalSection( &console_section );
+
+    if (!func)
+    {
+        if (add) NtCurrentTeb()->Peb->ProcessParameters->ConsoleFlags |= 1;
+        else NtCurrentTeb()->Peb->ProcessParameters->ConsoleFlags &= ~1;
+        ret = TRUE;
+    }
+    else if (add)
+    {
+        if ((handler = RtlAllocateHeap( GetProcessHeap(), 0, sizeof(*handler) )))
+        {
+            handler->func = func;
+            handler->next = ctrl_handlers;
+            ctrl_handlers = handler;
+            ret = TRUE;
+        }
+    }
+    else
+    {
+        struct ctrl_handler **p_handler;
+
+        for (p_handler = &ctrl_handlers; *p_handler; p_handler = &(*p_handler)->next)
+        {
+            if ((*p_handler)->func == func) break;
+        }
+        if (*p_handler && *p_handler != &default_handler)
+        {
+            handler = *p_handler;
+            *p_handler = handler->next;
+            RtlFreeHeap( GetProcessHeap(), 0, handler );
+            ret = TRUE;
+        }
+        else SetLastError( ERROR_INVALID_PARAMETER );
+    }
+
+    RtlLeaveCriticalSection( &console_section );
+    return ret;
+}
+
+
 /******************************************************************************
  *	SetConsoleCursorInfo   (kernelbase.@)
  */
diff --git a/dlls/kernelbase/kernelbase.spec b/dlls/kernelbase/kernelbase.spec
index 10de1821d1c9e424f659a92c9428e7c2b219599d..5e177a4932703b25ef5e3a279cd3a2eb0d52811c 100644
--- a/dlls/kernelbase/kernelbase.spec
+++ b/dlls/kernelbase/kernelbase.spec
@@ -234,7 +234,7 @@
 @ stdcall CreateWaitableTimerExW(ptr wstr long long)
 @ stdcall CreateWaitableTimerW(ptr long wstr)
 @ stdcall CreateWellKnownSid(long ptr ptr ptr)
-# @ stub CtrlRoutine
+@ stdcall CtrlRoutine(ptr)
 # @ stub CveEventWrite
 @ stdcall DeactivateActCtx(long long)
 @ stdcall DebugActiveProcess(long)
@@ -1404,7 +1404,7 @@
 @ stdcall SetComputerNameW(wstr)
 @ stdcall SetConsoleActiveScreenBuffer(long)
 @ stdcall SetConsoleCP(long)
-@ stdcall SetConsoleCtrlHandler(ptr long) kernel32.SetConsoleCtrlHandler
+@ stdcall SetConsoleCtrlHandler(ptr long)
 @ stdcall SetConsoleCursorInfo(long ptr)
 @ stdcall SetConsoleCursorPosition(long long)
 @ stdcall SetConsoleInputExeNameA(str)