且构网

分享程序员开发的那些事...
且构网 - 分享程序员编程开发的那些事

Keyboard.GetKeyStates误报,我如何才能真正知道是否按下了某个键?

更新时间:2023-12-03 08:53:40

因此,问题归结为Windows报告按键方式的错误.

So the issue comes down to a bug in the way Windows reports key presses.

Windows在称为虚拟键盘的物理键盘上创建了一个抽象层.虚拟键盘侦听物理键盘事件,并使用其维护键的状态,以后可以通过Windows API调用(其中User32.dll调用GetKeyState,GetAsyncKeyState和GetKeyboardState)或包装这些API调用的.NET调用来检索这些键的状态. (那些在System.Windows.Input.Keyboard命名空间中).

Windows creates a layer of abstraction over the physical keyboard called the virtual keyboard. The virtual keyboard listens to physical keyboard events and uses it to maintain a state of the keys that can later be retrieved by Windows API calls (amongst which User32.dll calls GetKeyState, GetAsyncKeyState and GetKeyboardState) or the .NET calls that wrap those API calls (those in the System.Windows.Input.Keyboard namespace).

在这种情况下,它收到NumPad2的KeyDown事件,但收到Down的KeyUp事件(这是NumPad2的 shifted 版本),因此NumPad2的状态会一直保持按下状态关注.

In this case, it receives a KeyDown event for NumPad2, but a KeyUp event for Down (which is the shifted version of NumPad2), so the state for NumPad2 stays pressed as far as those calls are concerned.

基本上,我找到的解决方法都是不依赖这些调用.

Basically the workarounds I found were all about not relying on those calls.

以下是我发现可以解决的一些变通方法:

Here are some workarounds I have found to work:

1.使用Windows API直接挂接到物理键盘事件.

这个想法是通过SetWindowsHookEx Win32 API调用使用到物理键盘的钩子.

The idea is to use a hook to the physical keyboard using the SetWindowsHookEx Win32 API call.

此方法的主要问题是API仅提供事件,不提供状态.您必须维护键盘所有键的状态才能正常工作.

The main issue with this approach is that the API only provides events, no state. You have to maintain your own state of all the keys of the keyboard for this to work.

2.使用DirectX.

我发现的另一个解决方案是使用DirectInput,它是DirectX的一部分.这为DirectX添加了一个依赖项(如果您的应用程序在Windows Vista之前不支持任何功能,那就很好了).此外,除了第三方库之外,没有从托管代码直接访问DirectX的功能(过去,Microsoft.NET包装器将DirectX的旧版本作为DirectX API的一部分,但该版本已经过时,并且无法与最新版本一起使用.NET). 您可以:

The other solution I have found is to use DirectInput, which is part of DirectX. This adds a dependency to DirectX (which is fine if your application does not support anything before Windows Vista). Furthermore, there is no direct access to DirectX from managed code except from third-party libraries (there used to be a Microsoft .NET wrapper around old versions of DirectX as part of the DirectX API but it is obsolete and would not work with recent versions of .NET). You can either:

  • 使用第三方.NET库(我已经使用一个这样的库SharpDX创建了一个有效的概念证明,该库似乎提供了与运行它的计算机上安装的DirectX版本无关的优点). /li>
  • 为此用途使用C ++ DirectX API创建C ++库.
  • use a third-party .NET library (I have created a working proof of concept with one such library, SharpDX, which seems to provide the advantage of being agnostic to the DirectX version installed on the machine it runs on).
  • create a C++ library using the C++ DirectX API, for this specific use.