好的,基本上我希望能够检索键盘文本。就像将文本输入文本字段或其他内容一样。我只是为Windows编写游戏。 我忽略了使用Guide.BeginShowKeyboardInput,因为它打破了自包含游戏的感觉,而且指南总是显示XBOX按钮的事实对我来说也不合适。是的,这是最简单的方法,但我不喜欢它。
接下来我尝试使用System.Windows.Forms.NativeWindow。我创建了一个继承自它的类,并将其传递给了Games窗口句柄,实现了WndProc函数来捕获WM_CHAR(或WM_KEYDOWN),尽管WndProc被调用了其他消息,WM_CHAR和WM_KEYDOWN从未这样做过。所以我不得不放弃这个想法,此外,我还引用了整个Windows表单,这意味着不必要的内存占用空间。
所以我的最后一个想法是创建一个Thread级别的低级键盘钩子。到目前为止,这是最成功的。我收到WM_KEYDOWN消息,(还没试过WM_CHAR)将虚拟键码与Win32函数MapVirtualKey一起转换为char。我收到了我的文字! (我现在只用Debug.Write打印)
虽然有几个问题。好像我有大写锁定,并且没有响应的移位键。 (当然不是,它只是每个键只有一个虚拟键代码,所以只翻译它有一个输出)并且它增加了开销,因为它将自己附加到Windows Hook列表并且没有我那么快d喜欢它,但缓慢可能更多是由于Debug.Write。
有没有其他人接近这个并解决了它,而不必求助于屏幕键盘?还是有人有进一步的想法让我尝试?
提前感谢。
Jimmy提出的问题
也许我不理解这个问题,但为什么你不能使用XNA Keyboard和KeyboardState类呢?
我的评论:
这是因为虽然您可以阅读关键状态,但您无法访问键入的文本以及用户输入的方式。
所以让我进一步澄清一下。我想实现能够从用户读取文本输入,就好像他们在文本框中键入是Windows。键盘和KeyboardState类获取所有键的状态,但我必须将每个键和组合映射到它的字符表示。当用户不使用与我特别用符号相同的键盘语言(我的双引号是shift + 2,而美国键盘在返回键附近的某处)时,这就会失败。
每当我收到WM _
KEYDOWN消息时添加TranslateMessage意味着我收到了WM_CHAR消息,然后我用它在我的MessageHuff类中发出了一个字符类型的事件,我的KeyboardBuffer类已订阅了该事件,然后缓冲到文本缓冲区:D(或StringBuilder,但结果是相同的)
所以我按照自己的意愿行事。
非常感谢Jimmy提供了一个链接到非常有用的信息。
答案 0 :(得分:9)
在XNA中添加Windows挂钩
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Runtime.InteropServices;
using System.Diagnostics;
using System.Reflection;
/* Author: Sekhat
*
* License: Public Domain.
*
* Usage:
*
* Inherit from this class, and override the WndProc function in your derived class,
* in which you handle your windows messages.
*
* To start recieving the message, create an instance of your derived class, passing in the
* window handle of the window you want to listen for messages for.
*
* in XNA: this would be the Game.Window.Handle property
* in Winforms Form.Handle property
*/
namespace WindowsHookExample
{
public abstract class WindowsHook : IDisposable
{
IntPtr hHook;
IntPtr hWnd;
// Stored here to stop it from getting garbage collected
Win32.WndProcDelegate wndProcDelegate;
public WindowsHook(IntPtr hWnd)
{
this.hWnd = hWnd;
wndProcDelegate = WndProcHook;
CreateHook();
}
~WindowsHook()
{
Dispose(false);
}
private void CreateHook()
{
uint threadId = Win32.GetWindowThreadProcessId(hWnd, IntPtr.Zero);
hHook = Win32.SetWindowsHookEx(Win32.HookType.WH_CALLWNDPROC, wndProcDelegate, IntPtr.Zero, threadId);
}
private int WndProcHook(int nCode, IntPtr wParam, ref Win32.Message lParam)
{
if (nCode >= 0)
{
Win32.TranslateMessage(ref lParam); // You may want to remove this line, if you find your not quite getting the right messages through. This is here so that WM_CHAR is correctly called when a key is pressed.
WndProc(ref lParam);
}
return Win32.CallNextHookEx(hHook, nCode, wParam, ref lParam);
}
protected abstract void WndProc(ref Win32.Message message);
#region Interop Stuff
// I say thankya to P/Invoke.net.
// Contains all the Win32 functions I need to deal with
protected static class Win32
{
public enum HookType : int
{
WH_JOURNALRECORD = 0,
WH_JOURNALPLAYBACK = 1,
WH_KEYBOARD = 2,
WH_GETMESSAGE = 3,
WH_CALLWNDPROC = 4,
WH_CBT = 5,
WH_SYSMSGFILTER = 6,
WH_MOUSE = 7,
WH_HARDWARE = 8,
WH_DEBUG = 9,
WH_SHELL = 10,
WH_FOREGROUNDIDLE = 11,
WH_CALLWNDPROCRET = 12,
WH_KEYBOARD_LL = 13,
WH_MOUSE_LL = 14
}
public struct Message
{
public IntPtr lparam;
public IntPtr wparam;
public uint msg;
public IntPtr hWnd;
}
/// <summary>
/// Defines the windows proc delegate to pass into the windows hook
/// </summary>
public delegate int WndProcDelegate(int nCode, IntPtr wParam, ref Message m);
[DllImport("user32.dll", SetLastError = true, CharSet = CharSet.Auto)]
public static extern IntPtr SetWindowsHookEx(HookType hook, WndProcDelegate callback,
IntPtr hMod, uint dwThreadId);
[DllImport("user32.dll", SetLastError = true, CharSet = CharSet.Auto)]
public static extern bool UnhookWindowsHookEx(IntPtr hhk);
[DllImport("user32.dll", SetLastError = true, CharSet = CharSet.Auto)]
public static extern int CallNextHookEx(IntPtr hhk, int nCode, IntPtr wParam, ref Message m);
[DllImport("coredll.dll", SetLastError = true)]
public static extern IntPtr GetModuleHandle(string module);
[DllImport("user32.dll", EntryPoint = "TranslateMessage")]
public extern static bool TranslateMessage(ref Message m);
[DllImport("user32.dll")]
public extern static uint GetWindowThreadProcessId(IntPtr window, IntPtr module);
}
#endregion
#region IDisposable Members
public void Dispose()
{
Dispose(true);
}
private void Dispose(bool disposing)
{
if (disposing)
{
// Free managed resources here
}
// Free unmanaged resources here
if (hHook != IntPtr.Zero)
{
Win32.UnhookWindowsHookEx(hHook);
}
}
#endregion
}
}
答案 1 :(得分:1)
也许我不理解这个问题,但为什么你不能使用XNA Keyboard和KeyboardState类呢?
答案 2 :(得分:1)
我使用了此gamedev.net post的解决方案,效果很好:)
答案 3 :(得分:1)
这是一种简单的方法,即IMO,拥有space
,back
,A-Z
,然后是特殊字符!,@,#,$,%,^,&,*,(,)
。 (注意,您需要导入System.Linq
)以下是字段:
Keys[] keys;
bool[] IskeyUp;
string[] SC = { ")" , "!", "@", "#", "$", "%", "^", "&", "*", "("};//special characters
构造
keys = new Keys[38];
Keys[] tempkeys;
tempkeys = Enum.GetValues(typeof(Keys)).Cast<Keys>().ToArray<Keys>();
int j = 0;
for (int i = 0; i < tempkeys.Length; i++)
{
if (i == 1 || i == 11 || (i > 26 && i < 63))//get the keys listed above as well as A-Z
{
keys[j] = tempkeys[i];//fill our key array
j++;
}
}
IskeyUp = new bool[keys.Length]; //boolean for each key to make the user have to release the key before adding to the string
for (int i = 0; i < keys.Length; i++)
IskeyUp[i] = true;
最后,更新方法:
string result = "";
public override void Update(GameTime gameTime)
{
KeyboardState state = Keyboard.GetState();
int i = 0;
foreach (Keys key in keys)
{
if (state.IsKeyDown(key))
{
if (IskeyUp[i])
{
if (key == Keys.Back && result != "") result = result.Remove(result.Length - 1);
if (key == Keys.Space) result += " ";
if (i > 1 && i < 12)
{
if (state.IsKeyDown(Keys.RightShift) || state.IsKeyDown(Keys.LeftShift))
result += SC[i - 2];//if shift is down, and a number is pressed, using the special key
else result += key.ToString()[1];
}
if (i > 11 && i < 38)
{
if (state.IsKeyDown(Keys.RightShift) || state.IsKeyDown(Keys.LeftShift))
result += key.ToString();
else result += key.ToString().ToLower(); //return the lowercase char is shift is up.
}
}
IskeyUp[i] = false; //make sure we know the key is pressed
}
else if (state.IsKeyUp(key)) IskeyUp[i] = true;
i++;
}
base.Update(gameTime, otherScreenHasFocus, coveredByOtherScreen);
}
希望这很好帮助。我个人认为它比钩子更容易使用,这也可以很容易地修改特殊结果(如洗牌)。
答案 4 :(得分:0)
此页面位于关于xna中WM_CHAR拦截的google结果之上,所以我在这里留下一些注意事项。也许这对其他人有用(如果他们能够理解我的英语=))。)。
我尝试使用Sekhat的windowshook代码,但似乎应该将WH_GETMESSAGE传递给SetWindowsHookEx而不是Win32.HookType.WH_CALLWNDPROC(仅使用WH_GETMESSAGE代码,lparaw将指向Win32.Message)。
还有一些重复的消息(使用wparam 0)。 (看这里 - http://msdn.microsoft.com/en-us/library/ms644981%28v=VS.85%29.aspx 关于WPARAM中PM_NOREMOVE / PM_REMOVE的内容
当我添加类似这样的内容时
if (nCode >= 0 && wParam == 1)
{
Win32.TranslateMessage(ref lParam);
WndProc(ref lParam);
}
wm_keypress wm_char重复停止(我认为1是PM_NOREMOVE或PM_REMOVE)。
P.S。 nuclex variant现在显示404页面,但可以使用webarchive查看。 nuclex变体工作,但它导致从原生XNA MouseState(在XNA 3.1上)处理中断的mouseWheel处理=(