Add support for winevent hooks.

diff --git a/server/hook.c b/server/hook.c
index b269114..293b20c 100644
--- a/server/hook.c
+++ b/server/hook.c
@@ -2,6 +2,7 @@
  * Server-side window hooks support
  *
  * Copyright (C) 2002 Alexandre Julliard
+ * Copyright (C) 2005 Dmitry Timoshkov
  *
  * This library is free software; you can redistribute it and/or
  * modify it under the terms of the GNU Lesser General Public
@@ -40,15 +41,22 @@
 {
     struct list         chain;    /* hook chain entry */
     user_handle_t       handle;   /* user handle for this hook */
-    struct thread      *thread;   /* thread owning the hook */
+    struct process     *process;  /* process the hook is set to */
+    struct thread      *thread;   /* thread the hook is set to */
+    struct thread      *owner;    /* owner of the out of context hook */
     int                 index;    /* hook table index */
+    int                 event_min;
+    int                 event_max;
+    int                 flags;
     void               *proc;     /* hook function */
     int                 unicode;  /* is it a unicode hook? */
     WCHAR              *module;   /* module name for global hooks */
     size_t              module_size;
 };
 
-#define NB_HOOKS (WH_MAXHOOK-WH_MINHOOK+1)
+#define WH_WINEVENT (WH_MAXHOOK+1)
+
+#define NB_HOOKS (WH_WINEVENT-WH_MINHOOK+1)
 #define HOOK_ENTRY(p)  LIST_ENTRY( (p), struct hook, chain )
 
 struct hook_table
@@ -124,6 +132,8 @@
     free_user_handle( hook->handle );
     if (hook->module) free( hook->module );
     if (hook->thread) release_object( hook->thread );
+    if (hook->process) release_object( hook->process );
+    release_object( hook->owner );
     list_remove( &hook->chain );
     free( hook );
 }
@@ -162,27 +172,68 @@
 }
 
 /* find the first non-deleted hook in the chain */
-inline static struct hook *get_first_valid_hook( struct hook_table *table, int index )
+inline static struct hook *get_first_valid_hook( struct hook_table *table, int index,
+                                                 int event, user_handle_t win,
+                                                 int object_id, int child_id )
 {
     struct hook *hook = get_first_hook( table, index );
-    while (hook && !hook->proc)
+
+    while (hook)
+    {
+        if ((!hook->process || hook->process == current->process) &&
+            (!(hook->flags & WINEVENT_SKIPOWNPROCESS) || hook->process != current->process))
+        {
+            if ((!hook->thread || hook->thread == current) &&
+                (!(hook->flags & WINEVENT_SKIPOWNTHREAD) || hook->thread != current))
+            {
+                if (hook->proc && event >= hook->event_min && event <= hook->event_max)
+                {
+                    if (hook->flags & WINEVENT_INCONTEXT) return hook;
+
+                    /* only winevent hooks may be out of context */
+                    assert(hook->index + WH_MINHOOK == WH_WINEVENT);
+                    post_win_event( hook->owner, event, win, object_id, child_id,
+                                    hook->proc, hook->module, hook->module_size,
+                                    hook->handle );
+                }
+            }
+        }
         hook = HOOK_ENTRY( list_next( &table->hooks[index], &hook->chain ) );
+    }
     return hook;
 }
 
 /* find the next hook in the chain, skipping the deleted ones */
-static struct hook *get_next_hook( struct hook *hook )
+static struct hook *get_next_hook( struct hook *hook, int event, user_handle_t win,
+                                   int object_id, int child_id )
 {
     struct hook_table *table = get_table( hook );
     int index = hook->index;
 
     while ((hook = HOOK_ENTRY( list_next( &table->hooks[index], &hook->chain ) )))
     {
-        if (hook->proc) return hook;
+        if ((!hook->process || hook->process == current->process) &&
+            (!(hook->flags & WINEVENT_SKIPOWNPROCESS) || hook->process != current->process))
+        {
+            if ((!hook->thread || hook->thread == current) &&
+                (!(hook->flags & WINEVENT_SKIPOWNTHREAD) || hook->thread != current))
+            {
+                if (hook->proc && event >= hook->event_min && event <= hook->event_max)
+                {
+                    if (hook->flags & WINEVENT_INCONTEXT) return hook;
+
+                    /* only winevent hooks may be out of context */
+                    assert(hook->index + WH_MINHOOK == WH_WINEVENT);
+                    post_win_event( hook->owner, event, win, object_id, child_id,
+                                    hook->proc, hook->module, hook->module_size,
+                                    hook->handle );
+                }
+            }
+        }
     }
     if (global_hooks && table != global_hooks)  /* now search through the global table */
     {
-        hook = get_first_valid_hook( global_hooks, index );
+        hook = get_first_valid_hook( global_hooks, index, event, win, object_id, child_id );
     }
     return hook;
 }
@@ -265,17 +316,37 @@
 /* set a window hook */
 DECL_HANDLER(set_hook)
 {
-    struct thread *thread;
+    struct process *process = NULL;
+    struct thread *thread = NULL;
     struct hook *hook;
     WCHAR *module;
     int global;
     size_t module_size = get_req_data_size();
 
-    if (!req->proc || req->id < WH_MINHOOK || req->id > WH_MAXHOOK)
+    if (!req->proc || req->id < WH_MINHOOK || req->id > WH_WINEVENT)
     {
         set_error( STATUS_INVALID_PARAMETER );
         return;
     }
+
+    if (req->pid && !(process = get_process_from_id( req->pid ))) return;
+
+    if (req->tid)
+    {
+        if (!(thread = get_thread_from_id( req->tid )))
+        {
+            if (process) release_object( process );
+            return;
+        }
+        if (process && process != thread->process)
+        {
+            release_object( process );
+            release_object( thread );
+            set_error( STATUS_INVALID_PARAMETER );
+            return;
+        }
+    }
+
     if (req->id == WH_KEYBOARD_LL || req->id == WH_MOUSE_LL)
     {
         /* low-level hardware hooks are special: always global, but without a module */
@@ -285,11 +356,15 @@
     }
     else if (!req->tid)
     {
-        if (!module_size)
+        /* out of context hooks do not need a module handle */
+        if (!module_size && (req->flags & WINEVENT_INCONTEXT))
         {
+            if (process) release_object( process );
+            if (thread) release_object( thread );
             set_error( STATUS_INVALID_PARAMETER );
             return;
         }
+
         if (!(module = memdup( get_req_data(), module_size ))) return;
         thread = NULL;
         global = 1;
@@ -298,11 +373,15 @@
     {
         module = NULL;
         global = 0;
-        if (!(thread = get_thread_from_id( req->tid ))) return;
     }
 
     if ((hook = add_hook( thread, req->id - WH_MINHOOK, global )))
     {
+        hook->owner = (struct thread *)grab_object( current );
+        hook->process = process ? (struct process *)grab_object( process ) : NULL;
+        hook->event_min   = req->event_min;
+        hook->event_max   = req->event_max;
+        hook->flags       = req->flags;
         hook->proc        = req->proc;
         hook->unicode     = req->unicode;
         hook->module      = module;
@@ -311,6 +390,7 @@
     }
     else if (module) free( module );
 
+    if (process) release_object( process );
     if (thread) release_object( thread );
 }
 
@@ -324,13 +404,13 @@
     {
         if (!(hook = get_user_object( req->handle, USER_HOOK )))
         {
-            set_win32_error( ERROR_INVALID_HOOK_HANDLE );
+            set_error( STATUS_INVALID_HANDLE );
             return;
         }
     }
     else
     {
-        if (!req->proc || req->id < WH_MINHOOK || req->id > WH_MAXHOOK)
+        if (!req->proc || req->id < WH_MINHOOK || req->id > WH_WINEVENT)
         {
             set_error( STATUS_INVALID_PARAMETER );
             return;
@@ -351,32 +431,31 @@
     struct hook *hook;
     struct hook_table *table = get_queue_hooks( current );
 
-    if (req->id < WH_MINHOOK || req->id > WH_MAXHOOK)
+    if (req->id < WH_MINHOOK || req->id > WH_WINEVENT)
     {
         set_error( STATUS_INVALID_PARAMETER );
         return;
     }
 
-    if (!table || !(hook = get_first_valid_hook( table, req->id - WH_MINHOOK )))
+    if (!table || !(hook = get_first_valid_hook( table, req->id - WH_MINHOOK, req->event, req->window, req->object_id, req->child_id )))
     {
         /* try global table */
         if (!(table = global_hooks) ||
-            !(hook = get_first_valid_hook( global_hooks, req->id - WH_MINHOOK )))
+            !(hook = get_first_valid_hook( global_hooks, req->id - WH_MINHOOK, req->event, req->window, req->object_id, req->child_id )))
             return;  /* no hook set */
     }
 
-    if (hook->thread && hook->thread != current)  /* must run in other thread */
+    if (hook->thread && hook->thread != current) /* must run in other thread */
     {
         reply->pid  = get_process_id( hook->thread->process );
         reply->tid  = get_thread_id( hook->thread );
-        reply->proc = 0;
     }
     else
     {
         reply->pid  = 0;
         reply->tid  = 0;
-        reply->proc = hook->proc;
     }
+    reply->proc    = hook->proc;
     reply->handle  = hook->handle;
     reply->unicode = hook->unicode;
     table->counts[hook->index]++;
@@ -390,7 +469,7 @@
     struct hook_table *table = get_queue_hooks( current );
     int index = req->id - WH_MINHOOK;
 
-    if (req->id < WH_MINHOOK || req->id > WH_MAXHOOK)
+    if (req->id < WH_MINHOOK || req->id > WH_WINEVENT)
     {
         set_error( STATUS_INVALID_PARAMETER );
         return;
@@ -411,7 +490,7 @@
         set_error( STATUS_INVALID_HANDLE );
         return;
     }
-    if ((next = get_next_hook( hook )))
+    if ((next = get_next_hook( hook, req->event, req->window, req->object_id, req->child_id )))
     {
         reply->next = next->handle;
         reply->id   = next->index + WH_MINHOOK;
@@ -422,13 +501,12 @@
         {
             reply->pid  = get_process_id( next->thread->process );
             reply->tid  = get_thread_id( next->thread );
-            reply->proc = 0;
         }
         else
         {
             reply->pid  = 0;
             reply->tid  = 0;
-            reply->proc = next->proc;
         }
+        reply->proc = next->proc;
     }
 }
diff --git a/server/protocol.def b/server/protocol.def
index 3307e93..94af8ad 100644
--- a/server/protocol.def
+++ b/server/protocol.def
@@ -1519,7 +1519,8 @@
     MSG_CALLBACK_RESULT,/* result of a callback message */
     MSG_OTHER_PROCESS,  /* sent from other process, may include vararg data, always Unicode */
     MSG_POSTED,         /* posted message (from PostMessageW), always Unicode */
-    MSG_HARDWARE        /* hardware message */
+    MSG_HARDWARE,       /* hardware message */
+    MSG_WINEVENT        /* winevent message */
 };
 #define SEND_MSG_ABORT_IF_HUNG  0x01
 
@@ -1538,6 +1539,8 @@
     unsigned int    lparam;    /* parameters (result for MSG_CALLBACK_RESULT) */
     int             x;         /* x position */
     int             y;         /* y position */
+    user_handle_t   hook;      /* winevent hook handle */
+    void*           hook_proc; /* winevent hook proc address */
     unsigned int    time;      /* message time */
     unsigned int    info;      /* extra info (callback argument for MSG_CALLBACK_RESULT) */
     size_t          total;     /* total size of extra data */
@@ -2064,7 +2067,11 @@
 /* Set a window hook */
 @REQ(set_hook)
     int            id;             /* id of the hook */
+    process_id_t   pid;            /* id of process to set the hook into */
     thread_id_t    tid;            /* id of thread to set the hook into */
+    int            event_min;
+    int            event_max;
+    int            flags;
     void*          proc;           /* hook procedure */
     int            unicode;        /* is it a unicode hook? */
     VARARG(module,unicode_str);    /* module name */
@@ -2084,6 +2091,10 @@
 /* Start calling a hook chain */
 @REQ(start_hook_chain)
     int            id;             /* id of the hook */
+    int            event;          /* signalled event */
+    user_handle_t  window;         /* handle to the event window */
+    int            object_id;      /* object id for out of context winevent */
+    int            child_id;       /* child id for out of context winevent */
 @REPLY
     user_handle_t  handle;         /* handle to the next hook */
     process_id_t   pid;            /* process id for low-level keyboard/mouse hooks */
@@ -2103,6 +2114,10 @@
 /* Get the next hook to call */
 @REQ(get_next_hook)
     user_handle_t  handle;         /* handle to the current hook */
+    int            event;          /* signalled event */
+    user_handle_t  window;         /* handle to the event window */
+    int            object_id;      /* object id for out of context winevent */
+    int            child_id;       /* child id for out of context winevent */
 @REPLY
     user_handle_t  next;           /* handle to the next hook */
     int            id;             /* id of the next hook */
diff --git a/server/queue.c b/server/queue.c
index 0c9b23f..e5a5979 100644
--- a/server/queue.c
+++ b/server/queue.c
@@ -70,6 +70,8 @@
     int                    y;         /* y position */
     unsigned int           time;      /* message time */
     unsigned int           info;      /* extra info */
+    user_handle_t          hook;      /* winevent hook handle */
+    void                  *hook_proc; /* winevent hook proc address */
     void                  *data;      /* message data for sent messages */
     unsigned int           data_size; /* size of message data */
     struct message_result *result;    /* result in sender queue */
@@ -550,6 +552,8 @@
     reply->y      = msg->y;
     reply->time   = msg->time;
     reply->info   = msg->info;
+    reply->hook   = msg->hook;
+    reply->hook_proc = msg->hook_proc;
 
     if (msg->data) set_reply_data_ptr( msg->data, msg->data_size );
 
@@ -1296,6 +1300,45 @@
     release_object( thread );
 }
 
+/* post a win event */
+void post_win_event( struct thread *thread, unsigned int event,
+                     user_handle_t win, unsigned int object_id,
+                     unsigned int child_id, void *hook_proc,
+                     const WCHAR *module, size_t module_size,
+                     user_handle_t hook)
+{
+    struct message *msg;
+
+    if (thread->queue && (msg = mem_alloc( sizeof(*msg) )))
+    {
+        msg->type      = MSG_WINEVENT;
+        msg->win       = get_user_full_handle( win );
+        msg->msg       = event;
+        msg->wparam    = object_id;
+        msg->lparam    = child_id;
+        msg->time      = get_tick_count();
+        msg->x         = 0;
+        msg->y         = 0;
+        msg->info      = get_thread_id( current );
+        msg->result    = NULL;
+        msg->hook      = hook;
+        msg->hook_proc = hook_proc;
+
+        if ((msg->data = malloc( module_size )))
+        {
+            msg->data_size = module_size;
+            memcpy( msg->data, module, module_size );
+
+            if (debug_level > 1)
+                fprintf( stderr, "post_win_event: tid %04x event %04x win %p object_id %d child_id %d\n",
+                         get_thread_id(thread), event, win, object_id, child_id );
+            append_message( &thread->queue->msg_list[SEND_MESSAGE], msg );
+            set_queue_bits( thread->queue, QS_SENDMESSAGE );
+        }
+        else
+            free( msg );
+    }
+}
 
 /* get the message queue of the current thread */
 DECL_HANDLER(get_msg_queue)
diff --git a/server/thread.c b/server/thread.c
index 64e2ad5c..e2a02d8 100644
--- a/server/thread.c
+++ b/server/thread.c
@@ -275,7 +275,7 @@
     struct object *obj = get_ptid_entry( id );
 
     if (obj && obj->ops == &thread_ops) return (struct thread *)grab_object( obj );
-    set_error( STATUS_INVALID_PARAMETER );
+    set_win32_error( ERROR_INVALID_THREAD_ID );
     return NULL;
 }
 
diff --git a/server/trace.c b/server/trace.c
index 146d0f2..b499013 100644
--- a/server/trace.c
+++ b/server/trace.c
@@ -1866,6 +1866,8 @@
     fprintf( stderr, " lparam=%08x,", req->lparam );
     fprintf( stderr, " x=%d,", req->x );
     fprintf( stderr, " y=%d,", req->y );
+    fprintf( stderr, " hook=%p,", req->hook );
+    fprintf( stderr, " hook_proc=%p,", req->hook_proc );
     fprintf( stderr, " time=%08x,", req->time );
     fprintf( stderr, " info=%08x,", req->info );
     fprintf( stderr, " total=%d,", req->total );
@@ -2460,7 +2462,11 @@
 static void dump_set_hook_request( const struct set_hook_request *req )
 {
     fprintf( stderr, " id=%d,", req->id );
+    fprintf( stderr, " pid=%04x,", req->pid );
     fprintf( stderr, " tid=%04x,", req->tid );
+    fprintf( stderr, " event_min=%d,", req->event_min );
+    fprintf( stderr, " event_max=%d,", req->event_max );
+    fprintf( stderr, " flags=%d,", req->flags );
     fprintf( stderr, " proc=%p,", req->proc );
     fprintf( stderr, " unicode=%d,", req->unicode );
     fprintf( stderr, " module=" );
@@ -2481,7 +2487,11 @@
 
 static void dump_start_hook_chain_request( const struct start_hook_chain_request *req )
 {
-    fprintf( stderr, " id=%d", req->id );
+    fprintf( stderr, " id=%d,", req->id );
+    fprintf( stderr, " event=%d,", req->event );
+    fprintf( stderr, " window=%p,", req->window );
+    fprintf( stderr, " object_id=%d,", req->object_id );
+    fprintf( stderr, " child_id=%d", req->child_id );
 }
 
 static void dump_start_hook_chain_reply( const struct start_hook_chain_reply *req )
@@ -2502,7 +2512,11 @@
 
 static void dump_get_next_hook_request( const struct get_next_hook_request *req )
 {
-    fprintf( stderr, " handle=%p", req->handle );
+    fprintf( stderr, " handle=%p,", req->handle );
+    fprintf( stderr, " event=%d,", req->event );
+    fprintf( stderr, " window=%p,", req->window );
+    fprintf( stderr, " object_id=%d,", req->object_id );
+    fprintf( stderr, " child_id=%d", req->child_id );
 }
 
 static void dump_get_next_hook_reply( const struct get_next_hook_reply *req )
@@ -3212,8 +3226,8 @@
         NAME(TIMEOUT),
         NAME(USER_APC),
         NAME(WAS_LOCKED),
-        NAME_WIN32(ERROR_INVALID_HOOK_HANDLE),
         NAME_WIN32(ERROR_INVALID_INDEX),
+        NAME_WIN32(ERROR_INVALID_THREAD_ID),
         NAME_WIN32(ERROR_NEGATIVE_SEEK),
         NAME_WIN32(ERROR_SEEK),
         { NULL, 0 }  /* terminator */
diff --git a/server/user.h b/server/user.h
index dfeda2a..a68949c 100644
--- a/server/user.h
+++ b/server/user.h
@@ -64,6 +64,11 @@
 extern int attach_thread_input( struct thread *thread_from, struct thread *thread_to );
 extern void post_message( user_handle_t win, unsigned int message,
                           unsigned int wparam, unsigned int lparam );
+extern void post_win_event( struct thread *thread, unsigned int event,
+                            user_handle_t win, unsigned int object_id,
+                            unsigned int child_id, void *proc,
+                            const WCHAR *module, size_t module_size,
+                            user_handle_t handle );
 
 /* region functions */