| 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.Linq; |
| 8 | using WixToolset.Core.Native.Msi; |
| 9 | using WixToolset.Data; |
| 10 | using WixToolset.Data.Symbols; |
| 11 | using WixToolset.Data.WindowsInstaller; |
| 12 | using WixToolset.Data.WindowsInstaller.Rows; |
| 13 | using WixToolset.Extensibility.Services; |
| 14 | |
| 15 | internal class CreateInstanceTransformsCommand |
| 16 | { |
| 17 | public CreateInstanceTransformsCommand(IntermediateSection section, WindowsInstallerData output, TableDefinitionCollection tableDefinitions, IBackendHelper backendHelper) |
| 18 | { |
| 19 | this.Section = section; |
| 20 | this.Output = output; |
| 21 | this.TableDefinitions = tableDefinitions; |
| 22 | this.BackendHelper = backendHelper; |
| 23 | } |
| 24 | |
| 25 | private IntermediateSection Section { get; } |
| 26 | |
| 27 | private WindowsInstallerData Output { get; } |
| 28 | |
| 29 | public TableDefinitionCollection TableDefinitions { get; } |
| 30 | |
| 31 | private IBackendHelper BackendHelper { get; } |
| 32 | |
| 33 | public IReadOnlyCollection<SubStorage> SubStorages { get; private set; } |
| 34 | |
| 35 | public IReadOnlyCollection<SubStorage> Execute() |
| 36 | { |
| 37 | var subStorages = new List<SubStorage>(); |
| 38 | |
| 39 | // Create and add substorages for instance transforms. |
| 40 | var wixInstanceTransformsSymbols = this.Section.Symbols.OfType<WixInstanceTransformsSymbol>(); |
| 41 | |
| 42 | if (wixInstanceTransformsSymbols.Any()) |
| 43 | { |
| 44 | var targetSummaryInformationTable = this.Output.Tables["_SummaryInformation"]; |
| 45 | var targetPropertyRows = new RowDictionary<PropertyRow>(this.Output.Tables["Property"]); |
| 46 | PropertyRow propertyRow = null; |
| 47 | |
| 48 | // Get the data from target database |
| 49 | var targetProductCode = targetPropertyRows.TryGetValue("ProductCode", out propertyRow) ? propertyRow.Value : null; |
| 50 | var targetProductVersion = targetPropertyRows.TryGetValue("ProductVersion", out propertyRow) ? propertyRow.Value : null; |
| 51 | var targetUpgradeCode = targetPropertyRows.TryGetValue("UpgradeCode", out propertyRow) ? propertyRow.Value : null; |
| 52 | |
| 53 | // Index the Instance Component Rows, we'll get the Components rows from the real Component table. |
| 54 | var instanceComponentSymbols = this.Section.Symbols.OfType<WixInstanceComponentSymbol>().ToDictionary(t => t.Id.Id, t => (ComponentRow)null); |
| 55 | |
| 56 | if (instanceComponentSymbols.Any()) |
| 57 | { |
| 58 | var targetComponentTable = this.Output.Tables["Component"]; |
| 59 | foreach (ComponentRow componentRow in targetComponentTable.Rows) |
| 60 | { |
| 61 | var component = (string)componentRow[0]; |
| 62 | if (instanceComponentSymbols.ContainsKey(component)) |
| 63 | { |
| 64 | instanceComponentSymbols[component] = componentRow; |
| 65 | } |
| 66 | } |
| 67 | } |
| 68 | |
| 69 | // Generate the instance transforms |
| 70 | foreach (var instanceSymbol in wixInstanceTransformsSymbols) |
| 71 | { |
| 72 | var instanceId = instanceSymbol.Id.Id; |
| 73 | |
| 74 | var instanceTransform = new WindowsInstallerData(instanceSymbol.SourceLineNumbers); |
| 75 | instanceTransform.Type = OutputType.Transform; |
| 76 | instanceTransform.Codepage = this.Output.Codepage; |
| 77 | |
| 78 | var instanceSummaryInformationTable = instanceTransform.EnsureTable(this.TableDefinitions["_SummaryInformation"]); |
| 79 | string targetPlatformAndLanguage = null; |
| 80 | |
| 81 | foreach (var summaryInformationRow in targetSummaryInformationTable.Rows) |
| 82 | { |
| 83 | if (7 == (int)summaryInformationRow[0]) // PID_TEMPLATE |
| 84 | { |
| 85 | targetPlatformAndLanguage = (string)summaryInformationRow[1]; |
| 86 | } |
| 87 | |
| 88 | // Copy the row's data to the transform. |
| 89 | var copyOfSummaryRow = instanceSummaryInformationTable.CreateRow(summaryInformationRow.SourceLineNumbers); |
| 90 | copyOfSummaryRow[0] = summaryInformationRow[0]; |
| 91 | copyOfSummaryRow[1] = summaryInformationRow[1]; |
| 92 | } |
| 93 | |
| 94 | // Modify the appropriate properties. |
| 95 | var propertyTable = instanceTransform.EnsureTable(this.TableDefinitions["Property"]); |
| 96 | |
| 97 | // Change the ProductCode property |
| 98 | var productCode = instanceSymbol.ProductCode; |
| 99 | if ("*" == productCode) |
| 100 | { |
| 101 | productCode = this.BackendHelper.CreateGuid(); |
| 102 | } |
| 103 | |
| 104 | var productCodeRow = propertyTable.CreateRow(instanceSymbol.SourceLineNumbers); |
| 105 | productCodeRow.Operation = RowOperation.Modify; |
| 106 | productCodeRow.Fields[1].Modified = true; |
| 107 | productCodeRow[0] = "ProductCode"; |
| 108 | productCodeRow[1] = productCode; |
| 109 | |
| 110 | // Set the instance property. If the property exists in the MSI already, mark the row as modified in the transform. |
| 111 | // Otherwise, we need to mark the row as an add. |
| 112 | var instanceIdRow = propertyTable.CreateRow(instanceSymbol.SourceLineNumbers); |
| 113 | instanceIdRow.Operation = targetPropertyRows.ContainsKey(instanceSymbol.PropertyId) ? RowOperation.Modify : RowOperation.Add; |
| 114 | instanceIdRow.Fields[1].Modified = true; |
| 115 | instanceIdRow[0] = instanceSymbol.PropertyId; |
| 116 | instanceIdRow[1] = instanceId; |
| 117 | |
| 118 | if (!String.IsNullOrEmpty(instanceSymbol.ProductName)) |
| 119 | { |
| 120 | // Change the ProductName property |
| 121 | var productNameRow = propertyTable.CreateRow(instanceSymbol.SourceLineNumbers); |
| 122 | productNameRow.Operation = RowOperation.Modify; |
| 123 | productNameRow.Fields[1].Modified = true; |
| 124 | productNameRow[0] = "ProductName"; |
| 125 | productNameRow[1] = instanceSymbol.ProductName; |
| 126 | } |
| 127 | |
| 128 | if (!String.IsNullOrEmpty(instanceSymbol.UpgradeCode)) |
| 129 | { |
| 130 | // Change the UpgradeCode property |
| 131 | var upgradeCodeRow = propertyTable.CreateRow(instanceSymbol.SourceLineNumbers); |
| 132 | upgradeCodeRow.Operation = RowOperation.Modify; |
| 133 | upgradeCodeRow.Fields[1].Modified = true; |
| 134 | upgradeCodeRow[0] = "UpgradeCode"; |
| 135 | upgradeCodeRow[1] = instanceSymbol.UpgradeCode; |
| 136 | |
| 137 | // Change the Upgrade table |
| 138 | var targetUpgradeTable = this.Output.Tables["Upgrade"]; |
| 139 | if (null != targetUpgradeTable && 0 <= targetUpgradeTable.Rows.Count) |
| 140 | { |
| 141 | var upgradeId = instanceSymbol.UpgradeCode; |
| 142 | var upgradeTable = instanceTransform.EnsureTable(this.TableDefinitions["Upgrade"]); |
| 143 | foreach (var row in targetUpgradeTable.Rows) |
| 144 | { |
| 145 | // In case they are upgrading other codes to this new product, leave the ones that don't match the |
| 146 | // Product.UpgradeCode intact. |
| 147 | if (targetUpgradeCode == (string)row[0]) |
| 148 | { |
| 149 | var upgradeRow = upgradeTable.CreateRow(row.SourceLineNumbers); |
| 150 | upgradeRow.Operation = RowOperation.Add; |
| 151 | upgradeRow.Fields[0].Modified = true; |
| 152 | // I was hoping to be able to RowOperation.Modify, but that didn't appear to function. |
| 153 | // upgradeRow.Fields[0].PreviousData = (string)row[0]; |
| 154 | |
| 155 | // Inserting a new Upgrade record with the updated UpgradeCode |
| 156 | upgradeRow[0] = upgradeId; |
| 157 | upgradeRow[1] = row[1]; |
| 158 | upgradeRow[2] = row[2]; |
| 159 | upgradeRow[3] = row[3]; |
| 160 | upgradeRow[4] = row[4]; |
| 161 | upgradeRow[5] = row[5]; |
| 162 | upgradeRow[6] = row[6]; |
| 163 | |
| 164 | // Delete the old row |
| 165 | var upgradeRemoveRow = upgradeTable.CreateRow(row.SourceLineNumbers); |
| 166 | upgradeRemoveRow.Operation = RowOperation.Delete; |
| 167 | upgradeRemoveRow[0] = row[0]; |
| 168 | upgradeRemoveRow[1] = row[1]; |
| 169 | upgradeRemoveRow[2] = row[2]; |
| 170 | upgradeRemoveRow[3] = row[3]; |
| 171 | upgradeRemoveRow[4] = row[4]; |
| 172 | upgradeRemoveRow[5] = row[5]; |
| 173 | upgradeRemoveRow[6] = row[6]; |
| 174 | } |
| 175 | } |
| 176 | } |
| 177 | } |
| 178 | |
| 179 | // If there are instance Components generate new GUIDs for them. |
| 180 | if (0 < instanceComponentSymbols.Count) |
| 181 | { |
| 182 | var componentTable = instanceTransform.EnsureTable(this.TableDefinitions["Component"]); |
| 183 | foreach (var targetComponentRow in instanceComponentSymbols.Values) |
| 184 | { |
| 185 | var guid = targetComponentRow.Guid; |
| 186 | if (!String.IsNullOrEmpty(guid)) |
| 187 | { |
| 188 | var instanceComponentRow = componentTable.CreateRow(targetComponentRow.SourceLineNumbers); |
| 189 | instanceComponentRow.Operation = RowOperation.Modify; |
| 190 | instanceComponentRow.Fields[1].Modified = true; |
| 191 | instanceComponentRow[0] = targetComponentRow[0]; |
| 192 | instanceComponentRow[1] = this.BackendHelper.CreateGuid(BindDatabaseCommand.WixComponentGuidNamespace, String.Concat(guid, instanceId)); |
| 193 | instanceComponentRow[2] = targetComponentRow[2]; |
| 194 | instanceComponentRow[3] = targetComponentRow[3]; |
| 195 | instanceComponentRow[4] = targetComponentRow[4]; |
| 196 | instanceComponentRow[5] = targetComponentRow[5]; |
| 197 | } |
| 198 | } |
| 199 | } |
| 200 | |
| 201 | // Update the summary information |
| 202 | var summaryRows = new Dictionary<int, Row>(instanceSummaryInformationTable.Rows.Count); |
| 203 | foreach (var row in instanceSummaryInformationTable.Rows) |
| 204 | { |
| 205 | summaryRows[(int)row[0]] = row; |
| 206 | |
| 207 | if ((int)SummaryInformation.Transform.UpdatedPlatformAndLanguage == (int)row[0]) |
| 208 | { |
| 209 | row[1] = targetPlatformAndLanguage; |
| 210 | } |
| 211 | else if ((int)SummaryInformation.Transform.ProductCodes == (int)row[0]) |
| 212 | { |
| 213 | row[1] = String.Concat(targetProductCode, targetProductVersion, ';', productCode, targetProductVersion, ';', targetUpgradeCode); |
| 214 | } |
| 215 | else if ((int)SummaryInformation.Transform.ValidationFlags == (int)row[0]) |
| 216 | { |
| 217 | row[1] = 0; |
| 218 | } |
| 219 | else if ((int)SummaryInformation.Transform.Security == (int)row[0]) |
| 220 | { |
| 221 | row[1] = "4"; |
| 222 | } |
| 223 | } |
| 224 | |
| 225 | if (!summaryRows.ContainsKey((int)SummaryInformation.Transform.UpdatedPlatformAndLanguage)) |
| 226 | { |
| 227 | var summaryRow = instanceSummaryInformationTable.CreateRow(instanceSymbol.SourceLineNumbers); |
| 228 | summaryRow[0] = (int)SummaryInformation.Transform.UpdatedPlatformAndLanguage; |
| 229 | summaryRow[1] = targetPlatformAndLanguage; |
| 230 | } |
| 231 | else if (!summaryRows.ContainsKey((int)SummaryInformation.Transform.ValidationFlags)) |
| 232 | { |
| 233 | var summaryRow = instanceSummaryInformationTable.CreateRow(instanceSymbol.SourceLineNumbers); |
| 234 | summaryRow[0] = (int)SummaryInformation.Transform.ValidationFlags; |
| 235 | summaryRow[1] = "0"; |
| 236 | } |
| 237 | else if (!summaryRows.ContainsKey((int)SummaryInformation.Transform.Security)) |
| 238 | { |
| 239 | var summaryRow = instanceSummaryInformationTable.CreateRow(instanceSymbol.SourceLineNumbers); |
| 240 | summaryRow[0] = (int)SummaryInformation.Transform.Security; |
| 241 | summaryRow[1] = "4"; |
| 242 | } |
| 243 | |
| 244 | subStorages.Add(new SubStorage(instanceId, instanceTransform)); |
| 245 | } |
| 246 | } |
| 247 | |
| 248 | return this.SubStorages = subStorages; |
| 249 | } |
| 250 | } |
| 251 | } |