blob: 7b889d8c398de866e64e85feb6409e228f5fbd78 [file] [log] [blame]
Alexandre Julliard02861352002-10-29 00:41:42 +00001/*
2 * Server-side window hooks support
3 *
4 * Copyright (C) 2002 Alexandre Julliard
Dmitry Timoshkov6dba0a72005-02-03 16:40:20 +00005 * Copyright (C) 2005 Dmitry Timoshkov
Alexandre Julliard02861352002-10-29 00:41:42 +00006 *
7 * This library is free software; you can redistribute it and/or
8 * modify it under the terms of the GNU Lesser General Public
9 * License as published by the Free Software Foundation; either
10 * version 2.1 of the License, or (at your option) any later version.
11 *
12 * This library is distributed in the hope that it will be useful,
13 * but WITHOUT ANY WARRANTY; without even the implied warranty of
14 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
15 * Lesser General Public License for more details.
16 *
17 * You should have received a copy of the GNU Lesser General Public
18 * License along with this library; if not, write to the Free Software
Jonathan Ernst360a3f92006-05-18 14:49:52 +020019 * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301, USA
Alexandre Julliard02861352002-10-29 00:41:42 +000020 */
21
22#include "config.h"
23#include "wine/port.h"
24
25#include <assert.h>
Alexandre Julliarde37c6e12003-09-05 23:08:26 +000026#include <stdarg.h>
Alexandre Julliard02861352002-10-29 00:41:42 +000027#include <stdio.h>
28
Ge van Geldorp1a1583a2005-11-28 17:32:54 +010029#include "ntstatus.h"
30#define WIN32_NO_STATUS
Alexandre Julliarde37c6e12003-09-05 23:08:26 +000031#include "windef.h"
Alexandre Julliard02861352002-10-29 00:41:42 +000032#include "winbase.h"
33#include "winuser.h"
Ge van Geldorp1a1583a2005-11-28 17:32:54 +010034#include "winternl.h"
Alexandre Julliard02861352002-10-29 00:41:42 +000035
36#include "object.h"
Alexandre Julliardca3ac8f2003-07-11 21:55:58 +000037#include "process.h"
Alexandre Julliard02861352002-10-29 00:41:42 +000038#include "request.h"
39#include "user.h"
40
41struct hook_table;
42
43struct hook
44{
45 struct list chain; /* hook chain entry */
46 user_handle_t handle; /* user handle for this hook */
Dmitry Timoshkov6dba0a72005-02-03 16:40:20 +000047 struct process *process; /* process the hook is set to */
48 struct thread *thread; /* thread the hook is set to */
49 struct thread *owner; /* owner of the out of context hook */
Alexandre Julliard4a40b2e2005-07-11 18:05:50 +000050 struct hook_table *table; /* hook table that contains this hook */
Alexandre Julliard02861352002-10-29 00:41:42 +000051 int index; /* hook table index */
Dmitry Timoshkov6dba0a72005-02-03 16:40:20 +000052 int event_min;
53 int event_max;
54 int flags;
Alexandre Julliardcc55fd32008-12-29 17:35:35 +010055 client_ptr_t proc; /* hook function */
Alexandre Julliard02861352002-10-29 00:41:42 +000056 int unicode; /* is it a unicode hook? */
Alexandre Julliard14e68ba2002-11-20 19:54:32 +000057 WCHAR *module; /* module name for global hooks */
Alexandre Julliard0f273c12006-07-26 10:43:25 +020058 data_size_t module_size;
Alexandre Julliard02861352002-10-29 00:41:42 +000059};
60
Dmitry Timoshkov6dba0a72005-02-03 16:40:20 +000061#define WH_WINEVENT (WH_MAXHOOK+1)
62
63#define NB_HOOKS (WH_WINEVENT-WH_MINHOOK+1)
Alexandre Julliard02861352002-10-29 00:41:42 +000064#define HOOK_ENTRY(p) LIST_ENTRY( (p), struct hook, chain )
65
66struct hook_table
67{
68 struct object obj; /* object header */
69 struct list hooks[NB_HOOKS]; /* array of hook chains */
70 int counts[NB_HOOKS]; /* use counts for each hook chain */
71};
72
73static void hook_table_dump( struct object *obj, int verbose );
74static void hook_table_destroy( struct object *obj );
75
76static const struct object_ops hook_table_ops =
77{
78 sizeof(struct hook_table), /* size */
79 hook_table_dump, /* dump */
Alexandre Julliard8382eb02007-12-05 18:16:42 +010080 no_get_type, /* get_type */
Alexandre Julliard02861352002-10-29 00:41:42 +000081 no_add_queue, /* add_queue */
82 NULL, /* remove_queue */
83 NULL, /* signaled */
84 NULL, /* satisfied */
Mike McCormackf92fff62005-04-24 17:35:52 +000085 no_signal, /* signal */
Alexandre Julliard02861352002-10-29 00:41:42 +000086 no_get_fd, /* get_fd */
Alexandre Julliard28beba32005-12-12 14:57:40 +010087 no_map_access, /* map_access */
Rob Shearmanc1707d82007-10-03 13:10:37 +010088 default_get_sd, /* get_sd */
89 default_set_sd, /* set_sd */
Vitaliy Margolenbaffcb92005-11-22 14:55:42 +000090 no_lookup_name, /* lookup_name */
Alexandre Julliard7e71c1d2007-03-22 11:44:29 +010091 no_open_file, /* open_file */
Alexandre Julliardb9b1ea92005-06-09 15:39:52 +000092 no_close_handle, /* close_handle */
Alexandre Julliard02861352002-10-29 00:41:42 +000093 hook_table_destroy /* destroy */
94};
95
96
97/* create a new hook table */
98static struct hook_table *alloc_hook_table(void)
99{
100 struct hook_table *table;
101 int i;
102
Alexandre Julliarde66207e2003-02-19 00:33:32 +0000103 if ((table = alloc_object( &hook_table_ops )))
Alexandre Julliard02861352002-10-29 00:41:42 +0000104 {
105 for (i = 0; i < NB_HOOKS; i++)
106 {
107 list_init( &table->hooks[i] );
108 table->counts[i] = 0;
109 }
110 }
111 return table;
112}
113
Alexandre Julliard4a40b2e2005-07-11 18:05:50 +0000114static struct hook_table *get_global_hooks( struct thread *thread )
115{
116 struct hook_table *table;
Alexandre Julliarde4faa122009-12-01 15:10:23 +0100117 struct desktop *desktop;
Alexandre Julliard4a40b2e2005-07-11 18:05:50 +0000118
Alexandre Julliarde4faa122009-12-01 15:10:23 +0100119 if (!thread->desktop) return NULL;
120 if (!(desktop = get_thread_desktop( thread, 0 ))) return NULL;
Alexandre Julliard4a40b2e2005-07-11 18:05:50 +0000121 table = desktop->global_hooks;
122 release_object( desktop );
123 return table;
124}
125
Alexandre Julliard02861352002-10-29 00:41:42 +0000126/* create a new hook and add it to the specified table */
Alexandre Julliard4a40b2e2005-07-11 18:05:50 +0000127static struct hook *add_hook( struct desktop *desktop, struct thread *thread, int index, int global )
Alexandre Julliard02861352002-10-29 00:41:42 +0000128{
129 struct hook *hook;
Alexandre Julliard4a40b2e2005-07-11 18:05:50 +0000130 struct hook_table *table = global ? desktop->global_hooks : get_queue_hooks(thread);
Alexandre Julliard02861352002-10-29 00:41:42 +0000131
132 if (!table)
133 {
134 if (!(table = alloc_hook_table())) return NULL;
Alexandre Julliard4a40b2e2005-07-11 18:05:50 +0000135 if (global) desktop->global_hooks = table;
Alexandre Julliardca3ac8f2003-07-11 21:55:58 +0000136 else set_queue_hooks( thread, table );
Alexandre Julliard02861352002-10-29 00:41:42 +0000137 }
138 if (!(hook = mem_alloc( sizeof(*hook) ))) return NULL;
139
140 if (!(hook->handle = alloc_user_handle( hook, USER_HOOK )))
141 {
142 free( hook );
143 return NULL;
144 }
145 hook->thread = thread ? (struct thread *)grab_object( thread ) : NULL;
Alexandre Julliard4a40b2e2005-07-11 18:05:50 +0000146 hook->table = table;
Alexandre Julliard02861352002-10-29 00:41:42 +0000147 hook->index = index;
148 list_add_head( &table->hooks[index], &hook->chain );
Alexandre Julliard92fec7b2005-06-28 19:37:52 +0000149 if (thread) thread->desktop_users++;
Alexandre Julliard02861352002-10-29 00:41:42 +0000150 return hook;
151}
152
153/* free a hook, removing it from its chain */
154static void free_hook( struct hook *hook )
155{
156 free_user_handle( hook->handle );
Michael Stefaniuc5ceccec2006-10-09 23:34:36 +0200157 free( hook->module );
Alexandre Julliard92fec7b2005-06-28 19:37:52 +0000158 if (hook->thread)
159 {
160 assert( hook->thread->desktop_users > 0 );
161 hook->thread->desktop_users--;
162 release_object( hook->thread );
163 }
Dmitry Timoshkov6dba0a72005-02-03 16:40:20 +0000164 if (hook->process) release_object( hook->process );
165 release_object( hook->owner );
Alexandre Julliard02861352002-10-29 00:41:42 +0000166 list_remove( &hook->chain );
167 free( hook );
168}
169
170/* find a hook from its index and proc */
Alexandre Julliardcc55fd32008-12-29 17:35:35 +0100171static struct hook *find_hook( struct thread *thread, int index, client_ptr_t proc )
Alexandre Julliard02861352002-10-29 00:41:42 +0000172{
173 struct list *p;
Alexandre Julliardd55e7f12003-07-03 18:16:48 +0000174 struct hook_table *table = get_queue_hooks( thread );
Alexandre Julliard02861352002-10-29 00:41:42 +0000175
176 if (table)
177 {
178 LIST_FOR_EACH( p, &table->hooks[index] )
179 {
180 struct hook *hook = HOOK_ENTRY( p );
181 if (hook->proc == proc) return hook;
182 }
183 }
184 return NULL;
185}
186
Alexandre Julliard02861352002-10-29 00:41:42 +0000187/* get the first hook in the chain */
Andrew Talbotb1788c82007-03-17 10:52:14 +0000188static inline struct hook *get_first_hook( struct hook_table *table, int index )
Alexandre Julliard02861352002-10-29 00:41:42 +0000189{
190 struct list *elem = list_head( &table->hooks[index] );
191 return elem ? HOOK_ENTRY( elem ) : NULL;
192}
193
Alexandre Julliard63342352005-05-11 13:03:15 +0000194/* check if a given hook should run in the current thread */
Andrew Talbotb1788c82007-03-17 10:52:14 +0000195static inline int run_hook_in_current_thread( struct hook *hook )
Alexandre Julliard63342352005-05-11 13:03:15 +0000196{
197 if ((!hook->process || hook->process == current->process) &&
198 (!(hook->flags & WINEVENT_SKIPOWNPROCESS) || hook->process != current->process))
199 {
200 if ((!hook->thread || hook->thread == current) &&
201 (!(hook->flags & WINEVENT_SKIPOWNTHREAD) || hook->thread != current))
202 return 1;
203 }
204 return 0;
205}
206
Alexandre Julliard6d85f3b2006-01-27 12:12:15 +0100207/* check if a given hook should run in the owner thread instead of the current thread */
Andrew Talbotb1788c82007-03-17 10:52:14 +0000208static inline int run_hook_in_owner_thread( struct hook *hook )
Alexandre Julliard6d85f3b2006-01-27 12:12:15 +0100209{
210 if ((hook->index == WH_MOUSE_LL - WH_MINHOOK ||
211 hook->index == WH_KEYBOARD_LL - WH_MINHOOK))
212 return hook->owner != current;
213 return 0;
214}
215
Alexandre Julliard14e68ba2002-11-20 19:54:32 +0000216/* find the first non-deleted hook in the chain */
Andrew Talbotb1788c82007-03-17 10:52:14 +0000217static inline struct hook *get_first_valid_hook( struct hook_table *table, int index,
Dmitry Timoshkov6dba0a72005-02-03 16:40:20 +0000218 int event, user_handle_t win,
219 int object_id, int child_id )
Alexandre Julliard14e68ba2002-11-20 19:54:32 +0000220{
221 struct hook *hook = get_first_hook( table, index );
Dmitry Timoshkov6dba0a72005-02-03 16:40:20 +0000222
223 while (hook)
224 {
Alexandre Julliard63342352005-05-11 13:03:15 +0000225 if (hook->proc && run_hook_in_current_thread( hook ))
Dmitry Timoshkov6dba0a72005-02-03 16:40:20 +0000226 {
Alexandre Julliard63342352005-05-11 13:03:15 +0000227 if (event >= hook->event_min && event <= hook->event_max)
Dmitry Timoshkov6dba0a72005-02-03 16:40:20 +0000228 {
Alexandre Julliard63342352005-05-11 13:03:15 +0000229 if (hook->flags & WINEVENT_INCONTEXT) return hook;
Dmitry Timoshkov6dba0a72005-02-03 16:40:20 +0000230
Alexandre Julliard63342352005-05-11 13:03:15 +0000231 /* only winevent hooks may be out of context */
232 assert(hook->index + WH_MINHOOK == WH_WINEVENT);
233 post_win_event( hook->owner, event, win, object_id, child_id,
234 hook->proc, hook->module, hook->module_size,
235 hook->handle );
Dmitry Timoshkov6dba0a72005-02-03 16:40:20 +0000236 }
237 }
Alexandre Julliard14e68ba2002-11-20 19:54:32 +0000238 hook = HOOK_ENTRY( list_next( &table->hooks[index], &hook->chain ) );
Dmitry Timoshkov6dba0a72005-02-03 16:40:20 +0000239 }
Alexandre Julliard14e68ba2002-11-20 19:54:32 +0000240 return hook;
241}
242
Alexandre Julliard02861352002-10-29 00:41:42 +0000243/* find the next hook in the chain, skipping the deleted ones */
Alexandre Julliard4a40b2e2005-07-11 18:05:50 +0000244static struct hook *get_next_hook( struct thread *thread, struct hook *hook, int event,
245 user_handle_t win, int object_id, int child_id )
Alexandre Julliard02861352002-10-29 00:41:42 +0000246{
Alexandre Julliard4a40b2e2005-07-11 18:05:50 +0000247 struct hook_table *global_hooks, *table = hook->table;
Alexandre Julliard14e68ba2002-11-20 19:54:32 +0000248 int index = hook->index;
Alexandre Julliard02861352002-10-29 00:41:42 +0000249
Alexandre Julliard14e68ba2002-11-20 19:54:32 +0000250 while ((hook = HOOK_ENTRY( list_next( &table->hooks[index], &hook->chain ) )))
Alexandre Julliard02861352002-10-29 00:41:42 +0000251 {
Alexandre Julliard63342352005-05-11 13:03:15 +0000252 if (hook->proc && run_hook_in_current_thread( hook ))
Dmitry Timoshkov6dba0a72005-02-03 16:40:20 +0000253 {
Alexandre Julliard63342352005-05-11 13:03:15 +0000254 if (event >= hook->event_min && event <= hook->event_max)
Dmitry Timoshkov6dba0a72005-02-03 16:40:20 +0000255 {
Alexandre Julliard63342352005-05-11 13:03:15 +0000256 if (hook->flags & WINEVENT_INCONTEXT) return hook;
Dmitry Timoshkov6dba0a72005-02-03 16:40:20 +0000257
Alexandre Julliard63342352005-05-11 13:03:15 +0000258 /* only winevent hooks may be out of context */
259 assert(hook->index + WH_MINHOOK == WH_WINEVENT);
260 post_win_event( hook->owner, event, win, object_id, child_id,
261 hook->proc, hook->module, hook->module_size,
262 hook->handle );
Dmitry Timoshkov6dba0a72005-02-03 16:40:20 +0000263 }
264 }
Alexandre Julliard02861352002-10-29 00:41:42 +0000265 }
Alexandre Julliard4a40b2e2005-07-11 18:05:50 +0000266 global_hooks = get_global_hooks( thread );
Alexandre Julliard14e68ba2002-11-20 19:54:32 +0000267 if (global_hooks && table != global_hooks) /* now search through the global table */
268 {
Dmitry Timoshkov6dba0a72005-02-03 16:40:20 +0000269 hook = get_first_valid_hook( global_hooks, index, event, win, object_id, child_id );
Alexandre Julliard14e68ba2002-11-20 19:54:32 +0000270 }
271 return hook;
Alexandre Julliard02861352002-10-29 00:41:42 +0000272}
273
274static void hook_table_dump( struct object *obj, int verbose )
275{
Alexandre Julliard4a40b2e2005-07-11 18:05:50 +0000276 /* struct hook_table *table = (struct hook_table *)obj; */
277 fprintf( stderr, "Hook table\n" );
Alexandre Julliard02861352002-10-29 00:41:42 +0000278}
279
280static void hook_table_destroy( struct object *obj )
281{
282 int i;
283 struct hook *hook;
284 struct hook_table *table = (struct hook_table *)obj;
285
286 for (i = 0; i < NB_HOOKS; i++)
287 {
288 while ((hook = get_first_hook( table, i )) != NULL) free_hook( hook );
289 }
290}
291
292/* remove a hook, freeing it if the chain is not in use */
293static void remove_hook( struct hook *hook )
294{
Alexandre Julliard4a40b2e2005-07-11 18:05:50 +0000295 if (hook->table->counts[hook->index])
Alexandre Julliardcc55fd32008-12-29 17:35:35 +0100296 hook->proc = 0; /* chain is in use, just mark it and return */
Alexandre Julliard02861352002-10-29 00:41:42 +0000297 else
298 free_hook( hook );
299}
300
301/* release a hook chain, removing deleted hooks if the use count drops to 0 */
302static void release_hook_chain( struct hook_table *table, int index )
303{
304 if (!table->counts[index]) /* use count shouldn't already be 0 */
305 {
306 set_error( STATUS_INVALID_PARAMETER );
307 return;
308 }
309 if (!--table->counts[index])
310 {
311 struct hook *hook = get_first_hook( table, index );
312 while (hook)
313 {
314 struct hook *next = HOOK_ENTRY( list_next( &table->hooks[hook->index], &hook->chain ) );
315 if (!hook->proc) free_hook( hook );
316 hook = next;
317 }
318 }
319}
320
Alexandre Julliardca3ac8f2003-07-11 21:55:58 +0000321/* remove all global hooks owned by a given thread */
322void remove_thread_hooks( struct thread *thread )
323{
Alexandre Julliard4a40b2e2005-07-11 18:05:50 +0000324 struct hook_table *global_hooks = get_global_hooks( thread );
Alexandre Julliardca3ac8f2003-07-11 21:55:58 +0000325 int index;
326
327 if (!global_hooks) return;
328
329 /* only low-level keyboard/mouse global hooks can be owned by a thread */
330 for (index = WH_KEYBOARD_LL - WH_MINHOOK; index <= WH_MOUSE_LL - WH_MINHOOK; index++)
331 {
332 struct hook *hook = get_first_hook( global_hooks, index );
333 while (hook)
334 {
335 struct hook *next = HOOK_ENTRY( list_next( &global_hooks->hooks[index], &hook->chain ) );
336 if (hook->thread == thread) remove_hook( hook );
337 hook = next;
338 }
339 }
340}
Alexandre Julliard02861352002-10-29 00:41:42 +0000341
Alexandre Julliard63342352005-05-11 13:03:15 +0000342/* get a bitmap of active hooks in a hook table */
343static int is_hook_active( struct hook_table *table, int index )
344{
345 struct hook *hook = get_first_hook( table, index );
346
347 while (hook)
348 {
349 if (hook->proc && run_hook_in_current_thread( hook )) return 1;
350 hook = HOOK_ENTRY( list_next( &table->hooks[index], &hook->chain ) );
351 }
352 return 0;
353}
354
355/* get a bitmap of all active hooks for the current thread */
356unsigned int get_active_hooks(void)
357{
358 struct hook_table *table = get_queue_hooks( current );
Alexandre Julliard4a40b2e2005-07-11 18:05:50 +0000359 struct hook_table *global_hooks = get_global_hooks( current );
Alexandre Julliard63342352005-05-11 13:03:15 +0000360 unsigned int ret = 1 << 31; /* set high bit to indicate that the bitmap is valid */
361 int id;
362
363 for (id = WH_MINHOOK; id <= WH_WINEVENT; id++)
364 {
365 if ((table && is_hook_active( table, id - WH_MINHOOK )) ||
366 (global_hooks && is_hook_active( global_hooks, id - WH_MINHOOK )))
367 ret |= 1 << (id - WH_MINHOOK);
368 }
369 return ret;
370}
371
Alexandre Julliard02861352002-10-29 00:41:42 +0000372/* set a window hook */
373DECL_HANDLER(set_hook)
374{
Dmitry Timoshkov6dba0a72005-02-03 16:40:20 +0000375 struct process *process = NULL;
376 struct thread *thread = NULL;
Alexandre Julliard4a40b2e2005-07-11 18:05:50 +0000377 struct desktop *desktop;
Alexandre Julliard02861352002-10-29 00:41:42 +0000378 struct hook *hook;
Alexandre Julliard14e68ba2002-11-20 19:54:32 +0000379 WCHAR *module;
Alexandre Julliardca3ac8f2003-07-11 21:55:58 +0000380 int global;
Alexandre Julliard0f273c12006-07-26 10:43:25 +0200381 data_size_t module_size = get_req_data_size();
Alexandre Julliard02861352002-10-29 00:41:42 +0000382
Dmitry Timoshkov6dba0a72005-02-03 16:40:20 +0000383 if (!req->proc || req->id < WH_MINHOOK || req->id > WH_WINEVENT)
Alexandre Julliard02861352002-10-29 00:41:42 +0000384 {
385 set_error( STATUS_INVALID_PARAMETER );
386 return;
387 }
Dmitry Timoshkov6dba0a72005-02-03 16:40:20 +0000388
Alexandre Julliard4a40b2e2005-07-11 18:05:50 +0000389 if (!(desktop = get_thread_desktop( current, DESKTOP_HOOKCONTROL ))) return;
390
391 if (req->pid && !(process = get_process_from_id( req->pid ))) goto done;
Dmitry Timoshkov6dba0a72005-02-03 16:40:20 +0000392
393 if (req->tid)
394 {
Alexandre Julliard4a40b2e2005-07-11 18:05:50 +0000395 if (!(thread = get_thread_from_id( req->tid ))) goto done;
Dmitry Timoshkov6dba0a72005-02-03 16:40:20 +0000396 if (process && process != thread->process)
397 {
Dmitry Timoshkov6dba0a72005-02-03 16:40:20 +0000398 set_error( STATUS_INVALID_PARAMETER );
Alexandre Julliard4a40b2e2005-07-11 18:05:50 +0000399 goto done;
Dmitry Timoshkov6dba0a72005-02-03 16:40:20 +0000400 }
401 }
402
Alexandre Julliardca3ac8f2003-07-11 21:55:58 +0000403 if (req->id == WH_KEYBOARD_LL || req->id == WH_MOUSE_LL)
404 {
405 /* low-level hardware hooks are special: always global, but without a module */
Alexandre Julliard6d85f3b2006-01-27 12:12:15 +0100406 if (thread)
407 {
408 set_error( STATUS_INVALID_PARAMETER );
409 goto done;
410 }
Alexandre Julliardca3ac8f2003-07-11 21:55:58 +0000411 module = NULL;
412 global = 1;
413 }
414 else if (!req->tid)
Alexandre Julliard14e68ba2002-11-20 19:54:32 +0000415 {
Dmitry Timoshkov6dba0a72005-02-03 16:40:20 +0000416 /* out of context hooks do not need a module handle */
417 if (!module_size && (req->flags & WINEVENT_INCONTEXT))
Alexandre Julliard14e68ba2002-11-20 19:54:32 +0000418 {
419 set_error( STATUS_INVALID_PARAMETER );
Alexandre Julliard4a40b2e2005-07-11 18:05:50 +0000420 goto done;
Alexandre Julliard14e68ba2002-11-20 19:54:32 +0000421 }
Alexandre Julliard4a40b2e2005-07-11 18:05:50 +0000422 if (!(module = memdup( get_req_data(), module_size ))) goto done;
Alexandre Julliardca3ac8f2003-07-11 21:55:58 +0000423 global = 1;
Alexandre Julliard14e68ba2002-11-20 19:54:32 +0000424 }
425 else
426 {
Alexandre Julliard04a9f932007-08-22 11:59:21 +0200427 /* module is optional only if hook is in current process */
428 if (!module_size)
429 {
430 module = NULL;
431 if (thread->process != current->process)
432 {
433 set_error( STATUS_INVALID_PARAMETER );
434 goto done;
435 }
436 }
437 else if (!(module = memdup( get_req_data(), module_size ))) goto done;
Alexandre Julliardca3ac8f2003-07-11 21:55:58 +0000438 global = 0;
Alexandre Julliard14e68ba2002-11-20 19:54:32 +0000439 }
Alexandre Julliard02861352002-10-29 00:41:42 +0000440
Alexandre Julliard4a40b2e2005-07-11 18:05:50 +0000441 if ((hook = add_hook( desktop, thread, req->id - WH_MINHOOK, global )))
Alexandre Julliard02861352002-10-29 00:41:42 +0000442 {
Dmitry Timoshkov6dba0a72005-02-03 16:40:20 +0000443 hook->owner = (struct thread *)grab_object( current );
444 hook->process = process ? (struct process *)grab_object( process ) : NULL;
445 hook->event_min = req->event_min;
446 hook->event_max = req->event_max;
447 hook->flags = req->flags;
Alexandre Julliard14e68ba2002-11-20 19:54:32 +0000448 hook->proc = req->proc;
449 hook->unicode = req->unicode;
450 hook->module = module;
451 hook->module_size = module_size;
Alexandre Julliard02861352002-10-29 00:41:42 +0000452 reply->handle = hook->handle;
Alexandre Julliard63342352005-05-11 13:03:15 +0000453 reply->active_hooks = get_active_hooks();
Alexandre Julliard02861352002-10-29 00:41:42 +0000454 }
Michael Stefaniuc5ceccec2006-10-09 23:34:36 +0200455 else free( module );
Alexandre Julliard14e68ba2002-11-20 19:54:32 +0000456
Alexandre Julliard4a40b2e2005-07-11 18:05:50 +0000457done:
Dmitry Timoshkov6dba0a72005-02-03 16:40:20 +0000458 if (process) release_object( process );
Alexandre Julliard14e68ba2002-11-20 19:54:32 +0000459 if (thread) release_object( thread );
Alexandre Julliard4a40b2e2005-07-11 18:05:50 +0000460 release_object( desktop );
Alexandre Julliard02861352002-10-29 00:41:42 +0000461}
462
463
464/* remove a window hook */
465DECL_HANDLER(remove_hook)
466{
467 struct hook *hook;
468
Dmitry Timoshkoved04d362005-01-27 11:14:19 +0000469 if (req->handle)
470 {
471 if (!(hook = get_user_object( req->handle, USER_HOOK )))
472 {
Dmitry Timoshkov6dba0a72005-02-03 16:40:20 +0000473 set_error( STATUS_INVALID_HANDLE );
Dmitry Timoshkoved04d362005-01-27 11:14:19 +0000474 return;
475 }
476 }
Alexandre Julliard02861352002-10-29 00:41:42 +0000477 else
478 {
Dmitry Timoshkov6dba0a72005-02-03 16:40:20 +0000479 if (!req->proc || req->id < WH_MINHOOK || req->id > WH_WINEVENT)
Alexandre Julliard02861352002-10-29 00:41:42 +0000480 {
481 set_error( STATUS_INVALID_PARAMETER );
482 return;
483 }
484 if (!(hook = find_hook( current, req->id - WH_MINHOOK, req->proc )))
Dmitry Timoshkoved04d362005-01-27 11:14:19 +0000485 {
Alexandre Julliard02861352002-10-29 00:41:42 +0000486 set_error( STATUS_INVALID_PARAMETER );
Dmitry Timoshkoved04d362005-01-27 11:14:19 +0000487 return;
488 }
Alexandre Julliard02861352002-10-29 00:41:42 +0000489 }
Dmitry Timoshkoved04d362005-01-27 11:14:19 +0000490 remove_hook( hook );
Alexandre Julliard63342352005-05-11 13:03:15 +0000491 reply->active_hooks = get_active_hooks();
Alexandre Julliard02861352002-10-29 00:41:42 +0000492}
493
494
495/* start calling a hook chain */
496DECL_HANDLER(start_hook_chain)
497{
498 struct hook *hook;
Alexandre Julliardd55e7f12003-07-03 18:16:48 +0000499 struct hook_table *table = get_queue_hooks( current );
Alexandre Julliard02861352002-10-29 00:41:42 +0000500
Dmitry Timoshkov6dba0a72005-02-03 16:40:20 +0000501 if (req->id < WH_MINHOOK || req->id > WH_WINEVENT)
Alexandre Julliard02861352002-10-29 00:41:42 +0000502 {
503 set_error( STATUS_INVALID_PARAMETER );
504 return;
505 }
Alexandre Julliard14e68ba2002-11-20 19:54:32 +0000506
Alexandre Julliard63342352005-05-11 13:03:15 +0000507 reply->active_hooks = get_active_hooks();
508
509 if (!table || !(hook = get_first_valid_hook( table, req->id - WH_MINHOOK, req->event,
510 req->window, req->object_id, req->child_id )))
Alexandre Julliard14e68ba2002-11-20 19:54:32 +0000511 {
512 /* try global table */
Alexandre Julliard4a40b2e2005-07-11 18:05:50 +0000513 if (!(table = get_global_hooks( current )) ||
514 !(hook = get_first_valid_hook( table, req->id - WH_MINHOOK, req->event,
Alexandre Julliard63342352005-05-11 13:03:15 +0000515 req->window, req->object_id, req->child_id )))
Alexandre Julliard14e68ba2002-11-20 19:54:32 +0000516 return; /* no hook set */
517 }
Alexandre Julliardca3ac8f2003-07-11 21:55:58 +0000518
Alexandre Julliard6d85f3b2006-01-27 12:12:15 +0100519 if (run_hook_in_owner_thread( hook ))
Alexandre Julliardca3ac8f2003-07-11 21:55:58 +0000520 {
Alexandre Julliard6d85f3b2006-01-27 12:12:15 +0100521 reply->pid = get_process_id( hook->owner->process );
522 reply->tid = get_thread_id( hook->owner );
Alexandre Julliardca3ac8f2003-07-11 21:55:58 +0000523 }
524 else
525 {
526 reply->pid = 0;
527 reply->tid = 0;
Alexandre Julliardca3ac8f2003-07-11 21:55:58 +0000528 }
Dmitry Timoshkov6dba0a72005-02-03 16:40:20 +0000529 reply->proc = hook->proc;
Alexandre Julliard02861352002-10-29 00:41:42 +0000530 reply->handle = hook->handle;
Alexandre Julliard02861352002-10-29 00:41:42 +0000531 reply->unicode = hook->unicode;
532 table->counts[hook->index]++;
Alexandre Julliard14e68ba2002-11-20 19:54:32 +0000533 if (hook->module) set_reply_data( hook->module, hook->module_size );
Alexandre Julliard02861352002-10-29 00:41:42 +0000534}
535
536
537/* finished calling a hook chain */
538DECL_HANDLER(finish_hook_chain)
539{
Alexandre Julliardd55e7f12003-07-03 18:16:48 +0000540 struct hook_table *table = get_queue_hooks( current );
Alexandre Julliard4a40b2e2005-07-11 18:05:50 +0000541 struct hook_table *global_hooks = get_global_hooks( current );
Alexandre Julliard02861352002-10-29 00:41:42 +0000542 int index = req->id - WH_MINHOOK;
543
Dmitry Timoshkov6dba0a72005-02-03 16:40:20 +0000544 if (req->id < WH_MINHOOK || req->id > WH_WINEVENT)
Alexandre Julliard02861352002-10-29 00:41:42 +0000545 {
546 set_error( STATUS_INVALID_PARAMETER );
547 return;
548 }
549 if (table) release_hook_chain( table, index );
Alexandre Julliard14e68ba2002-11-20 19:54:32 +0000550 if (global_hooks) release_hook_chain( global_hooks, index );
Alexandre Julliard02861352002-10-29 00:41:42 +0000551}
552
553
Alexandre Julliard2f80fcd2006-10-05 14:05:48 +0200554/* get the hook information */
555DECL_HANDLER(get_hook_info)
Alexandre Julliard02861352002-10-29 00:41:42 +0000556{
Alexandre Julliard2f80fcd2006-10-05 14:05:48 +0200557 struct hook *hook;
Alexandre Julliard02861352002-10-29 00:41:42 +0000558
559 if (!(hook = get_user_object( req->handle, USER_HOOK ))) return;
Alexandre Julliard14e68ba2002-11-20 19:54:32 +0000560 if (hook->thread && (hook->thread != current))
Alexandre Julliard02861352002-10-29 00:41:42 +0000561 {
562 set_error( STATUS_INVALID_HANDLE );
563 return;
564 }
Alexandre Julliard2f80fcd2006-10-05 14:05:48 +0200565 if (req->get_next && !(hook = get_next_hook( current, hook, req->event, req->window,
566 req->object_id, req->child_id )))
567 return;
568
569 reply->handle = hook->handle;
570 reply->id = hook->index + WH_MINHOOK;
571 reply->unicode = hook->unicode;
572 if (hook->module) set_reply_data( hook->module, min(hook->module_size,get_reply_max_size()) );
573 if (run_hook_in_owner_thread( hook ))
Alexandre Julliard02861352002-10-29 00:41:42 +0000574 {
Alexandre Julliard2f80fcd2006-10-05 14:05:48 +0200575 reply->pid = get_process_id( hook->owner->process );
576 reply->tid = get_thread_id( hook->owner );
Alexandre Julliard02861352002-10-29 00:41:42 +0000577 }
Alexandre Julliard2f80fcd2006-10-05 14:05:48 +0200578 else
579 {
580 reply->pid = 0;
581 reply->tid = 0;
582 }
583 reply->proc = hook->proc;
Alexandre Julliard02861352002-10-29 00:41:42 +0000584}