int System::Exec()

in util/process_wrapper/system_windows.cc [197:295]


int System::Exec(const System::StrType& executable,
                 const System::Arguments& arguments,
                 const System::EnvironmentBlock& environment_block,
                 const StrType& stdout_file, const StrType& stderr_file) {
  STARTUPINFO startup_info;
  ZeroMemory(&startup_info, sizeof(STARTUPINFO));
  startup_info.cb = sizeof(STARTUPINFO);

  OutputPipe stdout_pipe;
  OutputPipe stderr_pipe;

  if (!stdout_file.empty() || !stderr_file.empty()) {
    // We will be setting our own stdout/stderr handles. Note that when setting `STARTF_USESTDHANDLES`
    // it is critical to set *all* handles or the child process might get a null handle (or garbage).
    startup_info.dwFlags |= STARTF_USESTDHANDLES;
    startup_info.hStdInput = INVALID_HANDLE_VALUE;

    SECURITY_ATTRIBUTES saAttr;
    ZeroMemory(&saAttr, sizeof(SECURITY_ATTRIBUTES));
    saAttr.nLength = sizeof(SECURITY_ATTRIBUTES);
    saAttr.bInheritHandle = TRUE;
    saAttr.lpSecurityDescriptor = NULL;

    if (!stdout_file.empty()) {
      if (!stdout_pipe.CreateEnds(saAttr)) {
        std::cerr << "process wrapper error: failed to create stdout pipe: "
                  << GetLastErrorAsStr();
        return -1;
      }
      startup_info.hStdOutput = stdout_pipe.WriteEndHandle();
    } else {
      startup_info.hStdOutput = INVALID_HANDLE_VALUE;
    }

    if (!stderr_file.empty()) {
      if (!stderr_pipe.CreateEnds(saAttr)) {
        std::cerr << "process wrapper error: failed to create stderr pipe: "
                  << GetLastErrorAsStr();
        return -1;
      }
      startup_info.hStdError = stderr_pipe.WriteEndHandle();
    } else {
      startup_info.hStdError = INVALID_HANDLE_VALUE;
    }
  }

  System::StrType command_line;
  ArgumentQuote(executable, command_line);
  MakeCommandLine(arguments, command_line);

  System::StrType environment_block_win;
  MakeEnvironmentBlock(environment_block, environment_block_win);

  PROCESS_INFORMATION process_info;
  ZeroMemory(&process_info, sizeof(PROCESS_INFORMATION));

  BOOL success = ::CreateProcess(
      /*lpApplicationName*/ nullptr,
      /*lpCommandLine*/ command_line.empty() ? nullptr : &command_line[0],
      /*lpProcessAttributes*/ nullptr,
      /*lpThreadAttributes*/ nullptr,
      /*bInheritHandles*/ TRUE,
      /*dwCreationFlags*/ 0
#if defined(UNICODE)
          | CREATE_UNICODE_ENVIRONMENT
#endif  // defined(UNICODE)
      ,
      /*lpEnvironment*/ environment_block_win.empty()
          ? nullptr
          : &environment_block_win[0],
      /*lpCurrentDirectory*/ nullptr,
      /*lpStartupInfo*/ &startup_info,
      /*lpProcessInformation*/ &process_info);

  if (success == FALSE) {
    std::cerr << "process wrapper error: failed to launch a new process: "
              << GetLastErrorAsStr();
    return -1;
  }

  if (!stdout_file.empty()) {
    if (!stdout_pipe.WriteToFile(stdout_file)) {
      return -1;
    }
  }
  if (!stderr_file.empty()) {
    if (!stderr_pipe.WriteToFile(stderr_file)) {
      return -1;
    }
  }

  DWORD exit_status;
  WaitForSingleObject(process_info.hProcess, INFINITE);
  if (GetExitCodeProcess(process_info.hProcess, &exit_status) == FALSE)
    exit_status = -1;
  CloseHandle(process_info.hThread);
  CloseHandle(process_info.hProcess);
  return exit_status;
}