winapi_common
handle.cpp
1 // Copyright (c) 2020 Egor Tensin <Egor.Tensin@gmail.com>
2 // This file is part of the "winapi-common" project.
3 // For details, see https://github.com/egor-tensin/winapi-common.
4 // Distributed under the MIT License.
5 
6 #include <winapi/buffer.hpp>
7 #include <winapi/error.hpp>
8 #include <winapi/handle.hpp>
9 #include <winapi/utils.hpp>
10 
11 #include <windows.h>
12 
13 #include <cassert>
14 #include <cstddef>
15 #include <limits>
16 #include <sstream>
17 #include <stdexcept>
18 #include <utility>
19 
20 namespace winapi {
21 namespace {
22 
23 std::runtime_error write_file_incomplete(std::size_t expected, std::size_t actual) {
24  std::ostringstream oss;
25  oss << "WriteFile could only write " << actual << " bytes instead of " << expected;
26  return std::runtime_error{oss.str()};
27 }
28 
29 bool is_std_handle(HANDLE handle) {
30  return handle == ::GetStdHandle(STD_INPUT_HANDLE) ||
31  handle == ::GetStdHandle(STD_OUTPUT_HANDLE) ||
32  handle == ::GetStdHandle(STD_ERROR_HANDLE);
33 }
34 
35 } // namespace
36 
37 Handle::Handle(HANDLE impl) : m_impl{impl} {}
38 
39 bool Handle::is_valid() const {
40  return m_impl && is_valid(m_impl.get());
41 }
42 
43 bool Handle::is_valid(HANDLE handle) {
44  return handle != NULL && handle != INVALID_HANDLE_VALUE;
45 }
46 
47 void Handle::close() {
48  m_impl.reset();
49 }
50 
51 bool Handle::is_std() const {
52  return is_std_handle(m_impl.get());
53 }
54 
55 Handle Handle::std_in() {
56  return Handle{::GetStdHandle(STD_INPUT_HANDLE)};
57 }
58 
59 Handle Handle::std_out() {
60  return Handle{::GetStdHandle(STD_OUTPUT_HANDLE)};
61 }
62 
63 Handle Handle::std_err() {
64  return Handle{::GetStdHandle(STD_ERROR_HANDLE)};
65 }
66 
67 bool Handle::read_chunk(Buffer& buffer) const {
68  buffer.resize(max_chunk_size);
69  DWORD nb_read = 0;
70 
71  if (buffer.size() > std::numeric_limits<DWORD>::max())
72  throw std::range_error{"Read buffer is too large"};
73  const auto ret =
74  ::ReadFile(m_impl.get(), buffer.data(), static_cast<DWORD>(buffer.size()), &nb_read, NULL);
75 
76  buffer.resize(nb_read);
77 
78  if (ret) {
79  return nb_read != 0;
80  }
81 
82  const auto ec = GetLastError();
83 
84  switch (ec) {
85  case ERROR_BROKEN_PIPE:
86  // We've been reading from an anonymous pipe, and it's been closed.
87  return false;
88  default:
89  throw error::windows(ec, "ReadFile");
90  }
91 }
92 
93 Buffer Handle::read() const {
94  Buffer buffer;
95 
96  while (true) {
97  Buffer chunk;
98  const auto next = read_chunk(chunk);
99  buffer.add(chunk);
100 
101  if (!next) {
102  break;
103  }
104  }
105 
106  return buffer;
107 }
108 
109 void Handle::write(const void* data, std::size_t nb) const {
110  DWORD nb_written = 0;
111 
112  if (nb > std::numeric_limits<DWORD>::max())
113  throw std::range_error{"Write buffer is too large"};
114  const auto ret = ::WriteFile(m_impl.get(), data, static_cast<DWORD>(nb), &nb_written, NULL);
115 
116  if (!ret) {
117  throw error::windows(GetLastError(), "WriteFile");
118  }
119 
120  if (nb != nb_written) {
121  throw write_file_incomplete(nb, nb_written);
122  }
123 }
124 
125 void Handle::write(const Buffer& buffer) const {
126  write(buffer.data(), buffer.size());
127 }
128 
129 void Handle::inherit(bool yes) const {
130  if (!::SetHandleInformation(m_impl.get(), HANDLE_FLAG_INHERIT, yes ? 1 : 0)) {
131  throw error::windows(GetLastError(), "SetHandleInformation");
132  }
133 }
134 
135 void Handle::Close::operator()(HANDLE impl) const {
136  if (!is_valid(impl) || is_std_handle(impl))
137  return;
138  const auto ret = ::CloseHandle(impl);
139  assert(ret);
140  WINAPI_UNUSED_PARAMETER(ret);
141 }
142 
143 } // namespace winapi
Binary data container.
Definition: buffer.hpp:24
void add(const Buffer &src)
Definition: buffer.hpp:79
HANDLE wrapper.
Definition: handle.hpp:25
Make std::system_error work with GetLastError().