1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
|
// 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/buffer.hpp>
#include <winapi/error.hpp>
#include <winapi/handle.hpp>
#include <winapi/utils.hpp>
#include <windows.h>
#include <cassert>
#include <cstddef>
#include <limits>
#include <sstream>
#include <stdexcept>
#include <utility>
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_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} {}
bool Handle::is_valid() const {
return m_impl && is_valid(m_impl.get());
}
bool Handle::is_valid(HANDLE handle) {
return handle != NULL && handle != INVALID_HANDLE_VALUE;
}
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;
if (buffer.size() > std::numeric_limits<DWORD>::max())
throw std::range_error{"Read buffer is too large"};
const auto ret =
::ReadFile(m_impl.get(), buffer.data(), static_cast<DWORD>(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");
}
}
Buffer Handle::read() const {
Buffer buffer;
while (true) {
Buffer chunk;
const auto next = read_chunk(chunk);
buffer.add(chunk);
if (!next) {
break;
}
}
return buffer;
}
void Handle::write(const void* data, std::size_t nb) const {
DWORD nb_written = 0;
if (nb > std::numeric_limits<DWORD>::max())
throw std::range_error{"Write buffer is too large"};
const auto ret = ::WriteFile(m_impl.get(), data, static_cast<DWORD>(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_valid(impl) || is_std_handle(impl))
return;
const auto ret = ::CloseHandle(impl);
assert(ret);
WINAPI_UNUSED_PARAMETER(ret);
}
} // namespace winapi
|