Print dotnet stack trace on veh error msgbox, and use precompiled headers.

This commit is contained in:
Soreepeong 2021-12-24 12:23:23 +09:00
parent 2c99778eeb
commit e64cc7e687
16 changed files with 298 additions and 170 deletions

View file

@ -32,6 +32,8 @@
<IntDir>obj\$(Configuration)\</IntDir>
</PropertyGroup>
<Import Project="$(VCTargetsPath)\Microsoft.Cpp.props" />
<PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Debug|x64'" />
<PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Release|x64'" />
<ItemDefinitionGroup>
<ClCompile>
<WarningLevel>Level3</WarningLevel>
@ -81,21 +83,38 @@
</Content>
</ItemGroup>
<ItemGroup>
<ClCompile Include="..\lib\CoreCLR\boot.cpp" />
<ClCompile Include="..\lib\CoreCLR\CoreCLR.cpp" />
<ClCompile Include="..\lib\CoreCLR\pch.cpp" />
<ClCompile Include="dllmain.cpp" />
<ClCompile Include="rewrite_entrypoint.cpp" />
<ClCompile Include="veh.cpp" />
<ClCompile Include="..\lib\CoreCLR\boot.cpp">
<PrecompiledHeader Condition="'$(Configuration)|$(Platform)'=='Debug|x64'">NotUsing</PrecompiledHeader>
<PrecompiledHeader Condition="'$(Configuration)|$(Platform)'=='Release|x64'">NotUsing</PrecompiledHeader>
</ClCompile>
<ClCompile Include="..\lib\CoreCLR\CoreCLR.cpp">
<PrecompiledHeader Condition="'$(Configuration)|$(Platform)'=='Debug|x64'">NotUsing</PrecompiledHeader>
<PrecompiledHeader Condition="'$(Configuration)|$(Platform)'=='Release|x64'">NotUsing</PrecompiledHeader>
</ClCompile>
<ClCompile Include="pch.cpp">
<PrecompiledHeader Condition="'$(Configuration)|$(Platform)'=='Debug|x64'">Create</PrecompiledHeader>
<PrecompiledHeader Condition="'$(Configuration)|$(Platform)'=='Release|x64'">Create</PrecompiledHeader>
</ClCompile>
<ClCompile Include="dllmain.cpp">
<PrecompiledHeader Condition="'$(Configuration)|$(Platform)'=='Debug|x64'">Use</PrecompiledHeader>
<PrecompiledHeader Condition="'$(Configuration)|$(Platform)'=='Release|x64'">Use</PrecompiledHeader>
</ClCompile>
<ClCompile Include="rewrite_entrypoint.cpp">
<PrecompiledHeader Condition="'$(Configuration)|$(Platform)'=='Debug|x64'">Use</PrecompiledHeader>
<PrecompiledHeader Condition="'$(Configuration)|$(Platform)'=='Release|x64'">Use</PrecompiledHeader>
</ClCompile>
<ClCompile Include="veh.cpp">
<PrecompiledHeader Condition="'$(Configuration)|$(Platform)'=='Debug|x64'">Use</PrecompiledHeader>
<PrecompiledHeader Condition="'$(Configuration)|$(Platform)'=='Release|x64'">Use</PrecompiledHeader>
</ClCompile>
</ItemGroup>
<ItemGroup>
<ClInclude Include="..\lib\CoreCLR\boot.h" />
<ClInclude Include="..\lib\CoreCLR\CoreCLR.h" />
<ClInclude Include="..\lib\CoreCLR\core\coreclr_delegates.h" />
<ClInclude Include="..\lib\CoreCLR\core\hostfxr.h" />
<ClInclude Include="..\lib\CoreCLR\framework.h" />
<ClInclude Include="..\lib\CoreCLR\nethost\nethost.h" />
<ClInclude Include="..\lib\CoreCLR\pch.h" />
<ClInclude Include="pch.h" />
<ClInclude Include="veh.h" />
</ItemGroup>
<ItemGroup>

View file

@ -1,63 +1,59 @@
<?xml version="1.0" encoding="utf-8"?>
<Project ToolsVersion="4.0" xmlns="http://schemas.microsoft.com/developer/msbuild/2003">
<ItemGroup>
<Filter Include="Source Files">
<UniqueIdentifier>{4FC737F1-C7A5-4376-A066-2A32D752A2FF}</UniqueIdentifier>
<Extensions>cpp;c;cc;cxx;c++;cppm;ixx;def;odl;idl;hpj;bat;asm;asmx</Extensions>
</Filter>
<Filter Include="Header Files">
<UniqueIdentifier>{93995380-89BD-4b04-88EB-625FBE52EBFB}</UniqueIdentifier>
<Extensions>h;hh;hpp;hxx;h++;hm;inl;inc;ipp;xsd</Extensions>
</Filter>
<Filter Include="Library Files">
<UniqueIdentifier>{18be40ac-9367-46ff-b848-4c528aa97a8d}</UniqueIdentifier>
<Extensions>lib</Extensions>
</Filter>
<Filter Include="CoreCLR">
<UniqueIdentifier>{dc468303-865e-43bd-908f-a3542c4bb669}</UniqueIdentifier>
</Filter>
<Filter Include="Dalamud.Boot DLL">
<UniqueIdentifier>{4FC737F1-C7A5-4376-A066-2A32D752A2FF}</UniqueIdentifier>
<Extensions>cpp;c;cc;cxx;c++;cppm;ixx;def;odl;idl;hpj;bat;asm;asmx</Extensions>
</Filter>
</ItemGroup>
<ItemGroup>
<ClCompile Include="dllmain.cpp">
<Filter>Source Files</Filter>
<Filter>Dalamud.Boot DLL</Filter>
</ClCompile>
<ClCompile Include="..\lib\CoreCLR\pch.cpp">
<Filter>Source Files</Filter>
</ClCompile>
<ClCompile Include="..\lib\CoreCLR\boot.cpp">
<Filter>Source Files</Filter>
</ClCompile>
<ClCompile Include="..\lib\CoreCLR\CoreCLR.cpp">
<Filter>Source Files</Filter>
<ClCompile Include="pch.cpp">
<Filter>Dalamud.Boot DLL</Filter>
</ClCompile>
<ClCompile Include="veh.cpp">
<Filter>Source Files</Filter>
<Filter>Dalamud.Boot DLL</Filter>
</ClCompile>
<ClCompile Include="rewrite_entrypoint.cpp">
<Filter>Source Files</Filter>
<Filter>Dalamud.Boot DLL</Filter>
</ClCompile>
<ClCompile Include="..\lib\CoreCLR\CoreCLR.cpp">
<Filter>CoreCLR</Filter>
</ClCompile>
<ClCompile Include="..\lib\CoreCLR\boot.cpp">
<Filter>CoreCLR</Filter>
</ClCompile>
</ItemGroup>
<ItemGroup>
<ClInclude Include="..\lib\CoreCLR\core\coreclr_delegates.h">
<Filter>Header Files</Filter>
</ClInclude>
<ClInclude Include="..\lib\CoreCLR\core\hostfxr.h">
<Filter>Header Files</Filter>
</ClInclude>
<ClInclude Include="..\lib\CoreCLR\nethost\nethost.h">
<Filter>Header Files</Filter>
</ClInclude>
<ClInclude Include="..\lib\CoreCLR\CoreCLR.h">
<Filter>Header Files</Filter>
</ClInclude>
<ClInclude Include="..\lib\CoreCLR\framework.h">
<Filter>Header Files</Filter>
</ClInclude>
<ClInclude Include="..\lib\CoreCLR\pch.h">
<Filter>Header Files</Filter>
<Filter>CoreCLR</Filter>
</ClInclude>
<ClInclude Include="..\lib\CoreCLR\boot.h">
<Filter>Header Files</Filter>
<Filter>CoreCLR</Filter>
</ClInclude>
<ClInclude Include="..\lib\CoreCLR\core\coreclr_delegates.h">
<Filter>CoreCLR</Filter>
</ClInclude>
<ClInclude Include="..\lib\CoreCLR\core\hostfxr.h">
<Filter>CoreCLR</Filter>
</ClInclude>
<ClInclude Include="..\lib\CoreCLR\nethost\nethost.h">
<Filter>CoreCLR</Filter>
</ClInclude>
<ClInclude Include="veh.h">
<Filter>Header Files</Filter>
<Filter>Dalamud.Boot DLL</Filter>
</ClInclude>
<ClInclude Include="pch.h">
<Filter>Dalamud.Boot DLL</Filter>
</ClInclude>
</ItemGroup>
<ItemGroup>

View file

@ -1,10 +1,5 @@
#define WIN32_LEAN_AND_MEAN
#define DllExport extern "C" __declspec(dllexport)
#include "pch.h"
#include <filesystem>
#include <Windows.h>
#include "..\lib\CoreCLR\CoreCLR.h"
#include "..\lib\CoreCLR\boot.h"
#include "veh.h"
HMODULE g_hModule;
@ -69,7 +64,7 @@ DllExport DWORD WINAPI Initialize(LPVOID lpParam)
std::wstring runtimeconfig_path = _wcsdup(fs_module_path.replace_filename(L"Dalamud.runtimeconfig.json").c_str());
std::wstring module_path = _wcsdup(fs_module_path.replace_filename(L"Dalamud.dll").c_str());
// =========================================================================== //
// ============================== CLR ========================================= //
void* entrypoint_vfn;
int result = InitializeClrAndGetEntryPoint(
@ -86,10 +81,6 @@ DllExport DWORD WINAPI Initialize(LPVOID lpParam)
typedef void (CORECLR_DELEGATE_CALLTYPE* custom_component_entry_point_fn)(LPVOID);
custom_component_entry_point_fn entrypoint_fn = reinterpret_cast<custom_component_entry_point_fn>(entrypoint_vfn);
printf("Initializing Dalamud... ");
entrypoint_fn(lpParam);
printf("Done!\n");
// ============================== VEH ======================================== //
printf("Initializing VEH... ");
@ -108,7 +99,11 @@ DllExport DWORD WINAPI Initialize(LPVOID lpParam)
printf("VEH was disabled manually\n");
}
// =========================================================================== //
// ============================== Dalamud ==================================== //
printf("Initializing Dalamud... ");
entrypoint_fn(lpParam);
printf("Done!\n");
#ifndef NDEBUG
fclose(stdin);

41
Dalamud.Boot/pch.h Normal file
View file

@ -0,0 +1,41 @@
// pch.h: This is a precompiled header file.
// Files listed below are compiled only once, improving build performance for future builds.
// This also affects IntelliSense performance, including code completion and many code browsing features.
// However, files listed here are ALL re-compiled if any one of them is updated between builds.
// Do not add files here that you will be updating frequently as this negates the performance advantage.
#ifndef PCH_H
#define PCH_H
// Exclude rarely-used stuff from Windows headers
#define WIN32_LEAN_AND_MEAN
// Windows Header Files
#include <windows.h>
#include <DbgHelp.h>
#include <PathCch.h>
#include <Psapi.h>
#include <Shlobj.h>
#include <TlHelp32.h>
// C++ Standard Libraries
#include <cassert>
#include <cstdio>
#include <filesystem>
#include <format>
#include <fstream>
#include <span>
#include <mutex>
// https://github.com/dotnet/coreclr
#include "..\lib\CoreCLR\CoreCLR.h"
#include "..\lib\CoreCLR\boot.h"
// Commonly used macros
#define DllExport extern "C" __declspec(dllexport)
// Global variables
extern HMODULE g_hModule;
extern std::optional<CoreCLR> g_clr;
#endif //PCH_H

View file

@ -1,15 +1,5 @@
#define WIN32_LEAN_AND_MEAN
#define DllExport extern "C" __declspec(dllexport)
#include "pch.h"
#include <cassert>
#include <filesystem>
#include <span>
#include <Windows.h>
#include <PathCch.h>
#include <Psapi.h>
extern HMODULE g_hModule;
DllExport DWORD WINAPI Initialize(LPVOID lpParam);
struct RewrittenEntryPointParameters {

View file

@ -1,12 +1,6 @@
#define WIN32_LEAN_AND_MEAN
#include "pch.h"
#include "veh.h"
#include <filesystem>
#include <fstream>
#include <Windows.h>
#include <DbgHelp.h>
#include <format>
#include <string>
#include <TlHelp32.h>
bool is_whitelist_exception(const DWORD code)
{
@ -51,14 +45,16 @@ bool is_whitelist_exception(const DWORD code)
}
bool get_module_file_and_base(const DWORD64 address, DWORD64* module_base, std::filesystem::path& module_file)
bool get_module_file_and_base(const DWORD64 address, DWORD64& module_base, std::filesystem::path& module_file)
{
HMODULE handle;
if (GetModuleHandleExA(GET_MODULE_HANDLE_EX_FLAG_FROM_ADDRESS | GET_MODULE_HANDLE_EX_FLAG_UNCHANGED_REFCOUNT, reinterpret_cast<LPCSTR>(address), &handle))
{
if (wchar_t path[1024]; GetModuleFileNameW(handle, path, sizeof path / 2) > 0)
std::wstring path(PATHCCH_MAX_CCH, L'\0');
path.resize(GetModuleFileNameW(handle, &path[0], static_cast<DWORD>(path.size())));
if (!path.empty())
{
*module_base = reinterpret_cast<DWORD64>(handle);
module_base = reinterpret_cast<DWORD64>(handle);
module_file = path;
return true;
}
@ -70,22 +66,24 @@ bool get_module_file_and_base(const DWORD64 address, DWORD64* module_base, std::
bool is_ffxiv_address(const DWORD64 address)
{
DWORD64 module_base;
if (std::filesystem::path module_path; get_module_file_and_base(address, &module_base, module_path))
if (std::filesystem::path module_path; get_module_file_and_base(address, module_base, module_path))
return _wcsicmp(module_path.filename().c_str(), L"ffxiv_dx11.exe") == 0;
return false;
}
bool get_sym_from_addr(const DWORD64 address, DWORD64* displacement, std::wstring& symbol_name)
bool get_sym_from_addr(const DWORD64 address, DWORD64& displacement, std::wstring& symbol_name)
{
char buffer[sizeof(SYMBOL_INFOW) + MAX_SYM_NAME * sizeof(wchar_t)];
auto symbol = reinterpret_cast<PSYMBOL_INFOW>(buffer);
symbol->SizeOfStruct = sizeof(SYMBOL_INFO);
symbol->MaxNameLen = MAX_SYM_NAME;
union {
char buffer[sizeof(SYMBOL_INFOW) + MAX_SYM_NAME * sizeof(wchar_t)]{};
SYMBOL_INFOW symbol;
};
symbol.SizeOfStruct = sizeof(SYMBOL_INFO);
symbol.MaxNameLen = MAX_SYM_NAME;
if (SymFromAddrW(GetCurrentProcess(), address, displacement, symbol))
if (SymFromAddrW(GetCurrentProcess(), address, &displacement, &symbol) && symbol.Name[0])
{
symbol_name.assign(_wcsdup(symbol->Name));
symbol_name = symbol.Name;
return true;
}
return false;
@ -96,7 +94,7 @@ std::wstring to_address_string(const DWORD64 address, const bool try_ptrderef =
{
DWORD64 module_base;
std::filesystem::path module_path;
bool is_mod_addr = get_module_file_and_base(address, &module_base, module_path);
bool is_mod_addr = get_module_file_and_base(address, module_base, module_path);
DWORD64 value = 0;
if(try_ptrderef && address > 0x10000 && address < 0x7FFFFFFE0000)
@ -109,14 +107,32 @@ std::wstring to_address_string(const DWORD64 address, const bool try_ptrderef =
std::format(L"{:X}", address);
DWORD64 displacement;
if (std::wstring symbol; get_sym_from_addr(address, &displacement, symbol))
if (std::wstring symbol; get_sym_from_addr(address, displacement, symbol))
return std::format(L"{}\t({})", addr_str, displacement != 0 ? std::format(L"{}+0x{:X}", symbol, displacement) : std::format(L"{}", symbol));
return value != 0 ? std::format(L"{} [{}]", addr_str, to_address_string(value, false)) : addr_str;
}
void print_exception_info(const EXCEPTION_POINTERS* ex, std::wofstream& log)
void print_exception_info(const EXCEPTION_POINTERS* ex, std::wostringstream& log)
{
size_t rec_index = 0;
for (auto rec = ex->ExceptionRecord; rec; rec = rec->ExceptionRecord)
{
log << std::format(L"\nException Info #{}\n", ++rec_index);
log << std::format(L"Address: {:X}\n", rec->ExceptionCode);
log << std::format(L"Flags: {:X}\n", rec->ExceptionFlags);
log << std::format(L"Address: {:X}\n", reinterpret_cast<size_t>(rec->ExceptionAddress));
if (!rec->NumberParameters)
continue;
log << L"Parameters: ";
for (DWORD i = 0; i < rec->NumberParameters; ++i)
{
if (i != 0)
log << L", ";
log << std::format(L"{:X}", rec->ExceptionInformation[i]);
}
}
log << L"\nCall Stack\n{";
STACKFRAME64 sf;
@ -140,7 +156,7 @@ void print_exception_info(const EXCEPTION_POINTERS* ex, std::wofstream& log)
} while (sf.AddrReturn.Offset != 0 && sf.AddrPC.Offset != sf.AddrReturn.Offset);
log << L"\n}" << std::endl;
log << L"\n}\n";
ctx = *ex->ContextRecord;
@ -167,15 +183,15 @@ void print_exception_info(const EXCEPTION_POINTERS* ex, std::wofstream& log)
log << L"\n}" << std::endl;
if(ctx.Rsp <= 0x10000 || ctx.Rsp >= 0x7FFFFFFE0000)
return;
if(0x10000 < ctx.Rsp && ctx.Rsp < 0x7FFFFFFE0000)
{
log << L"\nStack\n{";
log << L"\nStack\n{";
for(DWORD64 i = 0; i < 16; i++)
log << std::format(L"\n [RSP+{:X}]\t{}", i * 8, to_address_string(*reinterpret_cast<DWORD64*>(ctx.Rsp + i * 8ull)));
for(DWORD64 i = 0; i < 16; i++)
log << std::format(L"\n [RSP+{:X}]\t{}", i * 8, to_address_string(*reinterpret_cast<DWORD64*>(ctx.Rsp + i * 8ull)));
log << L"\n}" << std::endl;
log << L"\n}\n";
}
log << L"\nModules\n{";
@ -194,17 +210,12 @@ void print_exception_info(const EXCEPTION_POINTERS* ex, std::wofstream& log)
CloseHandle(snap);
}
log << L"\n}" << std::endl;
log << L"\n}\n";
}
bool g_veh_message_open;
LONG exception_handler(EXCEPTION_POINTERS* ex)
{
//block any further exceptions while the message box is open
if (g_veh_message_open)
for (;;) Sleep(1);
static std::mutex s_exception_handler_mutex;
if (!is_whitelist_exception(ex->ExceptionRecord->ExceptionCode))
return EXCEPTION_CONTINUE_SEARCH;
@ -212,20 +223,21 @@ LONG exception_handler(EXCEPTION_POINTERS* ex)
if (!is_ffxiv_address(ex->ContextRecord->Rip))
return EXCEPTION_CONTINUE_SEARCH;
// block any other exceptions hitting the veh while the messagebox is open
const auto lock = std::lock_guard(s_exception_handler_mutex);
DWORD64 module_base;
std::filesystem::path module_path;
get_module_file_and_base(reinterpret_cast<DWORD64>(&exception_handler), &module_base, module_path);
get_module_file_and_base(reinterpret_cast<DWORD64>(&exception_handler), module_base, module_path);
#ifndef NDEBUG
std::wstring dmp_path = _wcsdup(module_path.replace_filename(L"dalamud_appcrashd.dmp").wstring().c_str());
std::wstring dmp_path = module_path.replace_filename(L"dalamud_appcrashd.dmp").wstring();
#else
std::wstring dmp_path = _wcsdup(module_path.replace_filename(L"dalamud_appcrash.dmp").wstring().c_str());
std::wstring dmp_path = module_path.replace_filename(L"dalamud_appcrash.dmp").wstring();
#endif
std::wstring log_path = _wcsdup(module_path.replace_filename(L"dalamud_appcrash.log").wstring().c_str());
std::wofstream log;
log.open(log_path, std::ios::trunc);
std::wstring log_path = module_path.replace_filename(L"dalamud_appcrash.log").wstring();
std::wostringstream log;
log << std::format(L"Unhandled native exception occurred at {}", to_address_string(ex->ContextRecord->Rip, false)) << std::endl;
log << std::format(L"Code: {:X}", ex->ExceptionRecord->ExceptionCode) << std::endl;
log << std::format(L"Dump at: {}", dmp_path) << std::endl;
@ -234,8 +246,6 @@ LONG exception_handler(EXCEPTION_POINTERS* ex)
SymRefreshModuleList(GetCurrentProcess());
print_exception_info(ex, log);
log.close();
MINIDUMP_EXCEPTION_INFORMATION ex_info;
ex_info.ClientPointers = false;
ex_info.ExceptionPointers = ex;
@ -245,31 +255,42 @@ LONG exception_handler(EXCEPTION_POINTERS* ex)
MiniDumpWriteDump(GetCurrentProcess(), GetCurrentProcessId(), file, MiniDumpWithDataSegs, &ex_info, nullptr, nullptr);
CloseHandle(file);
auto msg = L"An error within the game has occurred.\n\n"
L"This may be caused by a faulty plugin, a broken TexTools modification, any other third-party tool or simply a bug in the game.\n"
L"Please try \"Start Over\" in TexTools, an integrity check in the XIVLauncher settings and disabling plugins you don't need.\n\n"
L"The log file is located at:\n"
L"{1}\n\n"
L"Press OK to exit the application.";
void* fn;
if (const auto err = static_cast<DWORD>(g_clr->get_function_pointer(
L"Dalamud.EntryPoint, Dalamud",
L"VehCallback",
L"Dalamud.EntryPoint+VehDelegate, Dalamud",
nullptr, nullptr, &fn)))
{
const auto msg = L"An error within the game has occurred.\n\n"
L"This may be caused by a faulty plugin, a broken TexTools modification, any other third-party tool or simply a bug in the game.\n"
L"Please try \"Start Over\" or \"Download Index Backup\" in TexTools, an integrity check in the XIVLauncher settings, and disabling plugins you don't need.\n\n"
L"The log file is located at:\n"
L"{1}\n\n"
L"Press OK to exit the application.\n\nFailed to read stack trace: {2:08x}";
auto formatted = std::format(msg, dmp_path, log_path);
// block any other exceptions hitting the veh while the messagebox is open
g_veh_message_open = true;
MessageBoxW(nullptr, formatted.c_str(), L"Dalamud Error", MB_OK | MB_ICONERROR | MB_TOPMOST);
g_veh_message_open = false;
// show in another thread to prevent messagebox from pumping messages of current thread
std::thread([&]() {
MessageBoxW(nullptr, std::format(msg, dmp_path, log_path, err).c_str(), L"Dalamud Error", MB_OK | MB_ICONERROR | MB_TOPMOST);
}).join();
}
else
{
((void(__stdcall*)(const void*, const void*, const void*))fn)(dmp_path.c_str(), log_path.c_str(), log.str().c_str());
}
return EXCEPTION_CONTINUE_SEARCH;
}
PVOID g_veh_handle;
PVOID g_veh_handle = nullptr;
bool veh::add_handler()
{
if (g_veh_handle)
return false;
g_veh_handle = AddVectoredExceptionHandler(0, exception_handler);
g_veh_handle = AddVectoredExceptionHandler(1, exception_handler);
SetUnhandledExceptionFilter(nullptr);
return g_veh_handle != nullptr;
}

View file

@ -90,15 +90,12 @@
<ItemGroup>
<ClCompile Include="..\lib\CoreCLR\boot.cpp" />
<ClCompile Include="..\lib\CoreCLR\CoreCLR.cpp" />
<ClCompile Include="..\lib\CoreCLR\pch.cpp" />
<ClCompile Include="main.cpp" />
</ItemGroup>
<ItemGroup>
<ClInclude Include="..\lib\CoreCLR\boot.h" />
<ClInclude Include="..\lib\CoreCLR\CoreCLR.h" />
<ClInclude Include="..\lib\CoreCLR\core\coreclr_delegates.h" />
<ClInclude Include="..\lib\CoreCLR\core\hostfxr.h" />
<ClInclude Include="..\lib\CoreCLR\framework.h" />
<ClInclude Include="..\lib\CoreCLR\nethost\nethost.h" />
<ClInclude Include="..\lib\CoreCLR\pch.h" />
</ItemGroup>

View file

@ -31,9 +31,6 @@
<ClCompile Include="main.cpp">
<Filter>Source Files</Filter>
</ClCompile>
<ClCompile Include="..\lib\CoreCLR\pch.cpp">
<Filter>Source Files</Filter>
</ClCompile>
<ClCompile Include="..\lib\CoreCLR\boot.cpp">
<Filter>Source Files</Filter>
</ClCompile>
@ -42,15 +39,9 @@
</ClCompile>
</ItemGroup>
<ItemGroup>
<ClInclude Include="..\lib\CoreCLR\framework.h">
<Filter>Header Files</Filter>
</ClInclude>
<ClInclude Include="..\lib\CoreCLR\pch.h">
<Filter>Header Files</Filter>
</ClInclude>
<ClInclude Include="..\lib\CoreCLR\boot.h">
<Filter>Header Files</Filter>
</ClInclude>
<ClInclude Include="..\lib\CoreCLR\CoreCLR.h">
<Filter>Header Files</Filter>
</ClInclude>

View file

@ -34,6 +34,14 @@ namespace Dalamud
/// <param name="infoPtr">Pointer to a serialized <see cref="DalamudStartInfo"/> data.</param>
public delegate void InitDelegate(IntPtr infoPtr);
/// <summary>
/// A delegate used from VEH handler on exception which CoreCLR will fast fail by default.
/// </summary>
/// <param name="dumpPath">Path to minidump file created in UTF-16.</param>
/// <param name="logPath">Path to log file to create in UTF-16.</param>
/// <param name="log">Log text in UTF-16.</param>
public delegate void VehDelegate(IntPtr dumpPath, IntPtr logPath, IntPtr log);
/// <summary>
/// Initialize Dalamud.
/// </summary>
@ -46,6 +54,57 @@ namespace Dalamud
new Thread(() => RunThread(info)).Start();
}
/// <summary>
/// Show error message along with stack trace and exit.
/// </summary>
/// <param name="dumpPath">Path to minidump file created in UTF-16.</param>
/// <param name="logPath">Path to log file to create in UTF-16.</param>
/// <param name="log">Log text in UTF-16.</param>
public static void VehCallback(IntPtr dumpPath, IntPtr logPath, IntPtr log)
{
string stackTrace;
try
{
stackTrace = Environment.StackTrace;
}
catch (Exception e)
{
stackTrace = "Fail: " + e.ToString();
}
var msg = "An error within the game has occurred.\n\n"
+ "This may be caused by a faulty plugin, a broken TexTools modification, any other third-party tool or simply a bug in the game.\n"
+ "Please try \"Start Over\" or \"Download Index Backup\" in TexTools, an integrity check in the XIVLauncher settings, and disabling plugins you don't need.\n\n"
+ "The log file is located at:\n"
+ "{1}\n\n"
+ "Press OK to exit the application.\n\nStack trace:\n{2}";
try
{
File.WriteAllText(
Marshal.PtrToStringUni(logPath),
"Stack trace:\n" + stackTrace + "\n\n" + Marshal.PtrToStringUni(log));
}
catch (Exception e)
{
msg += "\n\nAdditionally, failed to write file: " + e.ToString();
}
// Show in another thread to prevent messagebox from pumping messages of current thread.
var msgThread = new Thread(() =>
{
Utility.Util.Fatal(
msg.Format(
Marshal.PtrToStringUni(dumpPath),
Marshal.PtrToStringUni(logPath),
stackTrace),
"Dalamud Error",
false);
});
msgThread.Start();
msgThread.Join();
}
/// <summary>
/// Initialize all Dalamud subsystems and start running on the main thread.
/// </summary>

View file

@ -326,12 +326,14 @@ namespace Dalamud.Utility
/// </summary>
/// <param name="message">MessageBox body.</param>
/// <param name="caption">MessageBox caption (title).</param>
public static void Fatal(string message, string caption)
/// <param name="exit">Specify whether to exit immediately.</param>
public static void Fatal(string message, string caption, bool exit = true)
{
var flags = NativeFunctions.MessageBoxType.Ok | NativeFunctions.MessageBoxType.IconError;
var flags = NativeFunctions.MessageBoxType.Ok | NativeFunctions.MessageBoxType.IconError | NativeFunctions.MessageBoxType.Topmost;
_ = NativeFunctions.MessageBoxW(Process.GetCurrentProcess().MainWindowHandle, message, caption, flags);
Environment.Exit(-1);
if (exit)
Environment.Exit(-1);
}
/// <summary>

View file

@ -72,7 +72,17 @@ bool CoreCLR::load_runtime(const std::wstring& runtime_config_path, const struct
hdt_load_assembly_and_get_function_pointer,
reinterpret_cast<void**>(&m_load_assembly_and_get_function_pointer_fptr));
if (result != 0 || m_load_assembly_and_get_function_pointer_fptr == nullptr)
if (result != 0 || m_load_assembly_and_get_function_pointer_fptr == nullptr) {
m_hostfxr_close_fptr(context);
return result;
}
result = m_hostfxr_get_runtime_delegate_fptr(
context,
hdt_get_function_pointer,
reinterpret_cast<void**>(&m_get_function_pointer_fptr));
if (result != 0 || m_get_function_pointer_fptr == nullptr)
{
m_hostfxr_close_fptr(context);
return result;
@ -99,6 +109,22 @@ int CoreCLR::load_assembly_and_get_function_pointer(
return result;
};
int CoreCLR::get_function_pointer(
const wchar_t* type_name,
const wchar_t* method_name,
const wchar_t* delegate_type_name,
void* load_context,
void* reserved,
void** delegate) const
{
int result = m_get_function_pointer_fptr(type_name, method_name, delegate_type_name, load_context, reserved, delegate);
if (result != 0)
delegate = nullptr;
return result;
}
/* Helpers */
uint64_t CoreCLR::load_library(const wchar_t* path)
{

View file

@ -24,12 +24,20 @@ class CoreCLR {
const wchar_t* delegate_type_name,
void* reserved,
void** delegate) const;
int get_function_pointer(
const wchar_t* type_name,
const wchar_t* method_name,
const wchar_t* delegate_type_name,
void* load_context,
void* reserved,
void** delegate) const;
private:
/* HostFXR delegates. */
hostfxr_initialize_for_runtime_config_fn m_hostfxr_initialize_for_runtime_config_fptr{};
hostfxr_get_runtime_delegate_fn m_hostfxr_get_runtime_delegate_fptr{};
hostfxr_close_fn m_hostfxr_close_fptr{};
get_function_pointer_fn m_get_function_pointer_fptr = nullptr;
load_assembly_and_get_function_pointer_fn m_load_assembly_and_get_function_pointer_fptr = nullptr;
/* Helper functions. */

View file

@ -23,6 +23,8 @@ void ConsoleTeardown()
FreeConsole();
}
std::optional<CoreCLR> g_clr;
int InitializeClrAndGetEntryPoint(
std::wstring runtimeconfig_path,
std::wstring module_path,
@ -31,8 +33,9 @@ int InitializeClrAndGetEntryPoint(
std::wstring entrypoint_delegate_type_name,
void** entrypoint_fn)
{
g_clr = CoreCLR();
int result;
CoreCLR clr;
SetEnvironmentVariable(L"DOTNET_MULTILEVEL_LOOKUP", L"0");
//SetEnvironmentVariable(L"COMPlus_legacyCorruptedStateExceptionsPolicy", L"1");
SetEnvironmentVariable(L"DOTNET_legacyCorruptedStateExceptionsPolicy", L"1");
@ -85,7 +88,7 @@ int InitializeClrAndGetEntryPoint(
};
printf("Loading hostfxr... ");
if ((result = clr.load_hostfxr(&init_parameters)) != 0)
if ((result = g_clr->load_hostfxr(&init_parameters)) != 0)
{
printf("\nError: Failed to load the `hostfxr` library (err=%d)\n", result);
return result;
@ -102,7 +105,7 @@ int InitializeClrAndGetEntryPoint(
};
printf("Loading coreclr... ");;
if ((result = clr.load_runtime(runtimeconfig_path, &runtime_parameters)) != 0)
if ((result = g_clr->load_runtime(runtimeconfig_path, &runtime_parameters)) != 0)
{
printf("\nError: Failed to load coreclr (err=%d)\n", result);
return result;
@ -112,7 +115,7 @@ int InitializeClrAndGetEntryPoint(
// =========================================================================== //
printf("Loading module... ");
if ((result = clr.load_assembly_and_get_function_pointer(
if ((result = g_clr->load_assembly_and_get_function_pointer(
module_path.c_str(),
entrypoint_assembly_name.c_str(),
entrypoint_method_name.c_str(),

View file

@ -1,7 +0,0 @@
#pragma once
// Exclude rarely-used stuff from Windows headers
#define WIN32_LEAN_AND_MEAN
// Windows Header Files
#include <windows.h>

View file

@ -1,13 +0,0 @@
// pch.h: This is a precompiled header file.
// Files listed below are compiled only once, improving build performance for future builds.
// This also affects IntelliSense performance, including code completion and many code browsing features.
// However, files listed here are ALL re-compiled if any one of them is updated between builds.
// Do not add files here that you will be updating frequently as this negates the performance advantage.
#ifndef PCH_H
#define PCH_H
// add headers that you want to pre-compile here
#include "framework.h"
#endif //PCH_H