Added support for system-wide hooks.
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 );
}
}