duckstation

duckstation, but archived from the revision just before upstream changed it to a proprietary software project, this version is the libre one
git clone https://git.neptards.moe/u3shit/duckstation.git
Log | Files | Refs | README | LICENSE

vcruntimecheck.cpp (4396B)


      1 // SPDX-FileCopyrightText: 2019-2024 Connor McLaughlin <stenzek@gmail.com>
      2 // SPDX-License-Identifier: (GPL-3.0 OR CC-BY-NC-ND-4.0)
      3 
      4 #include "common/windows_headers.h"
      5 #include <shellapi.h>
      6 
      7 #include "fmt/format.h"
      8 
      9 #define MAKE_VERSION64(v0, v1, v2, v3)                                                                                 \
     10   (static_cast<DWORD64>(v3) | (static_cast<DWORD64>(v2) << 16) | (static_cast<DWORD64>(v1) << 32) |                    \
     11    (static_cast<DWORD64>(v0) << 48))
     12 #define VERSION64_PART(v, p) (static_cast<WORD>(((v) >> (48 - ((p) * 16))) & 0xFFFFu))
     13 
     14 // Minimum version is 14.38.33135.0.
     15 static constexpr DWORD64 MIN_VERSION = MAKE_VERSION64(14, 38, 33135, 0);
     16 static constexpr const char* DOWNLOAD_URL = "https://aka.ms/vs/17/release/vc_redist.x64.exe";
     17 
     18 struct VCRuntimeCheckObject
     19 {
     20   VCRuntimeCheckObject()
     21   {
     22     const HMODULE crt_handle = GetModuleHandleW(L"msvcp140.dll");
     23     if (!crt_handle)
     24       return;
     25 
     26     const HANDLE heap = GetProcessHeap();
     27     DWORD filename_length = MAX_PATH;
     28     LPWSTR filename = static_cast<LPWSTR>(HeapAlloc(heap, 0, filename_length));
     29     if (!filename)
     30       return;
     31 
     32     for (;;)
     33     {
     34       DWORD len = GetModuleFileNameW(crt_handle, filename, filename_length);
     35       if (len == filename_length && GetLastError() == ERROR_INSUFFICIENT_BUFFER)
     36       {
     37         filename_length *= 2;
     38         if (filename_length >= 4 * 1024)
     39           return;
     40         LPWSTR new_filename = static_cast<LPWSTR>(HeapReAlloc(heap, 0, filename, filename_length));
     41         if (!new_filename)
     42         {
     43           HeapFree(heap, 0, filename);
     44           return;
     45         }
     46         filename = new_filename;
     47         continue;
     48       }
     49 
     50       break;
     51     }
     52 
     53     const DWORD version_size = GetFileVersionInfoSizeExW(0, filename, nullptr);
     54     LPVOID version_block;
     55     if (version_size == 0 || !(version_block = HeapAlloc(heap, 0, version_size)))
     56     {
     57       HeapFree(heap, 0, filename);
     58       return;
     59     }
     60 
     61     VS_FIXEDFILEINFO* fi;
     62     UINT fi_size;
     63     if (!GetFileVersionInfoExW(0, filename, 0, version_size, version_block) ||
     64         !VerQueryValueW(version_block, L"\\", reinterpret_cast<LPVOID*>(&fi), &fi_size))
     65     {
     66       HeapFree(heap, 0, version_block);
     67       HeapFree(heap, 0, filename);
     68       return;
     69     }
     70 
     71     const DWORD64 version = MAKE_VERSION64((fi->dwFileVersionMS >> 16) & 0xFFFFu, fi->dwFileVersionMS & 0xFFFFu,
     72                                            (fi->dwFileVersionLS >> 16) & 0xFFFFu, fi->dwFileVersionLS & 0xFFFFu);
     73 
     74     HeapFree(heap, 0, version_block);
     75     HeapFree(heap, 0, filename);
     76 
     77     if (version >= MIN_VERSION)
     78       return;
     79 
     80     // fmt is self-contained, hopefully it'll be okay.
     81     char message[512];
     82     const auto fmt_result =
     83       fmt::format_to_n(message, sizeof(message),
     84                        "Your Microsoft Visual C++ Runtime appears to be too old for this build of DuckStation.\n\n"
     85                        "Your version: {}.{}.{}.{}\n"
     86                        "Required version: {}.{}.{}.{}\n\n"
     87                        "You can download the latest version from {}.\n\n"
     88                        "Do you want to exit and download this version now?\n"
     89                        "If you select No, DuckStation will likely crash.",
     90                        VERSION64_PART(version, 0), VERSION64_PART(version, 1), VERSION64_PART(version, 2),
     91                        VERSION64_PART(version, 3), VERSION64_PART(MIN_VERSION, 0), VERSION64_PART(MIN_VERSION, 1),
     92                        VERSION64_PART(MIN_VERSION, 2), VERSION64_PART(MIN_VERSION, 3), DOWNLOAD_URL);
     93     message[(fmt_result.size > (sizeof(message) - 1)) ? (sizeof(message) - 1) : fmt_result.size] = 0;
     94 
     95     if (MessageBoxA(NULL, message, "Old Visual C++ Runtime Detected", MB_ICONERROR | MB_YESNO) == IDNO)
     96       return;
     97 
     98     if (!ShellExecuteA(NULL, "open", DOWNLOAD_URL, nullptr, nullptr, SW_SHOWNORMAL))
     99       MessageBoxA(NULL, "ShellExecuteA() failed, you may need to manually open the URL.", "Error", MB_OK);
    100 
    101     TerminateProcess(GetCurrentProcess(), 0xFFFFFFFF);
    102   }
    103 };
    104 
    105 // We have to use a special object which gets initialized before all other global objects, because those might use the
    106 // CRT and go kaboom. Yucky, but gets the job done.
    107 #pragma optimize("", off)
    108 #pragma warning(disable : 4075) // warning C4075: initializers put in unrecognized initialization area
    109 #pragma init_seg(".CRT$XCT")
    110 VCRuntimeCheckObject s_vcruntime_checker;