aboutsummaryrefslogtreecommitdiffstatshomepage
path: root/src/handle.cpp
blob: c94cb169e11e48af6742b018070f69abd6d5237f (plain) (blame)
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
144
145
146
147
148
149
150
151
152
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/buffer.hpp>
#include <winapi/error.hpp>
#include <winapi/handle.hpp>
#include <winapi/utils.hpp>

#include <boost/config.hpp>

#include <windows.h>

#include <cassert>
#include <cstddef>
#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_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");
    }
}

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;

    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