From b5dfe6320dc5e1952a7be9d8eed15a7c151a30f9 Mon Sep 17 00:00:00 2001 From: Andrey Nekrasov Date: Tue, 21 Apr 2020 20:52:28 +0300 Subject: [PATCH] msi: detect and set previous installation path via a custom action (#2108) --- installer/PowerToysSetup/Product.wxs | 14 ++- .../CustomAction.cpp | 85 ++++++++++++++++--- .../CustomAction.def | 1 + .../PowerToysSetupCustomActions.vcxproj | 12 ++- .../PowerToysSetupCustomActions/stdafx.h | 14 ++- 5 files changed, 106 insertions(+), 20 deletions(-) diff --git a/installer/PowerToysSetup/Product.wxs b/installer/PowerToysSetup/Product.wxs index 13737a9660..beddb2b74e 100644 --- a/installer/PowerToysSetup/Product.wxs +++ b/installer/PowerToysSetup/Product.wxs @@ -39,6 +39,8 @@ + + @@ -71,6 +73,9 @@ + + + @@ -163,6 +168,13 @@ DllEntry="TelemetryLogRepairFailCA" /> + + @@ -233,7 +245,7 @@ - + diff --git a/installer/PowerToysSetupCustomActions/CustomAction.cpp b/installer/PowerToysSetupCustomActions/CustomAction.cpp index a27a22581c..5c03294d6c 100644 --- a/installer/PowerToysSetupCustomActions/CustomAction.cpp +++ b/installer/PowerToysSetupCustomActions/CustomAction.cpp @@ -1,17 +1,5 @@ #include "stdafx.h" -#define SECURITY_WIN32 -#include -#pragma comment(lib, "Secur32.lib") -#include - -#include -#include -#pragma comment(lib, "taskschd.lib") -#pragma comment(lib, "comsupp.lib") - -#include -#include #include using namespace std; @@ -26,6 +14,9 @@ TRACELOGGING_DEFINE_PROVIDER( const DWORD USERNAME_DOMAIN_LEN = DNLEN + UNLEN + 2; // Domain Name + '\' + User Name + '\0' const DWORD USERNAME_LEN = UNLEN + 1; // User Name + '\0' +static const wchar_t* POWERTOYS_EXE_COMPONENT = L"{A2C66D91-3485-4D00-B04D-91844E6B345B}"; +static const wchar_t* POWERTOYS_UPGRADE_CODE = L"{42B84BF7-5FBF-473B-9C8B-049DC16F7708}"; + // Creates a Scheduled Task to run at logon for the current user. // The path of the executable to run should be passed as the CustomActionData (Value). // Based on the Task Scheduler Logon Trigger Example: @@ -48,6 +39,8 @@ UINT __stdcall CreateScheduledTaskCA(MSIHANDLE hInstall) ITriggerCollection* pTriggerCollection = NULL; IRegisteredTask* pRegisteredTask = NULL; + LPWSTR wszExecutablePath = NULL; + hr = WcaInitialize(hInstall, "CreateScheduledTaskCA"); ExitOnFailure(hr, "Failed to initialize"); @@ -77,7 +70,6 @@ UINT __stdcall CreateScheduledTaskCA(MSIHANDLE hInstall) wstrTaskName += username; // Get the executable path passed to the custom action. - LPWSTR wszExecutablePath = NULL; hr = WcaGetProperty(L"CustomActionData", &wszExecutablePath); ExitOnFailure(hr, "Failed to get the executable path from CustomActionData."); @@ -571,6 +563,73 @@ LExit: return WcaFinalize(er); } +std::optional getMsiPackageInstalledPath(const wchar_t* product_upgrade_code, const wchar_t* file_component) +{ + constexpr size_t guid_length = 39; + wchar_t product_ID[guid_length]; + if (const bool found = ERROR_SUCCESS == MsiEnumRelatedProductsW(product_upgrade_code, 0, 0, product_ID); !found) + { + return std::nullopt; + } + + if (const bool installed = INSTALLSTATE_DEFAULT == MsiQueryProductStateW(product_ID); !installed) + { + return std::nullopt; + } + + DWORD buf_size = MAX_PATH; + wchar_t buf[MAX_PATH]; + if (ERROR_SUCCESS == MsiGetProductInfoW(product_ID, INSTALLPROPERTY_INSTALLLOCATION, buf, &buf_size) && buf_size) + { + return buf; + } + + DWORD package_path_size = 0; + + if (ERROR_SUCCESS != MsiGetProductInfoW(product_ID, INSTALLPROPERTY_LOCALPACKAGE, nullptr, &package_path_size)) + { + return std::nullopt; + } + std::wstring package_path(++package_path_size, L'\0'); + + if (ERROR_SUCCESS != MsiGetProductInfoW(product_ID, INSTALLPROPERTY_LOCALPACKAGE, package_path.data(), &package_path_size)) + { + return std::nullopt; + } + package_path.resize(size(package_path) - 1); // trim additional \0 which we got from MsiGetProductInfoW + + wchar_t path[256]; + DWORD path_size = 256; + MsiGetComponentPathW(product_ID, file_component, path, &path_size); + if (!path_size) + { + return std::nullopt; + } + PathCchRemoveFileSpec(path, path_size); + return path; +} + +UINT __stdcall DetectPrevInstallPathCA(MSIHANDLE hInstall) +{ + HRESULT hr = S_OK; + UINT er = ERROR_SUCCESS; + hr = WcaInitialize(hInstall, "DetectPrevInstallPathCA"); + + try + { + if (auto install_path = getMsiPackageInstalledPath(POWERTOYS_UPGRADE_CODE, POWERTOYS_EXE_COMPONENT)) + { + MsiSetPropertyW(hInstall, L"INSTALLFOLDER", install_path->data()); + } + } + catch(...) + { + + } + er = SUCCEEDED(hr) ? ERROR_SUCCESS : ERROR_INSTALL_FAILURE; + return WcaFinalize(er); +} + // DllMain - Initialize and cleanup WiX custom action utils. extern "C" BOOL WINAPI DllMain(__in HINSTANCE hInst, __in ULONG ulReason, __in LPVOID) { diff --git a/installer/PowerToysSetupCustomActions/CustomAction.def b/installer/PowerToysSetupCustomActions/CustomAction.def index ff324e5657..be33fd02d4 100644 --- a/installer/PowerToysSetupCustomActions/CustomAction.def +++ b/installer/PowerToysSetupCustomActions/CustomAction.def @@ -2,6 +2,7 @@ LIBRARY "PowerToysSetupCustomActions" EXPORTS CreateScheduledTaskCA + DetectPrevInstallPathCA RemoveScheduledTasksCA TelemetryLogInstallSuccessCA TelemetryLogInstallCancelCA diff --git a/installer/PowerToysSetupCustomActions/PowerToysSetupCustomActions.vcxproj b/installer/PowerToysSetupCustomActions/PowerToysSetupCustomActions.vcxproj index fe20fba44d..b36a7f3978 100644 --- a/installer/PowerToysSetupCustomActions/PowerToysSetupCustomActions.vcxproj +++ b/installer/PowerToysSetupCustomActions/PowerToysSetupCustomActions.vcxproj @@ -55,11 +55,13 @@ EnableFastChecks MultiThreadedDebug Use - Level3 + Level4 ProgramDatabase + stdcpplatest + true - msi.lib;dutil.lib;wcautil.lib;Version.lib;%(AdditionalDependencies) + Pathcch.lib;comsupp.lib;taskschd.lib;Secur32.lib;msi.lib;dutil.lib;wcautil.lib;Version.lib;%(AdditionalDependencies) $(WIX)sdk\$(WixPlatformToolset)\lib\x64;$(SolutionDir)\packages\WiX.3.11.2\tools\sdk\vs2017\lib\x64;%(AdditionalLibraryDirectories) CustomAction.def true @@ -76,11 +78,13 @@ MultiThreaded true Use - Level3 + Level4 ProgramDatabase + stdcpplatest + true - msi.lib;dutil.lib;wcautil.lib;Version.lib;%(AdditionalDependencies) + Pathcch.lib;comsupp.lib;taskschd.lib;Secur32.lib;msi.lib;dutil.lib;wcautil.lib;Version.lib;%(AdditionalDependencies) $(WIX)sdk\$(WixPlatformToolset)\lib\x64;$(SolutionDir)\packages\WiX.3.11.2\tools\sdk\vs2017\lib\x64;%(AdditionalLibraryDirectories) CustomAction.def true diff --git a/installer/PowerToysSetupCustomActions/stdafx.h b/installer/PowerToysSetupCustomActions/stdafx.h index 490d80ec14..91ff9d5eb3 100644 --- a/installer/PowerToysSetupCustomActions/stdafx.h +++ b/installer/PowerToysSetupCustomActions/stdafx.h @@ -1,13 +1,23 @@ #pragma once -#define WIN32_LEAN_AND_MEAN // Exclude rarely-used stuff from Windows headers +#define WIN32_LEAN_AND_MEAN // Exclude rarely-used stuff from Windows headers // Windows Header Files: #include #include #include +#include // WiX Header Files: #include +#define SECURITY_WIN32 +#include +#include -// TODO: reference additional headers your program requires here +#include +#include +#include +#include +#include +#include +#include