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
);