aboutsummaryrefslogtreecommitdiffstatshomepage
path: root/src/process.cpp
blob: 08319faea762f804d523ab600a1656054d791230 (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
// Copyright (c) 2020 Egor Tensin <Egor.Tensin@gmail.com>
// This file is part of the "PDB repository" project.
// For details, see https://github.com/egor-tensin/pdb-repo.
// Distributed under the MIT License.

#include <pdb/all.hpp>

#include <SafeInt.hpp>
#include <boost/nowide/convert.hpp>

#include <windows.h>

#include <stdexcept>
#include <string>
#include <utility>
#include <vector>

namespace pdb {
namespace {

// Permissions required for MiniDumpWriteDump.
constexpr DWORD permissions = PROCESS_QUERY_INFORMATION | PROCESS_VM_READ;

Handle open_process(DWORD id) {
    Handle process{OpenProcess(permissions, FALSE, id)};
    if (!process) {
        throw error::windows(GetLastError());
    }
    return process;
}

class PathBuffer {
public:
    PathBuffer() : size{min_size} { data.resize(size); }

    DWORD get_size() const { return size; }

    wchar_t* get_data() { return data.data(); }

    void grow() {
        if (size < min_size) {
            size = min_size;
        } else {
            if (!SafeMultiply(size, 2, size)) {
                throw std::range_error{"couldn't allocate buffer sufficient for a file path"};
            }
        }
        data.resize(size);
    }

private:
    static constexpr DWORD min_size = 256;

    DWORD size;
    std::vector<wchar_t> data;
};

std::string get_current_executable_path(PathBuffer& buffer) {
    SetLastError(ERROR_SUCCESS);

    const auto ec = ::GetModuleFileNameW(NULL, buffer.get_data(), buffer.get_size());

    if (ec == 0) {
        throw error::windows(GetLastError());
    }

    if (ec == buffer.get_size() && GetLastError() == ERROR_INSUFFICIENT_BUFFER) {
        buffer.grow();
        return get_current_executable_path(buffer);
    }

    return boost::nowide::narrow(buffer.get_data());
}

std::string get_current_executable_path() {
    PathBuffer buffer;
    return get_current_executable_path(buffer);
}

std::string get_executable_path(const Handle& process, PathBuffer& buffer) {
    auto size = buffer.get_size();

    const auto ec = ::QueryFullProcessImageNameW(process.get(), 0, buffer.get_data(), &size);

    if (ec != 0) {
        return boost::nowide::narrow(buffer.get_data());
    }

    if (GetLastError() == ERROR_INSUFFICIENT_BUFFER) {
        buffer.grow();
        return get_executable_path(process, buffer);
    }

    throw error::windows(GetLastError());
}

std::string get_executable_path(const Handle& process) {
    PathBuffer buffer;
    return get_executable_path(process, buffer);
}

} // namespace

Process Process::current() {
    return Process{::GetCurrentProcessId(), Handle{::GetCurrentProcess()}};
}

Process Process::open(DWORD id) {
    return Process{id, open_process(id)};
}

Process::Process(Handle&& handle) : Process{::GetProcessId(handle.get()), std::move(handle)} {}

Process::Process(ID id, Handle&& handle) : id{id}, handle{std::move(handle)} {}

std::string Process::get_executable_path() const {
    if (handle.get() == ::GetCurrentProcess()) {
        return pdb::get_current_executable_path();
    } else {
        return pdb::get_executable_path(handle);
    }
}

} // namespace pdb