@joebigelow / wix / commits / 13c4becf

Add Condition to RemoveFoldersEx

Rob Mensching committed Apr 11, 2021 at 12:22 UTC 13c4becf524dbd12b92f099320726aa0b59f3bbc
5 files changed +87 -29
src/ca/RemoveFoldersEx.cpp
+54 -8
@@ -2,8 +2,11 @@
2
3 #include "precomp.h"
4
5 -LPCWSTR vcsRemoveFolderExQuery = L"SELECT `Wix4RemoveFolderEx`, `Component_`, `Property`, `InstallMode` FROM `Wix4RemoveFolderEx`";
6 -enum eRemoveFolderExQuery { rfqId = 1, rfqComponent, rfqProperty, feqMode };
5 +LPCWSTR vcsRemoveFolderExQuery =
6 + L"SELECT `Wix4RemoveFolderEx`, `Component_`, `Property`, `InstallMode`, `WixRemoveFolderEx`.`Condition`, `Component`.`Attributes`"
7 + L"FROM `Wix4RemoveFolderEx``,`Component` "
8 + L"WHERE `Wix4RemoveFolderEx`.`Component_`=`Component`.`Component`";
9 +enum eRemoveFolderExQuery { rfqId = 1, rfqComponent, rfqProperty, rfqMode, rfqCondition, rfqComponentAttributes };
10
11 static HRESULT RecursePath(
12 __in_z LPCWSTR wzPath,
@@ -11,6 +14,7 @@ static HRESULT RecursePath(
14 __in_z LPCWSTR wzComponent,
15 __in_z LPCWSTR wzProperty,
16 __in int iMode,
17 + __in BOOL fDisableWow64Redirection,
18 __inout DWORD* pdwCounter,
19 __inout MSIHANDLE* phTable,
20 __inout MSIHANDLE* phColumns
@@ -24,6 +28,12 @@ static HRESULT RecursePath(
28 WIN32_FIND_DATAW wfd;
29 LPWSTR sczNext = NULL;
30
31 + if (fDisableWow64Redirection)
32 + {
33 + hr = WcaDisableWow64FSRedirection();
34 + ExitOnFailure(hr, "Custom action was told to act on a 64-bit component, but was unable to disable filesystem redirection through the Wow64 API.");
35 + }
36 +
37 // First recurse down to all the child directories.
38 hr = StrAllocFormatted(&sczSearch, L"%s*", wzPath);
39 ExitOnFailure(hr, "Failed to allocate file search string in path: %S", wzPath);
@@ -34,7 +44,7 @@ static HRESULT RecursePath(
44 er = ::GetLastError();
45 if (ERROR_PATH_NOT_FOUND == er)
46 {
37 - WcaLog(LOGMSG_STANDARD, "Search path not found: %ls", sczSearch);
47 + WcaLog(LOGMSG_STANDARD, "Search path not found: %ls; skipping", sczSearch);
48 ExitFunction1(hr = S_FALSE);
49 }
50 else
@@ -55,7 +65,8 @@ static HRESULT RecursePath(
65 hr = StrAllocFormatted(&sczNext, L"%s%s\\", wzPath, wfd.cFileName);
66 ExitOnFailure(hr, "Failed to concat filename '%S' to string: %S", wfd.cFileName, wzPath);
67
58 - hr = RecursePath(sczNext, wzId, wzComponent, wzProperty, iMode, pdwCounter, phTable, phColumns);
68 + // Don't re-disable redirection; if it was necessary, we've already done it.
69 + hr = RecursePath(sczNext, wzId, wzComponent, wzProperty, iMode, FALSE, pdwCounter, phTable, phColumns);
70 ExitOnFailure(hr, "Failed to recurse path: %S", sczNext);
71 } while (::FindNextFileW(hFind, &wfd));
72
@@ -81,10 +92,10 @@ static HRESULT RecursePath(
92
93 // Add the row to remove any files and another row to remove the folder.
94 hr = WcaAddTempRecord(phTable, phColumns, L"RemoveFile", NULL, 1, 5, L"RfxFiles", wzComponent, L"*.*", sczProperty, iMode);
84 - ExitOnFailure(hr, "Failed to add row to remove all files for Wix4RemoveFolderEx row: %S under path:", wzId, wzPath);
95 + ExitOnFailure(hr, "Failed to add row to remove all files for Wix4RemoveFolderEx row: %ls under path: %ls", wzId, wzPath);
96
97 hr = WcaAddTempRecord(phTable, phColumns, L"RemoveFile", NULL, 1, 5, L"RfxFolder", wzComponent, NULL, sczProperty, iMode);
87 - ExitOnFailure(hr, "Failed to add row to remove folder for Wix4RemoveFolderEx row: %S under path: %S", wzId, wzPath);
98 + ExitOnFailure(hr, "Failed to add row to remove folder for Wix4RemoveFolderEx row: %ls under path: %ls", wzId, wzPath);
99
100 LExit:
101 if (INVALID_HANDLE_VALUE != hFind)
@@ -92,6 +103,11 @@ LExit:
103 ::FindClose(hFind);
104 }
105
106 + if (fDisableWow64Redirection)
107 + {
108 + WcaRevertWow64FSRedirection();
109 + }
110 +
111 ReleaseStr(sczNext);
112 ReleaseStr(sczProperty);
113 ReleaseStr(sczSearch);
@@ -110,9 +126,12 @@ extern "C" UINT WINAPI WixRemoveFoldersEx(
126 LPWSTR sczId = NULL;
127 LPWSTR sczComponent = NULL;
128 LPWSTR sczProperty = NULL;
129 + LPWSTR sczCondition = NULL;
130 LPWSTR sczPath = NULL;
131 LPWSTR sczExpandedPath = NULL;
132 int iMode = 0;
133 + int iComponentAttributes;
134 + BOOL f64BitComponent = FALSE;
135 DWORD dwCounter = 0;
136 DWORD_PTR cchLen = 0;
137 MSIHANDLE hTable = NULL;
@@ -121,6 +140,8 @@ extern "C" UINT WINAPI WixRemoveFoldersEx(
140 hr = WcaInitialize(hInstall, "WixRemoveFoldersEx");
141 ExitOnFailure(hr, "Failed to initialize WixRemoveFoldersEx.");
142
143 + WcaInitializeWow64();
144 +
145 // anything to do?
146 if (S_OK != WcaTableExists(L"Wix4RemoveFolderEx"))
147 {
@@ -137,18 +158,40 @@ extern "C" UINT WINAPI WixRemoveFoldersEx(
158 hr = WcaGetRecordString(hRec, rfqId, &sczId);
159 ExitOnFailure(hr, "Failed to get remove folder identity.");
160
161 + hr = WcaGetRecordString(hRec, rfqCondition, &sczCondition);
162 + ExitOnFailure(hr, "Failed to get remove folder condition.");
163 +
164 + if (sczCondition && *sczCondition)
165 + {
166 + MSICONDITION condition = ::MsiEvaluateConditionW(hInstall, sczCondition);
167 + if (MSICONDITION_TRUE == condition)
168 + {
169 + WcaLog(LOGMSG_STANDARD, "True condition for row %S: %S; processing.", sczId, sczCondition);
170 + }
171 + else
172 + {
173 + WcaLog(LOGMSG_STANDARD, "False or invalid condition for row %S: %S; skipping.", sczId, sczCondition);
174 + continue;
175 + }
176 + }
177 +
178 hr = WcaGetRecordString(hRec, rfqComponent, &sczComponent);
179 ExitOnFailure(hr, "Failed to get remove folder component.");
180
181 hr = WcaGetRecordString(hRec, rfqProperty, &sczProperty);
182 ExitOnFailure(hr, "Failed to get remove folder property.");
183
146 - hr = WcaGetRecordInteger(hRec, feqMode, &iMode);
184 + hr = WcaGetRecordInteger(hRec, rfqMode, &iMode);
185 ExitOnFailure(hr, "Failed to get remove folder mode");
186
187 hr = WcaGetProperty(sczProperty, &sczPath);
188 ExitOnFailure(hr, "Failed to resolve remove folder property: %S for row: %S", sczProperty, sczId);
189
190 + hr = WcaGetRecordInteger(hRec, rfqComponentAttributes, &iComponentAttributes);
191 + ExitOnFailure(hr, "failed to get component attributes for row: %ls", sczId);
192 +
193 + f64BitComponent = iComponentAttributes & msidbComponentAttributes64bit;
194 +
195 // fail early if the property isn't set as you probably don't want your installers trying to delete SystemFolder
196 // StringCchLengthW succeeds only if the string is zero characters plus 1 for the terminating null
197 hr = ::StringCchLengthW(sczPath, 1, reinterpret_cast<UINT_PTR*>(&cchLen));
@@ -164,7 +207,7 @@ extern "C" UINT WINAPI WixRemoveFoldersEx(
207 ExitOnFailure(hr, "Failed to backslash-terminate path: %S", sczExpandedPath);
208
209 WcaLog(LOGMSG_STANDARD, "Recursing path: %S for row: %S.", sczExpandedPath, sczId);
167 - hr = RecursePath(sczExpandedPath, sczId, sczComponent, sczProperty, iMode, &dwCounter, &hTable, &hColumns);
210 + hr = RecursePath(sczExpandedPath, sczId, sczComponent, sczProperty, iMode, f64BitComponent, &dwCounter, &hTable, &hColumns);
211 ExitOnFailure(hr, "Failed while navigating path: %S for row: %S", sczPath, sczId);
212 }
213
@@ -176,6 +219,8 @@ extern "C" UINT WINAPI WixRemoveFoldersEx(
219 ExitOnFailure(hr, "Failure occured while processing Wix4RemoveFolderEx table");
220
221 LExit:
222 + WcaFinalizeWow64();
223 +
224 if (hColumns)
225 {
226 ::MsiCloseHandle(hColumns);
@@ -190,6 +235,7 @@ LExit:
235 ReleaseStr(sczPath);
236 ReleaseStr(sczProperty);
237 ReleaseStr(sczComponent);
238 + ReleaseStr(sczCondition);
239 ReleaseStr(sczId);
240
241 DWORD er = SUCCEEDED(hr) ? ERROR_SUCCESS : ERROR_INSTALL_FAILURE;
src/test/WixToolsetTest.Util/UtilExtensionFixture.cs
+2 -2
@@ -139,8 +139,8 @@ namespace WixToolsetTest.Util
139 WixAssert.CompareLineByLine(new[]
140 {
141 "Binary:Wix4UtilCA_X64.047730A5_30FE_4A62_A520_DA9381B8226A\t[Binary data]",
142 - "CustomAction:Wix4RemoveFoldersEx_X64.047730A5_30FE_4A62_A520_DA9381B8226A\t65\tWix4UtilCA_X64.047730A5_30FE_4A62_A520_DA9381B8226A WixRemoveFoldersEx\t",
143 - "Wix4RemoveFolderEx:wrfB3e9CDihkNwm06LohylbJcjZ91w.047730A5_30FE_4A62_A520_DA9381B8226A\tfilh4juyUVjoUcWWtcQmd5L07FoON4.047730A5_30FE_4A62_A520_DA9381B8226A\tRemoveProp.047730A5_30FE_4A62_A520_DA9381B8226A\t3",
142 + "CustomAction:Wix4RemoveFoldersEx_X64.047730A5_30FE_4A62_A520_DA9381B8226A\t65\tWix4UtilCA_X64.047730A5_30FE_4A62_A520_DA9381B8226A\tWixRemoveFoldersEx\t",
143 + "Wix4RemoveFolderEx:wrf5qCm1SE.zp8djrlk78l1IYFXsEw.047730A5_30FE_4A62_A520_DA9381B8226A\tfilh4juyUVjoUcWWtcQmd5L07FoON4.047730A5_30FE_4A62_A520_DA9381B8226A\tRemoveProp.047730A5_30FE_4A62_A520_DA9381B8226A\t3\t",
144 }, results.OrderBy(s => s).ToArray());
145 }
146
src/wixext/Symbols/WixRemoveFolderExSymbol.cs
+18 -3
@@ -14,6 +14,7 @@ namespace WixToolset.Util
14 new IntermediateFieldDefinition(nameof(WixRemoveFolderExSymbolFields.ComponentRef), IntermediateFieldType.String),
15 new IntermediateFieldDefinition(nameof(WixRemoveFolderExSymbolFields.Property), IntermediateFieldType.String),
16 new IntermediateFieldDefinition(nameof(WixRemoveFolderExSymbolFields.InstallMode), IntermediateFieldType.Number),
17 + new IntermediateFieldDefinition(nameof(WixRemoveFolderExSymbolFields.Condition), IntermediateFieldType.String),
18 },
19 typeof(WixRemoveFolderExSymbol));
20 }
@@ -28,6 +29,14 @@ namespace WixToolset.Util.Symbols
29 ComponentRef,
30 Property,
31 InstallMode,
32 + Condition,
33 + }
34 +
35 + public enum WixRemoveFolderExInstallMode
36 + {
37 + Install = 1,
38 + Uninstall = 2,
39 + Both = 3,
40 }
41
42 public class WixRemoveFolderExSymbol : IntermediateSymbol
@@ -54,10 +63,16 @@ namespace WixToolset.Util.Symbols
63 set => this.Set((int)WixRemoveFolderExSymbolFields.Property, value);
64 }
65
57 - public int InstallMode
66 + public WixRemoveFolderExInstallMode InstallMode
67 + {
68 + get => (WixRemoveFolderExInstallMode)this.Fields[(int)WixRemoveFolderExSymbolFields.InstallMode].AsNumber();
69 + set => this.Set((int)WixRemoveFolderExSymbolFields.InstallMode, (int)value);
70 + }
71 +
72 + public string Condition
73 {
59 - get => this.Fields[(int)WixRemoveFolderExSymbolFields.InstallMode].AsNumber();
60 - set => this.Set((int)WixRemoveFolderExSymbolFields.InstallMode, value);
74 + get => this.Fields[(int)WixRemoveFolderExSymbolFields.Condition].AsString();
75 + set => this.Set((int)WixRemoveFolderExSymbolFields.Condition, value);
76 }
77 }
78 }
\ No newline at end of file
src/wixext/UtilCompiler.cs
+12 -16
@@ -41,13 +41,6 @@ namespace WixToolset.Util
41 Compatible,
42 }
43
44 - internal enum WixRemoveFolderExOn
45 - {
46 - Install = 1,
47 - Uninstall = 2,
48 - Both = 3,
49 - }
50 -
44 private static readonly Regex FindPropertyBrackets = new Regex(@"\[(?!\\|\])|(?<!\[\\\]|\[\\|\\\[)\]", RegexOptions.ExplicitCapture | RegexOptions.Compiled);
45
46 public override XNamespace Namespace => "http://wixtoolset.org/schemas/v4/wxs/util";
@@ -2812,8 +2805,9 @@ namespace WixToolset.Util
2805 {
2806 var sourceLineNumbers = this.ParseHelper.GetSourceLineNumbers(element);
2807 Identifier id = null;
2815 - var on = (int)WixRemoveFolderExOn.Uninstall;
2808 + var mode = WixRemoveFolderExInstallMode.Uninstall;
2809 string property = null;
2810 + string condition = null;
2811
2812 foreach (var attrib in element.Attributes())
2813 {
@@ -2821,6 +2815,9 @@ namespace WixToolset.Util
2815 {
2816 switch (attrib.Name.LocalName)
2817 {
2818 + case "Condition":
2819 + condition = this.ParseHelper.GetAttributeValue(sourceLineNumbers, attrib);
2820 + break;
2821 case "Id":
2822 id = this.ParseHelper.GetAttributeIdentifier(sourceLineNumbers, attrib);
2823 break;
@@ -2828,24 +2825,22 @@ namespace WixToolset.Util
2825 var onValue = this.ParseHelper.GetAttributeValue(sourceLineNumbers, attrib);
2826 if (onValue.Length == 0)
2827 {
2831 - on = CompilerConstants.IllegalInteger;
2828 }
2829 else
2830 {
2831 switch (onValue)
2832 {
2833 case "install":
2838 - on = (int)WixRemoveFolderExOn.Install;
2834 + mode = WixRemoveFolderExInstallMode.Install;
2835 break;
2836 case "uninstall":
2841 - on = (int)WixRemoveFolderExOn.Uninstall;
2837 + mode = WixRemoveFolderExInstallMode.Uninstall;
2838 break;
2839 case "both":
2844 - on = (int)WixRemoveFolderExOn.Both;
2840 + mode = WixRemoveFolderExInstallMode.Both;
2841 break;
2842 default:
2843 this.Messaging.Write(ErrorMessages.IllegalAttributeValue(sourceLineNumbers, element.Name.LocalName, "On", onValue, "install", "uninstall", "both"));
2848 - on = CompilerConstants.IllegalInteger;
2844 break;
2845 }
2846 }
@@ -2869,9 +2864,9 @@ namespace WixToolset.Util
2864 this.Messaging.Write(ErrorMessages.ExpectedAttribute(sourceLineNumbers, element.Name.LocalName, "Property"));
2865 }
2866
2872 - if (null == id)
2867 + if (id == null)
2868 {
2874 - id = this.ParseHelper.CreateIdentifier("wrf", componentId, property, on.ToString(CultureInfo.InvariantCulture.NumberFormat));
2869 + id = this.ParseHelper.CreateIdentifier("wrf", componentId, property, mode.ToString());
2870 }
2871
2872 this.ParseHelper.ParseForExtensionElements(this.Context.Extensions, intermediate, section, element);
@@ -2884,7 +2879,8 @@ namespace WixToolset.Util
2879 {
2880 ComponentRef = componentId,
2881 Property = property,
2887 - InstallMode = on,
2882 + InstallMode = mode,
2883 + Condition = condition
2884 });
2885
2886 this.ParseHelper.EnsureTable(section, sourceLineNumbers, "RemoveFile");
src/wixext/UtilTableDefinitions.cs
+1
@@ -33,6 +33,7 @@ namespace WixToolset.Util
33 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),
34 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),
35 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."),
36 + 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),
37 },
38 symbolIdIsPrimaryKey: true
39 );