简体   繁体   English

钩键盘重复输入(C#/ XNA)

[英]Hooked keyboard duplicating input (c# / xna)

I'm using a Keyboard hook in my XNA project (anyone who uses XNA knows how useless the built in keyboard class is for "text box" style typed input. 我在XNA项目中使用了Keyboard钩子(使用XNA的任何人都知道内置键盘类对于“文本框”样式键入的输入是多么无用。

It works just fine for use in XNA projects, until forms are introduced. 在引入表格之前,它可以很好地用于XNA项目。 If I try to implement any form in the project, be it a custom form, or even just an OpenFileDialog, any key press inside a text box of the form is doubled, making typing just about impossible. 如果我尝试在项目中实现任何窗体,无论是自定义窗体,甚至只是OpenFileDialog,窗体文本框内的任何按键都会加倍,从而几乎无法键入。

Does anyone know how I can stop the message from reaching the forms twice? 有谁知道我如何才能阻止邮件两次到达表单? perhaps by discarding the message when I get it? 也许是在我收到消息后就将其丢弃了? Perhaps there is a better solution? 也许有更好的解决方案? or perhaps its just something that cant be done. 也许这只是无法完成的事情。

Any help is appreciated. 任何帮助表示赞赏。

EDIT: 编辑:

Below is the keyboard hooking code I'm using, It may be familiar to anyone who's gone looking for XNA keyboard hooking as it seems to crop up wherever I looked for it. 下面是我正在使用的键盘挂钩代码,那些想寻找XNA键盘挂钩的人可能会很熟悉,因为无论我在哪里寻找它,它似乎都会冒出来。

using System;
using System.Runtime.InteropServices;
using System.Windows.Forms; // This class exposes WinForms-style key events.

namespace FatLib.Controls
{
    class KeyboardHookInput : IDisposable
    {
        private string _buffer = "";
        private bool _backSpace = false;
        private bool _enterKey = false; // I added this to the original code

        public string Buffer
        {
            get { return _buffer; }
        }
        public bool BackSpace
        {
            get
            {
                return _backSpace;
            }
        }
        public bool EnterKey
        {
            get
            {
                return _enterKey;
            }
        }
        public void Reset()
        {
            _buffer = "";
            _backSpace = false;
            _enterKey = false;
        }

        public enum HookId
        {
            // Types of hook that can be installed using the SetWindwsHookEx function.
            WH_CALLWNDPROC = 4,
            WH_CALLWNDPROCRET = 12,
            WH_CBT = 5,
            WH_DEBUG = 9,
            WH_FOREGROUNDIDLE = 11,
            WH_GETMESSAGE = 3,
            WH_HARDWARE = 8,
            WH_JOURNALPLAYBACK = 1,
            WH_JOURNALRECORD = 0,
            WH_KEYBOARD = 2,
            WH_KEYBOARD_LL = 13,
            WH_MAX = 11,
            WH_MAXHOOK = WH_MAX,
            WH_MIN = -1,
            WH_MINHOOK = WH_MIN,
            WH_MOUSE_LL = 14,
            WH_MSGFILTER = -1,
            WH_SHELL = 10,
            WH_SYSMSGFILTER = 6,
        };
        public enum WindowMessage
        {
            // Window message types.
            WM_KEYDOWN = 0x100,
            WM_KEYUP = 0x101,
            WM_CHAR = 0x102,
        };

        // A delegate used to create a hook callback.
        public delegate int GetMsgProc(int nCode, int wParam, ref Message msg);

        /// <summary>
        /// Install an application-defined hook procedure into a hook chain.
        /// </summary>
        /// <param name="idHook">Specifies the type of hook procedure to be installed.</param>
        /// <param name="lpfn">Pointer to the hook procedure.</param>
        /// <param name="hmod">Handle to the DLL containing the hook procedure pointed to by the lpfn parameter.</param>
        /// <param name="dwThreadId">Specifies the identifier of the thread with which the hook procedure is to be associated.</param>
        /// <returns>If the function succeeds, the return value is the handle to the hook procedure. Otherwise returns 0.</returns>
        [DllImport("user32.dll", EntryPoint = "SetWindowsHookExA")]
        public static extern IntPtr SetWindowsHookEx(HookId idHook, GetMsgProc lpfn, IntPtr hmod, int dwThreadId);

        /// <summary>
        /// Removes a hook procedure installed in a hook chain by the SetWindowsHookEx function. 
        /// </summary>
        /// <param name="hHook">Handle to the hook to be removed. This parameter is a hook handle obtained by a previous call to SetWindowsHookEx.</param>
        /// <returns>If the function fails, the return value is zero. To get extended error information, call GetLastError.</returns>
        [DllImport("user32.dll")]
        public static extern int UnhookWindowsHookEx(IntPtr hHook);

        /// <summary>
        /// Passes the hook information to the next hook procedure in the current hook chain.
        /// </summary>
        /// <param name="hHook">Ignored.</param>
        /// <param name="ncode">Specifies the hook code passed to the current hook procedure.</param>
        /// <param name="wParam">Specifies the wParam value passed to the current hook procedure.</param>
        /// <param name="lParam">Specifies the lParam value passed to the current hook procedure.</param>
        /// <returns>This value is returned by the next hook procedure in the chain.</returns>
        [DllImport("user32.dll")]
        public static extern int CallNextHookEx(int hHook, int ncode, int wParam, ref Message lParam);

        /// <summary>
        /// Translates virtual-key messages into character messages.
        /// </summary>
        /// <param name="lpMsg">Pointer to an Message structure that contains message information retrieved from the calling thread's message queue.</param>
        /// <returns>If the message is translated (that is, a character message is posted to the thread's message queue), the return value is true.</returns>
        [DllImport("user32.dll")]
        public static extern bool TranslateMessage(ref Message lpMsg);


        /// <summary>
        /// Retrieves the thread identifier of the calling thread.
        /// </summary>
        /// <returns>The thread identifier of the calling thread.</returns>
        [DllImport("kernel32.dll")]
        public static extern int GetCurrentThreadId();

        // Handle for the created hook.
        private readonly IntPtr HookHandle;

        private readonly GetMsgProc ProcessMessagesCallback;

        public KeyboardHookInput()
        {
            // Create the delegate callback:
            this.ProcessMessagesCallback = new GetMsgProc(ProcessMessages);
            // Create the keyboard hook:
            this.HookHandle = SetWindowsHookEx(HookId.WH_KEYBOARD, this.ProcessMessagesCallback, IntPtr.Zero, GetCurrentThreadId());
        }

        public void Dispose()
        {
            // Remove the hook.
            if (HookHandle != IntPtr.Zero) UnhookWindowsHookEx(HookHandle);
        }

        // comments found in this region are all from the original author: Darg.
        private int ProcessMessages(int nCode, int wParam, ref Message msg)
        {
            // Check if we must process this message (and whether it has been retrieved via GetMessage):
            if (nCode == 0 && wParam == 1)
            {
                // We need character input, so use TranslateMessage to generate WM_CHAR messages.
                TranslateMessage(ref msg);

                // If it's one of the keyboard-related messages, raise an event for it:
                switch ((WindowMessage)msg.Msg)
                {
                    case WindowMessage.WM_CHAR:
                        this.OnKeyPress(new KeyPressEventArgs((char)msg.WParam));
                        break;
                    case WindowMessage.WM_KEYDOWN:
                        this.OnKeyDown(new KeyEventArgs((Keys)msg.WParam));
                        break;
                    case WindowMessage.WM_KEYUP:
                        this.OnKeyUp(new KeyEventArgs((Keys)msg.WParam));
                        break;
                }
            }
            // Call next hook in chain:
            return CallNextHookEx(0, nCode, wParam, ref msg);
        }

        public event KeyEventHandler KeyUp;
        protected virtual void OnKeyUp(KeyEventArgs e)
        {
            if (KeyUp != null) KeyUp(this, e);
        }

        public event KeyEventHandler KeyDown;
        protected virtual void OnKeyDown(KeyEventArgs e)
        {
            if (KeyDown != null) KeyDown(this, e);
        }

        public event KeyPressEventHandler KeyPress;
        protected virtual void OnKeyPress(KeyPressEventArgs e)
        {
            if (KeyPress != null) KeyPress(this, e);
            if (e.KeyChar.GetHashCode().ToString() == "524296")
            {
                _backSpace = true;
            }
            else if (e.KeyChar == (char)Keys.Enter)
            {
                _enterKey = true;
            }
            else
            {
                _buffer += e.KeyChar;
            }
        }
    }
}

Since there has to be a form in XNA (the GraphicsDevice requires it) you're going to have 2 options depending on whether the user of your library creates the form ( project with XNA for rendering), or whether the form is created as a result of creating a normal XNA game. 由于XNA中必须有一个表单( GraphicsDevice需要它),因此您将有2个选项,具体取决于您的库用户是创建表单(带有XNA的项目进行渲染),还是将表单创建为创建普通XNA游戏的结果。

In the case, you can just handle the key events, job done. 情况下,您只可以处理关键事件,完成工作。

In the standard XNA game case, you can get the user to pass the handle of the window that the graphics device created, then before calling your OnKey... methods, check that the window has focus. 在标准XNA游戏情况下,您可以让用户传递图形设备创建的窗口的句柄,然后在调用OnKey...方法之前,检查窗口是否具有焦点。

You could go with the 2nd option in both cases to make it simpler. 在两种情况下,您都可以使用2nd选项,以使其更简单。 The user of your library would pass in the handle of the window to receive/buffer key presses in the constructor of KeyboardHookInput . 库的用户将传递窗口的句柄以在KeyboardHookInput构造函数中接收/缓冲按键。 Then you'd check that the window has focus first (see here: How can I tell if a Window has focus? (Win32 API) ). 然后,您将首先检查窗口是否具有焦点(请参见此处: 如何确定窗口是否具有焦点?(Win32 API) )。

A Windows Hook is a pretty nasty way of getting at all the keypresses, and should be an absolute last resort. Windows Hook是获取所有按键的一种非常讨厌的方式,并且绝对是不得已的手段。

Try installing a Message Filter into your application, which can watch for all the keyboard messages that are sent to your application (WM_KEYPRESS, KEYUP, KEYDOWN, etc) without interfering with other applications. 尝试在您的应用程序中安装消息过滤器 ,它可以监视发送到您的应用程序的所有键盘消息(WM_KEYPRESS,KEYUP,KEYDOWN等),而不会干扰其他应用程序。 A filter will also allow you to stop any messages reaching any forms in your application if you wish. 如果需要,过滤器还可以使您停止到达应用程序中任何形式的任何消息。

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM