返回

`CancelIoEx` 与 Windows 控制台中 `getline` 的输入取消问题剖析

windows

Windows 控制台中的 CancelIoExgetline:一次取消输入的深层探讨

在 Windows 控制台中处理用户输入时,有时需要取消异步输入操作,例如 ReadFileWriteFileCancelIoEx 函数通常用于此目的,但当它与 getline 一起使用时,可能会出现一些意外行为。

问题

当使用 CancelIoEx 异步取消 Windows 控制台输入时,getline 无法完全被取消。用户需要输入内容两次(按两次回车键)才能触发下一次 getline

可能的解决方案

猜测 Windows 控制台中的某些内容可能无法被 CancelIoEx 正确取消,从而导致此问题。

代码示例

以下 C++ 示例代码演示了此问题:

#include <iostream>
#include <thread>
#include <string>
#include <windows.h>

using namespace std;
HANDLE hStdin = nullptr;

void readInput() {
    string input;
    while (true) {
        cout << endl << "input: ";
        getline(std::cin, input);
        if (cin.good())
            cout << "got msg: " << input << std::endl;
        else
        {
            cin.clear();
        }
    }
}

void CancelInputPeriodically() {
    while (true) {
        this_thread::sleep_for(std::chrono::seconds(7));
        CancelIoEx(hStdin, nullptr);
        cout << "time's up!!!" << endl;
    }
}

int main() {
    hStdin = GetStdHandle(STD_INPUT_HANDLE);
    thread cancelThread(CancelInputPeriodically);

    readInput();

    cancelThread.join();
    return 0;
}

调试和分析

通过在 getline 处设置断点,我们发现它实际上没有收到第一次按回车键。

解决方法

一种解决方法是使用 WriteConsoleInput 函数在 cin 遇到 EOF 后发送 VK_RETURN 虚拟键。

其他注意事项

  • 确保 hStdin 在主线程中正确初始化,以便 CancelIoEx 可以成功取消输入。
  • CancelIoEx 只能取消异步输入操作,例如 ReadFileWriteFile。它无法取消同步输入操作,例如 scanfgets

结论

虽然 CancelIoEx 对于取消 Windows 控制台中的异步输入操作非常有用,但在与 getline 一起使用时可能会遇到一些限制。通过了解其工作原理和解决方法,我们可以充分利用 CancelIoEx 的能力,在应用程序中有效地管理输入。

常见问题解答

  1. 为什么 CancelIoEx 无法完全取消 getline

getline 内部使用缓存,因此在调用 CancelIoEx 之前,它可能已经将一些输入读取到缓存中。

  1. 如何解决此问题?

cin 遇到 EOF 后,使用 WriteConsoleInput 函数发送 VK_RETURN 虚拟键。

  1. 什么时候使用 CancelIoEx 比较合适?

当需要在未完成时取消异步输入操作时,CancelIoEx 非常有用,例如当需要响应用户输入或其他事件时。

  1. CancelIoExGetOverlappedResult 之间有什么区别?

CancelIoEx 用于取消异步输入操作,而 GetOverlappedResult 用于获取异步输入操作的结果,包括是否已取消。

  1. 使用 CancelIoEx 有什么限制?

CancelIoEx 只能取消异步输入操作,并且必须在操作完成之前调用。