WinUpdater: Check OS and VC++ Redist versions.
This commit is contained in:
parent
22197c09a3
commit
717c36bc43
|
@ -0,0 +1,4 @@
|
||||||
|
find_program(POWERSHELL_EXE NAMES powershell)
|
||||||
|
|
||||||
|
INCLUDE(FindPackageHandleStandardArgs)
|
||||||
|
find_package_handle_standard_args(PowerShell DEFAULT_MSG POWERSHELL_EXE)
|
|
@ -324,6 +324,17 @@ if(UNIX)
|
||||||
target_link_libraries(traversal_server PRIVATE ${SYSTEMD_LIBRARIES})
|
target_link_libraries(traversal_server PRIVATE ${SYSTEMD_LIBRARIES})
|
||||||
endif()
|
endif()
|
||||||
elseif(WIN32)
|
elseif(WIN32)
|
||||||
|
find_package(PowerShell REQUIRED)
|
||||||
|
execute_process(
|
||||||
|
COMMAND ${POWERSHELL_EXE} -Command "[System.Diagnostics.FileVersionInfo]::GetVersionInfo('$ENV{VCToolsRedistDir}vc_redist.x64.exe').ProductVersion"
|
||||||
|
OUTPUT_VARIABLE VC_TOOLS_VERSION
|
||||||
|
OUTPUT_STRIP_TRAILING_WHITESPACE
|
||||||
|
)
|
||||||
|
configure_file(
|
||||||
|
"${CMAKE_CURRENT_SOURCE_DIR}/build_info.txt.in"
|
||||||
|
"${CMAKE_RUNTIME_OUTPUT_DIRECTORY}/build_info.txt"
|
||||||
|
)
|
||||||
|
|
||||||
target_link_libraries(common PRIVATE "-INCLUDE:enableCompatPatches")
|
target_link_libraries(common PRIVATE "-INCLUDE:enableCompatPatches")
|
||||||
endif()
|
endif()
|
||||||
|
|
||||||
|
|
|
@ -0,0 +1,24 @@
|
||||||
|
// Indicate the minimum OS version required for the binary to run properly.
|
||||||
|
// Updater will fail the update if the user does not meet this requirement.
|
||||||
|
OSMinimumVersionWin10=10.0.15063.0
|
||||||
|
OSMinimumVersionWin11=10.0.22000.0
|
||||||
|
OSMinimumVersionMacOS=10.14
|
||||||
|
|
||||||
|
// This is the runtime which was compiled against - providing a way for Updater to detect if update
|
||||||
|
// is needed before executing this binary. Note that, annoyingly, the version in environment
|
||||||
|
// variables does not match the "real" version. Consider:
|
||||||
|
// VersionInfo : File: C:\Program Files (x86)\Microsoft Visual Studio\2022\BuildTools\VC\Redist\MSVC\14.32.31326\vc_redist.x64.exe
|
||||||
|
// InternalName: setup
|
||||||
|
// OriginalFilename: VC_redist.x64.exe
|
||||||
|
// FileVersion: 14.32.31332.0
|
||||||
|
// FileDescription: Microsoft Visual C++ 2015-2022 Redistributable (x64) - 14.32.31332
|
||||||
|
// Product: Microsoft Visual C++ 2015-2022 Redistributable (x64) - 14.32.31332
|
||||||
|
// ProductVersion: 14.32.31332.0
|
||||||
|
// Whereas the environment variables look like:
|
||||||
|
// VCToolsInstallDir=C:\Program Files (x86)\Microsoft Visual Studio\2022\BuildTools\VC\Tools\MSVC\14.33.31629\
|
||||||
|
// VCToolsRedistDir=C:\Program Files (x86)\Microsoft Visual Studio\2022\BuildTools\VC\Redist\MSVC\14.32.31326\
|
||||||
|
// VCToolsVersion=14.33.31629
|
||||||
|
// We're really looking for "14.32.31332.0" (because that's what will appear in the registry once
|
||||||
|
// installed), NOT the other values!
|
||||||
|
VCToolsVersion=${VC_TOOLS_VERSION}
|
||||||
|
VCToolsUpdateURL=https://aka.ms/vs/17/release/vc_redist.x64.exe
|
|
@ -60,4 +60,38 @@
|
||||||
<Import Project="$(ExternalsDir)zstd\exports.props" />
|
<Import Project="$(ExternalsDir)zstd\exports.props" />
|
||||||
<Import Project="$(VCTargetsPath)\Microsoft.Cpp.targets" />
|
<Import Project="$(VCTargetsPath)\Microsoft.Cpp.targets" />
|
||||||
<ImportGroup Label="ExtensionTargets" />
|
<ImportGroup Label="ExtensionTargets" />
|
||||||
|
<PropertyGroup>
|
||||||
|
<BuildInfoTemplate>Common\build_info.txt.in</BuildInfoTemplate>
|
||||||
|
<BuildInfoOutput>$(BinaryOutputDir)build_info.txt</BuildInfoOutput>
|
||||||
|
</PropertyGroup>
|
||||||
|
<ItemGroup>
|
||||||
|
<Text Include="$(BuildInfoTemplate)" />
|
||||||
|
</ItemGroup>
|
||||||
|
<UsingTask TaskName="GetProductVersion"
|
||||||
|
TaskFactory="CodeTaskFactory"
|
||||||
|
AssemblyFile="$(MSBuildToolsPath)\Microsoft.Build.Tasks.Core.dll">
|
||||||
|
<ParameterGroup>
|
||||||
|
<Path ParameterType="System.String" Required="true" />
|
||||||
|
<ProductVersion ParameterType="System.String" Output="true" />
|
||||||
|
</ParameterGroup>
|
||||||
|
<Task>
|
||||||
|
<Using Namespace="System.Diagnostics"/>
|
||||||
|
<Code Type="Fragment" Language="cs">
|
||||||
|
<![CDATA[
|
||||||
|
ProductVersion = FileVersionInfo.GetVersionInfo(Path).ProductVersion;
|
||||||
|
]]>
|
||||||
|
</Code>
|
||||||
|
</Task>
|
||||||
|
</UsingTask>
|
||||||
|
<Target Name="WriteBuildInfo" AfterTargets="Build" Inputs="$(BuildInfoTemplate)" Outputs="$(BuildInfoOutput)">
|
||||||
|
<GetProductVersion Path="$(VCToolsRedistInstallDir)vc_redist.x64.exe">
|
||||||
|
<Output PropertyName="VCToolsProductVersion" TaskParameter="ProductVersion" />
|
||||||
|
</GetProductVersion>
|
||||||
|
<Message Text="VCToolsProductVersion $(VCToolsProductVersion)" Importance="High" />
|
||||||
|
<WriteLinesToFile
|
||||||
|
File="$(BuildInfoOutput)"
|
||||||
|
Lines="$([System.IO.File]::ReadAllText($(BuildInfoTemplate)).Replace('${VC_TOOLS_VERSION}', $(VCToolsProductVersion)))"
|
||||||
|
Overwrite="true"
|
||||||
|
/>
|
||||||
|
</Target>
|
||||||
</Project>
|
</Project>
|
|
@ -3,6 +3,7 @@
|
||||||
|
|
||||||
#include "MacUpdater/ViewController.h"
|
#include "MacUpdater/ViewController.h"
|
||||||
|
|
||||||
|
#include "UpdaterCommon/Platform.h"
|
||||||
#include "UpdaterCommon/UI.h"
|
#include "UpdaterCommon/UI.h"
|
||||||
|
|
||||||
#include <Cocoa/Cocoa.h>
|
#include <Cocoa/Cocoa.h>
|
||||||
|
@ -136,3 +137,21 @@ void UI::Stop()
|
||||||
void UI::Init()
|
void UI::Init()
|
||||||
{
|
{
|
||||||
}
|
}
|
||||||
|
|
||||||
|
Platform::BuildInfo::BuildInfo(const std::string& content)
|
||||||
|
{
|
||||||
|
map = {{"OSMinimumVersionMacOS", ""}};
|
||||||
|
Parse(content);
|
||||||
|
}
|
||||||
|
|
||||||
|
bool Platform::VersionCheck(const BuildInfo& this_build_info, const BuildInfo& next_build_info)
|
||||||
|
{
|
||||||
|
// TODO implement OS Minimum Version check
|
||||||
|
// It should go something like this:
|
||||||
|
// auto target_version = next_build_info.GetVersion("OSMinimumVersionMacOS");
|
||||||
|
// if (!target_version.has_value() || current_version >= target_version)
|
||||||
|
// return true;
|
||||||
|
// show error
|
||||||
|
// return false;
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
|
@ -246,11 +246,11 @@ void AutoUpdateChecker::TriggerUpdate(const AutoUpdateChecker::NewVersionInforma
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
// Run the updater!
|
// Run the updater!
|
||||||
const std::string command_line = MakeUpdaterCommandLine(updater_flags);
|
std::string command_line = MakeUpdaterCommandLine(updater_flags);
|
||||||
INFO_LOG_FMT(COMMON, "Updater command line: {}", command_line);
|
INFO_LOG_FMT(COMMON, "Updater command line: {}", command_line);
|
||||||
|
|
||||||
#ifdef _WIN32
|
#ifdef _WIN32
|
||||||
STARTUPINFO sinfo = {sizeof(sinfo)};
|
STARTUPINFO sinfo{.cb = sizeof(sinfo)};
|
||||||
sinfo.dwFlags = STARTF_FORCEOFFFEEDBACK; // No hourglass cursor after starting the process.
|
sinfo.dwFlags = STARTF_FORCEOFFFEEDBACK; // No hourglass cursor after starting the process.
|
||||||
PROCESS_INFORMATION pinfo;
|
PROCESS_INFORMATION pinfo;
|
||||||
if (CreateProcessW(UTF8ToWString(reloc_updater_path).c_str(), UTF8ToWString(command_line).data(),
|
if (CreateProcessW(UTF8ToWString(reloc_updater_path).c_str(), UTF8ToWString(command_line).data(),
|
||||||
|
|
|
@ -0,0 +1,100 @@
|
||||||
|
// Copyright 2018 Dolphin Emulator Project
|
||||||
|
// SPDX-License-Identifier: GPL-2.0-or-later
|
||||||
|
|
||||||
|
#pragma once
|
||||||
|
|
||||||
|
#include <map>
|
||||||
|
#include <optional>
|
||||||
|
#include <sstream>
|
||||||
|
|
||||||
|
#include "Common/CommonTypes.h"
|
||||||
|
#include "Common/StringUtil.h"
|
||||||
|
|
||||||
|
namespace Platform
|
||||||
|
{
|
||||||
|
struct BuildVersion
|
||||||
|
{
|
||||||
|
u32 major{};
|
||||||
|
u32 minor{};
|
||||||
|
u32 build{};
|
||||||
|
auto operator<=>(BuildVersion const& rhs) const = default;
|
||||||
|
static std::optional<BuildVersion> from_string(const std::string& str)
|
||||||
|
{
|
||||||
|
auto components = SplitString(str, '.');
|
||||||
|
// Allow variable number of components (truncating after "build"), but not
|
||||||
|
// empty.
|
||||||
|
if (components.size() == 0)
|
||||||
|
return {};
|
||||||
|
BuildVersion version;
|
||||||
|
if (!TryParse(components[0], &version.major, 10))
|
||||||
|
return {};
|
||||||
|
if (components.size() > 1 && !TryParse(components[1], &version.minor, 10))
|
||||||
|
return {};
|
||||||
|
if (components.size() > 2 && !TryParse(components[2], &version.build, 10))
|
||||||
|
return {};
|
||||||
|
return version;
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
enum class VersionCheckStatus
|
||||||
|
{
|
||||||
|
NothingToDo,
|
||||||
|
UpdateOptional,
|
||||||
|
UpdateRequired,
|
||||||
|
};
|
||||||
|
|
||||||
|
struct VersionCheckResult
|
||||||
|
{
|
||||||
|
VersionCheckStatus status{VersionCheckStatus::NothingToDo};
|
||||||
|
std::optional<BuildVersion> current_version{};
|
||||||
|
std::optional<BuildVersion> target_version{};
|
||||||
|
};
|
||||||
|
|
||||||
|
class BuildInfo
|
||||||
|
{
|
||||||
|
using Map = std::map<std::string, std::string>;
|
||||||
|
|
||||||
|
public:
|
||||||
|
BuildInfo() = default;
|
||||||
|
BuildInfo(const std::string& content);
|
||||||
|
|
||||||
|
std::optional<std::string> GetString(const std::string& name) const
|
||||||
|
{
|
||||||
|
auto it = map.find(name);
|
||||||
|
if (it == map.end() || it->second.size() == 0)
|
||||||
|
return {};
|
||||||
|
return it->second;
|
||||||
|
}
|
||||||
|
|
||||||
|
std::optional<BuildVersion> GetVersion(const std::string& name) const
|
||||||
|
{
|
||||||
|
auto str = GetString(name);
|
||||||
|
if (!str.has_value())
|
||||||
|
return {};
|
||||||
|
return BuildVersion::from_string(str.value());
|
||||||
|
}
|
||||||
|
|
||||||
|
private:
|
||||||
|
void Parse(const std::string& content)
|
||||||
|
{
|
||||||
|
std::stringstream content_stream(content);
|
||||||
|
std::string line;
|
||||||
|
while (std::getline(content_stream, line))
|
||||||
|
{
|
||||||
|
if (line.starts_with("//"))
|
||||||
|
continue;
|
||||||
|
const size_t equals_index = line.find('=');
|
||||||
|
if (equals_index == line.npos)
|
||||||
|
continue;
|
||||||
|
auto key = line.substr(0, equals_index);
|
||||||
|
auto key_it = map.find(key);
|
||||||
|
if (key_it == map.end())
|
||||||
|
continue;
|
||||||
|
key_it->second = line.substr(equals_index + 1);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
Map map;
|
||||||
|
};
|
||||||
|
|
||||||
|
bool VersionCheck(const BuildInfo& this_build_info, const BuildInfo& next_build_info);
|
||||||
|
} // namespace Platform
|
|
@ -18,6 +18,7 @@
|
||||||
#include "Common/HttpRequest.h"
|
#include "Common/HttpRequest.h"
|
||||||
#include "Common/ScopeGuard.h"
|
#include "Common/ScopeGuard.h"
|
||||||
#include "Common/StringUtil.h"
|
#include "Common/StringUtil.h"
|
||||||
|
#include "UpdaterCommon/Platform.h"
|
||||||
#include "UpdaterCommon/UI.h"
|
#include "UpdaterCommon/UI.h"
|
||||||
|
|
||||||
#ifndef _WIN32
|
#ifndef _WIN32
|
||||||
|
@ -278,6 +279,41 @@ bool DownloadContent(const std::vector<TodoList::DownloadOp>& to_download,
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
bool PlatformVersionCheck(const std::vector<TodoList::UpdateOp>& to_update,
|
||||||
|
const std::string& install_base_path, const std::string& temp_dir)
|
||||||
|
{
|
||||||
|
UI::SetDescription("Checking platform...");
|
||||||
|
|
||||||
|
const auto op_it = std::find_if(to_update.cbegin(), to_update.cend(),
|
||||||
|
[&](const auto& op) { return op.filename == "build_info.txt"; });
|
||||||
|
if (op_it == to_update.cend())
|
||||||
|
return true;
|
||||||
|
|
||||||
|
const auto op = *op_it;
|
||||||
|
std::string build_info_path =
|
||||||
|
temp_dir + DIR_SEP + HexEncode(op.new_hash.data(), op.new_hash.size());
|
||||||
|
std::string build_info_content;
|
||||||
|
if (!File::ReadFileToString(build_info_path, build_info_content) ||
|
||||||
|
op.new_hash != ComputeHash(build_info_content))
|
||||||
|
{
|
||||||
|
fprintf(log_fp, "Failed to read %s\n.", build_info_path.c_str());
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
auto next_build_info = Platform::BuildInfo(build_info_content);
|
||||||
|
|
||||||
|
build_info_path = install_base_path + DIR_SEP + "build_info.txt";
|
||||||
|
auto this_build_info = Platform::BuildInfo();
|
||||||
|
if (File::ReadFileToString(build_info_path, build_info_content))
|
||||||
|
{
|
||||||
|
if (op.old_hash != ComputeHash(build_info_content))
|
||||||
|
fprintf(log_fp, "Using modified existing BuildInfo %s.\n", build_info_path.c_str());
|
||||||
|
this_build_info = Platform::BuildInfo(build_info_content);
|
||||||
|
}
|
||||||
|
|
||||||
|
// The existing BuildInfo may have been modified. Be careful not to overly trust its contents!
|
||||||
|
return Platform::VersionCheck(this_build_info, next_build_info);
|
||||||
|
}
|
||||||
|
|
||||||
TodoList ComputeActionsToDo(Manifest this_manifest, Manifest next_manifest)
|
TodoList ComputeActionsToDo(Manifest this_manifest, Manifest next_manifest)
|
||||||
{
|
{
|
||||||
TodoList todo;
|
TodoList todo;
|
||||||
|
@ -474,6 +510,11 @@ bool PerformUpdate(const TodoList& todo, const std::string& install_base_path,
|
||||||
return false;
|
return false;
|
||||||
fprintf(log_fp, "Download step completed.\n");
|
fprintf(log_fp, "Download step completed.\n");
|
||||||
|
|
||||||
|
fprintf(log_fp, "Starting platform version check step...\n");
|
||||||
|
if (!PlatformVersionCheck(todo.to_update, install_base_path, temp_path))
|
||||||
|
return false;
|
||||||
|
fprintf(log_fp, "Platform version check step completed.\n");
|
||||||
|
|
||||||
fprintf(log_fp, "Starting update step...\n");
|
fprintf(log_fp, "Starting update step...\n");
|
||||||
if (!UpdateFiles(todo.to_update, install_base_path, temp_path))
|
if (!UpdateFiles(todo.to_update, install_base_path, temp_path))
|
||||||
return false;
|
return false;
|
||||||
|
|
|
@ -2,6 +2,7 @@ set (MANIFEST_FILE Updater.exe.manifest)
|
||||||
|
|
||||||
add_executable(winupdater WIN32
|
add_executable(winupdater WIN32
|
||||||
Main.cpp
|
Main.cpp
|
||||||
|
Platform.cpp
|
||||||
WinUI.cpp
|
WinUI.cpp
|
||||||
${MANIFEST_FILE})
|
${MANIFEST_FILE})
|
||||||
|
|
||||||
|
|
|
@ -21,7 +21,7 @@ int WINAPI wWinMain(HINSTANCE hInstance, HINSTANCE hPrevInstance, PWSTR pCmdLine
|
||||||
{
|
{
|
||||||
if (lstrlenW(pCmdLine) == 0)
|
if (lstrlenW(pCmdLine) == 0)
|
||||||
{
|
{
|
||||||
MessageBox(nullptr,
|
MessageBoxW(nullptr,
|
||||||
L"This updater is not meant to be launched directly. Configure Auto-Update in "
|
L"This updater is not meant to be launched directly. Configure Auto-Update in "
|
||||||
"Dolphin's settings instead.",
|
"Dolphin's settings instead.",
|
||||||
L"Error", MB_ICONERROR);
|
L"Error", MB_ICONERROR);
|
||||||
|
|
|
@ -0,0 +1,187 @@
|
||||||
|
#include <Windows.h>
|
||||||
|
|
||||||
|
#include <filesystem>
|
||||||
|
#include <map>
|
||||||
|
#include <optional>
|
||||||
|
|
||||||
|
#include "Common/FileUtil.h"
|
||||||
|
#include "Common/HttpRequest.h"
|
||||||
|
#include "Common/IOFile.h"
|
||||||
|
#include "Common/ScopeGuard.h"
|
||||||
|
#include "Common/StringUtil.h"
|
||||||
|
|
||||||
|
#include "UpdaterCommon/Platform.h"
|
||||||
|
#include "UpdaterCommon/UI.h"
|
||||||
|
|
||||||
|
namespace Platform
|
||||||
|
{
|
||||||
|
BuildInfo::BuildInfo(const std::string& content)
|
||||||
|
{
|
||||||
|
map = {{"OSMinimumVersionWin10", ""},
|
||||||
|
{"OSMinimumVersionWin11", ""},
|
||||||
|
{"VCToolsVersion", ""},
|
||||||
|
{"VCToolsUpdateURL", ""}};
|
||||||
|
Parse(content);
|
||||||
|
}
|
||||||
|
|
||||||
|
// This default value should be kept in sync with the value of VCToolsUpdateURL in
|
||||||
|
// build_info.txt.in
|
||||||
|
static const char* VCToolsUpdateURLDefault = "https://aka.ms/vs/17/release/vc_redist.x64.exe";
|
||||||
|
#define VC_RUNTIME_REGKEY R"(SOFTWARE\Microsoft\VisualStudio\14.0\VC\Runtimes\)"
|
||||||
|
|
||||||
|
static const char* VCRuntimeRegistrySubkey()
|
||||||
|
{
|
||||||
|
return VC_RUNTIME_REGKEY
|
||||||
|
#ifdef _M_X86_64
|
||||||
|
"x64";
|
||||||
|
#elif _M_ARM_64
|
||||||
|
"arm64";
|
||||||
|
#else
|
||||||
|
#error unsupported architecture
|
||||||
|
#endif
|
||||||
|
}
|
||||||
|
|
||||||
|
static bool ReadVCRuntimeVersionField(u32* value, const char* name)
|
||||||
|
{
|
||||||
|
DWORD value_len = sizeof(*value);
|
||||||
|
return RegGetValueA(HKEY_LOCAL_MACHINE, VCRuntimeRegistrySubkey(), name, RRF_RT_REG_DWORD,
|
||||||
|
nullptr, value, &value_len) == ERROR_SUCCESS;
|
||||||
|
}
|
||||||
|
|
||||||
|
static std::optional<BuildVersion> GetInstalledVCRuntimeVersion()
|
||||||
|
{
|
||||||
|
u32 installed;
|
||||||
|
if (!ReadVCRuntimeVersionField(&installed, "Installed") || !installed)
|
||||||
|
return {};
|
||||||
|
BuildVersion version;
|
||||||
|
if (!ReadVCRuntimeVersionField(&version.major, "Major") ||
|
||||||
|
!ReadVCRuntimeVersionField(&version.minor, "Minor") ||
|
||||||
|
!ReadVCRuntimeVersionField(&version.build, "Bld"))
|
||||||
|
{
|
||||||
|
return {};
|
||||||
|
}
|
||||||
|
return version;
|
||||||
|
}
|
||||||
|
|
||||||
|
static VersionCheckResult VCRuntimeVersionCheck(const BuildInfo& this_build_info,
|
||||||
|
const BuildInfo& next_build_info)
|
||||||
|
{
|
||||||
|
VersionCheckResult result;
|
||||||
|
result.current_version = GetInstalledVCRuntimeVersion();
|
||||||
|
result.target_version = next_build_info.GetVersion("VCToolsVersion");
|
||||||
|
|
||||||
|
auto existing_version = this_build_info.GetVersion("VCToolsVersion");
|
||||||
|
|
||||||
|
if (!result.target_version.has_value())
|
||||||
|
result.status = VersionCheckStatus::UpdateOptional;
|
||||||
|
else if (!result.current_version.has_value() || result.current_version < result.target_version)
|
||||||
|
result.status = VersionCheckStatus::UpdateRequired;
|
||||||
|
|
||||||
|
// See if the current build was already running on acceptable version of the runtime. This could
|
||||||
|
// happen if the user manually copied the redist DLLs and got Dolphin running that way.
|
||||||
|
if (existing_version.has_value() && existing_version >= result.target_version)
|
||||||
|
result.status = VersionCheckStatus::NothingToDo;
|
||||||
|
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
|
||||||
|
static bool VCRuntimeUpdate(const BuildInfo& build_info)
|
||||||
|
{
|
||||||
|
UI::SetDescription("Updating VC++ Redist, please wait...");
|
||||||
|
|
||||||
|
Common::HttpRequest req;
|
||||||
|
req.FollowRedirects(10);
|
||||||
|
auto resp = req.Get(build_info.GetString("VCToolsUpdateURL").value_or(VCToolsUpdateURLDefault));
|
||||||
|
if (!resp)
|
||||||
|
return false;
|
||||||
|
|
||||||
|
// Write it to current working directory.
|
||||||
|
auto redist_path = std::filesystem::current_path() / L"vc_redist.x64.exe";
|
||||||
|
auto redist_path_u8 = WStringToUTF8(redist_path.wstring());
|
||||||
|
File::IOFile redist_file;
|
||||||
|
redist_file.Open(redist_path_u8, "wb");
|
||||||
|
if (!redist_file.WriteBytes(resp->data(), resp->size()))
|
||||||
|
return false;
|
||||||
|
redist_file.Close();
|
||||||
|
|
||||||
|
Common::ScopeGuard redist_deleter([&] { File::Delete(redist_path_u8); });
|
||||||
|
|
||||||
|
// The installer also supports /passive and /quiet. We pass neither to allow the user to see and
|
||||||
|
// interact with the installer.
|
||||||
|
std::wstring cmdline = redist_path.filename().wstring() + L" /install /norestart";
|
||||||
|
STARTUPINFOW startup_info{.cb = sizeof(startup_info)};
|
||||||
|
PROCESS_INFORMATION process_info;
|
||||||
|
if (!CreateProcessW(redist_path.c_str(), cmdline.data(), nullptr, nullptr, TRUE, 0, nullptr,
|
||||||
|
nullptr, &startup_info, &process_info))
|
||||||
|
{
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
CloseHandle(process_info.hThread);
|
||||||
|
|
||||||
|
// Wait for it to run
|
||||||
|
WaitForSingleObject(process_info.hProcess, INFINITE);
|
||||||
|
DWORD exit_code;
|
||||||
|
bool has_exit_code = GetExitCodeProcess(process_info.hProcess, &exit_code);
|
||||||
|
CloseHandle(process_info.hProcess);
|
||||||
|
// NOTE: Some nonzero exit codes can still be considered success (e.g. if installation was
|
||||||
|
// bypassed because the same version already installed).
|
||||||
|
return has_exit_code && exit_code == EXIT_SUCCESS;
|
||||||
|
}
|
||||||
|
|
||||||
|
static BuildVersion CurrentOSVersion()
|
||||||
|
{
|
||||||
|
typedef DWORD(WINAPI * RtlGetVersion_t)(PRTL_OSVERSIONINFOW);
|
||||||
|
auto RtlGetVersion =
|
||||||
|
(RtlGetVersion_t)GetProcAddress(GetModuleHandle(TEXT("ntdll")), "RtlGetVersion");
|
||||||
|
RTL_OSVERSIONINFOW info{.dwOSVersionInfoSize = sizeof(info)};
|
||||||
|
RtlGetVersion(&info);
|
||||||
|
return {.major = info.dwMajorVersion, .minor = info.dwMinorVersion, .build = info.dwBuildNumber};
|
||||||
|
}
|
||||||
|
|
||||||
|
static VersionCheckResult OSVersionCheck(const BuildInfo& build_info)
|
||||||
|
{
|
||||||
|
VersionCheckResult result;
|
||||||
|
result.current_version = CurrentOSVersion();
|
||||||
|
|
||||||
|
constexpr BuildVersion WIN11_BASE{10, 0, 22000};
|
||||||
|
const char* version_name =
|
||||||
|
(result.current_version >= WIN11_BASE) ? "OSMinimumVersionWin11" : "OSMinimumVersionWin10";
|
||||||
|
result.target_version = build_info.GetVersion(version_name);
|
||||||
|
|
||||||
|
if (!result.target_version.has_value() || result.current_version >= result.target_version)
|
||||||
|
result.status = VersionCheckStatus::NothingToDo;
|
||||||
|
else
|
||||||
|
result.status = VersionCheckStatus::UpdateRequired;
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
|
||||||
|
bool VersionCheck(const BuildInfo& this_build_info, const BuildInfo& next_build_info)
|
||||||
|
{
|
||||||
|
// If the binary requires more recent OS, inform the user.
|
||||||
|
auto os_check = OSVersionCheck(next_build_info);
|
||||||
|
if (os_check.status == VersionCheckStatus::UpdateRequired)
|
||||||
|
{
|
||||||
|
UI::Error("Please update Windows in order to update Dolphin.");
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Check if application being launched needs more recent version of VC Redist. If so, download
|
||||||
|
// latest updater and execute it.
|
||||||
|
auto vc_check = VCRuntimeVersionCheck(this_build_info, next_build_info);
|
||||||
|
if (vc_check.status != VersionCheckStatus::NothingToDo)
|
||||||
|
{
|
||||||
|
// Don't bother checking status of the install itself, just check if we actually see the new
|
||||||
|
// version.
|
||||||
|
VCRuntimeUpdate(next_build_info);
|
||||||
|
vc_check = VCRuntimeVersionCheck(this_build_info, next_build_info);
|
||||||
|
if (vc_check.status == VersionCheckStatus::UpdateRequired)
|
||||||
|
{
|
||||||
|
// The update is required and the install failed for some reason.
|
||||||
|
UI::Error("Please update VC++ Runtime in order to update Dolphin.");
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
} // namespace Platform
|
|
@ -253,8 +253,8 @@ void Stop()
|
||||||
|
|
||||||
void LaunchApplication(std::string path)
|
void LaunchApplication(std::string path)
|
||||||
{
|
{
|
||||||
// Hack: Launching the updater over the explorer ensures that admin priviliges are dropped. Why?
|
// Indirectly start the application via explorer. This effectively drops admin priviliges because
|
||||||
// Ask Microsoft.
|
// explorer is running as current user.
|
||||||
ShellExecuteW(nullptr, nullptr, L"explorer.exe", UTF8ToWString(path).c_str(), nullptr, SW_SHOW);
|
ShellExecuteW(nullptr, nullptr, L"explorer.exe", UTF8ToWString(path).c_str(), nullptr, SW_SHOW);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -24,9 +24,15 @@
|
||||||
<Project>{D79392F7-06D6-4B4B-A39F-4D587C215D3A}</Project>
|
<Project>{D79392F7-06D6-4B4B-A39F-4D587C215D3A}</Project>
|
||||||
</ProjectReference>
|
</ProjectReference>
|
||||||
</ItemGroup>
|
</ItemGroup>
|
||||||
|
<ItemGroup>
|
||||||
|
<ClInclude Include="..\UpdaterCommon\Platform.h" />
|
||||||
|
<ClInclude Include="..\UpdaterCommon\UI.h" />
|
||||||
|
<ClInclude Include="..\UpdaterCommon\UpdaterCommon.h" />
|
||||||
|
</ItemGroup>
|
||||||
<ItemGroup>
|
<ItemGroup>
|
||||||
<ClCompile Include="..\UpdaterCommon\UpdaterCommon.cpp" />
|
<ClCompile Include="..\UpdaterCommon\UpdaterCommon.cpp" />
|
||||||
<ClCompile Include="Main.cpp" />
|
<ClCompile Include="Main.cpp" />
|
||||||
|
<ClCompile Include="Platform.cpp" />
|
||||||
<ClCompile Include="WinUI.cpp" />
|
<ClCompile Include="WinUI.cpp" />
|
||||||
</ItemGroup>
|
</ItemGroup>
|
||||||
<Import Project="$(ExternalsDir)cpp-optparse\exports.props" />
|
<Import Project="$(ExternalsDir)cpp-optparse\exports.props" />
|
||||||
|
|
Loading…
Reference in New Issue