【问题标题】:C++ Win32 keyboard eventsC++ Win32 键盘事件
【发布时间】:2009-09-17 07:06:14
【问题描述】:

出于个人兴趣,我正在研究我的击键记录器,并问了一个与昨天有关的问题; While loop using a lot of CPU.

该程序的问题是它占用了过多的 CPU 使用率,人们建议将输入设为基于键事件。

由于我是 Win32 API 的新手,因此我尝试查找参考资料和教程,这些参考资料和教程将告诉我如何将键盘输入创建为基于事件的,而不是基于轮询的。但问题是我找不到任何可靠的例子或参考资料,因为对于一个完整的新手来说很难理解。

他们中的大多数人提到基于事件的编程驻留在 GUI 应用程序中,但我希望这个击键记录器应用程序是一个控制台应用程序。

我的两个主要问题是:

  • 我可以使用 Win32 API 编写基于事件的控制台击键记录器吗?如果没有,我有什么选择?

  • 是否有任何参考网站可以帮助我了解如何基于事件捕获击键?

如果需要更多信息,我在 Windows XP 下使用带有 GCC 编译器的代码块。

【问题讨论】:

    标签: c++ winapi event-handling


    【解决方案1】:

    键盘记录应用程序使用Win32 Hooks 等机制。具体来说,你需要设置一个WH_KEYBOARD 钩子。

    有一些高级的移动技术,比如创建自己的键盘驱动程序,但首先挂钩是一个不错的选择。

    编辑:为了了解钩子程序的外观,我发布了我个人实用程序的片段。

    // ...
    thehook = SetWindowsHookEx( WH_KEYBOARD_LL, hook_proc, hwnd, 0 );
    // ...
    
    /**
     *
     *  wParam, one of the: WM_KEYDOWN, WM_KEYUP, WM_SYSKEYDOWN, or WM_SYSKEYUP
        lParam: pointer to a KBDLLHOOKSTRUCT structure
    
        (*) "The hook procedure should process a message in less time than the
        data entry specified in the LowLevelHooksTimeout value in the following registry key: 
        HKEY_CURRENT_USER\Control Panel\Desktop 
    
        The value is in milliseconds. If the hook procedure does not 
        return during this interval, the system will pass the message to the next hook."
    
     *
     */
    LRESULT CALLBACK
    hook_proc( int code, WPARAM wParam, LPARAM lParam )
    {
      static long ctrl_cnt = 0;
      static bool mmode = false;
      static DWORD time;
    
      KBDLLHOOKSTRUCT*  kbd = (KBDLLHOOKSTRUCT*)lParam;
    
      if (  code < 0
      ||   (kbd->flags & 0x10) // ignore injected events
         ) return CallNextHookEx( thehook, code, wParam, lParam );
    
      long ret = 1; // by default I swallow the keys
      if (  mmode  ) { // macro mode is ON
        if (  WM_KEYDOWN == wParam  )
          PostMessage(mainwnd, WM_MCR_ACCUM, kbd->vkCode, 0);
    
        if (  WM_KEYUP == wParam  )
          switch (kbd->vkCode) {
            case VK_ESCAPE:
              mmode = false;
              keys.removeall();
              PostMessage(mainwnd, WM_MCR_HIDE, 0, 0);
              break;
    
            case VK_RETURN:
              PostMessage(mainwnd, WM_MCR_EXEC, 0, 0);
              break;
    
            case VK_LCONTROL:
              mmode = false;
              PostMessage(mainwnd, WM_MCR_HIDE, 0, 0);
              PostMessage(mainwnd, WM_MCR_EXEC, 0, 0);
              break;
          }
    
        /* Which non printable keys allow passing? */
        switch( kbd->vkCode ) {
          case VK_LCONTROL:
          case VK_CAPITAL:
          case VK_LSHIFT:
          case VK_RSHIFT:
            ret = CallNextHookEx( thehook, code, wParam, lParam );
        }
      }
      else { // macro mode is OFF
        /* Ctrl pressed */
        if (  kbd->vkCode == VK_LCONTROL && WM_KEYDOWN == wParam  ) {
          ctrl_cnt = 1;
          time = kbd->time;
        }
    
        /* Prevent ctrl combinations to activate macro mode */
        if (  kbd->vkCode != VK_LCONTROL  )
          ctrl_cnt = 0;
    
        /* Ctrl released */
        if (  ctrl_cnt == 1 && WM_KEYUP == wParam  ) {
          if (  kbd->time - time > 40  ) {
            mmode = true;
            PostMessage(mainwnd, WM_MCR_SHOW, 0, 0);
          }
        }
    
        ret = CallNextHookEx( thehook, code, wParam, lParam ); // let it pass
      }
    
      return ret;
    }
    

    【讨论】:

    • 如何获得不同程序的 HINSTANCE?
    • @MatoušVrba,您不需要获取其他程序的句柄。
    【解决方案2】:

    看看SetWindowsHookEx API。

    您可以使用控制台应用程序和窗口应用程序来执行此操作。您需要将键盘挂钩放入 DLL 中,以便在您自己的进程之外的进程中捕获击键。

    有一些示例代码here

    【讨论】:

    • 只是好奇,钩子到底是做什么的?我读过它会将自己注入另一个程序,或者它可能是我的理解。另外,这会减少按键记录器的 CPU 使用量吗?
    • 挂钩在发生选定的事件时将回调添加到您的函数中。您不需要循环不断地轮询键盘。您的代码仅在必要时执行。
    • 另见此讨论stackoverflow.com/questions/3134183/…(我相信 exe 可以处理 *_LL 消息,但其他任何内容都需要 dll)。
    • 我提交了一个编辑以将您的评论添加到您的答案中。如果您不喜欢编辑,请随时拒绝。 :)
    猜你喜欢
    • 2022-01-22
    • 2017-05-29
    • 2020-12-13
    • 1970-01-01
    • 1970-01-01
    • 2011-01-05
    • 1970-01-01
    • 2017-09-08
    • 1970-01-01
    相关资源
    最近更新 更多