C ++:GetKeyState()必须运行一次

问题描述 投票:3回答:6

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

#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. 我按SPACE键。
  2. DoSpaceKeyTask()执行“一次”。
  3. 我按OTHER键。
  4. DoOtherKeyTask()执行“一次”。

我喜欢我将要使用的5个键。这个案子有人能帮帮我吗?

PS。如果GetKeyState()功能对此任务无用,请随时向您推荐。我的函数知识在C ++上非常有限。

c++ winapi keyboard keypress
6个回答
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
}

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评论中建议更多“现代”异步方法。


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
    }
}

0
投票

我认为只有当你松开钥匙时才会“一次”执行你的功能,而不是当你按下(按下)它们时。

您不需要任何其他标志和辅助变量来定义,分配,分配给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发布的代码,但它对我不起作用。当我真正按下空格键或我选择的其他键时,没有显示我按下空格键或其他键的输出。


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函数来等待密钥unpress。更简单的解决方案。


-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代码所需的密钥。然后你可以循环遍历地图并减少开关所需的大量空间。

© www.soinside.com 2019 - 2024. All rights reserved.