Use validation now implemented in Core.Native
Fixes wixtoolset/issues#5946
Rob Mensching committed
Mar 16, 2021 at 10:52 UTC
0d4df529a7e34f033112a1c6f16f749880334e7d
6 files changed
+227
-401
src/WixToolset.Core.WindowsInstaller/Bind/BindDatabaseCommand.cs
+24
-26
@@ -21,11 +21,11 @@ namespace WixToolset.Core.WindowsInstaller.Bind
21
// As outlined in RFC 4122, this is our namespace for generating name-based (version 3) UUIDs.
22
internal static readonly Guid WixComponentGuidNamespace = new Guid("{3064E5C6-FB63-4FE9-AC49-E446A792EFA5}");
23
24
- public BindDatabaseCommand(IBindContext context, IEnumerable<IWindowsInstallerBackendBinderExtension> backendExtension, Validator validator) : this(context, backendExtension, null, validator)
24
+ public BindDatabaseCommand(IBindContext context, IEnumerable<IWindowsInstallerBackendBinderExtension> backendExtension, string cubeFile) : this(context, backendExtension, null, cubeFile)
25
{
26
}
27
28
- public BindDatabaseCommand(IBindContext context, IEnumerable<IWindowsInstallerBackendBinderExtension> backendExtension, IEnumerable<SubStorage> subStorages, Validator validator)
28
+ public BindDatabaseCommand(IBindContext context, IEnumerable<IWindowsInstallerBackendBinderExtension> backendExtension, IEnumerable<SubStorage> subStorages, string cubeFile)
29
{
30
this.ServiceProvider = context.ServiceProvider;
31
@@ -50,7 +50,11 @@ namespace WixToolset.Core.WindowsInstaller.Bind
50
this.SuppressLayout = context.SuppressLayout;
51
52
this.SubStorages = subStorages;
53
- this.Validator = validator;
53
+
54
+ this.SuppressValidation = context.SuppressValidation;
55
+ this.Ices = context.Ices;
56
+ this.SuppressedIces = context.SuppressIces;
57
+ this.CubeFiles = String.IsNullOrEmpty(cubeFile) ? null : new[] { cubeFile };
58
59
this.BackendExtensions = backendExtension;
60
}
@@ -97,7 +101,13 @@ namespace WixToolset.Core.WindowsInstaller.Bind
101
102
private string IntermediateFolder { get; }
103
100
- private Validator Validator { get; }
104
+ private bool SuppressValidation { get; }
105
+
106
+ private IEnumerable<string> Ices { get; }
107
+
108
+ private IEnumerable<string> SuppressedIces { get; }
109
+
110
+ private IEnumerable<string> CubeFiles { get; }
111
112
public IBindResult Execute()
113
{
@@ -522,34 +532,22 @@ namespace WixToolset.Core.WindowsInstaller.Bind
532
if (this.Messaging.EncounteredError)
533
{
534
return null;
525
- }
535
+ }
536
527
-#if TODO_FINISH_VALIDATION
528
- // Validate the output if there is an MSI validator.
529
- if (null != this.Validator)
537
+ // Validate the output if there are CUBe files and we're not explicitly suppressing validation.
538
+ if (this.CubeFiles != null && !this.SuppressValidation)
539
{
531
- Stopwatch stopwatch = Stopwatch.StartNew();
532
-
533
- // set the output file for source line information
534
- this.Validator.Output = this.Output;
535
-
536
- Messaging.Instance.Write(WixVerboses.ValidatingDatabase());
537
-
538
- this.Validator.Validate(this.OutputPath);
539
-
540
- stopwatch.Stop();
541
- Messaging.Instance.Write(WixVerboses.ValidatedDatabase(stopwatch.ElapsedMilliseconds));
540
+ var command = new ValidateDatabaseCommand(this.Messaging, this.IntermediateFolder, data, this.OutputPath, this.CubeFiles, this.Ices, this.SuppressedIces);
541
+ command.Execute();
542
+ }
543
543
- // Stop processing if an error occurred.
544
- if (Messaging.Instance.EncounteredError)
545
- {
546
- return;
547
- }
544
+ if (this.Messaging.EncounteredError)
545
+ {
546
+ return null;
547
}
549
-#endif
548
549
// Process uncompressed files.
552
- if (!this.Messaging.EncounteredError && !this.SuppressLayout && uncompressedFiles.Any())
550
+ if (!this.SuppressLayout && uncompressedFiles.Any())
551
{
552
var command = new ProcessUncompressedFilesCommand(section, this.WindowsInstallerBackendHelper, this.PathResolver);
553
command.Compressed = compressed;
src/WixToolset.Core.WindowsInstaller/Bind/ValidateDatabaseCommand.cs
new
+177
@@ -0,0 +1,177 @@
1
+// 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.
2
+
3
+namespace WixToolset.Core.WindowsInstaller.Bind
4
+{
5
+ using System;
6
+ using System.Collections.Generic;
7
+ using System.ComponentModel;
8
+ using System.Diagnostics;
9
+ using System.IO;
10
+ using System.Linq;
11
+ using System.Threading;
12
+ using WixToolset.Core.Native;
13
+ using WixToolset.Core.Native.Msi;
14
+ using WixToolset.Data;
15
+ using WixToolset.Data.WindowsInstaller;
16
+ using WixToolset.Extensibility.Services;
17
+
18
+ internal class ValidateDatabaseCommand : IWindowsInstallerValidatorCallback
19
+ {
20
+ // Set of ICEs that have equivalent-or-better checks in WiX.
21
+ private static readonly string[] WellKnownSuppressedIces = new[] { "ICE08", "ICE33", "ICE47", "ICE66" };
22
+
23
+ public ValidateDatabaseCommand(IMessaging messaging, string intermediateFolder, WindowsInstallerData data, string outputPath, IEnumerable<string> cubeFiles, IEnumerable<string> ices, IEnumerable<string> suppressedIces)
24
+ {
25
+ this.Messaging = messaging;
26
+ this.Data = data;
27
+ this.OutputPath = outputPath;
28
+ this.CubeFiles = cubeFiles;
29
+ this.Ices = ices;
30
+ this.SuppressedIces = suppressedIces == null ? WellKnownSuppressedIces : suppressedIces.Union(WellKnownSuppressedIces);
31
+
32
+ this.IntermediateFolder = intermediateFolder;
33
+ this.OutputSourceLineNumber = new SourceLineNumber(outputPath);
34
+ }
35
+
36
+ /// <summary>
37
+ /// Encountered error implementation for <see cref="IWindowsInstallerValidatorCallback"/>.
38
+ /// </summary>
39
+ public bool EncounteredError => this.Messaging.EncounteredError;
40
+
41
+ private IMessaging Messaging { get; }
42
+
43
+ private WindowsInstallerData Data { get; }
44
+
45
+ private string OutputPath { get; }
46
+
47
+ private IEnumerable<string> CubeFiles { get; }
48
+
49
+ private IEnumerable<string> Ices { get; }
50
+
51
+ private IEnumerable<string> SuppressedIces { get; }
52
+
53
+ private string IntermediateFolder { get; }
54
+
55
+ /// <summary>
56
+ /// Fallback when an exact source line number cannot be calculated for a validation error.
57
+ /// </summary>
58
+ private SourceLineNumber OutputSourceLineNumber { get; set; }
59
+
60
+ private Dictionary<string, SourceLineNumber> SourceLineNumbersByTablePrimaryKey { get; set; }
61
+
62
+ public void Execute()
63
+ {
64
+ var stopwatch = Stopwatch.StartNew();
65
+
66
+ this.Messaging.Write(VerboseMessages.ValidatingDatabase());
67
+
68
+ // Ensure the temporary files can be created the working folder.
69
+ var workingFolder = Path.Combine(this.IntermediateFolder, "_validate");
70
+ Directory.CreateDirectory(workingFolder);
71
+
72
+ // Copy the database to a temporary location so it can be manipulated.
73
+ // Ensure it is not read-only.
74
+ var workingDatabasePath = Path.Combine(workingFolder, Path.GetFileName(this.OutputPath));
75
+ FileSystem.CopyFile(this.OutputPath, workingDatabasePath, allowHardlink: false);
76
+
77
+ var attributes = File.GetAttributes(workingDatabasePath);
78
+ File.SetAttributes(workingDatabasePath, attributes & ~FileAttributes.ReadOnly);
79
+
80
+ var validator = new WindowsInstallerValidator(this, workingDatabasePath, this.CubeFiles, this.Ices, this.SuppressedIces);
81
+ validator.Execute();
82
+
83
+ stopwatch.Stop();
84
+ this.Messaging.Write(VerboseMessages.ValidatedDatabase(stopwatch.ElapsedMilliseconds));
85
+ }
86
+
87
+ private void LogValidationMessage(ValidationMessage message)
88
+ {
89
+ var messageSourceLineNumbers = this.OutputSourceLineNumber;
90
+ if (!String.IsNullOrEmpty(message.Table) && !String.IsNullOrEmpty(message.Column) && message.PrimaryKeys != null)
91
+ {
92
+ messageSourceLineNumbers = this.GetSourceLineNumbers(message.Table, message.PrimaryKeys);
93
+ }
94
+
95
+ switch (message.Type)
96
+ {
97
+ case ValidationMessageType.InternalFailure:
98
+ case ValidationMessageType.Error:
99
+ this.Messaging.Write(ErrorMessages.ValidationError(messageSourceLineNumbers, message.IceName, message.Description));
100
+ break;
101
+ case ValidationMessageType.Warning:
102
+ this.Messaging.Write(WarningMessages.ValidationWarning(messageSourceLineNumbers, message.IceName, message.Description));
103
+ break;
104
+ case ValidationMessageType.Info:
105
+ this.Messaging.Write(VerboseMessages.ValidationInfo(message.IceName, message.Description));
106
+ break;
107
+ default:
108
+ throw new WixException(ErrorMessages.InvalidValidatorMessageType(message.Type.ToString()));
109
+ }
110
+ }
111
+
112
+ /// <summary>
113
+ /// Validation blocked by other installation operation for <see cref="IWindowsInstallerValidatorCallback"/>.
114
+ /// </summary>
115
+ public void ValidationBlocked()
116
+ {
117
+ this.Messaging.Write(VerboseMessages.ValidationSerialized());
118
+ }
119
+
120
+ /// <summary>
121
+ /// Validation message implementation for <see cref="IWindowsInstallerValidatorCallback"/>.
122
+ /// </summary>
123
+ public bool ValidationMessage(ValidationMessage message)
124
+ {
125
+ this.LogValidationMessage(message);
126
+ return true;
127
+ }
128
+
129
+ /// <summary>
130
+ /// Gets the source line information (if available) for a row by its table name and primary key.
131
+ /// </summary>
132
+ /// <param name="tableName">The table name of the row.</param>
133
+ /// <param name="primaryKeys">The primary keys of the row.</param>
134
+ /// <returns>The source line number information if found; null otherwise.</returns>
135
+ private SourceLineNumber GetSourceLineNumbers(string tableName, IEnumerable<string> primaryKeys)
136
+ {
137
+ // Source line information only exists if an output file was supplied
138
+ if (this.Data == null)
139
+ {
140
+ // Use the file name as the source line information.
141
+ return this.OutputSourceLineNumber;
142
+ }
143
+
144
+ // Index the source line information if it hasn't been indexed already.
145
+ if (this.SourceLineNumbersByTablePrimaryKey == null)
146
+ {
147
+ this.SourceLineNumbersByTablePrimaryKey = new Dictionary<string, SourceLineNumber>();
148
+
149
+ // Index each real table
150
+ foreach (var table in this.Data.Tables.Where(t => !t.Definition.Unreal))
151
+ {
152
+ // Index each row that contain source line information
153
+ foreach (var row in table.Rows.Where(r => r.SourceLineNumbers != null))
154
+ {
155
+ // Index the row using its table name and primary key
156
+ var primaryKey = row.GetPrimaryKey(';');
157
+
158
+ if (!String.IsNullOrEmpty(primaryKey))
159
+ {
160
+ try
161
+ {
162
+ var key = String.Concat(table.Name, ":", primaryKey);
163
+ this.SourceLineNumbersByTablePrimaryKey.Add(key, row.SourceLineNumbers);
164
+ }
165
+ catch (ArgumentException)
166
+ {
167
+ this.Messaging.Write(WarningMessages.DuplicatePrimaryKey(row.SourceLineNumbers, primaryKey, table.Name));
168
+ }
169
+ }
170
+ }
171
+ }
172
+ }
173
+
174
+ return this.SourceLineNumbersByTablePrimaryKey.TryGetValue(String.Concat(tableName, ":", String.Join(";", primaryKeys)), out var sourceLineNumbers) ? sourceLineNumbers : null;
175
+ }
176
+ }
177
+}
src/WixToolset.Core.WindowsInstaller/MsiBackend.cs
+1
-3
@@ -24,13 +24,11 @@ namespace WixToolset.Core.WindowsInstaller
24
extension.PreBackendBind(context);
25
}
26
27
- var validator = Validator.CreateFromContext(context, "darice.cub");
28
-
27
IBindResult result = null;
28
var dispose = true;
29
try
30
{
33
- var command = new BindDatabaseCommand(context, backendExtensions, validator);
31
+ var command = new BindDatabaseCommand(context, backendExtensions, "darice.cub");
32
result = command.Execute();
33
34
foreach (var extension in backendExtensions)
src/WixToolset.Core.WindowsInstaller/MsmBackend.cs
+1
-3
@@ -23,12 +23,10 @@ namespace WixToolset.Core.WindowsInstaller
23
extension.PreBackendBind(context);
24
}
25
26
- var validator = Validator.CreateFromContext(context, "mergemod.cub");
27
-
26
IBindResult result = null;
27
try
28
{
31
- var command = new BindDatabaseCommand(context, backendExtensions, validator);
29
+ var command = new BindDatabaseCommand(context, backendExtensions, "mergemod.cub");
30
result = command.Execute();
31
32
foreach (var extension in backendExtensions)
src/WixToolset.Core.WindowsInstaller/Validator.cs
deleted
-365
@@ -1,365 +0,0 @@
1
-// 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.
2
-
3
-namespace WixToolset.Core.WindowsInstaller
4
-{
5
- using System;
6
- using System.Collections.Generic;
7
- using System.Collections.Specialized;
8
- using System.ComponentModel;
9
- using System.Diagnostics.CodeAnalysis;
10
- using System.Globalization;
11
- using System.IO;
12
- using System.Linq;
13
- using System.Reflection;
14
- using System.Threading;
15
- using WixToolset.Core.WindowsInstaller.Msi;
16
- using WixToolset.Data;
17
- using WixToolset.Data.WindowsInstaller;
18
- using WixToolset.Extensibility;
19
- using WixToolset.Extensibility.Data;
20
- using WixToolset.Extensibility.Services;
21
-
22
- /// <summary>
23
- /// Runs internal consistency evaluators (ICEs) from cub files against a database.
24
- /// </summary>
25
- internal sealed class Validator
26
- {
27
- private string actionName;
28
- private readonly StringCollection cubeFiles;
29
- private ValidatorExtension extension;
30
- private WindowsInstallerData output;
31
- private readonly InstallUIHandler validationUIHandler;
32
- private bool validationSessionComplete;
33
- private readonly IMessaging messaging;
34
-
35
- /// <summary>
36
- /// Instantiate a new Validator.
37
- /// </summary>
38
- public Validator(IMessaging messaging)
39
- {
40
- this.cubeFiles = new StringCollection();
41
- this.extension = new ValidatorExtension(messaging);
42
- this.validationUIHandler = new InstallUIHandler(this.ValidationUIHandler);
43
- this.messaging = messaging;
44
- }
45
-
46
- /// <summary>
47
- /// Gets or sets a <see cref="ValidatorExtension"/> that directs messages from the validator.
48
- /// </summary>
49
- /// <value>A <see cref="ValidatorExtension"/> that directs messages from the validator.</value>
50
- public ValidatorExtension Extension
51
- {
52
- get { return this.extension; }
53
- set { this.extension = value; }
54
- }
55
-
56
- /// <summary>
57
- /// Gets or sets the list of ICEs to run.
58
- /// </summary>
59
- /// <value>The list of ICEs.</value>
60
- public ISet<string> ICEs { get; set; }
61
-
62
- /// <summary>
63
- /// Gets or sets the output used for finding source line information.
64
- /// </summary>
65
- /// <value>The output used for finding source line information.</value>
66
- public WindowsInstallerData Output
67
- {
68
- // cache Output object until validation for changes in extension
69
- get { return this.output; }
70
- set { this.output = value; }
71
- }
72
-
73
- /// <summary>
74
- /// Gets or sets the suppressed ICEs.
75
- /// </summary>
76
- /// <value>The suppressed ICEs.</value>
77
- public ISet<string> SuppressedICEs { get; set; }
78
-
79
- /// <summary>
80
- /// Sets the temporary path for the Binder.
81
- /// </summary>
82
- public string IntermediateFolder { private get; set; }
83
-
84
- /// <summary>
85
- /// Add a cube file to the validation run.
86
- /// </summary>
87
- /// <param name="cubeFile">A cube file.</param>
88
- public void AddCubeFile(string cubeFile)
89
- {
90
- this.cubeFiles.Add(cubeFile);
91
- }
92
-
93
- /// <summary>
94
- /// Validate a database.
95
- /// </summary>
96
- /// <param name="databaseFile">The database to validate.</param>
97
- /// <returns>true if validation succeeded; false otherwise.</returns>
98
- public void Validate(string databaseFile)
99
- {
100
- int previousUILevel = (int)InstallUILevels.Basic;
101
- IntPtr previousHwnd = IntPtr.Zero;
102
- InstallUIHandler previousUIHandler = null;
103
-
104
- // initialize the validator extension
105
- this.extension.DatabaseFile = databaseFile ?? throw new ArgumentNullException(nameof(databaseFile));
106
- this.extension.Output = this.output;
107
- this.extension.InitializeValidator();
108
-
109
- // Ensure the temporary files can be created.
110
- Directory.CreateDirectory(this.IntermediateFolder);
111
-
112
- // copy the database to a temporary location so it can be manipulated
113
- string tempDatabaseFile = Path.Combine(this.IntermediateFolder, Path.GetFileName(databaseFile));
114
- File.Copy(databaseFile, tempDatabaseFile);
115
-
116
- // remove the read-only property from the temporary database
117
- FileAttributes attributes = File.GetAttributes(tempDatabaseFile);
118
- File.SetAttributes(tempDatabaseFile, attributes & ~FileAttributes.ReadOnly);
119
-
120
- Mutex mutex = new Mutex(false, "WixValidator");
121
- try
122
- {
123
- if (!mutex.WaitOne(0, false))
124
- {
125
- this.messaging.Write(VerboseMessages.ValidationSerialized());
126
- mutex.WaitOne();
127
- }
128
-
129
- using (Database database = new Database(tempDatabaseFile, OpenDatabase.Direct))
130
- {
131
- bool propertyTableExists = database.TableExists("Property");
132
- string productCode = null;
133
-
134
- // remove the product code from the database before opening a session to prevent opening an installed product
135
- if (propertyTableExists)
136
- {
137
- using (View view = database.OpenExecuteView("SELECT `Value` FROM `Property` WHERE Property = 'ProductCode'"))
138
- {
139
- using (Record record = view.Fetch())
140
- {
141
- if (null != record)
142
- {
143
- productCode = record.GetString(1);
144
-
145
- using (View dropProductCodeView = database.OpenExecuteView("DELETE FROM `Property` WHERE `Property` = 'ProductCode'"))
146
- {
147
- }
148
- }
149
- }
150
- }
151
- }
152
-
153
- // merge in the cube databases
154
- foreach (string cubeFile in this.cubeFiles)
155
- {
156
- try
157
- {
158
- using (Database cubeDatabase = new Database(cubeFile, OpenDatabase.ReadOnly))
159
- {
160
- try
161
- {
162
- database.Merge(cubeDatabase, "MergeConflicts");
163
- }
164
- catch
165
- {
166
- // ignore merge errors since they are expected in the _Validation table
167
- }
168
- }
169
- }
170
- catch (Win32Exception e)
171
- {
172
- if (0x6E == e.NativeErrorCode) // ERROR_OPEN_FAILED
173
- {
174
- throw new WixException(ErrorMessages.CubeFileNotFound(cubeFile));
175
- }
176
-
177
- throw;
178
- }
179
- }
180
-
181
- // commit the database before proceeding to ensure the streams don't get confused
182
- database.Commit();
183
-
184
- // the property table may have been added to the database
185
- // from a cub database without the proper validation rows
186
- if (!propertyTableExists)
187
- {
188
- using (View view = database.OpenExecuteView("DROP table `Property`"))
189
- {
190
- }
191
- }
192
-
193
- // get all the action names for ICEs which have not been suppressed
194
- List<string> actions = new List<string>();
195
- using (View view = database.OpenExecuteView("SELECT `Action` FROM `_ICESequence` ORDER BY `Sequence`"))
196
- {
197
- foreach (Record record in view.Records)
198
- {
199
- string action = record.GetString(1);
200
-
201
- if ((this.SuppressedICEs == null || !this.SuppressedICEs.Contains(action)) && (this.ICEs == null || this.ICEs.Contains(action)))
202
- {
203
- actions.Add(action);
204
- }
205
- }
206
- }
207
-
208
- // disable the internal UI handler and set an external UI handler
209
- previousUILevel = Installer.SetInternalUI((int)InstallUILevels.None, ref previousHwnd);
210
- previousUIHandler = Installer.SetExternalUI(this.validationUIHandler, (int)InstallLogModes.Error | (int)InstallLogModes.Warning | (int)InstallLogModes.User, IntPtr.Zero);
211
-
212
- // create a session for running the ICEs
213
- this.validationSessionComplete = false;
214
- using (Session session = new Session(database))
215
- {
216
- // add the product code back into the database
217
- if (null != productCode)
218
- {
219
- // some CUBs erroneously have a ProductCode property, so delete it if we just picked one up
220
- using (View dropProductCodeView = database.OpenExecuteView("DELETE FROM `Property` WHERE `Property` = 'ProductCode'"))
221
- {
222
- }
223
-
224
- using (View view = database.OpenExecuteView(String.Format(CultureInfo.InvariantCulture, "INSERT INTO `Property` (`Property`, `Value`) VALUES ('ProductCode', '{0}')", productCode)))
225
- {
226
- }
227
- }
228
-
229
- foreach (string action in actions)
230
- {
231
- this.actionName = action;
232
- try
233
- {
234
- session.DoAction(action);
235
- }
236
- catch (Win32Exception e)
237
- {
238
- if (!this.messaging.EncounteredError)
239
- {
240
- throw e;
241
- }
242
- // TODO: Review why this was clearing the error state when an exception had happened but an error was already encountered. That's weird.
243
- //else
244
- //{
245
- // this.encounteredError = false;
246
- //}
247
- }
248
- this.actionName = null;
249
- }
250
-
251
- // Mark the validation session complete so we ignore any messages that MSI may fire
252
- // during session clean-up.
253
- this.validationSessionComplete = true;
254
- }
255
- }
256
- }
257
- catch (Win32Exception e)
258
- {
259
- // avoid displaying errors twice since one may have already occurred in the UI handler
260
- if (!this.messaging.EncounteredError)
261
- {
262
- if (0x6E == e.NativeErrorCode) // ERROR_OPEN_FAILED
263
- {
264
- // databaseFile is not passed since during light
265
- // this would be the temporary copy and there would be
266
- // no final output since the error occured; during smoke
267
- // they should know the path passed into smoke
268
- this.messaging.Write(ErrorMessages.ValidationFailedToOpenDatabase());
269
- }
270
- else if (0x64D == e.NativeErrorCode)
271
- {
272
- this.messaging.Write(ErrorMessages.ValidationFailedDueToLowMsiEngine());
273
- }
274
- else if (0x654 == e.NativeErrorCode)
275
- {
276
- this.messaging.Write(ErrorMessages.ValidationFailedDueToInvalidPackage());
277
- }
278
- else if (0x658 == e.NativeErrorCode)
279
- {
280
- this.messaging.Write(ErrorMessages.ValidationFailedDueToMultilanguageMergeModule());
281
- }
282
- else if (0x659 == e.NativeErrorCode)
283
- {
284
- this.messaging.Write(WarningMessages.ValidationFailedDueToSystemPolicy());
285
- }
286
- else
287
- {
288
- string msgTemp = e.Message;
289
-
290
- if (null != this.actionName)
291
- {
292
- msgTemp = String.Concat("Action - '", this.actionName, "' ", e.Message);
293
- }
294
-
295
- this.messaging.Write(ErrorMessages.Win32Exception(e.NativeErrorCode, msgTemp));
296
- }
297
- }
298
- }
299
- finally
300
- {
301
- Installer.SetExternalUI(previousUIHandler, 0, IntPtr.Zero);
302
- Installer.SetInternalUI(previousUILevel, ref previousHwnd);
303
-
304
- this.validationSessionComplete = false; // no validation session at this point, so reset the completion flag.
305
-
306
- mutex.ReleaseMutex();
307
- this.cubeFiles.Clear();
308
- this.extension.FinalizeValidator();
309
- }
310
- }
311
-
312
- public static Validator CreateFromContext(IBindContext context, string cubeFilename)
313
- {
314
- Validator validator = null;
315
- var messaging = context.ServiceProvider.GetService<IMessaging>();
316
-
317
- // Tell the binder about the validator if validation isn't suppressed
318
- if (!context.SuppressValidation)
319
- {
320
- validator = new Validator(messaging);
321
- validator.IntermediateFolder = Path.Combine(context.IntermediateFolder, "validate");
322
-
323
- // set the default cube file
324
- string thisPath = Path.GetDirectoryName(Assembly.GetExecutingAssembly().Location);
325
- validator.AddCubeFile(Path.Combine(thisPath, cubeFilename));
326
-
327
- // Set the ICEs
328
- validator.ICEs = new SortedSet<string>(context.Ices);
329
-
330
- // Set the suppressed ICEs and disable ICEs that have equivalent-or-better checks in WiX.
331
- validator.SuppressedICEs = new SortedSet<string>(context.SuppressIces.Union(new[] { "ICE08", "ICE33", "ICE47", "ICE66" }));
332
- }
333
-
334
- return validator;
335
- }
336
-
337
- /// <summary>
338
- /// The validation external UI handler.
339
- /// </summary>
340
- /// <param name="context">Pointer to an application context.
341
- /// This parameter can be used for error checking.</param>
342
- /// <param name="messageType">Specifies a combination of one message box style,
343
- /// one message box icon type, one default button, and one installation message type.</param>
344
- /// <param name="message">Specifies the message text.</param>
345
- /// <returns>-1 for an error, 0 if no action was taken, 1 if OK, 3 to abort.</returns>
346
- private int ValidationUIHandler(IntPtr context, uint messageType, string message)
347
- {
348
- try
349
- {
350
- // If we're getting messges during the validation session, send them to
351
- // the extension. Otherwise, ignore the messages.
352
- if (!this.validationSessionComplete)
353
- {
354
- this.extension.Log(message, this.actionName);
355
- }
356
- }
357
- catch (WixException ex)
358
- {
359
- this.messaging.Write(ex.Error);
360
- }
361
-
362
- return 1;
363
- }
364
- }
365
-}
src/WixToolset.Core/CommandLine/BuildCommand.cs
+24
-4
@@ -344,14 +344,14 @@ namespace WixToolset.Core.CommandLine
344
context.ExpectedEmbeddedFiles = resolveResult.ExpectedEmbeddedFiles;
345
context.Extensions = this.ExtensionManager.GetServices<IBinderExtension>();
346
context.FileSystemExtensions = this.ExtensionManager.GetServices<IFileSystemExtension>();
347
- context.Ices = Array.Empty<string>(); // TODO: set this correctly
347
+ context.Ices = this.commandLine.Ices;
348
context.IntermediateFolder = intermediateFolder;
349
context.IntermediateRepresentation = resolveResult.IntermediateRepresentation;
350
context.OutputPath = this.OutputFile;
351
context.PdbType = this.PdbType;
352
context.PdbPath = this.PdbType == PdbType.None ? null : this.PdbFile ?? Path.ChangeExtension(this.OutputFile, ".wixpdb");
353
- context.SuppressIces = Array.Empty<string>(); // TODO: set this correctly
354
- context.SuppressValidation = true; // TODO: set this correctly
353
+ context.SuppressIces = this.commandLine.SuppressIces;
354
+ context.SuppressValidation = this.commandLine.SuppressValidation;
355
context.CancellationToken = cancellationToken;
356
357
var binder = this.ServiceProvider.GetService<IBinder>();
@@ -541,6 +541,12 @@ namespace WixToolset.Core.CommandLine
541
542
public string BuiltOutputsFile { get; private set; }
543
544
+ public List<string> Ices { get; } = new List<string>();
545
+
546
+ public List<string> SuppressIces { get; } = new List<string>();
547
+
548
+ public bool SuppressValidation { get; set; }
549
+
550
public CommandLine(IServiceProvider serviceProvider, IMessaging messaging)
551
{
552
this.ServiceProvider = serviceProvider;
@@ -634,6 +640,13 @@ namespace WixToolset.Core.CommandLine
640
parser.GetNextArgumentOrError(arg, this.IncludeSearchPaths);
641
return true;
642
643
+ case "ice":
644
+ {
645
+ var value = parser.GetNextArgumentOrError(arg);
646
+ this.Ices.Add(value);
647
+ return true;
648
+ }
649
+
650
case "intermediatefolder":
651
this.IntermediateFolder = parser.GetNextArgumentAsDirectoryOrError(arg);
652
return true;
@@ -670,6 +683,13 @@ namespace WixToolset.Core.CommandLine
683
return false;
684
}
685
686
+ case "sice":
687
+ {
688
+ var value = parser.GetNextArgumentOrError(arg);
689
+ this.SuppressIces.Add(value);
690
+ return true;
691
+ }
692
+
693
case "nologo":
694
this.ShowLogo = false;
695
return true;
@@ -680,7 +700,7 @@ namespace WixToolset.Core.CommandLine
700
return true;
701
702
case "sval":
683
- // todo: implement
703
+ this.SuppressValidation = true;
704
return true;
705
}
706