3

我需要为我的小应用程序监听键盘按键状态。

#include <windows.h>
#include <fstream>
#include <iostream>

using namespace std;

int main()
{
    while(1)
    {
        if(GetKeyState(VK_SPACE) & 0x80)
        {
            cout << "Space pressed.\r\n";
            DoSpaceKeyTask();
        }

        if(GetKeyState(OTHER_KEY) & 0x80)
        {
            cout << "Other key pressed.\r\n";
            DoOtherKeyTask();
        }
    }
    return 0;
}

一旦我从键盘上单击某些键,这些功能就必须运行一次。它们只是我的应用程序的一些小任务,与本主题无关。

我的问题是,当我按下一个键时,由于while(1)在按键期间循环了几次,它会执行几次功能。我不能Sleep()在这种情况下使用,因为它仍然不会有效。

我正在寻找这样的解决方案。

  1. 我按空格键。
  2. DoSpaceKeyTask()执行“一次”。
  3. 我按其他键。
  4. DoOtherKeyTask()执行“一次”。

我有 5 个我将要使用的键。有人可以帮我处理这个案子吗?

附言。如果GetKeyState()函数对此任务没有用,请随时提出您的建议。我对 C++ 的函数知识非常有限。

4

7 回答 7

5

由于按钮保持按下的持续时间,您的函数被多次调用。该系统非常敏感。所以这是一种解决方法。

你可以做这样的事情(设置一个标志,在按键按下时分配一个值,然后在按键向上时重新分配)。

int k=0;//Flag
while(1){
    //check if the key was pressed (key_down)
    if((GetAsyncKeyState('0') & 0x8000) && (k == 0)){k=1; cout<<"'0' PRESSED."<<k<<endl;}//do your stuff here
    //check if the key was released (key up)
    else if(GetAsyncKeyState('0') == 0) k = 0;//reset the flag
}
于 2014-02-26T12:47:52.827 回答
1

我认为您更愿意仅在松开按键时才执行“一次”功能,而不是在您按下(按下)它们时执行您的功能。

您不需要任何额外的标志和辅助变量来定义、分配、分配为 0,并将每个设置为 1 并重置为 0 等等,以实现此目标。您所需要的只是:首先您必须在 while(1) 的范围内使用 GetKeyState 函数来检查您何时按下了一个键。当表达式返回 true 时,执行器指针(执行代码行然后在您进入或跳过时前进到下一个代码行的箭头)将进入 if 语句的范围。然后立即将它困在一个循环中,并在您按下的键仍然按下时将其困在其中,并在它要执行该功能之前停止它,并在您释放该键时释放它,然后让它执行功能。

例如,要在您按下并释放空格键时只执行“一次” DoSpaceKeyTask 函数,然后执行以下应该可以工作的代码:

while (1)
{
    if (GetKeyState(VK_SPACE) & 0x80)
    {
        //The code here executes ONCE at the moment the space bar was pressed
        cout << "Space pressed.\r\n";
        while (GetKeyState(VK_SPACE) & 0x80) //You can write there ';' instead '{' and '}' below
        {
            //The executor pointer is trapped here while space bar is depressed and it will be free once space bar is released
        }
        //The code here executes ONCE at the moment the space bar was released
        cout << "Space released.\r\n";
        DoSpaceKeyTask();
    }
}

与 DoOtherKeyTask 函数相同:

while (1)
{
    if (GetKeyState(OTHER_KEY) & 0x80)
    {
        //The code here executes ONCE at the moment the other key was pressed
        cout << "Other key pressed.\r\n";
        while (GetKeyState(OTHER_KEY) & 0x80) //You can write there ';' instead '{' and '}' below
        {
            //The executor pointer is trapped here while other key is depressed and it will be free once other key is released
        }
        //The code here executes ONCE at the moment the other key was released
        cout << "Other key released.\r\n";
        DoOtherKeyTask();
    }
}

如果您已经使用了 BT_ 的想法或 Pawel Zubrycki 的想法,现在您想使用我的想法,那么您可以删除他们建议的所有标志和变量,因为您不再需要它们了。

顺便说一句,我已经尝试过 Pawel Zubrycki 发布的代码,但它对我不起作用。当我真正按下空格键或我选择的其他键时,没有显示说我按下了空格键或其他键的输出。

于 2015-01-06T13:45:44.543 回答
0

我遇到了同样的问题。我有几个键,它们的作用类似于切换按钮,并且每次按下时只想注册一次键事件。我的解决方案是制作一个简单的对象来处理逻辑。这使主代码保持干净:

class KeyToggle {
public:
    KeyToggle(int key):mKey(key),mActive(false){}
    operator bool() {
        if(GetAsyncKeyState(mKey)){
            if (!mActive){
                mActive = true;
                return true;
            }  
        }
        else
            mActive = false;
        return false;
    }
private:
    int mKey;
    bool mActive;
};

这是用法:

#include <windows.h>

KeyToggle toggleT(0x54);  // T key toggle
KeyToggle toggleF(0x46);  // F key toggle

void main(void)
{
    while(true){
        if(toggleT) {;} // do something
        if(toggleF) {;} // do something
    }
}
于 2015-03-30T12:56:33.667 回答
0

试试这个方法:

#include <windows.h>
#include <fstream>
#include <iostream>

using namespace std;

int main()
{
    char lastSpaceState = 0, lastOtherKeyState = 0, spaceState, otherKeyState;
    while(1)
    {
        spaceState = (GetKeyState(VK_SPACE & 0x80) != 0);
        lastSpaceState = (spaceState && !lastSpaceState);
        if(lastSpaceState)
        {
                cout << "Space pressed.\r\n";
                DoSpaceKeyTask();
        }

        otherKeyState = (GetKeyState(OTHER_KEY) & 0x80 != 0);
        lastOtherKeyState = (otherKeyState && !lastOtherKeyState);
        if(lastOtherKeyState)
        {
                cout << "Other key pressed.\r\n";
                DoOtherKeyTask();
        }

    }

    return 0;
}

或者正如克里斯在 OP 评论中建议的那样,更“现代”的异步方法。

于 2012-05-28T21:31:17.580 回答
0

我知道这是很老的线程,但我仍然想分享我的解决方案。我认为确实不需要创建某种“标志”或“开关”。这是我循环所有键码的代码:

    while (true) 
    {
        for (int i = 0x01; i < 0xFE; i++) 
        {
            if (GetKeyState(i) & 0x8000)
            {
                std::cout << (char)i << "\n";
                while (GetKeyState(i) & 0x8000) {}
            }
        }
    }

如您所见,您可以轻松地使用 while 和相同的 GetKeyState 函数来等待按键松开。更简单的解决方案。

于 2019-04-23T16:24:13.477 回答
0

试试看。像这样;

    while (!GetKeyState(VK_SPACE) && !GetKeyState('A') == 1) {
    //std::cout << "Key not pressed... \n";
    Sleep(40);
}
if (GetKeyState('A')) {
    std::cout << "\"A \" key pressed... \n";
}
else if (GetKeyState(VK_SPACE)) {
    std::cout << "Space key pressed... \n";
}
于 2020-03-17T21:37:04.220 回答
-1

您需要一个 windows 挂钩来挂钩游戏并对游戏获得的键盘输入做出反应。现在我还没有真正完成这种特定类型的钩子,但我可以让你对流程有一个很好的了解。我将留给您通过循环遍历您需要的键映射而不是巨大的重复开关来减少空间,并且还可以解决我放入的任何小问题。

int main()
{
    //I imagine the last argument here is the thread 
    //containing the game's message loop
    HHOOK hook = SetWindowsHookEx (WH_CALLWNDPROC, hookProc, NULL, NULL);

    //main loop

    UnhookWindowsHookEx (hook);
}

LRESULT CALLBACK hookProc (int code, WPARAM wParam, LPARAM lParam)
{
    if (code == HC_ACTION)
    {
         CWPSTRUCT details = *((LPCWPSTRUCT)lParam);

         if (details.message == WM_KEYDOWN)
         {   
              switch (details.wParam)
              { 
                  case KEY_ONE:
                      if (last [KEY_ONE] == UP)
                      {
                          DoKeyOneStuff();
                          last [KEY_ONE] = DOWN;
                      }
                      break;
              }
          }

          else if (details.message == WM_KEYUP)
          {
              switch (details.wParam)
              {
                   case KEY_ONE:
                       last [KEY_ONE] = UP;
                   break;
              }
          }
    }

    return CallNextHookEx (NULL, code, wParam, lParam);
}

注意我如何使用last [KEY_ONE]. 我建议使用std::map他们的 vk 代码来存储您需要的密钥。然后,您可以循环遍历地图并减少切换所需的大量空间。

于 2012-05-28T22:09:54.700 回答