注册 登录
编程论坛 C语言论坛

按键侦测遇到BUG

ianlin1024 发布于 2023-04-23 15:48, 205 次点击
该程式在执行时会侦测按键是否按下与纪录按键之间的间隔时间
遇到的问题是该段标注区域会多一次的纪录,不知道是什么地方出问题。
程式码与图片如下#

只有本站会员才能查看附件,请 登录


程序代码:

#include <stdio.h>
#include <windows.h>

#define MAX_KEYS 30
#define MAX_KEYSB 30
#define MAX_INTERVALS (MAX_KEYS - 1)
#define MAX_INTERVALSB (MAX_KEYSB - 1)// 設定驗證密碼紀錄的間隔數比輸入按鍵少一

int main()
{
    INPUT_RECORD input_record;
    HANDLE console = GetStdHandle(STD_INPUT_HANDLE);
    DWORD events_read; // DWORD變量: 事件讀取(記錄按鍵按下的時間和釋放的時間)
    LARGE_INTEGER start_time, end_time, frequency;
    BOOL last_event_was_release = FALSE;
    LARGE_INTEGER last_release_time;
    int key_count = 0;
    double sum, avg;


    char keys[MAX_KEYS] = { 0 };
    char keysB[MAX_KEYSB] = { 0 };
    double intervals[MAX_INTERVALS - 1] = { 0 };
    double intervalsB[MAX_INTERVALSB - 1] = { 0 };// 設定陣列紀錄間隔數組
    double intervaldifferent;

    char input[20]; // 儲存使用者輸入的字串
    char target[] = "password"; // 設定目標字串
    int flag = 1;

    QueryPerformanceFrequency(&frequency);

    printf("請設定節拍密碼\n\n");
    while (1) {
        if (ReadConsoleInput(console, &input_record, 1, &events_read)) { //讀取控制台輸入(console是正在讀取的控制台輸入緩衝區的句柄,&input_record是指向緩衝區的指針,該緩衝區接收讀取的輸入記錄,1是要讀取的輸入記錄數,&events_read是一個指向變量的指針,該變量接收實際讀取的輸入記錄數)
            if (input_record.EventType == KEY_EVENT) { //檢查輸入記錄的事件類型是否為鍵盤事件
                if (input_record.Event.KeyEvent.bKeyDown) { //檢查鍵是否被按下。如果此條件為真,代碼將執行 if 語句內的代碼塊
                    if (last_event_was_release && input_record.Event.KeyEvent.wVirtualKeyCode != VK_RETURN) { //檢查上一個事件是否為按鍵釋放以及當前事件是否為按鍵(不包括回車鍵)
                        LARGE_INTEGER press_time;
                        QueryPerformanceCounter(&press_time); //記錄按鍵事件的時間
                        double intervalA = (double)(press_time.QuadPart - last_release_time.QuadPart) / frequency.QuadPart * 1.0; //併計算按鍵與最後一次按鍵釋放事件之間的間隔
                        intervals[key_count - 1] = intervalA; // 將間隔存儲在適當索引 (key_count - 1) 的間隔數組中
                        printf("上下按鍵的時間間隔(節奏): %.3fs\n", (double)(press_time.QuadPart - last_release_time.QuadPart) / frequency.QuadPart * 1.0); //將間隔打印到控制台。打印的消息包括按鍵和最後一次按鍵釋放事件之間的時間間隔(以秒為單位)
                    }
                    QueryPerformanceCounter(&start_time); //用於獲取性能計數器的值,該值存儲在start_time變量中,然後用於計算按鍵按下和按鍵釋放之間經過的時間。
                    last_event_was_release = FALSE; //設置FALSE為表示最後一個事件是按鍵,這是計算節奏中按鍵之間的間隔所需要的。

                    if (key_count < MAX_KEYS) {
                        keys[key_count++] = input_record.Event.KeyEvent.uChar.AsciiChar;
                    }
                }
                else {
                    LARGE_INTEGER release_time;
                    QueryPerformanceCounter(&release_time);
                    last_release_time = release_time;
                    last_event_was_release = TRUE;
                    if (input_record.Event.KeyEvent.uChar.AsciiChar != '\0') {
                        printf("'%c'\n", input_record.Event.KeyEvent.uChar.AsciiChar);
                    }
                    else {
                        printf("Key pressed for %.3f s\n", (release_time.QuadPart - start_time.QuadPart) / frequency.QuadPart * 1.0);
                    }
                }
            }
        }



        // 如果Enter鍵被按下,跳出設定密碼
        if (GetAsyncKeyState(VK_RETURN) & 1) {

            printf("\n==============================================================================\n");
            printf("再輸入一次節拍密碼\n\n");
            int key_countB = 0;

            while (2) {
                if (ReadConsoleInput(console, &input_record, 1, &events_read)) { //讀取控制台輸入(console是正在讀取的控制台輸入緩衝區的句柄,&input_record是指向緩衝區的指針,該緩衝區接收讀取的輸入記錄,1是要讀取的輸入記錄數,&events_read是一個指向變量的指針,該變量接收實際讀取的輸入記錄數)
                    if (input_record.EventType == KEY_EVENT) { //檢查輸入記錄的事件類型是否為鍵盤事件
                        if (input_record.Event.KeyEvent.bKeyDown) { //檢查鍵是否被按下。如果此條件為真,代碼將執行 if 語句內的代碼塊
                            if (last_event_was_release && input_record.Event.KeyEvent.wVirtualKeyCode != VK_RETURN) { //檢查上一個事件是否為按鍵釋放以及當前事件是否為按鍵(不包括回車鍵)
                                LARGE_INTEGER press_time;
                                QueryPerformanceCounter(&press_time); //記錄按鍵事件的時間
                                double intervalA = (double)(press_time.QuadPart - last_release_time.QuadPart) / frequency.QuadPart * 1.0; //併計算按鍵與最後一次按鍵釋放事件之間的間隔
                                intervalsB[key_countB - 1] = intervalA; // 將間隔存儲在適當索引 (key_count - 1) 的間隔數組中
                                printf("上下按鍵的時間間隔(節奏): %.3fs\n", (double)(press_time.QuadPart - last_release_time.QuadPart) / frequency.QuadPart * 1.0); //將間隔打印到控制台。打印的消息包括按鍵和最後一次按鍵釋放事件之間的時間間隔(以秒為單位)
                            }
                            QueryPerformanceCounter(&start_time); //用於獲取性能計數器的值,該值存儲在start_time變量中,然後用於計算按鍵按下和按鍵釋放之間經過的時間。
                            last_event_was_release = FALSE; //設置FALSE為表示最後一個事件是按鍵,這是計算節奏中按鍵之間的間隔所需要的。

                            if (key_countB < MAX_KEYS) {
                                keysB[key_countB++] = input_record.Event.KeyEvent.uChar.AsciiChar;
                            }
                        }
                        else {
                            LARGE_INTEGER release_time;
                            QueryPerformanceCounter(&release_time);
                            last_release_time = release_time;
                            last_event_was_release = TRUE;
                            if (input_record.Event.KeyEvent.uChar.AsciiChar != '\0') {
                                printf("'%c' 鍵被按下\n", input_record.Event.KeyEvent.uChar.AsciiChar);
                            }
                            else {
                                printf("Key pressed for %.3f s\n", (release_time.QuadPart - start_time.QuadPart) / frequency.QuadPart * 1.0);
                            }
                        }
                    }
                }
                // 如果Enter鍵被按下,跳出設定密碼
                if (GetAsyncKeyState(VK_RETURN) & 1) {



                    int i = 0;
                    for (int i = 0; i < key_count; i++) {
                        if (keys[i] != keysB[i])
                        {
                            printf("密碼數字設定錯誤\n");
                        }
                        else {
                            printf("密碼數字設定正確\n");
                        }
                    }


                    for (int i = 0; i < key_count - 1; i++) {
                        intervaldifferent = intervals[i] - intervalsB[i];
                        if (intervaldifferent < 0) {
                            intervaldifferent = intervaldifferent * (-1);
                        }
                        if (intervaldifferent > 0.25 * intervals[i])
                        {
                            printf("密碼數字間隔錯誤\n");
                        }
                        else {
                            printf("密碼數字間隔正確\n");
                        }
                    }


                    break;
                }
            }

            break;

        }

    }
    return 0;
}

/*

*/
8 回复
#2
吹水佬2023-04-23 17:17
试试先 FlushConsoleInputBuffer
#3
ianlin10242023-04-23 18:53
以下是引用吹水佬在2023-4-23 17:17:32的发言:

试试先 FlushConsoleInputBuffer


貌似没有效
#4
ianlin10242023-04-23 19:23
回复 2楼 吹水佬
有可能是我回圈中哪一部份的值设定不正确吗?
#5
吹水佬2023-04-23 19:58
缓冲有东西,刷新无效就都取出丢掉
ReadConsoleInput
#6
ianlin10242023-04-23 21:48
回复 5楼 吹水佬
我的想法是第一次輸入完之後,資料記錄到A陣列中,在清除原本的緩衝區
再繼續輸入第二次的資料,記錄到B陣列。
但這樣的話我的ReadConsoleInput應該放在哪個部份呢?
#7
东海ECS2023-04-23 22:12
可能是因为在按下非字母按键时也会执行到纪录间隔的代码,导致间隔纪录的数量不对。可以将纪录间隔的代码放在一个if语句中,只在按下字母键时执行。

代码示例:

while (1) { if (ReadConsoleInput(console, &input_record, 1, &events_read)) { if (input_record.EventType == KEY_EVENT) { if (input_record.Event.KeyEvent.bKeyDown) { if (last_event_was_release && input_record.Event.KeyEvent.wVirtualKeyCode != VK_RETURN) { if (input_record.Event.KeyEvent.uChar.AsciiChar >= 'a' && input_record.Event.KeyEvent.uChar.AsciiChar <= 'z') { // 只在按下字母键时记录间隔 LARGE_INTEGER press_time; QueryPerformanceCounter(&press_time); double intervalA = (double)(press_time.QuadPart - last_release_time.QuadPart) / frequency.QuadPart * 1.0; intervals[key_count - 1] = intervalA; printf("上下按鍵的時間間隔(節奏): %.3fs\n", intervalA); } } QueryPerformanceCounter(&start_time); last_event_was_release = FALSE; if (key_count < MAX_KEYS) { keys[key_count++] = input_record.Event.KeyEvent.uChar.AsciiChar; } } else { LARGE_INTEGER release_time; QueryPerformanceCounter(&release_time); last_release_time = release_time; last_event_was_release = TRUE; if (input_record.Event.KeyEvent.uChar.AsciiChar != '\0') { printf("'%c'\n", input_record.Event.KeyEvent.uChar.AsciiChar); } else { printf("Key pressed for %.3f s\n", (release_time.QuadPart - start_time.QuadPart) / frequency.QuadPart * 1.0); } } } } }
#8
ianlin10242023-04-23 23:25
回复 7楼 东海ECS
只有本站会员才能查看附件,请 登录


谢谢您提供这个思路
但其实在原程式码中我在输入字母或着数字功能都是一样的,所以有排除掉这个可能性

在图片中其实是一个输入与重复验证的过程(用=====上下部份表示),第一次我输入3个数字,很标准的确实有3个数字与2个间隔时间,这部分是没问题的,在我按下回车键之后(用=====下部份),就是红框内的地方,而红框第一行的[' 键被按下],我判断是程式记录了我的回车键并输出出来了,但不知道怎么修掉这个地方。
红框第二行是我在第二次重复验证按下"1"时,系统记录到我前一个回车键的时间,并与"1"键输出两者的间隔时间。
#9
吹水佬2023-04-24 09:15
            ReadConsoleInput(console, &input_record, 1, &events_read);
            while (2) {
                if (ReadConsoleInput(console, &input_record, 1, &events_read))
1