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 89
    FARPROC proc;
    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 225

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

        if (HIWORD(cbtcwA->lpcs->lpszName))
226 227
        {
            RtlCreateUnicodeStringFromAsciiz(&usBuffer,cbtcwA->lpcs->lpszName);
228
            csW.lpszName = nameW = usBuffer.Buffer;
229
        }
230
        if (HIWORD(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 266
        if (HIWORD(cbtcwW->lpcs->lpszName)) {
            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 273
        }

        if (HIWORD(cbtcwW->lpcs->lpszClass)) {
            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 325 326
{
    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;
    }
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 391 392 393 394 395 396 397 398 399 400 401 402 403 404 405 406 407 408 409 410
    }
    return ret;
}

/***********************************************************************
 *		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;
411

412 413 414 415 416 417 418 419
    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;
420
            info.handle       = wine_server_ptr_handle( reply->handle );
421 422
            info.pid          = reply->pid;
            info.tid          = reply->tid;
423
            info.proc         = wine_server_get_ptr( reply->proc );
424 425 426
            info.next_unicode = reply->unicode;
            thread_info->active_hooks = reply->active_hooks;
        }
427
    }
428 429 430 431
    SERVER_END_REQ;

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

433 434 435 436 437 438
    SERVER_START_REQ( finish_hook_chain )
    {
        req->id = id;
        wine_server_call( req );
    }
    SERVER_END_REQ;
439 440 441 442 443 444 445 446 447
    return ret;
}


/***********************************************************************
 *           HOOK_IsHooked
 */
BOOL HOOK_IsHooked( INT id )
{
448 449 450 451
    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;
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
}


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



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

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


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

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

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

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


568 569 570 571 572 573 574 575
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 )
    {
576
        req->handle = wine_server_user_handle( hhook );
577 578 579 580 581 582
        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;
583
            info.handle       = wine_server_ptr_handle( reply->handle );
584 585 586
            info.id           = reply->id;
            info.pid          = reply->pid;
            info.tid          = reply->tid;
587
            info.proc         = wine_server_get_ptr( reply->proc );
588 589 590 591 592 593 594 595 596
            info.next_unicode = reply->unicode;
        }
    }
    SERVER_END_REQ;

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

597 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
/***********************************************************************
 *		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
623 624 625 626 627 628 629
 *  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
630 631 632 633 634
 *
 * RETURNS
 *  Success: A handle representing the hook.
 *  Failure: A NULL handle.
 */
635 636 637
HWINEVENTHOOK WINAPI SetWinEventHook(DWORD event_min, DWORD event_max,
                                     HMODULE inst, WINEVENTPROC proc,
                                     DWORD pid, DWORD tid, DWORD flags)
638
{
639 640 641 642
    HWINEVENTHOOK handle = 0;
    WCHAR module[MAX_PATH];
    DWORD len;

643
    TRACE("%d,%d,%p,%p,%08x,%04x,%08x\n", event_min, event_max, inst,
644 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
          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 */
        {
679
            req->proc = wine_server_client_ptr( (void *)((char *)proc - (char *)inst) );
680 681
            wine_server_add_data( req, module, strlenW(module) * sizeof(WCHAR) );
        }
682
        else req->proc = wine_server_client_ptr( proc );
683

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

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


/***********************************************************************
 *           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)
{
711 712 713 714
    BOOL ret;

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

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

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

736 737 738 739
    SERVER_START_REQ( start_hook_chain )
    {
        req->id = id;
        req->event = event;
740
        req->window = wine_server_user_handle( hwnd );
741 742 743 744 745 746 747
        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;
748
            info->handle    = wine_server_ptr_handle( reply->handle );
749
            info->proc      = wine_server_get_ptr( reply->proc );
750
            info->tid       = reply->tid;
751
            thread_info->active_hooks = reply->active_hooks;
752 753 754 755 756 757
        }
    }
    SERVER_END_REQ;
    return ret && (info->tid || info->proc);
}

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

763
    SERVER_START_REQ( get_hook_info )
764
    {
765
        req->handle = wine_server_user_handle( info->handle );
766
        req->get_next = 1;
767
        req->event = event;
768
        req->window = wine_server_user_handle( hwnd );
769 770 771 772 773 774 775
        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;
776
            info->handle    = wine_server_ptr_handle( reply->handle );
777
            info->proc      = wine_server_get_ptr( reply->proc );
778 779 780 781 782 783 784
            info->tid       = reply->tid;
        }
    }
    SERVER_END_REQ;
    return ret;
}

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

/***********************************************************************
 *           NotifyWinEvent                             [USER32.@]
 *
 * Inform the OS that an event has occurred.
 *
 * PARAMS
801 802 803 804
 *  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.
805 806 807 808
 *
 * RETURNS
 *  Nothing.
 */
809
void WINAPI NotifyWinEvent(DWORD event, HWND hwnd, LONG object_id, LONG child_id)
810
{
811 812
    struct hook_info info;

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

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

821 822
    USER_CheckNotLock();

823 824 825 826 827 828 829 830 831 832 833 834 835 836 837 838 839 840 841 842 843 844 845 846 847 848
#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
    {
        if (info.proc)
        {
849
            TRACE( "calling WH_WINEVENT hook %p event %x hwnd %p %x %x module %s\n",
850 851 852 853 854
                   info.proc, event, hwnd, object_id, child_id, debugstr_w(info.module) );

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

                info.proc(info.handle, event, hwnd, object_id, child_id,
                          GetCurrentThreadId(), GetCurrentTime());

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

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


/***********************************************************************
 *           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)
{
894 895
    /* FIXME: Needed by Office 2007 installer */
    WARN("(%d)-stub!\n", dwEvent);
896 897
    return TRUE;
}