aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorYuqian Yang <crupest@crupest.life>2025-11-15 16:43:25 +0800
committerYuqian Yang <crupest@crupest.life>2025-11-16 00:01:49 +0800
commit246eb9266b9349b44cbe96f3f839124ab30cbb89 (patch)
tree31604c8a4764d3a601d56599e56c98d91bd97758
parentb92aa78ac19476049ab881b49c51b1a970a4a973 (diff)
downloadcru-246eb9266b9349b44cbe96f3f839124ab30cbb89.tar.gz
cru-246eb9266b9349b44cbe96f3f839124ab30cbb89.tar.bz2
cru-246eb9266b9349b44cbe96f3f839124ab30cbb89.zip
Impl win subprocess.
-rw-r--r--include/cru/base/Base.h1
-rw-r--r--include/cru/base/StringUtil.h16
-rw-r--r--include/cru/base/io/Base.h (renamed from include/cru/base/io/OpenFileFlag.h)6
-rw-r--r--include/cru/base/io/Stream.h2
-rw-r--r--include/cru/base/platform/win/Base.h113
-rw-r--r--include/cru/base/platform/win/Stream.h66
-rw-r--r--include/cru/base/platform/win/StreamConvert.h13
-rw-r--r--include/cru/base/platform/win/Win32FileStream.h42
-rw-r--r--include/cru/base/platform/win/Win32SubProcess.h41
-rw-r--r--include/cru/platform/graphics/direct2d/Base.h2
-rw-r--r--src/base/Base.cpp17
-rw-r--r--src/base/CMakeLists.txt5
-rw-r--r--src/base/SubProcess.cpp10
-rw-r--r--src/base/io/Base.cpp (renamed from src/base/io/OpenFileFlag.cpp)2
-rw-r--r--src/base/io/Stream.cpp24
-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
-rw-r--r--src/platform/graphics/direct2d/Brush.cpp2
-rw-r--r--src/platform/graphics/direct2d/Factory.cpp18
-rw-r--r--src/platform/graphics/direct2d/Font.cpp6
-rw-r--r--src/platform/graphics/direct2d/Geometry.cpp18
-rw-r--r--src/platform/graphics/direct2d/Image.cpp4
-rw-r--r--src/platform/graphics/direct2d/ImageFactory.cpp42
-rw-r--r--src/platform/graphics/direct2d/Painter.cpp4
-rw-r--r--src/platform/graphics/direct2d/TextLayout.cpp20
-rw-r--r--src/platform/graphics/direct2d/WindowRenderTarget.cpp10
-rw-r--r--test/base/CMakeLists.txt3
-rw-r--r--test/base/SubProcessTest.cpp4
-rw-r--r--test/base/platform/win/StreamConvertTest.cpp40
-rw-r--r--test/base/platform/win/StreamTest.cpp67
-rw-r--r--test/base/platform/win/Win32FileStreamTest.cpp38
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);
-}