@@ -31,7 +31,7 @@ bool ProcessExecute::isWindowsNT()
3131 return (osv.dwPlatformId >= VER_PLATFORM_WIN32_NT);
3232}
3333
34- long ProcessExecute::execute (const TCHAR *commandLine, boost::python::object pyStdout, boost::python::object pyStderr, boost::python::object pyStdin)
34+ long ProcessExecute::execute (const TCHAR *commandLine, boost::python::object pyStdout, boost::python::object pyStderr, boost::python::object pyStdin, bool spoolToFile /* = false */ )
3535{
3636 DWORD returnValue = 0 ;
3737
@@ -44,7 +44,13 @@ long ProcessExecute::execute(const TCHAR *commandLine, boost::python::object pyS
4444 SECURITY_DESCRIPTOR sd;
4545 SECURITY_ATTRIBUTES sa;
4646 process_start_exception exceptionThrown (" " );
47+
4748 bool thrown = false ;
49+
50+ PipeReaderArgs stdoutReaderArgs;
51+ PipeReaderArgs stderrReaderArgs;
52+ // Only used if spooling, but we need to delete it later.
53+ TCHAR tmpFilename[MAX_PATH];
4854
4955 Py_BEGIN_ALLOW_THREADS
5056 try
@@ -74,22 +80,57 @@ long ProcessExecute::execute(const TCHAR *commandLine, boost::python::object pyS
7480
7581 HANDLE stopEvent = CreateEvent (NULL , TRUE , FALSE , NULL );
7682 DWORD dwThreadId;
77- PipeReaderArgs stdoutReaderArgs;
78- PipeReaderArgs stderrReaderArgs;
83+
7984
8085 stdoutReaderArgs.processExecute = this ;
8186 stdoutReaderArgs.hPipeRead = m_hStdOutReadPipe;
8287 stdoutReaderArgs.hPipeWrite = m_hStdOutWritePipe;
8388 stdoutReaderArgs.pythonFile = pyStdout;
8489 stdoutReaderArgs.stopEvent = stopEvent;
8590 stdoutReaderArgs.completedEvent = CreateEvent (NULL , FALSE , FALSE , NULL );
91+ stdoutReaderArgs.streamName = " STDOUT" ;
8692 stderrReaderArgs.processExecute = this ;
8793 stderrReaderArgs.hPipeRead = m_hStdErrReadPipe;
8894 stderrReaderArgs.hPipeWrite = m_hStdErrWritePipe;
8995 stderrReaderArgs.stopEvent = stopEvent;
9096 stderrReaderArgs.pythonFile = pyStderr;
97+ stderrReaderArgs.streamName = " STDERR" ;
98+
9199 stderrReaderArgs.completedEvent = CreateEvent (NULL , FALSE , FALSE , NULL );
92100
101+
102+
103+ /* If we're in an event, we need to spool the output to a file
104+ * first, before we write it to python - so that the python writing is
105+ * done in *this* thread
106+ */
107+ if (spoolToFile)
108+ {
109+ stdoutReaderArgs.toFile = true ;
110+ stderrReaderArgs.toFile = true ;
111+
112+ // / Create the mutex for writing to the file
113+ stdoutReaderArgs.fileMutex = CreateMutex (NULL , FALSE , NULL );
114+ stderrReaderArgs.fileMutex = stdoutReaderArgs.fileMutex ;
115+
116+ // / Create the temp file
117+ TCHAR tmpPath[MAX_PATH];
118+
119+ GetTempPath (MAX_PATH, tmpPath);
120+ if (!GetTempFileName (tmpPath, _T (" py" ), 0 , tmpFilename))
121+ {
122+ throw process_start_exception (" Error creating temporary filename for output spooling" );
123+ }
124+
125+ stdoutReaderArgs.fileHandle = CreateFile (tmpFilename, GENERIC_READ | GENERIC_WRITE, 0 , NULL , CREATE_ALWAYS, FILE_ATTRIBUTE_NORMAL, NULL );
126+ stderrReaderArgs.fileHandle = stdoutReaderArgs.fileHandle ;
127+ if (INVALID_HANDLE_VALUE == stdoutReaderArgs.fileHandle )
128+ {
129+ throw process_start_exception (" Error opening temporary file for output spooling" );
130+ }
131+
132+
133+ }
93134 // start thread functions for stdout and stderr
94135 HANDLE hStdoutThread = CreateThread (
95136 NULL , // no security attribute
@@ -199,6 +240,15 @@ long ProcessExecute::execute(const TCHAR *commandLine, boost::python::object pyS
199240
200241 Py_END_ALLOW_THREADS
201242
243+ if (spoolToFile)
244+ {
245+ CloseHandle (stdoutReaderArgs.fileHandle );
246+ CloseHandle (stdoutReaderArgs.fileMutex );
247+ pyStdout.attr (" write" )(boost::python::str (" File written" ));
248+
249+ pyStdout.attr (" write" )(boost::python::str (const_cast <const char *>(WcharMbcsConverter::tchar2char (tmpFilename).get ())));
250+ }
251+
202252 if (thrown)
203253 {
204254 throw exceptionThrown;
@@ -240,17 +290,16 @@ DWORD WINAPI ProcessExecute::pipeReader(void *args)
240290 if (bytesRead > 0 )
241291 {
242292 success = ReadFile (pipeReaderArgs->hPipeRead , buffer, PIPE_READBUFSIZE - 1 , &bytesRead, NULL );
243- buffer[bytesRead] = ' \0 ' ;
244- PyGILState_STATE gstate = PyGILState_Ensure ();
245- try
293+
294+ if (pipeReaderArgs->toFile )
246295 {
247- pipeReaderArgs->pythonFile . attr ( " write " )( boost::python::str ( const_cast < const char *>( buffer)) );
296+ pipeReaderArgs->processExecute -> writeToFile (pipeReaderArgs, bytesRead, buffer);
248297 }
249- catch (...)
298+ else
250299 {
251- PyErr_Print ( );
300+ pipeReaderArgs-> processExecute -> writeToPython (pipeReaderArgs, bytesRead, buffer );
252301 }
253- PyGILState_Release (gstate);
302+
254303 }
255304 else
256305 {
@@ -275,3 +324,40 @@ DWORD WINAPI ProcessExecute::pipeReader(void *args)
275324
276325 return 0 ;
277326}
327+
328+ void ProcessExecute::writeToPython (PipeReaderArgs *pipeReaderArgs, int bytesRead, char *buffer)
329+ {
330+ buffer[bytesRead] = ' \0 ' ;
331+ PyGILState_STATE gstate = PyGILState_Ensure ();
332+ try
333+ {
334+ pipeReaderArgs->pythonFile .attr (" write" )(boost::python::str (const_cast <const char *>(buffer)));
335+ }
336+ catch (...)
337+ {
338+ PyErr_Print ();
339+ }
340+ PyGILState_Release (gstate);
341+ }
342+
343+
344+ void ProcessExecute::writeToFile (PipeReaderArgs *pipeReaderArgs, int bytesRead, char *buffer)
345+ {
346+ WaitForSingleObject (pipeReaderArgs->fileMutex , INFINITE);
347+ DWORD written;
348+ WriteFile (pipeReaderArgs->fileHandle , pipeReaderArgs->streamName , ProcessExecute::STREAM_NAME_LENGTH, &written, NULL );
349+
350+ if (written != 6 )
351+ throw process_start_exception (" Error writing to spool file" );
352+
353+ char byteCount[20 ];
354+ _itoa_s (bytesRead, byteCount, 20 , 10 );
355+ strcat_s (byteCount, 20 , " \n " );
356+
357+ WriteFile (pipeReaderArgs->fileHandle , byteCount, strlen (byteCount), &written, NULL );
358+ WriteFile (pipeReaderArgs->fileHandle , buffer, bytesRead, &written, NULL );
359+ if (written != bytesRead)
360+ throw process_start_exception (" Error writing buffer to spool file" );
361+
362+ ReleaseMutex (pipeReaderArgs->fileMutex );
363+ }
0 commit comments