#define WIN32_LEAN_AND_MEAN #include <Windows.h> #include <concrt.h> #include <string> #include <vector> #include <deque> #include <array> #include <map> #include <unordered_map> #include <set> #include <unordered_set> #include <Psapi.h> #include <functional> #include <algorithm> #include <regex> #include <memory> #include <optional> #include <thread> #include <mutex> #include <atomic> #include <filesystem> #include <iostream> #include <sstream> #include <locale> #include <cstdint> #include <list> #include <utility> #include <cassert> #include <variant> template <typename T, typename... Xs> struct ArrayImpl { using Type = std::tuple<T, Xs...>[]; }; template <typename T> struct ArrayImpl<T> { using Type = T[]; }; template <typename... Ts> using Array = typename ArrayImpl<Ts...>::Type; template <auto F> using Functor = std::integral_constant<std::remove_reference_t<decltype(F)>, F>; // shouldn't need remove_reference_t but MSVC is bugged struct PermissivePointer { template <typename T> operator T*() { return (T*)p; } void* p; }; template <typename HandleCloser = Functor<CloseHandle>> class AutoHandle { public: AutoHandle(HANDLE h) : h(h) {} operator HANDLE() { return h.get(); } PHANDLE operator&() { static_assert(sizeof(*this) == sizeof(HANDLE)); assert(!h); return (PHANDLE)this; } operator bool() { return h.get() != NULL && h.get() != INVALID_HANDLE_VALUE; } private: struct HandleCleaner { void operator()(void* h) { if (h != INVALID_HANDLE_VALUE) HandleCloser()(PermissivePointer{ h }); } }; std::unique_ptr<void, HandleCleaner> h; }; template<typename T, typename M = std::mutex> class Synchronized { public: template <typename... Args> Synchronized(Args&&... args) : contents(std::forward<Args>(args)...) {} struct Locker { T* operator->() { return &contents; } std::unique_lock<M> lock; T& contents; }; Locker Acquire() { return { std::unique_lock(m), contents }; } Locker operator->() { return Acquire(); } T Copy() { return Acquire().contents; } private: T contents; M m; }; template <typename F> void SpawnThread(const F& f) // works in DllMain unlike std thread { F* copy = new F(f); CloseHandle(CreateThread(nullptr, 0, [](void* copy) { (*(F*)copy)(); delete (F*)copy; return 0UL; }, copy, 0, nullptr)); } inline struct { inline static BYTE DUMMY[100]; template <typename T> operator T*() { static_assert(sizeof(T) < sizeof(DUMMY)); return (T*)DUMMY; } } DUMMY; inline auto Swallow = [](auto&&...) {}; template <typename T> std::optional<std::remove_cv_t<T>> Copy(T* ptr) { if (ptr) return *ptr; return {}; } template <typename T> inline auto FormatArg(T arg) { return arg; } template <typename C> inline auto FormatArg(const std::basic_string<C>& arg) { return arg.c_str(); } #pragma warning(push) #pragma warning(disable: 4996) template <typename... Args> inline std::string FormatString(const char* format, const Args&... args) { std::string buffer(snprintf(nullptr, 0, format, FormatArg(args)...), '\0'); sprintf(buffer.data(), format, FormatArg(args)...); return buffer; } template <typename... Args> inline std::wstring FormatString(const wchar_t* format, const Args&... args) { std::wstring buffer(_snwprintf(nullptr, 0, format, FormatArg(args)...), L'\0'); _swprintf(buffer.data(), format, FormatArg(args)...); return buffer; } #pragma warning(pop) inline void Trim(std::wstring& text) { text.erase(text.begin(), std::find_if_not(text.begin(), text.end(), iswspace)); text.erase(std::find_if_not(text.rbegin(), text.rend(), iswspace).base(), text.end()); } inline std::optional<std::wstring> StringToWideString(const std::string& text, UINT encoding) { std::vector<wchar_t> buffer(text.size() + 1); if (int length = MultiByteToWideChar(encoding, 0, text.c_str(), text.size() + 1, buffer.data(), buffer.size())) return std::wstring(buffer.data(), length - 1); return {}; } inline std::wstring StringToWideString(const std::string& text) { std::vector<wchar_t> buffer(text.size() + 1); MultiByteToWideChar(CP_UTF8, 0, text.c_str(), -1, buffer.data(), buffer.size()); return buffer.data(); } inline std::string WideStringToString(const std::wstring& text) { std::vector<char> buffer((text.size() + 1) * 4); WideCharToMultiByte(CP_UTF8, 0, text.c_str(), -1, buffer.data(), buffer.size(), nullptr, nullptr); return buffer.data(); } template <typename... Args> inline void TEXTRACTOR_MESSAGE(const wchar_t* format, const Args&... args) { MessageBoxW(NULL, FormatString(format, args...).c_str(), L"Textractor", MB_OK); } template <typename... Args> inline void TEXTRACTOR_DEBUG(const wchar_t* format, const Args&... args) { SpawnThread([=] { TEXTRACTOR_MESSAGE(format, args...); }); } void Localize(); #ifdef _DEBUG #define TEST(...) static auto _ = CreateThread(nullptr, 0, [](auto) { __VA_ARGS__; return 0UL; }, NULL, 0, nullptr) #else #define TEST(...) #endif inline std::optional<std::wstring> GetModuleFilename(DWORD processId, HMODULE module = NULL) { std::vector<wchar_t> buffer(MAX_PATH); if (AutoHandle<> process = OpenProcess(PROCESS_VM_READ | PROCESS_QUERY_INFORMATION, FALSE, processId)) if (GetModuleFileNameExW(process, module, buffer.data(), MAX_PATH)) return buffer.data(); return {}; } inline std::optional<std::wstring> GetModuleFilename(HMODULE module = NULL) { std::vector<wchar_t> buffer(MAX_PATH); if (GetModuleFileNameW(module, buffer.data(), MAX_PATH)) return buffer.data(); return {}; } inline std::vector<std::pair<DWORD, std::optional<std::wstring>>> GetAllProcesses() { std::vector<DWORD> processIds(10000); DWORD spaceUsed = 0; EnumProcesses(processIds.data(), 10000 * sizeof(DWORD), &spaceUsed); std::vector<std::pair<DWORD, std::optional<std::wstring>>> processes; for (int i = 0; i < spaceUsed / sizeof(DWORD); ++i) processes.push_back({ processIds[i], GetModuleFilename(processIds[i]) }); return processes; }