summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorAndy Cedilnik <andy.cedilnik@kitware.com>2002-09-27 21:26:37 (GMT)
committerAndy Cedilnik <andy.cedilnik@kitware.com>2002-09-27 21:26:37 (GMT)
commitc310600df56e7896cb0b996eb0dd9d24203d94fb (patch)
tree68dc9528d41e962c706c243cc3e3599b870b2503
parent2f1052b0e526350a9ce7b6b02b65b94b25d59060 (diff)
downloadCMake-c310600df56e7896cb0b996eb0dd9d24203d94fb.zip
CMake-c310600df56e7896cb0b996eb0dd9d24203d94fb.tar.gz
CMake-c310600df56e7896cb0b996eb0dd9d24203d94fb.tar.bz2
Add class for process execution on Windows
-rw-r--r--Source/CMakeLists.txt2
-rw-r--r--Source/cmWin32ProcessExecution.cxx614
-rw-r--r--Source/cmWin32ProcessExecution.h85
3 files changed, 701 insertions, 0 deletions
diff --git a/Source/CMakeLists.txt b/Source/CMakeLists.txt
index 6a108b9..a1bc408 100644
--- a/Source/CMakeLists.txt
+++ b/Source/CMakeLists.txt
@@ -72,6 +72,8 @@ IF (WIN32)
cmLocalVisualStudio6Generator.h
cmGlobalVisualStudio7Generator.h
cmLocalVisualStudio7Generator.h
+ cmWin32ProcessExecution.cxx
+ cmWin32ProcessExecution.h
)
IF( NOT BORLAND )
LINK_LIBRARIES( rpcrt4.lib )
diff --git a/Source/cmWin32ProcessExecution.cxx b/Source/cmWin32ProcessExecution.cxx
new file mode 100644
index 0000000..916693a
--- /dev/null
+++ b/Source/cmWin32ProcessExecution.cxx
@@ -0,0 +1,614 @@
+/*=========================================================================
+
+ Program: Insight Segmentation & Registration Toolkit
+ Module: $RCSfile$
+ Language: C++
+ Date: $Date$
+ Version: $Revision$
+
+ Copyright (c) 2002 Insight Consortium. All rights reserved.
+ See ITKCopyright.txt or http://www.itk.org/HTML/Copyright.htm for details.
+
+ This software is distributed WITHOUT ANY WARRANTY; without even
+ the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR
+ PURPOSE. See the above copyright notices for more information.
+
+=========================================================================*/
+#include "cmWin32ProcessExecution.h"
+
+#include "cmSystemTools.h"
+
+#include <malloc.h>
+#include <io.h>
+#include <fcntl.h>
+#include <stdio.h>
+#include <sys/stat.h>
+#include <windows.h>
+
+#define POPEN_1 1
+#define POPEN_2 2
+#define POPEN_3 3
+#define POPEN_4 4
+
+#define cmMAX(x,y) (((x)<(y))?(y):(x))
+
+#define win32_error(x,y) std::cout << "Win32_Error(" << x << ", " << y << ")" << std::endl, false
+
+bool cmWin32ProcessExecution::StartProcess(
+ const char* cmd, const char* path, bool verbose)
+{
+ this->Initialize();
+ this->m_Verbose = verbose;
+ return this->PrivateOpen(cmd, path, _O_RDONLY | _O_TEXT, POPEN_3);
+}
+
+bool cmWin32ProcessExecution::Wait(int timeout)
+{
+ return this->PrivateClose(timeout);
+}
+
+/*
+ * Internal dictionary mapping popen* file pointers to process handles,
+ * for use when retrieving the process exit code. See _PyPclose() below
+ * for more information on this dictionary's use.
+ */
+static void *_PyPopenProcs = NULL;
+
+static BOOL RealPopenCreateProcess(const char *cmdstring,
+ const char *path,
+ const char *szConsoleSpawn,
+ HANDLE hStdin,
+ HANDLE hStdout,
+ HANDLE hStderr,
+ HANDLE *hProcess)
+{
+ PROCESS_INFORMATION piProcInfo;
+ STARTUPINFO siStartInfo;
+ char *s1,*s2, *s3 = " /c ";
+ int i;
+ int x;
+
+ if (i = GetEnvironmentVariable("COMSPEC",NULL,0))
+ {
+ char *comshell;
+
+ s1 = (char *)_alloca(i);
+ if (!(x = GetEnvironmentVariable("COMSPEC", s1, i)))
+ return x;
+
+ /* Explicitly check if we are using COMMAND.COM. If we are
+ * then use the w9xpopen hack.
+ */
+ comshell = s1 + x;
+ while (comshell >= s1 && *comshell != '\\')
+ --comshell;
+ ++comshell;
+
+ if (GetVersion() < 0x80000000 &&
+ _stricmp(comshell, "command.com") != 0)
+ {
+ /* NT/2000 and not using command.com. */
+ x = i + strlen(s3) + strlen(cmdstring) + 1;
+ s2 = (char *)_alloca(x);
+ ZeroMemory(s2, x);
+ sprintf(s2, "%s%s%s", s1, s3, cmdstring);
+ //std::cout << "s1: " << s1 << " s2: " << s2 << " s3: " << s3
+ // << std::endl;
+ }
+ else
+ {
+ /*
+ * Oh gag, we're on Win9x or using COMMAND.COM. Use
+ * the workaround listed in KB: Q150956
+ */
+ char modulepath[_MAX_PATH];
+ struct stat statinfo;
+ GetModuleFileName(NULL, modulepath, sizeof(modulepath));
+ for (i = x = 0; modulepath[i]; i++)
+ if (modulepath[i] == '\\')
+ x = i+1;
+ modulepath[x] = '\0';
+ /* Create the full-name to w9xpopen, so we can test it exists */
+ strncat(modulepath,
+ szConsoleSpawn,
+ (sizeof(modulepath)/sizeof(modulepath[0]))
+ -strlen(modulepath));
+ if (stat(modulepath, &statinfo) != 0)
+ {
+ /* Eeek - file-not-found - possibly an embedding
+ situation - see if we can locate it in sys.prefix
+ */
+ strncpy(modulepath,
+ ".",
+ sizeof(modulepath)/sizeof(modulepath[0]));
+ if (modulepath[strlen(modulepath)-1] != '\\')
+ strcat(modulepath, "\\");
+ strncat(modulepath,
+ szConsoleSpawn,
+ (sizeof(modulepath)/sizeof(modulepath[0]))
+ -strlen(modulepath));
+ /* No where else to look - raise an easily identifiable
+ error, rather than leaving Windows to report
+ "file not found" - as the user is probably blissfully
+ unaware this shim EXE is used, and it will confuse them.
+ (well, it confused me for a while ;-)
+ */
+ if (stat(modulepath, &statinfo) != 0)
+ {
+ std::cout
+ << "Can not locate '" << szConsoleSpawn
+ << "' which is needed "
+ "for popen to work with your shell "
+ "or platform." << std::endl;
+ return FALSE;
+ }
+ }
+ x = i + strlen(s3) + strlen(cmdstring) + 1 +
+ strlen(modulepath) +
+ strlen(szConsoleSpawn) + 1;
+
+ s2 = (char *)_alloca(x);
+ ZeroMemory(s2, x);
+ sprintf(
+ s2,
+ "%s \"%s%s%s\"",
+ modulepath,
+ s1,
+ s3,
+ cmdstring);
+ }
+ }
+
+ /* Could be an else here to try cmd.exe / command.com in the path
+ Now we'll just error out.. */
+ else
+ {
+ std::cout << "Cannot locate a COMSPEC environment variable to "
+ << "use as the shell" << std::endl;
+ return FALSE;
+ }
+
+ ZeroMemory(&siStartInfo, sizeof(STARTUPINFO));
+ siStartInfo.cb = sizeof(STARTUPINFO);
+ siStartInfo.dwFlags = STARTF_USESTDHANDLES | STARTF_USESHOWWINDOW;
+ siStartInfo.hStdInput = hStdin;
+ siStartInfo.hStdOutput = hStdout;
+ siStartInfo.hStdError = hStderr;
+ siStartInfo.wShowWindow = SW_HIDE;
+
+ if (CreateProcess(NULL,
+ s2,
+ NULL,
+ NULL,
+ TRUE,
+ CREATE_NEW_CONSOLE,
+ NULL,
+ path,
+ &siStartInfo,
+ &piProcInfo) )
+ {
+ /* Close the handles now so anyone waiting is woken. */
+ CloseHandle(piProcInfo.hThread);
+
+ /* Return process handle */
+ *hProcess = piProcInfo.hProcess;
+ return TRUE;
+ }
+ win32_error("CreateProcess", s2);
+ return FALSE;
+}
+
+/* The following code is based off of KB: Q190351 */
+
+bool cmWin32ProcessExecution::PrivateOpen(const char *cmdstring,
+ const char* path,
+ int mode,
+ int n)
+{
+ HANDLE hChildStdinRd, hChildStdinWr, hChildStdoutRd, hChildStdoutWr,
+ hChildStderrRd, hChildStderrWr, hChildStdinWrDup, hChildStdoutRdDup,
+ hChildStderrRdDup, hProcess; /* hChildStdoutWrDup; */
+
+ SECURITY_ATTRIBUTES saAttr;
+ BOOL fSuccess;
+ int fd1, fd2, fd3;
+ FILE *f1, *f2, *f3;
+ long file_count;
+
+ saAttr.nLength = sizeof(SECURITY_ATTRIBUTES);
+ saAttr.bInheritHandle = TRUE;
+ saAttr.lpSecurityDescriptor = NULL;
+
+ if (!CreatePipe(&hChildStdinRd, &hChildStdinWr, &saAttr, 0))
+ return win32_error("CreatePipe", NULL);
+
+ /* Create new output read handle and the input write handle. Set
+ * the inheritance properties to FALSE. Otherwise, the child inherits
+ * the these handles; resulting in non-closeable handles to the pipes
+ * being created. */
+ fSuccess = DuplicateHandle(GetCurrentProcess(), hChildStdinWr,
+ GetCurrentProcess(), &hChildStdinWrDup, 0,
+ FALSE,
+ DUPLICATE_SAME_ACCESS);
+ if (!fSuccess)
+ return win32_error("DuplicateHandle", NULL);
+
+ /* Close the inheritable version of ChildStdin
+ that we're using. */
+ CloseHandle(hChildStdinWr);
+
+ if (!CreatePipe(&hChildStdoutRd, &hChildStdoutWr, &saAttr, 0))
+ return win32_error("CreatePipe", NULL);
+
+ fSuccess = DuplicateHandle(GetCurrentProcess(), hChildStdoutRd,
+ GetCurrentProcess(), &hChildStdoutRdDup, 0,
+ FALSE, DUPLICATE_SAME_ACCESS);
+ if (!fSuccess)
+ return win32_error("DuplicateHandle", NULL);
+
+ /* Close the inheritable version of ChildStdout
+ that we're using. */
+ CloseHandle(hChildStdoutRd);
+
+ if (n != POPEN_4)
+ {
+ if (!CreatePipe(&hChildStderrRd, &hChildStderrWr, &saAttr, 0))
+ return win32_error("CreatePipe", NULL);
+ fSuccess = DuplicateHandle(GetCurrentProcess(),
+ hChildStderrRd,
+ GetCurrentProcess(),
+ &hChildStderrRdDup, 0,
+ FALSE, DUPLICATE_SAME_ACCESS);
+ if (!fSuccess)
+ return win32_error("DuplicateHandle", NULL);
+ /* Close the inheritable version of ChildStdErr that we're using. */
+ CloseHandle(hChildStderrRd);
+ }
+
+ switch (n)
+ {
+ case POPEN_1:
+ switch (mode & (_O_RDONLY | _O_TEXT | _O_BINARY | _O_WRONLY))
+ {
+ case _O_WRONLY | _O_TEXT:
+ /* Case for writing to child Stdin in text mode. */
+ fd1 = _open_osfhandle((long)hChildStdinWrDup, mode);
+ f1 = _fdopen(fd1, "w");
+ /* We don't care about these pipes anymore,
+ so close them. */
+ CloseHandle(hChildStdoutRdDup);
+ CloseHandle(hChildStderrRdDup);
+ break;
+
+ case _O_RDONLY | _O_TEXT:
+ /* Case for reading from child Stdout in text mode. */
+ fd1 = _open_osfhandle((long)hChildStdoutRdDup, mode);
+ f1 = _fdopen(fd1, "r");
+ /* We don't care about these pipes anymore,
+ so close them. */
+ CloseHandle(hChildStdinWrDup);
+ CloseHandle(hChildStderrRdDup);
+ break;
+
+ case _O_RDONLY | _O_BINARY:
+ /* Case for readinig from child Stdout in
+ binary mode. */
+ fd1 = _open_osfhandle((long)hChildStdoutRdDup, mode);
+ f1 = _fdopen(fd1, "rb");
+ /* We don't care about these pipes anymore,
+ so close them. */
+ CloseHandle(hChildStdinWrDup);
+ CloseHandle(hChildStderrRdDup);
+ break;
+
+ case _O_WRONLY | _O_BINARY:
+ /* Case for writing to child Stdin in binary mode. */
+ fd1 = _open_osfhandle((long)hChildStdinWrDup, mode);
+ f1 = _fdopen(fd1, "wb");
+ /* We don't care about these pipes anymore,
+ so close them. */
+ CloseHandle(hChildStdoutRdDup);
+ CloseHandle(hChildStderrRdDup);
+ break;
+ }
+ file_count = 1;
+ break;
+
+ case POPEN_2:
+ case POPEN_4:
+ if ( 1 )
+ {
+ char *m1, *m2;
+
+ if (mode && _O_TEXT)
+ {
+ m1 = "r";
+ m2 = "w";
+ }
+ else
+ {
+ m1 = "rb";
+ m2 = "wb";
+ }
+
+ fd1 = _open_osfhandle((long)hChildStdinWrDup, mode);
+ f1 = _fdopen(fd1, m2);
+ fd2 = _open_osfhandle((long)hChildStdoutRdDup, mode);
+ f2 = _fdopen(fd2, m1);
+
+ if (n != 4)
+ CloseHandle(hChildStderrRdDup);
+
+ file_count = 2;
+ break;
+ }
+
+ case POPEN_3:
+ if ( 1)
+ {
+ char *m1, *m2;
+
+ if (mode && _O_TEXT)
+ {
+ m1 = "r";
+ m2 = "w";
+ }
+ else
+ {
+ m1 = "rb";
+ m2 = "wb";
+ }
+
+ fd1 = _open_osfhandle((long)hChildStdinWrDup, mode);
+ f1 = _fdopen(fd1, m2);
+ fd2 = _open_osfhandle((long)hChildStdoutRdDup, mode);
+ f2 = _fdopen(fd2, m1);
+ fd3 = _open_osfhandle((long)hChildStderrRdDup, mode);
+ f3 = _fdopen(fd3, m1);
+
+ file_count = 3;
+ break;
+ }
+ }
+
+ if (n == POPEN_4)
+ {
+ if (!RealPopenCreateProcess(cmdstring,
+ path,
+ this->m_ConsoleSpawn.c_str(),
+ hChildStdinRd,
+ hChildStdoutWr,
+ hChildStdoutWr,
+ &hProcess))
+ return NULL;
+ }
+ else
+ {
+ if (!RealPopenCreateProcess(cmdstring,
+ path,
+ this->m_ConsoleSpawn.c_str(),
+ hChildStdinRd,
+ hChildStdoutWr,
+ hChildStderrWr,
+ &hProcess))
+ return NULL;
+ }
+
+ /*
+ * Insert the files we've created into the process dictionary
+ * all referencing the list with the process handle and the
+ * initial number of files (see description below in _PyPclose).
+ * Since if _PyPclose later tried to wait on a process when all
+ * handles weren't closed, it could create a deadlock with the
+ * child, we spend some energy here to try to ensure that we
+ * either insert all file handles into the dictionary or none
+ * at all. It's a little clumsy with the various popen modes
+ * and variable number of files involved.
+ */
+
+ /* Child is launched. Close the parents copy of those pipe
+ * handles that only the child should have open. You need to
+ * make sure that no handles to the write end of the output pipe
+ * are maintained in this process or else the pipe will not close
+ * when the child process exits and the ReadFile will hang. */
+
+ if (!CloseHandle(hChildStdinRd))
+ return win32_error("CloseHandle", NULL);
+
+ if (!CloseHandle(hChildStdoutWr))
+ return win32_error("CloseHandle", NULL);
+
+ if ((n != 4) && (!CloseHandle(hChildStderrWr)))
+ return win32_error("CloseHandle", NULL);
+
+ this->m_ProcessHandle = hProcess;
+ if ( f1 )
+ {
+ this->m_StdIn = f1;
+ this->m_pStdIn = fd1;
+ }
+ if ( f2 )
+ {
+ this->m_StdOut = f2;
+ this->m_pStdOut = fd2;
+ }
+ if ( f3 )
+ {
+ this->m_StdErr = f3;
+ this->m_pStdErr = fd3;
+ }
+
+ return true;
+}
+
+/*
+ * Wrapper for fclose() to use for popen* files, so we can retrieve the
+ * exit code for the child process and return as a result of the close.
+ *
+ * This function uses the _PyPopenProcs dictionary in order to map the
+ * input file pointer to information about the process that was
+ * originally created by the popen* call that created the file pointer.
+ * The dictionary uses the file pointer as a key (with one entry
+ * inserted for each file returned by the original popen* call) and a
+ * single list object as the value for all files from a single call.
+ * The list object contains the Win32 process handle at [0], and a file
+ * count at [1], which is initialized to the total number of file
+ * handles using that list.
+ *
+ * This function closes whichever handle it is passed, and decrements
+ * the file count in the dictionary for the process handle pointed to
+ * by this file. On the last close (when the file count reaches zero),
+ * this function will wait for the child process and then return its
+ * exit code as the result of the close() operation. This permits the
+ * files to be closed in any order - it is always the close() of the
+ * final handle that will return the exit code.
+ */
+
+ /* RED_FLAG 31-Aug-2000 Tim
+ * This is always called (today!) between a pair of
+ * Py_BEGIN_ALLOW_THREADS/ Py_END_ALLOW_THREADS
+ * macros. So the thread running this has no valid thread state, as
+ * far as Python is concerned. However, this calls some Python API
+ * functions that cannot be called safely without a valid thread
+ * state, in particular PyDict_GetItem.
+ * As a temporary hack (although it may last for years ...), we
+ * *rely* on not having a valid thread state in this function, in
+ * order to create our own "from scratch".
+ * This will deadlock if _PyPclose is ever called by a thread
+ * holding the global lock.
+ */
+
+bool cmWin32ProcessExecution::PrivateClose(int timeout)
+{
+ HANDLE hProcess = this->m_ProcessHandle;
+
+ int result = -1;
+ DWORD exit_code;
+
+ std::string output = "";
+ bool done = false;
+ int stdoutloc = 0;
+ int stderrloc = 0;
+ while(!done)
+ {
+ Sleep(10);
+ struct _stat fsout;
+ struct _stat fserr;
+ int rout = _fstat(this->m_pStdOut, &fsout);
+ int rerr = _fstat(this->m_pStdErr, &fserr);
+ if ( rout && rerr )
+ {
+ break;
+ }
+ if (fserr.st_size > 0)
+ {
+ int dist = fserr.st_size;
+ char buffer[1023];
+ int len = read(this->m_pStdErr, buffer, 1023);
+ buffer[len] = 0;
+ if ( this->m_Verbose )
+ {
+ std::cout << buffer << std::flush;
+ }
+ output += buffer;
+ }
+ if (fsout.st_size > 0)
+ {
+ int dist = fsout.st_size;
+ char buffer[1023];
+ int len = read(this->m_pStdOut, buffer, 1023);
+ buffer[len] = 0;
+ if ( this->m_Verbose )
+ {
+ std::cout << buffer << std::flush;
+ }
+ output += buffer;
+ }
+ unsigned long exitCode;
+ GetExitCodeProcess(hProcess,&exitCode);
+ if (exitCode != STILL_ACTIVE)
+ {
+ break;
+ }
+ }
+
+ if (WaitForSingleObject(hProcess, INFINITE) != WAIT_FAILED &&
+ GetExitCodeProcess(hProcess, &exit_code))
+ {
+ result = exit_code;
+ }
+ else
+ {
+ /* Indicate failure - this will cause the file object
+ * to raise an I/O error and translate the last Win32
+ * error code from errno. We do have a problem with
+ * last errors that overlap the normal errno table,
+ * but that's a consistent problem with the file object.
+ */
+ if (result != EOF)
+ {
+ /* If the error wasn't from the fclose(), then
+ * set errno for the file object error handling.
+ */
+ errno = GetLastError();
+ }
+ result = -1;
+ }
+
+ /* Free up the native handle at this point */
+ CloseHandle(hProcess);
+ this->m_ExitValue = result;
+ this->m_Output = output;
+ if ( result < 0 )
+ {
+ return false;
+ }
+ return true;
+}
+
+int cmWin32ProcessExecution::Windows9xHack(const char* command)
+{
+ BOOL bRet;
+ STARTUPINFO si;
+ PROCESS_INFORMATION pi;
+ DWORD exit_code=0;
+
+ if (!command)
+ {
+ cmSystemTools::Error("Windows9xHack: Command not specified");
+ return 1;
+ }
+
+ /* Make child process use this app's standard files. */
+ ZeroMemory(&si, sizeof si);
+ si.cb = sizeof si;
+ si.dwFlags = STARTF_USESTDHANDLES;
+ si.hStdInput = GetStdHandle(STD_INPUT_HANDLE);
+ si.hStdOutput = GetStdHandle(STD_OUTPUT_HANDLE);
+ si.hStdError = GetStdHandle(STD_ERROR_HANDLE);
+
+ char* cmd = new char[ strlen(command) + 1 ];
+ strcpy(cmd, command);
+
+ bRet = CreateProcess(
+ NULL, cmd,
+ NULL, NULL,
+ TRUE, 0,
+ NULL, NULL,
+ &si, &pi
+ );
+ delete [] cmd;
+
+ if (bRet)
+ {
+ if (WaitForSingleObject(pi.hProcess, INFINITE) != WAIT_FAILED)
+ {
+ GetExitCodeProcess(pi.hProcess, &exit_code);
+ }
+ CloseHandle(pi.hProcess);
+ CloseHandle(pi.hThread);
+ return exit_code;
+ }
+
+ return 1;
+}
diff --git a/Source/cmWin32ProcessExecution.h b/Source/cmWin32ProcessExecution.h
new file mode 100644
index 0000000..acc214b
--- /dev/null
+++ b/Source/cmWin32ProcessExecution.h
@@ -0,0 +1,85 @@
+/*=========================================================================
+
+ Program: Insight Segmentation & Registration Toolkit
+ Module: $RCSfile$
+ Language: C++
+ Date: $Date$
+ Version: $Revision$
+
+ Copyright (c) 2002 Insight Consortium. All rights reserved.
+ See ITKCopyright.txt or http://www.itk.org/HTML/Copyright.htm for details.
+
+ This software is distributed WITHOUT ANY WARRANTY; without even
+ the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR
+ PURPOSE. See the above copyright notices for more information.
+
+=========================================================================*/
+#ifndef cmWin32ProcessExecution_h
+#define cmWin32ProcessExecution_h
+
+/*
+ * Portable 'popen' replacement for Win32.
+ *
+ * Written by Bill Tutt <billtut@microsoft.com>. Minor tweaks
+ * and 2.0 integration by Fredrik Lundh <fredrik@pythonware.com>
+ * Return code handling by David Bolen <db3l@fitlinxx.com>.
+ */
+
+#include "cmStandardIncludes.h"
+#include "windows.h"
+
+class cmMakefile;
+
+/** \class cmWin32ProcessExecution
+ * \brief A process executor for windows
+ *
+ * cmWin32ProcessExecution is a class that provides a "clean" way of
+ * executing processes on Windows.
+ */
+class cmWin32ProcessExecution
+{
+public:
+ cmWin32ProcessExecution()
+ {
+ this->SetConsoleSpawn("w9xpopen.exe");
+ this->Initialize();
+ }
+ void Initialize()
+ {
+ this->m_ProcessHandle = 0;
+ this->m_ExitValue = -1;
+ this->m_StdIn = 0;
+ this->m_StdOut = 0;
+ this->m_StdErr = 0;
+ }
+ bool StartProcess(const char*, const char* path, bool verbose);
+ bool Wait(int timeout);
+ const std::string GetOutput() const { return this->m_Output; }
+ int GetExitValue() const { return this->m_ExitValue; }
+
+ void SetConsoleSpawn(const char* prog) { this->m_ConsoleSpawn = prog; }
+
+ static int Windows9xHack(const char* command);
+
+private:
+ bool PrivateOpen(const char*, const char*, int, int);
+ bool PrivateClose(int timeout);
+
+ HANDLE m_ProcessHandle;
+ FILE* m_StdIn;
+ FILE* m_StdOut;
+ FILE* m_StdErr;
+
+ int m_pStdIn;
+ int m_pStdOut;
+ int m_pStdErr;
+
+ int m_ExitValue;
+
+ std::string m_Output;
+ std::string m_ConsoleSpawn;
+ bool m_Verbose;
+};
+
+
+#endif