mirror of
https://github.com/microsoft/PowerToys.git
synced 2025-01-18 22:43:31 +08:00
wix: install dotnet 3 after installation if needed (#2775)
This commit is contained in:
parent
9f724221fa
commit
73c6cbb562
@ -47,8 +47,8 @@
|
||||
<UIRef Id="WixUI_PTInstallDir"/>
|
||||
<Publish Dialog="ExitDialog"
|
||||
Control="Finish"
|
||||
Event="DoAction"
|
||||
Value="LaunchApplication">WIXUI_EXITDIALOGOPTIONALCHECKBOX = 1 and NOT Installed</Publish>
|
||||
Event="EndDialog"
|
||||
Value="Return">NOT Installed</Publish>
|
||||
<Publish Dialog="MaintenanceTypeDlg" Control="RemoveButton" Property="_REMOVE_ALL" Value="Yes">1</Publish>
|
||||
<Publish Dialog="UserExit" Control="Finish" Event="DoAction" Value="TelemetryLogInstallCancel">NOT Installed</Publish>
|
||||
<Publish Dialog="FatalError" Control="Finish" Event="DoAction" Value="TelemetryLogInstallFail">NOT Installed</Publish>
|
||||
@ -62,10 +62,7 @@
|
||||
<WixVariable Id="WixUILicenseRtf" Value="$(var.RepoDir)\installer\License.rtf" />
|
||||
<Property Id="INSTALLSTARTMENUSHORTCUT" Value="1"/>
|
||||
<Property Id="CREATESCHEDULEDTASK" Value="1"/>
|
||||
<Property Id="WIXUI_EXITDIALOGOPTIONALCHECKBOX" Value="1"/>
|
||||
<Property Id="WIXUI_EXITDIALOGOPTIONALCHECKBOXTEXT" Value="Launch PowerToys" />
|
||||
<Property Id="WixShellExecTarget" Value="[#PowerToys.exe]" />
|
||||
<CustomAction Id="LaunchApplication" BinaryKey="WixCA" DllEntry="WixShellExec" Impersonate="yes" />
|
||||
<Property Id="WixShellExecTarget" Value="[#action_runner.exe]" />
|
||||
|
||||
<Property Id ="EXISTINGPOWERRENAMEEXTPATH">
|
||||
<RegistrySearch Id="ExistingExtPath" Root="HKCR" Key="CLSID\{0440049F-D1DC-4E46-B27B-98393D79486B}\InprocServer32" Type="raw"/>
|
||||
@ -90,8 +87,21 @@
|
||||
<Custom Action="TelemetryLogUninstallSuccess" After="InstallFinalize">
|
||||
Installed and (NOT UPGRADINGPRODUCTCODE) AND (REMOVE="ALL")
|
||||
</Custom>
|
||||
|
||||
<Custom Action="InstallDotNet" After="InstallFinalize">
|
||||
NOT Installed
|
||||
</Custom>
|
||||
|
||||
</InstallExecuteSequence>
|
||||
|
||||
<CustomAction
|
||||
Id="InstallDotNet"
|
||||
FileKey="action_runner.exe"
|
||||
ExeCommand="-install_dotnet"
|
||||
Return="asyncNoWait"
|
||||
Execute="immediate"
|
||||
Impersonate="no" />
|
||||
|
||||
<CustomAction Id="SetRegisterPowerToysSchTaskParam"
|
||||
Property="RegisterPowerToysSchTask"
|
||||
Value="[#PowerToys.exe]" />
|
||||
|
@ -14,6 +14,10 @@
|
||||
#include "../runner/tray_icon.h"
|
||||
#include "../runner/action_runner_utils.h"
|
||||
|
||||
#include "resource.h"
|
||||
|
||||
extern "C" IMAGE_DOS_HEADER __ImageBase;
|
||||
|
||||
int uninstall_msi_action()
|
||||
{
|
||||
const auto package_path = updating::get_msi_package_path();
|
||||
@ -122,6 +126,56 @@ bool install_new_version_stage_2(std::wstring_view installer_path, std::wstring_
|
||||
return true;
|
||||
}
|
||||
|
||||
bool dotnet_is_installed()
|
||||
{
|
||||
auto runtimes = exec_and_read_output(LR"(dotnet --list-runtimes)");
|
||||
if (!runtimes)
|
||||
{
|
||||
return false;
|
||||
}
|
||||
const char DESKTOP_DOTNET_RUNTIME_STRING[] = "Microsoft.WindowsDesktop.App 3.1.";
|
||||
return runtimes->find(DESKTOP_DOTNET_RUNTIME_STRING) != std::string::npos;
|
||||
}
|
||||
|
||||
bool install_dotnet()
|
||||
{
|
||||
const wchar_t DOTNET_DESKTOP_DOWNLOAD_LINK[] = L"https://download.visualstudio.microsoft.com/download/pr/a1510e74-b31a-4434-b8a0-8074ff31fb3f/b7de8ecba4a14d8312551cfdc745dea1/windowsdesktop-runtime-3.1.0-win-x64.exe";
|
||||
const wchar_t DOTNET_DESKTOP_FILENAME[] = L"windowsdesktop-runtime-3.1.0-win-x64.exe";
|
||||
|
||||
auto dotnet_download_path = fs::temp_directory_path() / DOTNET_DESKTOP_FILENAME;
|
||||
winrt::Windows::Foundation::Uri download_link{ DOTNET_DESKTOP_DOWNLOAD_LINK };
|
||||
|
||||
const size_t max_attempts = 3;
|
||||
bool download_success = false;
|
||||
for (size_t i = 0; i < max_attempts; ++i)
|
||||
{
|
||||
try
|
||||
{
|
||||
updating::try_download_file(dotnet_download_path, download_link).wait();
|
||||
download_success = true;
|
||||
break;
|
||||
}
|
||||
catch (...)
|
||||
{
|
||||
// couldn't download
|
||||
}
|
||||
}
|
||||
if (!download_success)
|
||||
{
|
||||
MessageBoxW(nullptr,
|
||||
GET_RESOURCE_STRING(IDS_DOTNET_CORE_DOWNLOAD_FAILURE).c_str(),
|
||||
GET_RESOURCE_STRING(IDS_DOTNET_CORE_DOWNLOAD_FAILURE_TITLE).c_str(),
|
||||
MB_OK | MB_ICONERROR);
|
||||
return false;
|
||||
}
|
||||
SHELLEXECUTEINFOW sei{ sizeof(sei) };
|
||||
sei.fMask = { SEE_MASK_NOASYNC };
|
||||
sei.lpFile = dotnet_download_path.c_str();
|
||||
sei.nShow = SW_SHOWNORMAL;
|
||||
sei.lpParameters = L"/install /passive";
|
||||
return ShellExecuteExW(&sei) == TRUE;
|
||||
}
|
||||
|
||||
int WINAPI WinMain(HINSTANCE, HINSTANCE, LPSTR, int)
|
||||
{
|
||||
int nArgs = 0;
|
||||
@ -132,7 +186,15 @@ int WINAPI WinMain(HINSTANCE, HINSTANCE, LPSTR, int)
|
||||
}
|
||||
std::wstring_view action{ args[1] };
|
||||
|
||||
if (action == L"-uninstall_msi")
|
||||
if (action == L"-install_dotnet")
|
||||
{
|
||||
if (dotnet_is_installed())
|
||||
{
|
||||
return 0;
|
||||
}
|
||||
return !install_dotnet();
|
||||
}
|
||||
else if (action == L"-uninstall_msi")
|
||||
{
|
||||
return uninstall_msi_action();
|
||||
}
|
||||
|
43
src/action_runner/action_runner.rc
Normal file
43
src/action_runner/action_runner.rc
Normal file
@ -0,0 +1,43 @@
|
||||
// Microsoft Visual C++ generated resource script.
|
||||
//
|
||||
#include "resource.h"
|
||||
#include "../common/version.h"
|
||||
|
||||
STRINGTABLE
|
||||
BEGIN
|
||||
IDS_DOTNET_CORE_DOWNLOAD_FAILURE "Couldn't download .NET Core Desktop Runtime 3.1.3, please install it manually."
|
||||
IDS_DOTNET_CORE_DOWNLOAD_FAILURE_TITLE "PowerToys installation error"
|
||||
END
|
||||
|
||||
1 VERSIONINFO
|
||||
FILEVERSION FILE_VERSION
|
||||
PRODUCTVERSION PRODUCT_VERSION
|
||||
FILEFLAGSMASK 0x3fL
|
||||
#ifdef _DEBUG
|
||||
FILEFLAGS 0x1L
|
||||
#else
|
||||
FILEFLAGS 0x0L
|
||||
#endif
|
||||
FILEOS 0x40004L
|
||||
FILETYPE 0x1L
|
||||
FILESUBTYPE 0x0L
|
||||
BEGIN
|
||||
BLOCK "StringFileInfo"
|
||||
BEGIN
|
||||
BLOCK "040904b0"
|
||||
BEGIN
|
||||
VALUE "CompanyName", COMPANY_NAME
|
||||
VALUE "FileDescription", "PowerToys Action Runner"
|
||||
VALUE "FileVersion", FILE_VERSION_STRING
|
||||
VALUE "InternalName", "PowerToys Action Runner"
|
||||
VALUE "LegalCopyright", COPYRIGHT_NOTE
|
||||
VALUE "OriginalFilename", "action_runner.exe"
|
||||
VALUE "ProductName", "PowerToys"
|
||||
VALUE "ProductVersion", PRODUCT_VERSION_STRING
|
||||
END
|
||||
END
|
||||
BLOCK "VarFileInfo"
|
||||
BEGIN
|
||||
VALUE "Translation", 0x409, 1200
|
||||
END
|
||||
END
|
@ -160,10 +160,14 @@
|
||||
</ItemGroup>
|
||||
<ItemGroup>
|
||||
<ClInclude Include="..\runner\updating.h" />
|
||||
<ClInclude Include="resource.h" />
|
||||
</ItemGroup>
|
||||
<ItemGroup>
|
||||
<None Include="packages.config" />
|
||||
</ItemGroup>
|
||||
<ItemGroup>
|
||||
<ResourceCompile Include="action_runner.rc" />
|
||||
</ItemGroup>
|
||||
<Import Project="$(VCTargetsPath)\Microsoft.Cpp.targets" />
|
||||
<ImportGroup Label="ExtensionTargets">
|
||||
<Import Project="..\..\packages\Microsoft.Windows.CppWinRT.2.0.200316.3\build\native\Microsoft.Windows.CppWinRT.targets" Condition="Exists('..\..\packages\Microsoft.Windows.CppWinRT.2.0.200316.3\build\native\Microsoft.Windows.CppWinRT.targets')" />
|
||||
|
3
src/action_runner/resource.h
Normal file
3
src/action_runner/resource.h
Normal file
@ -0,0 +1,3 @@
|
||||
#define IDS_DOTNET_CORE_DOWNLOAD_FAILURE 101
|
||||
#define IDS_DOTNET_CORE_DOWNLOAD_FAILURE_TITLE 102
|
||||
|
@ -6,6 +6,8 @@
|
||||
#include <sddl.h>
|
||||
#include "version.h"
|
||||
|
||||
#include <wil/resource.h>
|
||||
|
||||
#pragma comment(lib, "advapi32.lib")
|
||||
#pragma comment(lib, "shlwapi.lib")
|
||||
|
||||
@ -735,3 +737,64 @@ bool find_app_name_in_path(const std::wstring& where, const std::vector<std::wst
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
std::optional<std::string> exec_and_read_output(const std::wstring_view command, const DWORD timeout)
|
||||
{
|
||||
SECURITY_ATTRIBUTES saAttr{ sizeof(saAttr) };
|
||||
saAttr.bInheritHandle = true;
|
||||
|
||||
wil::unique_handle childStdoutRead;
|
||||
wil::unique_handle childStdoutWrite;
|
||||
if (!CreatePipe(&childStdoutRead, &childStdoutWrite, &saAttr, 0))
|
||||
{
|
||||
return std::nullopt;
|
||||
}
|
||||
|
||||
if (!SetHandleInformation(childStdoutRead.get(), HANDLE_FLAG_INHERIT, 0))
|
||||
{
|
||||
return std::nullopt;
|
||||
}
|
||||
|
||||
PROCESS_INFORMATION piProcInfo{};
|
||||
STARTUPINFOW siStartInfo{ sizeof(siStartInfo) };
|
||||
|
||||
siStartInfo.hStdError = childStdoutWrite.get();
|
||||
siStartInfo.hStdOutput = childStdoutWrite.get();
|
||||
siStartInfo.dwFlags |= STARTF_USESTDHANDLES | STARTF_USESHOWWINDOW;
|
||||
siStartInfo.wShowWindow = SW_HIDE;
|
||||
|
||||
std::wstring cmdLine{ command };
|
||||
if (!CreateProcessW(nullptr,
|
||||
cmdLine.data(),
|
||||
nullptr,
|
||||
nullptr,
|
||||
true,
|
||||
NORMAL_PRIORITY_CLASS | CREATE_NEW_CONSOLE,
|
||||
nullptr,
|
||||
nullptr,
|
||||
&siStartInfo,
|
||||
&piProcInfo))
|
||||
{
|
||||
return std::nullopt;
|
||||
}
|
||||
|
||||
WaitForSingleObject(piProcInfo.hProcess, timeout);
|
||||
|
||||
childStdoutWrite.reset();
|
||||
CloseHandle(piProcInfo.hThread);
|
||||
|
||||
std::string childOutput;
|
||||
for (;;)
|
||||
{
|
||||
char buffer[4096];
|
||||
DWORD gotBytes = 0;
|
||||
if (!ReadFile(childStdoutRead.get(), buffer, sizeof(buffer), &gotBytes, nullptr) || !gotBytes)
|
||||
{
|
||||
break;
|
||||
}
|
||||
childOutput += std::string_view{ buffer, gotBytes };
|
||||
}
|
||||
|
||||
CloseHandle(piProcInfo.hProcess);
|
||||
return childOutput;
|
||||
}
|
@ -99,6 +99,8 @@ std::wstring get_resource_string(UINT resource_id, HINSTANCE instance, const wch
|
||||
// is added to the .cpp file.
|
||||
#define GET_RESOURCE_STRING(resource_id) get_resource_string(resource_id, reinterpret_cast<HINSTANCE>(&__ImageBase), L#resource_id)
|
||||
|
||||
std::optional<std::string> exec_and_read_output(const std::wstring_view command, const DWORD timeout = INFINITE);
|
||||
|
||||
// Helper class for various COM-related APIs, e.g working with security descriptors
|
||||
template<typename T>
|
||||
struct typed_storage
|
||||
|
@ -209,7 +209,7 @@ namespace updating
|
||||
return { std::move(path_str) };
|
||||
}
|
||||
|
||||
std::future<void> attempt_to_download_installer(const std::filesystem::path& destination, const winrt::Windows::Foundation::Uri& url)
|
||||
std::future<void> try_download_file(const std::filesystem::path& destination, const winrt::Windows::Foundation::Uri& url)
|
||||
{
|
||||
namespace storage = winrt::Windows::Storage;
|
||||
|
||||
@ -218,6 +218,7 @@ namespace updating
|
||||
(void)response.EnsureSuccessStatusCode();
|
||||
auto msi_installer_file_stream = co_await storage::Streams::FileRandomAccessStream::OpenAsync(destination.c_str(), storage::FileAccessMode::ReadWrite, storage::StorageOpenOptions::AllowReadersAndWriters, storage::Streams::FileOpenDisposition::CreateAlways);
|
||||
co_await response.Content().WriteToStreamAsync(msi_installer_file_stream);
|
||||
msi_installer_file_stream.Close();
|
||||
}
|
||||
|
||||
std::future<void> try_autoupdate(const bool download_updates_automatically)
|
||||
@ -244,7 +245,7 @@ namespace updating
|
||||
{
|
||||
try
|
||||
{
|
||||
co_await attempt_to_download_installer(installer_download_dst, new_version->msi_download_url);
|
||||
co_await try_download_file(installer_download_dst, new_version->msi_download_url);
|
||||
download_success = true;
|
||||
break;
|
||||
}
|
||||
|
@ -9,6 +9,8 @@
|
||||
|
||||
namespace updating
|
||||
{
|
||||
std::future<void> try_download_file(const std::filesystem::path& destination, const winrt::Windows::Foundation::Uri& url);
|
||||
|
||||
std::wstring get_msi_package_path();
|
||||
bool uninstall_msi_version(const std::wstring& package_path);
|
||||
bool offer_msi_uninstallation();
|
||||
|
Loading…
Reference in New Issue
Block a user