首页 > 解决方案 > 我希望键“A”至少保持三秒钟而不会中断。确保是这种情况

问题描述

我希望按键A至少保持三秒钟而不会中断。如果按键中断,则应再次应用三秒时间。这是我以前的程序。不幸的是,当timer()程序运行时,程序没有注意到按钮释放或再次按下的任何信息。

我的问题是:我能做什么?

我想留在C。

我的 IDE 是 Microsoft Visual Studio Community 2019,版本 16.11.2。

我使用 Windows 10。

#pragma warning(disable : 4996) // Visual Studio doesn't want to see old functions like ‘scanf’.

#include <stdio.h>
#include <Windows.h>
#include <cstdint>
#include <time.h>

bool supposed_to_run = true;
VOID KeyEventProc(KEY_EVENT_RECORD);
long long waited = 0ll;
time_t Time_of_the_keydown_event;
bool triggered = false;


void timer()
{
    Time_of_the_keydown_event;
    time_t _to = time(&_to) + 3ll;

    do
    {
        Time_of_the_keydown_event = time(&Time_of_the_keydown_event);
    } while (Time_of_the_keydown_event < _to && triggered);

}

void Logic(INPUT_RECORD rec)
{
    if (rec.Event.KeyEvent.wVirtualKeyCode == 0)
    {
        return;
    }

    if (!rec.Event.KeyEvent.bKeyDown)
    {
        triggered = false;
        return;
    }

    switch (rec.Event.KeyEvent.wVirtualKeyCode)
    {
    case VK_SHIFT:
        break;
    case VK_CONTROL:
        break;
    case VK_SPACE:
        break;
    case VK_LEFT:
        break;
    case VK_UP:
        break;
    case VK_RIGHT:
        break;
    case VK_DOWN:
        break;
    case 0x41: /*A*/
        triggered = true;
        timer();
        
        printf("A\n");
        break;
    case 0x51: /*Q*/
        break;
    case 0x5A:  /*Z*/
        supposed_to_run = false;
        break;
    default:
        break;
    }
}

int main(void)
{
    INPUT_RECORD rec{};
    HANDLE hConInp = GetStdHandle(STD_INPUT_HANDLE);
    DWORD ReadCount = 0;

    //Change_size_position_and_color();

    while (supposed_to_run)
    {
        ReadConsoleInput(hConInp, &rec, 1, &ReadCount);
        Logic(rec); 
    }
    

    // end program
    printf("\nProgramm wird beendet. Dr\x81 \bcken Sie eine Taste und \x27 \bEnter\x27 \b.\n");
    getchar();
    return 0;
}

标签: cwinapitimerkeyevent

解决方案


我建议您使用该功能WaitForSingleObject。该功能允许您等待新的控制台输入可用,还允许您指定超时,在您的情况下应该是 3 秒。

根据该函数的返回值,您将知道是否发生了新的控制台输入或超时是否已过期。

switch ( WaitForSingleObject( hConInp, 3000 ) )
{
case WAIT_OBJECT_0:
    //handle new console input
    //ReadConsoleInput will not block if called now
    break;
case WAIT_TIMEOUT:
    //handle timeout
    break;
default:
    //handle error
}

然而,该解决方案存在一个问题。按住按键时,按键会自动重复,生成新的输入事件。因此,3 秒的超时可能永远不会过期(除非您已将操作系统配置为具有很长的重复延迟)。

因此,您将不得不忽略这些按键事件并WaitForSingleObject再次调用,但这次不是再次超时 3 秒。相反,新的超时时间应该是原来的 3 秒的剩余时间。

在您的代码中,您使用的是 ISO C 函数time。但是,这个函数的精度只有一秒,可能不够准确。因此,您可能希望改用特定于平台的功能GetTickCount

我相信通过进行上述更改,我能够使您的程序正常工作。这是我的代码:

#define WIN32_LEAN_AND_MEAN

#include <windows.h>
#include <stdio.h>
#include <stdlib.h>
#include <time.h>
#include <stdbool.h>

bool supposed_to_run = true;
bool triggered = false;
bool require_release = false;
DWORD remaining_timeout;

void Logic(INPUT_RECORD rec)
{
    if (rec.Event.KeyEvent.wVirtualKeyCode == 0)
    {
        return;
    }

    if (!rec.Event.KeyEvent.bKeyDown)
    {
        if ( triggered )
        {
            printf( "removing trigger\n" );
            triggered = false;
        }
        else if ( require_release && rec.Event.KeyEvent.wVirtualKeyCode == 0x41 )
        {
            printf( "key has been released and timeout may now be triggered again\n" );
            require_release = false;
        }
        return;
    }

    switch (rec.Event.KeyEvent.wVirtualKeyCode)
    {
    case VK_SHIFT:
        break;
    case VK_CONTROL:
        break;
    case VK_SPACE:
        break;
    case VK_LEFT:
        break;
    case VK_UP:
        break;
    case VK_RIGHT:
        break;
    case VK_DOWN:
        break;
    case 0x41: /*A*/
        if ( !triggered && !require_release )
        {
            printf( "triggering timeout\n" );
            triggered = true;
            remaining_timeout = 3000;
        }
        break;
    case 0x51: /*Q*/
        break;
    case 0x5A:  /*Z*/
        supposed_to_run = false;
        break;
    default:
        break;
    }
}

int main(void)
{
    INPUT_RECORD rec = {0};
    HANDLE hConInp = GetStdHandle(STD_INPUT_HANDLE);
    DWORD ReadCount;

    while (supposed_to_run)
    {
        if ( triggered )
        {
            bool timeout_expired = false;

            DWORD start_time = GetTickCount();

            switch ( WaitForSingleObject( hConInp, remaining_timeout ) )
            {
            case WAIT_OBJECT_0:
                break;
            case WAIT_TIMEOUT:
                timeout_expired = true;
                break;
            default:
                fprintf( stderr, "unexpected error!" );
                exit( EXIT_FAILURE );
            }

            DWORD time_expired = GetTickCount() - start_time;

            if ( !timeout_expired )
            {
                //It is possible that WaitForSingleObject did not report
                //a timeout, but that GetTickCount now reports that the
                //timeout expired. We should consider this also a timeout.
                if ( time_expired >= remaining_timeout )
                    timeout_expired = true;
            }

            if ( timeout_expired )
            {
                triggered = false;
                require_release = true;
                printf("A\n");
                continue;
            }
            else
            {
                remaining_timeout -= time_expired;
            }
        }

        ReadConsoleInput(hConInp, &rec, 1, &ReadCount);
        Logic(rec); 
    }

    return 0;
}

用户按住该键A三秒钟后,它将在屏幕上显示该字符。之后,直到用户释放键然后再次按下它才会触发新的超时。


推荐阅读