From 751c86943502008cb1e24d7249096d9bcef0c918 Mon Sep 17 00:00:00 2001 From: Nick Bolton Date: Tue, 11 Mar 2025 11:33:41 +0000 Subject: [PATCH] feat: Use `EnumProcessModules` to search loaded modules) --- src/lib/arch/win32/ArchMiscWindows.cpp | 43 +++++++++++++++++++++----- src/lib/arch/win32/ArchMiscWindows.h | 6 ++++ 2 files changed, 42 insertions(+), 7 deletions(-) diff --git a/src/lib/arch/win32/ArchMiscWindows.cpp b/src/lib/arch/win32/ArchMiscWindows.cpp index 81f9f06b5..e34cc9b62 100644 --- a/src/lib/arch/win32/ArchMiscWindows.cpp +++ b/src/lib/arch/win32/ArchMiscWindows.cpp @@ -12,10 +12,9 @@ #include "base/Log.h" #include "base/String.h" -#include -#pragma warning(disable : 4099) +#include #include -#pragma warning(default : 4099) +#include #include @@ -505,12 +504,43 @@ std::string ArchMiscWindows::getActiveDesktopName() return name; } +HMODULE ArchMiscWindows::findLoadedModule(std::array moduleNames) +{ + std::array hModules; + DWORD cbNeeded; + + HANDLE hProcess = GetCurrentProcess(); + if (!EnumProcessModules(hProcess, hModules.data(), sizeof(hModules), &cbNeeded)) { + errorMessageBox("Failed to enumerate process modules."); + abort(); + } + + std::string loadedModuleName; + for (size_t i = 0; i < (cbNeeded / sizeof(HMODULE)); ++i) { + if (!GetModuleBaseNameA(hProcess, hModules[i], loadedModuleName.data(), sizeof(loadedModuleName))) { + LOG_WARN("could not get base name of loaded module %d", i); + continue; + } + + for (const auto &moduleName : moduleNames) { + if (_stricmp(loadedModuleName.data(), moduleName) == 0) { + return hModules[i]; + } + } + } + + return nullptr; +} + // Enforcing minimum MSVC runtime version is quite strict, but we have a good reason. // // Microsoft lets you run a program with an older version of the runtime DLL than the one it was // compiled with. This is because the runtime DLLs are supposedly ABI-compatible when the major // version is the same, so hypothetically MSVC runtime 14.0 is compatible with 14.42. -// However, we have found that subtle edge cases (such as mutex lock causes access violation). +// However, we have found subtle edge cases such as mutex lock causes access violation. +// +// Example of how Microsoft breaks ABI compatibility between minor runtime versions: +// https://stackoverflow.com/questions/69990339/why-is-stdmutex-so-much-worse-than-stdshared-mutex-in-visual-c // // Our CI is set up to build with the latest compiler, so in this case we should insist on at least // the runtime DLL that was released at the same time as the compiler. @@ -519,10 +549,9 @@ std::string ArchMiscWindows::getActiveDesktopName() // requirements for the current Qt version we support. void ArchMiscWindows::guardRuntimeVersion() // NOSONAR - `noreturn` is not available { - const auto kRuntimeDll = "vcruntime140.dll"; + auto hModule = findLoadedModule({"vcruntime140.dll", "vcruntime140d.dll"}); - HMODULE hModule = nullptr; - if (!GetModuleHandleEx(0, kRuntimeDll, &hModule)) { + if (!hModule) { errorMessageBox("Could not find Microsoft Visual C++ Runtime."); abort(); } diff --git a/src/lib/arch/win32/ArchMiscWindows.h b/src/lib/arch/win32/ArchMiscWindows.h index f272eef98..ed9bf3602 100644 --- a/src/lib/arch/win32/ArchMiscWindows.h +++ b/src/lib/arch/win32/ArchMiscWindows.h @@ -196,6 +196,12 @@ private: //! Calls `getProcessEntry` with the parent process ID static BOOL WINAPI getParentProcessEntry(PROCESSENTRY32 &entry); + //! Searches the loaded modules and returns the matching module handle + /** + * @param moduleNames Provide two module names to search for both release and debug versions. + */ + static HMODULE findLoadedModule(std::array moduleNames); + private: using Dialogs = std::set; typedef DWORD(WINAPI *STES_t)(DWORD);