From 9092e36b3366aa2348d54378afa57236abd3b0d8 Mon Sep 17 00:00:00 2001 From: srkizer Date: Fri, 15 Aug 2025 16:02:32 +0900 Subject: [PATCH] Reduce usage of exceptions from Boot (#2373) * wip * make pretty * Remove CRT version check from IM * fix * Simplify IsDebuggerPresent hook --- Dalamud.Boot/Dalamud.Boot.rc | 32 +++ Dalamud.Boot/Dalamud.Boot.vcxproj | 13 +- Dalamud.Boot/Dalamud.Boot.vcxproj.filters | 6 + Dalamud.Boot/dllmain.cpp | 125 ++++++---- Dalamud.Boot/error_info.cpp | 26 +++ Dalamud.Boot/error_info.h | 42 ++++ Dalamud.Boot/hooks.cpp | 41 +--- Dalamud.Boot/pch.h | 1 + Dalamud.Boot/resource.h | 13 +- Dalamud.Boot/rewrite_entrypoint.cpp | 51 ++++- Dalamud.Boot/utils.cpp | 139 +++++++---- Dalamud.Boot/utils.h | 26 ++- Dalamud.Boot/veh.cpp | 27 ++- Dalamud.Boot/xivfixes.cpp | 216 ++++++++---------- .../Dalamud.Injector.Boot.vcxproj | 8 +- .../Interface/Internal/InterfaceManager.cs | 23 -- 16 files changed, 494 insertions(+), 295 deletions(-) create mode 100644 Dalamud.Boot/error_info.cpp create mode 100644 Dalamud.Boot/error_info.h diff --git a/Dalamud.Boot/Dalamud.Boot.rc b/Dalamud.Boot/Dalamud.Boot.rc index b46e81caf..655df27e1 100644 --- a/Dalamud.Boot/Dalamud.Boot.rc +++ b/Dalamud.Boot/Dalamud.Boot.rc @@ -26,6 +26,38 @@ LANGUAGE LANG_ENGLISH, SUBLANG_ENGLISH_US RT_MANIFEST_THEMES RT_MANIFEST "themes.manifest" + +///////////////////////////////////////////////////////////////////////////// +// +// String Table +// + +STRINGTABLE +BEGIN + IDS_APPNAME "Dalamud Boot" + IDS_MSVCRT_ACTION_OPENDOWNLOAD + "Download Microsoft Visual C++ Redistributable 2022\nExit the game and download the latest setup file from Microsoft." + IDS_MSVCRT_ACTION_IGNORE + "Ignore and Continue\nAttempt to continue with the currently installed version.\nDalamud or plugins may fail to load." + IDS_MSVCRT_DIALOG_MAININSTRUCTION + "Outdated Microsoft Visual C++ Redistributable" + IDS_MSVCRT_DIALOG_CONTENT + "The Microsoft Visual C++ Redistributable version detected on this computer (v{0}.{1}.{2}.{3}) is out of date and may not work with Dalamud." + IDS_MSVCRT_DOWNLOADURL "https://aka.ms/vs/17/release/vc_redist.x64.exe" + IDS_INITIALIZEFAIL_ACTION_ABORT "Abort\nExit the game." + IDS_INITIALIZEFAIL_ACTION_CONTINUE + "Load game without Dalamud\nThe game will launch without Dalamud enabled." + IDS_INITIALIZEFAIL_DIALOG_MAININSTRUCTION "Failed to load Dalamud." + IDS_INITIALIZEFAIL_DIALOG_CONTENT + "An error is preventing Dalamud from being loaded along with the game." +END + +STRINGTABLE +BEGIN + IDS_INITIALIZEFAIL_DIALOG_FOOTER + "Last operation: {0}\nHRESULT: 0x{1:08X}\nDescription: {2}" +END + #endif // English (United States) resources ///////////////////////////////////////////////////////////////////////////// diff --git a/Dalamud.Boot/Dalamud.Boot.vcxproj b/Dalamud.Boot/Dalamud.Boot.vcxproj index a15601af4..0a4a9c563 100644 --- a/Dalamud.Boot/Dalamud.Boot.vcxproj +++ b/Dalamud.Boot/Dalamud.Boot.vcxproj @@ -48,7 +48,7 @@ Level3 true true - stdcpp20 + stdcpp23 pch.h ProgramDatabase CPPDLLTEMPLATE_EXPORTS;_WINDOWS;_USRDLL;%(PreprocessorDefinitions) @@ -65,7 +65,7 @@ true false - MultiThreadedDebugDLL + MultiThreadedDebugDLL _DEBUG;%(PreprocessorDefinitions) Use 26812 @@ -80,7 +80,7 @@ true true - MultiThreadedDLL + MultiThreadedDLL NDEBUG;%(PreprocessorDefinitions) Use 26812 @@ -133,6 +133,10 @@ NotUsing + + NotUsing + NotUsing + NotUsing @@ -176,6 +180,7 @@ + @@ -206,4 +211,4 @@ - + \ No newline at end of file diff --git a/Dalamud.Boot/Dalamud.Boot.vcxproj.filters b/Dalamud.Boot/Dalamud.Boot.vcxproj.filters index 7c26b28ff..15e3eb8b3 100644 --- a/Dalamud.Boot/Dalamud.Boot.vcxproj.filters +++ b/Dalamud.Boot/Dalamud.Boot.vcxproj.filters @@ -76,6 +76,9 @@ Dalamud.Boot DLL + + Common Boot + @@ -146,6 +149,9 @@ Dalamud.Boot DLL + + Common Boot + diff --git a/Dalamud.Boot/dllmain.cpp b/Dalamud.Boot/dllmain.cpp index eee3953ad..80a16f89a 100644 --- a/Dalamud.Boot/dllmain.cpp +++ b/Dalamud.Boot/dllmain.cpp @@ -9,11 +9,12 @@ #include "utils.h" #include "veh.h" #include "xivfixes.h" +#include "resource.h" HMODULE g_hModule; HINSTANCE g_hGameInstance = GetModuleHandleW(nullptr); -void CheckMsvcrtVersion() { +static void CheckMsvcrtVersion() { // Commit introducing inline mutex ctor: tagged vs-2022-17.14 (2024-06-18) // - https://github.com/microsoft/STL/commit/22a88260db4d754bbc067e2002430144d6ec5391 // MSVC Redist versions: @@ -28,67 +29,102 @@ void CheckMsvcrtVersion() { | (static_cast(RequiredMsvcrtVersionComponents[2]) << 16) | (static_cast(RequiredMsvcrtVersionComponents[3]) << 0); -#ifdef _DEBUG constexpr const wchar_t* RuntimeDllNames[] = { +#ifdef _DEBUG L"msvcp140d.dll", L"vcruntime140d.dll", L"vcruntime140_1d.dll", - }; #else - constexpr const wchar_t* RuntimeDllNames[] = { L"msvcp140.dll", L"vcruntime140.dll", L"vcruntime140_1.dll", - }; #endif + }; uint64_t lowestVersion = 0; for (const auto& runtimeDllName : RuntimeDllNames) { const utils::loaded_module mod(GetModuleHandleW(runtimeDllName)); if (!mod) { - logging::E("Runtime DLL not found: {}", runtimeDllName); + logging::E("MSVCRT DLL not found: {}", runtimeDllName); continue; } - try { - const auto& versionFull = mod.get_file_version(); - logging::I("Runtime DLL {} has version {}.", runtimeDllName, utils::format_file_version(versionFull)); + const auto path = mod.path() + .transform([](const auto& p) { return p.wstring(); }) + .value_or(runtimeDllName); - const auto version = (static_cast(versionFull.dwFileVersionMS) << 32) | - static_cast(versionFull.dwFileVersionLS); + if (const auto versionResult = mod.get_file_version()) { + const auto& versionFull = versionResult->get(); + logging::I("MSVCRT DLL {} has version {}.", path, utils::format_file_version(versionFull)); + + const auto version = 0ULL | + (static_cast(versionFull.dwFileVersionMS) << 32) | + (static_cast(versionFull.dwFileVersionLS) << 0); if (version < RequiredMsvcrtVersion && (lowestVersion == 0 || lowestVersion > version)) lowestVersion = version; - } catch (const std::exception& e) { - logging::E("Failed to detect Runtime DLL version for {}: {}", runtimeDllName, e.what()); + } else { + logging::E("Failed to detect MSVCRT DLL version for {}: {}", path, versionResult.error().describe()); } } - if (lowestVersion) { - switch (MessageBoxW( - nullptr, - L"Microsoft Visual C++ Redistributable should be updated, or Dalamud may not work as expected." - L" Do you want to download and install the latest version from Microsoft?" - L"\n" - L"\n* Clicking \"Yes\" will exit the game and open the download page from Microsoft." - L"\n* Clicking \"No\" will continue loading the game with Dalamud. This may fail." - L"\n" - L"\nClick \"X64\" from the table in the download page, regardless of what CPU you have.", - L"Dalamud", - MB_YESNO | MB_ICONWARNING)) { - case IDYES: - ShellExecuteW( - nullptr, - L"open", - L"https://learn.microsoft.com/en-us/cpp/windows/latest-supported-vc-redist?view=msvc-170#latest-microsoft-visual-c-redistributable-version", - nullptr, - nullptr, - SW_SHOW); - ExitProcess(0); - break; - case IDNO: - break; - } + if (!lowestVersion) + return; + + enum IdTaskDialogAction { + IdTaskDialogActionOpenDownload = 101, + IdTaskDialogActionIgnore, + }; + + const TASKDIALOG_BUTTON buttons[]{ + {IdTaskDialogActionOpenDownload, MAKEINTRESOURCEW(IDS_MSVCRT_ACTION_OPENDOWNLOAD)}, + {IdTaskDialogActionIgnore, MAKEINTRESOURCEW(IDS_MSVCRT_ACTION_IGNORE)}, + }; + + const WORD lowestVersionComponents[]{ + static_cast(lowestVersion >> 48), + static_cast(lowestVersion >> 32), + static_cast(lowestVersion >> 16), + static_cast(lowestVersion >> 0), + }; + + const auto dialogContent = std::vformat( + utils::get_string_resource(IDS_MSVCRT_DIALOG_CONTENT), + std::make_wformat_args( + lowestVersionComponents[0], + lowestVersionComponents[1], + lowestVersionComponents[2], + lowestVersionComponents[3])); + + const TASKDIALOGCONFIG config{ + .cbSize = sizeof config, + .hInstance = g_hModule, + .dwFlags = TDF_CAN_BE_MINIMIZED | TDF_ALLOW_DIALOG_CANCELLATION | TDF_USE_COMMAND_LINKS, + .pszWindowTitle = MAKEINTRESOURCEW(IDS_APPNAME), + .pszMainIcon = MAKEINTRESOURCEW(IDI_ICON1), + .pszMainInstruction = MAKEINTRESOURCEW(IDS_MSVCRT_DIALOG_MAININSTRUCTION), + .pszContent = dialogContent.c_str(), + .cButtons = _countof(buttons), + .pButtons = buttons, + .nDefaultButton = IdTaskDialogActionOpenDownload, + }; + + int buttonPressed; + if (utils::scoped_dpi_awareness_context ctx; + FAILED(TaskDialogIndirect(&config, &buttonPressed, nullptr, nullptr))) + buttonPressed = IdTaskDialogActionOpenDownload; + + switch (buttonPressed) { + case IdTaskDialogActionOpenDownload: + ShellExecuteW( + nullptr, + L"open", + utils::get_string_resource(IDS_MSVCRT_DOWNLOADURL).c_str(), + nullptr, + nullptr, + SW_SHOW); + ExitProcess(0); + break; } } @@ -103,7 +139,7 @@ HRESULT WINAPI InitializeImpl(LPVOID lpParam, HANDLE hMainThreadContinue) { } if (g_startInfo.BootShowConsole) - ConsoleSetup(L"Dalamud Boot"); + ConsoleSetup(utils::get_string_resource(IDS_APPNAME).c_str()); logging::update_dll_load_status(true); @@ -240,7 +276,7 @@ HRESULT WINAPI InitializeImpl(LPVOID lpParam, HANDLE hMainThreadContinue) { if (minHookLoaded) { logging::I("Applying fixes..."); - xivfixes::apply_all(true); + std::thread([] { xivfixes::apply_all(true); }).join(); logging::I("Fixes OK"); } else { logging::W("Skipping fixes, as MinHook has failed to load."); @@ -251,11 +287,14 @@ HRESULT WINAPI InitializeImpl(LPVOID lpParam, HANDLE hMainThreadContinue) { while (!IsDebuggerPresent()) Sleep(100); logging::I("Debugger attached."); + __debugbreak(); } - const auto fs_module_path = utils::get_module_path(g_hModule); - const auto runtimeconfig_path = std::filesystem::path(fs_module_path).replace_filename(L"Dalamud.runtimeconfig.json").wstring(); - const auto module_path = std::filesystem::path(fs_module_path).replace_filename(L"Dalamud.dll").wstring(); + const auto fs_module_path = utils::loaded_module(g_hModule).path(); + if (!fs_module_path) + return fs_module_path.error(); + const auto runtimeconfig_path = std::filesystem::path(*fs_module_path).replace_filename(L"Dalamud.runtimeconfig.json").wstring(); + const auto module_path = std::filesystem::path(*fs_module_path).replace_filename(L"Dalamud.dll").wstring(); // ============================== CLR ========================================= // diff --git a/Dalamud.Boot/error_info.cpp b/Dalamud.Boot/error_info.cpp new file mode 100644 index 000000000..02356b730 --- /dev/null +++ b/Dalamud.Boot/error_info.cpp @@ -0,0 +1,26 @@ +#include "error_info.h" + +#define WIN32_LEAN_AND_MEAN +#include + +DalamudBootError::DalamudBootError(DalamudBootErrorDescription dalamudErrorDescription, long hresult) noexcept + : m_dalamudErrorDescription(dalamudErrorDescription) + , m_hresult(hresult) { +} + +DalamudBootError::DalamudBootError(DalamudBootErrorDescription dalamudErrorDescription) noexcept + : DalamudBootError(dalamudErrorDescription, E_FAIL) { +} + +const char* DalamudBootError::describe() const { + switch (m_dalamudErrorDescription) { + case DalamudBootErrorDescription::ModuleResourceLoadFail: + return "Failed to load resource."; + case DalamudBootErrorDescription::ModuleResourceVersionReadFail: + return "Failed to query version information."; + case DalamudBootErrorDescription::ModuleResourceVersionSignatureFail: + return "Invalid version info found."; + default: + return "(unavailable)"; + } +} diff --git a/Dalamud.Boot/error_info.h b/Dalamud.Boot/error_info.h new file mode 100644 index 000000000..b5862d0dd --- /dev/null +++ b/Dalamud.Boot/error_info.h @@ -0,0 +1,42 @@ +#pragma once + +#include +#include + +typedef unsigned long DWORD; +typedef _Return_type_success_(return >= 0) long HRESULT; + +enum class DalamudBootErrorDescription { + None, + ModulePathResolutionFail, + ModuleResourceLoadFail, + ModuleResourceVersionReadFail, + ModuleResourceVersionSignatureFail, +}; + +class DalamudBootError { + DalamudBootErrorDescription m_dalamudErrorDescription; + long m_hresult; + +public: + DalamudBootError(DalamudBootErrorDescription dalamudErrorDescription, long hresult) noexcept; + DalamudBootError(DalamudBootErrorDescription dalamudErrorDescription) noexcept; + + const char* describe() const; + + operator HRESULT() const { + return m_hresult; + } +}; + +template +using DalamudExpected = std::expected< + std::conditional_t< + std::is_reference_v, + std::reference_wrapper>, + T + >, + DalamudBootError +>; + +using DalamudUnexpected = std::unexpected; diff --git a/Dalamud.Boot/hooks.cpp b/Dalamud.Boot/hooks.cpp index bb11572a1..3443a5f8a 100644 --- a/Dalamud.Boot/hooks.cpp +++ b/Dalamud.Boot/hooks.cpp @@ -82,37 +82,14 @@ void hooks::getprocaddress_singleton_import_hook::initialize() { s_dllChanged = 1; if (notiReason == LDR_DLL_NOTIFICATION_REASON_LOADED) { const auto dllName = unicode::convert(pData->Loaded.FullDllName->Buffer); - std::wstring version = L""; - std::wstring description = L""; - DWORD versionSize = GetFileVersionInfoSizeA(dllName.c_str(), NULL); - if (versionSize > 0) { - std::vector versionData(versionSize); - if (GetFileVersionInfoA(dllName.c_str(), 0, versionSize, versionData.data())) { - struct LANGANDCODEPAGE { - WORD wLanguage; - WORD wCodePage; - } *translate = nullptr; + utils::loaded_module mod(pData->Loaded.DllBase); + const auto version = mod.get_file_version() + .transform([](const auto& v) { return utils::format_file_version(v.get()); }) + .value_or(L""); - UINT uLen = 0; - LPVOID lpBuffer; - if (VerQueryValueW(versionData.data(), L"\\VarFileInfo\\Translation", (LPVOID*)&translate, &uLen) && uLen >= sizeof(LANGANDCODEPAGE)) { - // Use the first language/codepage - wchar_t subBlock[256]; - swprintf(subBlock, 256, L"\\StringFileInfo\\%04x%04x\\FileDescription", translate[0].wLanguage, translate[0].wCodePage); - - if (VerQueryValueW(versionData.data(), subBlock, &lpBuffer, &uLen)) { - description = std::wstring((wchar_t *)lpBuffer, uLen - 1); - } - - swprintf(subBlock, 256, L"\\StringFileInfo\\%04x%04x\\FileVersion", translate[0].wLanguage, translate[0].wCodePage); - - if (VerQueryValueW(versionData.data(), subBlock, &lpBuffer, &uLen)) { - version = std::wstring((wchar_t*)lpBuffer, uLen - 1); - } - } - } - } + const auto description = mod.get_description() + .value_or(L""); logging::I(R"({} "{}" ("{}" ver {}) has been loaded at 0x{:X} ~ 0x{:X} (0x{:X}); finding import table items to hook.)", LogTag, dllName, description, version, @@ -142,7 +119,9 @@ void hooks::getprocaddress_singleton_import_hook::hook_module(const utils::loade if (mod.is_current_process()) return; - const auto path = unicode::convert(mod.path().wstring()); + const auto path = mod.path() + .transform([](const auto& p) { return unicode::convert(p.wstring()); }) + .value_or(""); for (const auto& [hModule, targetFns] : m_targetFns) { for (const auto& [targetFn, pfnThunk] : targetFns) { @@ -150,7 +129,7 @@ void hooks::getprocaddress_singleton_import_hook::hook_module(const utils::loade if (void* pGetProcAddressImport; mod.find_imported_function_pointer(dllName.c_str(), targetFn.c_str(), 0, pGetProcAddressImport)) { auto& hook = m_hooks[hModule][targetFn][mod]; if (!hook) { - logging::I("{} Hooking {}!{} imported by {}", LogTag, dllName, targetFn, unicode::convert(mod.path().wstring())); + logging::I("{} Hooking {}!{} imported by {}", LogTag, dllName, targetFn, path); hook.emplace(std::format("getprocaddress_singleton_import_hook::hook_module({}!{})", dllName, targetFn), static_cast(pGetProcAddressImport), pfnThunk); } diff --git a/Dalamud.Boot/pch.h b/Dalamud.Boot/pch.h index e46927f76..ac1394e57 100644 --- a/Dalamud.Boot/pch.h +++ b/Dalamud.Boot/pch.h @@ -55,6 +55,7 @@ #include #include #include +#include #include // https://www.akenotsuki.com/misc/srell/en/ diff --git a/Dalamud.Boot/resource.h b/Dalamud.Boot/resource.h index 51acf37df..2a1cde6e2 100644 --- a/Dalamud.Boot/resource.h +++ b/Dalamud.Boot/resource.h @@ -3,12 +3,23 @@ // Used by Dalamud.Boot.rc // #define IDI_ICON1 101 +#define IDS_APPNAME 102 +#define IDS_MSVCRT_ACTION_OPENDOWNLOAD 103 +#define IDS_MSVCRT_ACTION_IGNORE 104 +#define IDS_MSVCRT_DIALOG_MAININSTRUCTION 105 +#define IDS_MSVCRT_DIALOG_CONTENT 106 +#define IDS_MSVCRT_DOWNLOADURL 107 +#define IDS_INITIALIZEFAIL_ACTION_ABORT 108 +#define IDS_INITIALIZEFAIL_ACTION_CONTINUE 109 +#define IDS_INITIALIZEFAIL_DIALOG_MAININSTRUCTION 110 +#define IDS_INITIALIZEFAIL_DIALOG_CONTENT 111 +#define IDS_INITIALIZEFAIL_DIALOG_FOOTER 112 // Next default values for new objects // #ifdef APSTUDIO_INVOKED #ifndef APSTUDIO_READONLY_SYMBOLS -#define _APS_NEXT_RESOURCE_VALUE 102 +#define _APS_NEXT_RESOURCE_VALUE 103 #define _APS_NEXT_COMMAND_VALUE 40001 #define _APS_NEXT_CONTROL_VALUE 1001 #define _APS_NEXT_SYMED_VALUE 101 diff --git a/Dalamud.Boot/rewrite_entrypoint.cpp b/Dalamud.Boot/rewrite_entrypoint.cpp index 3a1672af7..69a4ec818 100644 --- a/Dalamud.Boot/rewrite_entrypoint.cpp +++ b/Dalamud.Boot/rewrite_entrypoint.cpp @@ -2,6 +2,7 @@ #include "logging.h" #include "utils.h" +#include "resource.h" HRESULT WINAPI InitializeImpl(LPVOID lpParam, HANDLE hMainThreadContinue); @@ -379,12 +380,50 @@ extern "C" void WINAPI RewrittenEntryPoint_AdjustedStack(RewrittenEntryPointPara auto desc = err.Description(); if (desc.length() == 0) desc = err.ErrorMessage(); - if (MessageBoxW(nullptr, std::format( - L"Failed to load Dalamud. Load game without Dalamud(yes) or abort(no)?\n\n{}\n{}", - last_operation, - desc.GetBSTR()).c_str(), - L"Dalamud.Boot", MB_OK | MB_YESNO) == IDNO) - ExitProcess(-1); + + enum IdTaskDialogAction { + IdTaskDialogActionAbort = 101, + IdTaskDialogActionContinue, + }; + + const TASKDIALOG_BUTTON buttons[]{ + {IdTaskDialogActionAbort, MAKEINTRESOURCEW(IDS_INITIALIZEFAIL_ACTION_ABORT)}, + {IdTaskDialogActionContinue, MAKEINTRESOURCEW(IDS_INITIALIZEFAIL_ACTION_CONTINUE)}, + }; + + const auto hru32 = static_cast(hr); + const auto footer = std::vformat( + utils::get_string_resource(IDS_INITIALIZEFAIL_DIALOG_FOOTER), + std::make_wformat_args( + last_operation, + hru32, + desc.GetBSTR())); + + const TASKDIALOGCONFIG config{ + .cbSize = sizeof config, + .hInstance = g_hModule, + .dwFlags = TDF_CAN_BE_MINIMIZED | TDF_ALLOW_DIALOG_CANCELLATION | TDF_USE_COMMAND_LINKS | TDF_EXPAND_FOOTER_AREA, + .pszWindowTitle = MAKEINTRESOURCEW(IDS_APPNAME), + .pszMainIcon = MAKEINTRESOURCEW(IDI_ICON1), + .pszMainInstruction = MAKEINTRESOURCEW(IDS_INITIALIZEFAIL_DIALOG_MAININSTRUCTION), + .pszContent = MAKEINTRESOURCEW(IDS_INITIALIZEFAIL_DIALOG_CONTENT), + .cButtons = _countof(buttons), + .pButtons = buttons, + .nDefaultButton = IdTaskDialogActionAbort, + .pszFooter = footer.c_str(), + }; + + int buttonPressed; + if (utils::scoped_dpi_awareness_context ctx; + FAILED(TaskDialogIndirect(&config, &buttonPressed, nullptr, nullptr))) + buttonPressed = IdTaskDialogActionAbort; + + switch (buttonPressed) { + case IdTaskDialogActionAbort: + ExitProcess(-1); + break; + } + if (hMainThreadContinue) { CloseHandle(hMainThreadContinue); hMainThreadContinue = nullptr; diff --git a/Dalamud.Boot/utils.cpp b/Dalamud.Boot/utils.cpp index 91be2fb31..9820e5b7f 100644 --- a/Dalamud.Boot/utils.cpp +++ b/Dalamud.Boot/utils.cpp @@ -3,22 +3,27 @@ #include "utils.h" -std::filesystem::path utils::loaded_module::path() const { - std::wstring buf(MAX_PATH, L'\0'); - for (;;) { - if (const auto len = GetModuleFileNameExW(GetCurrentProcess(), m_hModule, &buf[0], static_cast(buf.size())); len != buf.size()) { - if (buf.empty()) - throw std::runtime_error(std::format("Failed to resolve module path: Win32 error {}", GetLastError())); +DalamudExpected utils::loaded_module::path() const { + for (std::wstring buf(MAX_PATH, L'\0');; buf.resize(buf.size() * 2)) { + if (const auto len = GetModuleFileNameW(m_hModule, &buf[0], static_cast(buf.size())); + len != buf.size()) { + if (!len) { + return DalamudUnexpected( + std::in_place, + DalamudBootErrorDescription::ModulePathResolutionFail, + HRESULT_FROM_WIN32(GetLastError())); + } + buf.resize(len); return buf; } - if (buf.size() * 2 < PATHCCH_MAX_CCH) - buf.resize(buf.size() * 2); - else if (auto p = std::filesystem::path(buf); exists(p)) - return p; - else - throw std::runtime_error("Failed to resolve module path: no amount of buffer size would fit the data"); + if (buf.size() > PATHCCH_MAX_CCH) { + return DalamudUnexpected( + std::in_place, + DalamudBootErrorDescription::ModulePathResolutionFail, + E_OUTOFMEMORY); + } } } @@ -144,21 +149,24 @@ void* utils::loaded_module::get_imported_function_pointer(const char* pcszDllNam throw std::runtime_error(std::format("Failed to find import for {}!{} ({}).", pcszDllName, pcszFunctionName ? pcszFunctionName : "", hintOrOrdinal)); } -std::unique_ptr, decltype(&FreeResource)> utils::loaded_module::get_resource(LPCWSTR lpName, LPCWSTR lpType) const { +DalamudExpected, decltype(&FreeResource)>> utils::loaded_module::get_resource(LPCWSTR lpName, LPCWSTR lpType) const { const auto hres = FindResourceW(m_hModule, lpName, lpType); if (!hres) - throw std::runtime_error("No such resource"); + return DalamudUnexpected(std::in_place, DalamudBootErrorDescription::ModuleResourceLoadFail, GetLastError()); const auto hRes = LoadResource(m_hModule, hres); if (!hRes) - throw std::runtime_error("LoadResource failure"); + return DalamudUnexpected(std::in_place, DalamudBootErrorDescription::ModuleResourceLoadFail, GetLastError()); - return {hRes, &FreeResource}; + return std::unique_ptr, decltype(&FreeResource)>(hRes, &FreeResource); } -std::wstring utils::loaded_module::get_description() const { - const auto rsrc = get_resource(MAKEINTRESOURCE(VS_VERSION_INFO), RT_VERSION); - const auto pBlock = LockResource(rsrc.get()); +DalamudExpected utils::loaded_module::get_description() const { + auto rsrc = get_resource(MAKEINTRESOURCE(VS_VERSION_INFO), RT_VERSION); + if (!rsrc) + return DalamudUnexpected(std::move(rsrc.error())); + + const auto pBlock = LockResource(rsrc->get()); struct LANGANDCODEPAGE { WORD wLanguage; @@ -166,44 +174,65 @@ std::wstring utils::loaded_module::get_description() const { } * lpTranslate; UINT cbTranslate; if (!VerQueryValueW(pBlock, - TEXT("\\VarFileInfo\\Translation"), + L"\\VarFileInfo\\Translation", reinterpret_cast(&lpTranslate), &cbTranslate)) { - throw std::runtime_error("Invalid version information (1)"); + return DalamudUnexpected( + std::in_place, + DalamudBootErrorDescription::ModuleResourceVersionReadFail, + HRESULT_FROM_WIN32(GetLastError())); } for (size_t i = 0; i < (cbTranslate / sizeof(LANGANDCODEPAGE)); i++) { + wchar_t subblockNameBuf[64]; + *std::format_to_n( + subblockNameBuf, + _countof(subblockNameBuf), + L"\\StringFileInfo\\{:04x}{:04x}\\FileDescription", + lpTranslate[i].wLanguage, + lpTranslate[i].wCodePage).out = 0;; + wchar_t* buf = nullptr; UINT size = 0; - if (!VerQueryValueW(pBlock, - std::format(L"\\StringFileInfo\\{:04x}{:04x}\\FileDescription", - lpTranslate[i].wLanguage, - lpTranslate[i].wCodePage).c_str(), - reinterpret_cast(&buf), - &size)) { + if (!VerQueryValueW(pBlock, subblockNameBuf, reinterpret_cast(&buf), &size)) continue; - } + auto currName = std::wstring_view(buf, size); - while (!currName.empty() && currName.back() == L'\0') - currName = currName.substr(0, currName.size() - 1); + if (const auto p = currName.find(L'\0'); p != std::string::npos) + currName = currName.substr(0, p); if (currName.empty()) continue; return std::wstring(currName); } - throw std::runtime_error("Invalid version information (2)"); + return DalamudUnexpected( + std::in_place, + DalamudBootErrorDescription::ModuleResourceVersionReadFail, + HRESULT_FROM_WIN32(ERROR_NOT_FOUND)); } -const VS_FIXEDFILEINFO& utils::loaded_module::get_file_version() const { - const auto rsrc = get_resource(MAKEINTRESOURCE(VS_VERSION_INFO), RT_VERSION); - const auto pBlock = LockResource(rsrc.get()); +std::expected, DalamudBootError> utils::loaded_module::get_file_version() const { + auto rsrc = get_resource(MAKEINTRESOURCE(VS_VERSION_INFO), RT_VERSION); + if (!rsrc) + return DalamudUnexpected(std::move(rsrc.error())); + + const auto pBlock = LockResource(rsrc->get()); UINT size = 0; LPVOID lpBuffer = nullptr; - if (!VerQueryValueW(pBlock, L"\\", &lpBuffer, &size)) - throw std::runtime_error("Failed to query version information."); + if (!VerQueryValueW(pBlock, L"\\", &lpBuffer, &size)) { + return std::unexpected( + std::in_place, + DalamudBootErrorDescription::ModuleResourceVersionReadFail, + HRESULT_FROM_WIN32(GetLastError())); + } + const VS_FIXEDFILEINFO& versionInfo = *static_cast(lpBuffer); - if (versionInfo.dwSignature != 0xfeef04bd) - throw std::runtime_error("Invalid version info found."); + if (versionInfo.dwSignature != 0xfeef04bd) { + return std::unexpected( + std::in_place, + DalamudBootErrorDescription::ModuleResourceVersionSignatureFail); + } + return versionInfo; } @@ -589,17 +618,10 @@ bool utils::is_running_on_wine() { return g_startInfo.Platform != "WINDOWS"; } -std::filesystem::path utils::get_module_path(HMODULE hModule) { - std::wstring buf(MAX_PATH, L'\0'); - while (true) { - if (const auto res = GetModuleFileNameW(hModule, &buf[0], static_cast(buf.size())); !res) - throw std::runtime_error(std::format("GetModuleFileName failure: 0x{:X}", GetLastError())); - else if (res < buf.size()) { - buf.resize(res); - return buf; - } else - buf.resize(buf.size() * 2); - } +std::wstring utils::get_string_resource(uint32_t resId) { + LPCWSTR pstr; + const auto len = LoadStringW(g_hModule, resId, reinterpret_cast(&pstr), 0); + return std::wstring(pstr, len); } HWND utils::try_find_game_window() { @@ -677,3 +699,22 @@ std::wstring utils::format_win32_error(DWORD err) { return std::format(L"Win32 error ({}=0x{:X})", err, err); } + +utils::scoped_dpi_awareness_context::scoped_dpi_awareness_context() + : scoped_dpi_awareness_context(DPI_AWARENESS_CONTEXT_PER_MONITOR_AWARE_V2) { +} + +utils::scoped_dpi_awareness_context::scoped_dpi_awareness_context(DPI_AWARENESS_CONTEXT context) { + const auto user32 = GetModuleHandleW(L"user32.dll"); + m_setThreadDpiAwarenessContext = + user32 + ? reinterpret_cast( + GetProcAddress(user32, "SetThreadDpiAwarenessContext")) + : nullptr; + m_old = m_setThreadDpiAwarenessContext ? m_setThreadDpiAwarenessContext(context) : DPI_AWARENESS_CONTEXT_UNAWARE; +} + +utils::scoped_dpi_awareness_context::~scoped_dpi_awareness_context() { + if (m_setThreadDpiAwarenessContext) + m_setThreadDpiAwarenessContext(m_old); +} diff --git a/Dalamud.Boot/utils.h b/Dalamud.Boot/utils.h index cbbbccee8..c5833722b 100644 --- a/Dalamud.Boot/utils.h +++ b/Dalamud.Boot/utils.h @@ -1,5 +1,6 @@ #pragma once +#include #include #include #include @@ -7,6 +8,7 @@ #include #include +#include "error_info.h" #include "unicode.h" namespace utils { @@ -18,7 +20,7 @@ namespace utils { loaded_module(void* hModule) : m_hModule(reinterpret_cast(hModule)) {} loaded_module(size_t hModule) : m_hModule(reinterpret_cast(hModule)) {} - std::filesystem::path path() const; + DalamudExpected path() const; bool is_current_process() const { return m_hModule == GetModuleHandleW(nullptr); } bool owns_address(const void* pAddress) const; @@ -57,9 +59,9 @@ namespace utils { void* get_imported_function_pointer(const char* pcszDllName, const char* pcszFunctionName, uint32_t hintOrOrdinal) const; template TFn** get_imported_function_pointer(const char* pcszDllName, const char* pcszFunctionName, uint32_t hintOrOrdinal) { return reinterpret_cast(get_imported_function_pointer(pcszDllName, pcszFunctionName, hintOrOrdinal)); } - [[nodiscard]] std::unique_ptr, decltype(&FreeResource)> get_resource(LPCWSTR lpName, LPCWSTR lpType) const; - [[nodiscard]] std::wstring get_description() const; - [[nodiscard]] const VS_FIXEDFILEINFO& get_file_version() const; + [[nodiscard]] DalamudExpected, decltype(&FreeResource)>> get_resource(LPCWSTR lpName, LPCWSTR lpType) const; + [[nodiscard]] DalamudExpected get_description() const; + [[nodiscard]] DalamudExpected get_file_version() const; static loaded_module current_process(); static std::vector all_modules(); @@ -268,7 +270,7 @@ namespace utils { bool is_running_on_wine(); - std::filesystem::path get_module_path(HMODULE hModule); + std::wstring get_string_resource(uint32_t resId); /// @brief Find the game main window. /// @return Handle to the game main window, or nullptr if it doesn't exist (yet). @@ -279,4 +281,18 @@ namespace utils { std::wstring escape_shell_arg(const std::wstring& arg); std::wstring format_win32_error(DWORD err); + + class scoped_dpi_awareness_context { + DPI_AWARENESS_CONTEXT m_old; + decltype(&SetThreadDpiAwarenessContext) m_setThreadDpiAwarenessContext; + + public: + scoped_dpi_awareness_context(); + scoped_dpi_awareness_context(DPI_AWARENESS_CONTEXT); + ~scoped_dpi_awareness_context(); + scoped_dpi_awareness_context(const scoped_dpi_awareness_context&) = delete; + scoped_dpi_awareness_context(scoped_dpi_awareness_context&&) = delete; + scoped_dpi_awareness_context& operator=(const scoped_dpi_awareness_context&) = delete; + scoped_dpi_awareness_context& operator=(scoped_dpi_awareness_context&&) = delete; + }; } diff --git a/Dalamud.Boot/veh.cpp b/Dalamud.Boot/veh.cpp index 85d58eb9d..b0ec1cefa 100644 --- a/Dalamud.Boot/veh.cpp +++ b/Dalamud.Boot/veh.cpp @@ -102,9 +102,13 @@ bool is_ffxiv_address(const wchar_t* module_name, const DWORD64 address) return false; } -static void append_injector_launch_args(std::vector& args) +static DalamudExpected append_injector_launch_args(std::vector& args) { - args.emplace_back(L"--game=\"" + utils::loaded_module::current_process().path().wstring() + L"\""); + if (auto path = utils::loaded_module::current_process().path()) + args.emplace_back(L"--game=\"" + path->wstring() + L"\""); + else + return DalamudUnexpected(std::in_place, std::move(path.error())); + switch (g_startInfo.DalamudLoadMethod) { case DalamudStartInfo::LoadMethod::Entrypoint: args.emplace_back(L"--mode=entrypoint"); @@ -155,6 +159,8 @@ static void append_injector_launch_args(std::vector& args) args.emplace_back(szArgList[i]); LocalFree(szArgList); } + + return {}; } LONG exception_handler(EXCEPTION_POINTERS* ex) @@ -358,11 +364,20 @@ bool veh::add_handler(bool doFullDump, const std::string& workingDirectory) args.emplace_back(std::format(L"--process-handle={}", reinterpret_cast(hInheritableCurrentProcess))); args.emplace_back(std::format(L"--exception-info-pipe-read-handle={}", reinterpret_cast(hReadPipeInheritable->get()))); args.emplace_back(std::format(L"--asset-directory={}", unicode::convert(g_startInfo.AssetDirectory))); - args.emplace_back(std::format(L"--log-directory={}", g_startInfo.BootLogPath.empty() - ? utils::loaded_module(g_hModule).path().parent_path().wstring() - : std::filesystem::path(unicode::convert(g_startInfo.BootLogPath)).parent_path().wstring())); + if (const auto path = utils::loaded_module(g_hModule).path()) { + args.emplace_back(std::format(L"--log-directory={}", g_startInfo.BootLogPath.empty() + ? path->parent_path().wstring() + : std::filesystem::path(unicode::convert(g_startInfo.BootLogPath)).parent_path().wstring())); + } else { + logging::W("Failed to read path of the Dalamud Boot module: {}", path.error().describe()); + return false; + } + args.emplace_back(L"--"); - append_injector_launch_args(args); + if (auto r = append_injector_launch_args(args); !r) { + logging::W("Failed to generate injector launch args: {}", r.error().describe()); + return false; + } for (const auto& arg : args) { diff --git a/Dalamud.Boot/xivfixes.cpp b/Dalamud.Boot/xivfixes.cpp index eb0f7df56..7f9e92225 100644 --- a/Dalamud.Boot/xivfixes.cpp +++ b/Dalamud.Boot/xivfixes.cpp @@ -8,12 +8,6 @@ #include "ntdll.h" #include "utils.h" -template -static std::span assume_nonempty_span(std::span t, const char* descr) { - if (t.empty()) - throw std::runtime_error(std::format("Unexpected empty span found: {}", descr)); - return t; -} void xivfixes::unhook_dll(bool bApply) { static const auto LogTag = "[xivfixes:unhook_dll]"; static const auto LogTagW = L"[xivfixes:unhook_dll]"; @@ -23,77 +17,90 @@ void xivfixes::unhook_dll(bool bApply) { const auto mods = utils::loaded_module::all_modules(); - const auto test_module = [&](size_t i, const utils::loaded_module & mod) { - std::filesystem::path path; - try { - path = mod.path(); - std::wstring version, description; - try { - version = utils::format_file_version(mod.get_file_version()); - } catch (...) { - version = L""; - } - - try { - description = mod.get_description(); - } catch (...) { - description = L""; - } - - logging::I(R"({} [{}/{}] Module 0x{:X} ~ 0x{:X} (0x{:X}): "{}" ("{}" ver {}))", LogTagW, i + 1, mods.size(), mod.address_int(), mod.address_int() + mod.image_size(), mod.image_size(), path.wstring(), description, version); - } catch (const std::exception& e) { - logging::W("{} [{}/{}] Module 0x{:X}: Failed to resolve path: {}", LogTag, i + 1, mods.size(), mod.address_int(), e.what()); + for (size_t i = 0; i < mods.size(); i++) { + const auto& mod = mods[i]; + const auto path = mod.path(); + if (!path) { + logging::W( + "{} [{}/{}] Module 0x{:X}: Failed to resolve path: {}", + LogTag, + i + 1, + mods.size(), + mod.address_int(), + path.error().describe()); return; } - const auto moduleName = unicode::convert(path.filename().wstring()); + const auto version = mod.get_file_version() + .transform([](const auto& v) { return utils::format_file_version(v.get()); }) + .value_or(L""); - std::vector buf; - std::string formatBuf; + const auto description = mod.get_description() + .value_or(L""); + + logging::I( + R"({} [{}/{}] Module 0x{:X} ~ 0x{:X} (0x{:X}): "{}" ("{}" ver {}))", + LogTagW, + i + 1, + mods.size(), + mod.address_int(), + mod.address_int() + mod.image_size(), + mod.image_size(), + path->wstring(), + description, + version); + + const auto moduleName = unicode::convert(path->filename().wstring()); + + const auto& sectionHeader = mod.section_header(".text"); + const auto section = mod.span_as(sectionHeader.VirtualAddress, sectionHeader.Misc.VirtualSize); + if (section.empty()) { + logging::W("{} Error: .text[VA:VA + VS] is empty", LogTag); + return; + } + + auto hFsDllRaw = CreateFileW(path->c_str(), GENERIC_READ, FILE_SHARE_READ, nullptr, OPEN_EXISTING, 0, nullptr); + if (hFsDllRaw == INVALID_HANDLE_VALUE) { + logging::W("{} Module loaded in current process but could not open file: Win32 error {}", LogTag, GetLastError()); + return; + } + + auto hFsDll = std::unique_ptr(hFsDllRaw, &CloseHandle); + std::vector buf(section.size()); + SetFilePointer(hFsDll.get(), sectionHeader.PointerToRawData, nullptr, FILE_CURRENT); + if (DWORD read{}; ReadFile(hFsDll.get(), &buf[0], static_cast(buf.size()), &read, nullptr)) { + if (read < section.size_bytes()) { + logging::W("{} ReadFile: read {} bytes < requested {} bytes", LogTagW, read, section.size_bytes()); + return; + } + } else { + logging::I("{} ReadFile: Win32 error {}", LogTagW, GetLastError()); + return; + } + + const auto doRestore = g_startInfo.BootUnhookDlls.contains(unicode::convert(path->filename().u8string())); try { - const auto& sectionHeader = mod.section_header(".text"); - const auto section = assume_nonempty_span(mod.span_as(sectionHeader.VirtualAddress, sectionHeader.Misc.VirtualSize), ".text[VA:VA+VS]"); - auto hFsDllRaw = CreateFileW(path.c_str(), GENERIC_READ, FILE_SHARE_READ, nullptr, OPEN_EXISTING, 0, nullptr); - if (hFsDllRaw == INVALID_HANDLE_VALUE) { - logging::W("{} Module loaded in current process but could not open file: Win32 error {}", LogTag, GetLastError()); - return; - } - auto hFsDll = std::unique_ptr(hFsDllRaw, &CloseHandle); - - buf.resize(section.size()); - SetFilePointer(hFsDll.get(), sectionHeader.PointerToRawData, nullptr, FILE_CURRENT); - if (DWORD read{}; ReadFile(hFsDll.get(), &buf[0], static_cast(buf.size()), &read, nullptr)) { - if (read < section.size_bytes()) { - logging::W("{} ReadFile: read {} bytes < requested {} bytes", LogTagW, read, section.size_bytes()); - return; - } - } else { - logging::I("{} ReadFile: Win32 error {}", LogTagW, GetLastError()); - return; - } - - const auto doRestore = g_startInfo.BootUnhookDlls.contains(unicode::convert(path.filename().u8string())); - std::optional tenderizer; - for (size_t i = 0, instructionLength = 1, printed = 0; i < buf.size(); i += instructionLength) { - if (section[i] == buf[i]) { + std::string formatBuf; + for (size_t inst = 0, instructionLength = 1, printed = 0; inst < buf.size(); inst += instructionLength) { + if (section[inst] == buf[inst]) { instructionLength = 1; continue; } - const auto rva = sectionHeader.VirtualAddress + i; + const auto rva = sectionHeader.VirtualAddress + inst; nmd_x86_instruction instruction{}; - if (!nmd_x86_decode(§ion[i], section.size() - i, &instruction, NMD_X86_MODE_64, NMD_X86_DECODER_FLAGS_ALL)) { + if (!nmd_x86_decode(§ion[inst], section.size() - inst, &instruction, NMD_X86_MODE_64, NMD_X86_DECODER_FLAGS_ALL)) { instructionLength = 1; if (printed < 64) { - logging::W("{} {}+0x{:0X}: dd {:02X}", LogTag, moduleName, rva, static_cast(section[i])); + logging::W("{} {}+0x{:0X}: dd {:02X}", LogTag, moduleName, rva, static_cast(section[inst])); printed++; } } else { instructionLength = instruction.length; if (printed < 64) { formatBuf.resize(128); - nmd_x86_format(&instruction, &formatBuf[0], reinterpret_cast(§ion[i]), NMD_X86_FORMAT_FLAGS_DEFAULT | NMD_X86_FORMAT_FLAGS_BYTES); + nmd_x86_format(&instruction, &formatBuf[0], reinterpret_cast(§ion[inst]), NMD_X86_FORMAT_FLAGS_DEFAULT | NMD_X86_FORMAT_FLAGS_BYTES); formatBuf.resize(strnlen(&formatBuf[0], formatBuf.size())); const auto& directory = mod.data_directory(IMAGE_DIRECTORY_ENTRY_EXPORT); @@ -103,25 +110,25 @@ void xivfixes::unhook_dll(bool bApply) { const auto functions = mod.span_as(exportDirectory.AddressOfFunctions, exportDirectory.NumberOfFunctions); std::string resolvedExportName; - for (size_t j = 0; j < names.size(); ++j) { + for (size_t nameIndex = 0; nameIndex < names.size(); ++nameIndex) { std::string_view name; - if (const char* pcszName = mod.address_as(names[j]); pcszName < mod.address() || pcszName >= mod.address() + mod.image_size()) { + if (const char* pcszName = mod.address_as(names[nameIndex]); pcszName < mod.address() || pcszName >= mod.address() + mod.image_size()) { if (IsBadReadPtr(pcszName, 256)) { - logging::W("{} Name #{} points to an invalid address outside the executable. Skipping.", LogTag, j); + logging::W("{} Name #{} points to an invalid address outside the executable. Skipping.", LogTag, nameIndex); continue; } name = std::string_view(pcszName, strnlen(pcszName, 256)); - logging::W("{} Name #{} points to a seemingly valid address outside the executable: {}", LogTag, j, name); + logging::W("{} Name #{} points to a seemingly valid address outside the executable: {}", LogTag, nameIndex, name); } - if (ordinals[j] >= functions.size()) { - logging::W("{} Ordinal #{} points to function index #{} >= #{}. Skipping.", LogTag, j, ordinals[j], functions.size()); + if (ordinals[nameIndex] >= functions.size()) { + logging::W("{} Ordinal #{} points to function index #{} >= #{}. Skipping.", LogTag, nameIndex, ordinals[nameIndex], functions.size()); continue; } - const auto rva = functions[ordinals[j]]; - if (rva == §ion[i] - mod.address()) { + const auto rva = functions[ordinals[nameIndex]]; + if (rva == §ion[inst] - mod.address()) { resolvedExportName = std::format("[export:{}]", name); break; } @@ -135,7 +142,7 @@ void xivfixes::unhook_dll(bool bApply) { if (doRestore) { if (!tenderizer) tenderizer.emplace(section, PAGE_EXECUTE_READWRITE); - memcpy(§ion[i], &buf[i], instructionLength); + memcpy(§ion[inst], &buf[inst], instructionLength); } } @@ -147,21 +154,7 @@ void xivfixes::unhook_dll(bool bApply) { } catch (const std::exception& e) { logging::W("{} Error: {}", LogTag, e.what()); } - }; - - // This is needed since try and __try cannot be used in the same function. Lambdas circumvent the limitation. - const auto windows_exception_handler = [&]() { - for (size_t i = 0; i < mods.size(); i++) { - const auto& mod = mods[i]; - __try { - test_module(i, mod); - } __except (EXCEPTION_EXECUTE_HANDLER) { - logging::W("{} Error: Access Violation", LogTag); - } - } - }; - - windows_exception_handler(); + } } using TFnGetInputDeviceManager = void* (); @@ -294,13 +287,11 @@ static bool is_xivalex(const std::filesystem::path& dllPath) { static bool is_openprocess_already_dealt_with() { static const auto s_value = [] { for (const auto& mod : utils::loaded_module::all_modules()) { - try { - if (is_xivalex(mod.path())) - return true; - - } catch (...) { - // pass - } + const auto path = mod.path().value_or({}); + if (path.empty()) + continue; + if (is_xivalex(path)) + return true; } return false; }(); @@ -650,43 +641,22 @@ void xivfixes::symbol_load_patches(bool bApply) { void xivfixes::disable_game_debugging_protection(bool bApply) { static const char* LogTag = "[xivfixes:disable_game_debugging_protection]"; - static const std::vector patchBytes = { - 0x31, 0xC0, // XOR EAX, EAX - 0x90, // NOP - 0x90, // NOP - 0x90, // NOP - 0x90 // NOP - }; + static std::optional> s_hookIsDebuggerPresent; - if (!bApply) - return; + if (bApply) { + if (!g_startInfo.BootEnabledGameFixes.contains("disable_game_debugging_protection")) { + logging::I("{} Turned off via environment variable.", LogTag); + return; + } - if (!g_startInfo.BootEnabledGameFixes.contains("disable_game_debugging_protection")) { - logging::I("{} Turned off via environment variable.", LogTag); - return; - } - - // Find IsDebuggerPresent in Framework.Tick() - const char* matchPtr = utils::signature_finder() - .look_in(utils::loaded_module(g_hGameInstance), ".text") - .look_for_hex("FF 15 ?? ?? ?? ?? 85 C0 74 13 41") - .find_one() - .Match.data(); - - if (!matchPtr) { - logging::E("{} Failed to find signature.", LogTag); - return; - } - - void* address = const_cast(static_cast(matchPtr)); - - DWORD oldProtect; - if (VirtualProtect(address, patchBytes.size(), PAGE_EXECUTE_READWRITE, &oldProtect)) { - memcpy(address, patchBytes.data(), patchBytes.size()); - VirtualProtect(address, patchBytes.size(), oldProtect, &oldProtect); - logging::I("{} Patch applied at address 0x{:X}.", LogTag, reinterpret_cast(address)); + s_hookIsDebuggerPresent.emplace("kernel32.dll!IsDebuggerPresent", "kernel32.dll", "IsDebuggerPresent", 0); + s_hookIsDebuggerPresent->set_detour([]() { return false; }); + logging::I("{} Enable", LogTag); } else { - logging::E("{} Failed to change memory protection.", LogTag); + if (s_hookIsDebuggerPresent) { + logging::I("{} Disable", LogTag); + s_hookIsDebuggerPresent.reset(); + } } } diff --git a/Dalamud.Injector.Boot/Dalamud.Injector.Boot.vcxproj b/Dalamud.Injector.Boot/Dalamud.Injector.Boot.vcxproj index 1c89f4ff7..7f8de3843 100644 --- a/Dalamud.Injector.Boot/Dalamud.Injector.Boot.vcxproj +++ b/Dalamud.Injector.Boot/Dalamud.Injector.Boot.vcxproj @@ -38,7 +38,7 @@ Level3 true true - stdcpplatest + stdcpp23 pch.h ProgramDatabase CPPDLLTEMPLATE_EXPORTS;_WINDOWS;_USRDLL;%(PreprocessorDefinitions) @@ -55,7 +55,7 @@ true false - MultiThreadedDebugDLL + MultiThreadedDebugDLL _DEBUG;%(PreprocessorDefinitions) @@ -67,7 +67,7 @@ true true - MultiThreadedDLL + MultiThreadedDLL NDEBUG;%(PreprocessorDefinitions) @@ -108,4 +108,4 @@ - + \ No newline at end of file diff --git a/Dalamud/Interface/Internal/InterfaceManager.cs b/Dalamud/Interface/Internal/InterfaceManager.cs index e94501d92..d68bc8bef 100644 --- a/Dalamud/Interface/Internal/InterfaceManager.cs +++ b/Dalamud/Interface/Internal/InterfaceManager.cs @@ -635,29 +635,6 @@ internal partial class InterfaceManager : IInternalDisposableService Service.ProvideException(ex); Log.Error(ex, "Could not load ImGui dependencies."); - fixed (void* lpText = - "Dalamud plugins require the Microsoft Visual C++ Redistributable to be installed.\nPlease install the runtime from the official Microsoft website or disable Dalamud.\n\nDo you want to download the redistributable now?") - { - fixed (void* lpCaption = "Dalamud Error") - { - var res = MessageBoxW( - default, - (ushort*)lpText, - (ushort*)lpCaption, - MB.MB_YESNO | MB.MB_TOPMOST | MB.MB_ICONERROR); - - if (res == IDYES) - { - var psi = new ProcessStartInfo - { - FileName = "https://aka.ms/vs/16/release/vc_redist.x64.exe", - UseShellExecute = true, - }; - Process.Start(psi); - } - } - } - Environment.Exit(-1); // Doesn't reach here, but to make the compiler not complain