/// <summary>
        /// Convert wParam and lParam to a key.
        /// </summary>
        private Key TranslateKey(ulong wParam, ulong lParam)
        {
            switch (wParam)
            {
            // Control keys require special handling.
            case (uint)VirtualKey.CONTROL when(lParam& 0x01000000) != 0:
                return(Key.RightControl);

            case (uint)VirtualKey.CONTROL:
            {
                uint time = User32.GetMessageTime();

                // HACK: Alt Gr sends Left Ctrl and then Right Alt in close sequence
                //       We only want the Right Alt message, so if the next message is
                //       Right Alt we ignore this (synthetic) Left Ctrl message
                if (!User32.PeekMessage(out Message next, IntPtr.Zero, 0, 0, PeekMessageFlags.PM_NOREMOVE))
                {
                    return(Key.LeftControl);
                }
                if (next.Value != WM.KEYDOWN && next.Value != WM.SYSKEYDOWN && next.Value != WM.KEYUP && next.Value != WM.SYSKEYUP)
                {
                    return(Key.LeftControl);
                }
                if ((uint)next.WParam == (uint)VirtualKey.MENU && ((uint)next.LParam & 0x01000000) != 0 && next.Time == time)
                {
                    // Next message is Right Alt down so discard this
                    return(Key.Unknown);
                }

                return(Key.LeftControl);
            }

            // IME notifies that keys have been filtered by setting the virtual
            // key-code to VK_PROCESSKEY
            case (uint)VirtualKey.PROCESSKEY:
                return(Key.Unknown);
            }

            int index = NativeHelpers.HiWord(lParam) & 0x1FF;

            if (index < 0 || index >= _keyCodes.Length)
            {
                return(Key.Unknown);
            }
            return(_keyCodes[index]);
        }