diff options
Diffstat (limited to 'src/base/platform')
| -rw-r--r-- | src/base/platform/win/Base.cpp | 45 | ||||
| -rw-r--r-- | src/base/platform/win/Stream.cpp | 231 | ||||
| -rw-r--r-- | src/base/platform/win/StreamConvert.cpp | 25 | ||||
| -rw-r--r-- | src/base/platform/win/Win32FileStream.cpp | 102 | ||||
| -rw-r--r-- | src/base/platform/win/Win32FileStreamPrivate.h | 13 | ||||
| -rw-r--r-- | src/base/platform/win/Win32SubProcess.cpp | 91 |
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 |
