Check if CRT version is at least 14.40.33816.0 (#2361)

* Check if CRT version is at least 14.40.33816.0

* Fix ULD path

* Remove debugging code
This commit is contained in:
srkizer 2025-08-10 23:56:22 +09:00 committed by GitHub
parent 4413634508
commit 8fcf633f02
No known key found for this signature in database
GPG key ID: B5690EEEBB952194
5 changed files with 91 additions and 11 deletions

View file

@ -13,9 +13,86 @@
HMODULE g_hModule; HMODULE g_hModule;
HINSTANCE g_hGameInstance = GetModuleHandleW(nullptr); HINSTANCE g_hGameInstance = GetModuleHandleW(nullptr);
void CheckMsvcrtVersion() {
constexpr WORD RequiredMsvcrtVersionComponents[] = {14, 40, 33816, 0};
constexpr auto RequiredMsvcrtVersion = 0ULL
| (static_cast<uint64_t>(RequiredMsvcrtVersionComponents[0]) << 48)
| (static_cast<uint64_t>(RequiredMsvcrtVersionComponents[1]) << 32)
| (static_cast<uint64_t>(RequiredMsvcrtVersionComponents[2]) << 16)
| (static_cast<uint64_t>(RequiredMsvcrtVersionComponents[3]) << 0);
#ifdef _DEBUG
constexpr const wchar_t* RuntimeDllNames[] = {
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);
continue;
}
const auto& versionFull = mod.get_file_version();
logging::I("Runtime DLL {} has version {}.", runtimeDllName, utils::format_file_version(versionFull));
const auto version = (static_cast<uint64_t>(versionFull.dwFileVersionMS) << 32) |
static_cast<uint64_t>(versionFull.dwFileVersionLS);
// Commit introducing inline mutex ctor: tagged vs-2022-17.14 (2024-06-18)
// - https://github.com/microsoft/STL/commit/22a88260db4d754bbc067e2002430144d6ec5391
// MSVC Redist versions:
// - https://github.com/abbodi1406/vcredist/blob/master/source_links/README.md
// - 14.40.33810.0 dsig 2024-04-28
// - 14.40.33816.0 dsig 2024-09-11
if (version < RequiredMsvcrtVersion && (lowestVersion == 0 || lowestVersion > version))
lowestVersion = version;
}
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;
}
}
}
HRESULT WINAPI InitializeImpl(LPVOID lpParam, HANDLE hMainThreadContinue) { HRESULT WINAPI InitializeImpl(LPVOID lpParam, HANDLE hMainThreadContinue) {
g_startInfo.from_envvars(); g_startInfo.from_envvars();
CheckMsvcrtVersion();
std::string jsonParseError; std::string jsonParseError;
try { try {
from_json(nlohmann::json::parse(std::string_view(static_cast<char*>(lpParam))), g_startInfo); from_json(nlohmann::json::parse(std::string_view(static_cast<char*>(lpParam))), g_startInfo);

View file

@ -11,6 +11,9 @@
#define WIN32_LEAN_AND_MEAN #define WIN32_LEAN_AND_MEAN
#define NOMINMAX #define NOMINMAX
// https://developercommunity.visualstudio.com/t/Access-violation-with-std::mutex::lock-a/10664660
#define _DISABLE_CONSTEXPR_MUTEX_CONSTRUCTOR
// Windows Header Files (1) // Windows Header Files (1)
#include <Windows.h> #include <Windows.h>
@ -21,6 +24,7 @@
#include <iphlpapi.h> #include <iphlpapi.h>
#include <PathCch.h> #include <PathCch.h>
#include <Psapi.h> #include <Psapi.h>
#include <shellapi.h>
#include <ShlObj.h> #include <ShlObj.h>
#include <Shlwapi.h> #include <Shlwapi.h>
#include <SubAuth.h> #include <SubAuth.h>

View file

@ -148,7 +148,7 @@ std::unique_ptr<std::remove_pointer_t<HGLOBAL>, decltype(&FreeResource)> utils::
const auto hres = FindResourceW(m_hModule, lpName, lpType); const auto hres = FindResourceW(m_hModule, lpName, lpType);
if (!hres) if (!hres)
throw std::runtime_error("No such resource"); throw std::runtime_error("No such resource");
const auto hRes = LoadResource(m_hModule, hres); const auto hRes = LoadResource(m_hModule, hres);
if (!hRes) if (!hRes)
throw std::runtime_error("LoadResource failure"); throw std::runtime_error("LoadResource failure");
@ -159,7 +159,7 @@ std::unique_ptr<std::remove_pointer_t<HGLOBAL>, decltype(&FreeResource)> utils::
std::wstring utils::loaded_module::get_description() const { std::wstring utils::loaded_module::get_description() const {
const auto rsrc = get_resource(MAKEINTRESOURCE(VS_VERSION_INFO), RT_VERSION); const auto rsrc = get_resource(MAKEINTRESOURCE(VS_VERSION_INFO), RT_VERSION);
const auto pBlock = LockResource(rsrc.get()); const auto pBlock = LockResource(rsrc.get());
struct LANGANDCODEPAGE { struct LANGANDCODEPAGE {
WORD wLanguage; WORD wLanguage;
WORD wCodePage; WORD wCodePage;
@ -190,11 +190,11 @@ std::wstring utils::loaded_module::get_description() const {
continue; continue;
return std::wstring(currName); return std::wstring(currName);
} }
throw std::runtime_error("Invalid version information (2)"); throw std::runtime_error("Invalid version information (2)");
} }
VS_FIXEDFILEINFO utils::loaded_module::get_file_version() const { const VS_FIXEDFILEINFO& utils::loaded_module::get_file_version() const {
const auto rsrc = get_resource(MAKEINTRESOURCE(VS_VERSION_INFO), RT_VERSION); const auto rsrc = get_resource(MAKEINTRESOURCE(VS_VERSION_INFO), RT_VERSION);
const auto pBlock = LockResource(rsrc.get()); const auto pBlock = LockResource(rsrc.get());
UINT size = 0; UINT size = 0;
@ -353,7 +353,7 @@ const char* utils::signature_finder::result::resolve_jump_target(size_t instruct
nmd_x86_instruction instruction{}; nmd_x86_instruction instruction{};
if (!nmd_x86_decode(&Match[instructionOffset], NMD_X86_MAXIMUM_INSTRUCTION_LENGTH, &instruction, NMD_X86_MODE_64, NMD_X86_DECODER_FLAGS_ALL)) if (!nmd_x86_decode(&Match[instructionOffset], NMD_X86_MAXIMUM_INSTRUCTION_LENGTH, &instruction, NMD_X86_MODE_64, NMD_X86_DECODER_FLAGS_ALL))
throw std::runtime_error("Matched address does not have a valid assembly instruction"); throw std::runtime_error("Matched address does not have a valid assembly instruction");
size_t numExplicitOperands = 0; size_t numExplicitOperands = 0;
for (size_t i = 0; i < instruction.num_operands; i++) for (size_t i = 0; i < instruction.num_operands; i++)
numExplicitOperands += instruction.operands[i].is_implicit ? 0 : 1; numExplicitOperands += instruction.operands[i].is_implicit ? 0 : 1;
@ -625,7 +625,7 @@ void utils::wait_for_game_window() {
std::wstring utils::escape_shell_arg(const std::wstring& arg) { std::wstring utils::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);

View file

@ -23,9 +23,8 @@ namespace utils {
bool is_current_process() const { return m_hModule == GetModuleHandleW(nullptr); } bool is_current_process() const { return m_hModule == GetModuleHandleW(nullptr); }
bool owns_address(const void* pAddress) const; bool owns_address(const void* pAddress) const;
operator HMODULE() const { operator HMODULE() const { return m_hModule; }
return m_hModule; operator bool() const { return m_hModule; }
}
size_t address_int() const { return reinterpret_cast<size_t>(m_hModule); } size_t address_int() const { return reinterpret_cast<size_t>(m_hModule); }
size_t image_size() const { return is_pe64() ? nt_header64().OptionalHeader.SizeOfImage : nt_header32().OptionalHeader.SizeOfImage; } size_t image_size() const { return is_pe64() ? nt_header64().OptionalHeader.SizeOfImage : nt_header32().OptionalHeader.SizeOfImage; }
@ -60,7 +59,7 @@ namespace utils {
[[nodiscard]] std::unique_ptr<std::remove_pointer_t<HGLOBAL>, decltype(&FreeResource)> get_resource(LPCWSTR lpName, LPCWSTR lpType) const; [[nodiscard]] std::unique_ptr<std::remove_pointer_t<HGLOBAL>, decltype(&FreeResource)> get_resource(LPCWSTR lpName, LPCWSTR lpType) const;
[[nodiscard]] std::wstring get_description() const; [[nodiscard]] std::wstring get_description() const;
[[nodiscard]] VS_FIXEDFILEINFO get_file_version() const; [[nodiscard]] const VS_FIXEDFILEINFO& get_file_version() const;
static loaded_module current_process(); static loaded_module current_process();
static std::vector<loaded_module> all_modules(); static std::vector<loaded_module> all_modules();

View file

@ -27,7 +27,7 @@ internal class UldWidget : IDataWindowWidget
{ {
// ULD styles can be hardcoded for now as they don't add new ones regularly. Can later try and find where to load these from in the game EXE. // ULD styles can be hardcoded for now as they don't add new ones regularly. Can later try and find where to load these from in the game EXE.
private static readonly string[] ThemeDisplayNames = ["Dark", "Light", "Classic FF", "Clear Blue"]; private static readonly string[] ThemeDisplayNames = ["Dark", "Light", "Classic FF", "Clear Blue"];
private static readonly string[] ThemeBasePaths = ["ui/uld/", "ui/uld/light/", "ui/uld/third/", "ui/uld/fourth/"]; private static readonly string[] ThemeBasePaths = ["ui/uld/", "ui/uld/img01/", "ui/uld/img02/", "ui/uld/img03/"];
// 48 8D 15 ?? ?? ?? ?? is the part of the signatures that contain the string location offset // 48 8D 15 ?? ?? ?? ?? is the part of the signatures that contain the string location offset
// 48 = 64 bit register prefix // 48 = 64 bit register prefix