|
| 1 | +#include "stdafx.h" |
| 2 | +#include "ProcessExecute.h" |
| 3 | + |
| 4 | +/* The Console Redirection is taken from the TagsView plugin from Vitaliy Dovgan. |
| 5 | + * My thanks to him for pointing me in the right direction. :) |
| 6 | + * And of course for NppExec, without which, Notepad++ would only |
| 7 | + * be half as powerful. |
| 8 | + */ |
| 9 | + |
| 10 | +#define DEFAULT_PIPE_SIZE 1 |
| 11 | +#define PIPE_READBUFSIZE 4096 |
| 12 | + |
| 13 | +using namespace boost::python; |
| 14 | + |
| 15 | +ProcessExecute::ProcessExecute() |
| 16 | +{ |
| 17 | +} |
| 18 | + |
| 19 | +ProcessExecute::~ProcessExecute() |
| 20 | +{ |
| 21 | +} |
| 22 | + |
| 23 | + |
| 24 | +bool ProcessExecute::isWindowsNT() |
| 25 | +{ |
| 26 | + OSVERSIONINFO osv; |
| 27 | + osv.dwOSVersionInfoSize = sizeof(osv); |
| 28 | + ::GetVersionEx(&osv); |
| 29 | + return (osv.dwPlatformId >= VER_PLATFORM_WIN32_NT); |
| 30 | +} |
| 31 | + |
| 32 | +int ProcessExecute::execute(const TCHAR *commandLine, boost::python::object pyStdout, boost::python::object pyStderr, boost::python::object pyStdin) |
| 33 | +{ |
| 34 | + if (pyStdout.is_none()) |
| 35 | + return 3; |
| 36 | + if (pyStderr.is_none()) |
| 37 | + return 4; |
| 38 | + |
| 39 | + // Create out, err, and in pipes (ignore in, initially) |
| 40 | + SECURITY_DESCRIPTOR sd; |
| 41 | + SECURITY_ATTRIBUTES sa; |
| 42 | + |
| 43 | + Py_BEGIN_ALLOW_THREADS |
| 44 | + |
| 45 | + if (isWindowsNT()) |
| 46 | + { |
| 47 | + ::InitializeSecurityDescriptor( &sd, SECURITY_DESCRIPTOR_REVISION ); |
| 48 | + ::SetSecurityDescriptorDacl( &sd, TRUE, NULL, FALSE ); |
| 49 | + sa.lpSecurityDescriptor = &sd; |
| 50 | + } |
| 51 | + else |
| 52 | + { |
| 53 | + sa.lpSecurityDescriptor = NULL; |
| 54 | + } |
| 55 | + sa.nLength = sizeof(SECURITY_ATTRIBUTES); |
| 56 | + sa.bInheritHandle = TRUE; |
| 57 | + |
| 58 | + if (!::CreatePipe(&m_hStdOutReadPipe, &m_hStdOutWritePipe, &sa, DEFAULT_PIPE_SIZE)) |
| 59 | + { |
| 60 | + // TODO throw exception |
| 61 | + return -1; |
| 62 | + } |
| 63 | + |
| 64 | + if (!::CreatePipe(&m_hStdErrReadPipe, &m_hStdErrWritePipe, &sa, DEFAULT_PIPE_SIZE)) |
| 65 | + { |
| 66 | + // TODO throw exception |
| 67 | + return -1; |
| 68 | + } |
| 69 | + |
| 70 | + HANDLE stopEvent = CreateEvent(NULL, TRUE, FALSE, NULL); |
| 71 | + DWORD dwThreadId; |
| 72 | + PipeReaderArgs stdoutReaderArgs; |
| 73 | + PipeReaderArgs stderrReaderArgs; |
| 74 | + |
| 75 | + stdoutReaderArgs.processExecute = this; |
| 76 | + stdoutReaderArgs.hPipeRead = m_hStdOutReadPipe; |
| 77 | + stdoutReaderArgs.hPipeWrite = m_hStdOutWritePipe; |
| 78 | + stdoutReaderArgs.pythonFile = pyStdout; |
| 79 | + stdoutReaderArgs.stopEvent = stopEvent; |
| 80 | + stdoutReaderArgs.completedEvent = CreateEvent(NULL, FALSE, FALSE, NULL); |
| 81 | + stderrReaderArgs.processExecute = this; |
| 82 | + stderrReaderArgs.hPipeRead = m_hStdErrReadPipe; |
| 83 | + stderrReaderArgs.hPipeWrite = m_hStdErrWritePipe; |
| 84 | + stderrReaderArgs.stopEvent = stopEvent; |
| 85 | + stderrReaderArgs.pythonFile = pyStderr; |
| 86 | + stderrReaderArgs.completedEvent = CreateEvent(NULL, FALSE, FALSE, NULL); |
| 87 | + |
| 88 | + // start thread functions for stdout and stderr |
| 89 | + HANDLE hStdoutThread = CreateThread( |
| 90 | + NULL, // no security attribute |
| 91 | + 0, // default stack size |
| 92 | + pipeReader, // thread proc |
| 93 | + (LPVOID) &stdoutReaderArgs, // thread parameter |
| 94 | + 0, // not suspended |
| 95 | + &dwThreadId); // returns thread ID |
| 96 | + /* |
| 97 | + HANDLE hStderrThread = CreateThread( |
| 98 | + NULL, // no security attribute |
| 99 | + 0, // default stack size |
| 100 | + pipeReader, // thread proc |
| 101 | + (LPVOID) &stderrReaderArgs, // thread parameter |
| 102 | + 0, // not suspended |
| 103 | + &dwThreadId); // returns thread ID |
| 104 | + */ |
| 105 | + |
| 106 | + |
| 107 | + // start process |
| 108 | + PROCESS_INFORMATION pi; |
| 109 | + STARTUPINFO si; |
| 110 | + |
| 111 | + |
| 112 | + ::SetHandleInformation(m_hStdOutReadPipe, HANDLE_FLAG_INHERIT, 0); |
| 113 | + ::SetHandleInformation(m_hStdErrReadPipe, HANDLE_FLAG_INHERIT, 0); |
| 114 | + |
| 115 | + // initialize STARTUPINFO struct |
| 116 | + ::ZeroMemory( &si, sizeof(STARTUPINFO) ); |
| 117 | + si.cb = sizeof(STARTUPINFO); |
| 118 | + si.dwFlags = STARTF_USESHOWWINDOW | STARTF_USESTDHANDLES; |
| 119 | + si.wShowWindow = SW_HIDE; |
| 120 | + si.hStdInput = NULL; |
| 121 | + si.hStdOutput = m_hStdOutWritePipe; |
| 122 | + si.hStdError = m_hStdOutWritePipe; |
| 123 | + |
| 124 | + ::ZeroMemory( &pi, sizeof(PROCESS_INFORMATION) ); |
| 125 | + int commandLineLength = _tcslen(commandLine) + 1; |
| 126 | + TCHAR *cmdLine = new TCHAR[commandLineLength]; |
| 127 | + _tcscpy_s(cmdLine, commandLineLength, commandLine); |
| 128 | + |
| 129 | + if ( ::CreateProcess( |
| 130 | + NULL, |
| 131 | + cmdLine, |
| 132 | + NULL, // security |
| 133 | + NULL, // security |
| 134 | + TRUE, // inherits handles |
| 135 | + CREATE_NEW_PROCESS_GROUP, // creation flags |
| 136 | + NULL, // environment |
| 137 | + NULL, // current directory |
| 138 | + &si, // startup info |
| 139 | + &pi // process info |
| 140 | + )) |
| 141 | + { |
| 142 | + // wait for process to exit |
| 143 | + ::WaitForSingleObject(pi.hProcess, INFINITE); |
| 144 | + |
| 145 | + } |
| 146 | + |
| 147 | + |
| 148 | + // Abort / Stop somehow the reader threads |
| 149 | + SetEvent(stopEvent); |
| 150 | + HANDLE handles[] = { stdoutReaderArgs.completedEvent, stderrReaderArgs.completedEvent }; |
| 151 | + |
| 152 | + // WaitForMultipleObjects(2, handles, TRUE, INFINITE); |
| 153 | + int returnedIndex = WaitForSingleObject(stdoutReaderArgs.completedEvent, INFINITE); |
| 154 | + Py_END_ALLOW_THREADS |
| 155 | + return 0; |
| 156 | +} |
| 157 | + |
| 158 | + |
| 159 | +DWORD WINAPI ProcessExecute::pipeReader(void *args) |
| 160 | +{ |
| 161 | + PipeReaderArgs* pipeReaderArgs = reinterpret_cast<PipeReaderArgs*>(args); |
| 162 | + |
| 163 | + DWORD bytesRead; |
| 164 | + |
| 165 | + |
| 166 | + char buffer[PIPE_READBUFSIZE]; |
| 167 | + BOOL success = TRUE; |
| 168 | + BOOL processFinished = FALSE; |
| 169 | + BOOL dataFinished = FALSE; |
| 170 | + OVERLAPPED oOverlap; |
| 171 | + oOverlap.hEvent = CreateEvent(NULL, TRUE, TRUE, NULL); |
| 172 | + HANDLE handles[2]; |
| 173 | + handles[0] = pipeReaderArgs->stopEvent; |
| 174 | + int handleIndex; |
| 175 | + |
| 176 | + while(!dataFinished) |
| 177 | + { |
| 178 | + ::PeekNamedPipe(pipeReaderArgs->hPipeRead, NULL, 0, NULL, &bytesRead, NULL); |
| 179 | + |
| 180 | + if (processFinished && 0 == bytesRead) |
| 181 | + { |
| 182 | + dataFinished = TRUE; |
| 183 | + break; |
| 184 | + } |
| 185 | + |
| 186 | + |
| 187 | + |
| 188 | + if (bytesRead > 0) |
| 189 | + { |
| 190 | + success = ReadFile(pipeReaderArgs->hPipeRead, buffer, PIPE_READBUFSIZE - 1, &bytesRead, NULL); |
| 191 | + buffer[bytesRead] = '\0'; |
| 192 | + PyGILState_STATE gstate = PyGILState_Ensure(); |
| 193 | + try |
| 194 | + { |
| 195 | + pipeReaderArgs->pythonFile.attr("write")(boost::python::str(const_cast<const char *>(buffer))); |
| 196 | + } |
| 197 | + catch(...) |
| 198 | + { |
| 199 | + PyErr_Print(); |
| 200 | + } |
| 201 | + PyGILState_Release(gstate); |
| 202 | + //handleIndex = WaitForMultipleObjects(2, handles, FALSE, 100); |
| 203 | + } |
| 204 | + else |
| 205 | + { |
| 206 | + handleIndex = WaitForSingleObject(pipeReaderArgs->stopEvent, 100); |
| 207 | + } |
| 208 | + |
| 209 | + switch(handleIndex) |
| 210 | + { |
| 211 | + case WAIT_OBJECT_0: |
| 212 | + // Process Stopped |
| 213 | + { |
| 214 | + processFinished = TRUE; |
| 215 | + } |
| 216 | + break; |
| 217 | + |
| 218 | + |
| 219 | + } |
| 220 | + } |
| 221 | + |
| 222 | + CloseHandle(pipeReaderArgs->hPipeRead); |
| 223 | + CloseHandle(pipeReaderArgs->hPipeWrite); |
| 224 | + SetEvent(pipeReaderArgs->completedEvent); |
| 225 | + |
| 226 | + return 0; |
| 227 | +} |
0 commit comments