Created
January 27, 2026 08:20
-
-
Save floooh/e4a781854c381df1cedb000895275892 to your computer and use it in GitHub Desktop.
Nebula3 AppLauncher class
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| //------------------------------------------------------------------------------ | |
| // applauncher.cc | |
| // (C) 2008 Radon Labs GmbH | |
| //------------------------------------------------------------------------------ | |
| #include "stdneb.h" | |
| #include "applauncher.h" | |
| namespace ToolkitUtil | |
| { | |
| using namespace Util; | |
| using namespace IO; | |
| //------------------------------------------------------------------------------ | |
| /** | |
| */ | |
| AppLauncher::AppLauncher() : | |
| isRunning(false), | |
| noConsoleWindow(false), | |
| stdoutRead(0), | |
| stdoutWrite(0), | |
| stderrRead(0), | |
| stderrWrite(0), | |
| exitCode(-1) | |
| { | |
| Memory::Clear(&this->processInfo, sizeof(this->processInfo)); | |
| Memory::Clear(this->captureBuffer, sizeof(this->captureBuffer)); | |
| } | |
| //------------------------------------------------------------------------------ | |
| /** | |
| */ | |
| AppLauncher::~AppLauncher() | |
| { | |
| this->CloseOutputCapturing(); | |
| } | |
| //------------------------------------------------------------------------------ | |
| /** | |
| */ | |
| bool | |
| AppLauncher::CreateOutputCapturePipe(PHANDLE hRead, PHANDLE hWrite) const | |
| { | |
| // configure pipe security attributes | |
| SECURITY_ATTRIBUTES security; | |
| security.bInheritHandle = TRUE; | |
| security.lpSecurityDescriptor = 0; | |
| security.nLength = sizeof(SECURITY_ATTRIBUTES); | |
| // create a pipe for the child process's stdout | |
| if (!CreatePipe(hRead, hWrite, &security, pipeSize)) | |
| { | |
| return false; | |
| } | |
| // ensure the read handle to the pipe for stdout is not inherited. | |
| SetHandleInformation((*hRead), HANDLE_FLAG_INHERIT, 0); | |
| return true; | |
| } | |
| //------------------------------------------------------------------------------ | |
| /** | |
| */ | |
| bool | |
| AppLauncher::OpenOutputCapturing(STARTUPINFO& inOutStartupInfo) | |
| { | |
| if (this->stdoutCaptureStream.isvalid() || this->stderrCaptureStream.isvalid()) | |
| { | |
| n_assert(0 == this->stdoutRead); | |
| n_assert(0 == this->stdoutWrite); | |
| n_assert(0 == this->stderrRead); | |
| n_assert(0 == this->stderrWrite); | |
| if (this->stdoutCaptureStream.isvalid()) | |
| { | |
| this->stdoutCaptureStream->SetAccessMode(Stream::WriteAccess); | |
| if (!this->stdoutCaptureStream->Open()) | |
| { | |
| return false; | |
| } | |
| } | |
| if (this->stderrCaptureStream.isvalid()) | |
| { | |
| this->stderrCaptureStream->SetAccessMode(Stream::WriteAccess); | |
| if (!this->stderrCaptureStream->Open()) | |
| { | |
| return false; | |
| } | |
| } | |
| inOutStartupInfo.dwFlags |= STARTF_USESTDHANDLES; | |
| inOutStartupInfo.hStdInput = GetStdHandle(STD_INPUT_HANDLE); | |
| if (this->stdoutCaptureStream.isvalid()) | |
| { | |
| if (!this->CreateOutputCapturePipe((PHANDLE)&this->stdoutRead, (PHANDLE)&this->stdoutWrite)) | |
| { | |
| return false; | |
| } | |
| inOutStartupInfo.hStdOutput = this->stdoutWrite; | |
| } | |
| else | |
| { | |
| inOutStartupInfo.hStdOutput = GetStdHandle(STD_OUTPUT_HANDLE); | |
| } | |
| if (this->stderrCaptureStream.isvalid()) | |
| { | |
| if (!this->CreateOutputCapturePipe((PHANDLE)&this->stderrRead, (PHANDLE)&this->stderrWrite)) | |
| { | |
| return false; | |
| } | |
| inOutStartupInfo.hStdError = this->stderrWrite; | |
| } | |
| else | |
| { | |
| inOutStartupInfo.hStdError = GetStdHandle(STD_ERROR_HANDLE); | |
| } | |
| } | |
| return true; | |
| } | |
| //------------------------------------------------------------------------------ | |
| /** | |
| */ | |
| void | |
| AppLauncher::CloseOutputCapturing() | |
| { | |
| if (0 != this->stdoutRead) | |
| { | |
| CloseHandle(this->stdoutRead); | |
| this->stdoutRead = 0; | |
| } | |
| if (0 != this->stdoutWrite) | |
| { | |
| CloseHandle(this->stdoutWrite); | |
| this->stdoutWrite = 0; | |
| } | |
| if (0 != this->stderrRead) | |
| { | |
| CloseHandle(this->stderrRead); | |
| this->stderrRead = 0; | |
| } | |
| if (0 != this->stderrWrite) | |
| { | |
| CloseHandle(this->stderrWrite); | |
| this->stderrWrite = 0; | |
| } | |
| if (this->stdoutCaptureStream.isvalid() && this->stdoutCaptureStream->IsOpen()) | |
| { | |
| this->stdoutCaptureStream->Close(); | |
| this->stdoutCaptureStream->SetAccessMode(Stream::ReadAccess); | |
| } | |
| if (this->stderrCaptureStream.isvalid() && this->stderrCaptureStream->IsOpen()) | |
| { | |
| this->stderrCaptureStream->Close(); | |
| this->stderrCaptureStream->SetAccessMode(Stream::ReadAccess); | |
| } | |
| } | |
| //------------------------------------------------------------------------------ | |
| /** | |
| Update capture data for a pipe and capture stream. | |
| */ | |
| void | |
| AppLauncher::UpdateCaptureStream(HANDLE hRead, const Ptr<Stream>& captureStream) | |
| { | |
| n_assert(captureStream.isvalid()); | |
| n_assert(0 != hRead); | |
| n_assert(0 != this->processInfo.hProcess); | |
| DWORD numBytesRead = 0; | |
| DWORD numBytesAvailable = 0; | |
| Memory::Clear(this->captureBuffer, sizeof(this->captureBuffer)); | |
| // peek to check whether any data is on the stdout pipe | |
| PeekNamedPipe(hRead, this->captureBuffer, sizeof(this->captureBuffer), &numBytesRead, &numBytesAvailable, NULL); | |
| if (numBytesRead != 0) | |
| { | |
| if (numBytesAvailable > sizeof(this->captureBuffer)) | |
| { | |
| while (numBytesRead >= sizeof(this->captureBuffer)) | |
| { | |
| BOOL res = ::ReadFile(hRead, this->captureBuffer, sizeof(this->captureBuffer), &numBytesRead, NULL); | |
| n_assert(res); | |
| captureStream->Write(this->captureBuffer, numBytesRead); | |
| } | |
| } | |
| else | |
| { | |
| BOOL res = ::ReadFile(hRead, this->captureBuffer, sizeof(this->captureBuffer), &numBytesRead, NULL); | |
| n_assert(res); | |
| captureStream->Write(this->captureBuffer, numBytesRead); | |
| } | |
| } | |
| } | |
| //------------------------------------------------------------------------------ | |
| /** | |
| Reads all arrived data from stdout since the last call of this method and puts it to | |
| the capture streams. | |
| */ | |
| void | |
| AppLauncher::UpdateOutputCapturing() | |
| { | |
| if (this->stdoutCaptureStream.isvalid()) | |
| { | |
| this->UpdateCaptureStream(this->stdoutRead, this->stdoutCaptureStream); | |
| } | |
| if (this->stderrCaptureStream.isvalid()) | |
| { | |
| this->UpdateCaptureStream(this->stderrRead, this->stderrCaptureStream); | |
| } | |
| } | |
| //------------------------------------------------------------------------------ | |
| /** | |
| Gets the state of the application and also performs capturing. | |
| Call this frequently in a loop (don't forget to n_sleep()). | |
| */ | |
| bool | |
| AppLauncher::IsRunning() | |
| { | |
| if (this->isRunning) | |
| { | |
| this->UpdateOutputCapturing(); | |
| DWORD procExitCode = 0; | |
| GetExitCodeProcess(this->processInfo.hProcess, &procExitCode); | |
| if (procExitCode != STILL_ACTIVE) | |
| { | |
| this->exitCode = procExitCode; | |
| this->CloseOutputCapturing(); | |
| CloseHandle(this->processInfo.hProcess); | |
| CloseHandle(this->processInfo.hThread); | |
| this->processInfo.hProcess = 0; | |
| this->processInfo.hThread = 0; | |
| this->isRunning = false; | |
| } | |
| } | |
| return this->isRunning; | |
| } | |
| //------------------------------------------------------------------------------ | |
| /** | |
| Launch the application process and returns immediately. The state of the launched | |
| process must be checked by calling IsRunning() perdiodically. After | |
| IsRunning() returns false, GetExitCode() will return the exit code of the process. | |
| */ | |
| bool | |
| AppLauncher::Launch() | |
| { | |
| n_assert(this->exePath.IsValid()); | |
| n_assert(!this->isRunning); | |
| DWORD creationFlags = 0; | |
| if (this->noConsoleWindow) | |
| { | |
| creationFlags |= CREATE_NO_WINDOW; | |
| } | |
| this->exitCode = -1; | |
| // build a command line | |
| String cmdLine = this->exePath.LocalPath(); | |
| cmdLine.Append(" "); | |
| cmdLine.Append(this->args); | |
| // setup capturing if necessary | |
| STARTUPINFO startupInfo = { sizeof(STARTUPINFO), 0 }; | |
| this->OpenOutputCapturing(startupInfo); | |
| // create the process, return false if this fails | |
| if (!CreateProcess(NULL, // lpApplicationName | |
| (LPSTR) cmdLine.AsCharPtr(), // lpCommandLine | |
| NULL, // lpProcessAttributes | |
| NULL, // lpThreadAttributes | |
| TRUE, // bInheritsHandle | |
| creationFlags, // dwCreationFlags | |
| NULL, // lpEnvironment | |
| this->workingDir.LocalPath().AsCharPtr(), // lpCurrentDirectory | |
| &startupInfo, // lpStartupInfo | |
| (LPPROCESS_INFORMATION)&(this->processInfo))) // lpProcessInformation | |
| { | |
| this->CloseOutputCapturing(); | |
| return false; | |
| } | |
| this->isRunning = true; | |
| return true; | |
| } | |
| //------------------------------------------------------------------------------ | |
| /** | |
| */ | |
| bool | |
| AppLauncher::LaunchWait() | |
| { | |
| if (this->Launch()) | |
| { | |
| // launching was successful, wait until process quits and capture data if necessary | |
| while (this->IsRunning()) | |
| { | |
| n_sleep(0.01); | |
| } | |
| return true; | |
| } | |
| return false; | |
| } | |
| } // namespace ToolkitUtil |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment