diff options
Diffstat (limited to 'src')
58 files changed, 4481 insertions, 1192 deletions
diff --git a/src/WixToolset.Core.Burn/Bind/BindBundleCommand.cs b/src/WixToolset.Core.Burn/Bind/BindBundleCommand.cs index c2164744..283cd115 100644 --- a/src/WixToolset.Core.Burn/Bind/BindBundleCommand.cs +++ b/src/WixToolset.Core.Burn/Bind/BindBundleCommand.cs | |||
| @@ -208,7 +208,6 @@ namespace WixToolset.Core.Burn | |||
| 208 | variableCache.Add(String.Concat("packageManufacturer.", facade.PackageId), msiPackage.Manufacturer); | 208 | variableCache.Add(String.Concat("packageManufacturer.", facade.PackageId), msiPackage.Manufacturer); |
| 209 | } | 209 | } |
| 210 | } | 210 | } |
| 211 | |||
| 212 | } | 211 | } |
| 213 | break; | 212 | break; |
| 214 | 213 | ||
diff --git a/src/WixToolset.Core.Burn/Bundles/CreateNonUXContainers.cs b/src/WixToolset.Core.Burn/Bundles/CreateNonUXContainers.cs index 612e0e11..34e601a7 100644 --- a/src/WixToolset.Core.Burn/Bundles/CreateNonUXContainers.cs +++ b/src/WixToolset.Core.Burn/Bundles/CreateNonUXContainers.cs | |||
| @@ -113,7 +113,7 @@ namespace WixToolset.Core.Burn.Bundles | |||
| 113 | ++attachedContainerIndex; | 113 | ++attachedContainerIndex; |
| 114 | } | 114 | } |
| 115 | 115 | ||
| 116 | this.CreateContainer(container, containerPayloads, null); | 116 | this.CreateContainer(container, containerPayloads); |
| 117 | } | 117 | } |
| 118 | } | 118 | } |
| 119 | 119 | ||
| @@ -122,7 +122,7 @@ namespace WixToolset.Core.Burn.Bundles | |||
| 122 | this.FileTransfers = fileTransfers; | 122 | this.FileTransfers = fileTransfers; |
| 123 | } | 123 | } |
| 124 | 124 | ||
| 125 | private void CreateContainer(WixBundleContainerTuple container, IEnumerable<WixBundlePayloadTuple> containerPayloads, string manifestFile) | 125 | private void CreateContainer(WixBundleContainerTuple container, IEnumerable<WixBundlePayloadTuple> containerPayloads) |
| 126 | { | 126 | { |
| 127 | var command = new CreateContainerCommand(containerPayloads, container.WorkingPath, this.DefaultCompressionLevel); | 127 | var command = new CreateContainerCommand(containerPayloads, container.WorkingPath, this.DefaultCompressionLevel); |
| 128 | command.Execute(); | 128 | command.Execute(); |
diff --git a/src/WixToolset.Core.WindowsInstaller/Bind/AssignMediaCommand.cs b/src/WixToolset.Core.WindowsInstaller/Bind/AssignMediaCommand.cs index 2199bbde..2bfd587f 100644 --- a/src/WixToolset.Core.WindowsInstaller/Bind/AssignMediaCommand.cs +++ b/src/WixToolset.Core.WindowsInstaller/Bind/AssignMediaCommand.cs | |||
| @@ -57,7 +57,7 @@ namespace WixToolset.Core.WindowsInstaller.Bind | |||
| 57 | 57 | ||
| 58 | var mediaRows = new Dictionary<int, MediaTuple>(); | 58 | var mediaRows = new Dictionary<int, MediaTuple>(); |
| 59 | 59 | ||
| 60 | List<FileFacade> uncompressedFiles = new List<FileFacade>(); | 60 | var uncompressedFiles = new List<FileFacade>(); |
| 61 | 61 | ||
| 62 | var mediaTable = this.Section.Tuples.OfType<MediaTuple>().ToList(); | 62 | var mediaTable = this.Section.Tuples.OfType<MediaTuple>().ToList(); |
| 63 | var mediaTemplateTable = this.Section.Tuples.OfType<WixMediaTemplateTuple>().ToList(); | 63 | var mediaTemplateTable = this.Section.Tuples.OfType<WixMediaTemplateTuple>().ToList(); |
| @@ -109,8 +109,8 @@ namespace WixToolset.Core.WindowsInstaller.Bind | |||
| 109 | 109 | ||
| 110 | ulong currentPreCabSize = 0; | 110 | ulong currentPreCabSize = 0; |
| 111 | ulong maxPreCabSizeInBytes; | 111 | ulong maxPreCabSizeInBytes; |
| 112 | int maxPreCabSizeInMB = 0; | 112 | var maxPreCabSizeInMB = 0; |
| 113 | int currentCabIndex = 0; | 113 | var currentCabIndex = 0; |
| 114 | 114 | ||
| 115 | MediaTuple currentMediaRow = null; | 115 | MediaTuple currentMediaRow = null; |
| 116 | 116 | ||
| @@ -131,7 +131,7 @@ namespace WixToolset.Core.WindowsInstaller.Bind | |||
| 131 | this.CabinetNameTemplate = mediaTemplateRow.CabinetTemplate; | 131 | this.CabinetNameTemplate = mediaTemplateRow.CabinetTemplate; |
| 132 | } | 132 | } |
| 133 | 133 | ||
| 134 | string mumsString = Environment.GetEnvironmentVariable("WIX_MUMS"); | 134 | var mumsString = Environment.GetEnvironmentVariable("WIX_MUMS"); |
| 135 | 135 | ||
| 136 | try | 136 | try |
| 137 | { | 137 | { |
| @@ -170,13 +170,13 @@ namespace WixToolset.Core.WindowsInstaller.Bind | |||
| 170 | { | 170 | { |
| 171 | // Associate current file with last cab (irrespective of the size) and cab index is not incremented anymore. | 171 | // Associate current file with last cab (irrespective of the size) and cab index is not incremented anymore. |
| 172 | var cabinetFiles = filesByCabinetMedia[currentMediaRow]; | 172 | var cabinetFiles = filesByCabinetMedia[currentMediaRow]; |
| 173 | facade.File.DiskId = currentCabIndex; | 173 | facade.DiskId = currentCabIndex; |
| 174 | cabinetFiles.Add(facade); | 174 | cabinetFiles.Add(facade); |
| 175 | continue; | 175 | continue; |
| 176 | } | 176 | } |
| 177 | 177 | ||
| 178 | // Update current cab size. | 178 | // Update current cab size. |
| 179 | currentPreCabSize += (ulong)facade.File.FileSize; | 179 | currentPreCabSize += (ulong)facade.FileSize; |
| 180 | 180 | ||
| 181 | if (currentPreCabSize > maxPreCabSizeInBytes) | 181 | if (currentPreCabSize > maxPreCabSizeInBytes) |
| 182 | { | 182 | { |
| @@ -186,10 +186,10 @@ namespace WixToolset.Core.WindowsInstaller.Bind | |||
| 186 | filesByCabinetMedia.Add(currentMediaRow, new List<FileFacade>()); | 186 | filesByCabinetMedia.Add(currentMediaRow, new List<FileFacade>()); |
| 187 | 187 | ||
| 188 | var cabinetFileRows = filesByCabinetMedia[currentMediaRow]; | 188 | var cabinetFileRows = filesByCabinetMedia[currentMediaRow]; |
| 189 | facade.File.DiskId = currentCabIndex; | 189 | facade.DiskId = currentCabIndex; |
| 190 | cabinetFileRows.Add(facade); | 190 | cabinetFileRows.Add(facade); |
| 191 | // Now files larger than MaxUncompressedMediaSize will be the only file in its cabinet so as to respect MaxUncompressedMediaSize | 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 | } | 193 | } |
| 194 | else | 194 | else |
| 195 | { | 195 | { |
| @@ -204,7 +204,7 @@ namespace WixToolset.Core.WindowsInstaller.Bind | |||
| 204 | 204 | ||
| 205 | // Associate current file with current cab. | 205 | // Associate current file with current cab. |
| 206 | var cabinetFiles = filesByCabinetMedia[currentMediaRow]; | 206 | var cabinetFiles = filesByCabinetMedia[currentMediaRow]; |
| 207 | facade.File.DiskId = currentCabIndex; | 207 | facade.DiskId = currentCabIndex; |
| 208 | cabinetFiles.Add(facade); | 208 | cabinetFiles.Add(facade); |
| 209 | } | 209 | } |
| 210 | } | 210 | } |
| @@ -260,18 +260,18 @@ namespace WixToolset.Core.WindowsInstaller.Bind | |||
| 260 | } | 260 | } |
| 261 | } | 261 | } |
| 262 | 262 | ||
| 263 | foreach (FileFacade facade in fileFacades) | 263 | foreach (var facade in fileFacades) |
| 264 | { | 264 | { |
| 265 | if (!mediaRows.TryGetValue(facade.DiskId, out var mediaRow)) | 265 | if (!mediaRows.TryGetValue(facade.DiskId, out var mediaRow)) |
| 266 | { | 266 | { |
| 267 | this.Messaging.Write(ErrorMessages.MissingMedia(facade.File.SourceLineNumbers, facade.DiskId)); | 267 | this.Messaging.Write(ErrorMessages.MissingMedia(facade.SourceLineNumber, facade.DiskId)); |
| 268 | continue; | 268 | continue; |
| 269 | } | 269 | } |
| 270 | 270 | ||
| 271 | // When building a product, if the current file is to be uncompressed or if | 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. | 272 | // the package set not to be compressed, don't cab it. |
| 273 | var compressed = (facade.File.Attributes & FileTupleAttributes.Compressed) == FileTupleAttributes.Compressed; | 273 | var compressed = facade.Compressed; |
| 274 | var uncompressed = (facade.File.Attributes & FileTupleAttributes.Uncompressed) == FileTupleAttributes.Uncompressed; | 274 | var uncompressed = facade.Uncompressed; |
| 275 | if (SectionType.Product == this.Section.Type && (uncompressed || (!compressed && !this.FilesCompressed))) | 275 | if (SectionType.Product == this.Section.Type && (uncompressed || (!compressed && !this.FilesCompressed))) |
| 276 | { | 276 | { |
| 277 | uncompressedFiles.Add(facade); | 277 | uncompressedFiles.Add(facade); |
| @@ -284,7 +284,7 @@ namespace WixToolset.Core.WindowsInstaller.Bind | |||
| 284 | } | 284 | } |
| 285 | else | 285 | else |
| 286 | { | 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 | } | 288 | } |
| 289 | } | 289 | } |
| 290 | } | 290 | } |
diff --git a/src/WixToolset.Core.WindowsInstaller/Bind/AttachPatchTransformsCommand.cs b/src/WixToolset.Core.WindowsInstaller/Bind/AttachPatchTransformsCommand.cs new file mode 100644 index 00000000..aa5ca20a --- /dev/null +++ b/src/WixToolset.Core.WindowsInstaller/Bind/AttachPatchTransformsCommand.cs | |||
| @@ -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 | } | ||
diff --git a/src/WixToolset.Core.WindowsInstaller/Bind/BindDatabaseCommand.cs b/src/WixToolset.Core.WindowsInstaller/Bind/BindDatabaseCommand.cs index 175203ce..34104ef5 100644 --- a/src/WixToolset.Core.WindowsInstaller/Bind/BindDatabaseCommand.cs +++ b/src/WixToolset.Core.WindowsInstaller/Bind/BindDatabaseCommand.cs | |||
| @@ -24,7 +24,11 @@ namespace WixToolset.Core.WindowsInstaller.Bind | |||
| 24 | 24 | ||
| 25 | private bool disposed; | 25 | private bool disposed; |
| 26 | 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) | ||
| 28 | { | 32 | { |
| 29 | this.ServiceProvider = context.ServiceProvider; | 33 | this.ServiceProvider = context.ServiceProvider; |
| 30 | 34 | ||
| @@ -45,6 +49,7 @@ namespace WixToolset.Core.WindowsInstaller.Bind | |||
| 45 | this.OutputPath = context.OutputPath; | 49 | this.OutputPath = context.OutputPath; |
| 46 | this.OutputPdbPath = context.OutputPdbPath; | 50 | this.OutputPdbPath = context.OutputPdbPath; |
| 47 | this.IntermediateFolder = context.IntermediateFolder; | 51 | this.IntermediateFolder = context.IntermediateFolder; |
| 52 | this.SubStorages = subStorages; | ||
| 48 | this.Validator = validator; | 53 | this.Validator = validator; |
| 49 | 54 | ||
| 50 | this.BackendExtensions = backendExtension; | 55 | this.BackendExtensions = backendExtension; |
| @@ -76,6 +81,8 @@ namespace WixToolset.Core.WindowsInstaller.Bind | |||
| 76 | 81 | ||
| 77 | private IEnumerable<IWindowsInstallerBackendBinderExtension> BackendExtensions { get; } | 82 | private IEnumerable<IWindowsInstallerBackendBinderExtension> BackendExtensions { get; } |
| 78 | 83 | ||
| 84 | private IEnumerable<SubStorage> SubStorages { get; } | ||
| 85 | |||
| 79 | private Intermediate Intermediate { get; } | 86 | private Intermediate Intermediate { get; } |
| 80 | 87 | ||
| 81 | private string OutputPath { get; } | 88 | private string OutputPath { get; } |
| @@ -112,18 +119,10 @@ namespace WixToolset.Core.WindowsInstaller.Bind | |||
| 112 | // Load standard tables, authored custom tables, and extension custom tables. | 119 | // Load standard tables, authored custom tables, and extension custom tables. |
| 113 | TableDefinitionCollection tableDefinitions; | 120 | TableDefinitionCollection tableDefinitions; |
| 114 | { | 121 | { |
| 115 | var command = new LoadTableDefinitionsCommand(section); | 122 | var command = new LoadTableDefinitionsCommand(section, this.BackendExtensions); |
| 116 | command.Execute(); | 123 | command.Execute(); |
| 117 | 124 | ||
| 118 | tableDefinitions = command.TableDefinitions; | 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 | } | ||
| 127 | } | 126 | } |
| 128 | 127 | ||
| 129 | // Process the summary information table before the other tables. | 128 | // Process the summary information table before the other tables. |
| @@ -186,8 +185,7 @@ namespace WixToolset.Core.WindowsInstaller.Bind | |||
| 186 | 185 | ||
| 187 | // Sequence all the actions. | 186 | // Sequence all the actions. |
| 188 | { | 187 | { |
| 189 | var command = new SequenceActionsCommand(section); | 188 | var command = new SequenceActionsCommand(this.Messaging, section); |
| 190 | command.Messaging = this.Messaging; | ||
| 191 | command.Execute(); | 189 | command.Execute(); |
| 192 | } | 190 | } |
| 193 | 191 | ||
| @@ -196,7 +194,7 @@ namespace WixToolset.Core.WindowsInstaller.Bind | |||
| 196 | command.Execute(); | 194 | command.Execute(); |
| 197 | } | 195 | } |
| 198 | 196 | ||
| 199 | #if TODO_FINISH_PATCH | 197 | #if TODO_PATCHING |
| 200 | ////if (OutputType.Patch == this.Output.Type) | 198 | ////if (OutputType.Patch == this.Output.Type) |
| 201 | ////{ | 199 | ////{ |
| 202 | //// foreach (SubStorage substorage in this.Output.SubStorages) | 200 | //// foreach (SubStorage substorage in this.Output.SubStorages) |
| @@ -223,130 +221,162 @@ namespace WixToolset.Core.WindowsInstaller.Bind | |||
| 223 | return; | 221 | return; |
| 224 | } | 222 | } |
| 225 | 223 | ||
| 226 | this.Messaging.Write(VerboseMessages.UpdatingFileInformation()); | 224 | // Call extension |
| 225 | var ExtensionSaidSkip = false; | ||
| 227 | 226 | ||
| 228 | // This must occur after all variables and source paths have been resolved. | 227 | WindowsInstallerData output; |
| 229 | List<FileFacade> fileFacades; | 228 | if (ExtensionSaidSkip) |
| 230 | { | 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(); | 232 | command.Execute(); |
| 233 | 233 | ||
| 234 | fileFacades = command.FileFacades; | 234 | output = command.Output; |
| 235 | } | 235 | } |
| 236 | 236 | else | |
| 237 | // Extract files that come from binary .wixlibs and WixExtensions (this does not extract files from merge modules). | ||
| 238 | { | 237 | { |
| 239 | var command = new ExtractEmbeddedFilesCommand(this.ExpectedEmbeddedFiles); | 238 | this.Messaging.Write(VerboseMessages.UpdatingFileInformation()); |
| 240 | command.Execute(); | ||
| 241 | } | ||
| 242 | 239 | ||
| 243 | // Gather information about files that do not come from merge modules. | 240 | // Extract files that come from binary .wixlibs and WixExtensions (this does not extract files from merge modules). |
| 244 | { | 241 | { |
| 245 | var command = new UpdateFileFacadesCommand(this.Messaging, section); | 242 | var command = new ExtractEmbeddedFilesCommand(this.ExpectedEmbeddedFiles); |
| 246 | command.FileFacades = fileFacades; | 243 | command.Execute(); |
| 247 | command.UpdateFileFacades = fileFacades.Where(f => !f.FromModule); | 244 | } |
| 248 | command.OverwriteHash = true; | ||
| 249 | command.TableDefinitions = tableDefinitions; | ||
| 250 | command.VariableCache = variableCache; | ||
| 251 | command.Execute(); | ||
| 252 | } | ||
| 253 | 245 | ||
| 254 | // Now that the variable cache is populated, resolve any delayed fields. | 246 | // This must occur after all variables and source paths have been resolved. |
| 255 | if (this.DelayedFields.Any()) | 247 | List<FileFacade> fileFacades; |
| 256 | { | 248 | { |
| 257 | var command = new ResolveDelayedFieldsCommand(this.Messaging, this.DelayedFields, variableCache); | 249 | var command = new GetFileFacadesCommand(section); |
| 258 | command.Execute(); | 250 | command.Execute(); |
| 259 | } | ||
| 260 | 251 | ||
| 261 | // Set generated component guids. | 252 | fileFacades = command.FileFacades; |
| 262 | { | 253 | } |
| 263 | var command = new CalculateComponentGuids(this.Messaging, this.BackendHelper, this.PathResolver, section); | ||
| 264 | command.Execute(); | ||
| 265 | } | ||
| 266 | 254 | ||
| 267 | // Retrieve file information from merge modules. | 255 | // Retrieve file information from merge modules. |
| 268 | if (SectionType.Product == section.Type) | 256 | if (SectionType.Product == section.Type) |
| 269 | { | 257 | { |
| 270 | var wixMergeTuples = section.Tuples.OfType<WixMergeTuple>().ToList(); | 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(); | ||
| 271 | 270 | ||
| 272 | if (wixMergeTuples.Any()) | 271 | fileFacades.AddRange(command.MergeModulesFileFacades); |
| 272 | } | ||
| 273 | } | ||
| 274 | else if (SectionType.Patch == section.Type) | ||
| 273 | { | 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; | ||
| 275 | 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); | ||
| 277 | command.FileFacades = fileFacades; | 299 | command.FileFacades = fileFacades; |
| 278 | command.OutputInstallerVersion = installerVersion; | 300 | command.UpdateFileFacades = fileFacades.Where(f => !f.FromModule); |
| 279 | command.SuppressLayout = this.SuppressLayout; | 301 | command.OverwriteHash = true; |
| 280 | command.IntermediateFolder = this.IntermediateFolder; | 302 | command.TableDefinitions = tableDefinitions; |
| 303 | command.VariableCache = variableCache; | ||
| 281 | command.Execute(); | 304 | command.Execute(); |
| 282 | |||
| 283 | fileFacades.AddRange(command.MergeModulesFileFacades); | ||
| 284 | } | 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); | ||
| 291 | 306 | ||
| 292 | fileFacades.AddRange(filesFromTransform); | 307 | // Assign files to media. |
| 293 | } | 308 | Dictionary<int, MediaTuple> assignedMediaRows; |
| 294 | #endif | 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(); | ||
| 295 | 316 | ||
| 296 | // stop processing if an error previously occurred | 317 | assignedMediaRows = command.MediaRows; |
| 297 | if (this.Messaging.EncounteredError) | 318 | filesByCabinetMedia = command.FileFacadesByCabinetMedia; |
| 298 | { | 319 | uncompressedFiles = command.UncompressedFileFacades; |
| 299 | return; | 320 | } |
| 300 | } | ||
| 301 | 321 | ||
| 302 | // Assign files to media. | 322 | // stop processing if an error previously occurred |
| 303 | Dictionary<int, MediaTuple> assignedMediaRows; | 323 | if (this.Messaging.EncounteredError) |
| 304 | Dictionary<MediaTuple, IEnumerable<FileFacade>> filesByCabinetMedia; | 324 | { |
| 305 | IEnumerable<FileFacade> uncompressedFiles; | 325 | return; |
| 306 | { | 326 | } |
| 307 | var command = new AssignMediaCommand(section, this.Messaging); | ||
| 308 | command.FileFacades = fileFacades; | ||
| 309 | command.FilesCompressed = compressed; | ||
| 310 | command.Execute(); | ||
| 311 | 327 | ||
| 312 | assignedMediaRows = command.MediaRows; | 328 | // Now that the variable cache is populated, resolve any delayed fields. |
| 313 | filesByCabinetMedia = command.FileFacadesByCabinetMedia; | 329 | if (this.DelayedFields.Any()) |
| 314 | uncompressedFiles = command.UncompressedFileFacades; | 330 | { |
| 315 | } | 331 | var command = new ResolveDelayedFieldsCommand(this.Messaging, this.DelayedFields, variableCache); |
| 332 | command.Execute(); | ||
| 333 | } | ||
| 316 | 334 | ||
| 317 | // stop processing if an error previously occurred | 335 | // Set generated component guids. |
| 318 | if (this.Messaging.EncounteredError) | 336 | { |
| 319 | { | 337 | var command = new CalculateComponentGuids(this.Messaging, this.BackendHelper, this.PathResolver, section); |
| 320 | return; | 338 | command.Execute(); |
| 321 | } | 339 | } |
| 322 | 340 | ||
| 323 | // Time to create the output object. Try to put as much above here as possible, updating the IR is better. | 341 | // stop processing if an error previously occurred |
| 324 | WindowsInstallerData output; | 342 | if (this.Messaging.EncounteredError) |
| 325 | { | 343 | { |
| 326 | var command = new CreateOutputFromIRCommand(this.Messaging, section, tableDefinitions, this.BackendExtensions); | 344 | return; |
| 327 | command.Execute(); | 345 | } |
| 328 | 346 | ||
| 329 | output = command.Output; | 347 | // Time to create the output object. Try to put as much above here as possible, updating the IR is better. |
| 330 | } | 348 | { |
| 349 | var command = new CreateOutputFromIRCommand(this.Messaging, section, tableDefinitions, this.BackendExtensions); | ||
| 350 | command.Execute(); | ||
| 331 | 351 | ||
| 332 | // Update file sequence. | 352 | output = command.Output; |
| 333 | { | 353 | } |
| 334 | var command = new UpdateMediaSequencesCommand(output, fileFacades); | ||
| 335 | command.Execute(); | ||
| 336 | } | ||
| 337 | 354 | ||
| 338 | // Modularize identifiers. | 355 | // Update file sequence. |
| 339 | if (OutputType.Module == output.Type) | 356 | { |
| 340 | { | 357 | var command = new UpdateMediaSequencesCommand(output, fileFacades); |
| 341 | var command = new ModularizeCommand(output, modularizationGuid, section.Tuples.OfType<WixSuppressModularizationTuple>()); | 358 | command.Execute(); |
| 342 | command.Execute(); | 359 | } |
| 343 | } | 360 | |
| 344 | else // we can create instance transforms since Component Guids are set. | 361 | // Modularize identifiers. |
| 345 | { | 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 | { | ||
| 346 | #if TODO_FIX_INSTANCE_TRANSFORM | 376 | #if TODO_FIX_INSTANCE_TRANSFORM |
| 347 | this.CreateInstanceTransforms(this.Output); | 377 | this.CreateInstanceTransforms(this.Output); |
| 348 | #endif | 378 | #endif |
| 349 | } | 379 | } |
| 350 | 380 | ||
| 351 | #if TODO_FINISH_UPDATE | 381 | #if TODO_FINISH_UPDATE |
| 352 | // Extended binder extensions can be called now that fields are resolved. | 382 | // Extended binder extensions can be called now that fields are resolved. |
| @@ -384,116 +414,121 @@ namespace WixToolset.Core.WindowsInstaller.Bind | |||
| 384 | } | 414 | } |
| 385 | #endif | 415 | #endif |
| 386 | 416 | ||
| 387 | // Stop processing if an error previously occurred. | 417 | this.ValidateComponentGuids(output); |
| 388 | if (this.Messaging.EncounteredError) | ||
| 389 | { | ||
| 390 | return; | ||
| 391 | } | ||
| 392 | 418 | ||
| 393 | // Ensure the intermediate folder is created since delta patches will be | 419 | // Stop processing if an error previously occurred. |
| 394 | // created there. | 420 | if (this.Messaging.EncounteredError) |
| 395 | Directory.CreateDirectory(this.IntermediateFolder); | 421 | { |
| 422 | return; | ||
| 423 | } | ||
| 396 | 424 | ||
| 397 | if (SectionType.Patch == section.Type && this.DeltaBinaryPatch) | 425 | // Ensure the intermediate folder is created since delta patches will be |
| 398 | { | 426 | // created there. |
| 399 | var command = new CreateDeltaPatchesCommand(fileFacades, this.IntermediateFolder, section.Tuples.OfType<WixPatchIdTuple>().FirstOrDefault()); | 427 | Directory.CreateDirectory(this.IntermediateFolder); |
| 400 | command.Execute(); | ||
| 401 | } | ||
| 402 | 428 | ||
| 403 | // create cabinet files and process uncompressed files | 429 | if (SectionType.Patch == section.Type && this.DeltaBinaryPatch) |
| 404 | var layoutDirectory = Path.GetDirectoryName(this.OutputPath); | 430 | { |
| 405 | if (!this.SuppressLayout || OutputType.Module == output.Type) | 431 | var command = new CreateDeltaPatchesCommand(fileFacades, this.IntermediateFolder, section.Tuples.OfType<WixPatchIdTuple>().FirstOrDefault()); |
| 406 | { | 432 | command.Execute(); |
| 407 | this.Messaging.Write(VerboseMessages.CreatingCabinetFiles()); | 433 | } |
| 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(); | ||
| 423 | 434 | ||
| 424 | fileTransfers.AddRange(command.FileTransfers); | 435 | // create cabinet files and process uncompressed files |
| 425 | trackedFiles.AddRange(command.TrackedFiles); | 436 | var layoutDirectory = Path.GetDirectoryName(this.OutputPath); |
| 426 | } | 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(); | ||
| 427 | 455 | ||
| 428 | #if TODO_FINISH_PATCH | 456 | fileTransfers.AddRange(command.FileTransfers); |
| 429 | if (OutputType.Patch == this.Output.Type) | 457 | trackedFiles.AddRange(command.TrackedFiles); |
| 430 | { | 458 | } |
| 431 | // copy output data back into the transforms | ||
| 432 | this.CopyToTransformData(this.Output); | ||
| 433 | } | ||
| 434 | #endif | ||
| 435 | 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(); | ||
| 437 | 467 | ||
| 438 | // stop processing if an error previously occurred | 468 | this.CopyToTransformData(this.Output); |
| 439 | if (this.Messaging.EncounteredError) | 469 | #endif |
| 440 | { | 470 | } |
| 441 | return; | 471 | #endif |
| 442 | } | ||
| 443 | 472 | ||
| 444 | // Generate database file. | 473 | // stop processing if an error previously occurred |
| 445 | this.Messaging.Write(VerboseMessages.GeneratingDatabase()); | 474 | if (this.Messaging.EncounteredError) |
| 475 | { | ||
| 476 | return; | ||
| 477 | } | ||
| 446 | 478 | ||
| 447 | { | 479 | // Generate database file. |
| 448 | var trackMsi = this.BackendHelper.TrackFile(this.OutputPath, TrackedFileType.Final); | 480 | this.Messaging.Write(VerboseMessages.GeneratingDatabase()); |
| 449 | trackedFiles.Add(trackMsi); | ||
| 450 | 481 | ||
| 451 | var temporaryFiles = this.GenerateDatabase(output, tableDefinitions, trackMsi.Path, false, false); | 482 | { |
| 452 | trackedFiles.AddRange(temporaryFiles); | 483 | var trackMsi = this.BackendHelper.TrackFile(this.OutputPath, TrackedFileType.Final); |
| 453 | } | 484 | trackedFiles.Add(trackMsi); |
| 454 | 485 | ||
| 455 | // Stop processing if an error previously occurred. | 486 | var temporaryFiles = this.GenerateDatabase(output, tableDefinitions, trackMsi.Path, false, false); |
| 456 | if (this.Messaging.EncounteredError) | 487 | trackedFiles.AddRange(temporaryFiles); |
| 457 | { | 488 | } |
| 458 | return; | ||
| 459 | } | ||
| 460 | 489 | ||
| 461 | // Merge modules. | 490 | // Stop processing if an error previously occurred. |
| 462 | if (containsMergeModules) | 491 | if (this.Messaging.EncounteredError) |
| 463 | { | 492 | { |
| 464 | this.Messaging.Write(VerboseMessages.MergingModules()); | 493 | return; |
| 494 | } | ||
| 465 | 495 | ||
| 466 | // Add back possibly suppressed sequence tables since all sequence tables must be present | 496 | // Merge modules. |
| 467 | // for the merge process to work. We'll drop the suppressed sequence tables again as | 497 | if (containsMergeModules) |
| 468 | // necessary. | ||
| 469 | foreach (SequenceTable sequence in Enum.GetValues(typeof(SequenceTable))) | ||
| 470 | { | 498 | { |
| 471 | var sequenceTableName = sequence.ToString(); | 499 | this.Messaging.Write(VerboseMessages.MergingModules()); |
| 472 | var sequenceTable = output.Tables[sequenceTableName]; | ||
| 473 | 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))) | ||
| 475 | { | 505 | { |
| 476 | sequenceTable = output.EnsureTable(tableDefinitions[sequenceTableName]); | 506 | var sequenceTableName = sequence.ToString(); |
| 477 | } | 507 | var sequenceTable = output.Tables[sequenceTableName]; |
| 478 | 508 | ||
| 479 | if (0 == sequenceTable.Rows.Count) | 509 | if (null == sequenceTable) |
| 480 | { | 510 | { |
| 481 | suppressedTableNames.Add(sequenceTableName); | 511 | sequenceTable = output.EnsureTable(tableDefinitions[sequenceTableName]); |
| 512 | } | ||
| 513 | |||
| 514 | if (0 == sequenceTable.Rows.Count) | ||
| 515 | { | ||
| 516 | suppressedTableNames.Add(sequenceTableName); | ||
| 517 | } | ||
| 482 | } | 518 | } |
| 483 | } | ||
| 484 | 519 | ||
| 485 | var command = new MergeModulesCommand(); | 520 | var command = new MergeModulesCommand(); |
| 486 | command.FileFacades = fileFacades; | 521 | command.FileFacades = fileFacades; |
| 487 | command.Output = output; | 522 | command.Output = output; |
| 488 | command.OutputPath = this.OutputPath; | 523 | command.OutputPath = this.OutputPath; |
| 489 | command.SuppressedTableNames = suppressedTableNames; | 524 | command.SuppressedTableNames = suppressedTableNames; |
| 490 | command.Execute(); | 525 | command.Execute(); |
| 491 | } | 526 | } |
| 492 | 527 | ||
| 493 | if (this.Messaging.EncounteredError) | 528 | if (this.Messaging.EncounteredError) |
| 494 | { | 529 | { |
| 495 | return; | 530 | return; |
| 496 | } | 531 | } |
| 497 | 532 | ||
| 498 | #if TODO_FINISH_VALIDATION | 533 | #if TODO_FINISH_VALIDATION |
| 499 | // Validate the output if there is an MSI validator. | 534 | // Validate the output if there is an MSI validator. |
| @@ -519,27 +554,29 @@ namespace WixToolset.Core.WindowsInstaller.Bind | |||
| 519 | } | 554 | } |
| 520 | #endif | 555 | #endif |
| 521 | 556 | ||
| 522 | // Process uncompressed files. | 557 | // Process uncompressed files. |
| 523 | if (!this.Messaging.EncounteredError && !this.SuppressLayout && uncompressedFiles.Any()) | 558 | if (!this.Messaging.EncounteredError && !this.SuppressLayout && uncompressedFiles.Any()) |
| 524 | { | 559 | { |
| 525 | var command = new ProcessUncompressedFilesCommand(section, this.BackendHelper, this.PathResolver); | 560 | var command = new ProcessUncompressedFilesCommand(section, this.BackendHelper, this.PathResolver); |
| 526 | command.Compressed = compressed; | 561 | command.Compressed = compressed; |
| 527 | command.FileFacades = uncompressedFiles; | 562 | command.FileFacades = uncompressedFiles; |
| 528 | command.LayoutDirectory = layoutDirectory; | 563 | command.LayoutDirectory = layoutDirectory; |
| 529 | command.LongNamesInImage = longNames; | 564 | command.LongNamesInImage = longNames; |
| 530 | command.ResolveMedia = this.ResolveMedia; | 565 | command.ResolveMedia = this.ResolveMedia; |
| 531 | command.DatabasePath = this.OutputPath; | 566 | command.DatabasePath = this.OutputPath; |
| 532 | command.Execute(); | 567 | command.Execute(); |
| 533 | 568 | ||
| 534 | fileTransfers.AddRange(command.FileTransfers); | 569 | fileTransfers.AddRange(command.FileTransfers); |
| 535 | trackedFiles.AddRange(command.TrackedFiles); | 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))); | ||
| 536 | } | 575 | } |
| 537 | 576 | ||
| 538 | this.Wixout = this.CreateWixout(trackedFiles, this.Intermediate, output); | 577 | this.Wixout = this.CreateWixout(trackedFiles, this.Intermediate, output); |
| 539 | 578 | ||
| 540 | this.FileTransfers = fileTransfers; | 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))); | ||
| 543 | this.TrackedFiles = trackedFiles; | 580 | this.TrackedFiles = trackedFiles; |
| 544 | } | 581 | } |
| 545 | 582 | ||
| @@ -566,7 +603,7 @@ namespace WixToolset.Core.WindowsInstaller.Bind | |||
| 566 | return wixout; | 603 | return wixout; |
| 567 | } | 604 | } |
| 568 | 605 | ||
| 569 | #if TODO_FINISH_PATCH | 606 | #if TODO_PATCHING |
| 570 | /// <summary> | 607 | /// <summary> |
| 571 | /// Copy file data between transform substorages and the patch output object | 608 | /// Copy file data between transform substorages and the patch output object |
| 572 | /// </summary> | 609 | /// </summary> |
| @@ -936,28 +973,15 @@ namespace WixToolset.Core.WindowsInstaller.Bind | |||
| 936 | /// <param name="useSubdirectory">Whether to use a subdirectory based on the <paramref name="databaseFile"/> file name for intermediate files.</param> | 973 | /// <param name="useSubdirectory">Whether to use a subdirectory based on the <paramref name="databaseFile"/> file name for intermediate files.</param> |
| 937 | private IEnumerable<ITrackedFile> GenerateDatabase(WindowsInstallerData output, TableDefinitionCollection tableDefinitions, string databaseFile, bool keepAddedColumns, bool useSubdirectory) | 974 | private IEnumerable<ITrackedFile> GenerateDatabase(WindowsInstallerData output, TableDefinitionCollection tableDefinitions, string databaseFile, bool keepAddedColumns, bool useSubdirectory) |
| 938 | { | 975 | { |
| 939 | var command = new GenerateDatabaseCommand(); | 976 | var command = new GenerateDatabaseCommand(this.Messaging, this.BackendHelper, this.FileSystemExtensions, output, databaseFile, tableDefinitions, this.IntermediateFolder, this.Codepage, keepAddedColumns, this.SuppressAddingValidationRows, useSubdirectory); |
| 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; | ||
| 950 | command.Execute(); | 977 | command.Execute(); |
| 951 | 978 | ||
| 952 | return command.GeneratedTemporaryFiles; | 979 | return command.GeneratedTemporaryFiles; |
| 953 | } | 980 | } |
| 954 | 981 | ||
| 955 | #region IDisposable Support | 982 | #region IDisposable Support |
| 956 | 983 | ||
| 957 | public void Dispose() | 984 | public void Dispose() => this.Dispose(true); |
| 958 | { | ||
| 959 | this.Dispose(true); | ||
| 960 | } | ||
| 961 | 985 | ||
| 962 | protected virtual void Dispose(bool disposing) | 986 | protected virtual void Dispose(bool disposing) |
| 963 | { | 987 | { |
| @@ -972,6 +996,6 @@ namespace WixToolset.Core.WindowsInstaller.Bind | |||
| 972 | } | 996 | } |
| 973 | } | 997 | } |
| 974 | 998 | ||
| 975 | #endregion | 999 | #endregion |
| 976 | } | 1000 | } |
| 977 | } | 1001 | } |
diff --git a/src/WixToolset.Core.WindowsInstaller/Bind/BindTransformCommand.cs b/src/WixToolset.Core.WindowsInstaller/Bind/BindTransformCommand.cs index 8757024e..ea6e4f31 100644 --- a/src/WixToolset.Core.WindowsInstaller/Bind/BindTransformCommand.cs +++ b/src/WixToolset.Core.WindowsInstaller/Bind/BindTransformCommand.cs | |||
| @@ -15,24 +15,37 @@ namespace WixToolset.Core.WindowsInstaller.Bind | |||
| 15 | 15 | ||
| 16 | internal class BindTransformCommand | 16 | internal class BindTransformCommand |
| 17 | { | 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; } | ||
| 19 | 30 | ||
| 20 | public TableDefinitionCollection TableDefinitions { private get; set; } | 31 | private IBackendHelper BackendHelper { get; } |
| 21 | 32 | ||
| 22 | public string TempFilesLocation { private get; set; } | 33 | private IEnumerable<IFileSystemExtension> Extensions { get; } |
| 23 | 34 | ||
| 24 | public WindowsInstallerData Transform { private get; set; } | 35 | private TableDefinitionCollection TableDefinitions { get; } |
| 25 | 36 | ||
| 26 | public IMessaging Messaging { private get; set; } | 37 | private string IntermediateFolder { get; } |
| 27 | 38 | ||
| 28 | public string OutputPath { private get; set; } | 39 | private WindowsInstallerData Transform { get; } |
| 40 | |||
| 41 | private string OutputPath { get; } | ||
| 29 | 42 | ||
| 30 | public void Execute() | 43 | public void Execute() |
| 31 | { | 44 | { |
| 32 | int transformFlags = 0; | 45 | var transformFlags = 0; |
| 33 | 46 | ||
| 34 | WindowsInstallerData targetOutput = new WindowsInstallerData(null); | 47 | var targetOutput = new WindowsInstallerData(null); |
| 35 | WindowsInstallerData updatedOutput = new WindowsInstallerData(null); | 48 | var updatedOutput = new WindowsInstallerData(null); |
| 36 | 49 | ||
| 37 | // TODO: handle added columns | 50 | // TODO: handle added columns |
| 38 | 51 | ||
| @@ -49,8 +62,7 @@ namespace WixToolset.Core.WindowsInstaller.Bind | |||
| 49 | string targetUpgradeCode = null; | 62 | string targetUpgradeCode = null; |
| 50 | string updatedUpgradeCode = null; | 63 | string updatedUpgradeCode = null; |
| 51 | 64 | ||
| 52 | Table propertyTable = this.Transform.Tables["Property"]; | 65 | if (this.Transform.TryGetTable("Property", out var propertyTable)) |
| 53 | if (null != propertyTable) | ||
| 54 | { | 66 | { |
| 55 | for (int i = propertyTable.Rows.Count - 1; i >= 0; i--) | 67 | for (int i = propertyTable.Rows.Count - 1; i >= 0; i--) |
| 56 | { | 68 | { |
| @@ -68,18 +80,21 @@ namespace WixToolset.Core.WindowsInstaller.Bind | |||
| 68 | } | 80 | } |
| 69 | } | 81 | } |
| 70 | 82 | ||
| 71 | Table targetSummaryInfo = targetOutput.EnsureTable(this.TableDefinitions["_SummaryInformation"]); | 83 | var targetSummaryInfo = targetOutput.EnsureTable(this.TableDefinitions["_SummaryInformation"]); |
| 72 | Table updatedSummaryInfo = updatedOutput.EnsureTable(this.TableDefinitions["_SummaryInformation"]); | 84 | var updatedSummaryInfo = updatedOutput.EnsureTable(this.TableDefinitions["_SummaryInformation"]); |
| 73 | Table targetPropertyTable = targetOutput.EnsureTable(this.TableDefinitions["Property"]); | 85 | var targetPropertyTable = targetOutput.EnsureTable(this.TableDefinitions["Property"]); |
| 74 | Table updatedPropertyTable = updatedOutput.EnsureTable(this.TableDefinitions["Property"]); | 86 | var updatedPropertyTable = updatedOutput.EnsureTable(this.TableDefinitions["Property"]); |
| 75 | 87 | ||
| 76 | // process special summary information values | 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) |
| 78 | { | 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) | ||
| 80 | { | 95 | { |
| 81 | // convert from a web name if provided | 96 | // convert from a web name if provided |
| 82 | string codePage = (string)row.Fields[1].Data; | 97 | var codePage = summaryData; |
| 83 | if (null == codePage) | 98 | if (null == codePage) |
| 84 | { | 99 | { |
| 85 | codePage = "0"; | 100 | codePage = "0"; |
| @@ -89,7 +104,7 @@ namespace WixToolset.Core.WindowsInstaller.Bind | |||
| 89 | codePage = Common.GetValidCodePage(codePage).ToString(CultureInfo.InvariantCulture); | 104 | codePage = Common.GetValidCodePage(codePage).ToString(CultureInfo.InvariantCulture); |
| 90 | } | 105 | } |
| 91 | 106 | ||
| 92 | string previousCodePage = (string)row.Fields[1].PreviousData; | 107 | var previousCodePage = row.Fields[1].PreviousData; |
| 93 | if (null == previousCodePage) | 108 | if (null == previousCodePage) |
| 94 | { | 109 | { |
| 95 | previousCodePage = "0"; | 110 | previousCodePage = "0"; |
| @@ -99,50 +114,50 @@ namespace WixToolset.Core.WindowsInstaller.Bind | |||
| 99 | previousCodePage = Common.GetValidCodePage(previousCodePage).ToString(CultureInfo.InvariantCulture); | 114 | previousCodePage = Common.GetValidCodePage(previousCodePage).ToString(CultureInfo.InvariantCulture); |
| 100 | } | 115 | } |
| 101 | 116 | ||
| 102 | Row targetCodePageRow = targetSummaryInfo.CreateRow(null); | 117 | var targetCodePageRow = targetSummaryInfo.CreateRow(null); |
| 103 | targetCodePageRow[0] = 1; // PID_CODEPAGE | 118 | targetCodePageRow[0] = 1; // PID_CODEPAGE |
| 104 | targetCodePageRow[1] = previousCodePage; | 119 | targetCodePageRow[1] = previousCodePage; |
| 105 | 120 | ||
| 106 | Row updatedCodePageRow = updatedSummaryInfo.CreateRow(null); | 121 | var updatedCodePageRow = updatedSummaryInfo.CreateRow(null); |
| 107 | updatedCodePageRow[0] = 1; // PID_CODEPAGE | 122 | updatedCodePageRow[0] = 1; // PID_CODEPAGE |
| 108 | updatedCodePageRow[1] = codePage; | 123 | updatedCodePageRow[1] = codePage; |
| 109 | } | 124 | } |
| 110 | else if ((int)SummaryInformation.Transform.TargetPlatformAndLanguage == (int)row[0] || | 125 | else if ((int)SummaryInformation.Transform.TargetPlatformAndLanguage == summaryId || |
| 111 | (int)SummaryInformation.Transform.UpdatedPlatformAndLanguage == (int)row[0]) | 126 | (int)SummaryInformation.Transform.UpdatedPlatformAndLanguage == summaryId) |
| 112 | { | 127 | { |
| 113 | // the target language | 128 | // the target language |
| 114 | string[] propertyData = ((string)row[1]).Split(';'); | 129 | var propertyData = summaryData.Split(';'); |
| 115 | string lang = 2 == propertyData.Length ? propertyData[1] : "0"; | 130 | var lang = 2 == propertyData.Length ? propertyData[1] : "0"; |
| 116 | 131 | ||
| 117 | Table tempSummaryInfo = (int)SummaryInformation.Transform.TargetPlatformAndLanguage == (int)row[0] ? targetSummaryInfo : updatedSummaryInfo; | 132 | var tempSummaryInfo = (int)SummaryInformation.Transform.TargetPlatformAndLanguage == summaryId ? targetSummaryInfo : updatedSummaryInfo; |
| 118 | Table tempPropertyTable = (int)SummaryInformation.Transform.TargetPlatformAndLanguage == (int)row[0] ? targetPropertyTable : updatedPropertyTable; | 133 | var tempPropertyTable = (int)SummaryInformation.Transform.TargetPlatformAndLanguage == summaryId ? targetPropertyTable : updatedPropertyTable; |
| 119 | 134 | ||
| 120 | Row productLanguageRow = tempPropertyTable.CreateRow(null); | 135 | var productLanguageRow = tempPropertyTable.CreateRow(null); |
| 121 | productLanguageRow[0] = "ProductLanguage"; | 136 | productLanguageRow[0] = "ProductLanguage"; |
| 122 | productLanguageRow[1] = lang; | 137 | productLanguageRow[1] = lang; |
| 123 | 138 | ||
| 124 | // set the platform;language on the MSI to be generated | 139 | // set the platform;language on the MSI to be generated |
| 125 | Row templateRow = tempSummaryInfo.CreateRow(null); | 140 | var templateRow = tempSummaryInfo.CreateRow(null); |
| 126 | templateRow[0] = 7; // PID_TEMPLATE | 141 | templateRow[0] = 7; // PID_TEMPLATE |
| 127 | templateRow[1] = (string)row[1]; | 142 | templateRow[1] = summaryData; |
| 128 | } | 143 | } |
| 129 | else if ((int)SummaryInformation.Transform.ProductCodes == (int)row[0]) | 144 | else if ((int)SummaryInformation.Transform.ProductCodes == summaryId) |
| 130 | { | 145 | { |
| 131 | string[] propertyData = ((string)row[1]).Split(';'); | 146 | var propertyData = summaryData.Split(';'); |
| 132 | 147 | ||
| 133 | Row targetProductCodeRow = targetPropertyTable.CreateRow(null); | 148 | var targetProductCodeRow = targetPropertyTable.CreateRow(null); |
| 134 | targetProductCodeRow[0] = "ProductCode"; | 149 | targetProductCodeRow[0] = "ProductCode"; |
| 135 | targetProductCodeRow[1] = propertyData[0].Substring(0, 38); | 150 | targetProductCodeRow[1] = propertyData[0].Substring(0, 38); |
| 136 | 151 | ||
| 137 | Row targetProductVersionRow = targetPropertyTable.CreateRow(null); | 152 | var targetProductVersionRow = targetPropertyTable.CreateRow(null); |
| 138 | targetProductVersionRow[0] = "ProductVersion"; | 153 | targetProductVersionRow[0] = "ProductVersion"; |
| 139 | targetProductVersionRow[1] = propertyData[0].Substring(38); | 154 | targetProductVersionRow[1] = propertyData[0].Substring(38); |
| 140 | 155 | ||
| 141 | Row updatedProductCodeRow = updatedPropertyTable.CreateRow(null); | 156 | var updatedProductCodeRow = updatedPropertyTable.CreateRow(null); |
| 142 | updatedProductCodeRow[0] = "ProductCode"; | 157 | updatedProductCodeRow[0] = "ProductCode"; |
| 143 | updatedProductCodeRow[1] = propertyData[1].Substring(0, 38); | 158 | updatedProductCodeRow[1] = propertyData[1].Substring(0, 38); |
| 144 | 159 | ||
| 145 | Row updatedProductVersionRow = updatedPropertyTable.CreateRow(null); | 160 | var updatedProductVersionRow = updatedPropertyTable.CreateRow(null); |
| 146 | updatedProductVersionRow[0] = "ProductVersion"; | 161 | updatedProductVersionRow[0] = "ProductVersion"; |
| 147 | updatedProductVersionRow[1] = propertyData[1].Substring(38); | 162 | updatedProductVersionRow[1] = propertyData[1].Substring(38); |
| 148 | 163 | ||
| @@ -153,7 +168,7 @@ namespace WixToolset.Core.WindowsInstaller.Bind | |||
| 153 | targetUpgradeCode = propertyData[2]; | 168 | targetUpgradeCode = propertyData[2]; |
| 154 | if (!String.IsNullOrEmpty(targetUpgradeCode)) | 169 | if (!String.IsNullOrEmpty(targetUpgradeCode)) |
| 155 | { | 170 | { |
| 156 | Row targetUpgradeCodeRow = targetPropertyTable.CreateRow(null); | 171 | var targetUpgradeCodeRow = targetPropertyTable.CreateRow(null); |
| 157 | targetUpgradeCodeRow[0] = "UpgradeCode"; | 172 | targetUpgradeCodeRow[0] = "UpgradeCode"; |
| 158 | targetUpgradeCodeRow[1] = targetUpgradeCode; | 173 | targetUpgradeCodeRow[1] = targetUpgradeCode; |
| 159 | 174 | ||
| @@ -167,16 +182,16 @@ namespace WixToolset.Core.WindowsInstaller.Bind | |||
| 167 | 182 | ||
| 168 | if (!String.IsNullOrEmpty(updatedUpgradeCode)) | 183 | if (!String.IsNullOrEmpty(updatedUpgradeCode)) |
| 169 | { | 184 | { |
| 170 | Row updatedUpgradeCodeRow = updatedPropertyTable.CreateRow(null); | 185 | var updatedUpgradeCodeRow = updatedPropertyTable.CreateRow(null); |
| 171 | updatedUpgradeCodeRow[0] = "UpgradeCode"; | 186 | updatedUpgradeCodeRow[0] = "UpgradeCode"; |
| 172 | updatedUpgradeCodeRow[1] = updatedUpgradeCode; | 187 | updatedUpgradeCodeRow[1] = updatedUpgradeCode; |
| 173 | } | 188 | } |
| 174 | } | 189 | } |
| 175 | else if ((int)SummaryInformation.Transform.ValidationFlags == (int)row[0]) | 190 | else if ((int)SummaryInformation.Transform.ValidationFlags == summaryId) |
| 176 | { | 191 | { |
| 177 | transformFlags = Convert.ToInt32(row[1], CultureInfo.InvariantCulture); | 192 | transformFlags = Convert.ToInt32(summaryData, CultureInfo.InvariantCulture); |
| 178 | } | 193 | } |
| 179 | else if ((int)SummaryInformation.Transform.Reserved11 == (int)row[0]) | 194 | else if ((int)SummaryInformation.Transform.Reserved11 == summaryId) |
| 180 | { | 195 | { |
| 181 | // PID_LASTPRINTED should be null for transforms | 196 | // PID_LASTPRINTED should be null for transforms |
| 182 | row.Operation = RowOperation.None; | 197 | row.Operation = RowOperation.None; |
| @@ -184,11 +199,11 @@ namespace WixToolset.Core.WindowsInstaller.Bind | |||
| 184 | else | 199 | else |
| 185 | { | 200 | { |
| 186 | // add everything else as is | 201 | // add everything else as is |
| 187 | Row targetRow = targetSummaryInfo.CreateRow(null); | 202 | var targetRow = targetSummaryInfo.CreateRow(null); |
| 188 | targetRow[0] = row[0]; | 203 | targetRow[0] = row[0]; |
| 189 | targetRow[1] = row[1]; | 204 | targetRow[1] = row[1]; |
| 190 | 205 | ||
| 191 | Row updatedRow = updatedSummaryInfo.CreateRow(null); | 206 | var updatedRow = updatedSummaryInfo.CreateRow(null); |
| 192 | updatedRow[0] = row[0]; | 207 | updatedRow[0] = row[0]; |
| 193 | updatedRow[1] = row[1]; | 208 | updatedRow[1] = row[1]; |
| 194 | } | 209 | } |
| @@ -205,7 +220,7 @@ namespace WixToolset.Core.WindowsInstaller.Bind | |||
| 205 | 220 | ||
| 206 | string emptyFile = null; | 221 | string emptyFile = null; |
| 207 | 222 | ||
| 208 | foreach (Table table in this.Transform.Tables) | 223 | foreach (var table in this.Transform.Tables) |
| 209 | { | 224 | { |
| 210 | // Ignore unreal tables when building transforms except the _Stream table. | 225 | // Ignore unreal tables when building transforms except the _Stream table. |
| 211 | // These tables are ignored when generating the database so there is no reason | 226 | // These tables are ignored when generating the database so there is no reason |
| @@ -231,20 +246,21 @@ namespace WixToolset.Core.WindowsInstaller.Bind | |||
| 231 | } | 246 | } |
| 232 | 247 | ||
| 233 | // process row operations | 248 | // process row operations |
| 234 | foreach (Row row in table.Rows) | 249 | foreach (var row in table.Rows) |
| 235 | { | 250 | { |
| 236 | switch (row.Operation) | 251 | switch (row.Operation) |
| 237 | { | 252 | { |
| 238 | case RowOperation.Add: | 253 | case RowOperation.Add: |
| 239 | Table updatedTable = updatedOutput.EnsureTable(table.Definition); | 254 | var updatedTable = updatedOutput.EnsureTable(table.Definition); |
| 240 | updatedTable.Rows.Add(row); | 255 | updatedTable.Rows.Add(row); |
| 241 | continue; | 256 | continue; |
| 257 | |||
| 242 | case RowOperation.Delete: | 258 | case RowOperation.Delete: |
| 243 | Table targetTable = targetOutput.EnsureTable(table.Definition); | 259 | var targetTable = targetOutput.EnsureTable(table.Definition); |
| 244 | targetTable.Rows.Add(row); | 260 | targetTable.Rows.Add(row); |
| 245 | 261 | ||
| 246 | // fill-in non-primary key values | 262 | // fill-in non-primary key values |
| 247 | foreach (Field field in row.Fields) | 263 | foreach (var field in row.Fields) |
| 248 | { | 264 | { |
| 249 | if (!field.Column.PrimaryKey) | 265 | if (!field.Column.PrimaryKey) |
| 250 | { | 266 | { |
| @@ -256,7 +272,7 @@ namespace WixToolset.Core.WindowsInstaller.Bind | |||
| 256 | { | 272 | { |
| 257 | if (null == emptyFile) | 273 | if (null == emptyFile) |
| 258 | { | 274 | { |
| 259 | emptyFile = Path.Combine(this.TempFilesLocation, "empty"); | 275 | emptyFile = Path.Combine(this.IntermediateFolder, "empty"); |
| 260 | } | 276 | } |
| 261 | 277 | ||
| 262 | field.Data = emptyFile; | 278 | field.Data = emptyFile; |
| @@ -273,7 +289,7 @@ namespace WixToolset.Core.WindowsInstaller.Bind | |||
| 273 | // Assure that the file table's sequence is populated | 289 | // Assure that the file table's sequence is populated |
| 274 | if ("File" == table.Name) | 290 | if ("File" == table.Name) |
| 275 | { | 291 | { |
| 276 | foreach (Row fileRow in table.Rows) | 292 | foreach (var fileRow in table.Rows) |
| 277 | { | 293 | { |
| 278 | if (null == fileRow[7]) | 294 | if (null == fileRow[7]) |
| 279 | { | 295 | { |
| @@ -290,60 +306,48 @@ namespace WixToolset.Core.WindowsInstaller.Bind | |||
| 290 | } | 306 | } |
| 291 | 307 | ||
| 292 | // process modified and unmodified rows | 308 | // process modified and unmodified rows |
| 293 | bool modifiedRow = false; | 309 | var modifiedRow = false; |
| 294 | Row targetRow = new Row(null, table.Definition); | 310 | var targetRow = new Row(null, table.Definition); |
| 295 | Row updatedRow = row; | 311 | var updatedRow = row; |
| 296 | for (int i = 0; i < row.Fields.Length; i++) | 312 | for (var i = 0; i < row.Fields.Length; i++) |
| 297 | { | 313 | { |
| 298 | Field updatedField = row.Fields[i]; | 314 | var updatedField = row.Fields[i]; |
| 299 | 315 | ||
| 300 | if (updatedField.Modified) | 316 | if (updatedField.Modified) |
| 301 | { | 317 | { |
| 302 | // set a different value in the target row to ensure this value will be modified during transform generation | 318 | // set a different value in the target row to ensure this value will be modified during transform generation |
| 303 | if (ColumnType.Number == updatedField.Column.Type && !updatedField.Column.IsLocalizable) | 319 | if (ColumnType.Number == updatedField.Column.Type && !updatedField.Column.IsLocalizable) |
| 304 | { | 320 | { |
| 305 | if (null == updatedField.Data || 1 != (int)updatedField.Data) | 321 | var data = updatedField.AsNullableInteger(); |
| 306 | { | 322 | targetRow[i] = (data == 1) ? 2 : 1; |
| 307 | targetRow[i] = 1; | ||
| 308 | } | ||
| 309 | else | ||
| 310 | { | ||
| 311 | targetRow[i] = 2; | ||
| 312 | } | ||
| 313 | } | 323 | } |
| 314 | else if (ColumnType.Object == updatedField.Column.Type) | 324 | else if (ColumnType.Object == updatedField.Column.Type) |
| 315 | { | 325 | { |
| 316 | if (null == emptyFile) | 326 | if (null == emptyFile) |
| 317 | { | 327 | { |
| 318 | emptyFile = Path.Combine(this.TempFilesLocation, "empty"); | 328 | emptyFile = Path.Combine(this.IntermediateFolder, "empty"); |
| 319 | } | 329 | } |
| 320 | 330 | ||
| 321 | targetRow[i] = emptyFile; | 331 | targetRow[i] = emptyFile; |
| 322 | } | 332 | } |
| 323 | else | 333 | else |
| 324 | { | 334 | { |
| 325 | if ("0" != (string)updatedField.Data) | 335 | var data = updatedField.AsString(); |
| 326 | { | 336 | targetRow[i] = (data == "0") ? "1" : "0"; |
| 327 | targetRow[i] = "0"; | ||
| 328 | } | ||
| 329 | else | ||
| 330 | { | ||
| 331 | targetRow[i] = "1"; | ||
| 332 | } | ||
| 333 | } | 337 | } |
| 334 | 338 | ||
| 335 | modifiedRow = true; | 339 | modifiedRow = true; |
| 336 | } | 340 | } |
| 337 | else if (ColumnType.Object == updatedField.Column.Type) | 341 | else if (ColumnType.Object == updatedField.Column.Type) |
| 338 | { | 342 | { |
| 339 | ObjectField objectField = (ObjectField)updatedField; | 343 | var objectField = (ObjectField)updatedField; |
| 340 | 344 | ||
| 341 | // create an empty file for comparing against | 345 | // create an empty file for comparing against |
| 342 | if (null == objectField.PreviousData) | 346 | if (null == objectField.PreviousData) |
| 343 | { | 347 | { |
| 344 | if (null == emptyFile) | 348 | if (null == emptyFile) |
| 345 | { | 349 | { |
| 346 | emptyFile = Path.Combine(this.TempFilesLocation, "empty"); | 350 | emptyFile = Path.Combine(this.IntermediateFolder, "empty"); |
| 347 | } | 351 | } |
| 348 | 352 | ||
| 349 | targetRow[i] = emptyFile; | 353 | targetRow[i] = emptyFile; |
| @@ -372,10 +376,10 @@ namespace WixToolset.Core.WindowsInstaller.Bind | |||
| 372 | "ProductVersion" == (string)row[0] || | 376 | "ProductVersion" == (string)row[0] || |
| 373 | "UpgradeCode" == (string)row[0]))) | 377 | "UpgradeCode" == (string)row[0]))) |
| 374 | { | 378 | { |
| 375 | Table targetTable = targetOutput.EnsureTable(table.Definition); | 379 | var targetTable = targetOutput.EnsureTable(table.Definition); |
| 376 | targetTable.Rows.Add(targetRow); | 380 | targetTable.Rows.Add(targetRow); |
| 377 | 381 | ||
| 378 | Table updatedTable = updatedOutput.EnsureTable(table.Definition); | 382 | var updatedTable = updatedOutput.EnsureTable(table.Definition); |
| 379 | updatedTable.Rows.Add(updatedRow); | 383 | updatedTable.Rows.Add(updatedRow); |
| 380 | } | 384 | } |
| 381 | } | 385 | } |
| @@ -392,38 +396,36 @@ namespace WixToolset.Core.WindowsInstaller.Bind | |||
| 392 | return; | 396 | return; |
| 393 | } | 397 | } |
| 394 | 398 | ||
| 395 | string transformFileName = Path.GetFileNameWithoutExtension(this.OutputPath); | 399 | var transformFileName = Path.GetFileNameWithoutExtension(this.OutputPath); |
| 396 | string targetDatabaseFile = Path.Combine(this.TempFilesLocation, String.Concat(transformFileName, "_target.msi")); | 400 | var targetDatabaseFile = Path.Combine(this.IntermediateFolder, String.Concat(transformFileName, "_target.msi")); |
| 397 | string updatedDatabaseFile = Path.Combine(this.TempFilesLocation, String.Concat(transformFileName, "_updated.msi")); | 401 | var updatedDatabaseFile = Path.Combine(this.IntermediateFolder, String.Concat(transformFileName, "_updated.msi")); |
| 398 | 402 | ||
| 399 | try | 403 | try |
| 400 | { | 404 | { |
| 401 | if (!String.IsNullOrEmpty(emptyFile)) | 405 | if (!String.IsNullOrEmpty(emptyFile)) |
| 402 | { | 406 | { |
| 403 | using (FileStream fileStream = File.Create(emptyFile)) | 407 | using (var fileStream = File.Create(emptyFile)) |
| 404 | { | 408 | { |
| 405 | } | 409 | } |
| 406 | } | 410 | } |
| 407 | 411 | ||
| 408 | this.GenerateDatabase(targetOutput, targetDatabaseFile, false); | 412 | this.GenerateDatabase(targetOutput, targetDatabaseFile, keepAddedColumns: false); |
| 409 | this.GenerateDatabase(updatedOutput, updatedDatabaseFile, true); | 413 | this.GenerateDatabase(updatedOutput, updatedDatabaseFile, keepAddedColumns: true); |
| 410 | 414 | ||
| 411 | // make sure the directory exists | 415 | // make sure the directory exists |
| 412 | Directory.CreateDirectory(Path.GetDirectoryName(this.OutputPath)); | 416 | Directory.CreateDirectory(Path.GetDirectoryName(this.OutputPath)); |
| 413 | 417 | ||
| 414 | // create the transform file | 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)) | ||
| 416 | { | 421 | { |
| 417 | using (Database updatedDatabase = new Database(updatedDatabaseFile, OpenDatabase.ReadOnly)) | 422 | if (updatedDatabase.GenerateTransform(targetDatabase, this.OutputPath)) |
| 418 | { | 423 | { |
| 419 | if (updatedDatabase.GenerateTransform(targetDatabase, this.OutputPath)) | 424 | updatedDatabase.CreateTransformSummaryInfo(targetDatabase, this.OutputPath, (TransformErrorConditions)(transformFlags & 0xFFFF), (TransformValidations)((transformFlags >> 16) & 0xFFFF)); |
| 420 | { | 425 | } |
| 421 | updatedDatabase.CreateTransformSummaryInfo(targetDatabase, this.OutputPath, (TransformErrorConditions)(transformFlags & 0xFFFF), (TransformValidations)((transformFlags >> 16) & 0xFFFF)); | 426 | else |
| 422 | } | 427 | { |
| 423 | else | 428 | this.Messaging.Write(ErrorMessages.NoDifferencesInTransform(this.Transform.SourceLineNumbers)); |
| 424 | { | ||
| 425 | this.Messaging.Write(ErrorMessages.NoDifferencesInTransform(this.Transform.SourceLineNumbers)); | ||
| 426 | } | ||
| 427 | } | 429 | } |
| 428 | } | 430 | } |
| 429 | } | 431 | } |
| @@ -458,16 +460,7 @@ namespace WixToolset.Core.WindowsInstaller.Bind | |||
| 458 | 460 | ||
| 459 | private void GenerateDatabase(WindowsInstallerData output, string outputPath, bool keepAddedColumns) | 461 | private void GenerateDatabase(WindowsInstallerData output, string outputPath, bool keepAddedColumns) |
| 460 | { | 462 | { |
| 461 | var command = new GenerateDatabaseCommand(); | 463 | var command = new GenerateDatabaseCommand(this.Messaging, this.BackendHelper, this.Extensions, output, outputPath, this.TableDefinitions, this.IntermediateFolder, codepage: -1, keepAddedColumns, suppressAddingValidationRows: true, useSubdirectory: true ); |
| 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; | ||
| 471 | command.Execute(); | 464 | command.Execute(); |
| 472 | } | 465 | } |
| 473 | } | 466 | } |
diff --git a/src/WixToolset.Core.WindowsInstaller/Bind/CabinetBuilder.cs b/src/WixToolset.Core.WindowsInstaller/Bind/CabinetBuilder.cs index bf1140d8..ec4e0818 100644 --- a/src/WixToolset.Core.WindowsInstaller/Bind/CabinetBuilder.cs +++ b/src/WixToolset.Core.WindowsInstaller/Bind/CabinetBuilder.cs | |||
| @@ -152,7 +152,7 @@ namespace WixToolset.Core.WindowsInstaller.Bind | |||
| 152 | 152 | ||
| 153 | foreach (FileFacade facade in cabinetWorkItem.FileFacades) // No other easy way than looping to get the only row | 153 | foreach (FileFacade facade in cabinetWorkItem.FileFacades) // No other easy way than looping to get the only row |
| 154 | { | 154 | { |
| 155 | if ((ulong)facade.File.FileSize >= maxPreCompressedSizeInBytes) | 155 | if ((ulong)facade.FileSize >= maxPreCompressedSizeInBytes) |
| 156 | { | 156 | { |
| 157 | // If file is larger than MaximumUncompressedFileSize set Maximum Cabinet Size for Cabinet Splitting | 157 | // If file is larger than MaximumUncompressedFileSize set Maximum Cabinet Size for Cabinet Splitting |
| 158 | maxCabinetSize = this.MaximumCabinetSizeForLargeFileSplitting; | 158 | maxCabinetSize = this.MaximumCabinetSizeForLargeFileSplitting; |
| @@ -166,8 +166,8 @@ namespace WixToolset.Core.WindowsInstaller.Bind | |||
| 166 | 166 | ||
| 167 | var files = cabinetWorkItem.FileFacades | 167 | var files = cabinetWorkItem.FileFacades |
| 168 | .Select(facade => facade.Hash == null ? | 168 | .Select(facade => facade.Hash == null ? |
| 169 | new CabinetCompressFile(facade.File.Source.Path, facade.File.Id.Id) : | 169 | new CabinetCompressFile(facade.SourcePath, facade.Id) : |
| 170 | new CabinetCompressFile(facade.File.Source.Path, facade.File.Id.Id, facade.Hash.HashPart1, facade.Hash.HashPart2, facade.Hash.HashPart3, facade.Hash.HashPart4)) | 170 | new CabinetCompressFile(facade.SourcePath, facade.Id, facade.Hash.HashPart1, facade.Hash.HashPart2, facade.Hash.HashPart3, facade.Hash.HashPart4)) |
| 171 | .ToList(); | 171 | .ToList(); |
| 172 | 172 | ||
| 173 | var cab = new Cabinet(cabinetPath); | 173 | var cab = new Cabinet(cabinetPath); |
diff --git a/src/WixToolset.Core.WindowsInstaller/Bind/CabinetResolver.cs b/src/WixToolset.Core.WindowsInstaller/Bind/CabinetResolver.cs index 3fa3f3a0..79b1c619 100644 --- a/src/WixToolset.Core.WindowsInstaller/Bind/CabinetResolver.cs +++ b/src/WixToolset.Core.WindowsInstaller/Bind/CabinetResolver.cs | |||
| @@ -112,8 +112,8 @@ namespace WixToolset.Core.WindowsInstaller.Bind | |||
| 112 | private IBindFileWithPath CreateBindFileWithPath(FileFacade facade) | 112 | private IBindFileWithPath CreateBindFileWithPath(FileFacade facade) |
| 113 | { | 113 | { |
| 114 | var result = this.ServiceProvider.GetService<IBindFileWithPath>(); | 114 | var result = this.ServiceProvider.GetService<IBindFileWithPath>(); |
| 115 | result.Id = facade.File.Id.Id; | 115 | result.Id = facade.Id; |
| 116 | result.Path = facade.File.Source.Path; | 116 | result.Path = facade.SourcePath; |
| 117 | 117 | ||
| 118 | return result; | 118 | return result; |
| 119 | } | 119 | } |
diff --git a/src/WixToolset.Core.WindowsInstaller/Bind/CopyTransformDataCommand.cs b/src/WixToolset.Core.WindowsInstaller/Bind/CopyTransformDataCommand.cs index 107f3208..0dcce61b 100644 --- a/src/WixToolset.Core.WindowsInstaller/Bind/CopyTransformDataCommand.cs +++ b/src/WixToolset.Core.WindowsInstaller/Bind/CopyTransformDataCommand.cs | |||
| @@ -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. | 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 | 2 | ||
| 3 | #if DELETE | ||
| 4 | |||
| 3 | namespace WixToolset.Core.WindowsInstaller.Bind | 5 | namespace WixToolset.Core.WindowsInstaller.Bind |
| 4 | { | 6 | { |
| 5 | using System; | 7 | using System; |
| 6 | using System.Collections.Generic; | 8 | using System.Collections.Generic; |
| 7 | using System.Diagnostics; | 9 | using System.Diagnostics; |
| 10 | using System.IO; | ||
| 11 | using System.Linq; | ||
| 8 | using WixToolset.Core.Bind; | 12 | using WixToolset.Core.Bind; |
| 9 | using WixToolset.Core.Native; | ||
| 10 | using WixToolset.Data; | 13 | using WixToolset.Data; |
| 11 | using WixToolset.Data.Tuples; | 14 | using WixToolset.Data.Tuples; |
| 12 | using WixToolset.Data.WindowsInstaller; | 15 | using WixToolset.Data.WindowsInstaller; |
| @@ -16,15 +19,23 @@ namespace WixToolset.Core.WindowsInstaller.Bind | |||
| 16 | 19 | ||
| 17 | internal class CopyTransformDataCommand | 20 | internal class CopyTransformDataCommand |
| 18 | { | 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 | } | ||
| 20 | 29 | ||
| 21 | public IEnumerable<IFileSystemExtension> Extensions { private get; set; } | 30 | private bool CopyOutFileRows { get; } |
| 22 | 31 | ||
| 23 | public IMessaging Messaging { private get; set; } | 32 | public IEnumerable<IFileSystemExtension> Extensions { get; } |
| 24 | 33 | ||
| 25 | public WindowsInstallerData Output { private get; set; } | 34 | private IMessaging Messaging { get; } |
| 26 | 35 | ||
| 27 | public TableDefinitionCollection TableDefinitions { private get; set; } | 36 | private WindowsInstallerData Output { get; } |
| 37 | |||
| 38 | private TableDefinitionCollection TableDefinitions { get; } | ||
| 28 | 39 | ||
| 29 | public IEnumerable<FileFacade> FileFacades { get; private set; } | 40 | public IEnumerable<FileFacade> FileFacades { get; private set; } |
| 30 | 41 | ||
| @@ -32,18 +43,17 @@ namespace WixToolset.Core.WindowsInstaller.Bind | |||
| 32 | { | 43 | { |
| 33 | Debug.Assert(OutputType.Patch != this.Output.Type); | 44 | Debug.Assert(OutputType.Patch != this.Output.Type); |
| 34 | 45 | ||
| 35 | List<FileFacade> allFileRows = this.CopyOutFileRows ? new List<FileFacade>() : null; | 46 | var allFileRows = this.CopyOutFileRows ? new List<FileFacade>() : null; |
| 36 | 47 | ||
| 37 | #if REVISIT_FOR_PATCHING // TODO: Fix this patching related code to work correctly with FileFacades. | 48 | var copyToPatch = (allFileRows != null); |
| 38 | bool copyToPatch = (allFileRows != null); | 49 | var copyFromPatch = !copyToPatch; |
| 39 | bool copyFromPatch = !copyToPatch; | ||
| 40 | 50 | ||
| 41 | RowDictionary<MediaRow> patchMediaRows = new RowDictionary<MediaRow>(); | 51 | var patchMediaRows = new RowDictionary<MediaRow>(); |
| 42 | 52 | ||
| 43 | Dictionary<int, RowDictionary<WixFileRow>> patchMediaFileRows = new Dictionary<int, RowDictionary<WixFileRow>>(); | 53 | var patchMediaFileRows = new Dictionary<int, RowDictionary<WixFileRow>>(); |
| 44 | 54 | ||
| 45 | Table patchActualFileTable = this.Output.EnsureTable(this.TableDefinitions["File"]); | 55 | var patchActualFileTable = this.Output.EnsureTable(this.TableDefinitions["File"]); |
| 46 | Table patchFileTable = this.Output.EnsureTable(this.TableDefinitions["WixFile"]); | 56 | var patchFileTable = this.Output.EnsureTable(this.TableDefinitions["WixFile"]); |
| 47 | 57 | ||
| 48 | if (copyFromPatch) | 58 | if (copyFromPatch) |
| 49 | { | 59 | { |
| @@ -51,8 +61,7 @@ namespace WixToolset.Core.WindowsInstaller.Bind | |||
| 51 | foreach (WixFileRow patchFileRow in patchFileTable.Rows) | 61 | foreach (WixFileRow patchFileRow in patchFileTable.Rows) |
| 52 | { | 62 | { |
| 53 | int diskId = patchFileRow.DiskId; | 63 | int diskId = patchFileRow.DiskId; |
| 54 | RowDictionary<WixFileRow> mediaFileRows; | 64 | if (!patchMediaFileRows.TryGetValue(diskId, out var mediaFileRows)) |
| 55 | if (!patchMediaFileRows.TryGetValue(diskId, out mediaFileRows)) | ||
| 56 | { | 65 | { |
| 57 | mediaFileRows = new RowDictionary<WixFileRow>(); | 66 | mediaFileRows = new RowDictionary<WixFileRow>(); |
| 58 | patchMediaFileRows.Add(diskId, mediaFileRows); | 67 | patchMediaFileRows.Add(diskId, mediaFileRows); |
| @@ -61,24 +70,25 @@ namespace WixToolset.Core.WindowsInstaller.Bind | |||
| 61 | mediaFileRows.Add(patchFileRow); | 70 | mediaFileRows.Add(patchFileRow); |
| 62 | } | 71 | } |
| 63 | 72 | ||
| 64 | Table patchMediaTable = this.Output.EnsureTable(this.TableDefinitions["Media"]); | 73 | var patchMediaTable = this.Output.EnsureTable(this.TableDefinitions["Media"]); |
| 65 | patchMediaRows = new RowDictionary<MediaRow>(patchMediaTable); | 74 | patchMediaRows = new RowDictionary<MediaRow>(patchMediaTable); |
| 66 | } | 75 | } |
| 67 | 76 | ||
| 68 | // index paired transforms | 77 | // Index paired transforms by name without the "#" prefix. |
| 69 | Dictionary<string, Output> pairedTransforms = new Dictionary<string, Output>(); | 78 | var pairedTransforms = this.Output.SubStorages.Where(s => s.Name.StartsWith("#")).ToDictionary(s => s.Name.Substring(1), s => s.Data); |
| 70 | foreach (SubStorage substorage in this.Output.SubStorages) | 79 | //Dictionary<string, Output> pairedTransforms = new Dictionary<string, Output>(); |
| 71 | { | 80 | //foreach (SubStorage substorage in this.Output.SubStorages) |
| 72 | if (substorage.Name.StartsWith("#")) | 81 | //{ |
| 73 | { | 82 | // if (substorage.Name.StartsWith("#")) |
| 74 | pairedTransforms.Add(substorage.Name.Substring(1), substorage.Data); | 83 | // { |
| 75 | } | 84 | // pairedTransforms.Add(substorage.Name.Substring(1), substorage.Data); |
| 76 | } | 85 | // } |
| 86 | //} | ||
| 77 | 87 | ||
| 78 | try | 88 | try |
| 79 | { | 89 | { |
| 80 | // copy File bind data into substorages | 90 | // Copy File bind data into substorages |
| 81 | foreach (SubStorage substorage in this.Output.SubStorages) | 91 | foreach (var substorage in this.Output.SubStorages) |
| 82 | { | 92 | { |
| 83 | if (substorage.Name.StartsWith("#")) | 93 | if (substorage.Name.StartsWith("#")) |
| 84 | { | 94 | { |
| @@ -86,25 +96,25 @@ namespace WixToolset.Core.WindowsInstaller.Bind | |||
| 86 | continue; | 96 | continue; |
| 87 | } | 97 | } |
| 88 | 98 | ||
| 89 | Output mainTransform = substorage.Data; | 99 | var mainTransform = substorage.Data; |
| 90 | Table mainWixFileTable = mainTransform.Tables["WixFile"]; | 100 | var mainWixFileTable = mainTransform.Tables["WixFile"]; |
| 91 | Table mainMsiFileHashTable = mainTransform.Tables["MsiFileHash"]; | 101 | var mainMsiFileHashTable = mainTransform.Tables["MsiFileHash"]; |
| 92 | 102 | ||
| 93 | this.FileManagerCore.ActiveSubStorage = substorage; | 103 | this.FileManagerCore.ActiveSubStorage = substorage; |
| 94 | 104 | ||
| 95 | RowDictionary<WixFileRow> mainWixFiles = new RowDictionary<WixFileRow>(mainWixFileTable); | 105 | var mainWixFiles = new RowDictionary<WixFileRow>(mainWixFileTable); |
| 96 | RowDictionary<Row> mainMsiFileHashIndex = new RowDictionary<Row>(); | 106 | var mainMsiFileHashIndex = new RowDictionary<Row>(); |
| 97 | 107 | ||
| 98 | Table mainFileTable = mainTransform.Tables["File"]; | 108 | var mainFileTable = mainTransform.Tables["File"]; |
| 99 | Output pairedTransform = (Output)pairedTransforms[substorage.Name]; | 109 | var pairedTransform = pairedTransforms[substorage.Name]; |
| 100 | 110 | ||
| 101 | // copy Media.LastSequence and index the MsiFileHash table if it exists. | 111 | // copy Media.LastSequence and index the MsiFileHash table if it exists. |
| 102 | if (copyFromPatch) | 112 | if (copyFromPatch) |
| 103 | { | 113 | { |
| 104 | Table pairedMediaTable = pairedTransform.Tables["Media"]; | 114 | var pairedMediaTable = pairedTransform.Tables["Media"]; |
| 105 | foreach (MediaRow pairedMediaRow in pairedMediaTable.Rows) | 115 | foreach (MediaRow pairedMediaRow in pairedMediaTable.Rows) |
| 106 | { | 116 | { |
| 107 | MediaRow patchMediaRow = patchMediaRows.Get(pairedMediaRow.DiskId); | 117 | var patchMediaRow = patchMediaRows.Get(pairedMediaRow.DiskId); |
| 108 | pairedMediaRow.Fields[1] = patchMediaRow.Fields[1]; | 118 | pairedMediaRow.Fields[1] = patchMediaRow.Fields[1]; |
| 109 | } | 119 | } |
| 110 | 120 | ||
| @@ -118,8 +128,8 @@ namespace WixToolset.Core.WindowsInstaller.Bind | |||
| 118 | } | 128 | } |
| 119 | 129 | ||
| 120 | // Index File table of pairedTransform | 130 | // Index File table of pairedTransform |
| 121 | Table pairedFileTable = pairedTransform.Tables["File"]; | 131 | var pairedFileTable = pairedTransform.Tables["File"]; |
| 122 | RowDictionary<FileRow> pairedFileRows = new RowDictionary<FileRow>(pairedFileTable); | 132 | var pairedFileRows = new RowDictionary<FileRow>(pairedFileTable); |
| 123 | 133 | ||
| 124 | if (null != mainFileTable) | 134 | if (null != mainFileTable) |
| 125 | { | 135 | { |
| @@ -140,12 +150,12 @@ namespace WixToolset.Core.WindowsInstaller.Bind | |||
| 140 | continue; | 150 | continue; |
| 141 | } | 151 | } |
| 142 | 152 | ||
| 143 | WixFileRow mainWixFileRow = mainWixFiles.Get(mainFileRow.File); | 153 | var mainWixFileRow = mainWixFiles.Get(mainFileRow.File); |
| 144 | 154 | ||
| 145 | if (copyToPatch) // when copying to the patch, we need compare the underlying files and include all file changes. | 155 | if (copyToPatch) // when copying to the patch, we need compare the underlying files and include all file changes. |
| 146 | { | 156 | { |
| 147 | ObjectField objectField = (ObjectField)mainWixFileRow.Fields[6]; | 157 | var objectField = (ObjectField)mainWixFileRow.Fields[6]; |
| 148 | FileRow pairedFileRow = pairedFileRows.Get(mainFileRow.File); | 158 | var pairedFileRow = pairedFileRows.Get(mainFileRow.File); |
| 149 | 159 | ||
| 150 | // If the file is new, we always need to add it to the patch. | 160 | // If the file is new, we always need to add it to the patch. |
| 151 | if (mainFileRow.Operation != RowOperation.Add) | 161 | if (mainFileRow.Operation != RowOperation.Add) |
| @@ -169,8 +179,8 @@ namespace WixToolset.Core.WindowsInstaller.Bind | |||
| 169 | if (null != pairedFileRow) | 179 | if (null != pairedFileRow) |
| 170 | { | 180 | { |
| 171 | // Always patch-added, but never non-compressed. | 181 | // Always patch-added, but never non-compressed. |
| 172 | pairedFileRow.Attributes |= MsiInterop.MsidbFileAttributesPatchAdded; | 182 | pairedFileRow.Attributes |= WindowsInstallerConstants.MsidbFileAttributesPatchAdded; |
| 173 | pairedFileRow.Attributes &= ~MsiInterop.MsidbFileAttributesNoncompressed; | 183 | pairedFileRow.Attributes &= ~WindowsInstallerConstants.MsidbFileAttributesNoncompressed; |
| 174 | pairedFileRow.Fields[6].Modified = true; | 184 | pairedFileRow.Fields[6].Modified = true; |
| 175 | pairedFileRow.Operation = RowOperation.Modify; | 185 | pairedFileRow.Operation = RowOperation.Modify; |
| 176 | } | 186 | } |
| @@ -179,14 +189,14 @@ namespace WixToolset.Core.WindowsInstaller.Bind | |||
| 179 | { | 189 | { |
| 180 | // The File is same. We need mark all the attributes as unchanged. | 190 | // The File is same. We need mark all the attributes as unchanged. |
| 181 | mainFileRow.Operation = RowOperation.None; | 191 | mainFileRow.Operation = RowOperation.None; |
| 182 | foreach (Field field in mainFileRow.Fields) | 192 | foreach (var field in mainFileRow.Fields) |
| 183 | { | 193 | { |
| 184 | field.Modified = false; | 194 | field.Modified = false; |
| 185 | } | 195 | } |
| 186 | 196 | ||
| 187 | if (null != pairedFileRow) | 197 | if (null != pairedFileRow) |
| 188 | { | 198 | { |
| 189 | pairedFileRow.Attributes &= ~MsiInterop.MsidbFileAttributesPatchAdded; | 199 | pairedFileRow.Attributes &= ~WindowsInstallerConstants.MsidbFileAttributesPatchAdded; |
| 190 | pairedFileRow.Fields[6].Modified = false; | 200 | pairedFileRow.Fields[6].Modified = false; |
| 191 | pairedFileRow.Operation = RowOperation.None; | 201 | pairedFileRow.Operation = RowOperation.None; |
| 192 | } | 202 | } |
| @@ -197,8 +207,8 @@ namespace WixToolset.Core.WindowsInstaller.Bind | |||
| 197 | else if (null != pairedFileRow) // RowOperation.Add | 207 | else if (null != pairedFileRow) // RowOperation.Add |
| 198 | { | 208 | { |
| 199 | // Always patch-added, but never non-compressed. | 209 | // Always patch-added, but never non-compressed. |
| 200 | pairedFileRow.Attributes |= MsiInterop.MsidbFileAttributesPatchAdded; | 210 | pairedFileRow.Attributes |= WindowsInstallerConstants.MsidbFileAttributesPatchAdded; |
| 201 | pairedFileRow.Attributes &= ~MsiInterop.MsidbFileAttributesNoncompressed; | 211 | pairedFileRow.Attributes &= ~WindowsInstallerConstants.MsidbFileAttributesNoncompressed; |
| 202 | pairedFileRow.Fields[6].Modified = true; | 212 | pairedFileRow.Fields[6].Modified = true; |
| 203 | pairedFileRow.Operation = RowOperation.Add; | 213 | pairedFileRow.Operation = RowOperation.Add; |
| 204 | } | 214 | } |
| @@ -207,20 +217,19 @@ namespace WixToolset.Core.WindowsInstaller.Bind | |||
| 207 | // index patch files by diskId+fileId | 217 | // index patch files by diskId+fileId |
| 208 | int diskId = mainWixFileRow.DiskId; | 218 | int diskId = mainWixFileRow.DiskId; |
| 209 | 219 | ||
| 210 | RowDictionary<WixFileRow> mediaFileRows; | 220 | if (!patchMediaFileRows.TryGetValue(diskId, out var mediaFileRows)) |
| 211 | if (!patchMediaFileRows.TryGetValue(diskId, out mediaFileRows)) | ||
| 212 | { | 221 | { |
| 213 | mediaFileRows = new RowDictionary<WixFileRow>(); | 222 | mediaFileRows = new RowDictionary<WixFileRow>(); |
| 214 | patchMediaFileRows.Add(diskId, mediaFileRows); | 223 | patchMediaFileRows.Add(diskId, mediaFileRows); |
| 215 | } | 224 | } |
| 216 | 225 | ||
| 217 | string fileId = mainFileRow.File; | 226 | var fileId = mainFileRow.File; |
| 218 | WixFileRow patchFileRow = mediaFileRows.Get(fileId); | 227 | var patchFileRow = mediaFileRows.Get(fileId); |
| 219 | if (copyToPatch) | 228 | if (copyToPatch) |
| 220 | { | 229 | { |
| 221 | if (null == patchFileRow) | 230 | if (null == patchFileRow) |
| 222 | { | 231 | { |
| 223 | FileRow patchActualFileRow = (FileRow)patchFileTable.CreateRow(mainFileRow.SourceLineNumbers); | 232 | var patchActualFileRow = (FileRow)patchFileTable.CreateRow(mainFileRow.SourceLineNumbers); |
| 224 | patchActualFileRow.CopyFrom(mainFileRow); | 233 | patchActualFileRow.CopyFrom(mainFileRow); |
| 225 | 234 | ||
| 226 | patchFileRow = (WixFileRow)patchFileTable.CreateRow(mainFileRow.SourceLineNumbers); | 235 | patchFileRow = (WixFileRow)patchFileTable.CreateRow(mainFileRow.SourceLineNumbers); |
| @@ -237,7 +246,7 @@ namespace WixToolset.Core.WindowsInstaller.Bind | |||
| 237 | // make sure Source is same. Otherwise we are silently ignoring a file. | 246 | // make sure Source is same. Otherwise we are silently ignoring a file. |
| 238 | if (0 != String.Compare(patchFileRow.Source, mainWixFileRow.Source, StringComparison.OrdinalIgnoreCase)) | 247 | if (0 != String.Compare(patchFileRow.Source, mainWixFileRow.Source, StringComparison.OrdinalIgnoreCase)) |
| 239 | { | 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)); |
| 241 | } | 250 | } |
| 242 | 251 | ||
| 243 | // capture the previous file versions (and associated data) from this targeted instance of the baseline into the current filerow. | 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 | |||
| 249 | // copy data from the patch back to the transform | 258 | // copy data from the patch back to the transform |
| 250 | if (null != patchFileRow) | 259 | if (null != patchFileRow) |
| 251 | { | 260 | { |
| 252 | FileRow pairedFileRow = (FileRow)pairedFileRows.Get(fileId); | 261 | var pairedFileRow = pairedFileRows.Get(fileId); |
| 253 | for (int i = 0; i < patchFileRow.Fields.Length; i++) | 262 | for (var i = 0; i < patchFileRow.Fields.Length; i++) |
| 254 | { | 263 | { |
| 255 | string patchValue = patchFileRow[i] == null ? "" : patchFileRow[i].ToString(); | 264 | var patchValue = patchFileRow[i] == null ? String.Empty : patchFileRow.FieldAsString(i); |
| 256 | string mainValue = mainFileRow[i] == null ? "" : mainFileRow[i].ToString(); | 265 | var mainValue = mainFileRow[i] == null ? String.Empty : mainFileRow.FieldAsString(i); |
| 257 | 266 | ||
| 258 | if (1 == i) | 267 | if (1 == i) |
| 259 | { | 268 | { |
| @@ -298,17 +307,16 @@ namespace WixToolset.Core.WindowsInstaller.Bind | |||
| 298 | } | 307 | } |
| 299 | 308 | ||
| 300 | // copy MsiFileHash row for this File | 309 | // copy MsiFileHash row for this File |
| 301 | Row patchHashRow; | 310 | if (!mainMsiFileHashIndex.TryGetValue(patchFileRow.File, out var patchHashRow)) |
| 302 | if (!mainMsiFileHashIndex.TryGetValue(patchFileRow.File, out patchHashRow)) | ||
| 303 | { | 311 | { |
| 304 | patchHashRow = patchFileRow.Hash; | 312 | patchHashRow = patchFileRow.Hash; |
| 305 | } | 313 | } |
| 306 | 314 | ||
| 307 | if (null != patchHashRow) | 315 | if (null != patchHashRow) |
| 308 | { | 316 | { |
| 309 | Table mainHashTable = mainTransform.EnsureTable(this.TableDefinitions["MsiFileHash"]); | 317 | var mainHashTable = mainTransform.EnsureTable(this.TableDefinitions["MsiFileHash"]); |
| 310 | Row mainHashRow = mainHashTable.CreateRow(mainFileRow.SourceLineNumbers); | 318 | var mainHashRow = mainHashTable.CreateRow(mainFileRow.SourceLineNumbers); |
| 311 | for (int i = 0; i < patchHashRow.Fields.Length; i++) | 319 | for (var i = 0; i < patchHashRow.Fields.Length; i++) |
| 312 | { | 320 | { |
| 313 | mainHashRow[i] = patchHashRow[i]; | 321 | mainHashRow[i] = patchHashRow[i]; |
| 314 | if (i > 1) | 322 | if (i > 1) |
| @@ -326,12 +334,12 @@ namespace WixToolset.Core.WindowsInstaller.Bind | |||
| 326 | List<Row> patchAssemblyNameRows = patchFileRow.AssemblyNames; | 334 | List<Row> patchAssemblyNameRows = patchFileRow.AssemblyNames; |
| 327 | if (null != patchAssemblyNameRows) | 335 | if (null != patchAssemblyNameRows) |
| 328 | { | 336 | { |
| 329 | Table mainAssemblyNameTable = mainTransform.EnsureTable(this.TableDefinitions["MsiAssemblyName"]); | 337 | var mainAssemblyNameTable = mainTransform.EnsureTable(this.TableDefinitions["MsiAssemblyName"]); |
| 330 | foreach (Row patchAssemblyNameRow in patchAssemblyNameRows) | 338 | foreach (var patchAssemblyNameRow in patchAssemblyNameRows) |
| 331 | { | 339 | { |
| 332 | // Copy if there isn't an identical modified/added row already in the transform. | 340 | // Copy if there isn't an identical modified/added row already in the transform. |
| 333 | bool foundMatchingModifiedRow = false; | 341 | var foundMatchingModifiedRow = false; |
| 334 | foreach (Row mainAssemblyNameRow in mainAssemblyNameTable.Rows) | 342 | foreach (var mainAssemblyNameRow in mainAssemblyNameTable.Rows) |
| 335 | { | 343 | { |
| 336 | if (RowOperation.None != mainAssemblyNameRow.Operation && mainAssemblyNameRow.GetPrimaryKey('/').Equals(patchAssemblyNameRow.GetPrimaryKey('/'))) | 344 | if (RowOperation.None != mainAssemblyNameRow.Operation && mainAssemblyNameRow.GetPrimaryKey('/').Equals(patchAssemblyNameRow.GetPrimaryKey('/'))) |
| 337 | { | 345 | { |
| @@ -342,8 +350,8 @@ namespace WixToolset.Core.WindowsInstaller.Bind | |||
| 342 | 350 | ||
| 343 | if (!foundMatchingModifiedRow) | 351 | if (!foundMatchingModifiedRow) |
| 344 | { | 352 | { |
| 345 | Row mainAssemblyNameRow = mainAssemblyNameTable.CreateRow(mainFileRow.SourceLineNumbers); | 353 | var mainAssemblyNameRow = mainAssemblyNameTable.CreateRow(mainFileRow.SourceLineNumbers); |
| 346 | for (int i = 0; i < patchAssemblyNameRow.Fields.Length; i++) | 354 | for (var i = 0; i < patchAssemblyNameRow.Fields.Length; i++) |
| 347 | { | 355 | { |
| 348 | mainAssemblyNameRow[i] = patchAssemblyNameRow[i]; | 356 | mainAssemblyNameRow[i] = patchAssemblyNameRow[i]; |
| 349 | } | 357 | } |
| @@ -359,34 +367,36 @@ namespace WixToolset.Core.WindowsInstaller.Bind | |||
| 359 | if (null != patchFileRow.Patch) | 367 | if (null != patchFileRow.Patch) |
| 360 | { | 368 | { |
| 361 | // Add the PatchFiles action automatically to the AdminExecuteSequence and InstallExecuteSequence tables. | 369 | // Add the PatchFiles action automatically to the AdminExecuteSequence and InstallExecuteSequence tables. |
| 362 | AddPatchFilesActionToSequenceTable(SequenceTable.AdminExecuteSequence, mainTransform, pairedTransform, mainFileRow); | 370 | this.AddPatchFilesActionToSequenceTable(SequenceTable.AdminExecuteSequence, mainTransform, pairedTransform, mainFileRow); |
| 363 | AddPatchFilesActionToSequenceTable(SequenceTable.InstallExecuteSequence, mainTransform, pairedTransform, mainFileRow); | 371 | this.AddPatchFilesActionToSequenceTable(SequenceTable.InstallExecuteSequence, mainTransform, pairedTransform, mainFileRow); |
| 364 | 372 | ||
| 365 | // Add to Patch table | 373 | // Add to Patch table |
| 366 | Table patchTable = pairedTransform.EnsureTable(this.TableDefinitions["Patch"]); | 374 | var patchTable = pairedTransform.EnsureTable(this.TableDefinitions["Patch"]); |
| 367 | if (0 == patchTable.Rows.Count) | 375 | if (0 == patchTable.Rows.Count) |
| 368 | { | 376 | { |
| 369 | patchTable.Operation = TableOperation.Add; | 377 | patchTable.Operation = TableOperation.Add; |
| 370 | } | 378 | } |
| 371 | 379 | ||
| 372 | Row patchRow = patchTable.CreateRow(mainFileRow.SourceLineNumbers); | 380 | var patchRow = patchTable.CreateRow(mainFileRow.SourceLineNumbers); |
| 373 | patchRow[0] = patchFileRow.File; | 381 | patchRow[0] = patchFileRow.File; |
| 374 | patchRow[1] = patchFileRow.Sequence; | 382 | patchRow[1] = patchFileRow.Sequence; |
| 375 | 383 | ||
| 376 | FileInfo patchFile = new FileInfo(patchFileRow.Source); | 384 | var patchFile = new FileInfo(patchFileRow.Source); |
| 377 | patchRow[2] = (int)patchFile.Length; | 385 | patchRow[2] = (int)patchFile.Length; |
| 378 | patchRow[3] = 0 == (PatchAttributeType.AllowIgnoreOnError & patchFileRow.PatchAttributes) ? 0 : 1; | 386 | patchRow[3] = 0 == (PatchAttributeType.AllowIgnoreOnError & patchFileRow.PatchAttributes) ? 0 : 1; |
| 379 | 387 | ||
| 380 | string streamName = patchTable.Name + "." + patchRow[0] + "." + patchRow[1]; | 388 | var streamName = patchTable.Name + "." + patchRow[0] + "." + patchRow[1]; |
| 381 | if (MsiInterop.MsiMaxStreamNameLength < streamName.Length) | 389 | if (Msi.MsiInterop.MsiMaxStreamNameLength < streamName.Length) |
| 382 | { | 390 | { |
| 383 | streamName = "_" + Guid.NewGuid().ToString("D").ToUpperInvariant().Replace('-', '_'); | 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"]); | ||
| 385 | if (0 == patchHeadersTable.Rows.Count) | 394 | if (0 == patchHeadersTable.Rows.Count) |
| 386 | { | 395 | { |
| 387 | patchHeadersTable.Operation = TableOperation.Add; | 396 | patchHeadersTable.Operation = TableOperation.Add; |
| 388 | } | 397 | } |
| 389 | Row patchHeadersRow = patchHeadersTable.CreateRow(mainFileRow.SourceLineNumbers); | 398 | |
| 399 | var patchHeadersRow = patchHeadersTable.CreateRow(mainFileRow.SourceLineNumbers); | ||
| 390 | patchHeadersRow[0] = streamName; | 400 | patchHeadersRow[0] = streamName; |
| 391 | patchHeadersRow[1] = patchFileRow.Patch; | 401 | patchHeadersRow[1] = patchFileRow.Patch; |
| 392 | patchRow[5] = streamName; | 402 | patchRow[5] = streamName; |
| @@ -420,7 +430,7 @@ namespace WixToolset.Core.WindowsInstaller.Bind | |||
| 420 | { | 430 | { |
| 421 | this.FileManagerCore.ActiveSubStorage = null; | 431 | this.FileManagerCore.ActiveSubStorage = null; |
| 422 | } | 432 | } |
| 423 | #endif | 433 | |
| 424 | this.FileFacades = allFileRows; | 434 | this.FileFacades = allFileRows; |
| 425 | } | 435 | } |
| 426 | 436 | ||
| @@ -509,17 +519,19 @@ namespace WixToolset.Core.WindowsInstaller.Bind | |||
| 509 | foreach (var row in sequenceTable.Rows) | 519 | foreach (var row in sequenceTable.Rows) |
| 510 | { | 520 | { |
| 511 | var actionName = row.FieldAsString(0); | 521 | var actionName = row.FieldAsString(0); |
| 512 | if (String.Equals("PatchFiles", actionName, StringComparison.Ordinal)) | 522 | switch (actionName) |
| 513 | { | ||
| 514 | hasPatchFilesAction = true; | ||
| 515 | } | ||
| 516 | else if (String.Equals("InstallFiles", actionName, StringComparison.Ordinal)) | ||
| 517 | { | 523 | { |
| 518 | installFilesSequence = row.FieldAsInteger(2); | 524 | case "PatchFiles": |
| 519 | } | 525 | hasPatchFilesAction = true; |
| 520 | else if (String.Equals("DuplicateFiles", actionName, StringComparison.Ordinal)) | 526 | break; |
| 521 | { | 527 | |
| 522 | duplicateFilesSequence = row.FieldAsInteger(2); | 528 | case "InstallFiles": |
| 529 | installFilesSequence = row.FieldAsInteger(2); | ||
| 530 | break; | ||
| 531 | |||
| 532 | case "DuplicateFiles": | ||
| 533 | duplicateFilesSequence = row.FieldAsInteger(2); | ||
| 534 | break; | ||
| 523 | } | 535 | } |
| 524 | } | 536 | } |
| 525 | } | 537 | } |
| @@ -531,8 +543,8 @@ namespace WixToolset.Core.WindowsInstaller.Bind | |||
| 531 | /// <param name="output">The output to validate.</param> | 543 | /// <param name="output">The output to validate.</param> |
| 532 | private void ValidateFileRowChanges(WindowsInstallerData transform) | 544 | private void ValidateFileRowChanges(WindowsInstallerData transform) |
| 533 | { | 545 | { |
| 534 | Table componentTable = transform.Tables["Component"]; | 546 | var componentTable = transform.Tables["Component"]; |
| 535 | Table fileTable = transform.Tables["File"]; | 547 | var fileTable = transform.Tables["File"]; |
| 536 | 548 | ||
| 537 | // There's no sense validating keypaths if the transform has no component or file table | 549 | // There's no sense validating keypaths if the transform has no component or file table |
| 538 | if (componentTable == null || fileTable == null) | 550 | if (componentTable == null || fileTable == null) |
| @@ -540,31 +552,31 @@ namespace WixToolset.Core.WindowsInstaller.Bind | |||
| 540 | return; | 552 | return; |
| 541 | } | 553 | } |
| 542 | 554 | ||
| 543 | Dictionary<string, string> componentKeyPath = new Dictionary<string, string>(componentTable.Rows.Count); | 555 | var componentKeyPath = new Dictionary<string, string>(componentTable.Rows.Count); |
| 544 | 556 | ||
| 545 | // Index the Component table for non-directory & non-registry key paths. | 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) |
| 547 | { | 559 | { |
| 548 | if (null != row.Fields[5].Data && | 560 | var keyPath = row.FieldAsString(5); |
| 549 | 0 != ((int)row.Fields[3].Data & WindowsInstallerConstants.MsidbComponentAttributesRegistryKeyPath)) | 561 | if (keyPath != null && 0 != (row.FieldAsInteger(3) & WindowsInstallerConstants.MsidbComponentAttributesRegistryKeyPath)) |
| 550 | { | 562 | { |
| 551 | componentKeyPath.Add(row.Fields[0].Data.ToString(), row.Fields[5].Data.ToString()); | 563 | componentKeyPath.Add(row.FieldAsString(0), keyPath); |
| 552 | } | 564 | } |
| 553 | } | 565 | } |
| 554 | 566 | ||
| 555 | Dictionary<string, string> componentWithChangedKeyPath = new Dictionary<string, string>(); | 567 | var componentWithChangedKeyPath = new Dictionary<string, string>(); |
| 556 | Dictionary<string, string> componentWithNonKeyPathChanged = new Dictionary<string, string>(); | 568 | var componentWithNonKeyPathChanged = new Dictionary<string, string>(); |
| 557 | // Verify changes in the file table, now that file diffing has occurred | 569 | // Verify changes in the file table, now that file diffing has occurred |
| 558 | foreach (FileRow row in fileTable.Rows) | 570 | foreach (FileRow row in fileTable.Rows) |
| 559 | { | 571 | { |
| 560 | string fileId = row.Fields[0].Data.ToString(); | ||
| 561 | string componentId = row.Fields[1].Data.ToString(); | ||
| 562 | |||
| 563 | if (RowOperation.Modify != row.Operation) | 572 | if (RowOperation.Modify != row.Operation) |
| 564 | { | 573 | { |
| 565 | continue; | 574 | continue; |
| 566 | } | 575 | } |
| 567 | 576 | ||
| 577 | var fileId = row.FieldAsString(0); | ||
| 578 | var componentId = row.FieldAsString(1); | ||
| 579 | |||
| 568 | // If this file is the keypath of a component | 580 | // If this file is the keypath of a component |
| 569 | if (componentKeyPath.ContainsValue(fileId)) | 581 | if (componentKeyPath.ContainsValue(fileId)) |
| 570 | { | 582 | { |
| @@ -582,12 +594,12 @@ namespace WixToolset.Core.WindowsInstaller.Bind | |||
| 582 | } | 594 | } |
| 583 | } | 595 | } |
| 584 | 596 | ||
| 585 | foreach (KeyValuePair<string, string> componentFile in componentWithNonKeyPathChanged) | 597 | foreach (var componentFile in componentWithNonKeyPathChanged) |
| 586 | { | 598 | { |
| 587 | // Make sure all changes to non keypath files also had a change in the keypath. | 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)) |
| 589 | { | 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)); |
| 591 | } | 603 | } |
| 592 | } | 604 | } |
| 593 | } | 605 | } |
| @@ -614,3 +626,5 @@ namespace WixToolset.Core.WindowsInstaller.Bind | |||
| 614 | } | 626 | } |
| 615 | } | 627 | } |
| 616 | } | 628 | } |
| 629 | |||
| 630 | #endif | ||
diff --git a/src/WixToolset.Core.WindowsInstaller/Bind/CreateDeltaPatchesCommand.cs b/src/WixToolset.Core.WindowsInstaller/Bind/CreateDeltaPatchesCommand.cs index 19f7b9e5..f5ac00e6 100644 --- a/src/WixToolset.Core.WindowsInstaller/Bind/CreateDeltaPatchesCommand.cs +++ b/src/WixToolset.Core.WindowsInstaller/Bind/CreateDeltaPatchesCommand.cs | |||
| @@ -33,7 +33,7 @@ namespace WixToolset.Core.WindowsInstaller.Bind | |||
| 33 | var optimizePatchSizeForLargeFiles = this.WixPatchId?.OptimizePatchSizeForLargeFiles ?? false; | 33 | var optimizePatchSizeForLargeFiles = this.WixPatchId?.OptimizePatchSizeForLargeFiles ?? false; |
| 34 | var apiPatchingSymbolFlags = (PatchSymbolFlagsType)(this.WixPatchId?.ApiPatchingSymbolFlags ?? 0); | 34 | var apiPatchingSymbolFlags = (PatchSymbolFlagsType)(this.WixPatchId?.ApiPatchingSymbolFlags ?? 0); |
| 35 | 35 | ||
| 36 | #if REVISIT_FOR_PATCHING | 36 | #if TODO_PATCHING |
| 37 | foreach (FileFacade facade in this.FileFacades) | 37 | foreach (FileFacade facade in this.FileFacades) |
| 38 | { | 38 | { |
| 39 | if (RowOperation.Modify == facade.File.Operation && | 39 | if (RowOperation.Modify == facade.File.Operation && |
diff --git a/src/WixToolset.Core.WindowsInstaller/Bind/CreateIdtFileCommand.cs b/src/WixToolset.Core.WindowsInstaller/Bind/CreateIdtFileCommand.cs index 6b1dead5..f09a2e47 100644 --- a/src/WixToolset.Core.WindowsInstaller/Bind/CreateIdtFileCommand.cs +++ b/src/WixToolset.Core.WindowsInstaller/Bind/CreateIdtFileCommand.cs | |||
| @@ -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 | 2 | ||
| 3 | namespace WixToolset.Core.WindowsInstaller.Bind | 3 | namespace WixToolset.Core.WindowsInstaller.Bind |
| 4 | { | 4 | { |
| @@ -122,13 +122,18 @@ namespace WixToolset.Core.WindowsInstaller.Bind | |||
| 122 | tableString.Append(definition.Name); | 122 | tableString.Append(definition.Name); |
| 123 | foreach (var column in definition.Columns) | 123 | foreach (var column in definition.Columns) |
| 124 | { | 124 | { |
| 125 | // conditionally keep columns added in a transform; otherwise, | 125 | // Conditionally keep columns added in a transform; otherwise, |
| 126 | // break because columns can only be added at the end | 126 | // break because columns can only be added at the end. |
| 127 | if (column.Added && !keepAddedColumns) | 127 | if (column.Added && !keepAddedColumns) |
| 128 | { | 128 | { |
| 129 | break; | 129 | break; |
| 130 | } | 130 | } |
| 131 | 131 | ||
| 132 | if (column.Unreal) | ||
| 133 | { | ||
| 134 | continue; | ||
| 135 | } | ||
| 136 | |||
| 132 | if (!first) | 137 | if (!first) |
| 133 | { | 138 | { |
| 134 | columnString.Append('\t'); | 139 | columnString.Append('\t'); |
| @@ -168,6 +173,11 @@ namespace WixToolset.Core.WindowsInstaller.Bind | |||
| 168 | break; | 173 | break; |
| 169 | } | 174 | } |
| 170 | 175 | ||
| 176 | if (field.Column.Unreal) | ||
| 177 | { | ||
| 178 | continue; | ||
| 179 | } | ||
| 180 | |||
| 171 | if (first) | 181 | if (first) |
| 172 | { | 182 | { |
| 173 | first = false; | 183 | first = false; |
diff --git a/src/WixToolset.Core.WindowsInstaller/Bind/CreateOutputFromIRCommand.cs b/src/WixToolset.Core.WindowsInstaller/Bind/CreateOutputFromIRCommand.cs index 31d0b3a6..5707f7ce 100644 --- a/src/WixToolset.Core.WindowsInstaller/Bind/CreateOutputFromIRCommand.cs +++ b/src/WixToolset.Core.WindowsInstaller/Bind/CreateOutputFromIRCommand.cs | |||
| @@ -40,193 +40,193 @@ namespace WixToolset.Core.WindowsInstaller.Bind | |||
| 40 | 40 | ||
| 41 | public void Execute() | 41 | public void Execute() |
| 42 | { | 42 | { |
| 43 | var output = new WindowsInstallerData(this.Section.Tuples.First().SourceLineNumbers); | 43 | this.Output = new WindowsInstallerData(this.Section.Tuples.First().SourceLineNumbers) |
| 44 | output.Codepage = this.Section.Codepage; | 44 | { |
| 45 | output.Type = SectionTypeToOutputType(this.Section.Type); | 45 | Codepage = this.Section.Codepage, |
| 46 | 46 | Type = SectionTypeToOutputType(this.Section.Type) | |
| 47 | this.AddSectionToOutput(this.Section, output); | 47 | }; |
| 48 | 48 | ||
| 49 | this.Output = output; | 49 | this.AddSectionToOutput(); |
| 50 | } | 50 | } |
| 51 | 51 | ||
| 52 | private void AddSectionToOutput(IntermediateSection section, WindowsInstallerData output) | 52 | private void AddSectionToOutput() |
| 53 | { | 53 | { |
| 54 | foreach (var tuple in section.Tuples) | 54 | foreach (var tuple in this.Section.Tuples) |
| 55 | { | 55 | { |
| 56 | switch (tuple.Definition.Type) | 56 | switch (tuple.Definition.Type) |
| 57 | { | 57 | { |
| 58 | case TupleDefinitionType.AppSearch: | 58 | case TupleDefinitionType.AppSearch: |
| 59 | this.AddTupleDefaultly(tuple, output); | 59 | this.AddTupleDefaultly(tuple); |
| 60 | output.EnsureTable(this.TableDefinitions["Signature"]); | 60 | this.Output.EnsureTable(this.TableDefinitions["Signature"]); |
| 61 | break; | 61 | break; |
| 62 | 62 | ||
| 63 | case TupleDefinitionType.Assembly: | 63 | case TupleDefinitionType.Assembly: |
| 64 | this.AddAssemblyTuple((AssemblyTuple)tuple, output); | 64 | this.AddAssemblyTuple((AssemblyTuple)tuple); |
| 65 | break; | 65 | break; |
| 66 | 66 | ||
| 67 | case TupleDefinitionType.Binary: | 67 | case TupleDefinitionType.Binary: |
| 68 | this.AddTupleDefaultly(tuple, output, idIsPrimaryKey: true); | 68 | this.AddTupleDefaultly(tuple, idIsPrimaryKey: true); |
| 69 | break; | 69 | break; |
| 70 | 70 | ||
| 71 | case TupleDefinitionType.BBControl: | 71 | case TupleDefinitionType.BBControl: |
| 72 | this.AddBBControlTuple((BBControlTuple)tuple, output); | 72 | this.AddBBControlTuple((BBControlTuple)tuple); |
| 73 | break; | 73 | break; |
| 74 | 74 | ||
| 75 | case TupleDefinitionType.Class: | 75 | case TupleDefinitionType.Class: |
| 76 | this.AddClassTuple((ClassTuple)tuple, output); | 76 | this.AddClassTuple((ClassTuple)tuple); |
| 77 | break; | 77 | break; |
| 78 | 78 | ||
| 79 | case TupleDefinitionType.Control: | 79 | case TupleDefinitionType.Control: |
| 80 | this.AddControlTuple((ControlTuple)tuple, output); | 80 | this.AddControlTuple((ControlTuple)tuple); |
| 81 | break; | 81 | break; |
| 82 | 82 | ||
| 83 | case TupleDefinitionType.Component: | 83 | case TupleDefinitionType.Component: |
| 84 | this.AddComponentTuple((ComponentTuple)tuple, output); | 84 | this.AddComponentTuple((ComponentTuple)tuple); |
| 85 | break; | 85 | break; |
| 86 | 86 | ||
| 87 | case TupleDefinitionType.CustomAction: | 87 | case TupleDefinitionType.CustomAction: |
| 88 | this.AddCustomActionTuple((CustomActionTuple)tuple, output); | 88 | this.AddCustomActionTuple((CustomActionTuple)tuple); |
| 89 | break; | 89 | break; |
| 90 | 90 | ||
| 91 | case TupleDefinitionType.Dialog: | 91 | case TupleDefinitionType.Dialog: |
| 92 | this.AddDialogTuple((DialogTuple)tuple, output); | 92 | this.AddDialogTuple((DialogTuple)tuple); |
| 93 | break; | 93 | break; |
| 94 | 94 | ||
| 95 | case TupleDefinitionType.Directory: | 95 | case TupleDefinitionType.Directory: |
| 96 | this.AddDirectoryTuple((DirectoryTuple)tuple, output); | 96 | this.AddDirectoryTuple((DirectoryTuple)tuple); |
| 97 | break; | 97 | break; |
| 98 | 98 | ||
| 99 | case TupleDefinitionType.Environment: | 99 | case TupleDefinitionType.Environment: |
| 100 | this.AddEnvironmentTuple((EnvironmentTuple)tuple, output); | 100 | this.AddEnvironmentTuple((EnvironmentTuple)tuple); |
| 101 | break; | 101 | break; |
| 102 | 102 | ||
| 103 | case TupleDefinitionType.Error: | 103 | case TupleDefinitionType.Error: |
| 104 | this.AddErrorTuple((ErrorTuple)tuple, output); | 104 | this.AddErrorTuple((ErrorTuple)tuple); |
| 105 | break; | 105 | break; |
| 106 | 106 | ||
| 107 | case TupleDefinitionType.Feature: | 107 | case TupleDefinitionType.Feature: |
| 108 | this.AddFeatureTuple((FeatureTuple)tuple, output); | 108 | this.AddFeatureTuple((FeatureTuple)tuple); |
| 109 | break; | 109 | break; |
| 110 | 110 | ||
| 111 | case TupleDefinitionType.File: | 111 | case TupleDefinitionType.File: |
| 112 | this.AddFileTuple((FileTuple)tuple, output); | 112 | this.AddFileTuple((FileTuple)tuple); |
| 113 | break; | 113 | break; |
| 114 | 114 | ||
| 115 | case TupleDefinitionType.Icon: | 115 | case TupleDefinitionType.Icon: |
| 116 | this.AddTupleDefaultly(tuple, output, idIsPrimaryKey: true); | 116 | this.AddTupleDefaultly(tuple, idIsPrimaryKey: true); |
| 117 | break; | 117 | break; |
| 118 | 118 | ||
| 119 | case TupleDefinitionType.IniFile: | 119 | case TupleDefinitionType.IniFile: |
| 120 | this.AddIniFileTuple((IniFileTuple)tuple, output); | 120 | this.AddIniFileTuple((IniFileTuple)tuple); |
| 121 | break; | 121 | break; |
| 122 | 122 | ||
| 123 | case TupleDefinitionType.Media: | 123 | case TupleDefinitionType.Media: |
| 124 | this.AddMediaTuple((MediaTuple)tuple, output); | 124 | this.AddMediaTuple((MediaTuple)tuple); |
| 125 | break; | 125 | break; |
| 126 | 126 | ||
| 127 | case TupleDefinitionType.ModuleConfiguration: | 127 | case TupleDefinitionType.ModuleConfiguration: |
| 128 | this.AddModuleConfigurationTuple((ModuleConfigurationTuple)tuple, output); | 128 | this.AddModuleConfigurationTuple((ModuleConfigurationTuple)tuple); |
| 129 | break; | 129 | break; |
| 130 | 130 | ||
| 131 | case TupleDefinitionType.MsiEmbeddedUI: | 131 | case TupleDefinitionType.MsiEmbeddedUI: |
| 132 | this.AddMsiEmbeddedUITuple((MsiEmbeddedUITuple)tuple, output); | 132 | this.AddMsiEmbeddedUITuple((MsiEmbeddedUITuple)tuple); |
| 133 | break; | 133 | break; |
| 134 | 134 | ||
| 135 | case TupleDefinitionType.MsiFileHash: | 135 | case TupleDefinitionType.MsiFileHash: |
| 136 | this.AddMsiFileHashTuple((MsiFileHashTuple)tuple, output); | 136 | this.AddMsiFileHashTuple((MsiFileHashTuple)tuple); |
| 137 | break; | 137 | break; |
| 138 | 138 | ||
| 139 | case TupleDefinitionType.MsiServiceConfig: | 139 | case TupleDefinitionType.MsiServiceConfig: |
| 140 | this.AddMsiServiceConfigTuple((MsiServiceConfigTuple)tuple, output); | 140 | this.AddMsiServiceConfigTuple((MsiServiceConfigTuple)tuple); |
| 141 | break; | 141 | break; |
| 142 | 142 | ||
| 143 | case TupleDefinitionType.MsiServiceConfigFailureActions: | 143 | case TupleDefinitionType.MsiServiceConfigFailureActions: |
| 144 | this.AddMsiServiceConfigFailureActionsTuple((MsiServiceConfigFailureActionsTuple)tuple, output); | 144 | this.AddMsiServiceConfigFailureActionsTuple((MsiServiceConfigFailureActionsTuple)tuple); |
| 145 | break; | 145 | break; |
| 146 | 146 | ||
| 147 | case TupleDefinitionType.MsiShortcutProperty: | 147 | case TupleDefinitionType.MsiShortcutProperty: |
| 148 | this.AddTupleDefaultly(tuple, output, idIsPrimaryKey: true); | 148 | this.AddTupleDefaultly(tuple, idIsPrimaryKey: true); |
| 149 | break; | 149 | break; |
| 150 | 150 | ||
| 151 | case TupleDefinitionType.MoveFile: | 151 | case TupleDefinitionType.MoveFile: |
| 152 | this.AddMoveFileTuple((MoveFileTuple)tuple, output); | 152 | this.AddMoveFileTuple((MoveFileTuple)tuple); |
| 153 | break; | 153 | break; |
| 154 | 154 | ||
| 155 | case TupleDefinitionType.ProgId: | 155 | case TupleDefinitionType.ProgId: |
| 156 | this.AddTupleDefaultly(tuple, output); | 156 | this.AddTupleDefaultly(tuple); |
| 157 | output.EnsureTable(this.TableDefinitions["Extension"]); | 157 | this.Output.EnsureTable(this.TableDefinitions["Extension"]); |
| 158 | break; | 158 | break; |
| 159 | 159 | ||
| 160 | case TupleDefinitionType.Property: | 160 | case TupleDefinitionType.Property: |
| 161 | this.AddPropertyTuple((PropertyTuple)tuple, output); | 161 | this.AddPropertyTuple((PropertyTuple)tuple); |
| 162 | break; | 162 | break; |
| 163 | 163 | ||
| 164 | case TupleDefinitionType.RemoveFile: | 164 | case TupleDefinitionType.RemoveFile: |
| 165 | this.AddRemoveFileTuple((RemoveFileTuple)tuple, output); | 165 | this.AddRemoveFileTuple((RemoveFileTuple)tuple); |
| 166 | break; | 166 | break; |
| 167 | 167 | ||
| 168 | case TupleDefinitionType.Registry: | 168 | case TupleDefinitionType.Registry: |
| 169 | this.AddRegistryTuple((RegistryTuple)tuple, output); | 169 | this.AddRegistryTuple((RegistryTuple)tuple); |
| 170 | break; | 170 | break; |
| 171 | 171 | ||
| 172 | case TupleDefinitionType.RegLocator: | 172 | case TupleDefinitionType.RegLocator: |
| 173 | this.AddRegLocatorTuple((RegLocatorTuple)tuple, output); | 173 | this.AddRegLocatorTuple((RegLocatorTuple)tuple); |
| 174 | break; | 174 | break; |
| 175 | 175 | ||
| 176 | case TupleDefinitionType.RemoveRegistry: | 176 | case TupleDefinitionType.RemoveRegistry: |
| 177 | this.AddRemoveRegistryTuple((RemoveRegistryTuple)tuple, output); | 177 | this.AddRemoveRegistryTuple((RemoveRegistryTuple)tuple); |
| 178 | break; | 178 | break; |
| 179 | 179 | ||
| 180 | case TupleDefinitionType.ReserveCost: | 180 | case TupleDefinitionType.ReserveCost: |
| 181 | this.AddTupleDefaultly(tuple, output, idIsPrimaryKey: true); | 181 | this.AddTupleDefaultly(tuple, idIsPrimaryKey: true); |
| 182 | break; | 182 | break; |
| 183 | 183 | ||
| 184 | case TupleDefinitionType.ServiceControl: | 184 | case TupleDefinitionType.ServiceControl: |
| 185 | this.AddServiceControlTuple((ServiceControlTuple)tuple, output); | 185 | this.AddServiceControlTuple((ServiceControlTuple)tuple); |
| 186 | break; | 186 | break; |
| 187 | 187 | ||
| 188 | case TupleDefinitionType.ServiceInstall: | 188 | case TupleDefinitionType.ServiceInstall: |
| 189 | this.AddServiceInstallTuple((ServiceInstallTuple)tuple, output); | 189 | this.AddServiceInstallTuple((ServiceInstallTuple)tuple); |
| 190 | break; | 190 | break; |
| 191 | 191 | ||
| 192 | case TupleDefinitionType.Shortcut: | 192 | case TupleDefinitionType.Shortcut: |
| 193 | this.AddShortcutTuple((ShortcutTuple)tuple, output); | 193 | this.AddShortcutTuple((ShortcutTuple)tuple); |
| 194 | break; | 194 | break; |
| 195 | 195 | ||
| 196 | case TupleDefinitionType.Signature: | 196 | case TupleDefinitionType.Signature: |
| 197 | this.AddTupleDefaultly(tuple, output, idIsPrimaryKey: true); | 197 | this.AddTupleDefaultly(tuple, idIsPrimaryKey: true); |
| 198 | break; | 198 | break; |
| 199 | 199 | ||
| 200 | case TupleDefinitionType.SummaryInformation: | 200 | case TupleDefinitionType.SummaryInformation: |
| 201 | this.AddTupleDefaultly(tuple, output, tableName: "_SummaryInformation"); | 201 | this.AddTupleDefaultly(tuple, tableName: "_SummaryInformation"); |
| 202 | break; | 202 | break; |
| 203 | 203 | ||
| 204 | case TupleDefinitionType.TextStyle: | 204 | case TupleDefinitionType.TextStyle: |
| 205 | this.AddTextStyleTuple((TextStyleTuple)tuple, output); | 205 | this.AddTextStyleTuple((TextStyleTuple)tuple); |
| 206 | break; | 206 | break; |
| 207 | 207 | ||
| 208 | case TupleDefinitionType.Upgrade: | 208 | case TupleDefinitionType.Upgrade: |
| 209 | this.AddUpgradeTuple((UpgradeTuple)tuple, output); | 209 | this.AddUpgradeTuple((UpgradeTuple)tuple); |
| 210 | break; | 210 | break; |
| 211 | 211 | ||
| 212 | case TupleDefinitionType.WixAction: | 212 | case TupleDefinitionType.WixAction: |
| 213 | this.AddWixActionTuple((WixActionTuple)tuple, output); | 213 | this.AddWixActionTuple((WixActionTuple)tuple); |
| 214 | break; | 214 | break; |
| 215 | 215 | ||
| 216 | case TupleDefinitionType.WixMediaTemplate: | 216 | case TupleDefinitionType.WixMediaTemplate: |
| 217 | this.AddWixMediaTemplateTuple((WixMediaTemplateTuple)tuple, output); | 217 | this.AddWixMediaTemplateTuple((WixMediaTemplateTuple)tuple); |
| 218 | break; | 218 | break; |
| 219 | 219 | ||
| 220 | case TupleDefinitionType.MustBeFromAnExtension: | 220 | case TupleDefinitionType.MustBeFromAnExtension: |
| 221 | this.AddTupleFromExtension(tuple, output); | 221 | this.AddTupleFromExtension(tuple); |
| 222 | break; | 222 | break; |
| 223 | 223 | ||
| 224 | case TupleDefinitionType.WixCustomRow: | 224 | case TupleDefinitionType.WixCustomRow: |
| 225 | this.AddWixCustomRowTuple((WixCustomRowTuple)tuple, output); | 225 | this.AddWixCustomRowTuple((WixCustomRowTuple)tuple); |
| 226 | break; | 226 | break; |
| 227 | 227 | ||
| 228 | case TupleDefinitionType.WixEnsureTable: | 228 | case TupleDefinitionType.WixEnsureTable: |
| 229 | this.AddWixEnsureTableTuple((WixEnsureTableTuple)tuple, output); | 229 | this.AddWixEnsureTableTuple((WixEnsureTableTuple)tuple); |
| 230 | break; | 230 | break; |
| 231 | 231 | ||
| 232 | // ignored. | 232 | // ignored. |
| @@ -234,25 +234,25 @@ namespace WixToolset.Core.WindowsInstaller.Bind | |||
| 234 | case TupleDefinitionType.WixComponentGroup: | 234 | case TupleDefinitionType.WixComponentGroup: |
| 235 | case TupleDefinitionType.WixDeltaPatchFile: | 235 | case TupleDefinitionType.WixDeltaPatchFile: |
| 236 | case TupleDefinitionType.WixFeatureGroup: | 236 | case TupleDefinitionType.WixFeatureGroup: |
| 237 | break; | 237 | case TupleDefinitionType.WixPatchBaseline: |
| 238 | break; | ||
| 238 | 239 | ||
| 239 | // Already processed. | 240 | // Already processed. |
| 240 | case TupleDefinitionType.WixCustomTable: | 241 | case TupleDefinitionType.WixCustomTable: |
| 241 | break; | 242 | break; |
| 242 | 243 | ||
| 243 | default: | 244 | default: |
| 244 | this.AddTupleDefaultly(tuple, output); | 245 | this.AddTupleDefaultly(tuple); |
| 245 | break; | 246 | break; |
| 246 | } | 247 | } |
| 247 | } | 248 | } |
| 248 | } | 249 | } |
| 249 | 250 | ||
| 250 | private void AddAssemblyTuple(AssemblyTuple tuple, WindowsInstallerData output) | 251 | private void AddAssemblyTuple(AssemblyTuple tuple) |
| 251 | { | 252 | { |
| 252 | var attributes = tuple.Type == AssemblyType.Win32Assembly ? 1 : (int?)null; | 253 | var attributes = tuple.Type == AssemblyType.Win32Assembly ? 1 : (int?)null; |
| 253 | 254 | ||
| 254 | var table = output.EnsureTable(this.TableDefinitions["MsiAssembly"]); | 255 | var row = this.CreateRow(tuple, "MsiAssembly"); |
| 255 | var row = table.CreateRow(tuple.SourceLineNumbers); | ||
| 256 | row[0] = tuple.ComponentRef; | 256 | row[0] = tuple.ComponentRef; |
| 257 | row[1] = tuple.FeatureRef; | 257 | row[1] = tuple.FeatureRef; |
| 258 | row[2] = tuple.ManifestFileRef; | 258 | row[2] = tuple.ManifestFileRef; |
| @@ -260,7 +260,7 @@ namespace WixToolset.Core.WindowsInstaller.Bind | |||
| 260 | row[4] = attributes; | 260 | row[4] = attributes; |
| 261 | } | 261 | } |
| 262 | 262 | ||
| 263 | private void AddBBControlTuple(BBControlTuple tuple, WindowsInstallerData output) | 263 | private void AddBBControlTuple(BBControlTuple tuple) |
| 264 | { | 264 | { |
| 265 | var attributes = tuple.Attributes; | 265 | var attributes = tuple.Attributes; |
| 266 | attributes |= tuple.Enabled ? WindowsInstallerConstants.MsidbControlAttributesEnabled : 0; | 266 | attributes |= tuple.Enabled ? WindowsInstallerConstants.MsidbControlAttributesEnabled : 0; |
| @@ -272,8 +272,7 @@ namespace WixToolset.Core.WindowsInstaller.Bind | |||
| 272 | attributes |= tuple.Sunken ? WindowsInstallerConstants.MsidbControlAttributesSunken : 0; | 272 | attributes |= tuple.Sunken ? WindowsInstallerConstants.MsidbControlAttributesSunken : 0; |
| 273 | attributes |= tuple.Visible ? WindowsInstallerConstants.MsidbControlAttributesVisible : 0; | 273 | attributes |= tuple.Visible ? WindowsInstallerConstants.MsidbControlAttributesVisible : 0; |
| 274 | 274 | ||
| 275 | var table = output.EnsureTable(this.TableDefinitions["BBControl"]); | 275 | var row = this.CreateRow(tuple, "BBControl"); |
| 276 | var row = table.CreateRow(tuple.SourceLineNumbers); | ||
| 277 | row[0] = tuple.BillboardRef; | 276 | row[0] = tuple.BillboardRef; |
| 278 | row[1] = tuple.BBControl; | 277 | row[1] = tuple.BBControl; |
| 279 | row[2] = tuple.Type; | 278 | row[2] = tuple.Type; |
| @@ -285,10 +284,9 @@ namespace WixToolset.Core.WindowsInstaller.Bind | |||
| 285 | row[8] = tuple.Text; | 284 | row[8] = tuple.Text; |
| 286 | } | 285 | } |
| 287 | 286 | ||
| 288 | private void AddClassTuple(ClassTuple tuple, WindowsInstallerData output) | 287 | private void AddClassTuple(ClassTuple tuple) |
| 289 | { | 288 | { |
| 290 | var table = output.EnsureTable(this.TableDefinitions["Class"]); | 289 | var row = this.CreateRow(tuple, "Class"); |
| 291 | var row = table.CreateRow(tuple.SourceLineNumbers); | ||
| 292 | row[0] = tuple.CLSID; | 290 | row[0] = tuple.CLSID; |
| 293 | row[1] = tuple.Context; | 291 | row[1] = tuple.Context; |
| 294 | row[2] = tuple.ComponentRef; | 292 | row[2] = tuple.ComponentRef; |
| @@ -304,7 +302,7 @@ namespace WixToolset.Core.WindowsInstaller.Bind | |||
| 304 | row[12] = tuple.RelativePath ? (int?)1 : null; | 302 | row[12] = tuple.RelativePath ? (int?)1 : null; |
| 305 | } | 303 | } |
| 306 | 304 | ||
| 307 | private void AddControlTuple(ControlTuple tuple, WindowsInstallerData output) | 305 | private void AddControlTuple(ControlTuple tuple) |
| 308 | { | 306 | { |
| 309 | var text = tuple.Text; | 307 | var text = tuple.Text; |
| 310 | var attributes = tuple.Attributes; | 308 | var attributes = tuple.Attributes; |
| @@ -329,8 +327,7 @@ namespace WixToolset.Core.WindowsInstaller.Bind | |||
| 329 | text = String.Concat(text, " "); | 327 | text = String.Concat(text, " "); |
| 330 | } | 328 | } |
| 331 | 329 | ||
| 332 | var table = output.EnsureTable(this.TableDefinitions["Control"]); | 330 | var row = this.CreateRow(tuple, "Control"); |
| 333 | var row = table.CreateRow(tuple.SourceLineNumbers); | ||
| 334 | row[0] = tuple.DialogRef; | 331 | row[0] = tuple.DialogRef; |
| 335 | row[1] = tuple.Control; | 332 | row[1] = tuple.Control; |
| 336 | row[2] = tuple.Type; | 333 | row[2] = tuple.Type; |
| @@ -344,7 +341,7 @@ namespace WixToolset.Core.WindowsInstaller.Bind | |||
| 344 | row[10] = tuple.Help; | 341 | row[10] = tuple.Help; |
| 345 | } | 342 | } |
| 346 | 343 | ||
| 347 | private void AddComponentTuple(ComponentTuple tuple, WindowsInstallerData output) | 344 | private void AddComponentTuple(ComponentTuple tuple) |
| 348 | { | 345 | { |
| 349 | var attributes = ComponentLocation.Either == tuple.Location ? WindowsInstallerConstants.MsidbComponentAttributesOptional : 0; | 346 | var attributes = ComponentLocation.Either == tuple.Location ? WindowsInstallerConstants.MsidbComponentAttributesOptional : 0; |
| 350 | attributes |= ComponentLocation.SourceOnly == tuple.Location ? WindowsInstallerConstants.MsidbComponentAttributesSourceOnly : 0; | 347 | attributes |= ComponentLocation.SourceOnly == tuple.Location ? WindowsInstallerConstants.MsidbComponentAttributesSourceOnly : 0; |
| @@ -359,8 +356,7 @@ namespace WixToolset.Core.WindowsInstaller.Bind | |||
| 359 | attributes |= tuple.UninstallWhenSuperseded ? WindowsInstallerConstants.MsidbComponentAttributesUninstallOnSupersedence : 0; | 356 | attributes |= tuple.UninstallWhenSuperseded ? WindowsInstallerConstants.MsidbComponentAttributesUninstallOnSupersedence : 0; |
| 360 | attributes |= tuple.Win64 ? WindowsInstallerConstants.MsidbComponentAttributes64bit : 0; | 357 | attributes |= tuple.Win64 ? WindowsInstallerConstants.MsidbComponentAttributes64bit : 0; |
| 361 | 358 | ||
| 362 | var table = output.EnsureTable(this.TableDefinitions["Component"]); | 359 | var row = this.CreateRow(tuple, "Component"); |
| 363 | var row = table.CreateRow(tuple.SourceLineNumbers); | ||
| 364 | row[0] = tuple.Id.Id; | 360 | row[0] = tuple.Id.Id; |
| 365 | row[1] = tuple.ComponentId; | 361 | row[1] = tuple.ComponentId; |
| 366 | row[2] = tuple.DirectoryRef; | 362 | row[2] = tuple.DirectoryRef; |
| @@ -369,7 +365,7 @@ namespace WixToolset.Core.WindowsInstaller.Bind | |||
| 369 | row[5] = tuple.KeyPath; | 365 | row[5] = tuple.KeyPath; |
| 370 | } | 366 | } |
| 371 | 367 | ||
| 372 | private void AddCustomActionTuple(CustomActionTuple tuple, WindowsInstallerData output) | 368 | private void AddCustomActionTuple(CustomActionTuple tuple) |
| 373 | { | 369 | { |
| 374 | var type = tuple.Win64 ? WindowsInstallerConstants.MsidbCustomActionType64BitScript : 0; | 370 | var type = tuple.Win64 ? WindowsInstallerConstants.MsidbCustomActionType64BitScript : 0; |
| 375 | type |= tuple.IgnoreResult ? WindowsInstallerConstants.MsidbCustomActionTypeContinue : 0; | 371 | type |= tuple.IgnoreResult ? WindowsInstallerConstants.MsidbCustomActionTypeContinue : 0; |
| @@ -396,8 +392,7 @@ namespace WixToolset.Core.WindowsInstaller.Bind | |||
| 396 | type |= tuple.TSAware ? WindowsInstallerConstants.MsidbCustomActionTypeTSAware : 0; | 392 | type |= tuple.TSAware ? WindowsInstallerConstants.MsidbCustomActionTypeTSAware : 0; |
| 397 | } | 393 | } |
| 398 | 394 | ||
| 399 | var table = output.EnsureTable(this.TableDefinitions["CustomAction"]); | 395 | var row = this.CreateRow(tuple, "CustomAction"); |
| 400 | var row = table.CreateRow(tuple.SourceLineNumbers); | ||
| 401 | row[0] = tuple.Id.Id; | 396 | row[0] = tuple.Id.Id; |
| 402 | row[1] = type; | 397 | row[1] = type; |
| 403 | row[2] = tuple.Source; | 398 | row[2] = tuple.Source; |
| @@ -405,7 +400,7 @@ namespace WixToolset.Core.WindowsInstaller.Bind | |||
| 405 | row[4] = tuple.PatchUninstall ? (int?)WindowsInstallerConstants.MsidbCustomActionTypePatchUninstall : null; | 400 | row[4] = tuple.PatchUninstall ? (int?)WindowsInstallerConstants.MsidbCustomActionTypePatchUninstall : null; |
| 406 | } | 401 | } |
| 407 | 402 | ||
| 408 | private void AddDialogTuple(DialogTuple tuple, WindowsInstallerData output) | 403 | private void AddDialogTuple(DialogTuple tuple) |
| 409 | { | 404 | { |
| 410 | var attributes = tuple.Visible ? WindowsInstallerConstants.MsidbDialogAttributesVisible : 0; | 405 | var attributes = tuple.Visible ? WindowsInstallerConstants.MsidbDialogAttributesVisible : 0; |
| 411 | attributes|= tuple.Modal ? WindowsInstallerConstants.MsidbDialogAttributesModal : 0; | 406 | attributes|= tuple.Modal ? WindowsInstallerConstants.MsidbDialogAttributesModal : 0; |
| @@ -419,8 +414,7 @@ namespace WixToolset.Core.WindowsInstaller.Bind | |||
| 419 | attributes|= tuple.SystemModal ? WindowsInstallerConstants.MsidbDialogAttributesSysModal : 0; | 414 | attributes|= tuple.SystemModal ? WindowsInstallerConstants.MsidbDialogAttributesSysModal : 0; |
| 420 | attributes|= tuple.TrackDiskSpace ? WindowsInstallerConstants.MsidbDialogAttributesTrackDiskSpace : 0; | 415 | attributes|= tuple.TrackDiskSpace ? WindowsInstallerConstants.MsidbDialogAttributesTrackDiskSpace : 0; |
| 421 | 416 | ||
| 422 | var table = output.EnsureTable(this.TableDefinitions["Dialog"]); | 417 | var row = this.CreateRow(tuple, "Dialog"); |
| 423 | var row = table.CreateRow(tuple.SourceLineNumbers); | ||
| 424 | row[0] = tuple.Id.Id; | 418 | row[0] = tuple.Id.Id; |
| 425 | row[1] = tuple.HCentering; | 419 | row[1] = tuple.HCentering; |
| 426 | row[2] = tuple.VCentering; | 420 | row[2] = tuple.VCentering; |
| @@ -432,10 +426,10 @@ namespace WixToolset.Core.WindowsInstaller.Bind | |||
| 432 | row[8] = tuple.DefaultControlRef; | 426 | row[8] = tuple.DefaultControlRef; |
| 433 | row[9] = tuple.CancelControlRef; | 427 | row[9] = tuple.CancelControlRef; |
| 434 | 428 | ||
| 435 | output.EnsureTable(this.TableDefinitions["ListBox"]); | 429 | this.Output.EnsureTable(this.TableDefinitions["ListBox"]); |
| 436 | } | 430 | } |
| 437 | 431 | ||
| 438 | private void AddDirectoryTuple(DirectoryTuple tuple, WindowsInstallerData output) | 432 | private void AddDirectoryTuple(DirectoryTuple tuple) |
| 439 | { | 433 | { |
| 440 | var sourceName = GetMsiFilenameValue(tuple.SourceShortName, tuple.SourceName); | 434 | var sourceName = GetMsiFilenameValue(tuple.SourceShortName, tuple.SourceName); |
| 441 | var targetName = GetMsiFilenameValue(tuple.ShortName, tuple.Name); | 435 | var targetName = GetMsiFilenameValue(tuple.ShortName, tuple.Name); |
| @@ -447,14 +441,13 @@ namespace WixToolset.Core.WindowsInstaller.Bind | |||
| 447 | 441 | ||
| 448 | var defaultDir = String.IsNullOrEmpty(sourceName) ? targetName : targetName + ":" + sourceName ; | 442 | var defaultDir = String.IsNullOrEmpty(sourceName) ? targetName : targetName + ":" + sourceName ; |
| 449 | 443 | ||
| 450 | var table = output.EnsureTable(this.TableDefinitions["Directory"]); | 444 | var row = this.CreateRow(tuple, "Directory"); |
| 451 | var row = table.CreateRow(tuple.SourceLineNumbers); | ||
| 452 | row[0] = tuple.Id.Id; | 445 | row[0] = tuple.Id.Id; |
| 453 | row[1] = tuple.ParentDirectoryRef; | 446 | row[1] = tuple.ParentDirectoryRef; |
| 454 | row[2] = defaultDir; | 447 | row[2] = defaultDir; |
| 455 | } | 448 | } |
| 456 | 449 | ||
| 457 | private void AddEnvironmentTuple(EnvironmentTuple tuple, WindowsInstallerData output) | 450 | private void AddEnvironmentTuple(EnvironmentTuple tuple) |
| 458 | { | 451 | { |
| 459 | var action = String.Empty; | 452 | var action = String.Empty; |
| 460 | var system = tuple.System ? "*" : String.Empty; | 453 | var system = tuple.System ? "*" : String.Empty; |
| @@ -484,23 +477,21 @@ namespace WixToolset.Core.WindowsInstaller.Bind | |||
| 484 | break; | 477 | break; |
| 485 | } | 478 | } |
| 486 | 479 | ||
| 487 | var table = output.EnsureTable(this.TableDefinitions["Environment"]); | 480 | var row = this.CreateRow(tuple, "Environment"); |
| 488 | var row = table.CreateRow(tuple.SourceLineNumbers); | ||
| 489 | row[0] = tuple.Id.Id; | 481 | row[0] = tuple.Id.Id; |
| 490 | row[1] = String.Concat(action, uninstall, system, tuple.Name); | 482 | row[1] = String.Concat(action, uninstall, system, tuple.Name); |
| 491 | row[2] = value; | 483 | row[2] = value; |
| 492 | row[3] = tuple.ComponentRef; | 484 | row[3] = tuple.ComponentRef; |
| 493 | } | 485 | } |
| 494 | 486 | ||
| 495 | private void AddErrorTuple(ErrorTuple tuple, WindowsInstallerData output) | 487 | private void AddErrorTuple(ErrorTuple tuple) |
| 496 | { | 488 | { |
| 497 | var table = output.EnsureTable(this.TableDefinitions["Error"]); | 489 | var row = this.CreateRow(tuple, "Error"); |
| 498 | var row = table.CreateRow(tuple.SourceLineNumbers); | ||
| 499 | row[0] = Convert.ToInt32(tuple.Id.Id); | 490 | row[0] = Convert.ToInt32(tuple.Id.Id); |
| 500 | row[1] = tuple.Message; | 491 | row[1] = tuple.Message; |
| 501 | } | 492 | } |
| 502 | 493 | ||
| 503 | private void AddFeatureTuple(FeatureTuple tuple, WindowsInstallerData output) | 494 | private void AddFeatureTuple(FeatureTuple tuple) |
| 504 | { | 495 | { |
| 505 | var attributes = tuple.DisallowAbsent ? WindowsInstallerConstants.MsidbFeatureAttributesUIDisallowAbsent : 0; | 496 | var attributes = tuple.DisallowAbsent ? WindowsInstallerConstants.MsidbFeatureAttributesUIDisallowAbsent : 0; |
| 506 | attributes |= tuple.DisallowAdvertise ? WindowsInstallerConstants.MsidbFeatureAttributesDisallowAdvertise : 0; | 497 | attributes |= tuple.DisallowAdvertise ? WindowsInstallerConstants.MsidbFeatureAttributesDisallowAdvertise : 0; |
| @@ -508,8 +499,7 @@ namespace WixToolset.Core.WindowsInstaller.Bind | |||
| 508 | attributes |= FeatureInstallDefault.Source == tuple.InstallDefault ? WindowsInstallerConstants.MsidbFeatureAttributesFavorSource : 0; | 499 | attributes |= FeatureInstallDefault.Source == tuple.InstallDefault ? WindowsInstallerConstants.MsidbFeatureAttributesFavorSource : 0; |
| 509 | attributes |= FeatureTypicalDefault.Advertise == tuple.TypicalDefault ? WindowsInstallerConstants.MsidbFeatureAttributesFavorAdvertise : 0; | 500 | attributes |= FeatureTypicalDefault.Advertise == tuple.TypicalDefault ? WindowsInstallerConstants.MsidbFeatureAttributesFavorAdvertise : 0; |
| 510 | 501 | ||
| 511 | var table = output.EnsureTable(this.TableDefinitions["Feature"]); | 502 | var row = this.CreateRow(tuple, "Feature"); |
| 512 | var row = table.CreateRow(tuple.SourceLineNumbers); | ||
| 513 | row[0] = tuple.Id.Id; | 503 | row[0] = tuple.Id.Id; |
| 514 | row[1] = tuple.ParentFeatureRef; | 504 | row[1] = tuple.ParentFeatureRef; |
| 515 | row[2] = tuple.Title; | 505 | row[2] = tuple.Title; |
| @@ -520,16 +510,17 @@ namespace WixToolset.Core.WindowsInstaller.Bind | |||
| 520 | row[7] = attributes; | 510 | row[7] = attributes; |
| 521 | } | 511 | } |
| 522 | 512 | ||
| 523 | private void AddFileTuple(FileTuple tuple, WindowsInstallerData output) | 513 | private void AddFileTuple(FileTuple tuple) |
| 524 | { | 514 | { |
| 525 | var table = output.EnsureTable(this.TableDefinitions["File"]); | 515 | var row = (FileRow)this.CreateRow(tuple, "File"); |
| 526 | var row = (FileRow)table.CreateRow(tuple.SourceLineNumbers); | ||
| 527 | row.File = tuple.Id.Id; | 516 | row.File = tuple.Id.Id; |
| 528 | row.Component = tuple.ComponentRef; | 517 | row.Component = tuple.ComponentRef; |
| 529 | row.FileName = GetMsiFilenameValue(tuple.ShortName, tuple.Name); | 518 | row.FileName = GetMsiFilenameValue(tuple.ShortName, tuple.Name); |
| 530 | row.FileSize = tuple.FileSize; | 519 | row.FileSize = tuple.FileSize; |
| 531 | row.Version = tuple.Version; | 520 | row.Version = tuple.Version; |
| 532 | row.Language = tuple.Language; | 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; | ||
| 533 | 524 | ||
| 534 | var attributes = (tuple.Attributes & FileTupleAttributes.Checksum) == FileTupleAttributes.Checksum ? WindowsInstallerConstants.MsidbFileAttributesChecksum : 0; | 525 | var attributes = (tuple.Attributes & FileTupleAttributes.Checksum) == FileTupleAttributes.Checksum ? WindowsInstallerConstants.MsidbFileAttributesChecksum : 0; |
| 535 | attributes |= (tuple.Attributes & FileTupleAttributes.Compressed) == FileTupleAttributes.Compressed ? WindowsInstallerConstants.MsidbFileAttributesCompressed : 0; | 526 | attributes |= (tuple.Attributes & FileTupleAttributes.Compressed) == FileTupleAttributes.Compressed ? WindowsInstallerConstants.MsidbFileAttributesCompressed : 0; |
| @@ -542,19 +533,17 @@ namespace WixToolset.Core.WindowsInstaller.Bind | |||
| 542 | 533 | ||
| 543 | if (!String.IsNullOrEmpty(tuple.FontTitle)) | 534 | if (!String.IsNullOrEmpty(tuple.FontTitle)) |
| 544 | { | 535 | { |
| 545 | var fontTable = output.EnsureTable(this.TableDefinitions["Font"]); | 536 | var fontRow = this.CreateRow(tuple, "Font"); |
| 546 | var fontRow = fontTable.CreateRow(tuple.SourceLineNumbers); | ||
| 547 | fontRow[0] = tuple.Id.Id; | 537 | fontRow[0] = tuple.Id.Id; |
| 548 | fontRow[1] = tuple.FontTitle; | 538 | fontRow[1] = tuple.FontTitle; |
| 549 | } | 539 | } |
| 550 | } | 540 | } |
| 551 | 541 | ||
| 552 | private void AddIniFileTuple(IniFileTuple tuple, WindowsInstallerData output) | 542 | private void AddIniFileTuple(IniFileTuple tuple) |
| 553 | { | 543 | { |
| 554 | var tableName = (InifFileActionType.AddLine == tuple.Action || InifFileActionType.AddTag == tuple.Action || InifFileActionType.CreateLine == tuple.Action) ? "IniFile" : "RemoveIniFile"; | 544 | var tableName = (InifFileActionType.AddLine == tuple.Action || InifFileActionType.AddTag == tuple.Action || InifFileActionType.CreateLine == tuple.Action) ? "IniFile" : "RemoveIniFile"; |
| 555 | 545 | ||
| 556 | var table = output.EnsureTable(this.TableDefinitions[tableName]); | 546 | var row = this.CreateRow(tuple, tableName); |
| 557 | var row = table.CreateRow(tuple.SourceLineNumbers); | ||
| 558 | row[0] = tuple.Id.Id; | 547 | row[0] = tuple.Id.Id; |
| 559 | row[1] = tuple.FileName; | 548 | row[1] = tuple.FileName; |
| 560 | row[2] = tuple.DirProperty; | 549 | row[2] = tuple.DirProperty; |
| @@ -565,12 +554,11 @@ namespace WixToolset.Core.WindowsInstaller.Bind | |||
| 565 | row[7] = tuple.ComponentRef; | 554 | row[7] = tuple.ComponentRef; |
| 566 | } | 555 | } |
| 567 | 556 | ||
| 568 | private void AddMediaTuple(MediaTuple tuple, WindowsInstallerData output) | 557 | private void AddMediaTuple(MediaTuple tuple) |
| 569 | { | 558 | { |
| 570 | if (this.Section.Type != SectionType.Module) | 559 | if (this.Section.Type != SectionType.Module) |
| 571 | { | 560 | { |
| 572 | var table = output.EnsureTable(this.TableDefinitions["Media"]); | 561 | var row = (MediaRow)this.CreateRow(tuple, "Media"); |
| 573 | var row = (MediaRow)table.CreateRow(tuple.SourceLineNumbers); | ||
| 574 | row.DiskId = tuple.DiskId; | 562 | row.DiskId = tuple.DiskId; |
| 575 | row.LastSequence = tuple.LastSequence ?? 0; | 563 | row.LastSequence = tuple.LastSequence ?? 0; |
| 576 | row.DiskPrompt = tuple.DiskPrompt; | 564 | row.DiskPrompt = tuple.DiskPrompt; |
| @@ -580,10 +568,9 @@ namespace WixToolset.Core.WindowsInstaller.Bind | |||
| 580 | } | 568 | } |
| 581 | } | 569 | } |
| 582 | 570 | ||
| 583 | private void AddModuleConfigurationTuple(ModuleConfigurationTuple tuple, WindowsInstallerData output) | 571 | private void AddModuleConfigurationTuple(ModuleConfigurationTuple tuple) |
| 584 | { | 572 | { |
| 585 | var table = output.EnsureTable(this.TableDefinitions["ModuleConfiguration"]); | 573 | var row = this.CreateRow(tuple, "ModuleConfiguration"); |
| 586 | var row = table.CreateRow(tuple.SourceLineNumbers); | ||
| 587 | row[0] = tuple.Id.Id; | 574 | row[0] = tuple.Id.Id; |
| 588 | row[1] = tuple.Format; | 575 | row[1] = tuple.Format; |
| 589 | row[2] = tuple.Type; | 576 | row[2] = tuple.Type; |
| @@ -597,13 +584,12 @@ namespace WixToolset.Core.WindowsInstaller.Bind | |||
| 597 | row[9] = tuple.HelpKeyword; | 584 | row[9] = tuple.HelpKeyword; |
| 598 | } | 585 | } |
| 599 | 586 | ||
| 600 | private void AddMsiEmbeddedUITuple(MsiEmbeddedUITuple tuple, WindowsInstallerData output) | 587 | private void AddMsiEmbeddedUITuple(MsiEmbeddedUITuple tuple) |
| 601 | { | 588 | { |
| 602 | var attributes = tuple.EntryPoint ? WindowsInstallerConstants.MsidbEmbeddedUI : 0; | 589 | var attributes = tuple.EntryPoint ? WindowsInstallerConstants.MsidbEmbeddedUI : 0; |
| 603 | attributes |= tuple.SupportsBasicUI ? WindowsInstallerConstants.MsidbEmbeddedHandlesBasic : 0; | 590 | attributes |= tuple.SupportsBasicUI ? WindowsInstallerConstants.MsidbEmbeddedHandlesBasic : 0; |
| 604 | 591 | ||
| 605 | var table = output.EnsureTable(this.TableDefinitions["MsiEmbeddedUI"]); | 592 | var row = this.CreateRow(tuple, "MsiEmbeddedUI"); |
| 606 | var row = table.CreateRow(tuple.SourceLineNumbers); | ||
| 607 | row[0] = tuple.Id.Id; | 593 | row[0] = tuple.Id.Id; |
| 608 | row[1] = tuple.FileName; | 594 | row[1] = tuple.FileName; |
| 609 | row[2] = attributes; | 595 | row[2] = attributes; |
| @@ -611,10 +597,9 @@ namespace WixToolset.Core.WindowsInstaller.Bind | |||
| 611 | row[4] = tuple.Source; | 597 | row[4] = tuple.Source; |
| 612 | } | 598 | } |
| 613 | 599 | ||
| 614 | private void AddMsiFileHashTuple(MsiFileHashTuple tuple, WindowsInstallerData output) | 600 | private void AddMsiFileHashTuple(MsiFileHashTuple tuple) |
| 615 | { | 601 | { |
| 616 | var table = output.EnsureTable(this.TableDefinitions["MsiFileHash"]); | 602 | var row = this.CreateRow(tuple, "MsiFileHash"); |
| 617 | var row = table.CreateRow(tuple.SourceLineNumbers); | ||
| 618 | row[0] = tuple.Id.Id; | 603 | row[0] = tuple.Id.Id; |
| 619 | row[1] = tuple.Options; | 604 | row[1] = tuple.Options; |
| 620 | row[2] = tuple.HashPart1; | 605 | row[2] = tuple.HashPart1; |
| @@ -623,14 +608,13 @@ namespace WixToolset.Core.WindowsInstaller.Bind | |||
| 623 | row[5] = tuple.HashPart4; | 608 | row[5] = tuple.HashPart4; |
| 624 | } | 609 | } |
| 625 | 610 | ||
| 626 | private void AddMsiServiceConfigTuple(MsiServiceConfigTuple tuple, WindowsInstallerData output) | 611 | private void AddMsiServiceConfigTuple(MsiServiceConfigTuple tuple) |
| 627 | { | 612 | { |
| 628 | var events = tuple.OnInstall ? WindowsInstallerConstants.MsidbServiceConfigEventInstall : 0; | 613 | var events = tuple.OnInstall ? WindowsInstallerConstants.MsidbServiceConfigEventInstall : 0; |
| 629 | events |= tuple.OnReinstall ? WindowsInstallerConstants.MsidbServiceConfigEventReinstall : 0; | 614 | events |= tuple.OnReinstall ? WindowsInstallerConstants.MsidbServiceConfigEventReinstall : 0; |
| 630 | events |= tuple.OnUninstall ? WindowsInstallerConstants.MsidbServiceConfigEventUninstall : 0; | 615 | events |= tuple.OnUninstall ? WindowsInstallerConstants.MsidbServiceConfigEventUninstall : 0; |
| 631 | 616 | ||
| 632 | var table = output.EnsureTable(this.TableDefinitions["MsiServiceConfigFailureActions"]); | 617 | var row = this.CreateRow(tuple, "MsiServiceConfigFailureActions"); |
| 633 | var row = table.CreateRow(tuple.SourceLineNumbers); | ||
| 634 | row[0] = tuple.Id.Id; | 618 | row[0] = tuple.Id.Id; |
| 635 | row[1] = tuple.Name; | 619 | row[1] = tuple.Name; |
| 636 | row[2] = events; | 620 | row[2] = events; |
| @@ -639,14 +623,13 @@ namespace WixToolset.Core.WindowsInstaller.Bind | |||
| 639 | row[5] = tuple.ComponentRef; | 623 | row[5] = tuple.ComponentRef; |
| 640 | } | 624 | } |
| 641 | 625 | ||
| 642 | private void AddMsiServiceConfigFailureActionsTuple(MsiServiceConfigFailureActionsTuple tuple, WindowsInstallerData output) | 626 | private void AddMsiServiceConfigFailureActionsTuple(MsiServiceConfigFailureActionsTuple tuple) |
| 643 | { | 627 | { |
| 644 | var events = tuple.OnInstall ? WindowsInstallerConstants.MsidbServiceConfigEventInstall : 0; | 628 | var events = tuple.OnInstall ? WindowsInstallerConstants.MsidbServiceConfigEventInstall : 0; |
| 645 | events |= tuple.OnReinstall ? WindowsInstallerConstants.MsidbServiceConfigEventReinstall : 0; | 629 | events |= tuple.OnReinstall ? WindowsInstallerConstants.MsidbServiceConfigEventReinstall : 0; |
| 646 | events |= tuple.OnUninstall ? WindowsInstallerConstants.MsidbServiceConfigEventUninstall : 0; | 630 | events |= tuple.OnUninstall ? WindowsInstallerConstants.MsidbServiceConfigEventUninstall : 0; |
| 647 | 631 | ||
| 648 | var table = output.EnsureTable(this.TableDefinitions["MsiServiceConfig"]); | 632 | var row = this.CreateRow(tuple, "MsiServiceConfig"); |
| 649 | var row = table.CreateRow(tuple.SourceLineNumbers); | ||
| 650 | row[0] = tuple.Id.Id; | 633 | row[0] = tuple.Id.Id; |
| 651 | row[1] = tuple.Name; | 634 | row[1] = tuple.Name; |
| 652 | row[2] = events; | 635 | row[2] = events; |
| @@ -658,10 +641,9 @@ namespace WixToolset.Core.WindowsInstaller.Bind | |||
| 658 | row[8] = tuple.ComponentRef; | 641 | row[8] = tuple.ComponentRef; |
| 659 | } | 642 | } |
| 660 | 643 | ||
| 661 | private void AddMoveFileTuple(MoveFileTuple tuple, WindowsInstallerData output) | 644 | private void AddMoveFileTuple(MoveFileTuple tuple) |
| 662 | { | 645 | { |
| 663 | var table = output.EnsureTable(this.TableDefinitions["MoveFile"]); | 646 | var row = this.CreateRow(tuple, "MoveFile"); |
| 664 | var row = table.CreateRow(tuple.SourceLineNumbers); | ||
| 665 | row[0] = tuple.Id.Id; | 647 | row[0] = tuple.Id.Id; |
| 666 | row[1] = tuple.ComponentRef; | 648 | row[1] = tuple.ComponentRef; |
| 667 | row[2] = tuple.SourceName; | 649 | row[2] = tuple.SourceName; |
| @@ -671,26 +653,24 @@ namespace WixToolset.Core.WindowsInstaller.Bind | |||
| 671 | row[6] = tuple.Delete ? WindowsInstallerConstants.MsidbMoveFileOptionsMove : 0; | 653 | row[6] = tuple.Delete ? WindowsInstallerConstants.MsidbMoveFileOptionsMove : 0; |
| 672 | } | 654 | } |
| 673 | 655 | ||
| 674 | private void AddPropertyTuple(PropertyTuple tuple, WindowsInstallerData output) | 656 | private void AddPropertyTuple(PropertyTuple tuple) |
| 675 | { | 657 | { |
| 676 | if (String.IsNullOrEmpty(tuple.Value)) | 658 | if (String.IsNullOrEmpty(tuple.Value)) |
| 677 | { | 659 | { |
| 678 | return; | 660 | return; |
| 679 | } | 661 | } |
| 680 | 662 | ||
| 681 | var table = output.EnsureTable(this.TableDefinitions["Property"]); | 663 | var row = (PropertyRow)this.CreateRow(tuple, "Property"); |
| 682 | var row = (PropertyRow)table.CreateRow(tuple.SourceLineNumbers); | ||
| 683 | row.Property = tuple.Id.Id; | 664 | row.Property = tuple.Id.Id; |
| 684 | row.Value = tuple.Value; | 665 | row.Value = tuple.Value; |
| 685 | } | 666 | } |
| 686 | 667 | ||
| 687 | private void AddRemoveFileTuple(RemoveFileTuple tuple, WindowsInstallerData output) | 668 | private void AddRemoveFileTuple(RemoveFileTuple tuple) |
| 688 | { | 669 | { |
| 689 | var installMode = tuple.OnInstall == true ? WindowsInstallerConstants.MsidbRemoveFileInstallModeOnInstall : 0; | 670 | var installMode = tuple.OnInstall == true ? WindowsInstallerConstants.MsidbRemoveFileInstallModeOnInstall : 0; |
| 690 | installMode |= tuple.OnUninstall == true ? WindowsInstallerConstants.MsidbRemoveFileInstallModeOnRemove : 0; | 671 | installMode |= tuple.OnUninstall == true ? WindowsInstallerConstants.MsidbRemoveFileInstallModeOnRemove : 0; |
| 691 | 672 | ||
| 692 | var table = output.EnsureTable(this.TableDefinitions["RemoveFile"]); | 673 | var row = this.CreateRow(tuple, "RemoveFile"); |
| 693 | var row = table.CreateRow(tuple.SourceLineNumbers); | ||
| 694 | row[0] = tuple.Id.Id; | 674 | row[0] = tuple.Id.Id; |
| 695 | row[1] = tuple.ComponentRef; | 675 | row[1] = tuple.ComponentRef; |
| 696 | row[2] = tuple.FileName; | 676 | row[2] = tuple.FileName; |
| @@ -698,7 +678,7 @@ namespace WixToolset.Core.WindowsInstaller.Bind | |||
| 698 | row[4] = installMode; | 678 | row[4] = installMode; |
| 699 | } | 679 | } |
| 700 | 680 | ||
| 701 | private void AddRegistryTuple(RegistryTuple tuple, WindowsInstallerData output) | 681 | private void AddRegistryTuple(RegistryTuple tuple) |
| 702 | { | 682 | { |
| 703 | var value = tuple.Value; | 683 | var value = tuple.Value; |
| 704 | 684 | ||
| @@ -740,8 +720,7 @@ namespace WixToolset.Core.WindowsInstaller.Bind | |||
| 740 | break; | 720 | break; |
| 741 | } | 721 | } |
| 742 | 722 | ||
| 743 | var table = output.EnsureTable(this.TableDefinitions["Registry"]); | 723 | var row = this.CreateRow(tuple, "Registry"); |
| 744 | var row = table.CreateRow(tuple.SourceLineNumbers); | ||
| 745 | row[0] = tuple.Id.Id; | 724 | row[0] = tuple.Id.Id; |
| 746 | row[1] = tuple.Root; | 725 | row[1] = tuple.Root; |
| 747 | row[2] = tuple.Key; | 726 | row[2] = tuple.Key; |
| @@ -750,13 +729,12 @@ namespace WixToolset.Core.WindowsInstaller.Bind | |||
| 750 | row[5] = tuple.ComponentRef; | 729 | row[5] = tuple.ComponentRef; |
| 751 | } | 730 | } |
| 752 | 731 | ||
| 753 | private void AddRegLocatorTuple(RegLocatorTuple tuple, WindowsInstallerData output) | 732 | private void AddRegLocatorTuple(RegLocatorTuple tuple) |
| 754 | { | 733 | { |
| 755 | var type = (int)tuple.Type; | 734 | var type = (int)tuple.Type; |
| 756 | type |= tuple.Win64 ? WindowsInstallerConstants.MsidbLocatorType64bit : 0; | 735 | type |= tuple.Win64 ? WindowsInstallerConstants.MsidbLocatorType64bit : 0; |
| 757 | 736 | ||
| 758 | var table = output.EnsureTable(this.TableDefinitions["RegLocator"]); | 737 | var row = this.CreateRow(tuple, "RegLocator"); |
| 759 | var row = table.CreateRow(tuple.SourceLineNumbers); | ||
| 760 | row[0] = tuple.Id.Id; | 738 | row[0] = tuple.Id.Id; |
| 761 | row[1] = tuple.Root; | 739 | row[1] = tuple.Root; |
| 762 | row[2] = tuple.Key; | 740 | row[2] = tuple.Key; |
| @@ -764,12 +742,11 @@ namespace WixToolset.Core.WindowsInstaller.Bind | |||
| 764 | row[4] = type; | 742 | row[4] = type; |
| 765 | } | 743 | } |
| 766 | 744 | ||
| 767 | private void AddRemoveRegistryTuple(RemoveRegistryTuple tuple, WindowsInstallerData output) | 745 | private void AddRemoveRegistryTuple(RemoveRegistryTuple tuple) |
| 768 | { | 746 | { |
| 769 | if (tuple.Action == RemoveRegistryActionType.RemoveOnInstall) | 747 | if (tuple.Action == RemoveRegistryActionType.RemoveOnInstall) |
| 770 | { | 748 | { |
| 771 | var table = output.EnsureTable(this.TableDefinitions["RemoveRegistry"]); | 749 | var row = this.CreateRow(tuple, "RemoveRegistry"); |
| 772 | var row = table.CreateRow(tuple.SourceLineNumbers); | ||
| 773 | row[0] = tuple.Id.Id; | 750 | row[0] = tuple.Id.Id; |
| 774 | row[1] = tuple.Root; | 751 | row[1] = tuple.Root; |
| 775 | row[2] = tuple.Key; | 752 | row[2] = tuple.Key; |
| @@ -778,8 +755,7 @@ namespace WixToolset.Core.WindowsInstaller.Bind | |||
| 778 | } | 755 | } |
| 779 | else // Registry table is used to remove registry keys on uninstall. | 756 | else // Registry table is used to remove registry keys on uninstall. |
| 780 | { | 757 | { |
| 781 | var table = output.EnsureTable(this.TableDefinitions["Registry"]); | 758 | var row = this.CreateRow(tuple, "Registry"); |
| 782 | var row = table.CreateRow(tuple.SourceLineNumbers); | ||
| 783 | row[0] = tuple.Id.Id; | 759 | row[0] = tuple.Id.Id; |
| 784 | row[1] = tuple.Root; | 760 | row[1] = tuple.Root; |
| 785 | row[2] = tuple.Key; | 761 | row[2] = tuple.Key; |
| @@ -788,7 +764,7 @@ namespace WixToolset.Core.WindowsInstaller.Bind | |||
| 788 | } | 764 | } |
| 789 | } | 765 | } |
| 790 | 766 | ||
| 791 | private void AddServiceControlTuple(ServiceControlTuple tuple, WindowsInstallerData output) | 767 | private void AddServiceControlTuple(ServiceControlTuple tuple) |
| 792 | { | 768 | { |
| 793 | var events = tuple.InstallRemove ? WindowsInstallerConstants.MsidbServiceControlEventDelete : 0; | 769 | var events = tuple.InstallRemove ? WindowsInstallerConstants.MsidbServiceControlEventDelete : 0; |
| 794 | events |= tuple.UninstallRemove ? WindowsInstallerConstants.MsidbServiceControlEventUninstallDelete : 0; | 770 | events |= tuple.UninstallRemove ? WindowsInstallerConstants.MsidbServiceControlEventUninstallDelete : 0; |
| @@ -797,8 +773,7 @@ namespace WixToolset.Core.WindowsInstaller.Bind | |||
| 797 | events |= tuple.InstallStop ? WindowsInstallerConstants.MsidbServiceControlEventStop : 0; | 773 | events |= tuple.InstallStop ? WindowsInstallerConstants.MsidbServiceControlEventStop : 0; |
| 798 | events |= tuple.UninstallStop ? WindowsInstallerConstants.MsidbServiceControlEventUninstallStop : 0; | 774 | events |= tuple.UninstallStop ? WindowsInstallerConstants.MsidbServiceControlEventUninstallStop : 0; |
| 799 | 775 | ||
| 800 | var table = output.EnsureTable(this.TableDefinitions["ServiceControl"]); | 776 | var row = this.CreateRow(tuple, "ServiceControl"); |
| 801 | var row = table.CreateRow(tuple.SourceLineNumbers); | ||
| 802 | row[0] = tuple.Id.Id; | 777 | row[0] = tuple.Id.Id; |
| 803 | row[1] = tuple.Name; | 778 | row[1] = tuple.Name; |
| 804 | row[2] = events; | 779 | row[2] = events; |
| @@ -810,7 +785,7 @@ namespace WixToolset.Core.WindowsInstaller.Bind | |||
| 810 | row[5] = tuple.ComponentRef; | 785 | row[5] = tuple.ComponentRef; |
| 811 | } | 786 | } |
| 812 | 787 | ||
| 813 | private void AddServiceInstallTuple(ServiceInstallTuple tuple, WindowsInstallerData output) | 788 | private void AddServiceInstallTuple(ServiceInstallTuple tuple) |
| 814 | { | 789 | { |
| 815 | var errorControl = (int)tuple.ErrorControl; | 790 | var errorControl = (int)tuple.ErrorControl; |
| 816 | errorControl |= tuple.Vital ? WindowsInstallerConstants.MsidbServiceInstallErrorControlVital : 0; | 791 | errorControl |= tuple.Vital ? WindowsInstallerConstants.MsidbServiceInstallErrorControlVital : 0; |
| @@ -818,8 +793,7 @@ namespace WixToolset.Core.WindowsInstaller.Bind | |||
| 818 | var serviceType = (int)tuple.ServiceType; | 793 | var serviceType = (int)tuple.ServiceType; |
| 819 | serviceType |= tuple.Interactive ? WindowsInstallerConstants.MsidbServiceInstallInteractive : 0; | 794 | serviceType |= tuple.Interactive ? WindowsInstallerConstants.MsidbServiceInstallInteractive : 0; |
| 820 | 795 | ||
| 821 | var table = output.EnsureTable(this.TableDefinitions["ServiceInstall"]); | 796 | var row = this.CreateRow(tuple, "ServiceInstall"); |
| 822 | var row = table.CreateRow(tuple.SourceLineNumbers); | ||
| 823 | row[0] = tuple.Id.Id; | 797 | row[0] = tuple.Id.Id; |
| 824 | row[1] = tuple.Name; | 798 | row[1] = tuple.Name; |
| 825 | row[2] = tuple.DisplayName; | 799 | row[2] = tuple.DisplayName; |
| @@ -835,10 +809,9 @@ namespace WixToolset.Core.WindowsInstaller.Bind | |||
| 835 | row[12] = tuple.Description; | 809 | row[12] = tuple.Description; |
| 836 | } | 810 | } |
| 837 | 811 | ||
| 838 | private void AddShortcutTuple(ShortcutTuple tuple, WindowsInstallerData output) | 812 | private void AddShortcutTuple(ShortcutTuple tuple) |
| 839 | { | 813 | { |
| 840 | var table = output.EnsureTable(this.TableDefinitions["Shortcut"]); | 814 | var row = this.CreateRow(tuple, "Shortcut"); |
| 841 | var row = table.CreateRow(tuple.SourceLineNumbers); | ||
| 842 | row[0] = tuple.Id.Id; | 815 | row[0] = tuple.Id.Id; |
| 843 | row[1] = tuple.DirectoryRef; | 816 | row[1] = tuple.DirectoryRef; |
| 844 | row[2] = GetMsiFilenameValue(tuple.ShortName, tuple.Name); | 817 | row[2] = GetMsiFilenameValue(tuple.ShortName, tuple.Name); |
| @@ -857,7 +830,7 @@ namespace WixToolset.Core.WindowsInstaller.Bind | |||
| 857 | row[15] = tuple.DescriptionResourceId; | 830 | row[15] = tuple.DescriptionResourceId; |
| 858 | } | 831 | } |
| 859 | 832 | ||
| 860 | private void AddTextStyleTuple(TextStyleTuple tuple, WindowsInstallerData output) | 833 | private void AddTextStyleTuple(TextStyleTuple tuple) |
| 861 | { | 834 | { |
| 862 | var styleBits = tuple.Bold ? WindowsInstallerConstants.MsidbTextStyleStyleBitsBold : 0; | 835 | var styleBits = tuple.Bold ? WindowsInstallerConstants.MsidbTextStyleStyleBitsBold : 0; |
| 863 | styleBits |= tuple.Italic ? WindowsInstallerConstants.MsidbTextStyleStyleBitsItalic : 0; | 836 | styleBits |= tuple.Italic ? WindowsInstallerConstants.MsidbTextStyleStyleBitsItalic : 0; |
| @@ -873,8 +846,7 @@ namespace WixToolset.Core.WindowsInstaller.Bind | |||
| 873 | color += (long)(tuple.Blue ?? 0) * 65536; | 846 | color += (long)(tuple.Blue ?? 0) * 65536; |
| 874 | } | 847 | } |
| 875 | 848 | ||
| 876 | var table = output.EnsureTable(this.TableDefinitions["TextStyle"]); | 849 | var row = this.CreateRow(tuple, "TextStyle"); |
| 877 | var row = table.CreateRow(tuple.SourceLineNumbers); | ||
| 878 | row[0] = tuple.Id.Id; | 850 | row[0] = tuple.Id.Id; |
| 879 | row[1] = tuple.FaceName; | 851 | row[1] = tuple.FaceName; |
| 880 | row[2] = tuple.Size; | 852 | row[2] = tuple.Size; |
| @@ -882,10 +854,9 @@ namespace WixToolset.Core.WindowsInstaller.Bind | |||
| 882 | row[4] = styleBits == 0 ? null : (int?)styleBits; | 854 | row[4] = styleBits == 0 ? null : (int?)styleBits; |
| 883 | } | 855 | } |
| 884 | 856 | ||
| 885 | private void AddUpgradeTuple(UpgradeTuple tuple, WindowsInstallerData output) | 857 | private void AddUpgradeTuple(UpgradeTuple tuple) |
| 886 | { | 858 | { |
| 887 | var table = output.EnsureTable(this.TableDefinitions["Upgrade"]); | 859 | var row = (UpgradeRow)this.CreateRow(tuple, "Upgrade"); |
| 888 | var row = (UpgradeRow)table.CreateRow(tuple.SourceLineNumbers); | ||
| 889 | row.UpgradeCode = tuple.UpgradeCode; | 860 | row.UpgradeCode = tuple.UpgradeCode; |
| 890 | row.VersionMin = tuple.VersionMin; | 861 | row.VersionMin = tuple.VersionMin; |
| 891 | row.VersionMax = tuple.VersionMax; | 862 | row.VersionMax = tuple.VersionMax; |
| @@ -902,72 +873,71 @@ namespace WixToolset.Core.WindowsInstaller.Bind | |||
| 902 | row.Attributes = attributes; | 873 | row.Attributes = attributes; |
| 903 | } | 874 | } |
| 904 | 875 | ||
| 905 | private void AddWixActionTuple(WixActionTuple tuple, WindowsInstallerData output) | 876 | private void AddWixActionTuple(WixActionTuple tuple) |
| 906 | { | 877 | { |
| 907 | // Get the table definition for the action (and ensure the proper table exists for a module). | 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; |
| 909 | switch (tuple.SequenceTable) | 880 | switch (tuple.SequenceTable) |
| 910 | { | 881 | { |
| 911 | case SequenceTable.AdminExecuteSequence: | 882 | case SequenceTable.AdminExecuteSequence: |
| 912 | if (OutputType.Module == output.Type) | 883 | if (OutputType.Module == this.Output.Type) |
| 913 | { | 884 | { |
| 914 | output.EnsureTable(this.TableDefinitions["AdminExecuteSequence"]); | 885 | this.Output.EnsureTable(this.TableDefinitions["AdminExecuteSequence"]); |
| 915 | sequenceTableDefinition = this.TableDefinitions["ModuleAdminExecuteSequence"]; | 886 | sequenceTableName = "ModuleAdminExecuteSequence"; |
| 916 | } | 887 | } |
| 917 | else | 888 | else |
| 918 | { | 889 | { |
| 919 | sequenceTableDefinition = this.TableDefinitions["AdminExecuteSequence"]; | 890 | sequenceTableName = "AdminExecuteSequence"; |
| 920 | } | 891 | } |
| 921 | break; | 892 | break; |
| 922 | case SequenceTable.AdminUISequence: | 893 | case SequenceTable.AdminUISequence: |
| 923 | if (OutputType.Module == output.Type) | 894 | if (OutputType.Module == this.Output.Type) |
| 924 | { | 895 | { |
| 925 | output.EnsureTable(this.TableDefinitions["AdminUISequence"]); | 896 | this.Output.EnsureTable(this.TableDefinitions["AdminUISequence"]); |
| 926 | sequenceTableDefinition = this.TableDefinitions["ModuleAdminUISequence"]; | 897 | sequenceTableName = "ModuleAdminUISequence"; |
| 927 | } | 898 | } |
| 928 | else | 899 | else |
| 929 | { | 900 | { |
| 930 | sequenceTableDefinition = this.TableDefinitions["AdminUISequence"]; | 901 | sequenceTableName = "AdminUISequence"; |
| 931 | } | 902 | } |
| 932 | break; | 903 | break; |
| 933 | case SequenceTable.AdvertiseExecuteSequence: | 904 | case SequenceTable.AdvertiseExecuteSequence: |
| 934 | if (OutputType.Module == output.Type) | 905 | if (OutputType.Module == this.Output.Type) |
| 935 | { | 906 | { |
| 936 | output.EnsureTable(this.TableDefinitions["AdvtExecuteSequence"]); | 907 | this.Output.EnsureTable(this.TableDefinitions["AdvtExecuteSequence"]); |
| 937 | sequenceTableDefinition = this.TableDefinitions["ModuleAdvtExecuteSequence"]; | 908 | sequenceTableName = "ModuleAdvtExecuteSequence"; |
| 938 | } | 909 | } |
| 939 | else | 910 | else |
| 940 | { | 911 | { |
| 941 | sequenceTableDefinition = this.TableDefinitions["AdvtExecuteSequence"]; | 912 | sequenceTableName = "AdvtExecuteSequence"; |
| 942 | } | 913 | } |
| 943 | break; | 914 | break; |
| 944 | case SequenceTable.InstallExecuteSequence: | 915 | case SequenceTable.InstallExecuteSequence: |
| 945 | if (OutputType.Module == output.Type) | 916 | if (OutputType.Module == this.Output.Type) |
| 946 | { | 917 | { |
| 947 | output.EnsureTable(this.TableDefinitions["InstallExecuteSequence"]); | 918 | this.Output.EnsureTable(this.TableDefinitions["InstallExecuteSequence"]); |
| 948 | sequenceTableDefinition = this.TableDefinitions["ModuleInstallExecuteSequence"]; | 919 | sequenceTableName = "ModuleInstallExecuteSequence"; |
| 949 | } | 920 | } |
| 950 | else | 921 | else |
| 951 | { | 922 | { |
| 952 | sequenceTableDefinition = this.TableDefinitions["InstallExecuteSequence"]; | 923 | sequenceTableName = "InstallExecuteSequence"; |
| 953 | } | 924 | } |
| 954 | break; | 925 | break; |
| 955 | case SequenceTable.InstallUISequence: | 926 | case SequenceTable.InstallUISequence: |
| 956 | if (OutputType.Module == output.Type) | 927 | if (OutputType.Module == this.Output.Type) |
| 957 | { | 928 | { |
| 958 | output.EnsureTable(this.TableDefinitions["InstallUISequence"]); | 929 | this.Output.EnsureTable(this.TableDefinitions["InstallUISequence"]); |
| 959 | sequenceTableDefinition = this.TableDefinitions["ModuleInstallUISequence"]; | 930 | sequenceTableName = "ModuleInstallUISequence"; |
| 960 | } | 931 | } |
| 961 | else | 932 | else |
| 962 | { | 933 | { |
| 963 | sequenceTableDefinition = this.TableDefinitions["InstallUISequence"]; | 934 | sequenceTableName = "InstallUISequence"; |
| 964 | } | 935 | } |
| 965 | break; | 936 | break; |
| 966 | } | 937 | } |
| 967 | 938 | ||
| 968 | // create the action sequence row in the output | 939 | // create the action sequence row in the output |
| 969 | var sequenceTable = output.EnsureTable(sequenceTableDefinition); | 940 | var row = this.CreateRow(tuple, sequenceTableName); |
| 970 | var row = sequenceTable.CreateRow(tuple.SourceLineNumbers); | ||
| 971 | 941 | ||
| 972 | if (SectionType.Module == this.Section.Type) | 942 | if (SectionType.Module == this.Section.Type) |
| 973 | { | 943 | { |
| @@ -992,7 +962,7 @@ namespace WixToolset.Core.WindowsInstaller.Bind | |||
| 992 | } | 962 | } |
| 993 | } | 963 | } |
| 994 | 964 | ||
| 995 | private void AddWixCustomRowTuple(WixCustomRowTuple tuple, WindowsInstallerData output) | 965 | private void AddWixCustomRowTuple(WixCustomRowTuple tuple) |
| 996 | { | 966 | { |
| 997 | var customTableDefinition = this.TableDefinitions[tuple.Table]; | 967 | var customTableDefinition = this.TableDefinitions[tuple.Table]; |
| 998 | 968 | ||
| @@ -1002,8 +972,7 @@ namespace WixToolset.Core.WindowsInstaller.Bind | |||
| 1002 | return; | 972 | return; |
| 1003 | } | 973 | } |
| 1004 | 974 | ||
| 1005 | var customTable = output.EnsureTable(customTableDefinition); | 975 | var customRow = this.CreateRow(tuple, customTableDefinition); |
| 1006 | var customRow = customTable.CreateRow(tuple.SourceLineNumbers); | ||
| 1007 | 976 | ||
| 1008 | #if TODO // SectionId seems like a good thing to preserve. | 977 | #if TODO // SectionId seems like a good thing to preserve. |
| 1009 | customRow.SectionId = tuple.SectionId; | 978 | customRow.SectionId = tuple.SectionId; |
| @@ -1073,16 +1042,15 @@ namespace WixToolset.Core.WindowsInstaller.Bind | |||
| 1073 | } | 1042 | } |
| 1074 | } | 1043 | } |
| 1075 | 1044 | ||
| 1076 | private void AddWixEnsureTableTuple(WixEnsureTableTuple tuple, WindowsInstallerData output) | 1045 | private void AddWixEnsureTableTuple(WixEnsureTableTuple tuple) |
| 1077 | { | 1046 | { |
| 1078 | var tableDefinition = this.TableDefinitions[tuple.Table]; | 1047 | var tableDefinition = this.TableDefinitions[tuple.Table]; |
| 1079 | output.EnsureTable(tableDefinition); | 1048 | this.Output.EnsureTable(tableDefinition); |
| 1080 | } | 1049 | } |
| 1081 | 1050 | ||
| 1082 | private void AddWixMediaTemplateTuple(WixMediaTemplateTuple tuple, WindowsInstallerData output) | 1051 | private void AddWixMediaTemplateTuple(WixMediaTemplateTuple tuple) |
| 1083 | { | 1052 | { |
| 1084 | var table = output.EnsureTable(this.TableDefinitions["WixMediaTemplate"]); | 1053 | var row = (WixMediaTemplateRow)this.CreateRow(tuple, "WixMediaTemplate"); |
| 1085 | var row = (WixMediaTemplateRow)table.CreateRow(tuple.SourceLineNumbers); | ||
| 1086 | row.CabinetTemplate = tuple.CabinetTemplate; | 1054 | row.CabinetTemplate = tuple.CabinetTemplate; |
| 1087 | row.CompressionLevel = tuple.CompressionLevel; | 1055 | row.CompressionLevel = tuple.CompressionLevel; |
| 1088 | row.DiskPrompt = tuple.DiskPrompt; | 1056 | row.DiskPrompt = tuple.DiskPrompt; |
| @@ -1091,26 +1059,25 @@ namespace WixToolset.Core.WindowsInstaller.Bind | |||
| 1091 | row.MaximumCabinetSizeForLargeFileSplitting = tuple.MaximumCabinetSizeForLargeFileSplitting ?? MaxValueOfMaxCabSizeForLargeFileSplitting; | 1059 | row.MaximumCabinetSizeForLargeFileSplitting = tuple.MaximumCabinetSizeForLargeFileSplitting ?? MaxValueOfMaxCabSizeForLargeFileSplitting; |
| 1092 | } | 1060 | } |
| 1093 | 1061 | ||
| 1094 | private void AddTupleFromExtension(IntermediateTuple tuple, WindowsInstallerData output) | 1062 | private void AddTupleFromExtension(IntermediateTuple tuple) |
| 1095 | { | 1063 | { |
| 1096 | foreach (var extension in this.BackendExtensions) | 1064 | foreach (var extension in this.BackendExtensions) |
| 1097 | { | 1065 | { |
| 1098 | if (extension.TryAddTupleToOutput(tuple, output)) | 1066 | if (extension.TryAddTupleToOutput(tuple, this.Output)) |
| 1099 | { | 1067 | { |
| 1100 | break; | 1068 | break; |
| 1101 | } | 1069 | } |
| 1102 | } | 1070 | } |
| 1103 | } | 1071 | } |
| 1104 | 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) |
| 1106 | { | 1074 | { |
| 1107 | if (!this.TableDefinitions.TryGet(tableName ?? tuple.Definition.Name, out var tableDefinition)) | 1075 | if (!this.TableDefinitions.TryGet(tableName ?? tuple.Definition.Name, out var tableDefinition)) |
| 1108 | { | 1076 | { |
| 1109 | return; | 1077 | return; |
| 1110 | } | 1078 | } |
| 1111 | 1079 | ||
| 1112 | var table = output.EnsureTable(tableDefinition); | 1080 | var row = this.CreateRow(tuple, tableDefinition); |
| 1113 | var row = table.CreateRow(tuple.SourceLineNumbers); | ||
| 1114 | var rowOffset = 0; | 1081 | var rowOffset = 0; |
| 1115 | 1082 | ||
| 1116 | if (idIsPrimaryKey) | 1083 | if (idIsPrimaryKey) |
| @@ -1159,6 +1126,18 @@ namespace WixToolset.Core.WindowsInstaller.Bind | |||
| 1159 | } | 1126 | } |
| 1160 | } | 1127 | } |
| 1161 | 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 | |||
| 1162 | private static string GetMsiFilenameValue(string shortName, string longName) | 1141 | private static string GetMsiFilenameValue(string shortName, string longName) |
| 1163 | { | 1142 | { |
| 1164 | if (String.IsNullOrEmpty(shortName) || String.Equals(shortName, longName, StringComparison.OrdinalIgnoreCase)) | 1143 | if (String.IsNullOrEmpty(shortName) || String.Equals(shortName, longName, StringComparison.OrdinalIgnoreCase)) |
diff --git a/src/WixToolset.Core.WindowsInstaller/Bind/CreatePatchTransformsCommand.cs b/src/WixToolset.Core.WindowsInstaller/Bind/CreatePatchTransformsCommand.cs new file mode 100644 index 00000000..854d973e --- /dev/null +++ b/src/WixToolset.Core.WindowsInstaller/Bind/CreatePatchTransformsCommand.cs | |||
| @@ -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 | } | ||
diff --git a/src/WixToolset.Core.WindowsInstaller/Bind/ExtractMergeModuleFilesCommand.cs b/src/WixToolset.Core.WindowsInstaller/Bind/ExtractMergeModuleFilesCommand.cs index 5412c6f9..49b6a6f8 100644 --- a/src/WixToolset.Core.WindowsInstaller/Bind/ExtractMergeModuleFilesCommand.cs +++ b/src/WixToolset.Core.WindowsInstaller/Bind/ExtractMergeModuleFilesCommand.cs | |||
| @@ -48,7 +48,7 @@ namespace WixToolset.Core.WindowsInstaller.Bind | |||
| 48 | { | 48 | { |
| 49 | var mergeModulesFileFacades = new List<FileFacade>(); | 49 | var mergeModulesFileFacades = new List<FileFacade>(); |
| 50 | 50 | ||
| 51 | IMsmMerge2 merge = MsmInterop.GetMsmMerge(); | 51 | var merge = MsmInterop.GetMsmMerge(); |
| 52 | 52 | ||
| 53 | // Index all of the file rows to be able to detect collisions with files in the Merge Modules. | 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 | 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 | 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 | 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. | 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 | 61 | ||
| 62 | foreach (var wixMergeRow in this.WixMergeTuples) | 62 | foreach (var wixMergeRow in this.WixMergeTuples) |
| 63 | { | 63 | { |
| 64 | bool containsFiles = this.CreateFacadesForMergeModuleFiles(wixMergeRow, mergeModulesFileFacades, indexedFileFacades); | 64 | var containsFiles = this.CreateFacadesForMergeModuleFiles(wixMergeRow, mergeModulesFileFacades, indexedFileFacades); |
| 65 | 65 | ||
| 66 | // If the module has files and creating layout | 66 | // If the module has files and creating layout |
| 67 | if (containsFiles && !this.SuppressLayout) | 67 | if (containsFiles && !this.SuppressLayout) |
| @@ -75,21 +75,21 @@ namespace WixToolset.Core.WindowsInstaller.Bind | |||
| 75 | 75 | ||
| 76 | private bool CreateFacadesForMergeModuleFiles(WixMergeTuple wixMergeRow, List<FileFacade> mergeModulesFileFacades, Dictionary<string, FileFacade> indexedFileFacades) | 76 | private bool CreateFacadesForMergeModuleFiles(WixMergeTuple wixMergeRow, List<FileFacade> mergeModulesFileFacades, Dictionary<string, FileFacade> indexedFileFacades) |
| 77 | { | 77 | { |
| 78 | bool containsFiles = false; | 78 | var containsFiles = false; |
| 79 | 79 | ||
| 80 | try | 80 | try |
| 81 | { | 81 | { |
| 82 | // read the module's File table to get its FileMediaInformation entries and gather any other information needed from the module. | 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 | { | 84 | { |
| 85 | if (db.TableExists("File") && db.TableExists("Component")) | 85 | if (db.TableExists("File") && db.TableExists("Component")) |
| 86 | { | 86 | { |
| 87 | Dictionary<string, FileFacade> uniqueModuleFileIdentifiers = new Dictionary<string, FileFacade>(StringComparer.OrdinalIgnoreCase); | 87 | var uniqueModuleFileIdentifiers = new Dictionary<string, FileFacade>(StringComparer.OrdinalIgnoreCase); |
| 88 | 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 | { | 90 | { |
| 91 | // add each file row from the merge module into the file row collection (check for errors along the way) | 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 | { | 93 | { |
| 94 | // NOTE: this is very tricky - the merge module file rows are not added to the | 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 | 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); | 103 | var mergeModuleFileFacade = new FileFacade(true, fileTuple); |
| 104 | 104 | ||
| 105 | // If case-sensitive collision with another merge module or a user-authored file identifier. | 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 | { | 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 | } | 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 | { | 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 | } | 113 | } |
| 114 | else // no collision | 114 | else // no collision |
| 115 | { | 115 | { |
| 116 | mergeModulesFileFacades.Add(mergeModuleFileFacade); | 116 | mergeModulesFileFacades.Add(mergeModuleFileFacade); |
| 117 | 117 | ||
| 118 | // Keep updating the indexes as new rows are added. | 118 | // Keep updating the indexes as new rows are added. |
| 119 | indexedFileFacades.Add(mergeModuleFileFacade.File.Id.Id, mergeModuleFileFacade); | 119 | indexedFileFacades.Add(mergeModuleFileFacade.Id, mergeModuleFileFacade); |
| 120 | uniqueModuleFileIdentifiers.Add(mergeModuleFileFacade.File.Id.Id, mergeModuleFileFacade); | 120 | uniqueModuleFileIdentifiers.Add(mergeModuleFileFacade.Id, mergeModuleFileFacade); |
| 121 | } | 121 | } |
| 122 | 122 | ||
| 123 | containsFiles = true; | 123 | containsFiles = true; |
| @@ -126,13 +126,13 @@ namespace WixToolset.Core.WindowsInstaller.Bind | |||
| 126 | } | 126 | } |
| 127 | 127 | ||
| 128 | // Get the summary information to detect the Schema | 128 | // Get the summary information to detect the Schema |
| 129 | using (SummaryInformation summaryInformation = new SummaryInformation(db)) | 129 | using (var summaryInformation = new SummaryInformation(db)) |
| 130 | { | 130 | { |
| 131 | string moduleInstallerVersionString = summaryInformation.GetProperty(14); | 131 | var moduleInstallerVersionString = summaryInformation.GetProperty(14); |
| 132 | 132 | ||
| 133 | try | 133 | try |
| 134 | { | 134 | { |
| 135 | int moduleInstallerVersion = Convert.ToInt32(moduleInstallerVersionString, CultureInfo.InvariantCulture); | 135 | var moduleInstallerVersion = Convert.ToInt32(moduleInstallerVersionString, CultureInfo.InvariantCulture); |
| 136 | if (moduleInstallerVersion > this.OutputInstallerVersion) | 136 | if (moduleInstallerVersion > this.OutputInstallerVersion) |
| 137 | { | 137 | { |
| 138 | this.Messaging.Write(WarningMessages.InvalidHigherInstallerVersionInModule(wixMergeRow.SourceLineNumbers, wixMergeRow.Id.Id, moduleInstallerVersion, this.OutputInstallerVersion)); | 138 | this.Messaging.Write(WarningMessages.InvalidHigherInstallerVersionInModule(wixMergeRow.SourceLineNumbers, wixMergeRow.Id.Id, moduleInstallerVersion, this.OutputInstallerVersion)); |
| @@ -159,7 +159,7 @@ namespace WixToolset.Core.WindowsInstaller.Bind | |||
| 159 | 159 | ||
| 160 | private void ExtractFilesFromMergeModule(IMsmMerge2 merge, WixMergeTuple wixMergeRow) | 160 | private void ExtractFilesFromMergeModule(IMsmMerge2 merge, WixMergeTuple wixMergeRow) |
| 161 | { | 161 | { |
| 162 | bool moduleOpen = false; | 162 | var moduleOpen = false; |
| 163 | short mergeLanguage; | 163 | short mergeLanguage; |
| 164 | 164 | ||
| 165 | var mergeId = wixMergeRow.Id.Id; | 165 | var mergeId = wixMergeRow.Id.Id; |
| @@ -180,10 +180,10 @@ namespace WixToolset.Core.WindowsInstaller.Bind | |||
| 180 | moduleOpen = true; | 180 | moduleOpen = true; |
| 181 | 181 | ||
| 182 | // extract the module cabinet, then explode all of the files to a temp directory | 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); | 184 | merge.ExtractCAB(moduleCabPath); |
| 185 | 185 | ||
| 186 | string mergeIdPath = Path.Combine(this.IntermediateFolder, mergeId); | 186 | var mergeIdPath = Path.Combine(this.IntermediateFolder, mergeId); |
| 187 | Directory.CreateDirectory(mergeIdPath); | 187 | Directory.CreateDirectory(mergeIdPath); |
| 188 | 188 | ||
| 189 | try | 189 | try |
diff --git a/src/WixToolset.Core.WindowsInstaller/Bind/GenerateDatabaseCommand.cs b/src/WixToolset.Core.WindowsInstaller/Bind/GenerateDatabaseCommand.cs index 6b365ecd..ed3b6f01 100644 --- a/src/WixToolset.Core.WindowsInstaller/Bind/GenerateDatabaseCommand.cs +++ b/src/WixToolset.Core.WindowsInstaller/Bind/GenerateDatabaseCommand.cs | |||
| @@ -5,367 +5,396 @@ namespace WixToolset.Core.WindowsInstaller.Bind | |||
| 5 | using System; | 5 | using System; |
| 6 | using System.Collections.Generic; | 6 | using System.Collections.Generic; |
| 7 | using System.ComponentModel; | 7 | using System.ComponentModel; |
| 8 | using System.Globalization; | ||
| 9 | using System.IO; | 8 | using System.IO; |
| 10 | using System.Text; | 9 | using System.Text; |
| 10 | using WixToolset.Core.WindowsInstaller.Msi; | ||
| 11 | using WixToolset.Data; | 11 | using WixToolset.Data; |
| 12 | using WixToolset.Extensibility; | ||
| 13 | using WixToolset.Data.WindowsInstaller; | 12 | using WixToolset.Data.WindowsInstaller; |
| 14 | using WixToolset.Extensibility.Services; | 13 | using WixToolset.Extensibility; |
| 15 | using WixToolset.Extensibility.Data; | 14 | using WixToolset.Extensibility.Data; |
| 16 | using WixToolset.Core.WindowsInstaller.Msi; | 15 | using WixToolset.Extensibility.Services; |
| 17 | 16 | ||
| 18 | internal class GenerateDatabaseCommand | 17 | internal class GenerateDatabaseCommand |
| 19 | { | 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; } | ||
| 21 | 35 | ||
| 22 | public IBackendHelper BackendHelper { private get; set; } | 36 | private IBackendHelper BackendHelper { get; } |
| 23 | 37 | ||
| 24 | public IEnumerable<IFileSystemExtension> Extensions { private get; set; } | 38 | private IEnumerable<IFileSystemExtension> Extensions { get; } |
| 25 | 39 | ||
| 26 | /// <summary> | 40 | /// <summary> |
| 27 | /// Whether to keep columns added in a transform. | 41 | /// Whether to keep columns added in a transform. |
| 28 | /// </summary> | 42 | /// </summary> |
| 29 | public bool KeepAddedColumns { private get; set; } | 43 | private bool KeepAddedColumns { get; } |
| 30 | 44 | ||
| 31 | public IMessaging Messaging { private get; set; } | 45 | private IMessaging Messaging { get; } |
| 32 | 46 | ||
| 33 | public WindowsInstallerData Output { private get; set; } | 47 | private WindowsInstallerData Data { get; } |
| 34 | 48 | ||
| 35 | public string OutputPath { private get; set; } | 49 | private string OutputPath { get; } |
| 36 | 50 | ||
| 37 | public TableDefinitionCollection TableDefinitions { private get; set; } | 51 | private TableDefinitionCollection TableDefinitions { get; } |
| 38 | 52 | ||
| 39 | public string IntermediateFolder { private get; set; } | 53 | private string IntermediateFolder { get; } |
| 40 | 54 | ||
| 41 | public List<ITrackedFile> GeneratedTemporaryFiles { get; } = new List<ITrackedFile>(); | 55 | public List<ITrackedFile> GeneratedTemporaryFiles { get; } = new List<ITrackedFile>(); |
| 42 | 56 | ||
| 43 | /// <summary> | 57 | /// <summary> |
| 44 | /// Whether to use a subdirectory based on the <paramref name="databaseFile"/> file name for intermediate files. | 58 | /// Whether to use a subdirectory based on the <paramref name="databaseFile"/> file name for intermediate files. |
| 45 | /// </summary> | 59 | /// </summary> |
| 46 | public bool SuppressAddingValidationRows { private get; set; } | 60 | private bool SuppressAddingValidationRows { get; } |
| 47 | 61 | ||
| 48 | public bool UseSubDirectory { private get; set; } | 62 | private bool UseSubDirectory { get; } |
| 49 | 63 | ||
| 50 | public void Execute() | 64 | public void Execute() |
| 51 | { | 65 | { |
| 52 | // Add the _Validation rows. | 66 | // Add the _Validation rows. |
| 53 | if (!this.SuppressAddingValidationRows) | 67 | if (!this.SuppressAddingValidationRows) |
| 54 | { | 68 | { |
| 55 | var validationTable = this.Output.EnsureTable(this.TableDefinitions["_Validation"]); | 69 | this.AddValidationRows(); |
| 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 | } | ||
| 107 | } | 70 | } |
| 108 | 71 | ||
| 109 | // Set the base directory. | ||
| 110 | var baseDirectory = this.IntermediateFolder; | 72 | var baseDirectory = this.IntermediateFolder; |
| 111 | 73 | ||
| 112 | if (this.UseSubDirectory) | 74 | if (this.UseSubDirectory) |
| 113 | { | 75 | { |
| 114 | string filename = Path.GetFileNameWithoutExtension(this.OutputPath); | 76 | var filename = Path.GetFileNameWithoutExtension(this.OutputPath); |
| 115 | baseDirectory = Path.Combine(baseDirectory, filename); | 77 | baseDirectory = Path.Combine(baseDirectory, filename); |
| 116 | |||
| 117 | // make sure the directory exists | ||
| 118 | Directory.CreateDirectory(baseDirectory); | ||
| 119 | } | 78 | } |
| 120 | 79 | ||
| 121 | var idtDirectory = Path.Combine(baseDirectory, "_idts"); | 80 | var idtFolder = Path.Combine(baseDirectory, "_idts"); |
| 122 | Directory.CreateDirectory(idtDirectory); | ||
| 123 | 81 | ||
| 124 | try | 82 | var type = OpenDatabase.CreateDirect; |
| 83 | |||
| 84 | if (OutputType.Patch == this.Data.Type) | ||
| 125 | { | 85 | { |
| 126 | OpenDatabase type = OpenDatabase.CreateDirect; | 86 | type |= OpenDatabase.OpenPatchFile; |
| 87 | } | ||
| 127 | 88 | ||
| 128 | // set special flag for patch files | 89 | // Localize the codepage if a value was specified directly. |
| 129 | if (OutputType.Patch == this.Output.Type) | 90 | if (-1 != this.Codepage) |
| 130 | { | 91 | { |
| 131 | type |= OpenDatabase.OpenPatchFile; | 92 | this.Data.Codepage = this.Codepage; |
| 132 | } | 93 | } |
| 133 | 94 | ||
| 95 | try | ||
| 96 | { | ||
| 134 | #if DEBUG | 97 | #if DEBUG |
| 135 | Console.WriteLine("Opening database at: {0}", this.OutputPath); | 98 | Console.WriteLine("Opening database at: {0}", this.OutputPath); |
| 136 | #endif | 99 | #endif |
| 137 | 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 | |||
| 144 | Directory.CreateDirectory(Path.GetDirectoryName(this.OutputPath)); | 101 | Directory.CreateDirectory(Path.GetDirectoryName(this.OutputPath)); |
| 145 | 102 | ||
| 146 | using (Database db = new Database(this.OutputPath, type)) | 103 | Directory.CreateDirectory(idtFolder); |
| 104 | |||
| 105 | using (var db = new Database(this.OutputPath, type)) | ||
| 147 | { | 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 |
| 149 | // database before we add any tables (or the tables would be added | 108 | // database before we add any tables (or the tables would be added |
| 150 | // with the wrong codepage). | 109 | // with the wrong codepage). |
| 151 | if (0 != this.Output.Codepage) | 110 | if (0 != this.Data.Codepage) |
| 152 | { | 111 | { |
| 153 | this.SetDatabaseCodepage(db, this.Output.Codepage, idtDirectory); | 112 | this.SetDatabaseCodepage(db, this.Data.Codepage, idtFolder); |
| 154 | } | 113 | } |
| 155 | 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) | ||
| 157 | { | 141 | { |
| 158 | Table importTable = table; | 142 | var row = validationTable.CreateRow(null); |
| 159 | bool hasBinaryColumn = false; | 143 | |
| 144 | row[0] = table.Name; | ||
| 160 | 145 | ||
| 161 | // Skip all unreal tables other than _Streams. | 146 | row[1] = columnDef.Name; |
| 162 | if (table.Definition.Unreal && "_Streams" != table.Name) | 147 | |
| 148 | if (columnDef.Nullable) | ||
| 163 | { | 149 | { |
| 164 | continue; | 150 | row[2] = "Y"; |
| 151 | } | ||
| 152 | else | ||
| 153 | { | ||
| 154 | row[2] = "N"; | ||
| 165 | } | 155 | } |
| 166 | 156 | ||
| 167 | // Do not put the _Validation table in patches, it is not needed. | 157 | if (columnDef.MinValue.HasValue) |
| 168 | if (OutputType.Patch == this.Output.Type && "_Validation" == table.Name) | ||
| 169 | { | 158 | { |
| 170 | continue; | 159 | row[3] = columnDef.MinValue.Value; |
| 171 | } | 160 | } |
| 172 | 161 | ||
| 173 | // The only way to import binary data is to copy it to a local subdirectory first. | 162 | if (columnDef.MaxValue.HasValue) |
| 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) | ||
| 177 | { | 163 | { |
| 178 | if (ColumnType.Object == columnDefinition.Type) | 164 | row[4] = columnDef.MaxValue.Value; |
| 179 | { | ||
| 180 | importTable = new Table(table.Definition); | ||
| 181 | hasBinaryColumn = true; | ||
| 182 | break; | ||
| 183 | } | ||
| 184 | } | 165 | } |
| 185 | 166 | ||
| 186 | // Create the table via IDT import. | 167 | row[5] = columnDef.KeyTable; |
| 187 | if ("_Streams" != importTable.Name) | 168 | |
| 169 | if (columnDef.KeyColumn.HasValue) | ||
| 188 | { | 170 | { |
| 189 | try | 171 | row[6] = columnDef.KeyColumn.Value; |
| 190 | { | 172 | } |
| 191 | var command = new CreateIdtFileCommand(this.Messaging, importTable, this.Output.Codepage, idtDirectory, this.KeepAddedColumns); | ||
| 192 | command.Execute(); | ||
| 193 | 173 | ||
| 194 | var buildOutput = this.BackendHelper.TrackFile(command.IdtPath, TrackedFileType.Temporary); | 174 | if (ColumnCategory.Unknown != columnDef.Category) |
| 195 | this.GeneratedTemporaryFiles.Add(buildOutput); | 175 | { |
| 176 | row[7] = columnDef.Category.ToString(); | ||
| 177 | } | ||
| 196 | 178 | ||
| 197 | db.Import(command.IdtPath); | 179 | row[8] = columnDef.Possibilities; |
| 198 | } | ||
| 199 | catch (WixInvalidIdtException) | ||
| 200 | { | ||
| 201 | // If ValidateRows finds anything it doesn't like, it throws | ||
| 202 | importTable.ValidateRows(); | ||
| 203 | 180 | ||
| 204 | // Otherwise we rethrow the InvalidIdt | 181 | row[9] = columnDef.Description; |
| 205 | throw; | 182 | } |
| 206 | } | 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; | ||
| 207 | } | 254 | } |
| 208 | 255 | ||
| 209 | // insert the rows via SQL query if this table contains object fields | 256 | if (!firstColumn) |
| 210 | if (hasBinaryColumn) | ||
| 211 | { | 257 | { |
| 212 | StringBuilder query = new StringBuilder("SELECT "); | 258 | query.Append(","); |
| 259 | } | ||
| 213 | 260 | ||
| 214 | // Build the query for the view. | 261 | query.AppendFormat(" `{0}`", columnDefinition.Name); |
| 215 | bool firstColumn = true; | 262 | firstColumn = false; |
| 216 | foreach (ColumnDefinition columnDefinition in table.Definition.Columns) | 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)) | ||
| 217 | { | 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) | ||
| 219 | { | 279 | { |
| 220 | query.Append(","); | 280 | streamName.Append(table.Name); |
| 221 | } | 281 | } |
| 222 | 282 | ||
| 223 | query.AppendFormat(" `{0}`", columnDefinition.Name); | 283 | var needStream = false; |
| 224 | firstColumn = false; | ||
| 225 | } | ||
| 226 | query.AppendFormat(" FROM `{0}`", table.Name); | ||
| 227 | 284 | ||
| 228 | using (View tableView = db.OpenExecuteView(query.ToString())) | 285 | for (var i = 0; i < table.Definition.Columns.Length; i++) |
| 229 | { | ||
| 230 | // Import each row containing a stream | ||
| 231 | foreach (Row row in table.Rows) | ||
| 232 | { | 286 | { |
| 233 | using (Record record = new Record(table.Definition.Columns.Length)) | 287 | var columnDefinition = table.Definition.Columns[i]; |
| 288 | |||
| 289 | if (columnDefinition.Unreal) | ||
| 234 | { | 290 | { |
| 235 | StringBuilder streamName = new StringBuilder(); | 291 | continue; |
| 236 | bool needStream = false; | 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); | ||
| 237 | 300 | ||
| 238 | // the _Streams table doesn't prepend the table name (or a period) | 301 | if (columnDefinition.PrimaryKey) |
| 239 | if ("_Streams" != table.Name) | 302 | { |
| 240 | { | 303 | if (0 < streamName.Length) |
| 241 | streamName.Append(table.Name); | 304 | { |
| 242 | } | 305 | streamName.Append("."); |
| 306 | } | ||
| 307 | |||
| 308 | streamName.Append(str); | ||
| 309 | } | ||
| 243 | 310 | ||
| 244 | for (int i = 0; i < table.Definition.Columns.Length; i++) | 311 | record.SetString(i + 1, str); |
| 245 | { | 312 | break; |
| 246 | ColumnDefinition columnDefinition = table.Definition.Columns[i]; | 313 | case ColumnType.Number: |
| 314 | record.SetInteger(i + 1, row.FieldAsInteger(i)); | ||
| 315 | break; | ||
| 247 | 316 | ||
| 248 | switch (columnDefinition.Type) | 317 | case ColumnType.Object: |
| 318 | if (null != row[i]) | ||
| 249 | { | 319 | { |
| 250 | case ColumnType.Localized: | 320 | needStream = true; |
| 251 | case ColumnType.Preserved: | 321 | try |
| 252 | case ColumnType.String: | 322 | { |
| 253 | if (columnDefinition.PrimaryKey) | 323 | record.SetStream(i + 1, row.FieldAsString(i)); |
| 324 | } | ||
| 325 | catch (Win32Exception e) | ||
| 326 | { | ||
| 327 | if (0xA1 == e.NativeErrorCode) // ERROR_BAD_PATHNAME | ||
| 254 | { | 328 | { |
| 255 | if (0 < streamName.Length) | 329 | throw new WixException(ErrorMessages.FileNotFound(row.SourceLineNumbers, row.FieldAsString(i))); |
| 256 | { | ||
| 257 | streamName.Append("."); | ||
| 258 | } | ||
| 259 | streamName.Append((string)row[i]); | ||
| 260 | } | 330 | } |
| 261 | 331 | else | |
| 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]) | ||
| 269 | { | 332 | { |
| 270 | needStream = true; | 333 | throw new WixException(ErrorMessages.Win32Exception(e.NativeErrorCode, e.Message)); |
| 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 | } | ||
| 286 | } | 334 | } |
| 287 | break; | 335 | } |
| 288 | } | 336 | } |
| 289 | } | 337 | break; |
| 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 | } | ||
| 302 | } | 338 | } |
| 303 | } | 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 | } | ||
| 313 | 340 | ||
| 314 | // Insert substorages (usually transforms inside a patch or instance transforms in a package). | 341 | // check for a stream name that is more than 62 characters long (the maximum allowed length) |
| 315 | if (0 < this.Output.SubStorages.Count) | 342 | if (needStream && MsiInterop.MsiMaxStreamNameLength < streamName.Length) |
| 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) | ||
| 327 | { | 343 | { |
| 328 | continue; | 344 | this.Messaging.Write(ErrorMessages.StreamNameTooLong(row.SourceLineNumbers, table.Name, streamName.ToString(), streamName.Length)); |
| 329 | } | 345 | } |
| 330 | 346 | else // add the row to the database | |
| 331 | // add the storage | ||
| 332 | using (Record record = new Record(2)) | ||
| 333 | { | 347 | { |
| 334 | record.SetString(1, subStorage.Name); | 348 | tableView.Modify(ModifyView.Assign, record); |
| 335 | record.SetStream(2, transformFile); | ||
| 336 | storagesView.Modify(ModifyView.Assign, record); | ||
| 337 | } | 349 | } |
| 338 | } | 350 | } |
| 339 | } | 351 | } |
| 340 | } | 352 | } |
| 341 | 353 | ||
| 342 | // We're good, commit the changes to the new database. | 354 | // Remove rows from the _Streams table for wixpdbs. |
| 343 | db.Commit(); | 355 | if ("_Streams" == table.Name) |
| 356 | { | ||
| 357 | table.Rows.Clear(); | ||
| 358 | } | ||
| 344 | } | 359 | } |
| 345 | } | 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 | } | ||
| 351 | } | 361 | } |
| 352 | 362 | ||
| 353 | private void BindTransform(WindowsInstallerData transform, string outputPath) | 363 | private void ImportSubStorages(Database db) |
| 354 | { | 364 | { |
| 355 | var command = new BindTransformCommand(); | 365 | if (0 < this.Data.SubStorages.Count) |
| 356 | command.Messaging = this.Messaging; | 366 | { |
| 357 | command.Extensions = this.Extensions; | 367 | using (var storagesView = new View(db, "SELECT `Name`, `Data` FROM `_Storages`")) |
| 358 | command.TempFilesLocation = this.IntermediateFolder; | 368 | { |
| 359 | command.Transform = transform; | 369 | foreach (var subStorage in this.Data.SubStorages) |
| 360 | command.OutputPath = outputPath; | 370 | { |
| 361 | command.TableDefinitions = this.TableDefinitions; | 371 | var transformFile = Path.Combine(this.IntermediateFolder, String.Concat(subStorage.Name, ".mst")); |
| 362 | command.Execute(); | 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 | } | ||
| 363 | } | 392 | } |
| 364 | 393 | ||
| 365 | private void SetDatabaseCodepage(Database db, int codepage, string idtDirectory) | 394 | private void SetDatabaseCodepage(Database db, int codepage, string idtFolder) |
| 366 | { | 395 | { |
| 367 | // write out the _ForceCodepage IDT file | 396 | // Write out the _ForceCodepage IDT file. |
| 368 | var idtPath = Path.Combine(idtDirectory, "_ForceCodepage.idt"); | 397 | var idtPath = Path.Combine(idtFolder, "_ForceCodepage.idt"); |
| 369 | using (var idtFile = new StreamWriter(idtPath, false, Encoding.ASCII)) | 398 | using (var idtFile = new StreamWriter(idtPath, false, Encoding.ASCII)) |
| 370 | { | 399 | { |
| 371 | idtFile.WriteLine(); // dummy column name record | 400 | idtFile.WriteLine(); // dummy column name record |
| @@ -377,14 +406,14 @@ namespace WixToolset.Core.WindowsInstaller.Bind | |||
| 377 | var trackId = this.BackendHelper.TrackFile(idtPath, TrackedFileType.Temporary); | 406 | var trackId = this.BackendHelper.TrackFile(idtPath, TrackedFileType.Temporary); |
| 378 | this.GeneratedTemporaryFiles.Add(trackId); | 407 | this.GeneratedTemporaryFiles.Add(trackId); |
| 379 | 408 | ||
| 380 | // try to import the table into the MSI | 409 | // Try to import the table into the MSI. |
| 381 | try | 410 | try |
| 382 | { | 411 | { |
| 383 | db.Import(idtPath); | 412 | db.Import(idtPath); |
| 384 | } | 413 | } |
| 385 | catch (WixInvalidIdtException) | 414 | catch (WixInvalidIdtException) |
| 386 | { | 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. |
| 388 | throw new WixException(ErrorMessages.IllegalCodepage(codepage)); | 417 | throw new WixException(ErrorMessages.IllegalCodepage(codepage)); |
| 389 | } | 418 | } |
| 390 | } | 419 | } |
diff --git a/src/WixToolset.Core.WindowsInstaller/Bind/GenerateTransformCommand.cs b/src/WixToolset.Core.WindowsInstaller/Bind/GenerateTransformCommand.cs new file mode 100644 index 00000000..8a7dd702 --- /dev/null +++ b/src/WixToolset.Core.WindowsInstaller/Bind/GenerateTransformCommand.cs | |||
| @@ -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 | } | ||
diff --git a/src/WixToolset.Core.WindowsInstaller/Bind/GetFileFacadesCommand.cs b/src/WixToolset.Core.WindowsInstaller/Bind/GetFileFacadesCommand.cs index 0da6a6b0..2844f797 100644 --- a/src/WixToolset.Core.WindowsInstaller/Bind/GetFileFacadesCommand.cs +++ b/src/WixToolset.Core.WindowsInstaller/Bind/GetFileFacadesCommand.cs | |||
| @@ -132,7 +132,7 @@ namespace WixToolset.Core.WindowsInstaller.Bind | |||
| 132 | file.SymbolPaths = String.Concat(file.SymbolPaths, ";", row.SymbolPaths); | 132 | file.SymbolPaths = String.Concat(file.SymbolPaths, ";", row.SymbolPaths); |
| 133 | } | 133 | } |
| 134 | 134 | ||
| 135 | #if REVISIT_FOR_PATCHING | 135 | #if TODO_PATCHING |
| 136 | Field field = row.Fields[2]; | 136 | Field field = row.Fields[2]; |
| 137 | if (null != field.PreviousData) | 137 | if (null != field.PreviousData) |
| 138 | { | 138 | { |
diff --git a/src/WixToolset.Core.WindowsInstaller/Bind/GetFileFacadesFromTransforms.cs b/src/WixToolset.Core.WindowsInstaller/Bind/GetFileFacadesFromTransforms.cs new file mode 100644 index 00000000..9818f01a --- /dev/null +++ b/src/WixToolset.Core.WindowsInstaller/Bind/GetFileFacadesFromTransforms.cs | |||
| @@ -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 | } | ||
diff --git a/src/WixToolset.Core.WindowsInstaller/Bind/LoadTableDefinitionsCommand.cs b/src/WixToolset.Core.WindowsInstaller/Bind/LoadTableDefinitionsCommand.cs index 92ddad6f..1aa4065e 100644 --- a/src/WixToolset.Core.WindowsInstaller/Bind/LoadTableDefinitionsCommand.cs +++ b/src/WixToolset.Core.WindowsInstaller/Bind/LoadTableDefinitionsCommand.cs | |||
| @@ -9,15 +9,22 @@ namespace WixToolset.Core.WindowsInstaller.Bind | |||
| 9 | using WixToolset.Data; | 9 | using WixToolset.Data; |
| 10 | using WixToolset.Data.Tuples; | 10 | using WixToolset.Data.Tuples; |
| 11 | using WixToolset.Data.WindowsInstaller; | 11 | using WixToolset.Data.WindowsInstaller; |
| 12 | using WixToolset.Extensibility; | ||
| 12 | 13 | ||
| 13 | internal class LoadTableDefinitionsCommand | 14 | internal class LoadTableDefinitionsCommand |
| 14 | { | 15 | { |
| 15 | public LoadTableDefinitionsCommand(IntermediateSection section) => this.Section = section; | 16 | public LoadTableDefinitionsCommand(IntermediateSection section, IEnumerable<IWindowsInstallerBackendBinderExtension> backendExtensions) |
| 16 | 17 | { | |
| 17 | public TableDefinitionCollection TableDefinitions { get; private set; } | 18 | this.Section = section; |
| 19 | this.BackendExtensions = backendExtensions; | ||
| 20 | } | ||
| 18 | 21 | ||
| 19 | private IntermediateSection Section { get; } | 22 | private IntermediateSection Section { get; } |
| 20 | 23 | ||
| 24 | private IEnumerable<IWindowsInstallerBackendBinderExtension> BackendExtensions { get; } | ||
| 25 | |||
| 26 | public TableDefinitionCollection TableDefinitions { get; private set; } | ||
| 27 | |||
| 21 | public TableDefinitionCollection Execute() | 28 | public TableDefinitionCollection Execute() |
| 22 | { | 29 | { |
| 23 | var tableDefinitions = new TableDefinitionCollection(WindowsInstallerStandardInternal.GetTableDefinitions()); | 30 | var tableDefinitions = new TableDefinitionCollection(WindowsInstallerStandardInternal.GetTableDefinitions()); |
| @@ -28,6 +35,14 @@ namespace WixToolset.Core.WindowsInstaller.Bind | |||
| 28 | tableDefinitions.Add(customTableDefinition); | 35 | tableDefinitions.Add(customTableDefinition); |
| 29 | } | 36 | } |
| 30 | 37 | ||
| 38 | foreach (var backendExtension in this.BackendExtensions) | ||
| 39 | { | ||
| 40 | foreach (var tableDefinition in backendExtension.TableDefinitions) | ||
| 41 | { | ||
| 42 | tableDefinitions.Add(tableDefinition); | ||
| 43 | } | ||
| 44 | } | ||
| 45 | |||
| 31 | this.TableDefinitions = tableDefinitions; | 46 | this.TableDefinitions = tableDefinitions; |
| 32 | return this.TableDefinitions; | 47 | return this.TableDefinitions; |
| 33 | } | 48 | } |
diff --git a/src/WixToolset.Core.WindowsInstaller/Bind/MergeModulesCommand.cs b/src/WixToolset.Core.WindowsInstaller/Bind/MergeModulesCommand.cs index 8c11555e..b90aecd1 100644 --- a/src/WixToolset.Core.WindowsInstaller/Bind/MergeModulesCommand.cs +++ b/src/WixToolset.Core.WindowsInstaller/Bind/MergeModulesCommand.cs | |||
| @@ -277,7 +277,7 @@ namespace WixToolset.Core.WindowsInstaller.Bind | |||
| 277 | 277 | ||
| 278 | using (Record record = new Record(1)) | 278 | using (Record record = new Record(1)) |
| 279 | { | 279 | { |
| 280 | record.SetString(1, file.File.Id.Id); | 280 | record.SetString(1, file.Id); |
| 281 | view.Execute(record); | 281 | view.Execute(record); |
| 282 | } | 282 | } |
| 283 | 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."); | 288 | throw new InvalidOperationException("Failed to fetch a File row from the database that was merged in from a module."); |
| 289 | } | 289 | } |
| 290 | 290 | ||
| 291 | recordUpdate.SetInteger(1, file.File.Sequence); | 291 | recordUpdate.SetInteger(1, file.Sequence); |
| 292 | 292 | ||
| 293 | // Update the file attributes to match the compression specified | 293 | // Update the file attributes to match the compression specified |
| 294 | // on the Merge element or on the Package element. | 294 | // on the Merge element or on the Package element. |
| @@ -300,12 +300,12 @@ namespace WixToolset.Core.WindowsInstaller.Bind | |||
| 300 | attributes = recordUpdate.GetInteger(2); | 300 | attributes = recordUpdate.GetInteger(2); |
| 301 | } | 301 | } |
| 302 | 302 | ||
| 303 | if ((file.File.Attributes & FileTupleAttributes.Compressed) == FileTupleAttributes.Compressed) | 303 | if (file.Compressed) |
| 304 | { | 304 | { |
| 305 | attributes |= WindowsInstallerConstants.MsidbFileAttributesCompressed; | 305 | attributes |= WindowsInstallerConstants.MsidbFileAttributesCompressed; |
| 306 | attributes &= ~WindowsInstallerConstants.MsidbFileAttributesNoncompressed; | 306 | attributes &= ~WindowsInstallerConstants.MsidbFileAttributesNoncompressed; |
| 307 | } | 307 | } |
| 308 | else if ((file.File.Attributes & FileTupleAttributes.Uncompressed) == FileTupleAttributes.Uncompressed) | 308 | else if (file.Uncompressed) |
| 309 | { | 309 | { |
| 310 | attributes |= WindowsInstallerConstants.MsidbFileAttributesNoncompressed; | 310 | attributes |= WindowsInstallerConstants.MsidbFileAttributesNoncompressed; |
| 311 | attributes &= ~WindowsInstallerConstants.MsidbFileAttributesCompressed; | 311 | attributes &= ~WindowsInstallerConstants.MsidbFileAttributesCompressed; |
diff --git a/src/WixToolset.Core.WindowsInstaller/Bind/PatchTransform.cs b/src/WixToolset.Core.WindowsInstaller/Bind/PatchTransform.cs new file mode 100644 index 00000000..5ada29de --- /dev/null +++ b/src/WixToolset.Core.WindowsInstaller/Bind/PatchTransform.cs | |||
| @@ -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 | } | ||
diff --git a/src/WixToolset.Core.WindowsInstaller/Bind/ProcessUncompressedFilesCommand.cs b/src/WixToolset.Core.WindowsInstaller/Bind/ProcessUncompressedFilesCommand.cs index 373ada38..13d47215 100644 --- a/src/WixToolset.Core.WindowsInstaller/Bind/ProcessUncompressedFilesCommand.cs +++ b/src/WixToolset.Core.WindowsInstaller/Bind/ProcessUncompressedFilesCommand.cs | |||
| @@ -86,14 +86,14 @@ namespace WixToolset.Core.WindowsInstaller.Bind | |||
| 86 | 86 | ||
| 87 | // setup up the query record and find the appropriate file in the | 87 | // setup up the query record and find the appropriate file in the |
| 88 | // previously executed file view | 88 | // previously executed file view |
| 89 | fileQueryRecord[1] = facade.File.Id.Id; | 89 | fileQueryRecord[1] = facade.Id; |
| 90 | fileView.Execute(fileQueryRecord); | 90 | fileView.Execute(fileQueryRecord); |
| 91 | 91 | ||
| 92 | using (Record fileRecord = fileView.Fetch()) | 92 | using (Record fileRecord = fileView.Fetch()) |
| 93 | { | 93 | { |
| 94 | if (null == fileRecord) | 94 | if (null == fileRecord) |
| 95 | { | 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 | } | 97 | } |
| 98 | 98 | ||
| 99 | relativeFileLayoutPath = this.PathResolver.GetFileSourcePath(directories, fileRecord[1], fileRecord[2], this.Compressed, this.LongNamesInImage); | 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 | 102 | // finally put together the base media layout path and the relative file layout path |
| 103 | var fileLayoutPath = Path.Combine(mediaLayoutDirectory, relativeFileLayoutPath); | 103 | var fileLayoutPath = Path.Combine(mediaLayoutDirectory, relativeFileLayoutPath); |
| 104 | 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); | 106 | fileTransfers.Add(transfer); |
| 107 | 107 | ||
| 108 | // Track the location where the cabinet will be placed. If the transfer is | 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 | 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 | 111 | // don't want to clean the file because we'd be deleting the original |
| 112 | // (and that would be bad). | 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; | 114 | tracked.Clean = !transfer.Redundant; |
| 115 | 115 | ||
| 116 | trackedFiles.Add(tracked); | 116 | trackedFiles.Add(tracked); |
diff --git a/src/WixToolset.Core.WindowsInstaller/Bind/SequenceActionsCommand.cs b/src/WixToolset.Core.WindowsInstaller/Bind/SequenceActionsCommand.cs index e9b0d612..749f9ac0 100644 --- a/src/WixToolset.Core.WindowsInstaller/Bind/SequenceActionsCommand.cs +++ b/src/WixToolset.Core.WindowsInstaller/Bind/SequenceActionsCommand.cs | |||
| @@ -11,24 +11,25 @@ namespace WixToolset.Core.WindowsInstaller.Bind | |||
| 11 | using WixToolset.Data.WindowsInstaller; | 11 | using WixToolset.Data.WindowsInstaller; |
| 12 | using WixToolset.Extensibility.Services; | 12 | using WixToolset.Extensibility.Services; |
| 13 | 13 | ||
| 14 | /// <summary> | ||
| 15 | /// Set sequence numbers for all the actions and create tuples in the output object. | ||
| 16 | /// </summary> | ||
| 14 | internal class SequenceActionsCommand | 17 | internal class SequenceActionsCommand |
| 15 | { | 18 | { |
| 16 | public SequenceActionsCommand(IntermediateSection section) | 19 | public SequenceActionsCommand(IMessaging messaging, IntermediateSection section) |
| 17 | { | 20 | { |
| 21 | this.Messaging = messaging; | ||
| 18 | this.Section = section; | 22 | this.Section = section; |
| 19 | 23 | ||
| 20 | this.RelativeActionsForActions = new Dictionary<string, RelativeActions>(); | 24 | this.RelativeActionsForActions = new Dictionary<string, RelativeActions>(); |
| 21 | } | 25 | } |
| 22 | 26 | ||
| 27 | private IMessaging Messaging { get; } | ||
| 28 | |||
| 23 | private IntermediateSection Section { get; } | 29 | private IntermediateSection Section { get; } |
| 24 | 30 | ||
| 25 | private Dictionary<string, RelativeActions> RelativeActionsForActions { get; } | 31 | private Dictionary<string, RelativeActions> RelativeActionsForActions { get; } |
| 26 | 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> | ||
| 32 | public void Execute() | 33 | public void Execute() |
| 33 | { | 34 | { |
| 34 | var requiredActionTuples = new Dictionary<string, WixActionTuple>(); | 35 | var requiredActionTuples = new Dictionary<string, WixActionTuple>(); |
diff --git a/src/WixToolset.Core.WindowsInstaller/Bind/UpdateFileFacadesCommand.cs b/src/WixToolset.Core.WindowsInstaller/Bind/UpdateFileFacadesCommand.cs index 1f2a22d9..81d46b41 100644 --- a/src/WixToolset.Core.WindowsInstaller/Bind/UpdateFileFacadesCommand.cs +++ b/src/WixToolset.Core.WindowsInstaller/Bind/UpdateFileFacadesCommand.cs | |||
| @@ -59,27 +59,27 @@ namespace WixToolset.Core.WindowsInstaller.Bind | |||
| 59 | FileInfo fileInfo = null; | 59 | FileInfo fileInfo = null; |
| 60 | try | 60 | try |
| 61 | { | 61 | { |
| 62 | fileInfo = new FileInfo(facade.File.Source.Path); | 62 | fileInfo = new FileInfo(facade.SourcePath); |
| 63 | } | 63 | } |
| 64 | catch (ArgumentException) | 64 | catch (ArgumentException) |
| 65 | { | 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; | 67 | return; |
| 68 | } | 68 | } |
| 69 | catch (PathTooLongException) | 69 | catch (PathTooLongException) |
| 70 | { | 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; | 72 | return; |
| 73 | } | 73 | } |
| 74 | catch (NotSupportedException) | 74 | catch (NotSupportedException) |
| 75 | { | 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; | 77 | return; |
| 78 | } | 78 | } |
| 79 | 79 | ||
| 80 | if (!fileInfo.Exists) | 80 | if (!fileInfo.Exists) |
| 81 | { | 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; | 83 | return; |
| 84 | } | 84 | } |
| 85 | 85 | ||
| @@ -87,10 +87,10 @@ namespace WixToolset.Core.WindowsInstaller.Bind | |||
| 87 | { | 87 | { |
| 88 | if (Int32.MaxValue < fileStream.Length) | 88 | if (Int32.MaxValue < fileStream.Length) |
| 89 | { | 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 | } | 91 | } |
| 92 | 92 | ||
| 93 | facade.File.FileSize = Convert.ToInt32(fileStream.Length, CultureInfo.InvariantCulture); | 93 | facade.FileSize = Convert.ToInt32(fileStream.Length, CultureInfo.InvariantCulture); |
| 94 | } | 94 | } |
| 95 | 95 | ||
| 96 | string version = null; | 96 | string version = null; |
| @@ -103,7 +103,7 @@ namespace WixToolset.Core.WindowsInstaller.Bind | |||
| 103 | { | 103 | { |
| 104 | if (0x2 == e.NativeErrorCode) // ERROR_FILE_NOT_FOUND | 104 | if (0x2 == e.NativeErrorCode) // ERROR_FILE_NOT_FOUND |
| 105 | { | 105 | { |
| 106 | throw new WixException(ErrorMessages.FileNotFound(facade.File.SourceLineNumbers, fileInfo.FullName)); | 106 | throw new WixException(ErrorMessages.FileNotFound(facade.SourceLineNumber, fileInfo.FullName)); |
| 107 | } | 107 | } |
| 108 | else | 108 | else |
| 109 | { | 109 | { |
| @@ -118,7 +118,7 @@ namespace WixToolset.Core.WindowsInstaller.Bind | |||
| 118 | { | 118 | { |
| 119 | // not overwriting hash, so don't do the rest of these options. | 119 | // not overwriting hash, so don't do the rest of these options. |
| 120 | } | 120 | } |
| 121 | else if (null != facade.File.Version) | 121 | else if (null != facade.Version) |
| 122 | { | 122 | { |
| 123 | // Search all of the file rows available to see if the specified version is actually a companion file. Yes, this looks | 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. | 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 | // | 127 | // |
| 128 | // Also, if we do not find a matching file identifier then the user provided a default version and is providing a version | 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. | 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 | { | 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 | } | 133 | } |
| 134 | } | 134 | } |
| 135 | else | 135 | else |
| 136 | { | 136 | { |
| 137 | if (null != facade.File.Language) | 137 | if (null != facade.Language) |
| 138 | { | 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 | } | 140 | } |
| 141 | 141 | ||
| 142 | int[] hash; | 142 | int[] hash; |
| @@ -148,7 +148,7 @@ namespace WixToolset.Core.WindowsInstaller.Bind | |||
| 148 | { | 148 | { |
| 149 | if (0x2 == e.NativeErrorCode) // ERROR_FILE_NOT_FOUND | 149 | if (0x2 == e.NativeErrorCode) // ERROR_FILE_NOT_FOUND |
| 150 | { | 150 | { |
| 151 | throw new WixException(ErrorMessages.FileNotFound(facade.File.SourceLineNumbers, fileInfo.FullName)); | 151 | throw new WixException(ErrorMessages.FileNotFound(facade.SourceLineNumber, fileInfo.FullName)); |
| 152 | } | 152 | } |
| 153 | else | 153 | else |
| 154 | { | 154 | { |
| @@ -158,7 +158,7 @@ namespace WixToolset.Core.WindowsInstaller.Bind | |||
| 158 | 158 | ||
| 159 | if (null == facade.Hash) | 159 | if (null == facade.Hash) |
| 160 | { | 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); | 162 | this.Section.Tuples.Add(facade.Hash); |
| 163 | } | 163 | } |
| 164 | 164 | ||
| @@ -173,11 +173,11 @@ namespace WixToolset.Core.WindowsInstaller.Bind | |||
| 173 | { | 173 | { |
| 174 | // If no version was provided by the user, use the version from the file itself. | 174 | // If no version was provided by the user, use the version from the file itself. |
| 175 | // This is the most common case. | 175 | // This is the most common case. |
| 176 | if (String.IsNullOrEmpty(facade.File.Version)) | 176 | if (String.IsNullOrEmpty(facade.Version)) |
| 177 | { | 177 | { |
| 178 | facade.File.Version = version; | 178 | facade.Version = version; |
| 179 | } | 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 | { | 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 | 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 | 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 | // | 188 | // |
| 189 | // Also note this case can occur when the file is being updated using the WixBindUpdatedFiles extension mechanism. | 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. | 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 | } | 192 | } |
| 193 | 193 | ||
| 194 | if (!String.IsNullOrEmpty(facade.File.Language) && String.IsNullOrEmpty(language)) | 194 | if (!String.IsNullOrEmpty(facade.Language) && String.IsNullOrEmpty(language)) |
| 195 | { | 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 | } | 197 | } |
| 198 | else // override the default provided by the user (usually nothing) with the actual language from the file itself. | 198 | else // override the default provided by the user (usually nothing) with the actual language from the file itself. |
| 199 | { | 199 | { |
| 200 | facade.File.Language = language; | 200 | facade.Language = language; |
| 201 | } | 201 | } |
| 202 | 202 | ||
| 203 | // Populate the binder variables for this file information if requested. | 203 | // Populate the binder variables for this file information if requested. |
| 204 | if (null != this.VariableCache) | 204 | if (null != this.VariableCache) |
| 205 | { | 205 | { |
| 206 | if (!String.IsNullOrEmpty(facade.File.Version)) | 206 | if (!String.IsNullOrEmpty(facade.Version)) |
| 207 | { | 207 | { |
| 208 | var key = String.Format(CultureInfo.InvariantCulture, "fileversion.{0}", facade.File.Id.Id); | 208 | var key = String.Format(CultureInfo.InvariantCulture, "fileversion.{0}", facade.Id); |
| 209 | this.VariableCache[key] = facade.File.Version; | 209 | this.VariableCache[key] = facade.Version; |
| 210 | } | 210 | } |
| 211 | 211 | ||
| 212 | if (!String.IsNullOrEmpty(facade.File.Language)) | 212 | if (!String.IsNullOrEmpty(facade.Language)) |
| 213 | { | 213 | { |
| 214 | var key = String.Format(CultureInfo.InvariantCulture, "filelanguage.{0}", facade.File.Id.Id); | 214 | var key = String.Format(CultureInfo.InvariantCulture, "filelanguage.{0}", facade.Id); |
| 215 | this.VariableCache[key] = facade.File.Language; | 215 | this.VariableCache[key] = facade.Language; |
| 216 | } | 216 | } |
| 217 | } | 217 | } |
| 218 | } | 218 | } |
| 219 | 219 | ||
| 220 | // If this is a CLR assembly, load the assembly and get the assembly name information | 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 | { | 222 | { |
| 223 | try | 223 | try |
| 224 | { | 224 | { |
| 225 | var assemblyName = AssemblyNameReader.ReadAssembly(facade.File.SourceLineNumbers, fileInfo.FullName, version); | 225 | var assemblyName = AssemblyNameReader.ReadAssembly(facade.SourceLineNumber, fileInfo.FullName, version); |
| 226 | 226 | ||
| 227 | this.SetMsiAssemblyName(assemblyNameTuples, facade, "name", assemblyName.Name); | 227 | this.SetMsiAssemblyName(assemblyNameTuples, facade, "name", assemblyName.Name); |
| 228 | this.SetMsiAssemblyName(assemblyNameTuples, facade, "culture", assemblyName.Culture); | 228 | this.SetMsiAssemblyName(assemblyNameTuples, facade, "culture", assemblyName.Culture); |
| @@ -242,9 +242,9 @@ namespace WixToolset.Core.WindowsInstaller.Bind | |||
| 242 | { | 242 | { |
| 243 | this.SetMsiAssemblyName(assemblyNameTuples, facade, "publicKeyToken", assemblyName.PublicKeyToken); | 243 | this.SetMsiAssemblyName(assemblyNameTuples, facade, "publicKeyToken", assemblyName.PublicKeyToken); |
| 244 | } | 244 | } |
| 245 | else if (facade.Assembly.ApplicationFileRef == null) | 245 | else if (facade.AssemblyApplicationFileRef == null) |
| 246 | { | 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 | } | 248 | } |
| 249 | 249 | ||
| 250 | if (!String.IsNullOrEmpty(assemblyName.FileVersion)) | 250 | if (!String.IsNullOrEmpty(assemblyName.FileVersion)) |
| @@ -255,7 +255,7 @@ namespace WixToolset.Core.WindowsInstaller.Bind | |||
| 255 | // add the assembly name to the information cache | 255 | // add the assembly name to the information cache |
| 256 | if (null != this.VariableCache) | 256 | if (null != this.VariableCache) |
| 257 | { | 257 | { |
| 258 | this.VariableCache[$"assemblyfullname.{facade.File.Id.Id}"] = assemblyName.GetFullName(); | 258 | this.VariableCache[$"assemblyfullname.{facade.Id}"] = assemblyName.GetFullName(); |
| 259 | } | 259 | } |
| 260 | } | 260 | } |
| 261 | catch (WixException e) | 261 | catch (WixException e) |
| @@ -263,20 +263,20 @@ namespace WixToolset.Core.WindowsInstaller.Bind | |||
| 263 | this.Messaging.Write(e.Error); | 263 | this.Messaging.Write(e.Error); |
| 264 | } | 264 | } |
| 265 | } | 265 | } |
| 266 | else if (AssemblyType.Win32Assembly == facade.Assembly?.Type) | 266 | else if (AssemblyType.Win32Assembly == facade.AssemblyType) |
| 267 | { | 267 | { |
| 268 | // TODO: Consider passing in the this.FileFacades as an indexed collection instead of searching through | 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 | 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. | 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) | 272 | if (null == fileManifest) |
| 273 | { | 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 | } | 275 | } |
| 276 | 276 | ||
| 277 | try | 277 | try |
| 278 | { | 278 | { |
| 279 | var assemblyName = AssemblyNameReader.ReadAssemblyManifest(facade.File.SourceLineNumbers, fileManifest.File.Source.Path); | 279 | var assemblyName = AssemblyNameReader.ReadAssemblyManifest(facade.SourceLineNumber, fileManifest.SourcePath); |
| 280 | 280 | ||
| 281 | if (!String.IsNullOrEmpty(assemblyName.Name)) | 281 | if (!String.IsNullOrEmpty(assemblyName.Name)) |
| 282 | { | 282 | { |
| @@ -315,41 +315,41 @@ namespace WixToolset.Core.WindowsInstaller.Bind | |||
| 315 | /// create a new row. | 315 | /// create a new row. |
| 316 | /// </summary> | 316 | /// </summary> |
| 317 | /// <param name="assemblyNameTuples">MsiAssemblyName table.</param> | 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> | 319 | /// <param name="name">MsiAssemblyName name.</param> |
| 320 | /// <param name="value">MsiAssemblyName value.</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 | { | 322 | { |
| 323 | // check for null value (this can occur when grabbing the file version from an assembly without one) | 323 | // check for null value (this can occur when grabbing the file version from an assembly without one) |
| 324 | if (String.IsNullOrEmpty(value)) | 324 | if (String.IsNullOrEmpty(value)) |
| 325 | { | 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 | } | 327 | } |
| 328 | else | 328 | else |
| 329 | { | 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. | 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 && | 331 | if ("name" == name && AssemblyType.DotNetAssembly == facade.AssemblyType && |
| 332 | String.IsNullOrEmpty(file.Assembly.ApplicationFileRef) && | 332 | String.IsNullOrEmpty(facade.AssemblyApplicationFileRef) && |
| 333 | !String.Equals(Path.GetFileNameWithoutExtension(file.File.Name), value, StringComparison.OrdinalIgnoreCase)) | 333 | !String.Equals(Path.GetFileNameWithoutExtension(facade.FileName), value, StringComparison.OrdinalIgnoreCase)) |
| 334 | { | 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 | } | 336 | } |
| 337 | 337 | ||
| 338 | // override directly authored value | 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)) | 340 | if (!assemblyNameTuples.TryGetValue(lookup, out var assemblyNameRow)) |
| 341 | { | 341 | { |
| 342 | assemblyNameRow = new MsiAssemblyNameTuple(file.File.SourceLineNumbers); | 342 | assemblyNameRow = new MsiAssemblyNameTuple(facade.SourceLineNumber); |
| 343 | assemblyNameRow.ComponentRef = file.File.ComponentRef; | 343 | assemblyNameRow.ComponentRef = facade.ComponentRef; |
| 344 | assemblyNameRow.Name = name; | 344 | assemblyNameRow.Name = name; |
| 345 | assemblyNameRow.Value = value; | 345 | assemblyNameRow.Value = value; |
| 346 | 346 | ||
| 347 | if (null == file.AssemblyNames) | 347 | if (null == facade.AssemblyNames) |
| 348 | { | 348 | { |
| 349 | file.AssemblyNames = new List<MsiAssemblyNameTuple>(); | 349 | facade.AssemblyNames = new List<MsiAssemblyNameTuple>(); |
| 350 | } | 350 | } |
| 351 | 351 | ||
| 352 | file.AssemblyNames.Add(assemblyNameRow); | 352 | facade.AssemblyNames.Add(assemblyNameRow); |
| 353 | this.Section.Tuples.Add(assemblyNameRow); | 353 | this.Section.Tuples.Add(assemblyNameRow); |
| 354 | } | 354 | } |
| 355 | 355 | ||
| @@ -357,7 +357,7 @@ namespace WixToolset.Core.WindowsInstaller.Bind | |||
| 357 | 357 | ||
| 358 | if (this.VariableCache != null) | 358 | if (this.VariableCache != null) |
| 359 | { | 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; | 361 | this.VariableCache[key] = value; |
| 362 | } | 362 | } |
| 363 | } | 363 | } |
diff --git a/src/WixToolset.Core.WindowsInstaller/Bind/UpdateMediaSequencesCommand.cs b/src/WixToolset.Core.WindowsInstaller/Bind/UpdateMediaSequencesCommand.cs index f9e3bd5a..ae872f45 100644 --- a/src/WixToolset.Core.WindowsInstaller/Bind/UpdateMediaSequencesCommand.cs +++ b/src/WixToolset.Core.WindowsInstaller/Bind/UpdateMediaSequencesCommand.cs | |||
| @@ -33,7 +33,7 @@ namespace WixToolset.Core.WindowsInstaller.Bind | |||
| 33 | 33 | ||
| 34 | // Order by Component to group the files by directory. | 34 | // Order by Component to group the files by directory. |
| 35 | var optimized = this.OptimizedFileFacades(); | 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 | { | 37 | { |
| 38 | var fileRow = fileRows.Get(fileId); | 38 | var fileRow = fileRows.Get(fileId); |
| 39 | fileRow.Sequence = ++lastSequence; | 39 | fileRow.Sequence = ++lastSequence; |
| @@ -41,13 +41,13 @@ namespace WixToolset.Core.WindowsInstaller.Bind | |||
| 41 | } | 41 | } |
| 42 | else | 42 | else |
| 43 | { | 43 | { |
| 44 | int lastSequence = 0; | 44 | var lastSequence = 0; |
| 45 | MediaRow mediaRow = null; | 45 | MediaRow mediaRow = null; |
| 46 | Dictionary<int, List<FileFacade>> patchGroups = new Dictionary<int, List<FileFacade>>(); | 46 | var patchGroups = new Dictionary<int, List<FileFacade>>(); |
| 47 | 47 | ||
| 48 | // sequence the non-patch-added files | 48 | // sequence the non-patch-added files |
| 49 | var optimized = this.OptimizedFileFacades(); | 49 | var optimized = this.OptimizedFileFacades(); |
| 50 | foreach (FileFacade facade in optimized) | 50 | foreach (var facade in optimized) |
| 51 | { | 51 | { |
| 52 | if (null == mediaRow) | 52 | if (null == mediaRow) |
| 53 | { | 53 | { |
| @@ -64,19 +64,19 @@ namespace WixToolset.Core.WindowsInstaller.Bind | |||
| 64 | mediaRow = mediaRows.Get(facade.DiskId); | 64 | mediaRow = mediaRows.Get(facade.DiskId); |
| 65 | } | 65 | } |
| 66 | 66 | ||
| 67 | if (facade.File.PatchGroup.HasValue) | 67 | if (facade.PatchGroup.HasValue) |
| 68 | { | 68 | { |
| 69 | if (patchGroups.TryGetValue(facade.File.PatchGroup.Value, out var patchGroup)) | 69 | if (patchGroups.TryGetValue(facade.PatchGroup.Value, out var patchGroup)) |
| 70 | { | 70 | { |
| 71 | patchGroup = new List<FileFacade>(); | 71 | patchGroup = new List<FileFacade>(); |
| 72 | patchGroups.Add(facade.File.PatchGroup.Value, patchGroup); | 72 | patchGroups.Add(facade.PatchGroup.Value, patchGroup); |
| 73 | } | 73 | } |
| 74 | 74 | ||
| 75 | patchGroup.Add(facade); | 75 | patchGroup.Add(facade); |
| 76 | } | 76 | } |
| 77 | else | 77 | else |
| 78 | { | 78 | { |
| 79 | var fileRow = fileRows.Get(facade.File.Id.Id); | 79 | var fileRow = fileRows.Get(facade.Id); |
| 80 | fileRow.Sequence = ++lastSequence; | 80 | fileRow.Sequence = ++lastSequence; |
| 81 | } | 81 | } |
| 82 | } | 82 | } |
| @@ -102,7 +102,7 @@ namespace WixToolset.Core.WindowsInstaller.Bind | |||
| 102 | mediaRow = mediaRows.Get(facade.DiskId); | 102 | mediaRow = mediaRows.Get(facade.DiskId); |
| 103 | } | 103 | } |
| 104 | 104 | ||
| 105 | var fileRow = fileRows.Get(facade.File.Id.Id); | 105 | var fileRow = fileRows.Get(facade.Id); |
| 106 | fileRow.Sequence = ++lastSequence; | 106 | fileRow.Sequence = ++lastSequence; |
| 107 | } | 107 | } |
| 108 | } | 108 | } |
| @@ -119,7 +119,7 @@ namespace WixToolset.Core.WindowsInstaller.Bind | |||
| 119 | // TODO: Sort these facades even smarter by directory path and component id | 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 | 120 | // and maybe file size or file extension and other creative ideas to |
| 121 | // get optimal install speed out of MSI. | 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 | } | 123 | } |
| 124 | } | 124 | } |
| 125 | } | 125 | } |
diff --git a/src/WixToolset.Core.WindowsInstaller/Data/tables.xml b/src/WixToolset.Core.WindowsInstaller/Data/tables.xml index e4b5e954..7cd1767b 100644 --- a/src/WixToolset.Core.WindowsInstaller/Data/tables.xml +++ b/src/WixToolset.Core.WindowsInstaller/Data/tables.xml | |||
| @@ -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)"/> | 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" | 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."/> | 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."/> | ||
| 159 | </tableDefinition> | 163 | </tableDefinition> |
| 160 | <tableDefinition name="CCPSearch"> | 164 | <tableDefinition name="CCPSearch"> |
| 161 | <columnDefinition name="Signature_" type="string" length="72" primaryKey="yes" | 165 | <columnDefinition name="Signature_" type="string" length="72" primaryKey="yes" |
diff --git a/src/WixToolset.Core.WindowsInstaller/Differ.cs b/src/WixToolset.Core.WindowsInstaller/Differ.cs index 32172ffd..0e1a7315 100644 --- a/src/WixToolset.Core.WindowsInstaller/Differ.cs +++ b/src/WixToolset.Core.WindowsInstaller/Differ.cs | |||
| @@ -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. | 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 | 2 | ||
| 3 | #if DELETE | ||
| 4 | |||
| 3 | namespace WixToolset.Core.WindowsInstaller | 5 | namespace WixToolset.Core.WindowsInstaller |
| 4 | { | 6 | { |
| 5 | using System; | 7 | using System; |
| @@ -604,3 +606,5 @@ namespace WixToolset.Core.WindowsInstaller | |||
| 604 | } | 606 | } |
| 605 | } | 607 | } |
| 606 | } | 608 | } |
| 609 | |||
| 610 | #endif | ||
diff --git a/src/WixToolset.Core.WindowsInstaller/Inscribe/InscribeMsiPackageCommand.cs b/src/WixToolset.Core.WindowsInstaller/Inscribe/InscribeMsiPackageCommand.cs index ff7472ff..582e179e 100644 --- a/src/WixToolset.Core.WindowsInstaller/Inscribe/InscribeMsiPackageCommand.cs +++ b/src/WixToolset.Core.WindowsInstaller/Inscribe/InscribeMsiPackageCommand.cs | |||
| @@ -33,40 +33,40 @@ namespace WixToolset.Core.WindowsInstaller.Inscribe | |||
| 33 | public bool Execute() | 33 | public bool Execute() |
| 34 | { | 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 | 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; | 36 | var foundUnsignedExternals = false; |
| 37 | bool shouldCommit = false; | 37 | var shouldCommit = false; |
| 38 | 38 | ||
| 39 | FileAttributes attributes = File.GetAttributes(this.Context.InputFilePath); | 39 | var attributes = File.GetAttributes(this.Context.InputFilePath); |
| 40 | if (FileAttributes.ReadOnly == (attributes & FileAttributes.ReadOnly)) | 40 | if (FileAttributes.ReadOnly == (attributes & FileAttributes.ReadOnly)) |
| 41 | { | 41 | { |
| 42 | this.Messaging.Write(ErrorMessages.ReadOnlyOutputFile(this.Context.InputFilePath)); | 42 | this.Messaging.Write(ErrorMessages.ReadOnlyOutputFile(this.Context.InputFilePath)); |
| 43 | return shouldCommit; | 43 | return shouldCommit; |
| 44 | } | 44 | } |
| 45 | 45 | ||
| 46 | using (Database database = new Database(this.Context.InputFilePath, OpenDatabase.Transact)) | 46 | using (var database = new Database(this.Context.InputFilePath, OpenDatabase.Transact)) |
| 47 | { | 47 | { |
| 48 | // Just use the English codepage, because the tables we're importing only have binary streams / MSI identifiers / other non-localizable content | 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 | 50 | ||
| 51 | // list of certificates for this database (hash/identifier) | 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 | 53 | ||
| 54 | // Reset the in-memory tables for this new database | 54 | // Reset the in-memory tables for this new database |
| 55 | Table digitalSignatureTable = new Table(this.TableDefinitions["MsiDigitalSignature"]); | 55 | var digitalSignatureTable = new Table(this.TableDefinitions["MsiDigitalSignature"]); |
| 56 | Table digitalCertificateTable = new Table(this.TableDefinitions["MsiDigitalCertificate"]); | 56 | var digitalCertificateTable = new Table(this.TableDefinitions["MsiDigitalCertificate"]); |
| 57 | 57 | ||
| 58 | // If any digital signature records exist that are not of the media type, preserve them | 58 | // If any digital signature records exist that are not of the media type, preserve them |
| 59 | if (database.TableExists("MsiDigitalSignature")) | 59 | if (database.TableExists("MsiDigitalSignature")) |
| 60 | { | 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 | { | 62 | { |
| 63 | foreach (Record digitalSignatureRecord in digitalSignatureView.Records) | 63 | foreach (var digitalSignatureRecord in digitalSignatureView.Records) |
| 64 | { | 64 | { |
| 65 | Row digitalSignatureRow = null; | 65 | Row digitalSignatureRow = null; |
| 66 | digitalSignatureRow = digitalSignatureTable.CreateRow(null); | 66 | digitalSignatureRow = digitalSignatureTable.CreateRow(null); |
| 67 | 67 | ||
| 68 | string table = digitalSignatureRecord.GetString(0); | 68 | var table = digitalSignatureRecord.GetString(0); |
| 69 | string signObject = digitalSignatureRecord.GetString(1); | 69 | var signObject = digitalSignatureRecord.GetString(1); |
| 70 | 70 | ||
| 71 | digitalSignatureRow[0] = table; | 71 | digitalSignatureRow[0] = table; |
| 72 | digitalSignatureRow[1] = signObject; | 72 | digitalSignatureRow[1] = signObject; |
| @@ -75,16 +75,16 @@ namespace WixToolset.Core.WindowsInstaller.Inscribe | |||
| 75 | if (false == digitalSignatureRecord.IsNull(3)) | 75 | if (false == digitalSignatureRecord.IsNull(3)) |
| 76 | { | 76 | { |
| 77 | // Export to a file, because the MSI API's require us to provide a file path on disk | 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"); | 78 | var hashPath = Path.Combine(this.Context.IntermediateFolder, "MsiDigitalSignature"); |
| 79 | string hashFileName = string.Concat(table, ".", signObject, ".bin"); | 79 | var hashFileName = String.Concat(table, ".", signObject, ".bin"); |
| 80 | 80 | ||
| 81 | Directory.CreateDirectory(hashPath); | 81 | Directory.CreateDirectory(hashPath); |
| 82 | hashPath = Path.Combine(hashPath, hashFileName); | 82 | hashPath = Path.Combine(hashPath, hashFileName); |
| 83 | 83 | ||
| 84 | using (FileStream fs = File.Create(hashPath)) | 84 | using (var fs = File.Create(hashPath)) |
| 85 | { | 85 | { |
| 86 | int bytesRead; | 86 | int bytesRead; |
| 87 | byte[] buffer = new byte[1024 * 4]; | 87 | var buffer = new byte[1024 * 4]; |
| 88 | 88 | ||
| 89 | while (0 != (bytesRead = digitalSignatureRecord.GetStream(3, buffer, buffer.Length))) | 89 | while (0 != (bytesRead = digitalSignatureRecord.GetStream(3, buffer, buffer.Length))) |
| 90 | { | 90 | { |
| @@ -101,21 +101,21 @@ namespace WixToolset.Core.WindowsInstaller.Inscribe | |||
| 101 | // If any digital certificates exist, extract and preserve them | 101 | // If any digital certificates exist, extract and preserve them |
| 102 | if (database.TableExists("MsiDigitalCertificate")) | 102 | if (database.TableExists("MsiDigitalCertificate")) |
| 103 | { | 103 | { |
| 104 | using (View digitalCertificateView = database.OpenExecuteView("SELECT * FROM `MsiDigitalCertificate`")) | 104 | using (var digitalCertificateView = database.OpenExecuteView("SELECT * FROM `MsiDigitalCertificate`")) |
| 105 | { | 105 | { |
| 106 | foreach (Record digitalCertificateRecord in digitalCertificateView.Records) | 106 | foreach (var digitalCertificateRecord in digitalCertificateView.Records) |
| 107 | { | 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 | 109 | ||
| 110 | // Export to a file, because the MSI API's require us to provide a file path on disk | 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); | 112 | Directory.CreateDirectory(certPath); |
| 113 | certPath = Path.Combine(certPath, string.Concat(certificateId, ".cer")); | 113 | certPath = Path.Combine(certPath, String.Concat(certificateId, ".cer")); |
| 114 | 114 | ||
| 115 | using (FileStream fs = File.Create(certPath)) | 115 | using (var fs = File.Create(certPath)) |
| 116 | { | 116 | { |
| 117 | int bytesRead; | 117 | int bytesRead; |
| 118 | byte[] buffer = new byte[1024 * 4]; | 118 | var buffer = new byte[1024 * 4]; |
| 119 | 119 | ||
| 120 | while (0 != (bytesRead = digitalCertificateRecord.GetStream(2, buffer, buffer.Length))) | 120 | while (0 != (bytesRead = digitalCertificateRecord.GetStream(2, buffer, buffer.Length))) |
| 121 | { | 121 | { |
| @@ -124,37 +124,37 @@ namespace WixToolset.Core.WindowsInstaller.Inscribe | |||
| 124 | } | 124 | } |
| 125 | 125 | ||
| 126 | // Add it to our "add to MsiDigitalCertificate" table dictionary | 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; | 128 | digitalCertificateRow[0] = certificateId; |
| 129 | 129 | ||
| 130 | // Now set the file path on disk where this binary stream will be picked up at import time | 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 | 132 | ||
| 133 | // Load the cert to get it's thumbprint | 133 | // Load the cert to get it's thumbprint |
| 134 | X509Certificate cert = X509Certificate.CreateFromCertFile(certPath); | 134 | var cert = X509Certificate.CreateFromCertFile(certPath); |
| 135 | X509Certificate2 cert2 = new X509Certificate2(cert); | 135 | var cert2 = new X509Certificate2(cert); |
| 136 | 136 | ||
| 137 | certificates.Add(cert2.Thumbprint, certificateId); | 137 | certificates.Add(cert2.Thumbprint, certificateId); |
| 138 | } | 138 | } |
| 139 | } | 139 | } |
| 140 | } | 140 | } |
| 141 | 141 | ||
| 142 | using (View mediaView = database.OpenExecuteView("SELECT * FROM `Media`")) | 142 | using (var mediaView = database.OpenExecuteView("SELECT * FROM `Media`")) |
| 143 | { | 143 | { |
| 144 | foreach (Record mediaRecord in mediaView.Records) | 144 | foreach (var mediaRecord in mediaView.Records) |
| 145 | { | 145 | { |
| 146 | X509Certificate2 cert2 = null; | 146 | X509Certificate2 cert2 = null; |
| 147 | Row digitalSignatureRow = null; | 147 | Row digitalSignatureRow = null; |
| 148 | 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. | 150 | // If there is no cabinet or it's an internal cab, skip it. |
| 151 | if (String.IsNullOrEmpty(cabName) || cabName.StartsWith("#", StringComparison.Ordinal)) | 151 | if (String.IsNullOrEmpty(cabName) || cabName.StartsWith("#", StringComparison.Ordinal)) |
| 152 | { | 152 | { |
| 153 | continue; | 153 | continue; |
| 154 | } | 154 | } |
| 155 | 155 | ||
| 156 | string cabId = mediaRecord.GetString(1); // get the ID of the cab | 156 | var cabId = mediaRecord.GetString(1); // get the ID of the cab |
| 157 | string cabPath = Path.Combine(Path.GetDirectoryName(this.Context.InputFilePath), cabName); | 157 | var cabPath = Path.Combine(Path.GetDirectoryName(this.Context.InputFilePath), cabName); |
| 158 | 158 | ||
| 159 | // If the cabs aren't there, throw an error but continue to catch the other errors | 159 | // If the cabs aren't there, throw an error but continue to catch the other errors |
| 160 | if (!File.Exists(cabPath)) | 160 | if (!File.Exists(cabPath)) |
| @@ -166,12 +166,12 @@ namespace WixToolset.Core.WindowsInstaller.Inscribe | |||
| 166 | try | 166 | try |
| 167 | { | 167 | { |
| 168 | // Get the certificate from the cab | 168 | // Get the certificate from the cab |
| 169 | X509Certificate signedFileCert = X509Certificate.CreateFromSignedFile(cabPath); | 169 | var signedFileCert = X509Certificate.CreateFromSignedFile(cabPath); |
| 170 | cert2 = new X509Certificate2(signedFileCert); | 170 | cert2 = new X509Certificate2(signedFileCert); |
| 171 | } | 171 | } |
| 172 | catch (System.Security.Cryptography.CryptographicException e) | 172 | catch (System.Security.Cryptography.CryptographicException e) |
| 173 | { | 173 | { |
| 174 | uint HResult = unchecked((uint)Marshal.GetHRForException(e)); | 174 | var HResult = unchecked((uint)Marshal.GetHRForException(e)); |
| 175 | 175 | ||
| 176 | // If the file has no cert, continue, but flag that we found at least one so we can later give a warning | 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 | 177 | if (0x80092009 == HResult) // CRYPT_E_NO_MATCH |
| @@ -197,26 +197,26 @@ namespace WixToolset.Core.WindowsInstaller.Inscribe | |||
| 197 | if (!certificates.ContainsKey(cert2.Thumbprint)) | 197 | if (!certificates.ContainsKey(cert2.Thumbprint)) |
| 198 | { | 198 | { |
| 199 | // generate a stable identifier | 199 | // generate a stable identifier |
| 200 | string certificateGeneratedId = Common.GenerateIdentifier("cer", cert2.Thumbprint); | 200 | var certificateGeneratedId = Common.GenerateIdentifier("cer", cert2.Thumbprint); |
| 201 | 201 | ||
| 202 | // Add it to our "add to MsiDigitalCertificate" table dictionary | 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; | 204 | digitalCertificateRow[0] = certificateGeneratedId; |
| 205 | 205 | ||
| 206 | // Export to a file, because the MSI API's require us to provide a file path on disk | 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); | 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); | 210 | File.Delete(certPath); |
| 211 | 211 | ||
| 212 | using (BinaryWriter writer = new BinaryWriter(File.Open(certPath, FileMode.Create))) | 212 | using (var writer = new BinaryWriter(File.Open(certPath, FileMode.Create))) |
| 213 | { | 213 | { |
| 214 | writer.Write(cert2.RawData); | 214 | writer.Write(cert2.RawData); |
| 215 | writer.Close(); | 215 | writer.Close(); |
| 216 | } | 216 | } |
| 217 | 217 | ||
| 218 | // Now set the file path on disk where this binary stream will be picked up at import time | 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 | 220 | ||
| 221 | certificates.Add(cert2.Thumbprint, certificateGeneratedId); | 221 | certificates.Add(cert2.Thumbprint, certificateGeneratedId); |
| 222 | } | 222 | } |
diff --git a/src/WixToolset.Core.WindowsInstaller/MspBackend.cs b/src/WixToolset.Core.WindowsInstaller/MspBackend.cs index b6e72e11..8aa450bf 100644 --- a/src/WixToolset.Core.WindowsInstaller/MspBackend.cs +++ b/src/WixToolset.Core.WindowsInstaller/MspBackend.cs | |||
| @@ -3,36 +3,74 @@ | |||
| 3 | namespace WixToolset.Core.WindowsInstaller | 3 | namespace WixToolset.Core.WindowsInstaller |
| 4 | { | 4 | { |
| 5 | using System; | 5 | using System; |
| 6 | using System.ComponentModel; | 6 | using System.Collections.Generic; |
| 7 | using System.IO; | 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; | ||
| 9 | using WixToolset.Core.WindowsInstaller.Unbind; | 11 | using WixToolset.Core.WindowsInstaller.Unbind; |
| 10 | using WixToolset.Data; | 12 | using WixToolset.Data; |
| 11 | using WixToolset.Data.Bind; | 13 | using WixToolset.Data.Tuples; |
| 14 | using WixToolset.Data.WindowsInstaller; | ||
| 12 | using WixToolset.Extensibility; | 15 | using WixToolset.Extensibility; |
| 13 | using WixToolset.Extensibility.Data; | 16 | using WixToolset.Extensibility.Data; |
| 14 | using WixToolset.Ole32; | 17 | using WixToolset.Extensibility.Services; |
| 15 | 18 | ||
| 16 | internal class MspBackend : IBackend | 19 | internal class MspBackend : IBackend |
| 17 | { | 20 | { |
| 18 | public IBindResult Bind(IBindContext context) | 21 | public IBindResult Bind(IBindContext context) |
| 19 | { | 22 | { |
| 20 | throw new NotImplementedException(); | 23 | var messaging = context.ServiceProvider.GetService<IMessaging>(); |
| 21 | } | ||
| 22 | 24 | ||
| 23 | public IDecompileResult Decompile(IDecompileContext context) | 25 | var extensionManager = context.ServiceProvider.GetService<IExtensionManager>(); |
| 24 | { | ||
| 25 | throw new NotImplementedException(); | ||
| 26 | } | ||
| 27 | 26 | ||
| 28 | public bool Inscribe(IInscribeContext context) | 27 | var backendExtensions = extensionManager.GetServices<IWindowsInstallerBackendBinderExtension>(); |
| 29 | { | 28 | |
| 30 | throw new NotImplementedException(); | 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 | } | ||
| 31 | } | 65 | } |
| 32 | 66 | ||
| 67 | public IDecompileResult Decompile(IDecompileContext context) => throw new NotImplementedException(); | ||
| 68 | |||
| 69 | public bool Inscribe(IInscribeContext context) => throw new NotImplementedException(); | ||
| 70 | |||
| 33 | public Intermediate Unbind(IUnbindContext context) | 71 | public Intermediate Unbind(IUnbindContext context) |
| 34 | { | 72 | { |
| 35 | #if REVISIT_FOR_PATCHING | 73 | #if TODO_PATCHING |
| 36 | Output patch; | 74 | Output patch; |
| 37 | 75 | ||
| 38 | // patch files are essentially database files (use a special flag to let the API know its a patch file) | 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 | |||
| 116 | throw new NotImplementedException(); | 154 | throw new NotImplementedException(); |
| 117 | } | 155 | } |
| 118 | } | 156 | } |
| 119 | } \ No newline at end of file | 157 | } |
diff --git a/src/WixToolset.Core.WindowsInstaller/MstBackend.cs b/src/WixToolset.Core.WindowsInstaller/MstBackend.cs index b64f417a..a6d86c10 100644 --- a/src/WixToolset.Core.WindowsInstaller/MstBackend.cs +++ b/src/WixToolset.Core.WindowsInstaller/MstBackend.cs | |||
| @@ -12,7 +12,7 @@ namespace WixToolset.Core.WindowsInstaller | |||
| 12 | { | 12 | { |
| 13 | public IBindResult Bind(IBindContext context) | 13 | public IBindResult Bind(IBindContext context) |
| 14 | { | 14 | { |
| 15 | #if REVISIT_FOR_PATCHING | 15 | #if TODO_PATCHING |
| 16 | var command = new BindTransformCommand(); | 16 | var command = new BindTransformCommand(); |
| 17 | command.Extensions = context.Extensions; | 17 | command.Extensions = context.Extensions; |
| 18 | command.TempFilesLocation = context.IntermediateFolder; | 18 | command.TempFilesLocation = context.IntermediateFolder; |
diff --git a/src/WixToolset.Core.WindowsInstaller/Ole32/Storage.cs b/src/WixToolset.Core.WindowsInstaller/Ole32/Storage.cs index c6a43bc4..541d899a 100644 --- a/src/WixToolset.Core.WindowsInstaller/Ole32/Storage.cs +++ b/src/WixToolset.Core.WindowsInstaller/Ole32/Storage.cs | |||
| @@ -63,8 +63,8 @@ namespace WixToolset.Ole32 | |||
| 63 | /// </summary> | 63 | /// </summary> |
| 64 | internal sealed class Storage : IDisposable | 64 | internal sealed class Storage : IDisposable |
| 65 | { | 65 | { |
| 66 | private readonly IStorage storage; | ||
| 66 | private bool disposed; | 67 | private bool disposed; |
| 67 | private IStorage storage; | ||
| 68 | 68 | ||
| 69 | /// <summary> | 69 | /// <summary> |
| 70 | /// Instantiate a new Storage. | 70 | /// Instantiate a new Storage. |
diff --git a/src/WixToolset.Core.WindowsInstaller/Patch.cs b/src/WixToolset.Core.WindowsInstaller/Patch.cs index 6549e830..42cd7152 100644 --- a/src/WixToolset.Core.WindowsInstaller/Patch.cs +++ b/src/WixToolset.Core.WindowsInstaller/Patch.cs | |||
| @@ -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. | 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 | 2 | ||
| 3 | #if DELETE | ||
| 4 | |||
| 3 | namespace WixToolset.Data | 5 | namespace WixToolset.Data |
| 4 | { | 6 | { |
| 5 | using System; | 7 | using System; |
| @@ -50,7 +52,6 @@ namespace WixToolset.Data | |||
| 50 | [SuppressMessage("Microsoft.Globalization", "CA1303:DoNotPassLiteralsAsLocalizedParameters", MessageId = "System.InvalidOperationException.#ctor(System.String)")] | 52 | [SuppressMessage("Microsoft.Globalization", "CA1303:DoNotPassLiteralsAsLocalizedParameters", MessageId = "System.InvalidOperationException.#ctor(System.String)")] |
| 51 | public void AttachTransforms(List<PatchTransform> transforms) | 53 | public void AttachTransforms(List<PatchTransform> transforms) |
| 52 | { | 54 | { |
| 53 | #if REVISIT_FOR_PATCHING | ||
| 54 | // Track if at least one transform gets attached. | 55 | // Track if at least one transform gets attached. |
| 55 | bool attachedTransform = false; | 56 | bool attachedTransform = false; |
| 56 | 57 | ||
| @@ -1229,8 +1230,10 @@ namespace WixToolset.Data | |||
| 1229 | } | 1230 | } |
| 1230 | 1231 | ||
| 1231 | return pairedTransform; | 1232 | return pairedTransform; |
| 1232 | #endif | 1233 | |
| 1233 | throw new NotImplementedException(); | 1234 | throw new NotImplementedException(); |
| 1234 | } | 1235 | } |
| 1235 | } | 1236 | } |
| 1236 | } | 1237 | } |
| 1238 | |||
| 1239 | #endif | ||
diff --git a/src/WixToolset.Core.WindowsInstaller/PatchTransform.cs b/src/WixToolset.Core.WindowsInstaller/PatchTransform.cs index 0dc1e874..f58ca53f 100644 --- a/src/WixToolset.Core.WindowsInstaller/PatchTransform.cs +++ b/src/WixToolset.Core.WindowsInstaller/PatchTransform.cs | |||
| @@ -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. | 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 | 2 | ||
| 3 | #if DELETE | ||
| 4 | |||
| 3 | namespace WixToolset | 5 | namespace WixToolset |
| 4 | { | 6 | { |
| 5 | using System; | 7 | using System; |
| @@ -50,7 +52,6 @@ namespace WixToolset | |||
| 50 | /// </summary> | 52 | /// </summary> |
| 51 | public void Validate() | 53 | public void Validate() |
| 52 | { | 54 | { |
| 53 | #if REVISIT_FOR_PATCHING | ||
| 54 | // Changing the ProdocutCode in a patch transform is not recommended. | 55 | // Changing the ProdocutCode in a patch transform is not recommended. |
| 55 | Table propertyTable = this.Transform.Tables["Property"]; | 56 | Table propertyTable = this.Transform.Tables["Property"]; |
| 56 | if (null != propertyTable) | 57 | if (null != propertyTable) |
| @@ -261,8 +262,10 @@ namespace WixToolset | |||
| 261 | } | 262 | } |
| 262 | } | 263 | } |
| 263 | } | 264 | } |
| 264 | #endif | 265 | |
| 265 | throw new NotImplementedException(); | 266 | throw new NotImplementedException(); |
| 266 | } | 267 | } |
| 267 | } | 268 | } |
| 268 | } | 269 | } |
| 270 | |||
| 271 | #endif \ No newline at end of file | ||
diff --git a/src/WixToolset.Core.WindowsInstaller/Unbind/UnbindMsiOrMsmCommand.cs b/src/WixToolset.Core.WindowsInstaller/Unbind/UnbindMsiOrMsmCommand.cs index eca51caf..eea0fe23 100644 --- a/src/WixToolset.Core.WindowsInstaller/Unbind/UnbindMsiOrMsmCommand.cs +++ b/src/WixToolset.Core.WindowsInstaller/Unbind/UnbindMsiOrMsmCommand.cs | |||
| @@ -19,7 +19,7 @@ namespace WixToolset.Core.WindowsInstaller.Unbind | |||
| 19 | 19 | ||
| 20 | public Intermediate Execute() | 20 | public Intermediate Execute() |
| 21 | { | 21 | { |
| 22 | #if REVISIT_FOR_PATCHING | 22 | #if TODO_PATCHING |
| 23 | Output output; | 23 | Output output; |
| 24 | 24 | ||
| 25 | try | 25 | try |
diff --git a/src/WixToolset.Core.WindowsInstaller/Unbind/UnbindTranformCommand.cs b/src/WixToolset.Core.WindowsInstaller/Unbind/UnbindTranformCommand.cs index bdf8d542..9261fda0 100644 --- a/src/WixToolset.Core.WindowsInstaller/Unbind/UnbindTranformCommand.cs +++ b/src/WixToolset.Core.WindowsInstaller/Unbind/UnbindTranformCommand.cs | |||
| @@ -4,6 +4,7 @@ namespace WixToolset.Core.WindowsInstaller.Unbind | |||
| 4 | { | 4 | { |
| 5 | using System; | 5 | using System; |
| 6 | using System.Collections; | 6 | using System.Collections; |
| 7 | using System.Collections.Generic; | ||
| 7 | using System.ComponentModel; | 8 | using System.ComponentModel; |
| 8 | using System.Globalization; | 9 | using System.Globalization; |
| 9 | using System.IO; | 10 | using System.IO; |
| @@ -12,7 +13,6 @@ namespace WixToolset.Core.WindowsInstaller.Unbind | |||
| 12 | using WixToolset.Core.WindowsInstaller.Msi; | 13 | using WixToolset.Core.WindowsInstaller.Msi; |
| 13 | using WixToolset.Data; | 14 | using WixToolset.Data; |
| 14 | using WixToolset.Data.WindowsInstaller; | 15 | using WixToolset.Data.WindowsInstaller; |
| 15 | using WixToolset.Extensibility; | ||
| 16 | using WixToolset.Extensibility.Services; | 16 | using WixToolset.Extensibility.Services; |
| 17 | 17 | ||
| 18 | internal class UnbindTransformCommand | 18 | internal class UnbindTransformCommand |
| @@ -41,21 +41,21 @@ namespace WixToolset.Core.WindowsInstaller.Unbind | |||
| 41 | 41 | ||
| 42 | public WindowsInstallerData Execute() | 42 | public WindowsInstallerData Execute() |
| 43 | { | 43 | { |
| 44 | WindowsInstallerData transform = new WindowsInstallerData(new SourceLineNumber(this.TransformFile)); | 44 | var transform = new WindowsInstallerData(new SourceLineNumber(this.TransformFile)); |
| 45 | transform.Type = OutputType.Transform; | 45 | transform.Type = OutputType.Transform; |
| 46 | 46 | ||
| 47 | // get the summary information table | 47 | // get the summary information table |
| 48 | using (SummaryInformation summaryInformation = new SummaryInformation(this.TransformFile)) | 48 | using (var summaryInformation = new SummaryInformation(this.TransformFile)) |
| 49 | { | 49 | { |
| 50 | Table table = transform.EnsureTable(this.TableDefinitions["_SummaryInformation"]); | 50 | var table = transform.EnsureTable(this.TableDefinitions["_SummaryInformation"]); |
| 51 | 51 | ||
| 52 | for (int i = 1; 19 >= i; i++) | 52 | for (var i = 1; 19 >= i; i++) |
| 53 | { | 53 | { |
| 54 | string value = summaryInformation.GetProperty(i); | 54 | var value = summaryInformation.GetProperty(i); |
| 55 | 55 | ||
| 56 | if (0 < value.Length) | 56 | if (0 < value.Length) |
| 57 | { | 57 | { |
| 58 | Row row = table.CreateRow(transform.SourceLineNumbers); | 58 | var row = table.CreateRow(transform.SourceLineNumbers); |
| 59 | row[0] = i; | 59 | row[0] = i; |
| 60 | row[1] = value; | 60 | row[1] = value; |
| 61 | } | 61 | } |
| @@ -63,9 +63,9 @@ namespace WixToolset.Core.WindowsInstaller.Unbind | |||
| 63 | } | 63 | } |
| 64 | 64 | ||
| 65 | // create a schema msi which hopefully matches the table schemas in the transform | 65 | // create a schema msi which hopefully matches the table schemas in the transform |
| 66 | WindowsInstallerData schemaOutput = new WindowsInstallerData(null); | 66 | var schemaOutput = new WindowsInstallerData(null); |
| 67 | string msiDatabaseFile = Path.Combine(this.IntermediateFolder, "schema.msi"); | 67 | var msiDatabaseFile = Path.Combine(this.IntermediateFolder, "schema.msi"); |
| 68 | foreach (TableDefinition tableDefinition in this.TableDefinitions) | 68 | foreach (var tableDefinition in this.TableDefinitions) |
| 69 | { | 69 | { |
| 70 | // skip unreal tables and the Patch table | 70 | // skip unreal tables and the Patch table |
| 71 | if (!tableDefinition.Unreal && "Patch" != tableDefinition.Name) | 71 | if (!tableDefinition.Unreal && "Patch" != tableDefinition.Name) |
| @@ -74,40 +74,40 @@ namespace WixToolset.Core.WindowsInstaller.Unbind | |||
| 74 | } | 74 | } |
| 75 | } | 75 | } |
| 76 | 76 | ||
| 77 | Hashtable addedRows = new Hashtable(); | 77 | var addedRows = new Dictionary<string, Row>(); |
| 78 | Table transformViewTable; | 78 | Table transformViewTable; |
| 79 | 79 | ||
| 80 | // Bind the schema msi. | 80 | // Bind the schema msi. |
| 81 | this.GenerateDatabase(schemaOutput, msiDatabaseFile); | 81 | this.GenerateDatabase(schemaOutput, msiDatabaseFile); |
| 82 | 82 | ||
| 83 | // apply the transform to the database and retrieve the modifications | 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 | { | 85 | { |
| 86 | // apply the transform with the ViewTransform option to collect all the modifications | 86 | // apply the transform with the ViewTransform option to collect all the modifications |
| 87 | msiDatabase.ApplyTransform(this.TransformFile, TransformErrorConditions.All | TransformErrorConditions.ViewTransform); | 87 | msiDatabase.ApplyTransform(this.TransformFile, TransformErrorConditions.All | TransformErrorConditions.ViewTransform); |
| 88 | 88 | ||
| 89 | // unbind the database | 89 | // unbind the database |
| 90 | var unbindCommand = new UnbindDatabaseCommand(this.Messaging, msiDatabase, msiDatabaseFile, OutputType.Product, this.ExportBasePath, this.IntermediateFolder, false, false, skipSummaryInfo: true); | 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 | 92 | ||
| 93 | // index the added and possibly modified rows (added rows may also appears as modified rows) | 93 | // index the added and possibly modified rows (added rows may also appears as modified rows) |
| 94 | transformViewTable = transformViewOutput.Tables["_TransformView"]; | 94 | transformViewTable = transformViewOutput.Tables["_TransformView"]; |
| 95 | Hashtable modifiedRows = new Hashtable(); | 95 | var modifiedRows = new Hashtable(); |
| 96 | foreach (Row row in transformViewTable.Rows) | 96 | foreach (var row in transformViewTable.Rows) |
| 97 | { | 97 | { |
| 98 | string tableName = (string)row[0]; | 98 | var tableName = (string)row[0]; |
| 99 | string columnName = (string)row[1]; | 99 | var columnName = (string)row[1]; |
| 100 | string primaryKeys = (string)row[2]; | 100 | var primaryKeys = (string)row[2]; |
| 101 | 101 | ||
| 102 | if ("INSERT" == columnName) | 102 | if ("INSERT" == columnName) |
| 103 | { | 103 | { |
| 104 | string index = String.Concat(tableName, ':', primaryKeys); | 104 | var index = String.Concat(tableName, ':', primaryKeys); |
| 105 | 105 | ||
| 106 | addedRows.Add(index, null); | 106 | addedRows.Add(index, null); |
| 107 | } | 107 | } |
| 108 | else if ("CREATE" != columnName && "DELETE" != columnName && "DROP" != columnName && null != primaryKeys) // modified row | 108 | else if ("CREATE" != columnName && "DELETE" != columnName && "DROP" != columnName && null != primaryKeys) // modified row |
| 109 | { | 109 | { |
| 110 | string index = String.Concat(tableName, ':', primaryKeys); | 110 | var index = String.Concat(tableName, ':', primaryKeys); |
| 111 | 111 | ||
| 112 | modifiedRows[index] = row; | 112 | modifiedRows[index] = row; |
| 113 | } | 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 | 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) | 117 | foreach (Row row in modifiedRows.Values) |
| 118 | { | 118 | { |
| 119 | string tableName = (string)row[0]; | 119 | var tableName = (string)row[0]; |
| 120 | string columnName = (string)row[1]; | 120 | var columnName = (string)row[1]; |
| 121 | string primaryKeys = (string)row[2]; | 121 | var primaryKeys = (string)row[2]; |
| 122 | 122 | ||
| 123 | string index = String.Concat(tableName, ':', primaryKeys); | 123 | var index = String.Concat(tableName, ':', primaryKeys); |
| 124 | 124 | ||
| 125 | // ignore information for added rows | 125 | // ignore information for added rows |
| 126 | if (!addedRows.Contains(index)) | 126 | if (!addedRows.ContainsKey(index)) |
| 127 | { | 127 | { |
| 128 | Table table = schemaOutput.Tables[tableName]; | 128 | var table = schemaOutput.Tables[tableName]; |
| 129 | this.CreateRow(table, primaryKeys, true); | 129 | this.CreateRow(table, primaryKeys, true); |
| 130 | } | 130 | } |
| 131 | } | 131 | } |
| @@ -135,7 +135,7 @@ namespace WixToolset.Core.WindowsInstaller.Unbind | |||
| 135 | this.GenerateDatabase(schemaOutput, msiDatabaseFile); | 135 | this.GenerateDatabase(schemaOutput, msiDatabaseFile); |
| 136 | 136 | ||
| 137 | // apply the transform to the database and retrieve the modifications | 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 | { | 139 | { |
| 140 | try | 140 | try |
| 141 | { | 141 | { |
| @@ -158,26 +158,26 @@ namespace WixToolset.Core.WindowsInstaller.Unbind | |||
| 158 | 158 | ||
| 159 | // unbind the database | 159 | // unbind the database |
| 160 | var unbindCommand = new UnbindDatabaseCommand(this.Messaging, msiDatabase, msiDatabaseFile, OutputType.Product, this.ExportBasePath, this.IntermediateFolder, false, false, skipSummaryInfo: true); | 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 | 162 | ||
| 163 | // index all the rows to easily find modified rows | 163 | // index all the rows to easily find modified rows |
| 164 | Hashtable rows = new Hashtable(); | 164 | var rows = new Dictionary<string, Row>(); |
| 165 | foreach (Table table in output.Tables) | 165 | foreach (var table in output.Tables) |
| 166 | { | 166 | { |
| 167 | foreach (Row row in table.Rows) | 167 | foreach (var row in table.Rows) |
| 168 | { | 168 | { |
| 169 | rows.Add(String.Concat(table.Name, ':', row.GetPrimaryKey('\t', " ")), row); | 169 | rows.Add(String.Concat(table.Name, ':', row.GetPrimaryKey('\t', " ")), row); |
| 170 | } | 170 | } |
| 171 | } | 171 | } |
| 172 | 172 | ||
| 173 | // process the _TransformView rows into transform rows | 173 | // process the _TransformView rows into transform rows |
| 174 | foreach (Row row in transformViewTable.Rows) | 174 | foreach (var row in transformViewTable.Rows) |
| 175 | { | 175 | { |
| 176 | string tableName = (string)row[0]; | 176 | var tableName = (string)row[0]; |
| 177 | string columnName = (string)row[1]; | 177 | var columnName = (string)row[1]; |
| 178 | string primaryKeys = (string)row[2]; | 178 | var primaryKeys = (string)row[2]; |
| 179 | 179 | ||
| 180 | Table table = transform.EnsureTable(this.TableDefinitions[tableName]); | 180 | var table = transform.EnsureTable(this.TableDefinitions[tableName]); |
| 181 | 181 | ||
| 182 | if ("CREATE" == columnName) // added table | 182 | if ("CREATE" == columnName) // added table |
| 183 | { | 183 | { |
| @@ -185,7 +185,7 @@ namespace WixToolset.Core.WindowsInstaller.Unbind | |||
| 185 | } | 185 | } |
| 186 | else if ("DELETE" == columnName) // deleted row | 186 | else if ("DELETE" == columnName) // deleted row |
| 187 | { | 187 | { |
| 188 | Row deletedRow = this.CreateRow(table, primaryKeys, false); | 188 | var deletedRow = this.CreateRow(table, primaryKeys, false); |
| 189 | deletedRow.Operation = RowOperation.Delete; | 189 | deletedRow.Operation = RowOperation.Delete; |
| 190 | } | 190 | } |
| 191 | else if ("DROP" == columnName) // dropped table | 191 | else if ("DROP" == columnName) // dropped table |
| @@ -194,24 +194,24 @@ namespace WixToolset.Core.WindowsInstaller.Unbind | |||
| 194 | } | 194 | } |
| 195 | else if ("INSERT" == columnName) // added row | 195 | else if ("INSERT" == columnName) // added row |
| 196 | { | 196 | { |
| 197 | string index = String.Concat(tableName, ':', primaryKeys); | 197 | var index = String.Concat(tableName, ':', primaryKeys); |
| 198 | Row addedRow = (Row)rows[index]; | 198 | var addedRow = rows[index]; |
| 199 | addedRow.Operation = RowOperation.Add; | 199 | addedRow.Operation = RowOperation.Add; |
| 200 | table.Rows.Add(addedRow); | 200 | table.Rows.Add(addedRow); |
| 201 | } | 201 | } |
| 202 | else if (null != primaryKeys) // modified row | 202 | else if (null != primaryKeys) // modified row |
| 203 | { | 203 | { |
| 204 | string index = String.Concat(tableName, ':', primaryKeys); | 204 | var index = String.Concat(tableName, ':', primaryKeys); |
| 205 | 205 | ||
| 206 | // the _TransformView table includes information for added rows | 206 | // the _TransformView table includes information for added rows |
| 207 | // that looks like modified rows so it sometimes needs to be ignored | 207 | // that looks like modified rows so it sometimes needs to be ignored |
| 208 | if (!addedRows.Contains(index)) | 208 | if (!addedRows.ContainsKey(index)) |
| 209 | { | 209 | { |
| 210 | Row modifiedRow = (Row)rows[index]; | 210 | var modifiedRow = rows[index]; |
| 211 | 211 | ||
| 212 | // mark the field as modified | 212 | // mark the field as modified |
| 213 | int indexOfModifiedValue = -1; | 213 | var indexOfModifiedValue = -1; |
| 214 | for (int i = 0; i < modifiedRow.TableDefinition.Columns.Length; ++i) | 214 | for (var i = 0; i < modifiedRow.TableDefinition.Columns.Length; ++i) |
| 215 | { | 215 | { |
| 216 | if (columnName.Equals(modifiedRow.TableDefinition.Columns[i].Name, StringComparison.Ordinal)) | 216 | if (columnName.Equals(modifiedRow.TableDefinition.Columns[i].Name, StringComparison.Ordinal)) |
| 217 | { | 217 | { |
| @@ -231,7 +231,7 @@ namespace WixToolset.Core.WindowsInstaller.Unbind | |||
| 231 | } | 231 | } |
| 232 | else // added column | 232 | else // added column |
| 233 | { | 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; | 235 | column.Added = true; |
| 236 | } | 236 | } |
| 237 | } | 237 | } |
| @@ -240,21 +240,6 @@ namespace WixToolset.Core.WindowsInstaller.Unbind | |||
| 240 | return transform; | 240 | return transform; |
| 241 | } | 241 | } |
| 242 | 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 | |||
| 258 | /// <summary> | 243 | /// <summary> |
| 259 | /// Create a deleted or modified row. | 244 | /// Create a deleted or modified row. |
| 260 | /// </summary> | 245 | /// </summary> |
| @@ -264,14 +249,14 @@ namespace WixToolset.Core.WindowsInstaller.Unbind | |||
| 264 | /// <returns>The new row.</returns> | 249 | /// <returns>The new row.</returns> |
| 265 | private Row CreateRow(Table table, string primaryKeys, bool setRequiredFields) | 250 | private Row CreateRow(Table table, string primaryKeys, bool setRequiredFields) |
| 266 | { | 251 | { |
| 267 | Row row = table.CreateRow(null); | 252 | var row = table.CreateRow(null); |
| 268 | 253 | ||
| 269 | string[] primaryKeyParts = primaryKeys.Split('\t'); | 254 | var primaryKeyParts = primaryKeys.Split('\t'); |
| 270 | int primaryKeyPartIndex = 0; | 255 | var primaryKeyPartIndex = 0; |
| 271 | 256 | ||
| 272 | for (int i = 0; i < table.Definition.Columns.Length; i++) | 257 | for (var i = 0; i < table.Definition.Columns.Length; i++) |
| 273 | { | 258 | { |
| 274 | ColumnDefinition columnDefinition = table.Definition.Columns[i]; | 259 | var columnDefinition = table.Definition.Columns[i]; |
| 275 | 260 | ||
| 276 | if (columnDefinition.PrimaryKey) | 261 | if (columnDefinition.PrimaryKey) |
| 277 | { | 262 | { |
| @@ -294,8 +279,8 @@ namespace WixToolset.Core.WindowsInstaller.Unbind | |||
| 294 | { | 279 | { |
| 295 | if (null == this.EmptyFile) | 280 | if (null == this.EmptyFile) |
| 296 | { | 281 | { |
| 297 | this.EmptyFile = Path.GetTempFileName() + ".empty"; | 282 | this.EmptyFile = Path.Combine(this.IntermediateFolder, ".empty"); |
| 298 | using (FileStream fileStream = File.Create(this.EmptyFile)) | 283 | using (var fileStream = File.Create(this.EmptyFile)) |
| 299 | { | 284 | { |
| 300 | } | 285 | } |
| 301 | } | 286 | } |
| @@ -311,5 +296,11 @@ namespace WixToolset.Core.WindowsInstaller.Unbind | |||
| 311 | 296 | ||
| 312 | return row; | 297 | return row; |
| 313 | } | 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 | } | ||
| 314 | } | 305 | } |
| 315 | } | 306 | } |
diff --git a/src/WixToolset.Core.WindowsInstaller/WindowsInstallerBackendFactory.cs b/src/WixToolset.Core.WindowsInstaller/WindowsInstallerBackendFactory.cs index 173404d7..f9cf4492 100644 --- a/src/WixToolset.Core.WindowsInstaller/WindowsInstallerBackendFactory.cs +++ b/src/WixToolset.Core.WindowsInstaller/WindowsInstallerBackendFactory.cs | |||
| @@ -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 | 2 | ||
| 3 | namespace WixToolset.Core.WindowsInstaller | 3 | namespace WixToolset.Core.WindowsInstaller |
| 4 | { | 4 | { |
| 5 | using System; | 5 | using System; |
| 6 | using System.IO; | 6 | using System.IO; |
| 7 | using WixToolset.Extensibility; | 7 | using WixToolset.Extensibility; |
| 8 | using WixToolset.Extensibility.Data; | ||
| 9 | 8 | ||
| 10 | internal class WindowsInstallerBackendFactory : IBackendFactory | 9 | internal class WindowsInstallerBackendFactory : IBackendFactory |
| 11 | { | 10 | { |
| @@ -16,7 +15,7 @@ namespace WixToolset.Core.WindowsInstaller | |||
| 16 | outputType = Path.GetExtension(outputFile); | 15 | outputType = Path.GetExtension(outputFile); |
| 17 | } | 16 | } |
| 18 | 17 | ||
| 19 | switch (outputType.ToLowerInvariant()) | 18 | switch (outputType?.ToLowerInvariant()) |
| 20 | { | 19 | { |
| 21 | case "module": | 20 | case "module": |
| 22 | case ".msm": | 21 | case ".msm": |
diff --git a/src/WixToolset.Core/Bind/FileFacade.cs b/src/WixToolset.Core/Bind/FileFacade.cs index d631a3b5..7bfdb9bb 100644 --- a/src/WixToolset.Core/Bind/FileFacade.cs +++ b/src/WixToolset.Core/Bind/FileFacade.cs | |||
| @@ -2,32 +2,146 @@ | |||
| 2 | 2 | ||
| 3 | namespace WixToolset.Core.Bind | 3 | namespace WixToolset.Core.Bind |
| 4 | { | 4 | { |
| 5 | using System; | ||
| 5 | using System.Collections.Generic; | 6 | using System.Collections.Generic; |
| 7 | using WixToolset.Data; | ||
| 6 | using WixToolset.Data.Tuples; | 8 | using WixToolset.Data.Tuples; |
| 9 | using WixToolset.Data.WindowsInstaller; | ||
| 10 | using WixToolset.Data.WindowsInstaller.Rows; | ||
| 7 | 11 | ||
| 8 | public class FileFacade | 12 | public class FileFacade |
| 9 | { | 13 | { |
| 10 | public FileFacade(FileTuple file, AssemblyTuple assembly) | 14 | public FileFacade(FileTuple file, AssemblyTuple assembly) |
| 11 | { | 15 | { |
| 12 | this.File = file; | 16 | this.FileTuple = file; |
| 13 | this.Assembly = assembly; | 17 | this.AssemblyTuple = assembly; |
| 14 | } | 18 | } |
| 15 | 19 | ||
| 16 | public FileFacade(bool fromModule, FileTuple file) | 20 | public FileFacade(bool fromModule, FileTuple file) |
| 17 | { | 21 | { |
| 18 | this.FromModule = fromModule; | 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; | ||
| 20 | } | 30 | } |
| 21 | 31 | ||
| 22 | public bool FromModule { get; } | 32 | public bool FromModule { get; } |
| 23 | 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 | } | ||
| 25 | 139 | ||
| 26 | public AssemblyTuple Assembly { get; } | 140 | public AssemblyType? AssemblyType => this.FileRow == null ? this.AssemblyTuple?.Type : throw new NotImplementedException(); |
| 27 | 141 | ||
| 28 | public int DiskId => this.File.DiskId ?? 0; | 142 | public string AssemblyApplicationFileRef => this.FileRow == null ? this.AssemblyTuple?.ApplicationFileRef : throw new NotImplementedException(); |
| 29 | 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(); |
| 31 | 145 | ||
| 32 | /// <summary> | 146 | /// <summary> |
| 33 | /// Gets the set of MsiAssemblyName rows created for this file. | 147 | /// Gets the set of MsiAssemblyName rows created for this file. |
diff --git a/src/WixToolset.Core/Bind/ResolveFieldsCommand.cs b/src/WixToolset.Core/Bind/ResolveFieldsCommand.cs index 19a26915..5cb2524d 100644 --- a/src/WixToolset.Core/Bind/ResolveFieldsCommand.cs +++ b/src/WixToolset.Core/Bind/ResolveFieldsCommand.cs | |||
| @@ -89,7 +89,7 @@ namespace WixToolset.Core.Bind | |||
| 89 | { | 89 | { |
| 90 | var objectField = field.AsPath(); | 90 | var objectField = field.AsPath(); |
| 91 | 91 | ||
| 92 | #if REVISIT_FOR_PATCHING | 92 | #if TODO_PATCHING |
| 93 | // Skip file resolution if the file is to be deleted. | 93 | // Skip file resolution if the file is to be deleted. |
| 94 | if (RowOperation.Delete == tuple.Operation) | 94 | if (RowOperation.Delete == tuple.Operation) |
| 95 | { | 95 | { |
| @@ -111,7 +111,7 @@ namespace WixToolset.Core.Bind | |||
| 111 | { | 111 | { |
| 112 | if (!this.BuildingPatch) // Normal binding for non-Patch scenario such as link (light.exe) | 112 | if (!this.BuildingPatch) // Normal binding for non-Patch scenario such as link (light.exe) |
| 113 | { | 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 | 115 | // keep a copy of the un-resolved data for future replay. This will be saved into wixpdb file |
| 116 | if (null == objectField.UnresolvedData) | 116 | if (null == objectField.UnresolvedData) |
| 117 | { | 117 | { |
| @@ -129,7 +129,7 @@ namespace WixToolset.Core.Bind | |||
| 129 | var value = fileResolver.ResolveFile(objectField.Path, tuple.Definition, tuple.SourceLineNumbers, BindStage.Normal); | 129 | var value = fileResolver.ResolveFile(objectField.Path, tuple.Definition, tuple.SourceLineNumbers, BindStage.Normal); |
| 130 | field.Set(value); | 130 | field.Set(value); |
| 131 | } | 131 | } |
| 132 | #if REVISIT_FOR_PATCHING | 132 | #if TODO_PATCHING |
| 133 | else // Re-base binding path scenario caused by pyro.exe -bt -bu | 133 | else // Re-base binding path scenario caused by pyro.exe -bt -bu |
| 134 | { | 134 | { |
| 135 | // by default, use the resolved Data for file lookup | 135 | // by default, use the resolved Data for file lookup |
| @@ -158,7 +158,7 @@ namespace WixToolset.Core.Bind | |||
| 158 | } | 158 | } |
| 159 | } | 159 | } |
| 160 | 160 | ||
| 161 | #if REVISIT_FOR_PATCHING | 161 | #if TODO_PATCHING |
| 162 | if (null != objectField.PreviousData) | 162 | if (null != objectField.PreviousData) |
| 163 | { | 163 | { |
| 164 | objectField.PreviousData = this.BindVariableResolver.ResolveVariables(tuple.SourceLineNumbers, objectField.PreviousData, false, out isDefault); | 164 | objectField.PreviousData = this.BindVariableResolver.ResolveVariables(tuple.SourceLineNumbers, objectField.PreviousData, false, out isDefault); |
diff --git a/src/WixToolset.Core/Compiler.cs b/src/WixToolset.Core/Compiler.cs index 0c3d4c9c..cee64911 100644 --- a/src/WixToolset.Core/Compiler.cs +++ b/src/WixToolset.Core/Compiler.cs | |||
| @@ -7878,7 +7878,7 @@ namespace WixToolset.Core | |||
| 7878 | if (patch) | 7878 | if (patch) |
| 7879 | { | 7879 | { |
| 7880 | // /Patch/PatchProperty goes directly into MsiPatchMetadata table | 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 | { | 7882 | { |
| 7883 | Company = company, | 7883 | Company = company, |
| 7884 | Property = name, | 7884 | Property = name, |
| @@ -8130,12 +8130,15 @@ namespace WixToolset.Core | |||
| 8130 | /// </summary> | 8130 | /// </summary> |
| 8131 | /// <param name="node">The element to parse.</param> | 8131 | /// <param name="node">The element to parse.</param> |
| 8132 | /// <param name="diskId">Media index from parent element.</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 | { | 8134 | { |
| 8135 | var sourceLineNumbers = Preprocessor.GetSourceLineNumbers(node); | 8135 | var sourceLineNumbers = Preprocessor.GetSourceLineNumbers(node); |
| 8136 | Identifier id = null; | 8136 | Identifier id = null; |
| 8137 | var parsedValidate = false; | 8137 | var parsedValidate = false; |
| 8138 | var validationFlags = TransformFlags.PatchTransformDefault; | 8138 | var validationFlags = TransformFlags.PatchTransformDefault; |
| 8139 | string baselineFile = null; | ||
| 8140 | string updateFile = null; | ||
| 8141 | string transformFile = null; | ||
| 8139 | 8142 | ||
| 8140 | foreach (var attrib in node.Attributes()) | 8143 | foreach (var attrib in node.Attributes()) |
| 8141 | { | 8144 | { |
| @@ -8146,6 +8149,18 @@ namespace WixToolset.Core | |||
| 8146 | case "Id": | 8149 | case "Id": |
| 8147 | id = this.Core.GetAttributeIdentifier(sourceLineNumbers, attrib); | 8150 | id = this.Core.GetAttributeIdentifier(sourceLineNumbers, attrib); |
| 8148 | break; | 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; | ||
| 8149 | default: | 8164 | default: |
| 8150 | this.Core.UnexpectedAttribute(node, attrib); | 8165 | this.Core.UnexpectedAttribute(node, attrib); |
| 8151 | break; | 8166 | break; |
| @@ -8167,6 +8182,28 @@ namespace WixToolset.Core | |||
| 8167 | this.Core.Write(ErrorMessages.IdentifierTooLongError(sourceLineNumbers, node.Name.LocalName, "Id", id.Id, 27)); | 8182 | this.Core.Write(ErrorMessages.IdentifierTooLongError(sourceLineNumbers, node.Name.LocalName, "Id", id.Id, 27)); |
| 8168 | } | 8183 | } |
| 8169 | 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 | |||
| 8170 | foreach (var child in node.Elements()) | 8207 | foreach (var child in node.Elements()) |
| 8171 | { | 8208 | { |
| 8172 | if (CompilerCore.WixNamespace == child.Name.Namespace) | 8209 | if (CompilerCore.WixNamespace == child.Name.Namespace) |
| @@ -8200,8 +8237,11 @@ namespace WixToolset.Core | |||
| 8200 | { | 8237 | { |
| 8201 | var tuple = new WixPatchBaselineTuple(sourceLineNumbers, id) | 8238 | var tuple = new WixPatchBaselineTuple(sourceLineNumbers, id) |
| 8202 | { | 8239 | { |
| 8203 | DiskId = diskId, | 8240 | DiskId = diskId ?? 1, |
| 8204 | ValidationFlags = validationFlags | 8241 | ValidationFlags = validationFlags, |
| 8242 | BaselineFile = new IntermediateFieldPathValue { Path = baselineFile }, | ||
| 8243 | UpdateFile = new IntermediateFieldPathValue { Path = updateFile }, | ||
| 8244 | TransformFile = new IntermediateFieldPathValue { Path = transformFile } | ||
| 8205 | }; | 8245 | }; |
| 8206 | 8246 | ||
| 8207 | this.Core.AddTuple(tuple); | 8247 | this.Core.AddTuple(tuple); |
diff --git a/src/WixToolset.Core/Compiler_2.cs b/src/WixToolset.Core/Compiler_2.cs index 4b6839f1..89d4b6da 100644 --- a/src/WixToolset.Core/Compiler_2.cs +++ b/src/WixToolset.Core/Compiler_2.cs | |||
| @@ -1030,12 +1030,6 @@ namespace WixToolset.Core | |||
| 1030 | 1030 | ||
| 1031 | this.Core.AddTuple(new SummaryInformationTuple(sourceLineNumbers) | 1031 | this.Core.AddTuple(new SummaryInformationTuple(sourceLineNumbers) |
| 1032 | { | 1032 | { |
| 1033 | PropertyId = SumaryInformationType.WindowsInstallerVersion, | ||
| 1034 | Value = msiVersion.ToString(CultureInfo.InvariantCulture) | ||
| 1035 | }); | ||
| 1036 | |||
| 1037 | this.Core.AddTuple(new SummaryInformationTuple(sourceLineNumbers) | ||
| 1038 | { | ||
| 1039 | PropertyId = SumaryInformationType.Security, | 1033 | PropertyId = SumaryInformationType.Security, |
| 1040 | Value = YesNoDefaultType.No == security ? "0" : YesNoDefaultType.Yes == security ? "4" : "2" | 1034 | Value = YesNoDefaultType.No == security ? "0" : YesNoDefaultType.Yes == security ? "4" : "2" |
| 1041 | }); | 1035 | }); |
diff --git a/src/WixToolset.Core/Compiler_Patch.cs b/src/WixToolset.Core/Compiler_Patch.cs index 42951543..f8d05132 100644 --- a/src/WixToolset.Core/Compiler_Patch.cs +++ b/src/WixToolset.Core/Compiler_Patch.cs | |||
| @@ -197,9 +197,8 @@ namespace WixToolset.Core | |||
| 197 | 197 | ||
| 198 | if (!this.Core.EncounteredError) | 198 | if (!this.Core.EncounteredError) |
| 199 | { | 199 | { |
| 200 | var tuple = new WixPatchIdTuple(sourceLineNumbers) | 200 | var tuple = new WixPatchIdTuple(sourceLineNumbers, new Identifier(AccessModifier.Public, patchId)) |
| 201 | { | 201 | { |
| 202 | ProductCode = patchId, | ||
| 203 | ClientPatchId = clientPatchId, | 202 | ClientPatchId = clientPatchId, |
| 204 | OptimizePatchSizeForLargeFiles = optimizePatchSizeForLargeFiles, | 203 | OptimizePatchSizeForLargeFiles = optimizePatchSizeForLargeFiles, |
| 205 | ApiPatchingSymbolFlags = apiPatchingSymbolFlags | 204 | ApiPatchingSymbolFlags = apiPatchingSymbolFlags |
diff --git a/src/WixToolset.Core/Compiler_UI.cs b/src/WixToolset.Core/Compiler_UI.cs index 30bb7ab6..8a425fd4 100644 --- a/src/WixToolset.Core/Compiler_UI.cs +++ b/src/WixToolset.Core/Compiler_UI.cs | |||
| @@ -470,7 +470,7 @@ namespace WixToolset.Core | |||
| 470 | string defaultControl = null; | 470 | string defaultControl = null; |
| 471 | string cancelControl = null; | 471 | string cancelControl = null; |
| 472 | 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; | 474 | break; |
| 475 | default: | 475 | default: |
| 476 | this.Core.UnexpectedElement(node, child); | 476 | this.Core.UnexpectedElement(node, child); |
| @@ -966,7 +966,7 @@ namespace WixToolset.Core | |||
| 966 | switch (child.Name.LocalName) | 966 | switch (child.Name.LocalName) |
| 967 | { | 967 | { |
| 968 | case "Control": | 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; | 970 | break; |
| 971 | default: | 971 | default: |
| 972 | this.Core.UnexpectedElement(node, child); | 972 | this.Core.UnexpectedElement(node, child); |
| @@ -1032,7 +1032,7 @@ namespace WixToolset.Core | |||
| 1032 | /// <param name="defaultControl">Name of the default control.</param> | 1032 | /// <param name="defaultControl">Name of the default control.</param> |
| 1033 | /// <param name="cancelControl">Name of the candle 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> | 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 | { | 1036 | { |
| 1037 | var sourceLineNumbers = Preprocessor.GetSourceLineNumbers(node); | 1037 | var sourceLineNumbers = Preprocessor.GetSourceLineNumbers(node); |
| 1038 | Identifier controlId = null; | 1038 | Identifier controlId = null; |
diff --git a/src/WixToolset.Core/Resolver.cs b/src/WixToolset.Core/Resolver.cs index cbae3742..54bde848 100644 --- a/src/WixToolset.Core/Resolver.cs +++ b/src/WixToolset.Core/Resolver.cs | |||
| @@ -92,7 +92,7 @@ namespace WixToolset.Core | |||
| 92 | delayedFields = command.DelayedFields; | 92 | delayedFields = command.DelayedFields; |
| 93 | } | 93 | } |
| 94 | 94 | ||
| 95 | #if REVISIT_FOR_PATCHING | 95 | #if TODO_PATCHING |
| 96 | if (context.IntermediateRepresentation.SubStorages != null) | 96 | if (context.IntermediateRepresentation.SubStorages != null) |
| 97 | { | 97 | { |
| 98 | foreach (SubStorage transform in context.IntermediateRepresentation.SubStorages) | 98 | foreach (SubStorage transform in context.IntermediateRepresentation.SubStorages) |
diff --git a/src/test/WixToolsetTest.CoreIntegration/PatchFixture.cs b/src/test/WixToolsetTest.CoreIntegration/PatchFixture.cs new file mode 100644 index 00000000..584f86fe --- /dev/null +++ b/src/test/WixToolsetTest.CoreIntegration/PatchFixture.cs | |||
| @@ -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 | } | ||
diff --git a/src/test/WixToolsetTest.CoreIntegration/PreprocessorFixture.cs b/src/test/WixToolsetTest.CoreIntegration/PreprocessorFixture.cs index 4e48cbe1..b1a4c607 100644 --- a/src/test/WixToolsetTest.CoreIntegration/PreprocessorFixture.cs +++ b/src/test/WixToolsetTest.CoreIntegration/PreprocessorFixture.cs | |||
| @@ -68,34 +68,6 @@ namespace WixToolsetTest.CoreIntegration | |||
| 68 | } | 68 | } |
| 69 | 69 | ||
| 70 | [Fact] | 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 | |||
| 98 | [Fact] | ||
| 99 | public void ForEachLoopsWork() | 71 | public void ForEachLoopsWork() |
| 100 | { | 72 | { |
| 101 | var folder = TestData.Get(@"TestData\ForEach"); | 73 | var folder = TestData.Get(@"TestData\ForEach"); |
diff --git a/src/test/WixToolsetTest.CoreIntegration/TestData/PatchFamilyFilter/.data/Av1.0.0.txt b/src/test/WixToolsetTest.CoreIntegration/TestData/PatchFamilyFilter/.data/Av1.0.0.txt new file mode 100644 index 00000000..6fd385bd --- /dev/null +++ b/src/test/WixToolsetTest.CoreIntegration/TestData/PatchFamilyFilter/.data/Av1.0.0.txt | |||
| @@ -0,0 +1 @@ | |||
| This is A v1.0.0 | |||
diff --git a/src/test/WixToolsetTest.CoreIntegration/TestData/PatchFamilyFilter/.data/Av1.0.1.txt b/src/test/WixToolsetTest.CoreIntegration/TestData/PatchFamilyFilter/.data/Av1.0.1.txt new file mode 100644 index 00000000..b1f0bc01 --- /dev/null +++ b/src/test/WixToolsetTest.CoreIntegration/TestData/PatchFamilyFilter/.data/Av1.0.1.txt | |||
| @@ -0,0 +1 @@ | |||
| This ia A v1.0.1 | |||
diff --git a/src/test/WixToolsetTest.CoreIntegration/TestData/PatchFamilyFilter/.data/Bv1.0.0.txt b/src/test/WixToolsetTest.CoreIntegration/TestData/PatchFamilyFilter/.data/Bv1.0.0.txt new file mode 100644 index 00000000..ece55fec --- /dev/null +++ b/src/test/WixToolsetTest.CoreIntegration/TestData/PatchFamilyFilter/.data/Bv1.0.0.txt | |||
| @@ -0,0 +1 @@ | |||
| This is B v1.0.0 | |||
diff --git a/src/test/WixToolsetTest.CoreIntegration/TestData/PatchFamilyFilter/.data/Bv1.0.1.txt b/src/test/WixToolsetTest.CoreIntegration/TestData/PatchFamilyFilter/.data/Bv1.0.1.txt new file mode 100644 index 00000000..cf3372fd --- /dev/null +++ b/src/test/WixToolsetTest.CoreIntegration/TestData/PatchFamilyFilter/.data/Bv1.0.1.txt | |||
| @@ -0,0 +1 @@ | |||
| This ia B v1.0.1 | |||
diff --git a/src/test/WixToolsetTest.CoreIntegration/TestData/PatchFamilyFilter/Package.wxs b/src/test/WixToolsetTest.CoreIntegration/TestData/PatchFamilyFilter/Package.wxs new file mode 100644 index 00000000..2657797f --- /dev/null +++ b/src/test/WixToolsetTest.CoreIntegration/TestData/PatchFamilyFilter/Package.wxs | |||
| @@ -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> | ||
diff --git a/src/test/WixToolsetTest.CoreIntegration/TestData/PatchFamilyFilter/Patch.wxs b/src/test/WixToolsetTest.CoreIntegration/TestData/PatchFamilyFilter/Patch.wxs new file mode 100644 index 00000000..7c3cff7e --- /dev/null +++ b/src/test/WixToolsetTest.CoreIntegration/TestData/PatchFamilyFilter/Patch.wxs | |||
| @@ -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> | ||
diff --git a/src/test/WixToolsetTest.CoreIntegration/TestData/PatchSingle/.data/Av1.0.0.txt b/src/test/WixToolsetTest.CoreIntegration/TestData/PatchSingle/.data/Av1.0.0.txt new file mode 100644 index 00000000..6fd385bd --- /dev/null +++ b/src/test/WixToolsetTest.CoreIntegration/TestData/PatchSingle/.data/Av1.0.0.txt | |||
| @@ -0,0 +1 @@ | |||
| This is A v1.0.0 | |||
diff --git a/src/test/WixToolsetTest.CoreIntegration/TestData/PatchSingle/.data/Av1.0.1.txt b/src/test/WixToolsetTest.CoreIntegration/TestData/PatchSingle/.data/Av1.0.1.txt new file mode 100644 index 00000000..b1f0bc01 --- /dev/null +++ b/src/test/WixToolsetTest.CoreIntegration/TestData/PatchSingle/.data/Av1.0.1.txt | |||
| @@ -0,0 +1 @@ | |||
| This ia A v1.0.1 | |||
diff --git a/src/test/WixToolsetTest.CoreIntegration/TestData/PatchSingle/.data/Bv1.0.0.txt b/src/test/WixToolsetTest.CoreIntegration/TestData/PatchSingle/.data/Bv1.0.0.txt new file mode 100644 index 00000000..ece55fec --- /dev/null +++ b/src/test/WixToolsetTest.CoreIntegration/TestData/PatchSingle/.data/Bv1.0.0.txt | |||
| @@ -0,0 +1 @@ | |||
| This is B v1.0.0 | |||
diff --git a/src/test/WixToolsetTest.CoreIntegration/TestData/PatchSingle/.data/Bv1.0.1.txt b/src/test/WixToolsetTest.CoreIntegration/TestData/PatchSingle/.data/Bv1.0.1.txt new file mode 100644 index 00000000..cf3372fd --- /dev/null +++ b/src/test/WixToolsetTest.CoreIntegration/TestData/PatchSingle/.data/Bv1.0.1.txt | |||
| @@ -0,0 +1 @@ | |||
| This ia B v1.0.1 | |||
diff --git a/src/test/WixToolsetTest.CoreIntegration/TestData/PatchSingle/Package.wxs b/src/test/WixToolsetTest.CoreIntegration/TestData/PatchSingle/Package.wxs new file mode 100644 index 00000000..ee133ba3 --- /dev/null +++ b/src/test/WixToolsetTest.CoreIntegration/TestData/PatchSingle/Package.wxs | |||
| @@ -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> | ||
diff --git a/src/test/WixToolsetTest.CoreIntegration/TestData/PatchSingle/Patch.wxs b/src/test/WixToolsetTest.CoreIntegration/TestData/PatchSingle/Patch.wxs new file mode 100644 index 00000000..52e87f64 --- /dev/null +++ b/src/test/WixToolsetTest.CoreIntegration/TestData/PatchSingle/Patch.wxs | |||
| @@ -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> | ||
diff --git a/src/test/WixToolsetTest.CoreIntegration/WixToolsetTest.CoreIntegration.csproj b/src/test/WixToolsetTest.CoreIntegration/WixToolsetTest.CoreIntegration.csproj index 0330adf6..b17a27ff 100644 --- a/src/test/WixToolsetTest.CoreIntegration/WixToolsetTest.CoreIntegration.csproj +++ b/src/test/WixToolsetTest.CoreIntegration/WixToolsetTest.CoreIntegration.csproj | |||
| @@ -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. --> | 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 | 3 | ||
| 4 | <Project Sdk="Microsoft.NET.Sdk"> | 4 | <Project Sdk="Microsoft.NET.Sdk"> |
| @@ -41,6 +41,18 @@ | |||
| 41 | <Content Include="TestData\Font\TrueType.wxs" CopyToOutputDirectory="PreserveNewest" /> | 41 | <Content Include="TestData\Font\TrueType.wxs" CopyToOutputDirectory="PreserveNewest" /> |
| 42 | <Content Include="TestData\Icon\SampleIcon.wxs" CopyToOutputDirectory="PreserveNewest" /> | 42 | <Content Include="TestData\Icon\SampleIcon.wxs" CopyToOutputDirectory="PreserveNewest" /> |
| 43 | <Content Include="TestData\LockPermissions\EmptyPermissions.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" /> | ||
| 44 | <Content Include="TestData\SameFileFolders\data\a\test.txt" CopyToOutputDirectory="PreserveNewest" /> | 56 | <Content Include="TestData\SameFileFolders\data\a\test.txt" CopyToOutputDirectory="PreserveNewest" /> |
| 45 | <Content Include="TestData\SameFileFolders\data\c\test.txt" CopyToOutputDirectory="PreserveNewest" /> | 57 | <Content Include="TestData\SameFileFolders\data\c\test.txt" CopyToOutputDirectory="PreserveNewest" /> |
| 46 | <Content Include="TestData\SameFileFolders\data\b\test.txt" CopyToOutputDirectory="PreserveNewest" /> | 58 | <Content Include="TestData\SameFileFolders\data\b\test.txt" CopyToOutputDirectory="PreserveNewest" /> |
