Warning: file_get_contents(/data/phpspider/zhask/data//catemap/2/.net/25.json): failed to open stream: No such file or directory in /data/phpspider/zhask/libs/function.php on line 167

Warning: Invalid argument supplied for foreach() in /data/phpspider/zhask/libs/tag.function.php on line 1116

Notice: Undefined index: in /data/phpspider/zhask/libs/function.php on line 180

Warning: array_chunk() expects parameter 1 to be array, null given in /data/phpspider/zhask/libs/function.php on line 181
C# Keyboard.GetKeyStates为假阳性,如何实际知道是否按下了某个键?_C#_.net_Wpf_Winapi_Keyboard Events - Fatal编程技术网

C# Keyboard.GetKeyStates为假阳性,如何实际知道是否按下了某个键?

C# Keyboard.GetKeyStates为假阳性,如何实际知道是否按下了某个键?,c#,.net,wpf,winapi,keyboard-events,C#,.net,Wpf,Winapi,Keyboard Events,似乎有一种方法可以让Keyboard.GetKeyStates返回错误按下的键,例如Keyboard.GetKeyStates(Key.NumPad2)返回Down,即使未按下也切换 我已经能够在一个非常简单的WPF应用程序上重现这一点。 重现错误的方法如下所示: 按NumPad2 按下L档 释放NumPad2 释放档位 从那时起,检查NumPad2的状态将始终产生向下,切换,直到再次按下并释放它 不确定这是否重要,但我正在使用Windows 8.1 x64上的英国扩展键盘 原因似乎是LShif

似乎有一种方法可以让
Keyboard.GetKeyStates
返回错误按下的键,例如
Keyboard.GetKeyStates(Key.NumPad2)
返回
Down,即使未按下也切换

我已经能够在一个非常简单的WPF应用程序上重现这一点。 重现错误的方法如下所示:

  • 按NumPad2
  • 按下L档
  • 释放NumPad2
  • 释放档位
  • 从那时起,检查NumPad2的状态将始终产生
    向下,切换
    ,直到再次按下并释放它

    不确定这是否重要,但我正在使用Windows 8.1 x64上的英国扩展键盘

    原因似乎是LShift-NumPad2实际上相当于向下键(有意义),但Windows似乎没有意识到这意味着不再按下NumPad2

    我的测试应用程序只捕获KeyDown和keydup,并向我显示每个事件的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
    
    因此,正如您所看到的,尽管我在第3步释放了NumPad2键,但在第3步和第4步之后,NumPad2键显示为按下

    以下是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>
    
    
    清楚的
    

    公共部分类主窗口
    {
    私有字典_initialKeyStates;
    专用词典_keyStates;
    私钥_previousKeyDown;
    私钥previousKeyUp;
    公共主窗口()
    {
    初始化组件();
    }
    私有void主窗口\u已加载(对象发送方,路由目标)
    {
    _keyStates=GetKeyStates();
    _initialKeyStates=\u keyStates;
    }
    私有void主窗口\u OnKeyDown(对象发送方,KeyEventArgs e)
    {
    如果(e.Key!=\u previousKeyDown)
    {
    AppendKeyEventDescription(“主窗口_OnKeyDown”,e);
    _previousKeyDown=e.键;
    }
    }
    私有void主窗口\u OnKeyUp(对象发送方,KeyEventArgs e)
    {
    如果(e.Key!=\u previousKeyUp)
    {
    AppendKeyEventDescription(“MainWindow_OnKeyUp”,e);
    _previousKeyUp=e.键;
    }
    }
    私有无效清除\u OnClick(对象发送方,路由目标)
    {
    TextBox.Text=string.Empty;
    }
    私有静态字典GetKeyStates()
    {
    返回Enum.GetValues(typeof(Key))
    .Cast()
    .Distinct()
    。其中(x=>x!=键。无)
    .ToDictionary(
    x=>x,
    Keyboard.GetKeyStates);
    }
    private void AppendKeyEventDescription(字符串eventName,KeyEventArgs e)
    {
    if(TextBox.Text!=string.Empty)
    TextBox.Text+=“\n\n”;
    文本框,文本+=
    eventName+“”+e.Key+”:“+_keyStates[e.Key]+”->“+e.keyStates;
    _keyStates=GetKeyStates();
    var changedKeys=\u keyStates.Keys
    .Where(key=>\u keyStates[key]!=\u initialKeyStates[key])
    .ToArray();
    if(changedKeys.Any())
    {
    TextBox.Text+=changedKeys.Aggregate(
    “\n系统状态:”,
    (text,key)=>text+“\n\t”+key+”:“+\u keyStates[key]);
    }
    }
    }
    
    我探讨了使用Win32 API调用的其他几种方法:

    它们都有完全相同的问题(这并不奇怪,因为我认为它们的内部工作与它们的.net等效键盘.GetKeyStates共享)


    现在我要寻找的是一种在任何时候都能知道NumPad2键是否真的被按下的方法…

    所以问题归结为Windows报告按键的方式中的一个错误

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

    在这种情况下,它接收NumPad2的KeyDown事件,但接收Down的KeyUp事件(这是NumPad2的移位版本),因此就这些调用而言,NumPad2的状态保持按下状态

    基本上,我找到的解决办法都是不依赖那些电话

    以下是我发现的一些可行的解决办法:

    1。使用Windows API直接连接到物理键盘事件。

    其思想是使用SetWindowsHookEx Win32 API调用将钩子用于物理键盘

    这种方法的主要问题是API只提供事件,不提供状态。您必须保持键盘上所有键的自身状态,才能使其正常工作

    2。使用DirectX。

    我发现的另一个解决方案是使用DirectInput,它是DirectX的一部分。这将向DirectX添加一个依赖项(如果您的应用程序在Windows Vista之前不支持任何内容,则可以这样做)。此外,除了第三方库之外,托管代码无法直接访问DirectX(作为DirectX API的一部分,以前DirectX的旧版本周围有一个Microsoft.NET包装器,但它已过时,无法与.NET的最新版本一起使用)。 您可以:

    • 使用第三方.NET库(I ha
         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]);
               }
            }
         }