【问题标题】:Using istream (std cin): prevent "[input] is not recognized as ..." on Windows使用 istream (std cin):防止 Windows 上的“[输入] 未被识别为 ...”
【发布时间】:2020-05-01 14:54:28
【问题描述】:

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

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

然后构造我的主类的一个实例。在构造函数中,QCommandLineParser 确定是否有任何参数,并创建 cmdline 解析类或 gui 应用程序类的实例。

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

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]' 未被识别为内部或外部命令 [...]

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

我尝试了this answerstd::getline(),但没有任何区别。

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

【问题讨论】:

  • 这看起来像 QString 无法解析answer 一眼。前往约会,但不能读得太深。我将从那里开始,并在问题中包含您的输入和预期输出
  • 我稍微编辑了这个问题,希望现在更清楚了。当输入有效值时,它按预期正常工作,所以我认为 QString 没有问题。我怀疑这是因为它是一个 GUI 应用程序,正常的 cmd 功能不起作用,freopens 不足以启用它。

标签: c++ cmd cin


【解决方案1】:

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

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

或者,如果您想重复使用 same 控制台,则可以改为以控制台子系统为目标(链接器选项/SUBSYSTEM:CONSOLE)并使用常规的main() 函数而不是WinMain(是的,您可以在main() 内创建 Win32 窗口处理控制台 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. . .

【讨论】:

  • 使用AllocConsole 似乎是这里最好的解决方案。有没有办法关闭第一个控制台窗口?
  • Windows 用户通常不会从控制台启动程序。您可以检查父级是否为cmd.exe 并杀死它...尽管我个人希望我的控制台保持打开状态。有些项目提供两个可执行文件 - foo.exe 用于 GUI 和 fooc.exe 用于控制台模式,以便用户可以选择他们想要的。
  • 我想你是对的,用户可能不希望控制台关闭。我考虑将其拆分为两个 .exe,但我现在更愿意将它们保持统一。感谢您的帮助!
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 2021-06-13
  • 2012-05-01
  • 1970-01-01
  • 2020-10-24
  • 1970-01-01
  • 2014-09-15
  • 2022-01-12
相关资源
最近更新 更多