654 lines
23 KiB
C++
654 lines
23 KiB
C++
#if defined(_WIN32)
|
||
|
||
#include "windows_include.h"
|
||
|
||
#include <algorithm>
|
||
#include <cctype>
|
||
#include <cstdint>
|
||
#include <cstdlib>
|
||
#include <cwchar>
|
||
#include <filesystem>
|
||
#include <iomanip>
|
||
#include <iostream>
|
||
#include <memory>
|
||
#include <sstream>
|
||
#include <string>
|
||
#include <system_error>
|
||
#include <thread>
|
||
#include <type_traits>
|
||
#include <vector>
|
||
|
||
#include <dbghelp.h>
|
||
#include <objbase.h>
|
||
#include <oleauto.h>
|
||
#include <psapi.h>
|
||
#include <wbemidl.h>
|
||
|
||
|
||
#include "../Base/global_include.h"
|
||
#include "../system/export.h"
|
||
#include "Core/Base/JSON.h"
|
||
|
||
namespace Psc {
|
||
namespace {
|
||
std::wstring utf8_to_wide_impl(const std::string& input) {
|
||
if (input.empty()) {
|
||
return L"";
|
||
}
|
||
|
||
const int size = MultiByteToWideChar(CP_UTF8, 0, input.data(), static_cast<int>(input.size()), nullptr, 0);
|
||
if (size <= 0) {
|
||
return L"";
|
||
}
|
||
|
||
std::wstring result(static_cast<size_t>(size), L'\0');
|
||
MultiByteToWideChar(CP_UTF8, 0, input.data(), static_cast<int>(input.size()), result.data(), size);
|
||
return result;
|
||
}
|
||
|
||
std::string wide_to_utf8_impl(const wchar_t* input, int input_len = -1) {
|
||
if (!input) {
|
||
return "";
|
||
}
|
||
|
||
const int size = WideCharToMultiByte(CP_UTF8, 0, input, input_len, nullptr, 0, nullptr, nullptr);
|
||
if (size <= 0) {
|
||
return "";
|
||
}
|
||
|
||
std::string result(static_cast<size_t>(size), '\0');
|
||
WideCharToMultiByte(CP_UTF8, 0, input, input_len, result.data(), size, nullptr, nullptr);
|
||
if (input_len == -1 && !result.empty() && result.back() == '\0') {
|
||
result.pop_back();
|
||
}
|
||
return result;
|
||
}
|
||
|
||
std::string wide_to_utf8_impl(const std::wstring& input) {
|
||
return wide_to_utf8_impl(input.c_str(), static_cast<int>(input.size()));
|
||
}
|
||
|
||
std::string codepage_to_utf8(const std::string& input, UINT codepage) {
|
||
if (input.empty()) {
|
||
return "";
|
||
}
|
||
|
||
const int wide_len = MultiByteToWideChar(codepage, 0, input.data(), static_cast<int>(input.size()), nullptr, 0);
|
||
if (wide_len <= 0) {
|
||
return "";
|
||
}
|
||
|
||
std::wstring wide(static_cast<size_t>(wide_len), L'\0');
|
||
MultiByteToWideChar(codepage, 0, input.data(), static_cast<int>(input.size()), wide.data(), wide_len);
|
||
return wide_to_utf8_impl(wide);
|
||
}
|
||
|
||
std::string utf8_to_codepage(const std::string& input, UINT codepage) {
|
||
if (input.empty()) {
|
||
return "";
|
||
}
|
||
|
||
const std::wstring wide = utf8_to_wide_impl(input);
|
||
if (wide.empty()) {
|
||
return "";
|
||
}
|
||
|
||
const int len = WideCharToMultiByte(codepage, 0, wide.data(), static_cast<int>(wide.size()), nullptr, 0, nullptr, nullptr);
|
||
if (len <= 0) {
|
||
return "";
|
||
}
|
||
|
||
std::string result(static_cast<size_t>(len), '\0');
|
||
WideCharToMultiByte(codepage, 0, wide.data(), static_cast<int>(wide.size()), result.data(), len, nullptr, nullptr);
|
||
return result;
|
||
}
|
||
|
||
std::wstring read_env_w(const wchar_t* name) {
|
||
DWORD required = GetEnvironmentVariableW(name, nullptr, 0);
|
||
if (required == 0) {
|
||
return L"";
|
||
}
|
||
|
||
std::wstring result(required, L'\0');
|
||
DWORD written = GetEnvironmentVariableW(name, result.data(), required);
|
||
if (written == 0) {
|
||
return L"";
|
||
}
|
||
|
||
result.resize(written);
|
||
return result;
|
||
}
|
||
|
||
HANDLE thread_to_win32_handle(std::thread* thread) noexcept {
|
||
if (!thread) {
|
||
return nullptr;
|
||
}
|
||
|
||
using Native = std::thread::native_handle_type;
|
||
if constexpr (std::is_pointer<Native>::value) {
|
||
return reinterpret_cast<HANDLE>(thread->native_handle());
|
||
} else if constexpr (std::is_integral<Native>::value) {
|
||
return reinterpret_cast<HANDLE>(thread->native_handle());
|
||
} else {
|
||
// MinGW 的 libstdc++ 在某些配置下使用 winpthreads,native_handle_type 不是 Win32 HANDLE。
|
||
return nullptr;
|
||
}
|
||
}
|
||
|
||
struct Bstr_Guard {
|
||
explicit Bstr_Guard(const wchar_t* value) : value(SysAllocString(value)) {}
|
||
~Bstr_Guard() { if (value) SysFreeString(value); }
|
||
Bstr_Guard(const Bstr_Guard&) = delete;
|
||
Bstr_Guard& operator=(const Bstr_Guard&) = delete;
|
||
BSTR value{};
|
||
};
|
||
|
||
template <typename T>
|
||
void release_com(T*& ptr) noexcept {
|
||
if (ptr) {
|
||
ptr->Release();
|
||
ptr = nullptr;
|
||
}
|
||
}
|
||
|
||
std::string fallback_stack_trace_without_dbghelp(void* const* stack, USHORT frames) {
|
||
std::ostringstream oss;
|
||
oss << "Stack trace addresses only; DbgHelp unavailable.\n";
|
||
for (USHORT i = 0; i < frames; ++i) {
|
||
oss << "Frame " << std::setw(2) << i << ": " << stack[i] << '\n';
|
||
}
|
||
return oss.str();
|
||
}
|
||
}
|
||
|
||
std::string get_stack_trace() {
|
||
constexpr int max_frames = 64;
|
||
void* stack[max_frames]{};
|
||
const USHORT frames = CaptureStackBackTrace(0, max_frames, stack, nullptr);
|
||
|
||
HMODULE dbghelp = LoadLibraryW(L"DbgHelp.dll");
|
||
if (!dbghelp) {
|
||
return fallback_stack_trace_without_dbghelp(stack, frames);
|
||
}
|
||
|
||
using SymInitializeT = BOOL(WINAPI*)(HANDLE, PCSTR, BOOL);
|
||
using SymSetOptionsT = DWORD(WINAPI*)(DWORD);
|
||
using SymFromAddrT = BOOL(WINAPI*)(HANDLE, DWORD64, PDWORD64, PSYMBOL_INFO);
|
||
using SymGetLineFromAddr64T = BOOL(WINAPI*)(HANDLE, DWORD64, PDWORD, PIMAGEHLP_LINE64);
|
||
using SymCleanupT = BOOL(WINAPI*)(HANDLE);
|
||
|
||
auto pSymInitialize = reinterpret_cast<SymInitializeT>(GetProcAddress(dbghelp, "SymInitialize"));
|
||
auto pSymSetOptions = reinterpret_cast<SymSetOptionsT>(GetProcAddress(dbghelp, "SymSetOptions"));
|
||
auto pSymFromAddr = reinterpret_cast<SymFromAddrT>(GetProcAddress(dbghelp, "SymFromAddr"));
|
||
auto pSymGetLineFromAddr64 = reinterpret_cast<SymGetLineFromAddr64T>(GetProcAddress(dbghelp, "SymGetLineFromAddr64"));
|
||
auto pSymCleanup = reinterpret_cast<SymCleanupT>(GetProcAddress(dbghelp, "SymCleanup"));
|
||
|
||
if (!pSymInitialize || !pSymSetOptions || !pSymFromAddr || !pSymGetLineFromAddr64) {
|
||
FreeLibrary(dbghelp);
|
||
return fallback_stack_trace_without_dbghelp(stack, frames);
|
||
}
|
||
|
||
HANDLE process = GetCurrentProcess();
|
||
std::ostringstream oss;
|
||
|
||
if (!pSymInitialize(process, nullptr, TRUE)) {
|
||
FreeLibrary(dbghelp);
|
||
return fallback_stack_trace_without_dbghelp(stack, frames);
|
||
}
|
||
|
||
pSymSetOptions(SYMOPT_LOAD_LINES | SYMOPT_UNDNAME);
|
||
|
||
constexpr DWORD max_name_len = MAX_SYM_NAME;
|
||
std::vector<unsigned char> symbol_buffer(sizeof(SYMBOL_INFO) + max_name_len * sizeof(char));
|
||
auto* symbol = reinterpret_cast<SYMBOL_INFO*>(symbol_buffer.data());
|
||
symbol->SizeOfStruct = sizeof(SYMBOL_INFO);
|
||
symbol->MaxNameLen = max_name_len;
|
||
|
||
for (USHORT i = 0; i < frames; ++i) {
|
||
DWORD64 address = reinterpret_cast<DWORD64>(stack[i]);
|
||
DWORD64 displacement64 = 0;
|
||
|
||
oss << "Frame " << std::right << std::setw(2) << i << ": ";
|
||
|
||
if (pSymFromAddr(process, address, &displacement64, symbol)) {
|
||
oss << std::left << std::setw(30) << symbol->Name << " ";
|
||
} else {
|
||
oss << std::left << std::setw(30) << "<unknown>" << " ";
|
||
}
|
||
|
||
IMAGEHLP_LINE64 line{};
|
||
line.SizeOfStruct = sizeof(IMAGEHLP_LINE64);
|
||
DWORD displacement = 0;
|
||
if (pSymGetLineFromAddr64(process, address, &displacement, &line)) {
|
||
oss << line.FileName << ':' << line.LineNumber << " at 0x" << std::hex << address << std::dec << '\n';
|
||
} else {
|
||
oss << "at 0x" << std::hex << address << std::dec << '\n';
|
||
}
|
||
}
|
||
|
||
if (pSymCleanup) {
|
||
pSymCleanup(process);
|
||
}
|
||
FreeLibrary(dbghelp);
|
||
return oss.str();
|
||
}
|
||
|
||
void set_current_thread_name(const std::string& name) {
|
||
const std::wstring wname = utf8_to_wide_impl(name);
|
||
if (wname.empty()) {
|
||
return;
|
||
}
|
||
|
||
using SetThreadDescriptionT = HRESULT(WINAPI*)(HANDLE, PCWSTR);
|
||
HMODULE kernel32 = GetModuleHandleW(L"Kernel32.dll");
|
||
auto pSetThreadDescription = kernel32
|
||
? reinterpret_cast<SetThreadDescriptionT>(GetProcAddress(kernel32, "SetThreadDescription"))
|
||
: nullptr;
|
||
|
||
if (pSetThreadDescription) {
|
||
(void)pSetThreadDescription(GetCurrentThread(), wname.c_str());
|
||
}
|
||
}
|
||
|
||
void set_console_utf8() {
|
||
(void)SetConsoleOutputCP(CP_UTF8);
|
||
(void)SetConsoleCP(CP_UTF8);
|
||
}
|
||
|
||
PSC_DEFINE_TRIPLE_API_FROM_BOOL(void*, load_library, PSC_LOAD_LIBRARY_PARAMS)
|
||
|
||
bool load_library_ec(const std::string& library_path, void*& out, std::error_code& ec) noexcept {
|
||
out = nullptr;
|
||
ec.clear();
|
||
|
||
if (library_path.empty()) {
|
||
ec = std::make_error_code(std::errc::invalid_argument);
|
||
return false;
|
||
}
|
||
|
||
const std::wstring wpath = utf8_to_wide_impl(library_path);
|
||
HMODULE h = LoadLibraryW(wpath.c_str());
|
||
if (!h) {
|
||
ec = std::error_code(static_cast<int>(GetLastError()), std::system_category());
|
||
std::cerr << "加载库失败:【" << library_path << "】 "
|
||
<< ec.message() << " (code=" << ec.value() << ")\n";
|
||
return false;
|
||
}
|
||
|
||
out = reinterpret_cast<void*>(h);
|
||
return true;
|
||
}
|
||
|
||
void* load_library_fail_fast(const std::string& library_path) {
|
||
auto ret = try_load_library(library_path);
|
||
if (!ret) {
|
||
Psc::fail_fast();
|
||
}
|
||
return ret.value();
|
||
}
|
||
|
||
void free_library(void* library) {
|
||
if (library) {
|
||
FreeLibrary(reinterpret_cast<HMODULE>(library));
|
||
}
|
||
}
|
||
|
||
PSC_DEFINE_TRIPLE_API_FROM_BOOL(void*, load_function, PSC_LOAD_FUNCTION_PARAMS)
|
||
|
||
bool load_function_ec(void* library, const std::string& function_name, void*& out, std::error_code& ec) noexcept {
|
||
out = nullptr;
|
||
ec.clear();
|
||
|
||
if (!library) {
|
||
ec = std::make_error_code(std::errc::invalid_argument);
|
||
std::cerr << "load_function: " << function_name << " but library == nullptr\n";
|
||
return false;
|
||
}
|
||
|
||
FARPROC p = GetProcAddress(reinterpret_cast<HMODULE>(library), function_name.c_str());
|
||
if (!p) {
|
||
ec = std::error_code(static_cast<int>(GetLastError()), std::system_category());
|
||
std::cerr << "Failed to get function pointer for: " << function_name
|
||
<< " : " << ec.message() << " (code=" << ec.value() << ")\n";
|
||
return false;
|
||
}
|
||
|
||
out = reinterpret_cast<void*>(p);
|
||
return true;
|
||
}
|
||
|
||
void* load_function_fail_fast(void* library, const std::string& function_name) {
|
||
auto ret = try_load_function(library, function_name);
|
||
if (!ret) {
|
||
Psc::fail_fast();
|
||
}
|
||
return ret.value();
|
||
}
|
||
|
||
std::string get_computer_serial_number() {
|
||
HRESULT hr = CoInitializeEx(nullptr, COINIT_MULTITHREADED);
|
||
const bool need_uninit = SUCCEEDED(hr);
|
||
if (FAILED(hr) && hr != RPC_E_CHANGED_MODE) {
|
||
return "";
|
||
}
|
||
|
||
hr = CoInitializeSecurity(nullptr, -1, nullptr, nullptr,
|
||
RPC_C_AUTHN_LEVEL_DEFAULT,
|
||
RPC_C_IMP_LEVEL_IMPERSONATE,
|
||
nullptr, EOAC_NONE, nullptr);
|
||
if (FAILED(hr) && hr != RPC_E_TOO_LATE) {
|
||
if (need_uninit) {
|
||
CoUninitialize();
|
||
}
|
||
return "";
|
||
}
|
||
|
||
IWbemLocator* locator = nullptr;
|
||
IWbemServices* services = nullptr;
|
||
IEnumWbemClassObject* enumerator = nullptr;
|
||
IWbemClassObject* object = nullptr;
|
||
std::string result;
|
||
|
||
hr = CoCreateInstance(CLSID_WbemLocator, nullptr, CLSCTX_INPROC_SERVER,
|
||
IID_IWbemLocator, reinterpret_cast<void**>(&locator));
|
||
if (FAILED(hr) || !locator) {
|
||
if (need_uninit) CoUninitialize();
|
||
return "";
|
||
}
|
||
|
||
Bstr_Guard ns(L"ROOT\\CIMV2");
|
||
hr = locator->ConnectServer(ns.value, nullptr, nullptr, nullptr, 0, nullptr, nullptr, &services);
|
||
if (SUCCEEDED(hr) && services) {
|
||
hr = CoSetProxyBlanket(services,
|
||
RPC_C_AUTHN_WINNT,
|
||
RPC_C_AUTHZ_NONE,
|
||
nullptr,
|
||
RPC_C_AUTHN_LEVEL_CALL,
|
||
RPC_C_IMP_LEVEL_IMPERSONATE,
|
||
nullptr,
|
||
EOAC_NONE);
|
||
}
|
||
|
||
if (SUCCEEDED(hr) && services) {
|
||
Bstr_Guard lang(L"WQL");
|
||
Bstr_Guard query(L"SELECT SerialNumber FROM Win32_BIOS");
|
||
hr = services->ExecQuery(lang.value, query.value,
|
||
WBEM_FLAG_FORWARD_ONLY | WBEM_FLAG_RETURN_IMMEDIATELY,
|
||
nullptr, &enumerator);
|
||
}
|
||
|
||
if (SUCCEEDED(hr) && enumerator) {
|
||
ULONG returned = 0;
|
||
hr = enumerator->Next(WBEM_INFINITE, 1, &object, &returned);
|
||
if (SUCCEEDED(hr) && returned > 0 && object) {
|
||
VARIANT vtSerial;
|
||
VariantInit(&vtSerial);
|
||
hr = object->Get(L"SerialNumber", 0, &vtSerial, nullptr, nullptr);
|
||
if (SUCCEEDED(hr) && vtSerial.vt == VT_BSTR && vtSerial.bstrVal) {
|
||
result = wide_to_utf8_impl(vtSerial.bstrVal);
|
||
}
|
||
VariantClear(&vtSerial);
|
||
}
|
||
}
|
||
|
||
release_com(object);
|
||
release_com(enumerator);
|
||
release_com(services);
|
||
release_com(locator);
|
||
if (need_uninit) {
|
||
CoUninitialize();
|
||
}
|
||
return result;
|
||
}
|
||
|
||
std::string error_code_to_string(DWORD error_code) {
|
||
wchar_t* message = nullptr;
|
||
DWORD size = FormatMessageW(FORMAT_MESSAGE_ALLOCATE_BUFFER |
|
||
FORMAT_MESSAGE_FROM_SYSTEM |
|
||
FORMAT_MESSAGE_IGNORE_INSERTS,
|
||
nullptr,
|
||
error_code,
|
||
MAKELANGID(LANG_NEUTRAL, SUBLANG_DEFAULT),
|
||
reinterpret_cast<LPWSTR>(&message),
|
||
0,
|
||
nullptr);
|
||
|
||
std::string result = "错误码:" + std::to_string(error_code) + ": ";
|
||
if (size > 0 && message) {
|
||
result += wide_to_utf8_impl(message, static_cast<int>(size));
|
||
LocalFree(message);
|
||
} else {
|
||
result += "获取错误信息失败";
|
||
}
|
||
return result;
|
||
}
|
||
|
||
std::string get_error_message() {
|
||
return error_code_to_string(GetLastError());
|
||
}
|
||
|
||
std::string get_error_message(ERROR_CODE_TYPE code) {
|
||
return error_code_to_string(static_cast<DWORD>(code));
|
||
}
|
||
|
||
ERROR_CODE_TYPE get_error_code() {
|
||
return static_cast<ERROR_CODE_TYPE>(GetLastError());
|
||
}
|
||
|
||
void set_error_code(ERROR_CODE_TYPE code) {
|
||
SetLastError(static_cast<DWORD>(code));
|
||
}
|
||
|
||
std::string to_upper_case(const std::string& input) {
|
||
std::string result = input;
|
||
for (char& ch : result) {
|
||
ch = static_cast<char>(std::toupper(static_cast<unsigned char>(ch)));
|
||
}
|
||
return result;
|
||
}
|
||
|
||
std::string get_home_dir() {
|
||
std::wstring home = read_env_w(L"USERPROFILE");
|
||
if (home.empty()) {
|
||
std::wstring drive = read_env_w(L"HOMEDRIVE");
|
||
std::wstring path = read_env_w(L"HOMEPATH");
|
||
home = drive + path;
|
||
}
|
||
std::string result = wide_to_utf8_impl(home);
|
||
std::replace(result.begin(), result.end(), '\\', '/');
|
||
return result;
|
||
}
|
||
|
||
std::string get_exe_path() {
|
||
std::wstring buffer(MAX_PATH, L'\0');
|
||
|
||
for (;;) {
|
||
DWORD len = GetModuleFileNameW(nullptr, buffer.data(), static_cast<DWORD>(buffer.size()));
|
||
if (len == 0) {
|
||
return "";
|
||
}
|
||
|
||
if (len < buffer.size() - 1) {
|
||
buffer.resize(len);
|
||
break;
|
||
}
|
||
|
||
buffer.resize(buffer.size() * 2);
|
||
if (buffer.size() > 32768) {
|
||
return "";
|
||
}
|
||
}
|
||
|
||
std::string result = wide_to_utf8_impl(buffer);
|
||
std::replace(result.begin(), result.end(), '\\', '/');
|
||
return result;
|
||
}
|
||
|
||
std::string utf8_to_gbk(const std::string& utf8Str) {
|
||
return utf8_to_codepage(utf8Str, CP_ACP);
|
||
}
|
||
|
||
std::string gbk_to_utf8(const std::string& gbkStr) {
|
||
return codepage_to_utf8(gbkStr, CP_ACP);
|
||
}
|
||
|
||
bool set_thread_priority(std::thread* thread, int priority, bool realtime) {
|
||
HANDLE hThread = thread_to_win32_handle(thread);
|
||
if (!hThread) {
|
||
return false;
|
||
}
|
||
return set_thread_priority(reinterpret_cast<void*>(hThread), priority, realtime);
|
||
}
|
||
|
||
bool set_thread_priority(void* thread_ptr, int priority, bool realtime) {
|
||
HANDLE hThread = static_cast<HANDLE>(thread_ptr);
|
||
if (!hThread) {
|
||
return false;
|
||
}
|
||
|
||
if (realtime) {
|
||
if (!SetPriorityClass(GetCurrentProcess(), REALTIME_PRIORITY_CLASS)) {
|
||
return false;
|
||
}
|
||
return SetThreadPriority(hThread, THREAD_PRIORITY_TIME_CRITICAL) != 0;
|
||
}
|
||
|
||
if (priority < THREAD_PRIORITY_IDLE) {
|
||
priority = THREAD_PRIORITY_IDLE;
|
||
}
|
||
if (priority > THREAD_PRIORITY_TIME_CRITICAL) {
|
||
priority = THREAD_PRIORITY_TIME_CRITICAL;
|
||
}
|
||
|
||
return SetThreadPriority(hThread, priority) != 0;
|
||
}
|
||
|
||
bool set_thread_affinity(void* thread_ptr, unsigned cpu_index) {
|
||
HANDLE hThread = static_cast<HANDLE>(thread_ptr);
|
||
if (!hThread) {
|
||
return false;
|
||
}
|
||
|
||
const unsigned max_bits = sizeof(DWORD_PTR) * 8;
|
||
if (cpu_index >= max_bits) {
|
||
return false;
|
||
}
|
||
|
||
DWORD_PTR mask = (static_cast<DWORD_PTR>(1) << cpu_index);
|
||
return SetThreadAffinityMask(hThread, mask) != 0;
|
||
}
|
||
|
||
bool set_thread_affinity(std::thread* thread, unsigned cpu_index) {
|
||
HANDLE hThread = thread_to_win32_handle(thread);
|
||
if (!hThread) {
|
||
return false;
|
||
}
|
||
return set_thread_affinity(reinterpret_cast<void*>(hThread), cpu_index);
|
||
}
|
||
|
||
size_t get_system_memory() {
|
||
MEMORYSTATUSEX memStatus{};
|
||
memStatus.dwLength = sizeof(MEMORYSTATUSEX);
|
||
if (GlobalMemoryStatusEx(&memStatus)) {
|
||
return static_cast<size_t>(memStatus.ullTotalPhys);
|
||
}
|
||
return 0;
|
||
}
|
||
|
||
size_t get_process_memory() {
|
||
PROCESS_MEMORY_COUNTERS memCounter{};
|
||
if (GetProcessMemoryInfo(GetCurrentProcess(), &memCounter, sizeof(memCounter))) {
|
||
return static_cast<size_t>(memCounter.WorkingSetSize);
|
||
}
|
||
return 0;
|
||
}
|
||
|
||
std::string get_memory_info() {
|
||
MEMORYSTATUSEX status{};
|
||
status.dwLength = sizeof(status);
|
||
if (!GlobalMemoryStatusEx(&status)) {
|
||
return "Failed to get memory status";
|
||
}
|
||
|
||
auto ret = JSON::object();
|
||
ret.append({"Total physical memory (MB)", status.ullTotalPhys / (1024 * 1024)});
|
||
ret.append({"Available physical memory (MB)", status.ullAvailPhys / (1024 * 1024)});
|
||
ret.append({"Total virtual memory (MB)", status.ullTotalVirtual / (1024 * 1024)});
|
||
ret.append({"Available virtual memory (MB)", status.ullAvailVirtual / (1024 * 1024)});
|
||
ret.append({"Total page file (MB)", status.ullTotalPageFile / (1024 * 1024)});
|
||
ret.append({"Available page file (MB)", status.ullAvailPageFile / (1024 * 1024)});
|
||
return ret.to_json_string();
|
||
}
|
||
|
||
int GenerateMiniDump(PEXCEPTION_POINTERS pExceptionPointers) {
|
||
using MiniDumpWriteDumpT = BOOL(WINAPI*)(HANDLE,
|
||
DWORD,
|
||
HANDLE,
|
||
MINIDUMP_TYPE,
|
||
PMINIDUMP_EXCEPTION_INFORMATION,
|
||
PMINIDUMP_USER_STREAM_INFORMATION,
|
||
PMINIDUMP_CALLBACK_INFORMATION);
|
||
|
||
HMODULE hDbgHelp = LoadLibraryW(L"DbgHelp.dll");
|
||
if (!hDbgHelp) {
|
||
return EXCEPTION_CONTINUE_EXECUTION;
|
||
}
|
||
|
||
auto pfnMiniDumpWriteDump = reinterpret_cast<MiniDumpWriteDumpT>(GetProcAddress(hDbgHelp, "MiniDumpWriteDump"));
|
||
if (!pfnMiniDumpWriteDump) {
|
||
FreeLibrary(hDbgHelp);
|
||
return EXCEPTION_CONTINUE_EXECUTION;
|
||
}
|
||
|
||
wchar_t fileName[MAX_PATH]{};
|
||
SYSTEMTIME localTime{};
|
||
GetLocalTime(&localTime);
|
||
std::swprintf(fileName, MAX_PATH, L"DumpDemo_v1.0-%04u%02u%02u-%02u%02u%02u.dmp",
|
||
localTime.wYear,
|
||
localTime.wMonth,
|
||
localTime.wDay,
|
||
localTime.wHour,
|
||
localTime.wMinute,
|
||
localTime.wSecond);
|
||
|
||
HANDLE hDumpFile = CreateFileW(fileName,
|
||
GENERIC_READ | GENERIC_WRITE,
|
||
FILE_SHARE_WRITE | FILE_SHARE_READ,
|
||
nullptr,
|
||
CREATE_ALWAYS,
|
||
FILE_ATTRIBUTE_NORMAL,
|
||
nullptr);
|
||
if (hDumpFile == INVALID_HANDLE_VALUE) {
|
||
FreeLibrary(hDbgHelp);
|
||
return EXCEPTION_CONTINUE_EXECUTION;
|
||
}
|
||
|
||
MINIDUMP_EXCEPTION_INFORMATION expParam{};
|
||
expParam.ThreadId = GetCurrentThreadId();
|
||
expParam.ExceptionPointers = pExceptionPointers;
|
||
expParam.ClientPointers = FALSE;
|
||
|
||
pfnMiniDumpWriteDump(GetCurrentProcess(),
|
||
GetCurrentProcessId(),
|
||
hDumpFile,
|
||
MiniDumpWithDataSegs,
|
||
pExceptionPointers ? &expParam : nullptr,
|
||
nullptr,
|
||
nullptr);
|
||
|
||
CloseHandle(hDumpFile);
|
||
FreeLibrary(hDbgHelp);
|
||
return EXCEPTION_EXECUTE_HANDLER;
|
||
}
|
||
|
||
LONG WINAPI ExceptionFilter(LPEXCEPTION_POINTERS lpExceptionInfo) {
|
||
if (IsDebuggerPresent()) {
|
||
return EXCEPTION_CONTINUE_SEARCH;
|
||
}
|
||
return GenerateMiniDump(lpExceptionInfo);
|
||
}
|
||
|
||
} // namespace Psc
|
||
|
||
#endif // defined(_WIN32)
|