Ответ 1
Возможно, я не понимаю этот вопрос, но почему вы не можете использовать классы XNA Keyboard и KeyboardState?
Хорошо, поэтому в основном я хочу получить текст клавиатуры. Как ввод текста в текстовое поле или что-то в этом роде. Я только пишу свою игру для окон. Я проигнорировал использование Guide.BeginShowKeyboardInput, потому что он нарушает ощущение автономной игры, и тот факт, что Guide всегда показывает кнопки XBOX, тоже мне не подходит. Да это самый простой способ, но мне это не нравится.
Далее я попытался использовать System.Windows.Forms.NativeWindow. Я создал класс, унаследованный от него, и передал ему дескриптор окна Games, реализовал функцию WndProc, чтобы поймать WM_CHAR (или WM_KEYDOWN), хотя WndProc получил приглашение для других сообщений, WM_CHAR и WM_KEYDOWN так и не сделали. Поэтому мне пришлось отказаться от этой идеи, и кроме того, я также ссылался на все формы Windows, что означало излишнюю разметку памяти.
Итак, моя последняя идея заключалась в том, чтобы создать нить уровня, низкоуровневую клавиатуру. Это было самым успешным до сих пор. Я получаю сообщение WM_KEYDOWN (еще не проверенный WM_CHAR) переводит виртуальный ключевой код с помощью Win32 funcation MapVirtualKey на char. И я получаю текст! (Я просто печатаю с Debug.Write на данный момент)
Пара проблем. Это как будто я закрыл колпачок и не реагировал на клавиши переключения. (Конечно, однако, это не так, просто есть только один виртуальный Key Code для каждого ключа, поэтому при переводе он имеет только один вывод), и он добавляет накладные расходы, поскольку он присоединяется к списку подключений Windows и не так быстро, как я Как бы это ни было, но медленность может быть больше из-за Debug.Write.
Кто-нибудь еще подошел к этому и решил его, не прибегая к экранной клавиатуре? или у кого-нибудь есть другие идеи для меня, чтобы попробовать?
заблаговременно.
Вопрос, заданный Джимми
Возможно, я не понимаю этот вопрос, но почему вы не можете использовать классы XNA Keyboard и KeyboardState?
Мой комментарий:
Это потому, что, хотя вы можете читать keystates, вы не можете получить доступ к напечатанному тексту как и как он набирается пользователем.
Итак, позвольте мне уточнить. Я хочу реализовать возможность чтения текстового ввода от пользователя, как если бы они вводили текст в текстовое поле, это окна. Клавиатура и класс KeyboardState получают состояния всех ключей, но мне нужно будет сопоставить каждый ключ и комбинацию с символьным представлением. Это происходит, когда пользователь не использует тот же язык клавиатуры, что и я, особенно с символами (мои двойные кавычки имеют сдвиг + 2, а у американских клавиатур есть где-то рядом с ключом возврата).
Кажется, мой оконный крючок - это путь, просто причина, по которой я не получал WM_CHAR, потому что насос сообщений XNA не выполняет перевод сообщения.
Добавление TranslateMessage в каждый раз, когда я получил сообщение WM _
KEYDOWN, означало, что я получил свое сообщение WM_CHAR, затем использовал это, чтобы запустить событие с символом в моем классе MessageHook, к которому подписал класс KeyboardBuffer, который затем буферизует это текстовый буфер: D (или StringBuilder, но результат тот же)
Итак, у меня все работает так, как я хочу.
Большое спасибо Джимми за ссылку на очень информативный поток.
Возможно, я не понимаю этот вопрос, но почему вы не можете использовать классы XNA Keyboard и KeyboardState?
Для добавления окна в XNA
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
}
}
Я использовал решение из этого gamedev.net post, и он отлично работает:)
Здесь простой способ, 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);
}
Надеюсь, это поможет. Я лично думал, что это проще в использовании, чем крючки, и это также может быть легко изменено для особых результатов (например, перетасовка клавиш).
Эта страница находится поверх результата google о перехвате WM_CHAR в xna, поэтому я оставляю здесь заметку. Может быть, это будет полезно для других (если они смогут понять мой английский =))).
Я пытаюсь выполнить код с windowshook от Sekhat, но, похоже, должно быть 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 что-то о PM_NOREMOVE/PM_REMOVE в WPARAM)
Когда я добавляю что-то вроде этого
if (nCode >= 0 && wParam == 1)
{
Win32.TranslateMessage(ref lParam);
WndProc(ref lParam);
}
wm_keypress переустановка wm_char остановлена (я полагаю, что 1 является PM_NOREMOVE или PM_REMOVE).
P.S. нуклексный вариант теперь показывает 404 страницу, но может быть просмотрен с помощью webarchive. нуклексный вариант, но он приводит к нарушению обработки mouseWheel из собственного XNA MouseState (на XNA 3.1) = (