- html - 出于某种原因,IE8 对我的 Sass 文件中继承的 html5 CSS 不友好?
- JMeter 在响应断言中使用 span 标签的问题
- html - 在 :hover and :active? 上具有不同效果的 CSS 动画
- html - 相对于居中的 html 内容固定的 CSS 重复背景?
Keyboard.GetKeyStates
似乎有一种方法可以返回错误按下的键,例如 Keyboard.GetKeyStates(Key.NumPad2)
可以返回 向下,切换
,即使没有按下也是如此。
我已经能够在捕获 keyUp 事件的非常简单的 WPF 应用程序上重现这一点。重现bug的方法如下:
从那时起,检查 NumPad2 的状态将始终产生 Down, Toggled
,直到您再次按下并释放它。
不确定这是否重要,但我在 Windows 8.1 x64
上使用英语英国扩展键盘原因似乎是 LShift-NumPad2 实际上等同于向下键(有道理),但 Windows 似乎没有意识到这意味着不再按下 NumPad2。
我的测试应用程序只是捕获 KeyDown 和 KeyUp,并向我显示每个事件的 KeyStates 变化以及每个事件后整个键盘的 KeyStates 列表(我将其与应用程序启动时的状态进行比较,以免用 NumLock 键和其他键的状态污染输出。
这是我在之前的测试中得到的输出:
MainWindow_OnKeyDown NumPad2: Down, Toggled -> Down, Toggled
KeyStates:
NumPad2: Down, Toggled
MainWindow_OnKeyDown LeftShift: None -> Down, Toggled
KeyStates:
NumPad2: Down, Toggled
LeftShift: Down, Toggled
MainWindow_OnKeyUp LeftShift: Down, Toggled -> Toggled
KeyStates:
NumPad2: Down, Toggled
LeftShift: Toggled
MainWindow_OnKeyUp Down: None -> None
KeyStates:
NumPad2: Down, Toggled
LeftShift: Toggled
MainWindow_OnKeyUp LeftShift: Toggled -> None
KeyStates:
NumPad2: Down, Toggled
所以你可以看到 NumPad2 键在第 3 步和第 4 步后显示为已按下,尽管我在第 3 步释放了它。
这是 xaml 的完整代码和隐藏代码,以防您想将其直接复制/粘贴到新项目中并想查看它的实际效果:
<Window x:Class="WpfKeyboardTester.MainWindow"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
Title="MainWindow" Height="350" Width="525"
Loaded="MainWindow_OnLoaded"
KeyDown="MainWindow_OnKeyDown"
KeyUp="MainWindow_OnKeyUp"
WindowStartupLocation="CenterScreen">
<Grid>
<Grid.RowDefinitions>
<RowDefinition />
<RowDefinition Height="Auto"/>
</Grid.RowDefinitions>
<ScrollViewer
Grid.Row="0"
Name="ScrollViewer"
x:FieldModifier="private">
<TextBox
Name="TextBox"
IsReadOnly="True"
x:FieldModifier="private"/>
</ScrollViewer>
<Button
Grid.Row="1"
Click="Clear_OnClick">
Clear
</Button>
</Grid>
</Window>
和
public partial class MainWindow
{
private Dictionary<Key, KeyStates> _initialKeyStates;
private Dictionary<Key, KeyStates> _keyStates;
private Key _previousKeyDown;
private Key _previousKeyUp;
public MainWindow()
{
InitializeComponent();
}
private void MainWindow_OnLoaded(object sender, RoutedEventArgs e)
{
_keyStates = GetKeyStates();
_initialKeyStates = _keyStates;
}
private void MainWindow_OnKeyDown(object sender, KeyEventArgs e)
{
if (e.Key != _previousKeyDown)
{
AppendKeyEventDescription("MainWindow_OnKeyDown", e);
_previousKeyDown = e.Key;
}
}
private void MainWindow_OnKeyUp(object sender, KeyEventArgs e)
{
if (e.Key != _previousKeyUp)
{
AppendKeyEventDescription("MainWindow_OnKeyUp", e);
_previousKeyUp = e.Key;
}
}
private void Clear_OnClick(object sender, RoutedEventArgs e)
{
TextBox.Text = string.Empty;
}
private static Dictionary<Key, KeyStates> GetKeyStates()
{
return Enum.GetValues(typeof (Key))
.Cast<Key>()
.Distinct()
.Where(x => x != Key.None)
.ToDictionary(
x => x,
Keyboard.GetKeyStates);
}
private void AppendKeyEventDescription(string eventName, KeyEventArgs e)
{
if (TextBox.Text != string.Empty)
TextBox.Text += "\n\n";
TextBox.Text +=
eventName + " " + e.Key + ": " + _keyStates[e.Key] + " -> " + e.KeyStates;
_keyStates = GetKeyStates();
var changedKeys = _keyStates.Keys
.Where(key => _keyStates[key] != _initialKeyStates[key])
.ToArray();
if (changedKeys.Any())
{
TextBox.Text += changedKeys.Aggregate(
"\nKeyStates:",
(text, key) => text + "\n\t" + key + ": " + _keyStates[key]);
}
}
}
我探索了其他几种调用 Win32 API 的方法:
而且它们都有完全相同的问题(这并不奇怪,因为我认为它们的内部工作原理与它们的 .net 等效 Keyboard.GetKeyStates 是共享的)。
现在我正在寻找一种方法,可以在任何时候真正知道是否真的按下了 NumPad2 键...
最佳答案
所以这个问题归结为 Windows 报告按键的方式中的错误。
Windows 在物理键盘上创建了一个抽象层,称为虚拟键盘。虚拟键盘监听物理键盘事件并使用它来维护键的状态,稍后可以通过 Windows API 调用(其中 User32.dll 调用 GetKeyState、GetAsyncKeyState 和 GetKeyboardState)或包装这些 API 调用的 .NET 调用来检索这些键的状态(System.Windows.Input.Keyboard 命名空间中的那些)。
在这种情况下,它会收到 NumPad2 的 KeyDown 事件,但会收到 Down 的 KeyUp 事件(这是 NumPad2 的 shifted 版本),因此 NumPad2 的状态在这些调用期间保持按下状态很关心。
基本上,我发现的解决方法都是不依赖这些调用。
以下是我发现的一些可行的解决方法:
<强>1。使用 Windows API 直接连接到物理键盘事件。
想法是使用 SetWindowsHookEx Win32 API 调用来使用物理键盘的 Hook 。
这种方法的主要问题是 API 只提供事件,不提供状态。您必须维护键盘上所有键的状态才能使其正常工作。
<强>2。使用 DirectX。
我找到的另一个解决方案是使用 DirectInput,它是 DirectX 的一部分。这增加了对 DirectX 的依赖(如果您的应用程序在 Windows Vista 之前不支持任何东西,这很好)。此外,除了第三方库之外,无法从托管代码直接访问 DirectX(以前有一个 Microsoft .NET 包装器围绕旧版本的 DirectX 作为 DirectX API 的一部分,但它已经过时并且不适用于最新版本.NET)。您可以:
关于c# - Keyboard.GetKeyStates 误报,我如何真正知道是否按下了某个键?,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/26159752/
我希望在按下某个键时切换 bool 事件。具体来说,'s' 键。我已被指向函数 GetKeyState(),它应该在 Win32 API 下工作。我知道字母's'的ASCII码是115,所以我的代码如
我需要为我的小应用程序监听键盘按键状态。 #include #include #include using namespace std; int main() { while(1)
在VCL(Delphi 2010)中我使用这个函数来检查控制键是否被按下: function IsControlKeyPressed: Boolean; begin Result := GetKe
使用 GetKeyState,我可以在按下某个键时执行一些任务。但是,如果我有 if (GetKeyState(VK_UP) & 0x80),它会在整个按住键的过程中返回 true。 如果我只想在按下
大家好,我正在使用 C 中的代码块。我阅读了 https://msdn.microsoft.com/en-us/library/windows/desktop/ms646301(v=vs.85).as
当我使用 GetKeyState 或 GetAsyncKeyState 按下某些键时,我遇到了问题,两者都记录了按下键的历史记录。所以当我有一个 cin>> 这是我按下的键。 if(GetKeySta
我编写了一个简单的程序来监听大写锁定键,并显示一个消息框,说明当前大写锁定是打开还是关闭。 所以:用户按下大写锁定,程序确定大写锁定现在处于什么状态(打开或关闭)并显示一个消息框。 实际发生的是,当大
有时我会收到客户的错误报告,但我无法解释。在 Delphi 中的 Application.Run() 之后,我收到以下错误: EOSError: System error: Code:_5 Acce
Keyboard.GetKeyStates 似乎有一种方法可以返回错误按下的键,例如 Keyboard.GetKeyStates(Key.NumPad2) 可以返回 向下,切换,即使没有按下也是如此。
从MSDN ,我了解到GetKeyState 与当前线程的消息队列相关联。 然后我创建了两个示例应用程序:KeyPresser 和 BackChecker。 我在 KeyPresser 中按下一个键,
我正在使用 C# 开发一个简单的通用 Windows 应用程序。我有一个 RichEditBox,并且在使用 Control+I 组合键时发现了奇怪的行为,由于某种原因,它会插入一个 Tab(这是预期
我正在使用 C# 开发一个简单的通用 Windows 应用程序。我有一个 RichEditBox,并且在使用 Control+I 组合键时发现了奇怪的行为,由于某种原因,它会插入一个 Tab(这是预期
在下面的代码中:- BYTE ks[256]; auto keyboard_layout = GetKeyboardLayout(0); GetKeyboardState(ks); auto w =
与获得按键有什么区别: GetKeyState() GetAsyncKeyState() getch()? 我应该什么时候使用一个而不是另一个? 最佳答案 GetKeyState() 和 GetAsy
#include int main() { if ( !GetKeyState(VK_CAPITAL) & 1 ) { printf("caps off"); } else printf("caps
基本上, 它的灵感来自 Vim 我想使用一个键(例如 Alt、F1)组合(+I J K L)来映射到箭头键 Autohotkey 中已经完成的工作 Ralt & j::send{Left} Ralt
我是一名优秀的程序员,十分优秀!