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