努力在键盘钩子

时间:2016-01-22 17:17:28

标签: c# winforms macros interop keyboard-hook

我在这里度过了一整天,但几乎没有问任何问题;你们这么好,我觉得不值得(

我有这个应用程序我做了一段时间,我最近再次开始工作。 它记录了一个鼠标单击宏并重放它,它可以很好地用于我的用途但我开始觉得需要添加键盘笔划。

所以今天我添加了这个记录键盘笔划的功能,我可以捕捉笔画并显示它们,但我的问题在于我想要运行一个秒表,它从按键开始并在键释放时结束。 此外,我应该确保我按下的键和我发布的键是相同的,我希望它实际上是这样做的。

我尝试了许多不同的方法在那里运行秒表但是关于这段代码的一切让我感到不舒服。密钥的监听器应该具有keyup和keydown的分离,但是如何使用计时器?

一定要全力以赴。

问题是,我尝试在全球范围内实现秒表(Form1范围):

  1. 运行方法1次,进入keydown条件,启动计时器,
  2. 然后再跑一次,转到键盘并停止计时器。
  3. 我认为这应该可行,但我想我有范围问题,因为计时器永远不会给出准确的值。 (按3秒给出1,然后按下并快速释放3)我检查计时器是否在正确的时间启动和停止,我认为它确实如此。

    之后我尝试在钩子方法中实现它,但是因为它必须运行两次以便记录和放大。好了,计时器重置。 (这样写它我想也许我应该检查一下它是否正在运行而不是如果它是实例化的那样)

    我并不着急,真的不想寻找解决这个问题的快捷方法,而是寻找实现此功能的最佳方法,并尝试使这个程序更整洁。我有一种感觉,这是关闭的,我应该使用lParam,这将使它更容易。

    我在那里存储了按键信息,虽然我认为lParam实际上更有效地做到了这一点,但是无法理解它是如何工作的

    public class KeyPressHelper
    {
        public Keys currentPressedKeyCode;
        public Keys currentReleasedKeyCode;
        public Keys previousPressedKeyCode;
    
        public KeyPressHelper()
        {
            currentPressedKeyCode = new Keys();
            currentReleasedKeyCode = new Keys();
            previousPressedKeyCode = new Keys();
        }
    }
    

    这是钩子函数,大部分只是msdn提供的示例代码,我添加了keyup的条件,但我真的不确定这是实际的正确方法。

    private static IntPtr HookCallback(
            int nCode, IntPtr wParam, IntPtr lParam)
        {
            if (nCode >= 0 && wParam == (IntPtr)WM_KEYDOWN)
            {
                keyStrokeTimer.Start();   // global Stopwatch
    
                //keypresshelper kph1
                kph1.previousPressedKeyCode = 0;
                kph1.currentReleasedKeyCode = 0;
                int vkCode = Marshal.ReadInt32(lParam);   
    
                kph1.currentPressedKeyCode = (Keys)vkCode;
                kphList.Add(kph1);
            }
            if (nCode >= 0 && wParam == (IntPtr)WM_KEYUP)
            {
                //assign kph2.previousPressedkey to kph1.currentPressedKey
                kph2.previousPressedKeyCode
     = kphList.Find(k => k.Equals(kph1)).currentPressedKeyCode;
    
                kph2.currentPressedKeyCode = 0;
                int vkCode = Marshal.ReadInt32(lParam);
                kph2.currentReleasedKeyCode = (Keys)vkCode;
    
                //if they're identical well this is nice.
                if (kph2.previousPressedKeyCode == kph2.currentReleasedKeyCode)
                {
                    keyStrokeTimer.Stop();
                    MessageBox.Show("" + keyStrokeTimer.ElapsedMilliseconds)        //goal.
                }
                else
                {
                    keyStrokeTimer.Stop();
                }
            }
            return CallNextHookEx(_hookID, nCode, wParam, lParam);
    
        }
    

1 个答案:

答案 0 :(得分:0)

让我们来看看Stopwatch班级documentation

  

默认情况下,秒表实例的经过时间值等于所有测量时间间隔的总和。每次对Start的调用都会从累计经过的时间开始计算;每次调用Stop都会结束当前间隔测量并冻结累计经过时间值。使用“重置”方法清除现有秒表实例中的累计已用时间。

如您所见,您使用它的方式,Stopwatch正在累计总时间。如果您想衡量两个事件之间的时间,请使用Restart方法而不是Start