使用 istream (std cin):防止 Windows 上的“[input] 不被识别为 ...”

Using istream (std cin): prevent "[input] is not recognized as ..." on Windows

我有一个具有命令行功能的 Qt gui 应用程序。 为了完成这项工作,我将其添加到 main() 函数的顶部:

#ifdef _WIN32
if (AttachConsole(ATTACH_PARENT_PROCESS)) {
    freopen("CONOUT$", "w", stdout);
    freopen("CONIN$", "r", stdin);
    freopen("CONOUT$", "w", stderr);
}
#endif

然后构造了我的mainclass的一个实例。在构造函数中,QCommandLineParser 判断是否有任何参数,并创建 cmdline 解析实例 class,或 gui 应用程序实例 class.

在命令行解析class中,我要求用户输入某些值:

QString qanswer;

// `answerToInt` is an std::unordered_map
while (answerToInt.find(qanswer) == answerToInt.end()) {
    std::cout << std::endl << "File will be overwritten:" << std::endl
              << path.toStdString() << std::endl
              << "Are you sure? " << (multiple ? "(Yes/YesAll/No/NoAll)" : "(Yes/No)") << std::endl;

    std::string answer;
    std::cin >> answer;
    qanswer = QString::fromStdString(answer).toLower();
    std::cin.clear();
}

当输入 "Yes" "No"、"YesAll" 或 "NoAll"(不区分大小写)时,程序按预期继续,但当用户输入其他内容时, cmd 抛出这个:

'[input]' is not recognized as an internal or external command [...]

然后再次显示"C:\path\to\exe>",用户可以继续输入,直到输入正确的值之一。 输入有效字符串后,它会按预期再次继续。

我试过this answerstd::getline(),但没有区别。

那么如何防止错误出现并继续显示 cout

AttachConsole 只是附加到父进程的控制台,它不会阻止父进程也从中读取。因此控制台输入在父进程 (cmd.exe) 和您的应用程序之间交错,这可能难以管理(有些人建议 killing 父进程,这显然不是一个好主意) .

您可以做的是始终创建一个 控制台(参见 AllocConsole)。

或者,如果您想重新使用 相同的 控制台,则可以改用控制台子系统(链接器选项 /SUBSYSTEM:CONSOLE)并拥有一个常规 main() 函数而不是 WinMain(是的,您可以在 main() 中创建 Win32 windows 句柄控制台 I/O ).

您甚至可以拥有一个可以链接为 Windows 的多子系统源以及带有这样填充程序的控制台子系统(nCmdShow 和命令行参数仍有待实现) :

HWND hwnd;

int main() {
    std::thread t([] {
        // let GUI run in its own thread ...
        WinMain(GetModuleHandle(NULL), NULL, "", SW_SHOWDEFAULT);
        exit(0);
    });
    // meanwhile in this thread we handle console I/O ...
    std::string s;
    std::cout << "Press Enter to exit" << std::endl;
    while (std::getline(std::cin, s)) {
        if (s == "")
            break;
        std::cout << "Hello " << s << std::endl;
    }
    PostMessageA(hwnd, WM_CLOSE, 0, 0);
    t.join();
}

int WINAPI WinMain(HINSTANCE hInstance, HINSTANCE hPrevInstance, LPSTR lpCmdLine, int nCmdShow)
{
     // Your normal WinMain.
     // CreateWindow, GetMessage loop etc. . .