diff options
author | Egor Tensin <Egor.Tensin@gmail.com> | 2020-03-24 00:43:34 +0300 |
---|---|---|
committer | Egor Tensin <Egor.Tensin@gmail.com> | 2020-03-24 00:48:16 +0300 |
commit | eaf76a222e709f81e9d7c71b6c47ee172604af05 (patch) | |
tree | bf2fd37b9f5e4c6805d05be219d41d75156245cc | |
parent | Travis: incremental Docker builds (diff) | |
download | winapi-debug-eaf76a222e709f81e9d7c71b6c47ee172604af05.tar.gz winapi-debug-eaf76a222e709f81e9d7c71b6c47ee172604af05.zip |
add Process class
It's intended to be used for the future dump writing functionality, to
generate a meaningful file name, etc.
Also, add Boost.Nowide, which provides handy UTF-8 conversion functions.
I'm not sure about this, but things indicate that it's about to be
included in Boost.
-rw-r--r-- | .gitmodules | 3 | ||||
m--------- | 3rdparty/boost/nowide | 0 | ||||
-rw-r--r-- | CMakeLists.txt | 7 | ||||
-rw-r--r-- | include/pdb/all.hpp | 1 | ||||
-rw-r--r-- | include/pdb/process.hpp | 32 | ||||
-rw-r--r-- | src/process.cpp | 120 |
6 files changed, 160 insertions, 3 deletions
diff --git a/.gitmodules b/.gitmodules index 26c71b0..495304d 100644 --- a/.gitmodules +++ b/.gitmodules @@ -4,3 +4,6 @@ [submodule "3rdparty/microsoft/SafeInt"] path = 3rdparty/microsoft/SafeInt url = https://github.com/dcleblanc/SafeInt.git +[submodule "3rdparty/boost/nowide"] + path = 3rdparty/boost/nowide + url = https://github.com/boostorg/nowide.git diff --git a/3rdparty/boost/nowide b/3rdparty/boost/nowide new file mode 160000 +Subproject 7cf83cf3fcaaf0ee496300d8814fa03f627365e diff --git a/CMakeLists.txt b/CMakeLists.txt index 8af82e9..5ad6d0a 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -2,9 +2,10 @@ cmake_minimum_required(VERSION 3.5) # for Boost::* imported targets project(pdb_repo CXX) -include(cmake/cmake/common.cmake) - find_package(Boost REQUIRED) +add_subdirectory(3rdparty/boost/nowide) + +include(cmake/cmake/common.cmake) file(GLOB_RECURSE pdb_repo_include "include/*.hpp") file(GLOB_RECURSE pdb_repo_src "src/*.cpp") @@ -12,7 +13,7 @@ add_library(pdb_repo ${pdb_repo_include} ${pdb_repo_src}) target_compile_definitions(pdb_repo PUBLIC _NO_CVCONST_H) target_include_directories(pdb_repo PUBLIC include/) target_include_directories(pdb_repo SYSTEM PUBLIC 3rdparty/microsoft/SafeInt) -target_link_libraries(pdb_repo PUBLIC Boost::boost) +target_link_libraries(pdb_repo PUBLIC Boost::boost PRIVATE Boost::nowide) target_link_libraries(pdb_repo PRIVATE dbghelp) if(MINGW) diff --git a/include/pdb/all.hpp b/include/pdb/all.hpp index 36a68d3..0794362 100644 --- a/include/pdb/all.hpp +++ b/include/pdb/all.hpp @@ -11,6 +11,7 @@ #include "error.hpp" #include "handle.hpp" #include "module.hpp" +#include "process.hpp" #include "repo.hpp" #include "symbol.hpp" #include "utils/file.hpp" diff --git a/include/pdb/process.hpp b/include/pdb/process.hpp new file mode 100644 index 0000000..3ddb6b4 --- /dev/null +++ b/include/pdb/process.hpp @@ -0,0 +1,32 @@ +#pragma once + +#include "handle.hpp" + +#include <windows.h> + +#include <string> + +namespace pdb { + +class Process { +public: + using ID = DWORD; + + static Process current(); + static Process open(ID); + + ID get_id() const { return id; } + + const Handle& get_handle() const { return handle; } + + std::string get_executable_path() const; + +private: + explicit Process(Handle&&); + Process(ID, Handle&&); + + ID id; + Handle handle; +}; + +} // namespace pdb diff --git a/src/process.cpp b/src/process.cpp new file mode 100644 index 0000000..c2a32f4 --- /dev/null +++ b/src/process.cpp @@ -0,0 +1,120 @@ +#include "pdb/all.hpp" + +#include <boost/nowide/convert.hpp> + +#include <SafeInt.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 |