From 495331108b4ca25de06a2aec7b03a8b6f3e5f88b Mon Sep 17 00:00:00 2001 From: Nick Bolton Date: Thu, 6 Mar 2025 12:36:14 +0000 Subject: [PATCH] fix: Guard MSVC runtime version depending on compiler version --- cmake/Libraries.cmake | 2 +- src/apps/deskflow-client/deskflow-client.cpp | 2 + src/apps/deskflow-daemon/deskflow-daemon.cpp | 2 + src/apps/deskflow-server/deskflow-server.cpp | 2 + src/lib/arch/win32/ArchMiscWindows.cpp | 70 +++++++++++++++++++- src/lib/arch/win32/ArchMiscWindows.h | 3 + 6 files changed, 79 insertions(+), 2 deletions(-) diff --git a/cmake/Libraries.cmake b/cmake/Libraries.cmake index 5385ec01a..d7b3e8843 100644 --- a/cmake/Libraries.cmake +++ b/cmake/Libraries.cmake @@ -11,7 +11,7 @@ macro(configure_libs) find_package(Python REQUIRED QUIET) set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} /MP /D _BIND_TO_CURRENT_VCLIBS_VERSION=1") set(CMAKE_CXX_FLAGS_RELEASE "${CMAKE_CXX_FLAGS_RELEASE} /MD /O2 /Ob2") - list(APPEND libs Wtsapi32 Userenv Wininet comsuppw Shlwapi) + list(APPEND libs Wtsapi32 Userenv Wininet comsuppw Shlwapi version) add_definitions( /DWIN32 /D_WINDOWS diff --git a/src/apps/deskflow-client/deskflow-client.cpp b/src/apps/deskflow-client/deskflow-client.cpp index 284f02d86..f35e411b5 100644 --- a/src/apps/deskflow-client/deskflow-client.cpp +++ b/src/apps/deskflow-client/deskflow-client.cpp @@ -18,6 +18,8 @@ int main(int argc, char **argv) { #if SYSAPI_WIN32 + ArchMiscWindows::guardRuntimeVersion(); + // record window instance for tray icon, etc ArchMiscWindows::setInstanceWin32(GetModuleHandle(NULL)); #endif diff --git a/src/apps/deskflow-daemon/deskflow-daemon.cpp b/src/apps/deskflow-daemon/deskflow-daemon.cpp index 1650b99bb..b44a76da9 100644 --- a/src/apps/deskflow-daemon/deskflow-daemon.cpp +++ b/src/apps/deskflow-daemon/deskflow-daemon.cpp @@ -32,6 +32,8 @@ void handleError(const char *message); int main(int argc, char **argv) { #if SYSAPI_WIN32 + ArchMiscWindows::guardRuntimeVersion(); + // Save window instance for later use, e.g. `GetModuleFileName` which is used when installing the daemon. ArchMiscWindows::setInstanceWin32(GetModuleHandle(nullptr)); #endif diff --git a/src/apps/deskflow-server/deskflow-server.cpp b/src/apps/deskflow-server/deskflow-server.cpp index 5079f7199..83be6471d 100644 --- a/src/apps/deskflow-server/deskflow-server.cpp +++ b/src/apps/deskflow-server/deskflow-server.cpp @@ -18,6 +18,8 @@ int main(int argc, char **argv) { #if SYSAPI_WIN32 + ArchMiscWindows::guardRuntimeVersion(); + // record window instance for tray icon, etc ArchMiscWindows::setInstanceWin32(GetModuleHandle(NULL)); #endif diff --git a/src/lib/arch/win32/ArchMiscWindows.cpp b/src/lib/arch/win32/ArchMiscWindows.cpp index 6d79ea57e..b3c76aa54 100644 --- a/src/lib/arch/win32/ArchMiscWindows.cpp +++ b/src/lib/arch/win32/ArchMiscWindows.cpp @@ -6,16 +6,28 @@ */ #include "arch/win32/ArchMiscWindows.h" + #include "arch/win32/ArchDaemonWindows.h" #include "arch/win32/XArchWindows.h" #include "base/Log.h" -#include "common/constants.h" +#include "base/String.h" #include #pragma warning(disable : 4099) #include #pragma warning(default : 4099) +#include + +#if _MSC_VER >= 1942 // Visual Studio 2022 Update 12 +const auto kRequiredMajor = 14; +const auto kRequiredMinor = 42; +const auto kRuntimeDll = "vcruntime140.dll"; +#else +#pragma message("MSC version: " STRINGIFY(_MSC_VER)) +#error "Unsupported MSC version" +#endif + // parent process name for services in Vista #define SERVICE_LAUNCHER "services.exe" @@ -30,6 +42,11 @@ #endif using EXECUTION_STATE = DWORD; +void errorMessageBox(const char *message, const char *title = "Fatal Error") +{ + MessageBoxA(nullptr, message, title, MB_ICONERROR | MB_OK); +} + // // ArchMiscWindows // @@ -483,3 +500,54 @@ std::string ArchMiscWindows::getActiveDesktopName() CloseDesktop(desk); return name; } + +void ArchMiscWindows::guardRuntimeVersion() // NOSONAR - `noreturn` is not available +{ + HMODULE hModule = nullptr; + if (!GetModuleHandleEx(0, kRuntimeDll, &hModule) && hModule) { + errorMessageBox("Microsoft Visual C++ Runtime is not installed."); + abort(); + } + + std::array pathBuffer; + const auto path = pathBuffer.data(); + if (!GetModuleFileNameA(hModule, path, MAX_PATH)) { + errorMessageBox("Failed to get the path of Microsoft Visual C++ Runtime."); + abort(); + } + + DWORD handle; + DWORD size = GetFileVersionInfoSizeA(path, &handle); + if (size <= 0) { + errorMessageBox("Failed to get the version info size for Microsoft Visual C++ Runtime."); + abort(); + } + + std::vector versionInfo(size); + if (!GetFileVersionInfoA(path, handle, size, versionInfo.data())) { + errorMessageBox("Failed to get the file version info for Microsoft Visual C++ Runtime."); + abort(); + } + + VS_FIXEDFILEINFO *fileInfo = nullptr; + const auto lplpFileInfo = reinterpret_cast(&fileInfo); // NOSONAR - Idiomatic Win32 + if (UINT len = 0; !VerQueryValueA(versionInfo.data(), "\\", lplpFileInfo, &len)) { + errorMessageBox("Failed to get the version information for Microsoft Visual C++ Runtime."); + abort(); + } + + const auto currentMajor = HIWORD(fileInfo->dwFileVersionMS); + const auto currentMinor = LOWORD(fileInfo->dwFileVersionMS); + const auto currentBuild = HIWORD(fileInfo->dwFileVersionLS); + + if (currentMajor < kRequiredMajor || currentMinor < kRequiredMinor) { + const auto message = deskflow::string::sprintf( + "Installed Microsoft Visual C++ Runtime v%d.%d.%d is outdated.\n\n" + "Minimum required version: v%d.%d\n\n" + "Please update to the latest Microsoft Visual C++ Redistributable.", + currentMajor, currentMinor, currentBuild, kRequiredMajor, kRequiredMinor + ); + MessageBoxA(nullptr, message.c_str(), "Dependency Error", MB_ICONERROR | MB_OK); + exit(1); + } +} diff --git a/src/lib/arch/win32/ArchMiscWindows.h b/src/lib/arch/win32/ArchMiscWindows.h index 5655b5f2e..4f6a9f661 100644 --- a/src/lib/arch/win32/ArchMiscWindows.h +++ b/src/lib/arch/win32/ArchMiscWindows.h @@ -157,6 +157,9 @@ public: //! Returns true if we got the parent process name. static bool getParentProcessName(std::string &name); + //! Prevent hard to troubleshoot errors, e.g. access violations. + static void guardRuntimeVersion(); + static HINSTANCE instanceWin32(); static void setInstanceWin32(HINSTANCE instance); static BOOL WINAPI getProcessEntry(PROCESSENTRY32 &entry, DWORD processID);