diff options
| author | Yuqian Yang <crupest@crupest.life> | 2025-11-15 16:43:25 +0800 |
|---|---|---|
| committer | Yuqian Yang <crupest@crupest.life> | 2025-11-16 00:01:49 +0800 |
| commit | 246eb9266b9349b44cbe96f3f839124ab30cbb89 (patch) | |
| tree | 31604c8a4764d3a601d56599e56c98d91bd97758 | |
| parent | b92aa78ac19476049ab881b49c51b1a970a4a973 (diff) | |
| download | cru-246eb9266b9349b44cbe96f3f839124ab30cbb89.tar.gz cru-246eb9266b9349b44cbe96f3f839124ab30cbb89.tar.bz2 cru-246eb9266b9349b44cbe96f3f839124ab30cbb89.zip | |
Impl win subprocess.
35 files changed, 743 insertions, 400 deletions
diff --git a/include/cru/base/Base.h b/include/cru/base/Base.h index af4d38ff..88682c96 100644 --- a/include/cru/base/Base.h +++ b/include/cru/base/Base.h @@ -129,7 +129,6 @@ class CRU_BASE_API Exception : public std::exception { protected: void SetMessage(std::string message) { message_ = std::move(message); } - void AppendMessage(const std::string& additional_message); void AppendMessage(std::string_view additional_message); void AppendMessage(std::optional<std::string_view> additional_message); diff --git a/include/cru/base/StringUtil.h b/include/cru/base/StringUtil.h index 5c1c4be7..d3afda1d 100644 --- a/include/cru/base/StringUtil.h +++ b/include/cru/base/StringUtil.h @@ -39,6 +39,22 @@ struct SplitOptions { static constexpr SplitOption RemoveSpace = SplitOption::FromOffset(2); }; +template <typename R> +std::string Join(std::string_view sep, const R& range) { + bool start = true; + std::string result; + for (const auto& s : range) { + if (start) { + result += s; + start = false; + } else { + result += sep; + result += s; + } + } + return result; +} + std::vector<std::string> CRU_BASE_API Split(std::string_view str, std::string_view sep, SplitOption options = {}); diff --git a/include/cru/base/io/OpenFileFlag.h b/include/cru/base/io/Base.h index 4a5789fb..d5e2abff 100644 --- a/include/cru/base/io/OpenFileFlag.h +++ b/include/cru/base/io/Base.h @@ -1,8 +1,14 @@ #pragma once +#include "../Base.h" #include "../Bitmask.h" namespace cru::io { +class CRU_BASE_API FileNotExistException : public Exception { + public: + using Exception::Exception; +}; + namespace details { struct OpenFileFlagTag {}; } // namespace details diff --git a/include/cru/base/io/Stream.h b/include/cru/base/io/Stream.h index 503ccd27..cbcb3ced 100644 --- a/include/cru/base/io/Stream.h +++ b/include/cru/base/io/Stream.h @@ -113,7 +113,7 @@ class CRU_BASE_API Stream : public Object { void CheckClosed() { StreamClosedException::Check(closed_); } private: - std::optional<SupportedOperations> supported_operations_; + SupportedOperations supported_operations_; bool closed_; }; } // namespace cru::io diff --git a/include/cru/base/platform/win/Base.h b/include/cru/base/platform/win/Base.h index 83420461..8d221c83 100644 --- a/include/cru/base/platform/win/Base.h +++ b/include/cru/base/platform/win/Base.h @@ -5,6 +5,10 @@ #endif #include "../../Base.h" +#include "../../Bitmask.h" + +#include <optional> +#include <string_view> #define NOMINMAX #define WIN32_LEAN_AND_MEAN @@ -17,8 +21,7 @@ namespace cru::platform::win { class CRU_BASE_API HResultError : public Exception { public: - explicit HResultError(HRESULT h_result); - HResultError(HRESULT h_result, std::string_view message); + explicit HResultError(HRESULT h_result, std::string_view message = ""); HRESULT GetHResult() const { return h_result_; } @@ -26,16 +29,14 @@ class CRU_BASE_API HResultError : public Exception { HRESULT h_result_; }; -inline void ThrowIfFailed(const HRESULT h_result) { - if (FAILED(h_result)) throw HResultError(h_result); -} - -inline void ThrowIfFailed(const HRESULT h_result, std::string_view message) { +inline void CheckHResult(const HRESULT h_result, + std::string_view message = "") { if (FAILED(h_result)) throw HResultError(h_result, message); } class CRU_BASE_API Win32Error : public Exception { public: + Win32Error(); // ::GetLastError is automatically called to get the error code. // The same as Win32Error(::GetLastError(), message) explicit Win32Error(std::string_view message); @@ -46,4 +47,102 @@ class CRU_BASE_API Win32Error : public Exception { private: DWORD error_code_; }; + +inline void CheckWinReturn(BOOL r, std::string_view message = "") { + if (r == FALSE) { + throw Win32Error(message); + } +} + +template <typename H, void (*CloseFunc)(H handle) noexcept> +class CRU_BASE_API TWin32Handle { + public: + TWin32Handle() : handle_(std::nullopt), auto_close_(false) {} + + TWin32Handle(H handle, bool auto_close) + : handle_(handle), auto_close_(auto_close) {} + + CRU_DELETE_COPY(TWin32Handle) + + TWin32Handle(TWin32Handle&& other) noexcept + : handle_(other.handle_), auto_close_(other.auto_close_) { + other.handle_ = std::nullopt; + other.auto_close_ = false; + } + + TWin32Handle& operator=(TWin32Handle&& other) noexcept { + if (this != &other) { + DoClose(); + handle_ = other.handle_; + auto_close_ = other.auto_close_; + other.handle_ = std::nullopt; + other.auto_close_ = false; + } + return *this; + } + + ~TWin32Handle() { DoClose(); } + + public: + bool IsValid() const { return handle_.has_value(); } + + void CheckValid( + std::optional<std::string_view> additional_message = std::nullopt) const { + if (!IsValid()) { + std::string message("The win32 handle is invalid."); + if (additional_message) { + message += " "; + message += *additional_message; + } + throw Exception(std::move(message)); + } + } + + H Get() const { + CheckValid(); + return *handle_; + } + + H Release() { + CheckValid(); + auto handle = *handle_; + handle_ = std::nullopt; + auto_close_ = false; + return handle; + } + + private: + void DoClose() { + if (auto_close_ && handle_) { + CloseFunc(*handle_); + } + } + + private: + std::optional<H> handle_; + bool auto_close_; +}; + +namespace details { +inline void MyCloseHandle(HANDLE handle) noexcept { ::CloseHandle(handle); } +} // namespace details + +using Win32Handle = TWin32Handle<HANDLE, details::MyCloseHandle>; + +struct UniDirectionalWin32PipeResult { + Win32Handle read; + Win32Handle write; +}; + +namespace details { +struct Win32PipeFlagTag; +} +using Win32PipeFlag = Bitmask<details::Win32PipeFlagTag>; +struct Win32PipeFlags { + constexpr static auto ReadInheritable = Win32PipeFlag::FromOffset(1); + constexpr static auto WriteInheritable = Win32PipeFlag::FromOffset(2); +}; + +CRU_BASE_API UniDirectionalWin32PipeResult +OpenUniDirectionalPipe(Win32PipeFlag flag = {}); } // namespace cru::platform::win diff --git a/include/cru/base/platform/win/Stream.h b/include/cru/base/platform/win/Stream.h new file mode 100644 index 00000000..e49bf48b --- /dev/null +++ b/include/cru/base/platform/win/Stream.h @@ -0,0 +1,66 @@ +#pragma once + +#ifndef _WIN32 +#error "This file can only be included on Windows." +#endif + +#include "../../io/Base.h" +#include "../../io/Stream.h" +#include "Base.h" + +#include <objidlbase.h> + +namespace cru::platform::win { +class CRU_BASE_API Win32HandleStream : public io::Stream { + public: + Win32HandleStream(std::string_view path, io::OpenFileFlag flags); + Win32HandleStream(HANDLE handle, bool auto_close, bool can_seek, + bool can_read, bool can_write); + Win32HandleStream(Win32Handle&& handle, bool can_seek, bool can_read, + bool can_write); + ~Win32HandleStream() override; + + protected: + Index DoSeek(Index offset, SeekOrigin origin) override; + Index DoRead(std::byte* buffer, Index offset, Index size) override; + Index DoWrite(const std::byte* buffer, Index offset, Index size) override; + + public: + const Win32Handle& GetHandle() { return handle_; } + + CRU_STREAM_IMPLEMENT_CLOSE_BY_DO_CLOSE + + private: + void DoClose(); + + private: + Win32Handle handle_; +}; + +CRU_BASE_API IStream* ToComStream(io::Stream* stream); + +class CRU_BASE_API ComStream : public io::Stream { + public: + ComStream(std::string_view path, io::OpenFileFlag flags); + ComStream(IStream* com_stream, bool auto_release, bool can_seek, + bool can_read, bool can_write); + ~ComStream() override; + + protected: + Index DoSeek(Index offset, SeekOrigin origin) override; + Index DoRead(std::byte* buffer, Index offset, Index size) override; + Index DoWrite(const std::byte* buffer, Index offset, Index size) override; + + public: + IStream* GetComStream() { return stream_; } + + CRU_STREAM_IMPLEMENT_CLOSE_BY_DO_CLOSE + + private: + void DoClose(); + + private: + IStream* stream_; + bool auto_release_; +}; +} // namespace cru::platform::win diff --git a/include/cru/base/platform/win/StreamConvert.h b/include/cru/base/platform/win/StreamConvert.h deleted file mode 100644 index 085e94e6..00000000 --- a/include/cru/base/platform/win/StreamConvert.h +++ /dev/null @@ -1,13 +0,0 @@ -#pragma once - -#ifndef _WIN32 -#error "This file can only be included on Windows." -#endif - -#include "../../io/Stream.h" - -#include <objidlbase.h> - -namespace cru::platform::win { -CRU_BASE_API IStream* ConvertStreamToComStream(io::Stream* stream); -} diff --git a/include/cru/base/platform/win/Win32FileStream.h b/include/cru/base/platform/win/Win32FileStream.h deleted file mode 100644 index 2980d059..00000000 --- a/include/cru/base/platform/win/Win32FileStream.h +++ /dev/null @@ -1,42 +0,0 @@ -#pragma once - -#ifndef _WIN32 -#error "This file can only be included on Windows." -#endif - -#include "../../io/OpenFileFlag.h" -#include "../../io/Stream.h" - -namespace cru::platform::win { -namespace details { -class Win32FileStreamPrivate; -} - -class CRU_BASE_API Win32FileStream : public io::Stream { - public: - Win32FileStream(std::string path, io::OpenFileFlag flags); - ~Win32FileStream() override; - - protected: - Index DoSeek(Index offset, SeekOrigin origin) override; - Index DoRead(std::byte* buffer, Index offset, Index size) override; - Index DoWrite(const std::byte* buffer, Index offset, Index size) override; - - public: - std::string GetPath() const { return path_; } - io::OpenFileFlag GetOpenFileFlags() const { return flags_; } - - details::Win32FileStreamPrivate* GetPrivate_() { return p_; } - - CRU_STREAM_IMPLEMENT_CLOSE_BY_DO_CLOSE - - private: - void DoClose(); - - private: - std::string path_; - io::OpenFileFlag flags_; - - details::Win32FileStreamPrivate* p_; -}; -} // namespace cru::platform::win diff --git a/include/cru/base/platform/win/Win32SubProcess.h b/include/cru/base/platform/win/Win32SubProcess.h new file mode 100644 index 00000000..e7189b02 --- /dev/null +++ b/include/cru/base/platform/win/Win32SubProcess.h @@ -0,0 +1,41 @@ +#pragma once + +#ifndef _WIN32 +#error "This file can only be included on Windows." +#endif + +#include "../../SubProcess.h" +#include "../../io/AutoReadStream.h" +#include "Base.h" +#include "Stream.h" + +namespace cru::platform::win { +class CRU_BASE_API Win32SubProcessImpl + : public Object, + public virtual IPlatformSubProcessImpl { + CRU_DEFINE_CLASS_LOG_TAG("cru::platform::win::Win32SubProcessImpl") + + public: + explicit Win32SubProcessImpl(); + ~Win32SubProcessImpl(); + + void PlatformCreateProcess(const SubProcessStartInfo& start_info) override; + SubProcessExitResult PlatformWaitForProcess() override; + void PlatformKillProcess() override; + + io::Stream* GetStdinStream() override; + io::Stream* GetStdoutStream() override; + io::Stream* GetStderrStream() override; + + private: + PROCESS_INFORMATION process_; + int exit_code_; + + std::unique_ptr<Win32HandleStream> stdin_stream_; + std::unique_ptr<Win32HandleStream> stdout_stream_; + std::unique_ptr<Win32HandleStream> stderr_stream_; + + std::unique_ptr<io::AutoReadStream> stdout_buffer_stream_; + std::unique_ptr<io::AutoReadStream> stderr_buffer_stream_; +}; +} // namespace cru::platform::win diff --git a/include/cru/platform/graphics/direct2d/Base.h b/include/cru/platform/graphics/direct2d/Base.h index f42d6193..52e590c2 100644 --- a/include/cru/platform/graphics/direct2d/Base.h +++ b/include/cru/platform/graphics/direct2d/Base.h @@ -21,7 +21,7 @@ namespace cru::platform::graphics::direct2d { using platform::win::HResultError; -using platform::win::ThrowIfFailed; +using platform::win::CheckHResult; inline D2D1_MATRIX_3X2_F Convert(const platform::Matrix& matrix) { D2D1_MATRIX_3X2_F m; diff --git a/src/base/Base.cpp b/src/base/Base.cpp index 49b0c462..7dd6337d 100644 --- a/src/base/Base.cpp +++ b/src/base/Base.cpp @@ -9,8 +9,6 @@ namespace cru { void UnreachableCode() { std::terminate(); } void NotImplemented() { std::terminate(); } -static constexpr auto NO_MESSAGE = "No message."; - Exception::Exception(std::string message, std::shared_ptr<std::exception> inner) : message_(std::move(message)), inner_(std::move(inner)) {} @@ -18,11 +16,8 @@ Exception::~Exception() {} const char* Exception::what() const noexcept { return message_.c_str(); } -void Exception::AppendMessage(const std::string& additional_message) { - AppendMessage(std::string_view(additional_message)); -} - void Exception::AppendMessage(std::string_view additional_message) { + if (additional_message.empty()) return; message_ += ' '; message_ += additional_message; } @@ -32,15 +27,17 @@ void Exception::AppendMessage( if (additional_message) AppendMessage(*additional_message); } -ErrnoException::ErrnoException() : ErrnoException(NO_MESSAGE) {} +ErrnoException::ErrnoException() : ErrnoException("") {} ErrnoException::ErrnoException(int errno_code) - : ErrnoException(NO_MESSAGE, errno_code) {} + : ErrnoException("", errno_code) {} ErrnoException::ErrnoException(std::string_view message) : ErrnoException(message, errno) {} ErrnoException::ErrnoException(std::string_view message, int errno_code) - : Exception(std::format("{} Errno is {}.", message, errno_code)), - errno_code_(errno_code) {} + : Exception(std::format("Errno is {}.", errno_code)), + errno_code_(errno_code) { + AppendMessage(message); +} } // namespace cru diff --git a/src/base/CMakeLists.txt b/src/base/CMakeLists.txt index d8830714..0840b130 100644 --- a/src/base/CMakeLists.txt +++ b/src/base/CMakeLists.txt @@ -5,6 +5,7 @@ add_library(CruBase StringUtil.cpp SubProcess.cpp io/AutoReadStream.cpp + io/Base.cpp io/BufferStream.cpp io/CFileStream.cpp io/Stream.cpp @@ -56,8 +57,8 @@ if (WIN32) platform/win/BridgeComStream.cpp platform/win/ComAutoInit.cpp platform/win/DebugLogTarget.cpp - platform/win/StreamConvert.cpp - platform/win/Win32FileStream.cpp + platform/win/Stream.cpp + platform/win/Win32SubProcess.cpp ) target_compile_definitions(CruBase PUBLIC UNICODE _UNICODE diff --git a/src/base/SubProcess.cpp b/src/base/SubProcess.cpp index bad5c20f..e37cdae9 100644 --- a/src/base/SubProcess.cpp +++ b/src/base/SubProcess.cpp @@ -2,7 +2,9 @@ #include <thread> -#if defined(__APPLE__) || defined(__unix) +#if defined(_WIN32) +#include "cru/base/platform/win/Win32SubProcess.h" +#elif defined(__APPLE__) || defined(__unix) #include "cru/base/platform/unix/PosixSpawnSubProcess.h" #endif @@ -150,7 +152,11 @@ SubProcessExitResult SubProcess::Call( } SubProcess::SubProcess(SubProcessStartInfo start_info) { -#if defined(__APPLE__) || defined(__unix) +#if defined(_WIN32) + platform_process_.reset(new PlatformSubProcess( + std::move(start_info), + std::make_shared<platform::win::Win32SubProcessImpl>())); +#elif defined(__APPLE__) || defined(__unix) platform_process_.reset(new PlatformSubProcess( std::move(start_info), std::make_shared<platform::unix::PosixSpawnSubProcessImpl>())); diff --git a/src/base/io/OpenFileFlag.cpp b/src/base/io/Base.cpp index 47069b29..d591336a 100644 --- a/src/base/io/OpenFileFlag.cpp +++ b/src/base/io/Base.cpp @@ -1,4 +1,4 @@ -#include "cru/base/io/OpenFileFlag.h" +#include "cru/base/io/Base.h" namespace cru::io { bool CheckOpenFileFlag(OpenFileFlag flags) { diff --git a/src/base/io/Stream.cpp b/src/base/io/Stream.cpp index 56db547a..c7286241 100644 --- a/src/base/io/Stream.cpp +++ b/src/base/io/Stream.cpp @@ -115,32 +115,32 @@ void Stream::Flush() { } bool Stream::DoCanSeek() { - if (supported_operations_->can_seek) { - return *supported_operations_->can_seek; + if (supported_operations_.can_seek) { + return *supported_operations_.can_seek; } else { throw Exception( - "Can seek is neither set in supported_operations nor implemeted in " - "virtual function."); + "Can seek is neither set in supported_operations nor overriden by " + "derived class."); } } bool Stream::DoCanRead() { - if (supported_operations_->can_read) { - return *supported_operations_->can_read; + if (supported_operations_.can_read) { + return *supported_operations_.can_read; } else { throw Exception( - "Can read is neither set in supported_operations nor implemeted in " - "virtual function."); + "Can read is neither set in supported_operations nor overriden by " + "derived class."); } } bool Stream::DoCanWrite() { - if (supported_operations_->can_write) { - return *supported_operations_->can_write; + if (supported_operations_.can_write) { + return *supported_operations_.can_write; } else { throw Exception( - "Can write is neither set in supported_operations nor implemeted in " - "virtual function."); + "Can write is neither set in supported_operations nor overriden by " + "derived class."); } } 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 diff --git a/src/platform/graphics/direct2d/Brush.cpp b/src/platform/graphics/direct2d/Brush.cpp index 7ab35273..c067ed89 100644 --- a/src/platform/graphics/direct2d/Brush.cpp +++ b/src/platform/graphics/direct2d/Brush.cpp @@ -4,7 +4,7 @@ namespace cru::platform::graphics::direct2d { D2DSolidColorBrush::D2DSolidColorBrush(DirectGraphicsFactory* factory) : DirectGraphicsResource(factory) { - ThrowIfFailed(factory->GetDefaultD2D1DeviceContext()->CreateSolidColorBrush( + CheckHResult(factory->GetDefaultD2D1DeviceContext()->CreateSolidColorBrush( Convert(color_), &brush_)); } diff --git a/src/platform/graphics/direct2d/Factory.cpp b/src/platform/graphics/direct2d/Factory.cpp index 414ef2ab..6be4f797 100644 --- a/src/platform/graphics/direct2d/Factory.cpp +++ b/src/platform/graphics/direct2d/Factory.cpp @@ -27,33 +27,33 @@ DirectGraphicsFactory::DirectGraphicsFactory() : DirectGraphicsResource(this) { Microsoft::WRL::ComPtr<ID3D11DeviceContext> d3d11_device_context; - ThrowIfFailed(D3D11CreateDevice( + CheckHResult(D3D11CreateDevice( nullptr, D3D_DRIVER_TYPE_HARDWARE, nullptr, creation_flags, feature_levels, ARRAYSIZE(feature_levels), D3D11_SDK_VERSION, &d3d11_device_, nullptr, &d3d11_device_context)); Microsoft::WRL::ComPtr<IDXGIDevice> dxgi_device; - ThrowIfFailed(d3d11_device_->QueryInterface(dxgi_device.GetAddressOf())); + CheckHResult(d3d11_device_->QueryInterface(dxgi_device.GetAddressOf())); - ThrowIfFailed(D2D1CreateFactory(D2D1_FACTORY_TYPE_SINGLE_THREADED, + CheckHResult(D2D1CreateFactory(D2D1_FACTORY_TYPE_SINGLE_THREADED, IID_PPV_ARGS(&d2d1_factory_))); - ThrowIfFailed(d2d1_factory_->CreateDevice(dxgi_device.Get(), &d2d1_device_)); + CheckHResult(d2d1_factory_->CreateDevice(dxgi_device.Get(), &d2d1_device_)); d2d1_device_context_ = CreateD2D1DeviceContext(); // Identify the physical adapter (GPU or card) this device is runs on. Microsoft::WRL::ComPtr<IDXGIAdapter> dxgi_adapter; - ThrowIfFailed(dxgi_device->GetAdapter(&dxgi_adapter)); + CheckHResult(dxgi_device->GetAdapter(&dxgi_adapter)); // Get the factory object that created the DXGI device. - ThrowIfFailed(dxgi_adapter->GetParent(IID_PPV_ARGS(&dxgi_factory_))); + CheckHResult(dxgi_adapter->GetParent(IID_PPV_ARGS(&dxgi_factory_))); - ThrowIfFailed(DWriteCreateFactory( + CheckHResult(DWriteCreateFactory( DWRITE_FACTORY_TYPE_SHARED, __uuidof(IDWriteFactory), reinterpret_cast<IUnknown**>(dwrite_factory_.GetAddressOf()))); - ThrowIfFailed(dwrite_factory_->GetSystemFontCollection( + CheckHResult(dwrite_factory_->GetSystemFontCollection( &dwrite_system_font_collection_)); image_factory_ = std::make_unique<WinImageFactory>(this); @@ -64,7 +64,7 @@ DirectGraphicsFactory::~DirectGraphicsFactory() {} Microsoft::WRL::ComPtr<ID2D1DeviceContext1> DirectGraphicsFactory::CreateD2D1DeviceContext() { Microsoft::WRL::ComPtr<ID2D1DeviceContext1> d2d1_device_context; - ThrowIfFailed(d2d1_device_->CreateDeviceContext( + CheckHResult(d2d1_device_->CreateDeviceContext( D2D1_DEVICE_CONTEXT_OPTIONS_NONE, &d2d1_device_context)); return d2d1_device_context; } diff --git a/src/platform/graphics/direct2d/Font.cpp b/src/platform/graphics/direct2d/Font.cpp index 50f7c266..18a4a2c7 100644 --- a/src/platform/graphics/direct2d/Font.cpp +++ b/src/platform/graphics/direct2d/Font.cpp @@ -16,13 +16,13 @@ DWriteFont::DWriteFont(DirectGraphicsFactory* factory, std::string font_family, throw platform::win::Win32Error( ::GetLastError(), "Failed to get locale when create dwrite font."); - ThrowIfFailed(factory->GetDWriteFactory()->CreateTextFormat( + CheckHResult(factory->GetDWriteFactory()->CreateTextFormat( string::ToUtf16(font_family_).c_str(), nullptr, DWRITE_FONT_WEIGHT_NORMAL, DWRITE_FONT_STYLE_NORMAL, DWRITE_FONT_STRETCH_NORMAL, font_size, buffer.data(), &text_format_)); - ThrowIfFailed(text_format_->SetTextAlignment(DWRITE_TEXT_ALIGNMENT_LEADING)); - ThrowIfFailed( + CheckHResult(text_format_->SetTextAlignment(DWRITE_TEXT_ALIGNMENT_LEADING)); + CheckHResult( text_format_->SetParagraphAlignment(DWRITE_PARAGRAPH_ALIGNMENT_NEAR)); } diff --git a/src/platform/graphics/direct2d/Geometry.cpp b/src/platform/graphics/direct2d/Geometry.cpp index 89b2cfd9..b4e9fcbf 100644 --- a/src/platform/graphics/direct2d/Geometry.cpp +++ b/src/platform/graphics/direct2d/Geometry.cpp @@ -7,8 +7,8 @@ namespace cru::platform::graphics::direct2d { D2DGeometryBuilder::D2DGeometryBuilder(DirectGraphicsFactory* factory) : DirectGraphicsResource(factory) { - ThrowIfFailed(factory->GetD2D1Factory()->CreatePathGeometry(&geometry_)); - ThrowIfFailed(geometry_->Open(&geometry_sink_)); + CheckHResult(factory->GetD2D1Factory()->CreatePathGeometry(&geometry_)); + CheckHResult(geometry_->Open(&geometry_sink_)); } void D2DGeometryBuilder::CheckValidation() { @@ -72,7 +72,7 @@ void D2DGeometryBuilder::CloseFigure(bool close) { std::unique_ptr<IGeometry> D2DGeometryBuilder::Build() { CheckValidation(); - ThrowIfFailed(geometry_sink_->Close()); + CheckHResult(geometry_sink_->Close()); geometry_sink_ = nullptr; auto geometry = std::make_unique<D2DGeometry>(GetDirectFactory(), std::move(geometry_)); @@ -86,20 +86,20 @@ D2DGeometry::D2DGeometry(DirectGraphicsFactory* factory, bool D2DGeometry::FillContains(const Point& point) { BOOL result; - ThrowIfFailed(geometry_->FillContainsPoint( + CheckHResult(geometry_->FillContainsPoint( Convert(point), D2D1::Matrix3x2F::Identity(), &result)); return result != 0; } Rect D2DGeometry::GetBounds() { D2D1_RECT_F bounds; - ThrowIfFailed(geometry_->GetBounds(D2D1::Matrix3x2F::Identity(), &bounds)); + CheckHResult(geometry_->GetBounds(D2D1::Matrix3x2F::Identity(), &bounds)); return Convert(bounds); } std::unique_ptr<IGeometry> D2DGeometry::Transform(const Matrix& matrix) { Microsoft::WRL::ComPtr<ID2D1TransformedGeometry> d2d1_geometry; - ThrowIfFailed(GetDirectFactory()->GetD2D1Factory()->CreateTransformedGeometry( + CheckHResult(GetDirectFactory()->GetD2D1Factory()->CreateTransformedGeometry( geometry_.Get(), Convert(matrix), &d2d1_geometry)); return std::make_unique<D2DGeometry>(GetDirectFactory(), std::move(d2d1_geometry)); @@ -107,11 +107,11 @@ std::unique_ptr<IGeometry> D2DGeometry::Transform(const Matrix& matrix) { std::unique_ptr<IGeometry> D2DGeometry::CreateStrokeGeometry(float width) { Microsoft::WRL::ComPtr<ID2D1PathGeometry> d2d1_geometry; - ThrowIfFailed( + CheckHResult( GetDirectFactory()->GetD2D1Factory()->CreatePathGeometry(&d2d1_geometry)); Microsoft::WRL::ComPtr<ID2D1GeometrySink> d2d1_geometry_sink; - ThrowIfFailed(d2d1_geometry->Open(&d2d1_geometry_sink)); - ThrowIfFailed( + CheckHResult(d2d1_geometry->Open(&d2d1_geometry_sink)); + CheckHResult( geometry_->Widen(width, nullptr, nullptr, d2d1_geometry_sink.Get())); d2d1_geometry_sink->Close(); diff --git a/src/platform/graphics/direct2d/Image.cpp b/src/platform/graphics/direct2d/Image.cpp index 1c4619b3..ca982796 100644 --- a/src/platform/graphics/direct2d/Image.cpp +++ b/src/platform/graphics/direct2d/Image.cpp @@ -19,7 +19,7 @@ float Direct2DImage::GetHeight() { return d2d_bitmap_->GetSize().height; } std::unique_ptr<IImage> Direct2DImage::CreateWithRect(const Rect& rect) { auto device_context = GetDirectFactory()->CreateD2D1DeviceContext(); Microsoft::WRL::ComPtr<ID2D1Bitmap1> bitmap; - ThrowIfFailed(device_context->CreateBitmap( + CheckHResult(device_context->CreateBitmap( D2D1::SizeU(rect.width, rect.height), nullptr, 0, D2D1::BitmapProperties1(D2D1_BITMAP_OPTIONS_TARGET, D2D1::PixelFormat(DXGI_FORMAT_B8G8R8A8_UNORM, @@ -28,7 +28,7 @@ std::unique_ptr<IImage> Direct2DImage::CreateWithRect(const Rect& rect) { device_context->SetTarget(bitmap.Get()); device_context->BeginDraw(); device_context->DrawBitmap(d2d_bitmap_.Get(), Convert(rect)); - ThrowIfFailed(device_context->EndDraw()); + CheckHResult(device_context->EndDraw()); return std::make_unique<Direct2DImage>(GetDirectFactory(), std::move(bitmap)); } diff --git a/src/platform/graphics/direct2d/ImageFactory.cpp b/src/platform/graphics/direct2d/ImageFactory.cpp index 5e667f48..894f3c22 100644 --- a/src/platform/graphics/direct2d/ImageFactory.cpp +++ b/src/platform/graphics/direct2d/ImageFactory.cpp @@ -1,5 +1,5 @@ #include "cru/platform/graphics/direct2d/ImageFactory.h" -#include "cru/base/platform/win/StreamConvert.h" +#include "cru/base/platform/win/Stream.h" #include "cru/platform/graphics/direct2d/Factory.h" #include "cru/platform/graphics/direct2d/Image.h" @@ -14,7 +14,7 @@ WinImageFactory::WinImageFactory(DirectGraphicsFactory* graphics_factory) HRESULT hr = CoCreateInstance(CLSID_WICImagingFactory, NULL, CLSCTX_INPROC_SERVER, IID_PPV_ARGS(&wic_imaging_factory_)); - ThrowIfFailed(hr); + CheckHResult(hr); } WinImageFactory::~WinImageFactory() {} @@ -25,17 +25,17 @@ std::unique_ptr<IImage> WinImageFactory::DecodeFromStream(io::Stream* stream) { HRESULT hr; Microsoft::WRL::ComPtr<IStream> com_stream( - platform::win::ConvertStreamToComStream(stream)); + platform::win::ToComStream(stream)); Microsoft::WRL::ComPtr<IWICBitmapDecoder> wic_bitmap_decoder; hr = wic_imaging_factory_->CreateDecoderFromStream( com_stream.Get(), NULL, WICDecodeMetadataCacheOnDemand, &wic_bitmap_decoder); - ThrowIfFailed(hr); + CheckHResult(hr); Microsoft::WRL::ComPtr<IWICBitmapFrameDecode> wic_bitmap_frame_decode; hr = wic_bitmap_decoder->GetFrame(0, &wic_bitmap_frame_decode); - ThrowIfFailed(hr); + CheckHResult(hr); auto d2d_context = graphics_factory->GetDefaultD2D1DeviceContext(); @@ -74,7 +74,7 @@ void WinImageFactory::EncodeToStream(IImage* image, io::Stream* stream, auto direct_image = CheckPlatform<Direct2DImage>(image, GetPlatformId()); Microsoft::WRL::ComPtr<IStream> com_stream( - platform::win::ConvertStreamToComStream(stream)); + platform::win::ToComStream(stream)); auto d2d_bitmap = direct_image->GetD2DBitmap(); auto size = d2d_bitmap->GetPixelSize(); @@ -85,36 +85,36 @@ void WinImageFactory::EncodeToStream(IImage* image, io::Stream* stream, Ensures(pixel_format.alphaMode == D2D1_ALPHA_MODE_PREMULTIPLIED); Microsoft::WRL::ComPtr<ID2D1Bitmap1> cpu_bitmap; - ThrowIfFailed(GetDirectFactory()->GetDefaultD2D1DeviceContext()->CreateBitmap( + CheckHResult(GetDirectFactory()->GetDefaultD2D1DeviceContext()->CreateBitmap( size, nullptr, 0, D2D1::BitmapProperties1( D2D1_BITMAP_OPTIONS_CANNOT_DRAW | D2D1_BITMAP_OPTIONS_CPU_READ, pixel_format, dpi_x, dpi_y), &cpu_bitmap)); - ThrowIfFailed(cpu_bitmap->CopyFromBitmap(nullptr, d2d_bitmap.Get(), nullptr)); + CheckHResult(cpu_bitmap->CopyFromBitmap(nullptr, d2d_bitmap.Get(), nullptr)); D2D1_MAPPED_RECT mapped_rect; - ThrowIfFailed(cpu_bitmap->Map(D2D1_MAP_OPTIONS_READ, &mapped_rect)); + CheckHResult(cpu_bitmap->Map(D2D1_MAP_OPTIONS_READ, &mapped_rect)); Microsoft::WRL::ComPtr<IWICBitmap> wic_bitmap; - ThrowIfFailed(wic_imaging_factory_->CreateBitmapFromMemory( + CheckHResult(wic_imaging_factory_->CreateBitmapFromMemory( size.width, size.height, GUID_WICPixelFormat32bppPBGRA, mapped_rect.pitch, mapped_rect.pitch * size.height, mapped_rect.bits, &wic_bitmap)); - ThrowIfFailed(cpu_bitmap->Unmap()); + CheckHResult(cpu_bitmap->Unmap()); Microsoft::WRL::ComPtr<IWICBitmapEncoder> wic_bitmap_encoder; - ThrowIfFailed(wic_imaging_factory_->CreateEncoder( + CheckHResult(wic_imaging_factory_->CreateEncoder( ConvertImageFormatToGUID(format), nullptr, &wic_bitmap_encoder)); - ThrowIfFailed(wic_bitmap_encoder->Initialize(com_stream.Get(), + CheckHResult(wic_bitmap_encoder->Initialize(com_stream.Get(), WICBitmapEncoderNoCache)); Microsoft::WRL::ComPtr<IWICBitmapFrameEncode> wic_bitmap_frame_encode; Microsoft::WRL::ComPtr<IPropertyBag2> property_bag; - ThrowIfFailed(wic_bitmap_encoder->CreateNewFrame(&wic_bitmap_frame_encode, + CheckHResult(wic_bitmap_encoder->CreateNewFrame(&wic_bitmap_frame_encode, &property_bag)); if (format == ImageFormat::Jpeg) { @@ -124,15 +124,15 @@ void WinImageFactory::EncodeToStream(IImage* image, io::Stream* stream, VariantInit(&varValue); varValue.vt = VT_R4; varValue.fltVal = quality; - ThrowIfFailed(property_bag->Write(1, &option, &varValue)); + CheckHResult(property_bag->Write(1, &option, &varValue)); } - ThrowIfFailed(wic_bitmap_frame_encode->Initialize(property_bag.Get())); - ThrowIfFailed(wic_bitmap_frame_encode->SetResolution(dpi_x, dpi_y)); - ThrowIfFailed(wic_bitmap_frame_encode->WriteSource(wic_bitmap.Get(), NULL)); - ThrowIfFailed(wic_bitmap_frame_encode->Commit()); + CheckHResult(wic_bitmap_frame_encode->Initialize(property_bag.Get())); + CheckHResult(wic_bitmap_frame_encode->SetResolution(dpi_x, dpi_y)); + CheckHResult(wic_bitmap_frame_encode->WriteSource(wic_bitmap.Get(), NULL)); + CheckHResult(wic_bitmap_frame_encode->Commit()); - ThrowIfFailed(wic_bitmap_encoder->Commit()); + CheckHResult(wic_bitmap_encoder->Commit()); } std::unique_ptr<IImage> WinImageFactory::CreateBitmap(int width, int height) { @@ -144,7 +144,7 @@ std::unique_ptr<IImage> WinImageFactory::CreateBitmap(int width, int height) { Microsoft::WRL::ComPtr<ID2D1Bitmap1> bitmap; auto d2d_context = graphics_factory->GetDefaultD2D1DeviceContext(); - ThrowIfFailed(d2d_context->CreateBitmap( + CheckHResult(d2d_context->CreateBitmap( D2D1::SizeU(width, height), nullptr, 0, D2D1::BitmapProperties1(D2D1_BITMAP_OPTIONS_TARGET, D2D1::PixelFormat(DXGI_FORMAT_B8G8R8A8_UNORM, diff --git a/src/platform/graphics/direct2d/Painter.cpp b/src/platform/graphics/direct2d/Painter.cpp index 1744a638..79401f6e 100644 --- a/src/platform/graphics/direct2d/Painter.cpp +++ b/src/platform/graphics/direct2d/Painter.cpp @@ -133,7 +133,7 @@ void D2DDeviceContextPainter::PushLayer(const Rect& bounds) { CheckValidation(); Microsoft::WRL::ComPtr<ID2D1Layer> layer; - ThrowIfFailed(device_context_->CreateLayer(&layer)); + CheckHResult(device_context_->CreateLayer(&layer)); device_context_->PushLayer(D2D1::LayerParameters(Convert(bounds)), layer.Get()); @@ -167,7 +167,7 @@ void D2DDeviceContextPainter::PopState() { void D2DDeviceContextPainter::EndDraw() { if (is_drawing_) { is_drawing_ = false; - ThrowIfFailed(device_context_->EndDraw()); + CheckHResult(device_context_->EndDraw()); DoEndDraw(); } } diff --git a/src/platform/graphics/direct2d/TextLayout.cpp b/src/platform/graphics/direct2d/TextLayout.cpp index ce584608..d347e0f7 100644 --- a/src/platform/graphics/direct2d/TextLayout.cpp +++ b/src/platform/graphics/direct2d/TextLayout.cpp @@ -14,7 +14,7 @@ DWriteTextLayout::DWriteTextLayout(DirectGraphicsFactory* factory, font_ = CheckPlatform<DWriteFont>(font, GetPlatformId()); utf16_text_ = string::ToUtf16(text_); - ThrowIfFailed(factory->GetDWriteFactory()->CreateTextLayout( + CheckHResult(factory->GetDWriteFactory()->CreateTextLayout( utf16_text_.c_str(), static_cast<UINT32>(utf16_text_.size()), font_->GetComInterface(), max_width_, max_height_, &text_layout_)); } @@ -26,7 +26,7 @@ std::string DWriteTextLayout::GetText() { return text_; } void DWriteTextLayout::SetText(std::string new_text) { text_ = std::move(new_text); utf16_text_ = string::ToUtf16(text_); - ThrowIfFailed(GetDirectFactory()->GetDWriteFactory()->CreateTextLayout( + CheckHResult(GetDirectFactory()->GetDWriteFactory()->CreateTextLayout( utf16_text_.c_str(), static_cast<UINT32>(utf16_text_.size()), font_->GetComInterface(), max_width_, max_height_, &text_layout_)); } @@ -37,7 +37,7 @@ std::shared_ptr<IFont> DWriteTextLayout::GetFont() { void DWriteTextLayout::SetFont(std::shared_ptr<IFont> font) { font_ = CheckPlatform<DWriteFont>(font, GetPlatformId()); - ThrowIfFailed(GetDirectFactory()->GetDWriteFactory()->CreateTextLayout( + CheckHResult(GetDirectFactory()->GetDWriteFactory()->CreateTextLayout( reinterpret_cast<const wchar_t*>(text_.c_str()), static_cast<UINT32>(text_.size()), font_->GetComInterface(), max_width_, max_height_, &text_layout_)); @@ -45,12 +45,12 @@ void DWriteTextLayout::SetFont(std::shared_ptr<IFont> font) { void DWriteTextLayout::SetMaxWidth(float max_width) { max_width_ = max_width; - ThrowIfFailed(text_layout_->SetMaxWidth(max_width_)); + CheckHResult(text_layout_->SetMaxWidth(max_width_)); } void DWriteTextLayout::SetMaxHeight(float max_height) { max_height_ = max_height; - ThrowIfFailed(text_layout_->SetMaxHeight(max_height_)); + CheckHResult(text_layout_->SetMaxHeight(max_height_)); } bool DWriteTextLayout::IsEditMode() { return edit_mode_; } @@ -93,7 +93,7 @@ Index DWriteTextLayout::GetLineCount() { Rect DWriteTextLayout::GetTextBounds(bool includingTrailingSpace) { DWRITE_TEXT_METRICS metrics; - ThrowIfFailed(text_layout_->GetMetrics(&metrics)); + CheckHResult(text_layout_->GetMetrics(&metrics)); return Rect{metrics.left, metrics.top, includingTrailingSpace ? metrics.widthIncludingTrailingWhitespace : metrics.width, @@ -120,13 +120,13 @@ std::vector<Rect> DWriteTextLayout::TextRangeRect( .Normalize(); DWRITE_TEXT_METRICS text_metrics; - ThrowIfFailed(text_layout_->GetMetrics(&text_metrics)); + CheckHResult(text_layout_->GetMetrics(&text_metrics)); const auto metrics_count = text_metrics.lineCount * text_metrics.maxBidiReorderingDepth; std::vector<DWRITE_HIT_TEST_METRICS> hit_test_metrics(metrics_count); UINT32 actual_count; - ThrowIfFailed(text_layout_->HitTestTextRange( + CheckHResult(text_layout_->HitTestTextRange( static_cast<UINT32>(text_range.position), static_cast<UINT32>(text_range.count), 0, 0, hit_test_metrics.data(), metrics_count, &actual_count)); @@ -153,7 +153,7 @@ Rect DWriteTextLayout::TextSinglePoint(Index position, bool trailing) { DWRITE_HIT_TEST_METRICS metrics; FLOAT left; FLOAT top; - ThrowIfFailed(text_layout_->HitTestTextPosition(static_cast<UINT32>(position), + CheckHResult(text_layout_->HitTestTextPosition(static_cast<UINT32>(position), static_cast<BOOL>(trailing), &left, &top, &metrics)); return Rect{left, top, 0, GetFont()->GetFontSize()}; @@ -164,7 +164,7 @@ TextHitTestResult DWriteTextLayout::HitTest(const Point& point) { BOOL inside; DWRITE_HIT_TEST_METRICS metrics; - ThrowIfFailed(text_layout_->HitTestPoint(point.x, point.y, &trailing, &inside, + CheckHResult(text_layout_->HitTestPoint(point.x, point.y, &trailing, &inside, &metrics)); TextHitTestResult result; diff --git a/src/platform/graphics/direct2d/WindowRenderTarget.cpp b/src/platform/graphics/direct2d/WindowRenderTarget.cpp index 51b6ab22..91d0270b 100644 --- a/src/platform/graphics/direct2d/WindowRenderTarget.cpp +++ b/src/platform/graphics/direct2d/WindowRenderTarget.cpp @@ -29,7 +29,7 @@ D2DWindowRenderTarget::D2DWindowRenderTarget(DirectGraphicsFactory* factory, swap_chain_desc.Flags = 0; // Get the final swap chain for this window from the DXGI factory. - ThrowIfFailed(dxgi_factory->CreateSwapChainForHwnd( + CheckHResult(dxgi_factory->CreateSwapChainForHwnd( d3d11_device, hwnd, &swap_chain_desc, nullptr, nullptr, &dxgi_swap_chain_)); @@ -44,13 +44,13 @@ void D2DWindowRenderTarget::ResizeBuffer(const int width, const int height) { // In order to resize buffer, we need to untarget the buffer first. d2d1_device_context_->SetTarget(nullptr); target_bitmap_ = nullptr; - ThrowIfFailed(dxgi_swap_chain_->ResizeBuffers(0, width, height, + CheckHResult(dxgi_swap_chain_->ResizeBuffers(0, width, height, DXGI_FORMAT_UNKNOWN, 0)); CreateTargetBitmap(); } void D2DWindowRenderTarget::Present() { - ThrowIfFailed(dxgi_swap_chain_->Present(1, 0)); + CheckHResult(dxgi_swap_chain_->Present(1, 0)); } void D2DWindowRenderTarget::CreateTargetBitmap() { @@ -58,7 +58,7 @@ void D2DWindowRenderTarget::CreateTargetBitmap() { // Direct2D needs the dxgi version of the backbuffer surface pointer. Microsoft::WRL::ComPtr<IDXGISurface> dxgi_back_buffer; - ThrowIfFailed( + CheckHResult( dxgi_swap_chain_->GetBuffer(0, IID_PPV_ARGS(&dxgi_back_buffer))); float dpi_x, dpi_y; @@ -71,7 +71,7 @@ void D2DWindowRenderTarget::CreateTargetBitmap() { // Get a D2D surface from the DXGI back buffer to use as the D2D render // target. - ThrowIfFailed(d2d1_device_context_->CreateBitmapFromDxgiSurface( + CheckHResult(d2d1_device_context_->CreateBitmapFromDxgiSurface( dxgi_back_buffer.Get(), &bitmap_properties, &target_bitmap_)); d2d1_device_context_->SetTarget(target_bitmap_.Get()); diff --git a/test/base/CMakeLists.txt b/test/base/CMakeLists.txt index f0a7f272..731f268c 100644 --- a/test/base/CMakeLists.txt +++ b/test/base/CMakeLists.txt @@ -32,8 +32,7 @@ endif() if (WIN32) target_sources(CruBaseTest PRIVATE - platform/win/StreamConvertTest.cpp - platform/win/Win32FileStreamTest.cpp + platform/win/StreamTest.cpp ) endif() diff --git a/test/base/SubProcessTest.cpp b/test/base/SubProcessTest.cpp index f241c125..192fdfcb 100644 --- a/test/base/SubProcessTest.cpp +++ b/test/base/SubProcessTest.cpp @@ -5,10 +5,6 @@ using cru::SubProcess; TEST_CASE("SubProcess", "[subprocess]") { -#ifdef _WIN32 - SKIP("SubProcess is not implemented on Windows for now."); -#endif - SECTION("echo should work.") { SubProcess process = SubProcess::Create(CRU_TEST_HELPER_ECHO_LOCATION, {"abc"}); diff --git a/test/base/platform/win/StreamConvertTest.cpp b/test/base/platform/win/StreamConvertTest.cpp deleted file mode 100644 index b16aa7b6..00000000 --- a/test/base/platform/win/StreamConvertTest.cpp +++ /dev/null @@ -1,40 +0,0 @@ -#include "cru/base/StringUtil.h" -#include "cru/base/io/OpenFileFlag.h" -#include "cru/base/platform/win/Base.h" -#include "cru/base/platform/win/StreamConvert.h" -#include "cru/base/platform/win/Win32FileStream.h" - -#include <catch2/catch_test_macros.hpp> - -#include <cstdio> -#include <filesystem> - -TEST_CASE("StreamConvert FileStreamWork", "[stream]") { - using namespace cru; - using namespace cru::io; - using namespace cru::platform::win; - - auto temp_file_path = - (std::filesystem::temp_directory_path() / "cru_test_temp.XXXXXX") - .native(); - _wmktemp(temp_file_path.data()); - - std::string path = string::ToUtf8(temp_file_path); - - Win32FileStream file(path, OpenFileFlags::Write | OpenFileFlags::Create); - file.Write("abc", 3); - file.Close(); - - Win32FileStream file2(path, OpenFileFlags::Read); - IStream* com_stream = ConvertStreamToComStream(&file2); - LARGE_INTEGER position; - position.QuadPart = 0; - ThrowIfFailed(com_stream->Seek(position, SEEK_SET, nullptr)); - auto buffer = std::make_unique<char[]>(3); - ThrowIfFailed(com_stream->Read(buffer.get(), 3, nullptr)); - REQUIRE(std::string_view(buffer.get(), 3) == "abc"); - com_stream->Release(); - file2.Close(); - - std::filesystem::remove(temp_file_path); -} diff --git a/test/base/platform/win/StreamTest.cpp b/test/base/platform/win/StreamTest.cpp new file mode 100644 index 00000000..e1a6e4fe --- /dev/null +++ b/test/base/platform/win/StreamTest.cpp @@ -0,0 +1,67 @@ +#include "cru/base/StringUtil.h" +#include "cru/base/platform/win/Stream.h" + +#include <catch2/catch_test_macros.hpp> + +#include <cstdio> +#include <filesystem> + +TEST_CASE("StreamConvert FileStreamWork", "[stream]") { + using namespace cru; + using namespace cru::io; + using namespace cru::platform::win; + + auto temp_file_path = + (std::filesystem::temp_directory_path() / "cru_test_temp.XXXXXX") + .native(); + _wmktemp(temp_file_path.data()); + + std::string path = string::ToUtf8(temp_file_path); + + ComStream file(path, OpenFileFlags::Write | OpenFileFlags::Create); + file.Write("abc", 3); + file.Close(); + + ComStream file2(path, OpenFileFlags::Read); + IStream* com_stream = ToComStream(&file2); + LARGE_INTEGER position; + position.QuadPart = 0; + CheckHResult(com_stream->Seek(position, SEEK_SET, nullptr)); + auto buffer = std::make_unique<char[]>(3); + CheckHResult(com_stream->Read(buffer.get(), 3, nullptr)); + REQUIRE(std::string_view(buffer.get(), 3) == "abc"); + com_stream->Release(); + file2.Close(); + + std::filesystem::remove(temp_file_path); +} + +TEST_CASE("ComStream Work", "[stream]") { + using namespace cru; + using namespace cru::io; + using namespace cru::platform::win; + + auto temp_file_path = + (std::filesystem::temp_directory_path() / "cru_test_temp.XXXXXX") + .native(); + _wmktemp(temp_file_path.data()); + + std::string path = string::ToUtf8(temp_file_path); + + ComStream file(path, OpenFileFlags::Write | OpenFileFlags::Create); + auto write_count = file.Write("abc", 3); + REQUIRE(write_count == 3); + file.Close(); + + REQUIRE(std::filesystem::file_size(path) == 3); + + ComStream file2(path, OpenFileFlags::Read); + auto buffer = std::make_unique<std::byte[]>(3); + auto read_count = file2.Read(buffer.get(), 3); + REQUIRE(read_count == 3); + REQUIRE(std::string_view(reinterpret_cast<const char*>(buffer.get()), 3) == + "abc"); + file2.Close(); + + std::filesystem::remove(temp_file_path); +} diff --git a/test/base/platform/win/Win32FileStreamTest.cpp b/test/base/platform/win/Win32FileStreamTest.cpp deleted file mode 100644 index 798320f7..00000000 --- a/test/base/platform/win/Win32FileStreamTest.cpp +++ /dev/null @@ -1,38 +0,0 @@ -#include "cru/base/StringUtil.h" -#include "cru/base/io/OpenFileFlag.h" -#include "cru/base/platform/win/Win32FileStream.h" - -#include <catch2/catch_test_macros.hpp> - -#include <cstdio> -#include <filesystem> - -TEST_CASE("Win32FileStream Work", "[stream]") { - using namespace cru; - using namespace cru::io; - using namespace cru::platform::win; - - auto temp_file_path = - (std::filesystem::temp_directory_path() / "cru_test_temp.XXXXXX") - .native(); - _wmktemp(temp_file_path.data()); - - std::string path = string::ToUtf8(temp_file_path); - - Win32FileStream file(path, OpenFileFlags::Write | OpenFileFlags::Create); - auto write_count = file.Write("abc", 3); - REQUIRE(write_count == 3); - file.Close(); - - REQUIRE(std::filesystem::file_size(path) == 3); - - Win32FileStream file2(path, OpenFileFlags::Read); - auto buffer = std::make_unique<std::byte[]>(3); - auto read_count = file2.Read(buffer.get(), 3); - REQUIRE(read_count == 3); - REQUIRE(std::string_view(reinterpret_cast<const char*>(buffer.get()), 3) == - "abc"); - file2.Close(); - - std::filesystem::remove(temp_file_path); -} |
