summaryrefslogtreecommitdiffstats
path: root/source/core/windows
diff options
context:
space:
mode:
Diffstat (limited to 'source/core/windows')
-rw-r--r--source/core/windows/slang-win-process-util.cpp333
-rw-r--r--source/core/windows/slang-win-process.cpp495
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