Start on new patch infrastructure
Rob Mensching committed
Jan 24, 2020 at 15:27 UTC
6ff680e386b1543ad1a58d1b1d465ce8aa20bc7d
58 files changed
+4481
-1192
src/WixToolset.Core.Burn/Bind/BindBundleCommand.cs
-1
@@ -208,7 +208,6 @@ namespace WixToolset.Core.Burn
208
variableCache.Add(String.Concat("packageManufacturer.", facade.PackageId), msiPackage.Manufacturer);
209
}
210
}
211
-
211
}
212
break;
213
src/WixToolset.Core.Burn/Bundles/CreateNonUXContainers.cs
+2
-2
@@ -113,7 +113,7 @@ namespace WixToolset.Core.Burn.Bundles
113
++attachedContainerIndex;
114
}
115
116
- this.CreateContainer(container, containerPayloads, null);
116
+ this.CreateContainer(container, containerPayloads);
117
}
118
}
119
@@ -122,7 +122,7 @@ namespace WixToolset.Core.Burn.Bundles
122
this.FileTransfers = fileTransfers;
123
}
124
125
- private void CreateContainer(WixBundleContainerTuple container, IEnumerable<WixBundlePayloadTuple> containerPayloads, string manifestFile)
125
+ private void CreateContainer(WixBundleContainerTuple container, IEnumerable<WixBundlePayloadTuple> containerPayloads)
126
{
127
var command = new CreateContainerCommand(containerPayloads, container.WorkingPath, this.DefaultCompressionLevel);
128
command.Execute();
src/WixToolset.Core.WindowsInstaller/Bind/AssignMediaCommand.cs
+14
-14
@@ -57,7 +57,7 @@ namespace WixToolset.Core.WindowsInstaller.Bind
57
58
var mediaRows = new Dictionary<int, MediaTuple>();
59
60
- List<FileFacade> uncompressedFiles = new List<FileFacade>();
60
+ var uncompressedFiles = new List<FileFacade>();
61
62
var mediaTable = this.Section.Tuples.OfType<MediaTuple>().ToList();
63
var mediaTemplateTable = this.Section.Tuples.OfType<WixMediaTemplateTuple>().ToList();
@@ -109,8 +109,8 @@ namespace WixToolset.Core.WindowsInstaller.Bind
109
110
ulong currentPreCabSize = 0;
111
ulong maxPreCabSizeInBytes;
112
- int maxPreCabSizeInMB = 0;
113
- int currentCabIndex = 0;
112
+ var maxPreCabSizeInMB = 0;
113
+ var currentCabIndex = 0;
114
115
MediaTuple currentMediaRow = null;
116
@@ -131,7 +131,7 @@ namespace WixToolset.Core.WindowsInstaller.Bind
131
this.CabinetNameTemplate = mediaTemplateRow.CabinetTemplate;
132
}
133
134
- string mumsString = Environment.GetEnvironmentVariable("WIX_MUMS");
134
+ var mumsString = Environment.GetEnvironmentVariable("WIX_MUMS");
135
136
try
137
{
@@ -170,13 +170,13 @@ namespace WixToolset.Core.WindowsInstaller.Bind
170
{
171
// Associate current file with last cab (irrespective of the size) and cab index is not incremented anymore.
172
var cabinetFiles = filesByCabinetMedia[currentMediaRow];
173
- facade.File.DiskId = currentCabIndex;
173
+ facade.DiskId = currentCabIndex;
174
cabinetFiles.Add(facade);
175
continue;
176
}
177
178
// Update current cab size.
179
- currentPreCabSize += (ulong)facade.File.FileSize;
179
+ currentPreCabSize += (ulong)facade.FileSize;
180
181
if (currentPreCabSize > maxPreCabSizeInBytes)
182
{
@@ -186,10 +186,10 @@ namespace WixToolset.Core.WindowsInstaller.Bind
186
filesByCabinetMedia.Add(currentMediaRow, new List<FileFacade>());
187
188
var cabinetFileRows = filesByCabinetMedia[currentMediaRow];
189
- facade.File.DiskId = currentCabIndex;
189
+ facade.DiskId = currentCabIndex;
190
cabinetFileRows.Add(facade);
191
// Now files larger than MaxUncompressedMediaSize will be the only file in its cabinet so as to respect MaxUncompressedMediaSize
192
- currentPreCabSize = (ulong)facade.File.FileSize;
192
+ currentPreCabSize = (ulong)facade.FileSize;
193
}
194
else
195
{
@@ -204,7 +204,7 @@ namespace WixToolset.Core.WindowsInstaller.Bind
204
205
// Associate current file with current cab.
206
var cabinetFiles = filesByCabinetMedia[currentMediaRow];
207
- facade.File.DiskId = currentCabIndex;
207
+ facade.DiskId = currentCabIndex;
208
cabinetFiles.Add(facade);
209
}
210
}
@@ -260,18 +260,18 @@ namespace WixToolset.Core.WindowsInstaller.Bind
260
}
261
}
262
263
- foreach (FileFacade facade in fileFacades)
263
+ foreach (var facade in fileFacades)
264
{
265
if (!mediaRows.TryGetValue(facade.DiskId, out var mediaRow))
266
{
267
- this.Messaging.Write(ErrorMessages.MissingMedia(facade.File.SourceLineNumbers, facade.DiskId));
267
+ this.Messaging.Write(ErrorMessages.MissingMedia(facade.SourceLineNumber, facade.DiskId));
268
continue;
269
}
270
271
// When building a product, if the current file is to be uncompressed or if
272
// the package set not to be compressed, don't cab it.
273
- var compressed = (facade.File.Attributes & FileTupleAttributes.Compressed) == FileTupleAttributes.Compressed;
274
- var uncompressed = (facade.File.Attributes & FileTupleAttributes.Uncompressed) == FileTupleAttributes.Uncompressed;
273
+ var compressed = facade.Compressed;
274
+ var uncompressed = facade.Uncompressed;
275
if (SectionType.Product == this.Section.Type && (uncompressed || (!compressed && !this.FilesCompressed)))
276
{
277
uncompressedFiles.Add(facade);
@@ -284,7 +284,7 @@ namespace WixToolset.Core.WindowsInstaller.Bind
284
}
285
else
286
{
287
- this.Messaging.Write(ErrorMessages.ExpectedMediaCabinet(facade.File.SourceLineNumbers, facade.File.Id.Id, facade.DiskId));
287
+ this.Messaging.Write(ErrorMessages.ExpectedMediaCabinet(facade.SourceLineNumber, facade.Id, facade.DiskId));
288
}
289
}
290
}
src/WixToolset.Core.WindowsInstaller/Bind/AttachPatchTransformsCommand.cs
new
+1322
@@ -0,0 +1,1322 @@
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.Globalization;
8
+ using System.Linq;
9
+ using System.Text.RegularExpressions;
10
+ using WixToolset.Core.WindowsInstaller;
11
+ using WixToolset.Core.WindowsInstaller.Msi;
12
+ using WixToolset.Data;
13
+ using WixToolset.Data.Tuples;
14
+ using WixToolset.Data.WindowsInstaller;
15
+ using WixToolset.Data.WindowsInstaller.Rows;
16
+ using WixToolset.Extensibility.Services;
17
+
18
+ /// <summary>
19
+ /// Include transforms in a patch.
20
+ /// </summary>
21
+ internal class AttachPatchTransformsCommand
22
+ {
23
+ private static readonly string[] PatchUninstallBreakingTables = new[]
24
+ {
25
+ "AppId",
26
+ "BindImage",
27
+ "Class",
28
+ "Complus",
29
+ "CreateFolder",
30
+ "DuplicateFile",
31
+ "Environment",
32
+ "Extension",
33
+ "Font",
34
+ "IniFile",
35
+ "IsolatedComponent",
36
+ "LockPermissions",
37
+ "MIME",
38
+ "MoveFile",
39
+ "MsiLockPermissionsEx",
40
+ "MsiServiceConfig",
41
+ "MsiServiceConfigFailureActions",
42
+ "ODBCAttribute",
43
+ "ODBCDataSource",
44
+ "ODBCDriver",
45
+ "ODBCSourceAttribute",
46
+ "ODBCTranslator",
47
+ "ProgId",
48
+ "PublishComponent",
49
+ "RemoveIniFile",
50
+ "SelfReg",
51
+ "ServiceControl",
52
+ "ServiceInstall",
53
+ "TypeLib",
54
+ "Verb",
55
+ };
56
+
57
+ private readonly TableDefinitionCollection tableDefinitions;
58
+
59
+ public AttachPatchTransformsCommand(IMessaging messaging, Intermediate intermediate, IEnumerable<PatchTransform> transforms)
60
+ {
61
+ this.tableDefinitions = new TableDefinitionCollection(WindowsInstallerStandardInternal.GetTableDefinitions());
62
+ this.Messaging = messaging;
63
+ this.Intermediate = intermediate;
64
+ this.Transforms = transforms;
65
+ }
66
+
67
+ private IMessaging Messaging { get; }
68
+
69
+ private Intermediate Intermediate { get; }
70
+
71
+ private IEnumerable<PatchTransform> Transforms { get; }
72
+
73
+ public IEnumerable<SubStorage> SubStorages { get; private set; }
74
+
75
+ public IEnumerable<SubStorage> Execute()
76
+ {
77
+ var subStorages = new List<SubStorage>();
78
+
79
+ if (this.Transforms == null || !this.Transforms.Any())
80
+ {
81
+ this.Messaging.Write(ErrorMessages.PatchWithoutTransforms());
82
+ return subStorages;
83
+ }
84
+
85
+ var summaryInfo = this.ExtractPatchSummaryInfo();
86
+
87
+ var section = this.Intermediate.Sections.First();
88
+
89
+ var tuples = this.Intermediate.Sections.SelectMany(s => s.Tuples).ToList();
90
+
91
+ // Get the patch id from the WixPatchId tuple.
92
+ var patchIdTuple = tuples.OfType<WixPatchIdTuple>().FirstOrDefault();
93
+
94
+ if (String.IsNullOrEmpty(patchIdTuple.Id?.Id))
95
+ {
96
+ this.Messaging.Write(ErrorMessages.ExpectedPatchIdInWixMsp());
97
+ return subStorages;
98
+ }
99
+
100
+ if (String.IsNullOrEmpty(patchIdTuple.ClientPatchId))
101
+ {
102
+ this.Messaging.Write(ErrorMessages.ExpectedClientPatchIdInWixMsp());
103
+ return subStorages;
104
+ }
105
+
106
+ // enumerate patch.Media to map diskId to Media row
107
+ var patchMediaByDiskId = tuples.OfType<MediaTuple>().ToDictionary(t => t.DiskId);
108
+
109
+ if (patchMediaByDiskId.Count == 0)
110
+ {
111
+ this.Messaging.Write(ErrorMessages.ExpectedMediaRowsInWixMsp());
112
+ return subStorages;
113
+ }
114
+
115
+ // populate MSP summary information
116
+ var patchMetadata = this.PopulateSummaryInformation(summaryInfo, tuples, patchIdTuple, section.Codepage);
117
+
118
+ // enumerate transforms
119
+ var productCodes = new SortedSet<string>();
120
+ var transformNames = new List<string>();
121
+ var validTransform = new List<Tuple<string, WindowsInstallerData>>();
122
+
123
+ var baselineTuplesById = tuples.OfType<WixPatchBaselineTuple>().ToDictionary(t => t.Id.Id);
124
+
125
+ foreach (var mainTransform in this.Transforms)
126
+ {
127
+ var baselineTuple = baselineTuplesById[mainTransform.Baseline];
128
+
129
+ var patchRefTuples = tuples.OfType<WixPatchRefTuple>().ToList();
130
+ if (patchRefTuples.Count > 0)
131
+ {
132
+ if (!this.ReduceTransform(mainTransform.Transform, patchRefTuples))
133
+ {
134
+ // transform has none of the content authored into this patch
135
+ continue;
136
+ }
137
+ }
138
+
139
+ // Validate the transform doesn't break any patch specific rules.
140
+ this.Validate(mainTransform);
141
+
142
+ // ensure consistent File.Sequence within each Media
143
+ var mediaTuple = patchMediaByDiskId[baselineTuple.DiskId];
144
+
145
+ // Ensure that files are sequenced after the last file in any transform.
146
+ var transformMediaTable = mainTransform.Transform.Tables["Media"];
147
+ if (null != transformMediaTable && 0 < transformMediaTable.Rows.Count)
148
+ {
149
+ foreach (MediaRow transformMediaRow in transformMediaTable.Rows)
150
+ {
151
+ if (mediaTuple.LastSequence < transformMediaRow.LastSequence)
152
+ {
153
+ // The Binder will pre-increment the sequence.
154
+ mediaTuple.LastSequence = transformMediaRow.LastSequence;
155
+ }
156
+ }
157
+ }
158
+
159
+ // Use the Media/@DiskId if greater than the last sequence for backward compatibility.
160
+ if (mediaTuple.LastSequence < mediaTuple.DiskId)
161
+ {
162
+ mediaTuple.LastSequence = mediaTuple.DiskId;
163
+ }
164
+
165
+ // Ignore media table in the transform.
166
+ mainTransform.Transform.Tables.Remove("Media");
167
+ mainTransform.Transform.Tables.Remove("WixMedia");
168
+ mainTransform.Transform.Tables.Remove("MsiDigitalSignature");
169
+
170
+ var pairedTransform = this.BuildPairedTransform(summaryInfo, patchMetadata, patchIdTuple, mainTransform.Transform, mediaTuple, baselineTuple, out var productCode);
171
+
172
+ productCode = productCode.ToUpperInvariant();
173
+ productCodes.Add(productCode);
174
+ validTransform.Add(Tuple.Create(productCode, mainTransform.Transform));
175
+
176
+ // attach these transforms to the patch object
177
+ // TODO: is this an acceptable way to auto-generate transform stream names?
178
+ var transformName = mainTransform.Baseline + "." + validTransform.Count.ToString(CultureInfo.InvariantCulture);
179
+ subStorages.Add(new SubStorage(transformName, mainTransform.Transform));
180
+ subStorages.Add(new SubStorage("#" + transformName, pairedTransform));
181
+
182
+ transformNames.Add(":" + transformName);
183
+ transformNames.Add(":#" + transformName);
184
+ }
185
+
186
+ if (validTransform.Count == 0)
187
+ {
188
+ this.Messaging.Write(ErrorMessages.PatchWithoutValidTransforms());
189
+ return subStorages;
190
+ }
191
+
192
+ // Validate that a patch authored as removable is actually removable
193
+ if (patchMetadata.TryGetValue("AllowRemoval", out var allowRemoval) && allowRemoval.Value == "1")
194
+ {
195
+ var uninstallable = true;
196
+
197
+ foreach (var entry in validTransform)
198
+ {
199
+ uninstallable &= this.CheckUninstallableTransform(entry.Item1, entry.Item2);
200
+ }
201
+
202
+ if (!uninstallable)
203
+ {
204
+ this.Messaging.Write(ErrorMessages.PatchNotRemovable());
205
+ return subStorages;
206
+ }
207
+ }
208
+
209
+ // Finish filling tables with transform-dependent data.
210
+ productCodes = FinalizePatchProductCodes(tuples, productCodes);
211
+
212
+ // Semicolon delimited list of the product codes that can accept the patch.
213
+ summaryInfo.Add(SumaryInformationType.PatchProductCodes, new SummaryInformationTuple(patchIdTuple.SourceLineNumbers)
214
+ {
215
+ PropertyId = SumaryInformationType.PatchProductCodes,
216
+ Value = String.Join(";", productCodes)
217
+ });
218
+
219
+ // Semicolon delimited list of transform substorage names in the order they are applied.
220
+ summaryInfo.Add(SumaryInformationType.TransformNames, new SummaryInformationTuple(patchIdTuple.SourceLineNumbers)
221
+ {
222
+ PropertyId = SumaryInformationType.TransformNames,
223
+ Value = String.Join(";", transformNames)
224
+ });
225
+
226
+ // Put the summary information that was extracted back in now that it is updated.
227
+ foreach (var readSummaryInfo in summaryInfo.Values.OrderBy(s => s.PropertyId))
228
+ {
229
+ section.Tuples.Add(readSummaryInfo);
230
+ }
231
+
232
+ this.SubStorages = subStorages;
233
+
234
+ return subStorages;
235
+ }
236
+
237
+ private Dictionary<SumaryInformationType, SummaryInformationTuple> ExtractPatchSummaryInfo()
238
+ {
239
+ var result = new Dictionary<SumaryInformationType, SummaryInformationTuple>();
240
+
241
+ foreach (var section in this.Intermediate.Sections)
242
+ {
243
+ for (var i = section.Tuples.Count - 1; i >= 0; i--)
244
+ {
245
+ if (section.Tuples[i] is SummaryInformationTuple patchSummaryInfo)
246
+ {
247
+ // Remove all summary information from the tuples and remember those that
248
+ // are not calculated or reserved.
249
+ section.Tuples.RemoveAt(i);
250
+
251
+ if (patchSummaryInfo.PropertyId != SumaryInformationType.PatchProductCodes &&
252
+ patchSummaryInfo.PropertyId != SumaryInformationType.PatchCode &&
253
+ patchSummaryInfo.PropertyId != SumaryInformationType.PatchInstallerRequirement &&
254
+ patchSummaryInfo.PropertyId != SumaryInformationType.Reserved11 &&
255
+ patchSummaryInfo.PropertyId != SumaryInformationType.Reserved14 &&
256
+ patchSummaryInfo.PropertyId != SumaryInformationType.Reserved16)
257
+ {
258
+ result.Add(patchSummaryInfo.PropertyId, patchSummaryInfo);
259
+ }
260
+ }
261
+ }
262
+ }
263
+
264
+ return result;
265
+ }
266
+
267
+ private Dictionary<string, MsiPatchMetadataTuple> PopulateSummaryInformation(Dictionary<SumaryInformationType, SummaryInformationTuple> summaryInfo, List<IntermediateTuple> tuples, WixPatchIdTuple patchIdTuple, int codepage)
268
+ {
269
+ // PID_CODEPAGE
270
+ if (!summaryInfo.ContainsKey(SumaryInformationType.Codepage))
271
+ {
272
+ // Set the code page by default to the same code page for the
273
+ // string pool in the database.
274
+ AddSummaryInformation(SumaryInformationType.Codepage, codepage.ToString(CultureInfo.InvariantCulture), patchIdTuple.SourceLineNumbers);
275
+ }
276
+
277
+ // GUID patch code for the patch.
278
+ AddSummaryInformation(SumaryInformationType.PatchCode, patchIdTuple.Id.Id, patchIdTuple.SourceLineNumbers);
279
+
280
+ // Indicates the minimum Windows Installer version that is required to install the patch.
281
+ AddSummaryInformation(SumaryInformationType.PatchInstallerRequirement, ((int)SummaryInformation.InstallerRequirement.Version31).ToString(CultureInfo.InvariantCulture), patchIdTuple.SourceLineNumbers);
282
+
283
+ if (!summaryInfo.ContainsKey(SumaryInformationType.Security))
284
+ {
285
+ AddSummaryInformation(SumaryInformationType.Security, "4", patchIdTuple.SourceLineNumbers); // Read-only enforced;
286
+ }
287
+
288
+ // Use authored comments or default to display name.
289
+ MsiPatchMetadataTuple commentsTuple = null;
290
+
291
+ var metadataTuples = tuples.OfType<MsiPatchMetadataTuple>().Where(t => String.IsNullOrEmpty(t.Company)).ToDictionary(t => t.Property);
292
+
293
+ if (!summaryInfo.ContainsKey(SumaryInformationType.Title) &&
294
+ metadataTuples.TryGetValue("DisplayName", out var displayName))
295
+ {
296
+ AddSummaryInformation(SumaryInformationType.Title, displayName.Value, displayName.SourceLineNumbers);
297
+
298
+ // Default comments to use display name as-is.
299
+ commentsTuple = displayName;
300
+ }
301
+
302
+ // TODO: This code below seems unnecessary given the codepage is set at the top of this method.
303
+ //if (!summaryInfo.ContainsKey(SumaryInformationType.Codepage) &&
304
+ // metadataValues.TryGetValue("CodePage", out var codepage))
305
+ //{
306
+ // AddSummaryInformation(SumaryInformationType.Codepage, codepage);
307
+ //}
308
+
309
+ if (!summaryInfo.ContainsKey(SumaryInformationType.PatchPackageName) &&
310
+ metadataTuples.TryGetValue("Description", out var description))
311
+ {
312
+ AddSummaryInformation(SumaryInformationType.PatchPackageName, description.Value, description.SourceLineNumbers);
313
+ }
314
+
315
+ if (!summaryInfo.ContainsKey(SumaryInformationType.Author) &&
316
+ metadataTuples.TryGetValue("ManufacturerName", out var manufacturer))
317
+ {
318
+ AddSummaryInformation(SumaryInformationType.Author, manufacturer.Value, manufacturer.SourceLineNumbers);
319
+ }
320
+
321
+ // Special metadata marshalled through the build.
322
+ //var wixMetadataValues = tuples.OfType<WixPatchMetadataTuple>().ToDictionary(t => t.Id.Id, t => t.Value);
323
+
324
+ //if (wixMetadataValues.TryGetValue("Comments", out var wixComments))
325
+ if (metadataTuples.TryGetValue("Comments", out var wixComments))
326
+ {
327
+ commentsTuple = wixComments;
328
+ }
329
+
330
+ // Write the package comments to summary info.
331
+ if (!summaryInfo.ContainsKey(SumaryInformationType.Comments) &&
332
+ commentsTuple != null)
333
+ {
334
+ AddSummaryInformation(SumaryInformationType.Comments, commentsTuple.Value, commentsTuple.SourceLineNumbers);
335
+ }
336
+
337
+ return metadataTuples;
338
+
339
+ void AddSummaryInformation(SumaryInformationType type, string value, SourceLineNumber sourceLineNumber)
340
+ {
341
+ summaryInfo.Add(type, new SummaryInformationTuple(sourceLineNumber)
342
+ {
343
+ PropertyId = type,
344
+ Value = value
345
+ });
346
+ }
347
+ }
348
+
349
+ /// <summary>
350
+ /// Ensure transform is uninstallable.
351
+ /// </summary>
352
+ /// <param name="productCode">Product code in transform.</param>
353
+ /// <param name="transform">Transform generated by torch.</param>
354
+ /// <returns>True if the transform is uninstallable</returns>
355
+ private bool CheckUninstallableTransform(string productCode, WindowsInstallerData transform)
356
+ {
357
+ var success = true;
358
+
359
+ foreach (var tableName in PatchUninstallBreakingTables)
360
+ {
361
+ if (transform.TryGetTable(tableName, out var table))
362
+ {
363
+ foreach (var row in table.Rows)
364
+ {
365
+ if (row.Operation == RowOperation.Add)
366
+ {
367
+ success = false;
368
+
369
+ var primaryKey = row.GetPrimaryKey('/') ?? String.Empty;
370
+
371
+ this.Messaging.Write(ErrorMessages.NewRowAddedInTable(row.SourceLineNumbers, productCode, table.Name, primaryKey));
372
+ }
373
+ }
374
+ }
375
+ }
376
+
377
+ return success;
378
+ }
379
+
380
+ /// <summary>
381
+ /// Reduce the transform according to the patch references.
382
+ /// </summary>
383
+ /// <param name="transform">transform generated by torch.</param>
384
+ /// <param name="patchRefTuples">Table contains patch family filter.</param>
385
+ /// <returns>true if the transform is not empty</returns>
386
+ private bool ReduceTransform(WindowsInstallerData transform, IEnumerable<WixPatchRefTuple> patchRefTuples)
387
+ {
388
+ // identify sections to keep
389
+ var oldSections = new Dictionary<string, Row>();
390
+ var newSections = new Dictionary<string, Row>();
391
+ var tableKeyRows = new Dictionary<string, Dictionary<string, Row>>();
392
+ var sequenceList = new List<Table>();
393
+ var componentFeatureAddsIndex = new Dictionary<string, List<string>>();
394
+ var customActionTable = new Dictionary<string, Row>();
395
+ var directoryTableAdds = new Dictionary<string, Row>();
396
+ var featureTableAdds = new Dictionary<string, Row>();
397
+ var keptComponents = new Dictionary<string, Row>();
398
+ var keptDirectories = new Dictionary<string, Row>();
399
+ var keptFeatures = new Dictionary<string, Row>();
400
+ var keptLockPermissions = new HashSet<string>();
401
+ var keptMsiLockPermissionExs = new HashSet<string>();
402
+
403
+ var componentCreateFolderIndex = new Dictionary<string, List<string>>();
404
+ var directoryLockPermissionsIndex = new Dictionary<string, List<Row>>();
405
+ var directoryMsiLockPermissionsExIndex = new Dictionary<string, List<Row>>();
406
+
407
+ foreach (var patchRefTuple in patchRefTuples)
408
+ {
409
+ var tableName = patchRefTuple.Table;
410
+ var key = patchRefTuple.PrimaryKeys;
411
+
412
+ // Short circuit filtering if all changes should be included.
413
+ if ("*" == tableName && "*" == key)
414
+ {
415
+ RemoveProductCodeFromTransform(transform);
416
+ return true;
417
+ }
418
+
419
+ if (!transform.Tables.TryGetTable(tableName, out var table))
420
+ {
421
+ // Table not found.
422
+ continue;
423
+ }
424
+
425
+ // Index the table.
426
+ if (!tableKeyRows.TryGetValue(tableName, out var keyRows))
427
+ {
428
+ keyRows = new Dictionary<string, Row>();
429
+ tableKeyRows.Add(tableName, keyRows);
430
+
431
+ foreach (var newRow in table.Rows)
432
+ {
433
+ var primaryKey = newRow.GetPrimaryKey();
434
+ keyRows.Add(primaryKey, newRow);
435
+ }
436
+ }
437
+
438
+ if (!keyRows.TryGetValue(key, out var row))
439
+ {
440
+ // Row not found.
441
+ continue;
442
+ }
443
+
444
+ // Differ.sectionDelimiter
445
+ var sections = row.SectionId.Split('/');
446
+ oldSections[sections[0]] = row;
447
+ newSections[sections[1]] = row;
448
+ }
449
+
450
+ // throw away sections not referenced
451
+ var keptRows = 0;
452
+ Table directoryTable = null;
453
+ Table featureTable = null;
454
+ Table lockPermissionsTable = null;
455
+ Table msiLockPermissionsTable = null;
456
+
457
+ foreach (var table in transform.Tables)
458
+ {
459
+ if ("_SummaryInformation" == table.Name)
460
+ {
461
+ continue;
462
+ }
463
+
464
+ if (table.Name == "AdminExecuteSequence"
465
+ || table.Name == "AdminUISequence"
466
+ || table.Name == "AdvtExecuteSequence"
467
+ || table.Name == "InstallUISequence"
468
+ || table.Name == "InstallExecuteSequence")
469
+ {
470
+ sequenceList.Add(table);
471
+ continue;
472
+ }
473
+
474
+ for (var i = 0; i < table.Rows.Count; i++)
475
+ {
476
+ var row = table.Rows[i];
477
+
478
+ if (table.Name == "CreateFolder")
479
+ {
480
+ var createFolderComponentId = row.FieldAsString(1);
481
+
482
+ if (!componentCreateFolderIndex.TryGetValue(createFolderComponentId, out var directoryList))
483
+ {
484
+ directoryList = new List<string>();
485
+ componentCreateFolderIndex.Add(createFolderComponentId, directoryList);
486
+ }
487
+
488
+ directoryList.Add(row.FieldAsString(0));
489
+ }
490
+
491
+ if (table.Name == "CustomAction")
492
+ {
493
+ customActionTable.Add(row.FieldAsString(0), row);
494
+ }
495
+
496
+ if (table.Name == "Directory")
497
+ {
498
+ directoryTable = table;
499
+ if (RowOperation.Add == row.Operation)
500
+ {
501
+ directoryTableAdds.Add(row.FieldAsString(0), row);
502
+ }
503
+ }
504
+
505
+ if (table.Name == "Feature")
506
+ {
507
+ featureTable = table;
508
+ if (RowOperation.Add == row.Operation)
509
+ {
510
+ featureTableAdds.Add(row.FieldAsString(0), row);
511
+ }
512
+ }
513
+
514
+ if (table.Name == "FeatureComponents")
515
+ {
516
+ if (RowOperation.Add == row.Operation)
517
+ {
518
+ var featureId = row.FieldAsString(0);
519
+ var componentId = row.FieldAsString(1);
520
+
521
+ if (!componentFeatureAddsIndex.TryGetValue(componentId, out var featureList))
522
+ {
523
+ featureList = new List<string>();
524
+ componentFeatureAddsIndex.Add(componentId, featureList);
525
+ }
526
+
527
+ featureList.Add(featureId);
528
+ }
529
+ }
530
+
531
+ if (table.Name == "LockPermissions")
532
+ {
533
+ lockPermissionsTable = table;
534
+ if ("CreateFolder" == row.FieldAsString(1))
535
+ {
536
+ var directoryId = row.FieldAsString(0);
537
+
538
+ if (!directoryLockPermissionsIndex.TryGetValue(directoryId, out var rowList))
539
+ {
540
+ rowList = new List<Row>();
541
+ directoryLockPermissionsIndex.Add(directoryId, rowList);
542
+ }
543
+
544
+ rowList.Add(row);
545
+ }
546
+ }
547
+
548
+ if (table.Name == "MsiLockPermissionsEx")
549
+ {
550
+ msiLockPermissionsTable = table;
551
+ if ("CreateFolder" == row.FieldAsString(1))
552
+ {
553
+ var directoryId = row.FieldAsString(0);
554
+
555
+ if (!directoryMsiLockPermissionsExIndex.TryGetValue(directoryId, out var rowList))
556
+ {
557
+ rowList = new List<Row>();
558
+ directoryMsiLockPermissionsExIndex.Add(directoryId, rowList);
559
+ }
560
+
561
+ rowList.Add(row);
562
+ }
563
+ }
564
+
565
+ if (null == row.SectionId)
566
+ {
567
+ table.Rows.RemoveAt(i);
568
+ i--;
569
+ }
570
+ else
571
+ {
572
+ var sections = row.SectionId.Split('/');
573
+ // ignore the row without section id.
574
+ if (0 == sections[0].Length && 0 == sections[1].Length)
575
+ {
576
+ table.Rows.RemoveAt(i);
577
+ i--;
578
+ }
579
+ else if (IsInPatchFamily(sections[0], sections[1], oldSections, newSections))
580
+ {
581
+ if ("Component" == table.Name)
582
+ {
583
+ keptComponents.Add(row.FieldAsString(0), row);
584
+ }
585
+
586
+ if ("Directory" == table.Name)
587
+ {
588
+ keptDirectories.Add(row.FieldAsString(0), row);
589
+ }
590
+
591
+ if ("Feature" == table.Name)
592
+ {
593
+ keptFeatures.Add(row.FieldAsString(0), row);
594
+ }
595
+
596
+ keptRows++;
597
+ }
598
+ else
599
+ {
600
+ table.Rows.RemoveAt(i);
601
+ i--;
602
+ }
603
+ }
604
+ }
605
+ }
606
+
607
+ keptRows += ReduceTransformSequenceTable(sequenceList, oldSections, newSections, customActionTable);
608
+
609
+ if (null != directoryTable)
610
+ {
611
+ foreach (var componentRow in keptComponents.Values)
612
+ {
613
+ var componentId = componentRow.FieldAsString(0);
614
+
615
+ if (RowOperation.Add == componentRow.Operation)
616
+ {
617
+ // Make sure each added component has its required directory and feature heirarchy.
618
+ var directoryId = componentRow.FieldAsString(2);
619
+ while (null != directoryId && directoryTableAdds.TryGetValue(directoryId, out var directoryRow))
620
+ {
621
+ if (!keptDirectories.ContainsKey(directoryId))
622
+ {
623
+ directoryTable.Rows.Add(directoryRow);
624
+ keptDirectories.Add(directoryId, directoryRow);
625
+ keptRows++;
626
+ }
627
+
628
+ directoryId = directoryRow.FieldAsString(1);
629
+ }
630
+
631
+ if (componentFeatureAddsIndex.TryGetValue(componentId, out var componentFeatureIds))
632
+ {
633
+ foreach (var featureId in componentFeatureIds)
634
+ {
635
+ var currentFeatureId = featureId;
636
+ while (null != currentFeatureId && featureTableAdds.TryGetValue(currentFeatureId, out var featureRow))
637
+ {
638
+ if (!keptFeatures.ContainsKey(currentFeatureId))
639
+ {
640
+ featureTable.Rows.Add(featureRow);
641
+ keptFeatures.Add(currentFeatureId, featureRow);
642
+ keptRows++;
643
+ }
644
+
645
+ currentFeatureId = featureRow.FieldAsString(1);
646
+ }
647
+ }
648
+ }
649
+ }
650
+
651
+ // Hook in changes LockPermissions and MsiLockPermissions for folders for each component that has been kept.
652
+ foreach (var keptComponentId in keptComponents.Keys)
653
+ {
654
+ if (componentCreateFolderIndex.TryGetValue(keptComponentId, out var directoryList))
655
+ {
656
+ foreach (var directoryId in directoryList)
657
+ {
658
+ if (directoryLockPermissionsIndex.TryGetValue(directoryId, out var lockPermissionsRowList))
659
+ {
660
+ foreach (var lockPermissionsRow in lockPermissionsRowList)
661
+ {
662
+ var key = lockPermissionsRow.GetPrimaryKey('/');
663
+ if (keptLockPermissions.Add(key))
664
+ {
665
+ lockPermissionsTable.Rows.Add(lockPermissionsRow);
666
+ keptRows++;
667
+ }
668
+ }
669
+ }
670
+
671
+ if (directoryMsiLockPermissionsExIndex.TryGetValue(directoryId, out var msiLockPermissionsExRowList))
672
+ {
673
+ foreach (var msiLockPermissionsExRow in msiLockPermissionsExRowList)
674
+ {
675
+ var key = msiLockPermissionsExRow.GetPrimaryKey('/');
676
+ if (keptMsiLockPermissionExs.Add(key))
677
+ {
678
+ msiLockPermissionsTable.Rows.Add(msiLockPermissionsExRow);
679
+ keptRows++;
680
+ }
681
+ }
682
+ }
683
+ }
684
+ }
685
+ }
686
+ }
687
+ }
688
+
689
+ keptRows += ReduceTransformSequenceTable(sequenceList, oldSections, newSections, customActionTable);
690
+
691
+ // Delete tables that are empty.
692
+ var tablesToDelete = transform.Tables.Where(t => t.Rows.Count == 0).Select(t => t.Name);
693
+
694
+ foreach (var tableName in tablesToDelete)
695
+ {
696
+ transform.Tables.Remove(tableName);
697
+ }
698
+
699
+ return keptRows > 0;
700
+ }
701
+
702
+ private void Validate(PatchTransform patchTransform)
703
+ {
704
+ var transformPath = patchTransform.Baseline; // TODO: this is used in error messages, how best to set it?
705
+ var transform = patchTransform.Transform;
706
+
707
+ // Changing the ProdocutCode in a patch transform is not recommended.
708
+ if (transform.TryGetTable("Property", out var propertyTable))
709
+ {
710
+ foreach (var row in propertyTable.Rows)
711
+ {
712
+ // Only interested in modified rows; fast check.
713
+ if (RowOperation.Modify == row.Operation &&
714
+ "ProductCode".Equals(row.FieldAsString(0), StringComparison.Ordinal))
715
+ {
716
+ this.Messaging.Write(WarningMessages.MajorUpgradePatchNotRecommended());
717
+ }
718
+ }
719
+ }
720
+
721
+ // If there is nothing in the component table we can return early because the remaining checks are component based.
722
+ if (!transform.TryGetTable("Component", out var componentTable))
723
+ {
724
+ return;
725
+ }
726
+
727
+ // Index Feature table row operations
728
+ var featureOps = new Dictionary<string, RowOperation>();
729
+ if (transform.TryGetTable("Feature", out var featureTable))
730
+ {
731
+ foreach (var row in featureTable.Rows)
732
+ {
733
+ featureOps[row.FieldAsString(0)] = row.Operation;
734
+ }
735
+ }
736
+
737
+ // Index Component table and check for keypath modifications
738
+ var componentKeyPath = new Dictionary<string, string>();
739
+ var deletedComponent = new Dictionary<string, Row>();
740
+ foreach (var row in componentTable.Rows)
741
+ {
742
+ var id = row.FieldAsString(0);
743
+ var keypath = row.FieldAsString(5) ?? String.Empty;
744
+
745
+ componentKeyPath.Add(id, keypath);
746
+
747
+ if (RowOperation.Delete == row.Operation)
748
+ {
749
+ deletedComponent.Add(id, row);
750
+ }
751
+ else if (RowOperation.Modify == row.Operation)
752
+ {
753
+ if (row.Fields[1].Modified)
754
+ {
755
+ // Changing the guid of a component is equal to deleting the old one and adding a new one.
756
+ deletedComponent.Add(id, row);
757
+ }
758
+
759
+ // If the keypath is modified its an error
760
+ if (row.Fields[5].Modified)
761
+ {
762
+ this.Messaging.Write(ErrorMessages.InvalidKeypathChange(row.SourceLineNumbers, id, transformPath));
763
+ }
764
+ }
765
+ }
766
+
767
+ // Verify changes in the file table
768
+ if (transform.TryGetTable("File", out var fileTable))
769
+ {
770
+ var componentWithChangedKeyPath = new Dictionary<string, string>();
771
+ foreach (var row in fileTable.Rows)
772
+ {
773
+ if (RowOperation.None == row.Operation)
774
+ {
775
+ continue;
776
+ }
777
+
778
+ var fileId = row.FieldAsString(0);
779
+ var componentId = row.FieldAsString(1);
780
+
781
+ // If this file is the keypath of a component
782
+ if (componentKeyPath.TryGetValue(componentId, out var keyPath) && keyPath.Equals(fileId, StringComparison.Ordinal))
783
+ {
784
+ if (row.Fields[2].Modified)
785
+ {
786
+ // You can't change the filename of a file that is the keypath of a component.
787
+ this.Messaging.Write(ErrorMessages.InvalidKeypathChange(row.SourceLineNumbers, componentId, transformPath));
788
+ }
789
+
790
+ if (!componentWithChangedKeyPath.ContainsKey(componentId))
791
+ {
792
+ componentWithChangedKeyPath.Add(componentId, fileId);
793
+ }
794
+ }
795
+
796
+ if (RowOperation.Delete == row.Operation)
797
+ {
798
+ // If the file is removed from a component that is not deleted.
799
+ if (!deletedComponent.ContainsKey(componentId))
800
+ {
801
+ var foundRemoveFileEntry = false;
802
+ var filename = Common.GetName(row.FieldAsString(2), false, true);
803
+
804
+ if (transform.TryGetTable("RemoveFile", out var removeFileTable))
805
+ {
806
+ foreach (var removeFileRow in removeFileTable.Rows)
807
+ {
808
+ if (RowOperation.Delete == removeFileRow.Operation)
809
+ {
810
+ continue;
811
+ }
812
+
813
+ if (componentId == removeFileRow.FieldAsString(1))
814
+ {
815
+ // Check if there is a RemoveFile entry for this file
816
+ if (null != removeFileRow[2])
817
+ {
818
+ var removeFileName = Common.GetName(removeFileRow.FieldAsString(2), false, true);
819
+
820
+ // Convert the MSI format for a wildcard string to Regex format.
821
+ removeFileName = removeFileName.Replace('.', '|').Replace('?', '.').Replace("*", ".*").Replace("|", "\\.");
822
+
823
+ var regex = new Regex(removeFileName, RegexOptions.CultureInvariant | RegexOptions.IgnoreCase);
824
+ if (regex.IsMatch(filename))
825
+ {
826
+ foundRemoveFileEntry = true;
827
+ break;
828
+ }
829
+ }
830
+ }
831
+ }
832
+ }
833
+
834
+ if (!foundRemoveFileEntry)
835
+ {
836
+ this.Messaging.Write(WarningMessages.InvalidRemoveFile(row.SourceLineNumbers, fileId, componentId));
837
+ }
838
+ }
839
+ }
840
+ }
841
+ }
842
+
843
+ var featureComponentsTable = transform.Tables["FeatureComponents"];
844
+
845
+ if (0 < deletedComponent.Count)
846
+ {
847
+ // Index FeatureComponents table.
848
+ var featureComponents = new Dictionary<string, List<string>>();
849
+
850
+ if (null != featureComponentsTable)
851
+ {
852
+ foreach (var row in featureComponentsTable.Rows)
853
+ {
854
+ var componentId = row.FieldAsString(1);
855
+
856
+ if (!featureComponents.TryGetValue(componentId, out var features))
857
+ {
858
+ features = new List<string>();
859
+ featureComponents.Add(componentId, features);
860
+ }
861
+
862
+ features.Add(row.FieldAsString(0));
863
+ }
864
+ }
865
+
866
+ // Check to make sure if a component was deleted, the feature was too.
867
+ foreach (var entry in deletedComponent)
868
+ {
869
+ if (featureComponents.TryGetValue(entry.Key, out var features))
870
+ {
871
+ foreach (var featureId in features)
872
+ {
873
+ if (!featureOps.TryGetValue(featureId, out var op) || op != RowOperation.Delete)
874
+ {
875
+ // The feature was not deleted.
876
+ this.Messaging.Write(ErrorMessages.InvalidRemoveComponent(((Row)entry.Value).SourceLineNumbers, entry.Key.ToString(), featureId, transformPath));
877
+ }
878
+ }
879
+ }
880
+ }
881
+ }
882
+
883
+ // Warn if new components are added to existing features
884
+ if (null != featureComponentsTable)
885
+ {
886
+ foreach (var row in featureComponentsTable.Rows)
887
+ {
888
+ if (RowOperation.Add == row.Operation)
889
+ {
890
+ // Check if the feature is in the Feature table
891
+ var feature_ = row.FieldAsString(0);
892
+ var component_ = row.FieldAsString(1);
893
+
894
+ // Features may not be present if not referenced
895
+ if (!featureOps.ContainsKey(feature_) || RowOperation.Add != (RowOperation)featureOps[feature_])
896
+ {
897
+ this.Messaging.Write(WarningMessages.NewComponentAddedToExistingFeature(row.SourceLineNumbers, component_, feature_, transformPath));
898
+ }
899
+ }
900
+ }
901
+ }
902
+ }
903
+
904
+ /// <summary>
905
+ /// Remove the ProductCode property from the transform.
906
+ /// </summary>
907
+ /// <param name="transform">The transform.</param>
908
+ /// <remarks>
909
+ /// Changing the ProductCode is not supported in a patch.
910
+ /// </remarks>
911
+ private static void RemoveProductCodeFromTransform(WindowsInstallerData transform)
912
+ {
913
+ if (transform.Tables.TryGetTable("Property", out var propertyTable))
914
+ {
915
+ for (var i = 0; i < propertyTable.Rows.Count; ++i)
916
+ {
917
+ var propertyRow = propertyTable.Rows[i];
918
+ var property = (string)propertyRow[0];
919
+
920
+ if ("ProductCode" == property)
921
+ {
922
+ propertyTable.Rows.RemoveAt(i);
923
+ break;
924
+ }
925
+ }
926
+ }
927
+ }
928
+
929
+ /// <summary>
930
+ /// Check if the section is in a PatchFamily.
931
+ /// </summary>
932
+ /// <param name="oldSection">Section id in target wixout</param>
933
+ /// <param name="newSection">Section id in upgrade wixout</param>
934
+ /// <param name="oldSections">Dictionary contains section id should be kept in the baseline wixout.</param>
935
+ /// <param name="newSections">Dictionary contains section id should be kept in the upgrade wixout.</param>
936
+ /// <returns>true if section in patch family</returns>
937
+ private static bool IsInPatchFamily(string oldSection, string newSection, Dictionary<string, Row> oldSections, Dictionary<string, Row> newSections)
938
+ {
939
+ var result = false;
940
+
941
+ if ((String.IsNullOrEmpty(oldSection) && newSections.ContainsKey(newSection)) || (String.IsNullOrEmpty(newSection) && oldSections.ContainsKey(oldSection)))
942
+ {
943
+ result = true;
944
+ }
945
+ else if (!String.IsNullOrEmpty(oldSection) && !String.IsNullOrEmpty(newSection) && (oldSections.ContainsKey(oldSection) || newSections.ContainsKey(newSection)))
946
+ {
947
+ result = true;
948
+ }
949
+
950
+ return result;
951
+ }
952
+
953
+ /// <summary>
954
+ /// Reduce the transform sequence tables.
955
+ /// </summary>
956
+ /// <param name="sequenceList">ArrayList of tables to be reduced</param>
957
+ /// <param name="oldSections">Hashtable contains section id should be kept in the baseline wixout.</param>
958
+ /// <param name="newSections">Hashtable contains section id should be kept in the target wixout.</param>
959
+ /// <param name="customAction">Hashtable contains all the rows in the CustomAction table.</param>
960
+ /// <returns>Number of rows left</returns>
961
+ private static int ReduceTransformSequenceTable(List<Table> sequenceList, Dictionary<string, Row> oldSections, Dictionary<string, Row> newSections, Dictionary<string, Row> customAction)
962
+ {
963
+ var keptRows = 0;
964
+
965
+ foreach (var currentTable in sequenceList)
966
+ {
967
+ for (var i = 0; i < currentTable.Rows.Count; i++)
968
+ {
969
+ var row = currentTable.Rows[i];
970
+ var actionName = row.Fields[0].Data.ToString();
971
+ var sections = row.SectionId.Split('/');
972
+ var isSectionIdEmpty = (sections[0].Length == 0 && sections[1].Length == 0);
973
+
974
+ if (row.Operation == RowOperation.None)
975
+ {
976
+ // ignore the rows without section id.
977
+ if (isSectionIdEmpty)
978
+ {
979
+ currentTable.Rows.RemoveAt(i);
980
+ i--;
981
+ }
982
+ else if (IsInPatchFamily(sections[0], sections[1], oldSections, newSections))
983
+ {
984
+ keptRows++;
985
+ }
986
+ else
987
+ {
988
+ currentTable.Rows.RemoveAt(i);
989
+ i--;
990
+ }
991
+ }
992
+ else if (row.Operation == RowOperation.Modify)
993
+ {
994
+ var sequenceChanged = row.Fields[2].Modified;
995
+ var conditionChanged = row.Fields[1].Modified;
996
+
997
+ if (sequenceChanged && !conditionChanged)
998
+ {
999
+ keptRows++;
1000
+ }
1001
+ else if (!sequenceChanged && conditionChanged)
1002
+ {
1003
+ if (isSectionIdEmpty)
1004
+ {
1005
+ currentTable.Rows.RemoveAt(i);
1006
+ i--;
1007
+ }
1008
+ else if (IsInPatchFamily(sections[0], sections[1], oldSections, newSections))
1009
+ {
1010
+ keptRows++;
1011
+ }
1012
+ else
1013
+ {
1014
+ currentTable.Rows.RemoveAt(i);
1015
+ i--;
1016
+ }
1017
+ }
1018
+ else if (sequenceChanged && conditionChanged)
1019
+ {
1020
+ if (isSectionIdEmpty)
1021
+ {
1022
+ row.Fields[1].Modified = false;
1023
+ keptRows++;
1024
+ }
1025
+ else if (IsInPatchFamily(sections[0], sections[1], oldSections, newSections))
1026
+ {
1027
+ keptRows++;
1028
+ }
1029
+ else
1030
+ {
1031
+ row.Fields[1].Modified = false;
1032
+ keptRows++;
1033
+ }
1034
+ }
1035
+ }
1036
+ else if (row.Operation == RowOperation.Delete)
1037
+ {
1038
+ if (isSectionIdEmpty)
1039
+ {
1040
+ // it is a stardard action which is added by wix, we should keep this action.
1041
+ row.Operation = RowOperation.None;
1042
+ keptRows++;
1043
+ }
1044
+ else if (IsInPatchFamily(sections[0], sections[1], oldSections, newSections))
1045
+ {
1046
+ keptRows++;
1047
+ }
1048
+ else
1049
+ {
1050
+ if (customAction.ContainsKey(actionName))
1051
+ {
1052
+ currentTable.Rows.RemoveAt(i);
1053
+ i--;
1054
+ }
1055
+ else
1056
+ {
1057
+ // it is a stardard action, we should keep this action.
1058
+ row.Operation = RowOperation.None;
1059
+ keptRows++;
1060
+ }
1061
+ }
1062
+ }
1063
+ else if (row.Operation == RowOperation.Add)
1064
+ {
1065
+ if (isSectionIdEmpty)
1066
+ {
1067
+ keptRows++;
1068
+ }
1069
+ else if (IsInPatchFamily(sections[0], sections[1], oldSections, newSections))
1070
+ {
1071
+ keptRows++;
1072
+ }
1073
+ else
1074
+ {
1075
+ if (customAction.ContainsKey(actionName))
1076
+ {
1077
+ currentTable.Rows.RemoveAt(i);
1078
+ i--;
1079
+ }
1080
+ else
1081
+ {
1082
+ keptRows++;
1083
+ }
1084
+ }
1085
+ }
1086
+ }
1087
+ }
1088
+
1089
+ return keptRows;
1090
+ }
1091
+
1092
+ /// <summary>
1093
+ /// Create the #transform for the given main transform.
1094
+ /// </summary>
1095
+ private WindowsInstallerData BuildPairedTransform(Dictionary<SumaryInformationType, SummaryInformationTuple> summaryInfo, Dictionary<string, MsiPatchMetadataTuple> patchMetadata, WixPatchIdTuple patchIdTuple, WindowsInstallerData mainTransform, MediaTuple mediaTuple, WixPatchBaselineTuple baselineTuple, out string productCode)
1096
+ {
1097
+ productCode = null;
1098
+
1099
+ var pairedTransform = new WindowsInstallerData(null)
1100
+ {
1101
+ Type = OutputType.Transform,
1102
+ Codepage = mainTransform.Codepage
1103
+ };
1104
+
1105
+ // lookup productVersion property to correct summaryInformation
1106
+ var newProductVersion = mainTransform.Tables["Property"]?.Rows.FirstOrDefault(r => r.FieldAsString(0) == "ProductVersion")?.FieldAsString(1);
1107
+
1108
+ var mainSummaryTable = mainTransform.Tables["_SummaryInformation"];
1109
+ var mainSummaryRows = mainSummaryTable.Rows.ToDictionary(r => r.FieldAsInteger(0));
1110
+
1111
+ var baselineValidationFlags = ((int)baselineTuple.ValidationFlags).ToString(CultureInfo.InvariantCulture);
1112
+
1113
+ if (!mainSummaryRows.ContainsKey((int)SumaryInformationType.TransformValidationFlags))
1114
+ {
1115
+ var mainSummaryRow = mainSummaryTable.CreateRow(baselineTuple.SourceLineNumbers);
1116
+ mainSummaryRow[0] = (int)SumaryInformationType.TransformValidationFlags;
1117
+ mainSummaryRow[1] = baselineValidationFlags;
1118
+ }
1119
+
1120
+ // copy summary information from core transform
1121
+ var pairedSummaryTable = pairedTransform.EnsureTable(this.tableDefinitions["_SummaryInformation"]);
1122
+
1123
+ foreach (var mainSummaryRow in mainSummaryTable.Rows)
1124
+ {
1125
+ var type = (SumaryInformationType)mainSummaryRow.FieldAsInteger(0);
1126
+ var value = mainSummaryRow.FieldAsString(1);
1127
+ switch (type)
1128
+ {
1129
+ case SumaryInformationType.TransformProductCodes:
1130
+ var propertyData = value.Split(';');
1131
+ var oldProductVersion = propertyData[0].Substring(38);
1132
+ var upgradeCode = propertyData[2];
1133
+ productCode = propertyData[0].Substring(0, 38);
1134
+
1135
+ if (newProductVersion == null)
1136
+ {
1137
+ newProductVersion = oldProductVersion;
1138
+ }
1139
+
1140
+ // Force mainTranform to 'old;new;upgrade' and pairedTransform to 'new;new;upgrade'
1141
+ mainSummaryRow[1] = String.Concat(productCode, oldProductVersion, ';', productCode, newProductVersion, ';', upgradeCode);
1142
+ value = String.Concat(productCode, newProductVersion, ';', productCode, newProductVersion, ';', upgradeCode);
1143
+ break;
1144
+ case SumaryInformationType.TransformValidationFlags: // use validation flags authored into the patch XML.
1145
+ value = baselineValidationFlags;
1146
+ mainSummaryRow[1] = value;
1147
+ break;
1148
+ }
1149
+
1150
+ var pairedSummaryRow = pairedSummaryTable.CreateRow(mainSummaryRow.SourceLineNumbers);
1151
+ pairedSummaryRow[0] = mainSummaryRow[0];
1152
+ pairedSummaryRow[1] = value;
1153
+ }
1154
+
1155
+ if (productCode == null)
1156
+ {
1157
+ this.Messaging.Write(ErrorMessages.CouldNotDetermineProductCodeFromTransformSummaryInfo());
1158
+ return null;
1159
+ }
1160
+
1161
+ // copy File table
1162
+ if (mainTransform.Tables.TryGetTable("File", out var mainFileTable) && 0 < mainFileTable.Rows.Count)
1163
+ {
1164
+#if TODO_PATCHING
1165
+ // We require file source information.
1166
+ var mainWixFileTable = mainTransform.Tables["WixFile"];
1167
+ if (null == mainWixFileTable)
1168
+ {
1169
+ this.Messaging.Write(ErrorMessages.AdminImageRequired(productCode));
1170
+ return null;
1171
+ }
1172
+
1173
+ var mainFileRows = new RowDictionary<FileRow>(mainFileTable);
1174
+
1175
+ var pairedFileTable = pairedTransform.EnsureTable(mainFileTable.Definition);
1176
+ {
1177
+ var mainFileRow = mainFileRows[mainWixFileRow.File];
1178
+
1179
+ // set File.Sequence to non null to satisfy transform bind
1180
+ mainFileRow.Sequence = 1;
1181
+
1182
+ // delete's don't need rows in the paired transform
1183
+ if (mainFileRow.Operation == RowOperation.Delete)
1184
+ {
1185
+ continue;
1186
+ }
1187
+
1188
+ var pairedFileRow = (FileRow)pairedFileTable.CreateRow(null);
1189
+ pairedFileRow.Operation = RowOperation.Modify;
1190
+ for (var i = 0; i < mainFileRow.Fields.Length; i++)
1191
+ {
1192
+ pairedFileRow[i] = mainFileRow[i];
1193
+ }
1194
+
1195
+ // override authored media for patch bind
1196
+ mainWixFileRow.DiskId = mediaTuple.DiskId;
1197
+
1198
+ // suppress any change to File.Sequence to avoid bloat
1199
+ mainFileRow.Fields[7].Modified = false;
1200
+
1201
+ // force File row to appear in the transform
1202
+ switch (mainFileRow.Operation)
1203
+ {
1204
+ case RowOperation.Modify:
1205
+ case RowOperation.Add:
1206
+ pairedFileRow.Attributes |= WindowsInstallerConstants.MsidbFileAttributesPatchAdded;
1207
+ pairedFileRow.Fields[6].Modified = true;
1208
+ pairedFileRow.Operation = mainFileRow.Operation;
1209
+ break;
1210
+ default:
1211
+ pairedFileRow.Fields[6].Modified = false;
1212
+ break;
1213
+ }
1214
+ }
1215
+#endif
1216
+ }
1217
+
1218
+ // Add Media row to pairedTransform
1219
+ var pairedMediaTable = pairedTransform.EnsureTable(this.tableDefinitions["Media"]);
1220
+ var pairedMediaRow = pairedMediaTable.CreateRow(mediaTuple.SourceLineNumbers);
1221
+ pairedMediaRow.Operation = RowOperation.Add;
1222
+ pairedMediaRow[0] = mediaTuple.DiskId;
1223
+ pairedMediaRow[1] = mediaTuple.LastSequence ?? 0;
1224
+ pairedMediaRow[2] = mediaTuple.DiskPrompt;
1225
+ pairedMediaRow[3] = mediaTuple.Cabinet;
1226
+ pairedMediaRow[4] = mediaTuple.VolumeLabel;
1227
+ pairedMediaRow[5] = mediaTuple.Source;
1228
+
1229
+ // Add PatchPackage for this Media
1230
+ var pairedPackageTable = pairedTransform.EnsureTable(this.tableDefinitions["PatchPackage"]);
1231
+ pairedPackageTable.Operation = TableOperation.Add;
1232
+ var pairedPackageRow = pairedPackageTable.CreateRow(mediaTuple.SourceLineNumbers);
1233
+ pairedPackageRow.Operation = RowOperation.Add;
1234
+ pairedPackageRow[0] = patchIdTuple.Id.Id;
1235
+ pairedPackageRow[1] = mediaTuple.DiskId;
1236
+
1237
+ // Add the property to the patch transform's Property table.
1238
+ var pairedPropertyTable = pairedTransform.EnsureTable(this.tableDefinitions["Property"]);
1239
+ pairedPropertyTable.Operation = TableOperation.Add;
1240
+
1241
+ // Add property to both identify client patches and whether those patches are removable or not
1242
+ patchMetadata.TryGetValue("AllowRemoval", out var allowRemovalTuple);
1243
+
1244
+ var pairedPropertyRow = pairedPropertyTable.CreateRow(allowRemovalTuple?.SourceLineNumbers);
1245
+ pairedPropertyRow.Operation = RowOperation.Add;
1246
+ pairedPropertyRow[0] = String.Concat(patchIdTuple.ClientPatchId, ".AllowRemoval");
1247
+ pairedPropertyRow[1] = allowRemovalTuple?.Value ?? "0";
1248
+
1249
+ // Add this patch code GUID to the patch transform to identify
1250
+ // which patches are installed, including in multi-patch
1251
+ // installations.
1252
+ pairedPropertyRow = pairedPropertyTable.CreateRow(patchIdTuple.SourceLineNumbers);
1253
+ pairedPropertyRow.Operation = RowOperation.Add;
1254
+ pairedPropertyRow[0] = String.Concat(patchIdTuple.ClientPatchId, ".PatchCode");
1255
+ pairedPropertyRow[1] = patchIdTuple.Id.Id;
1256
+
1257
+ // Add PATCHNEWPACKAGECODE to apply to admin layouts.
1258
+ pairedPropertyRow = pairedPropertyTable.CreateRow(patchIdTuple.SourceLineNumbers);
1259
+ pairedPropertyRow.Operation = RowOperation.Add;
1260
+ pairedPropertyRow[0] = "PATCHNEWPACKAGECODE";
1261
+ pairedPropertyRow[1] = patchIdTuple.Id.Id;
1262
+
1263
+ // Add PATCHNEWSUMMARYCOMMENTS and PATCHNEWSUMMARYSUBJECT to apply to admin layouts.
1264
+ if (summaryInfo.TryGetValue(SumaryInformationType.Subject, out var subjectTuple))
1265
+ {
1266
+ pairedPropertyRow = pairedPropertyTable.CreateRow(subjectTuple.SourceLineNumbers);
1267
+ pairedPropertyRow.Operation = RowOperation.Add;
1268
+ pairedPropertyRow[0] = "PATCHNEWSUMMARYSUBJECT";
1269
+ pairedPropertyRow[1] = subjectTuple.Value;
1270
+ }
1271
+
1272
+ if (summaryInfo.TryGetValue(SumaryInformationType.Comments, out var commentsTuple))
1273
+ {
1274
+ pairedPropertyRow = pairedPropertyTable.CreateRow(commentsTuple.SourceLineNumbers);
1275
+ pairedPropertyRow.Operation = RowOperation.Add;
1276
+ pairedPropertyRow[0] = "PATCHNEWSUMMARYCOMMENTS";
1277
+ pairedPropertyRow[1] = commentsTuple.Value;
1278
+ }
1279
+
1280
+ return pairedTransform;
1281
+ }
1282
+
1283
+ private static SortedSet<string> FinalizePatchProductCodes(List<IntermediateTuple> tuples, SortedSet<string> productCodes)
1284
+ {
1285
+ var patchTargetTuples = tuples.OfType<WixPatchTargetTuple>().ToList();
1286
+
1287
+ if (patchTargetTuples.Count > 0)
1288
+ {
1289
+ var targets = new SortedSet<string>();
1290
+ var replace = true;
1291
+ foreach (var wixPatchTargetRow in patchTargetTuples)
1292
+ {
1293
+ var target = wixPatchTargetRow.ProductCode.ToUpperInvariant();
1294
+ if (target == "*")
1295
+ {
1296
+ replace = false;
1297
+ }
1298
+ else
1299
+ {
1300
+ targets.Add(target);
1301
+ }
1302
+ }
1303
+
1304
+ // Replace the target ProductCodes with the authored list.
1305
+ if (replace)
1306
+ {
1307
+ productCodes = targets;
1308
+ }
1309
+ else
1310
+ {
1311
+ // Copy the authored target ProductCodes into the list.
1312
+ foreach (var target in targets)
1313
+ {
1314
+ productCodes.Add(target);
1315
+ }
1316
+ }
1317
+ }
1318
+
1319
+ return productCodes;
1320
+ }
1321
+ }
1322
+}
src/WixToolset.Core.WindowsInstaller/Bind/BindDatabaseCommand.cs
+257
-233
@@ -24,7 +24,11 @@ namespace WixToolset.Core.WindowsInstaller.Bind
24
25
private bool disposed;
26
27
- public BindDatabaseCommand(IBindContext context, IEnumerable<IWindowsInstallerBackendBinderExtension> backendExtension, Validator validator)
27
+ public BindDatabaseCommand(IBindContext context, IEnumerable<IWindowsInstallerBackendBinderExtension> backendExtension, Validator validator):this(context, backendExtension, null, validator)
28
+ {
29
+ }
30
+
31
+ public BindDatabaseCommand(IBindContext context, IEnumerable<IWindowsInstallerBackendBinderExtension> backendExtension, IEnumerable<SubStorage> subStorages, Validator validator)
32
{
33
this.ServiceProvider = context.ServiceProvider;
34
@@ -45,6 +49,7 @@ namespace WixToolset.Core.WindowsInstaller.Bind
49
this.OutputPath = context.OutputPath;
50
this.OutputPdbPath = context.OutputPdbPath;
51
this.IntermediateFolder = context.IntermediateFolder;
52
+ this.SubStorages = subStorages;
53
this.Validator = validator;
54
55
this.BackendExtensions = backendExtension;
@@ -76,6 +81,8 @@ namespace WixToolset.Core.WindowsInstaller.Bind
81
82
private IEnumerable<IWindowsInstallerBackendBinderExtension> BackendExtensions { get; }
83
84
+ private IEnumerable<SubStorage> SubStorages { get; }
85
+
86
private Intermediate Intermediate { get; }
87
88
private string OutputPath { get; }
@@ -112,18 +119,10 @@ namespace WixToolset.Core.WindowsInstaller.Bind
119
// Load standard tables, authored custom tables, and extension custom tables.
120
TableDefinitionCollection tableDefinitions;
121
{
115
- var command = new LoadTableDefinitionsCommand(section);
122
+ var command = new LoadTableDefinitionsCommand(section, this.BackendExtensions);
123
command.Execute();
124
125
tableDefinitions = command.TableDefinitions;
119
-
120
- foreach (var backendExtension in this.BackendExtensions)
121
- {
122
- foreach (var tableDefinition in backendExtension.TableDefinitions)
123
- {
124
- tableDefinitions.Add(tableDefinition);
125
- }
126
- }
126
}
127
128
// Process the summary information table before the other tables.
@@ -186,8 +185,7 @@ namespace WixToolset.Core.WindowsInstaller.Bind
185
186
// Sequence all the actions.
187
{
189
- var command = new SequenceActionsCommand(section);
190
- command.Messaging = this.Messaging;
188
+ var command = new SequenceActionsCommand(this.Messaging, section);
189
command.Execute();
190
}
191
@@ -196,7 +194,7 @@ namespace WixToolset.Core.WindowsInstaller.Bind
194
command.Execute();
195
}
196
199
-#if TODO_FINISH_PATCH
197
+#if TODO_PATCHING
198
////if (OutputType.Patch == this.Output.Type)
199
////{
200
//// foreach (SubStorage substorage in this.Output.SubStorages)
@@ -223,130 +221,162 @@ namespace WixToolset.Core.WindowsInstaller.Bind
221
return;
222
}
223
226
- this.Messaging.Write(VerboseMessages.UpdatingFileInformation());
224
+ // Call extension
225
+ var ExtensionSaidSkip = false;
226
228
- // This must occur after all variables and source paths have been resolved.
229
- List<FileFacade> fileFacades;
227
+ WindowsInstallerData output;
228
+ if (ExtensionSaidSkip)
229
{
231
- var command = new GetFileFacadesCommand(section);
230
+ // Time to create the output object, since we're bypassing everything that touches files.
231
+ var command = new CreateOutputFromIRCommand(this.Messaging, section, tableDefinitions, this.BackendExtensions);
232
command.Execute();
233
234
- fileFacades = command.FileFacades;
234
+ output = command.Output;
235
}
236
-
237
- // Extract files that come from binary .wixlibs and WixExtensions (this does not extract files from merge modules).
236
+ else
237
{
239
- var command = new ExtractEmbeddedFilesCommand(this.ExpectedEmbeddedFiles);
240
- command.Execute();
241
- }
238
+ this.Messaging.Write(VerboseMessages.UpdatingFileInformation());
239
243
- // Gather information about files that do not come from merge modules.
244
- {
245
- var command = new UpdateFileFacadesCommand(this.Messaging, section);
246
- command.FileFacades = fileFacades;
247
- command.UpdateFileFacades = fileFacades.Where(f => !f.FromModule);
248
- command.OverwriteHash = true;
249
- command.TableDefinitions = tableDefinitions;
250
- command.VariableCache = variableCache;
251
- command.Execute();
252
- }
240
+ // Extract files that come from binary .wixlibs and WixExtensions (this does not extract files from merge modules).
241
+ {
242
+ var command = new ExtractEmbeddedFilesCommand(this.ExpectedEmbeddedFiles);
243
+ command.Execute();
244
+ }
245
254
- // Now that the variable cache is populated, resolve any delayed fields.
255
- if (this.DelayedFields.Any())
256
- {
257
- var command = new ResolveDelayedFieldsCommand(this.Messaging, this.DelayedFields, variableCache);
258
- command.Execute();
259
- }
246
+ // This must occur after all variables and source paths have been resolved.
247
+ List<FileFacade> fileFacades;
248
+ {
249
+ var command = new GetFileFacadesCommand(section);
250
+ command.Execute();
251
261
- // Set generated component guids.
262
- {
263
- var command = new CalculateComponentGuids(this.Messaging, this.BackendHelper, this.PathResolver, section);
264
- command.Execute();
265
- }
252
+ fileFacades = command.FileFacades;
253
+ }
254
267
- // Retrieve file information from merge modules.
268
- if (SectionType.Product == section.Type)
269
- {
270
- var wixMergeTuples = section.Tuples.OfType<WixMergeTuple>().ToList();
255
+ // Retrieve file information from merge modules.
256
+ if (SectionType.Product == section.Type)
257
+ {
258
+ var wixMergeTuples = section.Tuples.OfType<WixMergeTuple>().ToList();
259
+
260
+ if (wixMergeTuples.Any())
261
+ {
262
+ containsMergeModules = true;
263
+
264
+ var command = new ExtractMergeModuleFilesCommand(this.Messaging, section, wixMergeTuples);
265
+ command.FileFacades = fileFacades;
266
+ command.OutputInstallerVersion = installerVersion;
267
+ command.SuppressLayout = this.SuppressLayout;
268
+ command.IntermediateFolder = this.IntermediateFolder;
269
+ command.Execute();
270
272
- if (wixMergeTuples.Any())
271
+ fileFacades.AddRange(command.MergeModulesFileFacades);
272
+ }
273
+ }
274
+ else if (SectionType.Patch == section.Type)
275
{
274
- containsMergeModules = true;
276
+ // Merge transform data into the output object.
277
+ //IEnumerable<FileFacade> filesFromTransform = this.CopyFromTransformData(this.Output);
278
+
279
+ //var command = new CopyTransformDataCommand(this.Messaging, /*output*/this.SubStorages, tableDefinitions, copyOutFileRows: true);
280
+ //command.Output = output;
281
+ //command.TableDefinitions = this.TableDefinitions;
282
+ //command.CopyOutFileRows = true;
283
+ var command = new GetFileFacadesFromTransforms(this.Messaging, this.SubStorages, tableDefinitions);
284
+ command.Execute();
285
+ var filesFromTransforms = command.FileFacades;
286
276
- var command = new ExtractMergeModuleFilesCommand(this.Messaging, section, wixMergeTuples);
287
+ fileFacades.AddRange(filesFromTransforms);
288
+ }
289
+
290
+ // stop processing if an error previously occurred
291
+ if (this.Messaging.EncounteredError)
292
+ {
293
+ return;
294
+ }
295
+
296
+ // Gather information about files that do not come from merge modules.
297
+ {
298
+ var command = new UpdateFileFacadesCommand(this.Messaging, section);
299
command.FileFacades = fileFacades;
278
- command.OutputInstallerVersion = installerVersion;
279
- command.SuppressLayout = this.SuppressLayout;
280
- command.IntermediateFolder = this.IntermediateFolder;
300
+ command.UpdateFileFacades = fileFacades.Where(f => !f.FromModule);
301
+ command.OverwriteHash = true;
302
+ command.TableDefinitions = tableDefinitions;
303
+ command.VariableCache = variableCache;
304
command.Execute();
282
-
283
- fileFacades.AddRange(command.MergeModulesFileFacades);
305
}
285
- }
286
-#if TODO_FINISH_PATCH
287
- else if (OutputType.Patch == this.Output.Type)
288
- {
289
- // Merge transform data into the output object.
290
- IEnumerable<FileFacade> filesFromTransform = this.CopyFromTransformData(this.Output);
306
292
- fileFacades.AddRange(filesFromTransform);
293
- }
294
-#endif
307
+ // Assign files to media.
308
+ Dictionary<int, MediaTuple> assignedMediaRows;
309
+ Dictionary<MediaTuple, IEnumerable<FileFacade>> filesByCabinetMedia;
310
+ IEnumerable<FileFacade> uncompressedFiles;
311
+ {
312
+ var command = new AssignMediaCommand(section, this.Messaging);
313
+ command.FileFacades = fileFacades;
314
+ command.FilesCompressed = compressed;
315
+ command.Execute();
316
296
- // stop processing if an error previously occurred
297
- if (this.Messaging.EncounteredError)
298
- {
299
- return;
300
- }
317
+ assignedMediaRows = command.MediaRows;
318
+ filesByCabinetMedia = command.FileFacadesByCabinetMedia;
319
+ uncompressedFiles = command.UncompressedFileFacades;
320
+ }
321
302
- // Assign files to media.
303
- Dictionary<int, MediaTuple> assignedMediaRows;
304
- Dictionary<MediaTuple, IEnumerable<FileFacade>> filesByCabinetMedia;
305
- IEnumerable<FileFacade> uncompressedFiles;
306
- {
307
- var command = new AssignMediaCommand(section, this.Messaging);
308
- command.FileFacades = fileFacades;
309
- command.FilesCompressed = compressed;
310
- command.Execute();
322
+ // stop processing if an error previously occurred
323
+ if (this.Messaging.EncounteredError)
324
+ {
325
+ return;
326
+ }
327
312
- assignedMediaRows = command.MediaRows;
313
- filesByCabinetMedia = command.FileFacadesByCabinetMedia;
314
- uncompressedFiles = command.UncompressedFileFacades;
315
- }
328
+ // Now that the variable cache is populated, resolve any delayed fields.
329
+ if (this.DelayedFields.Any())
330
+ {
331
+ var command = new ResolveDelayedFieldsCommand(this.Messaging, this.DelayedFields, variableCache);
332
+ command.Execute();
333
+ }
334
317
- // stop processing if an error previously occurred
318
- if (this.Messaging.EncounteredError)
319
- {
320
- return;
321
- }
335
+ // Set generated component guids.
336
+ {
337
+ var command = new CalculateComponentGuids(this.Messaging, this.BackendHelper, this.PathResolver, section);
338
+ command.Execute();
339
+ }
340
323
- // Time to create the output object. Try to put as much above here as possible, updating the IR is better.
324
- WindowsInstallerData output;
325
- {
326
- var command = new CreateOutputFromIRCommand(this.Messaging, section, tableDefinitions, this.BackendExtensions);
327
- command.Execute();
341
+ // stop processing if an error previously occurred
342
+ if (this.Messaging.EncounteredError)
343
+ {
344
+ return;
345
+ }
346
329
- output = command.Output;
330
- }
347
+ // Time to create the output object. Try to put as much above here as possible, updating the IR is better.
348
+ {
349
+ var command = new CreateOutputFromIRCommand(this.Messaging, section, tableDefinitions, this.BackendExtensions);
350
+ command.Execute();
351
332
- // Update file sequence.
333
- {
334
- var command = new UpdateMediaSequencesCommand(output, fileFacades);
335
- command.Execute();
336
- }
352
+ output = command.Output;
353
+ }
354
338
- // Modularize identifiers.
339
- if (OutputType.Module == output.Type)
340
- {
341
- var command = new ModularizeCommand(output, modularizationGuid, section.Tuples.OfType<WixSuppressModularizationTuple>());
342
- command.Execute();
343
- }
344
- else // we can create instance transforms since Component Guids are set.
345
- {
355
+ // Update file sequence.
356
+ {
357
+ var command = new UpdateMediaSequencesCommand(output, fileFacades);
358
+ command.Execute();
359
+ }
360
+
361
+ // Modularize identifiers.
362
+ if (OutputType.Module == output.Type)
363
+ {
364
+ var command = new ModularizeCommand(output, modularizationGuid, section.Tuples.OfType<WixSuppressModularizationTuple>());
365
+ command.Execute();
366
+ }
367
+ else if (output.Type == OutputType.Patch)
368
+ {
369
+ foreach (var storage in this.SubStorages)
370
+ {
371
+ output.SubStorages.Add(storage);
372
+ }
373
+ }
374
+ else // we can create instance transforms since Component Guids are set.
375
+ {
376
#if TODO_FIX_INSTANCE_TRANSFORM
347
- this.CreateInstanceTransforms(this.Output);
377
+ this.CreateInstanceTransforms(this.Output);
378
#endif
349
- }
379
+ }
380
381
#if TODO_FINISH_UPDATE
382
// Extended binder extensions can be called now that fields are resolved.
@@ -384,116 +414,121 @@ namespace WixToolset.Core.WindowsInstaller.Bind
414
}
415
#endif
416
387
- // Stop processing if an error previously occurred.
388
- if (this.Messaging.EncounteredError)
389
- {
390
- return;
391
- }
417
+ this.ValidateComponentGuids(output);
418
393
- // Ensure the intermediate folder is created since delta patches will be
394
- // created there.
395
- Directory.CreateDirectory(this.IntermediateFolder);
419
+ // Stop processing if an error previously occurred.
420
+ if (this.Messaging.EncounteredError)
421
+ {
422
+ return;
423
+ }
424
397
- if (SectionType.Patch == section.Type && this.DeltaBinaryPatch)
398
- {
399
- var command = new CreateDeltaPatchesCommand(fileFacades, this.IntermediateFolder, section.Tuples.OfType<WixPatchIdTuple>().FirstOrDefault());
400
- command.Execute();
401
- }
425
+ // Ensure the intermediate folder is created since delta patches will be
426
+ // created there.
427
+ Directory.CreateDirectory(this.IntermediateFolder);
428
403
- // create cabinet files and process uncompressed files
404
- var layoutDirectory = Path.GetDirectoryName(this.OutputPath);
405
- if (!this.SuppressLayout || OutputType.Module == output.Type)
406
- {
407
- this.Messaging.Write(VerboseMessages.CreatingCabinetFiles());
408
-
409
- var command = new CreateCabinetsCommand(this.ServiceProvider, this.BackendHelper);
410
- command.CabbingThreadCount = this.CabbingThreadCount;
411
- command.CabCachePath = this.CabCachePath;
412
- command.DefaultCompressionLevel = this.DefaultCompressionLevel;
413
- command.Output = output;
414
- command.Messaging = this.Messaging;
415
- command.BackendExtensions = this.BackendExtensions;
416
- command.LayoutDirectory = layoutDirectory;
417
- command.Compressed = compressed;
418
- command.FileRowsByCabinet = filesByCabinetMedia;
419
- command.ResolveMedia = this.ResolveMedia;
420
- command.TableDefinitions = tableDefinitions;
421
- command.TempFilesLocation = this.IntermediateFolder;
422
- command.Execute();
429
+ if (SectionType.Patch == section.Type && this.DeltaBinaryPatch)
430
+ {
431
+ var command = new CreateDeltaPatchesCommand(fileFacades, this.IntermediateFolder, section.Tuples.OfType<WixPatchIdTuple>().FirstOrDefault());
432
+ command.Execute();
433
+ }
434
424
- fileTransfers.AddRange(command.FileTransfers);
425
- trackedFiles.AddRange(command.TrackedFiles);
426
- }
435
+ // create cabinet files and process uncompressed files
436
+ var layoutDirectory = Path.GetDirectoryName(this.OutputPath);
437
+ if (!this.SuppressLayout || OutputType.Module == output.Type)
438
+ {
439
+ this.Messaging.Write(VerboseMessages.CreatingCabinetFiles());
440
+
441
+ var command = new CreateCabinetsCommand(this.ServiceProvider, this.BackendHelper);
442
+ command.CabbingThreadCount = this.CabbingThreadCount;
443
+ command.CabCachePath = this.CabCachePath;
444
+ command.DefaultCompressionLevel = this.DefaultCompressionLevel;
445
+ command.Output = output;
446
+ command.Messaging = this.Messaging;
447
+ command.BackendExtensions = this.BackendExtensions;
448
+ command.LayoutDirectory = layoutDirectory;
449
+ command.Compressed = compressed;
450
+ command.FileRowsByCabinet = filesByCabinetMedia;
451
+ command.ResolveMedia = this.ResolveMedia;
452
+ command.TableDefinitions = tableDefinitions;
453
+ command.TempFilesLocation = this.IntermediateFolder;
454
+ command.Execute();
455
428
-#if TODO_FINISH_PATCH
429
- if (OutputType.Patch == this.Output.Type)
430
- {
431
- // copy output data back into the transforms
432
- this.CopyToTransformData(this.Output);
433
- }
434
-#endif
456
+ fileTransfers.AddRange(command.FileTransfers);
457
+ trackedFiles.AddRange(command.TrackedFiles);
458
+ }
459
436
- this.ValidateComponentGuids(output);
460
+#if DELETE
461
+ if (OutputType.Patch == output.Type)
462
+ {
463
+ // Copy output data back into the transforms.
464
+#if TODO_PATCHING
465
+ var command = new CopyTransformDataCommand(this.Messaging, output, tableDefinitions, copyOutFileRows: false);
466
+ command.Execute();
467
438
- // stop processing if an error previously occurred
439
- if (this.Messaging.EncounteredError)
440
- {
441
- return;
442
- }
468
+ this.CopyToTransformData(this.Output);
469
+#endif
470
+ }
471
+#endif
472
444
- // Generate database file.
445
- this.Messaging.Write(VerboseMessages.GeneratingDatabase());
473
+ // stop processing if an error previously occurred
474
+ if (this.Messaging.EncounteredError)
475
+ {
476
+ return;
477
+ }
478
447
- {
448
- var trackMsi = this.BackendHelper.TrackFile(this.OutputPath, TrackedFileType.Final);
449
- trackedFiles.Add(trackMsi);
479
+ // Generate database file.
480
+ this.Messaging.Write(VerboseMessages.GeneratingDatabase());
481
451
- var temporaryFiles = this.GenerateDatabase(output, tableDefinitions, trackMsi.Path, false, false);
452
- trackedFiles.AddRange(temporaryFiles);
453
- }
482
+ {
483
+ var trackMsi = this.BackendHelper.TrackFile(this.OutputPath, TrackedFileType.Final);
484
+ trackedFiles.Add(trackMsi);
485
455
- // Stop processing if an error previously occurred.
456
- if (this.Messaging.EncounteredError)
457
- {
458
- return;
459
- }
486
+ var temporaryFiles = this.GenerateDatabase(output, tableDefinitions, trackMsi.Path, false, false);
487
+ trackedFiles.AddRange(temporaryFiles);
488
+ }
489
461
- // Merge modules.
462
- if (containsMergeModules)
463
- {
464
- this.Messaging.Write(VerboseMessages.MergingModules());
490
+ // Stop processing if an error previously occurred.
491
+ if (this.Messaging.EncounteredError)
492
+ {
493
+ return;
494
+ }
495
466
- // Add back possibly suppressed sequence tables since all sequence tables must be present
467
- // for the merge process to work. We'll drop the suppressed sequence tables again as
468
- // necessary.
469
- foreach (SequenceTable sequence in Enum.GetValues(typeof(SequenceTable)))
496
+ // Merge modules.
497
+ if (containsMergeModules)
498
{
471
- var sequenceTableName = sequence.ToString();
472
- var sequenceTable = output.Tables[sequenceTableName];
499
+ this.Messaging.Write(VerboseMessages.MergingModules());
500
474
- if (null == sequenceTable)
501
+ // Add back possibly suppressed sequence tables since all sequence tables must be present
502
+ // for the merge process to work. We'll drop the suppressed sequence tables again as
503
+ // necessary.
504
+ foreach (SequenceTable sequence in Enum.GetValues(typeof(SequenceTable)))
505
{
476
- sequenceTable = output.EnsureTable(tableDefinitions[sequenceTableName]);
477
- }
506
+ var sequenceTableName = sequence.ToString();
507
+ var sequenceTable = output.Tables[sequenceTableName];
508
479
- if (0 == sequenceTable.Rows.Count)
480
- {
481
- suppressedTableNames.Add(sequenceTableName);
509
+ if (null == sequenceTable)
510
+ {
511
+ sequenceTable = output.EnsureTable(tableDefinitions[sequenceTableName]);
512
+ }
513
+
514
+ if (0 == sequenceTable.Rows.Count)
515
+ {
516
+ suppressedTableNames.Add(sequenceTableName);
517
+ }
518
}
483
- }
519
485
- var command = new MergeModulesCommand();
486
- command.FileFacades = fileFacades;
487
- command.Output = output;
488
- command.OutputPath = this.OutputPath;
489
- command.SuppressedTableNames = suppressedTableNames;
490
- command.Execute();
491
- }
520
+ var command = new MergeModulesCommand();
521
+ command.FileFacades = fileFacades;
522
+ command.Output = output;
523
+ command.OutputPath = this.OutputPath;
524
+ command.SuppressedTableNames = suppressedTableNames;
525
+ command.Execute();
526
+ }
527
493
- if (this.Messaging.EncounteredError)
494
- {
495
- return;
496
- }
528
+ if (this.Messaging.EncounteredError)
529
+ {
530
+ return;
531
+ }
532
533
#if TODO_FINISH_VALIDATION
534
// Validate the output if there is an MSI validator.
@@ -519,27 +554,29 @@ namespace WixToolset.Core.WindowsInstaller.Bind
554
}
555
#endif
556
522
- // Process uncompressed files.
523
- if (!this.Messaging.EncounteredError && !this.SuppressLayout && uncompressedFiles.Any())
524
- {
525
- var command = new ProcessUncompressedFilesCommand(section, this.BackendHelper, this.PathResolver);
526
- command.Compressed = compressed;
527
- command.FileFacades = uncompressedFiles;
528
- command.LayoutDirectory = layoutDirectory;
529
- command.LongNamesInImage = longNames;
530
- command.ResolveMedia = this.ResolveMedia;
531
- command.DatabasePath = this.OutputPath;
532
- command.Execute();
557
+ // Process uncompressed files.
558
+ if (!this.Messaging.EncounteredError && !this.SuppressLayout && uncompressedFiles.Any())
559
+ {
560
+ var command = new ProcessUncompressedFilesCommand(section, this.BackendHelper, this.PathResolver);
561
+ command.Compressed = compressed;
562
+ command.FileFacades = uncompressedFiles;
563
+ command.LayoutDirectory = layoutDirectory;
564
+ command.LongNamesInImage = longNames;
565
+ command.ResolveMedia = this.ResolveMedia;
566
+ command.DatabasePath = this.OutputPath;
567
+ command.Execute();
568
534
- fileTransfers.AddRange(command.FileTransfers);
535
- trackedFiles.AddRange(command.TrackedFiles);
569
+ fileTransfers.AddRange(command.FileTransfers);
570
+ trackedFiles.AddRange(command.TrackedFiles);
571
+ }
572
+
573
+ // TODO: this is not sufficient to collect all Input files (for example, it misses Binary and Icon tables).
574
+ trackedFiles.AddRange(fileFacades.Select(f => this.BackendHelper.TrackFile(f.SourcePath, TrackedFileType.Input, f.SourceLineNumber)));
575
}
576
577
this.Wixout = this.CreateWixout(trackedFiles, this.Intermediate, output);
578
579
this.FileTransfers = fileTransfers;
541
- // TODO: this is not sufficient to collect all Input files (for example, it misses Binary and Icon tables).
542
- trackedFiles.AddRange(fileFacades.Select(f => this.BackendHelper.TrackFile(f.File.Source.Path, TrackedFileType.Input, f.File.SourceLineNumbers)));
580
this.TrackedFiles = trackedFiles;
581
}
582
@@ -566,7 +603,7 @@ namespace WixToolset.Core.WindowsInstaller.Bind
603
return wixout;
604
}
605
569
-#if TODO_FINISH_PATCH
606
+#if TODO_PATCHING
607
/// <summary>
608
/// Copy file data between transform substorages and the patch output object
609
/// </summary>
@@ -936,28 +973,15 @@ namespace WixToolset.Core.WindowsInstaller.Bind
973
/// <param name="useSubdirectory">Whether to use a subdirectory based on the <paramref name="databaseFile"/> file name for intermediate files.</param>
974
private IEnumerable<ITrackedFile> GenerateDatabase(WindowsInstallerData output, TableDefinitionCollection tableDefinitions, string databaseFile, bool keepAddedColumns, bool useSubdirectory)
975
{
939
- var command = new GenerateDatabaseCommand();
940
- command.BackendHelper = this.BackendHelper;
941
- command.Extensions = this.FileSystemExtensions;
942
- command.Output = output;
943
- command.OutputPath = databaseFile;
944
- command.KeepAddedColumns = keepAddedColumns;
945
- command.UseSubDirectory = useSubdirectory;
946
- command.SuppressAddingValidationRows = this.SuppressAddingValidationRows;
947
- command.TableDefinitions = tableDefinitions;
948
- command.IntermediateFolder = this.IntermediateFolder;
949
- command.Codepage = this.Codepage;
976
+ var command = new GenerateDatabaseCommand(this.Messaging, this.BackendHelper, this.FileSystemExtensions, output, databaseFile, tableDefinitions, this.IntermediateFolder, this.Codepage, keepAddedColumns, this.SuppressAddingValidationRows, useSubdirectory);
977
command.Execute();
978
979
return command.GeneratedTemporaryFiles;
980
}
981
955
- #region IDisposable Support
982
+#region IDisposable Support
983
957
- public void Dispose()
958
- {
959
- this.Dispose(true);
960
- }
984
+ public void Dispose() => this.Dispose(true);
985
986
protected virtual void Dispose(bool disposing)
987
{
@@ -972,6 +996,6 @@ namespace WixToolset.Core.WindowsInstaller.Bind
996
}
997
}
998
975
- #endregion
999
+#endregion
1000
}
1001
}
src/WixToolset.Core.WindowsInstaller/Bind/BindTransformCommand.cs
+95
-102
@@ -15,24 +15,37 @@ namespace WixToolset.Core.WindowsInstaller.Bind
15
16
internal class BindTransformCommand
17
{
18
- public IEnumerable<IFileSystemExtension> Extensions { private get; set; }
18
+ public BindTransformCommand(IMessaging messaging, IBackendHelper backendHelper, IEnumerable<IFileSystemExtension> extensions, string intermediateFolder, WindowsInstallerData transform, string outputPath, TableDefinitionCollection tableDefinitions)
19
+ {
20
+ this.Messaging = messaging;
21
+ this.BackendHelper = backendHelper;
22
+ this.Extensions = extensions;
23
+ this.IntermediateFolder = intermediateFolder;
24
+ this.Transform = transform;
25
+ this.OutputPath = outputPath;
26
+ this.TableDefinitions = tableDefinitions;
27
+ }
28
+
29
+ private IMessaging Messaging { get; }
30
20
- public TableDefinitionCollection TableDefinitions { private get; set; }
31
+ private IBackendHelper BackendHelper { get; }
32
22
- public string TempFilesLocation { private get; set; }
33
+ private IEnumerable<IFileSystemExtension> Extensions { get; }
34
24
- public WindowsInstallerData Transform { private get; set; }
35
+ private TableDefinitionCollection TableDefinitions { get; }
36
26
- public IMessaging Messaging { private get; set; }
37
+ private string IntermediateFolder { get; }
38
28
- public string OutputPath { private get; set; }
39
+ private WindowsInstallerData Transform { get; }
40
+
41
+ private string OutputPath { get; }
42
43
public void Execute()
44
{
32
- int transformFlags = 0;
45
+ var transformFlags = 0;
46
34
- WindowsInstallerData targetOutput = new WindowsInstallerData(null);
35
- WindowsInstallerData updatedOutput = new WindowsInstallerData(null);
47
+ var targetOutput = new WindowsInstallerData(null);
48
+ var updatedOutput = new WindowsInstallerData(null);
49
50
// TODO: handle added columns
51
@@ -49,8 +62,7 @@ namespace WixToolset.Core.WindowsInstaller.Bind
62
string targetUpgradeCode = null;
63
string updatedUpgradeCode = null;
64
52
- Table propertyTable = this.Transform.Tables["Property"];
53
- if (null != propertyTable)
65
+ if (this.Transform.TryGetTable("Property", out var propertyTable))
66
{
67
for (int i = propertyTable.Rows.Count - 1; i >= 0; i--)
68
{
@@ -68,18 +80,21 @@ namespace WixToolset.Core.WindowsInstaller.Bind
80
}
81
}
82
71
- Table targetSummaryInfo = targetOutput.EnsureTable(this.TableDefinitions["_SummaryInformation"]);
72
- Table updatedSummaryInfo = updatedOutput.EnsureTable(this.TableDefinitions["_SummaryInformation"]);
73
- Table targetPropertyTable = targetOutput.EnsureTable(this.TableDefinitions["Property"]);
74
- Table updatedPropertyTable = updatedOutput.EnsureTable(this.TableDefinitions["Property"]);
83
+ var targetSummaryInfo = targetOutput.EnsureTable(this.TableDefinitions["_SummaryInformation"]);
84
+ var updatedSummaryInfo = updatedOutput.EnsureTable(this.TableDefinitions["_SummaryInformation"]);
85
+ var targetPropertyTable = targetOutput.EnsureTable(this.TableDefinitions["Property"]);
86
+ var updatedPropertyTable = updatedOutput.EnsureTable(this.TableDefinitions["Property"]);
87
88
// process special summary information values
77
- foreach (Row row in this.Transform.Tables["_SummaryInformation"].Rows)
89
+ foreach (var row in this.Transform.Tables["_SummaryInformation"].Rows)
90
{
79
- if ((int)SummaryInformation.Transform.CodePage == (int)row[0])
91
+ var summaryId = row.FieldAsInteger(0);
92
+ var summaryData = row.FieldAsString(1);
93
+
94
+ if ((int)SummaryInformation.Transform.CodePage == summaryId)
95
{
96
// convert from a web name if provided
82
- string codePage = (string)row.Fields[1].Data;
97
+ var codePage = summaryData;
98
if (null == codePage)
99
{
100
codePage = "0";
@@ -89,7 +104,7 @@ namespace WixToolset.Core.WindowsInstaller.Bind
104
codePage = Common.GetValidCodePage(codePage).ToString(CultureInfo.InvariantCulture);
105
}
106
92
- string previousCodePage = (string)row.Fields[1].PreviousData;
107
+ var previousCodePage = row.Fields[1].PreviousData;
108
if (null == previousCodePage)
109
{
110
previousCodePage = "0";
@@ -99,50 +114,50 @@ namespace WixToolset.Core.WindowsInstaller.Bind
114
previousCodePage = Common.GetValidCodePage(previousCodePage).ToString(CultureInfo.InvariantCulture);
115
}
116
102
- Row targetCodePageRow = targetSummaryInfo.CreateRow(null);
117
+ var targetCodePageRow = targetSummaryInfo.CreateRow(null);
118
targetCodePageRow[0] = 1; // PID_CODEPAGE
119
targetCodePageRow[1] = previousCodePage;
120
106
- Row updatedCodePageRow = updatedSummaryInfo.CreateRow(null);
121
+ var updatedCodePageRow = updatedSummaryInfo.CreateRow(null);
122
updatedCodePageRow[0] = 1; // PID_CODEPAGE
123
updatedCodePageRow[1] = codePage;
124
}
110
- else if ((int)SummaryInformation.Transform.TargetPlatformAndLanguage == (int)row[0] ||
111
- (int)SummaryInformation.Transform.UpdatedPlatformAndLanguage == (int)row[0])
125
+ else if ((int)SummaryInformation.Transform.TargetPlatformAndLanguage == summaryId ||
126
+ (int)SummaryInformation.Transform.UpdatedPlatformAndLanguage == summaryId)
127
{
128
// the target language
114
- string[] propertyData = ((string)row[1]).Split(';');
115
- string lang = 2 == propertyData.Length ? propertyData[1] : "0";
129
+ var propertyData = summaryData.Split(';');
130
+ var lang = 2 == propertyData.Length ? propertyData[1] : "0";
131
117
- Table tempSummaryInfo = (int)SummaryInformation.Transform.TargetPlatformAndLanguage == (int)row[0] ? targetSummaryInfo : updatedSummaryInfo;
118
- Table tempPropertyTable = (int)SummaryInformation.Transform.TargetPlatformAndLanguage == (int)row[0] ? targetPropertyTable : updatedPropertyTable;
132
+ var tempSummaryInfo = (int)SummaryInformation.Transform.TargetPlatformAndLanguage == summaryId ? targetSummaryInfo : updatedSummaryInfo;
133
+ var tempPropertyTable = (int)SummaryInformation.Transform.TargetPlatformAndLanguage == summaryId ? targetPropertyTable : updatedPropertyTable;
134
120
- Row productLanguageRow = tempPropertyTable.CreateRow(null);
135
+ var productLanguageRow = tempPropertyTable.CreateRow(null);
136
productLanguageRow[0] = "ProductLanguage";
137
productLanguageRow[1] = lang;
138
139
// set the platform;language on the MSI to be generated
125
- Row templateRow = tempSummaryInfo.CreateRow(null);
140
+ var templateRow = tempSummaryInfo.CreateRow(null);
141
templateRow[0] = 7; // PID_TEMPLATE
127
- templateRow[1] = (string)row[1];
142
+ templateRow[1] = summaryData;
143
}
129
- else if ((int)SummaryInformation.Transform.ProductCodes == (int)row[0])
144
+ else if ((int)SummaryInformation.Transform.ProductCodes == summaryId)
145
{
131
- string[] propertyData = ((string)row[1]).Split(';');
146
+ var propertyData = summaryData.Split(';');
147
133
- Row targetProductCodeRow = targetPropertyTable.CreateRow(null);
148
+ var targetProductCodeRow = targetPropertyTable.CreateRow(null);
149
targetProductCodeRow[0] = "ProductCode";
150
targetProductCodeRow[1] = propertyData[0].Substring(0, 38);
151
137
- Row targetProductVersionRow = targetPropertyTable.CreateRow(null);
152
+ var targetProductVersionRow = targetPropertyTable.CreateRow(null);
153
targetProductVersionRow[0] = "ProductVersion";
154
targetProductVersionRow[1] = propertyData[0].Substring(38);
155
141
- Row updatedProductCodeRow = updatedPropertyTable.CreateRow(null);
156
+ var updatedProductCodeRow = updatedPropertyTable.CreateRow(null);
157
updatedProductCodeRow[0] = "ProductCode";
158
updatedProductCodeRow[1] = propertyData[1].Substring(0, 38);
159
145
- Row updatedProductVersionRow = updatedPropertyTable.CreateRow(null);
160
+ var updatedProductVersionRow = updatedPropertyTable.CreateRow(null);
161
updatedProductVersionRow[0] = "ProductVersion";
162
updatedProductVersionRow[1] = propertyData[1].Substring(38);
163
@@ -153,7 +168,7 @@ namespace WixToolset.Core.WindowsInstaller.Bind
168
targetUpgradeCode = propertyData[2];
169
if (!String.IsNullOrEmpty(targetUpgradeCode))
170
{
156
- Row targetUpgradeCodeRow = targetPropertyTable.CreateRow(null);
171
+ var targetUpgradeCodeRow = targetPropertyTable.CreateRow(null);
172
targetUpgradeCodeRow[0] = "UpgradeCode";
173
targetUpgradeCodeRow[1] = targetUpgradeCode;
174
@@ -167,16 +182,16 @@ namespace WixToolset.Core.WindowsInstaller.Bind
182
183
if (!String.IsNullOrEmpty(updatedUpgradeCode))
184
{
170
- Row updatedUpgradeCodeRow = updatedPropertyTable.CreateRow(null);
185
+ var updatedUpgradeCodeRow = updatedPropertyTable.CreateRow(null);
186
updatedUpgradeCodeRow[0] = "UpgradeCode";
187
updatedUpgradeCodeRow[1] = updatedUpgradeCode;
188
}
189
}
175
- else if ((int)SummaryInformation.Transform.ValidationFlags == (int)row[0])
190
+ else if ((int)SummaryInformation.Transform.ValidationFlags == summaryId)
191
{
177
- transformFlags = Convert.ToInt32(row[1], CultureInfo.InvariantCulture);
192
+ transformFlags = Convert.ToInt32(summaryData, CultureInfo.InvariantCulture);
193
}
179
- else if ((int)SummaryInformation.Transform.Reserved11 == (int)row[0])
194
+ else if ((int)SummaryInformation.Transform.Reserved11 == summaryId)
195
{
196
// PID_LASTPRINTED should be null for transforms
197
row.Operation = RowOperation.None;
@@ -184,11 +199,11 @@ namespace WixToolset.Core.WindowsInstaller.Bind
199
else
200
{
201
// add everything else as is
187
- Row targetRow = targetSummaryInfo.CreateRow(null);
202
+ var targetRow = targetSummaryInfo.CreateRow(null);
203
targetRow[0] = row[0];
204
targetRow[1] = row[1];
205
191
- Row updatedRow = updatedSummaryInfo.CreateRow(null);
206
+ var updatedRow = updatedSummaryInfo.CreateRow(null);
207
updatedRow[0] = row[0];
208
updatedRow[1] = row[1];
209
}
@@ -205,7 +220,7 @@ namespace WixToolset.Core.WindowsInstaller.Bind
220
221
string emptyFile = null;
222
208
- foreach (Table table in this.Transform.Tables)
223
+ foreach (var table in this.Transform.Tables)
224
{
225
// Ignore unreal tables when building transforms except the _Stream table.
226
// These tables are ignored when generating the database so there is no reason
@@ -231,20 +246,21 @@ namespace WixToolset.Core.WindowsInstaller.Bind
246
}
247
248
// process row operations
234
- foreach (Row row in table.Rows)
249
+ foreach (var row in table.Rows)
250
{
251
switch (row.Operation)
252
{
253
case RowOperation.Add:
239
- Table updatedTable = updatedOutput.EnsureTable(table.Definition);
254
+ var updatedTable = updatedOutput.EnsureTable(table.Definition);
255
updatedTable.Rows.Add(row);
256
continue;
257
+
258
case RowOperation.Delete:
243
- Table targetTable = targetOutput.EnsureTable(table.Definition);
259
+ var targetTable = targetOutput.EnsureTable(table.Definition);
260
targetTable.Rows.Add(row);
261
262
// fill-in non-primary key values
247
- foreach (Field field in row.Fields)
263
+ foreach (var field in row.Fields)
264
{
265
if (!field.Column.PrimaryKey)
266
{
@@ -256,7 +272,7 @@ namespace WixToolset.Core.WindowsInstaller.Bind
272
{
273
if (null == emptyFile)
274
{
259
- emptyFile = Path.Combine(this.TempFilesLocation, "empty");
275
+ emptyFile = Path.Combine(this.IntermediateFolder, "empty");
276
}
277
278
field.Data = emptyFile;
@@ -273,7 +289,7 @@ namespace WixToolset.Core.WindowsInstaller.Bind
289
// Assure that the file table's sequence is populated
290
if ("File" == table.Name)
291
{
276
- foreach (Row fileRow in table.Rows)
292
+ foreach (var fileRow in table.Rows)
293
{
294
if (null == fileRow[7])
295
{
@@ -290,60 +306,48 @@ namespace WixToolset.Core.WindowsInstaller.Bind
306
}
307
308
// process modified and unmodified rows
293
- bool modifiedRow = false;
294
- Row targetRow = new Row(null, table.Definition);
295
- Row updatedRow = row;
296
- for (int i = 0; i < row.Fields.Length; i++)
309
+ var modifiedRow = false;
310
+ var targetRow = new Row(null, table.Definition);
311
+ var updatedRow = row;
312
+ for (var i = 0; i < row.Fields.Length; i++)
313
{
298
- Field updatedField = row.Fields[i];
314
+ var updatedField = row.Fields[i];
315
316
if (updatedField.Modified)
317
{
318
// set a different value in the target row to ensure this value will be modified during transform generation
319
if (ColumnType.Number == updatedField.Column.Type && !updatedField.Column.IsLocalizable)
320
{
305
- if (null == updatedField.Data || 1 != (int)updatedField.Data)
306
- {
307
- targetRow[i] = 1;
308
- }
309
- else
310
- {
311
- targetRow[i] = 2;
312
- }
321
+ var data = updatedField.AsNullableInteger();
322
+ targetRow[i] = (data == 1) ? 2 : 1;
323
}
324
else if (ColumnType.Object == updatedField.Column.Type)
325
{
326
if (null == emptyFile)
327
{
318
- emptyFile = Path.Combine(this.TempFilesLocation, "empty");
328
+ emptyFile = Path.Combine(this.IntermediateFolder, "empty");
329
}
330
331
targetRow[i] = emptyFile;
332
}
333
else
334
{
325
- if ("0" != (string)updatedField.Data)
326
- {
327
- targetRow[i] = "0";
328
- }
329
- else
330
- {
331
- targetRow[i] = "1";
332
- }
335
+ var data = updatedField.AsString();
336
+ targetRow[i] = (data == "0") ? "1" : "0";
337
}
338
339
modifiedRow = true;
340
}
341
else if (ColumnType.Object == updatedField.Column.Type)
342
{
339
- ObjectField objectField = (ObjectField)updatedField;
343
+ var objectField = (ObjectField)updatedField;
344
345
// create an empty file for comparing against
346
if (null == objectField.PreviousData)
347
{
348
if (null == emptyFile)
349
{
346
- emptyFile = Path.Combine(this.TempFilesLocation, "empty");
350
+ emptyFile = Path.Combine(this.IntermediateFolder, "empty");
351
}
352
353
targetRow[i] = emptyFile;
@@ -372,10 +376,10 @@ namespace WixToolset.Core.WindowsInstaller.Bind
376
"ProductVersion" == (string)row[0] ||
377
"UpgradeCode" == (string)row[0])))
378
{
375
- Table targetTable = targetOutput.EnsureTable(table.Definition);
379
+ var targetTable = targetOutput.EnsureTable(table.Definition);
380
targetTable.Rows.Add(targetRow);
381
378
- Table updatedTable = updatedOutput.EnsureTable(table.Definition);
382
+ var updatedTable = updatedOutput.EnsureTable(table.Definition);
383
updatedTable.Rows.Add(updatedRow);
384
}
385
}
@@ -392,38 +396,36 @@ namespace WixToolset.Core.WindowsInstaller.Bind
396
return;
397
}
398
395
- string transformFileName = Path.GetFileNameWithoutExtension(this.OutputPath);
396
- string targetDatabaseFile = Path.Combine(this.TempFilesLocation, String.Concat(transformFileName, "_target.msi"));
397
- string updatedDatabaseFile = Path.Combine(this.TempFilesLocation, String.Concat(transformFileName, "_updated.msi"));
399
+ var transformFileName = Path.GetFileNameWithoutExtension(this.OutputPath);
400
+ var targetDatabaseFile = Path.Combine(this.IntermediateFolder, String.Concat(transformFileName, "_target.msi"));
401
+ var updatedDatabaseFile = Path.Combine(this.IntermediateFolder, String.Concat(transformFileName, "_updated.msi"));
402
403
try
404
{
405
if (!String.IsNullOrEmpty(emptyFile))
406
{
403
- using (FileStream fileStream = File.Create(emptyFile))
407
+ using (var fileStream = File.Create(emptyFile))
408
{
409
}
410
}
411
408
- this.GenerateDatabase(targetOutput, targetDatabaseFile, false);
409
- this.GenerateDatabase(updatedOutput, updatedDatabaseFile, true);
412
+ this.GenerateDatabase(targetOutput, targetDatabaseFile, keepAddedColumns: false);
413
+ this.GenerateDatabase(updatedOutput, updatedDatabaseFile, keepAddedColumns: true);
414
415
// make sure the directory exists
416
Directory.CreateDirectory(Path.GetDirectoryName(this.OutputPath));
417
418
// create the transform file
415
- using (Database targetDatabase = new Database(targetDatabaseFile, OpenDatabase.ReadOnly))
419
+ using (var targetDatabase = new Database(targetDatabaseFile, OpenDatabase.ReadOnly))
420
+ using (var updatedDatabase = new Database(updatedDatabaseFile, OpenDatabase.ReadOnly))
421
{
417
- using (Database updatedDatabase = new Database(updatedDatabaseFile, OpenDatabase.ReadOnly))
422
+ if (updatedDatabase.GenerateTransform(targetDatabase, this.OutputPath))
423
{
419
- if (updatedDatabase.GenerateTransform(targetDatabase, this.OutputPath))
420
- {
421
- updatedDatabase.CreateTransformSummaryInfo(targetDatabase, this.OutputPath, (TransformErrorConditions)(transformFlags & 0xFFFF), (TransformValidations)((transformFlags >> 16) & 0xFFFF));
422
- }
423
- else
424
- {
425
- this.Messaging.Write(ErrorMessages.NoDifferencesInTransform(this.Transform.SourceLineNumbers));
426
- }
424
+ updatedDatabase.CreateTransformSummaryInfo(targetDatabase, this.OutputPath, (TransformErrorConditions)(transformFlags & 0xFFFF), (TransformValidations)((transformFlags >> 16) & 0xFFFF));
425
+ }
426
+ else
427
+ {
428
+ this.Messaging.Write(ErrorMessages.NoDifferencesInTransform(this.Transform.SourceLineNumbers));
429
}
430
}
431
}
@@ -458,16 +460,7 @@ namespace WixToolset.Core.WindowsInstaller.Bind
460
461
private void GenerateDatabase(WindowsInstallerData output, string outputPath, bool keepAddedColumns)
462
{
461
- var command = new GenerateDatabaseCommand();
462
- command.Codepage = output.Codepage;
463
- command.Extensions = this.Extensions;
464
- command.KeepAddedColumns = keepAddedColumns;
465
- command.Output = output;
466
- command.OutputPath = outputPath;
467
- command.TableDefinitions = this.TableDefinitions;
468
- command.IntermediateFolder = this.TempFilesLocation;
469
- command.SuppressAddingValidationRows = true;
470
- command.UseSubDirectory = true;
463
+ var command = new GenerateDatabaseCommand(this.Messaging, this.BackendHelper, this.Extensions, output, outputPath, this.TableDefinitions, this.IntermediateFolder, codepage: -1, keepAddedColumns, suppressAddingValidationRows: true, useSubdirectory: true );
464
command.Execute();
465
}
466
}
src/WixToolset.Core.WindowsInstaller/Bind/CabinetBuilder.cs
+3
-3
@@ -152,7 +152,7 @@ namespace WixToolset.Core.WindowsInstaller.Bind
152
153
foreach (FileFacade facade in cabinetWorkItem.FileFacades) // No other easy way than looping to get the only row
154
{
155
- if ((ulong)facade.File.FileSize >= maxPreCompressedSizeInBytes)
155
+ if ((ulong)facade.FileSize >= maxPreCompressedSizeInBytes)
156
{
157
// If file is larger than MaximumUncompressedFileSize set Maximum Cabinet Size for Cabinet Splitting
158
maxCabinetSize = this.MaximumCabinetSizeForLargeFileSplitting;
@@ -166,8 +166,8 @@ namespace WixToolset.Core.WindowsInstaller.Bind
166
167
var files = cabinetWorkItem.FileFacades
168
.Select(facade => facade.Hash == null ?
169
- new CabinetCompressFile(facade.File.Source.Path, facade.File.Id.Id) :
170
- new CabinetCompressFile(facade.File.Source.Path, facade.File.Id.Id, facade.Hash.HashPart1, facade.Hash.HashPart2, facade.Hash.HashPart3, facade.Hash.HashPart4))
169
+ new CabinetCompressFile(facade.SourcePath, facade.Id) :
170
+ new CabinetCompressFile(facade.SourcePath, facade.Id, facade.Hash.HashPart1, facade.Hash.HashPart2, facade.Hash.HashPart3, facade.Hash.HashPart4))
171
.ToList();
172
173
var cab = new Cabinet(cabinetPath);
src/WixToolset.Core.WindowsInstaller/Bind/CabinetResolver.cs
+2
-2
@@ -112,8 +112,8 @@ namespace WixToolset.Core.WindowsInstaller.Bind
112
private IBindFileWithPath CreateBindFileWithPath(FileFacade facade)
113
{
114
var result = this.ServiceProvider.GetService<IBindFileWithPath>();
115
- result.Id = facade.File.Id.Id;
116
- result.Path = facade.File.Source.Path;
115
+ result.Id = facade.Id;
116
+ result.Path = facade.SourcePath;
117
118
return result;
119
}
src/WixToolset.Core.WindowsInstaller/Bind/CopyTransformDataCommand.cs
+118
-104
@@ -1,12 +1,15 @@
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
+#if DELETE
4
+
5
namespace WixToolset.Core.WindowsInstaller.Bind
6
{
7
using System;
8
using System.Collections.Generic;
9
using System.Diagnostics;
10
+ using System.IO;
11
+ using System.Linq;
12
using WixToolset.Core.Bind;
9
- using WixToolset.Core.Native;
13
using WixToolset.Data;
14
using WixToolset.Data.Tuples;
15
using WixToolset.Data.WindowsInstaller;
@@ -16,15 +19,23 @@ namespace WixToolset.Core.WindowsInstaller.Bind
19
20
internal class CopyTransformDataCommand
21
{
19
- public bool CopyOutFileRows { private get; set; }
22
+ public CopyTransformDataCommand(IMessaging messaging, WindowsInstallerData output, TableDefinitionCollection tableDefinitions, bool copyOutFileRows)
23
+ {
24
+ this.Messaging = messaging;
25
+ this.Output = output;
26
+ this.TableDefinitions = tableDefinitions;
27
+ this.CopyOutFileRows = copyOutFileRows;
28
+ }
29
21
- public IEnumerable<IFileSystemExtension> Extensions { private get; set; }
30
+ private bool CopyOutFileRows { get; }
31
23
- public IMessaging Messaging { private get; set; }
32
+ public IEnumerable<IFileSystemExtension> Extensions { get; }
33
25
- public WindowsInstallerData Output { private get; set; }
34
+ private IMessaging Messaging { get; }
35
27
- public TableDefinitionCollection TableDefinitions { private get; set; }
36
+ private WindowsInstallerData Output { get; }
37
+
38
+ private TableDefinitionCollection TableDefinitions { get; }
39
40
public IEnumerable<FileFacade> FileFacades { get; private set; }
41
@@ -32,18 +43,17 @@ namespace WixToolset.Core.WindowsInstaller.Bind
43
{
44
Debug.Assert(OutputType.Patch != this.Output.Type);
45
35
- List<FileFacade> allFileRows = this.CopyOutFileRows ? new List<FileFacade>() : null;
46
+ var allFileRows = this.CopyOutFileRows ? new List<FileFacade>() : null;
47
37
-#if REVISIT_FOR_PATCHING // TODO: Fix this patching related code to work correctly with FileFacades.
38
- bool copyToPatch = (allFileRows != null);
39
- bool copyFromPatch = !copyToPatch;
48
+ var copyToPatch = (allFileRows != null);
49
+ var copyFromPatch = !copyToPatch;
50
41
- RowDictionary<MediaRow> patchMediaRows = new RowDictionary<MediaRow>();
51
+ var patchMediaRows = new RowDictionary<MediaRow>();
52
43
- Dictionary<int, RowDictionary<WixFileRow>> patchMediaFileRows = new Dictionary<int, RowDictionary<WixFileRow>>();
53
+ var patchMediaFileRows = new Dictionary<int, RowDictionary<WixFileRow>>();
54
45
- Table patchActualFileTable = this.Output.EnsureTable(this.TableDefinitions["File"]);
46
- Table patchFileTable = this.Output.EnsureTable(this.TableDefinitions["WixFile"]);
55
+ var patchActualFileTable = this.Output.EnsureTable(this.TableDefinitions["File"]);
56
+ var patchFileTable = this.Output.EnsureTable(this.TableDefinitions["WixFile"]);
57
58
if (copyFromPatch)
59
{
@@ -51,8 +61,7 @@ namespace WixToolset.Core.WindowsInstaller.Bind
61
foreach (WixFileRow patchFileRow in patchFileTable.Rows)
62
{
63
int diskId = patchFileRow.DiskId;
54
- RowDictionary<WixFileRow> mediaFileRows;
55
- if (!patchMediaFileRows.TryGetValue(diskId, out mediaFileRows))
64
+ if (!patchMediaFileRows.TryGetValue(diskId, out var mediaFileRows))
65
{
66
mediaFileRows = new RowDictionary<WixFileRow>();
67
patchMediaFileRows.Add(diskId, mediaFileRows);
@@ -61,24 +70,25 @@ namespace WixToolset.Core.WindowsInstaller.Bind
70
mediaFileRows.Add(patchFileRow);
71
}
72
64
- Table patchMediaTable = this.Output.EnsureTable(this.TableDefinitions["Media"]);
73
+ var patchMediaTable = this.Output.EnsureTable(this.TableDefinitions["Media"]);
74
patchMediaRows = new RowDictionary<MediaRow>(patchMediaTable);
75
}
76
68
- // index paired transforms
69
- Dictionary<string, Output> pairedTransforms = new Dictionary<string, Output>();
70
- foreach (SubStorage substorage in this.Output.SubStorages)
71
- {
72
- if (substorage.Name.StartsWith("#"))
73
- {
74
- pairedTransforms.Add(substorage.Name.Substring(1), substorage.Data);
75
- }
76
- }
77
+ // Index paired transforms by name without the "#" prefix.
78
+ var pairedTransforms = this.Output.SubStorages.Where(s => s.Name.StartsWith("#")).ToDictionary(s => s.Name.Substring(1), s => s.Data);
79
+ //Dictionary<string, Output> pairedTransforms = new Dictionary<string, Output>();
80
+ //foreach (SubStorage substorage in this.Output.SubStorages)
81
+ //{
82
+ // if (substorage.Name.StartsWith("#"))
83
+ // {
84
+ // pairedTransforms.Add(substorage.Name.Substring(1), substorage.Data);
85
+ // }
86
+ //}
87
88
try
89
{
80
- // copy File bind data into substorages
81
- foreach (SubStorage substorage in this.Output.SubStorages)
90
+ // Copy File bind data into substorages
91
+ foreach (var substorage in this.Output.SubStorages)
92
{
93
if (substorage.Name.StartsWith("#"))
94
{
@@ -86,25 +96,25 @@ namespace WixToolset.Core.WindowsInstaller.Bind
96
continue;
97
}
98
89
- Output mainTransform = substorage.Data;
90
- Table mainWixFileTable = mainTransform.Tables["WixFile"];
91
- Table mainMsiFileHashTable = mainTransform.Tables["MsiFileHash"];
99
+ var mainTransform = substorage.Data;
100
+ var mainWixFileTable = mainTransform.Tables["WixFile"];
101
+ var mainMsiFileHashTable = mainTransform.Tables["MsiFileHash"];
102
103
this.FileManagerCore.ActiveSubStorage = substorage;
104
95
- RowDictionary<WixFileRow> mainWixFiles = new RowDictionary<WixFileRow>(mainWixFileTable);
96
- RowDictionary<Row> mainMsiFileHashIndex = new RowDictionary<Row>();
105
+ var mainWixFiles = new RowDictionary<WixFileRow>(mainWixFileTable);
106
+ var mainMsiFileHashIndex = new RowDictionary<Row>();
107
98
- Table mainFileTable = mainTransform.Tables["File"];
99
- Output pairedTransform = (Output)pairedTransforms[substorage.Name];
108
+ var mainFileTable = mainTransform.Tables["File"];
109
+ var pairedTransform = pairedTransforms[substorage.Name];
110
111
// copy Media.LastSequence and index the MsiFileHash table if it exists.
112
if (copyFromPatch)
113
{
104
- Table pairedMediaTable = pairedTransform.Tables["Media"];
114
+ var pairedMediaTable = pairedTransform.Tables["Media"];
115
foreach (MediaRow pairedMediaRow in pairedMediaTable.Rows)
116
{
107
- MediaRow patchMediaRow = patchMediaRows.Get(pairedMediaRow.DiskId);
117
+ var patchMediaRow = patchMediaRows.Get(pairedMediaRow.DiskId);
118
pairedMediaRow.Fields[1] = patchMediaRow.Fields[1];
119
}
120
@@ -118,8 +128,8 @@ namespace WixToolset.Core.WindowsInstaller.Bind
128
}
129
130
// Index File table of pairedTransform
121
- Table pairedFileTable = pairedTransform.Tables["File"];
122
- RowDictionary<FileRow> pairedFileRows = new RowDictionary<FileRow>(pairedFileTable);
131
+ var pairedFileTable = pairedTransform.Tables["File"];
132
+ var pairedFileRows = new RowDictionary<FileRow>(pairedFileTable);
133
134
if (null != mainFileTable)
135
{
@@ -140,12 +150,12 @@ namespace WixToolset.Core.WindowsInstaller.Bind
150
continue;
151
}
152
143
- WixFileRow mainWixFileRow = mainWixFiles.Get(mainFileRow.File);
153
+ var mainWixFileRow = mainWixFiles.Get(mainFileRow.File);
154
155
if (copyToPatch) // when copying to the patch, we need compare the underlying files and include all file changes.
156
{
147
- ObjectField objectField = (ObjectField)mainWixFileRow.Fields[6];
148
- FileRow pairedFileRow = pairedFileRows.Get(mainFileRow.File);
157
+ var objectField = (ObjectField)mainWixFileRow.Fields[6];
158
+ var pairedFileRow = pairedFileRows.Get(mainFileRow.File);
159
160
// If the file is new, we always need to add it to the patch.
161
if (mainFileRow.Operation != RowOperation.Add)
@@ -169,8 +179,8 @@ namespace WixToolset.Core.WindowsInstaller.Bind
179
if (null != pairedFileRow)
180
{
181
// Always patch-added, but never non-compressed.
172
- pairedFileRow.Attributes |= MsiInterop.MsidbFileAttributesPatchAdded;
173
- pairedFileRow.Attributes &= ~MsiInterop.MsidbFileAttributesNoncompressed;
182
+ pairedFileRow.Attributes |= WindowsInstallerConstants.MsidbFileAttributesPatchAdded;
183
+ pairedFileRow.Attributes &= ~WindowsInstallerConstants.MsidbFileAttributesNoncompressed;
184
pairedFileRow.Fields[6].Modified = true;
185
pairedFileRow.Operation = RowOperation.Modify;
186
}
@@ -179,14 +189,14 @@ namespace WixToolset.Core.WindowsInstaller.Bind
189
{
190
// The File is same. We need mark all the attributes as unchanged.
191
mainFileRow.Operation = RowOperation.None;
182
- foreach (Field field in mainFileRow.Fields)
192
+ foreach (var field in mainFileRow.Fields)
193
{
194
field.Modified = false;
195
}
196
197
if (null != pairedFileRow)
198
{
189
- pairedFileRow.Attributes &= ~MsiInterop.MsidbFileAttributesPatchAdded;
199
+ pairedFileRow.Attributes &= ~WindowsInstallerConstants.MsidbFileAttributesPatchAdded;
200
pairedFileRow.Fields[6].Modified = false;
201
pairedFileRow.Operation = RowOperation.None;
202
}
@@ -197,8 +207,8 @@ namespace WixToolset.Core.WindowsInstaller.Bind
207
else if (null != pairedFileRow) // RowOperation.Add
208
{
209
// Always patch-added, but never non-compressed.
200
- pairedFileRow.Attributes |= MsiInterop.MsidbFileAttributesPatchAdded;
201
- pairedFileRow.Attributes &= ~MsiInterop.MsidbFileAttributesNoncompressed;
210
+ pairedFileRow.Attributes |= WindowsInstallerConstants.MsidbFileAttributesPatchAdded;
211
+ pairedFileRow.Attributes &= ~WindowsInstallerConstants.MsidbFileAttributesNoncompressed;
212
pairedFileRow.Fields[6].Modified = true;
213
pairedFileRow.Operation = RowOperation.Add;
214
}
@@ -207,20 +217,19 @@ namespace WixToolset.Core.WindowsInstaller.Bind
217
// index patch files by diskId+fileId
218
int diskId = mainWixFileRow.DiskId;
219
210
- RowDictionary<WixFileRow> mediaFileRows;
211
- if (!patchMediaFileRows.TryGetValue(diskId, out mediaFileRows))
220
+ if (!patchMediaFileRows.TryGetValue(diskId, out var mediaFileRows))
221
{
222
mediaFileRows = new RowDictionary<WixFileRow>();
223
patchMediaFileRows.Add(diskId, mediaFileRows);
224
}
225
217
- string fileId = mainFileRow.File;
218
- WixFileRow patchFileRow = mediaFileRows.Get(fileId);
226
+ var fileId = mainFileRow.File;
227
+ var patchFileRow = mediaFileRows.Get(fileId);
228
if (copyToPatch)
229
{
230
if (null == patchFileRow)
231
{
223
- FileRow patchActualFileRow = (FileRow)patchFileTable.CreateRow(mainFileRow.SourceLineNumbers);
232
+ var patchActualFileRow = (FileRow)patchFileTable.CreateRow(mainFileRow.SourceLineNumbers);
233
patchActualFileRow.CopyFrom(mainFileRow);
234
235
patchFileRow = (WixFileRow)patchFileTable.CreateRow(mainFileRow.SourceLineNumbers);
@@ -237,7 +246,7 @@ namespace WixToolset.Core.WindowsInstaller.Bind
246
// make sure Source is same. Otherwise we are silently ignoring a file.
247
if (0 != String.Compare(patchFileRow.Source, mainWixFileRow.Source, StringComparison.OrdinalIgnoreCase))
248
{
240
- Messaging.Instance.OnMessage(WixErrors.SameFileIdDifferentSource(mainFileRow.SourceLineNumbers, fileId, patchFileRow.Source, mainWixFileRow.Source));
249
+ this.Messaging.Write(ErrorMessages.SameFileIdDifferentSource(mainFileRow.SourceLineNumbers, fileId, patchFileRow.Source, mainWixFileRow.Source));
250
}
251
252
// capture the previous file versions (and associated data) from this targeted instance of the baseline into the current filerow.
@@ -249,11 +258,11 @@ namespace WixToolset.Core.WindowsInstaller.Bind
258
// copy data from the patch back to the transform
259
if (null != patchFileRow)
260
{
252
- FileRow pairedFileRow = (FileRow)pairedFileRows.Get(fileId);
253
- for (int i = 0; i < patchFileRow.Fields.Length; i++)
261
+ var pairedFileRow = pairedFileRows.Get(fileId);
262
+ for (var i = 0; i < patchFileRow.Fields.Length; i++)
263
{
255
- string patchValue = patchFileRow[i] == null ? "" : patchFileRow[i].ToString();
256
- string mainValue = mainFileRow[i] == null ? "" : mainFileRow[i].ToString();
264
+ var patchValue = patchFileRow[i] == null ? String.Empty : patchFileRow.FieldAsString(i);
265
+ var mainValue = mainFileRow[i] == null ? String.Empty : mainFileRow.FieldAsString(i);
266
267
if (1 == i)
268
{
@@ -298,17 +307,16 @@ namespace WixToolset.Core.WindowsInstaller.Bind
307
}
308
309
// copy MsiFileHash row for this File
301
- Row patchHashRow;
302
- if (!mainMsiFileHashIndex.TryGetValue(patchFileRow.File, out patchHashRow))
310
+ if (!mainMsiFileHashIndex.TryGetValue(patchFileRow.File, out var patchHashRow))
311
{
312
patchHashRow = patchFileRow.Hash;
313
}
314
315
if (null != patchHashRow)
316
{
309
- Table mainHashTable = mainTransform.EnsureTable(this.TableDefinitions["MsiFileHash"]);
310
- Row mainHashRow = mainHashTable.CreateRow(mainFileRow.SourceLineNumbers);
311
- for (int i = 0; i < patchHashRow.Fields.Length; i++)
317
+ var mainHashTable = mainTransform.EnsureTable(this.TableDefinitions["MsiFileHash"]);
318
+ var mainHashRow = mainHashTable.CreateRow(mainFileRow.SourceLineNumbers);
319
+ for (var i = 0; i < patchHashRow.Fields.Length; i++)
320
{
321
mainHashRow[i] = patchHashRow[i];
322
if (i > 1)
@@ -326,12 +334,12 @@ namespace WixToolset.Core.WindowsInstaller.Bind
334
List<Row> patchAssemblyNameRows = patchFileRow.AssemblyNames;
335
if (null != patchAssemblyNameRows)
336
{
329
- Table mainAssemblyNameTable = mainTransform.EnsureTable(this.TableDefinitions["MsiAssemblyName"]);
330
- foreach (Row patchAssemblyNameRow in patchAssemblyNameRows)
337
+ var mainAssemblyNameTable = mainTransform.EnsureTable(this.TableDefinitions["MsiAssemblyName"]);
338
+ foreach (var patchAssemblyNameRow in patchAssemblyNameRows)
339
{
340
// Copy if there isn't an identical modified/added row already in the transform.
333
- bool foundMatchingModifiedRow = false;
334
- foreach (Row mainAssemblyNameRow in mainAssemblyNameTable.Rows)
341
+ var foundMatchingModifiedRow = false;
342
+ foreach (var mainAssemblyNameRow in mainAssemblyNameTable.Rows)
343
{
344
if (RowOperation.None != mainAssemblyNameRow.Operation && mainAssemblyNameRow.GetPrimaryKey('/').Equals(patchAssemblyNameRow.GetPrimaryKey('/')))
345
{
@@ -342,8 +350,8 @@ namespace WixToolset.Core.WindowsInstaller.Bind
350
351
if (!foundMatchingModifiedRow)
352
{
345
- Row mainAssemblyNameRow = mainAssemblyNameTable.CreateRow(mainFileRow.SourceLineNumbers);
346
- for (int i = 0; i < patchAssemblyNameRow.Fields.Length; i++)
353
+ var mainAssemblyNameRow = mainAssemblyNameTable.CreateRow(mainFileRow.SourceLineNumbers);
354
+ for (var i = 0; i < patchAssemblyNameRow.Fields.Length; i++)
355
{
356
mainAssemblyNameRow[i] = patchAssemblyNameRow[i];
357
}
@@ -359,34 +367,36 @@ namespace WixToolset.Core.WindowsInstaller.Bind
367
if (null != patchFileRow.Patch)
368
{
369
// Add the PatchFiles action automatically to the AdminExecuteSequence and InstallExecuteSequence tables.
362
- AddPatchFilesActionToSequenceTable(SequenceTable.AdminExecuteSequence, mainTransform, pairedTransform, mainFileRow);
363
- AddPatchFilesActionToSequenceTable(SequenceTable.InstallExecuteSequence, mainTransform, pairedTransform, mainFileRow);
370
+ this.AddPatchFilesActionToSequenceTable(SequenceTable.AdminExecuteSequence, mainTransform, pairedTransform, mainFileRow);
371
+ this.AddPatchFilesActionToSequenceTable(SequenceTable.InstallExecuteSequence, mainTransform, pairedTransform, mainFileRow);
372
373
// Add to Patch table
366
- Table patchTable = pairedTransform.EnsureTable(this.TableDefinitions["Patch"]);
374
+ var patchTable = pairedTransform.EnsureTable(this.TableDefinitions["Patch"]);
375
if (0 == patchTable.Rows.Count)
376
{
377
patchTable.Operation = TableOperation.Add;
378
}
379
372
- Row patchRow = patchTable.CreateRow(mainFileRow.SourceLineNumbers);
380
+ var patchRow = patchTable.CreateRow(mainFileRow.SourceLineNumbers);
381
patchRow[0] = patchFileRow.File;
382
patchRow[1] = patchFileRow.Sequence;
383
376
- FileInfo patchFile = new FileInfo(patchFileRow.Source);
384
+ var patchFile = new FileInfo(patchFileRow.Source);
385
patchRow[2] = (int)patchFile.Length;
386
patchRow[3] = 0 == (PatchAttributeType.AllowIgnoreOnError & patchFileRow.PatchAttributes) ? 0 : 1;
387
380
- string streamName = patchTable.Name + "." + patchRow[0] + "." + patchRow[1];
381
- if (MsiInterop.MsiMaxStreamNameLength < streamName.Length)
388
+ var streamName = patchTable.Name + "." + patchRow[0] + "." + patchRow[1];
389
+ if (Msi.MsiInterop.MsiMaxStreamNameLength < streamName.Length)
390
{
391
streamName = "_" + Guid.NewGuid().ToString("D").ToUpperInvariant().Replace('-', '_');
384
- Table patchHeadersTable = pairedTransform.EnsureTable(this.TableDefinitions["MsiPatchHeaders"]);
392
+
393
+ var patchHeadersTable = pairedTransform.EnsureTable(this.TableDefinitions["MsiPatchHeaders"]);
394
if (0 == patchHeadersTable.Rows.Count)
395
{
396
patchHeadersTable.Operation = TableOperation.Add;
397
}
389
- Row patchHeadersRow = patchHeadersTable.CreateRow(mainFileRow.SourceLineNumbers);
398
+
399
+ var patchHeadersRow = patchHeadersTable.CreateRow(mainFileRow.SourceLineNumbers);
400
patchHeadersRow[0] = streamName;
401
patchHeadersRow[1] = patchFileRow.Patch;
402
patchRow[5] = streamName;
@@ -420,7 +430,7 @@ namespace WixToolset.Core.WindowsInstaller.Bind
430
{
431
this.FileManagerCore.ActiveSubStorage = null;
432
}
423
-#endif
433
+
434
this.FileFacades = allFileRows;
435
}
436
@@ -509,17 +519,19 @@ namespace WixToolset.Core.WindowsInstaller.Bind
519
foreach (var row in sequenceTable.Rows)
520
{
521
var actionName = row.FieldAsString(0);
512
- if (String.Equals("PatchFiles", actionName, StringComparison.Ordinal))
513
- {
514
- hasPatchFilesAction = true;
515
- }
516
- else if (String.Equals("InstallFiles", actionName, StringComparison.Ordinal))
522
+ switch (actionName)
523
{
518
- installFilesSequence = row.FieldAsInteger(2);
519
- }
520
- else if (String.Equals("DuplicateFiles", actionName, StringComparison.Ordinal))
521
- {
522
- duplicateFilesSequence = row.FieldAsInteger(2);
524
+ case "PatchFiles":
525
+ hasPatchFilesAction = true;
526
+ break;
527
+
528
+ case "InstallFiles":
529
+ installFilesSequence = row.FieldAsInteger(2);
530
+ break;
531
+
532
+ case "DuplicateFiles":
533
+ duplicateFilesSequence = row.FieldAsInteger(2);
534
+ break;
535
}
536
}
537
}
@@ -531,8 +543,8 @@ namespace WixToolset.Core.WindowsInstaller.Bind
543
/// <param name="output">The output to validate.</param>
544
private void ValidateFileRowChanges(WindowsInstallerData transform)
545
{
534
- Table componentTable = transform.Tables["Component"];
535
- Table fileTable = transform.Tables["File"];
546
+ var componentTable = transform.Tables["Component"];
547
+ var fileTable = transform.Tables["File"];
548
549
// There's no sense validating keypaths if the transform has no component or file table
550
if (componentTable == null || fileTable == null)
@@ -540,31 +552,31 @@ namespace WixToolset.Core.WindowsInstaller.Bind
552
return;
553
}
554
543
- Dictionary<string, string> componentKeyPath = new Dictionary<string, string>(componentTable.Rows.Count);
555
+ var componentKeyPath = new Dictionary<string, string>(componentTable.Rows.Count);
556
557
// Index the Component table for non-directory & non-registry key paths.
546
- foreach (Row row in componentTable.Rows)
558
+ foreach (var row in componentTable.Rows)
559
{
548
- if (null != row.Fields[5].Data &&
549
- 0 != ((int)row.Fields[3].Data & WindowsInstallerConstants.MsidbComponentAttributesRegistryKeyPath))
560
+ var keyPath = row.FieldAsString(5);
561
+ if (keyPath != null && 0 != (row.FieldAsInteger(3) & WindowsInstallerConstants.MsidbComponentAttributesRegistryKeyPath))
562
{
551
- componentKeyPath.Add(row.Fields[0].Data.ToString(), row.Fields[5].Data.ToString());
563
+ componentKeyPath.Add(row.FieldAsString(0), keyPath);
564
}
565
}
566
555
- Dictionary<string, string> componentWithChangedKeyPath = new Dictionary<string, string>();
556
- Dictionary<string, string> componentWithNonKeyPathChanged = new Dictionary<string, string>();
567
+ var componentWithChangedKeyPath = new Dictionary<string, string>();
568
+ var componentWithNonKeyPathChanged = new Dictionary<string, string>();
569
// Verify changes in the file table, now that file diffing has occurred
570
foreach (FileRow row in fileTable.Rows)
571
{
560
- string fileId = row.Fields[0].Data.ToString();
561
- string componentId = row.Fields[1].Data.ToString();
562
-
572
if (RowOperation.Modify != row.Operation)
573
{
574
continue;
575
}
576
577
+ var fileId = row.FieldAsString(0);
578
+ var componentId = row.FieldAsString(1);
579
+
580
// If this file is the keypath of a component
581
if (componentKeyPath.ContainsValue(fileId))
582
{
@@ -582,12 +594,12 @@ namespace WixToolset.Core.WindowsInstaller.Bind
594
}
595
}
596
585
- foreach (KeyValuePair<string, string> componentFile in componentWithNonKeyPathChanged)
597
+ foreach (var componentFile in componentWithNonKeyPathChanged)
598
{
599
// Make sure all changes to non keypath files also had a change in the keypath.
588
- if (!componentWithChangedKeyPath.ContainsKey(componentFile.Key) && componentKeyPath.ContainsKey(componentFile.Key))
600
+ if (!componentWithChangedKeyPath.ContainsKey(componentFile.Key) && componentKeyPath.TryGetValue(componentFile.Key, out var keyPath))
601
{
590
- this.Messaging.Write(WarningMessages.UpdateOfNonKeyPathFile((string)componentFile.Value, (string)componentFile.Key, (string)componentKeyPath[componentFile.Key]));
602
+ this.Messaging.Write(WarningMessages.UpdateOfNonKeyPathFile(componentFile.Value, componentFile.Key, keyPath));
603
}
604
}
605
}
@@ -614,3 +626,5 @@ namespace WixToolset.Core.WindowsInstaller.Bind
626
}
627
}
628
}
629
+
630
+#endif
src/WixToolset.Core.WindowsInstaller/Bind/CreateDeltaPatchesCommand.cs
+1
-1
@@ -33,7 +33,7 @@ namespace WixToolset.Core.WindowsInstaller.Bind
33
var optimizePatchSizeForLargeFiles = this.WixPatchId?.OptimizePatchSizeForLargeFiles ?? false;
34
var apiPatchingSymbolFlags = (PatchSymbolFlagsType)(this.WixPatchId?.ApiPatchingSymbolFlags ?? 0);
35
36
-#if REVISIT_FOR_PATCHING
36
+#if TODO_PATCHING
37
foreach (FileFacade facade in this.FileFacades)
38
{
39
if (RowOperation.Modify == facade.File.Operation &&
src/WixToolset.Core.WindowsInstaller/Bind/CreateIdtFileCommand.cs
+13
-3
@@ -1,4 +1,4 @@
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.
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
{
@@ -122,13 +122,18 @@ namespace WixToolset.Core.WindowsInstaller.Bind
122
tableString.Append(definition.Name);
123
foreach (var column in definition.Columns)
124
{
125
- // conditionally keep columns added in a transform; otherwise,
126
- // break because columns can only be added at the end
125
+ // Conditionally keep columns added in a transform; otherwise,
126
+ // break because columns can only be added at the end.
127
if (column.Added && !keepAddedColumns)
128
{
129
break;
130
}
131
132
+ if (column.Unreal)
133
+ {
134
+ continue;
135
+ }
136
+
137
if (!first)
138
{
139
columnString.Append('\t');
@@ -168,6 +173,11 @@ namespace WixToolset.Core.WindowsInstaller.Bind
173
break;
174
}
175
176
+ if (field.Column.Unreal)
177
+ {
178
+ continue;
179
+ }
180
+
181
if (first)
182
{
183
first = false;
src/WixToolset.Core.WindowsInstaller/Bind/CreateOutputFromIRCommand.cs
+166
-187
@@ -40,193 +40,193 @@ namespace WixToolset.Core.WindowsInstaller.Bind
40
41
public void Execute()
42
{
43
- var output = new WindowsInstallerData(this.Section.Tuples.First().SourceLineNumbers);
44
- output.Codepage = this.Section.Codepage;
45
- output.Type = SectionTypeToOutputType(this.Section.Type);
46
-
47
- this.AddSectionToOutput(this.Section, output);
43
+ this.Output = new WindowsInstallerData(this.Section.Tuples.First().SourceLineNumbers)
44
+ {
45
+ Codepage = this.Section.Codepage,
46
+ Type = SectionTypeToOutputType(this.Section.Type)
47
+ };
48
49
- this.Output = output;
49
+ this.AddSectionToOutput();
50
}
51
52
- private void AddSectionToOutput(IntermediateSection section, WindowsInstallerData output)
52
+ private void AddSectionToOutput()
53
{
54
- foreach (var tuple in section.Tuples)
54
+ foreach (var tuple in this.Section.Tuples)
55
{
56
switch (tuple.Definition.Type)
57
{
58
case TupleDefinitionType.AppSearch:
59
- this.AddTupleDefaultly(tuple, output);
60
- output.EnsureTable(this.TableDefinitions["Signature"]);
59
+ this.AddTupleDefaultly(tuple);
60
+ this.Output.EnsureTable(this.TableDefinitions["Signature"]);
61
break;
62
63
case TupleDefinitionType.Assembly:
64
- this.AddAssemblyTuple((AssemblyTuple)tuple, output);
64
+ this.AddAssemblyTuple((AssemblyTuple)tuple);
65
break;
66
67
case TupleDefinitionType.Binary:
68
- this.AddTupleDefaultly(tuple, output, idIsPrimaryKey: true);
68
+ this.AddTupleDefaultly(tuple, idIsPrimaryKey: true);
69
break;
70
71
case TupleDefinitionType.BBControl:
72
- this.AddBBControlTuple((BBControlTuple)tuple, output);
72
+ this.AddBBControlTuple((BBControlTuple)tuple);
73
break;
74
75
case TupleDefinitionType.Class:
76
- this.AddClassTuple((ClassTuple)tuple, output);
76
+ this.AddClassTuple((ClassTuple)tuple);
77
break;
78
79
case TupleDefinitionType.Control:
80
- this.AddControlTuple((ControlTuple)tuple, output);
80
+ this.AddControlTuple((ControlTuple)tuple);
81
break;
82
83
case TupleDefinitionType.Component:
84
- this.AddComponentTuple((ComponentTuple)tuple, output);
84
+ this.AddComponentTuple((ComponentTuple)tuple);
85
break;
86
87
case TupleDefinitionType.CustomAction:
88
- this.AddCustomActionTuple((CustomActionTuple)tuple, output);
88
+ this.AddCustomActionTuple((CustomActionTuple)tuple);
89
break;
90
91
case TupleDefinitionType.Dialog:
92
- this.AddDialogTuple((DialogTuple)tuple, output);
92
+ this.AddDialogTuple((DialogTuple)tuple);
93
break;
94
95
case TupleDefinitionType.Directory:
96
- this.AddDirectoryTuple((DirectoryTuple)tuple, output);
96
+ this.AddDirectoryTuple((DirectoryTuple)tuple);
97
break;
98
99
case TupleDefinitionType.Environment:
100
- this.AddEnvironmentTuple((EnvironmentTuple)tuple, output);
100
+ this.AddEnvironmentTuple((EnvironmentTuple)tuple);
101
break;
102
103
case TupleDefinitionType.Error:
104
- this.AddErrorTuple((ErrorTuple)tuple, output);
104
+ this.AddErrorTuple((ErrorTuple)tuple);
105
break;
106
107
case TupleDefinitionType.Feature:
108
- this.AddFeatureTuple((FeatureTuple)tuple, output);
108
+ this.AddFeatureTuple((FeatureTuple)tuple);
109
break;
110
111
case TupleDefinitionType.File:
112
- this.AddFileTuple((FileTuple)tuple, output);
112
+ this.AddFileTuple((FileTuple)tuple);
113
break;
114
115
case TupleDefinitionType.Icon:
116
- this.AddTupleDefaultly(tuple, output, idIsPrimaryKey: true);
116
+ this.AddTupleDefaultly(tuple, idIsPrimaryKey: true);
117
break;
118
119
case TupleDefinitionType.IniFile:
120
- this.AddIniFileTuple((IniFileTuple)tuple, output);
120
+ this.AddIniFileTuple((IniFileTuple)tuple);
121
break;
122
123
case TupleDefinitionType.Media:
124
- this.AddMediaTuple((MediaTuple)tuple, output);
124
+ this.AddMediaTuple((MediaTuple)tuple);
125
break;
126
127
case TupleDefinitionType.ModuleConfiguration:
128
- this.AddModuleConfigurationTuple((ModuleConfigurationTuple)tuple, output);
128
+ this.AddModuleConfigurationTuple((ModuleConfigurationTuple)tuple);
129
break;
130
131
case TupleDefinitionType.MsiEmbeddedUI:
132
- this.AddMsiEmbeddedUITuple((MsiEmbeddedUITuple)tuple, output);
132
+ this.AddMsiEmbeddedUITuple((MsiEmbeddedUITuple)tuple);
133
break;
134
135
case TupleDefinitionType.MsiFileHash:
136
- this.AddMsiFileHashTuple((MsiFileHashTuple)tuple, output);
136
+ this.AddMsiFileHashTuple((MsiFileHashTuple)tuple);
137
break;
138
139
case TupleDefinitionType.MsiServiceConfig:
140
- this.AddMsiServiceConfigTuple((MsiServiceConfigTuple)tuple, output);
140
+ this.AddMsiServiceConfigTuple((MsiServiceConfigTuple)tuple);
141
break;
142
143
case TupleDefinitionType.MsiServiceConfigFailureActions:
144
- this.AddMsiServiceConfigFailureActionsTuple((MsiServiceConfigFailureActionsTuple)tuple, output);
144
+ this.AddMsiServiceConfigFailureActionsTuple((MsiServiceConfigFailureActionsTuple)tuple);
145
break;
146
147
case TupleDefinitionType.MsiShortcutProperty:
148
- this.AddTupleDefaultly(tuple, output, idIsPrimaryKey: true);
148
+ this.AddTupleDefaultly(tuple, idIsPrimaryKey: true);
149
break;
150
151
case TupleDefinitionType.MoveFile:
152
- this.AddMoveFileTuple((MoveFileTuple)tuple, output);
152
+ this.AddMoveFileTuple((MoveFileTuple)tuple);
153
break;
154
155
case TupleDefinitionType.ProgId:
156
- this.AddTupleDefaultly(tuple, output);
157
- output.EnsureTable(this.TableDefinitions["Extension"]);
156
+ this.AddTupleDefaultly(tuple);
157
+ this.Output.EnsureTable(this.TableDefinitions["Extension"]);
158
break;
159
160
case TupleDefinitionType.Property:
161
- this.AddPropertyTuple((PropertyTuple)tuple, output);
161
+ this.AddPropertyTuple((PropertyTuple)tuple);
162
break;
163
164
case TupleDefinitionType.RemoveFile:
165
- this.AddRemoveFileTuple((RemoveFileTuple)tuple, output);
165
+ this.AddRemoveFileTuple((RemoveFileTuple)tuple);
166
break;
167
168
case TupleDefinitionType.Registry:
169
- this.AddRegistryTuple((RegistryTuple)tuple, output);
169
+ this.AddRegistryTuple((RegistryTuple)tuple);
170
break;
171
172
case TupleDefinitionType.RegLocator:
173
- this.AddRegLocatorTuple((RegLocatorTuple)tuple, output);
173
+ this.AddRegLocatorTuple((RegLocatorTuple)tuple);
174
break;
175
176
case TupleDefinitionType.RemoveRegistry:
177
- this.AddRemoveRegistryTuple((RemoveRegistryTuple)tuple, output);
177
+ this.AddRemoveRegistryTuple((RemoveRegistryTuple)tuple);
178
break;
179
180
case TupleDefinitionType.ReserveCost:
181
- this.AddTupleDefaultly(tuple, output, idIsPrimaryKey: true);
181
+ this.AddTupleDefaultly(tuple, idIsPrimaryKey: true);
182
break;
183
184
case TupleDefinitionType.ServiceControl:
185
- this.AddServiceControlTuple((ServiceControlTuple)tuple, output);
185
+ this.AddServiceControlTuple((ServiceControlTuple)tuple);
186
break;
187
188
case TupleDefinitionType.ServiceInstall:
189
- this.AddServiceInstallTuple((ServiceInstallTuple)tuple, output);
189
+ this.AddServiceInstallTuple((ServiceInstallTuple)tuple);
190
break;
191
192
case TupleDefinitionType.Shortcut:
193
- this.AddShortcutTuple((ShortcutTuple)tuple, output);
193
+ this.AddShortcutTuple((ShortcutTuple)tuple);
194
break;
195
196
case TupleDefinitionType.Signature:
197
- this.AddTupleDefaultly(tuple, output, idIsPrimaryKey: true);
197
+ this.AddTupleDefaultly(tuple, idIsPrimaryKey: true);
198
break;
199
200
case TupleDefinitionType.SummaryInformation:
201
- this.AddTupleDefaultly(tuple, output, tableName: "_SummaryInformation");
201
+ this.AddTupleDefaultly(tuple, tableName: "_SummaryInformation");
202
break;
203
204
case TupleDefinitionType.TextStyle:
205
- this.AddTextStyleTuple((TextStyleTuple)tuple, output);
205
+ this.AddTextStyleTuple((TextStyleTuple)tuple);
206
break;
207
208
case TupleDefinitionType.Upgrade:
209
- this.AddUpgradeTuple((UpgradeTuple)tuple, output);
209
+ this.AddUpgradeTuple((UpgradeTuple)tuple);
210
break;
211
212
case TupleDefinitionType.WixAction:
213
- this.AddWixActionTuple((WixActionTuple)tuple, output);
213
+ this.AddWixActionTuple((WixActionTuple)tuple);
214
break;
215
216
case TupleDefinitionType.WixMediaTemplate:
217
- this.AddWixMediaTemplateTuple((WixMediaTemplateTuple)tuple, output);
217
+ this.AddWixMediaTemplateTuple((WixMediaTemplateTuple)tuple);
218
break;
219
220
case TupleDefinitionType.MustBeFromAnExtension:
221
- this.AddTupleFromExtension(tuple, output);
221
+ this.AddTupleFromExtension(tuple);
222
break;
223
224
case TupleDefinitionType.WixCustomRow:
225
- this.AddWixCustomRowTuple((WixCustomRowTuple)tuple, output);
225
+ this.AddWixCustomRowTuple((WixCustomRowTuple)tuple);
226
break;
227
228
case TupleDefinitionType.WixEnsureTable:
229
- this.AddWixEnsureTableTuple((WixEnsureTableTuple)tuple, output);
229
+ this.AddWixEnsureTableTuple((WixEnsureTableTuple)tuple);
230
break;
231
232
// ignored.
@@ -234,25 +234,25 @@ namespace WixToolset.Core.WindowsInstaller.Bind
234
case TupleDefinitionType.WixComponentGroup:
235
case TupleDefinitionType.WixDeltaPatchFile:
236
case TupleDefinitionType.WixFeatureGroup:
237
- break;
237
+ case TupleDefinitionType.WixPatchBaseline:
238
+ break;
239
240
// Already processed.
241
case TupleDefinitionType.WixCustomTable:
242
break;
243
244
default:
244
- this.AddTupleDefaultly(tuple, output);
245
+ this.AddTupleDefaultly(tuple);
246
break;
247
}
248
}
249
}
250
250
- private void AddAssemblyTuple(AssemblyTuple tuple, WindowsInstallerData output)
251
+ private void AddAssemblyTuple(AssemblyTuple tuple)
252
{
253
var attributes = tuple.Type == AssemblyType.Win32Assembly ? 1 : (int?)null;
254
254
- var table = output.EnsureTable(this.TableDefinitions["MsiAssembly"]);
255
- var row = table.CreateRow(tuple.SourceLineNumbers);
255
+ var row = this.CreateRow(tuple, "MsiAssembly");
256
row[0] = tuple.ComponentRef;
257
row[1] = tuple.FeatureRef;
258
row[2] = tuple.ManifestFileRef;
@@ -260,7 +260,7 @@ namespace WixToolset.Core.WindowsInstaller.Bind
260
row[4] = attributes;
261
}
262
263
- private void AddBBControlTuple(BBControlTuple tuple, WindowsInstallerData output)
263
+ private void AddBBControlTuple(BBControlTuple tuple)
264
{
265
var attributes = tuple.Attributes;
266
attributes |= tuple.Enabled ? WindowsInstallerConstants.MsidbControlAttributesEnabled : 0;
@@ -272,8 +272,7 @@ namespace WixToolset.Core.WindowsInstaller.Bind
272
attributes |= tuple.Sunken ? WindowsInstallerConstants.MsidbControlAttributesSunken : 0;
273
attributes |= tuple.Visible ? WindowsInstallerConstants.MsidbControlAttributesVisible : 0;
274
275
- var table = output.EnsureTable(this.TableDefinitions["BBControl"]);
276
- var row = table.CreateRow(tuple.SourceLineNumbers);
275
+ var row = this.CreateRow(tuple, "BBControl");
276
row[0] = tuple.BillboardRef;
277
row[1] = tuple.BBControl;
278
row[2] = tuple.Type;
@@ -285,10 +284,9 @@ namespace WixToolset.Core.WindowsInstaller.Bind
284
row[8] = tuple.Text;
285
}
286
288
- private void AddClassTuple(ClassTuple tuple, WindowsInstallerData output)
287
+ private void AddClassTuple(ClassTuple tuple)
288
{
290
- var table = output.EnsureTable(this.TableDefinitions["Class"]);
291
- var row = table.CreateRow(tuple.SourceLineNumbers);
289
+ var row = this.CreateRow(tuple, "Class");
290
row[0] = tuple.CLSID;
291
row[1] = tuple.Context;
292
row[2] = tuple.ComponentRef;
@@ -304,7 +302,7 @@ namespace WixToolset.Core.WindowsInstaller.Bind
302
row[12] = tuple.RelativePath ? (int?)1 : null;
303
}
304
307
- private void AddControlTuple(ControlTuple tuple, WindowsInstallerData output)
305
+ private void AddControlTuple(ControlTuple tuple)
306
{
307
var text = tuple.Text;
308
var attributes = tuple.Attributes;
@@ -329,8 +327,7 @@ namespace WixToolset.Core.WindowsInstaller.Bind
327
text = String.Concat(text, " ");
328
}
329
332
- var table = output.EnsureTable(this.TableDefinitions["Control"]);
333
- var row = table.CreateRow(tuple.SourceLineNumbers);
330
+ var row = this.CreateRow(tuple, "Control");
331
row[0] = tuple.DialogRef;
332
row[1] = tuple.Control;
333
row[2] = tuple.Type;
@@ -344,7 +341,7 @@ namespace WixToolset.Core.WindowsInstaller.Bind
341
row[10] = tuple.Help;
342
}
343
347
- private void AddComponentTuple(ComponentTuple tuple, WindowsInstallerData output)
344
+ private void AddComponentTuple(ComponentTuple tuple)
345
{
346
var attributes = ComponentLocation.Either == tuple.Location ? WindowsInstallerConstants.MsidbComponentAttributesOptional : 0;
347
attributes |= ComponentLocation.SourceOnly == tuple.Location ? WindowsInstallerConstants.MsidbComponentAttributesSourceOnly : 0;
@@ -359,8 +356,7 @@ namespace WixToolset.Core.WindowsInstaller.Bind
356
attributes |= tuple.UninstallWhenSuperseded ? WindowsInstallerConstants.MsidbComponentAttributesUninstallOnSupersedence : 0;
357
attributes |= tuple.Win64 ? WindowsInstallerConstants.MsidbComponentAttributes64bit : 0;
358
362
- var table = output.EnsureTable(this.TableDefinitions["Component"]);
363
- var row = table.CreateRow(tuple.SourceLineNumbers);
359
+ var row = this.CreateRow(tuple, "Component");
360
row[0] = tuple.Id.Id;
361
row[1] = tuple.ComponentId;
362
row[2] = tuple.DirectoryRef;
@@ -369,7 +365,7 @@ namespace WixToolset.Core.WindowsInstaller.Bind
365
row[5] = tuple.KeyPath;
366
}
367
372
- private void AddCustomActionTuple(CustomActionTuple tuple, WindowsInstallerData output)
368
+ private void AddCustomActionTuple(CustomActionTuple tuple)
369
{
370
var type = tuple.Win64 ? WindowsInstallerConstants.MsidbCustomActionType64BitScript : 0;
371
type |= tuple.IgnoreResult ? WindowsInstallerConstants.MsidbCustomActionTypeContinue : 0;
@@ -396,8 +392,7 @@ namespace WixToolset.Core.WindowsInstaller.Bind
392
type |= tuple.TSAware ? WindowsInstallerConstants.MsidbCustomActionTypeTSAware : 0;
393
}
394
399
- var table = output.EnsureTable(this.TableDefinitions["CustomAction"]);
400
- var row = table.CreateRow(tuple.SourceLineNumbers);
395
+ var row = this.CreateRow(tuple, "CustomAction");
396
row[0] = tuple.Id.Id;
397
row[1] = type;
398
row[2] = tuple.Source;
@@ -405,7 +400,7 @@ namespace WixToolset.Core.WindowsInstaller.Bind
400
row[4] = tuple.PatchUninstall ? (int?)WindowsInstallerConstants.MsidbCustomActionTypePatchUninstall : null;
401
}
402
408
- private void AddDialogTuple(DialogTuple tuple, WindowsInstallerData output)
403
+ private void AddDialogTuple(DialogTuple tuple)
404
{
405
var attributes = tuple.Visible ? WindowsInstallerConstants.MsidbDialogAttributesVisible : 0;
406
attributes|= tuple.Modal ? WindowsInstallerConstants.MsidbDialogAttributesModal : 0;
@@ -419,8 +414,7 @@ namespace WixToolset.Core.WindowsInstaller.Bind
414
attributes|= tuple.SystemModal ? WindowsInstallerConstants.MsidbDialogAttributesSysModal : 0;
415
attributes|= tuple.TrackDiskSpace ? WindowsInstallerConstants.MsidbDialogAttributesTrackDiskSpace : 0;
416
422
- var table = output.EnsureTable(this.TableDefinitions["Dialog"]);
423
- var row = table.CreateRow(tuple.SourceLineNumbers);
417
+ var row = this.CreateRow(tuple, "Dialog");
418
row[0] = tuple.Id.Id;
419
row[1] = tuple.HCentering;
420
row[2] = tuple.VCentering;
@@ -432,10 +426,10 @@ namespace WixToolset.Core.WindowsInstaller.Bind
426
row[8] = tuple.DefaultControlRef;
427
row[9] = tuple.CancelControlRef;
428
435
- output.EnsureTable(this.TableDefinitions["ListBox"]);
429
+ this.Output.EnsureTable(this.TableDefinitions["ListBox"]);
430
}
431
438
- private void AddDirectoryTuple(DirectoryTuple tuple, WindowsInstallerData output)
432
+ private void AddDirectoryTuple(DirectoryTuple tuple)
433
{
434
var sourceName = GetMsiFilenameValue(tuple.SourceShortName, tuple.SourceName);
435
var targetName = GetMsiFilenameValue(tuple.ShortName, tuple.Name);
@@ -447,14 +441,13 @@ namespace WixToolset.Core.WindowsInstaller.Bind
441
442
var defaultDir = String.IsNullOrEmpty(sourceName) ? targetName : targetName + ":" + sourceName ;
443
450
- var table = output.EnsureTable(this.TableDefinitions["Directory"]);
451
- var row = table.CreateRow(tuple.SourceLineNumbers);
444
+ var row = this.CreateRow(tuple, "Directory");
445
row[0] = tuple.Id.Id;
446
row[1] = tuple.ParentDirectoryRef;
447
row[2] = defaultDir;
448
}
449
457
- private void AddEnvironmentTuple(EnvironmentTuple tuple, WindowsInstallerData output)
450
+ private void AddEnvironmentTuple(EnvironmentTuple tuple)
451
{
452
var action = String.Empty;
453
var system = tuple.System ? "*" : String.Empty;
@@ -484,23 +477,21 @@ namespace WixToolset.Core.WindowsInstaller.Bind
477
break;
478
}
479
487
- var table = output.EnsureTable(this.TableDefinitions["Environment"]);
488
- var row = table.CreateRow(tuple.SourceLineNumbers);
480
+ var row = this.CreateRow(tuple, "Environment");
481
row[0] = tuple.Id.Id;
482
row[1] = String.Concat(action, uninstall, system, tuple.Name);
483
row[2] = value;
484
row[3] = tuple.ComponentRef;
485
}
486
495
- private void AddErrorTuple(ErrorTuple tuple, WindowsInstallerData output)
487
+ private void AddErrorTuple(ErrorTuple tuple)
488
{
497
- var table = output.EnsureTable(this.TableDefinitions["Error"]);
498
- var row = table.CreateRow(tuple.SourceLineNumbers);
489
+ var row = this.CreateRow(tuple, "Error");
490
row[0] = Convert.ToInt32(tuple.Id.Id);
491
row[1] = tuple.Message;
492
}
493
503
- private void AddFeatureTuple(FeatureTuple tuple, WindowsInstallerData output)
494
+ private void AddFeatureTuple(FeatureTuple tuple)
495
{
496
var attributes = tuple.DisallowAbsent ? WindowsInstallerConstants.MsidbFeatureAttributesUIDisallowAbsent : 0;
497
attributes |= tuple.DisallowAdvertise ? WindowsInstallerConstants.MsidbFeatureAttributesDisallowAdvertise : 0;
@@ -508,8 +499,7 @@ namespace WixToolset.Core.WindowsInstaller.Bind
499
attributes |= FeatureInstallDefault.Source == tuple.InstallDefault ? WindowsInstallerConstants.MsidbFeatureAttributesFavorSource : 0;
500
attributes |= FeatureTypicalDefault.Advertise == tuple.TypicalDefault ? WindowsInstallerConstants.MsidbFeatureAttributesFavorAdvertise : 0;
501
511
- var table = output.EnsureTable(this.TableDefinitions["Feature"]);
512
- var row = table.CreateRow(tuple.SourceLineNumbers);
502
+ var row = this.CreateRow(tuple, "Feature");
503
row[0] = tuple.Id.Id;
504
row[1] = tuple.ParentFeatureRef;
505
row[2] = tuple.Title;
@@ -520,16 +510,17 @@ namespace WixToolset.Core.WindowsInstaller.Bind
510
row[7] = attributes;
511
}
512
523
- private void AddFileTuple(FileTuple tuple, WindowsInstallerData output)
513
+ private void AddFileTuple(FileTuple tuple)
514
{
525
- var table = output.EnsureTable(this.TableDefinitions["File"]);
526
- var row = (FileRow)table.CreateRow(tuple.SourceLineNumbers);
515
+ var row = (FileRow)this.CreateRow(tuple, "File");
516
row.File = tuple.Id.Id;
517
row.Component = tuple.ComponentRef;
518
row.FileName = GetMsiFilenameValue(tuple.ShortName, tuple.Name);
519
row.FileSize = tuple.FileSize;
520
row.Version = tuple.Version;
521
row.Language = tuple.Language;
522
+ row.DiskId = tuple.DiskId ?? 1; // TODO: is 0 the correct thing to default here
523
+ row.Source = tuple.Source.Path;
524
525
var attributes = (tuple.Attributes & FileTupleAttributes.Checksum) == FileTupleAttributes.Checksum ? WindowsInstallerConstants.MsidbFileAttributesChecksum : 0;
526
attributes |= (tuple.Attributes & FileTupleAttributes.Compressed) == FileTupleAttributes.Compressed ? WindowsInstallerConstants.MsidbFileAttributesCompressed : 0;
@@ -542,19 +533,17 @@ namespace WixToolset.Core.WindowsInstaller.Bind
533
534
if (!String.IsNullOrEmpty(tuple.FontTitle))
535
{
545
- var fontTable = output.EnsureTable(this.TableDefinitions["Font"]);
546
- var fontRow = fontTable.CreateRow(tuple.SourceLineNumbers);
536
+ var fontRow = this.CreateRow(tuple, "Font");
537
fontRow[0] = tuple.Id.Id;
538
fontRow[1] = tuple.FontTitle;
539
}
540
}
541
552
- private void AddIniFileTuple(IniFileTuple tuple, WindowsInstallerData output)
542
+ private void AddIniFileTuple(IniFileTuple tuple)
543
{
544
var tableName = (InifFileActionType.AddLine == tuple.Action || InifFileActionType.AddTag == tuple.Action || InifFileActionType.CreateLine == tuple.Action) ? "IniFile" : "RemoveIniFile";
545
556
- var table = output.EnsureTable(this.TableDefinitions[tableName]);
557
- var row = table.CreateRow(tuple.SourceLineNumbers);
546
+ var row = this.CreateRow(tuple, tableName);
547
row[0] = tuple.Id.Id;
548
row[1] = tuple.FileName;
549
row[2] = tuple.DirProperty;
@@ -565,12 +554,11 @@ namespace WixToolset.Core.WindowsInstaller.Bind
554
row[7] = tuple.ComponentRef;
555
}
556
568
- private void AddMediaTuple(MediaTuple tuple, WindowsInstallerData output)
557
+ private void AddMediaTuple(MediaTuple tuple)
558
{
559
if (this.Section.Type != SectionType.Module)
560
{
572
- var table = output.EnsureTable(this.TableDefinitions["Media"]);
573
- var row = (MediaRow)table.CreateRow(tuple.SourceLineNumbers);
561
+ var row = (MediaRow)this.CreateRow(tuple, "Media");
562
row.DiskId = tuple.DiskId;
563
row.LastSequence = tuple.LastSequence ?? 0;
564
row.DiskPrompt = tuple.DiskPrompt;
@@ -580,10 +568,9 @@ namespace WixToolset.Core.WindowsInstaller.Bind
568
}
569
}
570
583
- private void AddModuleConfigurationTuple(ModuleConfigurationTuple tuple, WindowsInstallerData output)
571
+ private void AddModuleConfigurationTuple(ModuleConfigurationTuple tuple)
572
{
585
- var table = output.EnsureTable(this.TableDefinitions["ModuleConfiguration"]);
586
- var row = table.CreateRow(tuple.SourceLineNumbers);
573
+ var row = this.CreateRow(tuple, "ModuleConfiguration");
574
row[0] = tuple.Id.Id;
575
row[1] = tuple.Format;
576
row[2] = tuple.Type;
@@ -597,13 +584,12 @@ namespace WixToolset.Core.WindowsInstaller.Bind
584
row[9] = tuple.HelpKeyword;
585
}
586
600
- private void AddMsiEmbeddedUITuple(MsiEmbeddedUITuple tuple, WindowsInstallerData output)
587
+ private void AddMsiEmbeddedUITuple(MsiEmbeddedUITuple tuple)
588
{
589
var attributes = tuple.EntryPoint ? WindowsInstallerConstants.MsidbEmbeddedUI : 0;
590
attributes |= tuple.SupportsBasicUI ? WindowsInstallerConstants.MsidbEmbeddedHandlesBasic : 0;
591
605
- var table = output.EnsureTable(this.TableDefinitions["MsiEmbeddedUI"]);
606
- var row = table.CreateRow(tuple.SourceLineNumbers);
592
+ var row = this.CreateRow(tuple, "MsiEmbeddedUI");
593
row[0] = tuple.Id.Id;
594
row[1] = tuple.FileName;
595
row[2] = attributes;
@@ -611,10 +597,9 @@ namespace WixToolset.Core.WindowsInstaller.Bind
597
row[4] = tuple.Source;
598
}
599
614
- private void AddMsiFileHashTuple(MsiFileHashTuple tuple, WindowsInstallerData output)
600
+ private void AddMsiFileHashTuple(MsiFileHashTuple tuple)
601
{
616
- var table = output.EnsureTable(this.TableDefinitions["MsiFileHash"]);
617
- var row = table.CreateRow(tuple.SourceLineNumbers);
602
+ var row = this.CreateRow(tuple, "MsiFileHash");
603
row[0] = tuple.Id.Id;
604
row[1] = tuple.Options;
605
row[2] = tuple.HashPart1;
@@ -623,14 +608,13 @@ namespace WixToolset.Core.WindowsInstaller.Bind
608
row[5] = tuple.HashPart4;
609
}
610
626
- private void AddMsiServiceConfigTuple(MsiServiceConfigTuple tuple, WindowsInstallerData output)
611
+ private void AddMsiServiceConfigTuple(MsiServiceConfigTuple tuple)
612
{
613
var events = tuple.OnInstall ? WindowsInstallerConstants.MsidbServiceConfigEventInstall : 0;
614
events |= tuple.OnReinstall ? WindowsInstallerConstants.MsidbServiceConfigEventReinstall : 0;
615
events |= tuple.OnUninstall ? WindowsInstallerConstants.MsidbServiceConfigEventUninstall : 0;
616
632
- var table = output.EnsureTable(this.TableDefinitions["MsiServiceConfigFailureActions"]);
633
- var row = table.CreateRow(tuple.SourceLineNumbers);
617
+ var row = this.CreateRow(tuple, "MsiServiceConfigFailureActions");
618
row[0] = tuple.Id.Id;
619
row[1] = tuple.Name;
620
row[2] = events;
@@ -639,14 +623,13 @@ namespace WixToolset.Core.WindowsInstaller.Bind
623
row[5] = tuple.ComponentRef;
624
}
625
642
- private void AddMsiServiceConfigFailureActionsTuple(MsiServiceConfigFailureActionsTuple tuple, WindowsInstallerData output)
626
+ private void AddMsiServiceConfigFailureActionsTuple(MsiServiceConfigFailureActionsTuple tuple)
627
{
628
var events = tuple.OnInstall ? WindowsInstallerConstants.MsidbServiceConfigEventInstall : 0;
629
events |= tuple.OnReinstall ? WindowsInstallerConstants.MsidbServiceConfigEventReinstall : 0;
630
events |= tuple.OnUninstall ? WindowsInstallerConstants.MsidbServiceConfigEventUninstall : 0;
631
648
- var table = output.EnsureTable(this.TableDefinitions["MsiServiceConfig"]);
649
- var row = table.CreateRow(tuple.SourceLineNumbers);
632
+ var row = this.CreateRow(tuple, "MsiServiceConfig");
633
row[0] = tuple.Id.Id;
634
row[1] = tuple.Name;
635
row[2] = events;
@@ -658,10 +641,9 @@ namespace WixToolset.Core.WindowsInstaller.Bind
641
row[8] = tuple.ComponentRef;
642
}
643
661
- private void AddMoveFileTuple(MoveFileTuple tuple, WindowsInstallerData output)
644
+ private void AddMoveFileTuple(MoveFileTuple tuple)
645
{
663
- var table = output.EnsureTable(this.TableDefinitions["MoveFile"]);
664
- var row = table.CreateRow(tuple.SourceLineNumbers);
646
+ var row = this.CreateRow(tuple, "MoveFile");
647
row[0] = tuple.Id.Id;
648
row[1] = tuple.ComponentRef;
649
row[2] = tuple.SourceName;
@@ -671,26 +653,24 @@ namespace WixToolset.Core.WindowsInstaller.Bind
653
row[6] = tuple.Delete ? WindowsInstallerConstants.MsidbMoveFileOptionsMove : 0;
654
}
655
674
- private void AddPropertyTuple(PropertyTuple tuple, WindowsInstallerData output)
656
+ private void AddPropertyTuple(PropertyTuple tuple)
657
{
658
if (String.IsNullOrEmpty(tuple.Value))
659
{
660
return;
661
}
662
681
- var table = output.EnsureTable(this.TableDefinitions["Property"]);
682
- var row = (PropertyRow)table.CreateRow(tuple.SourceLineNumbers);
663
+ var row = (PropertyRow)this.CreateRow(tuple, "Property");
664
row.Property = tuple.Id.Id;
665
row.Value = tuple.Value;
666
}
667
687
- private void AddRemoveFileTuple(RemoveFileTuple tuple, WindowsInstallerData output)
668
+ private void AddRemoveFileTuple(RemoveFileTuple tuple)
669
{
670
var installMode = tuple.OnInstall == true ? WindowsInstallerConstants.MsidbRemoveFileInstallModeOnInstall : 0;
671
installMode |= tuple.OnUninstall == true ? WindowsInstallerConstants.MsidbRemoveFileInstallModeOnRemove : 0;
672
692
- var table = output.EnsureTable(this.TableDefinitions["RemoveFile"]);
693
- var row = table.CreateRow(tuple.SourceLineNumbers);
673
+ var row = this.CreateRow(tuple, "RemoveFile");
674
row[0] = tuple.Id.Id;
675
row[1] = tuple.ComponentRef;
676
row[2] = tuple.FileName;
@@ -698,7 +678,7 @@ namespace WixToolset.Core.WindowsInstaller.Bind
678
row[4] = installMode;
679
}
680
701
- private void AddRegistryTuple(RegistryTuple tuple, WindowsInstallerData output)
681
+ private void AddRegistryTuple(RegistryTuple tuple)
682
{
683
var value = tuple.Value;
684
@@ -740,8 +720,7 @@ namespace WixToolset.Core.WindowsInstaller.Bind
720
break;
721
}
722
743
- var table = output.EnsureTable(this.TableDefinitions["Registry"]);
744
- var row = table.CreateRow(tuple.SourceLineNumbers);
723
+ var row = this.CreateRow(tuple, "Registry");
724
row[0] = tuple.Id.Id;
725
row[1] = tuple.Root;
726
row[2] = tuple.Key;
@@ -750,13 +729,12 @@ namespace WixToolset.Core.WindowsInstaller.Bind
729
row[5] = tuple.ComponentRef;
730
}
731
753
- private void AddRegLocatorTuple(RegLocatorTuple tuple, WindowsInstallerData output)
732
+ private void AddRegLocatorTuple(RegLocatorTuple tuple)
733
{
734
var type = (int)tuple.Type;
735
type |= tuple.Win64 ? WindowsInstallerConstants.MsidbLocatorType64bit : 0;
736
758
- var table = output.EnsureTable(this.TableDefinitions["RegLocator"]);
759
- var row = table.CreateRow(tuple.SourceLineNumbers);
737
+ var row = this.CreateRow(tuple, "RegLocator");
738
row[0] = tuple.Id.Id;
739
row[1] = tuple.Root;
740
row[2] = tuple.Key;
@@ -764,12 +742,11 @@ namespace WixToolset.Core.WindowsInstaller.Bind
742
row[4] = type;
743
}
744
767
- private void AddRemoveRegistryTuple(RemoveRegistryTuple tuple, WindowsInstallerData output)
745
+ private void AddRemoveRegistryTuple(RemoveRegistryTuple tuple)
746
{
747
if (tuple.Action == RemoveRegistryActionType.RemoveOnInstall)
748
{
771
- var table = output.EnsureTable(this.TableDefinitions["RemoveRegistry"]);
772
- var row = table.CreateRow(tuple.SourceLineNumbers);
749
+ var row = this.CreateRow(tuple, "RemoveRegistry");
750
row[0] = tuple.Id.Id;
751
row[1] = tuple.Root;
752
row[2] = tuple.Key;
@@ -778,8 +755,7 @@ namespace WixToolset.Core.WindowsInstaller.Bind
755
}
756
else // Registry table is used to remove registry keys on uninstall.
757
{
781
- var table = output.EnsureTable(this.TableDefinitions["Registry"]);
782
- var row = table.CreateRow(tuple.SourceLineNumbers);
758
+ var row = this.CreateRow(tuple, "Registry");
759
row[0] = tuple.Id.Id;
760
row[1] = tuple.Root;
761
row[2] = tuple.Key;
@@ -788,7 +764,7 @@ namespace WixToolset.Core.WindowsInstaller.Bind
764
}
765
}
766
791
- private void AddServiceControlTuple(ServiceControlTuple tuple, WindowsInstallerData output)
767
+ private void AddServiceControlTuple(ServiceControlTuple tuple)
768
{
769
var events = tuple.InstallRemove ? WindowsInstallerConstants.MsidbServiceControlEventDelete : 0;
770
events |= tuple.UninstallRemove ? WindowsInstallerConstants.MsidbServiceControlEventUninstallDelete : 0;
@@ -797,8 +773,7 @@ namespace WixToolset.Core.WindowsInstaller.Bind
773
events |= tuple.InstallStop ? WindowsInstallerConstants.MsidbServiceControlEventStop : 0;
774
events |= tuple.UninstallStop ? WindowsInstallerConstants.MsidbServiceControlEventUninstallStop : 0;
775
800
- var table = output.EnsureTable(this.TableDefinitions["ServiceControl"]);
801
- var row = table.CreateRow(tuple.SourceLineNumbers);
776
+ var row = this.CreateRow(tuple, "ServiceControl");
777
row[0] = tuple.Id.Id;
778
row[1] = tuple.Name;
779
row[2] = events;
@@ -810,7 +785,7 @@ namespace WixToolset.Core.WindowsInstaller.Bind
785
row[5] = tuple.ComponentRef;
786
}
787
813
- private void AddServiceInstallTuple(ServiceInstallTuple tuple, WindowsInstallerData output)
788
+ private void AddServiceInstallTuple(ServiceInstallTuple tuple)
789
{
790
var errorControl = (int)tuple.ErrorControl;
791
errorControl |= tuple.Vital ? WindowsInstallerConstants.MsidbServiceInstallErrorControlVital : 0;
@@ -818,8 +793,7 @@ namespace WixToolset.Core.WindowsInstaller.Bind
793
var serviceType = (int)tuple.ServiceType;
794
serviceType |= tuple.Interactive ? WindowsInstallerConstants.MsidbServiceInstallInteractive : 0;
795
821
- var table = output.EnsureTable(this.TableDefinitions["ServiceInstall"]);
822
- var row = table.CreateRow(tuple.SourceLineNumbers);
796
+ var row = this.CreateRow(tuple, "ServiceInstall");
797
row[0] = tuple.Id.Id;
798
row[1] = tuple.Name;
799
row[2] = tuple.DisplayName;
@@ -835,10 +809,9 @@ namespace WixToolset.Core.WindowsInstaller.Bind
809
row[12] = tuple.Description;
810
}
811
838
- private void AddShortcutTuple(ShortcutTuple tuple, WindowsInstallerData output)
812
+ private void AddShortcutTuple(ShortcutTuple tuple)
813
{
840
- var table = output.EnsureTable(this.TableDefinitions["Shortcut"]);
841
- var row = table.CreateRow(tuple.SourceLineNumbers);
814
+ var row = this.CreateRow(tuple, "Shortcut");
815
row[0] = tuple.Id.Id;
816
row[1] = tuple.DirectoryRef;
817
row[2] = GetMsiFilenameValue(tuple.ShortName, tuple.Name);
@@ -857,7 +830,7 @@ namespace WixToolset.Core.WindowsInstaller.Bind
830
row[15] = tuple.DescriptionResourceId;
831
}
832
860
- private void AddTextStyleTuple(TextStyleTuple tuple, WindowsInstallerData output)
833
+ private void AddTextStyleTuple(TextStyleTuple tuple)
834
{
835
var styleBits = tuple.Bold ? WindowsInstallerConstants.MsidbTextStyleStyleBitsBold : 0;
836
styleBits |= tuple.Italic ? WindowsInstallerConstants.MsidbTextStyleStyleBitsItalic : 0;
@@ -873,8 +846,7 @@ namespace WixToolset.Core.WindowsInstaller.Bind
846
color += (long)(tuple.Blue ?? 0) * 65536;
847
}
848
876
- var table = output.EnsureTable(this.TableDefinitions["TextStyle"]);
877
- var row = table.CreateRow(tuple.SourceLineNumbers);
849
+ var row = this.CreateRow(tuple, "TextStyle");
850
row[0] = tuple.Id.Id;
851
row[1] = tuple.FaceName;
852
row[2] = tuple.Size;
@@ -882,10 +854,9 @@ namespace WixToolset.Core.WindowsInstaller.Bind
854
row[4] = styleBits == 0 ? null : (int?)styleBits;
855
}
856
885
- private void AddUpgradeTuple(UpgradeTuple tuple, WindowsInstallerData output)
857
+ private void AddUpgradeTuple(UpgradeTuple tuple)
858
{
887
- var table = output.EnsureTable(this.TableDefinitions["Upgrade"]);
888
- var row = (UpgradeRow)table.CreateRow(tuple.SourceLineNumbers);
859
+ var row = (UpgradeRow)this.CreateRow(tuple, "Upgrade");
860
row.UpgradeCode = tuple.UpgradeCode;
861
row.VersionMin = tuple.VersionMin;
862
row.VersionMax = tuple.VersionMax;
@@ -902,72 +873,71 @@ namespace WixToolset.Core.WindowsInstaller.Bind
873
row.Attributes = attributes;
874
}
875
905
- private void AddWixActionTuple(WixActionTuple tuple, WindowsInstallerData output)
876
+ private void AddWixActionTuple(WixActionTuple tuple)
877
{
878
// Get the table definition for the action (and ensure the proper table exists for a module).
908
- TableDefinition sequenceTableDefinition = null;
879
+ string sequenceTableName = null;
880
switch (tuple.SequenceTable)
881
{
882
case SequenceTable.AdminExecuteSequence:
912
- if (OutputType.Module == output.Type)
883
+ if (OutputType.Module == this.Output.Type)
884
{
914
- output.EnsureTable(this.TableDefinitions["AdminExecuteSequence"]);
915
- sequenceTableDefinition = this.TableDefinitions["ModuleAdminExecuteSequence"];
885
+ this.Output.EnsureTable(this.TableDefinitions["AdminExecuteSequence"]);
886
+ sequenceTableName = "ModuleAdminExecuteSequence";
887
}
888
else
889
{
919
- sequenceTableDefinition = this.TableDefinitions["AdminExecuteSequence"];
890
+ sequenceTableName = "AdminExecuteSequence";
891
}
892
break;
893
case SequenceTable.AdminUISequence:
923
- if (OutputType.Module == output.Type)
894
+ if (OutputType.Module == this.Output.Type)
895
{
925
- output.EnsureTable(this.TableDefinitions["AdminUISequence"]);
926
- sequenceTableDefinition = this.TableDefinitions["ModuleAdminUISequence"];
896
+ this.Output.EnsureTable(this.TableDefinitions["AdminUISequence"]);
897
+ sequenceTableName = "ModuleAdminUISequence";
898
}
899
else
900
{
930
- sequenceTableDefinition = this.TableDefinitions["AdminUISequence"];
901
+ sequenceTableName = "AdminUISequence";
902
}
903
break;
904
case SequenceTable.AdvertiseExecuteSequence:
934
- if (OutputType.Module == output.Type)
905
+ if (OutputType.Module == this.Output.Type)
906
{
936
- output.EnsureTable(this.TableDefinitions["AdvtExecuteSequence"]);
937
- sequenceTableDefinition = this.TableDefinitions["ModuleAdvtExecuteSequence"];
907
+ this.Output.EnsureTable(this.TableDefinitions["AdvtExecuteSequence"]);
908
+ sequenceTableName = "ModuleAdvtExecuteSequence";
909
}
910
else
911
{
941
- sequenceTableDefinition = this.TableDefinitions["AdvtExecuteSequence"];
912
+ sequenceTableName = "AdvtExecuteSequence";
913
}
914
break;
915
case SequenceTable.InstallExecuteSequence:
945
- if (OutputType.Module == output.Type)
916
+ if (OutputType.Module == this.Output.Type)
917
{
947
- output.EnsureTable(this.TableDefinitions["InstallExecuteSequence"]);
948
- sequenceTableDefinition = this.TableDefinitions["ModuleInstallExecuteSequence"];
918
+ this.Output.EnsureTable(this.TableDefinitions["InstallExecuteSequence"]);
919
+ sequenceTableName = "ModuleInstallExecuteSequence";
920
}
921
else
922
{
952
- sequenceTableDefinition = this.TableDefinitions["InstallExecuteSequence"];
923
+ sequenceTableName = "InstallExecuteSequence";
924
}
925
break;
926
case SequenceTable.InstallUISequence:
956
- if (OutputType.Module == output.Type)
927
+ if (OutputType.Module == this.Output.Type)
928
{
958
- output.EnsureTable(this.TableDefinitions["InstallUISequence"]);
959
- sequenceTableDefinition = this.TableDefinitions["ModuleInstallUISequence"];
929
+ this.Output.EnsureTable(this.TableDefinitions["InstallUISequence"]);
930
+ sequenceTableName = "ModuleInstallUISequence";
931
}
932
else
933
{
963
- sequenceTableDefinition = this.TableDefinitions["InstallUISequence"];
934
+ sequenceTableName = "InstallUISequence";
935
}
936
break;
937
}
938
939
// create the action sequence row in the output
969
- var sequenceTable = output.EnsureTable(sequenceTableDefinition);
970
- var row = sequenceTable.CreateRow(tuple.SourceLineNumbers);
940
+ var row = this.CreateRow(tuple, sequenceTableName);
941
942
if (SectionType.Module == this.Section.Type)
943
{
@@ -992,7 +962,7 @@ namespace WixToolset.Core.WindowsInstaller.Bind
962
}
963
}
964
995
- private void AddWixCustomRowTuple(WixCustomRowTuple tuple, WindowsInstallerData output)
965
+ private void AddWixCustomRowTuple(WixCustomRowTuple tuple)
966
{
967
var customTableDefinition = this.TableDefinitions[tuple.Table];
968
@@ -1002,8 +972,7 @@ namespace WixToolset.Core.WindowsInstaller.Bind
972
return;
973
}
974
1005
- var customTable = output.EnsureTable(customTableDefinition);
1006
- var customRow = customTable.CreateRow(tuple.SourceLineNumbers);
975
+ var customRow = this.CreateRow(tuple, customTableDefinition);
976
977
#if TODO // SectionId seems like a good thing to preserve.
978
customRow.SectionId = tuple.SectionId;
@@ -1073,16 +1042,15 @@ namespace WixToolset.Core.WindowsInstaller.Bind
1042
}
1043
}
1044
1076
- private void AddWixEnsureTableTuple(WixEnsureTableTuple tuple, WindowsInstallerData output)
1045
+ private void AddWixEnsureTableTuple(WixEnsureTableTuple tuple)
1046
{
1047
var tableDefinition = this.TableDefinitions[tuple.Table];
1079
- output.EnsureTable(tableDefinition);
1048
+ this.Output.EnsureTable(tableDefinition);
1049
}
1050
1082
- private void AddWixMediaTemplateTuple(WixMediaTemplateTuple tuple, WindowsInstallerData output)
1051
+ private void AddWixMediaTemplateTuple(WixMediaTemplateTuple tuple)
1052
{
1084
- var table = output.EnsureTable(this.TableDefinitions["WixMediaTemplate"]);
1085
- var row = (WixMediaTemplateRow)table.CreateRow(tuple.SourceLineNumbers);
1053
+ var row = (WixMediaTemplateRow)this.CreateRow(tuple, "WixMediaTemplate");
1054
row.CabinetTemplate = tuple.CabinetTemplate;
1055
row.CompressionLevel = tuple.CompressionLevel;
1056
row.DiskPrompt = tuple.DiskPrompt;
@@ -1091,26 +1059,25 @@ namespace WixToolset.Core.WindowsInstaller.Bind
1059
row.MaximumCabinetSizeForLargeFileSplitting = tuple.MaximumCabinetSizeForLargeFileSplitting ?? MaxValueOfMaxCabSizeForLargeFileSplitting;
1060
}
1061
1094
- private void AddTupleFromExtension(IntermediateTuple tuple, WindowsInstallerData output)
1062
+ private void AddTupleFromExtension(IntermediateTuple tuple)
1063
{
1064
foreach (var extension in this.BackendExtensions)
1065
{
1098
- if (extension.TryAddTupleToOutput(tuple, output))
1066
+ if (extension.TryAddTupleToOutput(tuple, this.Output))
1067
{
1068
break;
1069
}
1070
}
1071
}
1072
1105
- private void AddTupleDefaultly(IntermediateTuple tuple, WindowsInstallerData output, bool idIsPrimaryKey = false, string tableName = null)
1073
+ private void AddTupleDefaultly(IntermediateTuple tuple, bool idIsPrimaryKey = false, string tableName = null)
1074
{
1075
if (!this.TableDefinitions.TryGet(tableName ?? tuple.Definition.Name, out var tableDefinition))
1076
{
1077
return;
1078
}
1079
1112
- var table = output.EnsureTable(tableDefinition);
1113
- var row = table.CreateRow(tuple.SourceLineNumbers);
1080
+ var row = this.CreateRow(tuple, tableDefinition);
1081
var rowOffset = 0;
1082
1083
if (idIsPrimaryKey)
@@ -1159,6 +1126,18 @@ namespace WixToolset.Core.WindowsInstaller.Bind
1126
}
1127
}
1128
1129
+ private Row CreateRow(IntermediateTuple tuple, string tableDefinitionName) => this.CreateRow(tuple, this.TableDefinitions[tableDefinitionName]);
1130
+
1131
+ private Row CreateRow(IntermediateTuple tuple, TableDefinition tableDefinition)
1132
+ {
1133
+ var table = this.Output.EnsureTable(tableDefinition);
1134
+
1135
+ var row = table.CreateRow(tuple.SourceLineNumbers);
1136
+ row.SectionId = this.Section.Id;
1137
+
1138
+ return row;
1139
+ }
1140
+
1141
private static string GetMsiFilenameValue(string shortName, string longName)
1142
{
1143
if (String.IsNullOrEmpty(shortName) || String.Equals(shortName, longName, StringComparison.OrdinalIgnoreCase))
src/WixToolset.Core.WindowsInstaller/Bind/CreatePatchTransformsCommand.cs
new
+90
@@ -0,0 +1,90 @@
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.IO;
8
+ using System.Linq;
9
+ using WixToolset.Core.WindowsInstaller.Msi;
10
+ using WixToolset.Core.WindowsInstaller.Unbind;
11
+ using WixToolset.Data;
12
+ using WixToolset.Data.Tuples;
13
+ using WixToolset.Data.WindowsInstaller;
14
+ using WixToolset.Extensibility.Services;
15
+
16
+ internal class CreatePatchTransformsCommand
17
+ {
18
+ public CreatePatchTransformsCommand(IMessaging messaging, Intermediate intermediate, string intermediateFolder)
19
+ {
20
+ this.Messaging = messaging;
21
+ this.Intermediate = intermediate;
22
+ this.IntermediateFolder = intermediateFolder;
23
+ }
24
+
25
+ private IMessaging Messaging { get; }
26
+
27
+ private Intermediate Intermediate { get; }
28
+
29
+ private string IntermediateFolder { get; }
30
+
31
+ public IEnumerable<PatchTransform> PatchTransforms { get; private set; }
32
+
33
+ public IEnumerable<PatchTransform> Execute()
34
+ {
35
+ var patchTransforms = new List<PatchTransform>();
36
+
37
+ var tuples = this.Intermediate.Sections.SelectMany(s => s.Tuples).OfType<WixPatchBaselineTuple>();
38
+
39
+ foreach (var tuple in tuples)
40
+ {
41
+ WindowsInstallerData transform;
42
+
43
+ if (tuple.TransformFile is null)
44
+ {
45
+ var baselineData = this.GetData(tuple.BaselineFile.Path);
46
+ var updateData = this.GetData(tuple.UpdateFile.Path);
47
+
48
+ var command = new GenerateTransformCommand(this.Messaging, baselineData, updateData, false);
49
+ transform = command.Execute();
50
+ }
51
+ else
52
+ {
53
+ var exportBasePath = Path.Combine(this.IntermediateFolder, "_trans"); // TODO: come up with a better path.
54
+
55
+ var command = new UnbindTransformCommand(this.Messaging, tuple.TransformFile.Path, exportBasePath, this.IntermediateFolder);
56
+ transform = command.Execute();
57
+ }
58
+
59
+ patchTransforms.Add(new PatchTransform(tuple.Id.Id, transform));
60
+ }
61
+
62
+ this.PatchTransforms = patchTransforms;
63
+
64
+ return this.PatchTransforms;
65
+ }
66
+
67
+ private WindowsInstallerData GetData(string path)
68
+ {
69
+ var ext = Path.GetExtension(path);
70
+
71
+ if (".msi".Equals(ext, StringComparison.OrdinalIgnoreCase))
72
+ {
73
+ using (var database = new Database(path, OpenDatabase.ReadOnly))
74
+ {
75
+ var exportBasePath = Path.Combine(this.IntermediateFolder, "_msi"); // TODO: come up with a better path.
76
+
77
+ var isAdminImage = false; // TODO: need a better way to set this
78
+
79
+ var command = new UnbindDatabaseCommand(this.Messaging, database, path, OutputType.Product, exportBasePath, this.IntermediateFolder, isAdminImage, suppressDemodularization: true, skipSummaryInfo: true);
80
+ return command.Execute();
81
+ }
82
+ }
83
+ else // assume .wixpdb (or .wixout)
84
+ {
85
+ var data = WindowsInstallerData.Load(path, true);
86
+ return data;
87
+ }
88
+ }
89
+ }
90
+}
src/WixToolset.Core.WindowsInstaller/Bind/ExtractMergeModuleFilesCommand.cs
+20
-20
@@ -48,7 +48,7 @@ namespace WixToolset.Core.WindowsInstaller.Bind
48
{
49
var mergeModulesFileFacades = new List<FileFacade>();
50
51
- IMsmMerge2 merge = MsmInterop.GetMsmMerge();
51
+ var merge = MsmInterop.GetMsmMerge();
52
53
// Index all of the file rows to be able to detect collisions with files in the Merge Modules.
54
// It may seem a bit expensive to build up this index solely for the purpose of checking collisions
@@ -57,11 +57,11 @@ namespace WixToolset.Core.WindowsInstaller.Bind
57
// Now since Merge Modules are already slow and generally less desirable than .wixlibs we'll let
58
// this case be slightly more expensive because the cost of maintaining an indexed file row collection
59
// is a lot more costly for the common cases.
60
- var indexedFileFacades = this.FileFacades.ToDictionary(f => f.File.Id.Id, StringComparer.Ordinal);
60
+ var indexedFileFacades = this.FileFacades.ToDictionary(f => f.Id, StringComparer.Ordinal);
61
62
foreach (var wixMergeRow in this.WixMergeTuples)
63
{
64
- bool containsFiles = this.CreateFacadesForMergeModuleFiles(wixMergeRow, mergeModulesFileFacades, indexedFileFacades);
64
+ var containsFiles = this.CreateFacadesForMergeModuleFiles(wixMergeRow, mergeModulesFileFacades, indexedFileFacades);
65
66
// If the module has files and creating layout
67
if (containsFiles && !this.SuppressLayout)
@@ -75,21 +75,21 @@ namespace WixToolset.Core.WindowsInstaller.Bind
75
76
private bool CreateFacadesForMergeModuleFiles(WixMergeTuple wixMergeRow, List<FileFacade> mergeModulesFileFacades, Dictionary<string, FileFacade> indexedFileFacades)
77
{
78
- bool containsFiles = false;
78
+ var containsFiles = false;
79
80
try
81
{
82
// read the module's File table to get its FileMediaInformation entries and gather any other information needed from the module.
83
- using (Database db = new Database(wixMergeRow.SourceFile, OpenDatabase.ReadOnly))
83
+ using (var db = new Database(wixMergeRow.SourceFile, OpenDatabase.ReadOnly))
84
{
85
if (db.TableExists("File") && db.TableExists("Component"))
86
{
87
- Dictionary<string, FileFacade> uniqueModuleFileIdentifiers = new Dictionary<string, FileFacade>(StringComparer.OrdinalIgnoreCase);
87
+ var uniqueModuleFileIdentifiers = new Dictionary<string, FileFacade>(StringComparer.OrdinalIgnoreCase);
88
89
- using (View view = db.OpenExecuteView("SELECT `File`, `Directory_` FROM `File`, `Component` WHERE `Component_`=`Component`"))
89
+ using (var view = db.OpenExecuteView("SELECT `File`, `Directory_` FROM `File`, `Component` WHERE `Component_`=`Component`"))
90
{
91
// add each file row from the merge module into the file row collection (check for errors along the way)
92
- foreach (Record record in view.Records)
92
+ foreach (var record in view.Records)
93
{
94
// NOTE: this is very tricky - the merge module file rows are not added to the
95
// file table because they should not be created via idt import. Instead, these
@@ -103,21 +103,21 @@ namespace WixToolset.Core.WindowsInstaller.Bind
103
var mergeModuleFileFacade = new FileFacade(true, fileTuple);
104
105
// If case-sensitive collision with another merge module or a user-authored file identifier.
106
- if (indexedFileFacades.TryGetValue(mergeModuleFileFacade.File.Id.Id, out var collidingFacade))
106
+ if (indexedFileFacades.TryGetValue(mergeModuleFileFacade.Id, out var collidingFacade))
107
{
108
- this.Messaging.Write(ErrorMessages.DuplicateModuleFileIdentifier(wixMergeRow.SourceLineNumbers, wixMergeRow.Id.Id, collidingFacade.File.Id.Id));
108
+ this.Messaging.Write(ErrorMessages.DuplicateModuleFileIdentifier(wixMergeRow.SourceLineNumbers, wixMergeRow.Id.Id, collidingFacade.Id));
109
}
110
- else if (uniqueModuleFileIdentifiers.TryGetValue(mergeModuleFileFacade.File.Id.Id, out collidingFacade)) // case-insensitive collision with another file identifier in the same merge module
110
+ else if (uniqueModuleFileIdentifiers.TryGetValue(mergeModuleFileFacade.Id, out collidingFacade)) // case-insensitive collision with another file identifier in the same merge module
111
{
112
- this.Messaging.Write(ErrorMessages.DuplicateModuleCaseInsensitiveFileIdentifier(wixMergeRow.SourceLineNumbers, wixMergeRow.Id.Id, mergeModuleFileFacade.File.Id.Id, collidingFacade.File.Id.Id));
112
+ this.Messaging.Write(ErrorMessages.DuplicateModuleCaseInsensitiveFileIdentifier(wixMergeRow.SourceLineNumbers, wixMergeRow.Id.Id, mergeModuleFileFacade.Id, collidingFacade.Id));
113
}
114
else // no collision
115
{
116
mergeModulesFileFacades.Add(mergeModuleFileFacade);
117
118
// Keep updating the indexes as new rows are added.
119
- indexedFileFacades.Add(mergeModuleFileFacade.File.Id.Id, mergeModuleFileFacade);
120
- uniqueModuleFileIdentifiers.Add(mergeModuleFileFacade.File.Id.Id, mergeModuleFileFacade);
119
+ indexedFileFacades.Add(mergeModuleFileFacade.Id, mergeModuleFileFacade);
120
+ uniqueModuleFileIdentifiers.Add(mergeModuleFileFacade.Id, mergeModuleFileFacade);
121
}
122
123
containsFiles = true;
@@ -126,13 +126,13 @@ namespace WixToolset.Core.WindowsInstaller.Bind
126
}
127
128
// Get the summary information to detect the Schema
129
- using (SummaryInformation summaryInformation = new SummaryInformation(db))
129
+ using (var summaryInformation = new SummaryInformation(db))
130
{
131
- string moduleInstallerVersionString = summaryInformation.GetProperty(14);
131
+ var moduleInstallerVersionString = summaryInformation.GetProperty(14);
132
133
try
134
{
135
- int moduleInstallerVersion = Convert.ToInt32(moduleInstallerVersionString, CultureInfo.InvariantCulture);
135
+ var moduleInstallerVersion = Convert.ToInt32(moduleInstallerVersionString, CultureInfo.InvariantCulture);
136
if (moduleInstallerVersion > this.OutputInstallerVersion)
137
{
138
this.Messaging.Write(WarningMessages.InvalidHigherInstallerVersionInModule(wixMergeRow.SourceLineNumbers, wixMergeRow.Id.Id, moduleInstallerVersion, this.OutputInstallerVersion));
@@ -159,7 +159,7 @@ namespace WixToolset.Core.WindowsInstaller.Bind
159
160
private void ExtractFilesFromMergeModule(IMsmMerge2 merge, WixMergeTuple wixMergeRow)
161
{
162
- bool moduleOpen = false;
162
+ var moduleOpen = false;
163
short mergeLanguage;
164
165
var mergeId = wixMergeRow.Id.Id;
@@ -180,10 +180,10 @@ namespace WixToolset.Core.WindowsInstaller.Bind
180
moduleOpen = true;
181
182
// extract the module cabinet, then explode all of the files to a temp directory
183
- string moduleCabPath = Path.Combine(this.IntermediateFolder, mergeId + ".cab");
183
+ var moduleCabPath = Path.Combine(this.IntermediateFolder, mergeId + ".cab");
184
merge.ExtractCAB(moduleCabPath);
185
186
- string mergeIdPath = Path.Combine(this.IntermediateFolder, mergeId);
186
+ var mergeIdPath = Path.Combine(this.IntermediateFolder, mergeId);
187
Directory.CreateDirectory(mergeIdPath);
188
189
try
src/WixToolset.Core.WindowsInstaller/Bind/GenerateDatabaseCommand.cs
+282
-253
@@ -5,367 +5,396 @@ namespace WixToolset.Core.WindowsInstaller.Bind
5
using System;
6
using System.Collections.Generic;
7
using System.ComponentModel;
8
- using System.Globalization;
8
using System.IO;
9
using System.Text;
10
+ using WixToolset.Core.WindowsInstaller.Msi;
11
using WixToolset.Data;
12
- using WixToolset.Extensibility;
12
using WixToolset.Data.WindowsInstaller;
14
- using WixToolset.Extensibility.Services;
13
+ using WixToolset.Extensibility;
14
using WixToolset.Extensibility.Data;
16
- using WixToolset.Core.WindowsInstaller.Msi;
15
+ using WixToolset.Extensibility.Services;
16
18
- internal class GenerateDatabaseCommand
17
+ internal class GenerateDatabaseCommand
18
{
20
- public int Codepage { private get; set; }
19
+ public GenerateDatabaseCommand(IMessaging messaging, IBackendHelper backendHelper, IEnumerable<IFileSystemExtension> fileSystemExtensions, WindowsInstallerData data, string outputPath, TableDefinitionCollection tableDefinitions, string intermediateFolder, int codepage, bool keepAddedColumns, bool suppressAddingValidationRows, bool useSubdirectory)
20
+ {
21
+ this.Messaging = messaging;
22
+ this.BackendHelper = backendHelper;
23
+ this.Extensions = fileSystemExtensions;
24
+ this.Data = data;
25
+ this.OutputPath = outputPath;
26
+ this.TableDefinitions = tableDefinitions;
27
+ this.IntermediateFolder = intermediateFolder;
28
+ this.Codepage = codepage;
29
+ this.KeepAddedColumns = keepAddedColumns;
30
+ this.SuppressAddingValidationRows = suppressAddingValidationRows;
31
+ this.UseSubDirectory = useSubdirectory;
32
+ }
33
+
34
+ private int Codepage { get; }
35
22
- public IBackendHelper BackendHelper { private get; set; }
36
+ private IBackendHelper BackendHelper { get; }
37
24
- public IEnumerable<IFileSystemExtension> Extensions { private get; set; }
38
+ private IEnumerable<IFileSystemExtension> Extensions { get; }
39
40
/// <summary>
41
/// Whether to keep columns added in a transform.
42
/// </summary>
29
- public bool KeepAddedColumns { private get; set; }
43
+ private bool KeepAddedColumns { get; }
44
31
- public IMessaging Messaging { private get; set; }
45
+ private IMessaging Messaging { get; }
46
33
- public WindowsInstallerData Output { private get; set; }
47
+ private WindowsInstallerData Data { get; }
48
35
- public string OutputPath { private get; set; }
49
+ private string OutputPath { get; }
50
37
- public TableDefinitionCollection TableDefinitions { private get; set; }
51
+ private TableDefinitionCollection TableDefinitions { get; }
52
39
- public string IntermediateFolder { private get; set; }
53
+ private string IntermediateFolder { get; }
54
55
public List<ITrackedFile> GeneratedTemporaryFiles { get; } = new List<ITrackedFile>();
56
57
/// <summary>
58
/// Whether to use a subdirectory based on the <paramref name="databaseFile"/> file name for intermediate files.
59
/// </summary>
46
- public bool SuppressAddingValidationRows { private get; set; }
60
+ private bool SuppressAddingValidationRows { get; }
61
48
- public bool UseSubDirectory { private get; set; }
62
+ private bool UseSubDirectory { get; }
63
64
public void Execute()
65
{
66
// Add the _Validation rows.
67
if (!this.SuppressAddingValidationRows)
68
{
55
- var validationTable = this.Output.EnsureTable(this.TableDefinitions["_Validation"]);
56
-
57
- foreach (var table in this.Output.Tables)
58
- {
59
- if (!table.Definition.Unreal)
60
- {
61
- // Add the validation rows for this table.
62
- foreach (ColumnDefinition columnDef in table.Definition.Columns)
63
- {
64
- var row = validationTable.CreateRow(null);
65
-
66
- row[0] = table.Name;
67
-
68
- row[1] = columnDef.Name;
69
-
70
- if (columnDef.Nullable)
71
- {
72
- row[2] = "Y";
73
- }
74
- else
75
- {
76
- row[2] = "N";
77
- }
78
-
79
- if (columnDef.MinValue.HasValue)
80
- {
81
- row[3] = columnDef.MinValue.Value;
82
- }
83
-
84
- if (columnDef.MaxValue.HasValue)
85
- {
86
- row[4] = columnDef.MaxValue.Value;
87
- }
88
-
89
- row[5] = columnDef.KeyTable;
90
-
91
- if (columnDef.KeyColumn.HasValue)
92
- {
93
- row[6] = columnDef.KeyColumn.Value;
94
- }
95
-
96
- if (ColumnCategory.Unknown != columnDef.Category)
97
- {
98
- row[7] = columnDef.Category.ToString();
99
- }
100
-
101
- row[8] = columnDef.Possibilities;
102
-
103
- row[9] = columnDef.Description;
104
- }
105
- }
106
- }
69
+ this.AddValidationRows();
70
}
71
109
- // Set the base directory.
72
var baseDirectory = this.IntermediateFolder;
73
74
if (this.UseSubDirectory)
75
{
114
- string filename = Path.GetFileNameWithoutExtension(this.OutputPath);
76
+ var filename = Path.GetFileNameWithoutExtension(this.OutputPath);
77
baseDirectory = Path.Combine(baseDirectory, filename);
116
-
117
- // make sure the directory exists
118
- Directory.CreateDirectory(baseDirectory);
78
}
79
121
- var idtDirectory = Path.Combine(baseDirectory, "_idts");
122
- Directory.CreateDirectory(idtDirectory);
80
+ var idtFolder = Path.Combine(baseDirectory, "_idts");
81
124
- try
82
+ var type = OpenDatabase.CreateDirect;
83
+
84
+ if (OutputType.Patch == this.Data.Type)
85
{
126
- OpenDatabase type = OpenDatabase.CreateDirect;
86
+ type |= OpenDatabase.OpenPatchFile;
87
+ }
88
128
- // set special flag for patch files
129
- if (OutputType.Patch == this.Output.Type)
130
- {
131
- type |= OpenDatabase.OpenPatchFile;
132
- }
89
+ // Localize the codepage if a value was specified directly.
90
+ if (-1 != this.Codepage)
91
+ {
92
+ this.Data.Codepage = this.Codepage;
93
+ }
94
95
+ try
96
+ {
97
#if DEBUG
98
Console.WriteLine("Opening database at: {0}", this.OutputPath);
99
#endif
100
138
- // Localize the codepage if a value was specified directly.
139
- if (-1 != this.Codepage)
140
- {
141
- this.Output.Codepage = this.Codepage;
142
- }
143
-
101
Directory.CreateDirectory(Path.GetDirectoryName(this.OutputPath));
102
146
- using (Database db = new Database(this.OutputPath, type))
103
+ Directory.CreateDirectory(idtFolder);
104
+
105
+ using (var db = new Database(this.OutputPath, type))
106
{
148
- // if we're not using the default codepage, import a new one into our
107
+ // If we're not using the default codepage, import a new one into our
108
// database before we add any tables (or the tables would be added
109
// with the wrong codepage).
151
- if (0 != this.Output.Codepage)
110
+ if (0 != this.Data.Codepage)
111
{
153
- this.SetDatabaseCodepage(db, this.Output.Codepage, idtDirectory);
112
+ this.SetDatabaseCodepage(db, this.Data.Codepage, idtFolder);
113
}
114
156
- foreach (Table table in this.Output.Tables)
115
+ this.ImportTables(db, idtFolder);
116
+
117
+ // Insert substorages (usually transforms inside a patch or instance transforms in a package).
118
+ this.ImportSubStorages(db);
119
+
120
+ // We're good, commit the changes to the new database.
121
+ db.Commit();
122
+ }
123
+ }
124
+ catch (IOException e)
125
+ {
126
+ // TODO: this error message doesn't seem specific enough
127
+ throw new WixException(ErrorMessages.FileNotFound(new SourceLineNumber(this.OutputPath), this.OutputPath), e);
128
+ }
129
+ }
130
+
131
+ private void AddValidationRows()
132
+ {
133
+ var validationTable = this.Data.EnsureTable(this.TableDefinitions["_Validation"]);
134
+
135
+ foreach (var table in this.Data.Tables)
136
+ {
137
+ if (!table.Definition.Unreal)
138
+ {
139
+ // Add the validation rows for this table.
140
+ foreach (var columnDef in table.Definition.Columns)
141
{
158
- Table importTable = table;
159
- bool hasBinaryColumn = false;
142
+ var row = validationTable.CreateRow(null);
143
+
144
+ row[0] = table.Name;
145
161
- // Skip all unreal tables other than _Streams.
162
- if (table.Definition.Unreal && "_Streams" != table.Name)
146
+ row[1] = columnDef.Name;
147
+
148
+ if (columnDef.Nullable)
149
{
164
- continue;
150
+ row[2] = "Y";
151
+ }
152
+ else
153
+ {
154
+ row[2] = "N";
155
}
156
167
- // Do not put the _Validation table in patches, it is not needed.
168
- if (OutputType.Patch == this.Output.Type && "_Validation" == table.Name)
157
+ if (columnDef.MinValue.HasValue)
158
{
170
- continue;
159
+ row[3] = columnDef.MinValue.Value;
160
}
161
173
- // The only way to import binary data is to copy it to a local subdirectory first.
174
- // To avoid this extra copying and perf hit, import an empty table with the same
175
- // definition and later import the binary data from source using records.
176
- foreach (ColumnDefinition columnDefinition in table.Definition.Columns)
162
+ if (columnDef.MaxValue.HasValue)
163
{
178
- if (ColumnType.Object == columnDefinition.Type)
179
- {
180
- importTable = new Table(table.Definition);
181
- hasBinaryColumn = true;
182
- break;
183
- }
164
+ row[4] = columnDef.MaxValue.Value;
165
}
166
186
- // Create the table via IDT import.
187
- if ("_Streams" != importTable.Name)
167
+ row[5] = columnDef.KeyTable;
168
+
169
+ if (columnDef.KeyColumn.HasValue)
170
{
189
- try
190
- {
191
- var command = new CreateIdtFileCommand(this.Messaging, importTable, this.Output.Codepage, idtDirectory, this.KeepAddedColumns);
192
- command.Execute();
171
+ row[6] = columnDef.KeyColumn.Value;
172
+ }
173
194
- var buildOutput = this.BackendHelper.TrackFile(command.IdtPath, TrackedFileType.Temporary);
195
- this.GeneratedTemporaryFiles.Add(buildOutput);
174
+ if (ColumnCategory.Unknown != columnDef.Category)
175
+ {
176
+ row[7] = columnDef.Category.ToString();
177
+ }
178
197
- db.Import(command.IdtPath);
198
- }
199
- catch (WixInvalidIdtException)
200
- {
201
- // If ValidateRows finds anything it doesn't like, it throws
202
- importTable.ValidateRows();
179
+ row[8] = columnDef.Possibilities;
180
204
- // Otherwise we rethrow the InvalidIdt
205
- throw;
206
- }
181
+ row[9] = columnDef.Description;
182
+ }
183
+ }
184
+ }
185
+ }
186
+
187
+ private void ImportTables(Database db, string idtDirectory)
188
+ {
189
+ foreach (var table in this.Data.Tables)
190
+ {
191
+ var importTable = table;
192
+ var hasBinaryColumn = false;
193
+
194
+ // Skip all unreal tables other than _Streams.
195
+ if (table.Definition.Unreal && "_Streams" != table.Name)
196
+ {
197
+ continue;
198
+ }
199
+
200
+ // Do not put the _Validation table in patches, it is not needed.
201
+ if (OutputType.Patch == this.Data.Type && "_Validation" == table.Name)
202
+ {
203
+ continue;
204
+ }
205
+
206
+ // The only way to import binary data is to copy it to a local subdirectory first.
207
+ // To avoid this extra copying and perf hit, import an empty table with the same
208
+ // definition and later import the binary data from source using records.
209
+ foreach (var columnDefinition in table.Definition.Columns)
210
+ {
211
+ if (ColumnType.Object == columnDefinition.Type)
212
+ {
213
+ importTable = new Table(table.Definition);
214
+ hasBinaryColumn = true;
215
+ break;
216
+ }
217
+ }
218
+
219
+ // Create the table via IDT import.
220
+ if ("_Streams" != importTable.Name)
221
+ {
222
+ try
223
+ {
224
+ var command = new CreateIdtFileCommand(this.Messaging, importTable, this.Data.Codepage, idtDirectory, this.KeepAddedColumns);
225
+ command.Execute();
226
+
227
+ var buildOutput = this.BackendHelper.TrackFile(command.IdtPath, TrackedFileType.Temporary);
228
+ this.GeneratedTemporaryFiles.Add(buildOutput);
229
+
230
+ db.Import(command.IdtPath);
231
+ }
232
+ catch (WixInvalidIdtException)
233
+ {
234
+ // If ValidateRows finds anything it doesn't like, it throws
235
+ importTable.ValidateRows();
236
+
237
+ // Otherwise we rethrow the InvalidIdt
238
+ throw;
239
+ }
240
+ }
241
+
242
+ // insert the rows via SQL query if this table contains object fields
243
+ if (hasBinaryColumn)
244
+ {
245
+ var query = new StringBuilder("SELECT ");
246
+
247
+ // Build the query for the view.
248
+ var firstColumn = true;
249
+ foreach (var columnDefinition in table.Definition.Columns)
250
+ {
251
+ if (columnDefinition.Unreal)
252
+ {
253
+ continue;
254
}
255
209
- // insert the rows via SQL query if this table contains object fields
210
- if (hasBinaryColumn)
256
+ if (!firstColumn)
257
{
212
- StringBuilder query = new StringBuilder("SELECT ");
258
+ query.Append(",");
259
+ }
260
214
- // Build the query for the view.
215
- bool firstColumn = true;
216
- foreach (ColumnDefinition columnDefinition in table.Definition.Columns)
261
+ query.AppendFormat(" `{0}`", columnDefinition.Name);
262
+ firstColumn = false;
263
+ }
264
+ query.AppendFormat(" FROM `{0}`", table.Name);
265
+
266
+ using (var tableView = db.OpenExecuteView(query.ToString()))
267
+ {
268
+ // Import each row containing a stream
269
+ foreach (var row in table.Rows)
270
+ {
271
+ using (var record = new Record(table.Definition.Columns.Length))
272
{
218
- if (!firstColumn)
273
+ // Stream names are created by concatenating the name of the table with the values
274
+ // of the primary key (delimited by periods).
275
+ var streamName = new StringBuilder();
276
+
277
+ // the _Streams table doesn't prepend the table name (or a period)
278
+ if ("_Streams" != table.Name)
279
{
220
- query.Append(",");
280
+ streamName.Append(table.Name);
281
}
282
223
- query.AppendFormat(" `{0}`", columnDefinition.Name);
224
- firstColumn = false;
225
- }
226
- query.AppendFormat(" FROM `{0}`", table.Name);
283
+ var needStream = false;
284
228
- using (View tableView = db.OpenExecuteView(query.ToString()))
229
- {
230
- // Import each row containing a stream
231
- foreach (Row row in table.Rows)
285
+ for (var i = 0; i < table.Definition.Columns.Length; i++)
286
{
233
- using (Record record = new Record(table.Definition.Columns.Length))
287
+ var columnDefinition = table.Definition.Columns[i];
288
+
289
+ if (columnDefinition.Unreal)
290
{
235
- StringBuilder streamName = new StringBuilder();
236
- bool needStream = false;
291
+ continue;
292
+ }
293
+
294
+ switch (columnDefinition.Type)
295
+ {
296
+ case ColumnType.Localized:
297
+ case ColumnType.Preserved:
298
+ case ColumnType.String:
299
+ var str = row.FieldAsString(i);
300
238
- // the _Streams table doesn't prepend the table name (or a period)
239
- if ("_Streams" != table.Name)
240
- {
241
- streamName.Append(table.Name);
242
- }
301
+ if (columnDefinition.PrimaryKey)
302
+ {
303
+ if (0 < streamName.Length)
304
+ {
305
+ streamName.Append(".");
306
+ }
307
+
308
+ streamName.Append(str);
309
+ }
310
244
- for (int i = 0; i < table.Definition.Columns.Length; i++)
245
- {
246
- ColumnDefinition columnDefinition = table.Definition.Columns[i];
311
+ record.SetString(i + 1, str);
312
+ break;
313
+ case ColumnType.Number:
314
+ record.SetInteger(i + 1, row.FieldAsInteger(i));
315
+ break;
316
248
- switch (columnDefinition.Type)
317
+ case ColumnType.Object:
318
+ if (null != row[i])
319
{
250
- case ColumnType.Localized:
251
- case ColumnType.Preserved:
252
- case ColumnType.String:
253
- if (columnDefinition.PrimaryKey)
320
+ needStream = true;
321
+ try
322
+ {
323
+ record.SetStream(i + 1, row.FieldAsString(i));
324
+ }
325
+ catch (Win32Exception e)
326
+ {
327
+ if (0xA1 == e.NativeErrorCode) // ERROR_BAD_PATHNAME
328
{
255
- if (0 < streamName.Length)
256
- {
257
- streamName.Append(".");
258
- }
259
- streamName.Append((string)row[i]);
329
+ throw new WixException(ErrorMessages.FileNotFound(row.SourceLineNumbers, row.FieldAsString(i)));
330
}
261
-
262
- record.SetString(i + 1, (string)row[i]);
263
- break;
264
- case ColumnType.Number:
265
- record.SetInteger(i + 1, Convert.ToInt32(row[i], CultureInfo.InvariantCulture));
266
- break;
267
- case ColumnType.Object:
268
- if (null != row[i])
331
+ else
332
{
270
- needStream = true;
271
- try
272
- {
273
- record.SetStream(i + 1, (string)row[i]);
274
- }
275
- catch (Win32Exception e)
276
- {
277
- if (0xA1 == e.NativeErrorCode) // ERROR_BAD_PATHNAME
278
- {
279
- throw new WixException(ErrorMessages.FileNotFound(row.SourceLineNumbers, (string)row[i]));
280
- }
281
- else
282
- {
283
- throw new WixException(ErrorMessages.Win32Exception(e.NativeErrorCode, e.Message));
284
- }
285
- }
333
+ throw new WixException(ErrorMessages.Win32Exception(e.NativeErrorCode, e.Message));
334
}
287
- break;
335
+ }
336
}
289
- }
290
-
291
- // stream names are created by concatenating the name of the table with the values
292
- // of the primary key (delimited by periods)
293
- // check for a stream name that is more than 62 characters long (the maximum allowed length)
294
- if (needStream && MsiInterop.MsiMaxStreamNameLength < streamName.Length)
295
- {
296
- this.Messaging.Write(ErrorMessages.StreamNameTooLong(row.SourceLineNumbers, table.Name, streamName.ToString(), streamName.Length));
297
- }
298
- else // add the row to the database
299
- {
300
- tableView.Modify(ModifyView.Assign, record);
301
- }
337
+ break;
338
}
339
}
304
- }
305
-
306
- // Remove rows from the _Streams table for wixpdbs.
307
- if ("_Streams" == table.Name)
308
- {
309
- table.Rows.Clear();
310
- }
311
- }
312
- }
340
314
- // Insert substorages (usually transforms inside a patch or instance transforms in a package).
315
- if (0 < this.Output.SubStorages.Count)
316
- {
317
- using (View storagesView = new View(db, "SELECT `Name`, `Data` FROM `_Storages`"))
318
- {
319
- foreach (SubStorage subStorage in this.Output.SubStorages)
320
- {
321
- string transformFile = Path.Combine(this.IntermediateFolder, String.Concat(subStorage.Name, ".mst"));
322
-
323
- // Bind the transform.
324
- this.BindTransform(subStorage.Data, transformFile);
325
-
326
- if (this.Messaging.EncounteredError)
341
+ // check for a stream name that is more than 62 characters long (the maximum allowed length)
342
+ if (needStream && MsiInterop.MsiMaxStreamNameLength < streamName.Length)
343
{
328
- continue;
344
+ this.Messaging.Write(ErrorMessages.StreamNameTooLong(row.SourceLineNumbers, table.Name, streamName.ToString(), streamName.Length));
345
}
330
-
331
- // add the storage
332
- using (Record record = new Record(2))
346
+ else // add the row to the database
347
{
334
- record.SetString(1, subStorage.Name);
335
- record.SetStream(2, transformFile);
336
- storagesView.Modify(ModifyView.Assign, record);
348
+ tableView.Modify(ModifyView.Assign, record);
349
}
350
}
351
}
352
}
353
342
- // We're good, commit the changes to the new database.
343
- db.Commit();
354
+ // Remove rows from the _Streams table for wixpdbs.
355
+ if ("_Streams" == table.Name)
356
+ {
357
+ table.Rows.Clear();
358
+ }
359
}
360
}
346
- catch (IOException e)
347
- {
348
- // TODO: this error message doesn't seem specific enough
349
- throw new WixException(ErrorMessages.FileNotFound(new SourceLineNumber(this.OutputPath), this.OutputPath), e);
350
- }
361
}
362
353
- private void BindTransform(WindowsInstallerData transform, string outputPath)
363
+ private void ImportSubStorages(Database db)
364
{
355
- var command = new BindTransformCommand();
356
- command.Messaging = this.Messaging;
357
- command.Extensions = this.Extensions;
358
- command.TempFilesLocation = this.IntermediateFolder;
359
- command.Transform = transform;
360
- command.OutputPath = outputPath;
361
- command.TableDefinitions = this.TableDefinitions;
362
- command.Execute();
365
+ if (0 < this.Data.SubStorages.Count)
366
+ {
367
+ using (var storagesView = new View(db, "SELECT `Name`, `Data` FROM `_Storages`"))
368
+ {
369
+ foreach (var subStorage in this.Data.SubStorages)
370
+ {
371
+ var transformFile = Path.Combine(this.IntermediateFolder, String.Concat(subStorage.Name, ".mst"));
372
+
373
+ // Bind the transform.
374
+ var command = new BindTransformCommand(this.Messaging, this.BackendHelper, this.Extensions, this.IntermediateFolder, subStorage.Data, transformFile, this.TableDefinitions);
375
+ command.Execute();
376
+
377
+ if (this.Messaging.EncounteredError)
378
+ {
379
+ continue;
380
+ }
381
+
382
+ // Add the storage to the database.
383
+ using (var record = new Record(2))
384
+ {
385
+ record.SetString(1, subStorage.Name);
386
+ record.SetStream(2, transformFile);
387
+ storagesView.Modify(ModifyView.Assign, record);
388
+ }
389
+ }
390
+ }
391
+ }
392
}
393
365
- private void SetDatabaseCodepage(Database db, int codepage, string idtDirectory)
394
+ private void SetDatabaseCodepage(Database db, int codepage, string idtFolder)
395
{
367
- // write out the _ForceCodepage IDT file
368
- var idtPath = Path.Combine(idtDirectory, "_ForceCodepage.idt");
396
+ // Write out the _ForceCodepage IDT file.
397
+ var idtPath = Path.Combine(idtFolder, "_ForceCodepage.idt");
398
using (var idtFile = new StreamWriter(idtPath, false, Encoding.ASCII))
399
{
400
idtFile.WriteLine(); // dummy column name record
@@ -377,14 +406,14 @@ namespace WixToolset.Core.WindowsInstaller.Bind
406
var trackId = this.BackendHelper.TrackFile(idtPath, TrackedFileType.Temporary);
407
this.GeneratedTemporaryFiles.Add(trackId);
408
380
- // try to import the table into the MSI
409
+ // Try to import the table into the MSI.
410
try
411
{
412
db.Import(idtPath);
413
}
414
catch (WixInvalidIdtException)
415
{
387
- // the IDT should be valid, so an invalid code page was given
416
+ // The IDT should be valid, so an invalid code page was given.
417
throw new WixException(ErrorMessages.IllegalCodepage(codepage));
418
}
419
}
src/WixToolset.Core.WindowsInstaller/Bind/GenerateTransformCommand.cs
new
+588
@@ -0,0 +1,588 @@
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.Globalization;
8
+ using WixToolset.Core.WindowsInstaller.Msi;
9
+ using WixToolset.Data;
10
+ using WixToolset.Data.Tuples;
11
+ using WixToolset.Data.WindowsInstaller;
12
+ using WixToolset.Data.WindowsInstaller.Rows;
13
+ using WixToolset.Extensibility;
14
+ using WixToolset.Extensibility.Services;
15
+
16
+ /// <summary>
17
+ /// Creates a transform by diffing two outputs.
18
+ /// </summary>
19
+ public sealed class GenerateTransformCommand
20
+ {
21
+ private const char sectionDelimiter = '/';
22
+ private readonly IMessaging messaging;
23
+ private SummaryInformationStreams transformSummaryInfo;
24
+
25
+ /// <summary>
26
+ /// Instantiates a new Differ class.
27
+ /// </summary>
28
+ public GenerateTransformCommand(IMessaging messaging, WindowsInstallerData targetOutput, WindowsInstallerData updatedOutput, bool showPedanticMessages)
29
+ {
30
+ this.messaging = messaging;
31
+ this.TargetOutput = targetOutput;
32
+ this.UpdatedOutput = updatedOutput;
33
+ this.ShowPedanticMessages = showPedanticMessages;
34
+ }
35
+
36
+ private WindowsInstallerData TargetOutput { get; }
37
+
38
+ private WindowsInstallerData UpdatedOutput { get; }
39
+
40
+ private TransformFlags ValidationFlags { get; }
41
+
42
+ /// <summary>
43
+ /// Gets or sets the option to show pedantic messages.
44
+ /// </summary>
45
+ /// <value>The option to show pedantic messages.</value>
46
+ private bool ShowPedanticMessages { get; }
47
+
48
+ /// <summary>
49
+ /// Gets or sets the option to suppress keeping special rows.
50
+ /// </summary>
51
+ /// <value>The option to suppress keeping special rows.</value>
52
+ private bool SuppressKeepingSpecialRows { get; }
53
+
54
+ /// <summary>
55
+ /// Gets or sets the flag to determine if all rows, even unchanged ones will be persisted in the output.
56
+ /// </summary>
57
+ /// <value>The option to keep all rows including unchanged rows.</value>
58
+ private bool PreserveUnchangedRows { get; }
59
+
60
+ public WindowsInstallerData Transform { get; private set; }
61
+
62
+ /// <summary>
63
+ /// Creates a transform by diffing two outputs.
64
+ /// </summary>
65
+ /// <param name="targetOutput">The target output.</param>
66
+ /// <param name="updatedOutput">The updated output.</param>
67
+ /// <param name="validationFlags"></param>
68
+ /// <returns>The transform.</returns>
69
+ public WindowsInstallerData Execute()
70
+ {
71
+ var targetOutput = this.TargetOutput;
72
+ var updatedOutput = this.UpdatedOutput;
73
+ var validationFlags = this.ValidationFlags;
74
+
75
+ var transform = new WindowsInstallerData(null)
76
+ {
77
+ Type = OutputType.Transform,
78
+ Codepage = updatedOutput.Codepage
79
+ };
80
+
81
+ this.transformSummaryInfo = new SummaryInformationStreams();
82
+
83
+ // compare the codepages
84
+ if (targetOutput.Codepage != updatedOutput.Codepage && 0 == (TransformFlags.ErrorChangeCodePage & validationFlags))
85
+ {
86
+ this.messaging.Write(ErrorMessages.OutputCodepageMismatch(targetOutput.SourceLineNumbers, targetOutput.Codepage, updatedOutput.Codepage));
87
+ if (null != updatedOutput.SourceLineNumbers)
88
+ {
89
+ this.messaging.Write(ErrorMessages.OutputCodepageMismatch2(updatedOutput.SourceLineNumbers));
90
+ }
91
+ }
92
+
93
+ // compare the output types
94
+ if (targetOutput.Type != updatedOutput.Type)
95
+ {
96
+ throw new WixException(ErrorMessages.OutputTypeMismatch(targetOutput.SourceLineNumbers, targetOutput.Type.ToString(), updatedOutput.Type.ToString()));
97
+ }
98
+
99
+ // compare the contents of the tables
100
+ foreach (var targetTable in targetOutput.Tables)
101
+ {
102
+ var updatedTable = updatedOutput.Tables[targetTable.Name];
103
+ var operation = TableOperation.None;
104
+
105
+ var rows = this.CompareTables(targetOutput, targetTable, updatedTable, out operation);
106
+
107
+ if (TableOperation.Drop == operation)
108
+ {
109
+ var droppedTable = transform.EnsureTable(targetTable.Definition);
110
+ droppedTable.Operation = TableOperation.Drop;
111
+ }
112
+ else if (TableOperation.None == operation)
113
+ {
114
+ var modified = transform.EnsureTable(updatedTable.Definition);
115
+ foreach (var row in rows)
116
+ {
117
+ modified.Rows.Add(row);
118
+ }
119
+ }
120
+ }
121
+
122
+ // added tables
123
+ foreach (var updatedTable in updatedOutput.Tables)
124
+ {
125
+ if (null == targetOutput.Tables[updatedTable.Name])
126
+ {
127
+ var addedTable = transform.EnsureTable(updatedTable.Definition);
128
+ addedTable.Operation = TableOperation.Add;
129
+
130
+ foreach (var updatedRow in updatedTable.Rows)
131
+ {
132
+ updatedRow.Operation = RowOperation.Add;
133
+ updatedRow.SectionId = sectionDelimiter + updatedRow.SectionId;
134
+ addedTable.Rows.Add(updatedRow);
135
+ }
136
+ }
137
+ }
138
+
139
+ // set summary information properties
140
+ if (!this.SuppressKeepingSpecialRows)
141
+ {
142
+ var summaryInfoTable = transform.Tables["_SummaryInformation"];
143
+ this.UpdateTransformSummaryInformationTable(summaryInfoTable, validationFlags);
144
+ }
145
+
146
+ this.Transform = transform;
147
+ return this.Transform;
148
+ }
149
+
150
+ /// <summary>
151
+ /// Add a row to the <paramref name="index"/> using the primary key.
152
+ /// </summary>
153
+ /// <param name="index">The indexed rows.</param>
154
+ /// <param name="row">The row to index.</param>
155
+ private void AddIndexedRow(Dictionary<string, Row> index, Row row)
156
+ {
157
+ var primaryKey = row.GetPrimaryKey();
158
+
159
+ if (null != primaryKey)
160
+ {
161
+ if (index.TryGetValue(primaryKey, out var collisionRow))
162
+ {
163
+ // Overriding WixActionRows have a primary key defined and take precedence in the index.
164
+ if (row is WixActionRow actionRow)
165
+ {
166
+ // If the current row is not overridable, see if the indexed row is.
167
+ if (!actionRow.Overridable)
168
+ {
169
+ if (collisionRow is WixActionRow indexedRow && indexedRow.Overridable)
170
+ {
171
+ // The indexed key is overridable and should be replaced.
172
+ index[primaryKey] = actionRow;
173
+ }
174
+ }
175
+
176
+ // If we got this far, the row does not need to be indexed.
177
+ return;
178
+ }
179
+
180
+ if (this.ShowPedanticMessages)
181
+ {
182
+ this.messaging.Write(ErrorMessages.DuplicatePrimaryKey(row.SourceLineNumbers, primaryKey, row.Table.Name));
183
+ }
184
+ }
185
+ else
186
+ {
187
+ index.Add(primaryKey, row);
188
+ }
189
+ }
190
+ else // use the string representation of the row as its primary key (it may not be unique)
191
+ {
192
+ // this is provided for compatibility with unreal tables with no primary key
193
+ // all real tables must specify at least one column as the primary key
194
+ primaryKey = row.ToString();
195
+ index[primaryKey] = row;
196
+ }
197
+ }
198
+
199
+ private bool CompareRows(Table targetTable, Row targetRow, Row updatedRow, out Row comparedRow)
200
+ {
201
+ comparedRow = null;
202
+
203
+ var keepRow = false;
204
+
205
+ if (null == targetRow ^ null == updatedRow)
206
+ {
207
+ if (null == targetRow)
208
+ {
209
+ updatedRow.Operation = RowOperation.Add;
210
+ comparedRow = updatedRow;
211
+ }
212
+ else if (null == updatedRow)
213
+ {
214
+ targetRow.Operation = RowOperation.Delete;
215
+ targetRow.SectionId += sectionDelimiter;
216
+
217
+ comparedRow = targetRow;
218
+ keepRow = true;
219
+ }
220
+ }
221
+ else // possibly modified
222
+ {
223
+ updatedRow.Operation = RowOperation.None;
224
+ if (!this.SuppressKeepingSpecialRows && "_SummaryInformation" == targetTable.Name)
225
+ {
226
+ // ignore rows that shouldn't be in a transform
227
+ if (Enum.IsDefined(typeof(SummaryInformation.Transform), (int)updatedRow[0]))
228
+ {
229
+ updatedRow.SectionId = targetRow.SectionId + sectionDelimiter + updatedRow.SectionId;
230
+ comparedRow = updatedRow;
231
+ keepRow = true;
232
+ }
233
+ }
234
+ else
235
+ {
236
+ if (this.PreserveUnchangedRows)
237
+ {
238
+ keepRow = true;
239
+ }
240
+
241
+ for (var i = 0; i < updatedRow.Fields.Length; i++)
242
+ {
243
+ var columnDefinition = updatedRow.Fields[i].Column;
244
+
245
+ if (columnDefinition.Unreal)
246
+ {
247
+ }
248
+ else if (!columnDefinition.PrimaryKey)
249
+ {
250
+ var modified = false;
251
+
252
+ if (i >= targetRow.Fields.Length)
253
+ {
254
+ columnDefinition.Added = true;
255
+ modified = true;
256
+ }
257
+ else if (ColumnType.Number == columnDefinition.Type && !columnDefinition.IsLocalizable)
258
+ {
259
+ if (null == targetRow[i] ^ null == updatedRow[i])
260
+ {
261
+ modified = true;
262
+ }
263
+ else if (null != targetRow[i] && null != updatedRow[i])
264
+ {
265
+ modified = (targetRow.FieldAsInteger(i) != updatedRow.FieldAsInteger(i));
266
+ }
267
+ }
268
+ else if (ColumnType.Preserved == columnDefinition.Type)
269
+ {
270
+ updatedRow.Fields[i].PreviousData = targetRow.FieldAsString(i);
271
+
272
+ // keep rows containing preserved fields so the historical data is available to the binder
273
+ keepRow = !this.SuppressKeepingSpecialRows;
274
+ }
275
+ else if (ColumnType.Object == columnDefinition.Type)
276
+ {
277
+ var targetObjectField = (ObjectField)targetRow.Fields[i];
278
+ var updatedObjectField = (ObjectField)updatedRow.Fields[i];
279
+
280
+ updatedObjectField.PreviousEmbeddedFileIndex = targetObjectField.EmbeddedFileIndex;
281
+ updatedObjectField.PreviousBaseUri = targetObjectField.BaseUri;
282
+
283
+ // always keep a copy of the previous data even if they are identical
284
+ // This makes diff.wixmst clean and easier to control patch logic
285
+ updatedObjectField.PreviousData = (string)targetObjectField.Data;
286
+
287
+ // always remember the unresolved data for target build
288
+ updatedObjectField.UnresolvedPreviousData = targetObjectField.UnresolvedData;
289
+
290
+ // keep rows containing object fields so the files can be compared in the binder
291
+ keepRow = !this.SuppressKeepingSpecialRows;
292
+ }
293
+ else
294
+ {
295
+ modified = (targetRow.FieldAsString(i) != updatedRow.FieldAsString(i));
296
+ }
297
+
298
+ if (modified)
299
+ {
300
+ if (null != updatedRow.Fields[i].PreviousData)
301
+ {
302
+ updatedRow.Fields[i].PreviousData = targetRow.FieldAsString(i);
303
+ }
304
+
305
+ updatedRow.Fields[i].Modified = true;
306
+ updatedRow.Operation = RowOperation.Modify;
307
+ keepRow = true;
308
+ }
309
+ }
310
+ }
311
+
312
+ if (keepRow)
313
+ {
314
+ comparedRow = updatedRow;
315
+ comparedRow.SectionId = targetRow.SectionId + sectionDelimiter + updatedRow.SectionId;
316
+ }
317
+ }
318
+ }
319
+
320
+ return keepRow;
321
+ }
322
+
323
+ private List<Row> CompareTables(WindowsInstallerData targetOutput, Table targetTable, Table updatedTable, out TableOperation operation)
324
+ {
325
+ var rows = new List<Row>();
326
+ operation = TableOperation.None;
327
+
328
+ // dropped tables
329
+ if (null == updatedTable ^ null == targetTable)
330
+ {
331
+ if (null == targetTable)
332
+ {
333
+ operation = TableOperation.Add;
334
+ rows.AddRange(updatedTable.Rows);
335
+ }
336
+ else if (null == updatedTable)
337
+ {
338
+ operation = TableOperation.Drop;
339
+ }
340
+ }
341
+ else // possibly modified tables
342
+ {
343
+ var updatedPrimaryKeys = new Dictionary<string, Row>();
344
+ var targetPrimaryKeys = new Dictionary<string, Row>();
345
+
346
+ // compare the table definitions
347
+ if (0 != targetTable.Definition.CompareTo(updatedTable.Definition))
348
+ {
349
+ // continue to the next table; may be more mismatches
350
+ this.messaging.Write(ErrorMessages.DatabaseSchemaMismatch(targetOutput.SourceLineNumbers, targetTable.Name));
351
+ }
352
+ else
353
+ {
354
+ this.IndexPrimaryKeys(targetTable, targetPrimaryKeys, updatedTable, updatedPrimaryKeys);
355
+
356
+ // diff the target and updated rows
357
+ foreach (var targetPrimaryKeyEntry in targetPrimaryKeys)
358
+ {
359
+ var targetPrimaryKey = targetPrimaryKeyEntry.Key;
360
+ var targetRow = targetPrimaryKeyEntry.Value;
361
+ updatedPrimaryKeys.TryGetValue(targetPrimaryKey, out var updatedRow);
362
+
363
+ var keepRow = this.CompareRows(targetTable, targetRow, updatedRow, out var compared);
364
+
365
+ if (keepRow)
366
+ {
367
+ rows.Add(compared);
368
+ }
369
+ }
370
+
371
+ // find the inserted rows
372
+ foreach (var updatedPrimaryKeyEntry in updatedPrimaryKeys)
373
+ {
374
+ var updatedPrimaryKey = updatedPrimaryKeyEntry.Key;
375
+
376
+ if (!targetPrimaryKeys.ContainsKey(updatedPrimaryKey))
377
+ {
378
+ var updatedRow = updatedPrimaryKeyEntry.Value;
379
+
380
+ updatedRow.Operation = RowOperation.Add;
381
+ updatedRow.SectionId = sectionDelimiter + updatedRow.SectionId;
382
+ rows.Add(updatedRow);
383
+ }
384
+ }
385
+ }
386
+ }
387
+
388
+ return rows;
389
+ }
390
+
391
+ private void IndexPrimaryKeys(Table targetTable, Dictionary<string, Row> targetPrimaryKeys, Table updatedTable, Dictionary<string, Row> updatedPrimaryKeys)
392
+ {
393
+ // index the target rows
394
+ foreach (var row in targetTable.Rows)
395
+ {
396
+ this.AddIndexedRow(targetPrimaryKeys, row);
397
+
398
+ if ("Property" == targetTable.Name)
399
+ {
400
+ var id = row.FieldAsString(0);
401
+
402
+ if ("ProductCode" == id)
403
+ {
404
+ this.transformSummaryInfo.TargetProductCode = row.FieldAsString(1);
405
+
406
+ if ("*" == this.transformSummaryInfo.TargetProductCode)
407
+ {
408
+ this.messaging.Write(ErrorMessages.ProductCodeInvalidForTransform(row.SourceLineNumbers));
409
+ }
410
+ }
411
+ else if ("ProductVersion" == id)
412
+ {
413
+ this.transformSummaryInfo.TargetProductVersion = row.FieldAsString(1);
414
+ }
415
+ else if ("UpgradeCode" == id)
416
+ {
417
+ this.transformSummaryInfo.TargetUpgradeCode = row.FieldAsString(1);
418
+ }
419
+ }
420
+ else if ("_SummaryInformation" == targetTable.Name)
421
+ {
422
+ var id = row.FieldAsInteger(0);
423
+
424
+ if (1 == id) // PID_CODEPAGE
425
+ {
426
+ this.transformSummaryInfo.TargetSummaryInfoCodepage = row.FieldAsString(1);
427
+ }
428
+ else if (7 == id) // PID_TEMPLATE
429
+ {
430
+ this.transformSummaryInfo.TargetPlatformAndLanguage = row.FieldAsString(1);
431
+ }
432
+ else if (14 == id) // PID_PAGECOUNT
433
+ {
434
+ this.transformSummaryInfo.TargetMinimumVersion = row.FieldAsString(1);
435
+ }
436
+ }
437
+ }
438
+
439
+ // index the updated rows
440
+ foreach (var row in updatedTable.Rows)
441
+ {
442
+ this.AddIndexedRow(updatedPrimaryKeys, row);
443
+
444
+ if ("Property" == updatedTable.Name)
445
+ {
446
+ var id = row.FieldAsString(0);
447
+
448
+ if ("ProductCode" == id)
449
+ {
450
+ this.transformSummaryInfo.UpdatedProductCode = row.FieldAsString(1);
451
+
452
+ if ("*" == this.transformSummaryInfo.UpdatedProductCode)
453
+ {
454
+ this.messaging.Write(ErrorMessages.ProductCodeInvalidForTransform(row.SourceLineNumbers));
455
+ }
456
+ }
457
+ else if ("ProductVersion" == id)
458
+ {
459
+ this.transformSummaryInfo.UpdatedProductVersion = row.FieldAsString(1);
460
+ }
461
+ }
462
+ else if ("_SummaryInformation" == updatedTable.Name)
463
+ {
464
+ var id = row.FieldAsInteger(0);
465
+
466
+ if (1 == id) // PID_CODEPAGE
467
+ {
468
+ this.transformSummaryInfo.UpdatedSummaryInfoCodepage = row.FieldAsString(1);
469
+ }
470
+ else if (7 == id) // PID_TEMPLATE
471
+ {
472
+ this.transformSummaryInfo.UpdatedPlatformAndLanguage = row.FieldAsString(1);
473
+ }
474
+ else if (14 == id) // PID_PAGECOUNT
475
+ {
476
+ this.transformSummaryInfo.UpdatedMinimumVersion = row.FieldAsString(1);
477
+ }
478
+ }
479
+ }
480
+ }
481
+
482
+ private void UpdateTransformSummaryInformationTable(Table summaryInfoTable, TransformFlags validationFlags)
483
+ {
484
+ // calculate the minimum version of MSI required to process the transform
485
+ var minimumVersion = 100;
486
+
487
+ if (Int32.TryParse(this.transformSummaryInfo.TargetMinimumVersion, out var targetMin) && Int32.TryParse(this.transformSummaryInfo.UpdatedMinimumVersion, out var updatedMin))
488
+ {
489
+ minimumVersion = Math.Max(targetMin, updatedMin);
490
+ }
491
+
492
+ var summaryRows = new Dictionary<int, Row>(summaryInfoTable.Rows.Count);
493
+
494
+ foreach (var row in summaryInfoTable.Rows)
495
+ {
496
+ var id = row.FieldAsInteger(0);
497
+
498
+ summaryRows[id] = row;
499
+
500
+ if ((int)SummaryInformation.Transform.CodePage == id)
501
+ {
502
+ row.Fields[1].Data = this.transformSummaryInfo.UpdatedSummaryInfoCodepage;
503
+ row.Fields[1].PreviousData = this.transformSummaryInfo.TargetSummaryInfoCodepage;
504
+ }
505
+ else if ((int)SummaryInformation.Transform.TargetPlatformAndLanguage == id)
506
+ {
507
+ row[1] = this.transformSummaryInfo.TargetPlatformAndLanguage;
508
+ }
509
+ else if ((int)SummaryInformation.Transform.UpdatedPlatformAndLanguage == id)
510
+ {
511
+ row[1] = this.transformSummaryInfo.UpdatedPlatformAndLanguage;
512
+ }
513
+ else if ((int)SummaryInformation.Transform.ProductCodes == id)
514
+ {
515
+ row[1] = String.Concat(this.transformSummaryInfo.TargetProductCode, this.transformSummaryInfo.TargetProductVersion, ';', this.transformSummaryInfo.UpdatedProductCode, this.transformSummaryInfo.UpdatedProductVersion, ';', this.transformSummaryInfo.TargetUpgradeCode);
516
+ }
517
+ else if ((int)SummaryInformation.Transform.InstallerRequirement == id)
518
+ {
519
+ row[1] = minimumVersion.ToString(CultureInfo.InvariantCulture);
520
+ }
521
+ else if ((int)SummaryInformation.Transform.Security == id)
522
+ {
523
+ row[1] = "4";
524
+ }
525
+ }
526
+
527
+ if (!summaryRows.ContainsKey((int)SummaryInformation.Transform.TargetPlatformAndLanguage))
528
+ {
529
+ var summaryRow = summaryInfoTable.CreateRow(null);
530
+ summaryRow[0] = (int)SummaryInformation.Transform.TargetPlatformAndLanguage;
531
+ summaryRow[1] = this.transformSummaryInfo.TargetPlatformAndLanguage;
532
+ }
533
+
534
+ if (!summaryRows.ContainsKey((int)SummaryInformation.Transform.UpdatedPlatformAndLanguage))
535
+ {
536
+ var summaryRow = summaryInfoTable.CreateRow(null);
537
+ summaryRow[0] = (int)SummaryInformation.Transform.UpdatedPlatformAndLanguage;
538
+ summaryRow[1] = this.transformSummaryInfo.UpdatedPlatformAndLanguage;
539
+ }
540
+
541
+ if (!summaryRows.ContainsKey((int)SummaryInformation.Transform.ValidationFlags))
542
+ {
543
+ var summaryRow = summaryInfoTable.CreateRow(null);
544
+ summaryRow[0] = (int)SummaryInformation.Transform.ValidationFlags;
545
+ summaryRow[1] = ((int)validationFlags).ToString(CultureInfo.InvariantCulture);
546
+ }
547
+
548
+ if (!summaryRows.ContainsKey((int)SummaryInformation.Transform.InstallerRequirement))
549
+ {
550
+ var summaryRow = summaryInfoTable.CreateRow(null);
551
+ summaryRow[0] = (int)SummaryInformation.Transform.InstallerRequirement;
552
+ summaryRow[1] = minimumVersion.ToString(CultureInfo.InvariantCulture);
553
+ }
554
+
555
+ if (!summaryRows.ContainsKey((int)SummaryInformation.Transform.Security))
556
+ {
557
+ var summaryRow = summaryInfoTable.CreateRow(null);
558
+ summaryRow[0] = (int)SummaryInformation.Transform.Security;
559
+ summaryRow[1] = "4";
560
+ }
561
+ }
562
+
563
+ private class SummaryInformationStreams
564
+ {
565
+ public string TargetSummaryInfoCodepage { get; set; }
566
+
567
+ public string TargetPlatformAndLanguage { get; set; }
568
+
569
+ public string TargetProductCode { get; set; }
570
+
571
+ public string TargetProductVersion { get; set; }
572
+
573
+ public string TargetUpgradeCode { get; set; }
574
+
575
+ public string TargetMinimumVersion { get; set; }
576
+
577
+ public string UpdatedSummaryInfoCodepage { get; set; }
578
+
579
+ public string UpdatedPlatformAndLanguage { get; set; }
580
+
581
+ public string UpdatedProductCode { get; set; }
582
+
583
+ public string UpdatedProductVersion { get; set; }
584
+
585
+ public string UpdatedMinimumVersion { get; set; }
586
+ }
587
+ }
588
+}
src/WixToolset.Core.WindowsInstaller/Bind/GetFileFacadesCommand.cs
+1
-1
@@ -132,7 +132,7 @@ namespace WixToolset.Core.WindowsInstaller.Bind
132
file.SymbolPaths = String.Concat(file.SymbolPaths, ";", row.SymbolPaths);
133
}
134
135
-#if REVISIT_FOR_PATCHING
135
+#if TODO_PATCHING
136
Field field = row.Fields[2];
137
if (null != field.PreviousData)
138
{
src/WixToolset.Core.WindowsInstaller/Bind/GetFileFacadesFromTransforms.cs
new
+585
@@ -0,0 +1,585 @@
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.Diagnostics;
8
+ using System.IO;
9
+ using System.Linq;
10
+ using WixToolset.Core.Bind;
11
+ using WixToolset.Data;
12
+ using WixToolset.Data.Tuples;
13
+ using WixToolset.Data.WindowsInstaller;
14
+ using WixToolset.Data.WindowsInstaller.Rows;
15
+ using WixToolset.Extensibility;
16
+ using WixToolset.Extensibility.Services;
17
+
18
+ internal class GetFileFacadesFromTransforms
19
+ {
20
+ public GetFileFacadesFromTransforms(IMessaging messaging, IEnumerable<SubStorage> subStorages, TableDefinitionCollection tableDefinitions)
21
+ {
22
+ this.Messaging = messaging;
23
+ this.SubStorages = subStorages;
24
+ this.TableDefinitions = tableDefinitions;
25
+ }
26
+
27
+ public IEnumerable<IFileSystemExtension> Extensions { get; }
28
+
29
+ private IMessaging Messaging { get; }
30
+
31
+ public IEnumerable<SubStorage> SubStorages { get; }
32
+
33
+ private TableDefinitionCollection TableDefinitions { get; }
34
+
35
+ public IEnumerable<FileFacade> FileFacades { get; private set; }
36
+
37
+ public void Execute()
38
+ {
39
+ var allFileRows = new List<FileFacade>();
40
+ var copyToPatch = (allFileRows != null);
41
+#if TODO_PATCHING
42
+ var patchMediaRows = new RowDictionary<MediaRow>();
43
+
44
+ var patchMediaFileRows = new Dictionary<int, RowDictionary<WixFileRow>>();
45
+
46
+ var patchActualFileTable = this.Output.EnsureTable(this.TableDefinitions["File"]);
47
+ var patchFileTable = this.Output.EnsureTable(this.TableDefinitions["WixFile"]);
48
+
49
+ if (copyFromPatch)
50
+ {
51
+ // index patch files by diskId+fileId
52
+ foreach (WixFileRow patchFileRow in patchFileTable.Rows)
53
+ {
54
+ int diskId = patchFileRow.DiskId;
55
+ if (!patchMediaFileRows.TryGetValue(diskId, out var mediaFileRows))
56
+ {
57
+ mediaFileRows = new RowDictionary<WixFileRow>();
58
+ patchMediaFileRows.Add(diskId, mediaFileRows);
59
+ }
60
+
61
+ mediaFileRows.Add(patchFileRow);
62
+ }
63
+
64
+ var patchMediaTable = this.Output.EnsureTable(this.TableDefinitions["Media"]);
65
+ patchMediaRows = new RowDictionary<MediaRow>(patchMediaTable);
66
+ }
67
+
68
+ // Index paired transforms by name without their "#" prefix.
69
+ var pairedTransforms = this.SubStorages.Where(s => s.Name.StartsWith("#")).ToDictionary(s => s.Name.Substring(1), s => s.Data);
70
+
71
+ foreach (var substorage in this.SubStorages)
72
+ {
73
+ if (substorage.Name.StartsWith("#"))
74
+ {
75
+ // Skip paired transforms.
76
+ continue;
77
+ }
78
+
79
+ var mainTransform = substorage.Data;
80
+ var mainWixFiles = new RowDictionary<WixFileRow>(mainTransform.Tables["WixFile"]);
81
+ var mainMsiFileHashIndex = new RowDictionary<Row>(mainTransform.Tables["MsiFileHash"]);
82
+
83
+ var mainFileTable = mainTransform.Tables["File"];
84
+ var pairedTransform = pairedTransforms[substorage.Name];
85
+
86
+ // copy Media.LastSequence and index the MsiFileHash table if it exists.
87
+ if (copyFromPatch)
88
+ {
89
+ var pairedMediaTable = pairedTransform.Tables["Media"];
90
+ foreach (MediaRow pairedMediaRow in pairedMediaTable.Rows)
91
+ {
92
+ var patchMediaRow = patchMediaRows.Get(pairedMediaRow.DiskId);
93
+ pairedMediaRow.Fields[1] = patchMediaRow.Fields[1];
94
+ }
95
+
96
+ if (null != mainMsiFileHashTable)
97
+ {
98
+ mainMsiFileHashIndex = new RowDictionary<Row>(mainMsiFileHashTable);
99
+ }
100
+
101
+ // Validate file row changes for keypath-related issues
102
+ this.ValidateFileRowChanges(mainTransform);
103
+ }
104
+
105
+ if (null == mainFileTable)
106
+ {
107
+ continue;
108
+ }
109
+
110
+ // Index File table of pairedTransform
111
+ var pairedFileRows = new RowDictionary<FileRow>(pairedTransform.Tables["File"]);
112
+
113
+ foreach (FileRow mainFileRow in mainFileTable.Rows)
114
+ {
115
+ if (RowOperation.Delete == mainFileRow.Operation)
116
+ {
117
+ continue;
118
+ }
119
+ else if (RowOperation.None == mainFileRow.Operation)
120
+ {
121
+ continue;
122
+ }
123
+
124
+ var mainWixFileRow = mainWixFiles.Get(mainFileRow.File);
125
+
126
+ if (copyToPatch) // when copying to the patch, we need compare the underlying files and include all file changes.
127
+ {
128
+ var objectField = (ObjectField)mainWixFileRow.Fields[6];
129
+ var pairedFileRow = pairedFileRows.Get(mainFileRow.File);
130
+
131
+ // If the file is new, we always need to add it to the patch.
132
+ if (mainFileRow.Operation != RowOperation.Add)
133
+ {
134
+ // If PreviousData doesn't exist, target and upgrade layout point to the same location. No need to compare.
135
+ if (null == objectField.PreviousData)
136
+ {
137
+ if (mainFileRow.Operation == RowOperation.None)
138
+ {
139
+ continue;
140
+ }
141
+ }
142
+ else
143
+ {
144
+ // TODO: should this entire condition be placed in the binder file manager?
145
+ if ((0 == (PatchAttributeType.Ignore & mainWixFileRow.PatchAttributes)) &&
146
+ !this.CompareFiles(objectField.PreviousData.ToString(), objectField.Data.ToString()))
147
+ {
148
+ // If the file is different, we need to mark the mainFileRow and pairedFileRow as modified.
149
+ mainFileRow.Operation = RowOperation.Modify;
150
+ if (null != pairedFileRow)
151
+ {
152
+ // Always patch-added, but never non-compressed.
153
+ pairedFileRow.Attributes |= WindowsInstallerConstants.MsidbFileAttributesPatchAdded;
154
+ pairedFileRow.Attributes &= ~WindowsInstallerConstants.MsidbFileAttributesNoncompressed;
155
+ pairedFileRow.Fields[6].Modified = true;
156
+ pairedFileRow.Operation = RowOperation.Modify;
157
+ }
158
+ }
159
+ else
160
+ {
161
+ // The File is same. We need mark all the attributes as unchanged.
162
+ mainFileRow.Operation = RowOperation.None;
163
+ foreach (var field in mainFileRow.Fields)
164
+ {
165
+ field.Modified = false;
166
+ }
167
+
168
+ if (null != pairedFileRow)
169
+ {
170
+ pairedFileRow.Attributes &= ~WindowsInstallerConstants.MsidbFileAttributesPatchAdded;
171
+ pairedFileRow.Fields[6].Modified = false;
172
+ pairedFileRow.Operation = RowOperation.None;
173
+ }
174
+ continue;
175
+ }
176
+ }
177
+ }
178
+ else if (null != pairedFileRow) // RowOperation.Add
179
+ {
180
+ // Always patch-added, but never non-compressed.
181
+ pairedFileRow.Attributes |= WindowsInstallerConstants.MsidbFileAttributesPatchAdded;
182
+ pairedFileRow.Attributes &= ~WindowsInstallerConstants.MsidbFileAttributesNoncompressed;
183
+ pairedFileRow.Fields[6].Modified = true;
184
+ pairedFileRow.Operation = RowOperation.Add;
185
+ }
186
+ }
187
+
188
+ // index patch files by diskId+fileId
189
+ int diskId = mainWixFileRow.DiskId;
190
+
191
+ if (!patchMediaFileRows.TryGetValue(diskId, out var mediaFileRows))
192
+ {
193
+ mediaFileRows = new RowDictionary<WixFileRow>();
194
+ patchMediaFileRows.Add(diskId, mediaFileRows);
195
+ }
196
+
197
+ var fileId = mainFileRow.File;
198
+ var patchFileRow = mediaFileRows.Get(fileId);
199
+ if (copyToPatch)
200
+ {
201
+ if (null == patchFileRow)
202
+ {
203
+ var patchActualFileRow = (FileRow)patchFileTable.CreateRow(mainFileRow.SourceLineNumbers);
204
+ patchActualFileRow.CopyFrom(mainFileRow);
205
+
206
+ patchFileRow = (WixFileRow)patchFileTable.CreateRow(mainFileRow.SourceLineNumbers);
207
+ patchFileRow.CopyFrom(mainWixFileRow);
208
+
209
+ mediaFileRows.Add(patchFileRow);
210
+
211
+ allFileRows.Add(new FileFacade(patchActualFileRow, patchFileRow, null)); // TODO: should we be passing along delta information? Probably, right?
212
+ }
213
+ else
214
+ {
215
+ // TODO: confirm the rest of data is identical?
216
+
217
+ // make sure Source is same. Otherwise we are silently ignoring a file.
218
+ if (0 != String.Compare(patchFileRow.Source, mainWixFileRow.Source, StringComparison.OrdinalIgnoreCase))
219
+ {
220
+ this.Messaging.Write(ErrorMessages.SameFileIdDifferentSource(mainFileRow.SourceLineNumbers, fileId, patchFileRow.Source, mainWixFileRow.Source));
221
+ }
222
+
223
+ // capture the previous file versions (and associated data) from this targeted instance of the baseline into the current filerow.
224
+ patchFileRow.AppendPreviousDataFrom(mainWixFileRow);
225
+ }
226
+ }
227
+ //else
228
+ //{
229
+ // // copy data from the patch back to the transform
230
+ // if (null != patchFileRow)
231
+ // {
232
+ // var pairedFileRow = pairedFileRows.Get(fileId);
233
+ // for (var i = 0; i < patchFileRow.Fields.Length; i++)
234
+ // {
235
+ // var patchValue = patchFileRow[i] == null ? String.Empty : patchFileRow.FieldAsString(i);
236
+ // var mainValue = mainFileRow[i] == null ? String.Empty : mainFileRow.FieldAsString(i);
237
+
238
+ // if (1 == i)
239
+ // {
240
+ // // File.Component_ changes should not come from the shared file rows
241
+ // // that contain the file information as each individual transform might
242
+ // // have different changes (or no changes at all).
243
+ // }
244
+ // // File.Attributes should not changed for binary deltas
245
+ // else if (6 == i)
246
+ // {
247
+ // if (null != patchFileRow.Patch)
248
+ // {
249
+ // // File.Attribute should not change for binary deltas
250
+ // pairedFileRow.Attributes = mainFileRow.Attributes;
251
+ // mainFileRow.Fields[i].Modified = false;
252
+ // }
253
+ // }
254
+ // // File.Sequence is updated in pairedTransform, not mainTransform
255
+ // else if (7 == i)
256
+ // {
257
+ // // file sequence is updated in Patch table instead of File table for delta patches
258
+ // if (null != patchFileRow.Patch)
259
+ // {
260
+ // pairedFileRow.Fields[i].Modified = false;
261
+ // }
262
+ // else
263
+ // {
264
+ // pairedFileRow[i] = patchFileRow[i];
265
+ // pairedFileRow.Fields[i].Modified = true;
266
+ // }
267
+ // mainFileRow.Fields[i].Modified = false;
268
+ // }
269
+ // else if (patchValue != mainValue)
270
+ // {
271
+ // mainFileRow[i] = patchFileRow[i];
272
+ // mainFileRow.Fields[i].Modified = true;
273
+ // if (mainFileRow.Operation == RowOperation.None)
274
+ // {
275
+ // mainFileRow.Operation = RowOperation.Modify;
276
+ // }
277
+ // }
278
+ // }
279
+
280
+ // // copy MsiFileHash row for this File
281
+ // if (!mainMsiFileHashIndex.TryGetValue(patchFileRow.File, out var patchHashRow))
282
+ // {
283
+ // patchHashRow = patchFileRow.Hash;
284
+ // }
285
+
286
+ // if (null != patchHashRow)
287
+ // {
288
+ // var mainHashTable = mainTransform.EnsureTable(this.TableDefinitions["MsiFileHash"]);
289
+ // var mainHashRow = mainHashTable.CreateRow(mainFileRow.SourceLineNumbers);
290
+ // for (var i = 0; i < patchHashRow.Fields.Length; i++)
291
+ // {
292
+ // mainHashRow[i] = patchHashRow[i];
293
+ // if (i > 1)
294
+ // {
295
+ // // assume all hash fields have been modified
296
+ // mainHashRow.Fields[i].Modified = true;
297
+ // }
298
+ // }
299
+
300
+ // // assume the MsiFileHash operation follows the File one
301
+ // mainHashRow.Operation = mainFileRow.Operation;
302
+ // }
303
+
304
+ // // copy MsiAssemblyName rows for this File
305
+ // List<Row> patchAssemblyNameRows = patchFileRow.AssemblyNames;
306
+ // if (null != patchAssemblyNameRows)
307
+ // {
308
+ // var mainAssemblyNameTable = mainTransform.EnsureTable(this.TableDefinitions["MsiAssemblyName"]);
309
+ // foreach (var patchAssemblyNameRow in patchAssemblyNameRows)
310
+ // {
311
+ // // Copy if there isn't an identical modified/added row already in the transform.
312
+ // var foundMatchingModifiedRow = false;
313
+ // foreach (var mainAssemblyNameRow in mainAssemblyNameTable.Rows)
314
+ // {
315
+ // if (RowOperation.None != mainAssemblyNameRow.Operation && mainAssemblyNameRow.GetPrimaryKey('/').Equals(patchAssemblyNameRow.GetPrimaryKey('/')))
316
+ // {
317
+ // foundMatchingModifiedRow = true;
318
+ // break;
319
+ // }
320
+ // }
321
+
322
+ // if (!foundMatchingModifiedRow)
323
+ // {
324
+ // var mainAssemblyNameRow = mainAssemblyNameTable.CreateRow(mainFileRow.SourceLineNumbers);
325
+ // for (var i = 0; i < patchAssemblyNameRow.Fields.Length; i++)
326
+ // {
327
+ // mainAssemblyNameRow[i] = patchAssemblyNameRow[i];
328
+ // }
329
+
330
+ // // assume value field has been modified
331
+ // mainAssemblyNameRow.Fields[2].Modified = true;
332
+ // mainAssemblyNameRow.Operation = mainFileRow.Operation;
333
+ // }
334
+ // }
335
+ // }
336
+
337
+ // // Add patch header for this file
338
+ // if (null != patchFileRow.Patch)
339
+ // {
340
+ // // Add the PatchFiles action automatically to the AdminExecuteSequence and InstallExecuteSequence tables.
341
+ // this.AddPatchFilesActionToSequenceTable(SequenceTable.AdminExecuteSequence, mainTransform, pairedTransform, mainFileRow);
342
+ // this.AddPatchFilesActionToSequenceTable(SequenceTable.InstallExecuteSequence, mainTransform, pairedTransform, mainFileRow);
343
+
344
+ // // Add to Patch table
345
+ // var patchTable = pairedTransform.EnsureTable(this.TableDefinitions["Patch"]);
346
+ // if (0 == patchTable.Rows.Count)
347
+ // {
348
+ // patchTable.Operation = TableOperation.Add;
349
+ // }
350
+
351
+ // var patchRow = patchTable.CreateRow(mainFileRow.SourceLineNumbers);
352
+ // patchRow[0] = patchFileRow.File;
353
+ // patchRow[1] = patchFileRow.Sequence;
354
+
355
+ // var patchFile = new FileInfo(patchFileRow.Source);
356
+ // patchRow[2] = (int)patchFile.Length;
357
+ // patchRow[3] = 0 == (PatchAttributeType.AllowIgnoreOnError & patchFileRow.PatchAttributes) ? 0 : 1;
358
+
359
+ // var streamName = patchTable.Name + "." + patchRow[0] + "." + patchRow[1];
360
+ // if (Msi.MsiInterop.MsiMaxStreamNameLength < streamName.Length)
361
+ // {
362
+ // streamName = "_" + Guid.NewGuid().ToString("D").ToUpperInvariant().Replace('-', '_');
363
+
364
+ // var patchHeadersTable = pairedTransform.EnsureTable(this.TableDefinitions["MsiPatchHeaders"]);
365
+ // if (0 == patchHeadersTable.Rows.Count)
366
+ // {
367
+ // patchHeadersTable.Operation = TableOperation.Add;
368
+ // }
369
+
370
+ // var patchHeadersRow = patchHeadersTable.CreateRow(mainFileRow.SourceLineNumbers);
371
+ // patchHeadersRow[0] = streamName;
372
+ // patchHeadersRow[1] = patchFileRow.Patch;
373
+ // patchRow[5] = streamName;
374
+ // patchHeadersRow.Operation = RowOperation.Add;
375
+ // }
376
+ // else
377
+ // {
378
+ // patchRow[4] = patchFileRow.Patch;
379
+ // }
380
+ // patchRow.Operation = RowOperation.Add;
381
+ // }
382
+ // }
383
+ // else
384
+ // {
385
+ // // TODO: throw because all transform rows should have made it into the patch
386
+ // }
387
+ //}
388
+ }
389
+ }
390
+#endif
391
+ this.FileFacades = allFileRows;
392
+ }
393
+
394
+ /// <summary>
395
+ /// Adds the PatchFiles action to the sequence table if it does not already exist.
396
+ /// </summary>
397
+ /// <param name="table">The sequence table to check or modify.</param>
398
+ /// <param name="mainTransform">The primary authoring transform.</param>
399
+ /// <param name="pairedTransform">The secondary patch transform.</param>
400
+ /// <param name="mainFileRow">The file row that contains information about the patched file.</param>
401
+ private void AddPatchFilesActionToSequenceTable(SequenceTable table, WindowsInstallerData mainTransform, WindowsInstallerData pairedTransform, Row mainFileRow)
402
+ {
403
+ var tableName = table.ToString();
404
+
405
+ // Find/add PatchFiles action (also determine sequence for it).
406
+ // Search mainTransform first, then pairedTransform (pairedTransform overrides).
407
+ var hasPatchFilesAction = false;
408
+ var installFilesSequence = 0;
409
+ var duplicateFilesSequence = 0;
410
+
411
+ TestSequenceTableForPatchFilesAction(
412
+ mainTransform.Tables[tableName],
413
+ ref hasPatchFilesAction,
414
+ ref installFilesSequence,
415
+ ref duplicateFilesSequence);
416
+ TestSequenceTableForPatchFilesAction(
417
+ pairedTransform.Tables[tableName],
418
+ ref hasPatchFilesAction,
419
+ ref installFilesSequence,
420
+ ref duplicateFilesSequence);
421
+ if (!hasPatchFilesAction)
422
+ {
423
+ WindowsInstallerStandard.TryGetStandardAction(tableName, "PatchFiles", out var patchFilesActionTuple);
424
+
425
+ var sequence = patchFilesActionTuple.Sequence;
426
+
427
+ // Test for default sequence value's appropriateness
428
+ if (installFilesSequence >= sequence || (0 != duplicateFilesSequence && duplicateFilesSequence <= sequence))
429
+ {
430
+ if (0 != duplicateFilesSequence)
431
+ {
432
+ if (duplicateFilesSequence < installFilesSequence)
433
+ {
434
+ throw new WixException(ErrorMessages.InsertInvalidSequenceActionOrder(mainFileRow.SourceLineNumbers, tableName, "InstallFiles", "DuplicateFiles", patchFilesActionTuple.Action));
435
+ }
436
+ else
437
+ {
438
+ sequence = (duplicateFilesSequence + installFilesSequence) / 2;
439
+ if (installFilesSequence == sequence || duplicateFilesSequence == sequence)
440
+ {
441
+ throw new WixException(ErrorMessages.InsertSequenceNoSpace(mainFileRow.SourceLineNumbers, tableName, "InstallFiles", "DuplicateFiles", patchFilesActionTuple.Action));
442
+ }
443
+ }
444
+ }
445
+ else
446
+ {
447
+ sequence = installFilesSequence + 1;
448
+ }
449
+ }
450
+
451
+ var sequenceTable = pairedTransform.EnsureTable(this.TableDefinitions[tableName]);
452
+ if (0 == sequenceTable.Rows.Count)
453
+ {
454
+ sequenceTable.Operation = TableOperation.Add;
455
+ }
456
+
457
+ var patchAction = sequenceTable.CreateRow(null);
458
+ patchAction[0] = patchFilesActionTuple.Action;
459
+ patchAction[1] = patchFilesActionTuple.Condition;
460
+ patchAction[2] = sequence;
461
+ patchAction.Operation = RowOperation.Add;
462
+ }
463
+ }
464
+
465
+ /// <summary>
466
+ /// Tests sequence table for PatchFiles and associated actions
467
+ /// </summary>
468
+ /// <param name="sequenceTable">The table to test.</param>
469
+ /// <param name="hasPatchFilesAction">Set to true if PatchFiles action is found. Left unchanged otherwise.</param>
470
+ /// <param name="installFilesSequence">Set to sequence value of InstallFiles action if found. Left unchanged otherwise.</param>
471
+ /// <param name="duplicateFilesSequence">Set to sequence value of DuplicateFiles action if found. Left unchanged otherwise.</param>
472
+ private static void TestSequenceTableForPatchFilesAction(Table sequenceTable, ref bool hasPatchFilesAction, ref int installFilesSequence, ref int duplicateFilesSequence)
473
+ {
474
+ if (null != sequenceTable)
475
+ {
476
+ foreach (var row in sequenceTable.Rows)
477
+ {
478
+ var actionName = row.FieldAsString(0);
479
+ switch (actionName)
480
+ {
481
+ case "PatchFiles":
482
+ hasPatchFilesAction = true;
483
+ break;
484
+
485
+ case "InstallFiles":
486
+ installFilesSequence = row.FieldAsInteger(2);
487
+ break;
488
+
489
+ case "DuplicateFiles":
490
+ duplicateFilesSequence = row.FieldAsInteger(2);
491
+ break;
492
+ }
493
+ }
494
+ }
495
+ }
496
+
497
+ /// <summary>
498
+ /// Signal a warning if a non-keypath file was changed in a patch without also changing the keypath file of the component.
499
+ /// </summary>
500
+ /// <param name="output">The output to validate.</param>
501
+ private void ValidateFileRowChanges(WindowsInstallerData transform)
502
+ {
503
+ var componentTable = transform.Tables["Component"];
504
+ var fileTable = transform.Tables["File"];
505
+
506
+ // There's no sense validating keypaths if the transform has no component or file table
507
+ if (componentTable == null || fileTable == null)
508
+ {
509
+ return;
510
+ }
511
+
512
+ var componentKeyPath = new Dictionary<string, string>(componentTable.Rows.Count);
513
+
514
+ // Index the Component table for non-directory & non-registry key paths.
515
+ foreach (var row in componentTable.Rows)
516
+ {
517
+ var keyPath = row.FieldAsString(5);
518
+ if (keyPath != null && 0 != (row.FieldAsInteger(3) & WindowsInstallerConstants.MsidbComponentAttributesRegistryKeyPath))
519
+ {
520
+ componentKeyPath.Add(row.FieldAsString(0), keyPath);
521
+ }
522
+ }
523
+
524
+ var componentWithChangedKeyPath = new Dictionary<string, string>();
525
+ var componentWithNonKeyPathChanged = new Dictionary<string, string>();
526
+ // Verify changes in the file table, now that file diffing has occurred
527
+ foreach (FileRow row in fileTable.Rows)
528
+ {
529
+ if (RowOperation.Modify != row.Operation)
530
+ {
531
+ continue;
532
+ }
533
+
534
+ var fileId = row.FieldAsString(0);
535
+ var componentId = row.FieldAsString(1);
536
+
537
+ // If this file is the keypath of a component
538
+ if (componentKeyPath.ContainsValue(fileId))
539
+ {
540
+ if (!componentWithChangedKeyPath.ContainsKey(componentId))
541
+ {
542
+ componentWithChangedKeyPath.Add(componentId, fileId);
543
+ }
544
+ }
545
+ else
546
+ {
547
+ if (!componentWithNonKeyPathChanged.ContainsKey(componentId))
548
+ {
549
+ componentWithNonKeyPathChanged.Add(componentId, fileId);
550
+ }
551
+ }
552
+ }
553
+
554
+ foreach (var componentFile in componentWithNonKeyPathChanged)
555
+ {
556
+ // Make sure all changes to non keypath files also had a change in the keypath.
557
+ if (!componentWithChangedKeyPath.ContainsKey(componentFile.Key) && componentKeyPath.TryGetValue(componentFile.Key, out var keyPath))
558
+ {
559
+ this.Messaging.Write(WarningMessages.UpdateOfNonKeyPathFile(componentFile.Value, componentFile.Key, keyPath));
560
+ }
561
+ }
562
+ }
563
+
564
+ private bool CompareFiles(string targetFile, string updatedFile)
565
+ {
566
+ bool? compared = null;
567
+ foreach (var extension in this.Extensions)
568
+ {
569
+ compared = extension.CompareFiles(targetFile, updatedFile);
570
+
571
+ if (compared.HasValue)
572
+ {
573
+ break;
574
+ }
575
+ }
576
+
577
+ if (!compared.HasValue)
578
+ {
579
+ throw new InvalidOperationException(); // TODO: something needs to be said here that none of the binder file managers returned a result.
580
+ }
581
+
582
+ return compared.Value;
583
+ }
584
+ }
585
+}
src/WixToolset.Core.WindowsInstaller/Bind/LoadTableDefinitionsCommand.cs
+18
-3
@@ -9,15 +9,22 @@ namespace WixToolset.Core.WindowsInstaller.Bind
9
using WixToolset.Data;
10
using WixToolset.Data.Tuples;
11
using WixToolset.Data.WindowsInstaller;
12
+ using WixToolset.Extensibility;
13
14
internal class LoadTableDefinitionsCommand
15
{
15
- public LoadTableDefinitionsCommand(IntermediateSection section) => this.Section = section;
16
-
17
- public TableDefinitionCollection TableDefinitions { get; private set; }
16
+ public LoadTableDefinitionsCommand(IntermediateSection section, IEnumerable<IWindowsInstallerBackendBinderExtension> backendExtensions)
17
+ {
18
+ this.Section = section;
19
+ this.BackendExtensions = backendExtensions;
20
+ }
21
22
private IntermediateSection Section { get; }
23
24
+ private IEnumerable<IWindowsInstallerBackendBinderExtension> BackendExtensions { get; }
25
+
26
+ public TableDefinitionCollection TableDefinitions { get; private set; }
27
+
28
public TableDefinitionCollection Execute()
29
{
30
var tableDefinitions = new TableDefinitionCollection(WindowsInstallerStandardInternal.GetTableDefinitions());
@@ -28,6 +35,14 @@ namespace WixToolset.Core.WindowsInstaller.Bind
35
tableDefinitions.Add(customTableDefinition);
36
}
37
38
+ foreach (var backendExtension in this.BackendExtensions)
39
+ {
40
+ foreach (var tableDefinition in backendExtension.TableDefinitions)
41
+ {
42
+ tableDefinitions.Add(tableDefinition);
43
+ }
44
+ }
45
+
46
this.TableDefinitions = tableDefinitions;
47
return this.TableDefinitions;
48
}
src/WixToolset.Core.WindowsInstaller/Bind/MergeModulesCommand.cs
+4
-4
@@ -277,7 +277,7 @@ namespace WixToolset.Core.WindowsInstaller.Bind
277
278
using (Record record = new Record(1))
279
{
280
- record.SetString(1, file.File.Id.Id);
280
+ record.SetString(1, file.Id);
281
view.Execute(record);
282
}
283
@@ -288,7 +288,7 @@ namespace WixToolset.Core.WindowsInstaller.Bind
288
throw new InvalidOperationException("Failed to fetch a File row from the database that was merged in from a module.");
289
}
290
291
- recordUpdate.SetInteger(1, file.File.Sequence);
291
+ recordUpdate.SetInteger(1, file.Sequence);
292
293
// Update the file attributes to match the compression specified
294
// on the Merge element or on the Package element.
@@ -300,12 +300,12 @@ namespace WixToolset.Core.WindowsInstaller.Bind
300
attributes = recordUpdate.GetInteger(2);
301
}
302
303
- if ((file.File.Attributes & FileTupleAttributes.Compressed) == FileTupleAttributes.Compressed)
303
+ if (file.Compressed)
304
{
305
attributes |= WindowsInstallerConstants.MsidbFileAttributesCompressed;
306
attributes &= ~WindowsInstallerConstants.MsidbFileAttributesNoncompressed;
307
}
308
- else if ((file.File.Attributes & FileTupleAttributes.Uncompressed) == FileTupleAttributes.Uncompressed)
308
+ else if (file.Uncompressed)
309
{
310
attributes |= WindowsInstallerConstants.MsidbFileAttributesNoncompressed;
311
attributes &= ~WindowsInstallerConstants.MsidbFileAttributesCompressed;
src/WixToolset.Core.WindowsInstaller/Bind/PatchTransform.cs
new
+246
@@ -0,0 +1,246 @@
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;
7
+ using System.Globalization;
8
+ using System.Text;
9
+ using System.Text.RegularExpressions;
10
+ using WixToolset.Data;
11
+ using WixToolset.Data.WindowsInstaller;
12
+ using WixToolset.Extensibility;
13
+
14
+ internal class PatchTransform
15
+ {
16
+ public PatchTransform(string baseline, WindowsInstallerData transform)
17
+ {
18
+ this.Baseline = baseline;
19
+ this.Transform = transform;
20
+ }
21
+
22
+ public string Baseline { get; }
23
+
24
+ public WindowsInstallerData Transform { get; }
25
+
26
+ /// <summary>
27
+ /// Validates that the differences in the transform are valid for patch transforms.
28
+ /// </summary>
29
+ public void Validate()
30
+ {
31
+#if TODO_PATCHING
32
+ // Changing the ProdocutCode in a patch transform is not recommended.
33
+ Table propertyTable = this.Transform.Tables["Property"];
34
+ if (null != propertyTable)
35
+ {
36
+ foreach (Row row in propertyTable.Rows)
37
+ {
38
+ // Only interested in modified rows; fast check.
39
+ if (RowOperation.Modify == row.Operation)
40
+ {
41
+ if (0 == String.CompareOrdinal("ProductCode", (string)row[0]))
42
+ {
43
+ this.OnMessage(WixWarnings.MajorUpgradePatchNotRecommended());
44
+ }
45
+ }
46
+ }
47
+ }
48
+
49
+ // If there is nothing in the component table we can return early because the remaining checks are component based.
50
+ Table componentTable = this.Transform.Tables["Component"];
51
+ if (null == componentTable)
52
+ {
53
+ return;
54
+ }
55
+
56
+ // Index Feature table row operations
57
+ Table featureTable = this.Transform.Tables["Feature"];
58
+ Table featureComponentsTable = this.Transform.Tables["FeatureComponents"];
59
+ Hashtable featureOps = null;
60
+ if (null != featureTable)
61
+ {
62
+ int capacity = featureTable.Rows.Count;
63
+ featureOps = new Hashtable(capacity);
64
+
65
+ foreach (Row row in featureTable.Rows)
66
+ {
67
+ featureOps[(string)row[0]] = row.Operation;
68
+ }
69
+ }
70
+ else
71
+ {
72
+ featureOps = new Hashtable();
73
+ }
74
+
75
+ // Index Component table and check for keypath modifications
76
+ Hashtable deletedComponent = new Hashtable();
77
+ Hashtable componentKeyPath = new Hashtable();
78
+ foreach (Row row in componentTable.Rows)
79
+ {
80
+ string id = row.Fields[0].Data.ToString();
81
+ string keypath = (null == row.Fields[5].Data) ? String.Empty : row.Fields[5].Data.ToString();
82
+
83
+ componentKeyPath.Add(id, keypath);
84
+ if (RowOperation.Delete == row.Operation)
85
+ {
86
+ deletedComponent.Add(id, row);
87
+ }
88
+ else if (RowOperation.Modify == row.Operation)
89
+ {
90
+ if (row.Fields[1].Modified)
91
+ {
92
+ // Changing the guid of a component is equal to deleting the old one and adding a new one.
93
+ deletedComponent.Add(id, row);
94
+ }
95
+
96
+ // If the keypath is modified its an error
97
+ if (row.Fields[5].Modified)
98
+ {
99
+ this.OnMessage(WixErrors.InvalidKeypathChange(row.SourceLineNumbers, id, this.transformPath));
100
+ }
101
+ }
102
+ }
103
+
104
+ // Verify changes in the file table
105
+ Table fileTable = this.Transform.Tables["File"];
106
+ if (null != fileTable)
107
+ {
108
+ Hashtable componentWithChangedKeyPath = new Hashtable();
109
+ foreach (Row row in fileTable.Rows)
110
+ {
111
+ if (RowOperation.None != row.Operation)
112
+ {
113
+ string fileId = row.Fields[0].Data.ToString();
114
+ string componentId = row.Fields[1].Data.ToString();
115
+
116
+ // If this file is the keypath of a component
117
+ if (String.Equals((string)componentKeyPath[componentId], fileId, StringComparison.Ordinal))
118
+ {
119
+ if (row.Fields[2].Modified)
120
+ {
121
+ // You cant change the filename of a file that is the keypath of a component.
122
+ this.OnMessage(WixErrors.InvalidKeypathChange(row.SourceLineNumbers, componentId, this.transformPath));
123
+ }
124
+
125
+ if (!componentWithChangedKeyPath.ContainsKey(componentId))
126
+ {
127
+ componentWithChangedKeyPath.Add(componentId, fileId);
128
+ }
129
+ }
130
+
131
+ if (RowOperation.Delete == row.Operation)
132
+ {
133
+ // If the file is removed from a component that is not deleted.
134
+ if (!deletedComponent.ContainsKey(componentId))
135
+ {
136
+ bool foundRemoveFileEntry = false;
137
+ string filename = Common.GetName((string)row[2], false, true);
138
+
139
+ Table removeFileTable = this.Transform.Tables["RemoveFile"];
140
+ if (null != removeFileTable)
141
+ {
142
+ foreach (Row removeFileRow in removeFileTable.Rows)
143
+ {
144
+ if (RowOperation.Delete == removeFileRow.Operation)
145
+ {
146
+ continue;
147
+ }
148
+
149
+ if (componentId == (string)removeFileRow[1])
150
+ {
151
+ // Check if there is a RemoveFile entry for this file
152
+ if (null != removeFileRow[2])
153
+ {
154
+ string removeFileName = Common.GetName((string)removeFileRow[2], false, true);
155
+
156
+ // Convert the MSI format for a wildcard string to Regex format.
157
+ removeFileName = removeFileName.Replace('.', '|').Replace('?', '.').Replace("*", ".*").Replace("|", "\\.");
158
+
159
+ Regex regex = new Regex(removeFileName, RegexOptions.CultureInvariant | RegexOptions.IgnoreCase);
160
+ if (regex.IsMatch(filename))
161
+ {
162
+ foundRemoveFileEntry = true;
163
+ break;
164
+ }
165
+ }
166
+ }
167
+ }
168
+ }
169
+
170
+ if (!foundRemoveFileEntry)
171
+ {
172
+ this.OnMessage(WixWarnings.InvalidRemoveFile(row.SourceLineNumbers, fileId, componentId));
173
+ }
174
+ }
175
+ }
176
+ }
177
+ }
178
+ }
179
+
180
+ if (0 < deletedComponent.Count)
181
+ {
182
+ // Index FeatureComponents table.
183
+ Hashtable featureComponents = new Hashtable();
184
+
185
+ if (null != featureComponentsTable)
186
+ {
187
+ foreach (Row row in featureComponentsTable.Rows)
188
+ {
189
+ ArrayList features;
190
+ string componentId = row.Fields[1].Data.ToString();
191
+
192
+ if (featureComponents.Contains(componentId))
193
+ {
194
+ features = (ArrayList)featureComponents[componentId];
195
+ }
196
+ else
197
+ {
198
+ features = new ArrayList();
199
+ featureComponents.Add(componentId, features);
200
+ }
201
+ features.Add(row.Fields[0].Data.ToString());
202
+ }
203
+ }
204
+
205
+ // Check to make sure if a component was deleted, the feature was too.
206
+ foreach (DictionaryEntry entry in deletedComponent)
207
+ {
208
+ if (featureComponents.Contains(entry.Key.ToString()))
209
+ {
210
+ ArrayList features = (ArrayList)featureComponents[entry.Key.ToString()];
211
+ foreach (string featureId in features)
212
+ {
213
+ if (!featureOps.ContainsKey(featureId) || RowOperation.Delete != (RowOperation)featureOps[featureId])
214
+ {
215
+ // The feature was not deleted.
216
+ this.OnMessage(WixErrors.InvalidRemoveComponent(((Row)entry.Value).SourceLineNumbers, entry.Key.ToString(), featureId, this.transformPath));
217
+ }
218
+ }
219
+ }
220
+ }
221
+ }
222
+
223
+ // Warn if new components are added to existing features
224
+ if (null != featureComponentsTable)
225
+ {
226
+ foreach (Row row in featureComponentsTable.Rows)
227
+ {
228
+ if (RowOperation.Add == row.Operation)
229
+ {
230
+ // Check if the feature is in the Feature table
231
+ string feature_ = (string)row[0];
232
+ string component_ = (string)row[1];
233
+
234
+ // Features may not be present if not referenced
235
+ if (!featureOps.ContainsKey(feature_) || RowOperation.Add != (RowOperation)featureOps[feature_])
236
+ {
237
+ this.OnMessage(WixWarnings.NewComponentAddedToExistingFeature(row.SourceLineNumbers, component_, feature_, this.transformPath));
238
+ }
239
+ }
240
+ }
241
+ }
242
+#endif
243
+ throw new NotImplementedException();
244
+ }
245
+ }
246
+}
src/WixToolset.Core.WindowsInstaller/Bind/ProcessUncompressedFilesCommand.cs
+4
-4
@@ -86,14 +86,14 @@ namespace WixToolset.Core.WindowsInstaller.Bind
86
87
// setup up the query record and find the appropriate file in the
88
// previously executed file view
89
- fileQueryRecord[1] = facade.File.Id.Id;
89
+ fileQueryRecord[1] = facade.Id;
90
fileView.Execute(fileQueryRecord);
91
92
using (Record fileRecord = fileView.Fetch())
93
{
94
if (null == fileRecord)
95
{
96
- throw new WixException(ErrorMessages.FileIdentifierNotFound(facade.File.SourceLineNumbers, facade.File.Id.Id));
96
+ throw new WixException(ErrorMessages.FileIdentifierNotFound(facade.SourceLineNumber, facade.Id));
97
}
98
99
relativeFileLayoutPath = this.PathResolver.GetFileSourcePath(directories, fileRecord[1], fileRecord[2], this.Compressed, this.LongNamesInImage);
@@ -102,7 +102,7 @@ namespace WixToolset.Core.WindowsInstaller.Bind
102
// finally put together the base media layout path and the relative file layout path
103
var fileLayoutPath = Path.Combine(mediaLayoutDirectory, relativeFileLayoutPath);
104
105
- var transfer = this.BackendHelper.CreateFileTransfer(facade.File.Source.Path, fileLayoutPath, false, facade.File.SourceLineNumbers);
105
+ var transfer = this.BackendHelper.CreateFileTransfer(facade.SourcePath, fileLayoutPath, false, facade.SourceLineNumber);
106
fileTransfers.Add(transfer);
107
108
// Track the location where the cabinet will be placed. If the transfer is
@@ -110,7 +110,7 @@ namespace WixToolset.Core.WindowsInstaller.Bind
110
// because if the source and destination of the transfer is the same, we
111
// don't want to clean the file because we'd be deleting the original
112
// (and that would be bad).
113
- var tracked = this.BackendHelper.TrackFile(transfer.Destination, TrackedFileType.Final, facade.File.SourceLineNumbers);
113
+ var tracked = this.BackendHelper.TrackFile(transfer.Destination, TrackedFileType.Final, facade.SourceLineNumber);
114
tracked.Clean = !transfer.Redundant;
115
116
trackedFiles.Add(tracked);
src/WixToolset.Core.WindowsInstaller/Bind/SequenceActionsCommand.cs
+7
-6
@@ -11,24 +11,25 @@ namespace WixToolset.Core.WindowsInstaller.Bind
11
using WixToolset.Data.WindowsInstaller;
12
using WixToolset.Extensibility.Services;
13
14
+ /// <summary>
15
+ /// Set sequence numbers for all the actions and create tuples in the output object.
16
+ /// </summary>
17
internal class SequenceActionsCommand
18
{
16
- public SequenceActionsCommand(IntermediateSection section)
19
+ public SequenceActionsCommand(IMessaging messaging, IntermediateSection section)
20
{
21
+ this.Messaging = messaging;
22
this.Section = section;
23
24
this.RelativeActionsForActions = new Dictionary<string, RelativeActions>();
25
}
26
27
+ private IMessaging Messaging { get; }
28
+
29
private IntermediateSection Section { get; }
30
31
private Dictionary<string, RelativeActions> RelativeActionsForActions { get; }
32
27
- public IMessaging Messaging { private get; set; }
28
-
29
- /// <summary>
30
- /// Set sequence numbers for all the actions and create tuples in the output object.
31
- /// </summary>
33
public void Execute()
34
{
35
var requiredActionTuples = new Dictionary<string, WixActionTuple>();
src/WixToolset.Core.WindowsInstaller/Bind/UpdateFileFacadesCommand.cs
+51
-51
@@ -59,27 +59,27 @@ namespace WixToolset.Core.WindowsInstaller.Bind
59
FileInfo fileInfo = null;
60
try
61
{
62
- fileInfo = new FileInfo(facade.File.Source.Path);
62
+ fileInfo = new FileInfo(facade.SourcePath);
63
}
64
catch (ArgumentException)
65
{
66
- this.Messaging.Write(ErrorMessages.InvalidFileName(facade.File.SourceLineNumbers, facade.File.Source.Path));
66
+ this.Messaging.Write(ErrorMessages.InvalidFileName(facade.SourceLineNumber, facade.SourcePath));
67
return;
68
}
69
catch (PathTooLongException)
70
{
71
- this.Messaging.Write(ErrorMessages.InvalidFileName(facade.File.SourceLineNumbers, facade.File.Source.Path));
71
+ this.Messaging.Write(ErrorMessages.InvalidFileName(facade.SourceLineNumber, facade.SourcePath));
72
return;
73
}
74
catch (NotSupportedException)
75
{
76
- this.Messaging.Write(ErrorMessages.InvalidFileName(facade.File.SourceLineNumbers, facade.File.Source.Path));
76
+ this.Messaging.Write(ErrorMessages.InvalidFileName(facade.SourceLineNumber, facade.SourcePath));
77
return;
78
}
79
80
if (!fileInfo.Exists)
81
{
82
- this.Messaging.Write(ErrorMessages.CannotFindFile(facade.File.SourceLineNumbers, facade.File.Id.Id, facade.File.Name, facade.File.Source.Path));
82
+ this.Messaging.Write(ErrorMessages.CannotFindFile(facade.SourceLineNumber, facade.Id, facade.FileName, facade.SourcePath));
83
return;
84
}
85
@@ -87,10 +87,10 @@ namespace WixToolset.Core.WindowsInstaller.Bind
87
{
88
if (Int32.MaxValue < fileStream.Length)
89
{
90
- throw new WixException(ErrorMessages.FileTooLarge(facade.File.SourceLineNumbers, facade.File.Source.Path));
90
+ throw new WixException(ErrorMessages.FileTooLarge(facade.SourceLineNumber, facade.SourcePath));
91
}
92
93
- facade.File.FileSize = Convert.ToInt32(fileStream.Length, CultureInfo.InvariantCulture);
93
+ facade.FileSize = Convert.ToInt32(fileStream.Length, CultureInfo.InvariantCulture);
94
}
95
96
string version = null;
@@ -103,7 +103,7 @@ namespace WixToolset.Core.WindowsInstaller.Bind
103
{
104
if (0x2 == e.NativeErrorCode) // ERROR_FILE_NOT_FOUND
105
{
106
- throw new WixException(ErrorMessages.FileNotFound(facade.File.SourceLineNumbers, fileInfo.FullName));
106
+ throw new WixException(ErrorMessages.FileNotFound(facade.SourceLineNumber, fileInfo.FullName));
107
}
108
else
109
{
@@ -118,7 +118,7 @@ namespace WixToolset.Core.WindowsInstaller.Bind
118
{
119
// not overwriting hash, so don't do the rest of these options.
120
}
121
- else if (null != facade.File.Version)
121
+ else if (null != facade.Version)
122
{
123
// Search all of the file rows available to see if the specified version is actually a companion file. Yes, this looks
124
// very expensive and you're probably thinking it would be better to create an index of some sort to do an O(1) look up.
@@ -127,16 +127,16 @@ namespace WixToolset.Core.WindowsInstaller.Bind
127
//
128
// Also, if we do not find a matching file identifier then the user provided a default version and is providing a version
129
// for unversioned file. That's allowed but generally a dangerous thing to do so let's point that out to the user.
130
- if (!this.FileFacades.Any(r => facade.File.Version.Equals(r.File.Id.Id, StringComparison.Ordinal)))
130
+ if (!this.FileFacades.Any(r => facade.Version.Equals(r.Id, StringComparison.Ordinal)))
131
{
132
- this.Messaging.Write(WarningMessages.DefaultVersionUsedForUnversionedFile(facade.File.SourceLineNumbers, facade.File.Version, facade.File.Id.Id));
132
+ this.Messaging.Write(WarningMessages.DefaultVersionUsedForUnversionedFile(facade.SourceLineNumber, facade.Version, facade.Id));
133
}
134
}
135
else
136
{
137
- if (null != facade.File.Language)
137
+ if (null != facade.Language)
138
{
139
- this.Messaging.Write(WarningMessages.DefaultLanguageUsedForUnversionedFile(facade.File.SourceLineNumbers, facade.File.Language, facade.File.Id.Id));
139
+ this.Messaging.Write(WarningMessages.DefaultLanguageUsedForUnversionedFile(facade.SourceLineNumber, facade.Language, facade.Id));
140
}
141
142
int[] hash;
@@ -148,7 +148,7 @@ namespace WixToolset.Core.WindowsInstaller.Bind
148
{
149
if (0x2 == e.NativeErrorCode) // ERROR_FILE_NOT_FOUND
150
{
151
- throw new WixException(ErrorMessages.FileNotFound(facade.File.SourceLineNumbers, fileInfo.FullName));
151
+ throw new WixException(ErrorMessages.FileNotFound(facade.SourceLineNumber, fileInfo.FullName));
152
}
153
else
154
{
@@ -158,7 +158,7 @@ namespace WixToolset.Core.WindowsInstaller.Bind
158
159
if (null == facade.Hash)
160
{
161
- facade.Hash = new MsiFileHashTuple(facade.File.SourceLineNumbers, facade.File.Id);
161
+ facade.Hash = new MsiFileHashTuple(facade.SourceLineNumber, facade.Identifier);
162
this.Section.Tuples.Add(facade.Hash);
163
}
164
@@ -173,11 +173,11 @@ namespace WixToolset.Core.WindowsInstaller.Bind
173
{
174
// If no version was provided by the user, use the version from the file itself.
175
// This is the most common case.
176
- if (String.IsNullOrEmpty(facade.File.Version))
176
+ if (String.IsNullOrEmpty(facade.Version))
177
{
178
- facade.File.Version = version;
178
+ facade.Version = version;
179
}
180
- else if (!this.FileFacades.Any(r => facade.File.Version.Equals(r.File.Id.Id, StringComparison.Ordinal))) // this looks expensive, but see explanation below.
180
+ else if (!this.FileFacades.Any(r => facade.Version.Equals(r.Id, StringComparison.Ordinal))) // this looks expensive, but see explanation below.
181
{
182
// The user provided a default version for the file row so we looked for a companion file (a file row with Id matching
183
// the version value). We didn't find it so, we will override the default version they provided with the actual
@@ -188,41 +188,41 @@ namespace WixToolset.Core.WindowsInstaller.Bind
188
//
189
// Also note this case can occur when the file is being updated using the WixBindUpdatedFiles extension mechanism.
190
// That's typically even more rare than companion files so again, no index, just search.
191
- facade.File.Version = version;
191
+ facade.Version = version;
192
}
193
194
- if (!String.IsNullOrEmpty(facade.File.Language) && String.IsNullOrEmpty(language))
194
+ if (!String.IsNullOrEmpty(facade.Language) && String.IsNullOrEmpty(language))
195
{
196
- this.Messaging.Write(WarningMessages.DefaultLanguageUsedForVersionedFile(facade.File.SourceLineNumbers, facade.File.Language, facade.File.Id.Id));
196
+ this.Messaging.Write(WarningMessages.DefaultLanguageUsedForVersionedFile(facade.SourceLineNumber, facade.Language, facade.Id));
197
}
198
else // override the default provided by the user (usually nothing) with the actual language from the file itself.
199
{
200
- facade.File.Language = language;
200
+ facade.Language = language;
201
}
202
203
// Populate the binder variables for this file information if requested.
204
if (null != this.VariableCache)
205
{
206
- if (!String.IsNullOrEmpty(facade.File.Version))
206
+ if (!String.IsNullOrEmpty(facade.Version))
207
{
208
- var key = String.Format(CultureInfo.InvariantCulture, "fileversion.{0}", facade.File.Id.Id);
209
- this.VariableCache[key] = facade.File.Version;
208
+ var key = String.Format(CultureInfo.InvariantCulture, "fileversion.{0}", facade.Id);
209
+ this.VariableCache[key] = facade.Version;
210
}
211
212
- if (!String.IsNullOrEmpty(facade.File.Language))
212
+ if (!String.IsNullOrEmpty(facade.Language))
213
{
214
- var key = String.Format(CultureInfo.InvariantCulture, "filelanguage.{0}", facade.File.Id.Id);
215
- this.VariableCache[key] = facade.File.Language;
214
+ var key = String.Format(CultureInfo.InvariantCulture, "filelanguage.{0}", facade.Id);
215
+ this.VariableCache[key] = facade.Language;
216
}
217
}
218
}
219
220
// If this is a CLR assembly, load the assembly and get the assembly name information
221
- if (AssemblyType.DotNetAssembly == facade.Assembly?.Type)
221
+ if (AssemblyType.DotNetAssembly == facade.AssemblyType)
222
{
223
try
224
{
225
- var assemblyName = AssemblyNameReader.ReadAssembly(facade.File.SourceLineNumbers, fileInfo.FullName, version);
225
+ var assemblyName = AssemblyNameReader.ReadAssembly(facade.SourceLineNumber, fileInfo.FullName, version);
226
227
this.SetMsiAssemblyName(assemblyNameTuples, facade, "name", assemblyName.Name);
228
this.SetMsiAssemblyName(assemblyNameTuples, facade, "culture", assemblyName.Culture);
@@ -242,9 +242,9 @@ namespace WixToolset.Core.WindowsInstaller.Bind
242
{
243
this.SetMsiAssemblyName(assemblyNameTuples, facade, "publicKeyToken", assemblyName.PublicKeyToken);
244
}
245
- else if (facade.Assembly.ApplicationFileRef == null)
245
+ else if (facade.AssemblyApplicationFileRef == null)
246
{
247
- throw new WixException(ErrorMessages.GacAssemblyNoStrongName(facade.File.SourceLineNumbers, fileInfo.FullName, facade.File.ComponentRef));
247
+ throw new WixException(ErrorMessages.GacAssemblyNoStrongName(facade.SourceLineNumber, fileInfo.FullName, facade.ComponentRef));
248
}
249
250
if (!String.IsNullOrEmpty(assemblyName.FileVersion))
@@ -255,7 +255,7 @@ namespace WixToolset.Core.WindowsInstaller.Bind
255
// add the assembly name to the information cache
256
if (null != this.VariableCache)
257
{
258
- this.VariableCache[$"assemblyfullname.{facade.File.Id.Id}"] = assemblyName.GetFullName();
258
+ this.VariableCache[$"assemblyfullname.{facade.Id}"] = assemblyName.GetFullName();
259
}
260
}
261
catch (WixException e)
@@ -263,20 +263,20 @@ namespace WixToolset.Core.WindowsInstaller.Bind
263
this.Messaging.Write(e.Error);
264
}
265
}
266
- else if (AssemblyType.Win32Assembly == facade.Assembly?.Type)
266
+ else if (AssemblyType.Win32Assembly == facade.AssemblyType)
267
{
268
// TODO: Consider passing in the this.FileFacades as an indexed collection instead of searching through
269
// all files like this. Even though this is a rare case it looks like we might be able to index the
270
// file earlier.
271
- var fileManifest = this.FileFacades.FirstOrDefault(r => r.File.Id.Id.Equals(facade.Assembly.ManifestFileRef, StringComparison.Ordinal));
271
+ var fileManifest = this.FileFacades.FirstOrDefault(r => r.Id.Equals(facade.AssemblyManifestFileRef, StringComparison.Ordinal));
272
if (null == fileManifest)
273
{
274
- this.Messaging.Write(ErrorMessages.MissingManifestForWin32Assembly(facade.File.SourceLineNumbers, facade.File.Id.Id, facade.Assembly.ManifestFileRef));
274
+ this.Messaging.Write(ErrorMessages.MissingManifestForWin32Assembly(facade.SourceLineNumber, facade.Id, facade.AssemblyManifestFileRef));
275
}
276
277
try
278
{
279
- var assemblyName = AssemblyNameReader.ReadAssemblyManifest(facade.File.SourceLineNumbers, fileManifest.File.Source.Path);
279
+ var assemblyName = AssemblyNameReader.ReadAssemblyManifest(facade.SourceLineNumber, fileManifest.SourcePath);
280
281
if (!String.IsNullOrEmpty(assemblyName.Name))
282
{
@@ -315,41 +315,41 @@ namespace WixToolset.Core.WindowsInstaller.Bind
315
/// create a new row.
316
/// </summary>
317
/// <param name="assemblyNameTuples">MsiAssemblyName table.</param>
318
- /// <param name="file">FileFacade containing the assembly read for the MsiAssemblyName row.</param>
318
+ /// <param name="facade">FileFacade containing the assembly read for the MsiAssemblyName row.</param>
319
/// <param name="name">MsiAssemblyName name.</param>
320
/// <param name="value">MsiAssemblyName value.</param>
321
- private void SetMsiAssemblyName(Dictionary<string, MsiAssemblyNameTuple> assemblyNameTuples, FileFacade file, string name, string value)
321
+ private void SetMsiAssemblyName(Dictionary<string, MsiAssemblyNameTuple> assemblyNameTuples, FileFacade facade, string name, string value)
322
{
323
// check for null value (this can occur when grabbing the file version from an assembly without one)
324
if (String.IsNullOrEmpty(value))
325
{
326
- this.Messaging.Write(WarningMessages.NullMsiAssemblyNameValue(file.File.SourceLineNumbers, file.File.ComponentRef, name));
326
+ this.Messaging.Write(WarningMessages.NullMsiAssemblyNameValue(facade.SourceLineNumber, facade.ComponentRef, name));
327
}
328
else
329
{
330
// if the assembly will be GAC'd and the name in the file table doesn't match the name in the MsiAssemblyName table, error because the install will fail.
331
- if ("name" == name && AssemblyType.DotNetAssembly == file.Assembly.Type &&
332
- String.IsNullOrEmpty(file.Assembly.ApplicationFileRef) &&
333
- !String.Equals(Path.GetFileNameWithoutExtension(file.File.Name), value, StringComparison.OrdinalIgnoreCase))
331
+ if ("name" == name && AssemblyType.DotNetAssembly == facade.AssemblyType &&
332
+ String.IsNullOrEmpty(facade.AssemblyApplicationFileRef) &&
333
+ !String.Equals(Path.GetFileNameWithoutExtension(facade.FileName), value, StringComparison.OrdinalIgnoreCase))
334
{
335
- this.Messaging.Write(ErrorMessages.GACAssemblyIdentityWarning(file.File.SourceLineNumbers, Path.GetFileNameWithoutExtension(file.File.Name), value));
335
+ this.Messaging.Write(ErrorMessages.GACAssemblyIdentityWarning(facade.SourceLineNumber, Path.GetFileNameWithoutExtension(facade.FileName), value));
336
}
337
338
// override directly authored value
339
- var lookup = String.Concat(file.File.ComponentRef, "/", name);
339
+ var lookup = String.Concat(facade.ComponentRef, "/", name);
340
if (!assemblyNameTuples.TryGetValue(lookup, out var assemblyNameRow))
341
{
342
- assemblyNameRow = new MsiAssemblyNameTuple(file.File.SourceLineNumbers);
343
- assemblyNameRow.ComponentRef = file.File.ComponentRef;
342
+ assemblyNameRow = new MsiAssemblyNameTuple(facade.SourceLineNumber);
343
+ assemblyNameRow.ComponentRef = facade.ComponentRef;
344
assemblyNameRow.Name = name;
345
assemblyNameRow.Value = value;
346
347
- if (null == file.AssemblyNames)
347
+ if (null == facade.AssemblyNames)
348
{
349
- file.AssemblyNames = new List<MsiAssemblyNameTuple>();
349
+ facade.AssemblyNames = new List<MsiAssemblyNameTuple>();
350
}
351
352
- file.AssemblyNames.Add(assemblyNameRow);
352
+ facade.AssemblyNames.Add(assemblyNameRow);
353
this.Section.Tuples.Add(assemblyNameRow);
354
}
355
@@ -357,7 +357,7 @@ namespace WixToolset.Core.WindowsInstaller.Bind
357
358
if (this.VariableCache != null)
359
{
360
- var key = String.Format(CultureInfo.InvariantCulture, "assembly{0}.{1}", name, file.File.Id.Id).ToLowerInvariant();
360
+ var key = String.Format(CultureInfo.InvariantCulture, "assembly{0}.{1}", name, facade.Id).ToLowerInvariant();
361
this.VariableCache[key] = value;
362
}
363
}
src/WixToolset.Core.WindowsInstaller/Bind/UpdateMediaSequencesCommand.cs
+10
-10
@@ -33,7 +33,7 @@ namespace WixToolset.Core.WindowsInstaller.Bind
33
34
// Order by Component to group the files by directory.
35
var optimized = this.OptimizedFileFacades();
36
- foreach (var fileId in optimized.Select(f => f.File.Id.Id))
36
+ foreach (var fileId in optimized.Select(f => f.Id))
37
{
38
var fileRow = fileRows.Get(fileId);
39
fileRow.Sequence = ++lastSequence;
@@ -41,13 +41,13 @@ namespace WixToolset.Core.WindowsInstaller.Bind
41
}
42
else
43
{
44
- int lastSequence = 0;
44
+ var lastSequence = 0;
45
MediaRow mediaRow = null;
46
- Dictionary<int, List<FileFacade>> patchGroups = new Dictionary<int, List<FileFacade>>();
46
+ var patchGroups = new Dictionary<int, List<FileFacade>>();
47
48
// sequence the non-patch-added files
49
var optimized = this.OptimizedFileFacades();
50
- foreach (FileFacade facade in optimized)
50
+ foreach (var facade in optimized)
51
{
52
if (null == mediaRow)
53
{
@@ -64,19 +64,19 @@ namespace WixToolset.Core.WindowsInstaller.Bind
64
mediaRow = mediaRows.Get(facade.DiskId);
65
}
66
67
- if (facade.File.PatchGroup.HasValue)
67
+ if (facade.PatchGroup.HasValue)
68
{
69
- if (patchGroups.TryGetValue(facade.File.PatchGroup.Value, out var patchGroup))
69
+ if (patchGroups.TryGetValue(facade.PatchGroup.Value, out var patchGroup))
70
{
71
patchGroup = new List<FileFacade>();
72
- patchGroups.Add(facade.File.PatchGroup.Value, patchGroup);
72
+ patchGroups.Add(facade.PatchGroup.Value, patchGroup);
73
}
74
75
patchGroup.Add(facade);
76
}
77
else
78
{
79
- var fileRow = fileRows.Get(facade.File.Id.Id);
79
+ var fileRow = fileRows.Get(facade.Id);
80
fileRow.Sequence = ++lastSequence;
81
}
82
}
@@ -102,7 +102,7 @@ namespace WixToolset.Core.WindowsInstaller.Bind
102
mediaRow = mediaRows.Get(facade.DiskId);
103
}
104
105
- var fileRow = fileRows.Get(facade.File.Id.Id);
105
+ var fileRow = fileRows.Get(facade.Id);
106
fileRow.Sequence = ++lastSequence;
107
}
108
}
@@ -119,7 +119,7 @@ namespace WixToolset.Core.WindowsInstaller.Bind
119
// TODO: Sort these facades even smarter by directory path and component id
120
// and maybe file size or file extension and other creative ideas to
121
// get optimal install speed out of MSI.
122
- return this.FileFacades.OrderBy(f => f.File.ComponentRef);
122
+ return this.FileFacades.OrderBy(f => f.ComponentRef);
123
}
124
}
125
}
src/WixToolset.Core.WindowsInstaller/Data/tables.xml
+4
@@ -156,6 +156,10 @@
156
minValue="0" maxValue="32767" description="Integer containing bit flags representing file attributes (with the decimal value of each bit position in parentheses)"/>
157
<columnDefinition name="Sequence" type="number" length="4"
158
minValue="1" maxValue="2147483647" description="Sequence with respect to the media images; order must track cabinet order."/>
159
+ <columnDefinition name="DiskId" type="number" length="4" unreal="yes"
160
+ minValue="1" maxValue="32767" description="Disk identifier for the file."/>
161
+ <columnDefinition name="Source" type="object" length="0" unreal="yes"
162
+ category="binary" description="Path to source of file."/>
163
</tableDefinition>
164
<tableDefinition name="CCPSearch">
165
<columnDefinition name="Signature_" type="string" length="72" primaryKey="yes"
src/WixToolset.Core.WindowsInstaller/Differ.cs
+4
@@ -1,5 +1,7 @@
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
+#if DELETE
4
+
5
namespace WixToolset.Core.WindowsInstaller
6
{
7
using System;
@@ -604,3 +606,5 @@ namespace WixToolset.Core.WindowsInstaller
606
}
607
}
608
}
609
+
610
+#endif
src/WixToolset.Core.WindowsInstaller/Inscribe/InscribeMsiPackageCommand.cs
+40
-40
@@ -33,40 +33,40 @@ namespace WixToolset.Core.WindowsInstaller.Inscribe
33
public bool Execute()
34
{
35
// Keeps track of whether we've encountered at least one signed cab or not - we'll throw a warning if no signed cabs were encountered
36
- bool foundUnsignedExternals = false;
37
- bool shouldCommit = false;
36
+ var foundUnsignedExternals = false;
37
+ var shouldCommit = false;
38
39
- FileAttributes attributes = File.GetAttributes(this.Context.InputFilePath);
39
+ var attributes = File.GetAttributes(this.Context.InputFilePath);
40
if (FileAttributes.ReadOnly == (attributes & FileAttributes.ReadOnly))
41
{
42
this.Messaging.Write(ErrorMessages.ReadOnlyOutputFile(this.Context.InputFilePath));
43
return shouldCommit;
44
}
45
46
- using (Database database = new Database(this.Context.InputFilePath, OpenDatabase.Transact))
46
+ using (var database = new Database(this.Context.InputFilePath, OpenDatabase.Transact))
47
{
48
// Just use the English codepage, because the tables we're importing only have binary streams / MSI identifiers / other non-localizable content
49
- int codepage = 1252;
49
+ var codepage = 1252;
50
51
// list of certificates for this database (hash/identifier)
52
- Dictionary<string, string> certificates = new Dictionary<string, string>();
52
+ var certificates = new Dictionary<string, string>();
53
54
// Reset the in-memory tables for this new database
55
- Table digitalSignatureTable = new Table(this.TableDefinitions["MsiDigitalSignature"]);
56
- Table digitalCertificateTable = new Table(this.TableDefinitions["MsiDigitalCertificate"]);
55
+ var digitalSignatureTable = new Table(this.TableDefinitions["MsiDigitalSignature"]);
56
+ var digitalCertificateTable = new Table(this.TableDefinitions["MsiDigitalCertificate"]);
57
58
// If any digital signature records exist that are not of the media type, preserve them
59
if (database.TableExists("MsiDigitalSignature"))
60
{
61
- using (View digitalSignatureView = database.OpenExecuteView("SELECT `Table`, `SignObject`, `DigitalCertificate_`, `Hash` FROM `MsiDigitalSignature` WHERE `Table` <> 'Media'"))
61
+ using (var digitalSignatureView = database.OpenExecuteView("SELECT `Table`, `SignObject`, `DigitalCertificate_`, `Hash` FROM `MsiDigitalSignature` WHERE `Table` <> 'Media'"))
62
{
63
- foreach (Record digitalSignatureRecord in digitalSignatureView.Records)
63
+ foreach (var digitalSignatureRecord in digitalSignatureView.Records)
64
{
65
Row digitalSignatureRow = null;
66
digitalSignatureRow = digitalSignatureTable.CreateRow(null);
67
68
- string table = digitalSignatureRecord.GetString(0);
69
- string signObject = digitalSignatureRecord.GetString(1);
68
+ var table = digitalSignatureRecord.GetString(0);
69
+ var signObject = digitalSignatureRecord.GetString(1);
70
71
digitalSignatureRow[0] = table;
72
digitalSignatureRow[1] = signObject;
@@ -75,16 +75,16 @@ namespace WixToolset.Core.WindowsInstaller.Inscribe
75
if (false == digitalSignatureRecord.IsNull(3))
76
{
77
// Export to a file, because the MSI API's require us to provide a file path on disk
78
- string hashPath = Path.Combine(this.Context.IntermediateFolder, "MsiDigitalSignature");
79
- string hashFileName = string.Concat(table, ".", signObject, ".bin");
78
+ var hashPath = Path.Combine(this.Context.IntermediateFolder, "MsiDigitalSignature");
79
+ var hashFileName = String.Concat(table, ".", signObject, ".bin");
80
81
Directory.CreateDirectory(hashPath);
82
hashPath = Path.Combine(hashPath, hashFileName);
83
84
- using (FileStream fs = File.Create(hashPath))
84
+ using (var fs = File.Create(hashPath))
85
{
86
int bytesRead;
87
- byte[] buffer = new byte[1024 * 4];
87
+ var buffer = new byte[1024 * 4];
88
89
while (0 != (bytesRead = digitalSignatureRecord.GetStream(3, buffer, buffer.Length)))
90
{
@@ -101,21 +101,21 @@ namespace WixToolset.Core.WindowsInstaller.Inscribe
101
// If any digital certificates exist, extract and preserve them
102
if (database.TableExists("MsiDigitalCertificate"))
103
{
104
- using (View digitalCertificateView = database.OpenExecuteView("SELECT * FROM `MsiDigitalCertificate`"))
104
+ using (var digitalCertificateView = database.OpenExecuteView("SELECT * FROM `MsiDigitalCertificate`"))
105
{
106
- foreach (Record digitalCertificateRecord in digitalCertificateView.Records)
106
+ foreach (var digitalCertificateRecord in digitalCertificateView.Records)
107
{
108
- string certificateId = digitalCertificateRecord.GetString(1); // get the identifier of the certificate
108
+ var certificateId = digitalCertificateRecord.GetString(1); // get the identifier of the certificate
109
110
// Export to a file, because the MSI API's require us to provide a file path on disk
111
- string certPath = Path.Combine(this.Context.IntermediateFolder, "MsiDigitalCertificate");
111
+ var certPath = Path.Combine(this.Context.IntermediateFolder, "MsiDigitalCertificate");
112
Directory.CreateDirectory(certPath);
113
- certPath = Path.Combine(certPath, string.Concat(certificateId, ".cer"));
113
+ certPath = Path.Combine(certPath, String.Concat(certificateId, ".cer"));
114
115
- using (FileStream fs = File.Create(certPath))
115
+ using (var fs = File.Create(certPath))
116
{
117
int bytesRead;
118
- byte[] buffer = new byte[1024 * 4];
118
+ var buffer = new byte[1024 * 4];
119
120
while (0 != (bytesRead = digitalCertificateRecord.GetStream(2, buffer, buffer.Length)))
121
{
@@ -124,37 +124,37 @@ namespace WixToolset.Core.WindowsInstaller.Inscribe
124
}
125
126
// Add it to our "add to MsiDigitalCertificate" table dictionary
127
- Row digitalCertificateRow = digitalCertificateTable.CreateRow(null);
127
+ var digitalCertificateRow = digitalCertificateTable.CreateRow(null);
128
digitalCertificateRow[0] = certificateId;
129
130
// Now set the file path on disk where this binary stream will be picked up at import time
131
- digitalCertificateRow[1] = string.Concat(certificateId, ".cer");
131
+ digitalCertificateRow[1] = String.Concat(certificateId, ".cer");
132
133
// Load the cert to get it's thumbprint
134
- X509Certificate cert = X509Certificate.CreateFromCertFile(certPath);
135
- X509Certificate2 cert2 = new X509Certificate2(cert);
134
+ var cert = X509Certificate.CreateFromCertFile(certPath);
135
+ var cert2 = new X509Certificate2(cert);
136
137
certificates.Add(cert2.Thumbprint, certificateId);
138
}
139
}
140
}
141
142
- using (View mediaView = database.OpenExecuteView("SELECT * FROM `Media`"))
142
+ using (var mediaView = database.OpenExecuteView("SELECT * FROM `Media`"))
143
{
144
- foreach (Record mediaRecord in mediaView.Records)
144
+ foreach (var mediaRecord in mediaView.Records)
145
{
146
X509Certificate2 cert2 = null;
147
Row digitalSignatureRow = null;
148
149
- string cabName = mediaRecord.GetString(4); // get the name of the cab
149
+ var cabName = mediaRecord.GetString(4); // get the name of the cab
150
// If there is no cabinet or it's an internal cab, skip it.
151
if (String.IsNullOrEmpty(cabName) || cabName.StartsWith("#", StringComparison.Ordinal))
152
{
153
continue;
154
}
155
156
- string cabId = mediaRecord.GetString(1); // get the ID of the cab
157
- string cabPath = Path.Combine(Path.GetDirectoryName(this.Context.InputFilePath), cabName);
156
+ var cabId = mediaRecord.GetString(1); // get the ID of the cab
157
+ var cabPath = Path.Combine(Path.GetDirectoryName(this.Context.InputFilePath), cabName);
158
159
// If the cabs aren't there, throw an error but continue to catch the other errors
160
if (!File.Exists(cabPath))
@@ -166,12 +166,12 @@ namespace WixToolset.Core.WindowsInstaller.Inscribe
166
try
167
{
168
// Get the certificate from the cab
169
- X509Certificate signedFileCert = X509Certificate.CreateFromSignedFile(cabPath);
169
+ var signedFileCert = X509Certificate.CreateFromSignedFile(cabPath);
170
cert2 = new X509Certificate2(signedFileCert);
171
}
172
catch (System.Security.Cryptography.CryptographicException e)
173
{
174
- uint HResult = unchecked((uint)Marshal.GetHRForException(e));
174
+ var HResult = unchecked((uint)Marshal.GetHRForException(e));
175
176
// If the file has no cert, continue, but flag that we found at least one so we can later give a warning
177
if (0x80092009 == HResult) // CRYPT_E_NO_MATCH
@@ -197,26 +197,26 @@ namespace WixToolset.Core.WindowsInstaller.Inscribe
197
if (!certificates.ContainsKey(cert2.Thumbprint))
198
{
199
// generate a stable identifier
200
- string certificateGeneratedId = Common.GenerateIdentifier("cer", cert2.Thumbprint);
200
+ var certificateGeneratedId = Common.GenerateIdentifier("cer", cert2.Thumbprint);
201
202
// Add it to our "add to MsiDigitalCertificate" table dictionary
203
- Row digitalCertificateRow = digitalCertificateTable.CreateRow(null);
203
+ var digitalCertificateRow = digitalCertificateTable.CreateRow(null);
204
digitalCertificateRow[0] = certificateGeneratedId;
205
206
// Export to a file, because the MSI API's require us to provide a file path on disk
207
- string certPath = Path.Combine(this.Context.IntermediateFolder, "MsiDigitalCertificate");
207
+ var certPath = Path.Combine(this.Context.IntermediateFolder, "MsiDigitalCertificate");
208
Directory.CreateDirectory(certPath);
209
- certPath = Path.Combine(certPath, string.Concat(cert2.Thumbprint, ".cer"));
209
+ certPath = Path.Combine(certPath, String.Concat(cert2.Thumbprint, ".cer"));
210
File.Delete(certPath);
211
212
- using (BinaryWriter writer = new BinaryWriter(File.Open(certPath, FileMode.Create)))
212
+ using (var writer = new BinaryWriter(File.Open(certPath, FileMode.Create)))
213
{
214
writer.Write(cert2.RawData);
215
writer.Close();
216
}
217
218
// Now set the file path on disk where this binary stream will be picked up at import time
219
- digitalCertificateRow[1] = string.Concat(cert2.Thumbprint, ".cer");
219
+ digitalCertificateRow[1] = String.Concat(cert2.Thumbprint, ".cer");
220
221
certificates.Add(cert2.Thumbprint, certificateGeneratedId);
222
}
src/WixToolset.Core.WindowsInstaller/MspBackend.cs
+53
-15
@@ -3,36 +3,74 @@
3
namespace WixToolset.Core.WindowsInstaller
4
{
5
using System;
6
- using System.ComponentModel;
6
+ using System.Collections.Generic;
7
using System.IO;
8
- using WixToolset.Core.Native;
8
+ using System.Linq;
9
+ using WixToolset.Core.WindowsInstaller.Bind;
10
+ using WixToolset.Core.WindowsInstaller.Msi;
11
using WixToolset.Core.WindowsInstaller.Unbind;
12
using WixToolset.Data;
11
- using WixToolset.Data.Bind;
13
+ using WixToolset.Data.Tuples;
14
+ using WixToolset.Data.WindowsInstaller;
15
using WixToolset.Extensibility;
16
using WixToolset.Extensibility.Data;
14
- using WixToolset.Ole32;
17
+ using WixToolset.Extensibility.Services;
18
19
internal class MspBackend : IBackend
20
{
21
public IBindResult Bind(IBindContext context)
22
{
20
- throw new NotImplementedException();
21
- }
23
+ var messaging = context.ServiceProvider.GetService<IMessaging>();
24
23
- public IDecompileResult Decompile(IDecompileContext context)
24
- {
25
- throw new NotImplementedException();
26
- }
25
+ var extensionManager = context.ServiceProvider.GetService<IExtensionManager>();
26
28
- public bool Inscribe(IInscribeContext context)
29
- {
30
- throw new NotImplementedException();
27
+ var backendExtensions = extensionManager.GetServices<IWindowsInstallerBackendBinderExtension>();
28
+
29
+ foreach (var extension in backendExtensions)
30
+ {
31
+ extension.PreBackendBind(context);
32
+ }
33
+
34
+ // Create transforms named in patch transforms.
35
+ IEnumerable<PatchTransform> patchTransforms;
36
+ {
37
+ var command = new CreatePatchTransformsCommand(messaging, context.IntermediateRepresentation, context.IntermediateFolder);
38
+ patchTransforms = command.Execute();
39
+ }
40
+
41
+ // Enhance the intermediate by attaching the created patch transforms.
42
+ IEnumerable<SubStorage> subStorages;
43
+ {
44
+ var command = new AttachPatchTransformsCommand(messaging, context.IntermediateRepresentation, patchTransforms);
45
+ subStorages = command.Execute();
46
+ }
47
+
48
+ // Create WindowsInstallerData with patch metdata and transforms as sub-storages
49
+ // Create MSP from WindowsInstallerData
50
+ using (var command = new BindDatabaseCommand(context, backendExtensions, subStorages, null))
51
+ {
52
+ command.Execute();
53
+
54
+ var result = context.ServiceProvider.GetService<IBindResult>();
55
+ result.FileTransfers = command.FileTransfers;
56
+ result.TrackedFiles = command.TrackedFiles;
57
+
58
+ foreach (var extension in backendExtensions)
59
+ {
60
+ extension.PostBackendBind(result, command.Wixout);
61
+ }
62
+
63
+ return result;
64
+ }
65
}
66
67
+ public IDecompileResult Decompile(IDecompileContext context) => throw new NotImplementedException();
68
+
69
+ public bool Inscribe(IInscribeContext context) => throw new NotImplementedException();
70
+
71
public Intermediate Unbind(IUnbindContext context)
72
{
35
-#if REVISIT_FOR_PATCHING
73
+#if TODO_PATCHING
74
Output patch;
75
76
// patch files are essentially database files (use a special flag to let the API know its a patch file)
@@ -116,4 +154,4 @@ namespace WixToolset.Core.WindowsInstaller
154
throw new NotImplementedException();
155
}
156
}
119
-}
\ No newline at end of file
157
+}
src/WixToolset.Core.WindowsInstaller/MstBackend.cs
+1
-1
@@ -12,7 +12,7 @@ namespace WixToolset.Core.WindowsInstaller
12
{
13
public IBindResult Bind(IBindContext context)
14
{
15
-#if REVISIT_FOR_PATCHING
15
+#if TODO_PATCHING
16
var command = new BindTransformCommand();
17
command.Extensions = context.Extensions;
18
command.TempFilesLocation = context.IntermediateFolder;
src/WixToolset.Core.WindowsInstaller/Ole32/Storage.cs
+1
-1
@@ -63,8 +63,8 @@ namespace WixToolset.Ole32
63
/// </summary>
64
internal sealed class Storage : IDisposable
65
{
66
+ private readonly IStorage storage;
67
private bool disposed;
67
- private IStorage storage;
68
69
/// <summary>
70
/// Instantiate a new Storage.
src/WixToolset.Core.WindowsInstaller/Patch.cs
+5
-2
@@ -1,5 +1,7 @@
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
+#if DELETE
4
+
5
namespace WixToolset.Data
6
{
7
using System;
@@ -50,7 +52,6 @@ namespace WixToolset.Data
52
[SuppressMessage("Microsoft.Globalization", "CA1303:DoNotPassLiteralsAsLocalizedParameters", MessageId = "System.InvalidOperationException.#ctor(System.String)")]
53
public void AttachTransforms(List<PatchTransform> transforms)
54
{
53
-#if REVISIT_FOR_PATCHING
55
// Track if at least one transform gets attached.
56
bool attachedTransform = false;
57
@@ -1229,8 +1230,10 @@ namespace WixToolset.Data
1230
}
1231
1232
return pairedTransform;
1232
-#endif
1233
+
1234
throw new NotImplementedException();
1235
}
1236
}
1237
}
1238
+
1239
+#endif
src/WixToolset.Core.WindowsInstaller/PatchTransform.cs
+5
-2
@@ -1,5 +1,7 @@
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
+#if DELETE
4
+
5
namespace WixToolset
6
{
7
using System;
@@ -50,7 +52,6 @@ namespace WixToolset
52
/// </summary>
53
public void Validate()
54
{
53
-#if REVISIT_FOR_PATCHING
55
// Changing the ProdocutCode in a patch transform is not recommended.
56
Table propertyTable = this.Transform.Tables["Property"];
57
if (null != propertyTable)
@@ -261,8 +262,10 @@ namespace WixToolset
262
}
263
}
264
}
264
-#endif
265
+
266
throw new NotImplementedException();
267
}
268
}
269
}
270
+
271
+#endif
\ No newline at end of file
src/WixToolset.Core.WindowsInstaller/Unbind/UnbindMsiOrMsmCommand.cs
+1
-1
@@ -19,7 +19,7 @@ namespace WixToolset.Core.WindowsInstaller.Unbind
19
20
public Intermediate Execute()
21
{
22
-#if REVISIT_FOR_PATCHING
22
+#if TODO_PATCHING
23
Output output;
24
25
try
src/WixToolset.Core.WindowsInstaller/Unbind/UnbindTranformCommand.cs
+58
-67
@@ -4,6 +4,7 @@ namespace WixToolset.Core.WindowsInstaller.Unbind
4
{
5
using System;
6
using System.Collections;
7
+ using System.Collections.Generic;
8
using System.ComponentModel;
9
using System.Globalization;
10
using System.IO;
@@ -12,7 +13,6 @@ namespace WixToolset.Core.WindowsInstaller.Unbind
13
using WixToolset.Core.WindowsInstaller.Msi;
14
using WixToolset.Data;
15
using WixToolset.Data.WindowsInstaller;
15
- using WixToolset.Extensibility;
16
using WixToolset.Extensibility.Services;
17
18
internal class UnbindTransformCommand
@@ -41,21 +41,21 @@ namespace WixToolset.Core.WindowsInstaller.Unbind
41
42
public WindowsInstallerData Execute()
43
{
44
- WindowsInstallerData transform = new WindowsInstallerData(new SourceLineNumber(this.TransformFile));
44
+ var transform = new WindowsInstallerData(new SourceLineNumber(this.TransformFile));
45
transform.Type = OutputType.Transform;
46
47
// get the summary information table
48
- using (SummaryInformation summaryInformation = new SummaryInformation(this.TransformFile))
48
+ using (var summaryInformation = new SummaryInformation(this.TransformFile))
49
{
50
- Table table = transform.EnsureTable(this.TableDefinitions["_SummaryInformation"]);
50
+ var table = transform.EnsureTable(this.TableDefinitions["_SummaryInformation"]);
51
52
- for (int i = 1; 19 >= i; i++)
52
+ for (var i = 1; 19 >= i; i++)
53
{
54
- string value = summaryInformation.GetProperty(i);
54
+ var value = summaryInformation.GetProperty(i);
55
56
if (0 < value.Length)
57
{
58
- Row row = table.CreateRow(transform.SourceLineNumbers);
58
+ var row = table.CreateRow(transform.SourceLineNumbers);
59
row[0] = i;
60
row[1] = value;
61
}
@@ -63,9 +63,9 @@ namespace WixToolset.Core.WindowsInstaller.Unbind
63
}
64
65
// create a schema msi which hopefully matches the table schemas in the transform
66
- WindowsInstallerData schemaOutput = new WindowsInstallerData(null);
67
- string msiDatabaseFile = Path.Combine(this.IntermediateFolder, "schema.msi");
68
- foreach (TableDefinition tableDefinition in this.TableDefinitions)
66
+ var schemaOutput = new WindowsInstallerData(null);
67
+ var msiDatabaseFile = Path.Combine(this.IntermediateFolder, "schema.msi");
68
+ foreach (var tableDefinition in this.TableDefinitions)
69
{
70
// skip unreal tables and the Patch table
71
if (!tableDefinition.Unreal && "Patch" != tableDefinition.Name)
@@ -74,40 +74,40 @@ namespace WixToolset.Core.WindowsInstaller.Unbind
74
}
75
}
76
77
- Hashtable addedRows = new Hashtable();
77
+ var addedRows = new Dictionary<string, Row>();
78
Table transformViewTable;
79
80
// Bind the schema msi.
81
this.GenerateDatabase(schemaOutput, msiDatabaseFile);
82
83
// apply the transform to the database and retrieve the modifications
84
- using (Database msiDatabase = new Database(msiDatabaseFile, OpenDatabase.Transact))
84
+ using (var msiDatabase = new Database(msiDatabaseFile, OpenDatabase.Transact))
85
{
86
// apply the transform with the ViewTransform option to collect all the modifications
87
msiDatabase.ApplyTransform(this.TransformFile, TransformErrorConditions.All | TransformErrorConditions.ViewTransform);
88
89
// unbind the database
90
var unbindCommand = new UnbindDatabaseCommand(this.Messaging, msiDatabase, msiDatabaseFile, OutputType.Product, this.ExportBasePath, this.IntermediateFolder, false, false, skipSummaryInfo: true);
91
- WindowsInstallerData transformViewOutput = unbindCommand.Execute();
91
+ var transformViewOutput = unbindCommand.Execute();
92
93
// index the added and possibly modified rows (added rows may also appears as modified rows)
94
transformViewTable = transformViewOutput.Tables["_TransformView"];
95
- Hashtable modifiedRows = new Hashtable();
96
- foreach (Row row in transformViewTable.Rows)
95
+ var modifiedRows = new Hashtable();
96
+ foreach (var row in transformViewTable.Rows)
97
{
98
- string tableName = (string)row[0];
99
- string columnName = (string)row[1];
100
- string primaryKeys = (string)row[2];
98
+ var tableName = (string)row[0];
99
+ var columnName = (string)row[1];
100
+ var primaryKeys = (string)row[2];
101
102
if ("INSERT" == columnName)
103
{
104
- string index = String.Concat(tableName, ':', primaryKeys);
104
+ var index = String.Concat(tableName, ':', primaryKeys);
105
106
addedRows.Add(index, null);
107
}
108
else if ("CREATE" != columnName && "DELETE" != columnName && "DROP" != columnName && null != primaryKeys) // modified row
109
{
110
- string index = String.Concat(tableName, ':', primaryKeys);
110
+ var index = String.Concat(tableName, ':', primaryKeys);
111
112
modifiedRows[index] = row;
113
}
@@ -116,16 +116,16 @@ namespace WixToolset.Core.WindowsInstaller.Unbind
116
// create placeholder rows for modified rows to make the transform insert the updated values when its applied
117
foreach (Row row in modifiedRows.Values)
118
{
119
- string tableName = (string)row[0];
120
- string columnName = (string)row[1];
121
- string primaryKeys = (string)row[2];
119
+ var tableName = (string)row[0];
120
+ var columnName = (string)row[1];
121
+ var primaryKeys = (string)row[2];
122
123
- string index = String.Concat(tableName, ':', primaryKeys);
123
+ var index = String.Concat(tableName, ':', primaryKeys);
124
125
// ignore information for added rows
126
- if (!addedRows.Contains(index))
126
+ if (!addedRows.ContainsKey(index))
127
{
128
- Table table = schemaOutput.Tables[tableName];
128
+ var table = schemaOutput.Tables[tableName];
129
this.CreateRow(table, primaryKeys, true);
130
}
131
}
@@ -135,7 +135,7 @@ namespace WixToolset.Core.WindowsInstaller.Unbind
135
this.GenerateDatabase(schemaOutput, msiDatabaseFile);
136
137
// apply the transform to the database and retrieve the modifications
138
- using (Database msiDatabase = new Database(msiDatabaseFile, OpenDatabase.Transact))
138
+ using (var msiDatabase = new Database(msiDatabaseFile, OpenDatabase.Transact))
139
{
140
try
141
{
@@ -158,26 +158,26 @@ namespace WixToolset.Core.WindowsInstaller.Unbind
158
159
// unbind the database
160
var unbindCommand = new UnbindDatabaseCommand(this.Messaging, msiDatabase, msiDatabaseFile, OutputType.Product, this.ExportBasePath, this.IntermediateFolder, false, false, skipSummaryInfo: true);
161
- WindowsInstallerData output = unbindCommand.Execute();
161
+ var output = unbindCommand.Execute();
162
163
// index all the rows to easily find modified rows
164
- Hashtable rows = new Hashtable();
165
- foreach (Table table in output.Tables)
164
+ var rows = new Dictionary<string, Row>();
165
+ foreach (var table in output.Tables)
166
{
167
- foreach (Row row in table.Rows)
167
+ foreach (var row in table.Rows)
168
{
169
rows.Add(String.Concat(table.Name, ':', row.GetPrimaryKey('\t', " ")), row);
170
}
171
}
172
173
// process the _TransformView rows into transform rows
174
- foreach (Row row in transformViewTable.Rows)
174
+ foreach (var row in transformViewTable.Rows)
175
{
176
- string tableName = (string)row[0];
177
- string columnName = (string)row[1];
178
- string primaryKeys = (string)row[2];
176
+ var tableName = (string)row[0];
177
+ var columnName = (string)row[1];
178
+ var primaryKeys = (string)row[2];
179
180
- Table table = transform.EnsureTable(this.TableDefinitions[tableName]);
180
+ var table = transform.EnsureTable(this.TableDefinitions[tableName]);
181
182
if ("CREATE" == columnName) // added table
183
{
@@ -185,7 +185,7 @@ namespace WixToolset.Core.WindowsInstaller.Unbind
185
}
186
else if ("DELETE" == columnName) // deleted row
187
{
188
- Row deletedRow = this.CreateRow(table, primaryKeys, false);
188
+ var deletedRow = this.CreateRow(table, primaryKeys, false);
189
deletedRow.Operation = RowOperation.Delete;
190
}
191
else if ("DROP" == columnName) // dropped table
@@ -194,24 +194,24 @@ namespace WixToolset.Core.WindowsInstaller.Unbind
194
}
195
else if ("INSERT" == columnName) // added row
196
{
197
- string index = String.Concat(tableName, ':', primaryKeys);
198
- Row addedRow = (Row)rows[index];
197
+ var index = String.Concat(tableName, ':', primaryKeys);
198
+ var addedRow = rows[index];
199
addedRow.Operation = RowOperation.Add;
200
table.Rows.Add(addedRow);
201
}
202
else if (null != primaryKeys) // modified row
203
{
204
- string index = String.Concat(tableName, ':', primaryKeys);
204
+ var index = String.Concat(tableName, ':', primaryKeys);
205
206
// the _TransformView table includes information for added rows
207
// that looks like modified rows so it sometimes needs to be ignored
208
- if (!addedRows.Contains(index))
208
+ if (!addedRows.ContainsKey(index))
209
{
210
- Row modifiedRow = (Row)rows[index];
210
+ var modifiedRow = rows[index];
211
212
// mark the field as modified
213
- int indexOfModifiedValue = -1;
214
- for (int i = 0; i < modifiedRow.TableDefinition.Columns.Length; ++i)
213
+ var indexOfModifiedValue = -1;
214
+ for (var i = 0; i < modifiedRow.TableDefinition.Columns.Length; ++i)
215
{
216
if (columnName.Equals(modifiedRow.TableDefinition.Columns[i].Name, StringComparison.Ordinal))
217
{
@@ -231,7 +231,7 @@ namespace WixToolset.Core.WindowsInstaller.Unbind
231
}
232
else // added column
233
{
234
- ColumnDefinition column = table.Definition.Columns.Single(c => c.Name.Equals(columnName, StringComparison.Ordinal));
234
+ var column = table.Definition.Columns.Single(c => c.Name.Equals(columnName, StringComparison.Ordinal));
235
column.Added = true;
236
}
237
}
@@ -240,21 +240,6 @@ namespace WixToolset.Core.WindowsInstaller.Unbind
240
return transform;
241
}
242
243
- private void GenerateDatabase(WindowsInstallerData output, string databaseFile)
244
- {
245
- var command = new GenerateDatabaseCommand();
246
- command.Extensions = Array.Empty<IFileSystemExtension>();
247
- command.Output = output;
248
- command.OutputPath = databaseFile;
249
- command.KeepAddedColumns = true;
250
- command.UseSubDirectory = false;
251
- command.SuppressAddingValidationRows = true;
252
- command.TableDefinitions = this.TableDefinitions;
253
- command.IntermediateFolder = this.IntermediateFolder;
254
- command.Codepage = -1;
255
- command.Execute();
256
- }
257
-
243
/// <summary>
244
/// Create a deleted or modified row.
245
/// </summary>
@@ -264,14 +249,14 @@ namespace WixToolset.Core.WindowsInstaller.Unbind
249
/// <returns>The new row.</returns>
250
private Row CreateRow(Table table, string primaryKeys, bool setRequiredFields)
251
{
267
- Row row = table.CreateRow(null);
252
+ var row = table.CreateRow(null);
253
269
- string[] primaryKeyParts = primaryKeys.Split('\t');
270
- int primaryKeyPartIndex = 0;
254
+ var primaryKeyParts = primaryKeys.Split('\t');
255
+ var primaryKeyPartIndex = 0;
256
272
- for (int i = 0; i < table.Definition.Columns.Length; i++)
257
+ for (var i = 0; i < table.Definition.Columns.Length; i++)
258
{
274
- ColumnDefinition columnDefinition = table.Definition.Columns[i];
259
+ var columnDefinition = table.Definition.Columns[i];
260
261
if (columnDefinition.PrimaryKey)
262
{
@@ -294,8 +279,8 @@ namespace WixToolset.Core.WindowsInstaller.Unbind
279
{
280
if (null == this.EmptyFile)
281
{
297
- this.EmptyFile = Path.GetTempFileName() + ".empty";
298
- using (FileStream fileStream = File.Create(this.EmptyFile))
282
+ this.EmptyFile = Path.Combine(this.IntermediateFolder, ".empty");
283
+ using (var fileStream = File.Create(this.EmptyFile))
284
{
285
}
286
}
@@ -311,5 +296,11 @@ namespace WixToolset.Core.WindowsInstaller.Unbind
296
297
return row;
298
}
299
+
300
+ private void GenerateDatabase(WindowsInstallerData output, string databaseFile)
301
+ {
302
+ var command = new GenerateDatabaseCommand(this.Messaging, null, null, output, databaseFile, this.TableDefinitions, this.IntermediateFolder, codepage: -1, keepAddedColumns: true, suppressAddingValidationRows: true, useSubdirectory: false);
303
+ command.Execute();
304
+ }
305
}
306
}
src/WixToolset.Core.WindowsInstaller/WindowsInstallerBackendFactory.cs
+2
-3
@@ -1,11 +1,10 @@
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.
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.IO;
7
using WixToolset.Extensibility;
8
- using WixToolset.Extensibility.Data;
8
9
internal class WindowsInstallerBackendFactory : IBackendFactory
10
{
@@ -16,7 +15,7 @@ namespace WixToolset.Core.WindowsInstaller
15
outputType = Path.GetExtension(outputFile);
16
}
17
19
- switch (outputType.ToLowerInvariant())
18
+ switch (outputType?.ToLowerInvariant())
19
{
20
case "module":
21
case ".msm":
src/WixToolset.Core/Bind/FileFacade.cs
+121
-7
@@ -2,32 +2,146 @@
2
3
namespace WixToolset.Core.Bind
4
{
5
+ using System;
6
using System.Collections.Generic;
7
+ using WixToolset.Data;
8
using WixToolset.Data.Tuples;
9
+ using WixToolset.Data.WindowsInstaller;
10
+ using WixToolset.Data.WindowsInstaller.Rows;
11
12
public class FileFacade
13
{
14
public FileFacade(FileTuple file, AssemblyTuple assembly)
15
{
12
- this.File = file;
13
- this.Assembly = assembly;
16
+ this.FileTuple = file;
17
+ this.AssemblyTuple = assembly;
18
}
19
20
public FileFacade(bool fromModule, FileTuple file)
21
{
22
this.FromModule = fromModule;
19
- this.File = file;
23
+ this.FileTuple = file;
24
+ }
25
+
26
+ internal FileFacade(FileRow row)
27
+ {
28
+ this.FromTransform = true;
29
+ this.FileRow = row;
30
}
31
32
public bool FromModule { get; }
33
24
- public FileTuple File { get; }
34
+ public bool FromTransform { get; }
35
+
36
+ private FileRow FileRow { get; }
37
+
38
+ private FileTuple FileTuple { get; }
39
+
40
+ private AssemblyTuple AssemblyTuple { get; }
41
+
42
+ public string Id => this.FileRow == null ? this.FileTuple.Id.Id : this.FileRow.File;
43
+
44
+ public Identifier Identifier => this.FileRow == null ? this.FileTuple.Id : throw new NotImplementedException();
45
+
46
+ public string ComponentRef => this.FileRow == null ? this.FileTuple.ComponentRef : this.FileRow.Component;
47
+
48
+ public int DiskId
49
+ {
50
+ get => this.FileRow == null ? this.FileTuple.DiskId ?? 0 : this.FileRow.DiskId;
51
+ set
52
+ {
53
+ if (this.FileRow == null)
54
+ {
55
+ this.FileTuple.DiskId = value;
56
+ }
57
+ else
58
+ {
59
+ this.FileRow.DiskId = value;
60
+ }
61
+ }
62
+ }
63
+
64
+ public string FileName => this.FileRow == null ? this.FileTuple.Name : this.FileRow.FileName;
65
+
66
+ public int FileSize
67
+ {
68
+ get => this.FileRow == null ? this.FileTuple.FileSize : this.FileRow.FileSize;
69
+ set
70
+ {
71
+ if (this.FileRow == null)
72
+ {
73
+ this.FileTuple.FileSize = value;
74
+ }
75
+ else
76
+ {
77
+ this.FileRow.FileSize = value;
78
+ }
79
+ }
80
+ }
81
+
82
+ public string Language
83
+ {
84
+ get => this.FileRow == null ? this.FileTuple.Language : this.FileRow.Language;
85
+ set
86
+ {
87
+ if (this.FileRow == null)
88
+ {
89
+ this.FileTuple.Language = value;
90
+ }
91
+ else
92
+ {
93
+ this.FileRow.Language = value;
94
+ }
95
+ }
96
+ }
97
+
98
+ public int? PatchGroup => this.FileRow == null ? this.FileTuple.PatchGroup : null;
99
+
100
+ public int Sequence
101
+ {
102
+ get => this.FileRow == null ? this.FileTuple.Sequence : this.FileRow.Sequence;
103
+ set
104
+ {
105
+ if (this.FileRow == null)
106
+ {
107
+ this.FileTuple.Sequence = value;
108
+ }
109
+ else
110
+ {
111
+ this.FileRow.Sequence = value;
112
+ }
113
+ }
114
+ }
115
+
116
+ public SourceLineNumber SourceLineNumber => this.FileRow == null ? this.FileTuple.SourceLineNumbers : this.FileRow.SourceLineNumbers;
117
+
118
+ public string SourcePath => this.FileRow == null ? this.FileTuple.Source.Path : this.FileRow.Source;
119
+
120
+ public bool Compressed => this.FileRow == null ? (this.FileTuple.Attributes & FileTupleAttributes.Compressed) == FileTupleAttributes.Compressed : (this.FileRow.Attributes & WindowsInstallerConstants.MsidbFileAttributesCompressed) == WindowsInstallerConstants.MsidbFileAttributesCompressed;
121
+
122
+ public bool Uncompressed => this.FileRow == null ? (this.FileTuple.Attributes & FileTupleAttributes.Uncompressed) == FileTupleAttributes.Uncompressed : (this.FileRow.Attributes & WindowsInstallerConstants.MsidbFileAttributesNoncompressed) == WindowsInstallerConstants.MsidbFileAttributesNoncompressed;
123
+
124
+ public string Version
125
+ {
126
+ get => this.FileRow == null ? this.FileTuple.Version : this.FileRow.Version;
127
+ set
128
+ {
129
+ if (this.FileRow == null)
130
+ {
131
+ this.FileTuple.Version = value;
132
+ }
133
+ else
134
+ {
135
+ this.FileRow.Version = value;
136
+ }
137
+ }
138
+ }
139
26
- public AssemblyTuple Assembly { get; }
140
+ public AssemblyType? AssemblyType => this.FileRow == null ? this.AssemblyTuple?.Type : throw new NotImplementedException();
141
28
- public int DiskId => this.File.DiskId ?? 0;
142
+ public string AssemblyApplicationFileRef => this.FileRow == null ? this.AssemblyTuple?.ApplicationFileRef : throw new NotImplementedException();
143
30
- public bool Uncompressed => (this.File.Attributes & FileTupleAttributes.Uncompressed) == FileTupleAttributes.Uncompressed;
144
+ public string AssemblyManifestFileRef => this.FileRow == null ? this.AssemblyTuple?.ManifestFileRef : throw new NotImplementedException();
145
146
/// <summary>
147
/// Gets the set of MsiAssemblyName rows created for this file.
src/WixToolset.Core/Bind/ResolveFieldsCommand.cs
+4
-4
@@ -89,7 +89,7 @@ namespace WixToolset.Core.Bind
89
{
90
var objectField = field.AsPath();
91
92
-#if REVISIT_FOR_PATCHING
92
+#if TODO_PATCHING
93
// Skip file resolution if the file is to be deleted.
94
if (RowOperation.Delete == tuple.Operation)
95
{
@@ -111,7 +111,7 @@ namespace WixToolset.Core.Bind
111
{
112
if (!this.BuildingPatch) // Normal binding for non-Patch scenario such as link (light.exe)
113
{
114
-#if REVISIT_FOR_PATCHING
114
+#if TODO_PATCHING
115
// keep a copy of the un-resolved data for future replay. This will be saved into wixpdb file
116
if (null == objectField.UnresolvedData)
117
{
@@ -129,7 +129,7 @@ namespace WixToolset.Core.Bind
129
var value = fileResolver.ResolveFile(objectField.Path, tuple.Definition, tuple.SourceLineNumbers, BindStage.Normal);
130
field.Set(value);
131
}
132
-#if REVISIT_FOR_PATCHING
132
+#if TODO_PATCHING
133
else // Re-base binding path scenario caused by pyro.exe -bt -bu
134
{
135
// by default, use the resolved Data for file lookup
@@ -158,7 +158,7 @@ namespace WixToolset.Core.Bind
158
}
159
}
160
161
-#if REVISIT_FOR_PATCHING
161
+#if TODO_PATCHING
162
if (null != objectField.PreviousData)
163
{
164
objectField.PreviousData = this.BindVariableResolver.ResolveVariables(tuple.SourceLineNumbers, objectField.PreviousData, false, out isDefault);
src/WixToolset.Core/Compiler.cs
+44
-4
@@ -7878,7 +7878,7 @@ namespace WixToolset.Core
7878
if (patch)
7879
{
7880
// /Patch/PatchProperty goes directly into MsiPatchMetadata table
7881
- this.Core.AddTuple(new MsiPatchMetadataTuple(sourceLineNumbers)
7881
+ this.Core.AddTuple(new MsiPatchMetadataTuple(sourceLineNumbers, new Identifier(AccessModifier.Public, company, name))
7882
{
7883
Company = company,
7884
Property = name,
@@ -8130,12 +8130,15 @@ namespace WixToolset.Core
8130
/// </summary>
8131
/// <param name="node">The element to parse.</param>
8132
/// <param name="diskId">Media index from parent element.</param>
8133
- private void ParsePatchBaselineElement(XElement node, int diskId)
8133
+ private void ParsePatchBaselineElement(XElement node, int? diskId)
8134
{
8135
var sourceLineNumbers = Preprocessor.GetSourceLineNumbers(node);
8136
Identifier id = null;
8137
var parsedValidate = false;
8138
var validationFlags = TransformFlags.PatchTransformDefault;
8139
+ string baselineFile = null;
8140
+ string updateFile = null;
8141
+ string transformFile = null;
8142
8143
foreach (var attrib in node.Attributes())
8144
{
@@ -8146,6 +8149,18 @@ namespace WixToolset.Core
8149
case "Id":
8150
id = this.Core.GetAttributeIdentifier(sourceLineNumbers, attrib);
8151
break;
8152
+ case "DiskId":
8153
+ diskId = this.Core.GetAttributeIntegerValue(sourceLineNumbers, attrib, 1, Int16.MaxValue);
8154
+ break;
8155
+ case "BaselineFile":
8156
+ baselineFile = this.Core.GetAttributeValue(sourceLineNumbers, attrib);
8157
+ break;
8158
+ case "UpdateFile":
8159
+ updateFile = this.Core.GetAttributeValue(sourceLineNumbers, attrib);
8160
+ break;
8161
+ case "TransformFile":
8162
+ transformFile = this.Core.GetAttributeValue(sourceLineNumbers, attrib);
8163
+ break;
8164
default:
8165
this.Core.UnexpectedAttribute(node, attrib);
8166
break;
@@ -8167,6 +8182,28 @@ namespace WixToolset.Core
8182
this.Core.Write(ErrorMessages.IdentifierTooLongError(sourceLineNumbers, node.Name.LocalName, "Id", id.Id, 27));
8183
}
8184
8185
+ if (!String.IsNullOrEmpty(baselineFile) || !String.IsNullOrEmpty(updateFile))
8186
+ {
8187
+ if (String.IsNullOrEmpty(baselineFile))
8188
+ {
8189
+ this.Core.Write(ErrorMessages.ExpectedAttribute(sourceLineNumbers, node.Name.LocalName, "BaselineFile", "UpdateFile"));
8190
+ }
8191
+
8192
+ if (String.IsNullOrEmpty(updateFile))
8193
+ {
8194
+ this.Core.Write(ErrorMessages.ExpectedAttribute(sourceLineNumbers, node.Name.LocalName, "UpdateFile", "BaselineFile"));
8195
+ }
8196
+
8197
+ if (!String.IsNullOrEmpty(transformFile))
8198
+ {
8199
+ this.Core.Write(ErrorMessages.IllegalAttributeWithOtherAttribute(sourceLineNumbers, node.Name.LocalName, "TransformFile", !String.IsNullOrEmpty(baselineFile) ? "BaselineFile" : "UpdateFile"));
8200
+ }
8201
+ }
8202
+ else if (String.IsNullOrEmpty(transformFile))
8203
+ {
8204
+ this.Core.Write(ErrorMessages.ExpectedAttribute(sourceLineNumbers, node.Name.LocalName, "BaselineFile", "TransformFile", true));
8205
+ }
8206
+
8207
foreach (var child in node.Elements())
8208
{
8209
if (CompilerCore.WixNamespace == child.Name.Namespace)
@@ -8200,8 +8237,11 @@ namespace WixToolset.Core
8237
{
8238
var tuple = new WixPatchBaselineTuple(sourceLineNumbers, id)
8239
{
8203
- DiskId = diskId,
8204
- ValidationFlags = validationFlags
8240
+ DiskId = diskId ?? 1,
8241
+ ValidationFlags = validationFlags,
8242
+ BaselineFile = new IntermediateFieldPathValue { Path = baselineFile },
8243
+ UpdateFile = new IntermediateFieldPathValue { Path = updateFile },
8244
+ TransformFile = new IntermediateFieldPathValue { Path = transformFile }
8245
};
8246
8247
this.Core.AddTuple(tuple);
src/WixToolset.Core/Compiler_2.cs
-6
@@ -1028,12 +1028,6 @@ namespace WixToolset.Core
1028
Value = "0"
1029
});
1030
1031
- this.Core.AddTuple(new SummaryInformationTuple(sourceLineNumbers)
1032
- {
1033
- PropertyId = SumaryInformationType.WindowsInstallerVersion,
1034
- Value = msiVersion.ToString(CultureInfo.InvariantCulture)
1035
- });
1036
-
1031
this.Core.AddTuple(new SummaryInformationTuple(sourceLineNumbers)
1032
{
1033
PropertyId = SumaryInformationType.Security,
src/WixToolset.Core/Compiler_Patch.cs
+1
-2
@@ -197,9 +197,8 @@ namespace WixToolset.Core
197
198
if (!this.Core.EncounteredError)
199
{
200
- var tuple = new WixPatchIdTuple(sourceLineNumbers)
200
+ var tuple = new WixPatchIdTuple(sourceLineNumbers, new Identifier(AccessModifier.Public, patchId))
201
{
202
- ProductCode = patchId,
202
ClientPatchId = clientPatchId,
203
OptimizePatchSizeForLargeFiles = optimizePatchSizeForLargeFiles,
204
ApiPatchingSymbolFlags = apiPatchingSymbolFlags
src/WixToolset.Core/Compiler_UI.cs
+3
-3
@@ -470,7 +470,7 @@ namespace WixToolset.Core
470
string defaultControl = null;
471
string cancelControl = null;
472
473
- this.ParseControlElement(child, id.Id, TupleDefinitionType.BBControl, ref lastTabRow, ref firstControl, ref defaultControl, ref cancelControl, false);
473
+ this.ParseControlElement(child, id.Id, TupleDefinitionType.BBControl, ref lastTabRow, ref firstControl, ref defaultControl, ref cancelControl);
474
break;
475
default:
476
this.Core.UnexpectedElement(node, child);
@@ -966,7 +966,7 @@ namespace WixToolset.Core
966
switch (child.Name.LocalName)
967
{
968
case "Control":
969
- this.ParseControlElement(child, id.Id, TupleDefinitionType.Control, ref lastTabRow, ref firstControl, ref defaultControl, ref cancelControl, trackDiskSpace);
969
+ this.ParseControlElement(child, id.Id, TupleDefinitionType.Control, ref lastTabRow, ref firstControl, ref defaultControl, ref cancelControl);
970
break;
971
default:
972
this.Core.UnexpectedElement(node, child);
@@ -1032,7 +1032,7 @@ namespace WixToolset.Core
1032
/// <param name="defaultControl">Name of the default control.</param>
1033
/// <param name="cancelControl">Name of the candle control.</param>
1034
/// <param name="trackDiskSpace">True if the containing dialog tracks disk space.</param>
1035
- private void ParseControlElement(XElement node, string dialog, TupleDefinitionType tupleType, ref IntermediateTuple lastTabTuple, ref string firstControl, ref string defaultControl, ref string cancelControl, bool trackDiskSpace)
1035
+ private void ParseControlElement(XElement node, string dialog, TupleDefinitionType tupleType, ref IntermediateTuple lastTabTuple, ref string firstControl, ref string defaultControl, ref string cancelControl)
1036
{
1037
var sourceLineNumbers = Preprocessor.GetSourceLineNumbers(node);
1038
Identifier controlId = null;
src/WixToolset.Core/Resolver.cs
+1
-1
@@ -92,7 +92,7 @@ namespace WixToolset.Core
92
delayedFields = command.DelayedFields;
93
}
94
95
-#if REVISIT_FOR_PATCHING
95
+#if TODO_PATCHING
96
if (context.IntermediateRepresentation.SubStorages != null)
97
{
98
foreach (SubStorage transform in context.IntermediateRepresentation.SubStorages)
src/test/WixToolsetTest.CoreIntegration/PatchFixture.cs
new
+112
@@ -0,0 +1,112 @@
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 WixToolsetTest.CoreIntegration
4
+{
5
+ using System.ComponentModel;
6
+ using System.IO;
7
+ using System.Linq;
8
+ using System.Runtime.InteropServices;
9
+ using System.Text;
10
+ using System.Xml.Linq;
11
+ using WixBuildTools.TestSupport;
12
+ using WixToolset.Core.TestPackage;
13
+ using Xunit;
14
+
15
+ public class PatchFixture
16
+ {
17
+ private static readonly XNamespace PatchNamespace = "http://www.microsoft.com/msi/patch_applicability.xsd";
18
+
19
+ [Fact(Skip = "Skip until patches have files in them")]
20
+ public void CanBuildSimplePatch()
21
+ {
22
+ var folder = TestData.Get(@"TestData\PatchSingle");
23
+
24
+ using (var fs = new DisposableFileSystem())
25
+ {
26
+ var tempFolder = fs.GetFolder();
27
+
28
+ var baselinePdb = BuildMsi("Baseline.msi", folder, tempFolder, "1.0.0", "1.0.0", "1.0.0");
29
+ var update1Pdb = BuildMsi("Update.msi", folder, tempFolder, "1.0.1", "1.0.1", "1.0.1");
30
+ var patchPdb = BuildMsp("Patch1.msp", folder, tempFolder, "1.0.1");
31
+ var baselinePath = Path.ChangeExtension(baselinePdb, ".msp");
32
+ var patchPath = Path.ChangeExtension(patchPdb, ".msp");
33
+
34
+ Assert.True(File.Exists(baselinePdb));
35
+ Assert.True(File.Exists(update1Pdb));
36
+
37
+ var doc = GetExtractPatchXml(patchPath);
38
+ Assert.Equal("{7D326855-E790-4A94-8611-5351F8321FCA}", doc.Root.Element(PatchNamespace + "TargetProductCode").Value);
39
+
40
+ var names = Query.GetSubStorageNames(patchPath);
41
+ Assert.Equal(new[] { "#RTM.1", "RTM.1" }, names);
42
+
43
+ var cab = Path.Combine(tempFolder, "foo.cab");
44
+ Query.ExtractStream(patchPath, "foo.cab", cab);
45
+ Assert.True(File.Exists(cab));
46
+
47
+ var files = Query.GetCabinetFiles(cab);
48
+ Assert.Equal(new[] { "a", "b" }, files.Select(f => f.ArchiveName).ToArray()); // This test may not be quite correct, yet.
49
+ }
50
+ }
51
+
52
+ private static string BuildMsi(string outputName, string sourceFolder, string baseFolder, string defineV, string defineA, string defineB)
53
+ {
54
+ var outputPath = Path.Combine(baseFolder, Path.Combine("bin", outputName));
55
+
56
+ var result = WixRunner.Execute(new[]
57
+ {
58
+ "build",
59
+ Path.Combine(sourceFolder, @"Package.wxs"),
60
+ "-d", "V=" + defineV,
61
+ "-d", "A=" + defineA,
62
+ "-d", "B=" + defineB,
63
+ "-bindpath", Path.Combine(sourceFolder, ".data"),
64
+ "-intermediateFolder", Path.Combine(baseFolder, "obj"),
65
+ "-o", outputPath
66
+ });
67
+
68
+ result.AssertSuccess();
69
+
70
+ return Path.ChangeExtension(outputPath, ".wixpdb");
71
+ }
72
+
73
+ private static string BuildMsp(string outputName, string sourceFolder, string baseFolder, string defineV)
74
+ {
75
+ var outputPath = Path.Combine(baseFolder, Path.Combine("bin", outputName));
76
+
77
+ var result = WixRunner.Execute(new[]
78
+ {
79
+ "build",
80
+ Path.Combine(sourceFolder, @"Patch.wxs"),
81
+ "-d", "V=" + defineV,
82
+ "-bindpath", Path.Combine(baseFolder, "bin"),
83
+ "-intermediateFolder", Path.Combine(baseFolder, "obj"),
84
+ "-o", outputPath
85
+ });
86
+
87
+ result.AssertSuccess();
88
+
89
+ return Path.ChangeExtension(outputPath, ".wixpdb");
90
+ }
91
+
92
+ private static XDocument GetExtractPatchXml(string path)
93
+ {
94
+ var buffer = new StringBuilder(65535);
95
+ var size = buffer.Capacity;
96
+
97
+ var er = MsiExtractPatchXMLData(path, 0, buffer, ref size);
98
+ if (er != 0)
99
+ {
100
+ throw new Win32Exception(er);
101
+ }
102
+
103
+ return XDocument.Parse(buffer.ToString());
104
+ }
105
+
106
+ [DllImport("msi.dll", EntryPoint = "MsiExtractPatchXMLDataW", CharSet = CharSet.Unicode, ExactSpelling = true)]
107
+ private static extern int MsiExtractPatchXMLData(string szPatchPath, int dwReserved, StringBuilder szXMLData, ref int pcchXMLData);
108
+
109
+ [DllImport("msi.dll", EntryPoint = "MsiApplyPatchW", CharSet = CharSet.Unicode, ExactSpelling = true)]
110
+ private static extern int MsiApplyPatch(string szPatchPackage, string szInstallPackage, int eInstallType, string szCommandLine);
111
+ }
112
+}
src/test/WixToolsetTest.CoreIntegration/PreprocessorFixture.cs
-28
@@ -67,34 +67,6 @@ namespace WixToolsetTest.CoreIntegration
67
}
68
}
69
70
- [Fact]
71
- public void WixVersionVariablesWork()
72
- {
73
- var folder = TestData.Get(@"TestData\Variables");
74
-
75
- using (var fs = new DisposableFileSystem())
76
- {
77
- var baseFolder = fs.GetFolder();
78
- var intermediateFolder = Path.Combine(baseFolder, "obj");
79
-
80
- var result = WixRunner.Execute(new[]
81
- {
82
- "build",
83
- Path.Combine(folder, "Package.wxs"),
84
- Path.Combine(folder, "PackageComponents.wxs"),
85
- "-loc", Path.Combine(folder, "Package.en-us.wxl"),
86
- "-bindpath", Path.Combine(folder, "data"),
87
- "-intermediateFolder", intermediateFolder,
88
- "-o", Path.Combine(baseFolder, @"bin\test.msi")
89
- });
90
-
91
- result.AssertSuccess();
92
-
93
- var warning = result.Messages.Where(message => message.Id == (int)WarningMessages.Ids.PreprocessorWarning);
94
- Assert.Single(warning);
95
- }
96
- }
97
-
70
[Fact]
71
public void ForEachLoopsWork()
72
{
src/test/WixToolsetTest.CoreIntegration/TestData/PatchFamilyFilter/.data/Av1.0.0.txt
new
+1
@@ -0,0 +1 @@
1
+This is A v1.0.0
src/test/WixToolsetTest.CoreIntegration/TestData/PatchFamilyFilter/.data/Av1.0.1.txt
new
+1
@@ -0,0 +1 @@
1
+This ia A v1.0.1
src/test/WixToolsetTest.CoreIntegration/TestData/PatchFamilyFilter/.data/Bv1.0.0.txt
new
+1
@@ -0,0 +1 @@
1
+This is B v1.0.0
src/test/WixToolsetTest.CoreIntegration/TestData/PatchFamilyFilter/.data/Bv1.0.1.txt
new
+1
@@ -0,0 +1 @@
1
+This ia B v1.0.1
src/test/WixToolsetTest.CoreIntegration/TestData/PatchFamilyFilter/Package.wxs
new
+31
@@ -0,0 +1,31 @@
1
+<Wix xmlns='http://schemas.microsoft.com/wix/2006/wi'>
2
+ <Product Id='e703bf17-4765-444c-91fd-88550fa681d4' Name='~Test Package'
3
+ Version='$(var.V)' Manufacturer='Example Corporation' Language='1033'
4
+ UpgradeCode='e703bf17-4765-444c-91fd-88550fa681d4'>
5
+ <Package InstallScope='perMachine' />
6
+
7
+ <MajorUpgrade DowngradeErrorMessage='Newer version already installed.' />
8
+ <MediaTemplate />
9
+
10
+ <Directory Id='TARGETDIR' Name='SourceDir'>
11
+ <Directory Id='ProgramFilesFolder'>
12
+ <Directory Id='INSTALLFOLDER' Name='~Test App' />
13
+ </Directory>
14
+ </Directory>
15
+
16
+ <Feature Id='Main'>
17
+ <ComponentGroupRef Id='Components' />
18
+ </Feature>
19
+ </Product>
20
+
21
+ <Fragment>
22
+ <ComponentGroup Id="Components" Directory='INSTALLFOLDER'>
23
+ <Component Id='A'>
24
+ <File Name='a.txt' Source='Av$(var.A).txt' />
25
+ </Component>
26
+ <Component Id='B'>
27
+ <File Name='b.txt' Source='Bv$(var.B).txt' />
28
+ </Component>
29
+ </ComponentGroup>
30
+ </Fragment>
31
+</Wix>
src/test/WixToolsetTest.CoreIntegration/TestData/PatchFamilyFilter/Patch.wxs
new
+23
@@ -0,0 +1,23 @@
1
+<Wix xmlns="http://schemas.microsoft.com/wix/2006/wi">
2
+ <Patch
3
+ AllowRemoval="yes"
4
+ Manufacturer="FireGiant"
5
+ MoreInfoURL="http://www.example.com/"
6
+ DisplayName="~Test Patch v$(var.V)"
7
+ Description="~Test Small Update Patch v($var.V)"
8
+ Classification="Update"
9
+ >
10
+
11
+ <Media Id="1" Cabinet="foo.cab">
12
+ <PatchBaseline Id="RTM"/>
13
+ </Media>
14
+
15
+ <PatchFamilyRef Id='SamplePatchFamily'/>
16
+ </Patch>
17
+
18
+ <Fragment>
19
+ <PatchFamily Id='SamplePatchFamily' Version='$(var.V)' Supersede='yes'>
20
+ <ComponentRef Id="A"/>
21
+ </PatchFamily>
22
+ </Fragment>
23
+</Wix>
src/test/WixToolsetTest.CoreIntegration/TestData/PatchSingle/.data/Av1.0.0.txt
new
+1
@@ -0,0 +1 @@
1
+This is A v1.0.0
src/test/WixToolsetTest.CoreIntegration/TestData/PatchSingle/.data/Av1.0.1.txt
new
+1
@@ -0,0 +1 @@
1
+This ia A v1.0.1
src/test/WixToolsetTest.CoreIntegration/TestData/PatchSingle/.data/Bv1.0.0.txt
new
+1
@@ -0,0 +1 @@
1
+This is B v1.0.0
src/test/WixToolsetTest.CoreIntegration/TestData/PatchSingle/.data/Bv1.0.1.txt
new
+1
@@ -0,0 +1 @@
1
+This ia B v1.0.1
src/test/WixToolsetTest.CoreIntegration/TestData/PatchSingle/Package.wxs
new
+31
@@ -0,0 +1,31 @@
1
+<Wix xmlns='http://wixtoolset.org/schemas/v4/wxs'>
2
+ <Product Id='7d326855-e790-4a94-8611-5351f8321fca' Name='~Test Package'
3
+ Version='$(var.V)' Manufacturer='Example Corporation' Language='1033'
4
+ UpgradeCode='7d326855-e790-4a94-8611-5351f8321fca'>
5
+ <Package InstallScope='perMachine' Compressed='yes' />
6
+
7
+ <MajorUpgrade DowngradeErrorMessage='Newer version already installed.' />
8
+ <MediaTemplate EmbedCab='yes' />
9
+
10
+ <Directory Id='TARGETDIR' Name='SourceDir'>
11
+ <Directory Id='ProgramFilesFolder'>
12
+ <Directory Id='INSTALLFOLDER' Name='~Test App' />
13
+ </Directory>
14
+ </Directory>
15
+
16
+ <Feature Id='Main'>
17
+ <ComponentGroupRef Id='Components' />
18
+ </Feature>
19
+ </Product>
20
+
21
+ <Fragment>
22
+ <ComponentGroup Id="Components" Directory='INSTALLFOLDER'>
23
+ <Component>
24
+ <File Id='a.txt' Name='a.txt' Source='Av$(var.A).txt' />
25
+ </Component>
26
+ <Component>
27
+ <File Id='b.txt' Name='b.txt' Source='Bv$(var.B).txt' />
28
+ </Component>
29
+ </ComponentGroup>
30
+ </Fragment>
31
+</Wix>
src/test/WixToolsetTest.CoreIntegration/TestData/PatchSingle/Patch.wxs
new
+16
@@ -0,0 +1,16 @@
1
+<Wix xmlns='http://wixtoolset.org/schemas/v4/wxs'>
2
+ <Patch
3
+ AllowRemoval="yes"
4
+ DisplayName="~Test Patch v$(var.V)"
5
+ Description="~Test Small Update Patch v$(var.V)"
6
+ MoreInfoURL="http://www.example.com/"
7
+ Manufacturer="Example Corporation"
8
+ Classification="Update">
9
+
10
+ <Media Id="1" Cabinet="foo.cab">
11
+ <PatchBaseline Id="RTM" BaselineFile="Baseline.wixpdb" UpdateFile="Update.wixpdb" />
12
+ </Media>
13
+
14
+ <PatchFamily Id='SequenceFamily' Version='$(var.V)' />
15
+ </Patch>
16
+</Wix>
src/test/WixToolsetTest.CoreIntegration/WixToolsetTest.CoreIntegration.csproj
+13
-1
@@ -1,4 +1,4 @@
1
-<?xml version="1.0" encoding="utf-8"?>
1
+<?xml version="1.0" encoding="utf-8"?>
2
<!-- 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. -->
3
4
<Project Sdk="Microsoft.NET.Sdk">
@@ -41,6 +41,18 @@
41
<Content Include="TestData\Font\TrueType.wxs" CopyToOutputDirectory="PreserveNewest" />
42
<Content Include="TestData\Icon\SampleIcon.wxs" CopyToOutputDirectory="PreserveNewest" />
43
<Content Include="TestData\LockPermissions\EmptyPermissions.wxs" CopyToOutputDirectory="PreserveNewest" />
44
+ <Content Include="TestData\PatchFamilyFilter\.data\Av1.0.0.txt" CopyToOutputDirectory="PreserveNewest" />
45
+ <Content Include="TestData\PatchFamilyFilter\.data\Av1.0.1.txt" CopyToOutputDirectory="PreserveNewest" />
46
+ <Content Include="TestData\PatchFamilyFilter\.data\Bv1.0.0.txt" CopyToOutputDirectory="PreserveNewest" />
47
+ <Content Include="TestData\PatchFamilyFilter\.data\Bv1.0.1.txt" CopyToOutputDirectory="PreserveNewest" />
48
+ <Content Include="TestData\PatchFamilyFilter\Package.wxs" CopyToOutputDirectory="PreserveNewest" />
49
+ <Content Include="TestData\PatchFamilyFilter\Patch.wxs" CopyToOutputDirectory="PreserveNewest" />
50
+ <Content Include="TestData\PatchSingle\.data\Av1.0.0.txt" CopyToOutputDirectory="PreserveNewest" />
51
+ <Content Include="TestData\PatchSingle\.data\Av1.0.1.txt" CopyToOutputDirectory="PreserveNewest" />
52
+ <Content Include="TestData\PatchSingle\.data\Bv1.0.0.txt" CopyToOutputDirectory="PreserveNewest" />
53
+ <Content Include="TestData\PatchSingle\.data\Bv1.0.1.txt" CopyToOutputDirectory="PreserveNewest" />
54
+ <Content Include="TestData\PatchSingle\Package.wxs" CopyToOutputDirectory="PreserveNewest" />
55
+ <Content Include="TestData\PatchSingle\Patch.wxs" CopyToOutputDirectory="PreserveNewest" />
56
<Content Include="TestData\SameFileFolders\data\a\test.txt" CopyToOutputDirectory="PreserveNewest" />
57
<Content Include="TestData\SameFileFolders\data\c\test.txt" CopyToOutputDirectory="PreserveNewest" />
58
<Content Include="TestData\SameFileFolders\data\b\test.txt" CopyToOutputDirectory="PreserveNewest" />