aboutsummaryrefslogtreecommitdiff
path: root/src/base/platform
diff options
context:
space:
mode:
Diffstat (limited to 'src/base/platform')
-rw-r--r--src/base/platform/win/Base.cpp45
-rw-r--r--src/base/platform/win/Stream.cpp231
-rw-r--r--src/base/platform/win/StreamConvert.cpp25
-rw-r--r--src/base/platform/win/Win32FileStream.cpp102
-rw-r--r--src/base/platform/win/Win32FileStreamPrivate.h13
-rw-r--r--src/base/platform/win/Win32SubProcess.cpp91
6 files changed, 345 insertions, 162 deletions
diff --git a/src/base/platform/win/Base.cpp b/src/base/platform/win/Base.cpp
index 5edf9779..0aae6466 100644
--- a/src/base/platform/win/Base.cpp
+++ b/src/base/platform/win/Base.cpp
@@ -1,37 +1,38 @@
#include "cru/base/platform/win/Base.h"
#include <format>
-#include <optional>
namespace cru::platform::win {
-inline std::string HResultMakeMessage(HRESULT h_result,
- std::optional<std::string_view> message) {
- if (message.has_value())
- return std::format("HRESULT: {}. Message: {}", h_result, *message);
- else
- return std::format("HRESULT: {}.", h_result);
-}
-
-HResultError::HResultError(HRESULT h_result)
- : Exception(HResultMakeMessage(h_result, std::nullopt)),
- h_result_(h_result) {}
-
HResultError::HResultError(HRESULT h_result,
std::string_view additional_message)
- : Exception(HResultMakeMessage(h_result, additional_message)),
- h_result_(h_result) {}
-
-inline std::string Win32MakeMessage(DWORD error_code,
- std::string_view message) {
- return std::format("Last error code: {}.\nMessage: {}\n", error_code,
- message);
+ : Exception(std::format("HRESULT is {}.", h_result)), h_result_(h_result) {
+ AppendMessage(additional_message);
}
Win32Error::Win32Error(std::string_view message)
: Win32Error(::GetLastError(), message) {}
Win32Error::Win32Error(DWORD error_code, std::string_view message)
- : Exception(Win32MakeMessage(error_code, message)),
- error_code_(error_code) {}
+ : Exception(std::format("Last Windows error code is {}.", error_code)),
+ error_code_(error_code) {
+ AppendMessage(message);
+}
+
+UniDirectionalWin32PipeResult OpenUniDirectionalPipe(Win32PipeFlag flag) {
+ HANDLE read, write;
+ CheckWinReturn(::CreatePipe(&read, &write, nullptr, 0));
+
+ if (flag.Has(Win32PipeFlags::ReadInheritable)) {
+ CheckWinReturn(
+ ::SetHandleInformation(read, HANDLE_FLAG_INHERIT, HANDLE_FLAG_INHERIT));
+ }
+
+ if (flag.Has(Win32PipeFlags::WriteInheritable)) {
+ CheckWinReturn(::SetHandleInformation(write, HANDLE_FLAG_INHERIT,
+ HANDLE_FLAG_INHERIT));
+ }
+
+ return {Win32Handle(read, true), Win32Handle(write, true)};
+}
} // namespace cru::platform::win
diff --git a/src/base/platform/win/Stream.cpp b/src/base/platform/win/Stream.cpp
new file mode 100644
index 00000000..d4b24d64
--- /dev/null
+++ b/src/base/platform/win/Stream.cpp
@@ -0,0 +1,231 @@
+#include "cru/base/platform/win/Stream.h"
+
+#include "BrigdeComStream.h"
+#include "cru/base/StringUtil.h"
+#include "cru/base/io/Base.h"
+#include "cru/base/io/MemoryStream.h"
+#include "cru/base/platform/win/ComAutoInit.h"
+
+#include <Windows.h>
+#include <coml2api.h>
+#include <errhandlingapi.h>
+#include <fileapi.h>
+#include <handleapi.h>
+#include <shlwapi.h>
+#include <winnt.h>
+#include <filesystem>
+#include <format>
+
+namespace cru::platform::win {
+using namespace cru::io;
+
+namespace {
+HANDLE OpenHandle(std::string_view path, OpenFileFlag flags) {
+ DWORD access = 0;
+ DWORD creation_disposition = 0;
+ if (flags & OpenFileFlags::Read) {
+ access |= GENERIC_READ;
+ } else {
+ access |= GENERIC_WRITE;
+ }
+
+ if (std::filesystem::exists(path)) {
+ creation_disposition =
+ flags & io::OpenFileFlags::Truncate ? TRUNCATE_EXISTING : OPEN_EXISTING;
+ } else {
+ if (!flags.Has(OpenFileFlags::Create)) {
+ throw FileNotExistException(std::format("File {} does not exist.", path));
+ } else {
+ creation_disposition = CREATE_NEW;
+ }
+ }
+
+ IStream* stream;
+
+ auto handle =
+ ::CreateFileW(cru::string::ToUtf16(path).c_str(), access, 0, nullptr,
+ creation_disposition, FILE_ATTRIBUTE_NORMAL, nullptr);
+
+ if (handle == INVALID_HANDLE_VALUE) {
+ throw Win32Error("Failed to call CreateFileW.");
+ }
+
+ if (flags.Has(OpenFileFlags::Append)) {
+ LARGE_INTEGER offset;
+ offset.QuadPart = 0;
+ CheckWinReturn(::SetFilePointerEx(handle, offset, nullptr, FILE_END));
+ }
+
+ return handle;
+}
+} // namespace
+
+Win32HandleStream::Win32HandleStream(std::string_view path, OpenFileFlag flags)
+ : Win32HandleStream(OpenHandle(path, flags), true, true,
+ flags.Has(OpenFileFlags::Read),
+ flags.Has(OpenFileFlags::Write)) {}
+
+Win32HandleStream::Win32HandleStream(HANDLE handle, bool auto_close,
+ bool can_seek, bool can_read,
+ bool can_write)
+ : Win32HandleStream(Win32Handle(handle, auto_close), can_seek, can_read,
+ can_write) {}
+
+Win32HandleStream::Win32HandleStream(Win32Handle&& handle, bool can_seek,
+ bool can_read, bool can_write)
+ : Stream(SupportedOperations(can_seek, can_read, can_write)),
+ handle_(std::move(handle)) {}
+
+Win32HandleStream::~Win32HandleStream() { DoClose(); }
+
+Index Win32HandleStream::DoSeek(Index offset, SeekOrigin origin) {
+ DWORD method = 0;
+
+ if (origin == SeekOrigin::Current) {
+ method = FILE_CURRENT;
+ } else if (origin == SeekOrigin::End) {
+ method = FILE_END;
+ } else {
+ method = FILE_BEGIN;
+ }
+
+ LARGE_INTEGER n_offset, new_pos;
+ n_offset.QuadPart = offset;
+
+ CheckWinReturn(::SetFilePointerEx(handle_.Get(), n_offset, &new_pos, method));
+
+ return new_pos.QuadPart;
+}
+
+Index Win32HandleStream::DoRead(std::byte* buffer, Index offset, Index size) {
+ DWORD real_read;
+ auto r = ::ReadFile(handle_.Get(), static_cast<LPVOID>(buffer + offset), size,
+ &real_read, nullptr);
+ if (r == FALSE) {
+ auto e = ::GetLastError();
+ if (e != ERROR_BROKEN_PIPE || e != ERROR_BROKEN_PIPE) {
+ throw Win32Error(e, "Failed to call ReadFile.");
+ }
+ }
+ return real_read;
+}
+
+Index Win32HandleStream::DoWrite(const std::byte* buffer, Index offset,
+ Index size) {
+ DWORD real_write;
+ CheckWinReturn(::WriteFile(handle_.Get(),
+ static_cast<LPCVOID>(buffer + offset), size,
+ &real_write, nullptr));
+ return real_write;
+}
+
+void Win32HandleStream::DoClose() {
+ CRU_STREAM_BEGIN_CLOSE
+
+ handle_ = {};
+}
+
+IStream* ToComStream(io::Stream* stream) {
+ static ComAutoInit com_auto_init;
+
+ if (auto memory_stream = dynamic_cast<io::MemoryStream*>(stream)) {
+ return SHCreateMemStream(
+ reinterpret_cast<const BYTE*>(memory_stream->GetBuffer()),
+ memory_stream->GetSize());
+ } else if (auto file_stream = dynamic_cast<ComStream*>(stream)) {
+ return file_stream->GetComStream();
+ } else {
+ return new BridgeComStream(stream);
+ }
+}
+
+namespace {
+IStream* OpenComStream(std::string_view path, OpenFileFlag flags) {
+ DWORD grfMode = STGM_SHARE_DENY_NONE;
+ if (flags & io::OpenFileFlags::Read) {
+ if (flags & io::OpenFileFlags::Write) {
+ grfMode |= STGM_READWRITE;
+ } else {
+ grfMode |= STGM_READ;
+ }
+ } else {
+ if (flags & io::OpenFileFlags::Write) {
+ grfMode |= STGM_WRITE;
+ } else {
+ throw Exception("Stream must be readable or writable.");
+ }
+ }
+
+ if (flags & io::OpenFileFlags::Truncate) {
+ grfMode |= STGM_CREATE;
+ }
+
+ IStream* stream;
+
+ CheckHResult(SHCreateStreamOnFileEx(
+ cru::string::ToUtf16(path).c_str(), grfMode, FILE_ATTRIBUTE_NORMAL,
+ flags & io::OpenFileFlags::Create ? TRUE : FALSE, NULL, &stream));
+
+ return stream;
+}
+} // namespace
+
+ComStream::ComStream(std::string_view path, OpenFileFlag flags)
+ : ComStream(OpenComStream(path, flags), true, true,
+ flags.Has(OpenFileFlags::Read),
+ flags.Has(OpenFileFlags::Write)) {}
+
+ComStream::ComStream(IStream* com_stream, bool auto_release, bool can_seek,
+ bool can_read, bool can_write)
+ : Stream(SupportedOperations(can_seek, can_read, can_write)),
+ stream_(com_stream),
+ auto_release_(auto_release) {}
+
+ComStream::~ComStream() { DoClose(); }
+
+Index ComStream::DoSeek(Index offset, SeekOrigin origin) {
+ DWORD dwOrigin = 0;
+
+ if (origin == SeekOrigin::Current) {
+ dwOrigin = STREAM_SEEK_CUR;
+ } else if (origin == SeekOrigin::End) {
+ dwOrigin = STREAM_SEEK_END;
+ } else {
+ dwOrigin = STREAM_SEEK_SET;
+ }
+
+ LARGE_INTEGER n_offset;
+ n_offset.QuadPart = offset;
+ ULARGE_INTEGER n_new_offset;
+
+ CheckHResult(stream_->Seek(n_offset, dwOrigin, &n_new_offset));
+
+ return n_new_offset.QuadPart;
+}
+
+Index ComStream::DoRead(std::byte* buffer, Index offset, Index size) {
+ ULONG n_read;
+ CheckHResult(stream_->Read(buffer + offset, size, &n_read));
+ return n_read;
+}
+
+Index ComStream::DoWrite(const std::byte* buffer, Index offset, Index size) {
+ if (size < 0) {
+ throw Exception("Size must be greater than 0.");
+ }
+
+ ULONG n_written;
+ CheckHResult(stream_->Write(buffer + offset, size, &n_written));
+
+ return n_written;
+}
+
+void ComStream::DoClose() {
+ CRU_STREAM_BEGIN_CLOSE
+
+ if (stream_ && auto_release_) {
+ stream_->Release();
+ stream_ = nullptr;
+ }
+}
+} // namespace cru::platform::win
diff --git a/src/base/platform/win/StreamConvert.cpp b/src/base/platform/win/StreamConvert.cpp
deleted file mode 100644
index cb353fd3..00000000
--- a/src/base/platform/win/StreamConvert.cpp
+++ /dev/null
@@ -1,25 +0,0 @@
-#include "cru/base/platform/win/StreamConvert.h"
-#include "BrigdeComStream.h"
-#include "Win32FileStreamPrivate.h"
-#include "cru/base/io/MemoryStream.h"
-#include "cru/base/platform/win/ComAutoInit.h"
-#include "cru/base/platform/win/Win32FileStream.h"
-
-#include <shlwapi.h>
-#include <winnt.h>
-
-namespace cru::platform::win {
-IStream* ConvertStreamToComStream(io::Stream* stream) {
- static ComAutoInit com_auto_init;
-
- if (auto memory_stream = dynamic_cast<io::MemoryStream*>(stream)) {
- return SHCreateMemStream(
- reinterpret_cast<const BYTE*>(memory_stream->GetBuffer()),
- memory_stream->GetSize());
- } else if (auto file_stream = dynamic_cast<Win32FileStream*>(stream)) {
- return file_stream->GetPrivate_()->stream_;
- } else {
- return new BridgeComStream(stream);
- }
-}
-} // namespace cru::platform::win
diff --git a/src/base/platform/win/Win32FileStream.cpp b/src/base/platform/win/Win32FileStream.cpp
deleted file mode 100644
index 96a3dc67..00000000
--- a/src/base/platform/win/Win32FileStream.cpp
+++ /dev/null
@@ -1,102 +0,0 @@
-#include "cru/base/platform/win/Win32FileStream.h"
-
-#include "Win32FileStreamPrivate.h"
-#include "cru/base/StringUtil.h"
-#include "cru/base/io/OpenFileFlag.h"
-
-#include <Windows.h>
-#include <coml2api.h>
-#include <shlwapi.h>
-#include <winnt.h>
-
-namespace cru::platform::win {
-using namespace cru::io;
-
-Win32FileStream::Win32FileStream(std::string path, OpenFileFlag flags)
- : Stream(true, true, true),
- path_(std::move(path)),
- flags_(flags),
- p_(new details::Win32FileStreamPrivate()) {
- DWORD grfMode = STGM_SHARE_DENY_NONE;
- if (flags & io::OpenFileFlags::Read) {
- if (flags & io::OpenFileFlags::Write) {
- grfMode |= STGM_READWRITE;
- } else {
- grfMode |= STGM_READ;
- }
- } else {
- if (flags & io::OpenFileFlags::Write) {
- grfMode |= STGM_WRITE;
- } else {
- throw Exception("Stream must be readable or writable.");
- }
- }
-
- if (flags & io::OpenFileFlags::Truncate) {
- grfMode |= STGM_CREATE;
- }
-
- IStream* stream;
-
- ThrowIfFailed(SHCreateStreamOnFileEx(
- cru::string::ToUtf16(path_).c_str(), grfMode, FILE_ATTRIBUTE_NORMAL,
- flags & io::OpenFileFlags::Create ? TRUE : FALSE, NULL, &stream));
-
- p_->stream_ = stream;
-}
-
-Win32FileStream::~Win32FileStream() {
- DoClose();
- delete p_;
-}
-
-Index Win32FileStream::DoSeek(Index offset, SeekOrigin origin) {
- DWORD dwOrigin = 0;
-
- if (origin == SeekOrigin::Current) {
- dwOrigin = STREAM_SEEK_CUR;
- } else if (origin == SeekOrigin::End) {
- dwOrigin = STREAM_SEEK_END;
- } else {
- dwOrigin = STREAM_SEEK_SET;
- }
-
- LARGE_INTEGER n_offset;
- n_offset.QuadPart = offset;
- ULARGE_INTEGER n_new_offset;
-
- ThrowIfFailed(p_->stream_->Seek(n_offset, dwOrigin, &n_new_offset));
-
- return n_new_offset.QuadPart;
-}
-
-Index Win32FileStream::DoRead(std::byte* buffer, Index offset, Index size) {
- ULONG n_read;
- ThrowIfFailed(p_->stream_->Read(buffer + offset, size, &n_read));
- return n_read;
-}
-
-Index Win32FileStream::DoWrite(const std::byte* buffer, Index offset,
- Index size) {
- if (size < 0) {
- throw Exception("Size must be greater than 0.");
- }
-
- CheckClosed();
-
- ULONG n_written;
- ThrowIfFailed(p_->stream_->Write(buffer + offset, size, &n_written));
-
- return n_written;
-}
-
-void Win32FileStream::DoClose() {
- CRU_STREAM_BEGIN_CLOSE
-
- if (p_->stream_) {
- p_->stream_->Release();
- p_->stream_ = nullptr;
- }
-
-}
-} // namespace cru::platform::win
diff --git a/src/base/platform/win/Win32FileStreamPrivate.h b/src/base/platform/win/Win32FileStreamPrivate.h
deleted file mode 100644
index c49fa913..00000000
--- a/src/base/platform/win/Win32FileStreamPrivate.h
+++ /dev/null
@@ -1,13 +0,0 @@
-#include "cru/base/platform/win/Base.h"
-
-#include <objidl.h>
-
-namespace cru::platform::win {
-
-namespace details {
-struct Win32FileStreamPrivate {
- IStream* stream_ = nullptr;
-};
-} // namespace details
-
-} // namespace cru::platform::win
diff --git a/src/base/platform/win/Win32SubProcess.cpp b/src/base/platform/win/Win32SubProcess.cpp
new file mode 100644
index 00000000..c97f3d66
--- /dev/null
+++ b/src/base/platform/win/Win32SubProcess.cpp
@@ -0,0 +1,91 @@
+#include "cru/base/platform/win/Win32SubProcess.h"
+#include <processthreadsapi.h>
+#include <synchapi.h>
+#include "cru/base/StringUtil.h"
+#include "cru/base/SubProcess.h"
+
+#include <memory>
+#include <string_view>
+
+namespace cru::platform::win {
+using cru::string::ToUtf16;
+
+Win32SubProcessImpl::Win32SubProcessImpl() : exit_code_(0) {}
+
+Win32SubProcessImpl::~Win32SubProcessImpl() {}
+
+void Win32SubProcessImpl::PlatformCreateProcess(
+ const SubProcessStartInfo& start_info) {
+ auto check_error = [](int error, std::string message) {
+ if (error == 0) return;
+ std::unique_ptr<ErrnoException> inner(new ErrnoException(error));
+ throw SubProcessFailedToStartException(std::move(message),
+ std::move(inner));
+ };
+
+ auto app = ToUtf16(start_info.program);
+ // TODO: Space and quoting problem.
+ auto command_line =
+ app + L" " + ToUtf16(cru::string::Join(" ", start_info.arguments));
+
+ std::wstring env_str;
+ for (const auto& [key, value] : start_info.environments) {
+ env_str += ToUtf16(key) + L"=" + ToUtf16(value) + L"\0";
+ }
+ env_str += L"\0";
+
+ STARTUPINFO startup_info;
+ ZeroMemory(&startup_info, sizeof(startup_info));
+ startup_info.cb = sizeof(startup_info);
+
+ auto my_stdin = OpenUniDirectionalPipe(Win32PipeFlags::ReadInheritable);
+ auto my_stdout = OpenUniDirectionalPipe(Win32PipeFlags::WriteInheritable);
+ auto my_stderr = OpenUniDirectionalPipe(Win32PipeFlags::WriteInheritable);
+ startup_info.dwFlags = STARTF_USESTDHANDLES;
+ startup_info.hStdInput = my_stdin.read.Get();
+ startup_info.hStdOutput = my_stdout.write.Get();
+ startup_info.hStdError = my_stderr.write.Get();
+
+ CheckWinReturn(::CreateProcessW(app.c_str(), command_line.data(), nullptr,
+ nullptr, TRUE, CREATE_UNICODE_ENVIRONMENT,
+ static_cast<LPVOID>(env_str.data()), nullptr,
+ &startup_info, &process_));
+
+ stdin_stream_ = std::make_unique<Win32HandleStream>(std::move(my_stdin.write),
+ false, false, true);
+ stdout_stream_ = std::make_unique<Win32HandleStream>(
+ std::move(my_stdout.read), false, true, false);
+ stderr_stream_ = std::make_unique<Win32HandleStream>(
+ std::move(my_stderr.read), false, true, false);
+
+ stdout_buffer_stream_ =
+ std::make_unique<io::AutoReadStream>(stdout_stream_.get(), true, false);
+ stderr_buffer_stream_ =
+ std::make_unique<io::AutoReadStream>(stderr_stream_.get(), true, false);
+}
+
+SubProcessExitResult Win32SubProcessImpl::PlatformWaitForProcess() {
+ ::WaitForSingleObject(process_.hProcess, INFINITE);
+
+ DWORD exit_code;
+ CheckWinReturn(::GetExitCodeProcess(process_.hProcess, &exit_code));
+
+ return SubProcessExitResult::Normal(exit_code);
+}
+
+void Win32SubProcessImpl::PlatformKillProcess() {
+ CheckWinReturn(TerminateProcess(process_.hProcess, -1));
+}
+
+io::Stream* Win32SubProcessImpl::GetStdinStream() {
+ return stdin_stream_.get();
+}
+
+io::Stream* Win32SubProcessImpl::GetStdoutStream() {
+ return stdout_buffer_stream_.get();
+}
+
+io::Stream* Win32SubProcessImpl::GetStderrStream() {
+ return stderr_buffer_stream_.get();
+}
+} // namespace cru::platform::win