Added specific routines for OUTPUT_DEBUG_STRING and EXCEPTION debug events.
Generate a breakpoint event on process attach.
Misc cleanups in request handling.

diff --git a/dlls/ntdll/exception.c b/dlls/ntdll/exception.c
index c8426b2..b36fff9 100644
--- a/dlls/ntdll/exception.c
+++ b/dlls/ntdll/exception.c
@@ -13,9 +13,10 @@
 #include "wine/exception.h"
 #include "stackframe.h"
 #include "miscemu.h"
+#include "server.h"
 #include "debugtools.h"
 
-DEFAULT_DEBUG_CHANNEL(seh)
+DEFAULT_DEBUG_CHANNEL(seh);
 
 /* Exception record for handling exceptions happening inside exception handlers */
 typedef struct
@@ -84,6 +85,22 @@
 }
 
 
+/**********************************************************************
+ *           EXC_SendEvent
+ *
+ * Send an EXCEPTION_DEBUG_EVENT event to the debugger.
+ */
+static inline int send_debug_event( EXCEPTION_RECORD *rec, int first_chance, CONTEXT *context )
+{
+    struct exception_event_request *req = get_req_buffer();
+    req->record  = *rec;
+    req->first   = first_chance;
+    req->context = *context;
+    if (!server_call_noerr( REQ_EXCEPTION_EVENT )) *context = req->context;
+    return req->status;
+}
+
+
 /*******************************************************************
  *         EXC_DefaultHandling
  *
@@ -91,8 +108,7 @@
  */
 static void EXC_DefaultHandling( EXCEPTION_RECORD *rec, CONTEXT *context )
 {
-    if (DEBUG_SendExceptionEvent( rec, FALSE, context ) == DBG_CONTINUE)
-        return;  /* continue execution */
+    if (send_debug_event( rec, FALSE, context ) == DBG_CONTINUE) return;  /* continue execution */
 
     if (rec->ExceptionFlags & EH_STACK_INVALID)
         ERR("Exception frame is not in stack limits => unable to dispatch exception.\n");
@@ -117,8 +133,7 @@
 
     TRACE( "code=%lx flags=%lx\n", rec->ExceptionCode, rec->ExceptionFlags );
 
-    if (DEBUG_SendExceptionEvent( rec, TRUE, context ) == DBG_CONTINUE)
-        return;  /* continue execution */
+    if (send_debug_event( rec, TRUE, context ) == DBG_CONTINUE) return;  /* continue execution */
 
     frame = NtCurrentTeb()->except;
     nested_frame = NULL;
diff --git a/include/server.h b/include/server.h
index ee70bd1..ffb014b 100644
--- a/include/server.h
+++ b/include/server.h
@@ -31,14 +31,8 @@
 /* definitions of the event data depending on the event code */
 struct debug_event_exception
 {
-    int        code;           /* exception code */
-    int        flags;          /* exception flags */
-    void      *record;         /* exception record ptr */
-    void      *addr;           /* exception address */
-    int        nb_params;      /* exceptions parameters */
-    int        params[15];
-    int        first_chance;   /* first chance to handle it? */
-    CONTEXT    context;        /* thread context */
+    EXCEPTION_RECORD record;   /* exception record */
+    int              first;    /* first chance exception? */
 };
 struct debug_event_create_thread
 {
@@ -813,11 +807,22 @@
 };
 
 
-/* Send a debug event */
-struct send_debug_event_request
+/* Send an exception event */
+struct exception_event_request
 {
-    OUT int           status;      /* event continuation status */
-    IN  debug_event_t event;       /* debug event data */
+    IN  EXCEPTION_RECORD record;   /* exception record */
+    IN  int              first;    /* first chance exception? */
+    IN  CONTEXT          context;  /* thread context */
+    OUT int              status;   /* event continuation status */
+};
+
+
+/* Send an output string to the debugger */
+struct output_debug_string_request
+{
+    IN  void*         string;      /* string to display (in debugged process address space) */
+    IN  int           unicode;     /* is it Unicode? */
+    IN  int           length;      /* string length */
 };
 
 
@@ -1164,7 +1169,8 @@
     REQ_CREATE_SNAPSHOT,
     REQ_NEXT_PROCESS,
     REQ_WAIT_DEBUG_EVENT,
-    REQ_SEND_DEBUG_EVENT,
+    REQ_EXCEPTION_EVENT,
+    REQ_OUTPUT_DEBUG_STRING,
     REQ_CONTINUE_DEBUG_EVENT,
     REQ_DEBUG_PROCESS,
     REQ_READ_PROCESS_MEMORY,
@@ -1196,7 +1202,7 @@
     REQ_NB_REQUESTS
 };
 
-#define SERVER_PROTOCOL_VERSION 2
+#define SERVER_PROTOCOL_VERSION 3
 
 /* ### make_requests end ### */
 /* Everything above this line is generated automatically by tools/make_requests */
diff --git a/scheduler/debugger.c b/scheduler/debugger.c
index d11a0d7..1d3ba43 100644
--- a/scheduler/debugger.c
+++ b/scheduler/debugger.c
@@ -7,39 +7,12 @@
 #include <string.h>
 
 #include "winerror.h"
-#include "process.h"
 #include "server.h"
 #include "debugtools.h"
 
 DEFAULT_DEBUG_CHANNEL(debugstr);
 
 
-/**********************************************************************
- *           DEBUG_SendExceptionEvent
- *
- * Send an EXCEPTION_DEBUG_EVENT event to the current process debugger.
- */
-DWORD DEBUG_SendExceptionEvent( EXCEPTION_RECORD *rec, BOOL first_chance, CONTEXT *context )
-{
-    int i;
-    struct send_debug_event_request *req = get_req_buffer();
-
-    req->event.code = EXCEPTION_DEBUG_EVENT;
-    req->event.info.exception.code         = rec->ExceptionCode;
-    req->event.info.exception.flags        = rec->ExceptionFlags;
-    req->event.info.exception.record       = rec->ExceptionRecord;
-    req->event.info.exception.addr         = rec->ExceptionAddress;
-    req->event.info.exception.nb_params    = rec->NumberParameters;
-    req->event.info.exception.first_chance = first_chance;
-    req->event.info.exception.context      = *context;
-    for (i = 0; i < req->event.info.exception.nb_params; i++)
-        req->event.info.exception.params[i] = rec->ExceptionInformation[i];
-    if (!server_call_noerr( REQ_SEND_DEBUG_EVENT ))
-        *context = req->event.info.exception.context;
-    return req->status;
-}
-
-
 /******************************************************************************
  *           WaitForDebugEvent   (KERNEL32.720)
  *
@@ -54,7 +27,6 @@
 BOOL WINAPI WaitForDebugEvent( LPDEBUG_EVENT event, DWORD timeout )
 {
     struct wait_debug_event_request *req = get_req_buffer();
-    int i;
 
     req->timeout = timeout;
     if (server_call( REQ_WAIT_DEBUG_EVENT )) return FALSE;
@@ -70,14 +42,8 @@
         SetLastError( ERROR_SEM_TIMEOUT );
         return FALSE;
     case EXCEPTION_DEBUG_EVENT:
-        event->u.Exception.ExceptionRecord.ExceptionCode    = req->event.info.exception.code;
-        event->u.Exception.ExceptionRecord.ExceptionFlags   = req->event.info.exception.flags;
-        event->u.Exception.ExceptionRecord.ExceptionRecord  = req->event.info.exception.record;
-        event->u.Exception.ExceptionRecord.ExceptionAddress = req->event.info.exception.addr;
-        event->u.Exception.ExceptionRecord.NumberParameters = req->event.info.exception.nb_params;
-        for (i = 0; i < req->event.info.exception.nb_params; i++)
-            event->u.Exception.ExceptionRecord.ExceptionInformation[i] = req->event.info.exception.params[i];
-        event->u.Exception.dwFirstChance = req->event.info.exception.first_chance;
+        event->u.Exception.ExceptionRecord = req->event.info.exception.record;
+        event->u.Exception.dwFirstChance   = req->event.info.exception.first;
         break;
     case CREATE_THREAD_DEBUG_EVENT:
         event->u.CreateThread.hThread           = req->event.info.create_thread.handle;
@@ -158,12 +124,11 @@
  */
 void WINAPI OutputDebugStringA( LPCSTR str )
 {
-    struct send_debug_event_request *req = get_req_buffer();
-    req->event.code = OUTPUT_DEBUG_STRING_EVENT;
-    req->event.info.output_string.string  = (void *)str;
-    req->event.info.output_string.unicode = 0;
-    req->event.info.output_string.length  = strlen(str) + 1;
-    server_call_noerr( REQ_SEND_DEBUG_EVENT );
+    struct output_debug_string_request *req = get_req_buffer();
+    req->string  = (void *)str;
+    req->unicode = 0;
+    req->length  = strlen(str) + 1;
+    server_call_noerr( REQ_OUTPUT_DEBUG_STRING );
     TRACE("%s\n", str);
 }
 
@@ -173,12 +138,11 @@
  */
 void WINAPI OutputDebugStringW( LPCWSTR str )
 {
-    struct send_debug_event_request *req = get_req_buffer();
-    req->event.code = OUTPUT_DEBUG_STRING_EVENT;
-    req->event.info.output_string.string  = (void *)str;
-    req->event.info.output_string.unicode = 1;
-    req->event.info.output_string.length  = (lstrlenW(str) + 1) * sizeof(WCHAR);
-    server_call_noerr( REQ_SEND_DEBUG_EVENT );
+    struct output_debug_string_request *req = get_req_buffer();
+    req->string  = (void *)str;
+    req->unicode = 1;
+    req->length  = (lstrlenW(str) + 1) * sizeof(WCHAR);
+    server_call_noerr( REQ_OUTPUT_DEBUG_STRING );
     TRACE("%s\n", debugstr_w(str));
 }
 
diff --git a/server/context_i386.c b/server/context_i386.c
index 9b65e25..787192c 100644
--- a/server/context_i386.c
+++ b/server/context_i386.c
@@ -457,13 +457,12 @@
 DECL_HANDLER(get_thread_context)
 {
     struct thread *thread;
-    CONTEXT *context;
 
     if ((thread = get_thread_from_handle( req->handle, THREAD_GET_CONTEXT )))
     {
-        if ((context = get_debug_context( thread )))  /* thread is inside an exception event */
+        if (thread->context)  /* thread is inside an exception event */
         {
-            copy_context( &req->context, context, req->flags );
+            copy_context( &req->context, thread->context, req->flags );
         }
         else
         {
@@ -481,13 +480,12 @@
 DECL_HANDLER(set_thread_context)
 {
     struct thread *thread;
-    CONTEXT *context;
 
     if ((thread = get_thread_from_handle( req->handle, THREAD_SET_CONTEXT )))
     {
-        if ((context = get_debug_context( thread )))  /* thread is inside an exception event */
+        if (thread->context)  /* thread is inside an exception event */
         {
-            copy_context( context, &req->context, req->flags );
+            copy_context( thread->context, &req->context, req->flags );
         }
         else
         {
diff --git a/server/debugger.c b/server/debugger.c
index 7b7b9b4..20a5452 100644
--- a/server/debugger.c
+++ b/server/debugger.c
@@ -83,93 +83,132 @@
 };
 
 
-/* initialise the fields that do not need to be filled by the client */
-static int fill_debug_event( struct debug_event *event, void *arg )
+/* routines to build an event according to its type */
+
+static int fill_exception_event( struct debug_event *event, void *arg )
 {
-    struct process *debugger = event->debugger->process;
-    struct process *process;
-    struct thread *thread;
-    struct process_dll *dll;
-    int handle;
-
-    /* some events need special handling */
-    switch(event->data.code)
-    {
-    case CREATE_THREAD_DEBUG_EVENT:
-        thread = arg;
-        /* documented: THREAD_GET_CONTEXT | THREAD_SET_CONTEXT | THREAD_SUSPEND_RESUME */
-        if ((handle = alloc_handle( debugger, thread, THREAD_ALL_ACCESS, FALSE )) == -1)
-            return 0;
-        event->data.info.create_thread.handle = handle;
-        event->data.info.create_thread.teb    = thread->teb;
-        event->data.info.create_thread.start  = thread->entry;
-        break;
-    case CREATE_PROCESS_DEBUG_EVENT:
-        process = arg;
-        thread = process->thread_list;
-        /* documented: PROCESS_VM_READ | PROCESS_VM_WRITE */
-        if ((handle = alloc_handle( debugger, process, PROCESS_ALL_ACCESS, FALSE )) == -1)
-            return 0;
-        event->data.info.create_process.process = handle;
-
-        /* documented: THREAD_GET_CONTEXT | THREAD_SET_CONTEXT | THREAD_SUSPEND_RESUME */
-        if ((handle = alloc_handle( debugger, thread, THREAD_ALL_ACCESS, FALSE )) == -1)
-        {
-            close_handle( debugger, event->data.info.create_process.process );
-            return 0;
-        }
-        event->data.info.create_process.thread = handle;
-
-        handle = -1;
-        if (process->exe.file &&
-            /* the doc says write access too, but this doesn't seem a good idea */
-            ((handle = alloc_handle( debugger, process->exe.file, GENERIC_READ, FALSE )) == -1))
-        {
-            close_handle( debugger, event->data.info.create_process.process );
-            close_handle( debugger, event->data.info.create_process.thread );
-            return 0;
-        }
-        event->data.info.create_process.file       = handle;
-        event->data.info.create_process.teb        = thread->teb;
-        event->data.info.create_process.base       = process->exe.base;
-        event->data.info.create_process.start      = thread->entry;
-        event->data.info.create_process.dbg_offset = process->exe.dbg_offset;
-        event->data.info.create_process.dbg_size   = process->exe.dbg_size;
-        event->data.info.create_process.name       = 0;
-        event->data.info.create_process.unicode    = 0;
-        break;
-    case LOAD_DLL_DEBUG_EVENT:
-        dll = arg;
-        handle = -1;
-        if (dll->file &&
-            (handle = alloc_handle( debugger, dll->file, GENERIC_READ, FALSE )) == -1)
-            return 0;
-        event->data.info.load_dll.handle     = handle;
-        event->data.info.load_dll.base       = dll->base;
-        event->data.info.load_dll.dbg_offset = dll->dbg_offset;
-        event->data.info.load_dll.dbg_size   = dll->dbg_size;
-        event->data.info.load_dll.name       = dll->name;
-        event->data.info.load_dll.unicode    = 0;
-        break;
-    case EXIT_PROCESS_DEBUG_EVENT:
-        process = arg;
-        event->data.info.exit.exit_code = process->exit_code;
-        break;
-    case EXIT_THREAD_DEBUG_EVENT:
-        thread = arg;
-        event->data.info.exit.exit_code = thread->exit_code;
-        break;
-    case UNLOAD_DLL_DEBUG_EVENT:
-        event->data.info.unload_dll.base = arg;
-        break;
-    case EXCEPTION_DEBUG_EVENT:
-    case OUTPUT_DEBUG_STRING_EVENT:
-    case RIP_EVENT:
-        break;
-    }
+    memcpy( &event->data.info.exception, arg, sizeof(event->data.info.exception) );
     return 1;
 }
 
+static int fill_create_thread_event( struct debug_event *event, void *arg )
+{
+    struct process *debugger = event->debugger->process;
+    struct thread *thread = arg;
+    int handle;
+    
+    /* documented: THREAD_GET_CONTEXT | THREAD_SET_CONTEXT | THREAD_SUSPEND_RESUME */
+    if ((handle = alloc_handle( debugger, thread, THREAD_ALL_ACCESS, FALSE )) == -1)
+        return 0;
+    event->data.info.create_thread.handle = handle;
+    event->data.info.create_thread.teb    = thread->teb;
+    event->data.info.create_thread.start  = thread->entry;
+    return 1;
+}
+
+static int fill_create_process_event( struct debug_event *event, void *arg )
+{
+    struct process *debugger = event->debugger->process;
+    struct process *process = arg;
+    struct thread *thread = process->thread_list;
+    int handle;
+
+    /* documented: PROCESS_VM_READ | PROCESS_VM_WRITE */
+    if ((handle = alloc_handle( debugger, process, PROCESS_ALL_ACCESS, FALSE )) == -1)
+        return 0;
+    event->data.info.create_process.process = handle;
+
+    /* documented: THREAD_GET_CONTEXT | THREAD_SET_CONTEXT | THREAD_SUSPEND_RESUME */
+    if ((handle = alloc_handle( debugger, thread, THREAD_ALL_ACCESS, FALSE )) == -1)
+    {
+        close_handle( debugger, event->data.info.create_process.process );
+        return 0;
+    }
+    event->data.info.create_process.thread = handle;
+
+    handle = -1;
+    if (process->exe.file &&
+        /* the doc says write access too, but this doesn't seem a good idea */
+        ((handle = alloc_handle( debugger, process->exe.file, GENERIC_READ, FALSE )) == -1))
+    {
+        close_handle( debugger, event->data.info.create_process.process );
+        close_handle( debugger, event->data.info.create_process.thread );
+        return 0;
+    }
+    event->data.info.create_process.file       = handle;
+    event->data.info.create_process.teb        = thread->teb;
+    event->data.info.create_process.base       = process->exe.base;
+    event->data.info.create_process.start      = thread->entry;
+    event->data.info.create_process.dbg_offset = process->exe.dbg_offset;
+    event->data.info.create_process.dbg_size   = process->exe.dbg_size;
+    event->data.info.create_process.name       = 0;
+    event->data.info.create_process.unicode    = 0;
+    return 1;
+}
+
+static int fill_exit_thread_event( struct debug_event *event, void *arg )
+{
+    struct thread *thread = arg;
+    event->data.info.exit.exit_code = thread->exit_code;
+    return 1;
+}
+
+static int fill_exit_process_event( struct debug_event *event, void *arg )
+{
+    struct process *process = arg;
+    event->data.info.exit.exit_code = process->exit_code;
+    return 1;
+}
+
+static int fill_load_dll_event( struct debug_event *event, void *arg )
+{
+    struct process *debugger = event->debugger->process;
+    struct process_dll *dll = arg;
+    int handle = -1;
+
+    if (dll->file && (handle = alloc_handle( debugger, dll->file, GENERIC_READ, FALSE )) == -1)
+        return 0;
+    event->data.info.load_dll.handle     = handle;
+    event->data.info.load_dll.base       = dll->base;
+    event->data.info.load_dll.dbg_offset = dll->dbg_offset;
+    event->data.info.load_dll.dbg_size   = dll->dbg_size;
+    event->data.info.load_dll.name       = dll->name;
+    event->data.info.load_dll.unicode    = 0;
+    return 1;
+}
+
+static int fill_unload_dll_event( struct debug_event *event, void *arg )
+{
+    event->data.info.unload_dll.base = arg;
+    return 1;
+}
+
+static int fill_output_debug_string_event( struct debug_event *event, void *arg )
+{
+    struct output_debug_string_request *req = arg;
+    event->data.info.output_string.string  = req->string;
+    event->data.info.output_string.unicode = req->unicode;
+    event->data.info.output_string.length  = req->length;
+    return 1;
+}
+
+typedef int (*fill_event_func)( struct debug_event *event, void *arg );
+
+#define NB_DEBUG_EVENTS OUTPUT_DEBUG_STRING_EVENT  /* RIP_EVENT not supported */
+
+static const fill_event_func fill_debug_event[NB_DEBUG_EVENTS] =
+{
+    fill_exception_event,            /* EXCEPTION_DEBUG_EVENT */
+    fill_create_thread_event,        /* CREATE_THREAD_DEBUG_EVENT */
+    fill_create_process_event,       /* CREATE_PROCESS_DEBUG_EVENT */
+    fill_exit_thread_event,          /* EXIT_THREAD_DEBUG_EVENT */
+    fill_exit_process_event,         /* EXIT_PROCESS_DEBUG_EVENT */
+    fill_load_dll_event,             /* LOAD_DLL_DEBUG_EVENT */
+    fill_unload_dll_event,           /* UNLOAD_DLL_DEBUG_EVENT */
+    fill_output_debug_string_event   /* OUTPUT_DEBUG_STRING_EVENT */
+};
+
+
 /* unlink the first event from the queue */
 static void unlink_event( struct debug_ctx *debug_ctx, struct debug_event *event )
 {
@@ -228,18 +267,13 @@
 }
 
 /* build a reply for the send_event request */
-static void build_send_event_reply( struct thread *thread, struct object *obj, int signaled )
+static void build_exception_event_reply( struct thread *thread, struct object *obj, int signaled )
 {
-    struct send_debug_event_request *req = get_req_ptr( thread );
+    struct exception_event_request *req = get_req_ptr( thread );
     struct debug_event *event = (struct debug_event *)obj;
     assert( obj->ops == &debug_event_ops );
-
     req->status = event->status;
-    /* copy the context into the reply */
-    if (event->data.code == EXCEPTION_DEBUG_EVENT)
-        memcpy( &req->event.info.exception.context,
-                &event->data.info.exception.context,
-                sizeof(req->event.info.exception.context) );
+    thread->context = NULL;
 }
 
 static void debug_event_dump( struct object *obj, int verbose )
@@ -363,28 +397,27 @@
 }
 
 /* queue a debug event for a debugger */
-static struct debug_event *queue_debug_event( struct thread *thread, int code, void *arg,
-                                              debug_event_t *data )
+static struct debug_event *queue_debug_event( struct thread *thread, int code, void *arg )
 {
     struct thread *debugger = thread->process->debugger;
     struct debug_ctx *debug_ctx = debugger->debug_ctx;
     struct debug_event *event;
 
+    assert( code > 0 && code <= NB_DEBUG_EVENTS );
     assert( debug_ctx );
     /* cannot queue a debug event for myself */
     assert( debugger->process != thread->process );
 
     /* build the event */
     if (!(event = alloc_object( &debug_event_ops, -1 ))) return NULL;
-    event->next     = NULL;
-    event->prev     = NULL;
-    event->state    = EVENT_QUEUED;
-    event->sender   = (struct thread *)grab_object( thread );
-    event->debugger = (struct thread *)grab_object( debugger );
-    if (data) memcpy( &event->data, data, sizeof(event->data) );
+    event->next      = NULL;
+    event->prev      = NULL;
+    event->state     = EVENT_QUEUED;
+    event->sender    = (struct thread *)grab_object( thread );
+    event->debugger  = (struct thread *)grab_object( debugger );
     event->data.code = code;
 
-    if (!fill_debug_event( event, arg ))
+    if (!fill_debug_event[code-1]( event, arg ))
     {
         event->data.code = -1;  /* make sure we don't attempt to close handles */
         release_object( event );
@@ -401,11 +434,43 @@
 {
     if (thread->process->debugger)
     {
-        struct debug_event *event = queue_debug_event( thread, code, arg, NULL );
+        struct debug_event *event = queue_debug_event( thread, code, arg );
         if (event) release_object( event );
     }
 }
 
+/* attach a process to a debugger thread and suspend it */
+static int debugger_attach( struct process *process, struct thread *debugger )
+{
+    struct thread *thread;
+
+    if (process->debugger) goto error;  /* already being debugged */
+    if (process->init_event) goto error;  /* still starting up */
+
+    /* make sure we don't create a debugging loop */
+    for (thread = debugger; thread; thread = thread->process->debugger)
+        if (thread->process == process) goto error;
+
+    suspend_process( process );
+
+    /* we must have been able to attach all threads */
+    for (thread = process->thread_list; thread; thread = thread->proc_next)
+        if (!thread->attached)
+        {
+            fprintf( stderr, "%p not attached\n", thread );
+            resume_process( process );
+            goto error;
+        }
+
+    if (set_process_debugger( process, debugger )) return 1;
+    resume_process( process );
+    return 0;
+
+ error:
+    set_error( STATUS_ACCESS_DENIED );
+    return 0;
+}
+
 /* generate all startup events of a given process */
 void generate_startup_debug_events( struct process *process )
 {
@@ -426,40 +491,12 @@
     }
 }
 
-/* return a pointer to the context in case the thread is inside an exception event */
-CONTEXT *get_debug_context( struct thread *thread )
-{
-    struct debug_event *event;
-    struct thread *debugger = thread->process->debugger;
-
-    if (!debugger) return NULL;  /* not being debugged */
-    assert( debugger->debug_ctx );
-
-    /* find the exception event in the debugger's queue */
-    for (event = debugger->debug_ctx->event_head; event; event = event->next)
-        if (event->sender == thread && (event->data.code == EXCEPTION_DEBUG_EVENT))
-            return &event->data.info.exception.context;
-    return NULL;
-}
-
-/* attach a process to a debugger thread */
-int debugger_attach( struct process *process, struct thread *debugger )
+/* set the debugger of a given process */
+int set_process_debugger( struct process *process, struct thread *debugger )
 {
     struct debug_ctx *debug_ctx;
-    struct thread *thread;
 
-    if (process->debugger)  /* already being debugged */
-    {
-        set_error( STATUS_ACCESS_DENIED );
-        return 0;
-    }
-    /* make sure we don't create a debugging loop */
-    for (thread = debugger; thread; thread = thread->process->debugger)
-        if (thread->process == process)
-        {
-            set_error( STATUS_ACCESS_DENIED );
-            return 0;
-        }
+    assert( !process->debugger );
 
     if (!debugger->debug_ctx)  /* need to allocate a context */
     {
@@ -515,34 +552,54 @@
 /* Start debugging an existing process */
 DECL_HANDLER(debug_process)
 {
+    struct debug_event_exception data;
+    struct debug_event *event;
     struct process *process = get_process_from_id( req->pid );
     if (!process) return;
+
     if (debugger_attach( process, current ))
     {
         generate_startup_debug_events( process );
-        /* FIXME: breakpoint exception event */
+        resume_process( process );
+
+        data.record.ExceptionCode    = EXCEPTION_BREAKPOINT;
+        data.record.ExceptionFlags   = EXCEPTION_CONTINUABLE;
+        data.record.ExceptionRecord  = NULL;
+        data.record.ExceptionAddress = process->thread_list->entry; /* FIXME */
+        data.record.NumberParameters = 0;
+        data.first = 1;
+        if ((event = queue_debug_event( process->thread_list, EXCEPTION_DEBUG_EVENT, &data )))
+            release_object( event );
     }
     release_object( process );
 }
 
-/* Send a debug event */
-DECL_HANDLER(send_debug_event)
+/* send an exception event */
+DECL_HANDLER(exception_event)
 {
-    struct debug_event *event;
-    int code = req->event.code;
+    if (current->process->debugger)
+    {
+        struct debug_event_exception data;
+        struct debug_event *event;
 
-    if ((code <= 0) || (code > RIP_EVENT))
-    {
-        fatal_protocol_error( current, "send_debug_event: bad code %d\n", code );
-        return;
+        data.record = req->record;
+        data.first  = req->first;
+        if ((event = queue_debug_event( current, EXCEPTION_DEBUG_EVENT, &data )))
+        {
+            struct object *obj = &event->obj;
+            current->context = &req->context;
+            sleep_on( 1, &obj, 0, -1, build_exception_event_reply );
+            release_object( event );
+        }
     }
-    req->status = 0;
-    if (current->process->debugger && ((event = queue_debug_event( current, code,
-                                                                   NULL, &req->event ))))
+}
+
+/* send an output string to the debugger */
+DECL_HANDLER(output_debug_string)
+{
+    if (current->process->debugger)
     {
-        /* wait for continue_debug_event */
-        struct object *obj = &event->obj;
-        sleep_on( 1, &obj, 0, -1, build_send_event_reply );
-        release_object( event );
+        struct debug_event *event = queue_debug_event( current, OUTPUT_DEBUG_STRING_EVENT, req );
+        if (event) release_object( event );
     }
 }
diff --git a/server/file.c b/server/file.c
index 70e12c8..e5b514a 100644
--- a/server/file.c
+++ b/server/file.c
@@ -468,15 +468,17 @@
     struct file *file;
 
     req->handle = -1;
-    if ((fd = dup(fd)) != -1)
+    if (current->pass_fd != -1)
     {
-        if ((file = create_file_for_fd( fd, req->access, FILE_SHARE_READ | FILE_SHARE_WRITE, 0 )))
+        if ((file = create_file_for_fd( current->pass_fd, req->access,
+                                        FILE_SHARE_READ | FILE_SHARE_WRITE, 0 )))
         {
             req->handle = alloc_handle( current->process, file, req->access, 0 );
             release_object( file );
         }
+        current->pass_fd = -1;
     }
-    else file_set_error();
+    else set_error( STATUS_INVALID_PARAMETER );
 }
 
 /* get a Unix fd to read from a file */
diff --git a/server/main.c b/server/main.c
index 50d667b..e106fba 100644
--- a/server/main.c
+++ b/server/main.c
@@ -60,7 +60,6 @@
 /* initialize signal handling */
 static void signal_init(void)
 {
-#if 0
     if (!debug_level)
     {
         switch(fork())
@@ -74,7 +73,6 @@
             exit(0);
         }
     }
-#endif
     signal( SIGPIPE, SIG_IGN );
     signal( SIGHUP, sigterm_handler );
     signal( SIGINT, sigterm_handler );
diff --git a/server/object.h b/server/object.h
index 6aa920d..2040ca4 100644
--- a/server/object.h
+++ b/server/object.h
@@ -153,11 +153,10 @@
 
 /* debugger functions */
 
-extern int debugger_attach( struct process *process, struct thread *debugger );
+extern int set_process_debugger( struct process *process, struct thread *debugger );
 extern void generate_debug_event( struct thread *thread, int code, void *arg );
 extern void generate_startup_debug_events( struct process *process );
 extern void debug_exit_thread( struct thread *thread );
-extern CONTEXT *get_debug_context( struct thread *thread );
 
 /* mapping functions */
 
diff --git a/server/process.c b/server/process.c
index 30a9bf5..297ef00 100644
--- a/server/process.c
+++ b/server/process.c
@@ -201,9 +201,9 @@
 
     /* attach to the debugger if requested */
     if (process->create_flags & (DEBUG_PROCESS | DEBUG_ONLY_THIS_PROCESS))
-        debugger_attach( process, current );
+        set_process_debugger( process, current );
     else if (parent && parent->debugger && !(parent->create_flags & DEBUG_ONLY_THIS_PROCESS))
-        debugger_attach( process, parent->debugger );
+        set_process_debugger( process, parent->debugger );
 
     release_object( process );
     return thread;
diff --git a/server/request.c b/server/request.c
index 7f67c1b..582f839 100644
--- a/server/request.c
+++ b/server/request.c
@@ -127,16 +127,16 @@
 }
 
 /* call a request handler */
-static void call_req_handler( struct thread *thread, enum request req, int fd )
+static inline void call_req_handler( struct thread *thread, enum request req )
 {
     current = thread;
     clear_error();
 
-    if (debug_level) trace_request( req, fd );
+    if (debug_level) trace_request( req );
 
     if (req < REQ_NB_REQUESTS)
     {
-        req_handlers[req].handler( current->buffer, fd );
+        req_handlers[req]( current->buffer );
         if (current && !current->wait) send_reply( current );
         current = NULL;
         return;
@@ -186,10 +186,8 @@
 
     if (ret == sizeof(req))
     {
-        int pass_fd = thread->pass_fd;
+        call_req_handler( thread, req );
         thread->pass_fd = -1;
-        call_req_handler( thread, req, pass_fd );
-        if (pass_fd != -1) close( pass_fd );
         return;
     }
     if (ret == -1)
diff --git a/server/request.h b/server/request.h
index 978b691..a907747 100644
--- a/server/request.h
+++ b/server/request.h
@@ -22,20 +22,26 @@
 #define PROTOCOL_ERROR -3
 
 /* request handler definition */
-#define DECL_HANDLER(name) void req_##name( struct name##_request *req, int fd )
+#define DECL_HANDLER(name) void req_##name( struct name##_request *req )
 
 /* request functions */
 
+#ifdef __GNUC__
+extern void fatal_protocol_error( struct thread *thread,
+                                  const char *err, ... ) __attribute__((format (printf,2,3)));
+#else
+extern void fatal_protocol_error( struct thread *thread, const char *err, ... );
+#endif
+
 extern void read_request( struct thread *thread );
 extern int write_request( struct thread *thread );
-extern void fatal_protocol_error( struct thread *thread, const char *err, ... );
 extern void set_reply_fd( struct thread *thread, int pass_fd );
 extern void send_reply( struct thread *thread );
 extern void open_master_socket(void);
 extern void close_master_socket(void);
 extern void lock_master_socket( int locked );
 
-extern void trace_request( enum request req, int fd );
+extern void trace_request( enum request req );
 extern void trace_reply( struct thread *thread );
 
 /* get the request buffer */
@@ -138,7 +144,8 @@
 DECL_HANDLER(create_snapshot);
 DECL_HANDLER(next_process);
 DECL_HANDLER(wait_debug_event);
-DECL_HANDLER(send_debug_event);
+DECL_HANDLER(exception_event);
+DECL_HANDLER(output_debug_string);
 DECL_HANDLER(continue_debug_event);
 DECL_HANDLER(debug_process);
 DECL_HANDLER(read_process_memory);
@@ -170,108 +177,108 @@
 
 #ifdef WANT_REQUEST_HANDLERS
 
-static const struct handler {
-    void       (*handler)( void *req, int fd );
-    unsigned int min_size;
-} req_handlers[REQ_NB_REQUESTS] = {
-    { (void(*)())req_new_process, sizeof(struct new_process_request) },
-    { (void(*)())req_new_thread, sizeof(struct new_thread_request) },
-    { (void(*)())req_boot_done, sizeof(struct boot_done_request) },
-    { (void(*)())req_init_process, sizeof(struct init_process_request) },
-    { (void(*)())req_init_process_done, sizeof(struct init_process_done_request) },
-    { (void(*)())req_init_thread, sizeof(struct init_thread_request) },
-    { (void(*)())req_get_thread_buffer, sizeof(struct get_thread_buffer_request) },
-    { (void(*)())req_terminate_process, sizeof(struct terminate_process_request) },
-    { (void(*)())req_terminate_thread, sizeof(struct terminate_thread_request) },
-    { (void(*)())req_get_process_info, sizeof(struct get_process_info_request) },
-    { (void(*)())req_set_process_info, sizeof(struct set_process_info_request) },
-    { (void(*)())req_get_thread_info, sizeof(struct get_thread_info_request) },
-    { (void(*)())req_set_thread_info, sizeof(struct set_thread_info_request) },
-    { (void(*)())req_suspend_thread, sizeof(struct suspend_thread_request) },
-    { (void(*)())req_resume_thread, sizeof(struct resume_thread_request) },
-    { (void(*)())req_load_dll, sizeof(struct load_dll_request) },
-    { (void(*)())req_unload_dll, sizeof(struct unload_dll_request) },
-    { (void(*)())req_queue_apc, sizeof(struct queue_apc_request) },
-    { (void(*)())req_get_apcs, sizeof(struct get_apcs_request) },
-    { (void(*)())req_close_handle, sizeof(struct close_handle_request) },
-    { (void(*)())req_get_handle_info, sizeof(struct get_handle_info_request) },
-    { (void(*)())req_set_handle_info, sizeof(struct set_handle_info_request) },
-    { (void(*)())req_dup_handle, sizeof(struct dup_handle_request) },
-    { (void(*)())req_open_process, sizeof(struct open_process_request) },
-    { (void(*)())req_select, sizeof(struct select_request) },
-    { (void(*)())req_create_event, sizeof(struct create_event_request) },
-    { (void(*)())req_event_op, sizeof(struct event_op_request) },
-    { (void(*)())req_open_event, sizeof(struct open_event_request) },
-    { (void(*)())req_create_mutex, sizeof(struct create_mutex_request) },
-    { (void(*)())req_release_mutex, sizeof(struct release_mutex_request) },
-    { (void(*)())req_open_mutex, sizeof(struct open_mutex_request) },
-    { (void(*)())req_create_semaphore, sizeof(struct create_semaphore_request) },
-    { (void(*)())req_release_semaphore, sizeof(struct release_semaphore_request) },
-    { (void(*)())req_open_semaphore, sizeof(struct open_semaphore_request) },
-    { (void(*)())req_create_file, sizeof(struct create_file_request) },
-    { (void(*)())req_alloc_file_handle, sizeof(struct alloc_file_handle_request) },
-    { (void(*)())req_get_read_fd, sizeof(struct get_read_fd_request) },
-    { (void(*)())req_get_write_fd, sizeof(struct get_write_fd_request) },
-    { (void(*)())req_set_file_pointer, sizeof(struct set_file_pointer_request) },
-    { (void(*)())req_truncate_file, sizeof(struct truncate_file_request) },
-    { (void(*)())req_set_file_time, sizeof(struct set_file_time_request) },
-    { (void(*)())req_flush_file, sizeof(struct flush_file_request) },
-    { (void(*)())req_get_file_info, sizeof(struct get_file_info_request) },
-    { (void(*)())req_lock_file, sizeof(struct lock_file_request) },
-    { (void(*)())req_unlock_file, sizeof(struct unlock_file_request) },
-    { (void(*)())req_create_pipe, sizeof(struct create_pipe_request) },
-    { (void(*)())req_create_socket, sizeof(struct create_socket_request) },
-    { (void(*)())req_accept_socket, sizeof(struct accept_socket_request) },
-    { (void(*)())req_set_socket_event, sizeof(struct set_socket_event_request) },
-    { (void(*)())req_get_socket_event, sizeof(struct get_socket_event_request) },
-    { (void(*)())req_enable_socket_event, sizeof(struct enable_socket_event_request) },
-    { (void(*)())req_alloc_console, sizeof(struct alloc_console_request) },
-    { (void(*)())req_free_console, sizeof(struct free_console_request) },
-    { (void(*)())req_open_console, sizeof(struct open_console_request) },
-    { (void(*)())req_set_console_fd, sizeof(struct set_console_fd_request) },
-    { (void(*)())req_get_console_mode, sizeof(struct get_console_mode_request) },
-    { (void(*)())req_set_console_mode, sizeof(struct set_console_mode_request) },
-    { (void(*)())req_set_console_info, sizeof(struct set_console_info_request) },
-    { (void(*)())req_get_console_info, sizeof(struct get_console_info_request) },
-    { (void(*)())req_write_console_input, sizeof(struct write_console_input_request) },
-    { (void(*)())req_read_console_input, sizeof(struct read_console_input_request) },
-    { (void(*)())req_create_change_notification, sizeof(struct create_change_notification_request) },
-    { (void(*)())req_create_mapping, sizeof(struct create_mapping_request) },
-    { (void(*)())req_open_mapping, sizeof(struct open_mapping_request) },
-    { (void(*)())req_get_mapping_info, sizeof(struct get_mapping_info_request) },
-    { (void(*)())req_create_device, sizeof(struct create_device_request) },
-    { (void(*)())req_create_snapshot, sizeof(struct create_snapshot_request) },
-    { (void(*)())req_next_process, sizeof(struct next_process_request) },
-    { (void(*)())req_wait_debug_event, sizeof(struct wait_debug_event_request) },
-    { (void(*)())req_send_debug_event, sizeof(struct send_debug_event_request) },
-    { (void(*)())req_continue_debug_event, sizeof(struct continue_debug_event_request) },
-    { (void(*)())req_debug_process, sizeof(struct debug_process_request) },
-    { (void(*)())req_read_process_memory, sizeof(struct read_process_memory_request) },
-    { (void(*)())req_write_process_memory, sizeof(struct write_process_memory_request) },
-    { (void(*)())req_create_key, sizeof(struct create_key_request) },
-    { (void(*)())req_open_key, sizeof(struct open_key_request) },
-    { (void(*)())req_delete_key, sizeof(struct delete_key_request) },
-    { (void(*)())req_close_key, sizeof(struct close_key_request) },
-    { (void(*)())req_enum_key, sizeof(struct enum_key_request) },
-    { (void(*)())req_query_key_info, sizeof(struct query_key_info_request) },
-    { (void(*)())req_set_key_value, sizeof(struct set_key_value_request) },
-    { (void(*)())req_get_key_value, sizeof(struct get_key_value_request) },
-    { (void(*)())req_enum_key_value, sizeof(struct enum_key_value_request) },
-    { (void(*)())req_delete_key_value, sizeof(struct delete_key_value_request) },
-    { (void(*)())req_load_registry, sizeof(struct load_registry_request) },
-    { (void(*)())req_save_registry, sizeof(struct save_registry_request) },
-    { (void(*)())req_set_registry_levels, sizeof(struct set_registry_levels_request) },
-    { (void(*)())req_create_timer, sizeof(struct create_timer_request) },
-    { (void(*)())req_open_timer, sizeof(struct open_timer_request) },
-    { (void(*)())req_set_timer, sizeof(struct set_timer_request) },
-    { (void(*)())req_cancel_timer, sizeof(struct cancel_timer_request) },
-    { (void(*)())req_get_thread_context, sizeof(struct get_thread_context_request) },
-    { (void(*)())req_set_thread_context, sizeof(struct set_thread_context_request) },
-    { (void(*)())req_get_selector_entry, sizeof(struct get_selector_entry_request) },
-    { (void(*)())req_add_atom, sizeof(struct add_atom_request) },
-    { (void(*)())req_delete_atom, sizeof(struct delete_atom_request) },
-    { (void(*)())req_find_atom, sizeof(struct find_atom_request) },
-    { (void(*)())req_get_atom_name, sizeof(struct get_atom_name_request) },
+typedef void (*req_handler)( void *req );
+static const req_handler req_handlers[REQ_NB_REQUESTS] =
+{
+    (req_handler)req_new_process,
+    (req_handler)req_new_thread,
+    (req_handler)req_boot_done,
+    (req_handler)req_init_process,
+    (req_handler)req_init_process_done,
+    (req_handler)req_init_thread,
+    (req_handler)req_get_thread_buffer,
+    (req_handler)req_terminate_process,
+    (req_handler)req_terminate_thread,
+    (req_handler)req_get_process_info,
+    (req_handler)req_set_process_info,
+    (req_handler)req_get_thread_info,
+    (req_handler)req_set_thread_info,
+    (req_handler)req_suspend_thread,
+    (req_handler)req_resume_thread,
+    (req_handler)req_load_dll,
+    (req_handler)req_unload_dll,
+    (req_handler)req_queue_apc,
+    (req_handler)req_get_apcs,
+    (req_handler)req_close_handle,
+    (req_handler)req_get_handle_info,
+    (req_handler)req_set_handle_info,
+    (req_handler)req_dup_handle,
+    (req_handler)req_open_process,
+    (req_handler)req_select,
+    (req_handler)req_create_event,
+    (req_handler)req_event_op,
+    (req_handler)req_open_event,
+    (req_handler)req_create_mutex,
+    (req_handler)req_release_mutex,
+    (req_handler)req_open_mutex,
+    (req_handler)req_create_semaphore,
+    (req_handler)req_release_semaphore,
+    (req_handler)req_open_semaphore,
+    (req_handler)req_create_file,
+    (req_handler)req_alloc_file_handle,
+    (req_handler)req_get_read_fd,
+    (req_handler)req_get_write_fd,
+    (req_handler)req_set_file_pointer,
+    (req_handler)req_truncate_file,
+    (req_handler)req_set_file_time,
+    (req_handler)req_flush_file,
+    (req_handler)req_get_file_info,
+    (req_handler)req_lock_file,
+    (req_handler)req_unlock_file,
+    (req_handler)req_create_pipe,
+    (req_handler)req_create_socket,
+    (req_handler)req_accept_socket,
+    (req_handler)req_set_socket_event,
+    (req_handler)req_get_socket_event,
+    (req_handler)req_enable_socket_event,
+    (req_handler)req_alloc_console,
+    (req_handler)req_free_console,
+    (req_handler)req_open_console,
+    (req_handler)req_set_console_fd,
+    (req_handler)req_get_console_mode,
+    (req_handler)req_set_console_mode,
+    (req_handler)req_set_console_info,
+    (req_handler)req_get_console_info,
+    (req_handler)req_write_console_input,
+    (req_handler)req_read_console_input,
+    (req_handler)req_create_change_notification,
+    (req_handler)req_create_mapping,
+    (req_handler)req_open_mapping,
+    (req_handler)req_get_mapping_info,
+    (req_handler)req_create_device,
+    (req_handler)req_create_snapshot,
+    (req_handler)req_next_process,
+    (req_handler)req_wait_debug_event,
+    (req_handler)req_exception_event,
+    (req_handler)req_output_debug_string,
+    (req_handler)req_continue_debug_event,
+    (req_handler)req_debug_process,
+    (req_handler)req_read_process_memory,
+    (req_handler)req_write_process_memory,
+    (req_handler)req_create_key,
+    (req_handler)req_open_key,
+    (req_handler)req_delete_key,
+    (req_handler)req_close_key,
+    (req_handler)req_enum_key,
+    (req_handler)req_query_key_info,
+    (req_handler)req_set_key_value,
+    (req_handler)req_get_key_value,
+    (req_handler)req_enum_key_value,
+    (req_handler)req_delete_key_value,
+    (req_handler)req_load_registry,
+    (req_handler)req_save_registry,
+    (req_handler)req_set_registry_levels,
+    (req_handler)req_create_timer,
+    (req_handler)req_open_timer,
+    (req_handler)req_set_timer,
+    (req_handler)req_cancel_timer,
+    (req_handler)req_get_thread_context,
+    (req_handler)req_set_thread_context,
+    (req_handler)req_get_selector_entry,
+    (req_handler)req_add_atom,
+    (req_handler)req_delete_atom,
+    (req_handler)req_find_atom,
+    (req_handler)req_get_atom_name,
 };
 #endif  /* WANT_REQUEST_HANDLERS */
 
diff --git a/server/thread.c b/server/thread.c
index cf02b87..37db99b 100644
--- a/server/thread.c
+++ b/server/thread.c
@@ -126,6 +126,7 @@
     if (!(thread = alloc_object( &thread_ops, fd ))) return NULL;
 
     thread->unix_pid    = 0;  /* not known yet */
+    thread->context     = NULL;
     thread->teb         = NULL;
     thread->mutex       = NULL;
     thread->debug_ctx   = NULL;
diff --git a/server/thread.h b/server/thread.h
index 9cc088d..198b77d 100644
--- a/server/thread.h
+++ b/server/thread.h
@@ -47,6 +47,7 @@
     int                 attached;  /* is thread attached with ptrace? */
     int                 exit_code; /* thread exit code */
     int                 unix_pid;  /* Unix pid of client */
+    CONTEXT            *context;   /* current context if in an exception handler */
     void               *teb;       /* TEB address (in client address space) */
     void               *entry;     /* thread entry point (in client address space) */
     int                 priority;  /* priority level */
diff --git a/server/trace.c b/server/trace.c
index 1313c15..d4ff840 100644
--- a/server/trace.c
+++ b/server/trace.c
@@ -79,23 +79,28 @@
 #endif
 }
 
-static void dump_debug_event_t( const debug_event_t *event )
+static void dump_exc_record( const EXCEPTION_RECORD *rec )
 {
     int i;
+    fprintf( stderr, "{code=%lx,flags=%lx,rec=%p,addr=%p,params={",
+             rec->ExceptionCode, rec->ExceptionFlags, rec->ExceptionRecord,
+             rec->ExceptionAddress );
+    for (i = 0; i < rec->NumberParameters; i++)
+    {
+        if (i) fputc( ',', stderr );
+        fprintf( stderr, "%lx", rec->ExceptionInformation[i] );
+    }
+    fputc( '}', stderr );
+}
+
+static void dump_debug_event_t( const debug_event_t *event )
+{
     switch(event->code)
     {
     case EXCEPTION_DEBUG_EVENT:
-        fprintf( stderr, "{exception,code=%x,flags=%x,rec=%p,addr=%p,params={",
-                 event->info.exception.code, event->info.exception.flags,
-                 event->info.exception.record, event->info.exception.addr );
-        for (i = 0; i < event->info.exception.nb_params; i++)
-        {
-            if (i) fputc( ',', stderr );
-            fprintf( stderr, "%x", event->info.exception.params[i] );
-        }
-        fprintf( stderr, "},first_chance=%d,context=", event->info.exception.first_chance );
-        dump_context( &event->info.exception.context );
-        fputc( '}', stderr );
+        fprintf( stderr, "{exception," );
+        dump_exc_record( &event->info.exception.record );
+        fprintf( stderr, ",first=%d}", event->info.exception.first );
         break;
     case CREATE_THREAD_DEBUG_EVENT:
         fprintf( stderr, "{create_thread,thread=%d,teb=%p,start=%p}",
@@ -927,17 +932,28 @@
     dump_debug_event_t( &req->event );
 }
 
-static void dump_send_debug_event_request( const struct send_debug_event_request *req )
+static void dump_exception_event_request( const struct exception_event_request *req )
 {
-    fprintf( stderr, " event=" );
-    dump_debug_event_t( &req->event );
+    fprintf( stderr, " record=" );
+    dump_exc_record( &req->record );
+    fprintf( stderr, "," );
+    fprintf( stderr, " first=%d,", req->first );
+    fprintf( stderr, " context=" );
+    dump_context( &req->context );
 }
 
-static void dump_send_debug_event_reply( const struct send_debug_event_request *req )
+static void dump_exception_event_reply( const struct exception_event_request *req )
 {
     fprintf( stderr, " status=%d", req->status );
 }
 
+static void dump_output_debug_string_request( const struct output_debug_string_request *req )
+{
+    fprintf( stderr, " string=%p,", req->string );
+    fprintf( stderr, " unicode=%d,", req->unicode );
+    fprintf( stderr, " length=%d", req->length );
+}
+
 static void dump_continue_debug_event_request( const struct continue_debug_event_request *req )
 {
     fprintf( stderr, " pid=%p,", req->pid );
@@ -1310,7 +1326,8 @@
     (dump_func)dump_create_snapshot_request,
     (dump_func)dump_next_process_request,
     (dump_func)dump_wait_debug_event_request,
-    (dump_func)dump_send_debug_event_request,
+    (dump_func)dump_exception_event_request,
+    (dump_func)dump_output_debug_string_request,
     (dump_func)dump_continue_debug_event_request,
     (dump_func)dump_debug_process_request,
     (dump_func)dump_read_process_memory_request,
@@ -1411,7 +1428,8 @@
     (dump_func)dump_create_snapshot_reply,
     (dump_func)dump_next_process_reply,
     (dump_func)dump_wait_debug_event_reply,
-    (dump_func)dump_send_debug_event_reply,
+    (dump_func)dump_exception_event_reply,
+    (dump_func)0,
     (dump_func)0,
     (dump_func)0,
     (dump_func)dump_read_process_memory_reply,
@@ -1512,7 +1530,8 @@
     "create_snapshot",
     "next_process",
     "wait_debug_event",
-    "send_debug_event",
+    "exception_event",
+    "output_debug_string",
     "continue_debug_event",
     "debug_process",
     "read_process_memory",
@@ -1546,7 +1565,7 @@
 /* ### make_requests end ### */
 /* Everything above this line is generated automatically by tools/make_requests */
 
-void trace_request( enum request req, int fd )
+void trace_request( enum request req )
 {
     current->last_req = req;
     if (req < REQ_NB_REQUESTS)
@@ -1556,7 +1575,7 @@
     }
     else
         fprintf( stderr, "%08x: %d(", (unsigned int)current, req );
-    if (fd != -1) fprintf( stderr, " ) fd=%d\n", fd );
+    if (current->pass_fd != -1) fprintf( stderr, " ) fd=%d\n", current->pass_fd );
     else fprintf( stderr, " )\n" );
 }
 
diff --git a/tools/make_requests b/tools/make_requests
index 9e84e79..d8f75f4 100755
--- a/tools/make_requests
+++ b/tools/make_requests
@@ -18,6 +18,7 @@
     "path_t"        => "&dump_path_t",
     "debug_event_t" => "&dump_debug_event_t",
     "CONTEXT"       => "&dump_context",
+    "EXCEPTION_RECORD" => "&dump_exc_record",
     "char[1]"       => "\\\"%s\\\"",
     "WCHAR[1]"      => "&dump_unicode_string"
 );
@@ -80,13 +81,11 @@
 
 foreach $req (@requests) { push @request_lines, "DECL_HANDLER($req);\n"; }
 push @request_lines, "\n#ifdef WANT_REQUEST_HANDLERS\n\n";
-push @request_lines, "static const struct handler {\n";
-push @request_lines, "    void       (*handler)( void *req, int fd );\n";
-push @request_lines, "    unsigned int min_size;\n";
-push @request_lines, "} req_handlers[REQ_NB_REQUESTS] = {\n";
+push @request_lines, "typedef void (*req_handler)( void *req );\n";
+push @request_lines, "static const req_handler req_handlers[REQ_NB_REQUESTS] =\n{\n";
 foreach $req (@requests)
 {
-    push @request_lines, "    { (void(*)())req_$req, sizeof(struct ${req}_request) },\n";
+    push @request_lines, "    (req_handler)req_$req,\n";
 }
 push @request_lines, "};\n#endif  /* WANT_REQUEST_HANDLERS */\n";
 
diff --git a/win32/except.c b/win32/except.c
index 0f2fe0d..f642481 100644
--- a/win32/except.c
+++ b/win32/except.c
@@ -34,9 +34,10 @@
 #include "process.h"
 #include "thread.h"
 #include "stackframe.h"
+#include "server.h"
 #include "debugtools.h"
 
-DEFAULT_DEBUG_CHANNEL(seh)
+DEFAULT_DEBUG_CHANNEL(seh);
 
 
 /*******************************************************************
@@ -69,12 +70,16 @@
  */
 DWORD WINAPI UnhandledExceptionFilter(PEXCEPTION_POINTERS epointers)
 {
+    struct exception_event_request *req = get_req_buffer();
     char message[80];
     PDB *pdb = PROCESS_Current();
 
-    if (DEBUG_SendExceptionEvent( epointers->ExceptionRecord, FALSE,
-                                  epointers->ContextRecord ) == DBG_CONTINUE)
-        return EXCEPTION_CONTINUE_EXECUTION;
+    /* send a last chance event to the debugger */
+    req->record  = *epointers->ExceptionRecord;
+    req->first   = 0;
+    req->context = *epointers->ContextRecord;
+    if (!server_call_noerr( REQ_EXCEPTION_EVENT )) *epointers->ContextRecord = req->context;
+    if (req->status == DBG_CONTINUE) return EXCEPTION_CONTINUE_EXECUTION;
 
     if (pdb->top_filter)
     {