diff options
Diffstat (limited to 'source/core/windows')
| -rw-r--r-- | source/core/windows/slang-win-process-util.cpp | 333 | ||||
| -rw-r--r-- | source/core/windows/slang-win-process.cpp | 495 |
2 files changed, 495 insertions, 333 deletions
diff --git a/source/core/windows/slang-win-process-util.cpp b/source/core/windows/slang-win-process-util.cpp deleted file mode 100644 index 4039361a4..000000000 --- a/source/core/windows/slang-win-process-util.cpp +++ /dev/null @@ -1,333 +0,0 @@ -// slang-win-process-util.cpp -#include "../slang-process-util.h" - -#include "../slang-string.h" -#include "../slang-string-escape-util.h" - -#ifdef _WIN32 -// Include Windows header in a way that minimized namespace pollution. -// TODO: We could try to avoid including this at all, but it would -// mean trying to hide certain struct layouts, which would add -// more dynamic allocation. -# define WIN32_LEAN_AND_MEAN -# define NOMINMAX -# include <Windows.h> -# undef WIN32_LEAN_AND_MEAN -# undef NOMINMAX -#endif - -#include <stdio.h> -#include <stdlib.h> - -namespace Slang { - -namespace { // anonymous - -struct ThreadInfo -{ - HANDLE file; - String output; -}; - -// Has behavior very similar to unique_ptr - assignment is a move. -class WinHandle -{ -public: - /// Detach the encapsulated handle. Returns the handle (which now must be externally handled) - HANDLE detach() { HANDLE handle = m_handle; m_handle = nullptr; return handle; } - - /// Return as a handle - operator HANDLE() const { return m_handle; } - - /// Assign - void operator=(HANDLE handle) { setNull(); m_handle = handle; } - void operator=(WinHandle&& rhs) { HANDLE handle = m_handle; m_handle = rhs.m_handle; rhs.m_handle = handle; } - - /// Get ready for writing - SLANG_FORCE_INLINE HANDLE* writeRef() { setNull(); return &m_handle; } - /// Get for read access - SLANG_FORCE_INLINE const HANDLE* readRef() const { return &m_handle; } - - void setNull() - { - if (m_handle) - { - CloseHandle(m_handle); - m_handle = nullptr; - } - } - - /// Ctor - WinHandle(HANDLE handle = nullptr):m_handle(handle) {} - WinHandle(WinHandle&& rhs):m_handle(rhs.m_handle) { rhs.m_handle = nullptr; } - - /// Dtor - ~WinHandle() { setNull(); } - -private: - - WinHandle(const WinHandle&) = delete; - void operator=(const WinHandle& rhs) = delete; - - HANDLE m_handle; -}; - -} // anonymous - -static DWORD WINAPI _readerThreadProc(LPVOID threadParam) -{ - ThreadInfo* info = (ThreadInfo*)threadParam; - HANDLE file = info->file; - - static const int kChunkSize = 1024; - char buffer[kChunkSize]; - - StringBuilder outputBuilder; - - // We need to re-write the output to deal with line - // endings, so we check for paired '\r' and '\n' - // characters, which may span chunks. - int prevChar = -1; - - for (;;) - { - DWORD bytesRead = 0; - BOOL readResult = ReadFile(file, buffer, kChunkSize, &bytesRead, nullptr); - - const DWORD lastError = GetLastError(); - if (lastError == ERROR_BROKEN_PIPE) - { - break; - } - - if (!readResult) - { - break; - } - - - // walk the buffer and rewrite to eliminate '\r' '\n' pairs - char* readCursor = buffer; - char const* end = buffer + bytesRead; - char* writeCursor = buffer; - - while (readCursor != end) - { - int p = prevChar; - int c = *readCursor++; - prevChar = c; - switch (c) - { - case '\r': case '\n': - // swallow input if '\r' and '\n' appear in sequence - if ((p ^ c) == ('\r' ^ '\n')) - { - // but don't swallow the next byte - prevChar = -1; - continue; - } - // always replace '\r' with '\n' - c = '\n'; - break; - - default: - break; - } - - *writeCursor++ = (char)c; - } - bytesRead = (DWORD)(writeCursor - buffer); - - // Note: Current "core" implementation gives no way to know - // the length of the buffer, so we ultimately have - // to just assume null termination... - outputBuilder.Append(buffer, bytesRead); - } - - info->output = outputBuilder.ProduceString(); - - return 0; -} - -/* static */StringEscapeHandler* ProcessUtil::getEscapeHandler() -{ - return StringEscapeUtil::getHandler(StringEscapeUtil::Style::Space); -} - -/* static */UnownedStringSlice ProcessUtil::getExecutableSuffix() -{ - return UnownedStringSlice::fromLiteral(".exe"); -} - -/* static */String ProcessUtil::getCommandLineString(const CommandLine& commandLine) -{ - auto escapeHandler = getEscapeHandler(); - - StringBuilder cmd; - StringEscapeUtil::appendMaybeQuoted(escapeHandler, commandLine.m_executable.getUnownedSlice(), cmd); - - for (const auto& arg : commandLine.m_args) - { - cmd << " "; - StringEscapeUtil::appendMaybeQuoted(escapeHandler, arg.getUnownedSlice(), cmd); - } - return cmd.ToString(); -} - -#define SLANG_RETURN_FAIL_ON_FALSE(x) if (!(x)) return SLANG_FAIL; - -/* static */SlangResult ProcessUtil::execute(const CommandLine& commandLine, ExecuteResult& outExecuteResult) -{ - outExecuteResult.init(); - - SECURITY_ATTRIBUTES securityAttributes; - securityAttributes.nLength = sizeof(securityAttributes); - securityAttributes.lpSecurityDescriptor = nullptr; - securityAttributes.bInheritHandle = true; - - WinHandle childStdOutRead; - WinHandle childStdErrRead; - WinHandle childStdInWrite; - - // Now we can actually get around to starting a process - PROCESS_INFORMATION processInfo; - ZeroMemory(&processInfo, sizeof(processInfo)); - { - WinHandle childStdOutWrite; - WinHandle childStdErrWrite; - WinHandle childStdInRead; - - { - WinHandle childStdOutReadTmp; - WinHandle childStdErrReadTmp; - WinHandle childStdInWriteTmp; - // create stdout pipe for child process - SLANG_RETURN_FAIL_ON_FALSE(CreatePipe(childStdOutReadTmp.writeRef(), childStdOutWrite.writeRef(), &securityAttributes, 0)); - // create stderr pipe for child process - SLANG_RETURN_FAIL_ON_FALSE(CreatePipe(childStdErrReadTmp.writeRef(), childStdErrWrite.writeRef(), &securityAttributes, 0)); - // create stdin pipe for child process - SLANG_RETURN_FAIL_ON_FALSE(CreatePipe(childStdInRead.writeRef(), childStdInWriteTmp.writeRef(), &securityAttributes, 0)); - - HANDLE currentProcess = GetCurrentProcess(); - - // create a non-inheritable duplicate of the stdout reader - SLANG_RETURN_FAIL_ON_FALSE(DuplicateHandle(currentProcess, childStdOutReadTmp, currentProcess, childStdOutRead.writeRef(), 0, FALSE, DUPLICATE_SAME_ACCESS)); - // create a non-inheritable duplicate of the stderr reader - SLANG_RETURN_FAIL_ON_FALSE(DuplicateHandle(currentProcess, childStdErrReadTmp, currentProcess, childStdErrRead.writeRef(), 0, FALSE, DUPLICATE_SAME_ACCESS)); - // create a non-inheritable duplicate of the stdin writer - SLANG_RETURN_FAIL_ON_FALSE(DuplicateHandle(currentProcess, childStdInWriteTmp, currentProcess, childStdInWrite.writeRef(), 0, FALSE, DUPLICATE_SAME_ACCESS)); - } - - - // TODO: switch to proper wide-character versions of these... - STARTUPINFOW startupInfo; - ZeroMemory(&startupInfo, sizeof(startupInfo)); - startupInfo.cb = sizeof(startupInfo); - startupInfo.hStdError = childStdErrWrite; - startupInfo.hStdOutput = childStdOutWrite; - startupInfo.hStdInput = childStdInRead; - startupInfo.dwFlags = STARTF_USESTDHANDLES; - - OSString pathBuffer; - LPCWSTR path = nullptr; - - if (commandLine.m_executableType == CommandLine::ExecutableType::Path) - { - StringBuilder cmd; - StringEscapeUtil::appendMaybeQuoted(getEscapeHandler(), commandLine.m_executable.getUnownedSlice(), cmd); - - pathBuffer = cmd.toWString(); - path = pathBuffer.begin(); - } - - // Produce the command line string - String cmdString = getCommandLineString(commandLine); - OSString cmdStringBuffer = cmdString.toWString(); - - // https://docs.microsoft.com/en-us/windows/desktop/api/processthreadsapi/nf-processthreadsapi-createprocessa - // `CreateProcess` requires write access to this, for some reason... - BOOL success = CreateProcessW( - path, - (LPWSTR)cmdStringBuffer.begin(), - nullptr, - nullptr, - true, - CREATE_NO_WINDOW, - nullptr, // TODO: allow specifying environment variables? - nullptr, - &startupInfo, - &processInfo); - - if (!success) - { - DWORD err = GetLastError(); - SLANG_UNUSED(err); - - return SLANG_FAIL; - } - - // close handles we are now done with - CloseHandle(processInfo.hThread); - } - - // Create a thread to read from the child's stdout. - ThreadInfo stdOutThreadInfo; - stdOutThreadInfo.file = childStdOutRead; - WinHandle stdOutThread = CreateThread(nullptr, 0, &_readerThreadProc, (LPVOID)&stdOutThreadInfo, 0, nullptr); - - // Create a thread to read from the child's stderr. - ThreadInfo stdErrThreadInfo; - stdErrThreadInfo.file = childStdErrRead; - WinHandle stdErrThread = CreateThread(nullptr, 0, &_readerThreadProc, (LPVOID)&stdErrThreadInfo, 0, nullptr); - - // wait for the process to exit - // TODO: set a timeout as a safety measure... - WaitForSingleObject(processInfo.hProcess, INFINITE); - - // get exit code for process - // https://docs.microsoft.com/en-us/windows/desktop/api/processthreadsapi/nf-processthreadsapi-getexitcodeprocess - - DWORD childExitCode = 0; - if (!GetExitCodeProcess(processInfo.hProcess, &childExitCode)) - { - // TODO(JS): Do we want to close here? It seems plausible because just because reading the exit code failed, doesn't mean the handle is closed - CloseHandle(processInfo.hProcess); - - return SLANG_FAIL; - } - - // wait for the reader threads - WaitForSingleObject(stdOutThread, INFINITE); - WaitForSingleObject(stdErrThread, INFINITE); - - CloseHandle(processInfo.hProcess); - - outExecuteResult.standardOutput = stdOutThreadInfo.output; - outExecuteResult.standardError = stdErrThreadInfo.output; - outExecuteResult.resultCode = childExitCode; - - return SLANG_OK; -} - -static uint64_t _getClockFrequency() -{ - LARGE_INTEGER timerFrequency; - QueryPerformanceFrequency(&timerFrequency); - return timerFrequency.QuadPart; -} - -static const uint64_t g_frequency = _getClockFrequency(); - -/* static */uint64_t ProcessUtil::getClockFrequency() -{ - return g_frequency; -} - -/* static */uint64_t ProcessUtil::getClockTick() -{ - LARGE_INTEGER counter; - QueryPerformanceCounter(&counter); - return counter.QuadPart; -} - -} diff --git a/source/core/windows/slang-win-process.cpp b/source/core/windows/slang-win-process.cpp new file mode 100644 index 000000000..57eb209c3 --- /dev/null +++ b/source/core/windows/slang-win-process.cpp @@ -0,0 +1,495 @@ +// slang-win-process-util.cpp +#include "../slang-process.h" + +#include "../slang-string.h" +#include "../slang-string-escape-util.h" +#include "../slang-string-util.h" +#include "../slang-process-util.h" + +#include "../../../slang-com-helper.h" + +#ifdef _WIN32 +// Include Windows header in a way that minimized namespace pollution. +// TODO: We could try to avoid including this at all, but it would +// mean trying to hide certain struct layouts, which would add +// more dynamic allocation. +# define WIN32_LEAN_AND_MEAN +# define NOMINMAX +# include <Windows.h> +# undef WIN32_LEAN_AND_MEAN +# undef NOMINMAX +#endif + +#include <stdio.h> +#include <stdlib.h> + +#ifndef SLANG_RETURN_FAIL_ON_FALSE +# define SLANG_RETURN_FAIL_ON_FALSE(x) if (!(x)) return SLANG_FAIL; +#endif + +namespace Slang { + +// Has behavior very similar to unique_ptr - assignment is a move. +class WinHandle +{ +public: + /// Detach the encapsulated handle. Returns the handle (which now must be externally handled) + HANDLE detach() { HANDLE handle = m_handle; m_handle = nullptr; return handle; } + + /// Return as a handle + operator HANDLE() const { return m_handle; } + + /// Assign + void operator=(HANDLE handle) { setNull(); m_handle = handle; } + void operator=(WinHandle&& rhs) { HANDLE handle = m_handle; m_handle = rhs.m_handle; rhs.m_handle = handle; } + + /// Get ready for writing + SLANG_FORCE_INLINE HANDLE* writeRef() { setNull(); return &m_handle; } + /// Get for read access + SLANG_FORCE_INLINE const HANDLE* readRef() const { return &m_handle; } + + void setNull() + { + if (m_handle) + { + CloseHandle(m_handle); + m_handle = nullptr; + } + } + bool isNull() const { return m_handle == nullptr; } + + /// Ctor + WinHandle(HANDLE handle = nullptr) :m_handle(handle) {} + WinHandle(WinHandle&& rhs) :m_handle(rhs.m_handle) { rhs.m_handle = nullptr; } + + /// Dtor + ~WinHandle() { setNull(); } + +private: + + WinHandle(const WinHandle&) = delete; + void operator=(const WinHandle& rhs) = delete; + + HANDLE m_handle; +}; + +/* A simple Stream implementation of a File HANDLE (or Pipe). Note that currently does not allow getPosition/seek/atEnd */ +class WinPipeStream : public Stream +{ +public: + typedef WinPipeStream ThisType; + + // Stream + virtual Int64 getPosition() SLANG_OVERRIDE { return 0; } + virtual SlangResult seek(SeekOrigin origin, Int64 offset) SLANG_OVERRIDE { SLANG_UNUSED(origin); SLANG_UNUSED(offset); return SLANG_E_NOT_AVAILABLE; } + virtual SlangResult read(void* buffer, size_t length, size_t& outReadBytes) SLANG_OVERRIDE; + virtual SlangResult write(const void* buffer, size_t length) SLANG_OVERRIDE; + virtual bool isEnd() SLANG_OVERRIDE { return m_streamHandle.isNull(); } + virtual bool canRead() SLANG_OVERRIDE { return _has(FileAccess::Read) && !m_streamHandle.isNull(); } + virtual bool canWrite() SLANG_OVERRIDE { return _has(FileAccess::Write) && !m_streamHandle.isNull(); } + virtual void close() SLANG_OVERRIDE; + virtual SlangResult flush() SLANG_OVERRIDE; + + WinPipeStream(HANDLE handle, FileAccess access, bool isOwned = true); + + ~WinPipeStream() { close(); } + +protected: + + bool _has(FileAccess access) const { return (Index(access) & Index(m_access)) != 0; } + + SlangResult _updateState(BOOL res); + + FileAccess m_access = FileAccess::None; + WinHandle m_streamHandle; + bool m_isOwned; +}; + + +class WinProcess : public Process +{ +public: + + virtual bool isTerminated() SLANG_OVERRIDE; + virtual void waitForTermination() SLANG_OVERRIDE; + + WinProcess(HANDLE handle, Stream* const* streams) : + m_processHandle(handle) + { + for (Index i = 0; i < Index(Process::StreamType::CountOf); ++i) + { + m_streams[i] = streams[i]; + } + } + +protected: + + void _hasTerminated(); + WinHandle m_processHandle; ///< If not set the process has terminated +}; + +/* !!!!!!!!!!!!!!!!!!!!!!!!!!! WinPipeStream !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!! */ + +WinPipeStream::WinPipeStream(HANDLE handle, FileAccess access, bool isOwned) : + m_streamHandle(handle), + m_access(access), + m_isOwned(isOwned) +{ + // It might be handy to get information about the handle + // https://docs.microsoft.com/en-us/windows/win32/api/namedpipeapi/nf-namedpipeapi-getnamedpipeinfo + + { + DWORD flags, outBufferSize, inBufferSize, maxInstances; + // It appears that by default windows pipe buffer size is 4k. + if (GetNamedPipeInfo(handle, &flags, &outBufferSize, &inBufferSize, &maxInstances)) + { + } + } +} + +SlangResult WinPipeStream::_updateState(BOOL res) +{ + if (res) + { + return SLANG_OK; + } + else + { + const auto err = GetLastError(); + + if (err == ERROR_BROKEN_PIPE) + { + m_streamHandle.setNull(); + return SLANG_OK; + } + + SLANG_UNUSED(err); + return SLANG_FAIL; + } +} + +SlangResult WinPipeStream::read(void* buffer, size_t length, size_t& outReadBytes) +{ + outReadBytes = 0; + if (!_has(FileAccess::Read)) + { + return SLANG_E_NOT_AVAILABLE; + } + + if (m_streamHandle.isNull()) + { + return SLANG_OK; + } + + // Check if there is any data, so won't block + { + DWORD bytesRead = 0; + DWORD totalBytes = 0; + DWORD remainingBytes = 0; + + // Works on anonymous pipes too + // https://docs.microsoft.com/en-us/windows/win32/api/namedpipeapi/nf-namedpipeapi-peeknamedpipe + + SLANG_RETURN_ON_FAIL(_updateState(PeekNamedPipe(m_streamHandle, nullptr, DWORD(0), &bytesRead, &totalBytes, &remainingBytes))); + // If there is nothing to read we are done + // If we don't do this ReadFile will *block* if there is nothing available + if (totalBytes == 0) + { + return SLANG_OK; + } + } + + { + DWORD bytesRead = 0; + SLANG_RETURN_ON_FAIL(_updateState(ReadFile(m_streamHandle, buffer, DWORD(length), &bytesRead, nullptr))); + + outReadBytes = size_t(bytesRead); + } + + return SLANG_OK; +} + +SlangResult WinPipeStream::write(const void* buffer, size_t length) +{ + if (!_has(FileAccess::Write)) + { + return SLANG_E_NOT_AVAILABLE; + } + + if (m_streamHandle.isNull()) + { + // Writing to closed stream + return SLANG_FAIL; + } + + DWORD numWritten = 0; + BOOL writeResult = WriteFile(m_streamHandle, buffer, DWORD(length), &numWritten, nullptr); + + if (!writeResult || numWritten != length) + { + return SLANG_FAIL; + } + + return SLANG_OK; +} + +void WinPipeStream::close() +{ + if (!m_isOwned) + { + // If we don't own it just detach it + m_streamHandle.detach(); + } + m_streamHandle.setNull(); +} + +SlangResult WinPipeStream::flush() +{ + if ((Index(m_access) & Index(FileAccess::Write)) == 0 || m_streamHandle.isNull()) + { + return SLANG_E_NOT_AVAILABLE; + } + + // https://docs.microsoft.com/en-us/windows/win32/api/fileapi/nf-fileapi-flushfilebuffers + + if (!FlushFileBuffers(m_streamHandle)) + { + auto err = GetLastError(); + SLANG_UNUSED(err); + } + return SLANG_OK; +} + +/* !!!!!!!!!!!!!!!!!!!!!!!!!!! WinProcess !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!! */ + +void WinProcess::_hasTerminated() +{ + if (!m_processHandle.isNull()) + { + // get exit code for process + // https://docs.microsoft.com/en-us/windows/desktop/api/processthreadsapi/nf-processthreadsapi-getexitcodeprocess + + DWORD childExitCode = 0; + if (GetExitCodeProcess(m_processHandle, &childExitCode)) + { + m_returnValue = int32_t(childExitCode); + } + m_processHandle.setNull(); + } +} + +void WinProcess::waitForTermination() +{ + if (m_processHandle.isNull()) + { + return; + } + + // wait for the process to exit + // TODO: set a timeout as a safety measure... + WaitForSingleObject(m_processHandle, INFINITE); + + _hasTerminated(); +} + +bool WinProcess::isTerminated() +{ + if (m_processHandle.isNull()) + { + return true; + } + + auto res = WaitForSingleObject(m_processHandle, 0); + + if (res == WAIT_TIMEOUT) + { + return false; + } + _hasTerminated(); + return true; +} + +/* !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!! */ + +/* static */StringEscapeHandler* Process::getEscapeHandler() +{ + return StringEscapeUtil::getHandler(StringEscapeUtil::Style::Space); +} + +/* static */UnownedStringSlice Process::getExecutableSuffix() +{ + return UnownedStringSlice::fromLiteral(".exe"); +} + +/* static */SlangResult Process::getStdStream(StreamType type, RefPtr<Stream>& out) +{ + switch (type) + { + case StreamType::StdIn: + { + const HANDLE stdinHandle = GetStdHandle(STD_INPUT_HANDLE); + out = new WinPipeStream(stdinHandle, FileAccess::Read, false); + return SLANG_OK; + } + } + + return SLANG_FAIL; +} + +/* static */SlangResult Process::create(const CommandLine& commandLine, Process::Flags flags, RefPtr<Process>& outProcess) +{ + WinHandle childStdOutRead; + WinHandle childStdErrRead; + WinHandle childStdInWrite; + + WinHandle processHandle; + { + WinHandle childStdOutWrite; + WinHandle childStdErrWrite; + WinHandle childStdInRead; + + SECURITY_ATTRIBUTES securityAttributes; + securityAttributes.nLength = sizeof(securityAttributes); + securityAttributes.lpSecurityDescriptor = nullptr; + securityAttributes.bInheritHandle = true; + + // 0 means use the 'system default' + //const DWORD bufferSize = 64 * 1024; + const DWORD bufferSize = 0; + + { + WinHandle childStdOutReadTmp; + WinHandle childStdErrReadTmp; + WinHandle childStdInWriteTmp; + // create stdout pipe for child process + SLANG_RETURN_FAIL_ON_FALSE(CreatePipe(childStdOutReadTmp.writeRef(), childStdOutWrite.writeRef(), &securityAttributes, bufferSize)); + // create stderr pipe for child process + SLANG_RETURN_FAIL_ON_FALSE(CreatePipe(childStdErrReadTmp.writeRef(), childStdErrWrite.writeRef(), &securityAttributes, bufferSize)); + // create stdin pipe for child process + SLANG_RETURN_FAIL_ON_FALSE(CreatePipe(childStdInRead.writeRef(), childStdInWriteTmp.writeRef(), &securityAttributes, bufferSize)); + + const HANDLE currentProcess = GetCurrentProcess(); + + // https://docs.microsoft.com/en-us/windows/win32/api/handleapi/nf-handleapi-duplicatehandle + + // create a non-inheritable duplicate of the stdout reader + SLANG_RETURN_FAIL_ON_FALSE(DuplicateHandle(currentProcess, childStdOutReadTmp, currentProcess, childStdOutRead.writeRef(), 0, FALSE, DUPLICATE_SAME_ACCESS)); + // create a non-inheritable duplicate of the stderr reader + SLANG_RETURN_FAIL_ON_FALSE(DuplicateHandle(currentProcess, childStdErrReadTmp, currentProcess, childStdErrRead.writeRef(), 0, FALSE, DUPLICATE_SAME_ACCESS)); + // create a non-inheritable duplicate of the stdin writer + SLANG_RETURN_FAIL_ON_FALSE(DuplicateHandle(currentProcess, childStdInWriteTmp, currentProcess, childStdInWrite.writeRef(), 0, FALSE, DUPLICATE_SAME_ACCESS)); + } + + // TODO: switch to proper wide-character versions of these... + STARTUPINFOW startupInfo; + ZeroMemory(&startupInfo, sizeof(startupInfo)); + startupInfo.cb = sizeof(startupInfo); + startupInfo.hStdError = childStdErrWrite; + startupInfo.hStdOutput = childStdOutWrite; + startupInfo.hStdInput = childStdInRead; + startupInfo.dwFlags = STARTF_USESTDHANDLES; + + OSString pathBuffer; + LPCWSTR path = nullptr; + + if (commandLine.m_executableType == CommandLine::ExecutableType::Path) + { + StringBuilder cmd; + StringEscapeUtil::appendMaybeQuoted(getEscapeHandler(), commandLine.m_executable.getUnownedSlice(), cmd); + + pathBuffer = cmd.toWString(); + path = pathBuffer.begin(); + } + + // Produce the command line string + String cmdString = commandLine.toString(); + OSString cmdStringBuffer = cmdString.toWString(); + + // Now we can actually get around to starting a process + PROCESS_INFORMATION processInfo; + ZeroMemory(&processInfo, sizeof(processInfo)); + + // https://docs.microsoft.com/en-us/windows/win32/procthread/process-creation-flags + + DWORD createFlags = CREATE_NO_WINDOW; + + if (flags & Process::Flag::AttachDebugger) + { + createFlags |= CREATE_SUSPENDED; + } + + // https://docs.microsoft.com/en-us/windows/desktop/api/processthreadsapi/nf-processthreadsapi-createprocessa + // `CreateProcess` requires write access to this, for some reason... + BOOL success = CreateProcessW( + path, + (LPWSTR)cmdStringBuffer.begin(), + nullptr, + nullptr, + true, + createFlags, + nullptr, // TODO: allow specifying environment variables? + nullptr, + &startupInfo, + &processInfo); + + if (!success) + { + DWORD err = GetLastError(); + SLANG_UNUSED(err); + return SLANG_FAIL; + } + + if (flags & Process::Flag::AttachDebugger) + { + // Lets see if we can set up to debug + // https://docs.microsoft.com/en-us/windows/win32/debug/debugging-a-running-process + + //DebugActiveProcess(processInfo.dwProcessId); + + // Resume the thread + ResumeThread(processInfo.hThread); + } + + // close handles we are now done with + CloseHandle(processInfo.hThread); + + // Save the process handle + processHandle = processInfo.hProcess; + } + + RefPtr<Stream> streams[Index(Process::StreamType::CountOf)]; + streams[Index(Process::StreamType::ErrorOut)] = new WinPipeStream(childStdErrRead.detach(), FileAccess::Read); + streams[Index(Process::StreamType::StdOut)] = new WinPipeStream(childStdOutRead.detach(), FileAccess::Read); + streams[Index(Process::StreamType::StdIn)] = new WinPipeStream(childStdInWrite.detach(), FileAccess::Write); + + outProcess = new WinProcess(processHandle.detach(), streams[0].readRef()); + return SLANG_OK; +} + +/* static */void Process::sleepCurrentThread(Index timeInMs) +{ + ::Sleep(DWORD(timeInMs)); +} + +static uint64_t _getClockFrequency() +{ + LARGE_INTEGER timerFrequency; + QueryPerformanceFrequency(&timerFrequency); + return timerFrequency.QuadPart; +} + +static const uint64_t g_frequency = _getClockFrequency(); + +/* static */uint64_t Process::getClockFrequency() +{ + return g_frequency; +} + +/* static */uint64_t Process::getClockTick() +{ + LARGE_INTEGER counter; + QueryPerformanceCounter(&counter); + return counter.QuadPart; +} + +} // namespace Slang |
