#if defined(_WIN32) #include "windows_include.h" #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #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(input.size()), nullptr, 0); if (size <= 0) { return L""; } std::wstring result(static_cast(size), L'\0'); MultiByteToWideChar(CP_UTF8, 0, input.data(), static_cast(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), '\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(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(input.size()), nullptr, 0); if (wide_len <= 0) { return ""; } std::wstring wide(static_cast(wide_len), L'\0'); MultiByteToWideChar(codepage, 0, input.data(), static_cast(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(wide.size()), nullptr, 0, nullptr, nullptr); if (len <= 0) { return ""; } std::string result(static_cast(len), '\0'); WideCharToMultiByte(codepage, 0, wide.data(), static_cast(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::value) { return reinterpret_cast(thread->native_handle()); } else if constexpr (std::is_integral::value) { return reinterpret_cast(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 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(GetProcAddress(dbghelp, "SymInitialize")); auto pSymSetOptions = reinterpret_cast(GetProcAddress(dbghelp, "SymSetOptions")); auto pSymFromAddr = reinterpret_cast(GetProcAddress(dbghelp, "SymFromAddr")); auto pSymGetLineFromAddr64 = reinterpret_cast(GetProcAddress(dbghelp, "SymGetLineFromAddr64")); auto pSymCleanup = reinterpret_cast(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 symbol_buffer(sizeof(SYMBOL_INFO) + max_name_len * sizeof(char)); auto* symbol = reinterpret_cast(symbol_buffer.data()); symbol->SizeOfStruct = sizeof(SYMBOL_INFO); symbol->MaxNameLen = max_name_len; for (USHORT i = 0; i < frames; ++i) { DWORD64 address = reinterpret_cast(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) << "" << " "; } 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(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(GetLastError()), std::system_category()); std::cerr << "加载库失败:【" << library_path << "】 " << ec.message() << " (code=" << ec.value() << ")\n"; return false; } out = reinterpret_cast(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(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(library), function_name.c_str()); if (!p) { ec = std::error_code(static_cast(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(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(&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(&message), 0, nullptr); std::string result = "错误码:" + std::to_string(error_code) + ": "; if (size > 0 && message) { result += wide_to_utf8_impl(message, static_cast(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(code)); } ERROR_CODE_TYPE get_error_code() { return static_cast(GetLastError()); } void set_error_code(ERROR_CODE_TYPE code) { SetLastError(static_cast(code)); } std::string to_upper_case(const std::string& input) { std::string result = input; for (char& ch : result) { ch = static_cast(std::toupper(static_cast(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(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(hThread), priority, realtime); } bool set_thread_priority(void* thread_ptr, int priority, bool realtime) { HANDLE hThread = static_cast(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(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(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(hThread), cpu_index); } size_t get_system_memory() { MEMORYSTATUSEX memStatus{}; memStatus.dwLength = sizeof(MEMORYSTATUSEX); if (GlobalMemoryStatusEx(&memStatus)) { return static_cast(memStatus.ullTotalPhys); } return 0; } size_t get_process_memory() { PROCESS_MEMORY_COUNTERS memCounter{}; if (GetProcessMemoryInfo(GetCurrentProcess(), &memCounter, sizeof(memCounter))) { return static_cast(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(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)