From bad0fa8af111ee1b6d97dfc6c682cd1012c2c06f Mon Sep 17 00:00:00 2001 From: "bob@firegiant.com" Date: Thu, 23 Jul 2015 14:01:42 -0400 Subject: [PATCH 01/17] Expand the checks used to determine the RebootPending property value. Add some helper functions to DUtil to help with that. --- src/burn/engine/variable.cpp | 40 ++++++++++++++++++++++++ src/libs/dutil/inc/regutil.h | 14 ++++++++- src/libs/dutil/regutil.cpp | 60 ++++++++++++++++++++++++++++++++++-- 3 files changed, 111 insertions(+), 3 deletions(-) diff --git a/src/burn/engine/variable.cpp b/src/burn/engine/variable.cpp index 3ad4624aa..d6a55f81a 100644 --- a/src/burn/engine/variable.cpp +++ b/src/burn/engine/variable.cpp @@ -2027,6 +2027,9 @@ static HRESULT InitializeVariableRebootPending( HRESULT hr = S_OK; BOOL fRebootPending = FALSE; BOOL fComInitialized = FALSE; + DWORD dwValue; + LPWSTR sczValue = NULL; + HKEY hk = NULL; // Do a best effort to ask WU if a reboot is required. If anything goes // wrong then let's pretend a reboot is not required. @@ -2043,6 +2046,40 @@ static HRESULT InitializeVariableRebootPending( } } + hr = RegKeyReadNumber(HKEY_LOCAL_MACHINE, L"SOFTWARE\\Microsoft\\ServerManager", L"CurrentRebootAttempts", TRUE, &dwValue); + fRebootPending |= SUCCEEDED(hr) && 0 < dwValue; + + hr = RegKeyReadNumber(HKEY_LOCAL_MACHINE, L"SOFTWARE\\Microsoft\\Updates", L"UpdateExeVolatile", TRUE, &dwValue); + fRebootPending |= SUCCEEDED(hr) && 0 < dwValue; + + hr = RegOpen(HKEY_LOCAL_MACHINE, L"SOFTWARE\\Microsoft\\Windows\\CurrentVersion\\Component Based Servicing\\RebootPending", KEY_READ | KEY_WOW64_64KEY, &hk); + fRebootPending |= SUCCEEDED(hr); + ReleaseRegKey(hk); + + hr = RegOpen(HKEY_LOCAL_MACHINE, L"SOFTWARE\\Microsoft\\Windows\\CurrentVersion\\Component Based Servicing\\RebootInProgress", KEY_READ | KEY_WOW64_64KEY, &hk); + fRebootPending |= SUCCEEDED(hr); + ReleaseRegKey(hk); + + hr = RegOpen(HKEY_LOCAL_MACHINE, L"SOFTWARE\\Microsoft\\Windows\\CurrentVersion\\WindowsUpdate\\Auto Update\\RebootRequired", KEY_READ | KEY_WOW64_64KEY, &hk); + fRebootPending |= SUCCEEDED(hr); + ReleaseRegKey(hk); + + hr = RegKeyReadNumber(HKEY_LOCAL_MACHINE, L"SOFTWARE\\Microsoft\\Windows\\CurrentVersion\\WindowsUpdate\\Auto Update", L"AUState ", TRUE, &dwValue); + fRebootPending |= SUCCEEDED(hr) && 8 == dwValue; + + fRebootPending |= RegValueExists(HKEY_LOCAL_MACHINE, L"SYSTEM\\CurrentControlSet\\Control\\Session Manager", L"PendingFileRenameOperations", TRUE); + + fRebootPending |= RegValueExists(HKEY_LOCAL_MACHINE, L"SYSTEM\\CurrentControlSet\\Control\\Session Manager", L"PendingFileRenameOperations2", TRUE); + + hr = RegOpen(HKEY_LOCAL_MACHINE, L"SYSTEM\\CurrentControlSet\\Control\\Session Manager\\FileRenameOperations", KEY_READ | KEY_WOW64_64KEY, &hk); + if (SUCCEEDED(hr)) + { + DWORD cSubKeys = 0; + DWORD cValues = 0; + hr = RegQueryKey(hk, &cSubKeys, &cValues); + fRebootPending |= SUCCEEDED(hr) && (0 < cSubKeys || 0 < cValues); + } + hr = BVariantSetNumeric(pValue, fRebootPending); ExitOnFailure(hr, "Failed to set reboot pending variant value."); @@ -2052,6 +2089,9 @@ static HRESULT InitializeVariableRebootPending( ::CoUninitialize(); } + ReleaseRegKey(hk); + ReleaseStr(sczValue); + return hr; } diff --git a/src/libs/dutil/inc/regutil.h b/src/libs/dutil/inc/regutil.h index 897b9d033..91a4801af 100644 --- a/src/libs/dutil/inc/regutil.h +++ b/src/libs/dutil/inc/regutil.h @@ -226,8 +226,20 @@ HRESULT DAPI RegQueryKey( __out_opt DWORD* pcSubKeys, __out_opt DWORD* pcValues ); +HRESULT DAPI RegKeyReadNumber( + __in HKEY hk, + __in_z LPCWSTR wzSubKey, + __in_z_opt LPCWSTR wzName, + __in BOOL f64Bit, + __out DWORD* pdwValue + ); +BOOL DAPI RegValueExists( + __in HKEY hk, + __in_z LPCWSTR wzSubKey, + __in_z_opt LPCWSTR wzName, + __in BOOL f64Bit + ); #ifdef __cplusplus } #endif - diff --git a/src/libs/dutil/regutil.cpp b/src/libs/dutil/regutil.cpp index 746ea9947..6448b0882 100644 --- a/src/libs/dutil/regutil.cpp +++ b/src/libs/dutil/regutil.cpp @@ -261,7 +261,7 @@ extern "C" HRESULT DAPI RegDelete( /******************************************************************** - RegKeyEnum - enumerates a registry key. + RegKeyEnum - enumerates child registry keys. *********************************************************************/ extern "C" HRESULT DAPI RegKeyEnum( @@ -318,7 +318,7 @@ extern "C" HRESULT DAPI RegKeyEnum( /******************************************************************** - RegValueEnum - enumerates a registry value. + RegValueEnum - enumerates registry values. *********************************************************************/ HRESULT DAPI RegValueEnum( @@ -924,3 +924,59 @@ extern "C" HRESULT DAPI RegQueryKey( LExit: return hr; } + +/******************************************************************** +RegKeyReadNumber - reads a DWORD registry key value as a number from +a specified subkey. + +*********************************************************************/ +extern "C" HRESULT DAPI RegKeyReadNumber( + __in HKEY hk, + __in_z LPCWSTR wzSubKey, + __in_z_opt LPCWSTR wzName, + __in BOOL f64Bit, + __out DWORD* pdwValue + ) +{ + HRESULT hr = S_OK; + HKEY hkKey = NULL; + + hr = RegOpen(hk, wzSubKey, KEY_READ | f64Bit ? KEY_WOW64_64KEY : 0, &hkKey); + ExitOnFailure1(hr, "Failed to open key: %ls", wzSubKey); + + hr = RegReadNumber(hkKey, wzName, pdwValue); + ExitOnFailure2(hr, "Failed to read value: %ls/@%ls", wzSubKey, wzName); + +LExit: + ReleaseRegKey(hkKey); + + return hr; +} + +/******************************************************************** +RegValueExists - determines whether a named value exists in a +specified subkey. + +*********************************************************************/ +extern "C" BOOL DAPI RegValueExists( + __in HKEY hk, + __in_z LPCWSTR wzSubKey, + __in_z_opt LPCWSTR wzName, + __in BOOL f64Bit + ) +{ + HRESULT hr = S_OK; + HKEY hkKey = NULL; + DWORD dwType = 0; + + hr = RegOpen(hk, wzSubKey, KEY_READ | f64Bit ? KEY_WOW64_64KEY : 0, &hkKey); + ExitOnFailure1(hr, "Failed to open key: %ls", wzSubKey); + + hr = RegGetType(hkKey, wzName, &dwType); + ExitOnFailure2(hr, "Failed to read value type: %ls/@%ls", wzSubKey, wzName); + +LExit: + ReleaseRegKey(hkKey); + + return SUCCEEDED(hr); +} From 700f72cf9b6f039a1bf88d667b1eb4f97819b2d2 Mon Sep 17 00:00:00 2001 From: "bob@firegiant.com" Date: Thu, 23 Jul 2015 17:27:04 -0400 Subject: [PATCH 02/17] Review feedback --- src/burn/engine/variable.cpp | 74 +++++++++++++++++++++--------------- 1 file changed, 44 insertions(+), 30 deletions(-) diff --git a/src/burn/engine/variable.cpp b/src/burn/engine/variable.cpp index d6a55f81a..ae7267d1d 100644 --- a/src/burn/engine/variable.cpp +++ b/src/burn/engine/variable.cpp @@ -2028,7 +2028,6 @@ static HRESULT InitializeVariableRebootPending( BOOL fRebootPending = FALSE; BOOL fComInitialized = FALSE; DWORD dwValue; - LPWSTR sczValue = NULL; HKEY hk = NULL; // Do a best effort to ask WU if a reboot is required. If anything goes @@ -2046,38 +2045,54 @@ static HRESULT InitializeVariableRebootPending( } } - hr = RegKeyReadNumber(HKEY_LOCAL_MACHINE, L"SOFTWARE\\Microsoft\\ServerManager", L"CurrentRebootAttempts", TRUE, &dwValue); - fRebootPending |= SUCCEEDED(hr) && 0 < dwValue; - - hr = RegKeyReadNumber(HKEY_LOCAL_MACHINE, L"SOFTWARE\\Microsoft\\Updates", L"UpdateExeVolatile", TRUE, &dwValue); - fRebootPending |= SUCCEEDED(hr) && 0 < dwValue; - - hr = RegOpen(HKEY_LOCAL_MACHINE, L"SOFTWARE\\Microsoft\\Windows\\CurrentVersion\\Component Based Servicing\\RebootPending", KEY_READ | KEY_WOW64_64KEY, &hk); - fRebootPending |= SUCCEEDED(hr); - ReleaseRegKey(hk); - - hr = RegOpen(HKEY_LOCAL_MACHINE, L"SOFTWARE\\Microsoft\\Windows\\CurrentVersion\\Component Based Servicing\\RebootInProgress", KEY_READ | KEY_WOW64_64KEY, &hk); - fRebootPending |= SUCCEEDED(hr); - ReleaseRegKey(hk); - - hr = RegOpen(HKEY_LOCAL_MACHINE, L"SOFTWARE\\Microsoft\\Windows\\CurrentVersion\\WindowsUpdate\\Auto Update\\RebootRequired", KEY_READ | KEY_WOW64_64KEY, &hk); - fRebootPending |= SUCCEEDED(hr); - ReleaseRegKey(hk); + if (!fRebootPending) + { + hr = RegKeyReadNumber(HKEY_LOCAL_MACHINE, L"SOFTWARE\\Microsoft\\ServerManager", L"CurrentRebootAttempts", TRUE, &dwValue); + fRebootPending = SUCCEEDED(hr) && 0 < dwValue; - hr = RegKeyReadNumber(HKEY_LOCAL_MACHINE, L"SOFTWARE\\Microsoft\\Windows\\CurrentVersion\\WindowsUpdate\\Auto Update", L"AUState ", TRUE, &dwValue); - fRebootPending |= SUCCEEDED(hr) && 8 == dwValue; + if (!fRebootPending) + { + hr = RegKeyReadNumber(HKEY_LOCAL_MACHINE, L"SOFTWARE\\Microsoft\\Updates", L"UpdateExeVolatile", TRUE, &dwValue); + fRebootPending = SUCCEEDED(hr) && 0 < dwValue; - fRebootPending |= RegValueExists(HKEY_LOCAL_MACHINE, L"SYSTEM\\CurrentControlSet\\Control\\Session Manager", L"PendingFileRenameOperations", TRUE); + if (!fRebootPending) + { + fRebootPending = RegValueExists(HKEY_LOCAL_MACHINE, L"SOFTWARE\\Microsoft\\Windows\\CurrentVersion\\Component Based Servicing\\RebootPending", NULL, TRUE); - fRebootPending |= RegValueExists(HKEY_LOCAL_MACHINE, L"SYSTEM\\CurrentControlSet\\Control\\Session Manager", L"PendingFileRenameOperations2", TRUE); + if (!fRebootPending) + { + fRebootPending = RegValueExists(HKEY_LOCAL_MACHINE, L"SOFTWARE\\Microsoft\\Windows\\CurrentVersion\\Component Based Servicing\\RebootInProgress", NULL, TRUE); - hr = RegOpen(HKEY_LOCAL_MACHINE, L"SYSTEM\\CurrentControlSet\\Control\\Session Manager\\FileRenameOperations", KEY_READ | KEY_WOW64_64KEY, &hk); - if (SUCCEEDED(hr)) - { - DWORD cSubKeys = 0; - DWORD cValues = 0; - hr = RegQueryKey(hk, &cSubKeys, &cValues); - fRebootPending |= SUCCEEDED(hr) && (0 < cSubKeys || 0 < cValues); + if (!fRebootPending) + { + hr = RegKeyReadNumber(HKEY_LOCAL_MACHINE, L"SOFTWARE\\Microsoft\\Windows\\CurrentVersion\\WindowsUpdate\\Auto Update", L"AUState", TRUE, &dwValue); + fRebootPending = SUCCEEDED(hr) && 8 == dwValue; + + if (!fRebootPending) + { + fRebootPending = RegValueExists(HKEY_LOCAL_MACHINE, L"SYSTEM\\CurrentControlSet\\Control\\Session Manager", L"PendingFileRenameOperations", TRUE); + + if (!fRebootPending) + { + fRebootPending = RegValueExists(HKEY_LOCAL_MACHINE, L"SYSTEM\\CurrentControlSet\\Control\\Session Manager", L"PendingFileRenameOperations2", TRUE); + + if (!fRebootPending) + { + hr = RegOpen(HKEY_LOCAL_MACHINE, L"SYSTEM\\CurrentControlSet\\Control\\Session Manager\\FileRenameOperations", KEY_READ | KEY_WOW64_64KEY, &hk); + if (SUCCEEDED(hr)) + { + DWORD cSubKeys = 0; + DWORD cValues = 0; + hr = RegQueryKey(hk, &cSubKeys, &cValues); + fRebootPending = SUCCEEDED(hr) && (0 < cSubKeys || 0 < cValues); + } + } + } + } + } + } + } + } } hr = BVariantSetNumeric(pValue, fRebootPending); @@ -2090,7 +2105,6 @@ static HRESULT InitializeVariableRebootPending( } ReleaseRegKey(hk); - ReleaseStr(sczValue); return hr; } From 2f21b3315adb33d1db65787b9e7180aef221d8ac Mon Sep 17 00:00:00 2001 From: Bob Arnson Date: Mon, 21 Mar 2016 16:31:40 -0400 Subject: [PATCH 03/17] Add BOOTSTRAPPER_ACTION_STATE_MEND and BOOTSTRAPPER_REQUEST_STATE_MEND to support repairs with "O" REINSTALLMODE. --- src/burn/engine/dependency.cpp | 1 + src/burn/engine/logging.cpp | 4 ++++ src/burn/engine/msiengine.cpp | 12 ++++++++++-- src/burn/inc/IBootstrapperEngine.h | 2 ++ 4 files changed, 17 insertions(+), 2 deletions(-) diff --git a/src/burn/engine/dependency.cpp b/src/burn/engine/dependency.cpp index b7591db2b..7707fa2ec 100644 --- a/src/burn/engine/dependency.cpp +++ b/src/burn/engine/dependency.cpp @@ -924,6 +924,7 @@ static void CalculateDependencyActionStates( break; case BOOTSTRAPPER_ACTION_STATE_INSTALL: __fallthrough; case BOOTSTRAPPER_ACTION_STATE_MODIFY: __fallthrough; + case BOOTSTRAPPER_ACTION_STATE_MEND: __fallthrough; case BOOTSTRAPPER_ACTION_STATE_REPAIR: __fallthrough; case BOOTSTRAPPER_ACTION_STATE_MINOR_UPGRADE: __fallthrough; case BOOTSTRAPPER_ACTION_STATE_MAJOR_UPGRADE: __fallthrough; diff --git a/src/burn/engine/logging.cpp b/src/burn/engine/logging.cpp index 76287216c..971e7c492 100644 --- a/src/burn/engine/logging.cpp +++ b/src/burn/engine/logging.cpp @@ -282,6 +282,8 @@ extern "C" LPCSTR LoggingActionStateToString( return "AdminInstall"; case BOOTSTRAPPER_ACTION_STATE_MODIFY: return "Modify"; + case BOOTSTRAPPER_ACTION_STATE_MEND: + return "Mend"; case BOOTSTRAPPER_ACTION_STATE_REPAIR: return "Repair"; case BOOTSTRAPPER_ACTION_STATE_MINOR_UPGRADE: @@ -561,6 +563,8 @@ extern "C" LPCSTR LoggingRequestStateToString( return "Cache"; case BOOTSTRAPPER_REQUEST_STATE_PRESENT: return "Present"; + case BOOTSTRAPPER_REQUEST_STATE_MEND: + return "Mend"; case BOOTSTRAPPER_REQUEST_STATE_REPAIR: return "Repair"; default: diff --git a/src/burn/engine/msiengine.cpp b/src/burn/engine/msiengine.cpp index ef27dfef4..feec53ca7 100644 --- a/src/burn/engine/msiengine.cpp +++ b/src/burn/engine/msiengine.cpp @@ -727,7 +727,7 @@ extern "C" HRESULT MsiEnginePlanCalculatePackage( { case BOOTSTRAPPER_PACKAGE_STATE_PRESENT: __fallthrough; case BOOTSTRAPPER_PACKAGE_STATE_SUPERSEDED: - if (BOOTSTRAPPER_REQUEST_STATE_PRESENT == pPackage->requested || BOOTSTRAPPER_REQUEST_STATE_REPAIR == pPackage->requested) + if (BOOTSTRAPPER_REQUEST_STATE_PRESENT == pPackage->requested || BOOTSTRAPPER_REQUEST_STATE_MEND == pPackage->requested || BOOTSTRAPPER_REQUEST_STATE_REPAIR == pPackage->requested) { // Take a look at the version and determine if this is a potential // minor upgrade (same ProductCode newer ProductVersion), otherwise, @@ -736,6 +736,10 @@ extern "C" HRESULT MsiEnginePlanCalculatePackage( { execute = BOOTSTRAPPER_ACTION_STATE_MINOR_UPGRADE; } + else if (BOOTSTRAPPER_REQUEST_STATE_MEND == pPackage->requested) + { + execute = BOOTSTRAPPER_ACTION_STATE_MEND; + } else if (BOOTSTRAPPER_REQUEST_STATE_REPAIR == pPackage->requested) { execute = BOOTSTRAPPER_ACTION_STATE_REPAIR; @@ -764,6 +768,7 @@ extern "C" HRESULT MsiEnginePlanCalculatePackage( switch (pPackage->requested) { case BOOTSTRAPPER_REQUEST_STATE_PRESENT: __fallthrough; + case BOOTSTRAPPER_REQUEST_STATE_MEND: __fallthrough; case BOOTSTRAPPER_REQUEST_STATE_REPAIR: execute = BOOTSTRAPPER_ACTION_STATE_INSTALL; break; @@ -779,6 +784,7 @@ extern "C" HRESULT MsiEnginePlanCalculatePackage( switch (pPackage->requested) { case BOOTSTRAPPER_REQUEST_STATE_PRESENT: __fallthrough; + case BOOTSTRAPPER_REQUEST_STATE_MEND: __fallthrough; case BOOTSTRAPPER_REQUEST_STATE_REPAIR: execute = BOOTSTRAPPER_ACTION_STATE_INSTALL; break; @@ -1222,11 +1228,13 @@ extern "C" HRESULT MsiEngineExecutePackage( break; case BOOTSTRAPPER_ACTION_STATE_MODIFY: __fallthrough; + case BOOTSTRAPPER_ACTION_STATE_MEND: __fallthrough; case BOOTSTRAPPER_ACTION_STATE_REPAIR: { LPCWSTR wzReinstallAll = (BOOTSTRAPPER_ACTION_STATE_MODIFY == pExecuteAction->msiPackage.action || pExecuteAction->msiPackage.pPackage->Msi.cFeatures) ? L"" : L" REINSTALL=ALL"; - LPCWSTR wzReinstallMode = (BOOTSTRAPPER_ACTION_STATE_MODIFY == pExecuteAction->msiPackage.action) ? L"o" : L"e"; + LPCWSTR wzReinstallMode = (BOOTSTRAPPER_ACTION_STATE_MODIFY == pExecuteAction->msiPackage.action + || BOOTSTRAPPER_ACTION_STATE_MEND == pExecuteAction->msiPackage.action) ? L"o" : L"e"; hr = StrAllocFormattedSecure(&sczProperties, L"%ls%ls REINSTALLMODE=\"cmus%ls\" REBOOT=ReallySuppress", sczProperties ? sczProperties : L"", wzReinstallAll, wzReinstallMode); ExitOnFailure(hr, "Failed to add reinstall mode and reboot suppression properties on repair."); diff --git a/src/burn/inc/IBootstrapperEngine.h b/src/burn/inc/IBootstrapperEngine.h index 4de57e21a..f3fd8b616 100644 --- a/src/burn/inc/IBootstrapperEngine.h +++ b/src/burn/inc/IBootstrapperEngine.h @@ -33,6 +33,7 @@ enum BOOTSTRAPPER_ACTION_STATE BOOTSTRAPPER_ACTION_STATE_INSTALL, BOOTSTRAPPER_ACTION_STATE_ADMIN_INSTALL, BOOTSTRAPPER_ACTION_STATE_MODIFY, + BOOTSTRAPPER_ACTION_STATE_MEND, BOOTSTRAPPER_ACTION_STATE_REPAIR, BOOTSTRAPPER_ACTION_STATE_MINOR_UPGRADE, BOOTSTRAPPER_ACTION_STATE_MAJOR_UPGRADE, @@ -56,6 +57,7 @@ enum BOOTSTRAPPER_REQUEST_STATE BOOTSTRAPPER_REQUEST_STATE_ABSENT, BOOTSTRAPPER_REQUEST_STATE_CACHE, BOOTSTRAPPER_REQUEST_STATE_PRESENT, + BOOTSTRAPPER_REQUEST_STATE_MEND, BOOTSTRAPPER_REQUEST_STATE_REPAIR, }; From 5392db2df8ddd508444ae078381feda71874c831 Mon Sep 17 00:00:00 2001 From: Bob Arnson Date: Fri, 9 Dec 2016 14:38:37 -0500 Subject: [PATCH 04/17] Add BOOTSTRAPPER_ACTION_STATE_MEND and BOOTSTRAPPER_REQUEST_STATE_MEND to MBA Core. --- src/ext/BalExtension/mba/core/IBootstrapperEngine.cs | 2 ++ 1 file changed, 2 insertions(+) diff --git a/src/ext/BalExtension/mba/core/IBootstrapperEngine.cs b/src/ext/BalExtension/mba/core/IBootstrapperEngine.cs index a557c2e95..0070f008a 100644 --- a/src/ext/BalExtension/mba/core/IBootstrapperEngine.cs +++ b/src/ext/BalExtension/mba/core/IBootstrapperEngine.cs @@ -155,6 +155,7 @@ public enum ActionState Install, AdminInstall, Modify, + Mend, Repair, MinorUpgrade, MajorUpgrade, @@ -248,6 +249,7 @@ public enum RequestState Absent, Cache, Present, + Mend, Repair, } From 6a911dbb597bc1bbb3ee89c3382c3504c9a7efea Mon Sep 17 00:00:00 2001 From: Bob Arnson Date: Tue, 20 Dec 2016 17:44:56 -0500 Subject: [PATCH 05/17] Clarify another limitation of duplicate GUIDs in LGHT1137 message. --- src/tools/wix/Data/messages.xml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/tools/wix/Data/messages.xml b/src/tools/wix/Data/messages.xml index 14d4ab4ac..8f43042b6 100644 --- a/src/tools/wix/Data/messages.xml +++ b/src/tools/wix/Data/messages.xml @@ -3722,7 +3722,7 @@ - Component/@Id='{0}' has a @Guid value '{1}' that duplicates another component in this package. This is not officially supported by Windows Installer but works as long as all components have mutually-exclusive conditions. It is recommended to give each component its own unique GUID. + Component/@Id='{0}' has a @Guid value '{1}' that duplicates another component in this package. This is not officially supported by Windows Installer and cannot be used when creating patches. It otherwise works as long as all components with the same GUID have mutually-exclusive conditions. It is recommended to give each component its own unique GUID. From 40e053330f0a8c21b74f64f4a86b669dfb6c2cf1 Mon Sep 17 00:00:00 2001 From: Bob Arnson Date: Wed, 4 Jan 2017 16:19:37 -0500 Subject: [PATCH 06/17] Add condition to RemoveFoldersEx. #1507, #1508, #1509 --- src/ext/UtilExtension/wixext/Data/tables.xml | 2 ++ src/ext/UtilExtension/wixext/UtilCompiler.cs | 5 ++++ src/ext/UtilExtension/wixext/Xsd/util.xsd | 5 ++++ src/ext/ca/wixca/dll/RemoveFoldersEx.cpp | 27 +++++++++++++++++--- 4 files changed, 35 insertions(+), 4 deletions(-) diff --git a/src/ext/UtilExtension/wixext/Data/tables.xml b/src/ext/UtilExtension/wixext/Data/tables.xml index 201eeef5d..d15e470a8 100644 --- a/src/ext/UtilExtension/wixext/Data/tables.xml +++ b/src/ext/UtilExtension/wixext/Data/tables.xml @@ -32,6 +32,8 @@ category="identifier" description="Name of Property that contains the root of the directory tree to remove."/> + + + + Condition for evaluating the removal. If this evaluates to false, the removal is not executed at all. + + diff --git a/src/ext/ca/wixca/dll/RemoveFoldersEx.cpp b/src/ext/ca/wixca/dll/RemoveFoldersEx.cpp index dbfaaf734..bfffd9678 100644 --- a/src/ext/ca/wixca/dll/RemoveFoldersEx.cpp +++ b/src/ext/ca/wixca/dll/RemoveFoldersEx.cpp @@ -2,8 +2,8 @@ #include "precomp.h" -LPCWSTR vcsRemoveFolderExQuery = L"SELECT `WixRemoveFolderEx`, `Component_`, `Property`, `InstallMode` FROM `WixRemoveFolderEx`"; -enum eRemoveFolderExQuery { rfqId = 1, rfqComponent, rfqProperty, feqMode }; +LPCWSTR vcsRemoveFolderExQuery = L"SELECT `WixRemoveFolderEx`, `Component_`, `Property`, `InstallMode`, `Condition` FROM `WixRemoveFolderEx`"; +enum eRemoveFolderExQuery { rfqId = 1, rfqComponent, rfqProperty, rfqMode, rfqCondition }; static HRESULT RecursePath( __in_z LPCWSTR wzPath, @@ -34,7 +34,7 @@ static HRESULT RecursePath( er = ::GetLastError(); if (ERROR_PATH_NOT_FOUND == er) { - WcaLog(LOGMSG_STANDARD, "Search path not found: %ls", sczSearch); + WcaLog(LOGMSG_STANDARD, "Search path not found: %ls; skipping", sczSearch); ExitFunction1(hr = S_FALSE); } else @@ -110,6 +110,7 @@ extern "C" UINT WINAPI WixRemoveFoldersEx( LPWSTR sczId = NULL; LPWSTR sczComponent = NULL; LPWSTR sczProperty = NULL; + LPWSTR sczCondition = NULL; LPWSTR sczPath = NULL; LPWSTR sczExpandedPath = NULL; int iMode = 0; @@ -137,13 +138,30 @@ extern "C" UINT WINAPI WixRemoveFoldersEx( hr = WcaGetRecordString(hRec, rfqId, &sczId); ExitOnFailure(hr, "Failed to get remove folder identity."); + hr = WcaGetRecordString(hRec, rfqCondition, &sczCondition); + ExitOnFailure(hr, "Failed to get remove folder condition."); + + if (sczCondition && *sczCondition) + { + MSICONDITION condition = ::MsiEvaluateConditionW(hInstall, sczCondition); + if (MSICONDITION_TRUE == condition) + { + WcaLog(LOGMSG_STANDARD, "True condition for row %S: %S; processing.", sczId, sczCondition); + } + else + { + WcaLog(LOGMSG_STANDARD, "False or invalid condition for row %S: %S; skipping.", sczId, sczCondition); + continue; + } + } + hr = WcaGetRecordString(hRec, rfqComponent, &sczComponent); ExitOnFailure(hr, "Failed to get remove folder component."); hr = WcaGetRecordString(hRec, rfqProperty, &sczProperty); ExitOnFailure(hr, "Failed to get remove folder property."); - hr = WcaGetRecordInteger(hRec, feqMode, &iMode); + hr = WcaGetRecordInteger(hRec, rfqMode, &iMode); ExitOnFailure(hr, "Failed to get remove folder mode"); hr = WcaGetProperty(sczProperty, &sczPath); @@ -190,6 +208,7 @@ extern "C" UINT WINAPI WixRemoveFoldersEx( ReleaseStr(sczPath); ReleaseStr(sczProperty); ReleaseStr(sczComponent); + ReleaseStr(sczCondition); ReleaseStr(sczId); DWORD er = SUCCEEDED(hr) ? ERROR_SUCCESS : ERROR_INSTALL_FAILURE; From 331ccd3d522a6870a06a7f6578615c094b08b21b Mon Sep 17 00:00:00 2001 From: Bob Arnson Date: Thu, 5 Jan 2017 16:06:06 -0500 Subject: [PATCH 07/17] Support WOW64 file-system redirection in WixRemoveFoldersEx. --- src/ext/ca/wixca/dll/RemoveFoldersEx.cpp | 35 +++++++++++++++++++++--- 1 file changed, 31 insertions(+), 4 deletions(-) diff --git a/src/ext/ca/wixca/dll/RemoveFoldersEx.cpp b/src/ext/ca/wixca/dll/RemoveFoldersEx.cpp index bfffd9678..fb676defa 100644 --- a/src/ext/ca/wixca/dll/RemoveFoldersEx.cpp +++ b/src/ext/ca/wixca/dll/RemoveFoldersEx.cpp @@ -2,8 +2,11 @@ #include "precomp.h" -LPCWSTR vcsRemoveFolderExQuery = L"SELECT `WixRemoveFolderEx`, `Component_`, `Property`, `InstallMode`, `Condition` FROM `WixRemoveFolderEx`"; -enum eRemoveFolderExQuery { rfqId = 1, rfqComponent, rfqProperty, rfqMode, rfqCondition }; +LPCWSTR vcsRemoveFolderExQuery = + L"SELECT `WixRemoveFolderEx`, `Component_`, `Property`, `InstallMode`, `WixRemoveFolderEx`.`Condition`, `Component`.`Attributes` " + L"FROM `WixRemoveFolderEx`,`Component` " + L"WHERE `WixRemoveFolderEx`.`Component_`=`Component`.`Component`"; +enum eRemoveFolderExQuery { rfqId = 1, rfqComponent, rfqProperty, rfqMode, rfqCondition, rfqComponentAttributes }; static HRESULT RecursePath( __in_z LPCWSTR wzPath, @@ -11,6 +14,7 @@ static HRESULT RecursePath( __in_z LPCWSTR wzComponent, __in_z LPCWSTR wzProperty, __in int iMode, + __in BOOL fDisableWow64Redirection, __inout DWORD* pdwCounter, __inout MSIHANDLE* phTable, __inout MSIHANDLE* phColumns @@ -24,6 +28,12 @@ static HRESULT RecursePath( WIN32_FIND_DATAW wfd; LPWSTR sczNext = NULL; + if (fDisableWow64Redirection) + { + hr = WcaDisableWow64FSRedirection(); + ExitOnFailure(hr, "Custom action was told to act on a 64-bit component, but was unable to disable filesystem redirection through the Wow64 API."); + } + // First recurse down to all the child directories. hr = StrAllocFormatted(&sczSearch, L"%s*", wzPath); ExitOnFailure1(hr, "Failed to allocate file search string in path: %S", wzPath); @@ -55,7 +65,8 @@ static HRESULT RecursePath( hr = StrAllocFormatted(&sczNext, L"%s%s\\", wzPath, wfd.cFileName); ExitOnFailure2(hr, "Failed to concat filename '%S' to string: %S", wfd.cFileName, wzPath); - hr = RecursePath(sczNext, wzId, wzComponent, wzProperty, iMode, pdwCounter, phTable, phColumns); + // Don't re-disable redirection; if it was necessary, we've already done it. + hr = RecursePath(sczNext, wzId, wzComponent, wzProperty, iMode, FALSE, pdwCounter, phTable, phColumns); ExitOnFailure1(hr, "Failed to recurse path: %S", sczNext); } while (::FindNextFileW(hFind, &wfd)); @@ -92,6 +103,11 @@ static HRESULT RecursePath( ::FindClose(hFind); } + if (fDisableWow64Redirection) + { + WcaRevertWow64FSRedirection(); + } + ReleaseStr(sczNext); ReleaseStr(sczProperty); ReleaseStr(sczSearch); @@ -114,6 +130,8 @@ extern "C" UINT WINAPI WixRemoveFoldersEx( LPWSTR sczPath = NULL; LPWSTR sczExpandedPath = NULL; int iMode = 0; + int iComponentAttributes; + BOOL f64BitComponent = FALSE; DWORD dwCounter = 0; DWORD_PTR cchLen = 0; MSIHANDLE hTable = NULL; @@ -122,6 +140,8 @@ extern "C" UINT WINAPI WixRemoveFoldersEx( hr = WcaInitialize(hInstall, "WixRemoveFoldersEx"); ExitOnFailure(hr, "Failed to initialize WixRemoveFoldersEx."); + WcaInitializeWow64(); + // anything to do? if (S_OK != WcaTableExists(L"WixRemoveFolderEx")) { @@ -167,6 +187,11 @@ extern "C" UINT WINAPI WixRemoveFoldersEx( hr = WcaGetProperty(sczProperty, &sczPath); ExitOnFailure2(hr, "Failed to resolve remove folder property: %S for row: %S", sczProperty, sczId); + hr = WcaGetRecordInteger(hRec, rfqComponentAttributes, &iComponentAttributes); + ExitOnFailure1(hr, "failed to get component attributes for row: %ls", sczId); + + f64BitComponent = iComponentAttributes & msidbComponentAttributes64bit; + // fail early if the property isn't set as you probably don't want your installers trying to delete SystemFolder // StringCchLengthW succeeds only if the string is zero characters plus 1 for the terminating null hr = ::StringCchLengthW(sczPath, 1, reinterpret_cast(&cchLen)); @@ -182,7 +207,7 @@ extern "C" UINT WINAPI WixRemoveFoldersEx( ExitOnFailure1(hr, "Failed to backslash-terminate path: %S", sczExpandedPath); WcaLog(LOGMSG_STANDARD, "Recursing path: %S for row: %S.", sczExpandedPath, sczId); - hr = RecursePath(sczExpandedPath, sczId, sczComponent, sczProperty, iMode, &dwCounter, &hTable, &hColumns); + hr = RecursePath(sczExpandedPath, sczId, sczComponent, sczProperty, iMode, f64BitComponent, &dwCounter, &hTable, &hColumns); ExitOnFailure2(hr, "Failed while navigating path: %S for row: %S", sczPath, sczId); } @@ -194,6 +219,8 @@ extern "C" UINT WINAPI WixRemoveFoldersEx( ExitOnFailure(hr, "Failure occured while processing WixRemoveFolderEx table"); LExit: + WcaFinalizeWow64(); + if (hColumns) { ::MsiCloseHandle(hColumns); From 67ff727a4611fda2ace48fcad45e4911ad685b66 Mon Sep 17 00:00:00 2001 From: Bob Arnson Date: Fri, 13 Jan 2017 16:04:12 -0500 Subject: [PATCH 08/17] WixRemoveRegistryKeyEx: Extension to conditionally remove registry keys --- src/ext/UtilExtension/wixext/Data/tables.xml | 14 + src/ext/UtilExtension/wixext/UtilCompiler.cs | 129 ++++++++- src/ext/UtilExtension/wixext/Xsd/util.xsd | 260 ++++++++++++------ .../UtilExtension/wixlib/UtilExtension.wxs | 8 + src/ext/ca/wixca/dll/RemoveRegistryKeyEx.cpp | 114 ++++++++ src/ext/ca/wixca/dll/wixca.def | 2 + src/ext/ca/wixca/dll/wixca.vcxproj | 12 +- 7 files changed, 443 insertions(+), 96 deletions(-) create mode 100644 src/ext/ca/wixca/dll/RemoveRegistryKeyEx.cpp diff --git a/src/ext/UtilExtension/wixext/Data/tables.xml b/src/ext/UtilExtension/wixext/Data/tables.xml index d15e470a8..09aafab5a 100644 --- a/src/ext/UtilExtension/wixext/Data/tables.xml +++ b/src/ext/UtilExtension/wixext/Data/tables.xml @@ -23,6 +23,20 @@ + + + + + + + + diff --git a/src/ext/UtilExtension/wixext/UtilCompiler.cs b/src/ext/UtilExtension/wixext/UtilCompiler.cs index 34811c109..66b145fc5 100644 --- a/src/ext/UtilExtension/wixext/UtilCompiler.cs +++ b/src/ext/UtilExtension/wixext/UtilCompiler.cs @@ -91,9 +91,15 @@ internal enum WixRestartResourceAttributes internal enum WixRemoveFolderExOn { - Install = 1, - Uninstall = 2, - Both = 3, + Install = 1, + Uninstall = 2, + Both = 3, + } + + internal enum WixRemoveRegistryKeyExOn + { + Install = 1, + Uninstall = 2, } private static readonly Regex FindPropertyBrackets = new Regex(@"\[(?!\\|\])|(? + /// Parses a RemoveRegistryKeyEx element. + /// + /// Element to parse. + /// Identifier of parent component. + private void ParseRemoveRegistryKeyExElement(XmlNode node, string componentId) + { + SourceLineNumberCollection sourceLineNumbers = Preprocessor.GetSourceLineNumbers(node); + string id = null; + int action = (int)WixRemoveRegistryKeyExOn.Uninstall; + string condition = null; + int root = CompilerCore.IntegerNotSet; + string key = null; + + foreach (XmlAttribute attrib in node.Attributes) + { + if (0 == attrib.NamespaceURI.Length || attrib.NamespaceURI == this.schema.TargetNamespace) + { + switch (attrib.LocalName) + { + case "Condition": + condition = this.Core.GetAttributeValue(sourceLineNumbers, attrib); + break; + case "Id": + id = this.Core.GetAttributeIdentifierValue(sourceLineNumbers, attrib); + break; + case "Action": + string actionValue = this.Core.GetAttributeValue(sourceLineNumbers, attrib); + if (actionValue.Length == 0) + { + action = CompilerCore.IllegalInteger; + } + else + { + switch (actionValue) + { + case "removeOnInstall": + action = (int)WixRemoveRegistryKeyExOn.Install; + break; + case "removeOnUninstall": + action = (int)WixRemoveRegistryKeyExOn.Uninstall; + break; + default: + this.Core.OnMessage(WixErrors.IllegalAttributeValue(sourceLineNumbers, node.Name, "Action", actionValue, "removeOnInstall", "removeOnUninstall")); + action = CompilerCore.IllegalInteger; + break; + } + } + break; + case "Root": + root = this.Core.GetAttributeMsidbRegistryRootValue(sourceLineNumbers, attrib, false); + break; + case "Key": + key = this.Core.GetAttributeValue(sourceLineNumbers, attrib); + break; + default: + this.Core.UnexpectedAttribute(sourceLineNumbers, attrib); + break; + } + } + else + { + this.Core.UnsupportedExtensionAttribute(sourceLineNumbers, attrib); + } + } + + if (CompilerCore.IntegerNotSet == root) + { + this.Core.OnMessage(WixErrors.ExpectedAttribute(sourceLineNumbers, node.Name, "Root")); + } + + if (null == key) + { + this.Core.OnMessage(WixErrors.ExpectedAttribute(sourceLineNumbers, node.Name, "Key")); + } + + if (String.IsNullOrEmpty(id)) + { + id = this.Core.GenerateIdentifier("rrx", componentId, condition, root.ToString(), key, action.ToString(CultureInfo.InvariantCulture.NumberFormat)); + } + + foreach (XmlNode child in node.ChildNodes) + { + if (XmlNodeType.Element == child.NodeType) + { + if (child.NamespaceURI == this.schema.TargetNamespace) + { + this.Core.UnexpectedElement(node, child); + } + else + { + this.Core.UnsupportedExtensionElement(node, child); + } + } + } + + if (!this.Core.EncounteredError) + { + Row row = this.Core.CreateRow(sourceLineNumbers, "WixRemoveRegistryKeyEx"); + row[0] = id; + row[1] = componentId; + row[2] = root; + row[3] = key; + row[4] = action; + row[5] = condition; + + this.Core.EnsureTable(sourceLineNumbers, "Registry"); + this.Core.EnsureTable(sourceLineNumbers, "RemoveRegistry"); + this.Core.CreateWixSimpleReferenceRow(sourceLineNumbers, "CustomAction", "WixRemoveRegistryKeyEx"); + } + } + /// /// Parses a RestartResource element. /// diff --git a/src/ext/UtilExtension/wixext/Xsd/util.xsd b/src/ext/UtilExtension/wixext/Xsd/util.xsd index b1dc71308..89f38380a 100644 --- a/src/ext/UtilExtension/wixext/Xsd/util.xsd +++ b/src/ext/UtilExtension/wixext/Xsd/util.xsd @@ -895,89 +895,183 @@ - - - - - - - The custom action that implements RemoveFolderEx does so by writing temporary rows to the RemoveFile table - for each subfolder of the root folder you specify. Because it might dramatically affect Windows Installer's - File Costing, - the temporary rows must be written before the CostInitialize standard action. Unfortunately, MSI doesn't - create properties for the Directory hierarchy in your package until later, in the CostFinalize action. - An easy workaround for a typical use case of removing a folder during uninstall is to write the directory - path to the registry and to load it during uninstall. See - The WiX toolset's "Remember Property" pattern - for an example. - If you use custom actions to set properties, ensure that they are scheduled before the WixRemoveFoldersEx custom action. - - - - Remove a folder and all contained files and folders if the parent component is selected for installation or removal. - The folder must be specified in the Property attribute as the name of a property that will have a value that resolves - to the full path of the folder before the CostInitialize action. Note that Directory ids cannot be used. - For more details, see the Remarks. - - - - - - Primary key used to identify this particular entry. If this is not specified, a stable identifier - will be generated at compile time based on the other attributes. - - - - - - The id of a property that resolves to the full path of the source directory. The property does not have - to exist in the installer database at creation time; it could be created at installation time by a custom - action, on the command line, etc. The property value can contain environment variables surrounded by - percent signs such as from a REG_EXPAND_SZ registry value; environment variables will be expanded before - being evaluated for a full path. - - - - - - Condition for evaluating the removal. If this evaluates to false, the removal is not executed at all. - - - - - - This value determines when the folder may be removed. - - - - - - - - Removes the folder only when the parent component is being installed (msiInstallStateLocal or msiInstallStateSource). - - - - - - - Default: Removes the folder only when the parent component is being removed (msiInstallStateAbsent). - - - - - - - Removes the folder when the parent component is being installed or removed. - - - - - - - - - + + + + + + + + The custom action that implements RemoveFolderEx does so by writing temporary rows to the RemoveFile table + for each subfolder of the root folder you specify. Because it might dramatically affect Windows Installer's + File Costing, + the temporary rows must be written before the CostInitialize standard action. Unfortunately, MSI doesn't + create properties for the Directory hierarchy in your package until later, in the CostFinalize action. + + + An easy workaround for a typical use case of removing a folder during uninstall is to write the directory + path to the registry and to load it during uninstall. See + The WiX toolset's "Remember Property" pattern + for an example. + + If you use custom actions to set properties, ensure that they are scheduled before the WixRemoveFoldersEx custom action. + + + + Remove a folder and all contained files and folders if the parent component is selected for installation or removal. + The folder must be specified in the Property attribute as the name of a property that will have a value that resolves + to the full path of the folder before the CostInitialize action. Note that Directory ids cannot be used. + For more details, see the Remarks. + + + + + + + Primary key used to identify this particular entry. If this is not specified, a stable identifier + will be generated at compile time based on the other attributes. + + + + + + + The id of a property that resolves to the full path of the source directory. The property does not have + to exist in the installer database at creation time; it could be created at installation time by a custom + action, on the command line, etc. The property value can contain environment variables surrounded by + percent signs such as from a REG_EXPAND_SZ registry value; environment variables will be expanded before + being evaluated for a full path. + + + + + + Condition for evaluating the removal. If this evaluates to false, the removal is not executed at all. + + + + + + This value determines when the folder may be removed. + + + + + + + + Removes the folder only when the parent component is being installed (msiInstallStateLocal or msiInstallStateSource). + + + + + + + Default: Removes the folder only when the parent component is being removed (msiInstallStateAbsent). + + + + + + + Removes the folder when the parent component is being installed or removed. + + + + + + + + + + + + + + + + Remove a registry key and all contained keys and values if the parent component is selected for installation or removal. + + + + + + + Primary key used to identify this particular entry. If this is not specified, a stable identifier + will be generated at compile time based on the other attributes. If specified, it must not be the + same as any id in the Registry or RemoveRegistry tables. + + + + + + Condition for evaluating the removal. If this evaluates to false, the removal is not executed at all. + + + + + + This is the action that will be taken for this registry value. + + + + + + + + Removes a key with all its values and subkeys when the parent component is installed. + + + + + + + Removes a key with all its values and subkeys when the parent component is uninstalled. + + + + + + + + + + The localizable key for the registry key. + + + + + + Registry root hive to search under. + + + + + + HKEY_LOCAL_MACHINE + + + + + HKEY_CURRENT_USER + + + + + HKEY_CLASSES_ROOT + + + + + HKEY_USERS + + + + + + + + Registers a resource with the Restart Manager. diff --git a/src/ext/UtilExtension/wixlib/UtilExtension.wxs b/src/ext/UtilExtension/wixlib/UtilExtension.wxs index 90ec76455..e17dcbcdd 100644 --- a/src/ext/UtilExtension/wixlib/UtilExtension.wxs +++ b/src/ext/UtilExtension/wixlib/UtilExtension.wxs @@ -102,6 +102,14 @@ + + + + + + + + diff --git a/src/ext/ca/wixca/dll/RemoveRegistryKeyEx.cpp b/src/ext/ca/wixca/dll/RemoveRegistryKeyEx.cpp new file mode 100644 index 000000000..0051fc97a --- /dev/null +++ b/src/ext/ca/wixca/dll/RemoveRegistryKeyEx.cpp @@ -0,0 +1,114 @@ +// Copyright (c) .NET Foundation and contributors. All rights reserved. Licensed under the Microsoft Reciprocal License. See LICENSE.TXT file in the project root for full license information. + +#include "precomp.h" + +LPCWSTR vcsRemoveRegistryKeyExQuery = + L"SELECT `WixRemoveRegistryKeyEx`, `Component_`, `Root`, `Key`, `InstallMode`, `Condition` FROM `WixRemoveRegistryKeyEx`"; +enum eRemoveRegistryKeyExQuery { rrxqId = 1, rrxqComponent, rrxqRoot, rrxqKey, rrxqMode, rrxqCondition }; + +extern "C" UINT WINAPI WixRemoveRegistryKeyEx( + __in MSIHANDLE hInstall + ) +{ + //AssertSz(FALSE, "debug WixRemoveRegistryKeyEx"); + + HRESULT hr = S_OK; + PMSIHANDLE hView; + PMSIHANDLE hRec; + LPWSTR sczId = NULL; + LPWSTR sczComponent = NULL; + LPWSTR sczCondition = NULL; + LPWSTR sczKey = NULL; + int iRoot = 0; + int iMode = 0; + MSIHANDLE hTable = NULL; + MSIHANDLE hColumns = NULL; + + hr = WcaInitialize(hInstall, __FUNCTION__); + ExitOnFailure(hr, "Failed to initialize " __FUNCTION__); + + // anything to do? + if (S_OK != WcaTableExists(L"WixRemoveRegistryKeyEx")) + { + WcaLog(LOGMSG_STANDARD, "WixRemoveRegistryKeyEx table doesn't exist, so there are no registry keys to remove."); + ExitFunction(); + } + + hr = WcaOpenExecuteView(vcsRemoveRegistryKeyExQuery, &hView); + ExitOnFailure(hr, "Failed to open view on WixRemoveRegistryKeyEx table"); + + while (S_OK == (hr = WcaFetchRecord(hView, &hRec))) + { + hr = WcaGetRecordString(hRec, rrxqId, &sczId); + ExitOnFailure(hr, "Failed to get WixRemoveRegistryKeyEx identity."); + + hr = WcaGetRecordString(hRec, rrxqCondition, &sczCondition); + ExitOnFailure(hr, "Failed to get WixRemoveRegistryKeyEx condition."); + + if (sczCondition && *sczCondition) + { + MSICONDITION condition = ::MsiEvaluateConditionW(hInstall, sczCondition); + if (MSICONDITION_TRUE == condition) + { + WcaLog(LOGMSG_STANDARD, "True condition for row %S: %S; processing.", sczId, sczCondition); + } + else + { + WcaLog(LOGMSG_STANDARD, "False or invalid condition for row %S: %S; skipping.", sczId, sczCondition); + continue; + } + } + + hr = WcaGetRecordString(hRec, rrxqComponent, &sczComponent); + ExitOnFailure(hr, "Failed to get WixRemoveRegistryKeyEx component."); + + hr = WcaGetRecordInteger(hRec, rrxqRoot, &iRoot); + ExitOnFailure(hr, "Failed to get WixRemoveRegistryKeyEx root."); + + hr = WcaGetRecordString(hRec, rrxqKey, &sczKey); + ExitOnFailure(hr, "Failed to get WixRemoveRegistryKeyEx key."); + + hr = WcaGetRecordInteger(hRec, rrxqMode, &iMode); + ExitOnFailure(hr, "Failed to get WixRemoveRegistryKeyEx mode."); + + switch (iMode) + { + case 1: // removeOnInstall + WcaLog(LOGMSG_STANDARD, "Adding RemoveRegistry row: %ls/%d/%ls/-/%ls", sczId, iRoot, sczKey, sczComponent); + hr = WcaAddTempRecord(&hTable, &hColumns, L"RemoveRegistry", NULL, 0, 5, sczId, iRoot, sczKey, L"-", sczComponent); + ExitOnFailure(hr, "Failed to add RemoveRegistry row for remove-on-install WixRemoveRegistryKeyEx row: %ls:", sczId); + break; + case 2: // removeOnUninstall + WcaLog(LOGMSG_STANDARD, "Adding Registry row: %ls/%d/%ls/-/null/%ls", sczId, iRoot, sczKey, sczComponent); + hr = WcaAddTempRecord(&hTable, &hColumns, L"Registry", NULL, 0, 6, sczId, iRoot, sczKey, L"-", NULL, sczComponent); + ExitOnFailure(hr, "Failed to add Registry row for remove-on-uninstall WixRemoveRegistryKeyEx row: %ls:", sczId); + break; + } + } + + // reaching the end of the list is actually a good thing, not an error + if (E_NOMOREITEMS == hr) + { + hr = S_OK; + } + ExitOnFailure(hr, "Failure occured while processing WixRemoveRegistryKeyEx table."); + +LExit: + if (hColumns) + { + ::MsiCloseHandle(hColumns); + } + + if (hTable) + { + ::MsiCloseHandle(hTable); + } + + ReleaseStr(sczKey); + ReleaseStr(sczComponent); + ReleaseStr(sczCondition); + ReleaseStr(sczId); + + DWORD er = SUCCEEDED(hr) ? ERROR_SUCCESS : ERROR_INSTALL_FAILURE; + return WcaFinalize(er); +} diff --git a/src/ext/ca/wixca/dll/wixca.def b/src/ext/ca/wixca/dll/wixca.def index 33f96afc5..a78dcb9f8 100644 --- a/src/ext/ca/wixca/dll/wixca.def +++ b/src/ext/ca/wixca/dll/wixca.def @@ -32,6 +32,8 @@ EXPORTS WixSilentExec64 ; RemoveFolders.cpp WixRemoveFoldersEx +; RemoveRegistryKeyEx.cpp + WixRemoveRegistryKeyEx ; RestartManager.cpp WixRegisterRestartResources ; secureobjects.cpp diff --git a/src/ext/ca/wixca/dll/wixca.vcxproj b/src/ext/ca/wixca/dll/wixca.vcxproj index 897ee9a8c..8db7166d5 100644 --- a/src/ext/ca/wixca/dll/wixca.vcxproj +++ b/src/ext/ca/wixca/dll/wixca.vcxproj @@ -1,7 +1,5 @@ - + - - @@ -43,7 +41,6 @@ ARM - {0D1A6856-D89F-4CC4-B3E9-B1DCFB2852F6} DynamicLibrary @@ -51,14 +48,11 @@ wixca.def wixca_x64.def - - $(WixRoot)src\libs\dutil\inc;$(WixRoot)src\libs\wcautil;..\..\inc msi.lib;dutil.lib;wcautil.lib - @@ -68,6 +62,7 @@ + @@ -89,6 +84,5 @@ - - + \ No newline at end of file From 5a95d34dbf7acfd1b2de0e757348e0d39e06decf Mon Sep 17 00:00:00 2001 From: Bob Arnson Date: Thu, 16 Mar 2017 13:04:48 -0400 Subject: [PATCH 09/17] Add wixtasks.dll fallback to WiX v3.10. --- src/tools/WixTasks/wix.ca.targets | 17 +++++++++-------- 1 file changed, 9 insertions(+), 8 deletions(-) diff --git a/src/tools/WixTasks/wix.ca.targets b/src/tools/WixTasks/wix.ca.targets index 0631b826d..d25d230e6 100644 --- a/src/tools/WixTasks/wix.ca.targets +++ b/src/tools/WixTasks/wix.ca.targets @@ -13,8 +13,9 @@ $(MSBuildExtensionsPath32)\..\WiX Toolset v3.11\bin\WixTasks.dll $(MSBuildExtensionsPath)\..\WiX Toolset v3.11\bin\WixTasks.dll - + $(MSBuildProgramFiles32)\WiX Toolset v3.11\bin\WixTasks.dll + $(MSBuildProgramFiles32)\WiX Toolset v3.10\bin\WixTasks.dll $(TargetName).CA$(TargetExt) @@ -81,7 +82,7 @@ - + - + - + - + - + From f506c4b3a1ef2946296a2e8514ad8e703f70705e Mon Sep 17 00:00:00 2001 From: Bob Arnson Date: Tue, 21 Mar 2017 17:52:40 -0400 Subject: [PATCH 10/17] Scale splashscreen bitmap and avoid double painting. --- src/burn/engine/splashscreen.cpp | 21 +++++++++++++++++++-- src/burn/stub/stub.manifest | 3 ++- src/burn/stub/stub_arm.manifest | 3 ++- 3 files changed, 23 insertions(+), 4 deletions(-) diff --git a/src/burn/engine/splashscreen.cpp b/src/burn/engine/splashscreen.cpp index b11297013..1bd8caca2 100644 --- a/src/burn/engine/splashscreen.cpp +++ b/src/burn/engine/splashscreen.cpp @@ -12,6 +12,7 @@ struct SPLASHSCREEN_INFO HBITMAP hBitmap; POINT pt; SIZE size; + SIZE bmpsize; }; struct SPLASHSCREEN_CONTEXT @@ -207,7 +208,7 @@ static LRESULT CALLBACK WndProc( HDC hdc = reinterpret_cast(wParam); HDC hdcMem = ::CreateCompatibleDC(hdc); HBITMAP hDefaultBitmap = static_cast(::SelectObject(hdcMem, pImage->hBitmap)); - ::StretchBlt(hdc, 0, 0, pImage->size.cx, pImage->size.cy, hdcMem, 0, 0, pImage->size.cx, pImage->size.cy, SRCCOPY); + ::StretchBlt(hdc, 0, 0, pImage->size.cx, pImage->size.cy, hdcMem, 0, 0, pImage->bmpsize.cx, pImage->bmpsize.cy, SRCCOPY); ::SelectObject(hdcMem, hDefaultBitmap); ::DeleteDC(hdcMem); } @@ -226,7 +227,7 @@ static HRESULT LoadSplashScreen( BITMAP bmp = { }; POINT ptCursor = { }; HMONITOR hMonitor = NULL; - MONITORINFO mi = { }; + MONITORINFOEXW mi = { }; pSplashScreen->hBitmap = ::LoadBitmapW(hInstance, MAKEINTRESOURCEW(IDB_SPLASHSCREEN)); ExitOnNullWithLastError(pSplashScreen->hBitmap, hr, "Failed to load splash screen bitmap."); @@ -234,6 +235,8 @@ static HRESULT LoadSplashScreen( ::GetObject(pSplashScreen->hBitmap, sizeof(bmp), static_cast(&bmp)); pSplashScreen->pt.x = CW_USEDEFAULT; pSplashScreen->pt.y = CW_USEDEFAULT; + pSplashScreen->bmpsize.cx = bmp.bmWidth; + pSplashScreen->bmpsize.cy = bmp.bmHeight; pSplashScreen->size.cx = bmp.bmWidth; pSplashScreen->size.cy = bmp.bmHeight; @@ -243,9 +246,23 @@ static HRESULT LoadSplashScreen( hMonitor = ::MonitorFromPoint(ptCursor, MONITOR_DEFAULTTONEAREST); if (hMonitor) { + ZeroMemory(&mi, sizeof(mi)); mi.cbSize = sizeof(mi); + if (::GetMonitorInfoW(hMonitor, &mi)) { + HDC hdc = ::CreateDCW(L"DISPLAY", mi.szDevice, NULL, NULL); + if (hdc) + { + UINT dpiX = ::GetDeviceCaps(hdc, LOGPIXELSX); + UINT dpiY = ::GetDeviceCaps(hdc, LOGPIXELSY); + + pSplashScreen->size.cx = pSplashScreen->size.cx * dpiX / 96; + pSplashScreen->size.cy = pSplashScreen->size.cy * dpiY / 96; + + ::DeleteDC(hdc); + } + pSplashScreen->pt.x = mi.rcWork.left + (mi.rcWork.right - mi.rcWork.left - pSplashScreen->size.cx) / 2; pSplashScreen->pt.y = mi.rcWork.top + (mi.rcWork.bottom - mi.rcWork.top - pSplashScreen->size.cy) / 2; } diff --git a/src/burn/stub/stub.manifest b/src/burn/stub/stub.manifest index 6aa1e7a40..d5767cdb8 100644 --- a/src/burn/stub/stub.manifest +++ b/src/burn/stub/stub.manifest @@ -2,9 +2,10 @@ - + WiX Toolset Bootstrapper + true diff --git a/src/burn/stub/stub_arm.manifest b/src/burn/stub/stub_arm.manifest index 54f247c03..0087aade8 100644 --- a/src/burn/stub/stub_arm.manifest +++ b/src/burn/stub/stub_arm.manifest @@ -2,9 +2,10 @@ - + WiX Toolset Bootstrapper + true From 9e25a0a1bd113d92a3cdf9dff29a1cec27accb5c Mon Sep 17 00:00:00 2001 From: Bob Arnson Date: Thu, 23 Mar 2017 15:56:59 -0400 Subject: [PATCH 11/17] Format log prefix to support variable references. --- src/burn/engine/logging.cpp | 7 ++++++- 1 file changed, 6 insertions(+), 1 deletion(-) diff --git a/src/burn/engine/logging.cpp b/src/burn/engine/logging.cpp index 971e7c492..7a383f515 100644 --- a/src/burn/engine/logging.cpp +++ b/src/burn/engine/logging.cpp @@ -33,6 +33,7 @@ extern "C" HRESULT LoggingOpen( { HRESULT hr = S_OK; LPWSTR sczLoggingBaseFolder = NULL; + LPWSTR sczFormattedPrefix = NULL; // Check if the logging policy is set and configure the logging appropriately. CheckLoggingPolicy(&pLog->dwAttributes); @@ -108,8 +109,11 @@ extern "C" HRESULT LoggingOpen( hr = GetNonSessionSpecificTempFolder(&sczLoggingBaseFolder); ExitOnFailure(hr, "Failed to get non-session specific TEMP folder."); + // don't fail if formatting fails for some reason + VariableFormatString(pVariables, pLog->sczPrefix, &sczFormattedPrefix, NULL); + // Best effort to open default logging. - hr = LogOpen(sczLoggingBaseFolder, pLog->sczPrefix, NULL, pLog->sczExtension, FALSE, FALSE, &pLog->sczPath); + hr = LogOpen(sczLoggingBaseFolder, sczFormattedPrefix ? sczFormattedPrefix : pLog->sczPrefix, NULL, pLog->sczExtension, FALSE, FALSE, &pLog->sczPath); if (FAILED(hr)) { LogDisable(); @@ -154,6 +158,7 @@ extern "C" HRESULT LoggingOpen( } LExit: + ReleaseStr(sczFormattedPrefix); ReleaseStr(sczLoggingBaseFolder); return hr; From 2e8f5f360529003f94d4d9ea1d9309909d23e324 Mon Sep 17 00:00:00 2001 From: Bob Arnson Date: Wed, 29 Mar 2017 14:46:55 -0400 Subject: [PATCH 12/17] Implement RepairCondition on ExePackage, MsiPackage, and MspPackage. --- src/burn/engine/package.cpp | 8 +++++++ src/burn/engine/package.h | 1 + src/burn/engine/plan.cpp | 37 +++++++++++++++++++++++++------ src/burn/engine/plan.h | 3 ++- src/tools/wix/Binder.cs | 5 +++++ src/tools/wix/ChainPackageInfo.cs | 8 +++++++ src/tools/wix/Compiler.cs | 7 ++++++ src/tools/wix/Data/tables.xml | 2 ++ src/tools/wix/Xsd/wix.xsd | 8 +++++++ 9 files changed, 71 insertions(+), 8 deletions(-) diff --git a/src/burn/engine/package.cpp b/src/burn/engine/package.cpp index 6b271ea9e..fb1669743 100644 --- a/src/burn/engine/package.cpp +++ b/src/burn/engine/package.cpp @@ -178,6 +178,13 @@ extern "C" HRESULT PackagesParseFromXml( ExitOnFailure(hr, "Failed to get @InstallCondition."); } + // @RepairCondition + hr = XmlGetAttributeEx(pixnNode, L"RepairCondition", &pPackage->sczRepairCondition); + if (E_NOTFOUND != hr) + { + ExitOnFailure(hr, "Failed to get @RepairCondition."); + } + // @RollbackBoundaryForward hr = XmlGetAttributeEx(pixnNode, L"RollbackBoundaryForward", &scz); if (E_NOTFOUND != hr) @@ -313,6 +320,7 @@ extern "C" void PackageUninitialize( ReleaseStr(pPackage->sczRollbackLogPathVariable); ReleaseStr(pPackage->sczInstallCondition); ReleaseStr(pPackage->sczRollbackInstallCondition); + ReleaseStr(pPackage->sczRepairCondition); ReleaseStr(pPackage->sczCacheId); if (pPackage->rgDependencyProviders) diff --git a/src/burn/engine/package.h b/src/burn/engine/package.h index 4bddb7f8c..bd75ef91d 100644 --- a/src/burn/engine/package.h +++ b/src/burn/engine/package.h @@ -170,6 +170,7 @@ typedef struct _BURN_PACKAGE LPWSTR sczInstallCondition; LPWSTR sczRollbackInstallCondition; + LPWSTR sczRepairCondition; BOOL fPerMachine; BOOL fUninstallable; BOOL fVital; diff --git a/src/burn/engine/plan.cpp b/src/burn/engine/plan.cpp index 1402fd7f8..95a68fdc6 100644 --- a/src/burn/engine/plan.cpp +++ b/src/burn/engine/plan.cpp @@ -297,6 +297,7 @@ extern "C" HRESULT PlanDefaultPackageRequestState( __in BOOTSTRAPPER_ACTION action, __in BURN_VARIABLES* pVariables, __in_z_opt LPCWSTR wzInstallCondition, + __in_z_opt LPCWSTR wzRepairCondition, __in BOOTSTRAPPER_RELATION_TYPE relationType, __out BOOTSTRAPPER_REQUEST_STATE* pRequestState ) @@ -339,14 +340,36 @@ extern "C" HRESULT PlanDefaultPackageRequestState( hr = GetActionDefaultRequestState(action, fPermanent, currentState, &defaultRequestState); ExitOnFailure(hr, "Failed to get default request state for action."); - // If there is an install condition (and we're doing an install) evaluate the condition - // to determine whether to use the default request state or make the package absent. - if (BOOTSTRAPPER_ACTION_UNINSTALL != action && wzInstallCondition && *wzInstallCondition) + // If we're not doing an uninstall, evaluate action conditions to override the default + // request state. + if (BOOTSTRAPPER_ACTION_UNINSTALL != action) { - hr = ConditionEvaluate(pVariables, wzInstallCondition, &fCondition); - ExitOnFailure(hr, "Failed to evaluate install condition."); + BOOTSTRAPPER_REQUEST_STATE requestState = defaultRequestState; - *pRequestState = fCondition ? defaultRequestState : BOOTSTRAPPER_REQUEST_STATE_ABSENT; + if (wzInstallCondition && *wzInstallCondition) + { + hr = ConditionEvaluate(pVariables, wzInstallCondition, &fCondition); + ExitOnFailure(hr, "Failed to evaluate install condition."); + + if (!fCondition) + { + requestState = BOOTSTRAPPER_REQUEST_STATE_ABSENT; + } + } + + // If the install condition was false, stick with that. + if (BOOTSTRAPPER_REQUEST_STATE_ABSENT != requestState && wzRepairCondition && *wzRepairCondition) + { + hr = ConditionEvaluate(pVariables, wzRepairCondition, &fCondition); + ExitOnFailure(hr, "Failed to evaluate repair condition."); + + if (fCondition) + { + requestState = BOOTSTRAPPER_REQUEST_STATE_REPAIR; + } + } + + *pRequestState = requestState; } else // just set the package to the default request state. { @@ -839,7 +862,7 @@ static HRESULT ProcessPackage( BURN_ROLLBACK_BOUNDARY* pEffectiveRollbackBoundary = NULL; // Remember the default requested state so the engine doesn't get blamed for planning the wrong thing if the UX changes it. - hr = PlanDefaultPackageRequestState(pPackage->type, pPackage->currentState, !pPackage->fUninstallable, pPlan->action, pVariables, pPackage->sczInstallCondition, relationType, &pPackage->defaultRequested); + hr = PlanDefaultPackageRequestState(pPackage->type, pPackage->currentState, !pPackage->fUninstallable, pPlan->action, pVariables, pPackage->sczInstallCondition, pPackage->sczRepairCondition, relationType, &pPackage->defaultRequested); ExitOnFailure(hr, "Failed to set default package state."); pPackage->requested = pPackage->defaultRequested; diff --git a/src/burn/engine/plan.h b/src/burn/engine/plan.h index 21ff876b8..8094158d6 100644 --- a/src/burn/engine/plan.h +++ b/src/burn/engine/plan.h @@ -357,9 +357,10 @@ HRESULT PlanDefaultPackageRequestState( __in BOOTSTRAPPER_ACTION action, __in BURN_VARIABLES* pVariables, __in_z_opt LPCWSTR wzInstallCondition, + __in_z_opt LPCWSTR wzRepairCondition, __in BOOTSTRAPPER_RELATION_TYPE relationType, __out BOOTSTRAPPER_REQUEST_STATE* pRequestState - ); +); HRESULT PlanLayoutBundle( __in BURN_PLAN* pPlan, __in_z LPCWSTR wzExecutableName, diff --git a/src/tools/wix/Binder.cs b/src/tools/wix/Binder.cs index fdd0aec0f..3b1ae056b 100644 --- a/src/tools/wix/Binder.cs +++ b/src/tools/wix/Binder.cs @@ -4570,6 +4570,11 @@ private void CreateBurnManifest(string outputPath, WixBundleRow bundleInfo, WixB writer.WriteAttributeString("InstallCondition", package.InstallCondition); } + if (!String.IsNullOrEmpty(package.RepairCondition)) + { + writer.WriteAttributeString("RepairCondition", package.RepairCondition); + } + if (Compiler.ChainPackageType.Exe == package.ChainPackageType) { writer.WriteAttributeString("DetectCondition", package.DetectCondition); diff --git a/src/tools/wix/ChainPackageInfo.cs b/src/tools/wix/ChainPackageInfo.cs index 1ec009904..cca4181e8 100644 --- a/src/tools/wix/ChainPackageInfo.cs +++ b/src/tools/wix/ChainPackageInfo.cs @@ -51,6 +51,7 @@ public ChainPackageInfo(Row chainPackageRow, Table wixGroupTable, Dictionary Payloads { get; private set; } public List RelatedPackages { get; private set; } diff --git a/src/tools/wix/Compiler.cs b/src/tools/wix/Compiler.cs index e8c95a5c9..0d2dac2bd 100644 --- a/src/tools/wix/Compiler.cs +++ b/src/tools/wix/Compiler.cs @@ -21446,6 +21446,7 @@ private string ParseChainPackage(XmlNode node, ChainPackageType packageType, Com string id = null; string after = null; string installCondition = null; + string repairCondition = null; YesNoAlwaysType cache = YesNoAlwaysType.NotSet; string cacheId = null; string description = null; @@ -21597,6 +21598,10 @@ private string ParseChainPackage(XmlNode node, ChainPackageType packageType, Com slipstream = this.core.GetAttributeYesNoValue(sourceLineNumbers, attrib); allowed = (packageType == ChainPackageType.Msp); break; + case "RepairCondition": + repairCondition = this.core.GetAttributeValue(sourceLineNumbers, attrib); + allowed = (packageType == ChainPackageType.Exe || packageType == ChainPackageType.Msi || packageType == ChainPackageType.Msp); + break; default: allowed = false; break; @@ -21914,6 +21919,8 @@ private string ParseChainPackage(XmlNode node, ChainPackageType packageType, Com row[22] = (YesNoType.Yes == displayInternalUI) ? 1 : 0; } + row[23] = repairCondition; + this.CreateChainPackageMetaRows(sourceLineNumbers, parentType, parentId, ComplexReferenceChildType.Package, id, previousType, previousId, after); } return id; diff --git a/src/tools/wix/Data/tables.xml b/src/tools/wix/Data/tables.xml index f7cc92118..eca907bfc 100644 --- a/src/tools/wix/Data/tables.xml +++ b/src/tools/wix/Data/tables.xml @@ -1724,6 +1724,7 @@ + @@ -1759,6 +1760,7 @@ + A condition to evaluate before installing the package. The package will only be installed if the condition evaluates to true. If the condition evaluates to false and the bundle is being installed, repaired, or modified, the package will be uninstalled. + + + + A condition to request that an MSI, MSP, or Exe package be repaired when the bundle is being installed, repaired, or modified. + If the condition is omitted or evaluates to false, the package will get its default behavior. + + + Whether to cache the package. The default is "yes". From 12b3679645517b193c0388ff01ca23fabc0a0dc9 Mon Sep 17 00:00:00 2001 From: Bob Arnson Date: Fri, 31 Mar 2017 14:09:47 -0400 Subject: [PATCH 13/17] Let superseded patches repair by default. --- src/burn/engine/plan.cpp | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/burn/engine/plan.cpp b/src/burn/engine/plan.cpp index 95a68fdc6..1602a7165 100644 --- a/src/burn/engine/plan.cpp +++ b/src/burn/engine/plan.cpp @@ -323,10 +323,10 @@ extern "C" HRESULT PlanDefaultPackageRequestState( *pRequestState = BOOTSTRAPPER_REQUEST_STATE_NONE; } } - else if (BOOTSTRAPPER_PACKAGE_STATE_SUPERSEDED == currentState && BOOTSTRAPPER_ACTION_UNINSTALL != action) + else if (BOOTSTRAPPER_PACKAGE_STATE_SUPERSEDED == currentState && BOOTSTRAPPER_ACTION_UNINSTALL != action && !(BURN_PACKAGE_TYPE_MSP == packageType && BOOTSTRAPPER_ACTION_REPAIR == action)) { // Superseded means the package is on the machine but not active, so only uninstall operations are allowed. - // All other operations do nothing. + // All other operations do nothing except for patches, which can be repaired. *pRequestState = BOOTSTRAPPER_REQUEST_STATE_NONE; } else if (BOOTSTRAPPER_PACKAGE_STATE_OBSOLETE == currentState && !(BOOTSTRAPPER_ACTION_UNINSTALL == action && BURN_PACKAGE_TYPE_MSP == packageType)) From adecc82f37808e9b33d7fe55a8fce45131da8169 Mon Sep 17 00:00:00 2001 From: Bob Arnson Date: Wed, 26 Apr 2017 16:33:19 -0400 Subject: [PATCH 14/17] - Add IBootstrapperEngine::SetUpdateSource to let a BA update the feed URL. - Pass OnDetectUpdateBegin a copy of the feed source, to preserve the value during the call in case it's updated by a call to IBootstrapperEngine::SetUpdateSource. --- src/burn/engine/EngineForApplication.cpp | 30 ++++++++++++++++++- src/burn/engine/detect.cpp | 8 ++++- src/burn/inc/IBootstrapperEngine.h | 6 ++++ src/ext/BalExtension/mba/core/Engine.cs | 13 +++++++- .../mba/core/IBootstrapperEngine.cs | 6 ++++ 5 files changed, 60 insertions(+), 3 deletions(-) diff --git a/src/burn/engine/EngineForApplication.cpp b/src/burn/engine/EngineForApplication.cpp index 9b3d67860..387a1b506 100644 --- a/src/burn/engine/EngineForApplication.cpp +++ b/src/burn/engine/EngineForApplication.cpp @@ -742,7 +742,7 @@ class CEngineForApplication : public IBootstrapperEngine, public IMarshal __in_z LPCWSTR wzApprovedExeForElevationId, __in_z_opt LPCWSTR wzArguments, __in DWORD dwWaitForInputIdleTimeout - ) + ) { HRESULT hr = S_OK; BURN_APPROVED_EXE* pApprovedExe = NULL; @@ -797,6 +797,34 @@ class CEngineForApplication : public IBootstrapperEngine, public IMarshal return hr; } + virtual STDMETHODIMP SetUpdateSource( + __in_z LPCWSTR wzUrl, + __in_z_opt LPWSTR /*wzUser*/, + __in_z_opt LPWSTR /*wzPassword*/ + ) + { + HRESULT hr = S_OK; + + ::EnterCriticalSection(&m_pEngineState->csActive); + UserExperienceDeactivateEngine(&m_pEngineState->userExperience); + + if (wzUrl && *wzUrl) + { + hr = StrAllocString(&m_pEngineState->update.sczUpdateSource, wzUrl, 0); + ExitOnFailure(hr, "Failed to set feed download URL."); + } + else // no URL provided means clear out the whole download source. + { + ReleaseNullStr(m_pEngineState->update.sczUpdateSource); + } + + LExit: + UserExperienceActivateEngine(&m_pEngineState->userExperience, NULL); + ::LeaveCriticalSection(&m_pEngineState->csActive); + + return hr; + } + public: // IMarshal virtual STDMETHODIMP GetUnmarshalClass( __in REFIID /*riid*/, diff --git a/src/burn/engine/detect.cpp b/src/burn/engine/detect.cpp index e226596d2..cda20b0d8 100644 --- a/src/burn/engine/detect.cpp +++ b/src/burn/engine/detect.cpp @@ -219,6 +219,7 @@ extern "C" HRESULT DetectUpdate( HRESULT hr = S_OK; int nResult = IDNOACTION; BOOL fBeginCalled = FALSE; + LPWSTR sczOriginalSource = NULL; // If no update source was specified, skip update detection. if (!pUpdate->sczUpdateSource || !*pUpdate->sczUpdateSource) @@ -228,7 +229,10 @@ extern "C" HRESULT DetectUpdate( fBeginCalled = TRUE; - nResult = pUX->pUserExperience->OnDetectUpdateBegin(pUpdate->sczUpdateSource, IDNOACTION); + hr = StrAllocString(&sczOriginalSource, pUpdate->sczUpdateSource, 0); + ExitOnFailure(hr, "Failed to duplicate update feed source."); + + nResult = pUX->pUserExperience->OnDetectUpdateBegin(sczOriginalSource, IDNOACTION); hr = UserExperienceInterpretResult(pUX, MB_OKCANCEL, nResult); ExitOnRootFailure(hr, "UX aborted detect update begin."); @@ -243,6 +247,8 @@ extern "C" HRESULT DetectUpdate( } LExit: + ReleaseStr(sczOriginalSource); + if (fBeginCalled) { pUX->pUserExperience->OnDetectUpdateComplete(hr, pUpdate->fUpdateAvailable ? pUpdate->sczUpdateSource : NULL); diff --git a/src/burn/inc/IBootstrapperEngine.h b/src/burn/inc/IBootstrapperEngine.h index f3fd8b616..608803469 100644 --- a/src/burn/inc/IBootstrapperEngine.h +++ b/src/burn/inc/IBootstrapperEngine.h @@ -220,4 +220,10 @@ DECLARE_INTERFACE_IID_(IBootstrapperEngine, IUnknown, "6480D616-27A0-44D7-905B-8 __in_z_opt LPCWSTR wzArguments, __in DWORD dwWaitForInputIdleTimeout ) = 0; + + STDMETHOD(SetUpdateSource)( + __in_z LPCWSTR wzUrl, + __in_z_opt LPWSTR wzUser, + __in_z_opt LPWSTR wzPassword + ) = 0; }; diff --git a/src/ext/BalExtension/mba/core/Engine.cs b/src/ext/BalExtension/mba/core/Engine.cs index 047b27ae8..afb1eab96 100644 --- a/src/ext/BalExtension/mba/core/Engine.cs +++ b/src/ext/BalExtension/mba/core/Engine.cs @@ -353,7 +353,7 @@ public void LaunchApprovedExe(IntPtr hwndParent, string approvedExeForElevationI /// The parent window of the elevation dialog (if the engine hasn't elevated yet). /// Id of the ApprovedExeForElevation element specified when the bundle was authored. /// Optional arguments. - /// Timeout in milliseconds. When set to something other than zero, the engine will call WaitForInputIdle for the new process with this timeout before calling OnLaunchApprovedExeComplete. + /// Timeout in milliseconds. When set to something other than zero, the engine will call WaitForInputIdle for the new process with this timeout before calling OnLaunchApprovedExeComplete. public void LaunchApprovedExe(IntPtr hwndParent, string approvedExeForElevationId, string arguments, int waitForInputIdleTimeout) { engine.LaunchApprovedExe(hwndParent, approvedExeForElevationId, arguments, waitForInputIdleTimeout); @@ -415,6 +415,17 @@ public void SetDownloadSource(string packageOrContainerId, string payloadId, str this.engine.SetDownloadSource(packageOrContainerId, payloadId, url, user, password); } + /// + /// Set the new download URL for the bundle update feed. + /// + /// The new url. + /// The user name for proxy authentication. + /// The password for proxy authentication. + public void SetUpdateSource(string url, string user, string password) + { + this.engine.SetUpdateSource(url, user, password); + } + /// /// Sends error message when embedded. /// diff --git a/src/ext/BalExtension/mba/core/IBootstrapperEngine.cs b/src/ext/BalExtension/mba/core/IBootstrapperEngine.cs index 0070f008a..51190779b 100644 --- a/src/ext/BalExtension/mba/core/IBootstrapperEngine.cs +++ b/src/ext/BalExtension/mba/core/IBootstrapperEngine.cs @@ -143,6 +143,12 @@ void LaunchApprovedExe( [MarshalAs(UnmanagedType.LPWStr)] string wzArguments, [MarshalAs(UnmanagedType.U4)] int dwWaitForInputIdleTimeout ); + + void SetUpdateSource( + [MarshalAs(UnmanagedType.LPWStr)] string wzUrl, + [MarshalAs(UnmanagedType.LPWStr)] string wzUser, + [MarshalAs(UnmanagedType.LPWStr)] string wzPassword + ); } /// From 2f9bed317e920d6ea71537ff7301df55affe520d Mon Sep 17 00:00:00 2001 From: Bob Arnson Date: Tue, 30 May 2017 16:39:45 -0400 Subject: [PATCH 15/17] Fix bad formatting types in a Burn engine message. --- src/burn/engine/engine.mc | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/burn/engine/engine.mc b/src/burn/engine/engine.mc index e0579e57e..84c782502 100644 --- a/src/burn/engine/engine.mc +++ b/src/burn/engine/engine.mc @@ -244,7 +244,7 @@ MessageId=152 Severity=Error SymbolicName=MSG_FAILED_READ_RELATED_PACKAGE_LANGUAGE Language=English -Detected related package: %2!ls!, but failed to read language: %3!hs!, error: 0x%1!x! +Detected related package: %2!ls!, but failed to read language: %3!ls!, error: 0x%1!ls! . MessageId=170 From 9e1da45bcb54c3863eec6f303309da9abdbcb708 Mon Sep 17 00:00:00 2001 From: Bob Arnson Date: Mon, 5 Jun 2017 18:18:18 -0400 Subject: [PATCH 16/17] Reverse checks to look for per-machine package registration first, before per-user unmanaged packages. Resolves wixbug:5479. --- src/burn/engine/msiengine.cpp | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) diff --git a/src/burn/engine/msiengine.cpp b/src/burn/engine/msiengine.cpp index feec53ca7..9a317ca63 100644 --- a/src/burn/engine/msiengine.cpp +++ b/src/burn/engine/msiengine.cpp @@ -491,19 +491,19 @@ extern "C" HRESULT MsiEngineDetectPackage( } // get product version - hr = WiuGetProductInfoEx(wzProductCode, NULL, MSIINSTALLCONTEXT_USERUNMANAGED, INSTALLPROPERTY_VERSIONSTRING, &sczInstalledVersion); + hr = WiuGetProductInfoEx(wzProductCode, NULL, MSIINSTALLCONTEXT_MACHINE, INSTALLPROPERTY_VERSIONSTRING, &sczInstalledVersion); if (HRESULT_FROM_WIN32(ERROR_UNKNOWN_PRODUCT) != hr && HRESULT_FROM_WIN32(ERROR_UNKNOWN_PROPERTY) != hr) { - ExitOnFailure1(hr, "Failed to get version for product in user unmanaged context: %ls", wzProductCode); - fPerMachine = FALSE; + ExitOnFailure1(hr, "Failed to get version for product in machine context: %ls", wzProductCode); + fPerMachine = TRUE; } else { - hr = WiuGetProductInfoEx(wzProductCode, NULL, MSIINSTALLCONTEXT_MACHINE, INSTALLPROPERTY_VERSIONSTRING, &sczInstalledVersion); + hr = WiuGetProductInfoEx(wzProductCode, NULL, MSIINSTALLCONTEXT_USERUNMANAGED, INSTALLPROPERTY_VERSIONSTRING, &sczInstalledVersion); if (HRESULT_FROM_WIN32(ERROR_UNKNOWN_PRODUCT) != hr && HRESULT_FROM_WIN32(ERROR_UNKNOWN_PROPERTY) != hr) { - ExitOnFailure1(hr, "Failed to get version for product in machine context: %ls", wzProductCode); - fPerMachine = TRUE; + ExitOnFailure1(hr, "Failed to get version for product in user unmanaged context: %ls", wzProductCode); + fPerMachine = FALSE; } else { From 5fb89eaff92e543bc35b1269aa0507fcdbe5cf69 Mon Sep 17 00:00:00 2001 From: Bob Arnson Date: Mon, 10 Jul 2017 13:27:41 -0400 Subject: [PATCH 17/17] Add debug logging mode. Add debug logging around WM_ENDSESSION message to help diagnose problems with elevated process dying (zd998). --- src/burn/engine/core.h | 1 + src/burn/engine/engine.cpp | 11 +++++++++++ src/burn/engine/uithread.cpp | 11 +++++++++++ 3 files changed, 23 insertions(+) diff --git a/src/burn/engine/core.h b/src/burn/engine/core.h index 6a6da2b1c..d99bda1c6 100644 --- a/src/burn/engine/core.h +++ b/src/burn/engine/core.h @@ -119,6 +119,7 @@ typedef struct _BURN_ENGINE_STATE BURN_AU_PAUSE_ACTION automaticUpdates; DWORD dwElevatedLoggingTlsId; + BOOL fDebugLogging; LPWSTR sczBundleEngineWorkingPath; BURN_PIPE_CONNECTION companionConnection; diff --git a/src/burn/engine/engine.cpp b/src/burn/engine/engine.cpp index 228f46211..bb014a905 100644 --- a/src/burn/engine/engine.cpp +++ b/src/burn/engine/engine.cpp @@ -593,6 +593,12 @@ static HRESULT RunElevated( ExitWithLastError(hr, "Failed to set elevated pipe into thread local storage for logging."); } + if (0 < ::GetEnvironmentVariableW(L"WIX_BURN_DEBUG_LOGGING", NULL, 0)) + { + pEngineState->fDebugLogging = TRUE; + LogSetLevel(REPORT_DEBUG, FALSE); + } + LogRedirect(RedirectLoggingOverPipe, pEngineState); // Create a top-level window to prevent shutting down the elevated process. @@ -801,6 +807,11 @@ static HRESULT DAPI RedirectLoggingOverPipe( SIZE_T cbData = 0; DWORD dwResult = 0; + if (pEngineState->fDebugLogging) + { + ::OutputDebugStringA(szString); + } + // Prevent this function from being called recursively. if (s_fCurrentlyLoggingToPipe) { diff --git a/src/burn/engine/uithread.cpp b/src/burn/engine/uithread.cpp index f0babe75b..cb3917721 100644 --- a/src/burn/engine/uithread.cpp +++ b/src/burn/engine/uithread.cpp @@ -207,6 +207,17 @@ static LRESULT CALLBACK WndProc( return fRet; } + case WM_ENDSESSION: + { + BOOL fEnding = static_cast(wParam); + DWORD dwEndSession = static_cast(lParam); + BOOL fMustClose = ENDSESSION_CLOSEAPP & dwEndSession; + BOOL fCritical = ENDSESSION_CRITICAL & dwEndSession; + BOOL fLoggingOff = ENDSESSION_LOGOFF & dwEndSession; + LogStringLine(REPORT_DEBUG, "WM_ENDSESSION: Session ending: %hs, must close: %hs, critical: %hs, logging off: %hs", LoggingBoolToString(fEnding), LoggingBoolToString(fMustClose), LoggingBoolToString(fCritical), LoggingBoolToString(fLoggingOff)); + return 0; + } + case WM_DESTROY: ::PostQuitMessage(0); return 0;