3

I have a Qt gui application with commandline functionality. To make this work I added this to the top of the main() function:

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

Then an instance of my main class is constructed. In the constructor, QCommandLineParser determines if there are any arguments, and either creates an instance of the cmdline parsing class, or the gui application class.

In the cmdline parsing class, I ask the user to input certain values:

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();
}

When "Yes" "No", "YesAll" or "NoAll" (case insensitive) is input, the program continues as expected, but when the user inputs something other than that, cmd throws this:

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

And then the "C:\path\to\exe>" is displayed again, where the user can continue to input until one of the correct values is typed. Once a valid string is input, it continues again as expected.

I tried this answer as well as std::getline(), but it makes no difference.

So how do I prevent the error from showing up and continue displaying the cout?

2
  • This looks like QString isn't able to parse answer from a glance. Headed into an appointment though, couldn't read too far into it. I'd start there, and include your inputs and expected outputs in the question Commented May 1, 2020 at 16:02
  • I edited the question a bit, hope it's clearer now. When a valid value is input it works correctly as expected, so I don't think it's a problem with QString. I suspect it's because it's a GUI application, where normal cmd functionality doesn't work, and freopens aren't enough to enable it. Commented May 1, 2020 at 16:24

1 Answer 1

2

AttachConsole just attaches to the parent process's console, it does not stop the parent process from also reading from it. So the console input is being interleaved between the parent process (cmd.exe) and your app, which can be problematic to manage (some people advice killing the parent process, which obviously isn't a good idea).

What you can do instead is to always create a new console (see AllocConsole).

Or if you want to re-use the same console, it might be an idea to target the Console subsystem instead (linker option /SUBSYSTEM:CONSOLE) and have a regular main() function instead of WinMain (yes you can create Win32 windows and handle console I/O all within main()).

You can even have a multi-subsystem source that can be linked as Windows as well as Console subsystem with a shim like this (nCmdShow and command-line arguments remain to be implemented):

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. . .
Sign up to request clarification or add additional context in comments.

3 Comments

Using AllocConsole seems like the best solution here. Is there a way to close the first console window?
Windows users typically don't start programs from the console. You could check if the parent is cmd.exe and kill it... Personally though I would prefer if my console remained open. Some projects ship two executables - foo.exe for GUI and fooc.exe for console mode, so that the user can choose which one they want.
I suppose you're right, the user might not want the console to close. I considered splitting it into two .exe, but I prefer to keep them unified for now. Thanks for the help!

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.