hook.c 27.8 KB
Newer Older
1 2 3 4
/*
 * Windows hook functions
 *
 * Copyright 2002 Alexandre Julliard
5
 * Copyright 2005 Dmitry Timoshkov
6 7 8 9 10 11 12 13 14 15 16 17 18
 *
 * This library is free software; you can redistribute it and/or
 * modify it under the terms of the GNU Lesser General Public
 * License as published by the Free Software Foundation; either
 * version 2.1 of the License, or (at your option) any later version.
 *
 * This library is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
 * Lesser General Public License for more details.
 *
 * You should have received a copy of the GNU Lesser General Public
 * License along with this library; if not, write to the Free Software
19
 * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301, USA
20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37
 *
 * NOTES:
 *   Status of the various hooks:
 *     WH_MSGFILTER                 OK
 *     WH_JOURNALRECORD             Partially implemented
 *     WH_JOURNALPLAYBACK           Partially implemented
 *     WH_KEYBOARD                  OK
 *     WH_GETMESSAGE	            OK (FIXME: A/W mapping?)
 *     WH_CALLWNDPROC	            OK (FIXME: A/W mapping?)
 *     WH_CBT
 *       HCBT_MOVESIZE              OK
 *       HCBT_MINMAX                OK
 *       HCBT_QS                    OK
 *       HCBT_CREATEWND             OK
 *       HCBT_DESTROYWND            OK
 *       HCBT_ACTIVATE              OK
 *       HCBT_CLICKSKIPPED          OK
 *       HCBT_KEYSKIPPED            OK
38
 *       HCBT_SYSCOMMAND            OK
39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64
 *       HCBT_SETFOCUS              OK
 *     WH_SYSMSGFILTER	            OK
 *     WH_MOUSE	                    OK
 *     WH_HARDWARE	            Not supported in Win32
 *     WH_DEBUG	                    Not implemented
 *     WH_SHELL
 *       HSHELL_WINDOWCREATED       OK
 *       HSHELL_WINDOWDESTROYED     OK
 *       HSHELL_ACTIVATESHELLWINDOW Not implemented
 *       HSHELL_WINDOWACTIVATED     Not implemented
 *       HSHELL_GETMINRECT          Not implemented
 *       HSHELL_REDRAW              Not implemented
 *       HSHELL_TASKMAN             Not implemented
 *       HSHELL_LANGUAGE            Not implemented
 *       HSHELL_SYSMENU             Not implemented
 *       HSHELL_ENDTASK             Not implemented
 *       HSHELL_ACCESSIBILITYSTATE  Not implemented
 *       HSHELL_APPCOMMAND          Not implemented
 *       HSHELL_WINDOWREPLACED      Not implemented
 *       HSHELL_WINDOWREPLACING     Not implemented
 *     WH_FOREGROUNDIDLE            Not implemented
 *     WH_CALLWNDPROCRET            OK (FIXME: A/W mapping?)
 *     WH_KEYBOARD_LL               Implemented but should use SendMessage instead
 *     WH_MOUSE_LL                  Implemented but should use SendMessage instead
 */

Steven Edwards's avatar
Steven Edwards committed
65 66 67
#include "config.h"
#include "wine/port.h"

68
#include <stdarg.h>
69
#include <assert.h>
70

71 72 73 74 75
#include "windef.h"
#include "winbase.h"
#include "winuser.h"
#include "winerror.h"
#include "win.h"
76
#include "user_private.h"
77
#include "wine/server.h"
78
#include "wine/unicode.h"
79
#include "wine/debug.h"
80
#include "winternl.h"
81 82 83 84

WINE_DEFAULT_DEBUG_CHANNEL(hook);
WINE_DECLARE_DEBUG_CHANNEL(relay);

85 86
struct hook_info
{
87
    INT id;
88
    void *proc;
89
    void *handle;
90 91
    DWORD pid, tid;
    BOOL prev_unicode, next_unicode;
92 93 94 95 96 97
    WCHAR module[MAX_PATH];
};

#define WH_WINEVENT (WH_MAXHOOK+1)

static const char * const hook_names[WH_WINEVENT - WH_MINHOOK + 1] =
98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113
{
    "WH_MSGFILTER",
    "WH_JOURNALRECORD",
    "WH_JOURNALPLAYBACK",
    "WH_KEYBOARD",
    "WH_GETMESSAGE",
    "WH_CALLWNDPROC",
    "WH_CBT",
    "WH_SYSMSGFILTER",
    "WH_MOUSE",
    "WH_HARDWARE",
    "WH_DEBUG",
    "WH_SHELL",
    "WH_FOREGROUNDIDLE",
    "WH_CALLWNDPROCRET",
    "WH_KEYBOARD_LL",
114 115
    "WH_MOUSE_LL",
    "WH_WINEVENT"
116 117 118
};


119 120 121 122 123 124 125 126 127 128 129
/***********************************************************************
 *		get_ll_hook_timeout
 *
 */
static UINT get_ll_hook_timeout(void)
{
    /* FIXME: should retrieve LowLevelHooksTimeout in HKEY_CURRENT_USER\Control Panel\Desktop */
    return 2000;
}


130 131 132 133 134 135 136 137
/***********************************************************************
 *		set_windows_hook
 *
 * Implementation of SetWindowsHookExA and SetWindowsHookExW.
 */
static HHOOK set_windows_hook( INT id, HOOKPROC proc, HINSTANCE inst, DWORD tid, BOOL unicode )
{
    HHOOK handle = 0;
138
    WCHAR module[MAX_PATH];
139
    DWORD len;
140

141 142 143 144 145 146
    if (!proc)
    {
        SetLastError( ERROR_INVALID_FILTER_PROC );
        return 0;
    }

147 148 149 150 151 152 153 154 155 156 157 158 159 160 161
    if (tid)  /* thread-local hook */
    {
        if (id == WH_JOURNALRECORD ||
            id == WH_JOURNALPLAYBACK ||
            id == WH_KEYBOARD_LL ||
            id == WH_MOUSE_LL ||
            id == WH_SYSMSGFILTER)
        {
            /* these can only be global */
            SetLastError( ERROR_INVALID_PARAMETER );
            return 0;
        }
    }
    else  /* system-global hook */
    {
162
        if (id == WH_KEYBOARD_LL || id == WH_MOUSE_LL) inst = 0;
163
        else if (!inst)
164
        {
165
            SetLastError( ERROR_HOOK_NEEDS_HMOD );
166 167 168 169
            return 0;
        }
    }

170 171 172 173 174 175
    if (inst && (!(len = GetModuleFileNameW( inst, module, MAX_PATH )) || len >= MAX_PATH))
    {
        SetLastError( ERROR_INVALID_PARAMETER );
        return 0;
    }

176 177
    SERVER_START_REQ( set_hook )
    {
178 179 180 181 182 183 184
        req->id        = id;
        req->pid       = 0;
        req->tid       = tid;
        req->event_min = EVENT_MIN;
        req->event_max = EVENT_MAX;
        req->flags     = WINEVENT_INCONTEXT;
        req->unicode   = unicode;
185 186
        if (inst) /* make proc relative to the module base */
        {
187
            req->proc = wine_server_client_ptr( (void *)((char *)proc - (char *)inst) );
188 189
            wine_server_add_data( req, module, strlenW(module) * sizeof(WCHAR) );
        }
190
        else req->proc = wine_server_client_ptr( proc );
191

192 193
        if (!wine_server_call_err( req ))
        {
194
            handle = wine_server_ptr_handle( reply->handle );
195 196
            get_user_thread_info()->active_hooks = reply->active_hooks;
        }
197 198 199
    }
    SERVER_END_REQ;

200
    TRACE( "%s %p %x -> %p\n", hook_names[id-WH_MINHOOK], proc, tid, handle );
201 202 203 204 205 206 207 208 209 210
    return handle;
}


/***********************************************************************
 *		call_hook_AtoW
 */
static LRESULT call_hook_AtoW( HOOKPROC proc, INT id, INT code, WPARAM wparam, LPARAM lparam )
{
    LRESULT ret;
211
    UNICODE_STRING usBuffer;
212 213 214 215 216 217
    if (id != WH_CBT || code != HCBT_CREATEWND) ret = proc( code, wparam, lparam );
    else
    {
        CBT_CREATEWNDA *cbtcwA = (CBT_CREATEWNDA *)lparam;
        CBT_CREATEWNDW cbtcwW;
        CREATESTRUCTW csW;
218 219
        LPWSTR nameW = NULL;
        LPWSTR classW = NULL;
220 221 222 223 224

        cbtcwW.lpcs = &csW;
        cbtcwW.hwndInsertAfter = cbtcwA->hwndInsertAfter;
        csW = *(CREATESTRUCTW *)cbtcwA->lpcs;

225
        if (!IS_INTRESOURCE(cbtcwA->lpcs->lpszName))
226 227
        {
            RtlCreateUnicodeStringFromAsciiz(&usBuffer,cbtcwA->lpcs->lpszName);
228
            csW.lpszName = nameW = usBuffer.Buffer;
229
        }
230
        if (!IS_INTRESOURCE(cbtcwA->lpcs->lpszClass))
231
        {
232
            RtlCreateUnicodeStringFromAsciiz(&usBuffer,cbtcwA->lpcs->lpszClass);
233
            csW.lpszClass = classW = usBuffer.Buffer;
234
        }
235 236
        ret = proc( code, wparam, (LPARAM)&cbtcwW );
        cbtcwA->hwndInsertAfter = cbtcwW.hwndInsertAfter;
237 238
        HeapFree( GetProcessHeap(), 0, nameW );
        HeapFree( GetProcessHeap(), 0, classW );
239 240 241 242 243 244 245 246 247 248 249 250 251 252 253 254 255 256
    }
    return ret;
}


/***********************************************************************
 *		call_hook_WtoA
 */
static LRESULT call_hook_WtoA( HOOKPROC proc, INT id, INT code, WPARAM wparam, LPARAM lparam )
{
    LRESULT ret;

    if (id != WH_CBT || code != HCBT_CREATEWND) ret = proc( code, wparam, lparam );
    else
    {
        CBT_CREATEWNDW *cbtcwW = (CBT_CREATEWNDW *)lparam;
        CBT_CREATEWNDA cbtcwA;
        CREATESTRUCTA csA;
257
        int len;
258 259
        LPSTR nameA = NULL;
        LPSTR classA = NULL;
260 261 262 263 264

        cbtcwA.lpcs = &csA;
        cbtcwA.hwndInsertAfter = cbtcwW->hwndInsertAfter;
        csA = *(CREATESTRUCTA *)cbtcwW->lpcs;

265
        if (!IS_INTRESOURCE(cbtcwW->lpcs->lpszName)) {
266
            len = WideCharToMultiByte( CP_ACP, 0, cbtcwW->lpcs->lpszName, -1, NULL, 0, NULL, NULL );
267 268 269
            nameA = HeapAlloc( GetProcessHeap(), 0, len*sizeof(CHAR) );
            WideCharToMultiByte( CP_ACP, 0, cbtcwW->lpcs->lpszName, -1, nameA, len, NULL, NULL );
            csA.lpszName = nameA;
270 271
        }

272
        if (!IS_INTRESOURCE(cbtcwW->lpcs->lpszClass)) {
273
            len = WideCharToMultiByte( CP_ACP, 0, cbtcwW->lpcs->lpszClass, -1, NULL, 0, NULL, NULL );
274 275 276
            classA = HeapAlloc( GetProcessHeap(), 0, len*sizeof(CHAR) );
            WideCharToMultiByte( CP_ACP, 0, cbtcwW->lpcs->lpszClass, -1, classA, len, NULL, NULL );
            csA.lpszClass = classA;
277 278
        }

279 280
        ret = proc( code, wparam, (LPARAM)&cbtcwA );
        cbtcwW->hwndInsertAfter = cbtcwA.hwndInsertAfter;
281 282
        HeapFree( GetProcessHeap(), 0, nameA );
        HeapFree( GetProcessHeap(), 0, classA );
283 284 285 286 287 288
    }
    return ret;
}


/***********************************************************************
289
 *		call_hook_proc
290
 */
291 292
static LRESULT call_hook_proc( HOOKPROC proc, INT id, INT code, WPARAM wparam, LPARAM lparam,
                               BOOL prev_unicode, BOOL next_unicode )
293 294 295 296
{
    LRESULT ret;

    if (TRACE_ON(relay))
297
        DPRINTF( "%04x:Call hook proc %p (id=%s,code=%x,wp=%08lx,lp=%08lx)\n",
298 299 300 301 302 303 304
                 GetCurrentThreadId(), proc, hook_names[id-WH_MINHOOK], code, wparam, lparam );

    if (!prev_unicode == !next_unicode) ret = proc( code, wparam, lparam );
    else if (prev_unicode) ret = call_hook_WtoA( proc, id, code, wparam, lparam );
    else ret = call_hook_AtoW( proc, id, code, wparam, lparam );

    if (TRACE_ON(relay))
305
        DPRINTF( "%04x:Ret  hook proc %p (id=%s,code=%x,wp=%08lx,lp=%08lx) retval=%08lx\n",
306 307 308 309 310 311
                 GetCurrentThreadId(), proc, hook_names[id-WH_MINHOOK], code, wparam, lparam, ret );

    return ret;
}


312 313 314 315 316
/***********************************************************************
 *		get_hook_proc
 *
 * Retrieve the hook procedure real value for a module-relative proc
 */
317
void *get_hook_proc( void *proc, const WCHAR *module )
318 319 320 321 322 323 324
{
    HMODULE mod;

    if (!(mod = GetModuleHandleW(module)))
    {
        TRACE( "loading %s\n", debugstr_w(module) );
        /* FIXME: the library will never be freed */
325
        if (!(mod = LoadLibraryExW(module, NULL, LOAD_WITH_ALTERED_SEARCH_PATH))) return NULL;
326
    }
327
    return (char *)mod + (ULONG_PTR)proc;
328 329
}

330
/***********************************************************************
331 332 333 334
 *		call_hook
 *
 * Call hook either in current thread or send message to the destination
 * thread.
335
 */
336
static LRESULT call_hook( struct hook_info *info, INT code, WPARAM wparam, LPARAM lparam )
337
{
338
    DWORD_PTR ret = 0;
339

340
    if (info->tid)
341
    {
342 343 344 345
        struct hook_extra_info h_extra;
        h_extra.handle = info->handle;
        h_extra.lparam = lparam;

346
        TRACE( "calling hook in thread %04x %s code %x wp %lx lp %lx\n",
347
               info->tid, hook_names[info->id-WH_MINHOOK], code, wparam, lparam );
348

349
        switch(info->id)
350 351
        {
        case WH_KEYBOARD_LL:
352 353 354
            MSG_SendInternalMessageTimeout( info->pid, info->tid, WM_WINE_KEYBOARD_LL_HOOK,
                                            wparam, (LPARAM)&h_extra, SMTO_ABORTIFHUNG,
                                            get_ll_hook_timeout(), &ret );
355 356
            break;
        case WH_MOUSE_LL:
357 358 359
            MSG_SendInternalMessageTimeout( info->pid, info->tid, WM_WINE_MOUSE_LL_HOOK,
                                            wparam, (LPARAM)&h_extra, SMTO_ABORTIFHUNG,
                                            get_ll_hook_timeout(), &ret );
360
            break;
361
        default:
362
            ERR("Unknown hook id %d\n", info->id);
363 364
            assert(0);
            break;
365 366
        }
    }
367
    else if (info->proc)
368
    {
369
        TRACE( "calling hook %p %s code %x wp %lx lp %lx module %s\n",
370 371
               info->proc, hook_names[info->id-WH_MINHOOK], code, wparam,
               lparam, debugstr_w(info->module) );
372

373 374
        if (!info->module[0] ||
            (info->proc = get_hook_proc( info->proc, info->module )) != NULL)
375
        {
376
            struct user_thread_info *thread_info = get_user_thread_info();
377
            HHOOK prev = thread_info->hook;
378
            BOOL prev_unicode = thread_info->hook_unicode;
379 380

            thread_info->hook = info->handle;
381
            thread_info->hook_unicode = info->next_unicode;
382
            ret = call_hook_proc( info->proc, info->id, code, wparam, lparam,
383
                                  info->prev_unicode, info->next_unicode );
384
            thread_info->hook = prev;
385
            thread_info->hook_unicode = prev_unicode;
386
        }
387 388 389 390
    }
    return ret;
}

391 392 393 394 395 396 397 398 399 400 401 402 403

/***********************************************************************
 *           HOOK_IsHooked
 */
static BOOL HOOK_IsHooked( INT id )
{
    struct user_thread_info *thread_info = get_user_thread_info();

    if (!thread_info->active_hooks) return TRUE;
    return (thread_info->active_hooks & (1 << (id - WH_MINHOOK))) != 0;
}


404 405 406 407 408 409 410 411 412 413 414 415 416 417 418 419 420 421 422 423
/***********************************************************************
 *		HOOK_CallHooks
 */
LRESULT HOOK_CallHooks( INT id, INT code, WPARAM wparam, LPARAM lparam, BOOL unicode )
{
    struct user_thread_info *thread_info = get_user_thread_info();
    struct hook_info info;
    DWORD_PTR ret = 0;

    USER_CheckNotLock();

    if (!HOOK_IsHooked( id ))
    {
        TRACE( "skipping hook %s mask %x\n", hook_names[id-WH_MINHOOK], thread_info->active_hooks );
        return 0;
    }

    ZeroMemory( &info, sizeof(info) - sizeof(info.module) );
    info.prev_unicode = unicode;
    info.id = id;
424

425 426 427 428 429 430 431 432
    SERVER_START_REQ( start_hook_chain )
    {
        req->id = info.id;
        req->event = EVENT_MIN;
        wine_server_set_reply( req, info.module, sizeof(info.module)-sizeof(WCHAR) );
        if (!wine_server_call( req ))
        {
            info.module[wine_server_reply_size(req) / sizeof(WCHAR)] = 0;
433
            info.handle       = wine_server_ptr_handle( reply->handle );
434 435
            info.pid          = reply->pid;
            info.tid          = reply->tid;
436
            info.proc         = wine_server_get_ptr( reply->proc );
437 438 439
            info.next_unicode = reply->unicode;
            thread_info->active_hooks = reply->active_hooks;
        }
440
    }
441 442 443 444
    SERVER_END_REQ;

    if (!info.tid && !info.proc) return 0;
    ret = call_hook( &info, code, wparam, lparam );
445

446 447 448 449 450 451
    SERVER_START_REQ( finish_hook_chain )
    {
        req->id = id;
        wine_server_call( req );
    }
    SERVER_END_REQ;
452 453 454 455 456 457 458 459 460 461 462 463 464 465 466 467 468 469 470 471 472 473 474 475 476 477 478 479 480 481 482 483 484 485 486 487 488 489 490 491 492 493 494 495 496 497 498 499 500 501
    return ret;
}


/***********************************************************************
 *		SetWindowsHookA (USER32.@)
 */
HHOOK WINAPI SetWindowsHookA( INT id, HOOKPROC proc )
{
    return SetWindowsHookExA( id, proc, 0, GetCurrentThreadId() );
}


/***********************************************************************
 *		SetWindowsHookW (USER32.@)
 */
HHOOK WINAPI SetWindowsHookW( INT id, HOOKPROC proc )
{
    return SetWindowsHookExW( id, proc, 0, GetCurrentThreadId() );
}


/***********************************************************************
 *		SetWindowsHookExA (USER32.@)
 */
HHOOK WINAPI SetWindowsHookExA( INT id, HOOKPROC proc, HINSTANCE inst, DWORD tid )
{
    return set_windows_hook( id, proc, inst, tid, FALSE );
}

/***********************************************************************
 *		SetWindowsHookExW (USER32.@)
 */
HHOOK WINAPI SetWindowsHookExW( INT id, HOOKPROC proc, HINSTANCE inst, DWORD tid )
{
    return set_windows_hook( id, proc, inst, tid, TRUE );
}


/***********************************************************************
 *		UnhookWindowsHook (USER32.@)
 */
BOOL WINAPI UnhookWindowsHook( INT id, HOOKPROC proc )
{
    BOOL ret;

    TRACE( "%s %p\n", hook_names[id-WH_MINHOOK], proc );

    SERVER_START_REQ( remove_hook )
    {
502
        req->handle = 0;
503
        req->id   = id;
504
        req->proc = wine_server_client_ptr( proc );
505
        ret = !wine_server_call_err( req );
506
        if (ret) get_user_thread_info()->active_hooks = reply->active_hooks;
507 508
    }
    SERVER_END_REQ;
509
    if (!ret && GetLastError() == ERROR_INVALID_HANDLE) SetLastError( ERROR_INVALID_HOOK_HANDLE );
510 511 512 513 514 515 516 517 518 519 520 521 522 523
    return ret;
}



/***********************************************************************
 *		UnhookWindowsHookEx (USER32.@)
 */
BOOL WINAPI UnhookWindowsHookEx( HHOOK hhook )
{
    BOOL ret;

    SERVER_START_REQ( remove_hook )
    {
524
        req->handle = wine_server_user_handle( hhook );
525
        req->id     = 0;
526
        ret = !wine_server_call_err( req );
527
        if (ret) get_user_thread_info()->active_hooks = reply->active_hooks;
528 529
    }
    SERVER_END_REQ;
530
    if (!ret && GetLastError() == ERROR_INVALID_HANDLE) SetLastError( ERROR_INVALID_HOOK_HANDLE );
531 532 533 534 535 536 537 538 539
    return ret;
}


/***********************************************************************
 *		CallNextHookEx (USER32.@)
 */
LRESULT WINAPI CallNextHookEx( HHOOK hhook, INT code, WPARAM wparam, LPARAM lparam )
{
540
    struct user_thread_info *thread_info = get_user_thread_info();
541 542 543
    struct hook_info info;

    ZeroMemory( &info, sizeof(info) - sizeof(info.module) );
544

545
    SERVER_START_REQ( get_hook_info )
546
    {
547
        req->handle = wine_server_user_handle( thread_info->hook );
548
        req->get_next = 1;
549
        req->event = EVENT_MIN;
550
        wine_server_set_reply( req, info.module, sizeof(info.module)-sizeof(WCHAR) );
551 552
        if (!wine_server_call_err( req ))
        {
553
            info.module[wine_server_reply_size(req) / sizeof(WCHAR)] = 0;
554
            info.handle       = wine_server_ptr_handle( reply->handle );
555 556 557
            info.id           = reply->id;
            info.pid          = reply->pid;
            info.tid          = reply->tid;
558
            info.proc         = wine_server_get_ptr( reply->proc );
559
            info.next_unicode = reply->unicode;
560 561 562
        }
    }
    SERVER_END_REQ;
563

564
    info.prev_unicode = thread_info->hook_unicode;
565
    return call_hook( &info, code, wparam, lparam );
566 567 568
}


569 570 571 572 573 574 575 576
LRESULT call_current_hook( HHOOK hhook, INT code, WPARAM wparam, LPARAM lparam )
{
    struct hook_info info;

    ZeroMemory( &info, sizeof(info) - sizeof(info.module) );

    SERVER_START_REQ( get_hook_info )
    {
577
        req->handle = wine_server_user_handle( hhook );
578 579 580 581 582 583
        req->get_next = 0;
        req->event = EVENT_MIN;
        wine_server_set_reply( req, info.module, sizeof(info.module)-sizeof(WCHAR) );
        if (!wine_server_call_err( req ))
        {
            info.module[wine_server_reply_size(req) / sizeof(WCHAR)] = 0;
584
            info.handle       = wine_server_ptr_handle( reply->handle );
585 586 587
            info.id           = reply->id;
            info.pid          = reply->pid;
            info.tid          = reply->tid;
588
            info.proc         = wine_server_get_ptr( reply->proc );
589 590 591 592 593 594 595 596 597
            info.next_unicode = reply->unicode;
        }
    }
    SERVER_END_REQ;

    info.prev_unicode = TRUE;  /* assume Unicode for this function */
    return call_hook( &info, code, wparam, lparam );
}

598 599 600 601 602 603 604 605 606 607 608 609 610 611 612 613 614 615 616 617 618 619 620 621 622 623
/***********************************************************************
 *		CallMsgFilterA (USER32.@)
 */
BOOL WINAPI CallMsgFilterA( LPMSG msg, INT code )
{
    if (HOOK_CallHooks( WH_SYSMSGFILTER, code, 0, (LPARAM)msg, FALSE )) return TRUE;
    return HOOK_CallHooks( WH_MSGFILTER, code, 0, (LPARAM)msg, FALSE );
}


/***********************************************************************
 *		CallMsgFilterW (USER32.@)
 */
BOOL WINAPI CallMsgFilterW( LPMSG msg, INT code )
{
    if (HOOK_CallHooks( WH_SYSMSGFILTER, code, 0, (LPARAM)msg, TRUE )) return TRUE;
    return HOOK_CallHooks( WH_MSGFILTER, code, 0, (LPARAM)msg, TRUE );
}


/***********************************************************************
 *           SetWinEventHook                            [USER32.@]
 *
 * Set up an event hook for a set of events.
 *
 * PARAMS
624 625 626 627 628 629 630
 *  event_min [I] Lowest event handled by pfnProc
 *  event_max [I] Highest event handled by pfnProc
 *  inst      [I] DLL containing pfnProc
 *  proc      [I] Callback event hook function
 *  pid       [I] Process to get events from, or 0 for all processes
 *  tid       [I] Thread to get events from, or 0 for all threads
 *  flags     [I] Flags indicating the status of pfnProc
631 632 633 634 635
 *
 * RETURNS
 *  Success: A handle representing the hook.
 *  Failure: A NULL handle.
 */
636 637 638
HWINEVENTHOOK WINAPI SetWinEventHook(DWORD event_min, DWORD event_max,
                                     HMODULE inst, WINEVENTPROC proc,
                                     DWORD pid, DWORD tid, DWORD flags)
639
{
640 641 642 643
    HWINEVENTHOOK handle = 0;
    WCHAR module[MAX_PATH];
    DWORD len;

644
    TRACE("%d,%d,%p,%p,%08x,%04x,%08x\n", event_min, event_max, inst,
645 646 647 648 649 650 651 652 653 654 655 656 657 658 659 660 661 662 663 664 665 666 667 668 669 670 671 672 673 674 675 676 677 678 679
          proc, pid, tid, flags);

    if (inst)
    {
        if (!(len = GetModuleFileNameW(inst, module, MAX_PATH)) || len >= MAX_PATH)
            inst = 0;
    }

    if ((flags & WINEVENT_INCONTEXT) && !inst)
    {
        SetLastError(ERROR_HOOK_NEEDS_HMOD);
        return 0;
    }

    if (event_min > event_max)
    {
        SetLastError(ERROR_INVALID_HOOK_FILTER);
        return 0;
    }

    /* FIXME: what if the tid or pid belongs to another process? */
    if (tid)  /* thread-local hook */
        inst = 0;

    SERVER_START_REQ( set_hook )
    {
        req->id        = WH_WINEVENT;
        req->pid       = pid;
        req->tid       = tid;
        req->event_min = event_min;
        req->event_max = event_max;
        req->flags     = flags;
        req->unicode   = 1;
        if (inst) /* make proc relative to the module base */
        {
680
            req->proc = wine_server_client_ptr( (void *)((char *)proc - (char *)inst) );
681 682
            wine_server_add_data( req, module, strlenW(module) * sizeof(WCHAR) );
        }
683
        else req->proc = wine_server_client_ptr( proc );
684

685 686
        if (!wine_server_call_err( req ))
        {
687
            handle = wine_server_ptr_handle( reply->handle );
688 689
            get_user_thread_info()->active_hooks = reply->active_hooks;
        }
690 691 692 693 694
    }
    SERVER_END_REQ;

    TRACE("-> %p\n", handle);
    return handle;
695 696 697 698 699 700 701 702 703 704 705 706 707 708 709 710 711
}


/***********************************************************************
 *           UnhookWinEvent                             [USER32.@]
 *
 * Remove an event hook for a set of events.
 *
 * PARAMS
 *  hEventHook [I] Event hook to remove
 *
 * RETURNS
 *  Success: TRUE. The event hook has been removed.
 *  Failure: FALSE, if hEventHook is invalid.
 */
BOOL WINAPI UnhookWinEvent(HWINEVENTHOOK hEventHook)
{
712 713 714 715
    BOOL ret;

    SERVER_START_REQ( remove_hook )
    {
716
        req->handle = wine_server_user_handle( hEventHook );
717 718
        req->id     = WH_WINEVENT;
        ret = !wine_server_call_err( req );
719
        if (ret) get_user_thread_info()->active_hooks = reply->active_hooks;
720 721 722
    }
    SERVER_END_REQ;
    return ret;
723 724
}

725
static inline BOOL find_first_hook(DWORD id, DWORD event, HWND hwnd, LONG object_id,
726 727
                                   LONG child_id, struct hook_info *info)
{
728
    struct user_thread_info *thread_info = get_user_thread_info();
729 730
    BOOL ret;

731 732 733 734 735 736
    if (!HOOK_IsHooked( id ))
    {
        TRACE( "skipping hook %s mask %x\n", hook_names[id-WH_MINHOOK], thread_info->active_hooks );
        return FALSE;
    }

737 738 739 740
    SERVER_START_REQ( start_hook_chain )
    {
        req->id = id;
        req->event = event;
741
        req->window = wine_server_user_handle( hwnd );
742 743 744 745 746 747 748
        req->object_id = object_id;
        req->child_id = child_id;
        wine_server_set_reply( req, info->module, sizeof(info->module)-sizeof(WCHAR) );
        ret = !wine_server_call( req );
        if (ret)
        {
            info->module[wine_server_reply_size(req) / sizeof(WCHAR)] = 0;
749
            info->handle    = wine_server_ptr_handle( reply->handle );
750
            info->proc      = wine_server_get_ptr( reply->proc );
751
            info->tid       = reply->tid;
752
            thread_info->active_hooks = reply->active_hooks;
753 754 755 756 757 758
        }
    }
    SERVER_END_REQ;
    return ret && (info->tid || info->proc);
}

759
static inline BOOL find_next_hook(DWORD event, HWND hwnd, LONG object_id,
760 761 762 763
                                  LONG child_id, struct hook_info *info)
{
    BOOL ret;

764
    SERVER_START_REQ( get_hook_info )
765
    {
766
        req->handle = wine_server_user_handle( info->handle );
767
        req->get_next = 1;
768
        req->event = event;
769
        req->window = wine_server_user_handle( hwnd );
770 771 772 773 774 775 776
        req->object_id = object_id;
        req->child_id = child_id;
        wine_server_set_reply( req, info->module, sizeof(info->module)-sizeof(WCHAR) );
        ret = !wine_server_call( req );
        if (ret)
        {
            info->module[wine_server_reply_size(req) / sizeof(WCHAR)] = 0;
777
            info->handle    = wine_server_ptr_handle( reply->handle );
778
            info->proc      = wine_server_get_ptr( reply->proc );
779 780 781 782 783 784 785
            info->tid       = reply->tid;
        }
    }
    SERVER_END_REQ;
    return ret;
}

786
static inline void find_hook_close(DWORD id)
787 788 789 790 791 792 793 794
{
    SERVER_START_REQ( finish_hook_chain )
    {
        req->id = id;
        wine_server_call( req );
    }
    SERVER_END_REQ;
}
795 796 797 798 799 800 801

/***********************************************************************
 *           NotifyWinEvent                             [USER32.@]
 *
 * Inform the OS that an event has occurred.
 *
 * PARAMS
802 803 804 805
 *  event     [I] Id of the event
 *  hwnd      [I] Window holding the object that created the event
 *  object_id [I] Type of object that created the event
 *  child_id  [I] Child object of nId, or CHILDID_SELF.
806 807 808 809
 *
 * RETURNS
 *  Nothing.
 */
810
void WINAPI NotifyWinEvent(DWORD event, HWND hwnd, LONG object_id, LONG child_id)
811
{
812 813
    struct hook_info info;

814
    TRACE("%04x,%p,%d,%d\n", event, hwnd, object_id, child_id);
815 816 817 818 819 820 821

    if (!hwnd)
    {
        SetLastError(ERROR_INVALID_WINDOW_HANDLE);
        return;
    }

822 823
    USER_CheckNotLock();

824 825 826 827 828 829 830 831 832 833 834 835 836 837 838 839 840 841 842 843 844 845 846 847
#if 0
    if (event & 0x80000000)
    {
        /* FIXME: on 64-bit platforms we need to invent some other way for
         * passing parameters, nId and nChildId can't hold full [W|L]PARAM.
         * struct call_hook *hook = (LRESULT *)hWnd;
         * wparam = hook->wparam;
         * lparam = hook->lparam;
         */
        LRESULT *ret = (LRESULT *)hwnd;
        INT id, code, unicode;

        id = (dwEvent & 0x7fff0000) >> 16;
        code = event & 0x7fff;
        unicode = event & 0x8000;
        *ret = HOOK_CallHooks(id, code, object_id, child_id, unicode);
        return;
    }
#endif

    if (!find_first_hook(WH_WINEVENT, event, hwnd, object_id, child_id, &info)) return;

    do
    {
848 849
        WINEVENTPROC proc = info.proc;
        if (proc)
850
        {
851
            TRACE( "calling WH_WINEVENT hook %p event %x hwnd %p %x %x module %s\n",
852
                   proc, event, hwnd, object_id, child_id, debugstr_w(info.module) );
853

854
            if (!info.module[0] || (proc = get_hook_proc( proc, info.module )) != NULL)
855 856
            {
                if (TRACE_ON(relay))
857
                    DPRINTF( "%04x:Call winevent hook proc %p (hhook=%p,event=%x,hwnd=%p,object_id=%x,child_id=%x,tid=%04x,time=%x)\n",
858
                             GetCurrentThreadId(), proc, info.handle, event, hwnd, object_id,
859 860
                             child_id, GetCurrentThreadId(), GetCurrentTime());

861 862
                proc( info.handle, event, hwnd, object_id, child_id,
                      GetCurrentThreadId(), GetCurrentTime());
863 864

                if (TRACE_ON(relay))
865
                    DPRINTF( "%04x:Ret  winevent hook proc %p (hhook=%p,event=%x,hwnd=%p,object_id=%x,child_id=%x,tid=%04x,time=%x)\n",
866
                             GetCurrentThreadId(), proc, info.handle, event, hwnd, object_id,
867 868 869 870 871 872 873 874 875
                             child_id, GetCurrentThreadId(), GetCurrentTime());
            }
        }
        else
            break;
    }
    while (find_next_hook(event, hwnd, object_id, child_id, &info));

    find_hook_close(WH_WINEVENT);
876 877 878 879 880 881 882 883 884 885 886 887 888 889 890 891 892 893 894 895
}


/***********************************************************************
 *           IsWinEventHookInstalled                       [USER32.@]
 *
 * Determine if an event hook is installed for an event.
 *
 * PARAMS
 *  dwEvent  [I] Id of the event
 *
 * RETURNS
 *  TRUE,  If there are any hooks installed for the event.
 *  FALSE, Otherwise.
 *
 * BUGS
 *  Not implemented.
 */
BOOL WINAPI IsWinEventHookInstalled(DWORD dwEvent)
{
896 897
    /* FIXME: Needed by Office 2007 installer */
    WARN("(%d)-stub!\n", dwEvent);
898 899
    return TRUE;
}