updater: Check and update VC runtime version

This commit is contained in:
derrod 2024-06-09 23:31:44 +02:00 committed by Ryan Foster
parent 051c11e7b2
commit 6779052902
3 changed files with 72 additions and 71 deletions

View file

@ -26,8 +26,16 @@ target_compile_definitions(updater PRIVATE NOMINMAX "PSAPI_VERSION=2")
target_include_directories(updater PRIVATE "${CMAKE_SOURCE_DIR}/libobs" "${CMAKE_SOURCE_DIR}/UI/win-update") target_include_directories(updater PRIVATE "${CMAKE_SOURCE_DIR}/libobs" "${CMAKE_SOURCE_DIR}/UI/win-update")
target_link_libraries(updater PRIVATE OBS::blake2 nlohmann_json::nlohmann_json zstd::libzstd_static comctl32 shell32 target_link_libraries(
winhttp) updater
PRIVATE OBS::blake2
nlohmann_json::nlohmann_json
zstd::libzstd_static
comctl32
shell32
version
winhttp
wintrust)
# zstd is hardcoded with /DEFAULTLIB:LIBCMT # zstd is hardcoded with /DEFAULTLIB:LIBCMT
target_link_options(updater PRIVATE /NODEFAULTLIB:LIBCMT) target_link_options(updater PRIVATE /NODEFAULTLIB:LIBCMT)

View file

@ -36,7 +36,15 @@ if(MSVC)
target_link_options(updater PRIVATE "LINKER:/IGNORE:4098") target_link_options(updater PRIVATE "LINKER:/IGNORE:4098")
endif() endif()
target_link_libraries(updater PRIVATE OBS::blake2 nlohmann_json::nlohmann_json zstd::libzstd_static comctl32 shell32 target_link_libraries(
winhttp) updater
PRIVATE OBS::blake2
nlohmann_json::nlohmann_json
zstd::libzstd_static
comctl32
shell32
version
winhttp
wintrust)
set_target_properties(updater PROPERTIES FOLDER "frontend") set_target_properties(updater PROPERTIES FOLDER "frontend")

View file

@ -18,6 +18,8 @@
#include "manifest.hpp" #include "manifest.hpp"
#include <psapi.h> #include <psapi.h>
#include <WinTrust.h>
#include <SoftPub.h>
#include <util/windows/CoTaskMemPtr.hpp> #include <util/windows/CoTaskMemPtr.hpp>
@ -39,6 +41,9 @@ constexpr const wchar_t *kCDNUpdateBaseUrl =
L"https://cdn-fastly.obsproject.com/update_studio"; L"https://cdn-fastly.obsproject.com/update_studio";
constexpr const wchar_t *kPatchManifestURL = constexpr const wchar_t *kPatchManifestURL =
L"https://obsproject.com/update_studio/getpatchmanifest"; L"https://obsproject.com/update_studio/getpatchmanifest";
constexpr const wchar_t *kVSRedistURL =
L"https://aka.ms/vs/17/release/vc_redist.x64.exe";
constexpr const wchar_t *kMSHostname = L"aka.ms";
/* ----------------------------------------------------------------------- */ /* ----------------------------------------------------------------------- */
@ -72,44 +77,28 @@ void FreeWinHttpHandle(HINTERNET handle)
/* ----------------------------------------------------------------------- */ /* ----------------------------------------------------------------------- */
static inline bool HasVS2019Redist2() static bool IsVSRedistOutdated()
{ {
wchar_t base[MAX_PATH]; VS_FIXEDFILEINFO *info = nullptr;
wchar_t path[MAX_PATH]; UINT len = 0;
WIN32_FIND_DATAW wfd; vector<std::byte> buf;
HANDLE handle;
SHGetFolderPathW(NULL, CSIDL_SYSTEM, NULL, SHGFP_TYPE_CURRENT, base); const wchar_t vc_dll[] = L"msvcp140";
#define check_dll_installed(dll) \ auto size = GetFileVersionInfoSize(vc_dll, nullptr);
do { \ if (!size)
StringCbCopyW(path, sizeof(path), base); \ return true;
StringCbCatW(path, sizeof(path), L"\\" dll ".dll"); \
handle = FindFirstFileW(path, &wfd); \
if (handle == INVALID_HANDLE_VALUE) { \
return false; \
} else { \
FindClose(handle); \
} \
} while (false)
check_dll_installed(L"msvcp140"); buf.resize(size);
check_dll_installed(L"vcruntime140"); if (!GetFileVersionInfo(vc_dll, 0, size, buf.data()))
check_dll_installed(L"vcruntime140_1"); return true;
#undef check_dll_installed bool success = VerQueryValue(buf.data(), L"\\",
reinterpret_cast<LPVOID *>(&info), &len);
if (!success || !info || !len)
return true;
return true; return LOWORD(info->dwFileVersionMS) < 40;
}
static bool HasVS2019Redist()
{
PVOID old = nullptr;
bool redirect = !!Wow64DisableWow64FsRedirection(&old);
bool success = HasVS2019Redist2();
if (redirect)
Wow64RevertWow64FsRedirection(old);
return success;
} }
static void Status(const wchar_t *fmt, ...) static void Status(const wchar_t *fmt, ...)
@ -1139,7 +1128,7 @@ try {
return false; return false;
} }
static bool UpdateVS2019Redists(const string &vc_redist_hash) static bool UpdateVSRedists()
{ {
/* ------------------------------------------ * /* ------------------------------------------ *
* Initialize session */ * Initialize session */
@ -1153,7 +1142,7 @@ static bool UpdateVS2019Redists(const string &vc_redist_hash)
WINHTTP_NO_PROXY_NAME, WINHTTP_NO_PROXY_NAME,
WINHTTP_NO_PROXY_BYPASS, 0); WINHTTP_NO_PROXY_BYPASS, 0);
if (!hSession) { if (!hSession) {
Status(L"Update failed: Couldn't open obsproject.com"); Status(L"VC Redist Update failed: Couldn't create session");
return false; return false;
} }
@ -1163,10 +1152,10 @@ static bool UpdateVS2019Redists(const string &vc_redist_hash)
WinHttpSetOption(hSession, WINHTTP_OPTION_DECOMPRESSION, WinHttpSetOption(hSession, WINHTTP_OPTION_DECOMPRESSION,
(LPVOID)&compressionFlags, sizeof(compressionFlags)); (LPVOID)&compressionFlags, sizeof(compressionFlags));
HttpHandle hConnect = WinHttpConnect(hSession, kCDNHostname, HttpHandle hConnect = WinHttpConnect(hSession, kMSHostname,
INTERNET_DEFAULT_HTTPS_PORT, 0); INTERNET_DEFAULT_HTTPS_PORT, 0);
if (!hConnect) { if (!hConnect) {
Status(L"Update failed: Couldn't connect to %S", kCDNHostname); Status(L"Update failed: Couldn't connect to %S", kMSHostname);
return false; return false;
} }
@ -1180,54 +1169,50 @@ static bool UpdateVS2019Redists(const string &vc_redist_hash)
/* ------------------------------------------ * /* ------------------------------------------ *
* Download redist */ * Download redist */
Status(L"Downloading Visual C++ 2019 Redistributable"); Status(L"Downloading Visual C++ Redistributable");
wstring sourceURL =
L"https://cdn-fastly.obsproject.com/downloads/VC_redist.x64.exe";
wstring destPath; wstring destPath;
destPath += tempPath; destPath += tempPath;
destPath += L"\\VC_redist.x64.exe"; destPath += L"\\VC_redist.x64.exe";
if (!HTTPGetFile(hConnect, sourceURL.c_str(), destPath.c_str(), if (!HTTPGetFile(hConnect, kVSRedistURL, destPath.c_str(),
L"Accept-Encoding: gzip", &responseCode)) { L"Accept-Encoding: gzip", &responseCode)) {
DeleteFile(destPath.c_str()); DeleteFile(destPath.c_str());
Status(L"Update failed: Could not download " Status(L"Update failed: Could not download "
L"%s (error code %d)", L"%s (error code %d)",
L"Visual C++ 2019 Redistributable", responseCode); L"Visual C++ Redistributable", responseCode);
return false; return false;
} }
/* ------------------------------------------ * /* ------------------------------------------ *
* Get expected hash */ * Verify file signature */
B2Hash expectedHash; GUID action = WINTRUST_ACTION_GENERIC_VERIFY_V2;
StringToHash(vc_redist_hash, expectedHash);
/* ------------------------------------------ * WINTRUST_FILE_INFO fileInfo = {};
* Get download hash */ fileInfo.cbStruct = sizeof(fileInfo);
fileInfo.pcwszFilePath = destPath.c_str();
B2Hash downloadHash; WINTRUST_DATA data = {};
if (!CalculateFileHash(destPath.c_str(), downloadHash)) { data.cbStruct = sizeof(data);
data.dwUIChoice = WTD_UI_NONE;
data.dwUnionChoice = WTD_CHOICE_FILE;
data.dwStateAction = WTD_STATEACTION_VERIFY;
data.pFile = &fileInfo;
LONG result = WinVerifyTrust(nullptr, &action, &data);
if (result != ERROR_SUCCESS) {
Status(L"Update failed: Signature verification failed for "
L"%s (error code %d / %d)",
L"Visual C++ Redistributable", result, GetLastError());
DeleteFile(destPath.c_str()); DeleteFile(destPath.c_str());
Status(L"Update failed: Couldn't verify integrity of %s",
L"Visual C++ 2019 Redistributable");
return false; return false;
} }
/* ------------------------------------------ * /* ------------------------------------------ *
* If hashes do not match, integrity failed */ * If verification succeeded, install redist */
if (downloadHash == expectedHash) {
DeleteFile(destPath.c_str());
Status(L"Update failed: Couldn't verify integrity of %s",
L"Visual C++ 2019 Redistributable");
return false;
}
/* ------------------------------------------ *
* If hashes match, install redist */
wchar_t commandline[MAX_PATH + MAX_PATH]; wchar_t commandline[MAX_PATH + MAX_PATH];
StringCbPrintf(commandline, sizeof(commandline), StringCbPrintf(commandline, sizeof(commandline),
@ -1241,7 +1226,7 @@ static bool UpdateVS2019Redists(const string &vc_redist_hash)
nullptr, false, CREATE_NO_WINDOW, nullptr, false, CREATE_NO_WINDOW,
nullptr, nullptr, &si, &pi); nullptr, nullptr, &si, &pi);
if (success) { if (success) {
Status(L"Installing %s...", L"Visual C++ 2019 Redistributable"); Status(L"Installing %s...", L"Visual C++ Redistributable");
CloseHandle(pi.hThread); CloseHandle(pi.hThread);
WaitForSingleObject(pi.hProcess, INFINITE); WaitForSingleObject(pi.hProcess, INFINITE);
@ -1249,7 +1234,7 @@ static bool UpdateVS2019Redists(const string &vc_redist_hash)
} else { } else {
Status(L"Update failed: Could not execute " Status(L"Update failed: Could not execute "
L"%s (error code %d)", L"%s (error code %d)",
L"Visual C++ 2019 Redistributable", (int)GetLastError()); L"Visual C++ Redistributable", (int)GetLastError());
} }
DeleteFile(destPath.c_str()); DeleteFile(destPath.c_str());
@ -1514,10 +1499,10 @@ static bool Update(wchar_t *cmdLine)
} }
/* ------------------------------------- * /* ------------------------------------- *
* Check for VS2019 redistributables */ * Check VS redistributables version */
if (!HasVS2019Redist()) { if (IsVSRedistOutdated()) {
if (!UpdateVS2019Redists(manifest.vc2019_redist_x64)) { if (!UpdateVSRedists()) {
return false; return false;
} }
} }