0

The application I'm working on needs to execute commands. Commands can be console commands or 'GUI applications' (like notepad).

I need to get the return code in both cases, and in the case of console commands I also need to catch the output from stdin and stderr.

In order to implement this feature, I based my code on the stack overflow question 'How to execute a command and get output of command within C++ using POSIX?'.

My code:

int ExecuteCmdEx(const char* cmd, std::string &result) 
{
    char buffer[128];
    int retCode = -1; // -1 if error ocurs.
    std::string command(cmd);
    command.append(" 2>&1"); // also redirect stderr to stdout

    result = "";
    FILE* pipe = _popen(command.c_str(), "r");
    if (pipe != NULL) {
        try {
            while (!feof(pipe)) {
                if (fgets(buffer, 128, pipe) != NULL)
                    result += buffer;
            }
        }
        catch (...) {
            retCode = _pclose(pipe);
            throw;
        }
        retCode = _pclose(pipe);
    }
    return retCode;
}

It works perfectly with console applications, but in the case of 'GUI applications' it doesn't work as expected...

With 'GUI applications', code stops on while (!feof(pipe)) expecting to get something from pipe.

I understand that 'GUI applications' like notepad don't finish until someone interacts with them (user closes the app, kills the process, etc.), but when I launch console applications from Windows Console, prompt comes back immediately. I would like to obtain the same behavior from 'GUI applications'...

One possible solution would be to add the isGui variable indicating when the code should read from the pipe, but I rejected this option, as I don't want to indicate if it is a 'GUI application' or not.

arturn
  • 725
  • 2
  • 11
  • 25

1 Answers1

0

Well you don't have to indicate isGui yourself but detect it by checking the subsystem of the executable (windows/console) prior to executing the command, and in case of windows skip waiting on the redirected pipes.

For example, using SHGetFileInfo with the SHGFI_EXETYPE flag:

bool isGuiApplication(const std::string& command)
{
  auto it = command.find_first_of(" \t");
  const std::string& executable = (it == std::string::npos ? command : command.substr(0, it));

  DWORD_PTR exetype = SHGetFileInfo(executable.c_str(), 0, nullptr, 0, SHGFI_EXETYPE);
  if (!exetype) {
    cerr << "Executable check failed\n";
  }
  return ((uintptr_t)exetype & 0xffff0000);
}

Then later in the code...

  if (isGuiApplication(command)) {
    cout << "GUI application\n";
    system(command.c_str()); // don't wait on stdin
  }
  else {
    cout << "Console application\n";
    . . .
    // _popen and stuff
  }
rustyx
  • 80,671
  • 25
  • 200
  • 267