- Update KTHREAD and KUSER_SHARED_DATA to latest versions. This should make 2K3 drive...
[reactos.git] / reactos / subsys / win32k / ntuser / message.c
index 015af9f..7e90408 100644 (file)
@@ -1,4 +1,22 @@
-/* $Id: message.c,v 1.17 2003/05/18 06:47:19 gvg Exp $
+/*
+ *  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
 
 /* INCLUDES ******************************************************************/
 
-#include <ddk/ntddk.h>
-#include <win32k/win32k.h>
-#include <include/guicheck.h>
-#include <include/msgqueue.h>
-#include <include/window.h>
-#include <include/class.h>
-#include <include/error.h>
-#include <include/object.h>
-#include <include/winsta.h>
-#include <include/callback.h>
-#include <include/painting.h>
+#include <w32k.h>
 
 #define NDEBUG
 #include <debug.h>
 
+typedef struct
+{
+   UINT uFlags;
+   UINT uTimeout;
+   ULONG_PTR Result;
+}
+DOSENDMESSAGE, *PDOSENDMESSAGE;
+
 /* FUNCTIONS *****************************************************************/
 
-NTSTATUS
-W32kInitMessageImpl(VOID)
+NTSTATUS FASTCALL
+IntInitMessageImpl(VOID)
 {
-  return(STATUS_SUCCESS);
+   return STATUS_SUCCESS;
 }
 
-NTSTATUS
-W32kCleanupMessageImpl(VOID)
+NTSTATUS FASTCALL
+IntCleanupMessageImpl(VOID)
 {
-  return(STATUS_SUCCESS);
+   return STATUS_SUCCESS;
 }
 
+#define MMS_SIZE_WPARAM      -1
+#define MMS_SIZE_WPARAMWCHAR -2
+#define MMS_SIZE_LPARAMSZ    -3
+#define MMS_SIZE_SPECIAL     -4
+#define MMS_FLAG_READ        0x01
+#define MMS_FLAG_WRITE       0x02
+#define MMS_FLAG_READWRITE   (MMS_FLAG_READ | MMS_FLAG_WRITE)
+typedef struct tagMSGMEMORY
+{
+   UINT Message;
+   UINT Size;
+   INT Flags;
+}
+MSGMEMORY, *PMSGMEMORY;
+
+static MSGMEMORY MsgMemory[] =
+   {
+      { WM_CREATE, MMS_SIZE_SPECIAL, MMS_FLAG_READWRITE },
+      { WM_DDE_ACK, sizeof(KMDDELPARAM), MMS_FLAG_READ },
+      { WM_DDE_EXECUTE, MMS_SIZE_WPARAM, MMS_FLAG_READ },
+      { WM_GETMINMAXINFO, sizeof(MINMAXINFO), MMS_FLAG_READWRITE },
+      { WM_GETTEXT, MMS_SIZE_WPARAMWCHAR, MMS_FLAG_WRITE },
+      { WM_NCCALCSIZE, MMS_SIZE_SPECIAL, MMS_FLAG_READWRITE },
+      { WM_NCCREATE, MMS_SIZE_SPECIAL, MMS_FLAG_READWRITE },
+      { WM_SETTEXT, MMS_SIZE_LPARAMSZ, MMS_FLAG_READ },
+      { WM_STYLECHANGED, sizeof(STYLESTRUCT), MMS_FLAG_READ },
+      { WM_STYLECHANGING, sizeof(STYLESTRUCT), MMS_FLAG_READWRITE },
+      { WM_COPYDATA, MMS_SIZE_SPECIAL, MMS_FLAG_READ },
+      { WM_WINDOWPOSCHANGED, sizeof(WINDOWPOS), MMS_FLAG_READ },
+      { WM_WINDOWPOSCHANGING, sizeof(WINDOWPOS), MMS_FLAG_READWRITE },
+   };
+
+static PMSGMEMORY FASTCALL
+FindMsgMemory(UINT Msg)
+{
+   PMSGMEMORY MsgMemoryEntry;
+
+   /* See if this message type is present in the table */
+   for (MsgMemoryEntry = MsgMemory;
+         MsgMemoryEntry < MsgMemory + sizeof(MsgMemory) / sizeof(MSGMEMORY);
+         MsgMemoryEntry++)
+   {
+      if (Msg == MsgMemoryEntry->Message)
+      {
+         return MsgMemoryEntry;
+      }
+   }
+
+   return NULL;
+}
+
+static UINT FASTCALL
+MsgMemorySize(PMSGMEMORY MsgMemoryEntry, WPARAM wParam, LPARAM lParam)
+{
+   CREATESTRUCTW *Cs;
+   PUNICODE_STRING WindowName;
+   PUNICODE_STRING ClassName;
+   UINT Size;
+
+   _SEH_TRY {
+      if (MMS_SIZE_WPARAM == MsgMemoryEntry->Size)
+   {
+      return (UINT) wParam;
+      }
+      else if (MMS_SIZE_WPARAMWCHAR == MsgMemoryEntry->Size)
+   {
+      return (UINT) (wParam * sizeof(WCHAR));
+      }
+      else if (MMS_SIZE_LPARAMSZ == MsgMemoryEntry->Size)
+   {
+      return (UINT) ((wcslen((PWSTR) lParam) + 1) * sizeof(WCHAR));
+      }
+      else if (MMS_SIZE_SPECIAL == MsgMemoryEntry->Size)
+   {
+      switch(MsgMemoryEntry->Message)
+         {
+            case WM_CREATE:
+            case WM_NCCREATE:
+               Cs = (CREATESTRUCTW *) lParam;
+               WindowName = (PUNICODE_STRING) Cs->lpszName;
+               ClassName = (PUNICODE_STRING) Cs->lpszClass;
+               Size = sizeof(CREATESTRUCTW) + WindowName->Length + sizeof(WCHAR);
+               if (IS_ATOM(ClassName->Buffer))
+               {
+                  Size += sizeof(WCHAR) + sizeof(ATOM);
+               }
+               else
+               {
+                  Size += sizeof(WCHAR) + ClassName->Length + sizeof(WCHAR);
+               }
+               return Size;
+               break;
+
+            case WM_NCCALCSIZE:
+               return wParam ? sizeof(NCCALCSIZE_PARAMS) + sizeof(WINDOWPOS) : sizeof(RECT);
+               break;
+
+            case WM_COPYDATA:
+               return sizeof(COPYDATASTRUCT) + ((PCOPYDATASTRUCT)lParam)->cbData;
+
+            default:
+               assert(FALSE);
+               return 0;
+               break;
+         }
+      }
+      else
+      {
+         return MsgMemoryEntry->Size;
+      }
+   } _SEH_HANDLE {
+
+      DPRINT1("Exception caught in MsgMemorySize()! Status: 0x%x\n", _SEH_GetExceptionCode());
+   } _SEH_END;
+   return 0;
+}
+
+static FASTCALL NTSTATUS
+PackParam(LPARAM *lParamPacked, UINT Msg, WPARAM wParam, LPARAM lParam)
+{
+   NCCALCSIZE_PARAMS *UnpackedNcCalcsize;
+   NCCALCSIZE_PARAMS *PackedNcCalcsize;
+   CREATESTRUCTW *UnpackedCs;
+   CREATESTRUCTW *PackedCs;
+   PUNICODE_STRING WindowName;
+   PUNICODE_STRING ClassName;
+   UINT Size;
+   PCHAR CsData;
+
+   *lParamPacked = lParam;
+   if (WM_NCCALCSIZE == Msg && wParam)
+   {
+      UnpackedNcCalcsize = (NCCALCSIZE_PARAMS *) lParam;
+      if (UnpackedNcCalcsize->lppos != (PWINDOWPOS) (UnpackedNcCalcsize + 1))
+      {
+         PackedNcCalcsize = ExAllocatePoolWithTag(PagedPool,
+                            sizeof(NCCALCSIZE_PARAMS) + sizeof(WINDOWPOS),
+                            TAG_MSG);
+         if (NULL == PackedNcCalcsize)
+         {
+            DPRINT1("Not enough memory to pack lParam\n");
+            return STATUS_NO_MEMORY;
+         }
+         RtlCopyMemory(PackedNcCalcsize, UnpackedNcCalcsize, sizeof(NCCALCSIZE_PARAMS));
+         PackedNcCalcsize->lppos = (PWINDOWPOS) (PackedNcCalcsize + 1);
+         RtlCopyMemory(PackedNcCalcsize->lppos, UnpackedNcCalcsize->lppos, sizeof(WINDOWPOS));
+         *lParamPacked = (LPARAM) PackedNcCalcsize;
+      }
+   }
+   else if (WM_CREATE == Msg || WM_NCCREATE == Msg)
+   {
+      UnpackedCs = (CREATESTRUCTW *) lParam;
+      WindowName = (PUNICODE_STRING) UnpackedCs->lpszName;
+      ClassName = (PUNICODE_STRING) UnpackedCs->lpszClass;
+      Size = sizeof(CREATESTRUCTW) + WindowName->Length + sizeof(WCHAR);
+      if (IS_ATOM(ClassName->Buffer))
+      {
+         Size += sizeof(WCHAR) + sizeof(ATOM);
+      }
+      else
+      {
+         Size += sizeof(WCHAR) + ClassName->Length + sizeof(WCHAR);
+      }
+      PackedCs = ExAllocatePoolWithTag(PagedPool, Size, TAG_MSG);
+      if (NULL == PackedCs)
+      {
+         DPRINT1("Not enough memory to pack lParam\n");
+         return STATUS_NO_MEMORY;
+      }
+      RtlCopyMemory(PackedCs, UnpackedCs, sizeof(CREATESTRUCTW));
+      CsData = (PCHAR) (PackedCs + 1);
+      PackedCs->lpszName = (LPCWSTR) (CsData - (PCHAR) PackedCs);
+      RtlCopyMemory(CsData, WindowName->Buffer, WindowName->Length);
+      CsData += WindowName->Length;
+      *((WCHAR *) CsData) = L'\0';
+      CsData += sizeof(WCHAR);
+      PackedCs->lpszClass = (LPCWSTR) (CsData - (PCHAR) PackedCs);
+      if (IS_ATOM(ClassName->Buffer))
+      {
+         *((WCHAR *) CsData) = L'A';
+         CsData += sizeof(WCHAR);
+         *((ATOM *) CsData) = (ATOM)(DWORD_PTR) ClassName->Buffer;
+         CsData += sizeof(ATOM);
+      }
+      else
+      {
+         *((WCHAR *) CsData) = L'S';
+         CsData += sizeof(WCHAR);
+         RtlCopyMemory(CsData, ClassName->Buffer, ClassName->Length);
+         CsData += ClassName->Length;
+         *((WCHAR *) CsData) = L'\0';
+         CsData += sizeof(WCHAR);
+      }
+      ASSERT(CsData == (PCHAR) PackedCs + Size);
+      *lParamPacked = (LPARAM) PackedCs;
+   }
+
+   return STATUS_SUCCESS;
+}
+
+static FASTCALL NTSTATUS
+UnpackParam(LPARAM lParamPacked, UINT Msg, WPARAM wParam, LPARAM lParam)
+{
+   NCCALCSIZE_PARAMS *UnpackedParams;
+   NCCALCSIZE_PARAMS *PackedParams;
+   PWINDOWPOS UnpackedWindowPos;
+
+   if (lParamPacked == lParam)
+   {
+      return STATUS_SUCCESS;
+   }
+
+   if (WM_NCCALCSIZE == Msg && wParam)
+   {
+      PackedParams = (NCCALCSIZE_PARAMS *) lParamPacked;
+      UnpackedParams = (NCCALCSIZE_PARAMS *) lParam;
+      UnpackedWindowPos = UnpackedParams->lppos;
+      RtlCopyMemory(UnpackedParams, PackedParams, sizeof(NCCALCSIZE_PARAMS));
+      UnpackedParams->lppos = UnpackedWindowPos;
+      RtlCopyMemory(UnpackedWindowPos, PackedParams + 1, sizeof(WINDOWPOS));
+      ExFreePool((PVOID) lParamPacked);
+
+      return STATUS_SUCCESS;
+   }
+   else if (WM_CREATE == Msg || WM_NCCREATE == Msg)
+   {
+      ExFreePool((PVOID) lParamPacked);
+
+      return STATUS_SUCCESS;
+   }
+
+   ASSERT(FALSE);
+
+   return STATUS_INVALID_PARAMETER;
+}
+
+BOOL
+STDCALL
+NtUserCallMsgFilter(
+   LPMSG msg,
+   INT code)
+{
+   DECLARE_RETURN(BOOL);
+
+   DPRINT("Enter NtUserCallMsgFilter\n");
+   UserEnterExclusive();
+
+   if (co_HOOK_CallHooks( WH_SYSMSGFILTER, code, 0, (LPARAM)msg))
+      RETURN( TRUE);
+   RETURN( co_HOOK_CallHooks( WH_MSGFILTER, code, 0, (LPARAM)msg));
+
+CLEANUP:
+   DPRINT("Leave NtUserCallMsgFilter. ret=%i\n", _ret_);
+   UserLeave();
+   END_CLEANUP;
+}
 
 LRESULT STDCALL
-NtUserDispatchMessage(CONST MSG* lpMsg)
-{
-  LRESULT Result;
-  ULONG PaintingFlag;
-  PWINDOW_OBJECT WindowObject;
-  NTSTATUS Status;
-
-  /* Process timer messages. */
-  if (lpMsg->message == WM_TIMER)
-    {
-      if (lpMsg->lParam)
-       {
-         /* FIXME: Call hooks. */
-
-         /* FIXME: Check for continuing validity of timer. */
-
-         return(W32kCallWindowProc((WNDPROC)lpMsg->lParam,
-                                     lpMsg->hwnd,
-                                     lpMsg->message,
-                                     lpMsg->wParam,
-                                     0 /* GetTickCount() */));
-       }
-    }
-
-  /* Get the window object. */
-  Status = 
-    ObmReferenceObjectByHandle(PsGetWin32Process()->WindowStation->HandleTable,
-                              lpMsg->hwnd,
-                              otWindow,
-                              (PVOID*)&WindowObject);
-  if (!NT_SUCCESS(Status))
-    {
-      return(0);
-    }
-
-  /* FIXME: Check for paint message. */
-  PaintingFlag = (lpMsg->message == WM_PAINT);
-  if (PaintingFlag)
-    {
-      WindowObject->Flags |= WINDOWOBJECT_NEED_BEGINPAINT;
-    }
-
-  /* FIXME: Call hook procedures. */
-
-  /* Call the window procedure. */
-  Result = W32kCallWindowProc(NULL /* WndProc */,
-                             lpMsg->hwnd,
-                             lpMsg->message,
-                             lpMsg->wParam,
-                             lpMsg->lParam);
-
-  if (PaintingFlag && WindowObject->Flags & WINDOWOBJECT_NEED_BEGINPAINT &&
-      WindowObject->UpdateRegion)
-    {
-      DbgBreakPoint();
-    }
-
-  return(Result);
+NtUserDispatchMessage(PNTUSERDISPATCHMESSAGEINFO UnsafeMsgInfo)
+{
+   NTSTATUS Status;
+   NTUSERDISPATCHMESSAGEINFO MsgInfo;
+   LRESULT Result = TRUE;
+   DECLARE_RETURN(LRESULT);
+
+   DPRINT("Enter NtUserDispatchMessage\n");
+   UserEnterExclusive();
+
+   Status = MmCopyFromCaller(&MsgInfo, UnsafeMsgInfo, sizeof(NTUSERDISPATCHMESSAGEINFO));
+   if (! NT_SUCCESS(Status))
+   {
+      SetLastNtError(Status);
+      RETURN( 0);
+   }
+
+   /* Process timer messages. */
+   if (WM_TIMER == MsgInfo.Msg.message && 0 != MsgInfo.Msg.lParam)
+   {
+      LARGE_INTEGER LargeTickCount;
+      /* FIXME: Call hooks. */
+
+      /* FIXME: Check for continuing validity of timer. */
+
+      MsgInfo.HandledByKernel = FALSE;
+      KeQueryTickCount(&LargeTickCount);
+      MsgInfo.Proc = (WNDPROC) MsgInfo.Msg.lParam;
+      MsgInfo.Msg.lParam = (LPARAM)LargeTickCount.u.LowPart;
+   }
+   else if (NULL == MsgInfo.Msg.hwnd)
+   {
+      MsgInfo.HandledByKernel = TRUE;
+      Result = 0;
+   }
+   else
+   {
+      PWINDOW_OBJECT Window;
+      
+      /* Get the window object. */
+      Window = UserGetWindowObject(MsgInfo.Msg.hwnd);
+      if (NULL == Window)
+      {
+         MsgInfo.HandledByKernel = TRUE;
+         Result = 0;
+      }
+      else
+      {
+         if (Window->OwnerThread != PsGetCurrentThread())
+         {
+            DPRINT1("Window doesn't belong to the calling thread!\n");
+            MsgInfo.HandledByKernel = TRUE;
+            Result = 0;
+         }
+         else
+         {
+            /* FIXME: Call hook procedures. */
+
+            MsgInfo.HandledByKernel = FALSE;
+            Result = 0;
+            if (0xFFFF0000 != ((DWORD) Window->WndProcW & 0xFFFF0000))
+            {
+               if (0xFFFF0000 != ((DWORD) Window->WndProcA & 0xFFFF0000))
+               {
+                  /* Both Unicode and Ansi winprocs are real, use whatever
+                     usermode prefers */
+                  MsgInfo.Proc = (MsgInfo.Ansi ? Window->WndProcA
+                                  : Window->WndProcW);
+               }
+               else
+               {
+                  /* Real Unicode winproc */
+                  MsgInfo.Ansi = FALSE;
+                  MsgInfo.Proc = Window->WndProcW;
+               }
+            }
+            else
+            {
+               /* Must have real Ansi winproc */
+               MsgInfo.Ansi = TRUE;
+               MsgInfo.Proc = Window->WndProcA;
+            }
+         }
+      }
+   }
+   Status = MmCopyToCaller(UnsafeMsgInfo, &MsgInfo, sizeof(NTUSERDISPATCHMESSAGEINFO));
+   if (! NT_SUCCESS(Status))
+   {
+      SetLastNtError(Status);
+      RETURN( 0);
+   }
+
+   RETURN( Result);
+
+CLEANUP:
+   DPRINT("Leave NtUserDispatchMessage. ret=%i\n", _ret_);
+   UserLeave();
+   END_CLEANUP;
 }
 
+
 BOOL STDCALL
-NtUserGetMessage(LPMSG lpMsg,
-                HWND hWnd,
-                UINT wMsgFilterMin,
-                UINT wMsgFilterMax)
-/*
- * FUNCTION: Get a message from the calling thread's message queue.
- * ARGUMENTS:
- *      lpMsg - Pointer to the structure which receives the returned message.
- *      hWnd - Window whose messages are to be retrieved.
- *      wMsgFilterMin - Integer value of the lowest message value to be
- *                      retrieved.
- *      wMsgFilterMax - Integer value of the highest message value to be
- *                      retrieved.
- */
+NtUserTranslateMessage(LPMSG lpMsg,
+                       HKL dwhkl)
 {
-  PUSER_MESSAGE_QUEUE ThreadQueue;
-  BOOLEAN Present;
-  PUSER_MESSAGE Message;
-  NTSTATUS Status;
+   NTSTATUS Status;
+   MSG SafeMsg;
+   DECLARE_RETURN(BOOL);
+
+   DPRINT("Enter NtUserTranslateMessage\n");
+   UserEnterExclusive();
+
+   Status = MmCopyFromCaller(&SafeMsg, lpMsg, sizeof(MSG));
+   if(!NT_SUCCESS(Status))
+   {
+      SetLastNtError(Status);
+      RETURN( FALSE);
+   }
+
+   RETURN( IntTranslateKbdMessage(&SafeMsg, dwhkl));
+
+CLEANUP:
+   DPRINT("Leave NtUserTranslateMessage: ret=%i\n",_ret_);
+   UserLeave();
+   END_CLEANUP;
+}
 
-  /* Initialize the thread's win32 state if necessary. */ 
-  W32kGuiCheck();
 
-  ThreadQueue = (PUSER_MESSAGE_QUEUE)PsGetWin32Thread()->MessageQueue;
+VOID FASTCALL
+co_IntSendHitTestMessages(PUSER_MESSAGE_QUEUE ThreadQueue, LPMSG Msg)
+{
+   if(!Msg->hwnd || ThreadQueue->CaptureWindow)
+   {
+      return;
+   }
+
+   switch(Msg->message)
+   {
+      case WM_MOUSEMOVE:
+         {
+            co_IntSendMessage(Msg->hwnd, WM_SETCURSOR, (WPARAM)Msg->hwnd, MAKELPARAM(HTCLIENT, Msg->message));
+            break;
+         }
+      case WM_NCMOUSEMOVE:
+         {
+            co_IntSendMessage(Msg->hwnd, WM_SETCURSOR, (WPARAM)Msg->hwnd, MAKELPARAM(Msg->wParam, Msg->message));
+            break;
+         }
+      case WM_LBUTTONDOWN:
+      case WM_MBUTTONDOWN:
+      case WM_RBUTTONDOWN:
+      case WM_XBUTTONDOWN:
+      case WM_LBUTTONDBLCLK:
+      case WM_MBUTTONDBLCLK:
+      case WM_RBUTTONDBLCLK:
+      case WM_XBUTTONDBLCLK:
+         {
+            WPARAM wParam;
+            PSYSTEM_CURSORINFO CurInfo;
+
+            if(!IntGetWindowStationObject(InputWindowStation))
+            {
+               break;
+            }
+            CurInfo = IntGetSysCursorInfo(InputWindowStation);
+            wParam = (WPARAM)(CurInfo->ButtonsDown);
+            ObDereferenceObject(InputWindowStation);
+
+            co_IntSendMessage(Msg->hwnd, WM_MOUSEMOVE, wParam, Msg->lParam);
+            co_IntSendMessage(Msg->hwnd, WM_SETCURSOR, (WPARAM)Msg->hwnd, MAKELPARAM(HTCLIENT, Msg->message));
+            break;
+         }
+      case WM_NCLBUTTONDOWN:
+      case WM_NCMBUTTONDOWN:
+      case WM_NCRBUTTONDOWN:
+      case WM_NCXBUTTONDOWN:
+      case WM_NCLBUTTONDBLCLK:
+      case WM_NCMBUTTONDBLCLK:
+      case WM_NCRBUTTONDBLCLK:
+      case WM_NCXBUTTONDBLCLK:
+         {
+            co_IntSendMessage(Msg->hwnd, WM_NCMOUSEMOVE, (WPARAM)Msg->wParam, Msg->lParam);
+            co_IntSendMessage(Msg->hwnd, WM_SETCURSOR, (WPARAM)Msg->hwnd, MAKELPARAM(Msg->wParam, Msg->message));
+            break;
+         }
+   }
+}
 
-  do
-    {
-      /* Dispatch sent messages here. */
-      while (MsqDispatchOneSentMessage(ThreadQueue));
-      
-      /* Now look for a quit message. */
-      /* FIXME: WINE checks the message number filter here. */
-      if (ThreadQueue->QuitPosted)
-       {
-         lpMsg->hwnd = hWnd;
-         lpMsg->message = WM_QUIT;
-         lpMsg->wParam = ThreadQueue->QuitExitCode;
-         lpMsg->lParam = 0;
-         ThreadQueue->QuitPosted = FALSE;
-         return(FALSE);
-       }
-
-      /* Now check for normal messages. */
-      Present = MsqFindMessage(ThreadQueue,
-                              FALSE,
-                              TRUE,
-                              hWnd,
-                              wMsgFilterMin,
-                              wMsgFilterMax,
-                              &Message);
-      if (Present)
-       {
-         RtlCopyMemory(lpMsg, &Message->Msg, sizeof(MSG));
-         ExFreePool(Message);
-         return(TRUE);
-       }
-
-      /* Check for hardware events. */
-      Present = MsqFindMessage(ThreadQueue,
-                              TRUE,
-                              TRUE,
-                              hWnd,
-                              wMsgFilterMin,
-                              wMsgFilterMax,
-                              &Message);
-      if (Present)
-       {
-         RtlCopyMemory(lpMsg, &Message->Msg, sizeof(MSG));
-         ExFreePool(Message);
-         return(TRUE);
-       }
-
-      /* Check for sent messages again. */
-      while (MsqDispatchOneSentMessage(ThreadQueue));
-
-      /* Check for paint messages. */
-      if (ThreadQueue->PaintPosted)
-       {
-         PWINDOW_OBJECT WindowObject;
-
-         lpMsg->hwnd = PaintingFindWinToRepaint(hWnd, PsGetWin32Thread());
-         lpMsg->message = WM_PAINT;
-         lpMsg->wParam = lpMsg->lParam = 0;
-
-         WindowObject = W32kGetWindowObject(lpMsg->hwnd);
-         if (WindowObject != NULL)
-           {
-             if (WindowObject->Style & WS_MINIMIZE &&
-                 (HICON)NtUserGetClassLong(lpMsg->hwnd, GCL_HICON) != NULL)
-               {
-                 lpMsg->message = WM_PAINTICON;
-                 lpMsg->wParam = 1;
-               }
-
-             if (lpMsg->hwnd == NULL || lpMsg->hwnd == hWnd ||
-                 W32kIsChildWindow(hWnd, lpMsg->hwnd))
-               {
-                 if (WindowObject->Flags & WINDOWOBJECT_NEED_INTERNALPAINT &&
-                     WindowObject->UpdateRegion == NULL)
-                   {
-                     WindowObject->Flags &= ~WINDOWOBJECT_NEED_INTERNALPAINT;
-                     MsqDecPaintCountQueue(WindowObject->MessageQueue);
-                   }
-               }
-             W32kReleaseWindowObject(WindowObject);
-           }
-
-         return(TRUE); 
-       }
-
-      /* Nothing found so far. Wait for new messages. */
-      Status = MsqWaitForNewMessages(ThreadQueue);
-    }
-  while (Status >= STATUS_WAIT_0 && Status <= STATUS_WAIT_63);
-  return((BOOLEAN)(-1));
+BOOL FASTCALL
+co_IntActivateWindowMouse(PUSER_MESSAGE_QUEUE ThreadQueue, LPMSG Msg, PWINDOW_OBJECT MsgWindow,
+                          USHORT *HitTest)
+{
+   ULONG Result;
+   PWINDOW_OBJECT Parent;
+
+   ASSERT_REFS_CO(MsgWindow);
+
+   if(*HitTest == (USHORT)HTTRANSPARENT)
+   {
+      /* eat the message, search again! */
+      return TRUE;
+   }
+
+   Parent = IntGetParent(MsgWindow);//fixme: deref retval?
+   /* fixme: abort if no parent ? */
+   Result = co_IntSendMessage(MsgWindow->hSelf,
+                              WM_MOUSEACTIVATE,
+                              (WPARAM) (Parent ? Parent->hSelf : NULL),
+                              (LPARAM)MAKELONG(*HitTest, Msg->message)
+                             );
+
+   switch (Result)
+   {
+      case MA_NOACTIVATEANDEAT:
+         return TRUE;
+      case MA_NOACTIVATE:
+         break;
+      case MA_ACTIVATEANDEAT:
+         co_IntMouseActivateWindow(MsgWindow);
+         return TRUE;
+      default:
+         /* MA_ACTIVATE */
+         co_IntMouseActivateWindow(MsgWindow);
+         break;
+   }
+
+   return FALSE;
 }
 
-DWORD
-STDCALL
-NtUserMessageCall(
-  DWORD Unknown0,
-  DWORD Unknown1,
-  DWORD Unknown2,
-  DWORD Unknown3,
-  DWORD Unknown4,
-  DWORD Unknown5,
-  DWORD Unknown6)
+BOOL FASTCALL
+co_IntTranslateMouseMessage(PUSER_MESSAGE_QUEUE ThreadQueue, LPMSG Msg, USHORT *HitTest, BOOL Remove)
+{
+   PWINDOW_OBJECT Window;
+   USER_REFERENCE_ENTRY Ref, DesktopRef;
+
+   if(!(Window = UserGetWindowObject(Msg->hwnd)))
+   {
+      /* let's just eat the message?! */
+      return TRUE;
+   }
+
+   UserRefObjectCo(Window, &Ref);
+
+   if(ThreadQueue == Window->MessageQueue &&
+         ThreadQueue->CaptureWindow != Window->hSelf)
+   {
+      /* only send WM_NCHITTEST messages if we're not capturing the window! */
+      *HitTest = co_IntSendMessage(Window->hSelf, WM_NCHITTEST, 0,
+                                   MAKELONG(Msg->pt.x, Msg->pt.y));
+
+      if(*HitTest == (USHORT)HTTRANSPARENT)
+      {
+         PWINDOW_OBJECT DesktopWindow;
+         HWND hDesktop = IntGetDesktopWindow();
+
+         if((DesktopWindow = UserGetWindowObject(hDesktop)))
+         {
+            PWINDOW_OBJECT Wnd;
+            
+            UserRefObjectCo(DesktopWindow, &DesktopRef);
+            
+            co_WinPosWindowFromPoint(DesktopWindow, Window->MessageQueue, &Msg->pt, &Wnd);
+            if(Wnd)
+            {
+               if(Wnd != Window)
+               {
+                  /* post the message to the other window */
+                  Msg->hwnd = Wnd->hSelf;
+                  if(!(Wnd->Status & WINDOWSTATUS_DESTROYING))
+                  {
+                     MsqPostMessage(Wnd->MessageQueue, Msg, FALSE,
+                                    Msg->message == WM_MOUSEMOVE ? QS_MOUSEMOVE :
+                                    QS_MOUSEBUTTON);
+                  }
+
+                  /* eat the message */
+                  UserDerefObject(Wnd);
+                  UserDerefObjectCo(DesktopWindow);
+                  UserDerefObjectCo(Window);
+                  return TRUE;
+               }
+               UserDerefObject(Wnd);
+            }
+
+            UserDerefObjectCo(DesktopWindow);
+         }
+      }
+   }
+   else
+   {
+      *HitTest = HTCLIENT;
+   }
+
+   if(IS_BTN_MESSAGE(Msg->message, DOWN))
+   {
+      /* generate double click messages, if necessary */
+      if ((((*HitTest) != HTCLIENT) ||
+            (IntGetClassLong(Window, GCL_STYLE, FALSE) & CS_DBLCLKS)) &&
+            MsqIsDblClk(Msg, Remove))
+      {
+         Msg->message += WM_LBUTTONDBLCLK - WM_LBUTTONDOWN;
+      }
+   }
+
+   if(Msg->message != WM_MOUSEWHEEL)
+   {
+
+      if ((*HitTest) != HTCLIENT)
+      {
+         Msg->message += WM_NCMOUSEMOVE - WM_MOUSEMOVE;
+         if((Msg->message == WM_NCRBUTTONUP) &&
+               (((*HitTest) == HTCAPTION) || ((*HitTest) == HTSYSMENU)))
+         {
+            Msg->message = WM_CONTEXTMENU;
+            Msg->wParam = (WPARAM)Window->hSelf;
+         }
+         else
+         {
+            Msg->wParam = *HitTest;
+         }
+         Msg->lParam = MAKELONG(Msg->pt.x, Msg->pt.y);
+      }
+      else if(ThreadQueue->MoveSize == NULL &&
+              ThreadQueue->MenuOwner == NULL)
+      {
+         /* NOTE: Msg->pt should remain in screen coordinates. -- FiN */
+         Msg->lParam = MAKELONG(
+                          Msg->pt.x - (WORD)Window->ClientRect.left,
+                          Msg->pt.y - (WORD)Window->ClientRect.top);
+      }
+   }
+
+   UserDerefObjectCo(Window);
+   return FALSE;
+}
+
+
+/*
+ * Internal version of PeekMessage() doing all the work
+ */
+BOOL FASTCALL
+co_IntPeekMessage(PUSER_MESSAGE Msg,
+                  HWND hWnd,
+                  UINT MsgFilterMin,
+                  UINT MsgFilterMax,
+                  UINT RemoveMsg)
+{
+   LARGE_INTEGER LargeTickCount;
+   PUSER_MESSAGE_QUEUE ThreadQueue;
+   PUSER_MESSAGE Message;
+   BOOL Present, RemoveMessages;
+   USER_REFERENCE_ENTRY Ref;
+
+   /* The queues and order in which they are checked are documented in the MSDN
+      article on GetMessage() */
+
+   ThreadQueue = (PUSER_MESSAGE_QUEUE)PsGetWin32Thread()->MessageQueue;
+
+   /* Inspect RemoveMsg flags */
+   /* FIXME: The only flag we process is PM_REMOVE - processing of others must still be implemented */
+   RemoveMessages = RemoveMsg & PM_REMOVE;
+
+CheckMessages:
+
+   Present = FALSE;
+
+   KeQueryTickCount(&LargeTickCount);
+   ThreadQueue->LastMsgRead = LargeTickCount.u.LowPart;
+
+   /* Dispatch sent messages here. */
+   while (co_MsqDispatchOneSentMessage(ThreadQueue))
+      ;
+
+   /* Now look for a quit message. */
+
+   if (ThreadQueue->QuitPosted)
+   {
+      /* According to the PSDK, WM_QUIT messages are always returned, regardless
+         of the filter specified */
+      Msg->Msg.hwnd = NULL;
+      Msg->Msg.message = WM_QUIT;
+      Msg->Msg.wParam = ThreadQueue->QuitExitCode;
+      Msg->Msg.lParam = 0;
+      Msg->FreeLParam = FALSE;
+      if (RemoveMessages)
+      {
+         ThreadQueue->QuitPosted = FALSE;
+      }
+      return TRUE;
+   }
+
+   /* Now check for normal messages. */
+   Present = co_MsqFindMessage(ThreadQueue,
+                               FALSE,
+                               RemoveMessages,
+                               hWnd,
+                               MsgFilterMin,
+                               MsgFilterMax,
+                               &Message);
+   if (Present)
+   {
+      RtlCopyMemory(Msg, Message, sizeof(USER_MESSAGE));
+      if (RemoveMessages)
+      {
+         MsqDestroyMessage(Message);
+      }
+      goto MessageFound;
+   }
+
+   /* Check for hardware events. */
+   Present = co_MsqFindMessage(ThreadQueue,
+                               TRUE,
+                               RemoveMessages,
+                               hWnd,
+                               MsgFilterMin,
+                               MsgFilterMax,
+                               &Message);
+   if (Present)
+   {
+      RtlCopyMemory(Msg, Message, sizeof(USER_MESSAGE));
+      if (RemoveMessages)
+      {
+         MsqDestroyMessage(Message);
+      }
+      goto MessageFound;
+   }
+
+   /* Check for sent messages again. */
+   while (co_MsqDispatchOneSentMessage(ThreadQueue))
+      ;
+
+   /* Check for paint messages. */
+   if (IntGetPaintMessage(hWnd, MsgFilterMin, MsgFilterMax, PsGetWin32Thread(), &Msg->Msg, RemoveMessages))
+   {
+      Msg->FreeLParam = FALSE;
+      return TRUE;
+   }
+
+   /* Check for WM_(SYS)TIMER messages */
+   Present = MsqGetTimerMessage(ThreadQueue, hWnd, MsgFilterMin, MsgFilterMax,
+                                &Msg->Msg, RemoveMessages);
+   if (Present)
+   {
+      Msg->FreeLParam = FALSE;
+      goto MessageFound;
+   }
+
+   if(Present)
+   {
+MessageFound:
+
+      if(RemoveMessages)
+      {
+         PWINDOW_OBJECT MsgWindow = NULL;
+
+         if(Msg->Msg.hwnd && (MsgWindow = UserGetWindowObject(Msg->Msg.hwnd)) &&
+               Msg->Msg.message >= WM_MOUSEFIRST && Msg->Msg.message <= WM_MOUSELAST)
+         {
+            USHORT HitTest;
+
+            UserRefObjectCo(MsgWindow, &Ref);
+
+            if(co_IntTranslateMouseMessage(ThreadQueue, &Msg->Msg, &HitTest, TRUE))
+               /* FIXME - check message filter again, if the message doesn't match anymore,
+                          search again */
+            {
+               UserDerefObjectCo(MsgWindow);
+               /* eat the message, search again */
+               goto CheckMessages;
+            }
+
+            if(ThreadQueue->CaptureWindow == NULL)
+            {
+               co_IntSendHitTestMessages(ThreadQueue, &Msg->Msg);
+               if((Msg->Msg.message != WM_MOUSEMOVE && Msg->Msg.message != WM_NCMOUSEMOVE) &&
+                     IS_BTN_MESSAGE(Msg->Msg.message, DOWN) &&
+                     co_IntActivateWindowMouse(ThreadQueue, &Msg->Msg, MsgWindow, &HitTest))
+               {
+                  UserDerefObjectCo(MsgWindow);
+                  /* eat the message, search again */
+                  goto CheckMessages;
+               }
+            }
+            
+            UserDerefObjectCo(MsgWindow);
+         }
+         else
+         {
+            co_IntSendHitTestMessages(ThreadQueue, &Msg->Msg);
+         }
+
+//         if(MsgWindow)
+//         {
+//            UserDerefObject(MsgWindow);
+//         }
+
+         return TRUE;
+      }
+
+      USHORT HitTest;
+      if((Msg->Msg.hwnd && Msg->Msg.message >= WM_MOUSEFIRST && Msg->Msg.message <= WM_MOUSELAST) &&
+            co_IntTranslateMouseMessage(ThreadQueue, &Msg->Msg, &HitTest, FALSE))
+         /* FIXME - check message filter again, if the message doesn't match anymore,
+                    search again */
+      {
+         /* eat the message, search again */
+         goto CheckMessages;
+      }
+
+      return TRUE;
+   }
+
+   return Present;
+}
+
+BOOL STDCALL
+NtUserPeekMessage(PNTUSERGETMESSAGEINFO UnsafeInfo,
+                  HWND hWnd,
+                  UINT MsgFilterMin,
+                  UINT MsgFilterMax,
+                  UINT RemoveMsg)
 {
-  UNIMPLEMENTED
+   NTSTATUS Status;
+   BOOL Present;
+   NTUSERGETMESSAGEINFO Info;
+   PWINDOW_OBJECT Window;
+   PMSGMEMORY MsgMemoryEntry;
+   PVOID UserMem;
+   UINT Size;
+   USER_MESSAGE Msg;
+   DECLARE_RETURN(BOOL);
+
+   DPRINT("Enter NtUserPeekMessage\n");
+   UserEnterExclusive();
+
+   /* Validate input */
+   if (hWnd && hWnd != INVALID_HANDLE_VALUE)
+   {
+      if (!(Window = UserGetWindowObject(hWnd)))
+      {
+         RETURN(-1);
+      }
+   }
+
+   if (MsgFilterMax < MsgFilterMin)
+   {
+      MsgFilterMin = 0;
+      MsgFilterMax = 0;
+   }
+
+   Present = co_IntPeekMessage(&Msg, hWnd, MsgFilterMin, MsgFilterMax, RemoveMsg);
+   if (Present)
+   {
+      Info.Msg = Msg.Msg;
+      /* See if this message type is present in the table */
+      MsgMemoryEntry = FindMsgMemory(Info.Msg.message);
+      if (NULL == MsgMemoryEntry)
+      {
+         /* Not present, no copying needed */
+         Info.LParamSize = 0;
+      }
+      else
+      {
+         /* Determine required size */
+         Size = MsgMemorySize(MsgMemoryEntry, Info.Msg.wParam,
+                              Info.Msg.lParam);
+         /* Allocate required amount of user-mode memory */
+         Info.LParamSize = Size;
+         UserMem = NULL;
+         Status = ZwAllocateVirtualMemory(NtCurrentProcess(), &UserMem, 0,
+                                          &Info.LParamSize, MEM_COMMIT, PAGE_READWRITE);
+         if (! NT_SUCCESS(Status))
+         {
+            SetLastNtError(Status);
+            RETURN( (BOOL) -1);
+         }
+         /* Transfer lParam data to user-mode mem */
+         Status = MmCopyToCaller(UserMem, (PVOID) Info.Msg.lParam, Size);
+         if (! NT_SUCCESS(Status))
+         {
+            ZwFreeVirtualMemory(NtCurrentProcess(), (PVOID *) &UserMem,
+                                &Info.LParamSize, MEM_DECOMMIT);
+            SetLastNtError(Status);
+            RETURN( (BOOL) -1);
+         }
+         Info.Msg.lParam = (LPARAM) UserMem;
+      }
+      if (RemoveMsg && Msg.FreeLParam && 0 != Msg.Msg.lParam)
+      {
+         ExFreePool((void *) Msg.Msg.lParam);
+      }
+      Status = MmCopyToCaller(UnsafeInfo, &Info, sizeof(NTUSERGETMESSAGEINFO));
+      if (! NT_SUCCESS(Status))
+      {
+         SetLastNtError(Status);
+         RETURN( (BOOL) -1);
+      }
+   }
+
+   RETURN( Present);
+
+CLEANUP:
+   DPRINT("Leave NtUserPeekMessage, ret=%i\n",_ret_);
+   UserLeave();
+   END_CLEANUP;
+}
+
+static BOOL FASTCALL
+co_IntWaitMessage(HWND Wnd,
+                  UINT MsgFilterMin,
+                  UINT MsgFilterMax)
+{
+   PUSER_MESSAGE_QUEUE ThreadQueue;
+   NTSTATUS Status;
+   USER_MESSAGE Msg;
+
+   ThreadQueue = (PUSER_MESSAGE_QUEUE)PsGetWin32Thread()->MessageQueue;
+
+   do
+   {
+      if (co_IntPeekMessage(&Msg, Wnd, MsgFilterMin, MsgFilterMax, PM_NOREMOVE))
+      {
+         return TRUE;
+      }
+
+      /* Nothing found. Wait for new messages. */
+      Status = co_MsqWaitForNewMessages(ThreadQueue, Wnd, MsgFilterMin, MsgFilterMax);
+   }
+   while ((STATUS_WAIT_0 <= Status && Status <= STATUS_WAIT_63) || STATUS_TIMEOUT == Status);
+
+   SetLastNtError(Status);
 
-  return 0;
+   return FALSE;
 }
 
 BOOL STDCALL
-NtUserPeekMessage(LPMSG lpMsg,
-  HWND hWnd,
-  UINT wMsgFilterMin,
-  UINT wMsgFilterMax,
-  UINT wRemoveMsg)
+NtUserGetMessage(PNTUSERGETMESSAGEINFO UnsafeInfo,
+                 HWND hWnd,
+                 UINT MsgFilterMin,
+                 UINT MsgFilterMax)
 /*
  * FUNCTION: Get a message from the calling thread's message queue.
  * ARGUMENTS:
- *      lpMsg - Pointer to the structure which receives the returned message.
- *      hWnd - Window whose messages are to be retrieved.
- *      wMsgFilterMin - Integer value of the lowest message value to be
- *                      retrieved.
- *      wMsgFilterMax - Integer value of the highest message value to be
- *                      retrieved.
- *      wRemoveMsg - Specificies whether or not to remove messages from the queue after processing
+ *      UnsafeMsg - Pointer to the structure which receives the returned message.
+ *      Wnd - Window whose messages are to be retrieved.
+ *      MsgFilterMin - Integer value of the lowest message value to be
+ *                     retrieved.
+ *      MsgFilterMax - Integer value of the highest message value to be
+ *                     retrieved.
  */
 {
-  PUSER_MESSAGE_QUEUE ThreadQueue;
-  BOOLEAN Present;
-  PUSER_MESSAGE Message;
-  BOOLEAN RemoveMessages;
+   BOOL GotMessage;
+   NTUSERGETMESSAGEINFO Info;
+   NTSTATUS Status;
+   PWINDOW_OBJECT Window = NULL;
+   PMSGMEMORY MsgMemoryEntry;
+   PVOID UserMem;
+   UINT Size;
+   USER_MESSAGE Msg;
+   DECLARE_RETURN(BOOL);
+//   USER_REFERENCE_ENTRY Ref;
+
+   DPRINT("Enter NtUserGetMessage\n");
+   UserEnterExclusive();
+
+   /* Validate input */
+   if (hWnd && !(Window = UserGetWindowObject(hWnd)))
+   {
+      RETURN(-1);
+   }
+   
+//   if (Window) UserRefObjectCo(Window, &Ref);
+   
+   if (MsgFilterMax < MsgFilterMin)
+   {
+      MsgFilterMin = 0;
+      MsgFilterMax = 0;
+   }
+
+   do
+   {
+      GotMessage = co_IntPeekMessage(&Msg, hWnd, MsgFilterMin, MsgFilterMax, PM_REMOVE);
+      if (GotMessage)
+      {
+         Info.Msg = Msg.Msg;
+         /* See if this message type is present in the table */
+         MsgMemoryEntry = FindMsgMemory(Info.Msg.message);
+         if (NULL == MsgMemoryEntry)
+         {
+            /* Not present, no copying needed */
+            Info.LParamSize = 0;
+         }
+         else
+         {
+            /* Determine required size */
+            Size = MsgMemorySize(MsgMemoryEntry, Info.Msg.wParam,
+                                 Info.Msg.lParam);
+            /* Allocate required amount of user-mode memory */
+            Info.LParamSize = Size;
+            UserMem = NULL;
+            Status = ZwAllocateVirtualMemory(NtCurrentProcess(), &UserMem, 0,
+                                             &Info.LParamSize, MEM_COMMIT, PAGE_READWRITE);
+
+            if (! NT_SUCCESS(Status))
+            {
+               SetLastNtError(Status);
+               RETURN( (BOOL) -1);
+            }
+            /* Transfer lParam data to user-mode mem */
+            Status = MmCopyToCaller(UserMem, (PVOID) Info.Msg.lParam, Size);
+            if (! NT_SUCCESS(Status))
+            {
+               ZwFreeVirtualMemory(NtCurrentProcess(), (PVOID *) &UserMem,
+                                   &Info.LParamSize, MEM_DECOMMIT);
+               SetLastNtError(Status);
+               RETURN( (BOOL) -1);
+            }
+            Info.Msg.lParam = (LPARAM) UserMem;
+         }
+         if (Msg.FreeLParam && 0 != Msg.Msg.lParam)
+         {
+            ExFreePool((void *) Msg.Msg.lParam);
+         }
+         Status = MmCopyToCaller(UnsafeInfo, &Info, sizeof(NTUSERGETMESSAGEINFO));
+         if (! NT_SUCCESS(Status))
+         {
+            SetLastNtError(Status);
+            RETURN( (BOOL) -1);
+         }
+      }
+      else if (! co_IntWaitMessage(hWnd, MsgFilterMin, MsgFilterMax))
+      {
+         RETURN( (BOOL) -1);
+      }
+   }
+   while (! GotMessage);
+
+   RETURN( WM_QUIT != Info.Msg.message);
+
+CLEANUP:
+//   if (Window) UserDerefObjectCo(Window);
+
+   DPRINT("Leave NtUserGetMessage\n");
+   UserLeave();
+   END_CLEANUP;
+}
+
+DWORD
+STDCALL
+NtUserMessageCall(
+   DWORD Unknown0,
+   DWORD Unknown1,
+   DWORD Unknown2,
+   DWORD Unknown3,
+   DWORD Unknown4,
+   DWORD Unknown5,
+   DWORD Unknown6)
+{
+   UNIMPLEMENTED
 
-  /* Initialize the thread's win32 state if necessary. */ 
-  W32kGuiCheck();
+   return 0;
+}
 
-  ThreadQueue = (PUSER_MESSAGE_QUEUE)PsGetWin32Thread()->MessageQueue;
+static NTSTATUS FASTCALL
+CopyMsgToKernelMem(MSG *KernelModeMsg, MSG *UserModeMsg, PMSGMEMORY MsgMemoryEntry)
+{
+   NTSTATUS Status;
+
+   PVOID KernelMem;
+   UINT Size;
+
+   *KernelModeMsg = *UserModeMsg;
+
+   /* See if this message type is present in the table */
+   if (NULL == MsgMemoryEntry)
+   {
+      /* Not present, no copying needed */
+      return STATUS_SUCCESS;
+   }
+
+   /* Determine required size */
+   Size = MsgMemorySize(MsgMemoryEntry, UserModeMsg->wParam, UserModeMsg->lParam);
+
+   if (0 != Size)
+   {
+      /* Allocate kernel mem */
+      KernelMem = ExAllocatePoolWithTag(PagedPool, Size, TAG_MSG);
+      if (NULL == KernelMem)
+      {
+         DPRINT1("Not enough memory to copy message to kernel mem\n");
+         return STATUS_NO_MEMORY;
+      }
+      KernelModeMsg->lParam = (LPARAM) KernelMem;
+
+      /* Copy data if required */
+      if (0 != (MsgMemoryEntry->Flags & MMS_FLAG_READ))
+      {
+         Status = MmCopyFromCaller(KernelMem, (PVOID) UserModeMsg->lParam, Size);
+         if (! NT_SUCCESS(Status))
+         {
+            DPRINT1("Failed to copy message to kernel: invalid usermode buffer\n");
+            ExFreePool(KernelMem);
+            return Status;
+         }
+      }
+      else
+      {
+         /* Make sure we don't pass any secrets to usermode */
+         RtlZeroMemory(KernelMem, Size);
+      }
+   }
+   else
+   {
+      KernelModeMsg->lParam = 0;
+   }
+
+   return STATUS_SUCCESS;
+}
 
-  /* Inspect wRemoveMsg flags */
-  /* FIXME: The only flag we process is PM_REMOVE - processing of others must still be implemented */
-  RemoveMessages = wRemoveMsg & PM_REMOVE;
+static NTSTATUS FASTCALL
+CopyMsgToUserMem(MSG *UserModeMsg, MSG *KernelModeMsg)
+{
+   NTSTATUS Status;
+   PMSGMEMORY MsgMemoryEntry;
+   UINT Size;
+
+   /* See if this message type is present in the table */
+   MsgMemoryEntry = FindMsgMemory(UserModeMsg->message);
+   if (NULL == MsgMemoryEntry)
+   {
+      /* Not present, no copying needed */
+      return STATUS_SUCCESS;
+   }
+
+   /* Determine required size */
+   Size = MsgMemorySize(MsgMemoryEntry, UserModeMsg->wParam, UserModeMsg->lParam);
+
+   if (0 != Size)
+   {
+      /* Copy data if required */
+      if (0 != (MsgMemoryEntry->Flags & MMS_FLAG_WRITE))
+      {
+         Status = MmCopyToCaller((PVOID) UserModeMsg->lParam, (PVOID) KernelModeMsg->lParam, Size);
+         if (! NT_SUCCESS(Status))
+         {
+            DPRINT1("Failed to copy message from kernel: invalid usermode buffer\n");
+            ExFreePool((PVOID) KernelModeMsg->lParam);
+            return Status;
+         }
+      }
+
+      ExFreePool((PVOID) KernelModeMsg->lParam);
+   }
+
+   return STATUS_SUCCESS;
+}
 
-  /* Dispatch sent messages here. */
-  while (MsqDispatchOneSentMessage(ThreadQueue));
+BOOL FASTCALL
+UserPostMessage(HWND Wnd,
+                UINT Msg,
+                WPARAM wParam,
+                LPARAM lParam)
+{
+   MSG UserModeMsg, KernelModeMsg;
+   LARGE_INTEGER LargeTickCount;
+   NTSTATUS Status;
+   PMSGMEMORY MsgMemoryEntry;
+
+   if (WM_QUIT == Msg)
+   {
+      MsqPostQuitMessage(PsGetWin32Thread()->MessageQueue, wParam);
+   }
+   else if (Wnd == HWND_BROADCAST)
+   {
+      HWND *List;
+      PWINDOW_OBJECT DesktopWindow;
+      ULONG i;
+
+      DesktopWindow = UserGetWindowObject(IntGetDesktopWindow());
+      List = IntWinListChildren(DesktopWindow);
+      
+      if (List != NULL)
+      {
+         for (i = 0; List[i]; i++)
+            UserPostMessage(List[i], Msg, wParam, lParam);
+         ExFreePool(List);
+      }
+   }
+   else
+   {
+      PWINDOW_OBJECT Window;
       
-  /* Now look for a quit message. */
-  /* FIXME: WINE checks the message number filter here. */
-  if (ThreadQueue->QuitPosted)
-  {
-         lpMsg->hwnd = hWnd;
-         lpMsg->message = WM_QUIT;
-         lpMsg->wParam = ThreadQueue->QuitExitCode;
-         lpMsg->lParam = 0;
-         ThreadQueue->QuitPosted = FALSE;
-         return(FALSE);
-  }
-
-  /* Now check for normal messages. */
-  Present = MsqFindMessage(ThreadQueue,
-       FALSE,
-       RemoveMessages,
-       hWnd,
-       wMsgFilterMin,
-       wMsgFilterMax,
-       &Message);
-  if (Present)
-  {
-         RtlCopyMemory(lpMsg, &Message->Msg, sizeof(MSG));
-         ExFreePool(Message);
-         return(TRUE);
-  }
-
-  /* Check for hardware events. */
-  Present = MsqFindMessage(ThreadQueue,
-       TRUE,
-       RemoveMessages,
-       hWnd,
-       wMsgFilterMin,
-       wMsgFilterMax,
-       &Message);
-  if (Present)
-  {
-         RtlCopyMemory(lpMsg, &Message->Msg, sizeof(MSG));
-         ExFreePool(Message);
-         return(TRUE);
-  }
-
-  /* Check for sent messages again. */
-  while (MsqDispatchOneSentMessage(ThreadQueue));
-
-  /* Check for paint messages. */
-
-  /* Check for paint messages. */
-  if (ThreadQueue->PaintPosted)
-    {
-      PWINDOW_OBJECT WindowObject;
-
-      lpMsg->hwnd = PaintingFindWinToRepaint(hWnd, PsGetWin32Thread());
-      lpMsg->message = WM_PAINT;
-      lpMsg->wParam = lpMsg->lParam = 0;
-
-      WindowObject = W32kGetWindowObject(lpMsg->hwnd);
-      if (WindowObject != NULL)
-       {
-         if (WindowObject->Style & WS_MINIMIZE &&
-             (HICON)NtUserGetClassLong(lpMsg->hwnd, GCL_HICON) != NULL)
-           {
-             lpMsg->message = WM_PAINTICON;
-             lpMsg->wParam = 1;
-           }
-
-         if (lpMsg->hwnd == NULL || lpMsg->hwnd == hWnd ||
-             W32kIsChildWindow(hWnd, lpMsg->hwnd))
-           {
-             if (WindowObject->Flags & WINDOWOBJECT_NEED_INTERNALPAINT &&
-                 WindowObject->UpdateRegion == NULL)
-               {
-                 WindowObject->Flags &= ~WINDOWOBJECT_NEED_INTERNALPAINT;
-                 MsqDecPaintCountQueue(WindowObject->MessageQueue);
-               }
-           }
-         W32kReleaseWindowObject(WindowObject);
-       }
-
-      return(TRUE); 
-    }
-
-  return FALSE;
+      Window = UserGetWindowObject(Wnd);
+      if (NULL == Window)
+      {
+         return FALSE;
+      }
+      if(Window->Status & WINDOWSTATUS_DESTROYING)
+      {
+         DPRINT1("Attempted to post message to window 0x%x that is being destroyed!\n", Wnd);
+         /* FIXME - last error code? */
+         return FALSE;
+      }
+
+      UserModeMsg.hwnd = Wnd;
+      UserModeMsg.message = Msg;
+      UserModeMsg.wParam = wParam;
+      UserModeMsg.lParam = lParam;
+      MsgMemoryEntry = FindMsgMemory(UserModeMsg.message);
+      Status = CopyMsgToKernelMem(&KernelModeMsg, &UserModeMsg, MsgMemoryEntry);
+      if (! NT_SUCCESS(Status))
+      {
+         SetLastWin32Error(ERROR_INVALID_PARAMETER);
+         return FALSE;
+      }
+      IntGetCursorLocation(PsGetWin32Thread()->Desktop->WindowStation,
+                           &KernelModeMsg.pt);
+      KeQueryTickCount(&LargeTickCount);
+      KernelModeMsg.time = LargeTickCount.u.LowPart;
+      MsqPostMessage(Window->MessageQueue, &KernelModeMsg,
+                     NULL != MsgMemoryEntry && 0 != KernelModeMsg.lParam,
+                     QS_POSTMESSAGE);
+   }
+
+   return TRUE;
 }
 
+
 BOOL STDCALL
 NtUserPostMessage(HWND hWnd,
-                 UINT Msg,
-                 WPARAM wParam,
-                 LPARAM lParam)
+                  UINT Msg,
+                  WPARAM wParam,
+                  LPARAM lParam)
 {
-  PUSER_MESSAGE_QUEUE ThreadQueue;
-
-  switch (Msg)
-  {
-    case WM_NULL:
-      break;
+   DECLARE_RETURN(BOOL);
 
-    case WM_QUIT:
-      ThreadQueue = (PUSER_MESSAGE_QUEUE)PsGetWin32Thread()->MessageQueue;
-      ThreadQueue->QuitPosted = TRUE;
-      ThreadQueue->QuitExitCode = wParam;
-      break;
+   DPRINT("Enter NtUserPostMessage\n");
+   UserEnterExclusive();
 
-    default:
-      DPRINT1("Unhandled message: %u\n", Msg);
-      return FALSE;
-  }
+   RETURN(UserPostMessage(hWnd, Msg, wParam, lParam));
 
-  return TRUE;
+CLEANUP:
+   DPRINT("Leave NtUserPostMessage, ret=%i\n",_ret_);
+   UserLeave();
+   END_CLEANUP;
 }
 
+
+
 BOOL STDCALL
 NtUserPostThreadMessage(DWORD idThread,
-                       UINT Msg,
-                       WPARAM wParam,
-                       LPARAM lParam)
+                        UINT Msg,
+                        WPARAM wParam,
+                        LPARAM lParam)
 {
-  UNIMPLEMENTED;
-
-  return 0;
+   MSG UserModeMsg, KernelModeMsg;
+   PETHREAD peThread;
+   PW32THREAD pThread;
+   NTSTATUS Status;
+   PMSGMEMORY MsgMemoryEntry;
+   DECLARE_RETURN(BOOL);
+
+   DPRINT("Enter NtUserPostThreadMessage\n");
+   UserEnterExclusive();
+
+   Status = PsLookupThreadByThreadId((HANDLE)idThread,&peThread);
+
+   if( Status == STATUS_SUCCESS )
+   {
+      pThread = (PW32THREAD)peThread->Tcb.Win32Thread;
+      if( !pThread || !pThread->MessageQueue )
+      {
+         ObDereferenceObject( peThread );
+         RETURN( FALSE);
+      }
+
+      UserModeMsg.hwnd = NULL;
+      UserModeMsg.message = Msg;
+      UserModeMsg.wParam = wParam;
+      UserModeMsg.lParam = lParam;
+      MsgMemoryEntry = FindMsgMemory(UserModeMsg.message);
+      Status = CopyMsgToKernelMem(&KernelModeMsg, &UserModeMsg, MsgMemoryEntry);
+      if (! NT_SUCCESS(Status))
+      {
+         ObDereferenceObject( peThread );
+         SetLastWin32Error(ERROR_INVALID_PARAMETER);
+         RETURN( FALSE);
+      }
+      MsqPostMessage(pThread->MessageQueue, &KernelModeMsg,
+                     NULL != MsgMemoryEntry && 0 != KernelModeMsg.lParam,
+                     QS_POSTMESSAGE);
+      ObDereferenceObject( peThread );
+      RETURN( TRUE);
+   }
+   else
+   {
+      SetLastNtError( Status );
+      RETURN( FALSE);
+   }
+
+CLEANUP:
+   DPRINT("Leave NtUserPostThreadMessage, ret=%i\n",_ret_);
+   UserLeave();
+   END_CLEANUP;
 }
 
 DWORD STDCALL
 NtUserQuerySendMessage(DWORD Unknown0)
 {
-  UNIMPLEMENTED;
+   UNIMPLEMENTED;
 
-  return 0;
+   return 0;
 }
 
-LRESULT STDCALL
-W32kSendMessage(HWND hWnd,
-               UINT Msg,
-               WPARAM wParam,
-               LPARAM lParam,
-               BOOL KernelMessage)
-{
-  LRESULT Result;
-  NTSTATUS Status;
-  PWINDOW_OBJECT Window;
-
-  /* FIXME: Check for a broadcast or topmost destination. */
-
-  /* FIXME: Call hooks. */
-
-  Status = 
-    ObmReferenceObjectByHandle(PsGetWin32Process()->WindowStation->HandleTable,
-                              hWnd,
-                              otWindow,
-                              (PVOID*)&Window);
-  if (!NT_SUCCESS(Status))
-    {
-      return(FALSE);
-    }
-
-  /* FIXME: Check for an exiting window. */
-
-  if (NULL != PsGetWin32Thread() &&
-      Window->MessageQueue == PsGetWin32Thread()->MessageQueue)
-    {
-      if (KernelMessage)
-       {
-         Result = W32kCallTrampolineWindowProc(NULL, hWnd, Msg, wParam,
-                                               lParam);
-         return(Result);
-       }
+LRESULT FASTCALL
+co_IntSendMessage(HWND hWnd,
+                  UINT Msg,
+                  WPARAM wParam,
+                  LPARAM lParam)
+{
+   ULONG_PTR Result = 0;
+   if(co_IntSendMessageTimeout(hWnd, Msg, wParam, lParam, SMTO_NORMAL, 0, &Result))
+   {
+      return (LRESULT)Result;
+   }
+   return 0;
+}
+
+static 
+LRESULT FASTCALL
+co_IntSendMessageTimeoutSingle(HWND hWnd,
+                               UINT Msg,
+                               WPARAM wParam,
+                               LPARAM lParam,
+                               UINT uFlags,
+                               UINT uTimeout,
+                               ULONG_PTR *uResult)
+{
+   ULONG_PTR Result;
+   NTSTATUS Status;
+   PWINDOW_OBJECT Window = NULL;
+   PMSGMEMORY MsgMemoryEntry;
+   INT lParamBufferSize;
+   LPARAM lParamPacked;
+   PW32THREAD Win32Thread;
+   DECLARE_RETURN(LRESULT);
+   USER_REFERENCE_ENTRY Ref;
+
+   /* FIXME: Call hooks. */
+   if (!(Window = UserGetWindowObject(hWnd)))
+   {
+       RETURN( FALSE);
+   }
+   
+   UserRefObjectCo(Window, &Ref);
+
+   Win32Thread = PsGetWin32Thread();
+
+   if (NULL != Win32Thread &&
+         Window->MessageQueue == Win32Thread->MessageQueue)
+   {
+      if (Win32Thread->IsExiting)
+      {
+         /* Never send messages to exiting threads */
+          RETURN( FALSE);
+      }
+
+      /* See if this message type is present in the table */
+      MsgMemoryEntry = FindMsgMemory(Msg);
+      if (NULL == MsgMemoryEntry)
+      {
+         lParamBufferSize = -1;
+      }
+      else
+      {
+         lParamBufferSize = MsgMemorySize(MsgMemoryEntry, wParam, lParam);
+      }
+
+      if (! NT_SUCCESS(PackParam(&lParamPacked, Msg, wParam, lParam)))
+      {
+         DPRINT1("Failed to pack message parameters\n");
+          RETURN( FALSE);
+      }
+      if (0xFFFF0000 != ((DWORD) Window->WndProcW & 0xFFFF0000))
+      {
+         Result = (ULONG_PTR)co_IntCallWindowProc(Window->WndProcW, FALSE, hWnd, Msg, wParam,
+                  lParamPacked,lParamBufferSize);
+      }
+      else
+      {
+         Result = (ULONG_PTR)co_IntCallWindowProc(Window->WndProcA, TRUE, hWnd, Msg, wParam,
+                  lParamPacked,lParamBufferSize);
+      }
+
+      if(uResult)
+      {
+         *uResult = Result;
+      }
+
+      if (! NT_SUCCESS(UnpackParam(lParamPacked, Msg, wParam, lParam)))
+      {
+         DPRINT1("Failed to unpack message parameters\n");
+          RETURN( TRUE);
+      }
+
+       RETURN( TRUE);
+   }
+
+   if(uFlags & SMTO_ABORTIFHUNG && MsqIsHung(Window->MessageQueue))
+   {
+      /* FIXME - Set a LastError? */
+       RETURN( FALSE);
+   }
+
+   if(Window->Status & WINDOWSTATUS_DESTROYING)
+   {
+      /* FIXME - last error? */
+      DPRINT1("Attempted to send message to window 0x%x that is being destroyed!\n", hWnd);
+       RETURN( FALSE);
+   }
+
+   Status = co_MsqSendMessage(Window->MessageQueue, hWnd, Msg, wParam, lParam,
+                              uTimeout, (uFlags & SMTO_BLOCK), FALSE, uResult);
+
+
+   if (STATUS_TIMEOUT == Status)
+   {
+      /* MSDN says GetLastError() should return 0 after timeout */
+      SetLastWin32Error(0);
+       RETURN( FALSE);
+   }
+   else if (! NT_SUCCESS(Status))
+   {
+      SetLastNtError(Status);
+       RETURN( FALSE);
+   }
+
+   RETURN( TRUE);
+   
+CLEANUP:
+   if (Window) UserDerefObjectCo(Window);
+   END_CLEANUP;
+}
+
+LRESULT FASTCALL
+co_IntSendMessageTimeout(HWND hWnd,
+                         UINT Msg,
+                         WPARAM wParam,
+                         LPARAM lParam,
+                         UINT uFlags,
+                         UINT uTimeout,
+                         ULONG_PTR *uResult)
+{
+   PWINDOW_OBJECT DesktopWindow;
+   HWND *Children;
+   HWND *Child;
+
+   if (HWND_BROADCAST != hWnd)
+   {
+      return co_IntSendMessageTimeoutSingle(hWnd, Msg, wParam, lParam, uFlags, uTimeout, uResult);
+   }
+
+   DesktopWindow = UserGetWindowObject(IntGetDesktopWindow());
+   if (NULL == DesktopWindow)
+   {
+      SetLastWin32Error(ERROR_INTERNAL_ERROR);
+      return 0;
+   }
+
+   Children = IntWinListChildren(DesktopWindow);
+   if (NULL == Children)
+   {
+      return 0;
+   }
+
+   for (Child = Children; NULL != *Child; Child++)
+   {
+      co_IntSendMessageTimeoutSingle(*Child, Msg, wParam, lParam, uFlags, uTimeout, uResult);
+   }
+
+   ExFreePool(Children);
+
+   return (LRESULT) TRUE;
+}
+
+
+/* This function posts a message if the destination's message queue belongs to
+   another thread, otherwise it sends the message. It does not support broadcast
+   messages! */
+LRESULT FASTCALL
+co_IntPostOrSendMessage(HWND hWnd,
+                        UINT Msg,
+                        WPARAM wParam,
+                        LPARAM lParam)
+{
+   ULONG_PTR Result;
+   PWINDOW_OBJECT Window;
+
+   if(hWnd == HWND_BROADCAST)
+   {
+      return 0;
+   }
+
+   if(!(Window = UserGetWindowObject(hWnd)))
+   {
+      return 0;
+   }
+
+   if(Window->MessageQueue != PsGetWin32Thread()->MessageQueue)
+   {
+      Result = UserPostMessage(hWnd, Msg, wParam, lParam);
+   }
+   else
+   {
+      if(!co_IntSendMessageTimeoutSingle(hWnd, Msg, wParam, lParam, SMTO_NORMAL, 0, &Result))
+      {
+         Result = 0;
+      }
+   }
+
+   return (LRESULT)Result;
+}
+
+LRESULT FASTCALL
+co_IntDoSendMessage(HWND hWnd,
+                    UINT Msg,
+                    WPARAM wParam,
+                    LPARAM lParam,
+                    PDOSENDMESSAGE dsm,
+                    PNTUSERSENDMESSAGEINFO UnsafeInfo)
+{
+   LRESULT Result = TRUE;
+   NTSTATUS Status;
+   PWINDOW_OBJECT Window;
+   NTUSERSENDMESSAGEINFO Info;
+   MSG UserModeMsg;
+   MSG KernelModeMsg;
+   PMSGMEMORY MsgMemoryEntry;
+
+   RtlZeroMemory(&Info, sizeof(NTUSERSENDMESSAGEINFO));
+
+   /* FIXME: Call hooks. */
+   if (HWND_BROADCAST != hWnd)
+   {
+      Window = UserGetWindowObject(hWnd);
+      if (NULL == Window)
+      {
+         /* Tell usermode to not touch this one */
+         Info.HandledByKernel = TRUE;
+         MmCopyToCaller(UnsafeInfo, &Info, sizeof(NTUSERSENDMESSAGEINFO));
+         return 0;
+      }
+   }
+
+   /* FIXME: Check for an exiting window. */
+
+   /* See if the current thread can handle the message */
+   if (HWND_BROADCAST != hWnd && NULL != PsGetWin32Thread() &&
+         Window->MessageQueue == PsGetWin32Thread()->MessageQueue)
+   {
+      /* Gather the information usermode needs to call the window proc directly */
+      Info.HandledByKernel = FALSE;
+      if (0xFFFF0000 != ((DWORD) Window->WndProcW & 0xFFFF0000))
+      {
+         if (0xFFFF0000 != ((DWORD) Window->WndProcA & 0xFFFF0000))
+         {
+            /* Both Unicode and Ansi winprocs are real, see what usermode prefers */
+            Status = MmCopyFromCaller(&(Info.Ansi), &(UnsafeInfo->Ansi),
+                                      sizeof(BOOL));
+            if (! NT_SUCCESS(Status))
+            {
+               Info.Ansi = ! Window->Unicode;
+            }
+            Info.Proc = (Info.Ansi ? Window->WndProcA : Window->WndProcW);
+         }
+         else
+         {
+            /* Real Unicode winproc */
+            Info.Ansi = FALSE;
+            Info.Proc = Window->WndProcW;
+         }
+      }
       else
-       {
-         Result = W32kCallWindowProc(NULL, hWnd, Msg, wParam, lParam);
-         return(Result);
-       }
-    }
-  else
-    {
-      PUSER_SENT_MESSAGE Message;
-      PKEVENT CompletionEvent;
-
-      CompletionEvent = ExAllocatePool(NonPagedPool, sizeof(KEVENT));
-      KeInitializeEvent(CompletionEvent, NotificationEvent, FALSE);
-
-      Message = ExAllocatePool(NonPagedPool, sizeof(USER_SENT_MESSAGE));
-      Message->Msg.hwnd = hWnd;
-      Message->Msg.message = Msg;
-      Message->Msg.wParam = wParam;
-      Message->Msg.lParam = lParam;
-      Message->CompletionEvent = CompletionEvent;
-      Message->Result = &Result;
-      Message->CompletionQueue = NULL;
-      Message->CompletionCallback = NULL;
-      MsqSendMessage(Window->MessageQueue, Message);
-
-      ObmDereferenceObject(Window);
-      Status = KeWaitForSingleObject(CompletionEvent,
-                                    UserRequest,
-                                    UserMode,
-                                    FALSE,
-                                    NULL);
-      if (Status == STATUS_WAIT_0)
-       {
-         return(Result);
-       }
+      {
+         /* Must have real Ansi winproc */
+         Info.Ansi = TRUE;
+         Info.Proc = Window->WndProcA;
+      }
+   }
+   else
+   {
+      /* Must be handled by other thread */
+//      if (HWND_BROADCAST != hWnd)
+//      {
+//         UserDerefObject(Window);
+//      }
+      Info.HandledByKernel = TRUE;
+      UserModeMsg.hwnd = hWnd;
+      UserModeMsg.message = Msg;
+      UserModeMsg.wParam = wParam;
+      UserModeMsg.lParam = lParam;
+      MsgMemoryEntry = FindMsgMemory(UserModeMsg.message);
+      Status = CopyMsgToKernelMem(&KernelModeMsg, &UserModeMsg, MsgMemoryEntry);
+      if (! NT_SUCCESS(Status))
+      {
+         MmCopyToCaller(UnsafeInfo, &Info, sizeof(NTUSERSENDMESSAGEINFO));
+         SetLastWin32Error(ERROR_INVALID_PARAMETER);
+         return (dsm ? 0 : -1);
+      }
+      if(!dsm)
+      {
+         Result = co_IntSendMessage(KernelModeMsg.hwnd, KernelModeMsg.message,
+                                    KernelModeMsg.wParam, KernelModeMsg.lParam);
+      }
       else
-       {
-         return(FALSE);
-       }
-    }
+      {
+         Result = co_IntSendMessageTimeout(KernelModeMsg.hwnd, KernelModeMsg.message,
+                                           KernelModeMsg.wParam, KernelModeMsg.lParam,
+                                           dsm->uFlags, dsm->uTimeout, &dsm->Result);
+      }
+      Status = CopyMsgToUserMem(&UserModeMsg, &KernelModeMsg);
+      if (! NT_SUCCESS(Status))
+      {
+         MmCopyToCaller(UnsafeInfo, &Info, sizeof(NTUSERSENDMESSAGEINFO));
+         SetLastWin32Error(ERROR_INVALID_PARAMETER);
+         return(dsm ? 0 : -1);
+      }
+   }
+
+   Status = MmCopyToCaller(UnsafeInfo, &Info, sizeof(NTUSERSENDMESSAGEINFO));
+   if (! NT_SUCCESS(Status))
+   {
+      SetLastWin32Error(ERROR_INVALID_PARAMETER);
+   }
+
+   return (LRESULT)Result;
+}
+
+LRESULT STDCALL
+NtUserSendMessageTimeout(HWND hWnd,
+                         UINT Msg,
+                         WPARAM wParam,
+                         LPARAM lParam,
+                         UINT uFlags,
+                         UINT uTimeout,
+                         ULONG_PTR *uResult,
+                         PNTUSERSENDMESSAGEINFO UnsafeInfo)
+{
+   DOSENDMESSAGE dsm;
+   LRESULT Result;
+   DECLARE_RETURN(BOOL);
+
+   DPRINT("Enter NtUserSendMessageTimeout\n");
+   UserEnterExclusive();
+
+   dsm.uFlags = uFlags;
+   dsm.uTimeout = uTimeout;
+   Result = co_IntDoSendMessage(hWnd, Msg, wParam, lParam, &dsm, UnsafeInfo);
+   if(uResult != NULL && Result != 0)
+   {
+      NTSTATUS Status;
+
+      Status = MmCopyToCaller(uResult, &dsm.Result, sizeof(ULONG_PTR));
+      if(!NT_SUCCESS(Status))
+      {
+         SetLastWin32Error(ERROR_INVALID_PARAMETER);
+         RETURN( FALSE);
+      }
+   }
+   RETURN( Result);
+
+CLEANUP:
+   DPRINT("Leave NtUserSendMessageTimeout, ret=%i\n",_ret_);
+   UserLeave();
+   END_CLEANUP;
 }
 
 LRESULT STDCALL
-NtUserSendMessage(HWND hWnd,
-                 UINT Msg,
-                 WPARAM wParam,
-                 LPARAM lParam)
+NtUserSendMessage(HWND Wnd,
+                  UINT Msg,
+                  WPARAM wParam,
+                  LPARAM lParam,
+                  PNTUSERSENDMESSAGEINFO UnsafeInfo)
 {
-  return(W32kSendMessage(hWnd, Msg, wParam, lParam, FALSE));
+   DECLARE_RETURN(BOOL);
+
+   DPRINT("Enter NtUserSendMessage\n");
+   UserEnterExclusive();
+
+   RETURN(co_IntDoSendMessage(Wnd, Msg, wParam, lParam, NULL, UnsafeInfo));
+
+CLEANUP:
+   DPRINT("Leave NtUserSendMessage, ret=%i\n",_ret_);
+   UserLeave();
+   END_CLEANUP;
 }
 
 BOOL STDCALL
 NtUserSendMessageCallback(HWND hWnd,
-                         UINT Msg,
-                         WPARAM wParam,
-                         LPARAM lParam,
-                         SENDASYNCPROC lpCallBack,
-                         ULONG_PTR dwData)
+                          UINT Msg,
+                          WPARAM wParam,
+                          LPARAM lParam,
+                          SENDASYNCPROC lpCallBack,
+                          ULONG_PTR dwData)
 {
-  UNIMPLEMENTED;
+   UNIMPLEMENTED;
 
-  return(0);
+   return 0;
 }
 
 BOOL STDCALL
 NtUserSendNotifyMessage(HWND hWnd,
-                       UINT Msg,
-                       WPARAM wParam,
-                       LPARAM lParam)
+                        UINT Msg,
+                        WPARAM wParam,
+                        LPARAM lParam)
 {
-  UNIMPLEMENTED;
+   UNIMPLEMENTED;
 
-  return 0;
+   return 0;
 }
 
 BOOL STDCALL
 NtUserWaitMessage(VOID)
 {
-  UNIMPLEMENTED;
+   DECLARE_RETURN(BOOL);
+
+   DPRINT("EnterNtUserWaitMessage\n");
+   UserEnterExclusive();
+
+   RETURN(co_IntWaitMessage(NULL, 0, 0));
+
+CLEANUP:
+   DPRINT("Leave NtUserWaitMessage, ret=%i\n",_ret_);
+   UserLeave();
+   END_CLEANUP;
+}
+
+DWORD STDCALL
+NtUserGetQueueStatus(BOOL ClearChanges)
+{
+   PUSER_MESSAGE_QUEUE Queue;
+   DWORD Result;
+   DECLARE_RETURN(DWORD);
+
+   DPRINT("Enter NtUserGetQueueStatus\n");
+   UserEnterExclusive();
+
+   Queue = PsGetWin32Thread()->MessageQueue;
+
+   Result = MAKELONG(Queue->QueueBits, Queue->ChangedBits);
+   if (ClearChanges)
+   {
+      Queue->ChangedBits = 0;
+   }
+
+   RETURN( Result);
+
+CLEANUP:
+   DPRINT("Leave NtUserGetQueueStatus, ret=%i\n",_ret_);
+   UserLeave();
+   END_CLEANUP;
+}
+
+BOOL STDCALL
+IntInitMessagePumpHook()
+{
+   ((PW32THREAD)PsGetCurrentThread()->Tcb.Win32Thread)->MessagePumpHookValue++;
+   return TRUE;
+}
 
-  return 0;
+BOOL STDCALL
+IntUninitMessagePumpHook()
+{
+   if (((PW32THREAD)PsGetCurrentThread()->Tcb.Win32Thread)->MessagePumpHookValue <= 0)
+   {
+      return FALSE;
+   }
+   ((PW32THREAD)PsGetCurrentThread()->Tcb.Win32Thread)->MessagePumpHookValue--;
+   return TRUE;
 }
 
 /* EOF */