mirror of
https://github.com/diamante0018/MW3ServerFreezer.git
synced 2025-05-12 07:24:51 +00:00
Compare commits
No commits in common. "572b71da00a9aed29dd3bb832ccd074d7a28b67f" and "b1e468ba4f3fd982046a8df7228c50e0e02595d4" have entirely different histories.
572b71da00
...
b1e468ba4f
3
.github/workflows/build.yml
vendored
3
.github/workflows/build.yml
vendored
@ -14,7 +14,7 @@ concurrency:
|
|||||||
cancel-in-progress: true
|
cancel-in-progress: true
|
||||||
|
|
||||||
env:
|
env:
|
||||||
PREMAKE_VERSION: "5.0.0-beta6"
|
PREMAKE_VERSION: "5.0.0-beta5"
|
||||||
|
|
||||||
jobs:
|
jobs:
|
||||||
build:
|
build:
|
||||||
@ -27,7 +27,6 @@ jobs:
|
|||||||
- release
|
- release
|
||||||
compiler:
|
compiler:
|
||||||
- msvc
|
- msvc
|
||||||
- clang
|
|
||||||
steps:
|
steps:
|
||||||
- name: Check out files
|
- name: Check out files
|
||||||
uses: actions/checkout@main
|
uses: actions/checkout@main
|
||||||
|
2
deps/GSL
vendored
2
deps/GSL
vendored
@ -1 +1 @@
|
|||||||
Subproject commit 3325bbd33d24d1f8f5a0f69e782c92ad5a39a68e
|
Subproject commit 2828399820ef4928cc89b65605dca5dc68efca6e
|
@ -60,7 +60,7 @@ filter {}
|
|||||||
filter "configurations:release"
|
filter "configurations:release"
|
||||||
optimize "Size"
|
optimize "Size"
|
||||||
defines {"NDEBUG"}
|
defines {"NDEBUG"}
|
||||||
fatalwarnings {"All"}
|
flags {"FatalCompileWarnings"}
|
||||||
|
|
||||||
filter "toolset:msc*"
|
filter "toolset:msc*"
|
||||||
buildoptions "/GL"
|
buildoptions "/GL"
|
||||||
|
@ -1,14 +0,0 @@
|
|||||||
#!/bin/bash
|
|
||||||
|
|
||||||
# Navigate to the repository root
|
|
||||||
cd "$(dirname "$0")/.." || exit 2
|
|
||||||
|
|
||||||
# Set the clang-format binary (defaults to 'clang-format')
|
|
||||||
CLANG_FORMAT_BIN="${CLANG_FORMAT_BIN:-clang-format}"
|
|
||||||
|
|
||||||
# Find and format all .hpp and .cpp files in the src directory
|
|
||||||
find ./src \( -iname '*.hpp' -o -iname '*.cpp' \) -print0 |
|
|
||||||
xargs -0 "$CLANG_FORMAT_BIN" -i || {
|
|
||||||
echo "Error: clang-format failed." >&2
|
|
||||||
exit 1
|
|
||||||
}
|
|
@ -2,6 +2,7 @@
|
|||||||
#include "loader/component_loader.hpp"
|
#include "loader/component_loader.hpp"
|
||||||
|
|
||||||
#include <utils/hook.hpp>
|
#include <utils/hook.hpp>
|
||||||
|
#include <utils/thread.hpp>
|
||||||
#include <utils/flags.hpp>
|
#include <utils/flags.hpp>
|
||||||
#include <utils/concurrency.hpp>
|
#include <utils/concurrency.hpp>
|
||||||
|
|
||||||
@ -36,7 +37,8 @@ public:
|
|||||||
void post_start() override {
|
void post_start() override {
|
||||||
this->terminate_runner_ = false;
|
this->terminate_runner_ = false;
|
||||||
|
|
||||||
this->console_runner_ = std::thread([this] { this->runner(); });
|
this->console_runner_ = utils::thread::create_named_thread(
|
||||||
|
"Console IO", [this] { this->runner(); });
|
||||||
}
|
}
|
||||||
|
|
||||||
void post_unpack() override {
|
void post_unpack() override {
|
||||||
@ -75,30 +77,31 @@ private:
|
|||||||
int handles_[2]{};
|
int handles_[2]{};
|
||||||
|
|
||||||
void initialize() {
|
void initialize() {
|
||||||
this->console_thread_ = std::thread([this] {
|
this->console_thread_ =
|
||||||
if (!utils::flags::has_flag("noconsole")) {
|
utils::thread::create_named_thread("Console", [this] {
|
||||||
game::Sys_ShowConsole();
|
if (!utils::flags::has_flag("noconsole")) {
|
||||||
}
|
game::Sys_ShowConsole();
|
||||||
|
|
||||||
messages.access(
|
|
||||||
[&](message_queue&) { this->console_initialized_ = true; });
|
|
||||||
|
|
||||||
MSG msg;
|
|
||||||
while (!this->terminate_runner_) {
|
|
||||||
if (PeekMessageA(&msg, nullptr, NULL, NULL, PM_REMOVE)) {
|
|
||||||
if (msg.message == WM_QUIT) {
|
|
||||||
command::execute("quit", false);
|
|
||||||
break;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
TranslateMessage(&msg);
|
messages.access(
|
||||||
DispatchMessageA(&msg);
|
[&](message_queue&) { this->console_initialized_ = true; });
|
||||||
} else {
|
|
||||||
this->log_messages();
|
MSG msg;
|
||||||
std::this_thread::sleep_for(1ms);
|
while (!this->terminate_runner_) {
|
||||||
}
|
if (PeekMessageA(&msg, nullptr, NULL, NULL, PM_REMOVE)) {
|
||||||
}
|
if (msg.message == WM_QUIT) {
|
||||||
});
|
command::execute("quit", false);
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
TranslateMessage(&msg);
|
||||||
|
DispatchMessageA(&msg);
|
||||||
|
} else {
|
||||||
|
this->log_messages();
|
||||||
|
std::this_thread::sleep_for(1ms);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
void log_messages() const {
|
void log_messages() const {
|
||||||
|
@ -3,6 +3,7 @@
|
|||||||
|
|
||||||
#include <utils/concurrency.hpp>
|
#include <utils/concurrency.hpp>
|
||||||
#include <utils/hook.hpp>
|
#include <utils/hook.hpp>
|
||||||
|
#include <utils/thread.hpp>
|
||||||
|
|
||||||
#include "scheduler.hpp"
|
#include "scheduler.hpp"
|
||||||
|
|
||||||
@ -134,7 +135,7 @@ unsigned int thread_id;
|
|||||||
class component final : public component_interface {
|
class component final : public component_interface {
|
||||||
public:
|
public:
|
||||||
void post_unpack() override {
|
void post_unpack() override {
|
||||||
thread = std::thread([] {
|
thread = utils::thread::create_named_thread("Async Scheduler", [] {
|
||||||
while (!kill) {
|
while (!kill) {
|
||||||
execute(pipeline::async);
|
execute(pipeline::async);
|
||||||
std::this_thread::sleep_for(10ms);
|
std::this_thread::sleep_for(10ms);
|
||||||
|
@ -2,19 +2,6 @@
|
|||||||
#include "loader/component_loader.hpp"
|
#include "loader/component_loader.hpp"
|
||||||
|
|
||||||
namespace {
|
namespace {
|
||||||
std::string get_current_date() {
|
|
||||||
auto now = std::chrono::system_clock::now();
|
|
||||||
auto current_time = std::chrono::system_clock::to_time_t(now);
|
|
||||||
std::tm local_time{};
|
|
||||||
|
|
||||||
(void)localtime_s(&local_time, ¤t_time);
|
|
||||||
|
|
||||||
std::stringstream ss;
|
|
||||||
ss << std::put_time(&local_time, "%Y%m%d_%H%M%S");
|
|
||||||
|
|
||||||
return ss.str();
|
|
||||||
}
|
|
||||||
|
|
||||||
LONG WINAPI exception_handler(PEXCEPTION_POINTERS exception_info) {
|
LONG WINAPI exception_handler(PEXCEPTION_POINTERS exception_info) {
|
||||||
if (exception_info->ExceptionRecord->ExceptionCode == 0x406D1388) {
|
if (exception_info->ExceptionRecord->ExceptionCode == 0x406D1388) {
|
||||||
return EXCEPTION_CONTINUE_EXECUTION;
|
return EXCEPTION_CONTINUE_EXECUTION;
|
||||||
@ -34,8 +21,8 @@ LONG WINAPI exception_handler(PEXCEPTION_POINTERS exception_info) {
|
|||||||
| MiniDumpWithFullMemoryInfo //
|
| MiniDumpWithFullMemoryInfo //
|
||||||
| MiniDumpWithThreadInfo;
|
| MiniDumpWithThreadInfo;
|
||||||
|
|
||||||
const auto file_name =
|
const auto file_name = std::format("minidumps\\mw3-server-freezer_{}.dmp",
|
||||||
std::format("minidumps\\mw3-server-freezer_{}.dmp", get_current_date());
|
game::Sys_Milliseconds());
|
||||||
const auto file_handle = CreateFileA(
|
const auto file_handle = CreateFileA(
|
||||||
file_name.data(), GENERIC_WRITE | GENERIC_READ,
|
file_name.data(), GENERIC_WRITE | GENERIC_READ,
|
||||||
FILE_SHARE_READ | FILE_SHARE_WRITE, nullptr, CREATE_ALWAYS,
|
FILE_SHARE_READ | FILE_SHARE_WRITE, nullptr, CREATE_ALWAYS,
|
||||||
|
@ -24,25 +24,4 @@ void __declspec(naked) Dvar_SetVariant(dvar_t* /*dvar*/, DvarValue /*value*/,
|
|||||||
ret
|
ret
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
XAssetEntry* db_find_x_asset_entry(int type_, const char* name) {
|
|
||||||
static DWORD DB_FindXAssetEntry_t = 0x5c88a0;
|
|
||||||
XAssetEntry* result{};
|
|
||||||
|
|
||||||
__asm {
|
|
||||||
pushad
|
|
||||||
push name
|
|
||||||
mov edi, type_
|
|
||||||
call DB_FindXAssetEntry_t
|
|
||||||
add esp, 0x4
|
|
||||||
mov result, eax
|
|
||||||
popad
|
|
||||||
}
|
|
||||||
|
|
||||||
return result;
|
|
||||||
}
|
|
||||||
|
|
||||||
XAssetEntry* DB_FindXAssetEntry(int type, const char* name) {
|
|
||||||
return db_find_x_asset_entry(type, name);
|
|
||||||
}
|
|
||||||
} // namespace game
|
} // namespace game
|
||||||
|
@ -18,8 +18,6 @@ private:
|
|||||||
// clang-format off
|
// clang-format off
|
||||||
extern ScreenPlacement* ScrPlace_GetUnsafeFullPlacement();
|
extern ScreenPlacement* ScrPlace_GetUnsafeFullPlacement();
|
||||||
extern void Dvar_SetVariant(dvar_t* dvar, DvarValue value, DvarSetSource source);
|
extern void Dvar_SetVariant(dvar_t* dvar, DvarValue value, DvarSetSource source);
|
||||||
|
|
||||||
extern XAssetEntry* DB_FindXAssetEntry(int type, const char* name);
|
|
||||||
// clang-format on
|
// clang-format on
|
||||||
} // namespace game
|
} // namespace game
|
||||||
|
|
||||||
|
@ -9,59 +9,6 @@ typedef vec_t vec2_t[2];
|
|||||||
typedef vec_t vec3_t[3];
|
typedef vec_t vec3_t[3];
|
||||||
typedef vec_t vec4_t[4];
|
typedef vec_t vec4_t[4];
|
||||||
|
|
||||||
struct MaterialTechniqueSet {
|
|
||||||
const char* name;
|
|
||||||
unsigned char worldVertFormat;
|
|
||||||
unsigned char unused[2];
|
|
||||||
MaterialTechniqueSet* remappedTechniqueSet;
|
|
||||||
void* techniques[54];
|
|
||||||
};
|
|
||||||
|
|
||||||
struct __declspec(align(8)) GfxDrawSurfFields {
|
|
||||||
uint64_t unused : 1;
|
|
||||||
uint64_t primarySortKey : 6;
|
|
||||||
uint64_t surfType : 4;
|
|
||||||
uint64_t viewModelRender : 1;
|
|
||||||
uint64_t sceneLightIndex : 8;
|
|
||||||
uint64_t useHeroLighting : 1;
|
|
||||||
uint64_t prepass : 2;
|
|
||||||
uint64_t materialSortedIndex : 12;
|
|
||||||
uint64_t customIndex : 5;
|
|
||||||
uint64_t hasGfxEntIndex : 1;
|
|
||||||
uint64_t reflectionProbeIndex : 8;
|
|
||||||
uint64_t objectId : 15;
|
|
||||||
};
|
|
||||||
|
|
||||||
union GfxDrawSurf {
|
|
||||||
__declspec(align(8)) GfxDrawSurfFields fields;
|
|
||||||
__declspec(align(8)) uint64_t packed;
|
|
||||||
};
|
|
||||||
|
|
||||||
struct MaterialInfo {
|
|
||||||
const char* name;
|
|
||||||
unsigned char gameFlags;
|
|
||||||
unsigned char sortKey;
|
|
||||||
unsigned char textureAtlasRowCount;
|
|
||||||
unsigned char textureAtlasColumnCount;
|
|
||||||
GfxDrawSurf drawSurf;
|
|
||||||
unsigned int surfaceTypeBits;
|
|
||||||
};
|
|
||||||
|
|
||||||
struct Material {
|
|
||||||
MaterialInfo info;
|
|
||||||
char stateBitsEntry[54];
|
|
||||||
unsigned char textureCount;
|
|
||||||
unsigned char constantCount;
|
|
||||||
unsigned char stateBitsCount;
|
|
||||||
unsigned char stateFlags;
|
|
||||||
unsigned char cameraRegion;
|
|
||||||
MaterialTechniqueSet* techniqueSet;
|
|
||||||
void* textureTable;
|
|
||||||
void* constantTable;
|
|
||||||
void* stateBitsTable;
|
|
||||||
const char** subMaterials;
|
|
||||||
};
|
|
||||||
|
|
||||||
enum bdLogMessageType {
|
enum bdLogMessageType {
|
||||||
BD_LOG_INFO,
|
BD_LOG_INFO,
|
||||||
BD_LOG_WARNING,
|
BD_LOG_WARNING,
|
||||||
@ -314,8 +261,6 @@ struct usercmd_s {
|
|||||||
int remoteControlMove;
|
int remoteControlMove;
|
||||||
};
|
};
|
||||||
|
|
||||||
static_assert(offsetof(usercmd_s, buttons) == 0x4);
|
|
||||||
|
|
||||||
enum LocSelInputState {
|
enum LocSelInputState {
|
||||||
LOC_SEL_INPUT_NONE = 0,
|
LOC_SEL_INPUT_NONE = 0,
|
||||||
LOC_SEL_INPUT_CONFIRM = 1,
|
LOC_SEL_INPUT_CONFIRM = 1,
|
||||||
@ -422,21 +367,6 @@ struct Font_s {
|
|||||||
|
|
||||||
union XAssetHeader {
|
union XAssetHeader {
|
||||||
Font_s* font;
|
Font_s* font;
|
||||||
Material* material;
|
|
||||||
};
|
|
||||||
|
|
||||||
struct XAsset {
|
|
||||||
int type;
|
|
||||||
XAssetHeader header;
|
|
||||||
};
|
|
||||||
|
|
||||||
struct XAssetEntry {
|
|
||||||
XAsset asset;
|
|
||||||
char zoneIndex;
|
|
||||||
volatile char inuseMask;
|
|
||||||
bool printedMissingAsset;
|
|
||||||
unsigned __int16 nextHash;
|
|
||||||
unsigned __int16 nextOverride;
|
|
||||||
};
|
};
|
||||||
|
|
||||||
} // namespace game
|
} // namespace game
|
||||||
|
@ -80,9 +80,6 @@ WEAK symbol<int(unsigned __int64, const void*, unsigned int)>
|
|||||||
WEAK symbol<XAssetHeader(int type, const char* name, int allowCreateDefault)>
|
WEAK symbol<XAssetHeader(int type, const char* name, int allowCreateDefault)>
|
||||||
DB_FindXAssetHeader{0x4B25C0};
|
DB_FindXAssetHeader{0x4B25C0};
|
||||||
|
|
||||||
WEAK symbol<int(char* dest, int size, const char* fmt, ...)> Com_sprintf{
|
|
||||||
0x450CF0};
|
|
||||||
|
|
||||||
// Variables
|
// Variables
|
||||||
WEAK symbol<CmdArgs> cmd_args{0x1C96850};
|
WEAK symbol<CmdArgs> cmd_args{0x1C96850};
|
||||||
WEAK symbol<PlayerKeyState> playerKeys{0xB3A38C};
|
WEAK symbol<PlayerKeyState> playerKeys{0xB3A38C};
|
||||||
|
@ -10,16 +10,13 @@
|
|||||||
|
|
||||||
#include <DbgHelp.h>
|
#include <DbgHelp.h>
|
||||||
|
|
||||||
|
#include <algorithm>
|
||||||
#include <cassert>
|
#include <cassert>
|
||||||
#include <cctype>
|
#include <cctype>
|
||||||
#include <cstring>
|
#include <cstring>
|
||||||
|
|
||||||
#include <algorithm>
|
|
||||||
#include <chrono>
|
|
||||||
#include <iomanip>
|
|
||||||
#include <iostream>
|
|
||||||
#include <format>
|
#include <format>
|
||||||
#include <functional>
|
#include <functional>
|
||||||
|
#include <iostream>
|
||||||
#include <mutex>
|
#include <mutex>
|
||||||
#include <queue>
|
#include <queue>
|
||||||
#include <source_location>
|
#include <source_location>
|
||||||
|
@ -103,6 +103,24 @@ void hook::quick() {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
bool hook::iat(const nt::library module, const std::string& target_module,
|
||||||
|
const std::string& process, void* stub) {
|
||||||
|
if (!module.is_valid())
|
||||||
|
return false;
|
||||||
|
|
||||||
|
auto ptr = module.get_iat_entry(target_module, process);
|
||||||
|
if (!ptr)
|
||||||
|
return false;
|
||||||
|
|
||||||
|
DWORD protect;
|
||||||
|
VirtualProtect(ptr, sizeof(*ptr), PAGE_EXECUTE_READWRITE, &protect);
|
||||||
|
|
||||||
|
*ptr = stub;
|
||||||
|
|
||||||
|
VirtualProtect(ptr, sizeof(*ptr), protect, &protect);
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
hook* hook::uninstall(const bool unprotect) {
|
hook* hook::uninstall(const bool unprotect) {
|
||||||
std::lock_guard _(this->state_mutex_);
|
std::lock_guard _(this->state_mutex_);
|
||||||
|
|
||||||
|
@ -75,6 +75,9 @@ public:
|
|||||||
void* get_original() const;
|
void* get_original() const;
|
||||||
void quick();
|
void quick();
|
||||||
|
|
||||||
|
static bool iat(nt::library module, const std::string& target_module,
|
||||||
|
const std::string& process, void* stub);
|
||||||
|
|
||||||
static void nop(void* place, size_t length);
|
static void nop(void* place, size_t length);
|
||||||
static void nop(DWORD place, size_t length);
|
static void nop(DWORD place, size_t length);
|
||||||
|
|
||||||
|
@ -135,6 +135,62 @@ void library::free() {
|
|||||||
|
|
||||||
HMODULE library::get_handle() const { return this->module_; }
|
HMODULE library::get_handle() const { return this->module_; }
|
||||||
|
|
||||||
|
void** library::get_iat_entry(const std::string& module_name,
|
||||||
|
const std::string& proc_name) const {
|
||||||
|
if (!this->is_valid())
|
||||||
|
return nullptr;
|
||||||
|
|
||||||
|
const library other_module(module_name);
|
||||||
|
if (!other_module.is_valid())
|
||||||
|
return nullptr;
|
||||||
|
|
||||||
|
auto* const target_function = other_module.get_proc<void*>(proc_name);
|
||||||
|
if (!target_function)
|
||||||
|
return nullptr;
|
||||||
|
|
||||||
|
auto* header = this->get_optional_header();
|
||||||
|
if (!header)
|
||||||
|
return nullptr;
|
||||||
|
|
||||||
|
auto* import_descriptor = reinterpret_cast<PIMAGE_IMPORT_DESCRIPTOR>(
|
||||||
|
this->get_ptr() +
|
||||||
|
header->DataDirectory[IMAGE_DIRECTORY_ENTRY_IMPORT].VirtualAddress);
|
||||||
|
|
||||||
|
while (import_descriptor->Name) {
|
||||||
|
if (!_stricmp(
|
||||||
|
reinterpret_cast<char*>(this->get_ptr() + import_descriptor->Name),
|
||||||
|
module_name.data())) {
|
||||||
|
auto* original_thunk_data = reinterpret_cast<PIMAGE_THUNK_DATA>(
|
||||||
|
import_descriptor->OriginalFirstThunk + this->get_ptr());
|
||||||
|
auto* thunk_data = reinterpret_cast<PIMAGE_THUNK_DATA>(
|
||||||
|
import_descriptor->FirstThunk + this->get_ptr());
|
||||||
|
|
||||||
|
while (original_thunk_data->u1.AddressOfData) {
|
||||||
|
const size_t ordinal_number =
|
||||||
|
original_thunk_data->u1.AddressOfData & 0xFFFFFFF;
|
||||||
|
|
||||||
|
if (ordinal_number > 0xFFFF)
|
||||||
|
continue;
|
||||||
|
|
||||||
|
if (GetProcAddress(other_module.module_,
|
||||||
|
reinterpret_cast<char*>(ordinal_number)) ==
|
||||||
|
target_function) {
|
||||||
|
return reinterpret_cast<void**>(&thunk_data->u1.Function);
|
||||||
|
}
|
||||||
|
|
||||||
|
++original_thunk_data;
|
||||||
|
++thunk_data;
|
||||||
|
}
|
||||||
|
|
||||||
|
// break;
|
||||||
|
}
|
||||||
|
|
||||||
|
++import_descriptor;
|
||||||
|
}
|
||||||
|
|
||||||
|
return nullptr;
|
||||||
|
}
|
||||||
|
|
||||||
std::string load_resource(const int id) {
|
std::string load_resource(const int id) {
|
||||||
auto* const res = FindResource(library(), MAKEINTRESOURCE(id), RT_RCDATA);
|
auto* const res = FindResource(library(), MAKEINTRESOURCE(id), RT_RCDATA);
|
||||||
if (!res)
|
if (!res)
|
||||||
|
@ -48,12 +48,27 @@ public:
|
|||||||
|
|
||||||
HMODULE get_handle() const;
|
HMODULE get_handle() const;
|
||||||
|
|
||||||
|
template <typename T> T get_proc(const std::string& process) const {
|
||||||
|
if (!this->is_valid())
|
||||||
|
return T{};
|
||||||
|
return reinterpret_cast<T>(GetProcAddress(this->module_, process.data()));
|
||||||
|
}
|
||||||
|
|
||||||
|
template <typename T> std::function<T> get(const std::string& process) const {
|
||||||
|
if (!this->is_valid())
|
||||||
|
return std::function<T>();
|
||||||
|
return reinterpret_cast<T*>(this->get_proc<void*>(process));
|
||||||
|
}
|
||||||
|
|
||||||
std::vector<PIMAGE_SECTION_HEADER> get_section_headers() const;
|
std::vector<PIMAGE_SECTION_HEADER> get_section_headers() const;
|
||||||
|
|
||||||
PIMAGE_NT_HEADERS get_nt_headers() const;
|
PIMAGE_NT_HEADERS get_nt_headers() const;
|
||||||
PIMAGE_DOS_HEADER get_dos_header() const;
|
PIMAGE_DOS_HEADER get_dos_header() const;
|
||||||
PIMAGE_OPTIONAL_HEADER get_optional_header() const;
|
PIMAGE_OPTIONAL_HEADER get_optional_header() const;
|
||||||
|
|
||||||
|
void** get_iat_entry(const std::string& module_name,
|
||||||
|
const std::string& proc_name) const;
|
||||||
|
|
||||||
private:
|
private:
|
||||||
HMODULE module_;
|
HMODULE module_;
|
||||||
};
|
};
|
||||||
|
105
src/common/utils/thread.cpp
Normal file
105
src/common/utils/thread.cpp
Normal file
@ -0,0 +1,105 @@
|
|||||||
|
#include <thread>
|
||||||
|
#include "nt.hpp"
|
||||||
|
#include "string.hpp"
|
||||||
|
|
||||||
|
#include "thread.hpp"
|
||||||
|
|
||||||
|
#include <TlHelp32.h>
|
||||||
|
|
||||||
|
#include <gsl/gsl>
|
||||||
|
|
||||||
|
namespace utils::thread {
|
||||||
|
bool set_name(const HANDLE t, const std::string& name) {
|
||||||
|
const nt::library kernel32("kernel32.dll");
|
||||||
|
if (!kernel32) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
const auto set_description =
|
||||||
|
kernel32.get_proc<HRESULT(WINAPI*)(HANDLE, PCWSTR)>(
|
||||||
|
"SetThreadDescription");
|
||||||
|
if (!set_description) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
return SUCCEEDED(set_description(t, string::convert(name).data()));
|
||||||
|
}
|
||||||
|
|
||||||
|
bool set_name(const DWORD id, const std::string& name) {
|
||||||
|
auto* const t = OpenThread(THREAD_SET_LIMITED_INFORMATION, FALSE, id);
|
||||||
|
if (!t)
|
||||||
|
return false;
|
||||||
|
|
||||||
|
const auto _ = gsl::finally([t]() { CloseHandle(t); });
|
||||||
|
|
||||||
|
return set_name(t, name);
|
||||||
|
}
|
||||||
|
|
||||||
|
bool set_name(std::thread& t, const std::string& name) {
|
||||||
|
return set_name(t.native_handle(), name);
|
||||||
|
}
|
||||||
|
|
||||||
|
bool set_name(const std::string& name) {
|
||||||
|
return set_name(GetCurrentThread(), name);
|
||||||
|
}
|
||||||
|
|
||||||
|
std::vector<DWORD> get_thread_ids() {
|
||||||
|
auto* const h =
|
||||||
|
CreateToolhelp32Snapshot(TH32CS_SNAPTHREAD, GetCurrentProcessId());
|
||||||
|
if (h == INVALID_HANDLE_VALUE) {
|
||||||
|
return {};
|
||||||
|
}
|
||||||
|
|
||||||
|
const auto _ = gsl::finally([h]() { CloseHandle(h); });
|
||||||
|
|
||||||
|
THREADENTRY32 entry{};
|
||||||
|
entry.dwSize = sizeof(entry);
|
||||||
|
if (!Thread32First(h, &entry)) {
|
||||||
|
return {};
|
||||||
|
}
|
||||||
|
|
||||||
|
std::vector<DWORD> ids;
|
||||||
|
|
||||||
|
do {
|
||||||
|
const auto check_size =
|
||||||
|
entry.dwSize < FIELD_OFFSET(THREADENTRY32, th32OwnerProcessID) +
|
||||||
|
sizeof(entry.th32OwnerProcessID);
|
||||||
|
entry.dwSize = sizeof(entry);
|
||||||
|
|
||||||
|
if (check_size && entry.th32OwnerProcessID == GetCurrentProcessId()) {
|
||||||
|
ids.emplace_back(entry.th32ThreadID);
|
||||||
|
}
|
||||||
|
} while (Thread32Next(h, &entry));
|
||||||
|
|
||||||
|
return ids;
|
||||||
|
}
|
||||||
|
|
||||||
|
void for_each_thread(const std::function<void(HANDLE)>& callback) {
|
||||||
|
const auto ids = get_thread_ids();
|
||||||
|
|
||||||
|
for (const auto& id : ids) {
|
||||||
|
auto* const thread = OpenThread(THREAD_ALL_ACCESS, FALSE, id);
|
||||||
|
if (thread != nullptr) {
|
||||||
|
const auto _ = gsl::finally([thread]() { CloseHandle(thread); });
|
||||||
|
|
||||||
|
callback(thread);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void suspend_other_threads() {
|
||||||
|
for_each_thread([](const HANDLE thread) {
|
||||||
|
if (GetThreadId(thread) != GetCurrentThreadId()) {
|
||||||
|
SuspendThread(thread);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
void resume_other_threads() {
|
||||||
|
for_each_thread([](const HANDLE thread) {
|
||||||
|
if (GetThreadId(thread) != GetCurrentThreadId()) {
|
||||||
|
ResumeThread(thread);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}
|
||||||
|
} // namespace utils::thread
|
21
src/common/utils/thread.hpp
Normal file
21
src/common/utils/thread.hpp
Normal file
@ -0,0 +1,21 @@
|
|||||||
|
#pragma once
|
||||||
|
|
||||||
|
namespace utils::thread {
|
||||||
|
bool set_name(HANDLE t, const std::string& name);
|
||||||
|
bool set_name(DWORD id, const std::string& name);
|
||||||
|
bool set_name(std::thread& t, const std::string& name);
|
||||||
|
bool set_name(const std::string& name);
|
||||||
|
|
||||||
|
template <typename... Args>
|
||||||
|
std::thread create_named_thread(const std::string& name, Args&&... args) {
|
||||||
|
auto t = std::thread(std::forward<Args>(args)...);
|
||||||
|
set_name(t, name);
|
||||||
|
return t;
|
||||||
|
}
|
||||||
|
|
||||||
|
std::vector<DWORD> get_thread_ids();
|
||||||
|
void for_each_thread(const std::function<void(HANDLE)>& callback);
|
||||||
|
|
||||||
|
void suspend_other_threads();
|
||||||
|
void resume_other_threads();
|
||||||
|
} // namespace utils::thread
|
Loading…
x
Reference in New Issue
Block a user