Is there any way of stopping _popen opening a dos

2020-02-12 04:16发布

问题:

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)?

回答1:

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.


回答2:

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


回答3:

[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.