捕捉按键事件 C++

4

我正在使用一个简单的循环来捕捉用户按键;


while (1)
{
    for(i = 8; i <= 190; i++)
    {
       if (GetAsyncKeyState(i) == -32767){
         //Do stuff
       }
    }
}

当用户按下某个键时,该程序将执行某些操作,但由于它在无限循环中,且我对C ++不熟悉,这会占用100%的CPU,这对于简单的输入来说并不好。我错在哪里了?我尝试了Sleep()函数(如果我将其放在'for loop'中,则会错过键入的内容,如果我将其放在'while loop'中,则完全不会降低CPU使用率,据我所知),我如何才能同时捕获按键,但又不占用太多CPU;我是否缺少技巧?我相信大多数程序都会捕捉按键,但你不会看到所有程序都占用100%!谢谢。

你不能使用线性算法来完成这个任务... 你需要向程序添加一个监听器。去谷歌一下吧。 - Ionut Hulub
使用钩子。GetMessage会一直等待,直到发生键盘事件。对于一个不太优雅的解决方案,在循环中加入Sleep(10);或其他类似操作。 - chris
过去我曾经使用RegisterHotkeyUnregisterHotkey的组合。然而,我只想在窗口处于焦点时检测按键,因此当窗口失去焦点时,我调用了UnregisterHotkey,并在窗口重新获得焦点时重新注册它。也许这些函数可以帮助你。如果在窗口失去焦点时不注销热键,可能会干扰其他应用程序的热键。 - Marlon
3个回答

3
将无限循环放在一个工作线程中,并在每次迭代时使其休眠一段合理的时间。C++11使这变得非常容易:
#include <thread>
#include <chrono>

std::chrono::milliseconds THREAD_WAIT = 50;

int keypress = -1;

void GetKeyPress()
{
   while (1)
   {
       for(i = 8; i <= 190; i++)
       {
          int k = GetAsyncKeyState(i);
          if (/*whatever condition needs to be satisfied*/)
              keypress = k;
       }
       if (keypress != -1) break; //Use this only if you have td.join() below
       std::this_thread::sleep_for(THREAD_WAIT);
   }
}

int main(void)
{
   ...

   std::thread td( GetKeyPress );
   td.join(); //If you want to block until the user presses a key, otherwise remove.

   //If no join(), do the rest of your program, checking in on keypress when need be

   return 0;
}

1
请查看以下链接,其中包含使用GetAsyncKeyState()函数的正确方法和示例代码。 http://www.mpgh.net/forum/31-c-c-programming/120656-proper-use-getasynckeystate.html 希望这个链接可以帮助您解决问题。
编辑: GetAsyncKeyState()函数并不适合您尝试做的事情。
它所做的只是简单地检查键盘上某个键的实际、当前位置。这样做几乎总是不正确的。
相反,使用适当的输入函数读取控制台输入。请参考下面的示例代码。
#include <stdio.h>
#include <windows.h>

int main()
{
    DWORD        mode;          /* Preserved console mode */
    INPUT_RECORD event;         /* Input event */
    BOOL         done = FALSE;  /* Program termination flag */
    unsigned int counter = 0;   /* The number of times 'Esc' is pressed */

    /* Don't use binary for text files, OK?  ;-) */
    FILE* myfile = fopen( "example.txt", "w" );

    /* Get the console input handle */
    HANDLE hstdin = GetStdHandle( STD_INPUT_HANDLE );

    /* Preserve the original console mode */
    GetConsoleMode( hstdin, &mode );

    /* Set to no line-buffering, no echo, no special-key-processing */
    SetConsoleMode( hstdin, 0 );

    /* Give the user instructions */
    printf(
        "Press Escape as many times as you like.\n"
        "Press anything else to quit.\n\n"
        );

    while (!done)
    {
        if (WaitForSingleObject( hstdin, 0 ) == WAIT_OBJECT_0)  /* if kbhit */
        {
            DWORD count;  /* ignored */

            /* Get the input event */
            ReadConsoleInput( hstdin, &event, 1, &count );

            /* Only respond to key release events */
            if ((event.EventType == KEY_EVENT)
            &&  !event.Event.KeyEvent.bKeyDown)
                switch (event.Event.KeyEvent.wVirtualKeyCode)
                {
                    case VK_ESCAPE:
                        counter++;
                        fprintf( myfile, "Escape: %d\n", counter );
                        printf( "Button pressed!\n" );
                        break;
                    default:
                        done = TRUE;
                }
        }
    }

    /* All done! */
    printf( "You pressed the Escape key %d times\n", counter );
    fclose( myfile );
    SetConsoleMode( hstdin, mode );
    return 0;
}

0

您可以使用

while (!kbhit());

这可能有帮助。


我的编译器无法识别 kbhit(DevC++),哪个编译器可以使用该命令? - James Andrew
现在可以工作了,谢谢。虽然 CPU 占用仍然很高,我会尝试像其他评论建议的钩子或监听器。谢谢。 - James Andrew

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接