Sync to trunk head (r42241)
[reactos.git] / reactos / subsystems / win32 / win32k / ntuser / hook.c
index c07f074..0c3f15d 100644 (file)
@@ -1,27 +1,8 @@
 /*
- *  ReactOS W32 Subsystem
- *  Copyright (C) 1998, 1999, 2000, 2001, 2002, 2003 ReactOS Team
- *
- *  This program is free software; you can redistribute it and/or modify
- *  it under the terms of the GNU General Public License as published by
- *  the Free Software Foundation; either version 2 of the License, or
- *  (at your option) any later version.
- *
- *  This program 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 General Public License for more details.
- *
- *  You should have received a copy of the GNU General Public License
- *  along with this program; if not, write to the Free Software
- *  Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
- */
-/* $Id$
- *
  * COPYRIGHT:        See COPYING in the top level directory
  * PROJECT:          ReactOS kernel
  * PURPOSE:          Window hooks
- * FILE:             subsys/win32k/ntuser/hook.c
+ * FILE:             subsystem/win32/win32k/ntuser/hook.c
  * PROGRAMER:        Casper S. Hornstrup (chorns@users.sourceforge.net)
  * REVISION HISTORY:
  *       06-06-2001  CSH  Created
 #define NDEBUG
 #include <debug.h>
 
-#define HOOKID_TO_INDEX(HookId) (HookId - WH_MINHOOK)
-
 static PHOOKTABLE GlobalHooks;
 
+
+/* PRIVATE FUNCTIONS *********************************************************/
+
+
 /* create a new hook table */
-static PHOOKTABLE
+static
+PHOOKTABLE
 IntAllocHookTable(void)
 {
-   PHOOKTABLE Table;
-   UINT i;
-
-   Table = ExAllocatePoolWithTag(PagedPool, sizeof(HOOKTABLE), TAG_HOOK);
-   if (NULL != Table)
-   {
-      for (i = 0; i < NB_HOOKS; i++)
-      {
-         InitializeListHead(&Table->Hooks[i]);
-         Table->Counts[i] = 0;
-      }
-   }
-
-   return Table;
+    PHOOKTABLE Table;
+    UINT i;
+
+    Table = ExAllocatePoolWithTag(PagedPool, sizeof(HOOKTABLE), TAG_HOOK);
+    if (NULL != Table)
+    {
+        for (i = 0; i < NB_HOOKS; i++)
+        {
+            InitializeListHead(&Table->Hooks[i]);
+            Table->Counts[i] = 0;
+        }
+    }
+
+    return Table;
 }
 
 
-PHOOK FASTCALL IntGetHookObject(HHOOK hHook)
+PHOOK
+FASTCALL
+IntGetHookObject(HHOOK hHook)
 {
-   PHOOK Hook;
-   
-   if (!hHook)
-   {
-      SetLastWin32Error(ERROR_INVALID_HOOK_HANDLE);
-      return NULL;
-   }
-   
-   Hook = (PHOOK)UserGetObject(&gHandleTable, hHook, otHook);
-   if (!Hook)
-   {
-      SetLastWin32Error(ERROR_INVALID_HOOK_HANDLE);
-      return NULL;
-   }
+    PHOOK Hook;
+
+    if (!hHook)
+    {
+        SetLastWin32Error(ERROR_INVALID_HOOK_HANDLE);
+        return NULL;
+    }
+
+    Hook = (PHOOK)UserGetObject(gHandleTable, hHook, otHook);
+    if (!Hook)
+    {
+        SetLastWin32Error(ERROR_INVALID_HOOK_HANDLE);
+        return NULL;
+    }
 
-   ASSERT(USER_BODY_TO_HEADER(Hook)->RefCount >= 0);
+    ASSERT(USER_BODY_TO_HEADER(Hook)->RefCount >= 0);
 
-   USER_BODY_TO_HEADER(Hook)->RefCount++;
+    USER_BODY_TO_HEADER(Hook)->RefCount++;
 
-   return Hook;
+    return Hook;
 }
 
 
 
 /* create a new hook and add it to the specified table */
-static PHOOK
+static
+PHOOK
 IntAddHook(PETHREAD Thread, int HookId, BOOLEAN Global, PWINSTATION_OBJECT WinStaObj)
 {
-   PHOOK Hook;
-   PHOOKTABLE Table = Global ? GlobalHooks : MsqGetHooks(((PW32THREAD)Thread->Tcb.Win32Thread)->MessageQueue);
-   HANDLE Handle;
-
-   if (NULL == Table)
-   {
-      Table = IntAllocHookTable();
-      if (NULL == Table)
-      {
-         return NULL;
-      }
-      if (Global)
-      {
-         GlobalHooks = Table;
-      }
-      else
-      {
-         MsqSetHooks(((PW32THREAD)Thread->Tcb.Win32Thread)->MessageQueue, Table);
-      }
-   }
-
-   Hook = ObmCreateObject(&gHandleTable, &Handle, otHook, sizeof(HOOK));
-   if (NULL == Hook)
-   {
-      return NULL;
-   }
-
-   Hook->Self = Handle;
-   Hook->Thread = Thread;
-   Hook->HookId = HookId;
-   RtlInitUnicodeString(&Hook->ModuleName, NULL);
-
-   InsertHeadList(&Table->Hooks[HOOKID_TO_INDEX(HookId)], &Hook->Chain);
-
-   return Hook;
+    PTHREADINFO W32Thread;
+    PHOOK Hook;
+    PHOOKTABLE Table = Global ? GlobalHooks : MsqGetHooks(((PTHREADINFO)Thread->Tcb.Win32Thread)->MessageQueue);
+    HANDLE Handle;
+
+    if (NULL == Table)
+    {
+        Table = IntAllocHookTable();
+        if (NULL == Table)
+        {
+            return NULL;
+        }
+        if (Global)
+        {
+            GlobalHooks = Table;
+        }
+        else
+        {
+            MsqSetHooks(((PTHREADINFO)Thread->Tcb.Win32Thread)->MessageQueue, Table);
+        }
+    }
+
+    Hook = UserCreateObject(gHandleTable, &Handle, otHook, sizeof(HOOK));
+    if (NULL == Hook)
+    {
+        return NULL;
+    }
+
+//    Hook->head.pti =?
+//    Hook->head.rpdesk
+    Hook->head.h = Handle;
+    Hook->Thread = Thread;
+    Hook->HookId = HookId;
+
+    if (Thread)
+    {
+        W32Thread = ((PTHREADINFO)Thread->Tcb.Win32Thread);
+        ASSERT(W32Thread != NULL);
+        W32Thread->fsHooks |= HOOKID_TO_FLAG(HookId);
+
+        GetWin32ClientInfo()->fsHooks = W32Thread->fsHooks;
+
+        if (W32Thread->ThreadInfo != NULL)
+            W32Thread->ThreadInfo->fsHooks = W32Thread->fsHooks;
+    }
+
+    RtlInitUnicodeString(&Hook->ModuleName, NULL);
+
+    InsertHeadList(&Table->Hooks[HOOKID_TO_INDEX(HookId)], &Hook->Chain);
+
+    return Hook;
 }
 
 /* get the hook table that a given hook belongs to */
-static PHOOKTABLE FASTCALL
+static
+PHOOKTABLE
+FASTCALL
 IntGetTable(PHOOK Hook)
 {
-   if (NULL == Hook->Thread || WH_KEYBOARD_LL == Hook->HookId ||
-         WH_MOUSE_LL == Hook->HookId)
-   {
-      return GlobalHooks;
-   }
+    if (NULL == Hook->Thread || WH_KEYBOARD_LL == Hook->HookId ||
+        WH_MOUSE_LL == Hook->HookId)
+    {
+        return GlobalHooks;
+    }
 
-   return MsqGetHooks(((PW32THREAD)Hook->Thread->Tcb.Win32Thread)->MessageQueue);
+    return MsqGetHooks(((PTHREADINFO)Hook->Thread->Tcb.Win32Thread)->MessageQueue);
 }
 
 /* get the first hook in the chain */
-static PHOOK FASTCALL
+static
+PHOOK
+FASTCALL
 IntGetFirstHook(PHOOKTABLE Table, int HookId)
 {
-   PLIST_ENTRY Elem = Table->Hooks[HOOKID_TO_INDEX(HookId)].Flink;
-   return Elem == &Table->Hooks[HOOKID_TO_INDEX(HookId)]
-          ? NULL : CONTAINING_RECORD(Elem, HOOK, Chain);
+    PLIST_ENTRY Elem = Table->Hooks[HOOKID_TO_INDEX(HookId)].Flink;
+
+    return Elem == &Table->Hooks[HOOKID_TO_INDEX(HookId)]
+           ? NULL : CONTAINING_RECORD(Elem, HOOK, Chain);
 }
 
 /* find the first non-deleted hook in the chain */
-static PHOOK FASTCALL
+static
+PHOOK
+FASTCALL
 IntGetFirstValidHook(PHOOKTABLE Table, int HookId)
 {
-   PHOOK Hook;
-   PLIST_ENTRY Elem;
-
-   Hook = IntGetFirstHook(Table, HookId);
-   while (NULL != Hook && NULL == Hook->Proc)
-   {
-      Elem = Hook->Chain.Flink;
-      Hook = (Elem == &Table->Hooks[HOOKID_TO_INDEX(HookId)]
-              ? NULL : CONTAINING_RECORD(Elem, HOOK, Chain));
-   }
-
-   return Hook;
+    PHOOK Hook;
+    PLIST_ENTRY Elem;
+
+    Hook = IntGetFirstHook(Table, HookId);
+
+    while (NULL != Hook && NULL == Hook->Proc)
+    {
+        Elem = Hook->Chain.Flink;
+        Hook = (Elem == &Table->Hooks[HOOKID_TO_INDEX(HookId)]
+                ? NULL : CONTAINING_RECORD(Elem, HOOK, Chain));
+    }
+
+    return Hook;
 }
 
 /* find the next hook in the chain, skipping the deleted ones */
-static PHOOK FASTCALL
+PHOOK
+FASTCALL
 IntGetNextHook(PHOOK Hook)
 {
-   PHOOKTABLE Table = IntGetTable(Hook);
-   int HookId = Hook->HookId;
-   PLIST_ENTRY Elem;
-
-   Elem = Hook->Chain.Flink;
-   while (Elem != &Table->Hooks[HOOKID_TO_INDEX(HookId)])
-   {
-      Hook = CONTAINING_RECORD(Elem, HOOK, Chain);
-      if (NULL != Hook->Proc)
-      {
-         return Hook;
-      }
-   }
-
-   if (NULL != GlobalHooks && Table != GlobalHooks)  /* now search through the global table */
-   {
-      return IntGetFirstValidHook(GlobalHooks, HookId);
-   }
-
-   return NULL;
+    PHOOKTABLE Table = IntGetTable(Hook);
+    int HookId = Hook->HookId;
+    PLIST_ENTRY Elem;
+
+    Elem = Hook->Chain.Flink;
+    while (Elem != &Table->Hooks[HOOKID_TO_INDEX(HookId)])
+    {
+        Hook = CONTAINING_RECORD(Elem, HOOK, Chain);
+        if (NULL != Hook->Proc)
+        {
+            return Hook;
+        }
+    }
+
+    if (NULL != GlobalHooks && Table != GlobalHooks)  /* now search through the global table */
+    {
+        return IntGetFirstValidHook(GlobalHooks, HookId);
+    }
+
+    return NULL;
 }
 
 /* free a hook, removing it from its chain */
-static VOID FASTCALL
+static
+VOID
+FASTCALL
 IntFreeHook(PHOOKTABLE Table, PHOOK Hook, PWINSTATION_OBJECT WinStaObj)
 {
-   RemoveEntryList(&Hook->Chain);
-   RtlFreeUnicodeString(&Hook->ModuleName);
+    RemoveEntryList(&Hook->Chain);
+    RtlFreeUnicodeString(&Hook->ModuleName);
 
-   /* Dereference thread if required */
-   if (Hook->Flags & HOOK_THREAD_REFERENCED)
-   {
-      ObDereferenceObject(Hook->Thread);
-   }
+    /* Dereference thread if required */
+    if (Hook->Flags & HOOK_THREAD_REFERENCED)
+    {
+        ObDereferenceObject(Hook->Thread);
+    }
 
-   /* Close handle */
-   ObmDeleteObject(Hook->Self, otHook);
+    /* Close handle */
+    UserDeleteObject(Hook->head.h, otHook);
 }
 
 /* remove a hook, freeing it if the chain is not in use */
-static VOID
+static
+VOID
 IntRemoveHook(PHOOK Hook, PWINSTATION_OBJECT WinStaObj, BOOL TableAlreadyLocked)
 {
-   PHOOKTABLE Table = IntGetTable(Hook);
-
-   ASSERT(NULL != Table);
-   if (NULL == Table)
-   {
-      return;
-   }
-
-   if (0 != Table->Counts[HOOKID_TO_INDEX(Hook->HookId)])
-   {
-      Hook->Proc = NULL; /* chain is in use, just mark it and return */
-   }
-   else
-   {
-      IntFreeHook(Table, Hook, WinStaObj);
-   }
+    PTHREADINFO W32Thread;
+    PHOOKTABLE Table = IntGetTable(Hook);
+
+    ASSERT(NULL != Table);
+    if (NULL == Table)
+    {
+        return;
+    }
+
+    W32Thread = ((PTHREADINFO)Hook->Thread->Tcb.Win32Thread);
+    ASSERT(W32Thread != NULL);
+    W32Thread->fsHooks &= ~HOOKID_TO_FLAG(Hook->HookId);
+
+    GetWin32ClientInfo()->fsHooks = W32Thread->fsHooks;
+
+    if (W32Thread->ThreadInfo != NULL)
+        W32Thread->ThreadInfo->fsHooks = W32Thread->fsHooks;
+
+    if (0 != Table->Counts[HOOKID_TO_INDEX(Hook->HookId)])
+    {
+        Hook->Proc = NULL; /* chain is in use, just mark it and return */
+    }
+    else
+    {
+        IntFreeHook(Table, Hook, WinStaObj);
+    }
 }
 
 /* release a hook chain, removing deleted hooks if the use count drops to 0 */
-static VOID FASTCALL
+static
+VOID
+FASTCALL
 IntReleaseHookChain(PHOOKTABLE Table, int HookId, PWINSTATION_OBJECT WinStaObj)
 {
-   PLIST_ENTRY Elem;
-   PHOOK HookObj;
-
-   if (NULL == Table)
-   {
-      return;
-   }
-
-   /* use count shouldn't already be 0 */
-   ASSERT(0 != Table->Counts[HOOKID_TO_INDEX(HookId)]);
-   if (0 == Table->Counts[HOOKID_TO_INDEX(HookId)])
-   {
-      return;
-   }
-   if (0 == --Table->Counts[HOOKID_TO_INDEX(HookId)])
-   {
-      Elem = Table->Hooks[HOOKID_TO_INDEX(HookId)].Flink;
-      while (Elem != &Table->Hooks[HOOKID_TO_INDEX(HookId)])
-      {
-         HookObj = CONTAINING_RECORD(Elem, HOOK, Chain);
-         Elem = Elem->Flink;
-         if (NULL == HookObj->Proc)
-         {
-            IntFreeHook(Table, HookObj, WinStaObj);
-         }
-      }
-   }
+    PLIST_ENTRY Elem;
+    PHOOK HookObj;
+
+    if (NULL == Table)
+    {
+        return;
+    }
+
+    /* use count shouldn't already be 0 */
+    ASSERT(0 != Table->Counts[HOOKID_TO_INDEX(HookId)]);
+
+    if (0 == Table->Counts[HOOKID_TO_INDEX(HookId)])
+    {
+        return;
+    }
+
+    if (0 == --Table->Counts[HOOKID_TO_INDEX(HookId)])
+    {
+        Elem = Table->Hooks[HOOKID_TO_INDEX(HookId)].Flink;
+
+        while (Elem != &Table->Hooks[HOOKID_TO_INDEX(HookId)])
+        {
+            HookObj = CONTAINING_RECORD(Elem, HOOK, Chain);
+            Elem = Elem->Flink;
+
+            if (NULL == HookObj->Proc)
+            {
+                IntFreeHook(Table, HookObj, WinStaObj);
+            }
+        }
+    }
 }
 
-static LRESULT FASTCALL
-IntCallLowLevelHook(INT HookId, INT Code, WPARAM wParam, LPARAM lParam, PHOOK Hook)
+static
+LRESULT
+FASTCALL
+IntCallLowLevelHook(PHOOK Hook, INT Code, WPARAM wParam, LPARAM lParam)
 {
-   NTSTATUS Status;
-   ULONG_PTR uResult;
-
-   /* FIXME should get timeout from
-    * HKEY_CURRENT_USER\Control Panel\Desktop\LowLevelHooksTimeout */
-   Status = co_MsqSendMessage(((PW32THREAD)Hook->Thread->Tcb.Win32Thread)->MessageQueue, (HWND) Code, HookId,
-                              wParam, lParam, 5000, TRUE, TRUE, &uResult);
-
-   return NT_SUCCESS(Status) ? uResult : 0;
+    NTSTATUS Status;
+    ULONG_PTR uResult;
+
+    /* FIXME should get timeout from
+     * HKEY_CURRENT_USER\Control Panel\Desktop\LowLevelHooksTimeout */
+    Status = co_MsqSendMessage(((PTHREADINFO)Hook->Thread->Tcb.Win32Thread)->MessageQueue,
+                                (HWND)(UINT_PTR)Code,
+                                Hook->HookId,
+                                wParam,
+                                lParam,
+                                5000,
+                                TRUE,
+                                MSQ_ISHOOK,
+                                &uResult);
+
+    return NT_SUCCESS(Status) ? uResult : 0;
 }
 
-LRESULT FASTCALL
+/*
+  Called from inside kernel space.
+ */
+LRESULT
+FASTCALL
 co_HOOK_CallHooks(INT HookId, INT Code, WPARAM wParam, LPARAM lParam)
 {
-   PHOOK Hook;
-   PW32THREAD Win32Thread;
-   PHOOKTABLE Table;
-   LRESULT Result;
-   PWINSTATION_OBJECT WinStaObj;
-   NTSTATUS Status;
-
-   ASSERT(WH_MINHOOK <= HookId && HookId <= WH_MAXHOOK);
-
-   Win32Thread = PsGetWin32Thread();
-   if (NULL == Win32Thread)
-   {
-      Table = NULL;
-   }
-   else
-   {
-      Table = MsqGetHooks(Win32Thread->MessageQueue);
-   }
-
-   if (NULL == Table || ! (Hook = IntGetFirstValidHook(Table, HookId)))
-   {
-      /* try global table */
-      Table = GlobalHooks;
-      if (NULL == Table || ! (Hook = IntGetFirstValidHook(Table, HookId)))
-      {
-         return 0;  /* no hook set */
-      }
-   }
-
-   if (Hook->Thread != PsGetCurrentThread()
-         && (WH_KEYBOARD_LL == HookId || WH_MOUSE_LL == HookId))
-   {
-      DPRINT("Calling hook in owning thread\n");
-      return IntCallLowLevelHook(HookId, Code, wParam, lParam, Hook);
-   }
-
-   if (Hook->Thread != PsGetCurrentThread())
-   {
-      DPRINT1("Calling hooks in other threads not implemented yet");
-      return 0;
-   }
-
-   Table->Counts[HOOKID_TO_INDEX(HookId)]++;
-   if (Table != GlobalHooks && GlobalHooks != NULL)
-   {
-      GlobalHooks->Counts[HOOKID_TO_INDEX(HookId)]++;
-   }
-
-   Result = co_IntCallHookProc(HookId, Code, wParam, lParam, Hook->Proc,
-                               Hook->Ansi, &Hook->ModuleName);
-
-   Status = IntValidateWindowStationHandle(PsGetCurrentProcess()->Win32WindowStation,
-                                           KernelMode,
-                                           0,
-                                           &WinStaObj);
-
-   if (! NT_SUCCESS(Status))
-   {
-      DPRINT1("Invalid window station????\n");
-   }
-   else
-   {
-      IntReleaseHookChain(MsqGetHooks(PsGetWin32Thread()->MessageQueue), HookId, WinStaObj);
-      IntReleaseHookChain(GlobalHooks, HookId, WinStaObj);
-      ObDereferenceObject(WinStaObj);
-   }
-
-   return Result;
+    PHOOK Hook, SaveHook;
+    PTHREADINFO pti;
+    PCLIENTINFO ClientInfo;
+    PHOOKTABLE Table;
+    LRESULT Result;
+    PWINSTATION_OBJECT WinStaObj;
+    NTSTATUS Status;
+
+    ASSERT(WH_MINHOOK <= HookId && HookId <= WH_MAXHOOK);
+
+    pti = PsGetCurrentThreadWin32Thread();
+    if (!pti)
+    {
+        Table = NULL;
+    }
+    else
+    {
+        Table = MsqGetHooks(pti->MessageQueue);
+    }
+
+    if (NULL == Table || ! (Hook = IntGetFirstValidHook(Table, HookId)))
+    {
+        /* try global table */
+        Table = GlobalHooks;
+        if (NULL == Table || ! (Hook = IntGetFirstValidHook(Table, HookId)))
+        {
+            return 0;  /* no hook set */
+        }
+    }
+
+    if ((Hook->Thread != PsGetCurrentThread()) && (Hook->Thread != NULL))
+    {
+        /* Post it in message queue. */
+        return IntCallLowLevelHook(Hook, Code, wParam, lParam);
+    }
+
+    Table->Counts[HOOKID_TO_INDEX(HookId)]++;
+    if (Table != GlobalHooks && GlobalHooks != NULL)
+    {
+        GlobalHooks->Counts[HOOKID_TO_INDEX(HookId)]++;
+    }
+
+    ClientInfo = GetWin32ClientInfo();
+    SaveHook = ClientInfo->phkCurrent;
+    ClientInfo->phkCurrent = Hook;     /* Load the call. */
+
+    Result = co_IntCallHookProc(HookId,
+                                Code,
+                                wParam,
+                                lParam,
+                                Hook->Proc,
+                                Hook->Ansi,
+                                &Hook->ModuleName);
+
+    ClientInfo->phkCurrent = SaveHook;
+
+    Status = IntValidateWindowStationHandle(PsGetCurrentProcess()->Win32WindowStation,
+                                            KernelMode,
+                                            0,
+                                            &WinStaObj);
+
+    if (!NT_SUCCESS(Status))
+    {
+        DPRINT1("Invalid window station????\n");
+    }
+    else
+    {
+        IntReleaseHookChain(MsqGetHooks(pti->MessageQueue), HookId, WinStaObj);
+        IntReleaseHookChain(GlobalHooks, HookId, WinStaObj);
+        ObDereferenceObject(WinStaObj);
+    }
+
+    return Result;
 }
 
-VOID FASTCALL
+VOID
+FASTCALL
 HOOK_DestroyThreadHooks(PETHREAD Thread)
 {
-   int HookId;
-   PLIST_ENTRY Elem;
-   PHOOK HookObj;
-   PWINSTATION_OBJECT WinStaObj;
-   NTSTATUS Status;
-
-   if (NULL != GlobalHooks)
-   {
-      Status = IntValidateWindowStationHandle(PsGetCurrentProcess()->Win32WindowStation,
-                                              KernelMode,
-                                              0,
-                                              &WinStaObj);
-
-      if (! NT_SUCCESS(Status))
-      {
-         DPRINT1("Invalid window station????\n");
-         return;
-      }
-
-      for (HookId = WH_MINHOOK; HookId <= WH_MAXHOOK; HookId++)
-      {
-         /* only low-level keyboard/mouse global hooks can be owned by a thread */
-         switch(HookId)
-         {
-            case WH_KEYBOARD_LL:
-            case WH_MOUSE_LL:
-               Elem = GlobalHooks->Hooks[HOOKID_TO_INDEX(HookId)].Flink;
-               while (Elem != &GlobalHooks->Hooks[HOOKID_TO_INDEX(HookId)])
-               {
-                  HookObj = CONTAINING_RECORD(Elem, HOOK, Chain);
-                  Elem = Elem->Flink;
-                  if (HookObj->Thread == Thread)
-                  {
-                     IntRemoveHook(HookObj, WinStaObj, TRUE);
-                  }
-               }
-               break;
-         }
-      }
-
-      ObDereferenceObject(WinStaObj);
-   }
+    int HookId;
+    PLIST_ENTRY Elem;
+    PHOOK HookObj;
+    PWINSTATION_OBJECT WinStaObj;
+    NTSTATUS Status;
+
+    if (NULL != GlobalHooks)
+    {
+        Status = IntValidateWindowStationHandle(PsGetCurrentProcess()->Win32WindowStation,
+                                                KernelMode,
+                                                0,
+                                                &WinStaObj);
+
+        if (!NT_SUCCESS(Status))
+        {
+            DPRINT1("Invalid window station????\n");
+            return;
+        }
+
+        for (HookId = WH_MINHOOK; HookId <= WH_MAXHOOK; HookId++)
+        {
+            /* only low-level keyboard/mouse global hooks can be owned by a thread */
+            switch(HookId)
+            {
+                case WH_KEYBOARD_LL:
+                case WH_MOUSE_LL:
+                    Elem = GlobalHooks->Hooks[HOOKID_TO_INDEX(HookId)].Flink;
+
+                    while (Elem != &GlobalHooks->Hooks[HOOKID_TO_INDEX(HookId)])
+                    {
+                        HookObj = CONTAINING_RECORD(Elem, HOOK, Chain);
+                        Elem = Elem->Flink;
+
+                        if (HookObj->Thread == Thread)
+                        {
+                            IntRemoveHook(HookObj, WinStaObj, TRUE);
+                        }
+                    }
+                    break;
+            }
+        }
+    }
+}
+
+static
+LRESULT
+FASTCALL
+co_HOOK_CallHookNext(PHOOK Hook, INT Code, WPARAM wParam, LPARAM lParam)
+{
+    if ((Hook->Thread != PsGetCurrentThread()) && (Hook->Thread != NULL))
+    {
+        DPRINT1("CALLING HOOK from another Thread. %d\n", Hook->HookId);
+        return IntCallLowLevelHook(Hook, Code, wParam, lParam);
+    }
+
+    DPRINT("CALLING HOOK %d\n", Hook->HookId);
+
+    return co_IntCallHookProc(Hook->HookId,
+                              Code,
+                              wParam,
+                              lParam,
+                              Hook->Proc,
+                              Hook->Ansi,
+                              &Hook->ModuleName);
+}
+
+
+LRESULT
+FASTCALL
+IntCallDebugHook(PHOOK Hook,
+                 int Code,
+                 WPARAM wParam,
+                 LPARAM lParam)
+{
+    LRESULT lResult = 0;
+    ULONG Size;
+    DEBUGHOOKINFO Debug;
+    PVOID HooklParam = NULL;
+    BOOL BadChk = FALSE;
+
+    if (lParam)
+    {
+        _SEH2_TRY
+        {
+            ProbeForRead((PVOID)lParam,
+                         sizeof(DEBUGHOOKINFO),
+                         1);
+
+            RtlCopyMemory(&Debug,
+                          (PVOID)lParam,
+                          sizeof(DEBUGHOOKINFO));
+        }
+        _SEH2_EXCEPT(EXCEPTION_EXECUTE_HANDLER)
+        {
+            BadChk = TRUE;
+        }
+        _SEH2_END;
+
+        if (BadChk)
+        {
+            DPRINT1("HOOK WH_DEBUG read from lParam ERROR!\n");
+            return lResult;
+        }
+    }
+    else
+        return lResult; /* Need lParam! */
+
+    switch (wParam)
+    {
+        case WH_CBT:
+        {
+            switch (Debug.code)
+            {
+                case HCBT_CLICKSKIPPED:
+                    Size = sizeof(MOUSEHOOKSTRUCTEX);
+                    break;
+
+                case HCBT_MOVESIZE:
+                    Size = sizeof(RECT);
+                    break;
+
+                case HCBT_ACTIVATE:
+                    Size = sizeof(CBTACTIVATESTRUCT); 
+                    break;
+
+                case HCBT_CREATEWND: /* Handle Ansi? */
+                    Size = sizeof(CBT_CREATEWND);
+                    /* What shall we do? Size += sizeof(HOOKPROC_CBT_CREATEWND_EXTRA_ARGUMENTS); same as CREATESTRUCTEX */
+                    break;
+
+                default:
+                    Size = sizeof(LPARAM);
+            }
+        }
+        break;
+
+        case WH_MOUSE_LL:
+            Size = sizeof(MSLLHOOKSTRUCT);
+            break;
+
+        case WH_KEYBOARD_LL:
+            Size = sizeof(KBDLLHOOKSTRUCT);
+            break;
+
+        case WH_MSGFILTER:
+        case WH_SYSMSGFILTER:
+        case WH_GETMESSAGE:
+            Size = sizeof(MSG);
+            break;
+
+        case WH_JOURNALPLAYBACK:
+        case WH_JOURNALRECORD:
+            Size = sizeof(EVENTMSG);
+            break;
+
+        case WH_FOREGROUNDIDLE:
+        case WH_KEYBOARD:
+        case WH_SHELL:
+        default:
+            Size = sizeof(LPARAM);
+    }
+
+    if (Size > sizeof(LPARAM))
+        HooklParam = ExAllocatePoolWithTag(NonPagedPool, Size, TAG_HOOK);
+
+    if (HooklParam)
+    {
+        _SEH2_TRY
+        {
+            ProbeForRead((PVOID)Debug.lParam,
+                         Size,
+                         1);
+
+            RtlCopyMemory(HooklParam,
+                          (PVOID)Debug.lParam,
+                          Size);
+        }
+        _SEH2_EXCEPT(EXCEPTION_EXECUTE_HANDLER)
+        {
+            BadChk = TRUE;
+        }
+        _SEH2_END;
+
+        if (BadChk)
+        {
+            DPRINT1("HOOK WH_DEBUG read from Debug.lParam ERROR!\n");
+            ExFreePool(HooklParam);
+            return lResult;
+        }
+    }
+
+    if (HooklParam) Debug.lParam = (LPARAM)HooklParam;
+    lResult = co_HOOK_CallHookNext(Hook, Code, wParam, (LPARAM)&Debug);
+    if (HooklParam) ExFreePoolWithTag(HooklParam, TAG_HOOK);
+
+    return lResult;
 }
 
+/*
+   Called from user space via CallNextHook.
+ */
 LRESULT
-STDCALL
-NtUserCallNextHookEx(
-   HHOOK Hook,
-   int Code,
-   WPARAM wParam,
-   LPARAM lParam)
+FASTCALL
+UserCallNextHookEx(PHOOK Hook,
+                   int Code,
+                   WPARAM wParam,
+                   LPARAM lParam,
+                   BOOL Ansi)
 {
-   PHOOK HookObj, NextObj;
-   PWINSTATION_OBJECT WinStaObj;
-   NTSTATUS Status;
-   DECLARE_RETURN(LRESULT);
-
-   DPRINT("Enter NtUserCallNextHookEx\n");
-   UserEnterExclusive();
-
-   Status = IntValidateWindowStationHandle(PsGetCurrentProcess()->Win32WindowStation,
-                                           KernelMode,
-                                           0,
-                                           &WinStaObj);
-
-   if (! NT_SUCCESS(Status))
-   {
-      SetLastNtError(Status);
-      RETURN( FALSE);
-   }
-
-   //Status = ObmReferenceObjectByHandle(gHandleTable, Hook,
-   //                             otHookProc, (PVOID *) &HookObj);
-   ObDereferenceObject(WinStaObj);
-
-   //  if (! NT_SUCCESS(Status))
-   //    {
-   //      DPRINT1("Invalid handle passed to NtUserCallNextHookEx\n");
-   //      SetLastNtError(Status);
-   //      RETURN( 0);
-   //    }
-
-   if (!(HookObj = IntGetHookObject(Hook)))
-   {
-      RETURN(0);
-   }
-
-   ASSERT(Hook == HookObj->Self);
-
-   if (NULL != HookObj->Thread && (HookObj->Thread != PsGetCurrentThread()))
-   {
-      DPRINT1("Thread mismatch\n");
-      ObmDereferenceObject(HookObj);
-      SetLastWin32Error(ERROR_INVALID_HANDLE);
-      RETURN( 0);
-   }
-
-   NextObj = IntGetNextHook(HookObj);
-   ObmDereferenceObject(HookObj);
-   if (NULL != NextObj)
-   {
-      DPRINT1("Calling next hook not implemented\n");
-      UNIMPLEMENTED
-      SetLastWin32Error(ERROR_NOT_SUPPORTED);
-      RETURN( 0);
-   }
-
-   RETURN( 0);
+    LRESULT lResult = 0;
+    BOOL BadChk = FALSE;
+
+    /* Handle this one first. */
+    if ((Hook->HookId == WH_MOUSE) ||
+        (Hook->HookId == WH_CBT && Code == HCBT_CLICKSKIPPED))
+    {
+        MOUSEHOOKSTRUCTEX Mouse;
+        if (lParam)
+        {
+            _SEH2_TRY
+            {
+                ProbeForRead((PVOID)lParam,
+                             sizeof(MOUSEHOOKSTRUCTEX),
+                             1);
+
+                RtlCopyMemory(&Mouse,
+                              (PVOID)lParam,
+                              sizeof(MOUSEHOOKSTRUCTEX));
+            }
+            _SEH2_EXCEPT(EXCEPTION_EXECUTE_HANDLER)
+            {
+                BadChk = TRUE;
+            }
+            _SEH2_END;
+
+            if (BadChk)
+            {
+                DPRINT1("HOOK WH_MOUSE read from lParam ERROR!\n");
+            }
+        }
+
+        if (!BadChk)
+        {
+            lResult = co_HOOK_CallHookNext(Hook, Code, wParam, (LPARAM)&Mouse);
+        }
+
+        return lResult;
+    }
+
+    switch(Hook->HookId)
+    {
+        case WH_MOUSE_LL:
+        {
+            MSLLHOOKSTRUCT Mouse;
+
+            if (lParam)
+            {
+                _SEH2_TRY
+                {
+                    ProbeForRead((PVOID)lParam,
+                                 sizeof(MSLLHOOKSTRUCT),
+                                 1);
+
+                    RtlCopyMemory(&Mouse,
+                                  (PVOID)lParam,
+                                  sizeof(MSLLHOOKSTRUCT));
+                }
+                _SEH2_EXCEPT(EXCEPTION_EXECUTE_HANDLER)
+                {
+                    BadChk = TRUE;
+                }
+                _SEH2_END;
+
+                if (BadChk)
+                {
+                    DPRINT1("HOOK WH_MOUSE_LL read from lParam ERROR!\n");
+                }
+            }
+
+            if (!BadChk)
+            {
+                lResult = co_HOOK_CallHookNext(Hook, Code, wParam, (LPARAM)&Mouse);
+            }
+            break;
+        }
+
+        case WH_KEYBOARD_LL:
+        {
+            KBDLLHOOKSTRUCT Keyboard;
+
+            if (lParam)
+            {
+                _SEH2_TRY
+                {
+                    ProbeForRead((PVOID)lParam,
+                                 sizeof(KBDLLHOOKSTRUCT),
+                                 1);
+
+                    RtlCopyMemory(&Keyboard,
+                                  (PVOID)lParam,
+                                  sizeof(KBDLLHOOKSTRUCT));
+                }
+                _SEH2_EXCEPT(EXCEPTION_EXECUTE_HANDLER)
+                {
+                    BadChk = TRUE;
+                }
+                _SEH2_END;
+
+                if (BadChk)
+                {
+                    DPRINT1("HOOK WH_KEYBORD_LL read from lParam ERROR!\n");
+                }
+            }
+
+            if (!BadChk)
+            {
+                lResult = co_HOOK_CallHookNext(Hook, Code, wParam, (LPARAM)&Keyboard);
+            }
+            break;
+        }
+
+        case WH_MSGFILTER:
+        case WH_SYSMSGFILTER:
+        case WH_GETMESSAGE:
+        {
+            MSG Msg;
+
+            if (lParam)
+            {
+                _SEH2_TRY
+                {
+                    ProbeForRead((PVOID)lParam,
+                                 sizeof(MSG),
+                                 1);
+
+                    RtlCopyMemory(&Msg,
+                                  (PVOID)lParam,
+                                  sizeof(MSG));
+                }
+                _SEH2_EXCEPT(EXCEPTION_EXECUTE_HANDLER)
+                {
+                    BadChk = TRUE;
+                }
+                _SEH2_END;
+
+                if (BadChk)
+                {
+                    DPRINT1("HOOK WH_XMESSAGEX read from lParam ERROR!\n");
+                }
+            }
+
+            if (!BadChk)
+            {
+                lResult = co_HOOK_CallHookNext(Hook, Code, wParam, (LPARAM)&Msg);
+
+                if (lParam && (Hook->HookId == WH_GETMESSAGE))
+                {
+                    _SEH2_TRY
+                    {
+                        ProbeForWrite((PVOID)lParam,
+                                      sizeof(MSG),
+                                      1);
+
+                        RtlCopyMemory((PVOID)lParam,
+                                      &Msg,
+                                      sizeof(MSG));
+                    }
+                    _SEH2_EXCEPT(EXCEPTION_EXECUTE_HANDLER)
+                    {
+                        BadChk = TRUE;
+                    }
+                    _SEH2_END;
+
+                    if (BadChk)
+                    {
+                        DPRINT1("HOOK WH_GETMESSAGE write to lParam ERROR!\n");
+                    }
+                }
+            }
+            break;
+        }
+
+        case WH_CBT:
+            DPRINT("HOOK WH_CBT!\n");
+            switch (Code)
+            {
+                case HCBT_CREATEWND:
+                {
+                    LPCBT_CREATEWNDW pcbtcww = (LPCBT_CREATEWNDW)lParam;
+
+                    DPRINT("HOOK HCBT_CREATEWND\n");
+                    _SEH2_TRY
+                    {
+                        if (Ansi)
+                        {
+                            ProbeForRead( pcbtcww,
+                                          sizeof(CBT_CREATEWNDA),
+                                          1);
+                            ProbeForWrite(pcbtcww->lpcs,
+                                          sizeof(CREATESTRUCTA),
+                                          1);
+                            ProbeForRead( pcbtcww->lpcs->lpszName,
+                                          sizeof(CHAR),
+                                          1);
+
+                            if (!IS_ATOM(pcbtcww->lpcs->lpszClass))
+                            {
+                               ProbeForRead( pcbtcww->lpcs->lpszClass,
+                                             sizeof(CHAR),
+                                             1);
+                            }
+                        }
+                        else
+                        {
+                            ProbeForRead( pcbtcww,
+                                          sizeof(CBT_CREATEWNDW),
+                                          1);
+                            ProbeForWrite(pcbtcww->lpcs,
+                                          sizeof(CREATESTRUCTW),
+                                          1);
+                            ProbeForRead( pcbtcww->lpcs->lpszName,
+                                          sizeof(WCHAR),
+                                          1);
+
+                            if (!IS_ATOM(pcbtcww->lpcs->lpszClass))
+                            {
+                               ProbeForRead( pcbtcww->lpcs->lpszClass,
+                                             sizeof(WCHAR),
+                                             1);
+                            }
+                        }
+                    }
+                    _SEH2_EXCEPT(EXCEPTION_EXECUTE_HANDLER)
+                    {
+                        BadChk = TRUE;
+                    }
+                    _SEH2_END;
+
+                    if (BadChk)
+                    {
+                        DPRINT1("HOOK HCBT_CREATEWND write ERROR!\n");
+                    }
+                    /* The next call handles the structures. */
+                    if (!BadChk && Hook->Proc)
+                    {
+                       lResult = co_HOOK_CallHookNext(Hook, Code, wParam, lParam);
+                    }
+                    break;
+                }
+
+                case HCBT_MOVESIZE:
+                {
+                    RECTL rt;
+
+                    DPRINT("HOOK HCBT_MOVESIZE\n");
+
+                    if (lParam)
+                    {
+                        _SEH2_TRY
+                        {
+                            ProbeForRead((PVOID)lParam,
+                                         sizeof(RECT),
+                                         1);
+
+                            RtlCopyMemory(&rt,
+                                          (PVOID)lParam,
+                                          sizeof(RECT));
+                        }
+                        _SEH2_EXCEPT(EXCEPTION_EXECUTE_HANDLER)
+                        {
+                            BadChk = TRUE;
+                        }
+                        _SEH2_END;
+
+                        if (BadChk)
+                        {
+                            DPRINT1("HOOK HCBT_MOVESIZE read from lParam ERROR!\n");
+                        }
+                    }
+
+                    if (!BadChk)
+                    {
+                        lResult = co_HOOK_CallHookNext(Hook, Code, wParam, (LPARAM)&rt);
+                    }
+                    break;
+                }
+
+                case HCBT_ACTIVATE:
+                {
+                    CBTACTIVATESTRUCT CbAs;
+
+                    DPRINT("HOOK HCBT_ACTIVATE\n");
+                    if (lParam)
+                    {
+                        _SEH2_TRY
+                        {
+                            ProbeForRead((PVOID)lParam,
+                                         sizeof(CBTACTIVATESTRUCT),
+                                         1);
+
+                            RtlCopyMemory(&CbAs,
+                                          (PVOID)lParam,
+                                          sizeof(CBTACTIVATESTRUCT));
+                        }
+                        _SEH2_EXCEPT(EXCEPTION_EXECUTE_HANDLER)
+                        {
+                            BadChk = TRUE;
+                        }
+                        _SEH2_END;
+
+                        if (BadChk)
+                        {
+                            DPRINT1("HOOK HCBT_ACTIVATE read from lParam ERROR!\n");
+                        }
+                    }
+
+                    if (!BadChk)
+                    {
+                        lResult = co_HOOK_CallHookNext(Hook, Code, wParam, (LPARAM)&CbAs);
+                    }
+                    break;
+                }
+
+                /* The rest just use default. */
+                default:
+                    DPRINT("HOOK HCBT_ %d\n",Code);
+                    lResult = co_HOOK_CallHookNext(Hook, Code, wParam, lParam);
+                    break;
+            }
+            break;
+
+        case WH_JOURNALPLAYBACK:
+        case WH_JOURNALRECORD:
+        {
+            EVENTMSG EventMsg;
+
+            if (lParam)
+            {
+                _SEH2_TRY
+                {
+                    ProbeForRead((PVOID)lParam,
+                                 sizeof(EVENTMSG),
+                                 1);
+
+                    RtlCopyMemory(&EventMsg,
+                                  (PVOID)lParam,
+                                  sizeof(EVENTMSG));
+                }
+                _SEH2_EXCEPT(EXCEPTION_EXECUTE_HANDLER)
+                {
+                    BadChk = TRUE;
+                }
+                _SEH2_END;
+
+                if (BadChk)
+                {
+                    DPRINT1("HOOK WH_JOURNAL read from lParam ERROR!\n");
+                }
+            }
+
+            if (!BadChk) 
+            {               
+                lResult = co_HOOK_CallHookNext(Hook, Code, wParam, (LPARAM)(lParam ? &EventMsg : NULL));
+
+                if (lParam)
+                {
+                    _SEH2_TRY
+                    {
+                        ProbeForWrite((PVOID)lParam,
+                                      sizeof(EVENTMSG),
+                                      1);
+
+                        RtlCopyMemory((PVOID)lParam,
+                                      &EventMsg,
+                                      sizeof(EVENTMSG));
+                    }
+                    _SEH2_EXCEPT(EXCEPTION_EXECUTE_HANDLER)
+                    {
+                        BadChk = TRUE;
+                    }
+                    _SEH2_END;
+
+                    if (BadChk)
+                    {
+                        DPRINT1("HOOK WH_JOURNAL write to lParam ERROR!\n");
+                    }
+                }
+            }
+            break;
+        }
+
+        case WH_DEBUG:
+            lResult = IntCallDebugHook(Hook, Code, wParam, lParam);
+            break;
+
+        /*
+         * Default the rest like, WH_FOREGROUNDIDLE, WH_KEYBOARD and WH_SHELL.
+         */
+        case WH_FOREGROUNDIDLE:
+        case WH_KEYBOARD:
+        case WH_SHELL:
+            lResult = co_HOOK_CallHookNext(Hook, Code, wParam, lParam);      
+            break;
+
+        default:
+            DPRINT1("Unsupported HOOK Id -> %d\n",Hook->HookId);
+            break;
+    }
+
+    return lResult; 
+}
+
+LRESULT
+APIENTRY
+NtUserCallNextHookEx(int Code,
+                     WPARAM wParam,
+                     LPARAM lParam,
+                     BOOL Ansi)
+{
+    PHOOK HookObj, NextObj;
+    PCLIENTINFO ClientInfo;
+    PWINSTATION_OBJECT WinStaObj;
+    NTSTATUS Status;
+    DECLARE_RETURN(LRESULT);
+
+    DPRINT("Enter NtUserCallNextHookEx\n");
+    UserEnterExclusive();
+
+    Status = IntValidateWindowStationHandle(PsGetCurrentProcess()->Win32WindowStation,
+                                            KernelMode,
+                                            0,
+                                            &WinStaObj);
+    if (!NT_SUCCESS(Status))
+    {
+        SetLastNtError(Status);
+        RETURN( 0);
+    }
+
+    ObDereferenceObject(WinStaObj);
+
+    ClientInfo = GetWin32ClientInfo();
+
+    if (!ClientInfo) RETURN( 0);
+
+    HookObj = ClientInfo->phkCurrent;
+
+    if (!HookObj) RETURN( 0);
+
+    UserReferenceObject(HookObj);
+
+    Ansi = HookObj->Ansi;
+
+    if (NULL != HookObj->Thread && (HookObj->Thread != PsGetCurrentThread()))
+    {
+        DPRINT1("Thread mismatch\n");
+        UserDereferenceObject(HookObj);
+        SetLastWin32Error(ERROR_INVALID_HANDLE);
+        RETURN( 0);
+    }
+   
+    NextObj = IntGetNextHook(HookObj);
+    ClientInfo->phkCurrent = NextObj; /* Preset next hook from list. */
+    UserCallNextHookEx( HookObj, Code, wParam, lParam, Ansi);
+    UserDereferenceObject(HookObj);
+
+    RETURN( (LRESULT)NextObj);
 
 CLEANUP:
-   DPRINT("Leave NtUserCallNextHookEx, ret=%i\n",_ret_);
-   UserLeave();
-   END_CLEANUP;
+    DPRINT("Leave NtUserCallNextHookEx, ret=%i\n",_ret_);
+    UserLeave();
+    END_CLEANUP;
 }
 
-DWORD
-STDCALL
-NtUserSetWindowsHookAW(
-   DWORD Unknown0,
-   DWORD Unknown1,
-   DWORD Unknown2)
+HHOOK
+APIENTRY
+NtUserSetWindowsHookAW(int idHook, 
+                       HOOKPROC lpfn,
+                       BOOL Ansi)
 {
-   UNIMPLEMENTED
+    UNICODE_STRING USModuleName;
+
+    RtlInitUnicodeString(&USModuleName, NULL);
 
-   return 0;
+    return NtUserSetWindowsHookEx(NULL, &USModuleName, 0, idHook, lpfn, Ansi);
 }
 
 HHOOK
-STDCALL
-NtUserSetWindowsHookEx(
-   HINSTANCE Mod,
-   PUNICODE_STRING UnsafeModuleName,
-   DWORD ThreadId,
-   int HookId,
-   HOOKPROC HookProc,
-   BOOL Ansi)
+APIENTRY
+NtUserSetWindowsHookEx(HINSTANCE Mod,
+                       PUNICODE_STRING UnsafeModuleName,
+                       DWORD ThreadId,
+                       int HookId,
+                       HOOKPROC HookProc,
+                       BOOL Ansi)
 {
-   PWINSTATION_OBJECT WinStaObj;
-   BOOLEAN Global;
-   PETHREAD Thread;
-   PHOOK Hook;
-   UNICODE_STRING ModuleName;
-   NTSTATUS Status;
-   HHOOK Handle;
-   DECLARE_RETURN(HHOOK);
-
-   DPRINT("Enter NtUserSetWindowsHookEx\n");
-   UserEnterExclusive();
-
-   if (HookId < WH_MINHOOK || WH_MAXHOOK < HookId || NULL == HookProc)
-   {
-      SetLastWin32Error(ERROR_INVALID_PARAMETER);
-      RETURN( NULL);
-   }
-
-   if (ThreadId)  /* thread-local hook */
-   {
-      if (HookId == WH_JOURNALRECORD ||
+    PWINSTATION_OBJECT WinStaObj;
+    PCLIENTINFO ClientInfo;
+    BOOLEAN Global;
+    PETHREAD Thread;
+    PHOOK Hook;
+    UNICODE_STRING ModuleName;
+    NTSTATUS Status;
+    HHOOK Handle;
+    BOOLEAN ThreadReferenced = FALSE;
+    DECLARE_RETURN(HHOOK);
+
+    DPRINT("Enter NtUserSetWindowsHookEx\n");
+    UserEnterExclusive();
+
+    if (HookId < WH_MINHOOK || WH_MAXHOOK < HookId )
+    {
+        SetLastWin32Error(ERROR_INVALID_PARAMETER);
+        RETURN( NULL);
+    }
+
+    if (!HookProc)
+    {
+        SetLastWin32Error(ERROR_INVALID_FILTER_PROC);
+        RETURN( NULL);
+    }
+
+    ClientInfo = GetWin32ClientInfo();
+
+    if (ThreadId)  /* thread-local hook */
+    {
+        if (HookId == WH_JOURNALRECORD ||
             HookId == WH_JOURNALPLAYBACK ||
             HookId == WH_KEYBOARD_LL ||
             HookId == WH_MOUSE_LL ||
             HookId == WH_SYSMSGFILTER)
-      {
-         /* these can only be global */
-         SetLastWin32Error(ERROR_INVALID_PARAMETER);
-         RETURN( NULL);
-      }
-      Mod = NULL;
-      Global = FALSE;
-      if (! NT_SUCCESS(PsLookupThreadByThreadId((HANDLE) ThreadId, &Thread)))
-      {
-         DPRINT1("Invalid thread id 0x%x\n", ThreadId);
-         SetLastWin32Error(ERROR_INVALID_PARAMETER);
-         RETURN( NULL);
-      }
-      if (Thread->ThreadsProcess != PsGetCurrentProcess())
-      {
-         ObDereferenceObject(Thread);
-         DPRINT1("Can't specify thread belonging to another process\n");
-         SetLastWin32Error(ERROR_INVALID_PARAMETER);
-         RETURN( NULL);
-      }
-   }
-   else  /* system-global hook */
-   {
-      if (HookId == WH_KEYBOARD_LL || HookId == WH_MOUSE_LL)
-      {
-         Mod = NULL;
-         Thread = PsGetCurrentThread();
-         Status = ObReferenceObjectByPointer(Thread,
-                                             THREAD_ALL_ACCESS,
-                                             PsThreadType,
-                                             KernelMode);
-
-         if (! NT_SUCCESS(Status))
-         {
-            SetLastNtError(Status);
-            RETURN( (HANDLE) NULL);
-         }
-      }
-      else if (NULL ==  Mod)
-      {
-         SetLastWin32Error(ERROR_INVALID_PARAMETER);
-         RETURN( NULL);
-      }
-      else
-      {
-         Thread = NULL;
-      }
-      Global = TRUE;
-   }
-
-   /* We only (partially) support local WH_CBT hooks and
-    * WH_KEYBOARD_LL/WH_MOUSE_LL hooks for now */
-   if ((WH_CBT != HookId || Global)
-         && WH_KEYBOARD_LL != HookId && WH_MOUSE_LL != HookId)
-   {
+        {
+            /* these can only be global */
+            SetLastWin32Error(ERROR_INVALID_PARAMETER);
+            RETURN( NULL);
+        }
+
+        Mod = NULL;
+        Global = FALSE;
+
+        if (!NT_SUCCESS(PsLookupThreadByThreadId((HANDLE)(DWORD_PTR) ThreadId, &Thread)))
+        {
+            DPRINT1("Invalid thread id 0x%x\n", ThreadId);
+            SetLastWin32Error(ERROR_INVALID_PARAMETER);
+            RETURN( NULL);
+        }
+
+        /* Thread was referenced */
+        ThreadReferenced = TRUE;
+        if (Thread->ThreadsProcess != PsGetCurrentProcess())
+        {
+            ObDereferenceObject(Thread);
+            DPRINT1("Can't specify thread belonging to another process\n");
+            SetLastWin32Error(ERROR_INVALID_PARAMETER);
+            RETURN( NULL);
+        }
+    }
+    else  /* system-global hook */
+    {
+        if (HookId == WH_KEYBOARD_LL || HookId == WH_MOUSE_LL)
+        {
+            Mod = NULL;
+            Thread = PsGetCurrentThread();
+
+            Status = ObReferenceObjectByPointer(Thread,
+                                                THREAD_ALL_ACCESS,
+                                                PsThreadType,
+                                                KernelMode);
+
+            if (!NT_SUCCESS(Status))
+            {
+                SetLastNtError(Status);
+                RETURN( (HANDLE) NULL);
+            }
+
+            /* Thread was referenced */
+            ThreadReferenced = TRUE;
+        }
+        else if (NULL ==  Mod)
+        {
+            SetLastWin32Error(ERROR_HOOK_NEEDS_HMOD);
+            RETURN( NULL);
+        }
+        else
+        {
+            Thread = NULL;
+        }
+        Global = TRUE;
+    }
+
+    if ((Global && (HookId != WH_KEYBOARD_LL && HookId != WH_MOUSE_LL)) ||
+        WH_DEBUG == HookId ||
+        WH_JOURNALPLAYBACK == HookId ||
+        WH_JOURNALRECORD == HookId)
+    {
 #if 0 /* Removed to get winEmbed working again */
-      UNIMPLEMENTED
+        UNIMPLEMENTED
 #else
-      DPRINT1("Not implemented: HookId %d Global %s\n", HookId, Global ? "TRUE" : "FALSE");
+        DPRINT1("Not implemented: HookId %d Global %s\n", HookId, Global ? "TRUE" : "FALSE");
 #endif
 
-      if (NULL != Thread)
-      {
-         ObDereferenceObject(Thread);
-      }
-      SetLastWin32Error(ERROR_NOT_SUPPORTED);
-      RETURN( NULL);
-   }
-
-   Status = IntValidateWindowStationHandle(PsGetCurrentProcess()->Win32WindowStation,
-                                           KernelMode,
-                                           0,
-                                           &WinStaObj);
-
-   if (! NT_SUCCESS(Status))
-   {
-      if (NULL != Thread)
-      {
-         ObDereferenceObject(Thread);
-      }
-      SetLastNtError(Status);
-      RETURN( (HANDLE) NULL);
-   }
-
-   Hook = IntAddHook(Thread, HookId, Global, WinStaObj);
-   if (NULL == Hook)
-   {
-      if (NULL != Thread)
-      {
-         ObDereferenceObject(Thread);
-      }
-      ObDereferenceObject(WinStaObj);
-      RETURN( NULL);
-   }
-
-   if (NULL != Thread)
-   {
-      Hook->Flags |= HOOK_THREAD_REFERENCED;
-   }
-
-   if (NULL != Mod)
-   {
-      Status = MmCopyFromCaller(&ModuleName, UnsafeModuleName, sizeof(UNICODE_STRING));
-      if (! NT_SUCCESS(Status))
-      {
-         ObmDereferenceObject(Hook);
-         IntRemoveHook(Hook, WinStaObj, FALSE);
-         if (NULL != Thread)
-         {
-            ObDereferenceObject(Thread);
-         }
-         ObDereferenceObject(WinStaObj);
-         SetLastNtError(Status);
-         RETURN( NULL);
-      }
-      Hook->ModuleName.Buffer = ExAllocatePoolWithTag(PagedPool,
-                                ModuleName.MaximumLength,
-                                TAG_HOOK);
-      if (NULL == Hook->ModuleName.Buffer)
-      {
-         ObmDereferenceObject(Hook);
-         IntRemoveHook(Hook, WinStaObj, FALSE);
-         if (NULL != Thread)
-         {
-            ObDereferenceObject(Thread);
-         }
-         ObDereferenceObject(WinStaObj);
-         SetLastWin32Error(ERROR_NOT_ENOUGH_MEMORY);
-         RETURN( NULL);
-      }
-      Hook->ModuleName.MaximumLength = ModuleName.MaximumLength;
-      Status = MmCopyFromCaller(Hook->ModuleName.Buffer,
-                                ModuleName.Buffer,
-                                ModuleName.MaximumLength);
-      if (! NT_SUCCESS(Status))
-      {
-            ExFreePool(Hook->ModuleName.Buffer);
-         ObmDereferenceObject(Hook);
-         IntRemoveHook(Hook, WinStaObj, FALSE);
-         if (NULL != Thread)
-         {
-            ObDereferenceObject(Thread);
-         }
-         ObDereferenceObject(WinStaObj);
-         SetLastNtError(Status);
-         RETURN( NULL);
-      }
-      Hook->ModuleName.Length = ModuleName.Length;
-   }
+        /* Dereference thread if needed */
+        if (ThreadReferenced) ObDereferenceObject(Thread);
+        SetLastWin32Error(ERROR_NOT_SUPPORTED);
+        RETURN( NULL);
+    }
+
+    Status = IntValidateWindowStationHandle(PsGetCurrentProcess()->Win32WindowStation,
+                                            KernelMode,
+                                            0,
+                                            &WinStaObj);
+
+    if (!NT_SUCCESS(Status))
+    {
+        /* Dereference thread if needed */
+        if (ThreadReferenced) ObDereferenceObject(Thread);
+        SetLastNtError(Status);
+        RETURN( (HANDLE) NULL);
+    }
+
+    Hook = IntAddHook(Thread, HookId, Global, WinStaObj);
+    if (NULL == Hook)
+    {
+        /* Dereference thread if needed */
+        if (ThreadReferenced) ObDereferenceObject(Thread);
+        ObDereferenceObject(WinStaObj);
+        RETURN( NULL);
+    }
+
+    /* Let IntFreeHook now that this thread needs a dereference */
+    if (ThreadReferenced)
+    {
+        Hook->Flags |= HOOK_THREAD_REFERENCED;
+    }
+
+    if (NULL != Mod)
+    {
+        Status = MmCopyFromCaller(&ModuleName, UnsafeModuleName, sizeof(UNICODE_STRING));
+        if (!NT_SUCCESS(Status))
+        {
+            UserDereferenceObject(Hook);
+            IntRemoveHook(Hook, WinStaObj, FALSE);
+            ObDereferenceObject(WinStaObj);
+            SetLastNtError(Status);
+            RETURN( NULL);
+        }
+
+        Hook->ModuleName.Buffer = ExAllocatePoolWithTag(PagedPool,
+                                                        ModuleName.MaximumLength,
+                                                        TAG_HOOK);
+        if (NULL == Hook->ModuleName.Buffer)
+        {
+            UserDereferenceObject(Hook);
+            IntRemoveHook(Hook, WinStaObj, FALSE);
+            ObDereferenceObject(WinStaObj);
+            SetLastWin32Error(ERROR_NOT_ENOUGH_MEMORY);
+            RETURN( NULL);
+        }
+
+        Hook->ModuleName.MaximumLength = ModuleName.MaximumLength;
+        Status = MmCopyFromCaller(Hook->ModuleName.Buffer,
+                                  ModuleName.Buffer,
+                                  ModuleName.MaximumLength);
+        if (!NT_SUCCESS(Status))
+        {
+            ExFreePoolWithTag(Hook->ModuleName.Buffer, TAG_HOOK);
+            UserDereferenceObject(Hook);
+            IntRemoveHook(Hook, WinStaObj, FALSE);
+            ObDereferenceObject(WinStaObj);
+            SetLastNtError(Status);
+            RETURN( NULL);
+        }
 
-   Hook->Proc = HookProc;
-   Hook->Ansi = Ansi;
-   Handle = Hook->Self;
+        Hook->ModuleName.Length = ModuleName.Length;
+        /* make proc relative to the module base */
+        Hook->Proc = (void *)((char *)HookProc - (char *)Mod);
+    }
+    else
+        Hook->Proc = HookProc;
 
-   ObmDereferenceObject(Hook);
-   ObDereferenceObject(WinStaObj);
+    Hook->Ansi = Ansi;
+    Handle = Hook->head.h;
 
-   RETURN( Handle);
+    /* Clear the client threads next hook. */
+    ClientInfo->phkCurrent = 0;
 
-CLEANUP:
-   DPRINT("Leave NtUserSetWindowsHookEx, ret=%i\n",_ret_);
-   UserLeave();
-   END_CLEANUP;
-}
+    UserDereferenceObject(Hook);
 
-DWORD
-STDCALL
-NtUserSetWinEventHook(
-   DWORD Unknown0,
-   DWORD Unknown1,
-   DWORD Unknown2,
-   DWORD Unknown3,
-   DWORD Unknown4,
-   DWORD Unknown5,
-   DWORD Unknown6,
-   DWORD Unknown7)
-{
-   UNIMPLEMENTED
+    ObDereferenceObject(WinStaObj);
+
+    RETURN( Handle);
 
-   return 0;
+CLEANUP:
+    DPRINT("Leave NtUserSetWindowsHookEx, ret=%i\n",_ret_);
+    UserLeave();
+    END_CLEANUP;
 }
 
+
 BOOL
-STDCALL
-NtUserUnhookWindowsHookEx(
-   HHOOK Hook)
+APIENTRY
+NtUserUnhookWindowsHookEx(HHOOK Hook)
 {
-   PWINSTATION_OBJECT WinStaObj;
-   PHOOK HookObj;
-   NTSTATUS Status;
-   DECLARE_RETURN(BOOL);
-
-   DPRINT("Enter NtUserUnhookWindowsHookEx\n");
-   UserEnterExclusive();
-
-   Status = IntValidateWindowStationHandle(PsGetCurrentProcess()->Win32WindowStation,
-                                           KernelMode,
-                                           0,
-                                           &WinStaObj);
-
-   if (! NT_SUCCESS(Status))
-   {
-      SetLastNtError(Status);
-      RETURN( FALSE);
-   }
-
-   //  Status = ObmReferenceObjectByHandle(gHandleTable, Hook,
-   //                                      otHookProc, (PVOID *) &HookObj);
-   if (!(HookObj = IntGetHookObject(Hook)))
-   {
-      DPRINT1("Invalid handle passed to NtUserUnhookWindowsHookEx\n");
-      ObDereferenceObject(WinStaObj);
-      //      SetLastNtError(Status);
-      RETURN( FALSE);
-   }
-   ASSERT(Hook == HookObj->Self);
-
-   IntRemoveHook(HookObj, WinStaObj, FALSE);
-
-   ObmDereferenceObject(HookObj);
-   ObDereferenceObject(WinStaObj);
-
-   RETURN( TRUE);
+    PWINSTATION_OBJECT WinStaObj;
+    PHOOK HookObj;
+    NTSTATUS Status;
+    DECLARE_RETURN(BOOL);
 
-CLEANUP:
-   DPRINT("Leave NtUserUnhookWindowsHookEx, ret=%i\n",_ret_);
-   UserLeave();
-   END_CLEANUP;
-}
+    DPRINT("Enter NtUserUnhookWindowsHookEx\n");
+    UserEnterExclusive();
 
-DWORD
-STDCALL
-NtUserUnhookWinEvent(
-   DWORD Unknown0)
-{
-   UNIMPLEMENTED
+    Status = IntValidateWindowStationHandle(PsGetCurrentProcess()->Win32WindowStation,
+                                            KernelMode,
+                                            0,
+                                            &WinStaObj);
+
+    if (!NT_SUCCESS(Status))
+    {
+        SetLastNtError(Status);
+        RETURN( FALSE);
+    }
+
+    /*  Status = UserReferenceObjectByHandle(gHandleTable, Hook,
+                                             otHookProc, (PVOID *) &HookObj); */
+    if (!(HookObj = IntGetHookObject(Hook)))
+    {
+        DPRINT1("Invalid handle passed to NtUserUnhookWindowsHookEx\n");
+        ObDereferenceObject(WinStaObj);
+        /* SetLastNtError(Status); */
+        RETURN( FALSE);
+    }
+
+    ASSERT(Hook == HookObj->head.h);
 
-   return 0;
+    IntRemoveHook(HookObj, WinStaObj, FALSE);
+
+    UserDereferenceObject(HookObj);
+    ObDereferenceObject(WinStaObj);
+
+    RETURN( TRUE);
+
+CLEANUP:
+    DPRINT("Leave NtUserUnhookWindowsHookEx, ret=%i\n",_ret_);
+    UserLeave();
+    END_CLEANUP;
 }
 
 /* EOF */