From 13c4becf524dbd12b92f099320726aa0b59f3bbc Mon Sep 17 00:00:00 2001 From: Rob Mensching Date: Sun, 11 Apr 2021 12:22:57 -0700 Subject: Add Condition to RemoveFoldersEx --- src/ca/RemoveFoldersEx.cpp | 62 +++++++++++++++++++--- .../WixToolsetTest.Util/UtilExtensionFixture.cs | 4 +- src/wixext/Symbols/WixRemoveFolderExSymbol.cs | 21 ++++++-- src/wixext/UtilCompiler.cs | 28 +++++----- src/wixext/UtilTableDefinitions.cs | 1 + 5 files changed, 87 insertions(+), 29 deletions(-) (limited to 'src') diff --git a/src/ca/RemoveFoldersEx.cpp b/src/ca/RemoveFoldersEx.cpp index ce64c2c2..cbc7f4bb 100644 --- a/src/ca/RemoveFoldersEx.cpp +++ b/src/ca/RemoveFoldersEx.cpp @@ -2,8 +2,11 @@ #include "precomp.h" -LPCWSTR vcsRemoveFolderExQuery = L"SELECT `Wix4RemoveFolderEx`, `Component_`, `Property`, `InstallMode` FROM `Wix4RemoveFolderEx`"; -enum eRemoveFolderExQuery { rfqId = 1, rfqComponent, rfqProperty, feqMode }; +LPCWSTR vcsRemoveFolderExQuery = + L"SELECT `Wix4RemoveFolderEx`, `Component_`, `Property`, `InstallMode`, `WixRemoveFolderEx`.`Condition`, `Component`.`Attributes`" + L"FROM `Wix4RemoveFolderEx``,`Component` " + L"WHERE `Wix4RemoveFolderEx`.`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); ExitOnFailure(hr, "Failed to allocate file search string in path: %S", wzPath); @@ -34,7 +44,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 @@ -55,7 +65,8 @@ static HRESULT RecursePath( hr = StrAllocFormatted(&sczNext, L"%s%s\\", wzPath, wfd.cFileName); ExitOnFailure(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); ExitOnFailure(hr, "Failed to recurse path: %S", sczNext); } while (::FindNextFileW(hFind, &wfd)); @@ -81,10 +92,10 @@ static HRESULT RecursePath( // Add the row to remove any files and another row to remove the folder. hr = WcaAddTempRecord(phTable, phColumns, L"RemoveFile", NULL, 1, 5, L"RfxFiles", wzComponent, L"*.*", sczProperty, iMode); - ExitOnFailure(hr, "Failed to add row to remove all files for Wix4RemoveFolderEx row: %S under path:", wzId, wzPath); + ExitOnFailure(hr, "Failed to add row to remove all files for Wix4RemoveFolderEx row: %ls under path: %ls", wzId, wzPath); hr = WcaAddTempRecord(phTable, phColumns, L"RemoveFile", NULL, 1, 5, L"RfxFolder", wzComponent, NULL, sczProperty, iMode); - ExitOnFailure(hr, "Failed to add row to remove folder for Wix4RemoveFolderEx row: %S under path: %S", wzId, wzPath); + ExitOnFailure(hr, "Failed to add row to remove folder for Wix4RemoveFolderEx row: %ls under path: %ls", wzId, wzPath); LExit: if (INVALID_HANDLE_VALUE != hFind) @@ -92,6 +103,11 @@ LExit: ::FindClose(hFind); } + if (fDisableWow64Redirection) + { + WcaRevertWow64FSRedirection(); + } + ReleaseStr(sczNext); ReleaseStr(sczProperty); ReleaseStr(sczSearch); @@ -110,9 +126,12 @@ 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; + int iComponentAttributes; + BOOL f64BitComponent = FALSE; DWORD dwCounter = 0; DWORD_PTR cchLen = 0; MSIHANDLE hTable = NULL; @@ -121,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"Wix4RemoveFolderEx")) { @@ -137,18 +158,40 @@ 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); ExitOnFailure(hr, "Failed to resolve remove folder property: %S for row: %S", sczProperty, sczId); + hr = WcaGetRecordInteger(hRec, rfqComponentAttributes, &iComponentAttributes); + ExitOnFailure(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)); @@ -164,7 +207,7 @@ extern "C" UINT WINAPI WixRemoveFoldersEx( ExitOnFailure(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); ExitOnFailure(hr, "Failed while navigating path: %S for row: %S", sczPath, sczId); } @@ -176,6 +219,8 @@ extern "C" UINT WINAPI WixRemoveFoldersEx( ExitOnFailure(hr, "Failure occured while processing Wix4RemoveFolderEx table"); LExit: + WcaFinalizeWow64(); + if (hColumns) { ::MsiCloseHandle(hColumns); @@ -190,6 +235,7 @@ LExit: ReleaseStr(sczPath); ReleaseStr(sczProperty); ReleaseStr(sczComponent); + ReleaseStr(sczCondition); ReleaseStr(sczId); DWORD er = SUCCEEDED(hr) ? ERROR_SUCCESS : ERROR_INSTALL_FAILURE; diff --git a/src/test/WixToolsetTest.Util/UtilExtensionFixture.cs b/src/test/WixToolsetTest.Util/UtilExtensionFixture.cs index b2af91b3..9c32ebc2 100644 --- a/src/test/WixToolsetTest.Util/UtilExtensionFixture.cs +++ b/src/test/WixToolsetTest.Util/UtilExtensionFixture.cs @@ -139,8 +139,8 @@ namespace WixToolsetTest.Util WixAssert.CompareLineByLine(new[] { "Binary:Wix4UtilCA_X64.047730A5_30FE_4A62_A520_DA9381B8226A\t[Binary data]", - "CustomAction:Wix4RemoveFoldersEx_X64.047730A5_30FE_4A62_A520_DA9381B8226A\t65\tWix4UtilCA_X64.047730A5_30FE_4A62_A520_DA9381B8226A WixRemoveFoldersEx\t", - "Wix4RemoveFolderEx:wrfB3e9CDihkNwm06LohylbJcjZ91w.047730A5_30FE_4A62_A520_DA9381B8226A\tfilh4juyUVjoUcWWtcQmd5L07FoON4.047730A5_30FE_4A62_A520_DA9381B8226A\tRemoveProp.047730A5_30FE_4A62_A520_DA9381B8226A\t3", + "CustomAction:Wix4RemoveFoldersEx_X64.047730A5_30FE_4A62_A520_DA9381B8226A\t65\tWix4UtilCA_X64.047730A5_30FE_4A62_A520_DA9381B8226A\tWixRemoveFoldersEx\t", + "Wix4RemoveFolderEx:wrf5qCm1SE.zp8djrlk78l1IYFXsEw.047730A5_30FE_4A62_A520_DA9381B8226A\tfilh4juyUVjoUcWWtcQmd5L07FoON4.047730A5_30FE_4A62_A520_DA9381B8226A\tRemoveProp.047730A5_30FE_4A62_A520_DA9381B8226A\t3\t", }, results.OrderBy(s => s).ToArray()); } diff --git a/src/wixext/Symbols/WixRemoveFolderExSymbol.cs b/src/wixext/Symbols/WixRemoveFolderExSymbol.cs index 0c50ab8e..86352b6c 100644 --- a/src/wixext/Symbols/WixRemoveFolderExSymbol.cs +++ b/src/wixext/Symbols/WixRemoveFolderExSymbol.cs @@ -14,6 +14,7 @@ namespace WixToolset.Util new IntermediateFieldDefinition(nameof(WixRemoveFolderExSymbolFields.ComponentRef), IntermediateFieldType.String), new IntermediateFieldDefinition(nameof(WixRemoveFolderExSymbolFields.Property), IntermediateFieldType.String), new IntermediateFieldDefinition(nameof(WixRemoveFolderExSymbolFields.InstallMode), IntermediateFieldType.Number), + new IntermediateFieldDefinition(nameof(WixRemoveFolderExSymbolFields.Condition), IntermediateFieldType.String), }, typeof(WixRemoveFolderExSymbol)); } @@ -28,6 +29,14 @@ namespace WixToolset.Util.Symbols ComponentRef, Property, InstallMode, + Condition, + } + + public enum WixRemoveFolderExInstallMode + { + Install = 1, + Uninstall = 2, + Both = 3, } public class WixRemoveFolderExSymbol : IntermediateSymbol @@ -54,10 +63,16 @@ namespace WixToolset.Util.Symbols set => this.Set((int)WixRemoveFolderExSymbolFields.Property, value); } - public int InstallMode + public WixRemoveFolderExInstallMode InstallMode + { + get => (WixRemoveFolderExInstallMode)this.Fields[(int)WixRemoveFolderExSymbolFields.InstallMode].AsNumber(); + set => this.Set((int)WixRemoveFolderExSymbolFields.InstallMode, (int)value); + } + + public string Condition { - get => this.Fields[(int)WixRemoveFolderExSymbolFields.InstallMode].AsNumber(); - set => this.Set((int)WixRemoveFolderExSymbolFields.InstallMode, value); + get => this.Fields[(int)WixRemoveFolderExSymbolFields.Condition].AsString(); + set => this.Set((int)WixRemoveFolderExSymbolFields.Condition, value); } } } \ No newline at end of file diff --git a/src/wixext/UtilCompiler.cs b/src/wixext/UtilCompiler.cs index 12213e63..63f2b469 100644 --- a/src/wixext/UtilCompiler.cs +++ b/src/wixext/UtilCompiler.cs @@ -41,13 +41,6 @@ namespace WixToolset.Util Compatible, } - internal enum WixRemoveFolderExOn - { - Install = 1, - Uninstall = 2, - Both = 3, - } - private static readonly Regex FindPropertyBrackets = new Regex(@"\[(?!\\|\])|(? "http://wixtoolset.org/schemas/v4/wxs/util"; @@ -2812,8 +2805,9 @@ namespace WixToolset.Util { var sourceLineNumbers = this.ParseHelper.GetSourceLineNumbers(element); Identifier id = null; - var on = (int)WixRemoveFolderExOn.Uninstall; + var mode = WixRemoveFolderExInstallMode.Uninstall; string property = null; + string condition = null; foreach (var attrib in element.Attributes()) { @@ -2821,6 +2815,9 @@ namespace WixToolset.Util { switch (attrib.Name.LocalName) { + case "Condition": + condition = this.ParseHelper.GetAttributeValue(sourceLineNumbers, attrib); + break; case "Id": id = this.ParseHelper.GetAttributeIdentifier(sourceLineNumbers, attrib); break; @@ -2828,24 +2825,22 @@ namespace WixToolset.Util var onValue = this.ParseHelper.GetAttributeValue(sourceLineNumbers, attrib); if (onValue.Length == 0) { - on = CompilerConstants.IllegalInteger; } else { switch (onValue) { case "install": - on = (int)WixRemoveFolderExOn.Install; + mode = WixRemoveFolderExInstallMode.Install; break; case "uninstall": - on = (int)WixRemoveFolderExOn.Uninstall; + mode = WixRemoveFolderExInstallMode.Uninstall; break; case "both": - on = (int)WixRemoveFolderExOn.Both; + mode = WixRemoveFolderExInstallMode.Both; break; default: this.Messaging.Write(ErrorMessages.IllegalAttributeValue(sourceLineNumbers, element.Name.LocalName, "On", onValue, "install", "uninstall", "both")); - on = CompilerConstants.IllegalInteger; break; } } @@ -2869,9 +2864,9 @@ namespace WixToolset.Util this.Messaging.Write(ErrorMessages.ExpectedAttribute(sourceLineNumbers, element.Name.LocalName, "Property")); } - if (null == id) + if (id == null) { - id = this.ParseHelper.CreateIdentifier("wrf", componentId, property, on.ToString(CultureInfo.InvariantCulture.NumberFormat)); + id = this.ParseHelper.CreateIdentifier("wrf", componentId, property, mode.ToString()); } this.ParseHelper.ParseForExtensionElements(this.Context.Extensions, intermediate, section, element); @@ -2884,7 +2879,8 @@ namespace WixToolset.Util { ComponentRef = componentId, Property = property, - InstallMode = on, + InstallMode = mode, + Condition = condition }); this.ParseHelper.EnsureTable(section, sourceLineNumbers, "RemoveFile"); diff --git a/src/wixext/UtilTableDefinitions.cs b/src/wixext/UtilTableDefinitions.cs index eff5aaf6..fd09367a 100644 --- a/src/wixext/UtilTableDefinitions.cs +++ b/src/wixext/UtilTableDefinitions.cs @@ -33,6 +33,7 @@ namespace WixToolset.Util new ColumnDefinition("Component_", ColumnType.String, 72, primaryKey: false, nullable: false, ColumnCategory.Identifier, keyTable: "Component", keyColumn: 1, description: "Foreign key into the Component table used to determine install state", modularizeType: ColumnModularizeType.Column), new ColumnDefinition("Property", ColumnType.String, 72, primaryKey: false, nullable: false, ColumnCategory.Identifier, description: "Name of Property that contains the root of the directory tree to remove.", modularizeType: ColumnModularizeType.Column), new ColumnDefinition("InstallMode", ColumnType.Number, 2, primaryKey: false, nullable: false, ColumnCategory.Unknown, minValue: 1, maxValue: 3, description: "1 == Remove only when the associated component is being installed (msiInstallStateLocal or msiInstallStateSource), 2 == Remove only when the associated component is being removed (msiInstallStateAbsent), 3 = Remove in either of the above cases."), + new ColumnDefinition("Condition", ColumnType.String, 0, primaryKey: false, nullable: true, ColumnCategory.Condition, description: "Optional expression which skips the removing of folders.", modularizeType: ColumnModularizeType.Condition, forceLocalizable: true), }, symbolIdIsPrimaryKey: true ); -- cgit v1.2.3-55-g6feb