Sweden-Number/dlls/win32u/hook.c

331 lines
9.7 KiB
C

/*
* Windows hook functions
*
* Copyright 2002 Alexandre Julliard
* Copyright 2005 Dmitry Timoshkov
*
* 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
* Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301, USA
*/
#if 0
#pragma makedep unix
#endif
#include "win32u_private.h"
#include "ntuser_private.h"
#include "wine/server.h"
#include "wine/debug.h"
WINE_DEFAULT_DEBUG_CHANNEL(hook);
#define WH_WINEVENT (WH_MAXHOOK+1)
static const char * const hook_names[WH_WINEVENT - WH_MINHOOK + 1] =
{
"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",
"WH_MOUSE_LL",
"WH_WINEVENT"
};
static const char *debugstr_hook_id( unsigned int id )
{
if (id - WH_MINHOOK >= ARRAYSIZE(hook_names)) return wine_dbg_sprintf( "%u", id );
return hook_names[id - WH_MINHOOK];
}
static BOOL is_hooked( 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;
}
/***********************************************************************
* NtUserSetWindowsHookEx (win32u.@)
*/
HHOOK WINAPI NtUserSetWindowsHookEx( HINSTANCE inst, UNICODE_STRING *module, DWORD tid, INT id,
HOOKPROC proc, BOOL ansi )
{
HHOOK handle = 0;
if (!proc)
{
SetLastError( ERROR_INVALID_FILTER_PROC );
return 0;
}
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 */
{
if (id == WH_KEYBOARD_LL || id == WH_MOUSE_LL) inst = 0;
else if (!inst)
{
SetLastError( ERROR_HOOK_NEEDS_HMOD );
return 0;
}
}
SERVER_START_REQ( set_hook )
{
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 = !ansi;
if (inst) /* make proc relative to the module base */
{
req->proc = wine_server_client_ptr( (void *)((char *)proc - (char *)inst) );
wine_server_add_data( req, module->Buffer, module->Length );
}
else req->proc = wine_server_client_ptr( proc );
if (!wine_server_call_err( req ))
{
handle = wine_server_ptr_handle( reply->handle );
get_user_thread_info()->active_hooks = reply->active_hooks;
}
}
SERVER_END_REQ;
TRACE( "%s %p %x -> %p\n", debugstr_hook_id(id), proc, tid, handle );
return handle;
}
/***********************************************************************
* NtUserUnhookWindowsHookEx (win32u.@)
*/
BOOL WINAPI NtUserUnhookWindowsHookEx( HHOOK handle )
{
NTSTATUS status;
SERVER_START_REQ( remove_hook )
{
req->handle = wine_server_user_handle( handle );
req->id = 0;
status = wine_server_call_err( req );
if (!status) get_user_thread_info()->active_hooks = reply->active_hooks;
}
SERVER_END_REQ;
if (status == STATUS_INVALID_HANDLE) SetLastError( ERROR_INVALID_HOOK_HANDLE );
return !status;
}
/* see UnhookWindowsHook */
BOOL unhook_windows_hook( INT id, HOOKPROC proc )
{
NTSTATUS status;
TRACE( "%s %p\n", debugstr_hook_id(id), proc );
SERVER_START_REQ( remove_hook )
{
req->handle = 0;
req->id = id;
req->proc = wine_server_client_ptr( proc );
status = wine_server_call_err( req );
if (!status) get_user_thread_info()->active_hooks = reply->active_hooks;
}
SERVER_END_REQ;
if (status == STATUS_INVALID_HANDLE) SetLastError( ERROR_INVALID_HOOK_HANDLE );
return !status;
}
/***********************************************************************
* NtUserSetWinEventHook (win32u.@)
*/
HWINEVENTHOOK WINAPI NtUserSetWinEventHook( DWORD event_min, DWORD event_max, HMODULE inst,
UNICODE_STRING *module, WINEVENTPROC proc,
DWORD pid, DWORD tid, DWORD flags )
{
HWINEVENTHOOK handle = 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) inst = 0; /* thread-local hook */
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 */
{
req->proc = wine_server_client_ptr( (void *)((char *)proc - (char *)inst) );
wine_server_add_data( req, module->Buffer, module->Length );
}
else req->proc = wine_server_client_ptr( proc );
if (!wine_server_call_err( req ))
{
handle = wine_server_ptr_handle( reply->handle );
get_user_thread_info()->active_hooks = reply->active_hooks;
}
}
SERVER_END_REQ;
TRACE("-> %p\n", handle);
return handle;
}
/***********************************************************************
* NtUserUnhookWinEvent (win32u.@)
*/
BOOL WINAPI NtUserUnhookWinEvent( HWINEVENTHOOK handle )
{
BOOL ret;
SERVER_START_REQ( remove_hook )
{
req->handle = wine_server_user_handle( handle );
req->id = WH_WINEVENT;
ret = !wine_server_call_err( req );
if (ret) get_user_thread_info()->active_hooks = reply->active_hooks;
}
SERVER_END_REQ;
return ret;
}
/***********************************************************************
* NtUserNotifyWinEvent (win32u.@)
*/
void WINAPI NtUserNotifyWinEvent( DWORD event, HWND hwnd, LONG object_id, LONG child_id )
{
struct user_thread_info *thread_info = get_user_thread_info();
struct win_hook_proc_params info;
void *ret_ptr;
ULONG ret_len;
BOOL ret;
TRACE( "%04x, %p, %d, %d\n", event, hwnd, object_id, child_id );
if (!hwnd)
{
SetLastError( ERROR_INVALID_WINDOW_HANDLE );
return;
}
if (!is_hooked( WH_WINEVENT ))
{
TRACE( "skipping hook mask %x\n", thread_info->active_hooks );
return;
}
info.event = event;
info.hwnd = hwnd;
info.object_id = object_id;
info.child_id = child_id;
SERVER_START_REQ( start_hook_chain )
{
req->id = WH_WINEVENT;
req->event = event;
req->window = wine_server_user_handle( hwnd );
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 ) && reply->proc;
if (ret)
{
info.module[wine_server_reply_size(req) / sizeof(WCHAR)] = 0;
info.handle = wine_server_ptr_handle( reply->handle );
info.proc = wine_server_get_ptr( reply->proc );
thread_info->active_hooks = reply->active_hooks;
}
}
SERVER_END_REQ;
if (!ret) return;
do
{
TRACE( "calling WH_WINEVENT hook %p event %x hwnd %p %x %x module %s\n",
info.proc, event, hwnd, object_id, child_id, debugstr_w(info.module) );
KeUserModeCallback( NtUserCallWinEventHook, &info,
FIELD_OFFSET( struct win_hook_proc_params, module[lstrlenW(info.module) + 1] ),
&ret_ptr, &ret_len );
SERVER_START_REQ( get_hook_info )
{
req->handle = wine_server_user_handle( info.handle );
req->get_next = 1;
req->event = event;
req->window = wine_server_user_handle( hwnd );
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 ) && reply->proc;
if (ret)
{
info.module[wine_server_reply_size(req) / sizeof(WCHAR)] = 0;
info.handle = wine_server_ptr_handle( reply->handle );
info.proc = wine_server_get_ptr( reply->proc );
}
}
SERVER_END_REQ;
}
while (ret);
SERVER_START_REQ( finish_hook_chain )
{
req->id = WH_WINEVENT;
wine_server_call( req );
}
SERVER_END_REQ;
}