Merge pull request #2490 from goaaats/feat/catch_clr_errors

Catch CLR exceptions
This commit is contained in:
goat 2025-12-06 18:43:32 +01:00 committed by GitHub
commit 501e30e31c
No known key found for this signature in database
GPG key ID: B5690EEEBB952194
5 changed files with 141 additions and 46 deletions

View file

@ -6,6 +6,8 @@
#define WIN32_LEAN_AND_MEAN #define WIN32_LEAN_AND_MEAN
#include <Windows.h> #include <Windows.h>
#define CUSTOM_EXCEPTION_EXTERNAL_EVENT 0x12345679
struct exception_info struct exception_info
{ {
LPEXCEPTION_POINTERS pExceptionPointers; LPEXCEPTION_POINTERS pExceptionPointers;

View file

@ -331,6 +331,51 @@ HRESULT WINAPI InitializeImpl(LPVOID lpParam, HANDLE hMainThreadContinue) {
logging::I("VEH was disabled manually"); logging::I("VEH was disabled manually");
} }
// ============================== CLR Reporting =================================== //
// This is pretty horrible - CLR just doesn't provide a way for us to handle these events, and the API for it
// was pushed back to .NET 11, so we have to hook ReportEventW and catch them ourselves for now.
// Ideally all of this will go away once they get to it.
static std::shared_ptr<hooks::global_import_hook<decltype(ReportEventW)>> s_report_event_hook;
s_report_event_hook = std::make_shared<hooks::global_import_hook<decltype(ReportEventW)>>(
"advapi32.dll!ReportEventW (global import, hook_clr_report_event)", L"advapi32.dll", "ReportEventW");
s_report_event_hook->set_detour([hook = s_report_event_hook.get()](
HANDLE hEventLog,
WORD wType,
WORD wCategory,
DWORD dwEventID,
PSID lpUserSid,
WORD wNumStrings,
DWORD dwDataSize,
LPCWSTR* lpStrings,
LPVOID lpRawData)-> BOOL {
// Check for CLR Error Event IDs
// https://github.com/dotnet/runtime/blob/v10.0.0/src/coreclr/vm/eventreporter.cpp#L370
if (dwEventID != 1026 && // ERT_UnhandledException: The process was terminated due to an unhandled exception
dwEventID != 1025 && // ERT_ManagedFailFast: The application requested process termination through System.Environment.FailFast
dwEventID != 1023 && // ERT_UnmanagedFailFast: The process was terminated due to an internal error in the .NET Runtime
dwEventID != 1027 && // ERT_StackOverflow: The process was terminated due to a stack overflow
dwEventID != 1028) // ERT_CodeContractFailed: The application encountered a bug. A managed code contract (precondition, postcondition, object invariant, or assert) failed
{
return hook->call_original(hEventLog, wType, wCategory, dwEventID, lpUserSid, wNumStrings, dwDataSize, lpStrings, lpRawData);
}
if (wNumStrings == 0 || lpStrings == nullptr) {
logging::W("ReportEventW called with no strings.");
return hook->call_original(hEventLog, wType, wCategory, dwEventID, lpUserSid, wNumStrings, dwDataSize, lpStrings, lpRawData);
}
// In most cases, DalamudCrashHandler will kill us now, so call original here to make sure we still write to the event log.
const BOOL original_ret = hook->call_original(hEventLog, wType, wCategory, dwEventID, lpUserSid, wNumStrings, dwDataSize, lpStrings, lpRawData);
const std::wstring error_details(lpStrings[0]);
veh::raise_external_event(error_details);
return original_ret;
});
logging::I("ReportEventW hook installed.");
// ============================== Dalamud ==================================== // // ============================== Dalamud ==================================== //
if (static_cast<int>(g_startInfo.BootWaitMessageBox) & static_cast<int>(DalamudStartInfo::WaitMessageboxFlags::BeforeDalamudEntrypoint)) if (static_cast<int>(g_startInfo.BootWaitMessageBox) & static_cast<int>(DalamudStartInfo::WaitMessageboxFlags::BeforeDalamudEntrypoint))

View file

@ -31,6 +31,8 @@ HANDLE g_crashhandler_process = nullptr;
HANDLE g_crashhandler_event = nullptr; HANDLE g_crashhandler_event = nullptr;
HANDLE g_crashhandler_pipe_write = nullptr; HANDLE g_crashhandler_pipe_write = nullptr;
wchar_t g_external_event_info[16384] = L"";
std::recursive_mutex g_exception_handler_mutex; std::recursive_mutex g_exception_handler_mutex;
std::chrono::time_point<std::chrono::system_clock> g_time_start; std::chrono::time_point<std::chrono::system_clock> g_time_start;
@ -190,7 +192,11 @@ LONG exception_handler(EXCEPTION_POINTERS* ex)
DuplicateHandle(GetCurrentProcess(), g_crashhandler_event, g_crashhandler_process, &exinfo.hEventHandle, 0, TRUE, DUPLICATE_SAME_ACCESS); DuplicateHandle(GetCurrentProcess(), g_crashhandler_event, g_crashhandler_process, &exinfo.hEventHandle, 0, TRUE, DUPLICATE_SAME_ACCESS);
std::wstring stackTrace; std::wstring stackTrace;
if (!g_clr) if (ex->ExceptionRecord->ExceptionCode == CUSTOM_EXCEPTION_EXTERNAL_EVENT)
{
stackTrace = std::wstring(g_external_event_info);
}
else if (!g_clr)
{ {
stackTrace = L"(no CLR stack trace available)"; stackTrace = L"(no CLR stack trace available)";
} }
@ -251,6 +257,12 @@ LONG WINAPI structured_exception_handler(EXCEPTION_POINTERS* ex)
LONG WINAPI vectored_exception_handler(EXCEPTION_POINTERS* ex) LONG WINAPI vectored_exception_handler(EXCEPTION_POINTERS* ex)
{ {
// special case for CLR exceptions, always trigger crash handler
if (ex->ExceptionRecord->ExceptionCode == CUSTOM_EXCEPTION_EXTERNAL_EVENT)
{
return exception_handler(ex);
}
if (ex->ExceptionRecord->ExceptionCode == 0x12345678) if (ex->ExceptionRecord->ExceptionCode == 0x12345678)
{ {
// pass // pass
@ -268,7 +280,7 @@ LONG WINAPI vectored_exception_handler(EXCEPTION_POINTERS* ex)
if (!is_ffxiv_address(L"ffxiv_dx11.exe", ex->ContextRecord->Rip) && if (!is_ffxiv_address(L"ffxiv_dx11.exe", ex->ContextRecord->Rip) &&
!is_ffxiv_address(L"cimgui.dll", ex->ContextRecord->Rip)) !is_ffxiv_address(L"cimgui.dll", ex->ContextRecord->Rip))
return EXCEPTION_CONTINUE_SEARCH; return EXCEPTION_CONTINUE_SEARCH;
} }
return exception_handler(ex); return exception_handler(ex);
@ -297,7 +309,7 @@ bool veh::add_handler(bool doFullDump, const std::string& workingDirectory)
if (HANDLE hReadPipeRaw, hWritePipeRaw; CreatePipe(&hReadPipeRaw, &hWritePipeRaw, nullptr, 65536)) if (HANDLE hReadPipeRaw, hWritePipeRaw; CreatePipe(&hReadPipeRaw, &hWritePipeRaw, nullptr, 65536))
{ {
hWritePipe.emplace(hWritePipeRaw, &CloseHandle); hWritePipe.emplace(hWritePipeRaw, &CloseHandle);
if (HANDLE hReadPipeInheritableRaw; DuplicateHandle(GetCurrentProcess(), hReadPipeRaw, GetCurrentProcess(), &hReadPipeInheritableRaw, 0, TRUE, DUPLICATE_SAME_ACCESS | DUPLICATE_CLOSE_SOURCE)) if (HANDLE hReadPipeInheritableRaw; DuplicateHandle(GetCurrentProcess(), hReadPipeRaw, GetCurrentProcess(), &hReadPipeInheritableRaw, 0, TRUE, DUPLICATE_SAME_ACCESS | DUPLICATE_CLOSE_SOURCE))
{ {
hReadPipeInheritable.emplace(hReadPipeInheritableRaw, &CloseHandle); hReadPipeInheritable.emplace(hReadPipeInheritableRaw, &CloseHandle);
@ -315,9 +327,9 @@ bool veh::add_handler(bool doFullDump, const std::string& workingDirectory)
} }
// additional information // additional information
STARTUPINFOEXW siex{}; STARTUPINFOEXW siex{};
PROCESS_INFORMATION pi{}; PROCESS_INFORMATION pi{};
siex.StartupInfo.cb = sizeof siex; siex.StartupInfo.cb = sizeof siex;
siex.StartupInfo.dwFlags = STARTF_USESHOWWINDOW; siex.StartupInfo.dwFlags = STARTF_USESHOWWINDOW;
siex.StartupInfo.wShowWindow = g_startInfo.CrashHandlerShow ? SW_SHOW : SW_HIDE; siex.StartupInfo.wShowWindow = g_startInfo.CrashHandlerShow ? SW_SHOW : SW_HIDE;
@ -385,7 +397,7 @@ bool veh::add_handler(bool doFullDump, const std::string& workingDirectory)
argstr.push_back(L' '); argstr.push_back(L' ');
} }
argstr.pop_back(); argstr.pop_back();
if (!handles.empty() && !UpdateProcThreadAttribute(siex.lpAttributeList, 0, PROC_THREAD_ATTRIBUTE_HANDLE_LIST, &handles[0], std::span(handles).size_bytes(), nullptr, nullptr)) if (!handles.empty() && !UpdateProcThreadAttribute(siex.lpAttributeList, 0, PROC_THREAD_ATTRIBUTE_HANDLE_LIST, &handles[0], std::span(handles).size_bytes(), nullptr, nullptr))
{ {
logging::W("Failed to launch DalamudCrashHandler.exe: UpdateProcThreadAttribute error 0x{:x}", GetLastError()); logging::W("Failed to launch DalamudCrashHandler.exe: UpdateProcThreadAttribute error 0x{:x}", GetLastError());
@ -400,7 +412,7 @@ bool veh::add_handler(bool doFullDump, const std::string& workingDirectory)
TRUE, // Set handle inheritance to FALSE TRUE, // Set handle inheritance to FALSE
EXTENDED_STARTUPINFO_PRESENT, // lpStartupInfo actually points to a STARTUPINFOEX(W) EXTENDED_STARTUPINFO_PRESENT, // lpStartupInfo actually points to a STARTUPINFOEX(W)
nullptr, // Use parent's environment block nullptr, // Use parent's environment block
nullptr, // Use parent's starting directory nullptr, // Use parent's starting directory
&siex.StartupInfo, // Pointer to STARTUPINFO structure &siex.StartupInfo, // Pointer to STARTUPINFO structure
&pi // Pointer to PROCESS_INFORMATION structure (removed extra parentheses) &pi // Pointer to PROCESS_INFORMATION structure (removed extra parentheses)
)) ))
@ -416,7 +428,7 @@ bool veh::add_handler(bool doFullDump, const std::string& workingDirectory)
} }
CloseHandle(pi.hThread); CloseHandle(pi.hThread);
g_crashhandler_process = pi.hProcess; g_crashhandler_process = pi.hProcess;
g_crashhandler_pipe_write = hWritePipe->release(); g_crashhandler_pipe_write = hWritePipe->release();
logging::I("Launched DalamudCrashHandler.exe: PID {}", pi.dwProcessId); logging::I("Launched DalamudCrashHandler.exe: PID {}", pi.dwProcessId);
@ -434,3 +446,10 @@ bool veh::remove_handler()
} }
return false; return false;
} }
void veh::raise_external_event(const std::wstring& info)
{
const auto info_size = std::min(info.size(), std::size(g_external_event_info) - 1);
wcsncpy_s(g_external_event_info, info.c_str(), info_size);
RaiseException(CUSTOM_EXCEPTION_EXTERNAL_EVENT, 0, 0, nullptr);
}

View file

@ -4,4 +4,5 @@ namespace veh
{ {
bool add_handler(bool doFullDump, const std::string& workingDirectory); bool add_handler(bool doFullDump, const std::string& workingDirectory);
bool remove_handler(); bool remove_handler();
void raise_external_event(const std::wstring& info);
} }

View file

@ -119,7 +119,7 @@ std::wstring describe_module(const std::filesystem::path& path) {
return std::format(L"<error: GetFileVersionInfoSizeW#2 returned {}>", GetLastError()); return std::format(L"<error: GetFileVersionInfoSizeW#2 returned {}>", GetLastError());
UINT size = 0; UINT size = 0;
std::wstring version = L"v?.?.?.?"; std::wstring version = L"v?.?.?.?";
if (LPVOID lpBuffer; VerQueryValueW(block.data(), L"\\", &lpBuffer, &size)) { if (LPVOID lpBuffer; VerQueryValueW(block.data(), L"\\", &lpBuffer, &size)) {
const auto& v = *static_cast<const VS_FIXEDFILEINFO*>(lpBuffer); const auto& v = *static_cast<const VS_FIXEDFILEINFO*>(lpBuffer);
@ -176,7 +176,7 @@ const std::map<HMODULE, size_t>& get_remote_modules() {
std::vector<HMODULE> buf(8192); std::vector<HMODULE> buf(8192);
for (size_t i = 0; i < 64; i++) { for (size_t i = 0; i < 64; i++) {
if (DWORD needed; !EnumProcessModules(g_hProcess, &buf[0], static_cast<DWORD>(std::span(buf).size_bytes()), &needed)) { if (DWORD needed; !EnumProcessModules(g_hProcess, &buf[0], static_cast<DWORD>(std::span(buf).size_bytes()), &needed)) {
std::cerr << std::format("EnumProcessModules error: 0x{:x}", GetLastError()) << std::endl; std::cerr << std::format("EnumProcessModules error: 0x{:x}", GetLastError()) << std::endl;
break; break;
} else if (needed > std::span(buf).size_bytes()) { } else if (needed > std::span(buf).size_bytes()) {
buf.resize(needed / sizeof(HMODULE) + 16); buf.resize(needed / sizeof(HMODULE) + 16);
@ -201,7 +201,7 @@ const std::map<HMODULE, size_t>& get_remote_modules() {
data[hModule] = nth64.OptionalHeader.SizeOfImage; data[hModule] = nth64.OptionalHeader.SizeOfImage;
} }
return data; return data;
}(); }();
@ -292,35 +292,43 @@ std::wstring to_address_string(const DWORD64 address, const bool try_ptrderef =
void print_exception_info(HANDLE hThread, const EXCEPTION_POINTERS& ex, const CONTEXT& ctx, std::wostringstream& log) { void print_exception_info(HANDLE hThread, const EXCEPTION_POINTERS& ex, const CONTEXT& ctx, std::wostringstream& log) {
std::vector<EXCEPTION_RECORD> exRecs; std::vector<EXCEPTION_RECORD> exRecs;
if (ex.ExceptionRecord) { if (ex.ExceptionRecord)
{
size_t rec_index = 0; size_t rec_index = 0;
size_t read; size_t read;
exRecs.emplace_back();
for (auto pRemoteExRec = ex.ExceptionRecord; for (auto pRemoteExRec = ex.ExceptionRecord;
pRemoteExRec pRemoteExRec && rec_index < 64;
&& rec_index < 64 rec_index++)
&& ReadProcessMemory(g_hProcess, pRemoteExRec, &exRecs.back(), sizeof exRecs.back(), &read) {
&& read >= offsetof(EXCEPTION_RECORD, ExceptionInformation) exRecs.emplace_back();
&& read >= static_cast<size_t>(reinterpret_cast<const char*>(&exRecs.back().ExceptionInformation[exRecs.back().NumberParameters]) - reinterpret_cast<const char*>(&exRecs.back()));
rec_index++) { if (!ReadProcessMemory(g_hProcess, pRemoteExRec, &exRecs.back(), sizeof exRecs.back(), &read)
|| read < offsetof(EXCEPTION_RECORD, ExceptionInformation)
|| read < static_cast<size_t>(reinterpret_cast<const char*>(&exRecs.back().ExceptionInformation[exRecs.
back().NumberParameters]) - reinterpret_cast<const char*>(&exRecs.back())))
{
exRecs.pop_back();
break;
}
log << std::format(L"\nException Info #{}\n", rec_index); log << std::format(L"\nException Info #{}\n", rec_index);
log << std::format(L"Address: {:X}\n", exRecs.back().ExceptionCode); log << std::format(L"Address: {:X}\n", exRecs.back().ExceptionCode);
log << std::format(L"Flags: {:X}\n", exRecs.back().ExceptionFlags); log << std::format(L"Flags: {:X}\n", exRecs.back().ExceptionFlags);
log << std::format(L"Address: {:X}\n", reinterpret_cast<size_t>(exRecs.back().ExceptionAddress)); log << std::format(L"Address: {:X}\n", reinterpret_cast<size_t>(exRecs.back().ExceptionAddress));
if (!exRecs.back().NumberParameters) if (exRecs.back().NumberParameters)
continue; {
log << L"Parameters: "; log << L"Parameters: ";
for (DWORD i = 0; i < exRecs.back().NumberParameters; ++i) { for (DWORD i = 0; i < exRecs.back().NumberParameters; ++i)
if (i != 0) {
log << L", "; if (i != 0)
log << std::format(L"{:X}", exRecs.back().ExceptionInformation[i]); log << L", ";
log << std::format(L"{:X}", exRecs.back().ExceptionInformation[i]);
}
} }
pRemoteExRec = exRecs.back().ExceptionRecord; pRemoteExRec = exRecs.back().ExceptionRecord;
exRecs.emplace_back();
} }
exRecs.pop_back();
} }
log << L"\nCall Stack\n{"; log << L"\nCall Stack\n{";
@ -410,7 +418,7 @@ void print_exception_info_extended(const EXCEPTION_POINTERS& ex, const CONTEXT&
std::wstring escape_shell_arg(const std::wstring& arg) { std::wstring escape_shell_arg(const std::wstring& arg) {
// https://docs.microsoft.com/en-us/archive/blogs/twistylittlepassagesallalike/everyone-quotes-command-line-arguments-the-wrong-way // https://docs.microsoft.com/en-us/archive/blogs/twistylittlepassagesallalike/everyone-quotes-command-line-arguments-the-wrong-way
std::wstring res; std::wstring res;
if (!arg.empty() && arg.find_first_of(L" \t\n\v\"") == std::wstring::npos) { if (!arg.empty() && arg.find_first_of(L" \t\n\v\"") == std::wstring::npos) {
res.append(arg); res.append(arg);
@ -504,7 +512,7 @@ void export_tspack(HWND hWndParent, const std::filesystem::path& logDir, const s
filePath.emplace(pFilePath); filePath.emplace(pFilePath);
std::fstream fileStream(*filePath, std::ios::binary | std::ios::in | std::ios::out | std::ios::trunc); std::fstream fileStream(*filePath, std::ios::binary | std::ios::in | std::ios::out | std::ios::trunc);
mz_zip_archive zipa{}; mz_zip_archive zipa{};
zipa.m_pIO_opaque = &fileStream; zipa.m_pIO_opaque = &fileStream;
zipa.m_pRead = [](void* pOpaque, mz_uint64 file_ofs, void* pBuf, size_t n) -> size_t { zipa.m_pRead = [](void* pOpaque, mz_uint64 file_ofs, void* pBuf, size_t n) -> size_t {
@ -566,7 +574,7 @@ void export_tspack(HWND hWndParent, const std::filesystem::path& logDir, const s
const auto hLogFile = CreateFileW(logFilePath.c_str(), GENERIC_READ, FILE_SHARE_READ | FILE_SHARE_WRITE, nullptr, OPEN_EXISTING, FILE_FLAG_SEQUENTIAL_SCAN, nullptr); const auto hLogFile = CreateFileW(logFilePath.c_str(), GENERIC_READ, FILE_SHARE_READ | FILE_SHARE_WRITE, nullptr, OPEN_EXISTING, FILE_FLAG_SEQUENTIAL_SCAN, nullptr);
if (hLogFile == INVALID_HANDLE_VALUE) if (hLogFile == INVALID_HANDLE_VALUE)
throw_last_error(std::format("indiv. log file: CreateFileW({})", ws_to_u8(logFilePath.wstring()))); throw_last_error(std::format("indiv. log file: CreateFileW({})", ws_to_u8(logFilePath.wstring())));
std::unique_ptr<void, decltype(&CloseHandle)> hLogFileClose(hLogFile, &CloseHandle); std::unique_ptr<void, decltype(&CloseHandle)> hLogFileClose(hLogFile, &CloseHandle);
LARGE_INTEGER size, baseOffset{}; LARGE_INTEGER size, baseOffset{};
@ -695,7 +703,7 @@ int main() {
// IFileSaveDialog only works on STA // IFileSaveDialog only works on STA
CoInitializeEx(nullptr, COINIT_APARTMENTTHREADED); CoInitializeEx(nullptr, COINIT_APARTMENTTHREADED);
std::vector<std::wstring> args; std::vector<std::wstring> args;
if (int argc = 0; const auto argv = CommandLineToArgvW(GetCommandLineW(), &argc)) { if (int argc = 0; const auto argv = CommandLineToArgvW(GetCommandLineW(), &argc)) {
for (auto i = 0; i < argc; i++) for (auto i = 0; i < argc; i++)
@ -823,14 +831,14 @@ int main() {
hr = pOleWindow->GetWindow(&hwndProgressDialog); hr = pOleWindow->GetWindow(&hwndProgressDialog);
if (SUCCEEDED(hr)) if (SUCCEEDED(hr))
{ {
SetWindowPos(hwndProgressDialog, HWND_TOPMOST, 0, 0, 0, 0, SetWindowPos(hwndProgressDialog, HWND_TOPMOST, 0, 0, 0, 0,
SWP_NOMOVE | SWP_NOSIZE | SWP_SHOWWINDOW); SWP_NOMOVE | SWP_NOSIZE | SWP_SHOWWINDOW);
SetForegroundWindow(hwndProgressDialog); SetForegroundWindow(hwndProgressDialog);
} }
pOleWindow->Release(); pOleWindow->Release();
} }
} }
else { else {
std::cerr << "Failed to create progress window" << std::endl; std::cerr << "Failed to create progress window" << std::endl;
@ -852,14 +860,14 @@ int main() {
https://github.com/sumatrapdfreader/sumatrapdf/blob/master/src/utils/DbgHelpDyn.cpp https://github.com/sumatrapdfreader/sumatrapdf/blob/master/src/utils/DbgHelpDyn.cpp
*/ */
if (g_bSymbolsAvailable) { if (g_bSymbolsAvailable) {
SymRefreshModuleList(g_hProcess); SymRefreshModuleList(g_hProcess);
} }
else if(!assetDir.empty()) else if(!assetDir.empty())
{ {
auto symbol_search_path = std::format(L".;{}", (assetDir / "UIRes" / "pdb").wstring()); auto symbol_search_path = std::format(L".;{}", (assetDir / "UIRes" / "pdb").wstring());
g_bSymbolsAvailable = SymInitializeW(g_hProcess, symbol_search_path.c_str(), true); g_bSymbolsAvailable = SymInitializeW(g_hProcess, symbol_search_path.c_str(), true);
std::wcout << std::format(L"Init symbols with PDB at {}", symbol_search_path) << std::endl; std::wcout << std::format(L"Init symbols with PDB at {}", symbol_search_path) << std::endl;
@ -870,12 +878,12 @@ int main() {
g_bSymbolsAvailable = SymInitializeW(g_hProcess, nullptr, true); g_bSymbolsAvailable = SymInitializeW(g_hProcess, nullptr, true);
std::cout << "Init symbols without PDB" << std::endl; std::cout << "Init symbols without PDB" << std::endl;
} }
if (!g_bSymbolsAvailable) { if (!g_bSymbolsAvailable) {
std::wcerr << std::format(L"SymInitialize error: 0x{:x}", GetLastError()) << std::endl; std::wcerr << std::format(L"SymInitialize error: 0x{:x}", GetLastError()) << std::endl;
} }
if (pProgressDialog) if (pProgressDialog)
pProgressDialog->SetLine(3, L"Reading troubleshooting data", FALSE, NULL); pProgressDialog->SetLine(3, L"Reading troubleshooting data", FALSE, NULL);
std::wstring stackTrace(exinfo.dwStackTraceLength, L'\0'); std::wstring stackTrace(exinfo.dwStackTraceLength, L'\0');
@ -930,13 +938,23 @@ int main() {
} while (false); } while (false);
} }
const bool is_external_event = exinfo.ExceptionRecord.ExceptionCode == CUSTOM_EXCEPTION_EXTERNAL_EVENT;
std::wostringstream log; std::wostringstream log;
log << std::format(L"Unhandled native exception occurred at {}", to_address_string(exinfo.ContextRecord.Rip, false)) << std::endl;
log << std::format(L"Code: {:X}", exinfo.ExceptionRecord.ExceptionCode) << std::endl; if (!is_external_event)
{
log << std::format(L"Unhandled native exception occurred at {}", to_address_string(exinfo.ContextRecord.Rip, false)) << std::endl;
log << std::format(L"Code: {:X}", exinfo.ExceptionRecord.ExceptionCode) << std::endl;
}
else
{
log << L"CLR error occurred" << std::endl;
}
if (shutup) if (shutup)
log << L"======= Crash handler was globally muted(shutdown?) =======" << std::endl; log << L"======= Crash handler was globally muted(shutdown?) =======" << std::endl;
if (dumpPath.empty()) if (dumpPath.empty())
log << L"Dump skipped" << std::endl; log << L"Dump skipped" << std::endl;
else if (dumpError.empty()) else if (dumpError.empty())
@ -949,9 +967,19 @@ int main() {
if (pProgressDialog) if (pProgressDialog)
pProgressDialog->SetLine(3, L"Refreshing Module List", FALSE, NULL); pProgressDialog->SetLine(3, L"Refreshing Module List", FALSE, NULL);
std::wstring window_log_str;
// Cut the log here for external events, the rest is unreadable and doesn't matter since we can't get
// symbols for mixed-mode stacks yet.
if (is_external_event)
window_log_str = log.str();
SymRefreshModuleList(GetCurrentProcess()); SymRefreshModuleList(GetCurrentProcess());
print_exception_info(exinfo.hThreadHandle, exinfo.ExceptionPointers, exinfo.ContextRecord, log); print_exception_info(exinfo.hThreadHandle, exinfo.ExceptionPointers, exinfo.ContextRecord, log);
const auto window_log_str = log.str();
if (!is_external_event)
window_log_str = log.str();
print_exception_info_extended(exinfo.ExceptionPointers, exinfo.ContextRecord, log); print_exception_info_extended(exinfo.ExceptionPointers, exinfo.ContextRecord, log);
std::wofstream(logPath) << log.str(); std::wofstream(logPath) << log.str();
@ -1003,7 +1031,7 @@ int main() {
R"aa(<a href="help">Help</a> | <a href="logdir">Open log directory</a> | <a href="logfile">Open log file</a>)aa" R"aa(<a href="help">Help</a> | <a href="logdir">Open log directory</a> | <a href="logfile">Open log file</a>)aa"
); );
#endif #endif
// Can't do this, xiv stops pumping messages here // Can't do this, xiv stops pumping messages here
//config.hwndParent = FindWindowA("FFXIVGAME", NULL); //config.hwndParent = FindWindowA("FFXIVGAME", NULL);
@ -1056,13 +1084,13 @@ int main() {
return (*reinterpret_cast<decltype(callback)*>(dwRefData))(hwnd, uNotification, wParam, lParam); return (*reinterpret_cast<decltype(callback)*>(dwRefData))(hwnd, uNotification, wParam, lParam);
}; };
config.lpCallbackData = reinterpret_cast<LONG_PTR>(&callback); config.lpCallbackData = reinterpret_cast<LONG_PTR>(&callback);
if (pProgressDialog) { if (pProgressDialog) {
pProgressDialog->StopProgressDialog(); pProgressDialog->StopProgressDialog();
pProgressDialog->Release(); pProgressDialog->Release();
pProgressDialog = NULL; pProgressDialog = NULL;
} }
const auto kill_game = [&] { TerminateProcess(g_hProcess, exinfo.ExceptionRecord.ExceptionCode); }; const auto kill_game = [&] { TerminateProcess(g_hProcess, exinfo.ExceptionRecord.ExceptionCode); };
if (shutup) { if (shutup) {