foobar2000 SDK  2015-08-03
ProcessUtils.h
Go to the documentation of this file.
1 #ifdef _WIN32
2 
3 namespace ProcessUtils {
4  class PipeIO : public stream_reader, public stream_writer {
5  public:
6  PFC_DECLARE_EXCEPTION(timeout, exception_io, "Timeout");
7  PipeIO(HANDLE handle, HANDLE hEvent, bool processMessages, DWORD timeOut = INFINITE) : m_handle(handle), m_event(hEvent), m_processMessages(processMessages), m_timeOut(timeOut) {
8  }
9  ~PipeIO() {
10  }
11 
12  void write(const void * p_buffer,size_t p_bytes, abort_callback & abort) {
13  if (p_bytes == 0) return;
14  OVERLAPPED ol = {};
15  ol.hEvent = m_event;
16  ResetEvent(m_event);
17  DWORD bytesWritten;
18  SetLastError(NO_ERROR);
19  if (WriteFile( m_handle, p_buffer, pfc::downcast_guarded<DWORD>(p_bytes), &bytesWritten, &ol)) {
20  // succeeded already?
21  if (bytesWritten != p_bytes) throw exception_io();
22  return;
23  }
24 
25  {
26  const DWORD code = GetLastError();
27  if (code != ERROR_IO_PENDING) exception_io_from_win32(code);
28  }
29  const HANDLE handles[] = {m_event, abort.get_abort_event()};
30  SetLastError(NO_ERROR);
31  DWORD state = myWait(_countof(handles), handles);
32  if (state == WAIT_OBJECT_0) {
33  try {
34  WIN32_IO_OP( GetOverlappedResult(m_handle,&ol,&bytesWritten,TRUE) );
35  } catch(...) {
36  _cancel(ol);
37  throw;
38  }
39  if (bytesWritten != p_bytes) throw exception_io();
40  return;
41  }
42  _cancel(ol);
43  abort.check();
44  throw timeout();
45  }
46  size_t read(void * p_buffer,size_t p_bytes, abort_callback & abort) {
47  uint8_t * ptr = (uint8_t*) p_buffer;
48  size_t done = 0;
49  while(done < p_bytes) {
50  abort.check();
51  size_t delta = readPass(ptr + done, p_bytes - done, abort);
52  if (delta == 0) break;
53  done += delta;
54  }
55  return done;
56  }
57  size_t readPass(void * p_buffer,size_t p_bytes, abort_callback & abort) {
58  if (p_bytes == 0) return 0;
59  OVERLAPPED ol = {};
60  ol.hEvent = m_event;
61  ResetEvent(m_event);
62  DWORD bytesDone;
63  SetLastError(NO_ERROR);
64  if (ReadFile( m_handle, p_buffer, pfc::downcast_guarded<DWORD>(p_bytes), &bytesDone, &ol)) {
65  // succeeded already?
66  return bytesDone;
67  }
68 
69  {
70  const DWORD code = GetLastError();
71  switch(code) {
72  case ERROR_HANDLE_EOF:
73  return 0;
74  case ERROR_IO_PENDING:
75  break; // continue
76  default:
78  };
79  }
80 
81  const HANDLE handles[] = {m_event, abort.get_abort_event()};
82  SetLastError(NO_ERROR);
83  DWORD state = myWait(_countof(handles), handles);
84  if (state == WAIT_OBJECT_0) {
85  SetLastError(NO_ERROR);
86  if (!GetOverlappedResult(m_handle,&ol,&bytesDone,TRUE)) {
87  const DWORD code = GetLastError();
88  if (code == ERROR_HANDLE_EOF) bytesDone = 0;
89  else {
90  _cancel(ol);
92  }
93  }
94  return bytesDone;
95  }
96  _cancel(ol);
97  abort.check();
98  throw timeout();
99  }
100  private:
101  DWORD myWait(DWORD count, const HANDLE * handles) {
102  if (m_processMessages) {
103  for(;;) {
104  DWORD state = MsgWaitForMultipleObjects(count, handles, FALSE, m_timeOut, QS_ALLINPUT);
105  if (state == WAIT_OBJECT_0 + count) {
107  } else {
108  return state;
109  }
110  }
111  } else {
112  return WaitForMultipleObjects(count, handles, FALSE, m_timeOut);
113  }
114  }
115  void _cancel(OVERLAPPED & ol) {
116  #if _WIN32_WINNT >= 0x600
117  CancelIoEx(m_handle,&ol);
118  #else
119  CancelIo(m_handle);
120  #endif
121  }
122  static void ProcessPendingMessages() {
123  MSG msg = {};
124  while(PeekMessage(&msg, NULL, 0, 0, PM_REMOVE)) DispatchMessage(&msg);
125  }
126 
129  const DWORD m_timeOut;
130  const bool m_processMessages;
131  };
132 
133  class SubProcess : public stream_reader, public stream_writer {
134  public:
135  PFC_DECLARE_EXCEPTION(failure, std::exception, "Unexpected failure");
136 
137  SubProcess(const char * exePath, DWORD timeOutMS = 60*1000) : ExePath(exePath), hStdIn(), hStdOut(), hProcess(), ProcessMessages(false), TimeOutMS(timeOutMS) {
138  HANDLE ev;
139  WIN32_OP( (ev = CreateEvent(NULL, TRUE, FALSE, NULL)) != NULL );
140  hEventRead = ev;
141  WIN32_OP( (ev = CreateEvent(NULL, TRUE, FALSE, NULL)) != NULL );
142  hEventWrite = ev;
143  Restart();
144  }
145  void Restart() {
146  CleanUp();
147  STARTUPINFO si = {};
148  try {
149  si.cb = sizeof(si);
150  si.dwFlags = STARTF_USESTDHANDLES | STARTF_FORCEOFFFEEDBACK;
151  //si.wShowWindow = SW_HIDE;
152 
153  myCreatePipeOut(si.hStdInput, hStdIn);
154  myCreatePipeIn(hStdOut, si.hStdOutput);
155  si.hStdError = GetStdHandle(STD_ERROR_HANDLE);
156 
157  PROCESS_INFORMATION pi = {};
158  try {
159  WIN32_OP( CreateProcess(pfc::stringcvt::string_os_from_utf8(ExePath), NULL, NULL, NULL, TRUE, 0, NULL, NULL, &si, &pi) );
160  } catch(std::exception const & e) {
161  throw failure(pfc::string_formatter() << "Could not start the worker process - " << e);
162  }
163  hProcess = pi.hProcess; _Close(pi.hThread);
164  } catch(...) {
165  _Close(si.hStdInput);
166  _Close(si.hStdOutput);
167  CleanUp(); throw;
168  }
169  _Close(si.hStdInput);
170  _Close(si.hStdOutput);
171  }
173  CleanUp();
174  CloseHandle(hEventRead);
175  CloseHandle(hEventWrite);
176  }
177 
178  bool IsRunning() const {
179  return hProcess != NULL;
180  }
181  void Detach() {
182  CleanUp(true);
183  }
185  DWORD TimeOutMS;
186 
187 
188  void write(const void * p_buffer,size_t p_bytes, abort_callback & abort) {
189  PipeIO writer(hStdIn, hEventWrite, ProcessMessages, TimeOutMS);
190  writer.write(p_buffer, p_bytes, abort);
191  }
192  size_t read(void * p_buffer,size_t p_bytes, abort_callback & abort) {
193  PipeIO reader(hStdOut, hEventRead, ProcessMessages, TimeOutMS);
194  return reader.read(p_buffer, p_bytes, abort);
195  }
196  void SetPriority(DWORD val) {
197  SetPriorityClass(hProcess, val);
198  }
199  protected:
200  HANDLE hStdIn, hStdOut, hProcess, hEventRead, hEventWrite;
202 
203  void CleanUp(bool bDetach = false) {
204  _Close(hStdIn); _Close(hStdOut);
205  if (hProcess != NULL) {
206  if (!bDetach) {
207  if (WaitForSingleObject(hProcess, TimeOutMS) != WAIT_OBJECT_0) {
208  //PFC_ASSERT( !"Should not get here - worker stuck" );
209  console::formatter() << pfc::string_filename_ext(ExePath) << " unresponsive - terminating";
210  TerminateProcess(hProcess, -1);
211  }
212  }
213  _Close(hProcess);
214  }
215  }
216  private:
217  static void _Close(HANDLE & h) {
218  if (h != NULL) {CloseHandle(h); h = NULL;}
219  }
220 
221  static void myCreatePipe(HANDLE & in, HANDLE & out) {
222  SECURITY_ATTRIBUTES Attributes = { sizeof(SECURITY_ATTRIBUTES), 0, true };
223  WIN32_OP( CreatePipe( &in, &out, &Attributes, 0 ) );
224  }
225 
227  GUID id;
228  CoCreateGuid (&id);
229  return pfc::string_formatter() << "\\\\.\\pipe\\" << pfc::print_guid(id);
230  }
231 
232  static void myCreatePipeOut(HANDLE & in, HANDLE & out) {
233  SECURITY_ATTRIBUTES Attributes = { sizeof(SECURITY_ATTRIBUTES), 0, true };
234  const pfc::stringcvt::string_os_from_utf8 pipeName( makePipeName() );
235  SetLastError(NO_ERROR);
236  HANDLE pipe = CreateNamedPipe(
237  pipeName,
238  FILE_FLAG_FIRST_PIPE_INSTANCE | PIPE_ACCESS_OUTBOUND | FILE_FLAG_OVERLAPPED,
239  PIPE_TYPE_BYTE | PIPE_READMODE_BYTE | PIPE_WAIT,
240  1,
241  1024*64,
242  1024*64,
243  NMPWAIT_USE_DEFAULT_WAIT,&Attributes);
244  if (pipe == INVALID_HANDLE_VALUE) throw exception_win32(GetLastError());
245 
246  in = CreateFile(pipeName,GENERIC_READ,FILE_SHARE_READ|FILE_SHARE_WRITE,&Attributes,OPEN_EXISTING,0,NULL);
247  DuplicateHandle ( GetCurrentProcess(), pipe, GetCurrentProcess(), &out, 0, FALSE, DUPLICATE_SAME_ACCESS );
248  CloseHandle(pipe);
249  }
250 
251  static void myCreatePipeIn(HANDLE & in, HANDLE & out) {
252  SECURITY_ATTRIBUTES Attributes = { sizeof(SECURITY_ATTRIBUTES), 0, true };
253  const pfc::stringcvt::string_os_from_utf8 pipeName( makePipeName() );
254  SetLastError(NO_ERROR);
255  HANDLE pipe = CreateNamedPipe(
256  pipeName,
257  FILE_FLAG_FIRST_PIPE_INSTANCE | PIPE_ACCESS_INBOUND | FILE_FLAG_OVERLAPPED,
258  PIPE_TYPE_BYTE | PIPE_READMODE_BYTE | PIPE_WAIT,
259  1,
260  1024*64,
261  1024*64,
262  NMPWAIT_USE_DEFAULT_WAIT,&Attributes);
263  if (pipe == INVALID_HANDLE_VALUE) throw exception_win32(GetLastError());
264 
265  out = CreateFile(pipeName,GENERIC_WRITE,FILE_SHARE_READ|FILE_SHARE_WRITE,&Attributes,OPEN_EXISTING,0,NULL);
266  DuplicateHandle ( GetCurrentProcess(), pipe, GetCurrentProcess(), &in, 0, FALSE, DUPLICATE_SAME_ACCESS );
267  CloseHandle(pipe);
268  }
269 
270  PFC_CLASS_NOT_COPYABLE_EX(SubProcess)
271  };
272 }
273 
274 #endif // _WIN32
275 
DWORD WaitForMultipleObjects(DWORD nCount, const HANDLE *lpHandles, BOOL bWaitAll, DWORD dwMilliseconds)
Definition: pp-winapi.h:20
Definition: pfc.h:71
void CleanUp(bool bDetach=false)
Definition: ProcessUtils.h:203
PFC_NORETURN void exception_io_from_win32(DWORD p_code)
Definition: filesystem.cpp:773
size_t read(void *p_buffer, size_t p_bytes, abort_callback &abort)
Definition: ProcessUtils.h:192
void write(const void *p_buffer, size_t p_bytes, abort_callback &abort)
Definition: ProcessUtils.h:12
const DWORD m_timeOut
Definition: ProcessUtils.h:129
SubProcess(const char *exePath, DWORD timeOutMS=60 *1000)
Definition: ProcessUtils.h:137
void SetPriority(DWORD val)
Definition: ProcessUtils.h:196
PipeIO(HANDLE handle, HANDLE hEvent, bool processMessages, DWORD timeOut=INFINITE)
Definition: ProcessUtils.h:7
PFC_DECLARE_EXCEPTION(timeout, exception_io,"Timeout")
Usage: console::formatter() << "blah " << somenumber << " asdf" << somestring;.
Definition: console.h:17
static void myCreatePipeIn(HANDLE &in, HANDLE &out)
Definition: ProcessUtils.h:251
typedef HANDLE(WINAPI *pPowerCreateRequest_t)(__in void *Context)
DWORD WaitForSingleObject(HANDLE hHandle, DWORD dwMilliseconds)
Definition: pp-winapi.h:16
string8_fastalloc string_formatter
Definition: string_base.h:615
const bool m_processMessages
Definition: ProcessUtils.h:130
HANDLE CreateEvent(LPSECURITY_ATTRIBUTES lpEventAttributes, BOOL bManualReset, BOOL bInitialState, LPCTSTR lpName)
Definition: pp-winapi.h:8
const pfc::string8 ExePath
Definition: ProcessUtils.h:201
std::exception exception
Definition: primitives.h:193
static void _Close(HANDLE &h)
Definition: ProcessUtils.h:217
static void myCreatePipeOut(HANDLE &in, HANDLE &out)
Definition: ProcessUtils.h:232
static void ProcessPendingMessages()
Definition: ProcessUtils.h:122
size_t read(void *p_buffer, size_t p_bytes, abort_callback &abort)
Definition: ProcessUtils.h:46
void _cancel(OVERLAPPED &ol)
Definition: ProcessUtils.h:115
void write(const void *p_buffer, size_t p_bytes, abort_callback &abort)
Definition: ProcessUtils.h:188
size_t readPass(void *p_buffer, size_t p_bytes, abort_callback &abort)
Definition: ProcessUtils.h:57
DWORD myWait(DWORD count, const HANDLE *handles)
Definition: ProcessUtils.h:101
static pfc::string_formatter makePipeName()
Definition: ProcessUtils.h:226
static void myCreatePipe(HANDLE &in, HANDLE &out)
Definition: ProcessUtils.h:221