/*
 * COPYRIGHT:       See COPYING in the top level directory
 * PROJECT:         ReactOS user32.dll
 * FILE:            win32ss/user/user32/windows/message.c
 * PURPOSE:         Messages
 * PROGRAMMER:      Casper S. Hornstrup (chorns@users.sourceforge.net)
 * UPDATE HISTORY:
 *      06-06-2001  CSH  Created
 */

#include <user32.h>

WINE_DEFAULT_DEBUG_CHANNEL(user32);

#ifdef __i386__
/* For bad applications which provide bad (non stdcall) WndProc */
extern
LRESULT
__cdecl
CALL_EXTERN_WNDPROC(
     WNDPROC WndProc,
     HWND hWnd,
     UINT Msg,
     WPARAM wParam,
     LPARAM lParam);
#else
#  define CALL_EXTERN_WNDPROC(proc, h, m, w, l) proc(h, m, w, l)
#endif

/* From wine: */
/* flag for messages that contain pointers */
/* 32 messages per entry, messages 0..31 map to bits 0..31 */

#define SET(msg) (1 << ((msg) & 31))

static const unsigned int message_pointer_flags[] =
{
    /* 0x00 - 0x1f */
    SET(WM_CREATE) | SET(WM_SETTEXT) | SET(WM_GETTEXT) |
    SET(WM_WININICHANGE) | SET(WM_DEVMODECHANGE),
    /* 0x20 - 0x3f */
    SET(WM_GETMINMAXINFO) | SET(WM_DRAWITEM) | SET(WM_MEASUREITEM) | SET(WM_DELETEITEM) |
    SET(WM_COMPAREITEM),
    /* 0x40 - 0x5f */
    SET(WM_WINDOWPOSCHANGING) | SET(WM_WINDOWPOSCHANGED) | SET(WM_COPYDATA) | SET(WM_COPYGLOBALDATA) | SET(WM_HELP),
    /* 0x60 - 0x7f */
    SET(WM_STYLECHANGING) | SET(WM_STYLECHANGED),
    /* 0x80 - 0x9f */
    SET(WM_NCCREATE) | SET(WM_NCCALCSIZE) | SET(WM_GETDLGCODE),
    /* 0xa0 - 0xbf */
    SET(EM_GETSEL) | SET(EM_GETRECT) | SET(EM_SETRECT) | SET(EM_SETRECTNP),
    /* 0xc0 - 0xdf */
    SET(EM_REPLACESEL) | SET(EM_GETLINE) | SET(EM_SETTABSTOPS),
    /* 0xe0 - 0xff */
    SET(SBM_GETRANGE) | SET(SBM_SETSCROLLINFO) | SET(SBM_GETSCROLLINFO) | SET(SBM_GETSCROLLBARINFO),
    /* 0x100 - 0x11f */
    0,
    /* 0x120 - 0x13f */
    0,
    /* 0x140 - 0x15f */
    SET(CB_GETEDITSEL) | SET(CB_ADDSTRING) | SET(CB_DIR) | SET(CB_GETLBTEXT) |
    SET(CB_INSERTSTRING) | SET(CB_FINDSTRING) | SET(CB_SELECTSTRING) |
    SET(CB_GETDROPPEDCONTROLRECT) | SET(CB_FINDSTRINGEXACT),
    /* 0x160 - 0x17f */
    0,
    /* 0x180 - 0x19f */
    SET(LB_ADDSTRING) | SET(LB_INSERTSTRING) | SET(LB_GETTEXT) | SET(LB_SELECTSTRING) |
    SET(LB_DIR) | SET(LB_FINDSTRING) |
    SET(LB_GETSELITEMS) | SET(LB_SETTABSTOPS) | SET(LB_ADDFILE) | SET(LB_GETITEMRECT),
    /* 0x1a0 - 0x1bf */
    SET(LB_FINDSTRINGEXACT),
    /* 0x1c0 - 0x1df */
    0,
    /* 0x1e0 - 0x1ff */
    0,
    /* 0x200 - 0x21f */
    SET(WM_NEXTMENU) | SET(WM_SIZING) | SET(WM_MOVING) | SET(WM_DEVICECHANGE),
    /* 0x220 - 0x23f */
    SET(WM_MDICREATE) | SET(WM_MDIGETACTIVE) | SET(WM_DROPOBJECT) |
    SET(WM_QUERYDROPOBJECT) | SET(WM_DRAGLOOP) | SET(WM_DRAGSELECT) | SET(WM_DRAGMOVE),
    /* 0x240 - 0x25f */
    0,
    /* 0x260 - 0x27f */
    0,
    /* 0x280 - 0x29f */
    0,
    /* 0x2a0 - 0x2bf */
    0,
    /* 0x2c0 - 0x2df */
    0,
    /* 0x2e0 - 0x2ff */
    0,
    /* 0x300 - 0x31f */
    SET(WM_ASKCBFORMATNAME)
};

/* check whether a given message type includes pointers */
static inline int is_pointer_message( UINT message )
{
    if (message >= 8*sizeof(message_pointer_flags)) return FALSE;
        return (message_pointer_flags[message / 32] & SET(message)) != 0;
}

#undef SET

/* check whether a combobox expects strings or ids in CB_ADDSTRING/CB_INSERTSTRING */
static BOOL FASTCALL combobox_has_strings( HWND hwnd )
{
    DWORD style = GetWindowLongA( hwnd, GWL_STYLE );
    return (!(style & (CBS_OWNERDRAWFIXED | CBS_OWNERDRAWVARIABLE)) || (style & CBS_HASSTRINGS));
}

/* check whether a listbox expects strings or ids in LB_ADDSTRING/LB_INSERTSTRING */
static BOOL FASTCALL listbox_has_strings( HWND hwnd )
{
    DWORD style = GetWindowLongA( hwnd, GWL_STYLE );
    return (!(style & (LBS_OWNERDRAWFIXED | LBS_OWNERDRAWVARIABLE)) || (style & LBS_HASSTRINGS));
}

/* 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.
 */

/* 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.
 */

typedef struct tagDDEPAIR
{
  HGLOBAL     ClientMem;
  HGLOBAL     ServerMem;
} DDEPAIR, *PDDEPAIR;

static PDDEPAIR DdePairs = NULL;
static unsigned DdeNumAlloc = 0;
static unsigned DdeNumUsed = 0;
static CRITICAL_SECTION DdeCrst;

BOOL FASTCALL
DdeAddPair(HGLOBAL ClientMem, HGLOBAL ServerMem)
{
  unsigned i;

  EnterCriticalSection(&DdeCrst);

  /* 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;
}

HGLOBAL FASTCALL
DdeGetPair(HGLOBAL ServerMem)
{
  unsigned i;
  HGLOBAL Ret = NULL;

  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;
}

DWORD FASTCALL get_input_codepage( void )
{
    DWORD cp;
    int ret;
    HKL hkl = GetKeyboardLayout( 0 );
    ret = GetLocaleInfoW( LOWORD(hkl), LOCALE_IDEFAULTANSICODEPAGE | LOCALE_RETURN_NUMBER, (WCHAR *)&cp, sizeof(cp) / sizeof(WCHAR) );
    if (!ret) cp = CP_ACP;
    return cp;
}

static WPARAM FASTCALL map_wparam_char_WtoA( WPARAM wParam, DWORD len )
{
    WCHAR wch = wParam;
    BYTE ch[2];
    DWORD cp = get_input_codepage();

    len = WideCharToMultiByte( cp, 0, &wch, 1, (LPSTR)ch, len, NULL, NULL );
    if (len == 2)
       return MAKEWPARAM( (ch[0] << 8) | ch[1], HIWORD(wParam) );
    else
    return MAKEWPARAM( ch[0], HIWORD(wParam) );
}

/***********************************************************************
 *		map_wparam_AtoW
 *
 * Convert the wparam of an ASCII message to Unicode.
 */
static WPARAM FASTCALL
map_wparam_AtoW( UINT message, WPARAM wparam )
{
    char ch[2];
    WCHAR wch[2];

    wch[0] = wch[1] = 0;
    switch(message)
    {
    case WM_CHAR:
        /* WM_CHAR is magic: a DBCS char can be sent/posted as two consecutive WM_CHAR
         * messages, in which case the first char is stored, and the conversion
         * to Unicode only takes place once the second char is sent/posted.
         */
#if 0
        if (mapping != WMCHAR_MAP_NOMAPPING) // NlsMbCodePageTag
        {
            PCLIENTINFO pci = GetWin32ClientInfo();

            struct wm_char_mapping_data *data = get_user_thread_info()->wmchar_data;

            BYTE low = LOBYTE(wparam);

            if (HIBYTE(wparam))
            {
                ch[0] = low;
                ch[1] = HIBYTE(wparam);
                RtlMultiByteToUnicodeN( wch, sizeof(wch), NULL, ch, 2 );
                TRACE( "map %02x,%02x -> %04x mapping %u\n", (BYTE)ch[0], (BYTE)ch[1], wch[0], mapping );
                if (data) data->lead_byte[mapping] = 0;
            }
            else if (data && data->lead_byte[mapping])
            {
                ch[0] = data->lead_byte[mapping];
                ch[1] = low;
                RtlMultiByteToUnicodeN( wch, sizeof(wch), NULL, ch, 2 );
                TRACE( "map stored %02x,%02x -> %04x mapping %u\n", (BYTE)ch[0], (BYTE)ch[1], wch[0], mapping );
                data->lead_byte[mapping] = 0;
            }
            else if (!IsDBCSLeadByte( low ))
            {
                ch[0] = low;
                RtlMultiByteToUnicodeN( wch, sizeof(wch), NULL, ch, 1 );
                TRACE( "map %02x -> %04x\n", (BYTE)ch[0], wch[0] );
                if (data) data->lead_byte[mapping] = 0;
            }
            else  /* store it and wait for trail byte */
            {
                if (!data)
                {
                    if (!(data = HeapAlloc( GetProcessHeap(), HEAP_ZERO_MEMORY, sizeof(*data) )))
                        return FALSE;
                    get_user_thread_info()->wmchar_data = data;
                }
                TRACE( "storing lead byte %02x mapping %u\n", low, mapping );
                data->lead_byte[mapping] = low;
                return FALSE;
            }
            wparam = MAKEWPARAM(wch[0], wch[1]);
            break;
        }
#endif
        /* else fall through */
    case WM_CHARTOITEM:
    case EM_SETPASSWORDCHAR:
    case WM_DEADCHAR:
    case WM_SYSCHAR:
    case WM_SYSDEADCHAR:
    case WM_MENUCHAR:
        ch[0] = LOBYTE(wparam);
        ch[1] = HIBYTE(wparam);
        RtlMultiByteToUnicodeN( wch, sizeof(wch), NULL, ch, 2 );
        wparam = MAKEWPARAM(wch[0], wch[1]);
        break;
    case WM_IME_CHAR:
        ch[0] = HIBYTE(wparam);
        ch[1] = LOBYTE(wparam);
        if (ch[0]) RtlMultiByteToUnicodeN( wch, sizeof(wch[0]), NULL, ch, 2 );
        else RtlMultiByteToUnicodeN( wch, sizeof(wch[0]), NULL, ch + 1, 1 );
        wparam = MAKEWPARAM(wch[0], HIWORD(wparam));
        break;
    }
    return wparam;
}

static
BOOL FASTCALL
MsgiUMToKMMessage(PMSG UMMsg, PMSG KMMsg, BOOL Posted)
{
  *KMMsg = *UMMsg;

  switch (UMMsg->message)
    {
      case WM_COPYDATA:
        {
          PCOPYDATASTRUCT pUMCopyData = (PCOPYDATASTRUCT)UMMsg->lParam;
          PCOPYDATASTRUCT pKMCopyData;

          pKMCopyData = HeapAlloc(GetProcessHeap(), 0, sizeof(COPYDATASTRUCT) + pUMCopyData->cbData);
          if (!pKMCopyData)
          {
              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;

      case WM_COPYGLOBALDATA:
        {
           KMMsg->lParam = (LPARAM)GlobalLock((HGLOBAL)UMMsg->lParam);;
           TRACE("WM_COPYGLOBALDATA get data ptr %p\n",KMMsg->lParam);
        }
        break;

      default:
        break;
    }

  return TRUE;
}

static
VOID FASTCALL
MsgiUMToKMCleanup(PMSG UMMsg, PMSG KMMsg)
{
  switch (KMMsg->message)
    {
      case WM_COPYDATA:
        HeapFree(GetProcessHeap(), 0, (LPVOID) KMMsg->lParam);
        break;
      case WM_COPYGLOBALDATA:
        TRACE("WM_COPYGLOBALDATA cleanup return\n");
        GlobalUnlock((HGLOBAL)UMMsg->lParam);
        GlobalFree((HGLOBAL)UMMsg->lParam);
        break;
      default:
        break;
    }

  return;
}

static BOOL FASTCALL
MsgiKMToUMMessage(PMSG KMMsg, PMSG UMMsg)
{
  *UMMsg = *KMMsg;

  if (KMMsg->lParam == 0) return TRUE;

  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_COPYDATA:
        {
          PCOPYDATASTRUCT pKMCopyData = (PCOPYDATASTRUCT)KMMsg->lParam;
          pKMCopyData->lpData = pKMCopyData + 1;
        }
        break;

      case WM_COPYGLOBALDATA:
        {
           PVOID Data;
           HGLOBAL hGlobal = GlobalAlloc(GHND | GMEM_SHARE, KMMsg->wParam);
           Data = GlobalLock(hGlobal);
           if (Data) RtlCopyMemory(Data, (PVOID)KMMsg->lParam, KMMsg->wParam);
           GlobalUnlock(hGlobal);
           TRACE("WM_COPYGLOBALDATA to User hGlobal %p\n",hGlobal);
           UMMsg->lParam = (LPARAM)hGlobal;
        }
        break;

      default:
        break;
    }

  return TRUE;
}

static VOID FASTCALL
MsgiKMToUMCleanup(PMSG KMMsg, PMSG UMMsg)
{
  switch (KMMsg->message)
    {
      case WM_DDE_EXECUTE:
#ifdef TODO // Kept as historic.
        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);

  return TRUE;
}

//
//  Ansi to Unicode -> callout
//
static BOOL FASTCALL
MsgiAnsiToUnicodeMessage(HWND hwnd, LPMSG UnicodeMsg, LPMSG AnsiMsg)
{
  UNICODE_STRING UnicodeString;

  *UnicodeMsg = *AnsiMsg;

  switch (AnsiMsg->message)
    {
    case WM_GETTEXT:
    case WM_ASKCBFORMATNAME:
      {
        LPWSTR Buffer;
        if (!AnsiMsg->lParam) break;
        Buffer = RtlAllocateHeap(GetProcessHeap(), HEAP_ZERO_MEMORY, AnsiMsg->wParam * sizeof(WCHAR));
        //ERR("WM_GETTEXT A2U Size %d\n",AnsiMsg->wParam);
        if (!Buffer) return FALSE;
        UnicodeMsg->lParam = (LPARAM)Buffer;
        break;
      }

    case LB_GETTEXT:
      {
        DWORD Size = 1024 * sizeof(WCHAR);
        if (!AnsiMsg->lParam || !listbox_has_strings( AnsiMsg->hwnd )) break;
        /*Size = SendMessageW( AnsiMsg->hwnd, LB_GETTEXTLEN, AnsiMsg->wParam, 0 );
        if (Size == LB_ERR)
        {
           ERR("LB_GETTEXT LB_ERR\n");
           Size = sizeof(ULONG_PTR);
        }
        Size = (Size + 1) * sizeof(WCHAR);*/
        UnicodeMsg->lParam = (LPARAM) RtlAllocateHeap(GetProcessHeap(), HEAP_ZERO_MEMORY, Size);
        if (!UnicodeMsg->lParam) return FALSE;
        break;
      }

    case CB_GETLBTEXT:
      {
        DWORD Size = 1024 * sizeof(WCHAR);
        if (!AnsiMsg->lParam || !combobox_has_strings( AnsiMsg->hwnd )) break;
        /*Size = SendMessageW( AnsiMsg->hwnd, CB_GETLBTEXTLEN, AnsiMsg->wParam, 0 );
        if (Size == LB_ERR)
        {
           ERR("CB_GETTEXT LB_ERR\n");
           Size = sizeof(ULONG_PTR);
        }
        Size = (Size + 1) * sizeof(WCHAR);*/
        UnicodeMsg->lParam = (LPARAM) RtlAllocateHeap(GetProcessHeap(), HEAP_ZERO_MEMORY, Size);
        if (!UnicodeMsg->lParam) return FALSE;
        break;
      }

    /* AnsiMsg->lParam is string (0-terminated) */
    case WM_SETTEXT:
    case WM_WININICHANGE:
    case WM_DEVMODECHANGE:
    case CB_DIR:
    case LB_DIR:
    case LB_ADDFILE:
    case EM_REPLACESEL:
      {
        if (!AnsiMsg->lParam) break;
        RtlCreateUnicodeStringFromAsciiz(&UnicodeString, (LPSTR)AnsiMsg->lParam);
        UnicodeMsg->lParam = (LPARAM)UnicodeString.Buffer;
        break;
      }

    case LB_ADDSTRING:
    case LB_ADDSTRING_LOWER:
    case LB_ADDSTRING_UPPER:
    case LB_INSERTSTRING:
    case LB_INSERTSTRING_UPPER:
    case LB_INSERTSTRING_LOWER:
    case LB_FINDSTRING:
    case LB_FINDSTRINGEXACT:
    case LB_SELECTSTRING:
      {
        if (AnsiMsg->lParam && listbox_has_strings(AnsiMsg->hwnd))
          {
            RtlCreateUnicodeStringFromAsciiz(&UnicodeString, (LPSTR)AnsiMsg->lParam);
            UnicodeMsg->lParam = (LPARAM)UnicodeString.Buffer;
          }
        break;
      }

    case CB_ADDSTRING:
    case CB_INSERTSTRING:
    case CB_FINDSTRING:
    case CB_FINDSTRINGEXACT:
    case CB_SELECTSTRING:
      {
        if (AnsiMsg->lParam && combobox_has_strings(AnsiMsg->hwnd))
          {
            RtlCreateUnicodeStringFromAsciiz(&UnicodeString, (LPSTR)AnsiMsg->lParam);
            UnicodeMsg->lParam = (LPARAM)UnicodeString.Buffer;
          }
        break;
      }

    case WM_NCCREATE:
    case WM_CREATE:
      {
        struct s
        {
           CREATESTRUCTW cs;          /* new structure */
           MDICREATESTRUCTW mdi_cs;   /* MDI info */
           LPCWSTR lpszName;          /* allocated Name */
           LPCWSTR lpszClass;         /* allocated Class */
        };

        struct s *xs = HeapAlloc(GetProcessHeap(), HEAP_ZERO_MEMORY, sizeof(struct s));
        if (!xs)
          {
            return FALSE;
          }
        xs->cs = *(CREATESTRUCTW *)AnsiMsg->lParam;
        if (!IS_INTRESOURCE(xs->cs.lpszName))
          {
            RtlCreateUnicodeStringFromAsciiz(&UnicodeString, (LPSTR)xs->cs.lpszName);
            xs->lpszName = xs->cs.lpszName = UnicodeString.Buffer;
          }
        if (!IS_ATOM(xs->cs.lpszClass))
          {
            RtlCreateUnicodeStringFromAsciiz(&UnicodeString, (LPSTR)xs->cs.lpszClass);
            xs->lpszClass = xs->cs.lpszClass = UnicodeString.Buffer;
          }

        if (GetWindowLongW(hwnd, GWL_EXSTYLE) & WS_EX_MDICHILD)
        {
           xs->mdi_cs = *(MDICREATESTRUCTW *)xs->cs.lpCreateParams;
           xs->mdi_cs.szTitle = xs->cs.lpszName;
           xs->mdi_cs.szClass = xs->cs.lpszClass;
           xs->cs.lpCreateParams = &xs->mdi_cs;
        }

        UnicodeMsg->lParam = (LPARAM)xs;
        break;
      }

    case WM_MDICREATE:
      {
        MDICREATESTRUCTW *cs =
            (MDICREATESTRUCTW *)HeapAlloc(GetProcessHeap(), 0, sizeof(*cs));

        if (!cs)
          {
            return FALSE;
          }

        *cs = *(MDICREATESTRUCTW *)AnsiMsg->lParam;

        if (!IS_ATOM(cs->szClass))
          {
            RtlCreateUnicodeStringFromAsciiz(&UnicodeString, (LPSTR)cs->szClass);
            cs->szClass = UnicodeString.Buffer;
          }

        RtlCreateUnicodeStringFromAsciiz(&UnicodeString, (LPSTR)cs->szTitle);
        cs->szTitle = UnicodeString.Buffer;

        UnicodeMsg->lParam = (LPARAM)cs;
        break;
      }

    case WM_GETDLGCODE:
      if (UnicodeMsg->lParam)
      {
         MSG newmsg = *(MSG *)UnicodeMsg->lParam;
         newmsg.wParam = map_wparam_AtoW( newmsg.message, newmsg.wParam);
      }
      break;

    case WM_CHARTOITEM:
    case WM_MENUCHAR:
    case WM_CHAR:
    case WM_DEADCHAR:
    case WM_SYSCHAR:
    case WM_SYSDEADCHAR:
    case EM_SETPASSWORDCHAR:
    case WM_IME_CHAR:
      UnicodeMsg->wParam = map_wparam_AtoW( AnsiMsg->message, AnsiMsg->wParam );
      break;
    case EM_GETLINE:
      ERR("FIXME EM_GETLINE A2U\n");
      break;

    }

  return TRUE;
}

static BOOL FASTCALL
MsgiAnsiToUnicodeCleanup(LPMSG UnicodeMsg, LPMSG AnsiMsg)
{
  UNICODE_STRING UnicodeString;

  switch (AnsiMsg->message)
    {
    case LB_GETTEXT:
        if (!listbox_has_strings( UnicodeMsg->hwnd )) break;
    case CB_GETLBTEXT:
        if (UnicodeMsg->message == CB_GETLBTEXT && !combobox_has_strings( UnicodeMsg->hwnd )) break;
    case WM_GETTEXT:
    case WM_ASKCBFORMATNAME:
      {
        if (!UnicodeMsg->lParam) break;
        RtlFreeHeap(GetProcessHeap(), 0, (PVOID) UnicodeMsg->lParam);
        break;
      }

    case WM_SETTEXT:
    case WM_WININICHANGE:
    case WM_DEVMODECHANGE:
    case CB_DIR:
    case LB_DIR:
    case LB_ADDFILE:
    case EM_REPLACESEL:
      {
        if (!UnicodeMsg->lParam) break;
        RtlInitUnicodeString(&UnicodeString, (PCWSTR)UnicodeMsg->lParam);
        RtlFreeUnicodeString(&UnicodeString);
        break;
      }

    case LB_ADDSTRING:
    case LB_ADDSTRING_LOWER:
    case LB_ADDSTRING_UPPER:
    case LB_INSERTSTRING:
    case LB_INSERTSTRING_UPPER:
    case LB_INSERTSTRING_LOWER:
    case LB_FINDSTRING:
    case LB_FINDSTRINGEXACT:
    case LB_SELECTSTRING:
      {
        if (UnicodeMsg->lParam && listbox_has_strings(AnsiMsg->hwnd))
          {
            RtlInitUnicodeString(&UnicodeString, (PCWSTR)UnicodeMsg->lParam);
            RtlFreeUnicodeString(&UnicodeString);
          }
        break;
      }

    case CB_ADDSTRING:
    case CB_INSERTSTRING:
    case CB_FINDSTRING:
    case CB_FINDSTRINGEXACT:
    case CB_SELECTSTRING:
      {
        if (UnicodeMsg->lParam && combobox_has_strings(AnsiMsg->hwnd))
          {
            RtlInitUnicodeString(&UnicodeString, (PCWSTR)UnicodeMsg->lParam);
            RtlFreeUnicodeString(&UnicodeString);
          }
        break;
      }

    case WM_NCCREATE:
    case WM_CREATE:
      {
        struct s
        {
           CREATESTRUCTW cs;          /* new structure */
           MDICREATESTRUCTW mdi_cs;   /* MDI info */
           LPWSTR lpszName;           /* allocated Name */
           LPWSTR lpszClass;          /* allocated Class */
        };

        struct s *xs = (struct s *)UnicodeMsg->lParam;
        if (xs->lpszName)
          {
            RtlInitUnicodeString(&UnicodeString, (PCWSTR)xs->lpszName);
            RtlFreeUnicodeString(&UnicodeString);
          }
        if (xs->lpszClass)
          {
            RtlInitUnicodeString(&UnicodeString, (PCWSTR)xs->lpszClass);
            RtlFreeUnicodeString(&UnicodeString);
          }
        HeapFree(GetProcessHeap(), 0, xs);
        break;
      }

    case WM_MDICREATE:
      {
        MDICREATESTRUCTW *cs = (MDICREATESTRUCTW *)UnicodeMsg->lParam;
        RtlInitUnicodeString(&UnicodeString, (PCWSTR)cs->szTitle);
        RtlFreeUnicodeString(&UnicodeString);
        if (!IS_ATOM(cs->szClass))
          {
            RtlInitUnicodeString(&UnicodeString, (PCWSTR)cs->szClass);
            RtlFreeUnicodeString(&UnicodeString);
          }
        HeapFree(GetProcessHeap(), 0, cs);
        break;
      }
    }

  return(TRUE);
}

/*
 *    callout return -> Unicode Result to Ansi Result
 */
static BOOL FASTCALL
MsgiAnsiToUnicodeReply(LPMSG UnicodeMsg, LPMSG AnsiMsg, LRESULT *Result)
{
  LPWSTR Buffer = (LPWSTR)UnicodeMsg->lParam;
  LPSTR AnsiBuffer = (LPSTR)AnsiMsg->lParam;

  switch (AnsiMsg->message)
    {
    case WM_GETTEXT:
    case WM_ASKCBFORMATNAME:
      {
        if (UnicodeMsg->wParam)
        {
           DWORD len = 0;
           if (*Result) RtlUnicodeToMultiByteN( AnsiBuffer, UnicodeMsg->wParam - 1, &len, Buffer, strlenW(Buffer) * sizeof(WCHAR));
           AnsiBuffer[len] = 0;
           *Result = len;
           //ERR("WM_GETTEXT U2A Result %d Size %d\n",*Result,AnsiMsg->wParam);
        }
        break;
      }
    case LB_GETTEXT:
      {
        if (!AnsiBuffer || !listbox_has_strings( UnicodeMsg->hwnd )) break;
        if (*Result >= 0)
        {
           DWORD len;
           RtlUnicodeToMultiByteN( AnsiBuffer, ~0u, &len, Buffer, (strlenW(Buffer) + 1) * sizeof(WCHAR) );
           *Result = len - 1;
        }
        break;
      }
    case CB_GETLBTEXT:
      {
        if (!AnsiBuffer || !combobox_has_strings( UnicodeMsg->hwnd )) break;
        if (*Result >= 0)
        {
           DWORD len;
           RtlUnicodeToMultiByteN( AnsiBuffer, ~0u, &len, Buffer, (strlenW(Buffer) + 1) * sizeof(WCHAR) );
           *Result = len - 1;
        }
        break;
      }
    }

  MsgiAnsiToUnicodeCleanup(UnicodeMsg, AnsiMsg);

  return TRUE;
}

//
//  Unicode to Ansi callout ->
//
static BOOL FASTCALL
MsgiUnicodeToAnsiMessage(HWND hwnd, LPMSG AnsiMsg, LPMSG UnicodeMsg)
{
  ANSI_STRING AnsiString;
  UNICODE_STRING UnicodeString;

  *AnsiMsg = *UnicodeMsg;

  switch(UnicodeMsg->message)
    {
      case WM_CREATE:
      case WM_NCCREATE:
        {
          MDICREATESTRUCTA *pmdi_cs;
          CREATESTRUCTA* CsA;
          CREATESTRUCTW* CsW;
          ULONG NameSize, ClassSize;
          NTSTATUS Status;

          CsW = (CREATESTRUCTW*)(UnicodeMsg->lParam);
          RtlInitUnicodeString(&UnicodeString, CsW->lpszName);
          NameSize = RtlUnicodeStringToAnsiSize(&UnicodeString);
          if (NameSize == 0)
            {
              return FALSE;
            }
          ClassSize = 0;
          if (!IS_ATOM(CsW->lpszClass))
            {
              RtlInitUnicodeString(&UnicodeString, CsW->lpszClass);
              ClassSize = RtlUnicodeStringToAnsiSize(&UnicodeString);
              if (ClassSize == 0)
                {
                  return FALSE;
                }
            }

          CsA = RtlAllocateHeap(GetProcessHeap(), 0, sizeof(CREATESTRUCTA) + sizeof(MDICREATESTRUCTA) + NameSize + ClassSize);
          if (NULL == CsA)
            {
              return FALSE;
            }
          memcpy(CsA, CsW, sizeof(CREATESTRUCTW));

          /* pmdi_cs starts right after CsA */
          pmdi_cs = (MDICREATESTRUCTA*)(CsA + 1);

          RtlInitUnicodeString(&UnicodeString, CsW->lpszName);
          RtlInitEmptyAnsiString(&AnsiString, (PCHAR)(pmdi_cs + 1), NameSize);
          Status = RtlUnicodeStringToAnsiString(&AnsiString, &UnicodeString, FALSE);
          if (! NT_SUCCESS(Status))
            {
              RtlFreeHeap(GetProcessHeap(), 0, CsA);
              return FALSE;
            }
          CsA->lpszName = AnsiString.Buffer;
          if (!IS_ATOM(CsW->lpszClass))
            {
              RtlInitUnicodeString(&UnicodeString, CsW->lpszClass);
              RtlInitEmptyAnsiString(&AnsiString, (PCHAR)(pmdi_cs + 1) + NameSize, ClassSize);
              Status = RtlUnicodeStringToAnsiString(&AnsiString, &UnicodeString, FALSE);
              if (! NT_SUCCESS(Status))
                {
                  RtlFreeHeap(GetProcessHeap(), 0, CsA);
                  return FALSE;
                }
              CsA->lpszClass = AnsiString.Buffer;
            }

          if (GetWindowLongW(hwnd, GWL_EXSTYLE) & WS_EX_MDICHILD)
          {
             *pmdi_cs = *(MDICREATESTRUCTA *)CsW->lpCreateParams;
             pmdi_cs->szTitle = CsA->lpszName;
             pmdi_cs->szClass = CsA->lpszClass;
             CsA->lpCreateParams = pmdi_cs;
          }

          AnsiMsg->lParam = (LPARAM)CsA;
          break;
        }
      case WM_GETTEXT:
      case WM_ASKCBFORMATNAME:
        {
          if (!UnicodeMsg->lParam) break;
          /* Ansi string might contain MBCS chars so we need 2 * the number of chars */
          AnsiMsg->lParam = (LPARAM) RtlAllocateHeap(GetProcessHeap(), HEAP_ZERO_MEMORY, UnicodeMsg->wParam * 2);
          //ERR("WM_GETTEXT U2A Size %d\n",AnsiMsg->wParam);

          if (!AnsiMsg->lParam) return FALSE;
          break;
        }

    case LB_GETTEXT:
      {
        DWORD Size = 1024;
        if (!UnicodeMsg->lParam || !listbox_has_strings( UnicodeMsg->hwnd )) break;
        /*Size = SendMessageA( UnicodeMsg->hwnd, LB_GETTEXTLEN, UnicodeMsg->wParam, 0 );
        if (Size == LB_ERR)
        {
           ERR("LB_GETTEXT LB_ERR\n");
           Size = sizeof(ULONG_PTR);
        }
        Size = (Size + 1) * sizeof(WCHAR);*/
        AnsiMsg->lParam = (LPARAM) RtlAllocateHeap(GetProcessHeap(), HEAP_ZERO_MEMORY, Size);
        if (!AnsiMsg->lParam) return FALSE;
        break;
      }

    case CB_GETLBTEXT:
      {
        DWORD Size = 1024;
        if (!UnicodeMsg->lParam || !combobox_has_strings( UnicodeMsg->hwnd )) break;
        /*Size = SendMessageA( UnicodeMsg->hwnd, CB_GETLBTEXTLEN, UnicodeMsg->wParam, 0 );
        if (Size == LB_ERR)
        {
           ERR("CB_GETTEXT LB_ERR\n");
           Size = sizeof(ULONG_PTR);
        }
        Size = (Size + 1) * sizeof(WCHAR);*/
        AnsiMsg->lParam = (LPARAM) RtlAllocateHeap(GetProcessHeap(), HEAP_ZERO_MEMORY, Size);
        if (!AnsiMsg->lParam) return FALSE;
        break;
      }

      case WM_SETTEXT:
      case WM_WININICHANGE:
      case WM_DEVMODECHANGE:
      case CB_DIR:
      case LB_DIR:
      case LB_ADDFILE:
      case EM_REPLACESEL:
        {
          if (!UnicodeMsg->lParam) break;
          RtlInitUnicodeString(&UnicodeString, (PWSTR) UnicodeMsg->lParam);
          if (! NT_SUCCESS(RtlUnicodeStringToAnsiString(&AnsiString,
                                                        &UnicodeString,
                                                        TRUE)))
            {
              return FALSE;
            }
          AnsiMsg->lParam = (LPARAM) AnsiString.Buffer;
          break;
        }

      case LB_ADDSTRING:
      case LB_ADDSTRING_LOWER:
      case LB_ADDSTRING_UPPER:
      case LB_INSERTSTRING:
      case LB_INSERTSTRING_UPPER:
      case LB_INSERTSTRING_LOWER:
      case LB_FINDSTRING:
      case LB_FINDSTRINGEXACT:
      case LB_SELECTSTRING:
        {
          if (UnicodeMsg->lParam && listbox_has_strings(AnsiMsg->hwnd))
            {
              RtlInitUnicodeString(&UnicodeString, (PWSTR) UnicodeMsg->lParam);
              if (! NT_SUCCESS(RtlUnicodeStringToAnsiString(&AnsiString,
                                                            &UnicodeString,
                                                            TRUE)))
                {
                  return FALSE;
                }
              AnsiMsg->lParam = (LPARAM) AnsiString.Buffer;
            }
          break;
        }

      case CB_ADDSTRING:
      case CB_INSERTSTRING:
      case CB_FINDSTRING:
      case CB_FINDSTRINGEXACT:
      case CB_SELECTSTRING:
        {
          if (UnicodeMsg->lParam && combobox_has_strings(AnsiMsg->hwnd))
            {
              RtlInitUnicodeString(&UnicodeString, (PWSTR) UnicodeMsg->lParam);
              if (! NT_SUCCESS(RtlUnicodeStringToAnsiString(&AnsiString,
                                                            &UnicodeString,
                                                            TRUE)))
                {
                  return FALSE;
                }
              AnsiMsg->lParam = (LPARAM) AnsiString.Buffer;
            }
          break;
        }

      case WM_MDICREATE:
        {
          MDICREATESTRUCTA *cs =
              (MDICREATESTRUCTA *)HeapAlloc(GetProcessHeap(), 0, sizeof(*cs));

          if (!cs)
            {
              return FALSE;
            }

          *cs = *(MDICREATESTRUCTA *)UnicodeMsg->lParam;

          if (!IS_ATOM(cs->szClass))
            {
              RtlInitUnicodeString(&UnicodeString, (LPCWSTR)cs->szClass);
              if (! NT_SUCCESS(RtlUnicodeStringToAnsiString(&AnsiString,
                                                            &UnicodeString,
                                                            TRUE)))
                {
                  HeapFree(GetProcessHeap(), 0, cs);
                  return FALSE;
                }
              cs->szClass = AnsiString.Buffer;
            }

          RtlInitUnicodeString(&UnicodeString, (LPCWSTR)cs->szTitle);
          if (! NT_SUCCESS(RtlUnicodeStringToAnsiString(&AnsiString,
                                                        &UnicodeString,
                                                        TRUE)))
            {
              if (!IS_ATOM(cs->szClass))
                {
                  RtlInitAnsiString(&AnsiString, cs->szClass);
                  RtlFreeAnsiString(&AnsiString);
                }

              HeapFree(GetProcessHeap(), 0, cs);
              return FALSE;
            }
          cs->szTitle = AnsiString.Buffer;

          AnsiMsg->lParam = (LPARAM)cs;
          break;
        }

      case WM_GETDLGCODE:
        if (UnicodeMsg->lParam)
        {
           MSG newmsg = *(MSG *)UnicodeMsg->lParam;
           switch(newmsg.message)
           {
              case WM_CHAR:
              case WM_DEADCHAR:
              case WM_SYSCHAR:
              case WM_SYSDEADCHAR:
                newmsg.wParam = map_wparam_char_WtoA( newmsg.wParam, 1 );
                break;
              case WM_IME_CHAR:
                newmsg.wParam = map_wparam_char_WtoA( newmsg.wParam, 2 );
                break;
           }
        }
        break;

      case WM_CHAR:
        {
           WCHAR wch = UnicodeMsg->wParam;
           char ch[2];
           DWORD cp = get_input_codepage();
           DWORD len = WideCharToMultiByte( cp, 0, &wch, 1, ch, 2, NULL, NULL );
           AnsiMsg->wParam = (BYTE)ch[0];
           if (len == 2) AnsiMsg->wParam = (BYTE)ch[1];
        }
        break;

      case WM_CHARTOITEM:
      case WM_MENUCHAR:
      case WM_DEADCHAR:
      case WM_SYSCHAR:
      case WM_SYSDEADCHAR:
      case EM_SETPASSWORDCHAR:
          AnsiMsg->wParam = map_wparam_char_WtoA(UnicodeMsg->wParam,1);
          break;

      case WM_IME_CHAR:
          AnsiMsg->wParam = map_wparam_char_WtoA(UnicodeMsg->wParam,2);
          break;
      case EM_GETLINE:
          ERR("FIXME EM_GETLINE U2A\n");
          break;
    }
  return TRUE;
}

static BOOL FASTCALL
MsgiUnicodeToAnsiCleanup(LPMSG AnsiMsg, LPMSG UnicodeMsg)
{
  ANSI_STRING AnsiString;

  switch(UnicodeMsg->message)
    {
      case LB_GETTEXT:
        if (!listbox_has_strings( AnsiMsg->hwnd )) break;
      case CB_GETLBTEXT:
        if (AnsiMsg->message == CB_GETLBTEXT && !combobox_has_strings( AnsiMsg->hwnd )) break;
      case WM_GETTEXT:
      case WM_ASKCBFORMATNAME:
        {
          if (!AnsiMsg->lParam) break;
          RtlFreeHeap(GetProcessHeap(), 0, (PVOID) AnsiMsg->lParam);
          break;
        }
      case WM_CREATE:
      case WM_NCCREATE:
        {
          CREATESTRUCTA* Cs;

          Cs = (CREATESTRUCTA*) AnsiMsg->lParam;
          RtlFreeHeap(GetProcessHeap(), 0, Cs);
          break;
        }

      case WM_SETTEXT:
      case WM_WININICHANGE:
      case WM_DEVMODECHANGE:
      case CB_DIR:
      case LB_DIR:
      case LB_ADDFILE:
      case EM_REPLACESEL:
        {
          if (!AnsiMsg->lParam) break;
          RtlInitAnsiString(&AnsiString, (PSTR) AnsiMsg->lParam);
          RtlFreeAnsiString(&AnsiString);
          break;
        }

      case LB_ADDSTRING:
      case LB_ADDSTRING_LOWER:
      case LB_ADDSTRING_UPPER:
      case LB_INSERTSTRING:
      case LB_INSERTSTRING_UPPER:
      case LB_INSERTSTRING_LOWER:
      case LB_FINDSTRING:
      case LB_FINDSTRINGEXACT:
      case LB_SELECTSTRING:
        {
          if (AnsiMsg->lParam && listbox_has_strings(AnsiMsg->hwnd))
            {
              RtlInitAnsiString(&AnsiString, (PSTR) AnsiMsg->lParam);
              RtlFreeAnsiString(&AnsiString);
            }
          break;
        }

      case CB_ADDSTRING:
      case CB_INSERTSTRING:
      case CB_FINDSTRING:
      case CB_FINDSTRINGEXACT:
      case CB_SELECTSTRING:
        {
          if (AnsiMsg->lParam && combobox_has_strings(AnsiMsg->hwnd))
            {
              RtlInitAnsiString(&AnsiString, (PSTR) AnsiMsg->lParam);
              RtlFreeAnsiString(&AnsiString);
            }
          break;
        }

      case WM_MDICREATE:
        {
          MDICREATESTRUCTA *cs = (MDICREATESTRUCTA *)AnsiMsg->lParam;
          RtlInitAnsiString(&AnsiString, (PCSTR)cs->szTitle);
          RtlFreeAnsiString(&AnsiString);
          if (!IS_ATOM(cs->szClass))
            {
              RtlInitAnsiString(&AnsiString, (PCSTR)cs->szClass);
              RtlFreeAnsiString(&AnsiString);
            }
          HeapFree(GetProcessHeap(), 0, cs);
          break;
        }

    }

  return TRUE;
}

/*
 *    callout return -> Ansi Result to Unicode Result
 */
static BOOL FASTCALL
MsgiUnicodeToAnsiReply(LPMSG AnsiMsg, LPMSG UnicodeMsg, LRESULT *Result)
{
  LPSTR Buffer = (LPSTR) AnsiMsg->lParam;
  LPWSTR UBuffer = (LPWSTR) UnicodeMsg->lParam;

  switch (UnicodeMsg->message)
    {
    case WM_GETTEXT:
    case WM_ASKCBFORMATNAME:
      {
        DWORD len = AnsiMsg->wParam;// * 2;
        if (len)
        {
           if (*Result)
           {
              RtlMultiByteToUnicodeN( UBuffer, AnsiMsg->wParam*sizeof(WCHAR), &len, Buffer, strlen(Buffer)+1 );
              *Result = len/sizeof(WCHAR) - 1;  /* do not count terminating null */
              //ERR("WM_GETTEXT U2A Result %d Size %d\n",*Result,AnsiMsg->wParam);
           }
           UBuffer[*Result] = 0;
        }
        break;
      }
    case LB_GETTEXT:
      {
        if (!UBuffer || !listbox_has_strings( UnicodeMsg->hwnd )) break;
        if (*Result >= 0)
        {
           DWORD len;
           RtlMultiByteToUnicodeN( UBuffer, ~0u, &len, Buffer, strlen(Buffer) + 1 );
           *Result = len / sizeof(WCHAR) - 1;
        }
        break;
      }
    case CB_GETLBTEXT:
      {
        if (!UBuffer || !combobox_has_strings( UnicodeMsg->hwnd )) break;
        if (*Result >= 0)
        {
           DWORD len;
           RtlMultiByteToUnicodeN( UBuffer, ~0u, &len, Buffer, strlen(Buffer) + 1 );
           *Result = len / sizeof(WCHAR) - 1;
        }
        break;
      }
    }

  MsgiUnicodeToAnsiCleanup(AnsiMsg, UnicodeMsg);

  return TRUE;
}


LRESULT
WINAPI
DesktopWndProcA( HWND hwnd, UINT message, WPARAM wParam, LPARAM lParam )
{
  LRESULT Result;
  MSG AnsiMsg, UcMsg;

  TRACE("Desktop A Class Atom! hWnd 0x%x, Msg %d\n", hwnd, message);

  AnsiMsg.hwnd = hwnd;
  AnsiMsg.message = message;
  AnsiMsg.wParam = wParam;
  AnsiMsg.lParam = lParam;
  AnsiMsg.time = 0;
  AnsiMsg.pt.x = 0;
  AnsiMsg.pt.y = 0;

  // Desktop is always Unicode so convert Ansi here.
  if (!MsgiAnsiToUnicodeMessage(hwnd, &UcMsg, &AnsiMsg))
  {
     return FALSE;
  }

  Result = DesktopWndProcW(hwnd, message, UcMsg.wParam, UcMsg.lParam);

  MsgiAnsiToUnicodeCleanup(&UcMsg, &AnsiMsg);

  return Result;
 }

/*
 * @implemented
 */
LPARAM
WINAPI
GetMessageExtraInfo(VOID)
{
  return NtUserxGetMessageExtraInfo();
}


/*
 * @implemented
 */
DWORD
WINAPI
GetMessagePos(VOID)
{
  return NtUserxGetMessagePos();
}


/*
 * @implemented
 */
LONG WINAPI
GetMessageTime(VOID)
{
  return NtUserGetThreadState(THREADSTATE_GETMESSAGETIME);
}


/*
 * @implemented
 */
BOOL
WINAPI
InSendMessage(VOID)
{
  PCLIENTTHREADINFO pcti = GetWin32ClientInfo()->pClientThreadInfo;
  if ( pcti )
  {
    if (pcti->CTI_flags & CTI_INSENDMESSAGE)
    {
       return TRUE;
    }
  }
  return(NtUserGetThreadState(THREADSTATE_INSENDMESSAGE) != ISMEX_NOSEND);
}


/*
 * @implemented
 */
DWORD
WINAPI
InSendMessageEx(
  LPVOID lpReserved)
{
  PCLIENTTHREADINFO pcti = GetWin32ClientInfo()->pClientThreadInfo;
  if (pcti && !(pcti->CTI_flags & CTI_INSENDMESSAGE))
     return ISMEX_NOSEND;
  else
     return NtUserGetThreadState(THREADSTATE_INSENDMESSAGE);
}


/*
 * @implemented
 */
BOOL
WINAPI
ReplyMessage(LRESULT lResult)
{
  return NtUserxReplyMessage(lResult);
}


/*
 * @implemented
 */
LPARAM
WINAPI
SetMessageExtraInfo(
  LPARAM lParam)
{
  return NtUserxSetMessageExtraInfo(lParam);
}

LRESULT FASTCALL
IntCallWindowProcW(BOOL IsAnsiProc,
                   WNDPROC WndProc,
                   PWND pWnd,
                   HWND hWnd,
                   UINT Msg,
                   WPARAM wParam,
                   LPARAM lParam)
{
  MSG AnsiMsg;
  MSG UnicodeMsg;
  BOOL Hook = FALSE, MsgOverride = FALSE, Dialog, DlgOverride = FALSE;
  LRESULT Result = 0, PreResult = 0;
  DWORD Data = 0;

  if (WndProc == NULL)
  {
      WARN("IntCallWindowsProcW() called with WndProc = NULL!\n");
      return FALSE;
  }

  if (pWnd)
     Dialog = (pWnd->fnid == FNID_DIALOG);
  else
     Dialog = FALSE;

  Hook = BeginIfHookedUserApiHook();
  if (Hook)
  {
     if (Dialog)
        DlgOverride = IsMsgOverride( Msg, &guah.DlgProcArray);
     MsgOverride = IsMsgOverride( Msg, &guah.WndProcArray);
  }

  if (IsAnsiProc)
  {
      UnicodeMsg.hwnd = hWnd;
      UnicodeMsg.message = Msg;
      UnicodeMsg.wParam = wParam;
      UnicodeMsg.lParam = lParam;
      UnicodeMsg.time = 0;
      UnicodeMsg.pt.x = 0;
      UnicodeMsg.pt.y = 0;
       if (! MsgiUnicodeToAnsiMessage(hWnd, &AnsiMsg, &UnicodeMsg))
      {
          goto Exit;
      }

      if (Hook && (MsgOverride || DlgOverride))
      {
         _SEH2_TRY
         {
            if (!DlgOverride)
               PreResult = guah.PreWndProc(AnsiMsg.hwnd, AnsiMsg.message, AnsiMsg.wParam, AnsiMsg.lParam, (ULONG_PTR)&Result, &Data );
            else
               PreResult = guah.PreDefDlgProc(AnsiMsg.hwnd, AnsiMsg.message, AnsiMsg.wParam, AnsiMsg.lParam, (ULONG_PTR)&Result, &Data );
         }
         _SEH2_EXCEPT(EXCEPTION_EXECUTE_HANDLER)
         {
            ERR("Got exception in hooked PreWndProc, dlg:%d!\n", DlgOverride);
         }
         _SEH2_END;
      }

      if (PreResult) goto Exit;

      if (!Dialog)
      Result = CALL_EXTERN_WNDPROC(WndProc, AnsiMsg.hwnd, AnsiMsg.message, AnsiMsg.wParam, AnsiMsg.lParam);
      else
      {
      _SEH2_TRY
      {
         Result = CALL_EXTERN_WNDPROC(WndProc, AnsiMsg.hwnd, AnsiMsg.message, AnsiMsg.wParam, AnsiMsg.lParam);
      }
      _SEH2_EXCEPT(EXCEPTION_EXECUTE_HANDLER)
      {
         ERR("Exception Dialog Ansi %p Msg %d pti %p Wndpti %p\n",WndProc,Msg,GetW32ThreadInfo(),pWnd->head.pti);
      }
      _SEH2_END;
      }

      if (Hook && (MsgOverride || DlgOverride))
      {
         _SEH2_TRY
         {
            if (!DlgOverride)
               guah.PostWndProc(AnsiMsg.hwnd, AnsiMsg.message, AnsiMsg.wParam, AnsiMsg.lParam, (ULONG_PTR)&Result, &Data );
            else
               guah.PostDefDlgProc(AnsiMsg.hwnd, AnsiMsg.message, AnsiMsg.wParam, AnsiMsg.lParam, (ULONG_PTR)&Result, &Data );
         }
         _SEH2_EXCEPT(EXCEPTION_EXECUTE_HANDLER)
         {
            ERR("Got exception in hooked PostWndProc, dlg:%d!\n", DlgOverride);
         }
         _SEH2_END;
      }

      if (! MsgiUnicodeToAnsiReply(&AnsiMsg, &UnicodeMsg, &Result))
      {
          goto Exit;
      }
  }
  else
  {
      if (Hook && (MsgOverride || DlgOverride))
      {
         _SEH2_TRY
         {
            if (!DlgOverride)
               PreResult = guah.PreWndProc(hWnd, Msg, wParam, lParam, (ULONG_PTR)&Result, &Data );
            else
               PreResult = guah.PreDefDlgProc(hWnd, Msg, wParam, lParam, (ULONG_PTR)&Result, &Data );
         }
         _SEH2_EXCEPT(EXCEPTION_EXECUTE_HANDLER)
         {
            ERR("Got exception in hooked PreWndProc, dlg:%d!\n", DlgOverride);
         }
         _SEH2_END;
      }

      if (PreResult) goto Exit;

      if (!Dialog)
      Result = CALL_EXTERN_WNDPROC(WndProc, hWnd, Msg, wParam, lParam);
      else
      {
      _SEH2_TRY
      {
         Result = CALL_EXTERN_WNDPROC(WndProc, hWnd, Msg, wParam, lParam);
      }
      _SEH2_EXCEPT(EXCEPTION_EXECUTE_HANDLER)
      {
         ERR("Exception Dialog unicode %p Msg %d pti %p Wndpti %p\n",WndProc, Msg,GetW32ThreadInfo(),pWnd->head.pti);
      }
      _SEH2_END;
      }

      if (Hook && (MsgOverride || DlgOverride))
      {
         _SEH2_TRY
         {
            if (!DlgOverride)
               guah.PostWndProc(hWnd, Msg, wParam, lParam, (ULONG_PTR)&Result, &Data );
            else
               guah.PostDefDlgProc(hWnd, Msg, wParam, lParam, (ULONG_PTR)&Result, &Data );
         }
         _SEH2_EXCEPT(EXCEPTION_EXECUTE_HANDLER)
         {
            ERR("Got exception in hooked PostWndProc, dlg:%d!\n", DlgOverride);
         }
         _SEH2_END;
      }
  }

Exit:
  if (Hook) EndUserApiHook();
  return Result;
}

static LRESULT FASTCALL
IntCallWindowProcA(BOOL IsAnsiProc,
                   WNDPROC WndProc,
                   PWND pWnd,
                   HWND hWnd,
                   UINT Msg,
                   WPARAM wParam,
                   LPARAM lParam)
{
  MSG AnsiMsg;
  MSG UnicodeMsg;
  BOOL Hook = FALSE, MsgOverride = FALSE, Dialog, DlgOverride = FALSE;
  LRESULT Result = 0, PreResult = 0;
  DWORD Data = 0;

  TRACE("IntCallWindowProcA: IsAnsiProc : %s, WndProc %p, pWnd %p, hWnd %p, Msg %u, wParam %Iu, lParam %Iu.\n",
      IsAnsiProc ? "TRUE" : "FALSE", WndProc, pWnd, hWnd, Msg, wParam, lParam);

  if (WndProc == NULL)
  {
      WARN("IntCallWindowsProcA() called with WndProc = NULL!\n");
      return FALSE;
  }

  if (pWnd)
     Dialog = (pWnd->fnid == FNID_DIALOG);
  else
     Dialog = FALSE;

  Hook = BeginIfHookedUserApiHook();
  if (Hook)
  {
     if (Dialog)
        DlgOverride = IsMsgOverride( Msg, &guah.DlgProcArray);
     MsgOverride = IsMsgOverride( Msg, &guah.WndProcArray);
  }

  if (IsAnsiProc)
  {
      if (Hook && (MsgOverride || DlgOverride))
      {
         _SEH2_TRY
         {
            if (!DlgOverride)
               PreResult = guah.PreWndProc(hWnd, Msg, wParam, lParam, (ULONG_PTR)&Result, &Data );
            else
               PreResult = guah.PreDefDlgProc(hWnd, Msg, wParam, lParam, (ULONG_PTR)&Result, &Data );
         }
         _SEH2_EXCEPT(EXCEPTION_EXECUTE_HANDLER)
         {
            ERR("Got exception in hooked PreWndProc, dlg:%d!\n", DlgOverride);
         }
         _SEH2_END;
      }

      if (PreResult) goto Exit;

      if (!Dialog)
      Result = CALL_EXTERN_WNDPROC(WndProc, hWnd, Msg, wParam, lParam);
      else
      {
      _SEH2_TRY
      {
         Result = CALL_EXTERN_WNDPROC(WndProc, hWnd, Msg, wParam, lParam);
      }
      _SEH2_EXCEPT(EXCEPTION_EXECUTE_HANDLER)
      {
         ERR("Exception Dialog Ansi %p Msg %d pti %p Wndpti %p\n",WndProc,Msg,GetW32ThreadInfo(),pWnd->head.pti);
      }
      _SEH2_END;
      }

      if (Hook && (MsgOverride || DlgOverride))
      {
         _SEH2_TRY
         {
            if (!DlgOverride)
               guah.PostWndProc(hWnd, Msg, wParam, lParam, (ULONG_PTR)&Result, &Data );
            else
               guah.PostDefDlgProc(hWnd, Msg, wParam, lParam, (ULONG_PTR)&Result, &Data );
         }
         _SEH2_EXCEPT(EXCEPTION_EXECUTE_HANDLER)
         {
            ERR("Got exception in hooked PostWndProc, dlg:%d!\n", DlgOverride);
         }
         _SEH2_END;
      }
  }
  else
  {
      AnsiMsg.hwnd = hWnd;
      AnsiMsg.message = Msg;
      AnsiMsg.wParam = wParam;
      AnsiMsg.lParam = lParam;
      AnsiMsg.time = 0;
      AnsiMsg.pt.x = 0;
      AnsiMsg.pt.y = 0;
      if (! MsgiAnsiToUnicodeMessage(hWnd, &UnicodeMsg, &AnsiMsg))
      {
          goto Exit;
      }

      if (Hook && (MsgOverride || DlgOverride))
      {
         _SEH2_TRY
         {
            if (!DlgOverride)
               PreResult = guah.PreWndProc(UnicodeMsg.hwnd, UnicodeMsg.message, UnicodeMsg.wParam, UnicodeMsg.lParam, (ULONG_PTR)&Result, &Data );
            else
               PreResult = guah.PreDefDlgProc(UnicodeMsg.hwnd, UnicodeMsg.message, UnicodeMsg.wParam, UnicodeMsg.lParam, (ULONG_PTR)&Result, &Data );
         }
         _SEH2_EXCEPT(EXCEPTION_EXECUTE_HANDLER)
         {
            ERR("Got exception in hooked PreWndProc, dlg:%d!\n", DlgOverride);
         }
         _SEH2_END;
      }

      if (PreResult) goto Exit;

      if (!Dialog)
      Result = CALL_EXTERN_WNDPROC(WndProc, UnicodeMsg.hwnd, UnicodeMsg.message, UnicodeMsg.wParam, UnicodeMsg.lParam);
      else
      {
      _SEH2_TRY
      {
         Result = CALL_EXTERN_WNDPROC(WndProc, UnicodeMsg.hwnd, UnicodeMsg.message, UnicodeMsg.wParam, UnicodeMsg.lParam);
      }
      _SEH2_EXCEPT(EXCEPTION_EXECUTE_HANDLER)
      {
         ERR("Exception Dialog unicode %p Msg %d pti %p Wndpti %p\n",WndProc, Msg,GetW32ThreadInfo(),pWnd->head.pti);
      }
      _SEH2_END;
      }

      if (Hook && (MsgOverride || DlgOverride))
      {
         _SEH2_TRY
         {
            if (!DlgOverride)
               guah.PostWndProc(UnicodeMsg.hwnd, UnicodeMsg.message, UnicodeMsg.wParam, UnicodeMsg.lParam, (ULONG_PTR)&Result, &Data );
            else
               guah.PostDefDlgProc(UnicodeMsg.hwnd, UnicodeMsg.message, UnicodeMsg.wParam, UnicodeMsg.lParam, (ULONG_PTR)&Result, &Data );
         }
         _SEH2_EXCEPT(EXCEPTION_EXECUTE_HANDLER)
         {
            ERR("Got exception in hooked PostWndProc, dlg:%d!\n", DlgOverride);
         }
         _SEH2_END;
      }

      if (! MsgiAnsiToUnicodeReply(&UnicodeMsg, &AnsiMsg, &Result))
      {
         goto Exit;
      }
  }

Exit:
  if (Hook) EndUserApiHook();
  return Result;
}


static LRESULT WINAPI
IntCallMessageProc(IN PWND Wnd, IN HWND hWnd, IN UINT Msg, IN WPARAM wParam, IN LPARAM lParam, IN BOOL Ansi)
{
    WNDPROC WndProc;
    BOOL IsAnsi;
    PCLS Class;

    Class = DesktopPtrToUser(Wnd->pcls);
    WndProc = NULL;

    if ( Wnd->head.pti != GetW32ThreadInfo())
    {  // Must be inside the same thread!
       SetLastError( ERROR_MESSAGE_SYNC_ONLY );
       return 0;
    }
  /*
      This is the message exchange for user32. If there's a need to monitor messages,
      do it here!
   */
    TRACE("HWND %p, MSG %u, WPARAM %p, LPARAM %p, Ansi %d\n", hWnd, Msg, wParam, lParam, Ansi);
//    if (Class->fnid <= FNID_GHOST && Class->fnid >= FNID_BUTTON )
    if (Class->fnid <= FNID_GHOST && Class->fnid >= FNID_FIRST )
    {
       if (Ansi)
       {
          if (GETPFNCLIENTW(Class->fnid) == Wnd->lpfnWndProc)
             WndProc = GETPFNCLIENTA(Class->fnid);
       }
       else
       {
          if (GETPFNCLIENTA(Class->fnid) == Wnd->lpfnWndProc)
             WndProc = GETPFNCLIENTW(Class->fnid);
       }

       IsAnsi = Ansi;

       if (!WndProc)
       {
          IsAnsi = !Wnd->Unicode;
          WndProc = Wnd->lpfnWndProc;
       }
    }
    else
    {
       IsAnsi = !Wnd->Unicode;
       WndProc = Wnd->lpfnWndProc;
    }
/*
   Message caller can be Ansi/Unicode and the receiver can be Unicode/Ansi or
   the same.
 */
    if (!Ansi)
        return IntCallWindowProcW(IsAnsi, WndProc, Wnd, hWnd, Msg, wParam, lParam);
    else
        return IntCallWindowProcA(IsAnsi, WndProc, Wnd, hWnd, Msg, wParam, lParam);
}


/*
 * @implemented
 */
LRESULT WINAPI
CallWindowProcA(WNDPROC lpPrevWndFunc,
		HWND hWnd,
		UINT Msg,
		WPARAM wParam,
		LPARAM lParam)
{
    PWND pWnd;
    PCALLPROCDATA CallProc;

    if (lpPrevWndFunc == NULL)
    {
        WARN("CallWindowProcA: lpPrevWndFunc == NULL!\n");
        return 0;
    }

    pWnd = ValidateHwnd(hWnd);

    if (!IsCallProcHandle(lpPrevWndFunc))
        return IntCallWindowProcA(TRUE, lpPrevWndFunc, pWnd, hWnd, Msg, wParam, lParam);
    else
    {
        CallProc = ValidateCallProc((HANDLE)lpPrevWndFunc);
        if (CallProc != NULL)
        {
            return IntCallWindowProcA(!(CallProc->wType & UserGetCPDA2U),
                                        CallProc->pfnClientPrevious,
                                        pWnd,
                                        hWnd,
                                        Msg,
                                        wParam,
                                        lParam);
        }
        else
        {
            WARN("CallWindowProcA: can not dereference WndProcHandle\n");
            return 0;
        }
    }
}


/*
 * @implemented
 */
LRESULT WINAPI
CallWindowProcW(WNDPROC lpPrevWndFunc,
		HWND hWnd,
		UINT Msg,
		WPARAM wParam,
		LPARAM lParam)
{
    PWND pWnd;
    PCALLPROCDATA CallProc;

    /* FIXME - can the first parameter be NULL? */
    if (lpPrevWndFunc == NULL)
    {
        WARN("CallWindowProcA: lpPrevWndFunc == NULL!\n");
        return 0;
    }

    pWnd = ValidateHwnd(hWnd);

    if (!IsCallProcHandle(lpPrevWndFunc))
        return IntCallWindowProcW(FALSE, lpPrevWndFunc, pWnd, hWnd, Msg, wParam, lParam);
    else
    {
        CallProc = ValidateCallProc((HANDLE)lpPrevWndFunc);
        if (CallProc != NULL)
        {
            return IntCallWindowProcW(!(CallProc->wType & UserGetCPDA2U),
                                        CallProc->pfnClientPrevious,
                                        pWnd,
                                        hWnd,
                                        Msg,
                                        wParam,
                                        lParam);
        }
        else
        {
            WARN("CallWindowProcW: can not dereference WndProcHandle\n");
            return 0;
        }
    }
}


/*
 * @implemented
 */
LRESULT
WINAPI
DECLSPEC_HOTPATCH
DispatchMessageA(CONST MSG *lpmsg)
{
    LRESULT Ret = 0;
    MSG UnicodeMsg;
    PWND Wnd;

    if ( lpmsg->message & ~WM_MAXIMUM )
    {
        SetLastError( ERROR_INVALID_PARAMETER );
        return 0;
    }

    if (lpmsg->hwnd != NULL)
    {
        Wnd = ValidateHwnd(lpmsg->hwnd);
        if (!Wnd) return 0;
    }
    else
        Wnd = NULL;

    if (is_pointer_message(lpmsg->message))
    {
       SetLastError( ERROR_MESSAGE_SYNC_ONLY );
       return 0;
    }

    if ((lpmsg->message == WM_TIMER || lpmsg->message == WM_SYSTIMER) && lpmsg->lParam != 0)
    {
        WNDPROC WndProc = (WNDPROC)lpmsg->lParam;

        if ( lpmsg->message == WM_SYSTIMER )
           return NtUserDispatchMessage( (PMSG)lpmsg );

        if (!NtUserValidateTimerCallback(lpmsg->lParam))
        {
           WARN("Validating Timer Callback failed!\n");
           return 0;
        }

       _SEH2_TRY // wine does this. Hint: Prevents call to another thread....
       {
           Ret = WndProc(lpmsg->hwnd,
                         lpmsg->message,
                         lpmsg->wParam,
                         GetTickCount());
       }
       _SEH2_EXCEPT(EXCEPTION_EXECUTE_HANDLER)
       {
           ERR("Exception in Timer Callback!\n");
       }
       _SEH2_END;
    }
    else if (Wnd != NULL)
    {
       if ( (lpmsg->message != WM_PAINT) && !(Wnd->state & WNDS_SERVERSIDEWINDOWPROC) )
       {
           Ret = IntCallMessageProc(Wnd,
                                    lpmsg->hwnd,
                                    lpmsg->message,
                                    lpmsg->wParam,
                                    lpmsg->lParam,
                                    TRUE);
       }
       else
       {
          if (!MsgiAnsiToUnicodeMessage(lpmsg->hwnd, &UnicodeMsg, (LPMSG)lpmsg))
          {
             return FALSE;
          }

          Ret = NtUserDispatchMessage(&UnicodeMsg);

          if (!MsgiAnsiToUnicodeReply(&UnicodeMsg, (LPMSG)lpmsg, &Ret))
          {
             return FALSE;
          }
       }
    }

    return Ret;
}


/*
 * @implemented
 */
LRESULT
WINAPI
DECLSPEC_HOTPATCH
DispatchMessageW(CONST MSG *lpmsg)
{
    LRESULT Ret = 0;
    PWND Wnd;
    BOOL Hit = FALSE;

    if ( lpmsg->message & ~WM_MAXIMUM )
    {
        SetLastError( ERROR_INVALID_PARAMETER );
        return 0;
    }

    if (lpmsg->hwnd != NULL)
    {
        Wnd = ValidateHwnd(lpmsg->hwnd);
        if (!Wnd) return 0;
    }
    else
        Wnd = NULL;

    if (is_pointer_message(lpmsg->message))
    {
       SetLastError( ERROR_MESSAGE_SYNC_ONLY );
       return 0;
    }

    if ((lpmsg->message == WM_TIMER || lpmsg->message == WM_SYSTIMER) && lpmsg->lParam != 0)
    {
        WNDPROC WndProc = (WNDPROC)lpmsg->lParam;

        if ( lpmsg->message == WM_SYSTIMER )
           return NtUserDispatchMessage( (PMSG) lpmsg );

        if (!NtUserValidateTimerCallback(lpmsg->lParam))
        {
           WARN("Validating Timer Callback failed!\n");
           return 0;
        }

       _SEH2_TRY
       {
           Ret = WndProc(lpmsg->hwnd,
                         lpmsg->message,
                         lpmsg->wParam,
                         GetTickCount());
       }
       _SEH2_EXCEPT(EXCEPTION_EXECUTE_HANDLER)
       {
          Hit = TRUE;
       }
       _SEH2_END;
    }
    else if (Wnd != NULL)
    {
       if ( (lpmsg->message != WM_PAINT) && !(Wnd->state & WNDS_SERVERSIDEWINDOWPROC) )
       {
           Ret = IntCallMessageProc(Wnd,
                                    lpmsg->hwnd,
                                    lpmsg->message,
                                    lpmsg->wParam,
                                    lpmsg->lParam,
                                    FALSE);
       }
       else
         Ret = NtUserDispatchMessage( (PMSG) lpmsg );
    }

    if (Hit)
    {
       WARN("Exception in Timer Callback WndProcW!\n");
    }
    return Ret;
}

static VOID
IntConvertMsgToAnsi(LPMSG lpMsg)
{
    CHAR ch[2];
    WCHAR wch[2];

    switch (lpMsg->message)
    {
        case WM_CHAR:
        case WM_DEADCHAR:
        case WM_SYSCHAR:
        case WM_SYSDEADCHAR:
        case WM_MENUCHAR:
            wch[0] = LOWORD(lpMsg->wParam);
            wch[1] = HIWORD(lpMsg->wParam);
            ch[0] = ch[1] = 0;
            WideCharToMultiByte(CP_THREAD_ACP, 0, wch, 2, ch, 2, NULL, NULL);
            lpMsg->wParam = MAKEWPARAM(ch[0] | (ch[1] << 8), 0);
            break;
    }
}

/*
 * @implemented
 */
BOOL
WINAPI
DECLSPEC_HOTPATCH
GetMessageA(LPMSG lpMsg,
            HWND hWnd,
            UINT wMsgFilterMin,
            UINT wMsgFilterMax)
{
  BOOL Res;

  if ( (wMsgFilterMin|wMsgFilterMax) & ~WM_MAXIMUM )
  {
     SetLastError( ERROR_INVALID_PARAMETER );
     return FALSE;
  }

  Res = NtUserGetMessage(lpMsg, hWnd, wMsgFilterMin, wMsgFilterMax);
  if (-1 == (int) Res)
  {
      return Res;
  }

  IntConvertMsgToAnsi(lpMsg);

  return Res;
}

/*
 * @implemented
 */
BOOL
WINAPI
DECLSPEC_HOTPATCH
GetMessageW(LPMSG lpMsg,
            HWND hWnd,
            UINT wMsgFilterMin,
            UINT wMsgFilterMax)
{
  BOOL Res;

  if ( (wMsgFilterMin|wMsgFilterMax) & ~WM_MAXIMUM )
  {
     SetLastError( ERROR_INVALID_PARAMETER );
     return FALSE;
  }

  Res = NtUserGetMessage(lpMsg, hWnd, wMsgFilterMin, wMsgFilterMax);
  if (-1 == (int) Res)
  {
      return Res;
  }

  return Res;
}

BOOL WINAPI
PeekMessageWorker( PMSG pMsg,
                   HWND hWnd,
                   UINT wMsgFilterMin,
                   UINT wMsgFilterMax,
                   UINT wRemoveMsg)
{
  PCLIENTINFO pci;
  PCLIENTTHREADINFO pcti;
  pci = GetWin32ClientInfo();
  pcti = pci->pClientThreadInfo;

  if (!hWnd && pci && pcti)
  {
     pci->cSpins++;

     if ((pci->cSpins >= 100) && (pci->dwTIFlags & TIF_SPINNING))
     {  // Yield after 100 spin cycles and ready to swap vinyl.
        if (!(pci->dwTIFlags & TIF_WAITFORINPUTIDLE))
        {  // Not waiting for idle event.
           if (!pcti->fsChangeBits && !pcti->fsWakeBits)
           {  // No messages are available.
              if ((GetTickCount() - pcti->tickLastMsgChecked) > 1000)
              {  // Up the msg read count if over 1 sec.
                 NtUserGetThreadState(THREADSTATE_UPTIMELASTREAD);
              }
              pci->cSpins = 0;
              ZwYieldExecution();
              FIXME("seeSpins!\n");
              return FALSE;
           }
        }
     }
  }
  return NtUserPeekMessage(pMsg, hWnd, wMsgFilterMin, wMsgFilterMax, wRemoveMsg);
}

/*
 * @implemented
 */
BOOL
WINAPI
DECLSPEC_HOTPATCH
PeekMessageA(LPMSG lpMsg,
	     HWND hWnd,
	     UINT wMsgFilterMin,
	     UINT wMsgFilterMax,
	     UINT wRemoveMsg)
{
  BOOL Res;

  Res = PeekMessageWorker(lpMsg, hWnd, wMsgFilterMin, wMsgFilterMax, wRemoveMsg);
  if (-1 == (int) Res || !Res)
  {
      return FALSE;
  }

  IntConvertMsgToAnsi(lpMsg);

  return Res;
}


/*
 * @implemented
 */
BOOL
WINAPI
DECLSPEC_HOTPATCH
PeekMessageW(
  LPMSG lpMsg,
  HWND hWnd,
  UINT wMsgFilterMin,
  UINT wMsgFilterMax,
  UINT wRemoveMsg)
{
  BOOL Res;

  Res = PeekMessageWorker(lpMsg, hWnd, wMsgFilterMin, wMsgFilterMax, wRemoveMsg);
  if (-1 == (int) Res || !Res)
  {
      return FALSE;
  }

  return Res;
}

/*
 * @implemented
 */
BOOL
WINAPI
PostMessageA(
  HWND hWnd,
  UINT Msg,
  WPARAM wParam,
  LPARAM lParam)
{
  LRESULT Ret;

  /* Check for combo box or a list box to send names. */
  if (Msg == CB_DIR || Msg == LB_DIR)
  {
  /*
     Set DDL_POSTMSGS, so use the PostMessage function to send messages to the
     combo/list box. Forces a call like DlgDirListComboBox.
  */
    //wParam |= DDL_POSTMSGS;
    return NtUserPostMessage(hWnd, Msg, wParam, lParam);
  }

  /* No drop files or current Process, just post message. */
  if ( (Msg != WM_DROPFILES) ||
       ( NtUserQueryWindow( hWnd, QUERY_WINDOW_UNIQUE_PROCESS_ID) ==
                  PtrToUint(NtCurrentTeb()->ClientId.UniqueProcess) ) )
  {
    return NtUserPostMessage(hWnd, Msg, wParam, lParam);
  }

  /* We have drop files and this is not the same process for this window. */

  /* Just incase, check wParam for Global memory handle and send size. */
  Ret = SendMessageA( hWnd,
                      WM_COPYGLOBALDATA,
                      (WPARAM)GlobalSize((HGLOBAL)wParam), // Zero if not a handle.
                      (LPARAM)wParam);                     // Send wParam as lParam.

  if ( Ret ) return NtUserPostMessage(hWnd, Msg, (WPARAM)Ret, lParam);

  return FALSE;
}

/*
 * @implemented
 */
BOOL
WINAPI
PostMessageW(
  HWND hWnd,
  UINT Msg,
  WPARAM wParam,
  LPARAM lParam)
{
  LRESULT Ret;

  /* Check for combo box or a list box to send names. */
  if (Msg == CB_DIR || Msg == LB_DIR)
  {
  /*
     Set DDL_POSTMSGS, so use the PostMessage function to send messages to the
     combo/list box. Forces a call like DlgDirListComboBox.
  */
    //wParam |= DDL_POSTMSGS;
    return NtUserPostMessage(hWnd, Msg, wParam, lParam);
  }

  /* No drop files or current Process, just post message. */
  if ( (Msg != WM_DROPFILES) ||
       ( NtUserQueryWindow( hWnd, QUERY_WINDOW_UNIQUE_PROCESS_ID) ==
                  PtrToUint(NtCurrentTeb()->ClientId.UniqueProcess) ) )
  {
    return NtUserPostMessage(hWnd, Msg, wParam, lParam);
  }

  /* We have drop files and this is not the same process for this window. */

  /* Just incase, check wParam for Global memory handle and send size. */
  Ret = SendMessageW( hWnd,
                      WM_COPYGLOBALDATA,
                      (WPARAM)GlobalSize((HGLOBAL)wParam), // Zero if not a handle.
                      (LPARAM)wParam);                     // Send wParam as lParam.

  if ( Ret ) return NtUserPostMessage(hWnd, Msg, (WPARAM)Ret, lParam);

  return FALSE;
}

/*
 * @implemented
 */
VOID
WINAPI
PostQuitMessage(
  int nExitCode)
{
    NtUserxPostQuitMessage(nExitCode);
}


/*
 * @implemented
 */
BOOL
WINAPI
PostThreadMessageA(
  DWORD idThread,
  UINT Msg,
  WPARAM wParam,
  LPARAM lParam)
{
  return NtUserPostThreadMessage(idThread, Msg, wParam, lParam);
}


/*
 * @implemented
 */
BOOL
WINAPI
PostThreadMessageW(
  DWORD idThread,
  UINT Msg,
  WPARAM wParam,
  LPARAM lParam)
{
  return NtUserPostThreadMessage(idThread, Msg, wParam, lParam);
}


/*
 * @implemented
 */
LRESULT WINAPI
SendMessageW(HWND Wnd,
	     UINT Msg,
	     WPARAM wParam,
	     LPARAM lParam)
{
  MSG UMMsg, KMMsg;
  LRESULT Result;
  BOOL Ret;
  PWND Window;
  PTHREADINFO ti = GetW32ThreadInfo();

  if ( Msg & ~WM_MAXIMUM )
  {
     SetLastError( ERROR_INVALID_PARAMETER );
     return 0;
  }

  if (Wnd != HWND_TOPMOST && Wnd != HWND_BROADCAST && (Msg < WM_DDE_FIRST || Msg > WM_DDE_LAST))
  {
      Window = ValidateHwnd(Wnd);

      if ( Window != NULL &&
           Window->head.pti == ti &&
          !ISITHOOKED(WH_CALLWNDPROC) &&
          !ISITHOOKED(WH_CALLWNDPROCRET) &&
          !(Window->state & WNDS_SERVERSIDEWINDOWPROC) )
      {
          /* NOTE: We can directly send messages to the window procedure
                   if *all* the following conditions are met:

                   * Window belongs to calling thread
                   * The calling thread is not being hooked for CallWndProc
                   * Not calling a server side proc:
                     Desktop, Switch, ScrollBar, Menu, IconTitle, or hWndMessage
           */

          return IntCallMessageProc(Window, Wnd, Msg, wParam, lParam, FALSE);
      }
  }

  UMMsg.hwnd = Wnd;
  UMMsg.message = Msg;
  UMMsg.wParam = wParam;
  UMMsg.lParam = lParam;
  UMMsg.time = 0;
  UMMsg.pt.x = 0;
  UMMsg.pt.y = 0;

  if (! MsgiUMToKMMessage(&UMMsg, &KMMsg, FALSE))
  {
     return FALSE;
  }

  Ret = NtUserMessageCall( Wnd,
                           KMMsg.message,
                           KMMsg.wParam,
                           KMMsg.lParam,
                          (ULONG_PTR)&Result,
                           FNID_SENDMESSAGE,
                           FALSE);
  if (!Ret)
  {
     ERR("SendMessageW Error\n");
  }

  MsgiUMToKMCleanup(&UMMsg, &KMMsg);

  return Result;
}


/*
 * @implemented
 */
LRESULT WINAPI
SendMessageA(HWND Wnd, UINT Msg, WPARAM wParam, LPARAM lParam)
{
  MSG AnsiMsg, UcMsg, KMMsg;
  LRESULT Result;
  BOOL Ret;
  PWND Window;
  PTHREADINFO ti = GetW32ThreadInfo();

  if ( Msg & ~WM_MAXIMUM )
  {
     SetLastError( ERROR_INVALID_PARAMETER );
     return 0;
  }

  if (Wnd != HWND_TOPMOST && Wnd != HWND_BROADCAST && (Msg < WM_DDE_FIRST || Msg > WM_DDE_LAST))
  {
      Window = ValidateHwnd(Wnd);

      if ( Window != NULL &&
           Window->head.pti == ti &&
          !ISITHOOKED(WH_CALLWNDPROC) &&
          !ISITHOOKED(WH_CALLWNDPROCRET) &&
          !(Window->state & WNDS_SERVERSIDEWINDOWPROC) )
      {
          /* NOTE: We can directly send messages to the window procedure
                   if *all* the following conditions are met:

                   * Window belongs to calling thread
                   * The calling thread is not being hooked for CallWndProc
                   * Not calling a server side proc:
                     Desktop, Switch, ScrollBar, Menu, IconTitle, or hWndMessage
           */

          return IntCallMessageProc(Window, Wnd, Msg, wParam, lParam, TRUE);
      }
  }

  AnsiMsg.hwnd = Wnd;
  AnsiMsg.message = Msg;
  AnsiMsg.wParam = wParam;
  AnsiMsg.lParam = lParam;
  AnsiMsg.time = 0;
  AnsiMsg.pt.x = 0;
  AnsiMsg.pt.y = 0;

  if (!MsgiAnsiToUnicodeMessage(Wnd, &UcMsg, &AnsiMsg))
  {
     return FALSE;
  }

  if (!MsgiUMToKMMessage(&UcMsg, &KMMsg, FALSE))
  {
      MsgiAnsiToUnicodeCleanup(&UcMsg, &AnsiMsg);
      return FALSE;
  }

  Ret = NtUserMessageCall( Wnd,
                           KMMsg.message,
                           KMMsg.wParam,
                           KMMsg.lParam,
                          (ULONG_PTR)&Result,
                           FNID_SENDMESSAGE,
                           TRUE);
  if (!Ret)
  {
     ERR("SendMessageA Error\n");
  }

  MsgiUMToKMCleanup(&UcMsg, &KMMsg);
  MsgiAnsiToUnicodeReply(&UcMsg, &AnsiMsg, &Result);

  return Result;
}

/*
 * @implemented
 */
BOOL
WINAPI
SendMessageCallbackA(
  HWND hWnd,
  UINT Msg,
  WPARAM wParam,
  LPARAM lParam,
  SENDASYNCPROC lpCallBack,
  ULONG_PTR dwData)
{
  BOOL Result;
  MSG AnsiMsg, UcMsg;
  CALL_BACK_INFO CallBackInfo;

  if (is_pointer_message(Msg))
  {
     SetLastError( ERROR_MESSAGE_SYNC_ONLY );
     return FALSE;
  }

  CallBackInfo.CallBack = lpCallBack;
  CallBackInfo.Context = dwData;

  AnsiMsg.hwnd = hWnd;
  AnsiMsg.message = Msg;
  AnsiMsg.wParam = wParam;
  AnsiMsg.lParam = lParam;
  AnsiMsg.time = 0;
  AnsiMsg.pt.x = 0;
  AnsiMsg.pt.y = 0;

  if (!MsgiAnsiToUnicodeMessage(hWnd, &UcMsg, &AnsiMsg))
  {
      return FALSE;
  }

  Result = NtUserMessageCall( hWnd,
                              UcMsg.message,
                              UcMsg.wParam,
                              UcMsg.lParam,
                             (ULONG_PTR)&CallBackInfo,
                              FNID_SENDMESSAGECALLBACK,
                              TRUE);

  MsgiAnsiToUnicodeCleanup(&UcMsg, &AnsiMsg);

  return Result;
}

/*
 * @implemented
 */
BOOL
WINAPI
SendMessageCallbackW(
  HWND hWnd,
  UINT Msg,
  WPARAM wParam,
  LPARAM lParam,
  SENDASYNCPROC lpCallBack,
  ULONG_PTR dwData)
{
  CALL_BACK_INFO CallBackInfo;

  if (is_pointer_message(Msg))
  {
     SetLastError( ERROR_MESSAGE_SYNC_ONLY );
     return FALSE;
  }

  CallBackInfo.CallBack = lpCallBack;
  CallBackInfo.Context = dwData;

  return NtUserMessageCall(hWnd,
                            Msg,
                         wParam,
                         lParam,
       (ULONG_PTR)&CallBackInfo,
       FNID_SENDMESSAGECALLBACK,
                          FALSE);
}

/*
 * @implemented
 */
LRESULT
WINAPI
SendMessageTimeoutA(
  HWND hWnd,
  UINT Msg,
  WPARAM wParam,
  LPARAM lParam,
  UINT fuFlags,
  UINT uTimeout,
  PDWORD_PTR lpdwResult)
{
  MSG AnsiMsg, UcMsg, KMMsg;
  LRESULT Result;
  DOSENDMESSAGE dsm;

  if ( Msg & ~WM_MAXIMUM || fuFlags & ~(SMTO_NOTIMEOUTIFNOTHUNG|SMTO_ABORTIFHUNG|SMTO_BLOCK))
  {
     SetLastError( ERROR_INVALID_PARAMETER );
     return 0;
  }

  if (lpdwResult) *lpdwResult = 0;

  SPY_EnterMessage(SPY_SENDMESSAGE, hWnd, Msg, wParam, lParam);

  dsm.uFlags = fuFlags;
  dsm.uTimeout = uTimeout;
  dsm.Result = 0;

  AnsiMsg.hwnd = hWnd;
  AnsiMsg.message = Msg;
  AnsiMsg.wParam = wParam;
  AnsiMsg.lParam = lParam;
  AnsiMsg.time = 0;
  AnsiMsg.pt.x = 0;
  AnsiMsg.pt.y = 0;

  if (! MsgiAnsiToUnicodeMessage(hWnd, &UcMsg, &AnsiMsg))
  {
      return FALSE;
  }

  if (!MsgiUMToKMMessage(&UcMsg, &KMMsg, FALSE))
  {
      MsgiAnsiToUnicodeCleanup(&UcMsg, &AnsiMsg);
      return FALSE;
  }

  Result = NtUserMessageCall( hWnd,
                              KMMsg.message,
                              KMMsg.wParam,
                              KMMsg.lParam,
                             (ULONG_PTR)&dsm,
                              FNID_SENDMESSAGEWTOOPTION,
                              TRUE);

  MsgiUMToKMCleanup(&UcMsg, &KMMsg);
  MsgiAnsiToUnicodeReply(&UcMsg, &AnsiMsg, &Result);

  if (lpdwResult) *lpdwResult = dsm.Result;

  SPY_ExitMessage(SPY_RESULT_OK, hWnd, Msg, Result, wParam, lParam);

  return Result;
}


/*
 * @implemented
 */
LRESULT
WINAPI
SendMessageTimeoutW(
  HWND hWnd,
  UINT Msg,
  WPARAM wParam,
  LPARAM lParam,
  UINT fuFlags,
  UINT uTimeout,
  PDWORD_PTR lpdwResult)
{
  LRESULT Result;
  DOSENDMESSAGE dsm;
  MSG UMMsg, KMMsg;

  if ( Msg & ~WM_MAXIMUM || fuFlags & ~(SMTO_NOTIMEOUTIFNOTHUNG|SMTO_ABORTIFHUNG|SMTO_BLOCK))
  {
     SetLastError( ERROR_INVALID_PARAMETER );
     return 0;
  }

  if (lpdwResult) *lpdwResult = 0;

  SPY_EnterMessage(SPY_SENDMESSAGE, hWnd, Msg, wParam, lParam);

  dsm.uFlags = fuFlags;
  dsm.uTimeout = uTimeout;
  dsm.Result = 0;

  UMMsg.hwnd = hWnd;
  UMMsg.message = Msg;
  UMMsg.wParam = wParam;
  UMMsg.lParam = lParam;
  UMMsg.time = 0;
  UMMsg.pt.x = 0;
  UMMsg.pt.y = 0;
  if (! MsgiUMToKMMessage(&UMMsg, &KMMsg, TRUE))
  {
     return FALSE;
  }

  Result = NtUserMessageCall( hWnd,
                              KMMsg.message,
                              KMMsg.wParam,
                              KMMsg.lParam,
                             (ULONG_PTR)&dsm,
                              FNID_SENDMESSAGEWTOOPTION,
                              FALSE);

  MsgiUMToKMCleanup(&UMMsg, &KMMsg);

  if (lpdwResult) *lpdwResult = dsm.Result;

  SPY_ExitMessage(SPY_RESULT_OK, hWnd, Msg, Result, wParam, lParam);

  return Result;
}

/*
 * @implemented
 */
BOOL
WINAPI
SendNotifyMessageA(
  HWND hWnd,
  UINT Msg,
  WPARAM wParam,
  LPARAM lParam)
{
  BOOL Ret;
  MSG AnsiMsg, UcMsg;

  if (is_pointer_message(Msg))
  {
     SetLastError( ERROR_MESSAGE_SYNC_ONLY );
     return FALSE;
  }

  AnsiMsg.hwnd = hWnd;
  AnsiMsg.message = Msg;
  AnsiMsg.wParam = wParam;
  AnsiMsg.lParam = lParam;
  AnsiMsg.time = 0;
  AnsiMsg.pt.x = 0;
  AnsiMsg.pt.y = 0;
  if (! MsgiAnsiToUnicodeMessage(hWnd, &UcMsg, &AnsiMsg))
  {
     return FALSE;
  }
  Ret = SendNotifyMessageW(hWnd, UcMsg.message, UcMsg.wParam, UcMsg.lParam);

  MsgiAnsiToUnicodeCleanup(&UcMsg, &AnsiMsg);

  return Ret;
}

/*
 * @implemented
 */
BOOL
WINAPI
SendNotifyMessageW(
  HWND hWnd,
  UINT Msg,
  WPARAM wParam,
  LPARAM lParam)
{
  LRESULT Result;

  if (is_pointer_message(Msg))
  {
     SetLastError( ERROR_MESSAGE_SYNC_ONLY );
     return FALSE;
  }

  Result = NtUserMessageCall( hWnd,
                              Msg,
                              wParam,
                              lParam,
                              0,
                              FNID_SENDNOTIFYMESSAGE,
                              FALSE);

  return Result;
}

/*
 * @implemented
 */
BOOL WINAPI
TranslateMessageEx(CONST MSG *lpMsg, UINT Flags)
{
    switch (lpMsg->message)
    {
        case WM_KEYDOWN:
        case WM_KEYUP:
        case WM_SYSKEYDOWN:
        case WM_SYSKEYUP:
            return(NtUserTranslateMessage((LPMSG)lpMsg, Flags));

        default:
            if ( lpMsg->message & ~WM_MAXIMUM )
               SetLastError(ERROR_INVALID_PARAMETER);
            return FALSE;
    }
}


/*
 * @implemented
 */
BOOL WINAPI
TranslateMessage(CONST MSG *lpMsg)
{
  BOOL Ret = FALSE;

// Ref: msdn ImmGetVirtualKey:
// http://msdn.microsoft.com/en-us/library/aa912145.aspx
/*
  if ( (LOWORD(lpMsg->wParam) != VK_PROCESSKEY) ||
       !(Ret = IMM_ImmTranslateMessage( lpMsg->hwnd,
                                        lpMsg->message,
                                        lpMsg->wParam,
                                        lpMsg->lParam)) )*/
  {
     Ret = TranslateMessageEx((LPMSG)lpMsg, 0);
  }
  return Ret;
}


/*
 * @implemented
 */
UINT WINAPI
RegisterWindowMessageA(LPCSTR lpString)
{
  UNICODE_STRING String;
  UINT Atom;

  if (!RtlCreateUnicodeStringFromAsciiz(&String, (PCSZ)lpString))
    {
      return(0);
    }
  Atom = NtUserRegisterWindowMessage(&String);
  RtlFreeUnicodeString(&String);
  return(Atom);
}


/*
 * @implemented
 */
UINT WINAPI
RegisterWindowMessageW(LPCWSTR lpString)
{
  UNICODE_STRING String;

  RtlInitUnicodeString(&String, lpString);
  return(NtUserRegisterWindowMessage(&String));
}

/*
 * @implemented
 */
HWND WINAPI
GetCapture(VOID)
{
  return (HWND)NtUserGetThreadState(THREADSTATE_CAPTUREWINDOW);
}

/*
 * @implemented
 */
BOOL WINAPI
ReleaseCapture(VOID)
{
  return NtUserxReleaseCapture();
}


/*
 * @implemented
 */
DWORD
WINAPI
RealGetQueueStatus(UINT flags)
{
   #define QS_TEMPALLINPUT 255 // ATM, do not support QS_RAWINPUT
   if (flags & ~(QS_SMRESULT|QS_ALLPOSTMESSAGE|QS_TEMPALLINPUT))
   {
      SetLastError( ERROR_INVALID_FLAGS );
      return 0;
   }
   return NtUserxGetQueueStatus(flags);
}


/*
 * @implemented
 */
BOOL WINAPI GetInputState(VOID)
{
   PCLIENTTHREADINFO pcti = GetWin32ClientInfo()->pClientThreadInfo;

   if ((!pcti) || (pcti->fsChangeBits & (QS_KEY|QS_MOUSEBUTTON)))
      return (BOOL)NtUserGetThreadState(THREADSTATE_GETINPUTSTATE);

   return FALSE;
}


NTSTATUS WINAPI
User32CallWindowProcFromKernel(PVOID Arguments, ULONG ArgumentLength)
{
  PWINDOWPROC_CALLBACK_ARGUMENTS CallbackArgs;
  MSG KMMsg, UMMsg;
  PWND pWnd = NULL;
  PCLIENTINFO pci = GetWin32ClientInfo();

  /* 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;
  KMMsg.time = 0;
  KMMsg.pt.x = 0;
  KMMsg.pt.y = 0;
  /* 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));
     switch(KMMsg.message)
     {
        case WM_CREATE:
        {
            TRACE("WM_CREATE CB %p lParam %p\n",CallbackArgs, KMMsg.lParam);
            break;
        }
        case WM_NCCREATE:
        {
            TRACE("WM_NCCREATE CB %p lParam %p\n",CallbackArgs, KMMsg.lParam);
            break;
        }
        case WM_SYSTIMER:
        {
            TRACE("WM_SYSTIMER %p\n",KMMsg.hwnd);
            break;
        }
        case WM_SIZING:
        {
           PRECT prect = (PRECT) KMMsg.lParam;
           TRACE("WM_SIZING 1 t %d l %d r %d b %d\n",prect->top,prect->left,prect->right,prect->bottom);
           break;
        }
        default:
           break;
     }
    }
  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))
    {
    }

  if (pci->CallbackWnd.hWnd == UMMsg.hwnd)
     pWnd = pci->CallbackWnd.pWnd;

  CallbackArgs->Result = IntCallWindowProcW( CallbackArgs->IsAnsiProc,
                                             CallbackArgs->Proc,
                                             pWnd,
                                             UMMsg.hwnd,
                                             UMMsg.message,
                                             UMMsg.wParam,
                                             UMMsg.lParam);

  if (! MsgiKMToUMReply(&KMMsg, &UMMsg, &CallbackArgs->Result))
    {
    }

  if (0 <= CallbackArgs->lParamBufferSize)
  {
     switch(KMMsg.message)
     {
        case WM_SIZING:
        {
           PRECT prect = (PRECT) KMMsg.lParam;
           TRACE("WM_SIZING 2 t %d l %d r %d b %d\n",prect->top,prect->left,prect->right,prect->bottom);
           break;
        }
        default:
           break;
     }
  }
  return ZwCallbackReturn(CallbackArgs, ArgumentLength, STATUS_SUCCESS);
}

/*
 * @implemented
 */
BOOL WINAPI SetMessageQueue(int cMessagesMax)
{
  /* Function does nothing on 32 bit windows */
  return TRUE;
}
typedef DWORD (WINAPI * RealGetQueueStatusProc)(UINT flags);
typedef DWORD (WINAPI * RealMsgWaitForMultipleObjectsExProc)(DWORD nCount, CONST HANDLE *lpHandles, DWORD dwMilliseconds, DWORD dwWakeMask, DWORD dwFlags);
typedef BOOL (WINAPI * RealInternalGetMessageProc)(LPMSG,HWND,UINT,UINT,UINT,BOOL);
typedef BOOL (WINAPI * RealWaitMessageExProc)(DWORD,UINT);

typedef struct _USER_MESSAGE_PUMP_ADDRESSES {
	DWORD cbSize;
	RealInternalGetMessageProc NtUserRealInternalGetMessage;
	RealWaitMessageExProc NtUserRealWaitMessageEx;
	RealGetQueueStatusProc RealGetQueueStatus;
	RealMsgWaitForMultipleObjectsExProc RealMsgWaitForMultipleObjectsEx;
} USER_MESSAGE_PUMP_ADDRESSES, * PUSER_MESSAGE_PUMP_ADDRESSES;

DWORD
WINAPI
RealMsgWaitForMultipleObjectsEx(
  DWORD nCount,
  CONST HANDLE *pHandles,
  DWORD dwMilliseconds,
  DWORD dwWakeMask,
  DWORD dwFlags);

typedef BOOL (WINAPI * MESSAGEPUMPHOOKPROC)(BOOL Unregistering,PUSER_MESSAGE_PUMP_ADDRESSES MessagePumpAddresses);

CRITICAL_SECTION gcsMPH;
MESSAGEPUMPHOOKPROC gpfnInitMPH;
DWORD gcLoadMPH = 0;
USER_MESSAGE_PUMP_ADDRESSES gmph = {sizeof(USER_MESSAGE_PUMP_ADDRESSES),
	NtUserRealInternalGetMessage,
	NtUserRealWaitMessageEx,
	RealGetQueueStatus,
	RealMsgWaitForMultipleObjectsEx
};

DWORD gfMessagePumpHook = 0;

BOOL WINAPI IsInsideMessagePumpHook()
{  // FF uses this and polls it when Min/Max
   PCLIENTTHREADINFO pcti = GetWin32ClientInfo()->pClientThreadInfo;
   return (gfMessagePumpHook && pcti && (pcti->dwcPumpHook > 0));
}

void WINAPI ResetMessagePumpHook(PUSER_MESSAGE_PUMP_ADDRESSES Addresses)
{
	Addresses->cbSize = sizeof(USER_MESSAGE_PUMP_ADDRESSES);
	Addresses->NtUserRealInternalGetMessage = NtUserRealInternalGetMessage;
	Addresses->NtUserRealWaitMessageEx = NtUserRealWaitMessageEx;
	Addresses->RealGetQueueStatus = RealGetQueueStatus;
	Addresses->RealMsgWaitForMultipleObjectsEx = RealMsgWaitForMultipleObjectsEx;
}

BOOL WINAPI RegisterMessagePumpHook(MESSAGEPUMPHOOKPROC Hook)
{
	EnterCriticalSection(&gcsMPH);
	if(!Hook) {
		SetLastError(ERROR_INVALID_PARAMETER);
		LeaveCriticalSection(&gcsMPH);
		return FALSE;
	}
	if(!gcLoadMPH) {
		USER_MESSAGE_PUMP_ADDRESSES Addresses;
		gpfnInitMPH = Hook;
		ResetMessagePumpHook(&Addresses);
		if(!Hook(FALSE, &Addresses) || !Addresses.cbSize) {
			LeaveCriticalSection(&gcsMPH);
			return FALSE;
		}
		memcpy(&gmph, &Addresses, Addresses.cbSize);
	} else {
		if(gpfnInitMPH != Hook) {
			LeaveCriticalSection(&gcsMPH);
			return FALSE;
		}
	}
	if(NtUserxInitMessagePump()) {
		LeaveCriticalSection(&gcsMPH);
		return FALSE;
	}
	if (!gcLoadMPH++) {
		InterlockedExchange((PLONG)&gfMessagePumpHook, 1);
	}
	LeaveCriticalSection(&gcsMPH);
	return TRUE;
}

BOOL WINAPI UnregisterMessagePumpHook(VOID)
{
	EnterCriticalSection(&gcsMPH);
	if(gcLoadMPH > 0) {
		if(NtUserxUnInitMessagePump()) {
			gcLoadMPH--;
			if(!gcLoadMPH) {
				InterlockedExchange((PLONG)&gfMessagePumpHook, 0);
				gpfnInitMPH(TRUE, NULL);
				ResetMessagePumpHook(&gmph);
				gpfnInitMPH = 0;
			}
			LeaveCriticalSection(&gcsMPH);
			return TRUE;
		}
	}
	LeaveCriticalSection(&gcsMPH);
	return FALSE;
}

DWORD WINAPI GetQueueStatus(UINT flags)
{
	return IsInsideMessagePumpHook() ? gmph.RealGetQueueStatus(flags) : RealGetQueueStatus(flags);
}

/**
 * @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 WINAPI
RealMsgWaitForMultipleObjectsEx(
   DWORD nCount,
   const HANDLE *pHandles,
   DWORD dwMilliseconds,
   DWORD dwWakeMask,
   DWORD dwFlags)
{
   LPHANDLE RealHandles;
   HANDLE MessageQueueHandle;
   DWORD Result;
   PCLIENTINFO pci;
   PCLIENTTHREADINFO pcti;

   if (dwFlags & ~(MWMO_WAITALL | MWMO_ALERTABLE | MWMO_INPUTAVAILABLE))
   {
      SetLastError(ERROR_INVALID_PARAMETER);
      return WAIT_FAILED;
   }

   pci = GetWin32ClientInfo();
   if (!pci) return WAIT_FAILED;

   pcti = pci->pClientThreadInfo;
   if (pcti && ( !nCount || !(dwFlags & MWMO_WAITALL) ))
   {
      if ( (pcti->fsChangeBits & LOWORD(dwWakeMask)) ||
           ( (dwFlags & MWMO_INPUTAVAILABLE) && (pcti->fsWakeBits & LOWORD(dwWakeMask)) ) )
      {
         //FIXME("Return Chg 0x%x Wake 0x%x Mask 0x%x nCnt %d\n",pcti->fsChangeBits, pcti->fsWakeBits, dwWakeMask, nCount);
         return nCount;
      }
   }

   MessageQueueHandle = NtUserxMsqSetWakeMask(MAKELONG(dwWakeMask, dwFlags));
   if (MessageQueueHandle == NULL)
   {
      SetLastError(0); /* ? */
      return WAIT_FAILED;
   }

   RealHandles = HeapAlloc(GetProcessHeap(), 0, (nCount + 1) * sizeof(HANDLE));
   if (RealHandles == NULL)
   {
      NtUserxMsqClearWakeMask();
      SetLastError(ERROR_NOT_ENOUGH_MEMORY);
      return WAIT_FAILED;
   }

   RtlCopyMemory(RealHandles, pHandles, nCount * sizeof(HANDLE));
   RealHandles[nCount] = MessageQueueHandle;

   //FIXME("1 Chg 0x%x Wake 0x%x Mask 0x%x nCnt %d\n",pcti->fsChangeBits, pcti->fsWakeBits, dwWakeMask, nCount);

   Result = WaitForMultipleObjectsEx( nCount + 1,
                                      RealHandles,
                                      dwFlags & MWMO_WAITALL,
                                      dwMilliseconds,
                                      dwFlags & MWMO_ALERTABLE );

   //FIXME("2 Chg 0x%x Wake 0x%x Mask 0x%x nCnt %d\n",pcti->fsChangeBits, pcti->fsWakeBits, dwWakeMask, nCount);

   HeapFree(GetProcessHeap(), 0, RealHandles);
   NtUserxMsqClearWakeMask();

   // wine hack! MSDN: If dwMilliseconds is zero,,specified objects are not signaled; it always returns immediately.
   if (!Result && !nCount && !dwMilliseconds) Result = WAIT_TIMEOUT;

   //FIXME("Result 0X%x\n",Result);
   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 WINAPI
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(&gcsMPH);

  return TRUE;
}

VOID FASTCALL MessageCleanup(VOID)
{
  DeleteCriticalSection(&DdeCrst);
  DeleteCriticalSection(&gcsMPH);
}

/*
 * @implemented
 */
BOOL WINAPI
IsDialogMessageA( HWND hwndDlg, LPMSG pmsg )
{
    MSG msg = *pmsg;
    msg.wParam = map_wparam_AtoW( msg.message, msg.wParam );
    return IsDialogMessageW( hwndDlg, &msg );
}

LONG
WINAPI
IntBroadcastSystemMessage(
    DWORD dwflags,
    LPDWORD lpdwRecipients,
    UINT uiMessage,
    WPARAM wParam,
    LPARAM lParam,
    PBSMINFO pBSMInfo,
    BOOL Ansi)
{
    BROADCASTPARM parm;
    DWORD recips = BSM_ALLCOMPONENTS;
    BOOL ret = -1; // Set to return fail
    static const DWORD all_flags = ( BSF_QUERY | BSF_IGNORECURRENTTASK | BSF_FLUSHDISK | BSF_NOHANG
                                   | BSF_POSTMESSAGE | BSF_FORCEIFHUNG | BSF_NOTIMEOUTIFNOTHUNG
                                   | BSF_ALLOWSFW | BSF_SENDNOTIFYMESSAGE | BSF_RETURNHDESK | BSF_LUID );

    if ((dwflags & ~all_flags) ||
        (!pBSMInfo && (dwflags & (BSF_RETURNHDESK|BSF_LUID))) )
    {
        SetLastError(ERROR_INVALID_PARAMETER);
        return 0;
    }

    if(uiMessage >= WM_USER && uiMessage < 0xC000)
    {
        SetLastError(ERROR_INVALID_PARAMETER);
        return 0;
    }

    if (dwflags & BSF_FORCEIFHUNG) dwflags |= BSF_NOHANG;

    if (dwflags & BSF_QUERY) dwflags &= ~BSF_SENDNOTIFYMESSAGE|BSF_POSTMESSAGE;

    if (!lpdwRecipients)
        lpdwRecipients = &recips;

    if (*lpdwRecipients & ~(BSM_APPLICATIONS|BSM_ALLDESKTOPS|BSM_INSTALLABLEDRIVERS|BSM_NETDRIVER|BSM_VXDS))
    {
       SetLastError(ERROR_INVALID_PARAMETER);
       return 0;
    }

    if ( pBSMInfo && (dwflags & BSF_QUERY) )
    {
       if (pBSMInfo->cbSize != sizeof(BSMINFO))
       {
           SetLastError(ERROR_INVALID_PARAMETER);
           return 0;
       }
    }

    parm.hDesk = NULL;
    parm.hWnd = NULL;
    parm.flags = dwflags;
    parm.recipients = *lpdwRecipients;

    if (dwflags & BSF_LUID) parm.luid = pBSMInfo->luid;

    ret = NtUserMessageCall(GetDesktopWindow(),
                                     uiMessage,
                                        wParam,
                                        lParam,
                              (ULONG_PTR)&parm,
                   FNID_BROADCASTSYSTEMMESSAGE,
                                          Ansi);

    if (!ret)
    {
       if ( pBSMInfo && (dwflags & BSF_QUERY) )
       {
          pBSMInfo->hdesk = parm.hDesk;
          pBSMInfo->hwnd = parm.hWnd;
       }
    }
    return ret;
}

/*
 * @implemented
 */
LONG
WINAPI
BroadcastSystemMessageA(
  DWORD dwFlags,
  LPDWORD lpdwRecipients,
  UINT uiMessage,
  WPARAM wParam,
  LPARAM lParam)
{
  return IntBroadcastSystemMessage( dwFlags, lpdwRecipients, uiMessage, wParam, lParam, NULL, TRUE );
}

/*
 * @implemented
 */
LONG
WINAPI
BroadcastSystemMessageW(
  DWORD dwFlags,
  LPDWORD lpdwRecipients,
  UINT uiMessage,
  WPARAM wParam,
  LPARAM lParam)
{
  return IntBroadcastSystemMessage( dwFlags, lpdwRecipients, uiMessage, wParam, lParam, NULL, FALSE );
}

/*
 * @implemented
 */
LONG
WINAPI
BroadcastSystemMessageExA(
    DWORD dwflags,
    LPDWORD lpdwRecipients,
    UINT uiMessage,
    WPARAM wParam,
    LPARAM lParam,
    PBSMINFO pBSMInfo)
{
  return IntBroadcastSystemMessage( dwflags, lpdwRecipients, uiMessage, wParam, lParam , pBSMInfo, TRUE );
}

/*
 * @implemented
 */
LONG
WINAPI
BroadcastSystemMessageExW(
    DWORD dwflags,
    LPDWORD lpdwRecipients,
    UINT uiMessage,
    WPARAM wParam,
    LPARAM lParam,
    PBSMINFO pBSMInfo)
{
  return IntBroadcastSystemMessage( dwflags, lpdwRecipients, uiMessage, wParam, lParam , pBSMInfo, FALSE );
}