9

I am using _popen to start a process to run a command and gather the output

This is my c++ code:

bool exec(string &cmd, string &result)
{
   result = "";

   FILE* pipe = _popen(cmd.c_str(), "rt");
   if (!pipe)
      return(false);

   char buffer[128];
   while(!feof(pipe))
   {
        if(fgets(buffer, 128, pipe) != NULL)
               result += buffer;
   }
   _pclose(pipe);
   return(true);
}

Is there any way of doing this without a console window opening (as it currently does at the _popen statement)?

Andreas Rejbrand
  • 95,177
  • 8
  • 253
  • 351
  • Does it still show if you redirect it's output somewhere? To another pipe or a file? http://msdn.microsoft.com/en-us/library/ms682499.aspx (I don't know, I'm merely speculating) – Mooing Duck Apr 04 '12 at 23:39
  • I faced the same problem, here's a solution using boost::process - [link.](https://stackoverflow.com/questions/49509960/why-does-popen-work-here-but-boostprocess-does-not) – Vivek Subramanian Mar 28 '18 at 10:16
  • Here is a solution that works! https://stackoverflow.com/a/43600962/82856 – jparanich Feb 25 '21 at 17:49

3 Answers3

4

On Windows, CreateProcess with a STARTUPINFO structure that has dwFlags to include STARTF_USESSHOWWINDOW. Then setting STARTUPINFO.dwFlags to SW_HIDE will cause the console window to be hidden when triggered. Example code (which may be poorly formatted, and contains a mix of C++ and WinAPI):

#include <windows.h>
#include <iostream>
#include <string>

using std::cout;
using std::endl;

void printError(DWORD);

int main()
{
  STARTUPINFOA si = {0};
  PROCESS_INFORMATION pi = { 0 };

  si.dwFlags = STARTF_USESHOWWINDOW | STARTF_USESTDHANDLES;
  si.wShowWindow = SW_HIDE;
  BOOL result = ::CreateProcessA("c:/windows/system32/notepad.exe", NULL, NULL, NULL, FALSE, 0, NULL, NULL, &si, &pi);
  if(result == 0) {
      DWORD error = ::GetLastError();
      printError(error);
      std::string dummy;
      std::getline(std::cin, dummy);
      return error;
  }

  LPDWORD retval = new DWORD[1];
  ::GetExitCodeProcess(pi.hProcess, retval);
  cout << "Retval: " << retval[0] << endl;
  delete[] retval;

  cout << "Press enter to continue..." << endl;
  std::string dummy;
  std::getline(std::cin, dummy);

  return 0;
}

void printError(DWORD error) {
    LPTSTR lpMsgBuf = nullptr;
     FormatMessage(
        FORMAT_MESSAGE_ALLOCATE_BUFFER | 
        FORMAT_MESSAGE_FROM_SYSTEM |
        FORMAT_MESSAGE_IGNORE_INSERTS,
        NULL,
        error,
        MAKELANGID(LANG_NEUTRAL, SUBLANG_DEFAULT),
        (LPTSTR) &lpMsgBuf,
        0, NULL );
     cout << reinterpret_cast<char*>(lpMsgBuf) << endl;
     LocalFree(lpMsgBuf);
}
freeone3000
  • 404
  • 3
  • 9
  • It should be noted via my attempted edit (http://stackoverflow.com/review/suggested-edits/4113333) that you do not need to call new[] delete[] for retval. It would be perform better and be easier to read/maintain by creating an DWORD retval = 0; and passing ::GetExitCodeProcess(pi.hProcess, &retval); The cout line would change from retval[0] to retval as well. – Tim Beaudet Feb 20 '14 at 15:07
  • What's with the blank namespace before ::CreateProcessA() and ::GetLastError()? – aj.toulan Jul 23 '19 at 20:54
3

As far as I know, you can't1: you are starting a console application (cmd.exe, that will run the specified command), and Windows always creates a console window when starting a console application.


  1. although, you can hide the window after the process started, or even create it hidden if you pass the appropriate flags to CreateProcess; problem is, _popen do not pass these flags, so you have to use the Win32 APIs instead of _popen to create your pipe.
Matteo Italia
  • 115,256
  • 16
  • 181
  • 279
  • I was thinking perhaps I could run a child process...can that be done and not show a window? Also would using "system" also show a window? - thanks –  Apr 04 '12 at 23:35
  • The only way I see to make it hide the window is to call directly `CreateProcess`, specifying `SW_HIDE` in the `wShowWindow` member of the `STARTUPINFO` structure. – Matteo Italia Apr 04 '12 at 23:39
0

[Final Edit]

a similar SO question merges everything said above and gets you your output C++ popen command without console

[Edited again]

erk. sorry I got excited about spawning processes. I reread your q. and apart from the extra window you're actually trying to get the processes's stdout/stderr. I'd just like to add that for that purpose, all my suggestions are sadly irrelevant. but I'll leave them here for reference.

[Edited]

For no good specific reason (except that "open" works for both windows and macs), I use ShellExecute for spawning processes rather than CreateProcess. I'll research that later..but here is my StartProcess function.

Hidden or Minimized seem to produce the same result. the cmd window does come into being but it is minimized and doesn't ever pop up on the desktop which might be your primary goal.

#if defined(PLATFORM_WIN32)
#include <Windows.h>
#include <shellapi.h>
#elif defined(PLATFORM_OSX)
#include <sys/param.h>
#endif
namespace LGSysUtils
{
// -----------------------------------------------------------------------
// pWindow      : {Optional} - can be NULL
// pOperation   : "edit", "explore", "find", "open", "print"
// pFile        : url, local file to execute
// pParameters  : {Optional} - can be NULL otherwise a string of args to pass to pFile
// pDirectory   : {Optional} - set cwd for process
// type         : kProcessWinNormal, kProcessWinMinimized, kProcessWinMaximized, kProcessHidden
//
bool StartProcess(void* pWindow, const char* pOperation, const char* pFile, const char* pParameters, const char* pDirectory, LGSysUtils::eProcessWin type)
{
    bool rc = false;
#if defined(PLATFORM_WIN32)

    int showCmd;
    switch(type)
    {
    case kProcessWinMaximized:
        showCmd = SW_SHOWMAXIMIZED;
        break;

    case kProcessWinMinimized:
        showCmd = SW_SHOWMINIMIZED;
        break;

    case kProcessHidden:
        showCmd = SW_HIDE;
        break;

    case kProcessWinNormal:
    default:
        showCmd = SW_NORMAL;
    }

    int shellRC = (int)ShellExecute(reinterpret_cast<HWND>(pWindow), pOperation,pFile,pParameters,pDirectory,showCmd);
    //Returns a value greater than 32 if successful, or an error value that is less than or equal to 32 otherwise.
    if( shellRC > 32 )
    {
        rc = true;
    }

#elif defined(PLATFORM_OSX)
    char cmd[1024];
    sprintf(cmd, "%s %s", pOperation, pFile);
    int sysrc = system( cmd );
    dbPrintf("sysrc = %d", sysrc);
    rc = true;
#endif
    return rc;
}

}

[and previously mentioned]

If you are in control of the source code for the application that is launched, you could try adding this to the top of your main.cpp (or whatever you have named it)

// make this process windowless/aka no console window
#pragma comment( linker, "/subsystem:\"windows\" /entry:\"mainCRTStartup\"" )

You could also feed those options to the linker directly. The above is easier to play with for different build configurations imho.

Community
  • 1
  • 1
Sassafras_wot
  • 551
  • 4
  • 17
  • 2
    Not so easy, since `_popen` always launches `cmd.exe` to run the given command, and over `cmd.exe` linker flags you have no control. – Matteo Italia Apr 05 '12 at 01:27
  • Thank you for the suggestion but regretfully I am not in control of the code that is launched. –  Apr 05 '12 at 01:37
  • @user236520 You don't necessarily need access to the source code. If you supply the binary yourself, you could change the subsystem in the PE header with a hex editor. – a3f Mar 28 '18 at 10:26