aboutsummaryrefslogtreecommitdiffstatshomepage
path: root/src/handle.cpp
diff options
context:
space:
mode:
authorEgor Tensin <Egor.Tensin@gmail.com>2020-10-16 10:09:55 +0300
committerEgor Tensin <Egor.Tensin@gmail.com>2020-10-16 10:12:47 +0300
commita632bb8e796be52929f1541d305910d704e55076 (patch)
tree6a03b50fcb16ef1713f6cbbe5f2377b2fdc10990 /src/handle.cpp
parentecho: make it really UTF-16 (diff)
downloadwinapi-common-a632bb8e796be52929f1541d305910d704e55076.tar.gz
winapi-common-a632bb8e796be52929f1541d305910d704e55076.zip
Process: support pipe redirection
Diffstat (limited to 'src/handle.cpp')
-rw-r--r--src/handle.cpp153
1 files changed, 153 insertions, 0 deletions
diff --git a/src/handle.cpp b/src/handle.cpp
new file mode 100644
index 0000000..a6ab3a5
--- /dev/null
+++ b/src/handle.cpp
@@ -0,0 +1,153 @@
+// Copyright (c) 2020 Egor Tensin <Egor.Tensin@gmail.com>
+// This file is part of the "winapi-common" project.
+// For details, see https://github.com/egor-tensin/winapi-common.
+// Distributed under the MIT License.
+
+#include <winapi/error.hpp>
+#include <winapi/handle.hpp>
+#include <winapi/workarounds.hpp>
+
+#include <boost/config.hpp>
+
+#include <windows.h>
+
+#include <cassert>
+#include <cstddef>
+#include <sstream>
+#include <stdexcept>
+
+namespace winapi {
+namespace {
+
+std::runtime_error write_file_incomplete(std::size_t expected, std::size_t actual) {
+ std::ostringstream oss;
+ oss << "WriteFile could only write " << actual << " bytes instead of " << expected;
+ return std::runtime_error{oss.str()};
+}
+
+bool is_invalid_handle(HANDLE handle) {
+ return handle == NULL || handle == INVALID_HANDLE_VALUE;
+}
+
+bool is_std_handle(HANDLE handle) {
+ return handle == ::GetStdHandle(STD_INPUT_HANDLE) ||
+ handle == ::GetStdHandle(STD_OUTPUT_HANDLE) ||
+ handle == ::GetStdHandle(STD_ERROR_HANDLE);
+}
+
+} // namespace
+
+Handle::Handle(HANDLE impl) : m_impl{impl} {}
+
+Handle::Handle(Handle&& other) BOOST_NOEXCEPT_OR_NOTHROW {
+ swap(other);
+}
+
+Handle& Handle::operator=(Handle other) BOOST_NOEXCEPT_OR_NOTHROW {
+ swap(other);
+ return *this;
+}
+
+void Handle::swap(Handle& other) BOOST_NOEXCEPT_OR_NOTHROW {
+ using std::swap;
+ swap(m_impl, other.m_impl);
+}
+
+bool Handle::is_invalid() const {
+ return !m_impl || is_invalid_handle(m_impl.get());
+}
+
+void Handle::close() {
+ m_impl.reset();
+}
+
+bool Handle::is_std() const {
+ return is_std_handle(m_impl.get());
+}
+
+Handle Handle::std_in() {
+ return Handle{::GetStdHandle(STD_INPUT_HANDLE)};
+}
+
+Handle Handle::std_out() {
+ return Handle{::GetStdHandle(STD_OUTPUT_HANDLE)};
+}
+
+Handle Handle::std_err() {
+ return Handle{::GetStdHandle(STD_ERROR_HANDLE)};
+}
+
+bool Handle::read_chunk(Buffer& buffer) const {
+ buffer.resize(max_chunk_size);
+ DWORD nb_read = 0;
+
+ const auto ret = ::ReadFile(m_impl.get(), buffer.data(), buffer.size(), &nb_read, NULL);
+
+ buffer.resize(nb_read);
+
+ if (ret) {
+ return nb_read != 0;
+ }
+
+ const auto ec = GetLastError();
+
+ switch (ec) {
+ case ERROR_BROKEN_PIPE:
+ // We've been reading from an anonymous pipe, and it's been closed.
+ return false;
+ default:
+ throw error::windows(ec, "ReadFile");
+ }
+}
+
+Handle::Buffer Handle::read() const {
+ Buffer buffer;
+ Buffer chunk;
+
+ while (true) {
+ const auto next = read_chunk(chunk);
+
+ buffer.reserve(buffer.size() + chunk.size());
+ buffer.insert(buffer.cend(), chunk.cbegin(), chunk.cend());
+
+ if (!next) {
+ break;
+ }
+ }
+
+ return buffer;
+}
+
+void Handle::write(const void* data, std::size_t nb) const {
+ DWORD nb_written = 0;
+
+ const auto ret = ::WriteFile(m_impl.get(), data, nb, &nb_written, NULL);
+
+ if (!ret) {
+ throw error::windows(GetLastError(), "WriteFile");
+ }
+
+ if (nb != nb_written) {
+ throw write_file_incomplete(nb, nb_written);
+ }
+}
+
+void Handle::write(const Buffer& buffer) const {
+ write(buffer.data(), buffer.size());
+}
+
+void Handle::inherit(bool yes) const {
+ if (!::SetHandleInformation(m_impl.get(), HANDLE_FLAG_INHERIT, yes ? 1 : 0)) {
+ throw error::windows(GetLastError(), "SetHandleInformation");
+ }
+}
+
+void Handle::Close::operator()(HANDLE impl) const {
+ if (is_invalid_handle(impl) || is_std_handle(impl))
+ return;
+ const auto ret = ::CloseHandle(impl);
+ assert(ret);
+ WINAPI_UNUSED_PARAMETER(ret);
+}
+
+} // namespace winapi