问题标题: 控制台事件接管

2
0
已解决
王牌工作室官方
王牌工作室官方
新手光能
新手光能

大家写游戏时总要监听用户做出了什么操作,这叫事件监听

监听键盘事件时,大家用GetAsyncKeyState,但是假如焦点不在控制台上,它也照样处理。

监听鼠标事件时,大家用GetAsyncKeyState+GetCursorPos,但是如果鼠标不在控制台窗口内,GetCursorPos照样捕获;如果鼠标点击了另一个窗口,GetAsyncKeyState照样处理

经过一番百度后,我找到了最终的解决方法:控制台事件接管,用ReadConsoleInput

函数原型:

WINBASEAPI WINBOOL WINAPI ReadConsoleInputA(
    HANDLE hConsoleInput,           //in
    PINPUT_RECORD lpBuffer,         //out
    DWORD nLength,                  //in
    LPDWORD lpNumberOfEventsRead);  //out

我们暂时不做Unicode编程,统一用WINAPI函数后缀A的,当然,省略默认为后缀A,有办法改变这种默认行为(呃呃,有点跑题)

好,回归正题,我们解释一下参数

[in]  HANDLE hConsoleInput

对于单缓冲区程序,默认为GetStdHandle(STD_INPUT_HANDLE)

[out] PINPUT_RECORD lpBuffer

首先定义一个INPUT_RECORD类型的数组,把数组名传进来

[in]  DWORD nLength

数组的大小,有助于防止函数进行数组越界

[out] PDWORD lpNumberOfEventReads

实际读入的事件个数,得先定义一个DWORD类型的变量,这个参数这里填'&'加上变量名

 

微软文档的实例:

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

HANDLE hStdin;
DWORD fdwSaveOldMode;

VOID ErrorExit(LPSTR);
VOID KeyEventProc(KEY_EVENT_RECORD);
VOID MouseEventProc(MOUSE_EVENT_RECORD);
VOID ResizeEventProc(WINDOW_BUFFER_SIZE_RECORD);

int main(VOID)
{
    DWORD cNumRead, fdwMode, i;
    INPUT_RECORD irInBuf[128];
    int counter=0;

    // Get the standard input handle.

    hStdin = GetStdHandle(STD_INPUT_HANDLE);
    if (hStdin == INVALID_HANDLE_VALUE)
        ErrorExit("GetStdHandle");

    // Save the current input mode, to be restored on exit.

    if (! GetConsoleMode(hStdin, &fdwSaveOldMode) )
        ErrorExit("GetConsoleMode");

    // Enable the window and mouse input events.

    fdwMode = ENABLE_WINDOW_INPUT | ENABLE_MOUSE_INPUT;
    if (! SetConsoleMode(hStdin, fdwMode) )
        ErrorExit("SetConsoleMode");

    // Loop to read and handle the next 100 input events.

    while (counter++ <= 100)
    {
        // Wait for the events.

        if (! ReadConsoleInput(
                hStdin,      // input buffer handle
                irInBuf,     // buffer to read into
                128,         // size of read buffer
                &cNumRead) ) // number of records read
            ErrorExit("ReadConsoleInput");

        // Dispatch the events to the appropriate handler.

        for (i = 0; i < cNumRead; i++)
        {
            switch(irInBuf[i].EventType)
            {
                case KEY_EVENT: // keyboard input
                    KeyEventProc(irInBuf[i].Event.KeyEvent);
                    break;

                case MOUSE_EVENT: // mouse input
                    MouseEventProc(irInBuf[i].Event.MouseEvent);
                    break;

                case WINDOW_BUFFER_SIZE_EVENT: // scrn buf. resizing
                    ResizeEventProc( irInBuf[i].Event.WindowBufferSizeEvent );
                    break;

                case FOCUS_EVENT:  // disregard focus events

                case MENU_EVENT:   // disregard menu events
                    break;

                default:
                    ErrorExit("Unknown event type");
                    break;
            }
        }
    }

    // Restore input mode on exit.

    SetConsoleMode(hStdin, fdwSaveOldMode);

    return 0;
}

VOID ErrorExit (LPSTR lpszMessage)
{
    fprintf(stderr, "%s\n", lpszMessage);

    // Restore input mode on exit.

    SetConsoleMode(hStdin, fdwSaveOldMode);

    ExitProcess(0);
}

VOID KeyEventProc(KEY_EVENT_RECORD ker)
{
    printf("Key event: ");

    if(ker.bKeyDown)
        printf("key pressed\n");
    else printf("key released\n");
}

VOID MouseEventProc(MOUSE_EVENT_RECORD mer)
{
#ifndef MOUSE_HWHEELED
#define MOUSE_HWHEELED 0x0008
#endif
    printf("Mouse event: ");

    switch(mer.dwEventFlags)
    {
        case 0:

            if(mer.dwButtonState == FROM_LEFT_1ST_BUTTON_PRESSED)
            {
                printf("left button press \n");
            }
            else if(mer.dwButtonState == RIGHTMOST_BUTTON_PRESSED)
            {
                printf("right button press \n");
            }
            else
            {
                printf("button press\n");
            }
            break;
        case DOUBLE_CLICK:
            printf("double click\n");
            break;
        case MOUSE_HWHEELED:
            printf("horizontal mouse wheel\n");
            break;
        case MOUSE_MOVED:
            printf("mouse moved\n");
            break;
        case MOUSE_WHEELED:
            printf("vertical mouse wheel\n");
            break;
        default:
            printf("unknown\n");
            break;
    }
}

VOID ResizeEventProc(WINDOW_BUFFER_SIZE_RECORD wbsr)
{
    printf("Resize event\n");
    printf("Console screen buffer is %d columns by %d rows.\n", wbsr.dwSize.X, wbsr.dwSize.Y);
}

另外,我结合实例做了一个API。

#ifndef CONSOLEEVENT_H_
#define CONSOLEEVENT_H_

#include<windef.h>
#include<winbase.h>
#include<wincon.h>
#include<stdio.h>
short UnitSize=128;
INPUT_RECORD input_buffer[32768];
VOID ErrorExit(LPCSTR);
VOID (*KeyEvtProc)(KEY_EVENT_RECORD);
VOID (*MouseEvtProc)(MOUSE_EVENT_RECORD);
VOID (*ResEvtProc)(WINDOW_BUFFER_SIZE_RECORD);
void Listen()
{
	DWORD cNumRead;
	if (!ReadConsoleInput(
            GetStdHandle(STD_INPUT_HANDLE),      // input buffer handle
            input_buffer,                        // buffer to read into
            UnitSize,                            // size of read buffer
            &cNumRead))                          // number of records read
        ErrorExit("ReadConsoleInput");

    // Dispatch the events to the appropriate handler.

    for(int i=0;i<cNumRead;i++)
    {
        switch(input_buffer[i].EventType)
        {
            case KEY_EVENT: // keyboard input
                if(KeyEvtProc) KeyEvtProc(input_buffer[i].Event.KeyEvent);
                break;

            case MOUSE_EVENT: // mouse input
                if(MouseEvtProc) MouseEvtProc(input_buffer[i].Event.MouseEvent);
                break;

            case WINDOW_BUFFER_SIZE_EVENT: // scrn buf. resizing
                if(ResEvtProc) ResEvtProc(input_buffer[i].Event.WindowBufferSizeEvent);
                break;

            case FOCUS_EVENT:  // disregard focus events

            case MENU_EVENT:   // disregard menu events
                break;

            default:
                ErrorExit("Unknown event type");
                break;
        }
    }
}
VOID ErrorExit(LPCSTR lpszMessage)
{
    fprintf(stderr, "%s\n", lpszMessage);
//    SetConsoleMode(GdtStdHandle(STD_INPUT_HANDLE),fdwSaveOldMode);
    ExitProcess(0);
}

#endif

把它加到头文件后,using namespace std;前,

使用方法见WP文档(等写完文档后把链接发出来)

王牌工作室官方在2022-03-25 13:39:35追加了内容

那个,我封装的那个要改成这样

#ifndef CONSOLEEVENT_H_
#define CONSOLEEVENT_H_

#include<windef.h>
#include<winbase.h>
#include<wincon.h>
#include<stdio.h>
short UnitSize=128;
INPUT_RECORD input_buffer[32768];
VOID ErrorExit(LPCSTR);
VOID (*KeyEvtProc)(KEY_EVENT_RECORD);
VOID (*MouseEvtProc)(MOUSE_EVENT_RECORD);
VOID (*ResEvtProc)(WINDOW_BUFFER_SIZE_RECORD);
void InitConsoleEvent()
{
	DWORD fdwMode=ENABLE_WINDOW_INPUT|ENABLE_MOUSE_INPUT;
	SetConsoleMode(GetStdHandle(STD_INPUT_HANDLE),fdwMode);
}
void Listen()
{
	DWORD cNumRead;
	if (!ReadConsoleInput(
            GetStdHandle(STD_INPUT_HANDLE),      // input buffer handle
            input_buffer,                        // buffer to read into
            UnitSize,                            // size of read buffer
            &cNumRead))                          // number of records read
        ErrorExit("ReadConsoleInput");

    // Dispatch the events to the appropriate handler.

    for(int i=0;i<cNumRead;i++)
    {
        switch(input_buffer[i].EventType)
        {
            case KEY_EVENT: // keyboard input
                if(KeyEvtProc) KeyEvtProc(input_buffer[i].Event.KeyEvent);
                break;

            case MOUSE_EVENT: // mouse input
                if(MouseEvtProc) MouseEvtProc(input_buffer[i].Event.MouseEvent);
                break;

            case WINDOW_BUFFER_SIZE_EVENT: // scrn buf. resizing
                if(ResEvtProc) ResEvtProc(input_buffer[i].Event.WindowBufferSizeEvent);
                break;

            case FOCUS_EVENT:  // disregard focus events

            case MENU_EVENT:   // disregard menu events
                break;

            default:
                ErrorExit("Unknown event type");
                break;
        }
    }
}
VOID ErrorExit(LPCSTR lpszMessage)
{
    fprintf(stderr, "%s\n", lpszMessage);
//    SetConsoleMode(GdtStdHandle(STD_INPUT_HANDLE),fdwSaveOldMode);
    ExitProcess(0);
}

#endif

 

王牌工作室官方在2022-03-26 15:09:25追加了内容

新的1.4版本的ConsoleEvent和文档一起发出

源码:https://gitee.com/wp-studio/code-base/blob/master/ConsoleEvent.h

文档:https://gitee.com/wp-studio/repository/blob/master/WangpaiConsoleEventApiDunuments.docx

王牌工作室官方在2022-03-26 15:11:29追加了内容

1.4版本得感谢@薛乘志,是他给我提的建议


0
已采纳
薛乘志
薛乘志
初级启示者
初级启示者

一个问题:在win10上点击控制台会因为快速编辑模式卡**,需要一段代码关闭快速编辑模式

另外,你的API怎么用,我也不知道啊,建议写一个教程

0
0
我要回答