Added support for system-wide hooks.

diff --git a/dlls/user/hook.c b/dlls/user/hook.c
index c60ff5e..43b7022 100644
--- a/dlls/user/hook.c
+++ b/dlls/user/hook.c
@@ -69,6 +69,7 @@
 #include "queue.h"
 #include "win.h"
 #include "wine/server.h"
+#include "wine/unicode.h"
 #include "wine/debug.h"
 
 WINE_DEFAULT_DEBUG_CHANNEL(hook);
@@ -103,6 +104,7 @@
 static HHOOK set_windows_hook( INT id, HOOKPROC proc, HINSTANCE inst, DWORD tid, BOOL unicode )
 {
     HHOOK handle = 0;
+    WCHAR module[MAX_PATH];
 
     if (tid)  /* thread-local hook */
     {
@@ -116,23 +118,29 @@
             SetLastError( ERROR_INVALID_PARAMETER );
             return 0;
         }
+        inst = 0;
     }
     else  /* system-global hook */
     {
-        if (!inst)
+        if (!inst || !GetModuleFileNameW( inst, module, MAX_PATH ))
         {
             SetLastError( ERROR_INVALID_PARAMETER );
             return 0;
         }
-        FIXME( "system hook %s won't work right\n", hook_names[id-WH_MINHOOK] );
     }
 
     SERVER_START_REQ( set_hook )
     {
         req->id      = id;
         req->tid     = tid;
-        req->proc    = proc;
         req->unicode = unicode;
+        if (inst) /* make proc relative to the module base */
+        {
+            req->proc = (void *)((char *)proc - (char *)inst);
+            wine_server_add_data( req, module, strlenW(module) * sizeof(WCHAR) );
+        }
+        else req->proc = proc;
+
         if (!wine_server_call_err( req )) handle = reply->handle;
     }
     SERVER_END_REQ;
@@ -229,6 +237,25 @@
 
 
 /***********************************************************************
+ *		get_hook_proc
+ *
+ * Retrieve the hook procedure real value for a module-relative proc
+ */
+static HOOKPROC get_hook_proc( HOOKPROC proc, const WCHAR *module )
+{
+    HMODULE mod;
+
+    if (!(mod = GetModuleHandleW(module)))
+    {
+        TRACE( "loading %s\n", debugstr_w(module) );
+        /* FIXME: the library will never be freed */
+        if (!(mod = LoadLibraryW(module))) return NULL;
+    }
+    return (HOOKPROC)((char *)mod + (ULONG_PTR)proc);
+}
+
+
+/***********************************************************************
  *		HOOK_CallHooks
  */
 LRESULT HOOK_CallHooks( INT id, INT code, WPARAM wparam, LPARAM lparam, BOOL unicode )
@@ -236,17 +263,19 @@
     MESSAGEQUEUE *queue = QUEUE_Current();
     HOOKPROC proc = NULL;
     HHOOK prev;
+    WCHAR module[MAX_PATH];
     BOOL unicode_hook = FALSE;
     LRESULT ret = 0;
-    int locks;
 
     if (!queue) return 0;
     prev = queue->hook;
     SERVER_START_REQ( start_hook_chain )
     {
         req->id = id;
+        wine_server_set_reply( req, module, sizeof(module)-sizeof(WCHAR) );
         if (!wine_server_call_err( req ))
         {
+            module[wine_server_reply_size(req) / sizeof(WCHAR)] = 0;
             queue->hook = reply->handle;
             proc = reply->proc;
             unicode_hook = reply->unicode;
@@ -256,12 +285,15 @@
 
     if (proc)
     {
-        TRACE( "calling hook %p %s code %x wp %x lp %lx\n",
-               proc, hook_names[id-WH_MINHOOK], code, wparam, lparam );
+        TRACE( "calling hook %p %s code %x wp %x lp %lx module %s\n",
+               proc, hook_names[id-WH_MINHOOK], code, wparam, lparam, debugstr_w(module) );
 
-        locks = WIN_SuspendWndsLock();
-        ret = call_hook( proc, id, code, wparam, lparam, unicode, unicode_hook );
-        WIN_RestoreWndsLock( locks );
+        if (!module[0] || (proc = get_hook_proc( proc, module )) != NULL)
+        {
+            int locks = WIN_SuspendWndsLock();
+            ret = call_hook( proc, id, code, wparam, lparam, unicode, unicode_hook );
+            WIN_RestoreWndsLock( locks );
+        }
 
         SERVER_START_REQ( finish_hook_chain )
         {
@@ -367,6 +399,7 @@
 {
     MESSAGEQUEUE *queue = QUEUE_Current();
     HHOOK prev;
+    WCHAR module[MAX_PATH];
     HOOKPROC proc = NULL;
     INT id = 0;
     BOOL prev_unicode = FALSE, next_unicode = FALSE;
@@ -377,8 +410,10 @@
     SERVER_START_REQ( get_next_hook )
     {
         req->handle = prev;
+        wine_server_set_reply( req, module, sizeof(module)-sizeof(WCHAR) );
         if (!wine_server_call_err( req ))
         {
+            module[wine_server_reply_size(req) / sizeof(WCHAR)] = 0;
             queue->hook  = reply->next;
             id           = reply->id;
             proc         = reply->proc;
@@ -387,12 +422,14 @@
         }
     }
     SERVER_END_REQ;
+
     if (proc)
     {
-        TRACE( "calling hook %p %s code %x wp %x lp %lx\n",
-               proc, hook_names[id-WH_MINHOOK], code, wparam, lparam );
+        TRACE( "calling hook %p %s code %x wp %x lp %lx module %s\n",
+               proc, hook_names[id-WH_MINHOOK], code, wparam, lparam, debugstr_w(module) );
 
-        return call_hook( proc, id, code, wparam, lparam, prev_unicode, next_unicode );
+        if (!module[0] || (proc = get_hook_proc( proc, module )) != NULL)
+            return call_hook( proc, id, code, wparam, lparam, prev_unicode, next_unicode );
     }
     queue->hook = prev;
     return ret;
diff --git a/include/wine/server_protocol.h b/include/wine/server_protocol.h
index 122747c..6a70289 100644
--- a/include/wine/server_protocol.h
+++ b/include/wine/server_protocol.h
@@ -2874,6 +2874,7 @@
     thread_id_t    tid;
     void*          proc;
     int            unicode;
+    /* VARARG(module,unicode_str); */
 };
 struct set_hook_reply
 {
@@ -2908,6 +2909,7 @@
     user_handle_t  handle;
     void*          proc;
     int            unicode;
+    /* VARARG(module,unicode_str); */
 };
 
 
@@ -2937,6 +2939,7 @@
     void*          proc;
     int            prev_unicode;
     int            next_unicode;
+    /* VARARG(module,unicode_str); */
 };
 
 
@@ -3463,6 +3466,6 @@
     struct get_next_hook_reply get_next_hook_reply;
 };
 
-#define SERVER_PROTOCOL_VERSION 89
+#define SERVER_PROTOCOL_VERSION 90
 
 #endif /* __WINE_WINE_SERVER_PROTOCOL_H */
diff --git a/server/hook.c b/server/hook.c
index cf81fa4..05eb3de 100644
--- a/server/hook.c
+++ b/server/hook.c
@@ -41,6 +41,8 @@
     int                 index;    /* hook table index */
     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)
@@ -74,6 +76,8 @@
 };
 
 
+static struct hook_table *global_hooks;
+
 /* create a new hook table */
 static struct hook_table *alloc_hook_table(void)
 {
@@ -95,12 +99,13 @@
 static struct hook *add_hook( struct thread *thread, int index )
 {
     struct hook *hook;
-    struct hook_table *table = thread->hooks;
+    struct hook_table *table = thread ? thread->hooks : global_hooks;
 
     if (!table)
     {
         if (!(table = alloc_hook_table())) return NULL;
-        thread->hooks = table;
+        if (thread) thread->hooks = table;
+        else global_hooks = table;
     }
     if (!(hook = mem_alloc( sizeof(*hook) ))) return NULL;
 
@@ -119,6 +124,7 @@
 static void free_hook( struct hook *hook )
 {
     free_user_handle( hook->handle );
+    if (hook->module) free( hook->module );
     if (hook->thread) release_object( hook->thread );
     list_remove( &hook->chain );
     free( hook );
@@ -144,7 +150,7 @@
 /* get the hook table that a given hook belongs to */
 inline static struct hook_table *get_table( struct hook *hook )
 {
-    return hook->thread->hooks;
+    return hook->thread ? hook->thread->hooks : global_hooks;
 }
 
 /* get the first hook in the chain */
@@ -154,23 +160,37 @@
     return elem ? HOOK_ENTRY( elem ) : NULL;
 }
 
+/* find the first non-deleted hook in the chain */
+inline static struct hook *get_first_valid_hook( struct hook_table *table, int index )
+{
+    struct hook *hook = get_first_hook( table, index );
+    while (hook && !hook->proc)
+        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 )
 {
     struct hook_table *table = get_table( hook );
-    struct hook *next;
+    int index = hook->index;
 
-    while ((next = HOOK_ENTRY( list_next( &table->hooks[hook->index], &hook->chain ) )))
+    while ((hook = HOOK_ENTRY( list_next( &table->hooks[index], &hook->chain ) )))
     {
-        if (next->proc) break;
+        if (hook->proc) return hook;
     }
-    return next;
+    if (global_hooks && table != global_hooks)  /* now search through the global table */
+    {
+        hook = get_first_valid_hook( global_hooks, index );
+    }
+    return hook;
 }
 
 static void hook_table_dump( struct object *obj, int verbose )
 {
-/*    struct hook_table *table = (struct hook_table *)obj; */
-    fprintf( stderr, "Hook table\n" );
+    struct hook_table *table = (struct hook_table *)obj;
+    if (table == global_hooks) fprintf( stderr, "Global hook table\n" );
+    else fprintf( stderr, "Hook table\n" );
 }
 
 static void hook_table_destroy( struct object *obj )
@@ -185,6 +205,12 @@
     }
 }
 
+/* free the global hooks table */
+void close_global_hooks(void)
+{
+    if (global_hooks) release_object( global_hooks );
+}
+
 /* remove a hook, freeing it if the chain is not in use */
 static void remove_hook( struct hook *hook )
 {
@@ -221,22 +247,41 @@
 {
     struct thread *thread;
     struct hook *hook;
+    WCHAR *module;
+    size_t module_size = get_req_data_size();
 
     if (!req->proc || req->id < WH_MINHOOK || req->id > WH_MAXHOOK)
     {
         set_error( STATUS_INVALID_PARAMETER );
         return;
     }
-    if (!req->tid) thread = (struct thread *)grab_object( current );
-    else if (!(thread = get_thread_from_id( req->tid ))) return;
+    if (!req->tid)
+    {
+        if (!module_size)
+        {
+            set_error( STATUS_INVALID_PARAMETER );
+            return;
+        }
+        if (!(module = memdup( get_req_data(), module_size ))) return;
+        thread = NULL;
+    }
+    else
+    {
+        module = NULL;
+        if (!(thread = get_thread_from_id( req->tid ))) return;
+    }
 
     if ((hook = add_hook( thread, req->id - WH_MINHOOK )))
     {
-        hook->proc    = req->proc;
-        hook->unicode = req->unicode;
+        hook->proc        = req->proc;
+        hook->unicode     = req->unicode;
+        hook->module      = module;
+        hook->module_size = module_size;
         reply->handle = hook->handle;
     }
-    release_object( thread );
+    else if (module) free( module );
+
+    if (thread) release_object( thread );
 }
 
 
@@ -271,12 +316,19 @@
         set_error( STATUS_INVALID_PARAMETER );
         return;
     }
-    if (!table) return;  /* no hook set */
-    if (!(hook = get_first_hook( table, req->id - WH_MINHOOK ))) return;  /* no hook set */
+
+    if (!table || !(hook = get_first_valid_hook( table, req->id - WH_MINHOOK )))
+    {
+        /* try global table */
+        if (!(table = global_hooks) ||
+            !(hook = get_first_valid_hook( global_hooks, req->id - WH_MINHOOK )))
+            return;  /* no hook set */
+    }
     reply->handle  = hook->handle;
     reply->proc    = hook->proc;
     reply->unicode = hook->unicode;
     table->counts[hook->index]++;
+    if (hook->module) set_reply_data( hook->module, hook->module_size );
 }
 
 
@@ -292,6 +344,7 @@
         return;
     }
     if (table) release_hook_chain( table, index );
+    if (global_hooks) release_hook_chain( global_hooks, index );
 }
 
 
@@ -301,7 +354,7 @@
     struct hook *hook, *next;
 
     if (!(hook = get_user_object( req->handle, USER_HOOK ))) return;
-    if (hook->thread != current)
+    if (hook->thread && (hook->thread != current))
     {
         set_error( STATUS_INVALID_HANDLE );
         return;
@@ -313,5 +366,6 @@
         reply->proc = next->proc;
         reply->prev_unicode = hook->unicode;
         reply->next_unicode = next->unicode;
+        if (hook->module) set_reply_data( hook->module, hook->module_size );
     }
 }
diff --git a/server/protocol.def b/server/protocol.def
index 38c91c4..7472291 100644
--- a/server/protocol.def
+++ b/server/protocol.def
@@ -2012,6 +2012,7 @@
     thread_id_t    tid;            /* id of thread to set the hook into */
     void*          proc;           /* hook procedure */
     int            unicode;        /* is it a unicode hook? */
+    VARARG(module,unicode_str);    /* module name */
 @REPLY
     user_handle_t  handle;         /* handle to the hook */
 @END
@@ -2032,6 +2033,7 @@
     user_handle_t  handle;         /* handle to the next hook */
     void*          proc;           /* hook procedure */
     int            unicode;        /* is it a unicode hook? */
+    VARARG(module,unicode_str);    /* module name */
 @END
 
 
@@ -2050,4 +2052,5 @@
     void*          proc;           /* next hook procedure */
     int            prev_unicode;   /* was the previous a unicode hook? */
     int            next_unicode;   /* is the next a unicode hook? */
+    VARARG(module,unicode_str);    /* module name */
 @END
diff --git a/server/request.c b/server/request.c
index 888841e..66c1b11 100644
--- a/server/request.c
+++ b/server/request.c
@@ -51,6 +51,7 @@
 #include "handle.h"
 #include "thread.h"
 #include "process.h"
+#include "user.h"
 #define WANT_REQUEST_HANDLERS
 #include "request.h"
 
@@ -742,6 +743,7 @@
 #ifdef DEBUG_OBJECTS
     /* shut down everything properly */
     release_object( master_socket );
+    close_global_hooks();
     close_global_handles();
     close_registry();
     close_atom_table();
diff --git a/server/trace.c b/server/trace.c
index 9d89ef7..49f486b 100644
--- a/server/trace.c
+++ b/server/trace.c
@@ -2287,7 +2287,9 @@
     fprintf( stderr, " id=%d,", req->id );
     fprintf( stderr, " tid=%08x,", req->tid );
     fprintf( stderr, " proc=%p,", req->proc );
-    fprintf( stderr, " unicode=%d", req->unicode );
+    fprintf( stderr, " unicode=%d,", req->unicode );
+    fprintf( stderr, " module=" );
+    dump_varargs_unicode_str( cur_size );
 }
 
 static void dump_set_hook_reply( const struct set_hook_reply *req )
@@ -2311,7 +2313,9 @@
 {
     fprintf( stderr, " handle=%p,", req->handle );
     fprintf( stderr, " proc=%p,", req->proc );
-    fprintf( stderr, " unicode=%d", req->unicode );
+    fprintf( stderr, " unicode=%d,", req->unicode );
+    fprintf( stderr, " module=" );
+    dump_varargs_unicode_str( cur_size );
 }
 
 static void dump_finish_hook_chain_request( const struct finish_hook_chain_request *req )
@@ -2330,7 +2334,9 @@
     fprintf( stderr, " id=%d,", req->id );
     fprintf( stderr, " proc=%p,", req->proc );
     fprintf( stderr, " prev_unicode=%d,", req->prev_unicode );
-    fprintf( stderr, " next_unicode=%d", req->next_unicode );
+    fprintf( stderr, " next_unicode=%d,", req->next_unicode );
+    fprintf( stderr, " module=" );
+    dump_varargs_unicode_str( cur_size );
 }
 
 static const dump_func req_dumpers[REQ_NB_REQUESTS] = {
diff --git a/server/user.h b/server/user.h
index f70cfea..55eb8aa 100644
--- a/server/user.h
+++ b/server/user.h
@@ -42,6 +42,10 @@
 extern void *free_user_handle( user_handle_t handle );
 extern void *next_user_handle( user_handle_t *handle, enum user_object type );
 
+/* hook functions */
+
+extern void close_global_hooks(void);
+
 /* queue functions */
 
 extern void free_msg_queue( struct thread *thread );