[WIN32K]
[reactos.git] / reactos / subsystems / win32 / win32k / ntuser / accelerator.c
index a913de1..1e20a83 100644 (file)
@@ -1,93 +1,40 @@
 /*
- *  ReactOS W32 Subsystem
- *  Copyright (C) 1998, 1999, 2000, 2001, 2002, 2003 ReactOS Team
- *
- *  This program is free software; you can redistribute it and/or modify
- *  it under the terms of the GNU General Public License as published by
- *  the Free Software Foundation; either version 2 of the License, or
- *  (at your option) any later version.
- *
- *  This program is distributed in the hope that it will be useful,
- *  but WITHOUT ANY WARRANTY; without even the implied warranty of
- *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
- *  GNU General Public License for more details.
- *
- *  You should have received a copy of the GNU General Public License along
- *  with this program; if not, write to the Free Software Foundation, Inc.,
- *  51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
- */
-/* $Id$
- *
  * COPYRIGHT:        See COPYING in the top level directory
  * PROJECT:          ReactOS kernel
- * PURPOSE:          Window classes
- * FILE:             subsys/win32k/ntuser/class.c
+ * PURPOSE:          Window accelerator
+ * FILE:             subsystems/win32/win32k/ntuser/accelerator.c
  * PROGRAMER:        Casper S. Hornstrup (chorns@users.sourceforge.net)
- * REVISION HISTORY:
- *       06-06-2001  CSH  Created
- */
-
-/*
- * Copyright 1993 Martin Ayotte
- * Copyright 1994 Alexandre Julliard
- * Copyright 1997 Morten Welinder
- *
- * This library is free software; you can redistribute it and/or
- * modify it under the terms of the GNU Lesser General Public
- * License as published by the Free Software Foundation; either
- * version 2.1 of the License, or (at your option) any later version.
- *
- * This library is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
- * Lesser General Public License for more details.
- *
- * You should have received a copy of the GNU Lesser General Public
- * License along with this library; if not, write to the Free Software
- * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301  USA
+ *                   Copyright 1993 Martin Ayotte
+ *                   Copyright 1994 Alexandre Julliard
+ *                   Copyright 1997 Morten Welinder
+ *                   Copyright 2011 Rafal Harabien
  */
 
-/* INCLUDES ******************************************************************/
-
 #include <win32k.h>
+DBG_DEFAULT_CHANNEL(UserAccel);
 
-#define NDEBUG
-#include <debug.h>
+#define FVIRT_TBL_END 0x80
+#define FVIRT_MASK 0x7F
 
 /* FUNCTIONS *****************************************************************/
 
-NTSTATUS FASTCALL
-InitAcceleratorImpl(VOID)
-{
-   return(STATUS_SUCCESS);
-}
-
-NTSTATUS FASTCALL
-CleanupAcceleratorImpl(VOID)
-{
-   return(STATUS_SUCCESS);
-}
-
-
 PACCELERATOR_TABLE FASTCALL UserGetAccelObject(HACCEL hAccel)
 {
    PACCELERATOR_TABLE Accel;
 
    if (!hAccel)
    {
-      SetLastWin32Error(ERROR_INVALID_ACCEL_HANDLE);
+      EngSetLastError(ERROR_INVALID_ACCEL_HANDLE);
       return NULL;
    }
 
-   Accel= UserGetObject(gHandleTable, hAccel,  otAccel);
+   Accel= UserGetObject(gHandleTable, hAccel, otAccel);
    if (!Accel)
    {
-      SetLastWin32Error(ERROR_INVALID_ACCEL_HANDLE);
+      EngSetLastError(ERROR_INVALID_ACCEL_HANDLE);
       return NULL;
    }
 
-   ASSERT(Accel->head.cLockObj >= 0);
-
    return Accel;
 }
 
@@ -96,188 +43,133 @@ static
 BOOLEAN FASTCALL
 co_IntTranslateAccelerator(
    PWND Window,
-   UINT message,
-   WPARAM wParam,
-   LPARAM lParam,
-   BYTE fVirt,
-   WORD key,
-   WORD cmd)
+   CONST MSG *pMsg,
+   CONST ACCEL *pAccel)
 {
-   UINT mesg = 0;
+   BOOL bFound = FALSE;
+   UINT Mask = 0, nPos;
+   HWND hWnd;
+   HMENU hMenu, hSubMenu;
+   PMENU_OBJECT MenuObject, SubMenu;
+   PMENU_ITEM MenuItem;
 
    ASSERT_REFS_CO(Window);
 
-   DPRINT("IntTranslateAccelerator(hwnd %x, message %x, wParam %x, lParam %x, fVirt %d, key %x, cmd %x)\n",
-          Window->head.h, message, wParam, lParam, fVirt, key, cmd);
+   hWnd = Window->head.h;
 
-   if (wParam != key)
-   {
-      return FALSE;
-   }
+   TRACE("IntTranslateAccelerator(hwnd %x, message %x, wParam %x, lParam %x, fVirt %d, key %x, cmd %x)\n",
+         hWnd, pMsg->message, pMsg->wParam, pMsg->lParam, pAccel->fVirt, pAccel->key, pAccel->cmd);
 
-   if (message == WM_CHAR)
+   if (UserGetKeyState(VK_CONTROL) & 0x8000) Mask |= FCONTROL;
+   if (UserGetKeyState(VK_MENU) & 0x8000) Mask |= FALT; // FIXME: VK_LMENU (msg winetest!)
+   if (UserGetKeyState(VK_SHIFT) & 0x8000) Mask |= FSHIFT;
+   TRACE("Mask 0x%x\n", Mask);
+
+   if (pAccel->fVirt & FVIRTKEY)
    {
-      if (!(fVirt & FALT) && !(fVirt & FVIRTKEY))
-      {
-         DPRINT("found accel for WM_CHAR: ('%c')\n", wParam & 0xff);
-         goto found;
-      }
+       /* This is a virtual key. Process WM_(SYS)KEYDOWN messages. */
+       if (pMsg->message == WM_KEYDOWN || pMsg->message == WM_SYSKEYDOWN)
+       {
+           /* Check virtual key and SHIFT, CTRL, LALT state */
+           if (pMsg->wParam == pAccel->key && Mask == (pAccel->fVirt & (FSHIFT | FCONTROL | FALT)))
+           {
+               bFound = TRUE;
+           }
+       }
    }
    else
    {
-      if ((fVirt & FVIRTKEY) > 0)
-      {
-         INT mask = 0;
-         DPRINT("found accel for virt_key %04x (scan %04x)\n",
-                wParam, 0xff & HIWORD(lParam));
-
-         DPRINT("NtUserGetKeyState(VK_SHIFT) = 0x%x\n",
-                UserGetKeyState(VK_SHIFT));
-         DPRINT("NtUserGetKeyState(VK_CONTROL) = 0x%x\n",
-                UserGetKeyState(VK_CONTROL));
-         DPRINT("NtUserGetKeyState(VK_MENU) = 0x%x\n",
-                UserGetKeyState(VK_MENU));
-
-         if (UserGetKeyState(VK_SHIFT) & 0x8000)
-            mask |= FSHIFT;
-         if (UserGetKeyState(VK_CONTROL) & 0x8000)
-            mask |= FCONTROL;
-         if (UserGetKeyState(VK_MENU) & 0x8000)
-            mask |= FALT;
-         if (mask == (fVirt & (FSHIFT | FCONTROL | FALT)))
-            goto found;
-         DPRINT("but incorrect SHIFT/CTRL/ALT-state\n");
-      }
-      else
-      {
-         if (!(lParam & 0x01000000))  /* no special_key */
-         {
-            if ((fVirt & FALT) && (lParam & 0x20000000))
-            {                            /* ^^ ALT pressed */
-               DPRINT("found accel for Alt-%c\n", wParam & 0xff);
-               goto found;
-            }
-         }
-      }
+       /* This is a char code. Process WM_(SYS)CHAR messages. */
+       if (pMsg->message == WM_CHAR || pMsg->message == WM_SYSCHAR)
+       {
+           /* Check char code and LALT state only */
+           if (pMsg->wParam == pAccel->key && (Mask & FALT) == (pAccel->fVirt & FALT))
+           {
+               bFound = TRUE;
+           }
+       }
    }
 
-   DPRINT("IntTranslateAccelerator(hwnd %x, message %x, wParam %x, lParam %x, fVirt %d, key %x, cmd %x) = FALSE\n",
-          Window->head.h, message, wParam, lParam, fVirt, key, cmd);
-
-   return FALSE;
-
-found:
-   if (message == WM_KEYUP || message == WM_SYSKEYUP)
-      mesg = 1;
-   else if (IntGetCaptureWindow())
-      mesg = 2;
-   else if (!IntIsWindowVisible(Window)) /* FIXME: WINE IsWindowEnabled == IntIsWindowVisible? */
-      mesg = 3;
-   else
+   if (!bFound)
    {
-#if 0
-      HMENU hMenu, hSubMenu, hSysMenu;
-      UINT uSysStat = (UINT)-1, uStat = (UINT)-1, nPos;
-
-      hMenu = (UserGetWindowLongW(hWnd, GWL_STYLE) & WS_CHILD) ? 0 : GetMenu(hWnd);
-      hSysMenu = get_win_sys_menu(hWnd);
-
-      /* find menu item and ask application to initialize it */
-      /* 1. in the system menu */
-      hSubMenu = hSysMenu;
-      nPos = cmd;
-      if(MENU_FindItem(&hSubMenu, &nPos, MF_BYCOMMAND))
-      {
-         co_IntSendMessage(hWnd, WM_INITMENU, (WPARAM)hSysMenu, 0L);
-         if(hSubMenu != hSysMenu)
-         {
-            nPos = MENU_FindSubMenu(&hSysMenu, hSubMenu);
-            TRACE_(accel)("hSysMenu = %p, hSubMenu = %p, nPos = %d\n", hSysMenu, hSubMenu, nPos);
-            co_IntSendMessage(hWnd, WM_INITMENUPOPUP, (WPARAM)hSubMenu, MAKELPARAM(nPos, TRUE));
-         }
-         uSysStat = GetMenuState(GetSubMenu(hSysMenu, 0), cmd, MF_BYCOMMAND);
-      }
-      else /* 2. in the window's menu */
-      {
-         hSubMenu = hMenu;
-         nPos = cmd;
-         if(MENU_FindItem(&hSubMenu, &nPos, MF_BYCOMMAND))
-         {
-            co_IntSendMessage(hWnd, WM_INITMENU, (WPARAM)hMenu, 0L);
-            if(hSubMenu != hMenu)
-            {
-               nPos = MENU_FindSubMenu(&hMenu, hSubMenu);
-               TRACE_(accel)("hMenu = %p, hSubMenu = %p, nPos = %d\n", hMenu, hSubMenu, nPos);
-               co_IntSendMessage(hWnd, WM_INITMENUPOPUP, (WPARAM)hSubMenu, MAKELPARAM(nPos, FALSE));
-            }
-            uStat = GetMenuState(hMenu, cmd, MF_BYCOMMAND);
-         }
-      }
-
-      if (uSysStat != (UINT)-1)
-      {
-         if (uSysStat & (MF_DISABLED|MF_GRAYED))
-            mesg=4;
-         else
-            mesg=WM_SYSCOMMAND;
-      }
-      else
-      {
-         if (uStat != (UINT)-1)
-         {
-            if (IsIconic(hWnd))
-               mesg=5;
-            else
-            {
-               if (uStat & (MF_DISABLED|MF_GRAYED))
-                  mesg=6;
-               else
-                  mesg=WM_COMMAND;
-            }
-         }
-         else
-         {
-            mesg=WM_COMMAND;
-         }
-      }
-#else
-      DPRINT1("Menu search not implemented\n");
-      mesg = WM_COMMAND;
-#endif
-
+       /* Don't translate this msg */
+       TRACE("IntTranslateAccelerator returns FALSE\n");
+       return FALSE;
    }
 
-   if (mesg == WM_COMMAND)
+   /* Check if accelerator is associated with menu command */
+   hMenu = (Window->style & WS_CHILD) ? 0 : (HMENU)Window->IDMenu;
+   hSubMenu = NULL;
+   MenuObject = IntGetMenuObject(hMenu);
+   if (MenuObject)
    {
-      DPRINT(", sending WM_COMMAND, wParam=%0x\n", 0x10000 | cmd);
-      co_IntSendMessage(Window->head.h, mesg, 0x10000 | cmd, 0L);
+       nPos = IntGetMenuItemByFlag(MenuObject,
+                                   pAccel->cmd,
+                                   MF_BYCOMMAND,
+                                   &SubMenu,
+                                   &MenuItem,
+                                   NULL);
+       if (nPos != (UINT)-1)
+           hSubMenu = SubMenu->head.h;
+       else
+           hMenu = NULL;
    }
-   else if (mesg == WM_SYSCOMMAND)
+   if (!hMenu)
    {
-      DPRINT(", sending WM_SYSCOMMAND, wParam=%0x\n", cmd);
-      co_IntSendMessage(Window->head.h, mesg, cmd, 0x00010000L);
+       /* Check system menu now */
+       hMenu = Window->SystemMenu;
+       hSubMenu = hMenu; /* system menu is a popup menu */
+       MenuObject = IntGetMenuObject(hMenu);
+       if (MenuObject)
+       {
+           nPos = IntGetMenuItemByFlag(MenuObject,
+                                       pAccel->cmd,
+                                       MF_BYCOMMAND,
+                                       &SubMenu,
+                                       &MenuItem,
+                                       NULL);
+           if (nPos != (UINT)-1)
+               hSubMenu = SubMenu->head.h;
+           else
+               hMenu = NULL;
+       }
    }
-   else
+
+   /* If this is a menu item, there is no capturing enabled and
+      window is not disabled, send WM_INITMENU */
+   if (hMenu && !IntGetCaptureWindow())
    {
-      /*  some reasons for NOT sending the WM_{SYS}COMMAND message:
-       *   #0: unknown (please report!)
-       *   #1: for WM_KEYUP,WM_SYSKEYUP
-       *   #2: mouse is captured
-       *   #3: window is disabled
-       *   #4: it's a disabled system menu option
-       *   #5: it's a menu option, but window is iconic
-       *   #6: it's a menu option, but disabled
-       */
-      DPRINT(", but won't send WM_{SYS}COMMAND, reason is #%d\n", mesg);
-      if (mesg == 0)
-      {
-         DPRINT1(" unknown reason - please report!");
-      }
+       co_IntSendMessage(hWnd, WM_INITMENU, (WPARAM)hMenu, 0L);
+       if (hSubMenu)
+       {
+           nPos = IntFindSubMenu(&hMenu, hSubMenu);
+           TRACE("hSysMenu = %p, hSubMenu = %p, nPos = %d\n", hMenu, hSubMenu, nPos);
+           co_IntSendMessage(hWnd, WM_INITMENUPOPUP, (WPARAM)hSubMenu, MAKELPARAM(nPos, TRUE));
+       }
    }
 
-   DPRINT("IntTranslateAccelerator(hWnd %x, message %x, wParam %x, lParam %x, fVirt %d, key %x, cmd %x) = TRUE\n",
-          Window->head.h, message, wParam, lParam, fVirt, key, cmd);
+   /* Don't send any message if:
+      - window is disabled
+      - menu item is disabled
+      - this is window menu and window is minimized */
+   if (!(Window->style & WS_DISABLED) &&
+       !(hMenu && IntGetMenuState(hMenu, pAccel->cmd, MF_BYCOMMAND) & (MF_DISABLED|MF_GRAYED)) &&
+       !(hMenu && hMenu == (HMENU)Window->IDMenu && (Window->style & WS_MINIMIZED)))
+   {
+       /* If this is system menu item, send WM_SYSCOMMAND, otherwise send WM_COMMAND */
+       if (hMenu && hMenu == Window->SystemMenu)
+       {
+          TRACE("Sending WM_SYSCOMMAND, wParam=%0x\n", pAccel->cmd);
+          co_IntSendMessage(hWnd, WM_SYSCOMMAND, pAccel->cmd, 0x00010000L);
+       }
+       else
+       {
+          TRACE("Sending WM_COMMAND, wParam=%0x\n", 0x10000 | pAccel->cmd);
+          co_IntSendMessage(hWnd, WM_COMMAND, 0x10000 | pAccel->cmd, 0L);
+       }
+   }
 
+   TRACE("IntTranslateAccelerator returns TRUE\n");
    return TRUE;
 }
 
@@ -294,44 +186,51 @@ NtUserCopyAcceleratorTable(
 {
    PACCELERATOR_TABLE Accel;
    int Ret;
-   BOOL Done = FALSE;
    DECLARE_RETURN(int);
 
-   DPRINT("Enter NtUserCopyAcceleratorTable\n");
+   TRACE("Enter NtUserCopyAcceleratorTable\n");
    UserEnterShared();
 
    Accel = UserGetAccelObject(hAccel);
+   if (!Accel)
+   {
+       RETURN(0);
+   }
 
-   if ((Entries && (EntriesCount < 1)) || ((hAccel == NULL) || (!Accel)))
+   /* If Entries is NULL return table size */
+   if (!Entries)
    {
-      RETURN(0);
+      RETURN(Accel->Count);
    }
 
+   /* Don't overrun */
    if (Accel->Count < EntriesCount)
        EntriesCount = Accel->Count;
 
    Ret = 0;
 
-   while (!Done)
+   _SEH2_TRY
    {
-       if (Entries)
+       ProbeForWrite(Entries, EntriesCount*sizeof(Entries[0]), 4);
+
+       for (Ret = 0; Ret < EntriesCount; Ret++)
        {
-           Entries[Ret].fVirt = Accel->Table[Ret].fVirt & 0x7f;
+           Entries[Ret].fVirt = Accel->Table[Ret].fVirt;
            Entries[Ret].key = Accel->Table[Ret].key;
            Entries[Ret].cmd = Accel->Table[Ret].cmd;
-
-           if(Ret + 1 == EntriesCount) Done = TRUE;
        }
-
-       if((Accel->Table[Ret].fVirt & 0x80) != 0) Done = TRUE;
-
-       Ret++;
    }
+   _SEH2_EXCEPT(EXCEPTION_EXECUTE_HANDLER)
+   {
+      SetLastNtError(_SEH2_GetExceptionCode());
+      Ret = 0;
+   }
+   _SEH2_END;
 
    RETURN(Ret);
 
 CLEANUP:
-   DPRINT("Leave NtUserCopyAcceleratorTable, ret=%i\n",_ret_);
+   TRACE("Leave NtUserCopyAcceleratorTable, ret=%i\n", _ret_);
    UserLeave();
    END_CLEANUP;
 }
@@ -345,13 +244,14 @@ NtUserCreateAcceleratorTable(
    PACCELERATOR_TABLE Accel;
    HACCEL hAccel;
    INT Index;
+   NTSTATUS Status = STATUS_SUCCESS;
    DECLARE_RETURN(HACCEL);
 
-   DPRINT("Enter NtUserCreateAcceleratorTable(Entries %p, EntriesCount %d)\n",
+   TRACE("Enter NtUserCreateAcceleratorTable(Entries %p, EntriesCount %d)\n",
           Entries, EntriesCount);
    UserEnterExclusive();
 
-   if (!Entries || EntriesCount < 1)
+   if (!Entries || EntriesCount <= 0)
    {
       SetLastNtError(STATUS_INVALID_PARAMETER);
       RETURN( (HACCEL) NULL );
@@ -366,38 +266,51 @@ NtUserCreateAcceleratorTable(
    }
 
    Accel->Count = EntriesCount;
-   if (Accel->Count > 0)
+   Accel->Table = ExAllocatePoolWithTag(PagedPool, EntriesCount * sizeof(ACCEL), USERTAG_ACCEL);
+   if (Accel->Table == NULL)
    {
-      Accel->Table = ExAllocatePoolWithTag(PagedPool, EntriesCount * sizeof(ACCEL), TAG_ACCEL);
-      if (Accel->Table == NULL)
-      {
-         UserDereferenceObject(Accel);
-         UserDeleteObject(hAccel, otAccel);
-         SetLastNtError(STATUS_NO_MEMORY);
-         RETURN( (HACCEL) NULL);
-      }
+       UserDereferenceObject(Accel);
+       UserDeleteObject(hAccel, otAccel);
+       SetLastNtError(STATUS_NO_MEMORY);
+       RETURN( (HACCEL) NULL);
+   }
 
-      for (Index = 0; Index < EntriesCount; Index++)
-      {
-          Accel->Table[Index].fVirt = Entries[Index].fVirt&0x7f;
-          if(Accel->Table[Index].fVirt & FVIRTKEY)
-          {
-              Accel->Table[Index].key = Entries[Index].key;
-          }
-          else
-          {
-             RtlMultiByteToUnicodeN(&Accel->Table[Index].key, 
-                                    sizeof(WCHAR), 
-                                    NULL, 
-                                    (PCSTR)&Entries[Index].key, 
-                                    sizeof(CHAR));
-          }
-
-          Accel->Table[Index].cmd = Entries[Index].cmd;
-      }
+   _SEH2_TRY
+   {
+       ProbeForRead(Entries, EntriesCount * sizeof(ACCEL), 4);
 
-      /* Set the end-of-table terminator. */
-      Accel->Table[EntriesCount - 1].fVirt |= 0x80;
+       for (Index = 0; Index < EntriesCount; Index++)
+       {
+           Accel->Table[Index].fVirt = Entries[Index].fVirt & FVIRT_MASK;
+           if(Accel->Table[Index].fVirt & FVIRTKEY)
+           {
+               Accel->Table[Index].key = Entries[Index].key;
+           }
+           else
+           {
+               RtlMultiByteToUnicodeN(&Accel->Table[Index].key,
+                                      sizeof(WCHAR),
+                                      NULL,
+                                      (PCSTR)&Entries[Index].key,
+                                      sizeof(CHAR));
+           }
+
+           Accel->Table[Index].cmd = Entries[Index].cmd;
+       }
+   }
+   _SEH2_EXCEPT(EXCEPTION_EXECUTE_HANDLER)
+   {
+       Status = _SEH2_GetExceptionCode();
+   }
+   _SEH2_END;
+
+   if (!NT_SUCCESS(Status))
+   {
+       ExFreePoolWithTag(Accel->Table, USERTAG_ACCEL);
+       UserDereferenceObject(Accel);
+       UserDeleteObject(hAccel, otAccel);
+       SetLastNtError(Status);
+       RETURN( (HACCEL) NULL);
    }
 
    /* FIXME: Save HandleTable in a list somewhere so we can clean it up again */
@@ -405,14 +318,12 @@ NtUserCreateAcceleratorTable(
    RETURN(hAccel);
 
 CLEANUP:
-   DPRINT("Leave NtUserCreateAcceleratorTable(Entries %p, EntriesCount %d) = %x\n",
-          Entries, EntriesCount,_ret_);
+   TRACE("Leave NtUserCreateAcceleratorTable(Entries %p, EntriesCount %d) = %x\n",
+          Entries, EntriesCount, _ret_);
    UserLeave();
    END_CLEANUP;
 }
 
-
-
 BOOLEAN
 APIENTRY
 NtUserDestroyAcceleratorTable(
@@ -426,7 +337,7 @@ NtUserDestroyAcceleratorTable(
    FIXME: Destroy only tables created using CreateAcceleratorTable.
     */
 
-   DPRINT("NtUserDestroyAcceleratorTable(Table %x)\n", hAccel);
+   TRACE("NtUserDestroyAcceleratorTable(Table %x)\n", hAccel);
    UserEnterExclusive();
 
    if (!(Accel = UserGetAccelObject(hAccel)))
@@ -436,7 +347,7 @@ NtUserDestroyAcceleratorTable(
 
    if (Accel->Table != NULL)
    {
-      ExFreePoolWithTag(Accel->Table, TAG_ACCEL);
+      ExFreePoolWithTag(Accel->Table, USERTAG_ACCEL);
       Accel->Table = NULL;
    }
 
@@ -445,74 +356,82 @@ NtUserDestroyAcceleratorTable(
    RETURN( TRUE);
 
 CLEANUP:
-   DPRINT("Leave NtUserDestroyAcceleratorTable(Table %x) = %i\n", hAccel,_ret_);
+   TRACE("Leave NtUserDestroyAcceleratorTable(Table %x) = %i\n", hAccel,_ret_);
    UserLeave();
    END_CLEANUP;
 }
 
-
 int
 APIENTRY
 NtUserTranslateAccelerator(
    HWND hWnd,
    HACCEL hAccel,
-   LPMSG Message)
+   LPMSG pUnsafeMessage)
 {
    PWND Window = NULL;
    PACCELERATOR_TABLE Accel = NULL;
    ULONG i;
+   MSG Message;
    USER_REFERENCE_ENTRY AccelRef, WindowRef;
    DECLARE_RETURN(int);
 
-   DPRINT("NtUserTranslateAccelerator(hWnd %x, Table %x, Message %p)\n",
-          hWnd, hAccel, Message);
+   TRACE("NtUserTranslateAccelerator(hWnd %x, Table %x, Message %p)\n",
+          hWnd, hAccel, pUnsafeMessage);
    UserEnterShared();
 
-   if (Message == NULL)
+   if (hWnd == NULL)
    {
-      SetLastNtError(STATUS_INVALID_PARAMETER);
       RETURN( 0);
    }
 
-   if ((Message->message != WM_KEYDOWN) &&
-         (Message->message != WM_SYSKEYDOWN) &&
-         (Message->message != WM_SYSCHAR) &&
-         (Message->message != WM_CHAR))
+   _SEH2_TRY
+   {
+       ProbeForRead(pUnsafeMessage, sizeof(MSG), 4);
+       RtlCopyMemory(&Message, pUnsafeMessage, sizeof(MSG));
+   }
+   _SEH2_EXCEPT(EXCEPTION_EXECUTE_HANDLER)
+   {
+      SetLastNtError(_SEH2_GetExceptionCode());
+      _SEH2_YIELD(RETURN( 0));
+   }
+   _SEH2_END;
+
+   if ((Message.message != WM_KEYDOWN) &&
+       (Message.message != WM_SYSKEYDOWN) &&
+       (Message.message != WM_SYSCHAR) &&
+       (Message.message != WM_CHAR))
    {
       RETURN( 0);
    }
 
-   if (!(Accel = UserGetAccelObject(hAccel)))
+   Accel = UserGetAccelObject(hAccel);
+   if (!Accel)
    {
       RETURN( 0);
    }
 
    UserRefObjectCo(Accel, &AccelRef);
 
-   if (!(Window = UserGetWindowObject(hWnd)))
+   Window = UserGetWindowObject(hWnd);
+   if (!Window)
    {
       RETURN( 0);
    }
 
    UserRefObjectCo(Window, &WindowRef);
 
-
    /* FIXME: Associate AcceleratorTable with the current thread */
 
    for (i = 0; i < Accel->Count; i++)
    {
-      if (co_IntTranslateAccelerator(Window, Message->message, Message->wParam, Message->lParam,
-                                     Accel->Table[i].fVirt, Accel->Table[i].key,
-                                     Accel->Table[i].cmd))
+      if (co_IntTranslateAccelerator(Window, &Message, &Accel->Table[i]))
       {
-         DPRINT("NtUserTranslateAccelerator(hWnd %x, Table %x, Message %p) = %i end\n",
-                hWnd, hAccel, Message, 1);
-         RETURN( 1);
+          RETURN( 1);
       }
-      if (((Accel->Table[i].fVirt & 0x80) > 0))
-      {
+
+      /* Undocumented feature... */
+      if (Accel->Table[i].fVirt & FVIRT_TBL_END)
          break;
-      }
    }
 
    RETURN( 0);
@@ -521,8 +440,7 @@ CLEANUP:
    if (Window) UserDerefObjectCo(Window);
    if (Accel) UserDerefObjectCo(Accel);
 
-   DPRINT("NtUserTranslateAccelerator(hWnd %x, Table %x, Message %p) = %i end\n",
-          hWnd, hAccel, Message, 0);
+   TRACE("NtUserTranslateAccelerator returns %d\n", _ret_);
    UserLeave();
    END_CLEANUP;
 }