#pragma once #include "types.hpp" #include #include #include #include #ifdef _WIN32 #define WIN32_LEAN_AND_MEAN #ifndef NOMINMAX #define NOMINMAX #endif #include #include namespace util { namespace threads { auto get_all_threads(u32 pid) -> std::vector; auto get_all_threads() -> std::vector; auto get_other_threads() -> std::vector; auto suspend_threads(const std::vector &thread_ids) -> std::vector; auto resume_threads(const std::vector &thread_ids) -> bool; /// suspends all threads, only then executes function, and resumes threads /// returns `nullopt` if not all threads were suspended. template auto with_suspended_threads(F &&fn) -> std::optional { auto threads = get_other_threads(); auto suspended_threads = suspend_threads(threads); const auto suspended_all_threads = suspended_threads.size() == threads.size(); std::optional result = std::nullopt; if (suspended_all_threads) { result = fn(); } return result; } } // namespace threads namespace processes { auto get_process_id(std::string_view name) -> std::optional; auto get_process_id(std::wstring_view name) -> std::optional; } // namespace processes /// impl namespace threads { inline auto get_all_threads(u32 pid) -> std::vector { std::vector threads; const auto snapshot = CreateToolhelp32Snapshot(TH32CS_SNAPTHREAD, 0); if (snapshot != INVALID_HANDLE_VALUE) { THREADENTRY32 t; t.dwSize = sizeof(t); if (Thread32First(snapshot, &t)) { if (t.dwSize >= FIELD_OFFSET(THREADENTRY32, th32OwnerProcessID) + sizeof(t.th32OwnerProcessID)) { if (t.th32OwnerProcessID == pid) { threads.push_back(t.th32ThreadID); } } } } return threads; } inline auto get_all_threads() -> std::vector { return get_all_threads(GetCurrentProcessId()); } inline auto get_other_threads() -> std::vector { auto threads = get_all_threads(GetCurrentProcessId()); threads.erase( std::remove_if(threads.begin(), threads.end(), [](auto &&id) { return id == GetCurrentThreadId(); }), threads.end()); return threads; } inline auto resume_threads(const std::vector &thread_ids) -> bool { // return false if any threads failed to resume return std::count_if(thread_ids.begin(), thread_ids.end(), [](auto &&id) { if (auto handle = OpenThread(THREAD_SUSPEND_RESUME, false, id)) { const auto result = ResumeThread(handle); CloseHandle(handle); return result != (u32)-1; } return false; }) == thread_ids.size(); } inline auto suspend_threads(const std::vector &thread_ids) -> std::vector { std::vector suspended_threads; for (auto &&id : thread_ids) { if (auto handle = OpenThread(THREAD_SUSPEND_RESUME, false, id)) { if (SuspendThread(handle) != (u32)-1) { suspended_threads.push_back(id); } CloseHandle(handle); } } return suspended_threads; } } // namespace threads namespace processes { inline auto get_process_id(std::string_view name) -> std::optional { auto snapshot = CreateToolhelp32Snapshot(TH32CS_SNAPPROCESS, 0); std::optional result = std::nullopt; PROCESSENTRY32 process_entry; if (Process32First(snapshot, &process_entry)) { do { const auto process_name = std::string_view(process_entry.szExeFile); if (name == process_name) { result = process_entry.th32ProcessID; } } while (Process32Next(snapshot, &process_entry)); } CloseHandle(snapshot); return result; } inline auto get_process_id(std::wstring_view name) -> std::optional { auto snapshot = CreateToolhelp32Snapshot(TH32CS_SNAPPROCESS, 0); std::optional result = std::nullopt; PROCESSENTRY32W process_entry; if (Process32FirstW(snapshot, &process_entry)) { do { const auto process_name = std::wstring_view(process_entry.szExeFile); if (name == process_name) { result = process_entry.th32ProcessID; } } while (Process32NextW(snapshot, &process_entry)); } CloseHandle(snapshot); return result; } } // namespace processes } // namespace util #endif