333 lines
9.7 KiB
C
333 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 );
|
|
|
|
user_check_not_lock();
|
|
|
|
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;
|
|
}
|