Fix the USER32 DLL initialization and cleanup routines to prevent memory/resource...
[reactos.git] / reactos / lib / user32 / windows / message.c
index 4b8981b..3169308 100644 (file)
@@ -1,5 +1,4 @@
-/* $Id: message.c,v 1.30 2003/12/14 14:01:38 gvg Exp $
- *
+/*
  * COPYRIGHT:       See COPYING in the top level directory
  * PROJECT:         ReactOS user32.dll
  * FILE:            lib/user32/windows/message.c
  * UPDATE HISTORY:
  *      06-06-2001  CSH  Created
  */
-#include <windows.h>
+
 #include <user32.h>
-#include <string.h>
+#define NDEBUG
 #include <debug.h>
 
-/*
- * @implemented
+/* DDE message exchange
+ *
+ * - Session initialization
+ *   Client sends a WM_DDE_INITIATE message, usually a broadcast message. lParam of
+ *   this message contains a pair of global atoms, the Application and Topic atoms.
+ *   The client must destroy the atoms.
+ *   Server window proc handles the WM_DDE_INITIATE message and if the Application
+ *   and Topic atoms are recognized sends a WM_DDE_ACK message to the client. lParam
+ *   of the reply message contains another pair of global atoms (Application and
+ *   Topic again), which must be destroyed by the server.
+ *
+ * - Execute
+ *   Client posts a WM_DDE_EXECUTE message to the server window. lParam of that message
+ *   is a global memory handle containing the string to execute. After the command has
+ *   been executed the server posts a WM_DDE_ACK message to the client, which contains
+ *   a packed lParam which in turn contains that global memory handle. The client takes
+ *   ownership of both the packed lParam (meaning it needs to call FreeDDElParam() on
+ *   it and the global memory handle.
+ *   This might work nice and easy in Win3.1, but things are more complicated for NT.
+ *   Global memory handles in NT are not really global, they're still local to the
+ *   process. So, what happens under the hood is that PostMessage must handle the
+ *   WM_DDE_EXECUTE message specially. It will obtain the contents of the global memory
+ *   area, repack that into a new structure together with the original memory handle
+ *   and pass that off to the win32k. Win32k will marshall that data over to the target
+ *   (server) process where it will be unpacked and stored in a newly allocated global
+ *   memory area. The handle of that area will then be sent to the window proc, after
+ *   storing it together with the "original" (client) handle in a table.
+ *   The server will eventually post the WM_DDE_ACK response, containing the global
+ *   memory handle it received. PostMessage must then lookup that memory handle (only
+ *   valid in the server process) and replace it with the corresponding client memory
+ *   handle. To avoid memory leaks, the server-side global memory block must be freed.
+ *   Also, the WM_DDE_ACK lParam (a PackDDElParam() result) is unpacked and the
+ *   individual components are handed to win32k.sys to post to the client side. Since
+ *   the server side app hands over ownership of the packed lParam when it calls
+ *   PostMessage(), the packed lParam needs to be freed on the server side too.
+ *   When the WM_DDE_ACK message (containing the client-side global memory handle)
+ *   arrives at the client side a new lParam is PackDDElParam()'ed and this is handed
+ *   to the client side window proc which is expected to free/reuse it.
  */
-LPARAM
-STDCALL
-GetMessageExtraInfo(VOID)
-{
-  UNIMPLEMENTED;
-  return (LPARAM)0;
-}
-
 
-/*
- * @implemented
+/* since the WM_DDE_ACK response to a WM_DDE_EXECUTE message should contain the handle
+ * to the memory handle, we keep track (in the server side) of all pairs of handle
+ * used (the client passes its value and the content of the memory handle), and
+ * the server stored both values (the client, and the local one, created after the
+ * content). When a ACK message is generated, the list of pair is searched for a
+ * matching pair, so that the client memory handle can be returned.
  */
-DWORD
-STDCALL
-GetMessagePos(VOID)
+typedef struct tagDDEPAIR
 {
-  PUSER32_THREAD_DATA ThreadData = User32GetThreadData();
-  return(MAKELONG(ThreadData->LastMessage.pt.x, ThreadData->LastMessage.pt.y));
-}
+  HGLOBAL     ClientMem;
+  HGLOBAL     ServerMem;
+} DDEPAIR, *PDDEPAIR;
 
+static PDDEPAIR DdePairs = NULL;
+static unsigned DdeNumAlloc = 0;
+static unsigned DdeNumUsed = 0;
+static CRITICAL_SECTION DdeCrst;
 
-/*
- * @implemented
- */
-LONG STDCALL
-GetMessageTime(VOID)
+static BOOL FASTCALL
+DdeAddPair(HGLOBAL ClientMem, HGLOBAL ServerMem)
 {
-  PUSER32_THREAD_DATA ThreadData = User32GetThreadData();
-  return(ThreadData->LastMessage.time);
-}
+  unsigned i;
 
+  EnterCriticalSection(&DdeCrst);
 
-/*
- * @unimplemented
- */
-WINBOOL
-STDCALL
-InSendMessage(VOID)
-{
-  return FALSE;
+  /* now remember the pair of hMem on both sides */
+  if (DdeNumUsed == DdeNumAlloc)
+    {
+#define GROWBY  4
+      PDDEPAIR New;
+      if (NULL != DdePairs)
+        {
+          New = HeapReAlloc(GetProcessHeap(), 0, DdePairs,
+                            (DdeNumAlloc + GROWBY) * sizeof(DDEPAIR));
+        }
+      else
+        {
+          New = HeapAlloc(GetProcessHeap(), 0,
+                          (DdeNumAlloc + GROWBY) * sizeof(DDEPAIR));
+        }
+
+      if (NULL == New)
+        {
+          LeaveCriticalSection(&DdeCrst);
+          return FALSE;
+        }
+      DdePairs = New;
+      /* zero out newly allocated part */
+      memset(&DdePairs[DdeNumAlloc], 0, GROWBY * sizeof(DDEPAIR));
+      DdeNumAlloc += GROWBY;
+#undef GROWBY
+    }
+
+  for (i = 0; i < DdeNumAlloc; i++)
+    {
+      if (NULL == DdePairs[i].ServerMem)
+        {
+          DdePairs[i].ClientMem = ClientMem;
+          DdePairs[i].ServerMem = ServerMem;
+          DdeNumUsed++;
+          break;
+        }
+    }
+  LeaveCriticalSection(&DdeCrst);
+
+  return TRUE;
 }
 
+static HGLOBAL FASTCALL
+DdeGetPair(HGLOBAL ServerMem)
+{
+  unsigned i;
+  HGLOBAL Ret = NULL;
 
-/*
- * @unimplemented
- */
-DWORD
-STDCALL
-InSendMessageEx(
-  LPVOID lpReserved)
+  EnterCriticalSection(&DdeCrst);
+  for (i = 0; i < DdeNumAlloc; i++)
+    {
+      if (DdePairs[i].ServerMem == ServerMem)
+        {
+          /* free this pair */
+          DdePairs[i].ServerMem = 0;
+          DdeNumUsed--;
+          Ret = DdePairs[i].ClientMem;
+          break;
+        }
+    }
+  LeaveCriticalSection(&DdeCrst);
+
+  return Ret;
+}
+
+static BOOL FASTCALL
+MsgiUMToKMMessage(PMSG UMMsg, PMSG KMMsg, BOOL Posted)
 {
-  UNIMPLEMENTED;
-  return 0;
+  *KMMsg = *UMMsg;
+
+  switch (UMMsg->message)
+    {
+      case WM_DDE_ACK:
+        {
+          PKMDDELPARAM DdeLparam;
+          DdeLparam = HeapAlloc(GetProcessHeap(), 0, sizeof(KMDDELPARAM));
+          if (NULL == DdeLparam)
+            {
+              return FALSE;
+            }
+          if (Posted)
+            {
+              DdeLparam->Packed = TRUE;
+              if (! UnpackDDElParam(UMMsg->message, UMMsg->lParam,
+                                    &DdeLparam->Value.Packed.uiLo,
+                                    &DdeLparam->Value.Packed.uiHi))
+                {
+                  return FALSE;
+                }
+              if (0 != HIWORD(DdeLparam->Value.Packed.uiHi))
+                {
+                  /* uiHi should contain a hMem from WM_DDE_EXECUTE */
+                  HGLOBAL h = DdeGetPair((HGLOBAL) DdeLparam->Value.Packed.uiHi);
+                  if (NULL != h)
+                    {
+                      GlobalFree((HGLOBAL) DdeLparam->Value.Packed.uiHi);
+                      DdeLparam->Value.Packed.uiHi = (UINT) h;
+                    }
+                }
+              FreeDDElParam(UMMsg->message, UMMsg->lParam);
+            }
+          else
+            {
+              DdeLparam->Packed = FALSE;
+              DdeLparam->Value.Unpacked = UMMsg->lParam;
+            }
+          KMMsg->lParam = (LPARAM) DdeLparam;
+        }
+        break;
+
+      case WM_DDE_EXECUTE:
+        {
+          SIZE_T Size;
+          PKMDDEEXECUTEDATA KMDdeExecuteData;
+          PVOID Data;
+
+          Size = GlobalSize((HGLOBAL) UMMsg->lParam);
+          Data = GlobalLock((HGLOBAL) UMMsg->lParam);
+          if (NULL == Data)
+            {
+              SetLastError(ERROR_INVALID_HANDLE);
+              return FALSE;
+            }
+          KMDdeExecuteData = HeapAlloc(GetProcessHeap(), 0, sizeof(KMDDEEXECUTEDATA) + Size);
+          if (NULL == KMDdeExecuteData)
+            {
+              SetLastError(ERROR_OUTOFMEMORY);
+              return FALSE;
+            }
+          KMDdeExecuteData->Sender = (HWND) UMMsg->wParam;
+          KMDdeExecuteData->ClientMem = (HGLOBAL) UMMsg->lParam;
+          memcpy((PVOID) (KMDdeExecuteData + 1), Data, Size);
+          KMMsg->wParam = sizeof(KMDDEEXECUTEDATA) + Size;
+          KMMsg->lParam = (LPARAM) KMDdeExecuteData;
+          GlobalUnlock((HGLOBAL) UMMsg->lParam);
+        }
+        break;
+
+      case WM_COPYDATA:
+        {
+          PCOPYDATASTRUCT pUMCopyData = (PCOPYDATASTRUCT)UMMsg->lParam;
+          PCOPYDATASTRUCT pKMCopyData;
+
+          pKMCopyData = HeapAlloc(GetProcessHeap(), 0,
+                                  sizeof(COPYDATASTRUCT) + pUMCopyData->cbData);
+          if (pKMCopyData == NULL)
+            {
+              SetLastError(ERROR_OUTOFMEMORY);
+              return FALSE;
+            }
+
+          pKMCopyData->dwData = pUMCopyData->dwData;
+          pKMCopyData->cbData = pUMCopyData->cbData;
+          pKMCopyData->lpData = pKMCopyData + 1;
+
+          RtlCopyMemory(pKMCopyData + 1, pUMCopyData->lpData,
+                        pUMCopyData->cbData);
+
+          KMMsg->lParam = (LPARAM)pKMCopyData;
+        }
+        break;
+
+      default:
+        break;
+    }
+
+  return TRUE;
 }
 
+static VOID FASTCALL
+MsgiUMToKMCleanup(PMSG UMMsg, PMSG KMMsg)
+{
+  switch (KMMsg->message)
+    {
+      case WM_DDE_ACK:
+      case WM_DDE_EXECUTE:
+      case WM_COPYDATA:
+        HeapFree(GetProcessHeap(), 0, (LPVOID) KMMsg->lParam);
+        break;
+      default:
+        break;
+    }
 
-/*
- * @unimplemented
- */
-WINBOOL
-STDCALL
-ReplyMessage(
-  LRESULT lResult)
+  return;
+}
+
+static BOOL FASTCALL
+MsgiUMToKMReply(PMSG UMMsg, PMSG KMMsg, LRESULT *Result)
 {
-  UNIMPLEMENTED;
-  return FALSE;
+  MsgiUMToKMCleanup(UMMsg, KMMsg);
+
+  return TRUE;
 }
 
+static BOOL FASTCALL
+MsgiKMToUMMessage(PMSG KMMsg, PMSG UMMsg)
+{
+  *UMMsg = *KMMsg;
 
-/*
- * @unimplemented
- */
-LPARAM
-STDCALL
-SetMessageExtraInfo(
-  LPARAM lParam)
+  switch (UMMsg->message)
+    {
+      case WM_CREATE:
+      case WM_NCCREATE:
+        {
+          CREATESTRUCTW *Cs = (CREATESTRUCTW *) KMMsg->lParam;
+          PCHAR Class;
+          Cs->lpszName = (LPCWSTR) ((PCHAR) Cs + (DWORD_PTR) Cs->lpszName);
+          Class = (PCHAR) Cs + (DWORD_PTR) Cs->lpszClass;
+          if (L'A' == *((WCHAR *) Class))
+            {
+              Class += sizeof(WCHAR);
+              Cs->lpszClass = (LPCWSTR)(DWORD_PTR) (*((ATOM *) Class));
+            }
+          else
+            {
+              ASSERT(L'S' == *((WCHAR *) Class));
+              Class += sizeof(WCHAR);
+              Cs->lpszClass = (LPCWSTR) Class;
+            }
+        }
+        break;
+
+      case WM_DDE_ACK:
+        {
+          PKMDDELPARAM DdeLparam = (PKMDDELPARAM) KMMsg->lParam;
+          if (DdeLparam->Packed)
+            {
+              UMMsg->lParam = PackDDElParam(KMMsg->message,
+                                            DdeLparam->Value.Packed.uiLo,
+                                            DdeLparam->Value.Packed.uiHi);
+            }
+          else
+            {
+              UMMsg->lParam = DdeLparam->Value.Unpacked;
+            }
+        }
+        break;
+
+      case WM_DDE_EXECUTE:
+        {
+          PKMDDEEXECUTEDATA KMDdeExecuteData;
+          HGLOBAL GlobalData;
+          PVOID Data;
+
+          KMDdeExecuteData = (PKMDDEEXECUTEDATA) KMMsg->lParam;
+          GlobalData = GlobalAlloc(GMEM_MOVEABLE, KMMsg->wParam - sizeof(KMDDEEXECUTEDATA));
+          if (NULL == GlobalData)
+            {
+              return FALSE;
+            }
+          Data = GlobalLock(GlobalData);
+          if (NULL == Data)
+            {
+              GlobalFree(GlobalData);
+              return FALSE;
+            }
+          memcpy(Data, (PVOID) (KMDdeExecuteData + 1), KMMsg->wParam - sizeof(KMDDEEXECUTEDATA));
+          GlobalUnlock(GlobalData);
+          if (! DdeAddPair(KMDdeExecuteData->ClientMem, GlobalData))
+            {
+              GlobalFree(GlobalData);
+              return FALSE;
+            }
+          UMMsg->wParam = (WPARAM) KMDdeExecuteData->Sender;
+          UMMsg->lParam = (LPARAM) GlobalData;
+        }
+        break;
+
+      case WM_COPYDATA:
+        {
+          PCOPYDATASTRUCT pKMCopyData = (PCOPYDATASTRUCT)KMMsg->lParam;
+          pKMCopyData->lpData = pKMCopyData + 1;
+        }
+        break;
+
+      default:
+        break;
+    }
+
+  return TRUE;
+}
+
+static VOID FASTCALL
+MsgiKMToUMCleanup(PMSG KMMsg, PMSG UMMsg)
 {
-  UNIMPLEMENTED;
-  return (LPARAM)0;
+  switch (KMMsg->message)
+    {
+      case WM_DDE_EXECUTE:
+#ifdef TODO
+        HeapFree(GetProcessHeap(), 0, (LPVOID) KMMsg->lParam);
+        GlobalUnlock((HGLOBAL) UMMsg->lParam);
+#endif
+        break;
+      default:
+        break;
+    }
+
+  return;
 }
 
+static BOOL FASTCALL
+MsgiKMToUMReply(PMSG KMMsg, PMSG UMMsg, LRESULT *Result)
+{
+  MsgiKMToUMCleanup(KMMsg, UMMsg);
 
-BOOL
+  return TRUE;
+}
+
+static BOOL FASTCALL
 MsgiAnsiToUnicodeMessage(LPMSG UnicodeMsg, LPMSG AnsiMsg)
 {
   *UnicodeMsg = *AnsiMsg;
@@ -193,7 +481,7 @@ MsgiAnsiToUnicodeMessage(LPMSG UnicodeMsg, LPMSG AnsiMsg)
 }
 
 
-BOOL
+static BOOL FASTCALL
 MsgiAnsiToUnicodeCleanup(LPMSG UnicodeMsg, LPMSG AnsiMsg)
 {
   switch (AnsiMsg->message)
@@ -266,7 +554,7 @@ MsgiAnsiToUnicodeCleanup(LPMSG UnicodeMsg, LPMSG AnsiMsg)
 }
 
 
-BOOL
+static BOOL FASTCALL
 MsgiAnsiToUnicodeReply(LPMSG UnicodeMsg, LPMSG AnsiMsg, LRESULT *Result)
 {
   switch (AnsiMsg->message)
@@ -284,15 +572,6 @@ MsgiAnsiToUnicodeReply(LPMSG UnicodeMsg, LPMSG AnsiMsg, LRESULT *Result)
           }
         break;
       }
-
-    case WM_GETTEXTLENGTH:
-    case CB_GETLBTEXTLEN:
-    case LB_GETTEXTLEN:
-      {
-        /* FIXME: There may be one DBCS char for each Unicode char */
-        *Result *= 2;
-        break;
-      }
     }
 
   MsgiAnsiToUnicodeCleanup(UnicodeMsg, AnsiMsg);
@@ -301,107 +580,355 @@ MsgiAnsiToUnicodeReply(LPMSG UnicodeMsg, LPMSG AnsiMsg, LRESULT *Result)
 }
 
 
-VOID STATIC
-User32ConvertToAsciiMessage(UINT* Msg, WPARAM* wParam, LPARAM* lParam)
+static BOOL FASTCALL
+MsgiUnicodeToAnsiMessage(LPMSG AnsiMsg, LPMSG UnicodeMsg)
 {
-  switch((*Msg))
+  *AnsiMsg = *UnicodeMsg;
+
+  switch(UnicodeMsg->message)
     {
-    case WM_CREATE:
-    case WM_NCCREATE:
-      {
-       CREATESTRUCTA* CsA;
-       CREATESTRUCTW* CsW;
-       UNICODE_STRING UString;
-       ANSI_STRING AString;
-
-       CsW = (CREATESTRUCTW*)(*lParam);
-       CsA = RtlAllocateHeap(RtlGetProcessHeap(), 0, sizeof(CREATESTRUCTA));
-       memcpy(CsA, CsW, sizeof(CREATESTRUCTW));
-
-       RtlInitUnicodeString(&UString, CsW->lpszName);
-       RtlUnicodeStringToAnsiString(&AString, &UString, TRUE);
-       CsA->lpszName = AString.Buffer;
-       if (HIWORD((ULONG)CsW->lpszClass) != 0)
-         {
-           RtlInitUnicodeString(&UString, CsW->lpszClass);
-           RtlUnicodeStringToAnsiString(&AString, &UString, TRUE);
-           CsA->lpszClass = AString.Buffer;
-         }
-       (*lParam) = (LPARAM)CsA;
-       break;
-      }
-    case WM_SETTEXT:
-      {
-       ANSI_STRING AnsiString;
-       UNICODE_STRING UnicodeString;
-       RtlInitUnicodeString(&UnicodeString, (PWSTR) *lParam);
-       if (NT_SUCCESS(RtlUnicodeStringToAnsiString(&AnsiString,
-                                                   &UnicodeString,
-                                                   TRUE)))
-         {
-           *lParam = (LPARAM) AnsiString.Buffer;
-         }
-       break;
-      }
+      case WM_CREATE:
+      case WM_NCCREATE:
+        {
+          CREATESTRUCTA* CsA;
+          CREATESTRUCTW* CsW;
+          UNICODE_STRING UString;
+          ANSI_STRING AString;
+          NTSTATUS Status;
+
+          CsW = (CREATESTRUCTW*)(UnicodeMsg->lParam);
+          CsA = RtlAllocateHeap(GetProcessHeap(), 0, sizeof(CREATESTRUCTA));
+          if (NULL == CsA)
+            {
+              return FALSE;
+            }
+          memcpy(CsA, CsW, sizeof(CREATESTRUCTW));
+
+          RtlInitUnicodeString(&UString, CsW->lpszName);
+          Status = RtlUnicodeStringToAnsiString(&AString, &UString, TRUE);
+          if (! NT_SUCCESS(Status))
+            {
+              RtlFreeHeap(GetProcessHeap(), 0, CsA);
+              return FALSE;
+            }
+          CsA->lpszName = AString.Buffer;
+          if (HIWORD((ULONG)CsW->lpszClass) != 0)
+            {
+              RtlInitUnicodeString(&UString, CsW->lpszClass);
+              Status = RtlUnicodeStringToAnsiString(&AString, &UString, TRUE);
+              if (! NT_SUCCESS(Status))
+                {
+                  RtlInitAnsiString(&AString, CsA->lpszName);
+                  RtlFreeAnsiString(&AString);
+                  RtlFreeHeap(GetProcessHeap(), 0, CsA);
+                  return FALSE;
+                }
+              CsA->lpszClass = AString.Buffer;
+            }
+          AnsiMsg->lParam = (LPARAM)CsA;
+          break;
+        }
+      case WM_GETTEXT:
+        {
+          /* Ansi string might contain MBCS chars so we need 2 * the number of chars */
+          AnsiMsg->wParam = UnicodeMsg->wParam * 2;
+          AnsiMsg->lParam = (LPARAM) RtlAllocateHeap(GetProcessHeap(), 0, AnsiMsg->wParam);
+          if (NULL == (PVOID) AnsiMsg->lParam)
+            {
+              return FALSE;
+            }
+          break;
+        }
+      case WM_SETTEXT:
+        {
+          ANSI_STRING AnsiString;
+          UNICODE_STRING UnicodeString;
+          RtlInitUnicodeString(&UnicodeString, (PWSTR) UnicodeMsg->lParam);
+          if (! NT_SUCCESS(RtlUnicodeStringToAnsiString(&AnsiString,
+                                                        &UnicodeString,
+                                                        TRUE)))
+            {
+              return FALSE;
+            }
+          AnsiMsg->lParam = (LPARAM) AnsiString.Buffer;
+          break;
+       }
     }
+
+  return TRUE;
 }
 
 
-VOID STATIC
-User32FreeAsciiConvertedMessage(UINT Msg, WPARAM wParam, LPARAM lParam)
+static BOOL FASTCALL
+MsgiUnicodeToAnsiCleanup(LPMSG AnsiMsg, LPMSG UnicodeMsg)
 {
-  switch(Msg)
+  switch(UnicodeMsg->message)
+    {
+      case WM_GETTEXT:
+        {
+          RtlFreeHeap(GetProcessHeap(), 0, (PVOID) AnsiMsg->lParam);
+          break;
+        }
+      case WM_SETTEXT:
+        {
+          ANSI_STRING AString;
+          RtlInitAnsiString(&AString, (PSTR) AnsiMsg->lParam);
+          RtlFreeAnsiString(&AString);
+          break;
+        }
+      case WM_CREATE:
+      case WM_NCCREATE:
+        {
+          CREATESTRUCTA* Cs;
+          ANSI_STRING AString;
+
+          Cs = (CREATESTRUCTA*) AnsiMsg->lParam;
+          RtlInitAnsiString(&AString, Cs->lpszName);
+          RtlFreeAnsiString(&AString);
+          if (HIWORD((ULONG)Cs->lpszClass) != 0)
+            {
+              RtlInitAnsiString(&AString, Cs->lpszClass);
+              RtlFreeAnsiString(&AString);
+            }
+          RtlFreeHeap(GetProcessHeap(), 0, Cs);
+          break;
+        }
+    }
+
+  return TRUE;
+}
+
+
+static BOOL FASTCALL
+MsgiUnicodeToAnsiReply(LPMSG AnsiMsg, LPMSG UnicodeMsg, LRESULT *Result)
+{
+  switch (UnicodeMsg->message)
     {
     case WM_GETTEXT:
+    case WM_ASKCBFORMATNAME:
       {
-       ANSI_STRING AnsiString;
-       UNICODE_STRING UnicodeString;
-       LPSTR TempString;
-       LPSTR InString;
-       InString = (LPSTR)lParam;
-       TempString = RtlAllocateHeap(RtlGetProcessHeap(), 0, strlen(InString) + 1);
-       strcpy(TempString, InString);
-       RtlInitAnsiString(&AnsiString, TempString);
-       UnicodeString.Length = wParam * sizeof(WCHAR);
-       UnicodeString.MaximumLength = wParam * sizeof(WCHAR);
-       UnicodeString.Buffer = (PWSTR)lParam;
-       if (! NT_SUCCESS(RtlAnsiStringToUnicodeString(&UnicodeString,
-                                                     &AnsiString,
-                                                     FALSE)))
-         {
-           if (1 <= wParam)
-             {
-               UnicodeString.Buffer[0] = L'\0';
-             }
-         }
-       RtlFreeHeap(RtlGetProcessHeap(), 0, TempString);
-       break;
-      }
-    case WM_SETTEXT:
-      {
-       ANSI_STRING AnsiString;
-       RtlInitAnsiString(&AnsiString, (PSTR) lParam);
-       RtlFreeAnsiString(&AnsiString);
-       break;
-      }
-    case WM_CREATE:
-    case WM_NCCREATE:
-      {
-       CREATESTRUCTA* Cs;
-
-       Cs = (CREATESTRUCTA*)lParam;
-       RtlFreeHeap(RtlGetProcessHeap(), 0, (LPSTR)Cs->lpszName);
-       if (HIWORD((ULONG)Cs->lpszClass) != 0)
-         {
-            RtlFreeHeap(RtlGetProcessHeap(), 0, (LPSTR)Cs->lpszClass);
-         }
-       RtlFreeHeap(RtlGetProcessHeap(), 0, Cs);
-       break;
+        LPSTR Buffer = (LPSTR) AnsiMsg->lParam;
+        LPWSTR UBuffer = (LPWSTR) UnicodeMsg->lParam;
+        if (0 < AnsiMsg->wParam &&
+            ! MultiByteToWideChar(CP_ACP, 0, Buffer, -1, UBuffer, UnicodeMsg->wParam))
+          {
+            UBuffer[UnicodeMsg->wParam - 1] = L'\0';
+          }
+        break;
       }
     }
+
+  MsgiUnicodeToAnsiCleanup(AnsiMsg, UnicodeMsg);
+
+  return TRUE;
+}
+
+typedef struct tagMSGCONVERSION
+{
+  BOOL InUse;
+  BOOL Ansi;
+  MSG KMMsg;
+  MSG UnicodeMsg;
+  MSG AnsiMsg;
+  PMSG FinalMsg;
+  ULONG LParamSize;
+} MSGCONVERSION, *PMSGCONVERSION;
+
+static PMSGCONVERSION MsgConversions = NULL;
+static unsigned MsgConversionNumAlloc = 0;
+static unsigned MsgConversionNumUsed = 0;
+static CRITICAL_SECTION MsgConversionCrst;
+
+static BOOL FASTCALL
+MsgConversionAdd(PMSGCONVERSION Conversion)
+{
+  unsigned i;
+
+  EnterCriticalSection(&MsgConversionCrst);
+
+  if (MsgConversionNumUsed == MsgConversionNumAlloc)
+    {
+#define GROWBY  4
+      PMSGCONVERSION New;
+      if (NULL != MsgConversions)
+        {
+          New = HeapReAlloc(GetProcessHeap(), 0, MsgConversions,
+                            (MsgConversionNumAlloc + GROWBY) * sizeof(MSGCONVERSION));
+        }
+      else
+        {
+          New = HeapAlloc(GetProcessHeap(), 0,
+                          (MsgConversionNumAlloc + GROWBY) * sizeof(MSGCONVERSION));
+        }
+
+      if (NULL == New)
+        {
+          LeaveCriticalSection(&MsgConversionCrst);
+          return FALSE;
+        }
+      MsgConversions = New;
+      /* zero out newly allocated part */
+      memset(MsgConversions + MsgConversionNumAlloc, 0, GROWBY * sizeof(MSGCONVERSION));
+      MsgConversionNumAlloc += GROWBY;
+#undef GROWBY
+    }
+
+  for (i = 0; i < MsgConversionNumAlloc; i++)
+    {
+      if (! MsgConversions[i].InUse)
+        {
+          MsgConversions[i] = *Conversion;
+          MsgConversions[i].InUse = TRUE;
+          MsgConversionNumUsed++;
+          break;
+        }
+    }
+  LeaveCriticalSection(&MsgConversionCrst);
+
+  return TRUE;
+}
+
+static void FASTCALL
+MsgConversionCleanup(CONST MSG *Msg, BOOL Ansi, BOOL CheckMsgContents, LRESULT *Result)
+{
+  BOOL Found;
+  PMSGCONVERSION Conversion;
+  LRESULT Dummy;
+
+  EnterCriticalSection(&MsgConversionCrst);
+  for (Conversion = MsgConversions;
+       Conversion < MsgConversions + MsgConversionNumAlloc;
+       Conversion++)
+    {
+      if (Conversion->InUse &&
+          ((Ansi && Conversion->Ansi) ||
+           (! Ansi && ! Conversion->Ansi)))
+        {
+          Found = (Conversion->FinalMsg == Msg);
+          if (! Found && CheckMsgContents)
+            {
+              if (Ansi)
+                {
+                  Found = (0 == memcmp(Msg, &Conversion->AnsiMsg, sizeof(MSG)));
+                }
+              else
+                {
+                  Found = (0 == memcmp(Msg, &Conversion->UnicodeMsg, sizeof(MSG)));
+                }
+            }
+          if (Found)
+            {
+              if (Ansi)
+                {
+                  MsgiUnicodeToAnsiReply(&Conversion->AnsiMsg, &Conversion->UnicodeMsg,
+                                         NULL == Result ? &Dummy : Result);
+                }
+              MsgiKMToUMReply(&Conversion->KMMsg, &Conversion->UnicodeMsg,
+                              NULL == Result ? &Dummy : Result);
+              if (0 != Conversion->LParamSize)
+                {
+                  NtFreeVirtualMemory(NtCurrentProcess(), (PVOID *) &Conversion->KMMsg.lParam,
+                                      &Conversion->LParamSize, MEM_DECOMMIT);
+                }
+              Conversion->InUse = FALSE;
+              MsgConversionNumUsed--;
+            }
+        }
+    }
+  LeaveCriticalSection(&MsgConversionCrst);
+}
+
+/*
+ * @implemented
+ */
+LPARAM
+STDCALL
+GetMessageExtraInfo(VOID)
+{
+  return (LPARAM)NtUserCallNoParam(NOPARAM_ROUTINE_GETMESSAGEEXTRAINFO);
+}
+
+
+/*
+ * @implemented
+ */
+DWORD
+STDCALL
+GetMessagePos(VOID)
+{
+  PUSER32_THREAD_DATA ThreadData = User32GetThreadData();
+  return(MAKELONG(ThreadData->LastMessage.pt.x, ThreadData->LastMessage.pt.y));
+}
+
+
+/*
+ * @implemented
+ */
+LONG STDCALL
+GetMessageTime(VOID)
+{
+  PUSER32_THREAD_DATA ThreadData = User32GetThreadData();
+  return(ThreadData->LastMessage.time);
+}
+
+
+/*
+ * @unimplemented
+ */
+BOOL
+STDCALL
+InSendMessage(VOID)
+{
+  static DWORD ShowNotImplemented = TRUE;
+  if (ShowNotImplemented)
+    {
+      DbgPrint("InSendMessage is unimplemented\n");
+      ShowNotImplemented = FALSE;
+    }
+  /* return(NtUserGetThreadState(THREADSTATE_INSENDMESSAGE) != ISMEX_NOSEND); */
+  return FALSE;
+}
+
+
+/*
+ * @unimplemented
+ */
+DWORD
+STDCALL
+InSendMessageEx(
+  LPVOID lpReserved)
+{
+  /* return NtUserGetThreadState(THREADSTATE_INSENDMESSAGE); */
+  UNIMPLEMENTED;
+  return 0;
+}
+
+
+/*
+ * @unimplemented
+ */
+BOOL
+STDCALL
+ReplyMessage(
+  LRESULT lResult)
+{
+  UNIMPLEMENTED;
+  return FALSE;
+}
+
+
+/*
+ * @implemented
+ */
+LPARAM
+STDCALL
+SetMessageExtraInfo(
+  LPARAM lParam)
+{
+  return NtUserSetMessageExtraInfo(lParam);
 }
 
-STATIC LRESULT FASTCALL
+LRESULT FASTCALL
 IntCallWindowProcW(BOOL IsAnsiProc,
                    WNDPROC WndProc,
                    HWND hWnd,
@@ -409,13 +936,25 @@ IntCallWindowProcW(BOOL IsAnsiProc,
                    WPARAM wParam,
                    LPARAM lParam)
 {
+  MSG AnsiMsg;
+  MSG UnicodeMsg;
   LRESULT Result;
 
   if (IsAnsiProc)
     {
-      User32ConvertToAsciiMessage(&Msg, &wParam, &lParam);
-      Result = WndProc(hWnd, Msg, wParam, lParam);
-      User32FreeAsciiConvertedMessage(Msg, wParam, lParam);
+      UnicodeMsg.hwnd = hWnd;
+      UnicodeMsg.message = Msg;
+      UnicodeMsg.wParam = wParam;
+      UnicodeMsg.lParam = lParam;
+      if (! MsgiUnicodeToAnsiMessage(&AnsiMsg, &UnicodeMsg))
+        {
+          return FALSE;
+        }
+      Result = WndProc(AnsiMsg.hwnd, AnsiMsg.message, AnsiMsg.wParam, AnsiMsg.lParam);
+      if (! MsgiUnicodeToAnsiReply(&AnsiMsg, &UnicodeMsg, &Result))
+        {
+          return FALSE;
+        }
       return Result;
     }
   else
@@ -424,7 +963,7 @@ IntCallWindowProcW(BOOL IsAnsiProc,
     }
 }
 
-STATIC LRESULT FASTCALL
+static LRESULT FASTCALL
 IntCallWindowProcA(BOOL IsAnsiProc,
                    WNDPROC WndProc,
                    HWND hWnd,
@@ -474,6 +1013,9 @@ CallWindowProcA(WNDPROC lpPrevWndFunc,
   BOOL IsHandle;
   WndProcHandle wphData;
 
+  if (lpPrevWndFunc == NULL)
+    lpPrevWndFunc = (WNDPROC)NtUserGetWindowLong(hWnd, GWL_WNDPROC, FALSE);
+
   IsHandle = NtUserDereferenceWndProcHandle(lpPrevWndFunc,&wphData);
   if (! IsHandle)
     {
@@ -519,7 +1061,21 @@ CallWindowProcW(WNDPROC lpPrevWndFunc,
 LRESULT STDCALL
 DispatchMessageA(CONST MSG *lpmsg)
 {
-  return(NtUserDispatchMessage(lpmsg));
+  NTUSERDISPATCHMESSAGEINFO Info;
+  LRESULT Result;
+
+  Info.Ansi = TRUE;
+  Info.Msg = *lpmsg;
+  Result = NtUserDispatchMessage(&Info);
+  if (! Info.HandledByKernel)
+    {
+      /* We need to send the message ourselves */
+      Result = IntCallWindowProcA(Info.Ansi, Info.Proc, Info.Msg.hwnd,
+                                  Info.Msg.message, Info.Msg.wParam, Info.Msg.lParam);
+    }
+  MsgConversionCleanup(lpmsg, TRUE, TRUE, &Result);
+
+  return Result;
 }
 
 
@@ -529,56 +1085,113 @@ DispatchMessageA(CONST MSG *lpmsg)
 LRESULT STDCALL
 DispatchMessageW(CONST MSG *lpmsg)
 {
-  return(NtUserDispatchMessage((LPMSG)lpmsg));
+  NTUSERDISPATCHMESSAGEINFO Info;
+  LRESULT Result;
+
+  Info.Ansi = FALSE;
+  Info.Msg = *lpmsg;
+  Result = NtUserDispatchMessage(&Info);
+  if (! Info.HandledByKernel)
+    {
+      /* We need to send the message ourselves */
+      Result = IntCallWindowProcW(Info.Ansi, Info.Proc, Info.Msg.hwnd,
+                                  Info.Msg.message, Info.Msg.wParam, Info.Msg.lParam);
+    }
+  MsgConversionCleanup(lpmsg, FALSE, TRUE, &Result);
+
+  return Result;
 }
 
 
 /*
  * @implemented
  */
-WINBOOL STDCALL
+BOOL STDCALL
 GetMessageA(LPMSG lpMsg,
            HWND hWnd,
            UINT wMsgFilterMin,
            UINT wMsgFilterMax)
 {
   BOOL Res;
+  MSGCONVERSION Conversion;
+  NTUSERGETMESSAGEINFO Info;
   PUSER32_THREAD_DATA ThreadData = User32GetThreadData();
 
-  Res = NtUserGetMessage(lpMsg, hWnd, wMsgFilterMin, wMsgFilterMax);
+  MsgConversionCleanup(lpMsg, TRUE, FALSE, NULL);
+  Res = NtUserGetMessage(&Info, hWnd, wMsgFilterMin, wMsgFilterMax);
+  if (-1 == (int) Res)
+    {
+      return Res;
+    }
+  Conversion.LParamSize = Info.LParamSize;
+  Conversion.KMMsg = Info.Msg;
+
+  if (! MsgiKMToUMMessage(&Conversion.KMMsg, &Conversion.UnicodeMsg))
+    {
+      return (BOOL) -1;
+    }
+  if (! MsgiUnicodeToAnsiMessage(&Conversion.AnsiMsg, &Conversion.UnicodeMsg))
+    {
+      MsgiKMToUMCleanup(&Info.Msg, &Conversion.UnicodeMsg);
+      return (BOOL) -1;
+    }
+  *lpMsg = Conversion.AnsiMsg;
+  Conversion.Ansi = TRUE;
+  Conversion.FinalMsg = lpMsg;
+  MsgConversionAdd(&Conversion);
   if (Res && lpMsg->message != WM_PAINT && lpMsg->message != WM_QUIT)
     {
-      ThreadData->LastMessage = *lpMsg;
+      ThreadData->LastMessage = Info.Msg;
     }
-  return(Res);
+
+  return Res;
 }
 
 
 /*
  * @implemented
  */
-WINBOOL STDCALL
+BOOL STDCALL
 GetMessageW(LPMSG lpMsg,
            HWND hWnd,
            UINT wMsgFilterMin,
            UINT wMsgFilterMax)
 {
   BOOL Res;
+  MSGCONVERSION Conversion;
+  NTUSERGETMESSAGEINFO Info;
   PUSER32_THREAD_DATA ThreadData = User32GetThreadData();
 
-  Res = NtUserGetMessage(lpMsg, hWnd, wMsgFilterMin, wMsgFilterMax);
+  MsgConversionCleanup(lpMsg, FALSE, FALSE, NULL);
+  Res = NtUserGetMessage(&Info, hWnd, wMsgFilterMin, wMsgFilterMax);
+  if (-1 == (int) Res)
+    {
+      return Res;
+    }
+  Conversion.LParamSize = Info.LParamSize;
+  Conversion.KMMsg = Info.Msg;
+
+  if (! MsgiKMToUMMessage(&Conversion.KMMsg, &Conversion.UnicodeMsg))
+    {
+      return (BOOL) -1;
+    }
+  *lpMsg = Conversion.UnicodeMsg;
+  Conversion.Ansi = FALSE;
+  Conversion.FinalMsg = lpMsg;
+  MsgConversionAdd(&Conversion);
   if (Res && lpMsg->message != WM_PAINT && lpMsg->message != WM_QUIT)
     {
-      ThreadData->LastMessage = *lpMsg;
+      ThreadData->LastMessage = Info.Msg;
     }
-  return(Res);
+
+  return Res;
 }
 
 
 /*
  * @implemented
  */
-WINBOOL STDCALL
+BOOL STDCALL
 PeekMessageA(LPMSG lpMsg,
             HWND hWnd,
             UINT wMsgFilterMin,
@@ -586,21 +1199,45 @@ PeekMessageA(LPMSG lpMsg,
             UINT wRemoveMsg)
 {
   BOOL Res;
+  MSGCONVERSION Conversion;
+  NTUSERGETMESSAGEINFO Info;
   PUSER32_THREAD_DATA ThreadData = User32GetThreadData();
 
-  Res =  NtUserPeekMessage(lpMsg, hWnd, wMsgFilterMin, wMsgFilterMax, wRemoveMsg);
+  MsgConversionCleanup(lpMsg, TRUE, FALSE, NULL);
+  Res = NtUserPeekMessage(&Info, hWnd, wMsgFilterMin, wMsgFilterMax, wRemoveMsg);
+  if (-1 == (int) Res || ! Res)
+    {
+      return Res;
+    }
+  Conversion.LParamSize = Info.LParamSize;
+  Conversion.KMMsg = Info.Msg;
+
+  if (! MsgiKMToUMMessage(&Conversion.KMMsg, &Conversion.UnicodeMsg))
+    {
+      return (BOOL) -1;
+    }
+  if (! MsgiUnicodeToAnsiMessage(&Conversion.AnsiMsg, &Conversion.UnicodeMsg))
+    {
+      MsgiKMToUMCleanup(&Info.Msg, &Conversion.UnicodeMsg);
+      return (BOOL) -1;
+    }
+  *lpMsg = Conversion.AnsiMsg;
+  Conversion.Ansi = TRUE;
+  Conversion.FinalMsg = lpMsg;
+  MsgConversionAdd(&Conversion);
   if (Res && lpMsg->message != WM_PAINT && lpMsg->message != WM_QUIT)
     {
-      ThreadData->LastMessage = *lpMsg;
+      ThreadData->LastMessage = Info.Msg;
     }
-  return(Res);
+
+  return Res;
 }
 
 
 /*
  * @implemented
  */
-WINBOOL
+BOOL
 STDCALL
 PeekMessageW(
   LPMSG lpMsg,
@@ -610,44 +1247,101 @@ PeekMessageW(
   UINT wRemoveMsg)
 {
   BOOL Res;
+  MSGCONVERSION Conversion;
+  NTUSERGETMESSAGEINFO Info;
   PUSER32_THREAD_DATA ThreadData = User32GetThreadData();
-  
-  Res = NtUserPeekMessage(lpMsg, hWnd, wMsgFilterMin, wMsgFilterMax, wRemoveMsg);
+
+  MsgConversionCleanup(lpMsg, FALSE, FALSE, NULL);
+  Res = NtUserPeekMessage(&Info, hWnd, wMsgFilterMin, wMsgFilterMax, wRemoveMsg);
+  if (-1 == (int) Res || ! Res)
+    {
+      return Res;
+    }
+  Conversion.LParamSize = Info.LParamSize;
+  Conversion.KMMsg = Info.Msg;
+
+  if (! MsgiKMToUMMessage(&Conversion.KMMsg, &Conversion.UnicodeMsg))
+    {
+      return (BOOL) -1;
+    }
+  *lpMsg = Conversion.UnicodeMsg;
+  Conversion.Ansi = FALSE;
+  Conversion.FinalMsg = lpMsg;
+  MsgConversionAdd(&Conversion);
   if (Res && lpMsg->message != WM_PAINT && lpMsg->message != WM_QUIT)
     {
-      ThreadData->LastMessage = *lpMsg;
+      ThreadData->LastMessage = Info.Msg;
     }
-  return(Res);
+
+  return Res;
 }
 
 
 /*
  * @implemented
  */
-WINBOOL
+BOOL
 STDCALL
 PostMessageA(
-  HWND hWnd,
+  HWND Wnd,
   UINT Msg,
   WPARAM wParam,
   LPARAM lParam)
 {
-  return NtUserPostMessage(hWnd, Msg, wParam, lParam);
+  MSG AnsiMsg, UcMsg;
+  MSG KMMsg;
+  LRESULT Result;
+
+  AnsiMsg.hwnd = Wnd;
+  AnsiMsg.message = Msg;
+  AnsiMsg.wParam = wParam;
+  AnsiMsg.lParam = lParam;
+  if (! MsgiAnsiToUnicodeMessage(&UcMsg, &AnsiMsg))
+    {
+      return FALSE;
+    }
+
+  if (! MsgiUMToKMMessage(&UcMsg, &KMMsg, TRUE))
+    {
+      MsgiAnsiToUnicodeCleanup(&UcMsg, &AnsiMsg);
+      return FALSE;
+    }
+  Result = NtUserPostMessage(KMMsg.hwnd, KMMsg.message,
+                             KMMsg.wParam, KMMsg.lParam);
+  MsgiUMToKMCleanup(&UcMsg, &KMMsg);
+  MsgiAnsiToUnicodeCleanup(&UcMsg, &AnsiMsg);
+
+  return Result;
 }
 
 
 /*
  * @implemented
  */
-WINBOOL
+BOOL
 STDCALL
 PostMessageW(
-  HWND hWnd,
+  HWND Wnd,
   UINT Msg,
   WPARAM wParam,
   LPARAM lParam)
 {
-  return NtUserPostMessage(hWnd, Msg, wParam, lParam);
+  MSG UMMsg, KMMsg;
+  LRESULT Result;
+
+  UMMsg.hwnd = Wnd;
+  UMMsg.message = Msg;
+  UMMsg.wParam = wParam;
+  UMMsg.lParam = lParam;
+  if (! MsgiUMToKMMessage(&UMMsg, &KMMsg, TRUE))
+    {
+      return FALSE;
+    }
+  Result = NtUserPostMessage(KMMsg.hwnd, KMMsg.message,
+                             KMMsg.wParam, KMMsg.lParam);
+  MsgiUMToKMCleanup(&UMMsg, &KMMsg);
+
+  return Result;
 }
 
 
@@ -666,7 +1360,7 @@ PostQuitMessage(
 /*
  * @implemented
  */
-WINBOOL
+BOOL
 STDCALL
 PostThreadMessageA(
   DWORD idThread,
@@ -681,7 +1375,7 @@ PostThreadMessageA(
 /*
  * @implemented
  */
-WINBOOL
+BOOL
 STDCALL
 PostThreadMessageW(
   DWORD idThread,
@@ -702,14 +1396,31 @@ SendMessageW(HWND Wnd,
             WPARAM wParam,
             LPARAM lParam)
 {
+  MSG UMMsg, KMMsg;
   NTUSERSENDMESSAGEINFO Info;
   LRESULT Result;
 
-  Result = NtUserSendMessage(Wnd, Msg, wParam, lParam, &Info);
+  UMMsg.hwnd = Wnd;
+  UMMsg.message = Msg;
+  UMMsg.wParam = wParam;
+  UMMsg.lParam = lParam;
+  if (! MsgiUMToKMMessage(&UMMsg, &KMMsg, FALSE))
+    {
+      return FALSE;
+    }
+  Info.Ansi = FALSE;
+  Result = NtUserSendMessage(KMMsg.hwnd, KMMsg.message,
+                             KMMsg.wParam, KMMsg.lParam, &Info);
   if (! Info.HandledByKernel)
     {
+      MsgiUMToKMCleanup(&UMMsg, &KMMsg);
       /* We need to send the message ourselves */
-      Result = IntCallWindowProcW(Info.Ansi, Info.Proc, Wnd, Msg, wParam, lParam);
+      Result = IntCallWindowProcW(Info.Ansi, Info.Proc, UMMsg.hwnd, UMMsg.message,
+                                  UMMsg.wParam, UMMsg.lParam);
+    }
+  else if (! MsgiUMToKMReply(&UMMsg, &KMMsg, &Result))
+    {
+      return FALSE;
     }
 
   return Result;
@@ -722,8 +1433,8 @@ SendMessageW(HWND Wnd,
 LRESULT STDCALL
 SendMessageA(HWND Wnd, UINT Msg, WPARAM wParam, LPARAM lParam)
 {
-  MSG AnsiMsg;
-  MSG UcMsg;
+  MSG AnsiMsg, UcMsg;
+  MSG KMMsg;
   LRESULT Result;
   NTUSERSENDMESSAGEINFO Info;
 
@@ -736,8 +1447,14 @@ SendMessageA(HWND Wnd, UINT Msg, WPARAM wParam, LPARAM lParam)
       return FALSE;
     }
 
-  Result = NtUserSendMessage(UcMsg.hwnd, UcMsg.message,
-                             UcMsg.wParam, UcMsg.lParam, &Info);
+  if (! MsgiUMToKMMessage(&UcMsg, &KMMsg, FALSE))
+    {
+      MsgiAnsiToUnicodeCleanup(&UcMsg, &AnsiMsg);
+      return FALSE;
+    }
+  Info.Ansi = TRUE;
+  Result = NtUserSendMessage(KMMsg.hwnd, KMMsg.message,
+                             KMMsg.wParam, KMMsg.lParam, &Info);
   if (! Info.HandledByKernel)
     {
       /* We need to send the message ourselves */
@@ -745,6 +1462,7 @@ SendMessageA(HWND Wnd, UINT Msg, WPARAM wParam, LPARAM lParam)
         {
           /* Ansi message and Ansi window proc, that's easy. Clean up
              the Unicode message though */
+          MsgiUMToKMCleanup(&UcMsg, &KMMsg);
           MsgiAnsiToUnicodeCleanup(&UcMsg, &AnsiMsg);
           Result = IntCallWindowProcA(Info.Ansi, Info.Proc, Wnd, Msg, wParam, lParam);
         }
@@ -761,13 +1479,11 @@ SendMessageA(HWND Wnd, UINT Msg, WPARAM wParam, LPARAM lParam)
             }
         }
     }
-  else
+  /* Message sent by kernel. Convert back to Ansi */
+  else if (! MsgiUMToKMReply(&UcMsg, &KMMsg, &Result) ||
+           ! MsgiAnsiToUnicodeReply(&UcMsg, &AnsiMsg, &Result))
     {
-      /* Message sent by kernel. Convert back to Ansi */
-      if (! MsgiAnsiToUnicodeReply(&UcMsg, &AnsiMsg, &Result))
-        {
-          return FALSE;
-        }
+      return FALSE;
     }
 
   return Result;
@@ -777,7 +1493,7 @@ SendMessageA(HWND Wnd, UINT Msg, WPARAM wParam, LPARAM lParam)
 /*
  * @implemented
  */
-WINBOOL
+BOOL
 STDCALL
 SendMessageCallbackA(
   HWND hWnd,
@@ -800,7 +1516,7 @@ SendMessageCallbackA(
 /*
  * @implemented
  */
-WINBOOL
+BOOL
 STDCALL
 SendMessageCallbackW(
   HWND hWnd,
@@ -834,8 +1550,64 @@ SendMessageTimeoutA(
   UINT uTimeout,
   PDWORD_PTR lpdwResult)
 {
-  UNIMPLEMENTED;
-  return (LRESULT)0;
+  MSG AnsiMsg;
+  MSG UcMsg;
+  LRESULT Result;
+  NTUSERSENDMESSAGEINFO Info;
+
+  AnsiMsg.hwnd = hWnd;
+  AnsiMsg.message = Msg;
+  AnsiMsg.wParam = wParam;
+  AnsiMsg.lParam = lParam;
+  if (! MsgiAnsiToUnicodeMessage(&UcMsg, &AnsiMsg))
+    {
+      return FALSE;
+    }
+
+  Info.Ansi = TRUE;
+  Result = NtUserSendMessageTimeout(UcMsg.hwnd, UcMsg.message,
+                                    UcMsg.wParam, UcMsg.lParam,
+                                    fuFlags, uTimeout, (ULONG_PTR*)lpdwResult, &Info);
+  if(!Result)
+  {
+      return FALSE;
+  }
+  if (! Info.HandledByKernel)
+    {
+      /* We need to send the message ourselves */
+      if (Info.Ansi)
+        {
+          /* Ansi message and Ansi window proc, that's easy. Clean up
+             the Unicode message though */
+          MsgiAnsiToUnicodeCleanup(&UcMsg, &AnsiMsg);
+          Result = IntCallWindowProcA(Info.Ansi, Info.Proc, hWnd, Msg, wParam, lParam);
+        }
+      else
+        {
+          /* Unicode winproc. Although we started out with an Ansi message we
+             already converted it to Unicode for the kernel call. Reuse that
+             message to avoid another conversion */
+          Result = IntCallWindowProcW(Info.Ansi, Info.Proc, UcMsg.hwnd,
+                                      UcMsg.message, UcMsg.wParam, UcMsg.lParam);
+          if (! MsgiAnsiToUnicodeReply(&UcMsg, &AnsiMsg, &Result))
+            {
+              return FALSE;
+            }
+        }
+      if(lpdwResult)
+        *lpdwResult = Result;
+      Result = TRUE;
+    }
+  else
+    {
+      /* Message sent by kernel. Convert back to Ansi */
+      if (! MsgiAnsiToUnicodeReply(&UcMsg, &AnsiMsg, &Result))
+        {
+          return FALSE;
+        }
+    }
+
+  return Result;
 }
 
 
@@ -853,15 +1625,29 @@ SendMessageTimeoutW(
   UINT uTimeout,
   PDWORD_PTR lpdwResult)
 {
-  UNIMPLEMENTED;
-  return (LRESULT)0;
+  NTUSERSENDMESSAGEINFO Info;
+  LRESULT Result;
+
+  Info.Ansi = FALSE;
+  Result = NtUserSendMessageTimeout(hWnd, Msg, wParam, lParam, fuFlags, uTimeout,
+                                    lpdwResult, &Info);
+  if (! Info.HandledByKernel)
+    {
+      /* We need to send the message ourselves */
+      Result = IntCallWindowProcW(Info.Ansi, Info.Proc, hWnd, Msg, wParam, lParam);
+      if(lpdwResult)
+        *lpdwResult = Result;
+      return TRUE;
+    }
+
+  return Result;
 }
 
 
 /*
  * @unimplemented
  */
-WINBOOL
+BOOL
 STDCALL
 SendNotifyMessageA(
   HWND hWnd,
@@ -877,7 +1663,7 @@ SendNotifyMessageA(
 /*
  * @unimplemented
  */
-WINBOOL
+BOOL
 STDCALL
 SendNotifyMessageW(
   HWND hWnd,
@@ -893,26 +1679,27 @@ SendNotifyMessageW(
 /*
  * @implemented
  */
-WINBOOL STDCALL
-TranslateMessage(CONST MSG *lpMsg)
+BOOL STDCALL
+TranslateMessageEx(CONST MSG *lpMsg, DWORD unk)
 {
-  return(TranslateMessageEx((LPMSG)lpMsg, 0));
+  return(NtUserTranslateMessage((LPMSG)lpMsg, (HKL)unk));
 }
 
+
 /*
  * @implemented
  */
-WINBOOL STDCALL
-TranslateMessageEx(CONST MSG *lpMsg, DWORD unk)
+BOOL STDCALL
+TranslateMessage(CONST MSG *lpMsg)
 {
-  return(NtUserTranslateMessage((LPMSG)lpMsg, (HKL)unk));
+  return(TranslateMessageEx((LPMSG)lpMsg, 0));
 }
 
 
 /*
  * @implemented
  */
-WINBOOL
+BOOL
 STDCALL
 WaitMessage(VOID)
 {
@@ -974,7 +1761,7 @@ GetCapture(VOID)
 /*
  * @implemented
  */
-WINBOOL STDCALL
+BOOL STDCALL
 ReleaseCapture(VOID)
 {
   NtUserSetCapture(NULL);
@@ -990,7 +1777,7 @@ STDCALL
 RealGetQueueStatus(UINT flags)
 {
    DWORD ret;
-   WORD changed_bits, wake_bits; 
+   WORD changed_bits, wake_bits;
 
 #if 0 /* wine stuff. don't know what it does... */
 
@@ -1011,12 +1798,12 @@ RealGetQueueStatus(UINT flags)
 /*
  * @unimplemented
  */
-WINBOOL STDCALL GetInputState(VOID)
+BOOL STDCALL GetInputState(VOID)
 {
    DWORD ret;
    WORD  wake_bits;
 
-#if 0 /* wine stuff. don't know what it does... */ 
+#if 0 /* wine stuff. don't know what it does... */
 
    /* check for pending X events */
    if (USER_Driver.pMsgWaitForMultipleObjectsEx)
@@ -1024,22 +1811,79 @@ WINBOOL STDCALL GetInputState(VOID)
 #endif
 
    ret = NtUserGetQueueStatus(FALSE /*ClearChanges*/);
-   
+
    wake_bits = HIWORD(ret);
 
    return wake_bits & (QS_KEY | QS_MOUSEBUTTON);
 }
 
+
+NTSTATUS STDCALL
+User32CallWindowProcFromKernel(PVOID Arguments, ULONG ArgumentLength)
+{
+  PWINDOWPROC_CALLBACK_ARGUMENTS CallbackArgs;
+  MSG KMMsg, UMMsg;
+
+  /* Make sure we don't try to access mem beyond what we were given */
+  if (ArgumentLength < sizeof(WINDOWPROC_CALLBACK_ARGUMENTS))
+    {
+      return STATUS_INFO_LENGTH_MISMATCH;
+    }
+
+  CallbackArgs = (PWINDOWPROC_CALLBACK_ARGUMENTS) Arguments;
+  KMMsg.hwnd = CallbackArgs->Wnd;
+  KMMsg.message = CallbackArgs->Msg;
+  KMMsg.wParam = CallbackArgs->wParam;
+  /* Check if lParam is really a pointer and adjust it if it is */
+  if (0 <= CallbackArgs->lParamBufferSize)
+    {
+      if (ArgumentLength != sizeof(WINDOWPROC_CALLBACK_ARGUMENTS)
+                            + CallbackArgs->lParamBufferSize)
+        {
+          return STATUS_INFO_LENGTH_MISMATCH;
+        }
+      KMMsg.lParam = (LPARAM) ((char *) CallbackArgs + sizeof(WINDOWPROC_CALLBACK_ARGUMENTS));
+    }
+  else
+    {
+      if (ArgumentLength != sizeof(WINDOWPROC_CALLBACK_ARGUMENTS))
+        {
+          return STATUS_INFO_LENGTH_MISMATCH;
+        }
+      KMMsg.lParam = CallbackArgs->lParam;
+    }
+
+  if (WM_NCCALCSIZE == CallbackArgs->Msg && CallbackArgs->wParam)
+    {
+      NCCALCSIZE_PARAMS *Params = (NCCALCSIZE_PARAMS *) KMMsg.lParam;
+      Params->lppos = (PWINDOWPOS) (Params + 1);
+    }
+
+  if (! MsgiKMToUMMessage(&KMMsg, &UMMsg))
+    {
+    }
+
+  CallbackArgs->Result = IntCallWindowProcW(CallbackArgs->IsAnsiProc, CallbackArgs->Proc,
+                                            UMMsg.hwnd, UMMsg.message,
+                                            UMMsg.wParam, UMMsg.lParam);
+
+  if (! MsgiKMToUMReply(&KMMsg, &UMMsg, &CallbackArgs->Result))
+    {
+    }
+
+  return ZwCallbackReturn(CallbackArgs, ArgumentLength, STATUS_SUCCESS);
+}
+
 /*
  * @implemented
  */
-WINBOOL STDCALL SetMessageQueue(int cMessagesMax)
+BOOL STDCALL SetMessageQueue(int cMessagesMax)
 {
   /* Function does nothing on 32 bit windows */
   return TRUE;
 }
 typedef DWORD (WINAPI * RealGetQueueStatusProc)(UINT flags);
-typedef DWORD (WINAPI * RealMsgWaitForMultipleObjectsExProc)(DWORD nCount, LPHANDLE lpHandles, DWORD dwMilliseconds, DWORD dwWakeMask, DWORD dwFlags);
+typedef DWORD (WINAPI * RealMsgWaitForMultipleObjectsExProc)(DWORD nCount, CONST HANDLE *lpHandles, DWORD dwMilliseconds, DWORD dwWakeMask, DWORD dwFlags);
 
 typedef struct _USER_MESSAGE_PUMP_ADDRESSES {
        DWORD cbSize;
@@ -1053,14 +1897,14 @@ DWORD
 STDCALL
 RealMsgWaitForMultipleObjectsEx(
   DWORD nCount,
-  LPHANDLE pHandles,
+  CONST HANDLE *pHandles,
   DWORD dwMilliseconds,
   DWORD dwWakeMask,
   DWORD dwFlags);
 
 typedef BOOL (WINAPI * MESSAGEPUMPHOOKPROC)(BOOL Unregistering,PUSER_MESSAGE_PUMP_ADDRESSES MessagePumpAddresses);
 
-RTL_CRITICAL_SECTION gcsMPH;
+CRITICAL_SECTION gcsMPH;
 MESSAGEPUMPHOOKPROC gpfnInitMPH;
 DWORD gcLoadMPH = 0;
 USER_MESSAGE_PUMP_ADDRESSES gmph = {sizeof(USER_MESSAGE_PUMP_ADDRESSES),
@@ -1076,7 +1920,9 @@ BOOL WINAPI IsInsideMessagePumpHook()
 {
        if(!gfMessagePumpHook)
                return FALSE;
-       
+
+    /* This code checks if we're inside SendMessage. */
+#if 0
        /* Since our TEB doesnt match that of real windows, testing this value is useless until we know what it does
        PUCHAR NtTeb = (PUCHAR)NtCurrentTeb();
 
@@ -1085,6 +1931,7 @@ BOOL WINAPI IsInsideMessagePumpHook()
 
        if(**(PLONG*)&NtTeb[0x708] <= 0)
                return FALSE;*/
+#endif
 
        return TRUE;
 }
@@ -1100,10 +1947,10 @@ void WINAPI ResetMessagePumpHook(PUSER_MESSAGE_PUMP_ADDRESSES Addresses)
 
 BOOL WINAPI RegisterMessagePumpHook(MESSAGEPUMPHOOKPROC Hook)
 {
-       RtlEnterCriticalSection(&gcsMPH);
+       EnterCriticalSection(&gcsMPH);
        if(!Hook) {
                SetLastError(ERROR_INVALID_PARAMETER);
-               RtlLeaveCriticalSection(&gcsMPH);
+               LeaveCriticalSection(&gcsMPH);
                return FALSE;
        }
        if(!gcLoadMPH) {
@@ -1111,44 +1958,44 @@ BOOL WINAPI RegisterMessagePumpHook(MESSAGEPUMPHOOKPROC Hook)
                gpfnInitMPH = Hook;
                ResetMessagePumpHook(&Addresses);
                if(!Hook(FALSE, &Addresses) || !Addresses.cbSize) {
-                       RtlLeaveCriticalSection(&gcsMPH);
+                       LeaveCriticalSection(&gcsMPH);
                        return FALSE;
                }
                memcpy(&gmph, &Addresses, Addresses.cbSize);
        } else {
                if(gpfnInitMPH != Hook) {
-                       RtlLeaveCriticalSection(&gcsMPH);
+                       LeaveCriticalSection(&gcsMPH);
                        return FALSE;
                }
        }
        if(NtUserCallNoParam(NOPARAM_ROUTINE_INIT_MESSAGE_PUMP)) {
-               RtlLeaveCriticalSection(&gcsMPH);
+               LeaveCriticalSection(&gcsMPH);
                return FALSE;
        }
        if (!gcLoadMPH++) {
-               InterlockedExchange(&gfMessagePumpHook, 1);
+               InterlockedExchange((PLONG)&gfMessagePumpHook, 1);
        }
-       RtlLeaveCriticalSection(&gcsMPH);
+       LeaveCriticalSection(&gcsMPH);
        return TRUE;
 }
 
 BOOL WINAPI UnregisterMessagePumpHook(VOID)
 {
-       RtlEnterCriticalSection(&gcsMPH);
+       EnterCriticalSection(&gcsMPH);
        if(gcLoadMPH > 0) {
                if(NtUserCallNoParam(NOPARAM_ROUTINE_UNINIT_MESSAGE_PUMP)) {
                        gcLoadMPH--;
                        if(!gcLoadMPH) {
-                               InterlockedExchange(&gfMessagePumpHook, 0);
+                               InterlockedExchange((PLONG)&gfMessagePumpHook, 0);
                                gpfnInitMPH(TRUE, NULL);
                                ResetMessagePumpHook(&gmph);
                                gpfnInitMPH = 0;
                        }
-                       RtlLeaveCriticalSection(&gcsMPH);
+                       LeaveCriticalSection(&gcsMPH);
                        return TRUE;
                }
        }
-       RtlLeaveCriticalSection(&gcsMPH);
+       LeaveCriticalSection(&gcsMPH);
        return FALSE;
 }
 
@@ -1157,7 +2004,173 @@ DWORD WINAPI GetQueueStatus(UINT flags)
        return IsInsideMessagePumpHook() ? gmph.RealGetQueueStatus(flags) : RealGetQueueStatus(flags);
 }
 
-DWORD WINAPI MsgWaitForMultipleObjectsEx(DWORD nCount, LPHANDLE lpHandles, DWORD dwMilliseconds, DWORD dwWakeMask, DWORD dwFlags)
+/**
+ * @name RealMsgWaitForMultipleObjectsEx
+ *
+ * Wait either for either message arrival or for one of the passed events
+ * to be signalled.
+ *
+ * @param nCount
+ *        Number of handles in the pHandles array.
+ * @param pHandles
+ *        Handles of events to wait for.
+ * @param dwMilliseconds
+ *        Timeout interval.
+ * @param dwWakeMask
+ *        Mask specifying on which message events we should wakeup.
+ * @param dwFlags
+ *        Wait type (see MWMO_* constants).
+ *
+ * @implemented
+ */
+
+DWORD STDCALL
+RealMsgWaitForMultipleObjectsEx(
+   DWORD nCount,
+   const HANDLE *pHandles,
+   DWORD dwMilliseconds,
+   DWORD dwWakeMask,
+   DWORD dwFlags)
+{
+   LPHANDLE RealHandles;
+   HANDLE MessageQueueHandle;
+   DWORD Result;
+
+   if (dwFlags & ~(MWMO_WAITALL | MWMO_ALERTABLE | MWMO_INPUTAVAILABLE))
+   {
+      SetLastError(ERROR_INVALID_PARAMETER);
+      return WAIT_FAILED;
+   }
+
+/*
+   if (dwFlags & MWMO_INPUTAVAILABLE)
+   {
+      RealGetQueueStatus(dwWakeMask);
+   }
+   */
+
+   MessageQueueHandle = NtUserMsqSetWakeMask(dwWakeMask);
+   if (MessageQueueHandle == NULL)
+   {
+      SetLastError(0); /* ? */
+      return WAIT_FAILED;
+   }
+
+   RealHandles = HeapAlloc(GetProcessHeap(), 0, (nCount + 1) * sizeof(HANDLE));
+   if (RealHandles == NULL)
+   {
+      NtUserMsqClearWakeMask();
+      SetLastError(ERROR_NOT_ENOUGH_MEMORY);
+      return WAIT_FAILED;
+   }
+
+   RtlCopyMemory(RealHandles, pHandles, nCount * sizeof(HANDLE));
+   RealHandles[nCount] = MessageQueueHandle;
+
+   Result = WaitForMultipleObjectsEx(nCount + 1, RealHandles,
+                                     dwFlags & MWMO_WAITALL,
+                                     dwMilliseconds, dwFlags & MWMO_ALERTABLE);
+
+   HeapFree(GetProcessHeap(), 0, RealHandles);
+   NtUserMsqClearWakeMask();
+
+   return Result;
+}
+
+/*
+ * @implemented
+ */
+DWORD WINAPI
+MsgWaitForMultipleObjectsEx(
+   DWORD nCount,
+   CONST HANDLE *lpHandles,
+   DWORD dwMilliseconds,
+   DWORD dwWakeMask,
+   DWORD dwFlags)
+{
+   return IsInsideMessagePumpHook() ? gmph.RealMsgWaitForMultipleObjectsEx(nCount, lpHandles, dwMilliseconds, dwWakeMask, dwFlags) : RealMsgWaitForMultipleObjectsEx(nCount, lpHandles,dwMilliseconds, dwWakeMask, dwFlags);
+}
+
+/*
+ * @implemented
+ */
+DWORD STDCALL
+MsgWaitForMultipleObjects(
+   DWORD nCount,
+   CONST HANDLE *lpHandles,
+   BOOL fWaitAll,
+   DWORD dwMilliseconds,
+   DWORD dwWakeMask)
+{
+   return MsgWaitForMultipleObjectsEx(nCount, lpHandles, dwMilliseconds,
+                                      dwWakeMask, fWaitAll ? MWMO_WAITALL : 0);
+}
+
+
+BOOL FASTCALL MessageInit(VOID)
+{
+  InitializeCriticalSection(&DdeCrst);
+  InitializeCriticalSection(&MsgConversionCrst);
+  InitializeCriticalSection(&gcsMPH);
+
+  return TRUE;
+}
+
+VOID FASTCALL MessageCleanup(VOID)
+{
+  DeleteCriticalSection(&DdeCrst);
+  DeleteCriticalSection(&MsgConversionCrst);
+  DeleteCriticalSection(&gcsMPH);
+}
+
+/***********************************************************************
+ *             map_wparam_AtoW
+ *
+ * Convert the wparam of an ASCII message to Unicode.
+ */
+static WPARAM
+map_wparam_AtoW( UINT message, WPARAM wparam )
+{
+    switch(message)
+    {
+    case WM_CHARTOITEM:
+    case EM_SETPASSWORDCHAR:
+    case WM_CHAR:
+    case WM_DEADCHAR:
+    case WM_SYSCHAR:
+    case WM_SYSDEADCHAR:
+    case WM_MENUCHAR:
+        {
+            char ch[2];
+            WCHAR wch[2];
+            ch[0] = (wparam & 0xff);
+            ch[1] = (wparam >> 8);
+            MultiByteToWideChar(CP_ACP, 0, ch, 2, wch, 2);
+            wparam = MAKEWPARAM(wch[0], wch[1]);
+        }
+        break;
+    case WM_IME_CHAR:
+        {
+            char ch[2];
+            WCHAR wch;
+            ch[0] = (wparam >> 8);
+            ch[1] = (wparam & 0xff);
+            if (ch[0]) MultiByteToWideChar(CP_ACP, 0, ch, 2, &wch, 1);
+            else MultiByteToWideChar(CP_ACP, 0, &ch[1], 1, &wch, 1);
+            wparam = MAKEWPARAM( wch, HIWORD(wparam) );
+        }
+        break;
+    }
+    return wparam;
+}
+
+/*
+ * @implemented
+ */
+BOOL WINAPI
+IsDialogMessageA( HWND hwndDlg, LPMSG pmsg )
 {
-       return IsInsideMessagePumpHook() ? gmph.RealMsgWaitForMultipleObjectsEx(nCount, lpHandles,dwMilliseconds, dwWakeMask, dwFlags) : RealMsgWaitForMultipleObjectsEx(nCount, lpHandles,dwMilliseconds, dwWakeMask, dwFlags);
+    MSG msg = *pmsg;
+    msg.wParam = map_wparam_AtoW( msg.message, msg.wParam );
+    return IsDialogMessageW( hwndDlg, &msg );
 }