You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

主线程读取stdin时,如何在后台线程可靠使用PeekConsoleInput?

如何在保留主线程控制台输入打印的同时获取INPUT_RECORD?

我需要实时从控制台输入缓冲区获取INPUT_RECORD(优先仅获取KEY_EVENT_RECORD),同时不阻止主线程读取这些记录并将字符打印到控制台屏幕缓冲区。

目前我只能实现二者其一:要么用PeekConsoleInput(或ReadConsoleInput)获取INPUT_RECORD,要么让主线程处理输入字符的读取与屏幕打印。

当前实现的控制台输入打印行为

我可以通过以下两行代码让控制台应用将用户输入的内容(包括按退格键/删除键删除字符、按方向键插入字符等操作)打印到屏幕:

let mut line = String::new();
std::io::stdin().read_line(&mut line).unwrap();

console_input

我希望保留这一行为,同时从控制台输入缓冲区获取INPUT_RECORD。获取到这些记录后,我可以将其发送给子进程,通过WriteConsoleInput在多个控制台中同步复现用户输入。

遇到的问题

我尝试在后台线程中使用PeekConsoleInput,主线程保留上述代码,但主线程似乎更快地将INPUT_RECORD移出输入缓冲区,导致后台线程没有记录可查看。

附带代码展示了只要没有其他线程移除记录(flush = true 对比 flush = false),在后台线程运行PeekConsoleInput就能成功查看INPUT_RECORD。

如何在让主线程负责将输入字符打印到屏幕的同时,可靠地查看控制台输入缓冲区并获取INPUT_RECORD?

依赖配置(Cargo.toml)

[dependencies]
tokio = {version = "1.27.0", features = ["macros", "rt-multi-thread", "time"]}

[dependencies.windows]
version = "0.44.0"
features = [
    "Win32_Foundation",
    "Win32_System_Console",
]

示例代码(main.rs)

use std::time::Duration;

use windows::Win32::System::Console::{
    FlushConsoleInputBuffer, GetStdHandle, PeekConsoleInputW, INPUT_RECORD, STD_INPUT_HANDLE,
};

async fn peek_console_input(nb_of_records_to_read: u32, flush: bool) {
    const NB_EVENTS: usize = 2;
    let mut nb_of_records_read = 0;
    let console_handle = unsafe { GetStdHandle(STD_INPUT_HANDLE).unwrap() };
    loop {
        let mut input_buffer: [INPUT_RECORD; NB_EVENTS] = [INPUT_RECORD::default(); NB_EVENTS];
        let mut number_of_events_read = 0;
        unsafe {
            PeekConsoleInputW(
                console_handle,
                &mut input_buffer,
                &mut number_of_events_read,
            )
            .expect("Failed to read console input");
        }
        if number_of_events_read > 0 as u32 {
            if flush {
                unsafe {
                    FlushConsoleInputBuffer(console_handle);
                };
            }
            nb_of_records_read += number_of_events_read;
            for record in input_buffer {
                let event = unsafe { record.Event.KeyEvent };
                println!(
                    "key_down: {}, unicode_char: {}",
                    event.bKeyDown.as_bool(),
                    unsafe { event.uChar.UnicodeChar }
                );
            }
        }
        if nb_of_records_read >= nb_of_records_to_read {
            break;
        }
    }
}

#[tokio::main]
async fn main() {
    let nb_of_records_to_read = 8;
    for flush in [true, false] {
        println!(
            "Spawning a thread peeking at the console input buffer \
            for the next {nb_of_records_to_read} input records \
            (key down and up count as 1 record each)"
        );
        let join_handle = tokio::spawn(peek_console_input(nb_of_records_to_read, flush));
        if flush {
            println!(
                "Putting main thread to sleep for 2 milliseconds at a time \
                while waiting for the background thread to finish."
            );
            println!("Please press any combination of keys and behold the output");
            while !join_handle.is_finished() {
                tokio::time::sleep(Duration::from_millis(2)).await;
            }
        } else {
            println!("Main thread is reading a line from stdin, please start pressing keys");
            let mut line = String::new();
            std::io::stdin().read_line(&mut line).unwrap();
            println!("read line: {}", line);
        }
        join_handle.abort();
        join_handle.await.unwrap();
    }
}

内容的提问来源于stack exchange,提问作者whme

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.07.25 19:20:05