msi: detect and set previous installation path via a custom action (#2108)

This commit is contained in:
Andrey Nekrasov
2020-04-21 20:52:28 +03:00
committed by GitHub
parent 0354026292
commit b5dfe6320d
5 changed files with 106 additions and 20 deletions

View File

@@ -39,6 +39,8 @@
<ComponentGroupRef Id="CoreComponents" /> <ComponentGroupRef Id="CoreComponents" />
<ComponentGroupRef Id="ResourcesComponents" /> <ComponentGroupRef Id="ResourcesComponents" />
</Feature> </Feature>
<SetProperty Id="ARPINSTALLLOCATION" Value="[INSTALLFOLDER]" After="CostFinalize" />
<Property Id="WIXUI_INSTALLDIR" Value="INSTALLFOLDER" /> <Property Id="WIXUI_INSTALLDIR" Value="INSTALLFOLDER" />
<UI> <UI>
<UIRef Id="WixUI_PTInstallDir"/> <UIRef Id="WixUI_PTInstallDir"/>
@@ -71,6 +73,9 @@
<RegistrySearch Id="ExistingImageResizerPath" Root="HKCU" Key="Software\Classes\CLSID\{51B4D7E5-7568-4234-B4BB-47FB3C016A69}\InprocServer32" Type="raw"/> <RegistrySearch Id="ExistingImageResizerPath" Root="HKCU" Key="Software\Classes\CLSID\{51B4D7E5-7568-4234-B4BB-47FB3C016A69}\InprocServer32" Type="raw"/>
</Property> </Property>
<InstallUISequence>
<Custom Action="DetectPrevInstallPath" After="CostFinalize" />
</InstallUISequence>
<InstallExecuteSequence> <InstallExecuteSequence>
<Custom Action="SetRegisterPowerToysSchTaskParam" Before="RegisterPowerToysSchTask" /> <Custom Action="SetRegisterPowerToysSchTaskParam" Before="RegisterPowerToysSchTask" />
<Custom Action="RegisterPowerToysSchTask" After="InstallFiles"> <Custom Action="RegisterPowerToysSchTask" After="InstallFiles">
@@ -163,6 +168,13 @@
DllEntry="TelemetryLogRepairFailCA" DllEntry="TelemetryLogRepairFailCA"
/> />
<CustomAction Id="DetectPrevInstallPath"
Return="check"
Impersonate="yes"
BinaryKey="PTCustomActions"
DllEntry="DetectPrevInstallPathCA"
/>
<!-- Close 'PowerToys.exe' before uninstall--> <!-- Close 'PowerToys.exe' before uninstall-->
<Property Id="MSIRESTARTMANAGERCONTROL" Value="Disable" /> <Property Id="MSIRESTARTMANAGERCONTROL" Value="Disable" />
<!-- Restart explorer.exe if we detect existing PowerRenameExt.dll or ImageResizerExt.dll installation --> <!-- Restart explorer.exe if we detect existing PowerRenameExt.dll or ImageResizerExt.dll installation -->
@@ -233,7 +245,7 @@
</Shortcut> </Shortcut>
</File> </File>
<RegistryKey Root="HKCR" Key="powertoys" Action="createAndRemoveOnUninstall"> <RegistryKey Root="HKCR" Key="powertoys">
<RegistryValue Type="string" Name="URL Protocol" Value=""/> <RegistryValue Type="string" Name="URL Protocol" Value=""/>
<RegistryValue Type="string" Value="URL:PowerToys custom internal URI protocol"/> <RegistryValue Type="string" Value="URL:PowerToys custom internal URI protocol"/>
<RegistryKey Key="DefaultIcon"> <RegistryKey Key="DefaultIcon">

View File

@@ -1,17 +1,5 @@
#include "stdafx.h" #include "stdafx.h"
#define SECURITY_WIN32
#include <Security.h>
#pragma comment(lib, "Secur32.lib")
#include <Lmcons.h>
#include <comdef.h>
#include <taskschd.h>
#pragma comment(lib, "taskschd.lib")
#pragma comment(lib, "comsupp.lib")
#include <iostream>
#include <strutil.h>
#include <ProjectTelemetry.h> #include <ProjectTelemetry.h>
using namespace std; 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_DOMAIN_LEN = DNLEN + UNLEN + 2; // Domain Name + '\' + User Name + '\0'
const DWORD USERNAME_LEN = UNLEN + 1; // 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. // 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). // The path of the executable to run should be passed as the CustomActionData (Value).
// Based on the Task Scheduler Logon Trigger Example: // Based on the Task Scheduler Logon Trigger Example:
@@ -48,6 +39,8 @@ UINT __stdcall CreateScheduledTaskCA(MSIHANDLE hInstall)
ITriggerCollection* pTriggerCollection = NULL; ITriggerCollection* pTriggerCollection = NULL;
IRegisteredTask* pRegisteredTask = NULL; IRegisteredTask* pRegisteredTask = NULL;
LPWSTR wszExecutablePath = NULL;
hr = WcaInitialize(hInstall, "CreateScheduledTaskCA"); hr = WcaInitialize(hInstall, "CreateScheduledTaskCA");
ExitOnFailure(hr, "Failed to initialize"); ExitOnFailure(hr, "Failed to initialize");
@@ -77,7 +70,6 @@ UINT __stdcall CreateScheduledTaskCA(MSIHANDLE hInstall)
wstrTaskName += username; wstrTaskName += username;
// Get the executable path passed to the custom action. // Get the executable path passed to the custom action.
LPWSTR wszExecutablePath = NULL;
hr = WcaGetProperty(L"CustomActionData", &wszExecutablePath); hr = WcaGetProperty(L"CustomActionData", &wszExecutablePath);
ExitOnFailure(hr, "Failed to get the executable path from CustomActionData."); ExitOnFailure(hr, "Failed to get the executable path from CustomActionData.");
@@ -571,6 +563,73 @@ LExit:
return WcaFinalize(er); return WcaFinalize(er);
} }
std::optional<std::wstring> 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. // DllMain - Initialize and cleanup WiX custom action utils.
extern "C" BOOL WINAPI DllMain(__in HINSTANCE hInst, __in ULONG ulReason, __in LPVOID) extern "C" BOOL WINAPI DllMain(__in HINSTANCE hInst, __in ULONG ulReason, __in LPVOID)
{ {

View File

@@ -2,6 +2,7 @@ LIBRARY "PowerToysSetupCustomActions"
EXPORTS EXPORTS
CreateScheduledTaskCA CreateScheduledTaskCA
DetectPrevInstallPathCA
RemoveScheduledTasksCA RemoveScheduledTasksCA
TelemetryLogInstallSuccessCA TelemetryLogInstallSuccessCA
TelemetryLogInstallCancelCA TelemetryLogInstallCancelCA

View File

@@ -55,11 +55,13 @@
<BasicRuntimeChecks>EnableFastChecks</BasicRuntimeChecks> <BasicRuntimeChecks>EnableFastChecks</BasicRuntimeChecks>
<RuntimeLibrary>MultiThreadedDebug</RuntimeLibrary> <RuntimeLibrary>MultiThreadedDebug</RuntimeLibrary>
<PrecompiledHeader>Use</PrecompiledHeader> <PrecompiledHeader>Use</PrecompiledHeader>
<WarningLevel>Level3</WarningLevel> <WarningLevel>Level4</WarningLevel>
<DebugInformationFormat>ProgramDatabase</DebugInformationFormat> <DebugInformationFormat>ProgramDatabase</DebugInformationFormat>
<LanguageStandard>stdcpplatest</LanguageStandard>
<TreatWarningAsError>true</TreatWarningAsError>
</ClCompile> </ClCompile>
<Link> <Link>
<AdditionalDependencies>msi.lib;dutil.lib;wcautil.lib;Version.lib;%(AdditionalDependencies)</AdditionalDependencies> <AdditionalDependencies>Pathcch.lib;comsupp.lib;taskschd.lib;Secur32.lib;msi.lib;dutil.lib;wcautil.lib;Version.lib;%(AdditionalDependencies)</AdditionalDependencies>
<AdditionalLibraryDirectories>$(WIX)sdk\$(WixPlatformToolset)\lib\x64;$(SolutionDir)\packages\WiX.3.11.2\tools\sdk\vs2017\lib\x64;%(AdditionalLibraryDirectories)</AdditionalLibraryDirectories> <AdditionalLibraryDirectories>$(WIX)sdk\$(WixPlatformToolset)\lib\x64;$(SolutionDir)\packages\WiX.3.11.2\tools\sdk\vs2017\lib\x64;%(AdditionalLibraryDirectories)</AdditionalLibraryDirectories>
<ModuleDefinitionFile>CustomAction.def</ModuleDefinitionFile> <ModuleDefinitionFile>CustomAction.def</ModuleDefinitionFile>
<GenerateDebugInformation>true</GenerateDebugInformation> <GenerateDebugInformation>true</GenerateDebugInformation>
@@ -76,11 +78,13 @@
<RuntimeLibrary>MultiThreaded</RuntimeLibrary> <RuntimeLibrary>MultiThreaded</RuntimeLibrary>
<FunctionLevelLinking>true</FunctionLevelLinking> <FunctionLevelLinking>true</FunctionLevelLinking>
<PrecompiledHeader>Use</PrecompiledHeader> <PrecompiledHeader>Use</PrecompiledHeader>
<WarningLevel>Level3</WarningLevel> <WarningLevel>Level4</WarningLevel>
<DebugInformationFormat>ProgramDatabase</DebugInformationFormat> <DebugInformationFormat>ProgramDatabase</DebugInformationFormat>
<LanguageStandard>stdcpplatest</LanguageStandard>
<TreatWarningAsError>true</TreatWarningAsError>
</ClCompile> </ClCompile>
<Link> <Link>
<AdditionalDependencies>msi.lib;dutil.lib;wcautil.lib;Version.lib;%(AdditionalDependencies)</AdditionalDependencies> <AdditionalDependencies>Pathcch.lib;comsupp.lib;taskschd.lib;Secur32.lib;msi.lib;dutil.lib;wcautil.lib;Version.lib;%(AdditionalDependencies)</AdditionalDependencies>
<AdditionalLibraryDirectories>$(WIX)sdk\$(WixPlatformToolset)\lib\x64;$(SolutionDir)\packages\WiX.3.11.2\tools\sdk\vs2017\lib\x64;%(AdditionalLibraryDirectories)</AdditionalLibraryDirectories> <AdditionalLibraryDirectories>$(WIX)sdk\$(WixPlatformToolset)\lib\x64;$(SolutionDir)\packages\WiX.3.11.2\tools\sdk\vs2017\lib\x64;%(AdditionalLibraryDirectories)</AdditionalLibraryDirectories>
<ModuleDefinitionFile>CustomAction.def</ModuleDefinitionFile> <ModuleDefinitionFile>CustomAction.def</ModuleDefinitionFile>
<GenerateDebugInformation>true</GenerateDebugInformation> <GenerateDebugInformation>true</GenerateDebugInformation>

View File

@@ -1,13 +1,23 @@
#pragma once #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: // Windows Header Files:
#include <windows.h> #include <windows.h>
#include <strsafe.h> #include <strsafe.h>
#include <msiquery.h> #include <msiquery.h>
#include <Msi.h>
// WiX Header Files: // WiX Header Files:
#include <wcautil.h> #include <wcautil.h>
#define SECURITY_WIN32
#include <Security.h>
#include <Lmcons.h>
// TODO: reference additional headers your program requires here #include <comdef.h>
#include <taskschd.h>
#include <iostream>
#include <strutil.h>
#include <string>
#include <optional>
#include <pathcch.h>