aboutsummaryrefslogtreecommitdiff
path: root/src
diff options
context:
space:
mode:
Diffstat (limited to 'src')
-rw-r--r--src/WixToolset.Core.WindowsInstaller/Bind/AssignMediaCommand.cs187
-rw-r--r--src/WixToolset.Core.WindowsInstaller/Bind/BindDatabaseCommand.cs739
-rw-r--r--src/WixToolset.Core.WindowsInstaller/Bind/BindSummaryInfoCommand.cs178
-rw-r--r--src/WixToolset.Core.WindowsInstaller/Bind/BindTransformCommand.cs2
-rw-r--r--src/WixToolset.Core.WindowsInstaller/Bind/CabinetBuilder.cs2
-rw-r--r--src/WixToolset.Core.WindowsInstaller/Bind/CabinetWorkItem.cs3
-rw-r--r--src/WixToolset.Core.WindowsInstaller/Bind/CalculateComponentGuids.cs174
-rw-r--r--src/WixToolset.Core.WindowsInstaller/Bind/ConfigurationCallback.cs2
-rw-r--r--src/WixToolset.Core.WindowsInstaller/Bind/CopyTransformDataCommand.cs7
-rw-r--r--src/WixToolset.Core.WindowsInstaller/Bind/CreateCabinetsCommand.cs24
-rw-r--r--src/WixToolset.Core.WindowsInstaller/Bind/CreateDeltaPatchesCommand.cs44
-rw-r--r--src/WixToolset.Core.WindowsInstaller/Bind/CreateOutputFromIRCommand.cs230
-rw-r--r--src/WixToolset.Core.WindowsInstaller/Bind/CreateSpecialPropertiesCommand.cs87
-rw-r--r--src/WixToolset.Core.WindowsInstaller/Bind/ExtractMergeModuleFilesCommand.cs51
-rw-r--r--src/WixToolset.Core.WindowsInstaller/Bind/GenerateDatabaseCommand.cs2
-rw-r--r--src/WixToolset.Core.WindowsInstaller/Bind/GetFileFacadesCommand.cs61
-rw-r--r--src/WixToolset.Core.WindowsInstaller/Bind/MergeModulesCommand.cs32
-rw-r--r--src/WixToolset.Core.WindowsInstaller/Bind/ProcessUncompressedFilesCommand.cs36
-rw-r--r--src/WixToolset.Core.WindowsInstaller/Bind/SequenceActionsCommand.cs671
-rw-r--r--src/WixToolset.Core.WindowsInstaller/Bind/UpdateControlTextCommand.cs2
-rw-r--r--src/WixToolset.Core.WindowsInstaller/Bind/UpdateFileFacadesCommand.cs112
-rw-r--r--src/WixToolset.Core.WindowsInstaller/Bind/UpdateMediaSequencesCommand.cs126
-rw-r--r--src/WixToolset.Core.WindowsInstaller/Inscribe/InscribeMsiPackageCommand.cs2
-rw-r--r--src/WixToolset.Core.WindowsInstaller/MstBackend.cs1
-rw-r--r--src/WixToolset.Core.WindowsInstaller/Patch.cs2
-rw-r--r--src/WixToolset.Core.WindowsInstaller/Unbind/UnbindDatabaseCommand.cs2
-rw-r--r--src/WixToolset.Core.WindowsInstaller/Unbind/UnbindTranformCommand.cs7
-rw-r--r--src/WixToolset.Core/Bind/ExtractEmbeddedFilesCommand.cs15
-rw-r--r--src/WixToolset.Core/Bind/ResolveDelayedFieldsCommand.cs78
-rw-r--r--src/WixToolset.Core/Bind/ResolvedDirectory.cs18
-rw-r--r--src/WixToolset.Core/Binder.cs8
-rw-r--r--src/WixToolset.Core/Compiler.cs8
-rw-r--r--src/WixToolset.Core/Linker.cs670
-rw-r--r--src/WixToolset.Core/WindowsInstallerStandard.cs260
-rw-r--r--src/WixToolset.Core/WixStrings.Designer.cs4
-rw-r--r--src/WixToolset.Data.WindowsInstaller/Data/tables.xml2
-rw-r--r--src/WixToolset.Data.WindowsInstaller/RowDictionary.cs3
-rw-r--r--src/WixToolset.Data.WindowsInstaller/Rows/SymbolPathType.cs17
-rw-r--r--src/WixToolset.Data.WindowsInstaller/Rows/WixActionRow.cs52
-rw-r--r--src/WixToolset.Data.WindowsInstaller/Rows/WixActionRowCollection.cs1
-rw-r--r--src/WixToolset.Data.WindowsInstaller/Rows/WixDeltaPatchSymbolPathsRow.cs2
-rw-r--r--src/WixToolset.Data.WindowsInstaller/TableDefinitionCollection.cs11
-rw-r--r--src/WixToolset.Data.WindowsInstaller/WindowsInstallerStandard.cs442
-rw-r--r--src/WixToolset.Data.WindowsInstaller/WindowsInstallerStandardInternal.cs59
-rw-r--r--src/test/WixToolsetTest.CoreIntegrationFixture/ProgramFixture.cs7
45 files changed, 2001 insertions, 2442 deletions
diff --git a/src/WixToolset.Core.WindowsInstaller/Bind/AssignMediaCommand.cs b/src/WixToolset.Core.WindowsInstaller/Bind/AssignMediaCommand.cs
index 74e2cdb5..f426b96d 100644
--- a/src/WixToolset.Core.WindowsInstaller/Bind/AssignMediaCommand.cs
+++ b/src/WixToolset.Core.WindowsInstaller/Bind/AssignMediaCommand.cs
@@ -1,43 +1,44 @@
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
3namespace WixToolset.Core.WindowsInstaller.Databases 3namespace WixToolset.Core.WindowsInstaller.Bind
4{ 4{
5 using System; 5 using System;
6 using System.Collections.Generic; 6 using System.Collections.Generic;
7 using System.Globalization; 7 using System.Globalization;
8 using System.Linq;
8 using WixToolset.Core.Bind; 9 using WixToolset.Core.Bind;
9 using WixToolset.Data; 10 using WixToolset.Data;
10 using WixToolset.Data.Rows; 11 using WixToolset.Data.Rows;
12 using WixToolset.Data.Tuples;
11 13
12 /// <summary> 14 /// <summary>
13 /// AssignMediaCommand assigns files to cabs based on Media or MediaTemplate rows. 15 /// AssignMediaCommand assigns files to cabs based on Media or MediaTemplate rows.
14 /// </summary> 16 /// </summary>
15 public class AssignMediaCommand 17 internal class AssignMediaCommand
16 { 18 {
17 public AssignMediaCommand() 19 public AssignMediaCommand(IntermediateSection section)
18 { 20 {
19 this.CabinetNameTemplate = "Cab{0}.cab"; 21 this.CabinetNameTemplate = "Cab{0}.cab";
22 this.Section = section;
20 } 23 }
21 24
22 public Output Output { private get; set; } 25 private IntermediateSection Section { get; }
26
27 public IEnumerable<FileFacade> FileFacades { private get; set; }
23 28
24 public bool FilesCompressed { private get; set; } 29 public bool FilesCompressed { private get; set; }
25 30
26 public string CabinetNameTemplate { private get; set; } 31 public string CabinetNameTemplate { private get; set; }
27 32
28 public IEnumerable<FileFacade> FileFacades { private get; set; }
29
30 public TableDefinitionCollection TableDefinitions { private get; set; }
31
32 /// <summary> 33 /// <summary>
33 /// Gets cabinets with their file rows. 34 /// Gets cabinets with their file rows.
34 /// </summary> 35 /// </summary>
35 public Dictionary<MediaRow, IEnumerable<FileFacade>> FileFacadesByCabinetMedia { get; private set; } 36 public Dictionary<MediaTuple, IEnumerable<FileFacade>> FileFacadesByCabinetMedia { get; private set; }
36 37
37 /// <summary> 38 /// <summary>
38 /// Get media rows. 39 /// Get media rows.
39 /// </summary> 40 /// </summary>
40 public RowDictionary<MediaRow> MediaRows { get; private set; } 41 public Dictionary<int, MediaTuple> MediaRows { get; private set; }
41 42
42 /// <summary> 43 /// <summary>
43 /// Get uncompressed file rows. This will contain file rows of File elements that are marked with compression=no. 44 /// Get uncompressed file rows. This will contain file rows of File elements that are marked with compression=no.
@@ -47,42 +48,41 @@ namespace WixToolset.Core.WindowsInstaller.Databases
47 48
48 public void Execute() 49 public void Execute()
49 { 50 {
50 Dictionary<MediaRow, List<FileFacade>> filesByCabinetMedia = new Dictionary<MediaRow, List<FileFacade>>(); 51 var filesByCabinetMedia = new Dictionary<MediaTuple, List<FileFacade>>();
51 52
52 RowDictionary<MediaRow> mediaRows = new RowDictionary<MediaRow>(); 53 var mediaRows = new Dictionary<int, MediaTuple>();
53 54
54 List<FileFacade> uncompressedFiles = new List<FileFacade>(); 55 List<FileFacade> uncompressedFiles = new List<FileFacade>();
55 56
56 MediaRow mergeModuleMediaRow = null; 57 var mediaTable = this.Section.Tuples.OfType<MediaTuple>().ToList();
57 Table mediaTable = this.Output.Tables["Media"]; 58 var mediaTemplateTable = this.Section.Tuples.OfType<WixMediaTemplateTuple>().ToList();
58 Table mediaTemplateTable = this.Output.Tables["WixMediaTemplate"];
59 59
60 // If both tables are authored, it is an error. 60 // If both tables are authored, it is an error.
61 if ((mediaTemplateTable != null && mediaTemplateTable.Rows.Count > 0) && (mediaTable != null && mediaTable.Rows.Count > 1)) 61 if ((mediaTemplateTable != null && mediaTemplateTable.Count > 0) && (mediaTable != null && mediaTable.Count > 1))
62 { 62 {
63 throw new WixException(WixErrors.MediaTableCollision(null)); 63 throw new WixException(WixErrors.MediaTableCollision(null));
64 } 64 }
65 65
66 // When building merge module, all the files go to "#MergeModule.CABinet". 66 // When building merge module, all the files go to "#MergeModule.CABinet".
67 if (OutputType.Module == this.Output.Type) 67 if (SectionType.Module == this.Section.Type)
68 { 68 {
69 Table mergeModuleMediaTable = new Table(this.TableDefinitions["Media"]); 69 var mergeModuleMediaRow = new MediaTuple();
70 mergeModuleMediaRow = (MediaRow)mergeModuleMediaTable.CreateRow(null);
71 mergeModuleMediaRow.Cabinet = "#MergeModule.CABinet"; 70 mergeModuleMediaRow.Cabinet = "#MergeModule.CABinet";
72 71
73 filesByCabinetMedia.Add(mergeModuleMediaRow, new List<FileFacade>()); 72 this.Section.Tuples.Add(mergeModuleMediaRow);
74 }
75 73
76 if (OutputType.Module == this.Output.Type || null == mediaTemplateTable) 74 filesByCabinetMedia.Add(mergeModuleMediaRow, new List<FileFacade>(this.FileFacades));
75 }
76 else if (null == mediaTemplateTable)
77 { 77 {
78 this.ManuallyAssignFiles(mediaTable, mergeModuleMediaRow, this.FileFacades, filesByCabinetMedia, mediaRows, uncompressedFiles); 78 this.ManuallyAssignFiles(mediaTable, this.FileFacades, filesByCabinetMedia, mediaRows, uncompressedFiles);
79 } 79 }
80 else 80 else
81 { 81 {
82 this.AutoAssignFiles(mediaTable, this.FileFacades, filesByCabinetMedia, mediaRows, uncompressedFiles); 82 this.AutoAssignFiles(mediaTable, this.FileFacades, filesByCabinetMedia, mediaRows, uncompressedFiles);
83 } 83 }
84 84
85 this.FileFacadesByCabinetMedia = new Dictionary<MediaRow, IEnumerable<FileFacade>>(); 85 this.FileFacadesByCabinetMedia = new Dictionary<MediaTuple, IEnumerable<FileFacade>>();
86 86
87 foreach (var mediaRowWithFiles in filesByCabinetMedia) 87 foreach (var mediaRowWithFiles in filesByCabinetMedia)
88 { 88 {
@@ -98,7 +98,7 @@ namespace WixToolset.Core.WindowsInstaller.Databases
98 /// Assign files to cabinets based on MediaTemplate authoring. 98 /// Assign files to cabinets based on MediaTemplate authoring.
99 /// </summary> 99 /// </summary>
100 /// <param name="fileFacades">FileRowCollection</param> 100 /// <param name="fileFacades">FileRowCollection</param>
101 private void AutoAssignFiles(Table mediaTable, IEnumerable<FileFacade> fileFacades, Dictionary<MediaRow, List<FileFacade>> filesByCabinetMedia, RowDictionary<MediaRow> mediaRows, List<FileFacade> uncompressedFiles) 101 private void AutoAssignFiles(List<MediaTuple> mediaTable, IEnumerable<FileFacade> fileFacades, Dictionary<MediaTuple, List<FileFacade>> filesByCabinetMedia, Dictionary<int, MediaTuple> mediaRows, List<FileFacade> uncompressedFiles)
102 { 102 {
103 const int MaxCabIndex = 999; 103 const int MaxCabIndex = 999;
104 104
@@ -107,13 +107,19 @@ namespace WixToolset.Core.WindowsInstaller.Databases
107 int maxPreCabSizeInMB = 0; 107 int maxPreCabSizeInMB = 0;
108 int currentCabIndex = 0; 108 int currentCabIndex = 0;
109 109
110 MediaRow currentMediaRow = null; 110 MediaTuple currentMediaRow = null;
111 111
112 Table mediaTemplateTable = this.Output.Tables["WixMediaTemplate"]; 112 var mediaTemplateTable = this.Section.Tuples.OfType<WixMediaTemplateTuple>();
113
114 // Remove all previous media tuples since they will be replaced with
115 // media template.
116 foreach (var mediaTuple in mediaTable)
117 {
118 this.Section.Tuples.Remove(mediaTuple);
119 }
113 120
114 // Auto assign files to cabinets based on maximum uncompressed media size 121 // Auto assign files to cabinets based on maximum uncompressed media size
115 mediaTable.Rows.Clear(); 122 var mediaTemplateRow = mediaTemplateTable.Single();
116 WixMediaTemplateRow mediaTemplateRow = (WixMediaTemplateRow)mediaTemplateTable.Rows[0];
117 123
118 if (!String.IsNullOrEmpty(mediaTemplateRow.CabinetTemplate)) 124 if (!String.IsNullOrEmpty(mediaTemplateRow.CabinetTemplate))
119 { 125 {
@@ -149,9 +155,9 @@ namespace WixToolset.Core.WindowsInstaller.Databases
149 { 155 {
150 // When building a product, if the current file is not to be compressed or if 156 // When building a product, if the current file is not to be compressed or if
151 // the package set not to be compressed, don't cab it. 157 // the package set not to be compressed, don't cab it.
152 if (OutputType.Product == this.Output.Type && 158 if (SectionType.Product == this.Section.Type &&
153 (!facade.File.Compressed.Value || 159 ((facade.File.Compressed.HasValue && !facade.File.Compressed.Value) ||
154 (!facade.File.Compressed.HasValue && !this.FilesCompressed))) 160 (!facade.File.Compressed.HasValue && !this.FilesCompressed)))
155 { 161 {
156 uncompressedFiles.Add(facade); 162 uncompressedFiles.Add(facade);
157 continue; 163 continue;
@@ -172,8 +178,8 @@ namespace WixToolset.Core.WindowsInstaller.Databases
172 if (currentPreCabSize > maxPreCabSizeInBytes) 178 if (currentPreCabSize > maxPreCabSizeInBytes)
173 { 179 {
174 // Overflow due to current file 180 // Overflow due to current file
175 currentMediaRow = this.AddMediaRow(mediaTemplateRow, mediaTable, ++currentCabIndex); 181 currentMediaRow = this.AddMediaRow(mediaTemplateRow, ++currentCabIndex);
176 mediaRows.Add(currentMediaRow); 182 mediaRows.Add(currentMediaRow.DiskId, currentMediaRow);
177 filesByCabinetMedia.Add(currentMediaRow, new List<FileFacade>()); 183 filesByCabinetMedia.Add(currentMediaRow, new List<FileFacade>());
178 184
179 List<FileFacade> cabinetFileRows = filesByCabinetMedia[currentMediaRow]; 185 List<FileFacade> cabinetFileRows = filesByCabinetMedia[currentMediaRow];
@@ -188,8 +194,8 @@ namespace WixToolset.Core.WindowsInstaller.Databases
188 if (currentMediaRow == null) 194 if (currentMediaRow == null)
189 { 195 {
190 // Create new cab and MediaRow 196 // Create new cab and MediaRow
191 currentMediaRow = this.AddMediaRow(mediaTemplateRow, mediaTable, ++currentCabIndex); 197 currentMediaRow = this.AddMediaRow(mediaTemplateRow, ++currentCabIndex);
192 mediaRows.Add(currentMediaRow); 198 mediaRows.Add(currentMediaRow.DiskId, currentMediaRow);
193 filesByCabinetMedia.Add(currentMediaRow, new List<FileFacade>()); 199 filesByCabinetMedia.Add(currentMediaRow, new List<FileFacade>());
194 } 200 }
195 201
@@ -201,11 +207,13 @@ namespace WixToolset.Core.WindowsInstaller.Databases
201 } 207 }
202 208
203 // If there are uncompressed files and no MediaRow, create a default one. 209 // If there are uncompressed files and no MediaRow, create a default one.
204 if (uncompressedFiles.Count > 0 && mediaTable.Rows.Count == 0) 210 if (uncompressedFiles.Count > 0 && !this.Section.Tuples.OfType<MediaTuple>().Any())
205 { 211 {
206 MediaRow defaultMediaRow = (MediaRow)mediaTable.CreateRow(null); 212 var defaultMediaRow = new MediaTuple(null, new Identifier(1, AccessModifier.Private));
207 defaultMediaRow.DiskId = 1; 213 defaultMediaRow.DiskId = 1;
208 mediaRows.Add(defaultMediaRow); 214
215 mediaRows.Add(1, defaultMediaRow);
216 this.Section.Tuples.Add(defaultMediaRow);
209 } 217 }
210 } 218 }
211 219
@@ -213,79 +221,65 @@ namespace WixToolset.Core.WindowsInstaller.Databases
213 /// Assign files to cabinets based on Media authoring. 221 /// Assign files to cabinets based on Media authoring.
214 /// </summary> 222 /// </summary>
215 /// <param name="mediaTable"></param> 223 /// <param name="mediaTable"></param>
216 /// <param name="mergeModuleMediaRow"></param>
217 /// <param name="fileFacades"></param> 224 /// <param name="fileFacades"></param>
218 private void ManuallyAssignFiles(Table mediaTable, MediaRow mergeModuleMediaRow, IEnumerable<FileFacade> fileFacades, Dictionary<MediaRow, List<FileFacade>> filesByCabinetMedia, RowDictionary<MediaRow> mediaRows, List<FileFacade> uncompressedFiles) 225 private void ManuallyAssignFiles(List<MediaTuple> mediaTable, IEnumerable<FileFacade> fileFacades, Dictionary<MediaTuple, List<FileFacade>> filesByCabinetMedia, Dictionary<int, MediaTuple> mediaRows, List<FileFacade> uncompressedFiles)
219 { 226 {
220 if (OutputType.Module != this.Output.Type) 227 if (mediaTable.Any())
221 { 228 {
222 if (null != mediaTable) 229 var cabinetMediaRows = new Dictionary<string, MediaTuple>(StringComparer.OrdinalIgnoreCase);
230 foreach (var mediaRow in mediaTable)
223 { 231 {
224 Dictionary<string, MediaRow> cabinetMediaRows = new Dictionary<string, MediaRow>(StringComparer.InvariantCultureIgnoreCase); 232 // If the Media row has a cabinet, make sure it is unique across all Media rows.
225 foreach (MediaRow mediaRow in mediaTable.Rows) 233 if (!String.IsNullOrEmpty(mediaRow.Cabinet))
226 { 234 {
227 // If the Media row has a cabinet, make sure it is unique across all Media rows. 235 if (cabinetMediaRows.TryGetValue(mediaRow.Cabinet, out var existingRow))
228 if (!String.IsNullOrEmpty(mediaRow.Cabinet))
229 { 236 {
230 MediaRow existingRow; 237 Messaging.Instance.OnMessage(WixErrors.DuplicateCabinetName(mediaRow.SourceLineNumbers, mediaRow.Cabinet));
231 if (cabinetMediaRows.TryGetValue(mediaRow.Cabinet, out existingRow)) 238 Messaging.Instance.OnMessage(WixErrors.DuplicateCabinetName2(existingRow.SourceLineNumbers, existingRow.Cabinet));
232 { 239 }
233 Messaging.Instance.OnMessage(WixErrors.DuplicateCabinetName(mediaRow.SourceLineNumbers, mediaRow.Cabinet)); 240 else
234 Messaging.Instance.OnMessage(WixErrors.DuplicateCabinetName2(existingRow.SourceLineNumbers, existingRow.Cabinet)); 241 {
235 } 242 cabinetMediaRows.Add(mediaRow.Cabinet, mediaRow);
236 else
237 {
238 cabinetMediaRows.Add(mediaRow.Cabinet, mediaRow);
239 }
240 } 243 }
241
242 mediaRows.Add(mediaRow);
243 } 244 }
245
246 mediaRows.Add(mediaRow.DiskId, mediaRow);
244 } 247 }
248 }
245 249
246 foreach (MediaRow mediaRow in mediaRows.Values) 250 foreach (var mediaRow in mediaRows.Values)
251 {
252 if (null != mediaRow.Cabinet)
247 { 253 {
248 if (null != mediaRow.Cabinet) 254 filesByCabinetMedia.Add(mediaRow, new List<FileFacade>());
249 {
250 filesByCabinetMedia.Add(mediaRow, new List<FileFacade>());
251 }
252 } 255 }
253 } 256 }
254 257
255 foreach (FileFacade facade in fileFacades) 258 foreach (FileFacade facade in fileFacades)
256 { 259 {
257 if (OutputType.Module == this.Output.Type) 260 if (!mediaRows.TryGetValue(facade.WixFile.DiskId, out var mediaRow))
258 { 261 {
259 filesByCabinetMedia[mergeModuleMediaRow].Add(facade); 262 Messaging.Instance.OnMessage(WixErrors.MissingMedia(facade.File.SourceLineNumbers, facade.WixFile.DiskId));
263 continue;
260 } 264 }
261 else
262 {
263 MediaRow mediaRow;
264 if (!mediaRows.TryGetValue(facade.WixFile.DiskId.ToString(CultureInfo.InvariantCulture), out mediaRow))
265 {
266 Messaging.Instance.OnMessage(WixErrors.MissingMedia(facade.File.SourceLineNumbers, facade.WixFile.DiskId));
267 continue;
268 }
269 265
270 // When building a product, if the current file is not to be compressed or if 266 // When building a product, if the current file is not to be compressed or if
271 // the package set not to be compressed, don't cab it. 267 // the package set not to be compressed, don't cab it.
272 if (OutputType.Product == this.Output.Type && 268 if (SectionType.Product == this.Section.Type &&
273 (!facade.File.Compressed.Value || 269 (!facade.File.Compressed.Value ||
274 (!facade.File.Compressed.HasValue && !this.FilesCompressed))) 270 (!facade.File.Compressed.HasValue && !this.FilesCompressed)))
271 {
272 uncompressedFiles.Add(facade);
273 }
274 else // file is marked compressed.
275 {
276 if (filesByCabinetMedia.TryGetValue(mediaRow, out var cabinetFiles))
275 { 277 {
276 uncompressedFiles.Add(facade); 278 cabinetFiles.Add(facade);
277 } 279 }
278 else // file is marked compressed. 280 else
279 { 281 {
280 List<FileFacade> cabinetFiles; 282 Messaging.Instance.OnMessage(WixErrors.ExpectedMediaCabinet(facade.File.SourceLineNumbers, facade.File.File, facade.WixFile.DiskId));
281 if (filesByCabinetMedia.TryGetValue(mediaRow, out cabinetFiles))
282 {
283 cabinetFiles.Add(facade);
284 }
285 else
286 {
287 Messaging.Instance.OnMessage(WixErrors.ExpectedMediaCabinet(facade.File.SourceLineNumbers, facade.File.File, facade.WixFile.DiskId));
288 }
289 } 283 }
290 } 284 }
291 } 285 }
@@ -297,17 +291,20 @@ namespace WixToolset.Core.WindowsInstaller.Databases
297 /// <param name="mediaTable"></param> 291 /// <param name="mediaTable"></param>
298 /// <param name="cabIndex"></param> 292 /// <param name="cabIndex"></param>
299 /// <returns></returns> 293 /// <returns></returns>
300 private MediaRow AddMediaRow(WixMediaTemplateRow mediaTemplateRow, Table mediaTable, int cabIndex) 294 private MediaTuple AddMediaRow(WixMediaTemplateTuple mediaTemplateRow, int cabIndex)
301 { 295 {
302 MediaRow currentMediaRow = (MediaRow)mediaTable.CreateRow(mediaTemplateRow.SourceLineNumbers); 296 var currentMediaRow = new MediaTuple(mediaTemplateRow.SourceLineNumbers, new Identifier(cabIndex, AccessModifier.Private));
303 currentMediaRow.DiskId = cabIndex; 297 currentMediaRow.DiskId = cabIndex;
304 currentMediaRow.Cabinet = String.Format(CultureInfo.InvariantCulture, this.CabinetNameTemplate, cabIndex); 298 currentMediaRow.Cabinet = String.Format(CultureInfo.InvariantCulture, this.CabinetNameTemplate, cabIndex);
305 299
306 Table wixMediaTable = this.Output.EnsureTable(this.TableDefinitions["WixMedia"]); 300 this.Section.Tuples.Add(currentMediaRow);
307 WixMediaRow row = (WixMediaRow)wixMediaTable.CreateRow(mediaTemplateRow.SourceLineNumbers); 301
308 row.DiskId = cabIndex; 302 var row = new WixMediaTuple(mediaTemplateRow.SourceLineNumbers, new Identifier(cabIndex, AccessModifier.Private));
303 row.DiskId_ = cabIndex;
309 row.CompressionLevel = mediaTemplateRow.CompressionLevel; 304 row.CompressionLevel = mediaTemplateRow.CompressionLevel;
310 305
306 this.Section.Tuples.Add(row);
307
311 return currentMediaRow; 308 return currentMediaRow;
312 } 309 }
313 } 310 }
diff --git a/src/WixToolset.Core.WindowsInstaller/Bind/BindDatabaseCommand.cs b/src/WixToolset.Core.WindowsInstaller/Bind/BindDatabaseCommand.cs
index 5a61b63c..30a19a4b 100644
--- a/src/WixToolset.Core.WindowsInstaller/Bind/BindDatabaseCommand.cs
+++ b/src/WixToolset.Core.WindowsInstaller/Bind/BindDatabaseCommand.cs
@@ -3,21 +3,15 @@
3namespace WixToolset.Core.WindowsInstaller.Bind 3namespace WixToolset.Core.WindowsInstaller.Bind
4{ 4{
5 using System; 5 using System;
6 using System.Collections;
7 using System.Collections.Generic; 6 using System.Collections.Generic;
8 using System.Diagnostics;
9 using System.Globalization;
10 using System.IO; 7 using System.IO;
11 using System.Linq; 8 using System.Linq;
12 using WixToolset.Bind;
13 using WixToolset.Core.Bind; 9 using WixToolset.Core.Bind;
14 using WixToolset.Core.WindowsInstaller.Databases;
15 using WixToolset.Data; 10 using WixToolset.Data;
16 using WixToolset.Data.Bind; 11 using WixToolset.Data.Bind;
17 using WixToolset.Data.Rows; 12 using WixToolset.Data.Tuples;
18 using WixToolset.Extensibility; 13 using WixToolset.Extensibility;
19 using WixToolset.Extensibility.Services; 14 using WixToolset.Extensibility.Services;
20 using WixToolset.Msi;
21 15
22 /// <summary> 16 /// <summary>
23 /// Binds a databse. 17 /// Binds a databse.
@@ -25,11 +19,11 @@ namespace WixToolset.Core.WindowsInstaller.Bind
25 internal class BindDatabaseCommand 19 internal class BindDatabaseCommand
26 { 20 {
27 // As outlined in RFC 4122, this is our namespace for generating name-based (version 3) UUIDs. 21 // As outlined in RFC 4122, this is our namespace for generating name-based (version 3) UUIDs.
28 private static readonly Guid WixComponentGuidNamespace = new Guid("{3064E5C6-FB63-4FE9-AC49-E446A792EFA5}"); 22 internal static readonly Guid WixComponentGuidNamespace = new Guid("{3064E5C6-FB63-4FE9-AC49-E446A792EFA5}");
29 23
30 public BindDatabaseCommand(IBindContext context, Validator validator) 24 public BindDatabaseCommand(IBindContext context, Validator validator)
31 { 25 {
32 this.TableDefinitions = WindowsInstallerStandard.GetTableDefinitions(); 26 this.TableDefinitions = WindowsInstallerStandardInternal.GetTableDefinitions();
33 27
34 this.BindPaths = context.BindPaths; 28 this.BindPaths = context.BindPaths;
35 this.CabbingThreadCount = context.CabbingThreadCount; 29 this.CabbingThreadCount = context.CabbingThreadCount;
@@ -73,8 +67,6 @@ namespace WixToolset.Core.WindowsInstaller.Bind
73 67
74 private Intermediate Intermediate { get; } 68 private Intermediate Intermediate { get; }
75 69
76 private Output Output { get; }
77
78 private string OutputPath { get; } 70 private string OutputPath { get; }
79 71
80 private bool SuppressAddingValidationRows { get; } 72 private bool SuppressAddingValidationRows { get; }
@@ -95,22 +87,16 @@ namespace WixToolset.Core.WindowsInstaller.Bind
95 87
96 public void Execute() 88 public void Execute()
97 { 89 {
98 this.Intermediate.Save(this.OutputPath); 90 var section = this.Intermediate.Sections.Single();
99#if FINISH
100 List<FileTransfer> fileTransfers = new List<FileTransfer>();
101 91
102 HashSet<string> suppressedTableNames = new HashSet<string>(); 92 var fileTransfers = new List<FileTransfer>();
103 93
104 // If there are any fields to resolve later, create the cache to populate during bind. 94 var suppressedTableNames = new HashSet<string>();
105 IDictionary<string, string> variableCache = null;
106 if (this.DelayedFields.Any())
107 {
108 variableCache = new Dictionary<string, string>(StringComparer.InvariantCultureIgnoreCase);
109 }
110 95
111 this.LocalizeUI(this.Output.Tables); 96 // If there are any fields to resolve later, create the cache to populate during bind.
97 var variableCache = this.DelayedFields.Any() ? new Dictionary<string, string>(StringComparer.InvariantCultureIgnoreCase) : null;
112 98
113 this.Output = CreateOutputFromIR(this.Intermediate); 99 this.LocalizeUI(section);
114 100
115 // Process the summary information table before the other tables. 101 // Process the summary information table before the other tables.
116 bool compressed; 102 bool compressed;
@@ -118,8 +104,7 @@ namespace WixToolset.Core.WindowsInstaller.Bind
118 int installerVersion; 104 int installerVersion;
119 string modularizationGuid; 105 string modularizationGuid;
120 { 106 {
121 BindSummaryInfoCommand command = new BindSummaryInfoCommand(); 107 var command = new BindSummaryInfoCommand(section);
122 command.Output = this.Output;
123 command.Execute(); 108 command.Execute();
124 109
125 compressed = command.Compressed; 110 compressed = command.Compressed;
@@ -128,42 +113,61 @@ namespace WixToolset.Core.WindowsInstaller.Bind
128 modularizationGuid = command.ModularizationGuid; 113 modularizationGuid = command.ModularizationGuid;
129 } 114 }
130 115
131 // Stop processing if an error previously occurred. 116 // Add binder variables for all properties.
132 if (Messaging.Instance.EncounteredError) 117 if (SectionType.Product == section.Type || variableCache != null)
133 {
134 return;
135 }
136
137 // Modularize identifiers and add tables with real streams to the import tables.
138 if (OutputType.Module == this.Output.Type)
139 { 118 {
140 // Gather all the suppress modularization identifiers 119 foreach (var propertyRow in section.Tuples.OfType<PropertyTuple>())
141 HashSet<string> suppressModularizationIdentifiers = null;
142 Table wixSuppressModularizationTable = this.Output.Tables["WixSuppressModularization"];
143 if (null != wixSuppressModularizationTable)
144 { 120 {
145 suppressModularizationIdentifiers = new HashSet<string>(wixSuppressModularizationTable.Rows.Select(row => (string)row[0])); 121 // Set the ProductCode if it is to be generated.
146 } 122 if ("ProductCode".Equals(propertyRow.Property, StringComparison.Ordinal) && "*".Equals(propertyRow.Value, StringComparison.Ordinal))
123 {
124 propertyRow.Value = Common.GenerateGuid();
147 125
148 foreach (Table table in this.Output.Tables) 126#if TODO_FIX_INSTANCE_TRANSFORM
149 { 127 // Update the target ProductCode in any instance transforms.
150 table.Modularize(modularizationGuid, suppressModularizationIdentifiers); 128 foreach (SubStorage subStorage in this.Output.SubStorages)
129 {
130 Output subStorageOutput = subStorage.Data;
131 if (OutputType.Transform != subStorageOutput.Type)
132 {
133 continue;
134 }
135
136 Table instanceSummaryInformationTable = subStorageOutput.Tables["_SummaryInformation"];
137 foreach (Row row in instanceSummaryInformationTable.Rows)
138 {
139 if ((int)SummaryInformation.Transform.ProductCodes == row.FieldAsInteger(0))
140 {
141 row[1] = row.FieldAsString(1).Replace("*", propertyRow.Value);
142 break;
143 }
144 }
145 }
146#endif
147 }
148
149 // Add the property name and value to the variableCache.
150 if (variableCache != null)
151 {
152 var key = String.Concat("property.", propertyRow.Property);
153 variableCache[key] = propertyRow.Value;
154 }
151 } 155 }
152 } 156 }
153 157
154 // This must occur after all variables and source paths have been resolved and after modularization. 158 // Sequence all the actions.
155 List<FileFacade> fileFacades;
156 { 159 {
157 GetFileFacadesCommand command = new GetFileFacadesCommand(); 160 var command = new SequenceActionsCommand(section);
158 command.FileTable = this.Output.Tables["File"]; 161 command.Messaging = Messaging.Instance;
159 command.WixFileTable = this.Output.Tables["WixFile"];
160 command.WixDeltaPatchFileTable = this.Output.Tables["WixDeltaPatchFile"];
161 command.WixDeltaPatchSymbolPathsTable = this.Output.Tables["WixDeltaPatchSymbolPaths"];
162 command.Execute(); 162 command.Execute();
163 }
163 164
164 fileFacades = command.FileFacades; 165 {
166 var command = new CreateSpecialPropertiesCommand(section);
167 command.Execute();
165 } 168 }
166 169
170#if TODO_FINISH_PATCH
167 ////if (OutputType.Patch == this.Output.Type) 171 ////if (OutputType.Patch == this.Output.Type)
168 ////{ 172 ////{
169 //// foreach (SubStorage substorage in this.Output.SubStorages) 173 //// foreach (SubStorage substorage in this.Output.SubStorages)
@@ -183,87 +187,72 @@ namespace WixToolset.Core.WindowsInstaller.Bind
183 //// this.MergeUnrealTables(transform.Tables); 187 //// this.MergeUnrealTables(transform.Tables);
184 //// } 188 //// }
185 ////} 189 ////}
186 190#endif
187 {
188 CreateSpecialPropertiesCommand command = new CreateSpecialPropertiesCommand();
189 command.PropertyTable = this.Output.Tables["Property"];
190 command.WixPropertyTable = this.Output.Tables["WixProperty"];
191 command.Execute();
192 }
193 191
194 if (Messaging.Instance.EncounteredError) 192 if (Messaging.Instance.EncounteredError)
195 { 193 {
196 return; 194 return;
197 } 195 }
198 196
199 // Add binder variables for all properties. 197 Messaging.Instance.OnMessage(WixVerboses.UpdatingFileInformation());
200 Table propertyTable = this.Output.Tables["Property"]; 198
201 if (null != propertyTable) 199 // This must occur after all variables and source paths have been resolved.
200 List<FileFacade> fileFacades;
202 { 201 {
203 foreach (PropertyRow propertyRow in propertyTable.Rows) 202 var command = new GetFileFacadesCommand(section);
204 { 203 command.Execute();
205 // Set the ProductCode if it is to be generated.
206 if (OutputType.Product == this.Output.Type && "ProductCode".Equals(propertyRow.Property, StringComparison.Ordinal) && "*".Equals(propertyRow.Value, StringComparison.Ordinal))
207 {
208 propertyRow.Value = Common.GenerateGuid();
209 204
210 // Update the target ProductCode in any instance transforms. 205 fileFacades = command.FileFacades;
211 foreach (SubStorage subStorage in this.Output.SubStorages) 206 }
212 {
213 Output subStorageOutput = subStorage.Data;
214 if (OutputType.Transform != subStorageOutput.Type)
215 {
216 continue;
217 }
218 207
219 Table instanceSummaryInformationTable = subStorageOutput.Tables["_SummaryInformation"]; 208 // Extract files that come from binary .wixlibs and WixExtensions (this does not extract files from merge modules).
220 foreach (Row row in instanceSummaryInformationTable.Rows) 209 {
221 { 210 var command = new ExtractEmbeddedFilesCommand(this.ExpectedEmbeddedFiles);
222 if ((int)SummaryInformation.Transform.ProductCodes == row.FieldAsInteger(0)) 211 command.Execute();
223 { 212 }
224 row[1] = row.FieldAsString(1).Replace("*", propertyRow.Value);
225 break;
226 }
227 }
228 }
229 }
230 213
231 // Add the property name and value to the variableCache. 214 // Gather information about files that did not come from merge modules (i.e. rows with a reference to the File table).
232 if (null != variableCache) 215 {
233 { 216 var command = new UpdateFileFacadesCommand(section);
234 string key = String.Concat("property.", Common.Demodularize(this.Output.Type, modularizationGuid, propertyRow.Property)); 217 command.FileFacades = fileFacades;
235 variableCache[key] = propertyRow.Value; 218 command.UpdateFileFacades = fileFacades.Where(f => !f.FromModule);
236 } 219 command.OverwriteHash = true;
237 } 220 command.TableDefinitions = this.TableDefinitions;
221 command.VariableCache = variableCache;
222 command.Execute();
238 } 223 }
239 224
240 // Extract files that come from cabinet files (this does not extract files from merge modules). 225 // Now that the variable cache is populated, resolve any delayed fields.
226 if (this.DelayedFields.Any())
241 { 227 {
242 ExtractEmbeddedFilesCommand command = new ExtractEmbeddedFilesCommand(); 228 var command = new ResolveDelayedFieldsCommand(this.DelayedFields, variableCache);
243 command.FilesWithEmbeddedFiles = this.ExpectedEmbeddedFiles;
244 command.Execute(); 229 command.Execute();
245 } 230 }
246 231
247 if (OutputType.Product == this.Output.Type) 232 // Set generated component guids.
233 {
234 var command = new CalculateComponentGuids(section);
235 command.Execute();
236 }
237
238 if (SectionType.Product == section.Type)
248 { 239 {
249 // Retrieve files and their information from merge modules. 240 // Retrieve files and their information from merge modules.
250 Table wixMergeTable = this.Output.Tables["WixMerge"]; 241 var wixMergeTuples = section.Tuples.OfType<WixMergeTuple>().ToList();
251 242
252 if (null != wixMergeTable) 243 if (wixMergeTuples.Any())
253 { 244 {
254 ExtractMergeModuleFilesCommand command = new ExtractMergeModuleFilesCommand(); 245 var command = new ExtractMergeModuleFilesCommand(section, wixMergeTuples);
255 command.FileFacades = fileFacades; 246 command.FileFacades = fileFacades;
256 command.FileTable = this.Output.Tables["File"];
257 command.WixFileTable = this.Output.Tables["WixFile"];
258 command.WixMergeTable = wixMergeTable;
259 command.OutputInstallerVersion = installerVersion; 247 command.OutputInstallerVersion = installerVersion;
260 command.SuppressLayout = this.SuppressLayout; 248 command.SuppressLayout = this.SuppressLayout;
261 command.TempFilesLocation = this.IntermediateFolder; 249 command.IntermediateFolder = this.IntermediateFolder;
262 command.Execute(); 250 command.Execute();
263 251
264 fileFacades.AddRange(command.MergeModulesFileFacades); 252 fileFacades.AddRange(command.MergeModulesFileFacades);
265 } 253 }
266 } 254 }
255#if TODO_FINISH_PATCH
267 else if (OutputType.Patch == this.Output.Type) 256 else if (OutputType.Patch == this.Output.Type)
268 { 257 {
269 // Merge transform data into the output object. 258 // Merge transform data into the output object.
@@ -271,6 +260,7 @@ namespace WixToolset.Core.WindowsInstaller.Bind
271 260
272 fileFacades.AddRange(filesFromTransform); 261 fileFacades.AddRange(filesFromTransform);
273 } 262 }
263#endif
274 264
275 // stop processing if an error previously occurred 265 // stop processing if an error previously occurred
276 if (Messaging.Instance.EncounteredError) 266 if (Messaging.Instance.EncounteredError)
@@ -278,51 +268,19 @@ namespace WixToolset.Core.WindowsInstaller.Bind
278 return; 268 return;
279 } 269 }
280 270
281 Messaging.Instance.OnMessage(WixVerboses.UpdatingFileInformation()); 271#if TODO_FIX_INSTANCE_TRANSFORM
282
283 // Gather information about files that did not come from merge modules (i.e. rows with a reference to the File table).
284 {
285 UpdateFileFacadesCommand command = new UpdateFileFacadesCommand();
286 command.FileFacades = fileFacades;
287 command.UpdateFileFacades = fileFacades.Where(f => !f.FromModule);
288 command.ModularizationGuid = modularizationGuid;
289 command.Output = this.Output;
290 command.OverwriteHash = true;
291 command.TableDefinitions = this.TableDefinitions;
292 command.VariableCache = variableCache;
293 command.Execute();
294 }
295
296 // Set generated component guids.
297 this.SetComponentGuids(this.Output);
298
299 // With the Component Guids set now we can create instance transforms. 272 // With the Component Guids set now we can create instance transforms.
300 this.CreateInstanceTransforms(this.Output); 273 this.CreateInstanceTransforms(this.Output);
301 274#endif
302 this.ValidateComponentGuids(this.Output);
303
304 this.UpdateControlText(this.Output);
305
306 if (this.DelayedFields.Any())
307 {
308 ResolveDelayedFieldsCommand command = new ResolveDelayedFieldsCommand();
309 command.OutputType = this.Output.Type;
310 command.DelayedFields = this.DelayedFields;
311 command.ModularizationGuid = null;
312 command.VariableCache = variableCache;
313 command.Execute();
314 }
315 275
316 // Assign files to media. 276 // Assign files to media.
317 RowDictionary<MediaRow> assignedMediaRows; 277 Dictionary<int, MediaTuple> assignedMediaRows;
318 Dictionary<MediaRow, IEnumerable<FileFacade>> filesByCabinetMedia; 278 Dictionary<MediaTuple, IEnumerable<FileFacade>> filesByCabinetMedia;
319 IEnumerable<FileFacade> uncompressedFiles; 279 IEnumerable<FileFacade> uncompressedFiles;
320 { 280 {
321 AssignMediaCommand command = new AssignMediaCommand(); 281 var command = new AssignMediaCommand(section);
322 command.FilesCompressed = compressed;
323 command.FileFacades = fileFacades; 282 command.FileFacades = fileFacades;
324 command.Output = this.Output; 283 command.FilesCompressed = compressed;
325 command.TableDefinitions = this.TableDefinitions;
326 command.Execute(); 284 command.Execute();
327 285
328 assignedMediaRows = command.MediaRows; 286 assignedMediaRows = command.MediaRows;
@@ -330,15 +288,40 @@ namespace WixToolset.Core.WindowsInstaller.Bind
330 uncompressedFiles = command.UncompressedFileFacades; 288 uncompressedFiles = command.UncompressedFileFacades;
331 } 289 }
332 290
333 // Update file sequence.
334 this.UpdateMediaSequences(this.Output.Type, fileFacades, assignedMediaRows);
335
336 // stop processing if an error previously occurred 291 // stop processing if an error previously occurred
337 if (Messaging.Instance.EncounteredError) 292 if (Messaging.Instance.EncounteredError)
338 { 293 {
339 return; 294 return;
340 } 295 }
341 296
297 // Try to put as much above here as possible, updating the IR is better.
298 Output output;
299 {
300 var command = new CreateOutputFromIRCommand(section, this.TableDefinitions);
301 command.Execute();
302
303 output = command.Output;
304 }
305
306 // Update file sequence.
307 {
308 var command = new UpdateMediaSequencesCommand(output, fileFacades, assignedMediaRows);
309 command.Execute();
310 }
311
312 // Modularize identifiers and add tables with real streams to the import tables.
313 if (OutputType.Module == output.Type)
314 {
315 // Gather all the suppress modularization identifiers
316 var suppressModularizationIdentifiers = new HashSet<string>(section.Tuples.OfType<WixSuppressModularizationTuple>().Select(s => s.WixSuppressModularization));
317
318 foreach (var table in output.Tables)
319 {
320 table.Modularize(modularizationGuid, suppressModularizationIdentifiers);
321 }
322 }
323
324#if TODO_FINISH_UPDATE
342 // Extended binder extensions can be called now that fields are resolved. 325 // Extended binder extensions can be called now that fields are resolved.
343 { 326 {
344 Table updatedFiles = this.Output.EnsureTable(this.TableDefinitions["WixBindUpdatedFiles"]); 327 Table updatedFiles = this.Output.EnsureTable(this.TableDefinitions["WixBindUpdatedFiles"]);
@@ -372,27 +355,27 @@ namespace WixToolset.Core.WindowsInstaller.Bind
372 command.Execute(); 355 command.Execute();
373 } 356 }
374 } 357 }
358#endif
375 359
376 // stop processing if an error previously occurred 360 // Stop processing if an error previously occurred.
377 if (Messaging.Instance.EncounteredError) 361 if (Messaging.Instance.EncounteredError)
378 { 362 {
379 return; 363 return;
380 } 364 }
381 365
366 // Ensure the intermediate folder is created since delta patches will be
367 // created there.
382 Directory.CreateDirectory(this.IntermediateFolder); 368 Directory.CreateDirectory(this.IntermediateFolder);
383 369
384 if (OutputType.Patch == this.Output.Type && this.DeltaBinaryPatch) 370 if (SectionType.Patch == section.Type && this.DeltaBinaryPatch)
385 { 371 {
386 CreateDeltaPatchesCommand command = new CreateDeltaPatchesCommand(); 372 var command = new CreateDeltaPatchesCommand(fileFacades, this.IntermediateFolder, section.Tuples.OfType<WixPatchIdTuple>().FirstOrDefault());
387 command.FileFacades = fileFacades;
388 command.WixPatchIdTable = this.Output.Tables["WixPatchId"];
389 command.TempFilesLocation = this.IntermediateFolder;
390 command.Execute(); 373 command.Execute();
391 } 374 }
392 375
393 // create cabinet files and process uncompressed files 376 // create cabinet files and process uncompressed files
394 string layoutDirectory = Path.GetDirectoryName(this.OutputPath); 377 string layoutDirectory = Path.GetDirectoryName(this.OutputPath);
395 if (!this.SuppressLayout || OutputType.Module == this.Output.Type) 378 if (!this.SuppressLayout || OutputType.Module == output.Type)
396 { 379 {
397 Messaging.Instance.OnMessage(WixVerboses.CreatingCabinetFiles()); 380 Messaging.Instance.OnMessage(WixVerboses.CreatingCabinetFiles());
398 381
@@ -400,7 +383,7 @@ namespace WixToolset.Core.WindowsInstaller.Bind
400 command.CabbingThreadCount = this.CabbingThreadCount; 383 command.CabbingThreadCount = this.CabbingThreadCount;
401 command.CabCachePath = this.CabCachePath; 384 command.CabCachePath = this.CabCachePath;
402 command.DefaultCompressionLevel = this.DefaultCompressionLevel; 385 command.DefaultCompressionLevel = this.DefaultCompressionLevel;
403 command.Output = this.Output; 386 command.Output = output;
404 command.BackendExtensions = this.BackendExtensions; 387 command.BackendExtensions = this.BackendExtensions;
405 command.LayoutDirectory = layoutDirectory; 388 command.LayoutDirectory = layoutDirectory;
406 command.Compressed = compressed; 389 command.Compressed = compressed;
@@ -408,39 +391,35 @@ namespace WixToolset.Core.WindowsInstaller.Bind
408 command.ResolveMedia = this.ResolveMedia; 391 command.ResolveMedia = this.ResolveMedia;
409 command.TableDefinitions = this.TableDefinitions; 392 command.TableDefinitions = this.TableDefinitions;
410 command.TempFilesLocation = this.IntermediateFolder; 393 command.TempFilesLocation = this.IntermediateFolder;
411 command.WixMediaTable = this.Output.Tables["WixMedia"]; 394 command.WixMediaTable = output.Tables["WixMedia"];
412 command.Execute(); 395 command.Execute();
413 396
414 fileTransfers.AddRange(command.FileTransfers); 397 fileTransfers.AddRange(command.FileTransfers);
415 } 398 }
416 399
400#if TODO_FINISH_PATCH
417 if (OutputType.Patch == this.Output.Type) 401 if (OutputType.Patch == this.Output.Type)
418 { 402 {
419 // copy output data back into the transforms 403 // copy output data back into the transforms
420 this.CopyToTransformData(this.Output); 404 this.CopyToTransformData(this.Output);
421 } 405 }
406#endif
422 407
423 // stop processing if an error previously occurred 408 // Add back suppressed tables which must be present prior to merging in modules.
424 if (Messaging.Instance.EncounteredError) 409 if (OutputType.Product == output.Type)
425 {
426 return;
427 }
428
429 // add back suppressed tables which must be present prior to merging in modules
430 if (OutputType.Product == this.Output.Type)
431 { 410 {
432 Table wixMergeTable = this.Output.Tables["WixMerge"]; 411 Table wixMergeTable = output.Tables["WixMerge"];
433 412
434 if (null != wixMergeTable && 0 < wixMergeTable.Rows.Count) 413 if (null != wixMergeTable && 0 < wixMergeTable.Rows.Count)
435 { 414 {
436 foreach (SequenceTable sequence in Enum.GetValues(typeof(SequenceTable))) 415 foreach (SequenceTable sequence in Enum.GetValues(typeof(SequenceTable)))
437 { 416 {
438 string sequenceTableName = sequence.ToString(); 417 string sequenceTableName = sequence.ToString();
439 Table sequenceTable = this.Output.Tables[sequenceTableName]; 418 Table sequenceTable = output.Tables[sequenceTableName];
440 419
441 if (null == sequenceTable) 420 if (null == sequenceTable)
442 { 421 {
443 sequenceTable = this.Output.EnsureTable(this.TableDefinitions[sequenceTableName]); 422 sequenceTable = output.EnsureTable(this.TableDefinitions[sequenceTableName]);
444 } 423 }
445 424
446 if (0 == sequenceTable.Rows.Count) 425 if (0 == sequenceTable.Rows.Count)
@@ -456,67 +435,59 @@ namespace WixToolset.Core.WindowsInstaller.Bind
456 // extension.PostBind(this.Context); 435 // extension.PostBind(this.Context);
457 //} 436 //}
458 437
459 // generate database file 438 this.ValidateComponentGuids(output);
439
440 // stop processing if an error previously occurred
441 if (Messaging.Instance.EncounteredError)
442 {
443 return;
444 }
445
446 // Generate database file.
460 Messaging.Instance.OnMessage(WixVerboses.GeneratingDatabase()); 447 Messaging.Instance.OnMessage(WixVerboses.GeneratingDatabase());
461 string tempDatabaseFile = Path.Combine(this.IntermediateFolder, Path.GetFileName(this.OutputPath)); 448 string tempDatabaseFile = Path.Combine(this.IntermediateFolder, Path.GetFileName(this.OutputPath));
462 this.GenerateDatabase(this.Output, tempDatabaseFile, false, false); 449 this.GenerateDatabase(output, tempDatabaseFile, false, false);
463 450
464 FileTransfer transfer; 451 if (FileTransfer.TryCreate(tempDatabaseFile, this.OutputPath, true, output.Type.ToString(), null, out var transfer)) // note where this database needs to move in the future
465 if (FileTransfer.TryCreate(tempDatabaseFile, this.OutputPath, true, this.Output.Type.ToString(), null, out transfer)) // note where this database needs to move in the future
466 { 452 {
467 transfer.Built = true; 453 transfer.Built = true;
468 fileTransfers.Add(transfer); 454 fileTransfers.Add(transfer);
469 } 455 }
470 456
471 // stop processing if an error previously occurred 457 // Stop processing if an error previously occurred.
472 if (Messaging.Instance.EncounteredError) 458 if (Messaging.Instance.EncounteredError)
473 { 459 {
474 return; 460 return;
475 } 461 }
476 462
477 // Output the output to a file 463 // Output the output to a file.
478 Pdb pdb = new Pdb(); 464 Pdb pdb = new Pdb();
479 pdb.Output = this.Output; 465 pdb.Output = output;
480 if (!String.IsNullOrEmpty(this.PdbFile)) 466 if (!String.IsNullOrEmpty(this.PdbFile))
481 { 467 {
482 pdb.Save(this.PdbFile); 468 pdb.Save(this.PdbFile);
483 } 469 }
484 470
485 // Merge modules. 471 // Merge modules.
486 if (OutputType.Product == this.Output.Type) 472 if (OutputType.Product == output.Type)
487 { 473 {
488 Messaging.Instance.OnMessage(WixVerboses.MergingModules()); 474 Messaging.Instance.OnMessage(WixVerboses.MergingModules());
489 475
490 MergeModulesCommand command = new MergeModulesCommand(); 476 var command = new MergeModulesCommand();
491 command.FileFacades = fileFacades; 477 command.FileFacades = fileFacades;
492 command.Output = this.Output; 478 command.Output = output;
493 command.OutputPath = tempDatabaseFile; 479 command.OutputPath = tempDatabaseFile;
494 command.SuppressedTableNames = suppressedTableNames; 480 command.SuppressedTableNames = suppressedTableNames;
495 command.Execute(); 481 command.Execute();
496
497 // stop processing if an error previously occurred
498 if (Messaging.Instance.EncounteredError)
499 {
500 return;
501 }
502 } 482 }
503 483
504 // inspect the MSI prior to running ICEs
505 //InspectorCore inspectorCore = new InspectorCore();
506 //foreach (InspectorExtension inspectorExtension in this.InspectorExtensions)
507 //{
508 // inspectorExtension.Core = inspectorCore;
509 // inspectorExtension.InspectDatabase(tempDatabaseFile, pdb);
510
511 // inspectorExtension.Core = null; // reset.
512 //}
513
514 if (Messaging.Instance.EncounteredError) 484 if (Messaging.Instance.EncounteredError)
515 { 485 {
516 return; 486 return;
517 } 487 }
518 488
519 // validate the output if there is an MSI validator 489#if TODO_FINISH_VALIDATION
490 // Validate the output if there is an MSI validator.
520 if (null != this.Validator) 491 if (null != this.Validator)
521 { 492 {
522 Stopwatch stopwatch = Stopwatch.StartNew(); 493 Stopwatch stopwatch = Stopwatch.StartNew();
@@ -537,19 +508,18 @@ namespace WixToolset.Core.WindowsInstaller.Bind
537 return; 508 return;
538 } 509 }
539 } 510 }
511#endif
540 512
541 // Process uncompressed files. 513 // Process uncompressed files.
542 if (!Messaging.Instance.EncounteredError && !this.SuppressLayout && uncompressedFiles.Any()) 514 if (!Messaging.Instance.EncounteredError && !this.SuppressLayout && uncompressedFiles.Any())
543 { 515 {
544 var command = new ProcessUncompressedFilesCommand(); 516 var command = new ProcessUncompressedFilesCommand(section);
545 command.Compressed = compressed; 517 command.Compressed = compressed;
546 command.FileFacades = uncompressedFiles; 518 command.FileFacades = uncompressedFiles;
547 command.LayoutDirectory = layoutDirectory; 519 command.LayoutDirectory = layoutDirectory;
548 command.LongNamesInImage = longNames; 520 command.LongNamesInImage = longNames;
549 command.MediaRows = assignedMediaRows;
550 command.ResolveMedia = this.ResolveMedia; 521 command.ResolveMedia = this.ResolveMedia;
551 command.DatabasePath = tempDatabaseFile; 522 command.DatabasePath = tempDatabaseFile;
552 command.WixMediaTable = this.Output.Tables["WixMedia"];
553 command.Execute(); 523 command.Execute();
554 524
555 fileTransfers.AddRange(command.FileTransfers); 525 fileTransfers.AddRange(command.FileTransfers);
@@ -557,94 +527,92 @@ namespace WixToolset.Core.WindowsInstaller.Bind
557 527
558 this.FileTransfers = fileTransfers; 528 this.FileTransfers = fileTransfers;
559 this.ContentFilePaths = fileFacades.Select(r => r.WixFile.Source).ToList(); 529 this.ContentFilePaths = fileFacades.Select(r => r.WixFile.Source).ToList();
530
531 // TODO: Eventually this gets removed
532 var intermediate = new Intermediate(this.Intermediate.Id, new[] { section }, this.Intermediate.Localizations.ToDictionary(l => l.Culture, StringComparer.OrdinalIgnoreCase), this.Intermediate.EmbedFilePaths);
533 intermediate.Save(Path.ChangeExtension(this.OutputPath, "wir"));
560 } 534 }
561 535
562 /// <summary> 536 /// <summary>
563 /// Localize dialogs and controls. 537 /// Localize dialogs and controls.
564 /// </summary> 538 /// </summary>
565 /// <param name="tables">The tables to localize.</param> 539 /// <param name="tables">The tables to localize.</param>
566 private void LocalizeUI(TableIndexedCollection tables) 540 private void LocalizeUI(IntermediateSection section)
567 { 541 {
568 Table dialogTable = tables["Dialog"]; 542 foreach (var row in section.Tuples.OfType<DialogTuple>())
569 if (null != dialogTable)
570 { 543 {
571 foreach (Row row in dialogTable.Rows) 544 string dialog = row.Dialog;
572 {
573 string dialog = (string)row[0];
574 545
575 if (this.WixVariableResolver.TryGetLocalizedControl(dialog, null, out LocalizedControl localizedControl)) 546 if (this.WixVariableResolver.TryGetLocalizedControl(dialog, null, out LocalizedControl localizedControl))
547 {
548 if (CompilerConstants.IntegerNotSet != localizedControl.X)
576 { 549 {
577 if (CompilerConstants.IntegerNotSet != localizedControl.X) 550 row.HCentering = localizedControl.X;
578 { 551 }
579 row[1] = localizedControl.X;
580 }
581 552
582 if (CompilerConstants.IntegerNotSet != localizedControl.Y) 553 if (CompilerConstants.IntegerNotSet != localizedControl.Y)
583 { 554 {
584 row[2] = localizedControl.Y; 555 row.VCentering = localizedControl.Y;
585 } 556 }
586 557
587 if (CompilerConstants.IntegerNotSet != localizedControl.Width) 558 if (CompilerConstants.IntegerNotSet != localizedControl.Width)
588 { 559 {
589 row[3] = localizedControl.Width; 560 row.Width = localizedControl.Width;
590 } 561 }
591 562
592 if (CompilerConstants.IntegerNotSet != localizedControl.Height) 563 if (CompilerConstants.IntegerNotSet != localizedControl.Height)
593 { 564 {
594 row[4] = localizedControl.Height; 565 row.Height = localizedControl.Height;
595 } 566 }
596 567
597 row[5] = (int)row[5] | localizedControl.Attributes; 568 row.Attributes = row.Attributes | localizedControl.Attributes;
598 569
599 if (!String.IsNullOrEmpty(localizedControl.Text)) 570 if (!String.IsNullOrEmpty(localizedControl.Text))
600 { 571 {
601 row[6] = localizedControl.Text; 572 row.Title = localizedControl.Text;
602 }
603 } 573 }
604 } 574 }
605 } 575 }
606 576
607 Table controlTable = tables["Control"]; 577
608 if (null != controlTable) 578 foreach (var row in section.Tuples.OfType<ControlTuple>())
609 { 579 {
610 foreach (Row row in controlTable.Rows) 580 string dialog = row.Dialog_;
611 { 581 string control = row.Control;
612 string dialog = (string)row[0];
613 string control = (string)row[1];
614 582
615 if (this.WixVariableResolver.TryGetLocalizedControl(dialog, control, out LocalizedControl localizedControl)) 583 if (this.WixVariableResolver.TryGetLocalizedControl(dialog, control, out LocalizedControl localizedControl))
584 {
585 if (CompilerConstants.IntegerNotSet != localizedControl.X)
616 { 586 {
617 if (CompilerConstants.IntegerNotSet != localizedControl.X) 587 row.X = localizedControl.X;
618 { 588 }
619 row[3] = localizedControl.X.ToString();
620 }
621 589
622 if (CompilerConstants.IntegerNotSet != localizedControl.Y) 590 if (CompilerConstants.IntegerNotSet != localizedControl.Y)
623 { 591 {
624 row[4] = localizedControl.Y.ToString(); 592 row.Y = localizedControl.Y;
625 } 593 }
626 594
627 if (CompilerConstants.IntegerNotSet != localizedControl.Width) 595 if (CompilerConstants.IntegerNotSet != localizedControl.Width)
628 { 596 {
629 row[5] = localizedControl.Width.ToString(); 597 row.Width = localizedControl.Width;
630 } 598 }
631 599
632 if (CompilerConstants.IntegerNotSet != localizedControl.Height) 600 if (CompilerConstants.IntegerNotSet != localizedControl.Height)
633 { 601 {
634 row[6] = localizedControl.Height.ToString(); 602 row.Height = localizedControl.Height;
635 } 603 }
636 604
637 row[7] = (int)row[7] | localizedControl.Attributes; 605 row.Attributes = row.Attributes | localizedControl.Attributes;
638 606
639 if (!String.IsNullOrEmpty(localizedControl.Text)) 607 if (!String.IsNullOrEmpty(localizedControl.Text))
640 { 608 {
641 row[9] = localizedControl.Text; 609 row.Text = localizedControl.Text;
642 }
643 } 610 }
644 } 611 }
645 } 612 }
646 } 613 }
647 614
615#if TODO_FINISH_PATCH
648 /// <summary> 616 /// <summary>
649 /// Copy file data between transform substorages and the patch output object 617 /// Copy file data between transform substorages and the patch output object
650 /// </summary> 618 /// </summary>
@@ -674,269 +642,10 @@ namespace WixToolset.Core.WindowsInstaller.Bind
674 command.TableDefinitions = this.TableDefinitions; 642 command.TableDefinitions = this.TableDefinitions;
675 command.Execute(); 643 command.Execute();
676 } 644 }
645#endif
677 646
678 private void UpdateMediaSequences(OutputType outputType, IEnumerable<FileFacade> fileFacades, RowDictionary<MediaRow> mediaRows)
679 {
680 // Calculate sequence numbers and media disk id layout for all file media information objects.
681 if (OutputType.Module == outputType)
682 {
683 int lastSequence = 0;
684 foreach (FileFacade facade in fileFacades) // TODO: Sort these rows directory path and component id and maybe file size or file extension and other creative ideas to get optimal install speed out of MSI.
685 {
686 facade.File.Sequence = ++lastSequence;
687 }
688 }
689 else
690 {
691 int lastSequence = 0;
692 MediaRow mediaRow = null;
693 Dictionary<int, List<FileFacade>> patchGroups = new Dictionary<int, List<FileFacade>>();
694
695 // sequence the non-patch-added files
696 foreach (FileFacade facade in fileFacades) // TODO: Sort these rows directory path and component id and maybe file size or file extension and other creative ideas to get optimal install speed out of MSI.
697 {
698 if (null == mediaRow)
699 {
700 mediaRow = mediaRows.Get(facade.WixFile.DiskId);
701 if (OutputType.Patch == outputType)
702 {
703 // patch Media cannot start at zero
704 lastSequence = mediaRow.LastSequence;
705 }
706 }
707 else if (mediaRow.DiskId != facade.WixFile.DiskId)
708 {
709 mediaRow.LastSequence = lastSequence;
710 mediaRow = mediaRows.Get(facade.WixFile.DiskId);
711 }
712
713 if (0 < facade.WixFile.PatchGroup)
714 {
715 List<FileFacade> patchGroup = patchGroups[facade.WixFile.PatchGroup];
716
717 if (null == patchGroup)
718 {
719 patchGroup = new List<FileFacade>();
720 patchGroups.Add(facade.WixFile.PatchGroup, patchGroup);
721 }
722
723 patchGroup.Add(facade);
724 }
725 else
726 {
727 facade.File.Sequence = ++lastSequence;
728 }
729 }
730
731 if (null != mediaRow)
732 {
733 mediaRow.LastSequence = lastSequence;
734 mediaRow = null;
735 }
736
737 // sequence the patch-added files
738 foreach (List<FileFacade> patchGroup in patchGroups.Values)
739 {
740 foreach (FileFacade facade in patchGroup)
741 {
742 if (null == mediaRow)
743 {
744 mediaRow = mediaRows.Get(facade.WixFile.DiskId);
745 }
746 else if (mediaRow.DiskId != facade.WixFile.DiskId)
747 {
748 mediaRow.LastSequence = lastSequence;
749 mediaRow = mediaRows.Get(facade.WixFile.DiskId);
750 }
751
752 facade.File.Sequence = ++lastSequence;
753 }
754 }
755
756 if (null != mediaRow)
757 {
758 mediaRow.LastSequence = lastSequence;
759 }
760 }
761 }
762
763 /// <summary>
764 /// Set the guids for components with generatable guids.
765 /// </summary>
766 /// <param name="output">Internal representation of the database to operate on.</param>
767 private void SetComponentGuids(Output output)
768 {
769 Table componentTable = output.Tables["Component"];
770 if (null != componentTable)
771 {
772 Hashtable registryKeyRows = null;
773 Hashtable directories = null;
774 Hashtable componentIdGenSeeds = null;
775 Dictionary<string, List<FileRow>> fileRows = null;
776
777 // find components with generatable guids
778 foreach (ComponentRow componentRow in componentTable.Rows)
779 {
780 // component guid will be generated
781 if ("*" == componentRow.Guid)
782 {
783 if (null == componentRow.KeyPath || componentRow.IsOdbcDataSourceKeyPath)
784 {
785 Messaging.Instance.OnMessage(WixErrors.IllegalComponentWithAutoGeneratedGuid(componentRow.SourceLineNumbers));
786 }
787 else if (componentRow.IsRegistryKeyPath)
788 {
789 if (null == registryKeyRows)
790 {
791 Table registryTable = output.Tables["Registry"];
792
793 registryKeyRows = new Hashtable(registryTable.Rows.Count);
794
795 foreach (Row registryRow in registryTable.Rows)
796 {
797 registryKeyRows.Add((string)registryRow[0], registryRow);
798 }
799 }
800
801 Row foundRow = registryKeyRows[componentRow.KeyPath] as Row;
802
803 string bitness = componentRow.Is64Bit ? "64" : String.Empty;
804 if (null != foundRow)
805 {
806 string regkey = String.Concat(bitness, foundRow[1], "\\", foundRow[2], "\\", foundRow[3]);
807 componentRow.Guid = Uuid.NewUuid(BindDatabaseCommand.WixComponentGuidNamespace, regkey.ToLowerInvariant()).ToString("B").ToUpperInvariant();
808 }
809 }
810 else // must be a File KeyPath
811 {
812 // if the directory table hasn't been loaded into an indexed hash
813 // of directory ids to target names do that now.
814 if (null == directories)
815 {
816 Table directoryTable = output.Tables["Directory"];
817
818 int numDirectoryTableRows = (null != directoryTable) ? directoryTable.Rows.Count : 0;
819
820 directories = new Hashtable(numDirectoryTableRows);
821
822 // get the target paths for all directories
823 if (null != directoryTable)
824 {
825 foreach (Row row in directoryTable.Rows)
826 {
827 // if the directory Id already exists, we will skip it here since
828 // checking for duplicate primary keys is done later when importing tables
829 // into database
830 if (directories.ContainsKey(row[0]))
831 {
832 continue;
833 }
834
835 string targetName = Common.GetName((string)row[2], false, true);
836 directories.Add(row[0], new ResolvedDirectory((string)row[1], targetName));
837 }
838 }
839 }
840
841 // if the component id generation seeds have not been indexed
842 // from the WixDirectory table do that now.
843 if (null == componentIdGenSeeds)
844 {
845 Table wixDirectoryTable = output.Tables["WixDirectory"];
846
847 int numWixDirectoryRows = (null != wixDirectoryTable) ? wixDirectoryTable.Rows.Count : 0;
848
849 componentIdGenSeeds = new Hashtable(numWixDirectoryRows);
850
851 // if there are any WixDirectory rows, build up the Component Guid
852 // generation seeds indexed by Directory/@Id.
853 if (null != wixDirectoryTable)
854 {
855 foreach (Row row in wixDirectoryTable.Rows)
856 {
857 componentIdGenSeeds.Add(row[0], (string)row[1]);
858 }
859 }
860 }
861
862 // if the file rows have not been indexed by File.Component yet
863 // then do that now
864 if (null == fileRows)
865 {
866 Table fileTable = output.Tables["File"];
867
868 int numFileRows = (null != fileTable) ? fileTable.Rows.Count : 0;
869
870 fileRows = new Dictionary<string, List<FileRow>>(numFileRows);
871
872 if (null != fileTable)
873 {
874 foreach (FileRow file in fileTable.Rows)
875 {
876 List<FileRow> files;
877 if (!fileRows.TryGetValue(file.Component, out files))
878 {
879 files = new List<FileRow>();
880 fileRows.Add(file.Component, files);
881 }
882
883 files.Add(file);
884 }
885 }
886 }
887
888 // validate component meets all the conditions to have a generated guid
889 List<FileRow> currentComponentFiles = fileRows[componentRow.Component];
890 int numFilesInComponent = currentComponentFiles.Count;
891 string path = null;
892
893 foreach (FileRow fileRow in currentComponentFiles)
894 {
895 if (fileRow.File == componentRow.KeyPath)
896 {
897 // calculate the key file's canonical target path
898 string directoryPath = Binder.GetDirectoryPath(directories, componentIdGenSeeds, componentRow.Directory, true);
899 string fileName = Common.GetName(fileRow.FileName, false, true).ToLower(CultureInfo.InvariantCulture);
900 path = Path.Combine(directoryPath, fileName);
901
902 // find paths that are not canonicalized
903 if (path.StartsWith(@"PersonalFolder\my pictures", StringComparison.Ordinal) ||
904 path.StartsWith(@"ProgramFilesFolder\common files", StringComparison.Ordinal) ||
905 path.StartsWith(@"ProgramMenuFolder\startup", StringComparison.Ordinal) ||
906 path.StartsWith("TARGETDIR", StringComparison.Ordinal) ||
907 path.StartsWith(@"StartMenuFolder\programs", StringComparison.Ordinal) ||
908 path.StartsWith(@"WindowsFolder\fonts", StringComparison.Ordinal))
909 {
910 Messaging.Instance.OnMessage(WixErrors.IllegalPathForGeneratedComponentGuid(componentRow.SourceLineNumbers, fileRow.Component, path));
911 }
912
913 // if component has more than one file, the key path must be versioned
914 if (1 < numFilesInComponent && String.IsNullOrEmpty(fileRow.Version))
915 {
916 Messaging.Instance.OnMessage(WixErrors.IllegalGeneratedGuidComponentUnversionedKeypath(componentRow.SourceLineNumbers));
917 }
918 }
919 else
920 {
921 // not a key path, so it must be an unversioned file if component has more than one file
922 if (1 < numFilesInComponent && !String.IsNullOrEmpty(fileRow.Version))
923 {
924 Messaging.Instance.OnMessage(WixErrors.IllegalGeneratedGuidComponentVersionedNonkeypath(componentRow.SourceLineNumbers));
925 }
926 }
927 }
928
929 // if the rules were followed, reward with a generated guid
930 if (!Messaging.Instance.EncounteredError)
931 {
932 componentRow.Guid = Uuid.NewUuid(BindDatabaseCommand.WixComponentGuidNamespace, path).ToString("B").ToUpperInvariant();
933 }
934 }
935 }
936 }
937 }
938 }
939 647
648#if TODO_FIX_INSTANCE_TRANSFORM
940 /// <summary> 649 /// <summary>
941 /// Creates instance transform substorages in the output. 650 /// Creates instance transform substorages in the output.
942 /// </summary> 651 /// </summary>
@@ -1171,6 +880,7 @@ namespace WixToolset.Core.WindowsInstaller.Bind
1171 } 880 }
1172 } 881 }
1173 } 882 }
883#endif
1174 884
1175 /// <summary> 885 /// <summary>
1176 /// Validate that there are no duplicate GUIDs in the output. 886 /// Validate that there are no duplicate GUIDs in the output.
@@ -1186,7 +896,7 @@ namespace WixToolset.Core.WindowsInstaller.Bind
1186 { 896 {
1187 Dictionary<string, bool> componentGuidConditions = new Dictionary<string, bool>(componentTable.Rows.Count); 897 Dictionary<string, bool> componentGuidConditions = new Dictionary<string, bool>(componentTable.Rows.Count);
1188 898
1189 foreach (ComponentRow row in componentTable.Rows) 899 foreach (Data.Rows.ComponentRow row in componentTable.Rows)
1190 { 900 {
1191 // we don't care about unmanaged components and if there's a * GUID remaining, 901 // we don't care about unmanaged components and if there's a * GUID remaining,
1192 // there's already an error that prevented it from being replaced with a real GUID. 902 // there's already an error that prevented it from being replaced with a real GUID.
@@ -1221,7 +931,7 @@ namespace WixToolset.Core.WindowsInstaller.Bind
1221 /// <param name="output">Internal representation of the msi database to operate upon.</param> 931 /// <param name="output">Internal representation of the msi database to operate upon.</param>
1222 private void UpdateControlText(Output output) 932 private void UpdateControlText(Output output)
1223 { 933 {
1224 UpdateControlTextCommand command = new UpdateControlTextCommand(); 934 var command = new UpdateControlTextCommand();
1225 command.BBControlTable = output.Tables["BBControl"]; 935 command.BBControlTable = output.Tables["BBControl"];
1226 command.WixBBControlTable = output.Tables["WixBBControl"]; 936 command.WixBBControlTable = output.Tables["WixBBControl"];
1227 command.ControlTable = output.Tables["Control"]; 937 command.ControlTable = output.Tables["Control"];
@@ -1229,7 +939,7 @@ namespace WixToolset.Core.WindowsInstaller.Bind
1229 command.Execute(); 939 command.Execute();
1230 } 940 }
1231 941
1232 private string ResolveMedia(MediaRow mediaRow, string mediaLayoutDirectory, string layoutDirectory) 942 private string ResolveMedia(MediaTuple mediaRow, string mediaLayoutDirectory, string layoutDirectory)
1233 { 943 {
1234 string layout = null; 944 string layout = null;
1235 945
@@ -1282,7 +992,6 @@ namespace WixToolset.Core.WindowsInstaller.Bind
1282 command.TempFilesLocation = this.IntermediateFolder; 992 command.TempFilesLocation = this.IntermediateFolder;
1283 command.Codepage = this.Codepage; 993 command.Codepage = this.Codepage;
1284 command.Execute(); 994 command.Execute();
1285#endif
1286 } 995 }
1287 } 996 }
1288} 997}
diff --git a/src/WixToolset.Core.WindowsInstaller/Bind/BindSummaryInfoCommand.cs b/src/WixToolset.Core.WindowsInstaller/Bind/BindSummaryInfoCommand.cs
index 5471792d..c9286a38 100644
--- a/src/WixToolset.Core.WindowsInstaller/Bind/BindSummaryInfoCommand.cs
+++ b/src/WixToolset.Core.WindowsInstaller/Bind/BindSummaryInfoCommand.cs
@@ -1,20 +1,24 @@
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
3namespace WixToolset.Core.WindowsInstaller.Databases 3namespace WixToolset.Core.WindowsInstaller.Bind
4{ 4{
5 using System; 5 using System;
6 using System.Globalization; 6 using System.Globalization;
7 using System.Linq;
7 using WixToolset.Data; 8 using WixToolset.Data;
9 using WixToolset.Data.Tuples;
8 10
9 /// <summary> 11 /// <summary>
10 /// Binds the summary information table of a database. 12 /// Binds the summary information table of a database.
11 /// </summary> 13 /// </summary>
12 internal class BindSummaryInfoCommand 14 internal class BindSummaryInfoCommand
13 { 15 {
14 /// <summary> 16 public BindSummaryInfoCommand(IntermediateSection section)
15 /// The output to bind. 17 {
16 /// </summary> 18 this.Section = section;
17 public Output Output { private get; set; } 19 }
20
21 private IntermediateSection Section { get; }
18 22
19 /// <summary> 23 /// <summary>
20 /// Returns a flag indicating if files are compressed by default. 24 /// Returns a flag indicating if files are compressed by default.
@@ -40,95 +44,97 @@ namespace WixToolset.Core.WindowsInstaller.Databases
40 this.InstallerVersion = 0; 44 this.InstallerVersion = 0;
41 this.ModularizationGuid = null; 45 this.ModularizationGuid = null;
42 46
43 Table summaryInformationTable = this.Output.Tables["_SummaryInformation"]; 47 bool foundCreateDataTime = false;
48 bool foundLastSaveDataTime = false;
49 bool foundCreatingApplication = false;
50 string now = DateTime.Now.ToString("yyyy/MM/dd HH:mm:ss", CultureInfo.InvariantCulture);
44 51
45 if (null != summaryInformationTable) 52 foreach (var summaryInformationRow in this.Section.Tuples.OfType<_SummaryInformationTuple>())
46 { 53 {
47 bool foundCreateDataTime = false; 54 switch (summaryInformationRow.PropertyId)
48 bool foundLastSaveDataTime = false;
49 bool foundCreatingApplication = false;
50 string now = DateTime.Now.ToString("yyyy/MM/dd HH:mm:ss", CultureInfo.InvariantCulture);
51
52 foreach (Row summaryInformationRow in summaryInformationTable.Rows)
53 { 55 {
54 switch (summaryInformationRow.FieldAsInteger(0)) 56 case 1: // PID_CODEPAGE
55 {
56 case 1: // PID_CODEPAGE
57 // make sure the code page is an int and not a web name or null 57 // make sure the code page is an int and not a web name or null
58 string codepage = summaryInformationRow.FieldAsString(1); 58 var codepage = summaryInformationRow.Value;
59
60 if (null == codepage)
61 {
62 codepage = "0";
63 }
64 else
65 {
66 summaryInformationRow[1] = Common.GetValidCodePage(codepage, false, false, summaryInformationRow.SourceLineNumbers).ToString(CultureInfo.InvariantCulture);
67 }
68 break;
69 case 9: // PID_REVNUMBER
70 string packageCode = (string)summaryInformationRow[1];
71
72 if (OutputType.Module == this.Output.Type)
73 {
74 this.ModularizationGuid = packageCode.Substring(1, 36).Replace('-', '_');
75 }
76 else if ("*" == packageCode)
77 {
78 // set the revision number (package/patch code) if it should be automatically generated
79 summaryInformationRow[1] = Common.GenerateGuid();
80 }
81 break;
82 case 12: // PID_CREATE_DTM
83 foundCreateDataTime = true;
84 break;
85 case 13: // PID_LASTSAVE_DTM
86 foundLastSaveDataTime = true;
87 break;
88 case 14:
89 this.InstallerVersion = summaryInformationRow.FieldAsInteger(1);
90 break;
91 case 15: // PID_WORDCOUNT
92 if (OutputType.Patch == this.Output.Type)
93 {
94 this.LongNames = true;
95 this.Compressed = true;
96 }
97 else
98 {
99 this.LongNames = (0 == (summaryInformationRow.FieldAsInteger(1) & 1));
100 this.Compressed = (2 == (summaryInformationRow.FieldAsInteger(1) & 2));
101 }
102 break;
103 case 18: // PID_APPNAME
104 foundCreatingApplication = true;
105 break;
106 }
107 }
108 59
109 // add a summary information row for the create time/date property if its not already set 60 if (String.IsNullOrEmpty(codepage))
110 if (!foundCreateDataTime) 61 {
111 { 62 codepage = "0";
112 Row createTimeDateRow = summaryInformationTable.CreateRow(null); 63 }
113 createTimeDateRow[0] = 12; 64 else
114 createTimeDateRow[1] = now; 65 {
115 } 66 summaryInformationRow.Value = Common.GetValidCodePage(codepage, false, false, summaryInformationRow.SourceLineNumbers).ToString(CultureInfo.InvariantCulture);
67 }
68 break;
69 case 9: // PID_REVNUMBER
70 var packageCode = summaryInformationRow.Value;
116 71
117 // add a summary information row for the last save time/date property if its not already set 72 if (SectionType.Module == this.Section.Type)
118 if (!foundLastSaveDataTime) 73 {
119 { 74 this.ModularizationGuid = packageCode.Substring(1, 36).Replace('-', '_');
120 Row lastSaveTimeDateRow = summaryInformationTable.CreateRow(null); 75 }
121 lastSaveTimeDateRow[0] = 13; 76 else if ("*" == packageCode)
122 lastSaveTimeDateRow[1] = now; 77 {
78 // set the revision number (package/patch code) if it should be automatically generated
79 summaryInformationRow.Value = Common.GenerateGuid();
80 }
81 break;
82 case 12: // PID_CREATE_DTM
83 foundCreateDataTime = true;
84 break;
85 case 13: // PID_LASTSAVE_DTM
86 foundLastSaveDataTime = true;
87 break;
88 case 14:
89 this.InstallerVersion = summaryInformationRow[_SummaryInformationTupleFields.Value].AsNumber();
90 break;
91 case 15: // PID_WORDCOUNT
92 if (SectionType.Patch == this.Section.Type)
93 {
94 this.LongNames = true;
95 this.Compressed = true;
96 }
97 else
98 {
99 var attributes = summaryInformationRow[_SummaryInformationTupleFields.Value].AsNumber();
100 this.LongNames = (0 == (attributes & 1));
101 this.Compressed = (2 == (attributes & 2));
102 }
103 break;
104 case 18: // PID_APPNAME
105 foundCreatingApplication = true;
106 break;
123 } 107 }
108 }
124 109
125 // add a summary information row for the creating application property if its not already set 110 // add a summary information row for the create time/date property if its not already set
126 if (!foundCreatingApplication) 111 if (!foundCreateDataTime)
127 { 112 {
128 Row creatingApplicationRow = summaryInformationTable.CreateRow(null); 113 var createTimeDateRow = new _SummaryInformationTuple(null, new Identifier(12, AccessModifier.Private));
129 creatingApplicationRow[0] = 18; 114 createTimeDateRow.PropertyId = 12;
130 creatingApplicationRow[1] = String.Format(CultureInfo.InvariantCulture, AppCommon.GetCreatingApplicationString()); 115 createTimeDateRow.Value = now;
131 } 116
117 this.Section.Tuples.Add(createTimeDateRow);
118 }
119
120 // add a summary information row for the last save time/date property if its not already set
121 if (!foundLastSaveDataTime)
122 {
123 var lastSaveTimeDateRow = new _SummaryInformationTuple(null, new Identifier(13, AccessModifier.Private));
124 lastSaveTimeDateRow.PropertyId = 13;
125 lastSaveTimeDateRow.Value = now;
126
127 this.Section.Tuples.Add(lastSaveTimeDateRow);
128 }
129
130 // add a summary information row for the creating application property if its not already set
131 if (!foundCreatingApplication)
132 {
133 var creatingApplicationRow = new _SummaryInformationTuple(null, new Identifier(18, AccessModifier.Private));
134 creatingApplicationRow.PropertyId = 18;
135 creatingApplicationRow.Value = String.Format(CultureInfo.InvariantCulture, AppCommon.GetCreatingApplicationString());
136
137 this.Section.Tuples.Add(creatingApplicationRow);
132 } 138 }
133 } 139 }
134 } 140 }
diff --git a/src/WixToolset.Core.WindowsInstaller/Bind/BindTransformCommand.cs b/src/WixToolset.Core.WindowsInstaller/Bind/BindTransformCommand.cs
index 425d1f9c..b4027834 100644
--- a/src/WixToolset.Core.WindowsInstaller/Bind/BindTransformCommand.cs
+++ b/src/WixToolset.Core.WindowsInstaller/Bind/BindTransformCommand.cs
@@ -1,6 +1,6 @@
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
3namespace WixToolset.Core.WindowsInstaller.Databases 3namespace WixToolset.Core.WindowsInstaller.Bind
4{ 4{
5 using System; 5 using System;
6 using System.Collections.Generic; 6 using System.Collections.Generic;
diff --git a/src/WixToolset.Core.WindowsInstaller/Bind/CabinetBuilder.cs b/src/WixToolset.Core.WindowsInstaller/Bind/CabinetBuilder.cs
index b2cc76fc..c25a497e 100644
--- a/src/WixToolset.Core.WindowsInstaller/Bind/CabinetBuilder.cs
+++ b/src/WixToolset.Core.WindowsInstaller/Bind/CabinetBuilder.cs
@@ -1,6 +1,6 @@
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
3namespace WixToolset.Core.WindowsInstaller.Databases 3namespace WixToolset.Core.WindowsInstaller.Bind
4{ 4{
5 using System; 5 using System;
6 using System.Collections; 6 using System.Collections;
diff --git a/src/WixToolset.Core.WindowsInstaller/Bind/CabinetWorkItem.cs b/src/WixToolset.Core.WindowsInstaller/Bind/CabinetWorkItem.cs
index dcafcd36..405b840b 100644
--- a/src/WixToolset.Core.WindowsInstaller/Bind/CabinetWorkItem.cs
+++ b/src/WixToolset.Core.WindowsInstaller/Bind/CabinetWorkItem.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
3namespace WixToolset.Core.WindowsInstaller.Databases 3namespace WixToolset.Core.WindowsInstaller.Bind
4{ 4{
5 using System.Collections.Generic; 5 using System.Collections.Generic;
6 using WixToolset.Core.Bind; 6 using WixToolset.Core.Bind;
7 using WixToolset.Data; 7 using WixToolset.Data;
8 using WixToolset.Data.Rows;
9 8
10 /// <summary> 9 /// <summary>
11 /// A cabinet builder work item. 10 /// A cabinet builder work item.
diff --git a/src/WixToolset.Core.WindowsInstaller/Bind/CalculateComponentGuids.cs b/src/WixToolset.Core.WindowsInstaller/Bind/CalculateComponentGuids.cs
new file mode 100644
index 00000000..0c0aea1f
--- /dev/null
+++ b/src/WixToolset.Core.WindowsInstaller/Bind/CalculateComponentGuids.cs
@@ -0,0 +1,174 @@
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
3namespace WixToolset.Core.WindowsInstaller.Bind
4{
5 using System;
6 using System.Collections.Generic;
7 using System.IO;
8 using System.Linq;
9 using WixToolset.Bind;
10 using WixToolset.Core.Native;
11 using WixToolset.Data;
12 using WixToolset.Data.Tuples;
13
14 /// <summary>
15 /// Set the guids for components with generatable guids.
16 /// </summary>
17 internal class CalculateComponentGuids
18 {
19 public CalculateComponentGuids(IntermediateSection section)
20 {
21 this.Section = section;
22 }
23
24 private IntermediateSection Section { get; }
25
26 public void Execute()
27 {
28 Dictionary<string, RegistryTuple> registryKeyRows = null;
29 Dictionary<string, ResolvedDirectory> targetPathsByDirectoryId = null;
30 Dictionary<string, string> componentIdGenSeeds = null;
31 Dictionary<string, List<FileTuple>> filesByComponentId = null;
32
33 // Find components with generatable guids.
34 foreach (var componentRow in this.Section.Tuples.OfType<ComponentTuple>())
35 {
36 // Skip components that do not specify generate guid.
37 if (componentRow.ComponentId != "*")
38 {
39 continue;
40 }
41
42 var odbcDataSourceKeyPath = (componentRow.Attributes & MsiInterop.MsidbComponentAttributesODBCDataSource) != 0;
43
44 if (String.IsNullOrEmpty(componentRow.KeyPath) || odbcDataSourceKeyPath)
45 {
46 Messaging.Instance.OnMessage(WixErrors.IllegalComponentWithAutoGeneratedGuid(componentRow.SourceLineNumbers));
47 continue;
48 }
49
50 var registryKeyPath = (componentRow.Attributes & MsiInterop.MsidbComponentAttributesRegistryKeyPath) != 0;
51
52 if (registryKeyPath)
53 {
54 if (registryKeyRows is null)
55 {
56 registryKeyRows = this.Section.Tuples.OfType<RegistryTuple>().ToDictionary(t => t.Registry);
57 }
58
59 if (registryKeyRows.TryGetValue(componentRow.KeyPath, out var foundRow))
60 {
61 var is64Bit = (componentRow.Attributes & MsiInterop.MsidbComponentAttributes64bit) != 0;
62 var bitness = is64Bit ? "64" : String.Empty;
63 var regkey = String.Concat(bitness, foundRow[1], "\\", foundRow[2], "\\", foundRow[3]);
64 componentRow.ComponentId = Uuid.NewUuid(BindDatabaseCommand.WixComponentGuidNamespace, regkey.ToLowerInvariant()).ToString("B").ToUpperInvariant();
65 }
66 }
67 else // must be a File KeyPath.
68 {
69 // If the directory table hasn't been loaded into an indexed hash
70 // of directory ids to target names do that now.
71 if (targetPathsByDirectoryId is null)
72 {
73 var directories = this.Section.Tuples.OfType<DirectoryTuple>().ToList();
74
75 targetPathsByDirectoryId = new Dictionary<string, ResolvedDirectory>(directories.Count);
76
77 // Get the target paths for all directories.
78 foreach (var row in directories)
79 {
80 // If the directory Id already exists, we will skip it here since
81 // checking for duplicate primary keys is done later when importing tables
82 // into database
83 if (targetPathsByDirectoryId.ContainsKey(row.Directory))
84 {
85 continue;
86 }
87
88 var targetName = Common.GetName(row.DefaultDir, false, true);
89 targetPathsByDirectoryId.Add(row.Directory, new ResolvedDirectory(row.Directory_Parent, targetName));
90 }
91 }
92
93 // If the component id generation seeds have not been indexed
94 // from the WixDirectory table do that now.
95 if (componentIdGenSeeds is null)
96 {
97 // If there are any WixDirectory rows, build up the Component Guid
98 // generation seeds indexed by Directory/@Id.
99 componentIdGenSeeds = this.Section.Tuples.OfType<WixDirectoryTuple>()
100 .Where(t => !String.IsNullOrEmpty(t.ComponentGuidGenerationSeed))
101 .ToDictionary(t => t.Directory_, t => t.ComponentGuidGenerationSeed);
102 }
103
104 // if the file rows have not been indexed by File.Component yet
105 // then do that now
106 if (filesByComponentId is null)
107 {
108 var files = this.Section.Tuples.OfType<FileTuple>().ToList();
109
110 filesByComponentId = new Dictionary<string, List<FileTuple>>(files.Count);
111
112 foreach (var file in files)
113 {
114 if (!filesByComponentId.TryGetValue(file.Component_, out var componentFiles))
115 {
116 componentFiles = new List<FileTuple>();
117 filesByComponentId.Add(file.Component_, componentFiles);
118 }
119
120 componentFiles.Add(file);
121 }
122 }
123
124 // validate component meets all the conditions to have a generated guid
125 var currentComponentFiles = filesByComponentId[componentRow.Component];
126 var numFilesInComponent = currentComponentFiles.Count;
127 string path = null;
128
129 foreach (var fileRow in currentComponentFiles)
130 {
131 if (fileRow.File == componentRow.KeyPath)
132 {
133 // calculate the key file's canonical target path
134 string directoryPath = Binder.GetDirectoryPath(targetPathsByDirectoryId, componentIdGenSeeds, componentRow.Directory_, true);
135 string fileName = Common.GetName(fileRow.LongFileName, false, true).ToLowerInvariant();
136 path = Path.Combine(directoryPath, fileName);
137
138 // find paths that are not canonicalized
139 if (path.StartsWith(@"PersonalFolder\my pictures", StringComparison.Ordinal) ||
140 path.StartsWith(@"ProgramFilesFolder\common files", StringComparison.Ordinal) ||
141 path.StartsWith(@"ProgramMenuFolder\startup", StringComparison.Ordinal) ||
142 path.StartsWith("TARGETDIR", StringComparison.Ordinal) ||
143 path.StartsWith(@"StartMenuFolder\programs", StringComparison.Ordinal) ||
144 path.StartsWith(@"WindowsFolder\fonts", StringComparison.Ordinal))
145 {
146 Messaging.Instance.OnMessage(WixErrors.IllegalPathForGeneratedComponentGuid(componentRow.SourceLineNumbers, fileRow.Component_, path));
147 }
148
149 // if component has more than one file, the key path must be versioned
150 if (1 < numFilesInComponent && String.IsNullOrEmpty(fileRow.Version))
151 {
152 Messaging.Instance.OnMessage(WixErrors.IllegalGeneratedGuidComponentUnversionedKeypath(componentRow.SourceLineNumbers));
153 }
154 }
155 else
156 {
157 // not a key path, so it must be an unversioned file if component has more than one file
158 if (1 < numFilesInComponent && !String.IsNullOrEmpty(fileRow.Version))
159 {
160 Messaging.Instance.OnMessage(WixErrors.IllegalGeneratedGuidComponentVersionedNonkeypath(componentRow.SourceLineNumbers));
161 }
162 }
163 }
164
165 // if the rules were followed, reward with a generated guid
166 if (!Messaging.Instance.EncounteredError)
167 {
168 componentRow.ComponentId = Uuid.NewUuid(BindDatabaseCommand.WixComponentGuidNamespace, path).ToString("B").ToUpperInvariant();
169 }
170 }
171 }
172 }
173 }
174}
diff --git a/src/WixToolset.Core.WindowsInstaller/Bind/ConfigurationCallback.cs b/src/WixToolset.Core.WindowsInstaller/Bind/ConfigurationCallback.cs
index d4d3799f..9a8e2bba 100644
--- a/src/WixToolset.Core.WindowsInstaller/Bind/ConfigurationCallback.cs
+++ b/src/WixToolset.Core.WindowsInstaller/Bind/ConfigurationCallback.cs
@@ -1,6 +1,6 @@
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
3namespace WixToolset.Core.WindowsInstaller.Databases 3namespace WixToolset.Core.WindowsInstaller.Bind
4{ 4{
5 using System; 5 using System;
6 using System.Collections; 6 using System.Collections;
diff --git a/src/WixToolset.Core.WindowsInstaller/Bind/CopyTransformDataCommand.cs b/src/WixToolset.Core.WindowsInstaller/Bind/CopyTransformDataCommand.cs
index 6388a352..0dcddb99 100644
--- a/src/WixToolset.Core.WindowsInstaller/Bind/CopyTransformDataCommand.cs
+++ b/src/WixToolset.Core.WindowsInstaller/Bind/CopyTransformDataCommand.cs
@@ -1,6 +1,6 @@
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
3namespace WixToolset.Core.WindowsInstaller.Databases 3namespace WixToolset.Core.WindowsInstaller.Bind
4{ 4{
5 using System; 5 using System;
6 using System.Collections.Generic; 6 using System.Collections.Generic;
@@ -10,6 +10,7 @@ namespace WixToolset.Core.WindowsInstaller.Databases
10 using WixToolset.Extensibility; 10 using WixToolset.Extensibility;
11 using WixToolset.Core.Native; 11 using WixToolset.Core.Native;
12 using WixToolset.Core.Bind; 12 using WixToolset.Core.Bind;
13 using WixToolset.Data.Tuples;
13 14
14 internal class CopyTransformDataCommand 15 internal class CopyTransformDataCommand
15 { 16 {
@@ -29,7 +30,7 @@ namespace WixToolset.Core.WindowsInstaller.Databases
29 30
30 List<FileFacade> allFileRows = this.CopyOutFileRows ? new List<FileFacade>() : null; 31 List<FileFacade> allFileRows = this.CopyOutFileRows ? new List<FileFacade>() : null;
31 32
32#if false // TODO: Fix this patching related code to work correctly with FileFacades. 33#if REVISIT_FOR_PATCHING // TODO: Fix this patching related code to work correctly with FileFacades.
33 bool copyToPatch = (allFileRows != null); 34 bool copyToPatch = (allFileRows != null);
34 bool copyFromPatch = !copyToPatch; 35 bool copyFromPatch = !copyToPatch;
35 36
@@ -454,7 +455,7 @@ namespace WixToolset.Core.WindowsInstaller.Databases
454 } 455 }
455 456
456 Row patchAction = iesTable.CreateRow(null); 457 Row patchAction = iesTable.CreateRow(null);
457 WixActionRow wixPatchAction = WindowsInstallerStandard.GetStandardActions()[table, "PatchFiles"]; 458 WixActionRow wixPatchAction = WindowsInstallerStandardInternal.GetStandardActionRows()[table, "PatchFiles"];
458 int sequence = wixPatchAction.Sequence; 459 int sequence = wixPatchAction.Sequence;
459 // Test for default sequence value's appropriateness 460 // Test for default sequence value's appropriateness
460 if (seqInstallFiles >= sequence || (0 != seqDuplicateFiles && seqDuplicateFiles <= sequence)) 461 if (seqInstallFiles >= sequence || (0 != seqDuplicateFiles && seqDuplicateFiles <= sequence))
diff --git a/src/WixToolset.Core.WindowsInstaller/Bind/CreateCabinetsCommand.cs b/src/WixToolset.Core.WindowsInstaller/Bind/CreateCabinetsCommand.cs
index 02015744..b5a436c5 100644
--- a/src/WixToolset.Core.WindowsInstaller/Bind/CreateCabinetsCommand.cs
+++ b/src/WixToolset.Core.WindowsInstaller/Bind/CreateCabinetsCommand.cs
@@ -1,6 +1,6 @@
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
3namespace WixToolset.Core.WindowsInstaller.Databases 3namespace WixToolset.Core.WindowsInstaller.Bind
4{ 4{
5 using System; 5 using System;
6 using System.Collections.Generic; 6 using System.Collections.Generic;
@@ -10,10 +10,10 @@ namespace WixToolset.Core.WindowsInstaller.Databases
10 using System.Runtime.InteropServices; 10 using System.Runtime.InteropServices;
11 using System.Threading; 11 using System.Threading;
12 using WixToolset.Core.Bind; 12 using WixToolset.Core.Bind;
13 using WixToolset.Core.WindowsInstaller.Bind;
14 using WixToolset.Data; 13 using WixToolset.Data;
15 using WixToolset.Data.Bind; 14 using WixToolset.Data.Bind;
16 using WixToolset.Data.Rows; 15 using WixToolset.Data.Rows;
16 using WixToolset.Data.Tuples;
17 using WixToolset.Extensibility; 17 using WixToolset.Extensibility;
18 18
19 /// <summary> 19 /// <summary>
@@ -60,9 +60,9 @@ namespace WixToolset.Core.WindowsInstaller.Databases
60 60
61 public bool Compressed { private get; set; } 61 public bool Compressed { private get; set; }
62 62
63 public Dictionary<MediaRow, IEnumerable<FileFacade>> FileRowsByCabinet { private get; set; } 63 public Dictionary<MediaTuple, IEnumerable<FileFacade>> FileRowsByCabinet { private get; set; }
64 64
65 public Func<MediaRow, string, string, string> ResolveMedia { private get; set; } 65 public Func<MediaTuple, string, string, string> ResolveMedia { private get; set; }
66 66
67 public TableDefinitionCollection TableDefinitions { private get; set; } 67 public TableDefinitionCollection TableDefinitions { private get; set; }
68 68
@@ -77,7 +77,7 @@ namespace WixToolset.Core.WindowsInstaller.Databases
77 /// <returns>The uncompressed file rows.</returns> 77 /// <returns>The uncompressed file rows.</returns>
78 public void Execute() 78 public void Execute()
79 { 79 {
80 RowDictionary<WixMediaRow> wixMediaRows = new RowDictionary<WixMediaRow>(this.WixMediaTable); 80 var wixMediaRows = new RowDictionary<WixMediaRow>(this.WixMediaTable);
81 81
82 this.lastCabinetAddedToMediaTable = new Dictionary<string, string>(); 82 this.lastCabinetAddedToMediaTable = new Dictionary<string, string>();
83 83
@@ -87,22 +87,19 @@ namespace WixToolset.Core.WindowsInstaller.Databases
87 CabinetBuilder cabinetBuilder = new CabinetBuilder(this.CabbingThreadCount, Marshal.GetFunctionPointerForDelegate(this.newCabNamesCallBack)); 87 CabinetBuilder cabinetBuilder = new CabinetBuilder(this.CabbingThreadCount, Marshal.GetFunctionPointerForDelegate(this.newCabNamesCallBack));
88 88
89 // Supply Compile MediaTemplate Attributes to Cabinet Builder 89 // Supply Compile MediaTemplate Attributes to Cabinet Builder
90 int MaximumCabinetSizeForLargeFileSplitting; 90 this.GetMediaTemplateAttributes(out var MaximumCabinetSizeForLargeFileSplitting, out var MaximumUncompressedMediaSize);
91 int MaximumUncompressedMediaSize;
92 this.GetMediaTemplateAttributes(out MaximumCabinetSizeForLargeFileSplitting, out MaximumUncompressedMediaSize);
93 cabinetBuilder.MaximumCabinetSizeForLargeFileSplitting = MaximumCabinetSizeForLargeFileSplitting; 91 cabinetBuilder.MaximumCabinetSizeForLargeFileSplitting = MaximumCabinetSizeForLargeFileSplitting;
94 cabinetBuilder.MaximumUncompressedMediaSize = MaximumUncompressedMediaSize; 92 cabinetBuilder.MaximumUncompressedMediaSize = MaximumUncompressedMediaSize;
95 93
96 foreach (var entry in this.FileRowsByCabinet) 94 foreach (var entry in this.FileRowsByCabinet)
97 { 95 {
98 MediaRow mediaRow = entry.Key; 96 var mediaRow = entry.Key;
99 IEnumerable<FileFacade> files = entry.Value; 97 IEnumerable<FileFacade> files = entry.Value;
100 CompressionLevel compressionLevel = this.DefaultCompressionLevel; 98 CompressionLevel compressionLevel = this.DefaultCompressionLevel;
101 99
102 WixMediaRow wixMediaRow = null;
103 string mediaLayoutFolder = null; 100 string mediaLayoutFolder = null;
104 101
105 if (wixMediaRows.TryGetValue(mediaRow.GetKey(), out wixMediaRow)) 102 if (wixMediaRows.TryGetValue(mediaRow.Id.Id, out var wixMediaRow))
106 { 103 {
107 mediaLayoutFolder = wixMediaRow.Layout; 104 mediaLayoutFolder = wixMediaRow.Layout;
108 105
@@ -185,7 +182,7 @@ namespace WixToolset.Core.WindowsInstaller.Databases
185 /// <param name="fileFacades">Collection of files in this cabinet.</param> 182 /// <param name="fileFacades">Collection of files in this cabinet.</param>
186 /// <param name="fileTransfers">Array of files to be transfered.</param> 183 /// <param name="fileTransfers">Array of files to be transfered.</param>
187 /// <returns>created CabinetWorkItem object</returns> 184 /// <returns>created CabinetWorkItem object</returns>
188 private CabinetWorkItem CreateCabinetWorkItem(Output output, string cabinetDir, MediaRow mediaRow, CompressionLevel compressionLevel, IEnumerable<FileFacade> fileFacades, List<FileTransfer> fileTransfers) 185 private CabinetWorkItem CreateCabinetWorkItem(Output output, string cabinetDir, MediaTuple mediaRow, CompressionLevel compressionLevel, IEnumerable<FileFacade> fileFacades, List<FileTransfer> fileTransfers)
189 { 186 {
190 CabinetWorkItem cabinetWorkItem = null; 187 CabinetWorkItem cabinetWorkItem = null;
191 string tempCabinetFileX = Path.Combine(this.TempFilesLocation, mediaRow.Cabinet); 188 string tempCabinetFileX = Path.Combine(this.TempFilesLocation, mediaRow.Cabinet);
@@ -254,8 +251,7 @@ namespace WixToolset.Core.WindowsInstaller.Databases
254 else 251 else
255 { 252 {
256 string destinationPath = Path.Combine(cabinetDir, mediaRow.Cabinet); 253 string destinationPath = Path.Combine(cabinetDir, mediaRow.Cabinet);
257 FileTransfer transfer; 254 if (FileTransfer.TryCreate(resolvedCabinet.Path, destinationPath, CabinetBuildOption.BuildAndMove == resolvedCabinet.BuildOption, "Cabinet", mediaRow.SourceLineNumbers, out var transfer))
258 if (FileTransfer.TryCreate(resolvedCabinet.Path, destinationPath, CabinetBuildOption.BuildAndMove == resolvedCabinet.BuildOption, "Cabinet", mediaRow.SourceLineNumbers, out transfer))
259 { 255 {
260 transfer.Built = true; 256 transfer.Built = true;
261 fileTransfers.Add(transfer); 257 fileTransfers.Add(transfer);
diff --git a/src/WixToolset.Core.WindowsInstaller/Bind/CreateDeltaPatchesCommand.cs b/src/WixToolset.Core.WindowsInstaller/Bind/CreateDeltaPatchesCommand.cs
index 42a69310..7c7b07cc 100644
--- a/src/WixToolset.Core.WindowsInstaller/Bind/CreateDeltaPatchesCommand.cs
+++ b/src/WixToolset.Core.WindowsInstaller/Bind/CreateDeltaPatchesCommand.cs
@@ -1,6 +1,6 @@
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
3namespace WixToolset.Core.WindowsInstaller.Databases 3namespace WixToolset.Core.WindowsInstaller.Bind
4{ 4{
5 using System; 5 using System;
6 using System.Collections.Generic; 6 using System.Collections.Generic;
@@ -9,49 +9,40 @@ namespace WixToolset.Core.WindowsInstaller.Databases
9 using WixToolset.Core.Bind; 9 using WixToolset.Core.Bind;
10 using WixToolset.Data; 10 using WixToolset.Data;
11 using WixToolset.Data.Rows; 11 using WixToolset.Data.Rows;
12 using WixToolset.Data.Tuples;
12 13
13 /// <summary> 14 /// <summary>
14 /// Creates delta patches and updates the appropriate rows to point to the newly generated patches. 15 /// Creates delta patches and updates the appropriate rows to point to the newly generated patches.
15 /// </summary> 16 /// </summary>
16 internal class CreateDeltaPatchesCommand 17 internal class CreateDeltaPatchesCommand
17 { 18 {
18 public IEnumerable<FileFacade> FileFacades { private get; set; } 19 public CreateDeltaPatchesCommand(List<FileFacade> fileFacades, string intermediateFolder, WixPatchIdTuple wixPatchId)
20 {
21 this.FileFacades = fileFacades;
22 this.IntermediateFolder = intermediateFolder;
23 this.WixPatchId = wixPatchId;
24 }
19 25
20 public Table WixPatchIdTable { private get; set; } 26 private IEnumerable<FileFacade> FileFacades { get; }
21 27
22 public string TempFilesLocation { private get; set; } 28 private WixPatchIdTuple WixPatchId { get; }
29
30 private string IntermediateFolder { get; }
23 31
24 public void Execute() 32 public void Execute()
25 { 33 {
26#if REVISIT_FOR_PATCHING 34 var optimizePatchSizeForLargeFiles = this.WixPatchId?.OptimizePatchSizeForLargeFiles ?? false;
27 bool optimizePatchSizeForLargeFiles = false; 35 var apiPatchingSymbolFlags = (PatchSymbolFlagsType)(this.WixPatchId?.ApiPatchingSymbolFlags ?? 0);
28 PatchSymbolFlagsType apiPatchingSymbolFlags = 0;
29
30 if (null != this.WixPatchIdTable)
31 {
32 Row row = this.WixPatchIdTable.Rows[0];
33 if (null != row)
34 {
35 if (null != row[2])
36 {
37 optimizePatchSizeForLargeFiles = (1 == Convert.ToUInt32(row[2], CultureInfo.InvariantCulture));
38 }
39
40 if (null != row[3])
41 {
42 apiPatchingSymbolFlags = (PatchSymbolFlagsType)Convert.ToUInt32(row[3], CultureInfo.InvariantCulture);
43 }
44 }
45 }
46 36
37#if REVISIT_FOR_PATCHING
47 foreach (FileFacade facade in this.FileFacades) 38 foreach (FileFacade facade in this.FileFacades)
48 { 39 {
49 if (RowOperation.Modify == facade.File.Operation && 40 if (RowOperation.Modify == facade.File.Operation &&
50 0 != (facade.WixFile.PatchAttributes & PatchAttributeType.IncludeWholeFile)) 41 0 != (facade.WixFile.PatchAttributes & PatchAttributeType.IncludeWholeFile))
51 { 42 {
52 string deltaBase = String.Concat("delta_", facade.File.File); 43 string deltaBase = String.Concat("delta_", facade.File.File);
53 string deltaFile = Path.Combine(this.TempFilesLocation, String.Concat(deltaBase, ".dpf")); 44 string deltaFile = Path.Combine(this.IntermediateFolder, String.Concat(deltaBase, ".dpf"));
54 string headerFile = Path.Combine(this.TempFilesLocation, String.Concat(deltaBase, ".phd")); 45 string headerFile = Path.Combine(this.IntermediateFolder, String.Concat(deltaBase, ".phd"));
55 46
56 bool retainRangeWarning = false; 47 bool retainRangeWarning = false;
57 48
@@ -84,6 +75,7 @@ namespace WixToolset.Core.WindowsInstaller.Databases
84 } 75 }
85 } 76 }
86#endif 77#endif
78
87 throw new NotImplementedException(); 79 throw new NotImplementedException();
88 } 80 }
89 } 81 }
diff --git a/src/WixToolset.Core.WindowsInstaller/Bind/CreateOutputFromIRCommand.cs b/src/WixToolset.Core.WindowsInstaller/Bind/CreateOutputFromIRCommand.cs
new file mode 100644
index 00000000..85b3b25a
--- /dev/null
+++ b/src/WixToolset.Core.WindowsInstaller/Bind/CreateOutputFromIRCommand.cs
@@ -0,0 +1,230 @@
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
3namespace WixToolset.Core.WindowsInstaller.Bind
4{
5 using System;
6 using System.Linq;
7 using WixToolset.Core.Native;
8 using WixToolset.Data;
9 using WixToolset.Data.Rows;
10 using WixToolset.Data.Tuples;
11
12 internal class CreateOutputFromIRCommand
13 {
14 public CreateOutputFromIRCommand(IntermediateSection section, TableDefinitionCollection tableDefinitions)
15 {
16 this.Section = section;
17 this.TableDefinitions = tableDefinitions;
18 }
19
20 private TableDefinitionCollection TableDefinitions { get; }
21
22 private IntermediateSection Section { get; }
23
24 public Output Output { get; private set; }
25
26 public void Execute()
27 {
28 var output = new Output(this.Section.Tuples.First().SourceLineNumbers);
29 output.Codepage = this.Section.Codepage;
30 output.Type = SectionTypeToOutputType(this.Section.Type);
31
32 this.AddSectionToOutput(this.Section, output);
33
34 this.Output = output;
35 }
36
37 private void AddSectionToOutput(IntermediateSection section, Output output)
38 {
39 foreach (var tuple in section.Tuples)
40 {
41 switch (tuple.Definition.Type)
42 {
43 case TupleDefinitionType.File:
44 this.AddFileTuple((FileTuple)tuple, output);
45 break;
46
47 case TupleDefinitionType.WixAction:
48 this.AddWixActionTuple((WixActionTuple)tuple, output);
49 break;
50
51 default:
52 this.AddTupleDefaultly(tuple, output);
53 break;
54 }
55 }
56 }
57
58 private void AddFileTuple(FileTuple tuple, Output output)
59 {
60 var table = output.EnsureTable(this.TableDefinitions["File"]);
61 var row = (FileRow)table.CreateRow(tuple.SourceLineNumbers);
62 row.File = tuple.File;
63 row.Component = tuple.Component_;
64 row.FileName = GetMsiFilenameValue(tuple.ShortFileName, tuple.LongFileName);
65 row.FileSize = tuple.FileSize;
66 row.Version = tuple.Version;
67 row.Language = tuple.Language;
68
69 var attributes = tuple.Checksum ? MsiInterop.MsidbFileAttributesChecksum : 0;
70 attributes |= (tuple.Compressed.HasValue && tuple.Compressed.Value) ? MsiInterop.MsidbFileAttributesCompressed : 0;
71 attributes |= (tuple.Compressed.HasValue && !tuple.Compressed.Value) ? MsiInterop.MsidbFileAttributesNoncompressed : 0;
72 attributes |= tuple.Hidden ? MsiInterop.MsidbFileAttributesHidden : 0;
73 attributes |= tuple.ReadOnly ? MsiInterop.MsidbFileAttributesReadOnly : 0;
74 attributes |= tuple.System ? MsiInterop.MsidbFileAttributesSystem : 0;
75 attributes |= tuple.Vital ? MsiInterop.MsidbFileAttributesVital : 0;
76 row.Attributes = attributes;
77 }
78
79 private void AddWixActionTuple(WixActionTuple actionRow, Output output)
80 {
81 // Get the table definition for the action (and ensure the proper table exists for a module).
82 TableDefinition sequenceTableDefinition = null;
83 switch (actionRow.SequenceTable)
84 {
85 case SequenceTable.AdminExecuteSequence:
86 if (OutputType.Module == output.Type)
87 {
88 output.EnsureTable(this.TableDefinitions["AdminExecuteSequence"]);
89 sequenceTableDefinition = this.TableDefinitions["ModuleAdminExecuteSequence"];
90 }
91 else
92 {
93 sequenceTableDefinition = this.TableDefinitions["AdminExecuteSequence"];
94 }
95 break;
96 case SequenceTable.AdminUISequence:
97 if (OutputType.Module == output.Type)
98 {
99 output.EnsureTable(this.TableDefinitions["AdminUISequence"]);
100 sequenceTableDefinition = this.TableDefinitions["ModuleAdminUISequence"];
101 }
102 else
103 {
104 sequenceTableDefinition = this.TableDefinitions["AdminUISequence"];
105 }
106 break;
107 case SequenceTable.AdvtExecuteSequence:
108 if (OutputType.Module == output.Type)
109 {
110 output.EnsureTable(this.TableDefinitions["AdvtExecuteSequence"]);
111 sequenceTableDefinition = this.TableDefinitions["ModuleAdvtExecuteSequence"];
112 }
113 else
114 {
115 sequenceTableDefinition = this.TableDefinitions["AdvtExecuteSequence"];
116 }
117 break;
118 case SequenceTable.InstallExecuteSequence:
119 if (OutputType.Module == output.Type)
120 {
121 output.EnsureTable(this.TableDefinitions["InstallExecuteSequence"]);
122 sequenceTableDefinition = this.TableDefinitions["ModuleInstallExecuteSequence"];
123 }
124 else
125 {
126 sequenceTableDefinition = this.TableDefinitions["InstallExecuteSequence"];
127 }
128 break;
129 case SequenceTable.InstallUISequence:
130 if (OutputType.Module == output.Type)
131 {
132 output.EnsureTable(this.TableDefinitions["InstallUISequence"]);
133 sequenceTableDefinition = this.TableDefinitions["ModuleInstallUISequence"];
134 }
135 else
136 {
137 sequenceTableDefinition = this.TableDefinitions["InstallUISequence"];
138 }
139 break;
140 }
141
142 // create the action sequence row in the output
143 var sequenceTable = output.EnsureTable(sequenceTableDefinition);
144 var row = sequenceTable.CreateRow(actionRow.SourceLineNumbers);
145
146 if (SectionType.Module == this.Section.Type)
147 {
148 row[0] = actionRow.Action;
149 if (0 != actionRow.Sequence)
150 {
151 row[1] = actionRow.Sequence;
152 }
153 else
154 {
155 bool after = (null == actionRow.Before);
156 row[2] = after ? actionRow.After : actionRow.Before;
157 row[3] = after ? 1 : 0;
158 }
159 row[4] = actionRow.Condition;
160 }
161 else
162 {
163 row[0] = actionRow.Action;
164 row[1] = actionRow.Condition;
165 row[2] = actionRow.Sequence;
166 }
167 }
168
169 private void AddTupleDefaultly(IntermediateTuple tuple, Output output)
170 {
171 if (!this.TableDefinitions.TryGet(tuple.Definition.Name, out var tableDefinition))
172 {
173 return;
174 }
175
176 var table = output.EnsureTable(tableDefinition);
177 var row = table.CreateRow(tuple.SourceLineNumbers);
178 for (var i = 0; i < tuple.Fields.Length; ++i)
179 {
180 if (i < tableDefinition.Columns.Count)
181 {
182 var column = tableDefinition.Columns[i];
183
184 switch (column.Type)
185 {
186 case ColumnType.Number:
187 row[i] = tuple.AsNumber(i);
188 break;
189
190 default:
191 row[i] = tuple.AsString(i);
192 break;
193 }
194 }
195 }
196 }
197
198 private static OutputType SectionTypeToOutputType(SectionType type)
199 {
200 switch (type)
201 {
202 case SectionType.Bundle:
203 return OutputType.Bundle;
204 case SectionType.Module:
205 return OutputType.Module;
206 case SectionType.Product:
207 return OutputType.Product;
208 case SectionType.PatchCreation:
209 return OutputType.PatchCreation;
210 case SectionType.Patch:
211 return OutputType.Patch;
212
213 default:
214 throw new ArgumentOutOfRangeException(nameof(type));
215 }
216 }
217
218 private static string GetMsiFilenameValue(string shortName, string longName)
219 {
220 if (String.IsNullOrEmpty(shortName))
221 {
222 return longName;
223 }
224 else
225 {
226 return shortName + "|" + longName;
227 }
228 }
229 }
230}
diff --git a/src/WixToolset.Core.WindowsInstaller/Bind/CreateSpecialPropertiesCommand.cs b/src/WixToolset.Core.WindowsInstaller/Bind/CreateSpecialPropertiesCommand.cs
index aef130b0..ab2e8201 100644
--- a/src/WixToolset.Core.WindowsInstaller/Bind/CreateSpecialPropertiesCommand.cs
+++ b/src/WixToolset.Core.WindowsInstaller/Bind/CreateSpecialPropertiesCommand.cs
@@ -1,67 +1,72 @@
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
3namespace WixToolset.Core.WindowsInstaller.Databases 3namespace WixToolset.Core.WindowsInstaller.Bind
4{ 4{
5 using System; 5 using System;
6 using System.Collections.Generic; 6 using System.Collections.Generic;
7 using System.Linq;
7 using WixToolset.Data; 8 using WixToolset.Data;
8 using WixToolset.Data.Rows; 9 using WixToolset.Data.Tuples;
9 10
10 internal class CreateSpecialPropertiesCommand 11 internal class CreateSpecialPropertiesCommand
11 { 12 {
12 public Table PropertyTable { private get; set; } 13 public CreateSpecialPropertiesCommand(IntermediateSection section)
14 {
15 this.Section = section;
16 }
13 17
14 public Table WixPropertyTable { private get; set; } 18 private IntermediateSection Section { get; }
15 19
16 public void Execute() 20 public void Execute()
17 { 21 {
18 // Create the special properties. 22 // Create lists of the properties that contribute to the special lists of properties.
19 if (null != this.WixPropertyTable) 23 SortedSet<string> adminProperties = new SortedSet<string>();
20 { 24 SortedSet<string> secureProperties = new SortedSet<string>();
21 // Create lists of the properties that contribute to the special lists of properties. 25 SortedSet<string> hiddenProperties = new SortedSet<string>();
22 SortedSet<string> adminProperties = new SortedSet<string>();
23 SortedSet<string> secureProperties = new SortedSet<string>();
24 SortedSet<string> hiddenProperties = new SortedSet<string>();
25 26
26 foreach (WixPropertyRow wixPropertyRow in this.WixPropertyTable.Rows) 27 foreach (var wixPropertyRow in this.Section.Tuples.OfType<WixPropertyTuple>())
28 {
29 if (wixPropertyRow.Admin)
27 { 30 {
28 if (wixPropertyRow.Admin) 31 adminProperties.Add(wixPropertyRow.Property_);
29 {
30 adminProperties.Add(wixPropertyRow.Id);
31 }
32
33 if (wixPropertyRow.Hidden)
34 {
35 hiddenProperties.Add(wixPropertyRow.Id);
36 }
37
38 if (wixPropertyRow.Secure)
39 {
40 secureProperties.Add(wixPropertyRow.Id);
41 }
42 } 32 }
43 33
44 Table propertyTable = this.PropertyTable; 34 if (wixPropertyRow.Hidden)
45 if (0 < adminProperties.Count)
46 { 35 {
47 PropertyRow row = (PropertyRow)propertyTable.CreateRow(null); 36 hiddenProperties.Add(wixPropertyRow.Property_);
48 row.Property = "AdminProperties";
49 row.Value = String.Join(";", adminProperties);
50 } 37 }
51 38
52 if (0 < secureProperties.Count) 39 if (wixPropertyRow.Secure)
53 { 40 {
54 PropertyRow row = (PropertyRow)propertyTable.CreateRow(null); 41 secureProperties.Add(wixPropertyRow.Property_);
55 row.Property = "SecureCustomProperties";
56 row.Value = String.Join(";", secureProperties);
57 } 42 }
43 }
58 44
59 if (0 < hiddenProperties.Count) 45 if (0 < adminProperties.Count)
60 { 46 {
61 PropertyRow row = (PropertyRow)propertyTable.CreateRow(null); 47 var tuple = new PropertyTuple(null, new Identifier("AdminProperties", AccessModifier.Private));
62 row.Property = "MsiHiddenProperties"; 48 tuple.Property = "AdminProperties";
63 row.Value = String.Join(";", hiddenProperties); 49 tuple.Value = String.Join(";", adminProperties);
64 } 50
51 this.Section.Tuples.Add(tuple);
52 }
53
54 if (0 < secureProperties.Count)
55 {
56 var tuple = new PropertyTuple(null, new Identifier("SecureCustomProperties", AccessModifier.Private));
57 tuple.Property = "SecureCustomProperties";
58 tuple.Value = String.Join(";", secureProperties);
59
60 this.Section.Tuples.Add(tuple);
61 }
62
63 if (0 < hiddenProperties.Count)
64 {
65 var tuple = new PropertyTuple(null, new Identifier("MsiHiddenProperties", AccessModifier.Private));
66 tuple.Property = "MsiHiddenProperties";
67 tuple.Value = String.Join(";", hiddenProperties);
68
69 this.Section.Tuples.Add(tuple);
65 } 70 }
66 } 71 }
67 } 72 }
diff --git a/src/WixToolset.Core.WindowsInstaller/Bind/ExtractMergeModuleFilesCommand.cs b/src/WixToolset.Core.WindowsInstaller/Bind/ExtractMergeModuleFilesCommand.cs
index 0d3e7bd1..32d1cfda 100644
--- a/src/WixToolset.Core.WindowsInstaller/Bind/ExtractMergeModuleFilesCommand.cs
+++ b/src/WixToolset.Core.WindowsInstaller/Bind/ExtractMergeModuleFilesCommand.cs
@@ -1,6 +1,6 @@
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
3namespace WixToolset.Core.WindowsInstaller.Databases 3namespace WixToolset.Core.WindowsInstaller.Bind
4{ 4{
5 using System; 5 using System;
6 using System.Collections.Generic; 6 using System.Collections.Generic;
@@ -10,7 +10,6 @@ namespace WixToolset.Core.WindowsInstaller.Databases
10 using System.Linq; 10 using System.Linq;
11 using System.Runtime.InteropServices; 11 using System.Runtime.InteropServices;
12 using WixToolset.Data; 12 using WixToolset.Data;
13 using WixToolset.Data.Rows;
14 using WixToolset.MergeMod; 13 using WixToolset.MergeMod;
15 using WixToolset.Msi; 14 using WixToolset.Msi;
16 using WixToolset.Core.Native; 15 using WixToolset.Core.Native;
@@ -23,13 +22,17 @@ namespace WixToolset.Core.WindowsInstaller.Databases
23 /// </summary> 22 /// </summary>
24 internal class ExtractMergeModuleFilesCommand 23 internal class ExtractMergeModuleFilesCommand
25 { 24 {
26 public IEnumerable<FileFacade> FileFacades { private get; set; } 25 public ExtractMergeModuleFilesCommand(IntermediateSection section, List<WixMergeTuple> wixMergeTuples)
26 {
27 this.Section = section;
28 this.WixMergeTuples = wixMergeTuples;
29 }
27 30
28 public Table FileTable { private get; set; } 31 private IntermediateSection Section { get; }
29 32
30 public Table WixFileTable { private get; set; } 33 private List<WixMergeTuple> WixMergeTuples { get; }
31 34
32 public Table WixMergeTable { private get; set; } 35 public IEnumerable<FileFacade> FileFacades { private get; set; }
33 36
34 public int OutputInstallerVersion { private get; set; } 37 public int OutputInstallerVersion { private get; set; }
35 38
@@ -41,7 +44,7 @@ namespace WixToolset.Core.WindowsInstaller.Databases
41 44
42 public void Execute() 45 public void Execute()
43 { 46 {
44 List<FileFacade> mergeModulesFileFacades = new List<FileFacade>(); 47 var mergeModulesFileFacades = new List<FileFacade>();
45 48
46 IMsmMerge2 merge = MsmInterop.GetMsmMerge(); 49 IMsmMerge2 merge = MsmInterop.GetMsmMerge();
47 50
@@ -52,9 +55,9 @@ namespace WixToolset.Core.WindowsInstaller.Databases
52 // Now since Merge Modules are already slow and generally less desirable than .wixlibs we'll let 55 // Now since Merge Modules are already slow and generally less desirable than .wixlibs we'll let
53 // this case be slightly more expensive because the cost of maintaining an indexed file row collection 56 // this case be slightly more expensive because the cost of maintaining an indexed file row collection
54 // is a lot more costly for the common cases. 57 // is a lot more costly for the common cases.
55 Dictionary<string, FileFacade> indexedFileFacades = this.FileFacades.ToDictionary(f => f.File.File, StringComparer.Ordinal); 58 var indexedFileFacades = this.FileFacades.ToDictionary(f => f.File.File, StringComparer.Ordinal);
56 59
57 foreach (WixMergeRow wixMergeRow in this.WixMergeTable.Rows) 60 foreach (var wixMergeRow in this.WixMergeTuples)
58 { 61 {
59 bool containsFiles = this.CreateFacadesForMergeModuleFiles(wixMergeRow, mergeModulesFileFacades, indexedFileFacades); 62 bool containsFiles = this.CreateFacadesForMergeModuleFiles(wixMergeRow, mergeModulesFileFacades, indexedFileFacades);
60 63
@@ -68,7 +71,7 @@ namespace WixToolset.Core.WindowsInstaller.Databases
68 this.MergeModulesFileFacades = mergeModulesFileFacades; 71 this.MergeModulesFileFacades = mergeModulesFileFacades;
69 } 72 }
70 73
71 private bool CreateFacadesForMergeModuleFiles(WixMergeRow wixMergeRow, List<FileFacade> mergeModulesFileFacades, Dictionary<string, FileFacade> indexedFileFacades) 74 private bool CreateFacadesForMergeModuleFiles(WixMergeTuple wixMergeRow, List<FileFacade> mergeModulesFileFacades, Dictionary<string, FileFacade> indexedFileFacades)
72 { 75 {
73 bool containsFiles = false; 76 bool containsFiles = false;
74 77
@@ -98,7 +101,7 @@ namespace WixToolset.Core.WindowsInstaller.Databases
98 // rows are created by merging in the actual modules. 101 // rows are created by merging in the actual modules.
99 var fileRow = new FileTuple(wixMergeRow.SourceLineNumbers, new Identifier(record[1], AccessModifier.Private)); 102 var fileRow = new FileTuple(wixMergeRow.SourceLineNumbers, new Identifier(record[1], AccessModifier.Private));
100 fileRow.File = record[1]; 103 fileRow.File = record[1];
101 fileRow.Compressed = (wixMergeRow.FileCompression == YesNoType.Yes) ? true : (wixMergeRow.FileCompression == YesNoType.No) ? (bool?)false : null; 104 fileRow.Compressed = wixMergeRow.FileCompression;
102 //FileRow fileRow = (FileRow)this.FileTable.CreateRow(wixMergeRow.SourceLineNumbers, false); 105 //FileRow fileRow = (FileRow)this.FileTable.CreateRow(wixMergeRow.SourceLineNumbers, false);
103 //fileRow.File = record[1]; 106 //fileRow.File = record[1];
104 //fileRow.Compressed = wixMergeRow.FileCompression; 107 //fileRow.Compressed = wixMergeRow.FileCompression;
@@ -107,7 +110,7 @@ namespace WixToolset.Core.WindowsInstaller.Databases
107 wixFileRow.Directory_ = record[2]; 110 wixFileRow.Directory_ = record[2];
108 wixFileRow.DiskId = wixMergeRow.DiskId; 111 wixFileRow.DiskId = wixMergeRow.DiskId;
109 wixFileRow.PatchGroup = -1; 112 wixFileRow.PatchGroup = -1;
110 wixFileRow.Source = Path.Combine(this.IntermediateFolder, "MergeId.", wixMergeRow.Number.ToString(CultureInfo.InvariantCulture), record[1]); 113 wixFileRow.Source = Path.Combine(this.IntermediateFolder, wixMergeRow.Id.Id, record[1]);
111 //WixFileRow wixFileRow = (WixFileRow)this.WixFileTable.CreateRow(wixMergeRow.SourceLineNumbers, false); 114 //WixFileRow wixFileRow = (WixFileRow)this.WixFileTable.CreateRow(wixMergeRow.SourceLineNumbers, false);
112 //wixFileRow.Directory = record[2]; 115 //wixFileRow.Directory = record[2];
113 //wixFileRow.DiskId = wixMergeRow.DiskId; 116 //wixFileRow.DiskId = wixMergeRow.DiskId;
@@ -119,11 +122,11 @@ namespace WixToolset.Core.WindowsInstaller.Databases
119 // If case-sensitive collision with another merge module or a user-authored file identifier. 122 // If case-sensitive collision with another merge module or a user-authored file identifier.
120 if (indexedFileFacades.TryGetValue(mergeModuleFileFacade.File.File, out var collidingFacade)) 123 if (indexedFileFacades.TryGetValue(mergeModuleFileFacade.File.File, out var collidingFacade))
121 { 124 {
122 Messaging.Instance.OnMessage(WixErrors.DuplicateModuleFileIdentifier(wixMergeRow.SourceLineNumbers, wixMergeRow.Id, collidingFacade.File.File)); 125 Messaging.Instance.OnMessage(WixErrors.DuplicateModuleFileIdentifier(wixMergeRow.SourceLineNumbers, wixMergeRow.Id.Id, collidingFacade.File.File));
123 } 126 }
124 else if (uniqueModuleFileIdentifiers.TryGetValue(mergeModuleFileFacade.File.File, out collidingFacade)) // case-insensitive collision with another file identifier in the same merge module 127 else if (uniqueModuleFileIdentifiers.TryGetValue(mergeModuleFileFacade.File.File, out collidingFacade)) // case-insensitive collision with another file identifier in the same merge module
125 { 128 {
126 Messaging.Instance.OnMessage(WixErrors.DuplicateModuleCaseInsensitiveFileIdentifier(wixMergeRow.SourceLineNumbers, wixMergeRow.Id, mergeModuleFileFacade.File.File, collidingFacade.File.File)); 129 Messaging.Instance.OnMessage(WixErrors.DuplicateModuleCaseInsensitiveFileIdentifier(wixMergeRow.SourceLineNumbers, wixMergeRow.Id.Id, mergeModuleFileFacade.File.File, collidingFacade.File.File));
127 } 130 }
128 else // no collision 131 else // no collision
129 { 132 {
@@ -150,12 +153,12 @@ namespace WixToolset.Core.WindowsInstaller.Databases
150 int moduleInstallerVersion = Convert.ToInt32(moduleInstallerVersionString, CultureInfo.InvariantCulture); 153 int moduleInstallerVersion = Convert.ToInt32(moduleInstallerVersionString, CultureInfo.InvariantCulture);
151 if (moduleInstallerVersion > this.OutputInstallerVersion) 154 if (moduleInstallerVersion > this.OutputInstallerVersion)
152 { 155 {
153 Messaging.Instance.OnMessage(WixWarnings.InvalidHigherInstallerVersionInModule(wixMergeRow.SourceLineNumbers, wixMergeRow.Id, moduleInstallerVersion, this.OutputInstallerVersion)); 156 Messaging.Instance.OnMessage(WixWarnings.InvalidHigherInstallerVersionInModule(wixMergeRow.SourceLineNumbers, wixMergeRow.Id.Id, moduleInstallerVersion, this.OutputInstallerVersion));
154 } 157 }
155 } 158 }
156 catch (FormatException) 159 catch (FormatException)
157 { 160 {
158 throw new WixException(WixErrors.MissingOrInvalidModuleInstallerVersion(wixMergeRow.SourceLineNumbers, wixMergeRow.Id, wixMergeRow.SourceFile, moduleInstallerVersionString)); 161 throw new WixException(WixErrors.MissingOrInvalidModuleInstallerVersion(wixMergeRow.SourceLineNumbers, wixMergeRow.Id.Id, wixMergeRow.SourceFile, moduleInstallerVersionString));
159 } 162 }
160 } 163 }
161 } 164 }
@@ -166,24 +169,26 @@ namespace WixToolset.Core.WindowsInstaller.Databases
166 } 169 }
167 catch (Win32Exception) 170 catch (Win32Exception)
168 { 171 {
169 throw new WixException(WixErrors.CannotOpenMergeModule(wixMergeRow.SourceLineNumbers, wixMergeRow.Id, wixMergeRow.SourceFile)); 172 throw new WixException(WixErrors.CannotOpenMergeModule(wixMergeRow.SourceLineNumbers, wixMergeRow.Id.Id, wixMergeRow.SourceFile));
170 } 173 }
171 174
172 return containsFiles; 175 return containsFiles;
173 } 176 }
174 177
175 private void ExtractFilesFromMergeModule(IMsmMerge2 merge, WixMergeRow wixMergeRow) 178 private void ExtractFilesFromMergeModule(IMsmMerge2 merge, WixMergeTuple wixMergeRow)
176 { 179 {
177 bool moduleOpen = false; 180 bool moduleOpen = false;
178 short mergeLanguage; 181 short mergeLanguage;
179 182
183 var mergeId = wixMergeRow.Id.Id;
184
180 try 185 try
181 { 186 {
182 mergeLanguage = Convert.ToInt16(wixMergeRow.Language, CultureInfo.InvariantCulture); 187 mergeLanguage = Convert.ToInt16(wixMergeRow.Language, CultureInfo.InvariantCulture);
183 } 188 }
184 catch (System.FormatException) 189 catch (FormatException)
185 { 190 {
186 Messaging.Instance.OnMessage(WixErrors.InvalidMergeLanguage(wixMergeRow.SourceLineNumbers, wixMergeRow.Id, wixMergeRow.Language)); 191 Messaging.Instance.OnMessage(WixErrors.InvalidMergeLanguage(wixMergeRow.SourceLineNumbers, mergeId, wixMergeRow.Language.ToString()));
187 return; 192 return;
188 } 193 }
189 194
@@ -192,13 +197,11 @@ namespace WixToolset.Core.WindowsInstaller.Databases
192 merge.OpenModule(wixMergeRow.SourceFile, mergeLanguage); 197 merge.OpenModule(wixMergeRow.SourceFile, mergeLanguage);
193 moduleOpen = true; 198 moduleOpen = true;
194 199
195 string safeMergeId = wixMergeRow.Number.ToString(CultureInfo.InvariantCulture.NumberFormat);
196
197 // extract the module cabinet, then explode all of the files to a temp directory 200 // extract the module cabinet, then explode all of the files to a temp directory
198 string moduleCabPath = String.Concat(this.IntermediateFolder, Path.DirectorySeparatorChar, safeMergeId, ".module.cab"); 201 string moduleCabPath = Path.Combine(this.IntermediateFolder, mergeId + ".cab");
199 merge.ExtractCAB(moduleCabPath); 202 merge.ExtractCAB(moduleCabPath);
200 203
201 string mergeIdPath = String.Concat(this.IntermediateFolder, Path.DirectorySeparatorChar, "MergeId.", safeMergeId); 204 string mergeIdPath = Path.Combine(this.IntermediateFolder, mergeId);
202 Directory.CreateDirectory(mergeIdPath); 205 Directory.CreateDirectory(mergeIdPath);
203 206
204 using (var extractCab = new WixExtractCab()) 207 using (var extractCab = new WixExtractCab())
diff --git a/src/WixToolset.Core.WindowsInstaller/Bind/GenerateDatabaseCommand.cs b/src/WixToolset.Core.WindowsInstaller/Bind/GenerateDatabaseCommand.cs
index 47b58058..a3d3ecf7 100644
--- a/src/WixToolset.Core.WindowsInstaller/Bind/GenerateDatabaseCommand.cs
+++ b/src/WixToolset.Core.WindowsInstaller/Bind/GenerateDatabaseCommand.cs
@@ -1,6 +1,6 @@
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
3namespace WixToolset.Core.WindowsInstaller.Databases 3namespace WixToolset.Core.WindowsInstaller.Bind
4{ 4{
5 using System; 5 using System;
6 using System.Collections.Generic; 6 using System.Collections.Generic;
diff --git a/src/WixToolset.Core.WindowsInstaller/Bind/GetFileFacadesCommand.cs b/src/WixToolset.Core.WindowsInstaller/Bind/GetFileFacadesCommand.cs
index 9bbb4763..70ba971f 100644
--- a/src/WixToolset.Core.WindowsInstaller/Bind/GetFileFacadesCommand.cs
+++ b/src/WixToolset.Core.WindowsInstaller/Bind/GetFileFacadesCommand.cs
@@ -1,6 +1,6 @@
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
3namespace WixToolset.Core.WindowsInstaller.Databases 3namespace WixToolset.Core.WindowsInstaller.Bind
4{ 4{
5 using System; 5 using System;
6 using System.Collections.Generic; 6 using System.Collections.Generic;
@@ -8,57 +8,50 @@ namespace WixToolset.Core.WindowsInstaller.Databases
8 using System.Linq; 8 using System.Linq;
9 using WixToolset.Core.Bind; 9 using WixToolset.Core.Bind;
10 using WixToolset.Data; 10 using WixToolset.Data;
11 using WixToolset.Data.Rows; 11 using WixToolset.Data.Tuples;
12 12
13 internal class GetFileFacadesCommand 13 internal class GetFileFacadesCommand
14 { 14 {
15 public Table FileTable { private get; set; } 15 public GetFileFacadesCommand(IntermediateSection section)
16 16 {
17 public Table WixFileTable { private get; set; } 17 this.Section = section;
18 18 }
19 public Table WixDeltaPatchFileTable { private get; set; }
20 19
21 public Table WixDeltaPatchSymbolPathsTable { private get; set; } 20 private IntermediateSection Section { get; }
22 21
23 public List<FileFacade> FileFacades { get; private set; } 22 public List<FileFacade> FileFacades { get; private set; }
24 23
25 public void Execute() 24 public void Execute()
26 { 25 {
27 throw new NotImplementedException(); 26 var facades = new List<FileFacade>();
28#if TODO
29 List<FileFacade> facades = new List<FileFacade>(this.FileTable.Rows.Count);
30 27
31 RowDictionary<WixFileRow> wixFiles = new RowDictionary<WixFileRow>(this.WixFileTable); 28 var wixFiles = this.Section.Tuples.OfType<WixFileTuple>().ToDictionary(t => t.File_);
32 RowDictionary<WixDeltaPatchFileRow> deltaPatchFiles = new RowDictionary<WixDeltaPatchFileRow>(this.WixDeltaPatchFileTable); 29 var deltaPatchFiles = this.Section.Tuples.OfType<WixDeltaPatchFileTuple>().ToDictionary(t => t.File_);
33 30
34 foreach (FileRow file in this.FileTable.Rows) 31 foreach (var file in this.Section.Tuples.OfType<FileTuple>())
35 { 32 {
36 WixDeltaPatchFileRow deltaPatchFile = null; 33 var wixFile = wixFiles[file.File];
37 34
38 deltaPatchFiles.TryGetValue(file.File, out deltaPatchFile); 35 deltaPatchFiles.TryGetValue(file.File, out var deltaPatchFile);
39 36
40 facades.Add(new FileFacade(file, wixFiles[file.File], deltaPatchFile)); 37 facades.Add(new FileFacade(file, wixFile, deltaPatchFile));
41 } 38 }
42 39
43 if (null != this.WixDeltaPatchSymbolPathsTable) 40 this.ResolveDeltaPatchSymbolPaths(deltaPatchFiles, facades);
44 {
45 this.ResolveDeltaPatchSymbolPaths(deltaPatchFiles, facades);
46 }
47 41
48 this.FileFacades = facades; 42 this.FileFacades = facades;
49#endif
50 } 43 }
51 44
52 /// <summary> 45 /// <summary>
53 /// Merge data from the WixPatchSymbolPaths rows into the WixDeltaPatchFile rows. 46 /// Merge data from the WixPatchSymbolPaths rows into the WixDeltaPatchFile rows.
54 /// </summary> 47 /// </summary>
55 public RowDictionary<WixDeltaPatchFileRow> ResolveDeltaPatchSymbolPaths(RowDictionary<WixDeltaPatchFileRow> deltaPatchFiles, IEnumerable<FileFacade> facades) 48 public void ResolveDeltaPatchSymbolPaths(Dictionary<string, WixDeltaPatchFileTuple> deltaPatchFiles, IEnumerable<FileFacade> facades)
56 { 49 {
57 ILookup<string, FileFacade> filesByComponent = null; 50 ILookup<string, FileFacade> filesByComponent = null;
58 ILookup<string, FileFacade> filesByDirectory = null; 51 ILookup<string, FileFacade> filesByDirectory = null;
59 ILookup<string, FileFacade> filesByDiskId = null; 52 ILookup<string, FileFacade> filesByDiskId = null;
60 53
61 foreach (WixDeltaPatchSymbolPathsRow row in this.WixDeltaPatchSymbolPathsTable.RowsAs<WixDeltaPatchSymbolPathsRow>().OrderBy(r => r.Type)) 54 foreach (var row in this.Section.Tuples.OfType<WixDeltaPatchSymbolPathsTuple>().OrderBy(r => r.Type))
62 { 55 {
63 switch (row.Type) 56 switch (row.Type)
64 { 57 {
@@ -72,7 +65,7 @@ namespace WixToolset.Core.WindowsInstaller.Databases
72 filesByComponent = facades.ToLookup(f => f.File.Component_); 65 filesByComponent = facades.ToLookup(f => f.File.Component_);
73 } 66 }
74 67
75 foreach (FileFacade facade in filesByComponent[row.Id]) 68 foreach (var facade in filesByComponent[row.Id])
76 { 69 {
77 this.MergeSymbolPaths(row, deltaPatchFiles[facade.File.File]); 70 this.MergeSymbolPaths(row, deltaPatchFiles[facade.File.File]);
78 } 71 }
@@ -84,7 +77,7 @@ namespace WixToolset.Core.WindowsInstaller.Databases
84 filesByDirectory = facades.ToLookup(f => f.WixFile.Directory_); 77 filesByDirectory = facades.ToLookup(f => f.WixFile.Directory_);
85 } 78 }
86 79
87 foreach (FileFacade facade in filesByDirectory[row.Id]) 80 foreach (var facade in filesByDirectory[row.Id])
88 { 81 {
89 this.MergeSymbolPaths(row, deltaPatchFiles[facade.File.File]); 82 this.MergeSymbolPaths(row, deltaPatchFiles[facade.File.File]);
90 } 83 }
@@ -96,14 +89,14 @@ namespace WixToolset.Core.WindowsInstaller.Databases
96 filesByDiskId = facades.ToLookup(f => f.WixFile.DiskId.ToString(CultureInfo.InvariantCulture)); 89 filesByDiskId = facades.ToLookup(f => f.WixFile.DiskId.ToString(CultureInfo.InvariantCulture));
97 } 90 }
98 91
99 foreach (FileFacade facade in filesByDiskId[row.Id]) 92 foreach (var facade in filesByDiskId[row.Id])
100 { 93 {
101 this.MergeSymbolPaths(row, deltaPatchFiles[facade.File.File]); 94 this.MergeSymbolPaths(row, deltaPatchFiles[facade.File.File]);
102 } 95 }
103 break; 96 break;
104 97
105 case SymbolPathType.Product: 98 case SymbolPathType.Product:
106 foreach (WixDeltaPatchFileRow fileRow in deltaPatchFiles.Values) 99 foreach (var fileRow in deltaPatchFiles.Values)
107 { 100 {
108 this.MergeSymbolPaths(row, fileRow); 101 this.MergeSymbolPaths(row, fileRow);
109 } 102 }
@@ -114,8 +107,6 @@ namespace WixToolset.Core.WindowsInstaller.Databases
114 break; 107 break;
115 } 108 }
116 } 109 }
117
118 return deltaPatchFiles;
119 } 110 }
120 111
121 /// <summary> 112 /// <summary>
@@ -124,17 +115,18 @@ namespace WixToolset.Core.WindowsInstaller.Databases
124 /// <param name="row">Row from the WixPatchSymbolsPaths table.</param> 115 /// <param name="row">Row from the WixPatchSymbolsPaths table.</param>
125 /// <param name="file">FileRow into which to set symbol information.</param> 116 /// <param name="file">FileRow into which to set symbol information.</param>
126 /// <comment>This includes PreviousData as well.</comment> 117 /// <comment>This includes PreviousData as well.</comment>
127 private void MergeSymbolPaths(WixDeltaPatchSymbolPathsRow row, WixDeltaPatchFileRow file) 118 private void MergeSymbolPaths(WixDeltaPatchSymbolPathsTuple row, WixDeltaPatchFileTuple file)
128 { 119 {
129 if (null == file.Symbols) 120 if (file.SymbolPaths is null)
130 { 121 {
131 file.Symbols = row.SymbolPaths; 122 file.SymbolPaths = row.SymbolPaths;
132 } 123 }
133 else 124 else
134 { 125 {
135 file.Symbols = String.Concat(file.Symbols, ";", row.SymbolPaths); 126 file.SymbolPaths = String.Concat(file.SymbolPaths, ";", row.SymbolPaths);
136 } 127 }
137 128
129#if REVISIT_FOR_PATCHING
138 Field field = row.Fields[2]; 130 Field field = row.Fields[2];
139 if (null != field.PreviousData) 131 if (null != field.PreviousData)
140 { 132 {
@@ -147,6 +139,7 @@ namespace WixToolset.Core.WindowsInstaller.Databases
147 file.PreviousSymbols = String.Concat(file.PreviousSymbols, ";", field.PreviousData); 139 file.PreviousSymbols = String.Concat(file.PreviousSymbols, ";", field.PreviousData);
148 } 140 }
149 } 141 }
142#endif
150 } 143 }
151 } 144 }
152} 145}
diff --git a/src/WixToolset.Core.WindowsInstaller/Bind/MergeModulesCommand.cs b/src/WixToolset.Core.WindowsInstaller/Bind/MergeModulesCommand.cs
index f1605eca..dcf67c05 100644
--- a/src/WixToolset.Core.WindowsInstaller/Bind/MergeModulesCommand.cs
+++ b/src/WixToolset.Core.WindowsInstaller/Bind/MergeModulesCommand.cs
@@ -1,20 +1,13 @@
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
3namespace WixToolset.Core.WindowsInstaller.Databases 3namespace WixToolset.Core.WindowsInstaller.Bind
4{ 4{
5 using System; 5 using System;
6 using System.Collections.Generic; 6 using System.Collections.Generic;
7 using System.Collections.Specialized;
8 using System.ComponentModel;
9 using System.Diagnostics;
10 using System.Globalization; 7 using System.Globalization;
11 using System.IO; 8 using System.IO;
12 using System.Linq;
13 using System.Runtime.InteropServices; 9 using System.Runtime.InteropServices;
14 using System.Text; 10 using System.Text;
15 using System.Xml;
16 using System.Xml.XPath;
17 using WixToolset.Clr.Interop;
18 using WixToolset.Data; 11 using WixToolset.Data;
19 using WixToolset.Data.Rows; 12 using WixToolset.Data.Rows;
20 using WixToolset.MergeMod; 13 using WixToolset.MergeMod;
@@ -35,12 +28,10 @@ namespace WixToolset.Core.WindowsInstaller.Databases
35 28
36 public IEnumerable<string> SuppressedTableNames { private get; set; } 29 public IEnumerable<string> SuppressedTableNames { private get; set; }
37 30
38 public string TempFilesLocation { private get; set; } 31 public string IntermediateFolder { private get; set; }
39 32
40 public void Execute() 33 public void Execute()
41 { 34 {
42 Debug.Assert(OutputType.Product == this.Output.Type);
43
44 Table wixMergeTable = this.Output.Tables["WixMerge"]; 35 Table wixMergeTable = this.Output.Tables["WixMerge"];
45 Table wixFeatureModulesTable = this.Output.Tables["WixFeatureModules"]; 36 Table wixFeatureModulesTable = this.Output.Tables["WixFeatureModules"];
46 37
@@ -59,7 +50,7 @@ namespace WixToolset.Core.WindowsInstaller.Databases
59 { 50 {
60 merge = MsmInterop.GetMsmMerge(); 51 merge = MsmInterop.GetMsmMerge();
61 52
62 logPath = Path.Combine(this.TempFilesLocation, "merge.log"); 53 logPath = Path.Combine(this.IntermediateFolder, "merge.log");
63 merge.OpenLog(logPath); 54 merge.OpenLog(logPath);
64 logOpen = true; 55 logOpen = true;
65 56
@@ -79,7 +70,7 @@ namespace WixToolset.Core.WindowsInstaller.Databases
79 { 70 {
80 mergeLanguage = Convert.ToInt16(wixMergeRow.Language, CultureInfo.InvariantCulture); 71 mergeLanguage = Convert.ToInt16(wixMergeRow.Language, CultureInfo.InvariantCulture);
81 } 72 }
82 catch (System.FormatException) 73 catch (FormatException)
83 { 74 {
84 Messaging.Instance.OnMessage(WixErrors.InvalidMergeLanguage(wixMergeRow.SourceLineNumbers, wixMergeRow.Id, wixMergeRow.Language)); 75 Messaging.Instance.OnMessage(WixErrors.InvalidMergeLanguage(wixMergeRow.SourceLineNumbers, wixMergeRow.Id, wixMergeRow.Language));
85 continue; 76 continue;
@@ -284,7 +275,7 @@ namespace WixToolset.Core.WindowsInstaller.Databases
284 Messaging.Instance.OnMessage(WixVerboses.ResequencingMergeModuleFiles()); 275 Messaging.Instance.OnMessage(WixVerboses.ResequencingMergeModuleFiles());
285 using (View view = db.OpenView("SELECT `Sequence`, `Attributes` FROM `File` WHERE `File`=?")) 276 using (View view = db.OpenView("SELECT `Sequence`, `Attributes` FROM `File` WHERE `File`=?"))
286 { 277 {
287 foreach (FileFacade file in this.FileFacades) 278 foreach (var file in this.FileFacades)
288 { 279 {
289 if (!file.FromModule) 280 if (!file.FromModule)
290 { 281 {
@@ -307,32 +298,29 @@ namespace WixToolset.Core.WindowsInstaller.Databases
307 //recordUpdate.SetInteger(1, file.File.Sequence); 298 //recordUpdate.SetInteger(1, file.File.Sequence);
308 throw new NotImplementedException(); 299 throw new NotImplementedException();
309 300
310 // update the file attributes to match the compression specified 301 // Update the file attributes to match the compression specified
311 // on the Merge element or on the Package element 302 // on the Merge element or on the Package element.
312 int attributes = 0; 303 var attributes = 0;
313 304
314 // get the current value if its not null 305 // Get the current value if its not null.
315 if (!recordUpdate.IsNull(2)) 306 if (!recordUpdate.IsNull(2))
316 { 307 {
317 attributes = recordUpdate.GetInteger(2); 308 attributes = recordUpdate.GetInteger(2);
318 } 309 }
319 310
320 // not specified
321 if (!file.File.Compressed.HasValue) 311 if (!file.File.Compressed.HasValue)
322 { 312 {
323 // clear any compression bits 313 // Clear all compression bits.
324 attributes &= ~MsiInterop.MsidbFileAttributesCompressed; 314 attributes &= ~MsiInterop.MsidbFileAttributesCompressed;
325 attributes &= ~MsiInterop.MsidbFileAttributesNoncompressed; 315 attributes &= ~MsiInterop.MsidbFileAttributesNoncompressed;
326 } 316 }
327 else if (file.File.Compressed.Value) 317 else if (file.File.Compressed.Value)
328 { 318 {
329 // these are mutually exclusive
330 attributes |= MsiInterop.MsidbFileAttributesCompressed; 319 attributes |= MsiInterop.MsidbFileAttributesCompressed;
331 attributes &= ~MsiInterop.MsidbFileAttributesNoncompressed; 320 attributes &= ~MsiInterop.MsidbFileAttributesNoncompressed;
332 } 321 }
333 else if (!file.File.Compressed.Value) 322 else if (!file.File.Compressed.Value)
334 { 323 {
335 // these are mutually exclusive
336 attributes |= MsiInterop.MsidbFileAttributesNoncompressed; 324 attributes |= MsiInterop.MsidbFileAttributesNoncompressed;
337 attributes &= ~MsiInterop.MsidbFileAttributesCompressed; 325 attributes &= ~MsiInterop.MsidbFileAttributesCompressed;
338 } 326 }
diff --git a/src/WixToolset.Core.WindowsInstaller/Bind/ProcessUncompressedFilesCommand.cs b/src/WixToolset.Core.WindowsInstaller/Bind/ProcessUncompressedFilesCommand.cs
index b3c09b9e..d71724d1 100644
--- a/src/WixToolset.Core.WindowsInstaller/Bind/ProcessUncompressedFilesCommand.cs
+++ b/src/WixToolset.Core.WindowsInstaller/Bind/ProcessUncompressedFilesCommand.cs
@@ -1,39 +1,42 @@
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
3namespace WixToolset.Core.WindowsInstaller.Databases 3namespace WixToolset.Core.WindowsInstaller.Bind
4{ 4{
5 using System; 5 using System;
6 using System.Collections;
7 using System.Collections.Generic; 6 using System.Collections.Generic;
8 using System.IO; 7 using System.IO;
9 using WixToolset.Data; 8 using WixToolset.Data;
10 using WixToolset.Data.Rows;
11 using WixToolset.Msi; 9 using WixToolset.Msi;
12 using WixToolset.Core.Native; 10 using WixToolset.Core.Native;
13 using WixToolset.Bind; 11 using WixToolset.Bind;
14 using WixToolset.Core.Bind; 12 using WixToolset.Core.Bind;
15 using WixToolset.Data.Bind; 13 using WixToolset.Data.Bind;
14 using WixToolset.Data.Tuples;
15 using System.Linq;
16 16
17 /// <summary> 17 /// <summary>
18 /// Defines the file transfers necessary to layout the uncompressed files. 18 /// Defines the file transfers necessary to layout the uncompressed files.
19 /// </summary> 19 /// </summary>
20 internal class ProcessUncompressedFilesCommand 20 internal class ProcessUncompressedFilesCommand
21 { 21 {
22 public ProcessUncompressedFilesCommand(IntermediateSection section)
23 {
24 this.Section = section;
25 }
26
27 private IntermediateSection Section { get; }
28
22 public string DatabasePath { private get; set; } 29 public string DatabasePath { private get; set; }
23 30
24 public IEnumerable<FileFacade> FileFacades { private get; set; } 31 public IEnumerable<FileFacade> FileFacades { private get; set; }
25 32
26 public RowDictionary<MediaRow> MediaRows { private get; set; }
27
28 public string LayoutDirectory { private get; set; } 33 public string LayoutDirectory { private get; set; }
29 34
30 public bool Compressed { private get; set; } 35 public bool Compressed { private get; set; }
31 36
32 public bool LongNamesInImage { private get; set; } 37 public bool LongNamesInImage { private get; set; }
33 38
34 public Func<MediaRow, string, string, string> ResolveMedia { private get; set; } 39 public Func<MediaTuple, string, string, string> ResolveMedia { private get; set; }
35
36 public Table WixMediaTable { private get; set; }
37 40
38 public IEnumerable<FileTransfer> FileTransfers { get; private set; } 41 public IEnumerable<FileTransfer> FileTransfers { get; private set; }
39 42
@@ -41,9 +44,11 @@ namespace WixToolset.Core.WindowsInstaller.Databases
41 { 44 {
42 List<FileTransfer> fileTransfers = new List<FileTransfer>(); 45 List<FileTransfer> fileTransfers = new List<FileTransfer>();
43 46
44 Hashtable directories = new Hashtable(); 47 var directories = new Dictionary<string, ResolvedDirectory>();
45 48
46 RowDictionary<WixMediaRow> wixMediaRows = new RowDictionary<WixMediaRow>(this.WixMediaTable); 49 var mediaRows = this.Section.Tuples.OfType<MediaTuple>().ToDictionary(t => t.DiskId);
50
51 var wixMediaRows = this.Section.Tuples.OfType<WixMediaTuple>().ToDictionary(t => t.DiskId_);
47 52
48 using (Database db = new Database(this.DatabasePath, OpenDatabase.ReadOnly)) 53 using (Database db = new Database(this.DatabasePath, OpenDatabase.ReadOnly))
49 { 54 {
@@ -72,18 +77,16 @@ namespace WixToolset.Core.WindowsInstaller.Databases
72 // for each file in the array of uncompressed files 77 // for each file in the array of uncompressed files
73 foreach (FileFacade facade in this.FileFacades) 78 foreach (FileFacade facade in this.FileFacades)
74 { 79 {
75 MediaRow mediaRow = this.MediaRows.Get(facade.WixFile.DiskId); 80 var mediaTuple = mediaRows[facade.WixFile.DiskId];
76 string relativeFileLayoutPath = null; 81 string relativeFileLayoutPath = null;
77
78 WixMediaRow wixMediaRow = null;
79 string mediaLayoutFolder = null; 82 string mediaLayoutFolder = null;
80 83
81 if (wixMediaRows.TryGetValue(mediaRow.GetKey(), out wixMediaRow)) 84 if (wixMediaRows.TryGetValue(facade.WixFile.DiskId, out var wixMediaRow))
82 { 85 {
83 mediaLayoutFolder = wixMediaRow.Layout; 86 mediaLayoutFolder = wixMediaRow.Layout;
84 } 87 }
85 88
86 string mediaLayoutDirectory = this.ResolveMedia(mediaRow, mediaLayoutFolder, this.LayoutDirectory); 89 var mediaLayoutDirectory = this.ResolveMedia(mediaTuple, mediaLayoutFolder, this.LayoutDirectory);
87 90
88 // setup up the query record and find the appropriate file in the 91 // setup up the query record and find the appropriate file in the
89 // previously executed file view 92 // previously executed file view
@@ -102,8 +105,7 @@ namespace WixToolset.Core.WindowsInstaller.Databases
102 105
103 // finally put together the base media layout path and the relative file layout path 106 // finally put together the base media layout path and the relative file layout path
104 string fileLayoutPath = Path.Combine(mediaLayoutDirectory, relativeFileLayoutPath); 107 string fileLayoutPath = Path.Combine(mediaLayoutDirectory, relativeFileLayoutPath);
105 FileTransfer transfer; 108 if (FileTransfer.TryCreate(facade.WixFile.Source, fileLayoutPath, false, "File", facade.File.SourceLineNumbers, out var transfer))
106 if (FileTransfer.TryCreate(facade.WixFile.Source, fileLayoutPath, false, "File", facade.File.SourceLineNumbers, out transfer))
107 { 109 {
108 fileTransfers.Add(transfer); 110 fileTransfers.Add(transfer);
109 } 111 }
diff --git a/src/WixToolset.Core.WindowsInstaller/Bind/SequenceActionsCommand.cs b/src/WixToolset.Core.WindowsInstaller/Bind/SequenceActionsCommand.cs
new file mode 100644
index 00000000..cf9c0332
--- /dev/null
+++ b/src/WixToolset.Core.WindowsInstaller/Bind/SequenceActionsCommand.cs
@@ -0,0 +1,671 @@
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
3namespace WixToolset.Core.WindowsInstaller.Bind
4{
5 using System;
6 using System.Collections.Generic;
7 using System.Globalization;
8 using System.Linq;
9 using WixToolset.Core.Native;
10 using WixToolset.Data;
11 using WixToolset.Data.Tuples;
12
13 internal class SequenceActionsCommand
14 {
15 public SequenceActionsCommand(IntermediateSection section)
16 {
17 this.Section = section;
18
19 this.RelativeActionsForActions = new Dictionary<string, RelativeActions>();
20
21 this.StandardActionsById = WindowsInstallerStandard.StandardActions().ToDictionary(a => a.Id.Id);
22 }
23
24 private IntermediateSection Section { get; }
25
26 private Dictionary<string, RelativeActions> RelativeActionsForActions { get; }
27
28 private Dictionary<string, WixActionTuple> StandardActionsById { get; }
29
30 public Messaging Messaging { private get; set; }
31
32 public void Execute()
33 {
34 var actions = this.Section.Tuples.OfType<WixActionTuple>().ToList();
35 var suppressActions = this.Section.Tuples.OfType<WixSuppressActionTuple>().ToList();
36
37 this.SequenceActions(actions, suppressActions);
38 }
39
40 /// <summary>
41 /// Set sequence numbers for all the actions and create rows in the output object.
42 /// </summary>
43 /// <param name="actionRows">Collection of actions to schedule.</param>
44 /// <param name="suppressActionRows">Collection of actions to suppress.</param>
45 private void SequenceActions(List<WixActionTuple> actionRows, List<WixSuppressActionTuple> suppressActionRows)
46 {
47 var overridableActionRows = new Dictionary<string, WixActionTuple>();
48 var requiredActionRows = new Dictionary<string, WixActionTuple>();
49
50 // Get the standard actions required based on tuples in the section.
51 var requiredActionIds = this.GetRequiredActionIds();
52
53 foreach (var actionId in requiredActionIds)
54 {
55 var standardAction = this.StandardActionsById[actionId];
56
57 overridableActionRows.Add(standardAction.Id.Id, standardAction);
58 }
59
60 // Index all the action rows and look for collisions.
61 foreach (var actionRow in this.Section.Tuples.OfType<WixActionTuple>())
62 {
63 if (actionRow.Overridable) // overridable action
64 {
65 if (overridableActionRows.TryGetValue(actionRow.Id.Id, out var collidingActionRow))
66 {
67 this.Messaging.OnMessage(WixErrors.OverridableActionCollision(actionRow.SourceLineNumbers, actionRow.SequenceTable.ToString(), actionRow.Action));
68 if (null != collidingActionRow.SourceLineNumbers)
69 {
70 this.Messaging.OnMessage(WixErrors.OverridableActionCollision2(collidingActionRow.SourceLineNumbers));
71 }
72 }
73 else
74 {
75 overridableActionRows.Add(actionRow.Id.Id, actionRow);
76 }
77 }
78 else // unsequenced or sequenced action.
79 {
80 // Unsequenced action (allowed for certain standard actions).
81 if (null == actionRow.Before && null == actionRow.After && 0 == actionRow.Sequence)
82 {
83 if (this.StandardActionsById.TryGetValue(actionRow.Id.Id, out var standardAction))
84 {
85 // Populate the sequence from the standard action
86 actionRow.Sequence = standardAction.Sequence;
87 }
88 else // not a supported unscheduled action.
89 {
90 throw new InvalidOperationException(WixStrings.EXP_FoundActionRowWithNoSequenceBeforeOrAfterColumnSet);
91 }
92 }
93
94 if (overridableActionRows.TryGetValue(actionRow.Id.Id, out var collidingActionRow))
95 {
96 this.Messaging.OnMessage(WixErrors.ActionCollision(actionRow.SourceLineNumbers, actionRow.SequenceTable.ToString(), actionRow.Action));
97 if (null != collidingActionRow.SourceLineNumbers)
98 {
99 this.Messaging.OnMessage(WixErrors.ActionCollision2(collidingActionRow.SourceLineNumbers));
100 }
101 }
102 else
103 {
104 requiredActionRows.Add(actionRow.Id.Id, actionRow);
105 }
106 }
107 }
108
109 // Add the overridable action rows that are not overridden to the required action rows.
110 foreach (var actionRow in overridableActionRows.Values)
111 {
112 if (!requiredActionRows.ContainsKey(actionRow.Id.Id))
113 {
114 requiredActionRows.Add(actionRow.Id.Id, actionRow);
115 }
116 }
117
118 // Suppress the required actions that are overridable.
119 foreach (var suppressActionRow in suppressActionRows)
120 {
121 var key = suppressActionRow.Id.Id;
122
123 // If there is an overridable row to suppress; suppress it. There is no warning if there
124 // is no action to suppress because the action may be suppressed from a merge module in
125 // the binder.
126 if (requiredActionRows.TryGetValue(key, out var requiredActionRow))
127 {
128 if (requiredActionRow.Overridable)
129 {
130 this.Messaging.OnMessage(WixWarnings.SuppressAction(suppressActionRow.SourceLineNumbers, suppressActionRow.Action, suppressActionRow.SequenceTable.ToString()));
131 if (null != requiredActionRow.SourceLineNumbers)
132 {
133 this.Messaging.OnMessage(WixWarnings.SuppressAction2(requiredActionRow.SourceLineNumbers));
134 }
135
136 requiredActionRows.Remove(key);
137 }
138 else // suppressing a non-overridable action row
139 {
140 this.Messaging.OnMessage(WixErrors.SuppressNonoverridableAction(suppressActionRow.SourceLineNumbers, suppressActionRow.SequenceTable.ToString(), suppressActionRow.Action));
141 if (null != requiredActionRow.SourceLineNumbers)
142 {
143 this.Messaging.OnMessage(WixErrors.SuppressNonoverridableAction2(requiredActionRow.SourceLineNumbers));
144 }
145 }
146 }
147 }
148
149 // Build up dependency trees of the relatively scheduled actions.
150 // Use ToList() to create a copy of the required action rows so that new tuples can
151 // be added while enumerating.
152 foreach (var actionRow in requiredActionRows.Values.ToList())
153 {
154 if (0 == actionRow.Sequence)
155 {
156 // check for standard actions that don't have a sequence number in a merge module
157 if (SectionType.Module == this.Section.Type && WindowsInstallerStandard.IsStandardAction(actionRow.Action))
158 {
159 this.Messaging.OnMessage(WixErrors.StandardActionRelativelyScheduledInModule(actionRow.SourceLineNumbers, actionRow.SequenceTable.ToString(), actionRow.Action));
160 }
161
162 this.SequenceActionRow(actionRow, requiredActionRows);
163 }
164 else if (SectionType.Module == this.Section.Type && 0 < actionRow.Sequence && !WindowsInstallerStandard.IsStandardAction(actionRow.Action)) // check for custom actions and dialogs that have a sequence number
165 {
166 this.Messaging.OnMessage(WixErrors.CustomActionSequencedInModule(actionRow.SourceLineNumbers, actionRow.SequenceTable.ToString(), actionRow.Action));
167 }
168 }
169
170 // Look for standard actions with sequence restrictions that aren't necessarily scheduled based
171 // on the presence of a particular table.
172 if (requiredActionRows.ContainsKey("InstallExecuteSequence/DuplicateFiles") && !requiredActionRows.ContainsKey("InstallExecuteSequence/InstallFiles"))
173 {
174 var standardAction = this.StandardActionsById["InstallExecuteSequence/InstallFiles"];
175 requiredActionRows.Add(standardAction.Id.Id, standardAction);
176 }
177
178 // Schedule actions.
179 List<WixActionTuple> scheduledActionRows;
180 if (SectionType.Module == this.Section.Type)
181 {
182 scheduledActionRows = requiredActionRows.Values.ToList();
183 }
184 else
185 {
186 scheduledActionRows = ScheduleActions(requiredActionRows);
187 }
188
189 // Remove all existing WixActionTuples from the section then add the
190 // scheduled actions back to the section. Note: we add the indices in
191 // reverse order to make it easy to remove them from the list later.
192 var removeIndices = new List<int>();
193 for (var i = this.Section.Tuples.Count - 1; i >= 0; --i)
194 {
195 var tuple = this.Section.Tuples[i];
196 if (tuple.Definition.Type == TupleDefinitionType.WixAction)
197 {
198 removeIndices.Add(i);
199 }
200 }
201
202 foreach (var removeIndex in removeIndices)
203 {
204 this.Section.Tuples.RemoveAt(removeIndex);
205 }
206
207 foreach (var action in scheduledActionRows)
208 {
209 this.Section.Tuples.Add(action);
210 }
211 }
212
213 private List<WixActionTuple> ScheduleActions(Dictionary<string, WixActionTuple> requiredActionRows)
214 {
215 var scheduledActionRows = new List<WixActionTuple>();
216
217 // Process each sequence table individually.
218 foreach (SequenceTable sequenceTable in Enum.GetValues(typeof(SequenceTable)))
219 {
220 // Create a collection of just the action rows in this sequence
221 var sequenceActionRows = requiredActionRows.Values.Where(a => a.SequenceTable == sequenceTable).ToList();
222
223 // Schedule the absolutely scheduled actions (by sorting them by their sequence numbers).
224 var absoluteActionRows = new List<WixActionTuple>();
225 foreach (var actionRow in sequenceActionRows)
226 {
227 if (0 != actionRow.Sequence)
228 {
229 // Look for sequence number collisions
230 foreach (var sequenceScheduledActionRow in absoluteActionRows)
231 {
232 if (sequenceScheduledActionRow.Sequence == actionRow.Sequence)
233 {
234 this.Messaging.OnMessage(WixWarnings.ActionSequenceCollision(actionRow.SourceLineNumbers, actionRow.SequenceTable.ToString(), actionRow.Action, sequenceScheduledActionRow.Action, actionRow.Sequence));
235 if (null != sequenceScheduledActionRow.SourceLineNumbers)
236 {
237 this.Messaging.OnMessage(WixWarnings.ActionSequenceCollision2(sequenceScheduledActionRow.SourceLineNumbers));
238 }
239 }
240 }
241
242 absoluteActionRows.Add(actionRow);
243 }
244 }
245
246 absoluteActionRows.Sort((x, y) => x.Sequence.CompareTo(y.Sequence));
247
248 // Schedule the relatively scheduled actions (by resolving the dependency trees).
249 var previousUsedSequence = 0;
250 var relativeActionRows = new List<WixActionTuple>();
251 for (int j = 0; j < absoluteActionRows.Count; j++)
252 {
253 var absoluteActionRow = absoluteActionRows[j];
254
255 // Get all the relatively scheduled action rows occuring before and after this absolutely scheduled action row.
256 var relativeActions = this.GetAllRelativeActionsForSequenceType(sequenceTable, absoluteActionRow);
257
258 // Check for relatively scheduled actions occuring before/after a special action
259 // (those actions with a negative sequence number).
260 if (absoluteActionRow.Sequence < 0 && (relativeActions.PreviousActions.Any() || relativeActions.NextActions.Any()))
261 {
262 // Create errors for all the before actions.
263 foreach (var actionRow in relativeActions.PreviousActions)
264 {
265 this.Messaging.OnMessage(WixErrors.ActionScheduledRelativeToTerminationAction(actionRow.SourceLineNumbers, actionRow.SequenceTable.ToString(), actionRow.Action, absoluteActionRow.Action));
266 }
267
268 // Create errors for all the after actions.
269 foreach (var actionRow in relativeActions.NextActions)
270 {
271 this.Messaging.OnMessage(WixErrors.ActionScheduledRelativeToTerminationAction(actionRow.SourceLineNumbers, actionRow.SequenceTable.ToString(), actionRow.Action, absoluteActionRow.Action));
272 }
273
274 // If there is source line information for the absolutely scheduled action display it
275 if (absoluteActionRow.SourceLineNumbers != null)
276 {
277 this.Messaging.OnMessage(WixErrors.ActionScheduledRelativeToTerminationAction2(absoluteActionRow.SourceLineNumbers));
278 }
279
280 continue;
281 }
282
283 // Schedule the action rows before this one.
284 var unusedSequence = absoluteActionRow.Sequence - 1;
285 for (var i = relativeActions.PreviousActions.Count - 1; i >= 0; i--)
286 {
287 var relativeActionRow = relativeActions.PreviousActions[i];
288
289 // look for collisions
290 if (unusedSequence == previousUsedSequence)
291 {
292 this.Messaging.OnMessage(WixErrors.NoUniqueActionSequenceNumber(relativeActionRow.SourceLineNumbers, relativeActionRow.SequenceTable.ToString(), relativeActionRow.Action, absoluteActionRow.Action));
293 if (absoluteActionRow.SourceLineNumbers != null)
294 {
295 this.Messaging.OnMessage(WixErrors.NoUniqueActionSequenceNumber2(absoluteActionRow.SourceLineNumbers));
296 }
297
298 unusedSequence++;
299 }
300
301 relativeActionRow.Sequence = unusedSequence;
302 relativeActionRows.Add(relativeActionRow);
303
304 unusedSequence--;
305 }
306
307 // Determine the next used action sequence number.
308 var nextUsedSequence = Int16.MaxValue + 1;
309 if (absoluteActionRows.Count > j + 1)
310 {
311 nextUsedSequence = absoluteActionRows[j + 1].Sequence;
312 }
313
314 // Schedule the action rows after this one.
315 unusedSequence = absoluteActionRow.Sequence + 1;
316 for (var i = 0; i < relativeActions.NextActions.Count; i++)
317 {
318 var relativeActionRow = relativeActions.NextActions[i];
319
320 if (unusedSequence == nextUsedSequence)
321 {
322 this.Messaging.OnMessage(WixErrors.NoUniqueActionSequenceNumber(relativeActionRow.SourceLineNumbers, relativeActionRow.SequenceTable.ToString(), relativeActionRow.Action, absoluteActionRow.Action));
323 if (absoluteActionRow.SourceLineNumbers != null)
324 {
325 this.Messaging.OnMessage(WixErrors.NoUniqueActionSequenceNumber2(absoluteActionRow.SourceLineNumbers));
326 }
327
328 unusedSequence--;
329 }
330
331 relativeActionRow.Sequence = unusedSequence;
332 relativeActionRows.Add(relativeActionRow);
333
334 unusedSequence++;
335 }
336
337 // keep track of this sequence number as the previous used sequence number for the next iteration
338 previousUsedSequence = absoluteActionRow.Sequence;
339 }
340
341 // add the absolutely and relatively scheduled actions to the list of scheduled actions
342 scheduledActionRows.AddRange(absoluteActionRows);
343 scheduledActionRows.AddRange(relativeActionRows);
344 }
345
346 return scheduledActionRows;
347 }
348
349 private IEnumerable<string> GetRequiredActionIds()
350 {
351 var set = new HashSet<string>();
352
353 // gather the required actions for the output type
354 if (SectionType.Product == this.Section.Type)
355 {
356 // AdminExecuteSequence table
357 set.Add("AdminExecuteSequence/CostFinalize");
358 set.Add("AdminExecuteSequence/CostInitialize");
359 set.Add("AdminExecuteSequence/FileCost");
360 set.Add("AdminExecuteSequence/InstallAdminPackage");
361 set.Add("AdminExecuteSequence/InstallFiles");
362 set.Add("AdminExecuteSequence/InstallFinalize");
363 set.Add("AdminExecuteSequence/InstallInitialize");
364 set.Add("AdminExecuteSequence/InstallValidate");
365
366 // AdminUISequence table
367 set.Add("AdminUISequence/CostFinalize");
368 set.Add("AdminUISequence/CostInitialize");
369 set.Add("AdminUISequence/ExecuteAction");
370 set.Add("AdminUISequence/FileCost");
371
372 // AdvtExecuteSequence table
373 set.Add("AdvtExecuteSequence/CostFinalize");
374 set.Add("AdvtExecuteSequence/CostInitialize");
375 set.Add("AdvtExecuteSequence/InstallFinalize");
376 set.Add("AdvtExecuteSequence/InstallValidate");
377 set.Add("AdvtExecuteSequence/PublishFeatures");
378 set.Add("AdvtExecuteSequence/PublishProduct");
379
380 // InstallExecuteSequence table
381 set.Add("InstallExecuteSequence/CostFinalize");
382 set.Add("InstallExecuteSequence/CostInitialize");
383 set.Add("InstallExecuteSequence/FileCost");
384 set.Add("InstallExecuteSequence/InstallFinalize");
385 set.Add("InstallExecuteSequence/InstallInitialize");
386 set.Add("InstallExecuteSequence/InstallValidate");
387 set.Add("InstallExecuteSequence/ProcessComponents");
388 set.Add("InstallExecuteSequence/PublishFeatures");
389 set.Add("InstallExecuteSequence/PublishProduct");
390 set.Add("InstallExecuteSequence/RegisterProduct");
391 set.Add("InstallExecuteSequence/RegisterUser");
392 set.Add("InstallExecuteSequence/UnpublishFeatures");
393 set.Add("InstallExecuteSequence/ValidateProductID");
394
395 // InstallUISequence table
396 set.Add("InstallUISequence/CostFinalize");
397 set.Add("InstallUISequence/CostInitialize");
398 set.Add("InstallUISequence/ExecuteAction");
399 set.Add("InstallUISequence/FileCost");
400 set.Add("InstallUISequence/ValidateProductID");
401 }
402
403 // Gather the required actions for each tuple type.
404 foreach (var tupleType in this.Section.Tuples.Select(t => t.Definition.Type).Distinct())
405 {
406 switch (tupleType)
407 {
408 case TupleDefinitionType.AppSearch:
409 set.Add("InstallExecuteSequence/AppSearch");
410 set.Add("InstallUISequence/AppSearch");
411 break;
412 case TupleDefinitionType.BindImage:
413 set.Add("InstallExecuteSequence/BindImage");
414 break;
415 case TupleDefinitionType.CCPSearch:
416 set.Add("InstallExecuteSequence/AppSearch");
417 set.Add("InstallExecuteSequence/CCPSearch");
418 set.Add("InstallExecuteSequence/RMCCPSearch");
419 set.Add("InstallUISequence/AppSearch");
420 set.Add("InstallUISequence/CCPSearch");
421 set.Add("InstallUISequence/RMCCPSearch");
422 break;
423 case TupleDefinitionType.Class:
424 set.Add("AdvtExecuteSequence/RegisterClassInfo");
425 set.Add("InstallExecuteSequence/RegisterClassInfo");
426 set.Add("InstallExecuteSequence/UnregisterClassInfo");
427 break;
428 case TupleDefinitionType.Complus:
429 set.Add("InstallExecuteSequence/RegisterComPlus");
430 set.Add("InstallExecuteSequence/UnregisterComPlus");
431 break;
432 case TupleDefinitionType.CreateFolder:
433 set.Add("InstallExecuteSequence/CreateFolders");
434 set.Add("InstallExecuteSequence/RemoveFolders");
435 break;
436 case TupleDefinitionType.DuplicateFile:
437 set.Add("InstallExecuteSequence/DuplicateFiles");
438 set.Add("InstallExecuteSequence/RemoveDuplicateFiles");
439 break;
440 case TupleDefinitionType.Environment:
441 set.Add("InstallExecuteSequence/WriteEnvironmentStrings");
442 set.Add("InstallExecuteSequence/RemoveEnvironmentStrings");
443 break;
444 case TupleDefinitionType.Extension:
445 set.Add("AdvtExecuteSequence/RegisterExtensionInfo");
446 set.Add("InstallExecuteSequence/RegisterExtensionInfo");
447 set.Add("InstallExecuteSequence/UnregisterExtensionInfo");
448 break;
449 case TupleDefinitionType.File:
450 set.Add("InstallExecuteSequence/InstallFiles");
451 set.Add("InstallExecuteSequence/RemoveFiles");
452 break;
453 case TupleDefinitionType.Font:
454 set.Add("InstallExecuteSequence/RegisterFonts");
455 set.Add("InstallExecuteSequence/UnregisterFonts");
456 break;
457 case TupleDefinitionType.IniFile:
458 case TupleDefinitionType.RemoveIniFile:
459 set.Add("InstallExecuteSequence/WriteIniValues");
460 set.Add("InstallExecuteSequence/RemoveIniValues");
461 break;
462 case TupleDefinitionType.IsolatedComponent:
463 set.Add("InstallExecuteSequence/IsolateComponents");
464 break;
465 case TupleDefinitionType.LaunchCondition:
466 set.Add("InstallExecuteSequence/LaunchConditions");
467 set.Add("InstallUISequence/LaunchConditions");
468 break;
469 case TupleDefinitionType.MIME:
470 set.Add("AdvtExecuteSequence/RegisterMIMEInfo");
471 set.Add("InstallExecuteSequence/RegisterMIMEInfo");
472 set.Add("InstallExecuteSequence/UnregisterMIMEInfo");
473 break;
474 case TupleDefinitionType.MoveFile:
475 set.Add("InstallExecuteSequence/MoveFiles");
476 break;
477 case TupleDefinitionType.MsiAssembly:
478 set.Add("AdvtExecuteSequence/MsiPublishAssemblies");
479 set.Add("InstallExecuteSequence/MsiPublishAssemblies");
480 set.Add("InstallExecuteSequence/MsiUnpublishAssemblies");
481 break;
482 case TupleDefinitionType.MsiServiceConfig:
483 case TupleDefinitionType.MsiServiceConfigFailureActions:
484 set.Add("InstallExecuteSequence/MsiConfigureServices");
485 break;
486 case TupleDefinitionType.ODBCDataSource:
487 case TupleDefinitionType.ODBCTranslator:
488 case TupleDefinitionType.ODBCDriver:
489 set.Add("InstallExecuteSequence/SetODBCFolders");
490 set.Add("InstallExecuteSequence/InstallODBC");
491 set.Add("InstallExecuteSequence/RemoveODBC");
492 break;
493 case TupleDefinitionType.ProgId:
494 set.Add("AdvtExecuteSequence/RegisterProgIdInfo");
495 set.Add("InstallExecuteSequence/RegisterProgIdInfo");
496 set.Add("InstallExecuteSequence/UnregisterProgIdInfo");
497 break;
498 case TupleDefinitionType.PublishComponent:
499 set.Add("AdvtExecuteSequence/PublishComponents");
500 set.Add("InstallExecuteSequence/PublishComponents");
501 set.Add("InstallExecuteSequence/UnpublishComponents");
502 break;
503 case TupleDefinitionType.Registry:
504 case TupleDefinitionType.RemoveRegistry:
505 set.Add("InstallExecuteSequence/WriteRegistryValues");
506 set.Add("InstallExecuteSequence/RemoveRegistryValues");
507 break;
508 case TupleDefinitionType.RemoveFile:
509 set.Add("InstallExecuteSequence/RemoveFiles");
510 break;
511 case TupleDefinitionType.SelfReg:
512 set.Add("InstallExecuteSequence/SelfRegModules");
513 set.Add("InstallExecuteSequence/SelfUnregModules");
514 break;
515 case TupleDefinitionType.ServiceControl:
516 set.Add("InstallExecuteSequence/StartServices");
517 set.Add("InstallExecuteSequence/StopServices");
518 set.Add("InstallExecuteSequence/DeleteServices");
519 break;
520 case TupleDefinitionType.ServiceInstall:
521 set.Add("InstallExecuteSequence/InstallServices");
522 break;
523 case TupleDefinitionType.Shortcut:
524 set.Add("AdvtExecuteSequence/CreateShortcuts");
525 set.Add("InstallExecuteSequence/CreateShortcuts");
526 set.Add("InstallExecuteSequence/RemoveShortcuts");
527 break;
528 case TupleDefinitionType.TypeLib:
529 set.Add("InstallExecuteSequence/RegisterTypeLibraries");
530 set.Add("InstallExecuteSequence/UnregisterTypeLibraries");
531 break;
532 case TupleDefinitionType.Upgrade:
533 set.Add("InstallExecuteSequence/FindRelatedProducts");
534 set.Add("InstallUISequence/FindRelatedProducts");
535
536 // Only add the MigrateFeatureStates action if MigrateFeature attribute is set on
537 // at least one UpgradeVersion element.
538 if (this.Section.Tuples.OfType<UpgradeTuple>().Any(t => (t.Attributes & MsiInterop.MsidbUpgradeAttributesMigrateFeatures) == MsiInterop.MsidbUpgradeAttributesMigrateFeatures))
539 {
540 set.Add("InstallExecuteSequence/MigrateFeatureStates");
541 set.Add("InstallUISequence/MigrateFeatureStates");
542 }
543 break;
544 }
545 }
546
547 return set;
548 }
549
550 private IEnumerable<WixActionTuple> GetActions(SequenceTable sequence, string[] actionNames)
551 {
552 foreach (var action in WindowsInstallerStandard.StandardActions())
553 {
554 if (action.SequenceTable == sequence && actionNames.Contains(action.Action))
555 {
556 yield return action;
557 }
558 }
559 }
560
561 /// <summary>
562 /// Sequence an action before or after a standard action.
563 /// </summary>
564 /// <param name="actionRow">The action row to be sequenced.</param>
565 /// <param name="requiredActionRows">Collection of actions which must be included.</param>
566 private void SequenceActionRow(WixActionTuple actionRow, Dictionary<string, WixActionTuple> requiredActionRows)
567 {
568 var after = false;
569
570 if (actionRow.After != null)
571 {
572 after = true;
573 }
574 else if (actionRow.Before == null)
575 {
576 throw new InvalidOperationException(WixStrings.EXP_FoundActionRowWithNoSequenceBeforeOrAfterColumnSet);
577 }
578
579 var parentActionName = (after ? actionRow.After : actionRow.Before);
580 var parentActionKey = actionRow.SequenceTable.ToString() + "/" + parentActionName;
581
582 if (!requiredActionRows.TryGetValue(parentActionKey, out var parentActionRow))
583 {
584 // If the missing parent action is a standard action (with a suggested sequence number), add it.
585 if (this.StandardActionsById.TryGetValue(parentActionKey, out parentActionRow))
586 {
587 // Create a clone to avoid modifying the static copy of the object.
588 // TODO: consider this: parentActionRow = parentActionRow.Clone();
589
590 requiredActionRows.Add(parentActionRow.Id.Id, parentActionRow);
591 }
592 else
593 {
594 throw new InvalidOperationException(String.Format(CultureInfo.CurrentUICulture, WixStrings.EXP_FoundActionRowWinNonExistentAction, (after ? "After" : "Before"), parentActionName));
595 }
596 }
597 else if (actionRow == parentActionRow || this.ContainsChildActionRow(actionRow, parentActionRow)) // cycle detected
598 {
599 throw new WixException(WixErrors.ActionCircularDependency(actionRow.SourceLineNumbers, actionRow.SequenceTable.ToString(), actionRow.Action, parentActionRow.Action));
600 }
601
602 // Add this action to the appropriate list of dependent action rows.
603 var relativeActions = this.GetRelativeActions(parentActionRow);
604 var relatedRows = (after ? relativeActions.NextActions : relativeActions.PreviousActions);
605 relatedRows.Add(actionRow);
606 }
607
608 private bool ContainsChildActionRow(WixActionTuple childTuple, WixActionTuple parentTuple)
609 {
610 var result = false;
611
612 if (this.RelativeActionsForActions.TryGetValue(childTuple.Id.Id, out var relativeActions))
613 {
614 result = relativeActions.NextActions.Any(a => a.SequenceTable == parentTuple.SequenceTable && a.Id.Id == parentTuple.Id.Id) ||
615 relativeActions.PreviousActions.Any(a => a.SequenceTable == parentTuple.SequenceTable && a.Id.Id == parentTuple.Id.Id);
616 }
617
618 return result;
619 }
620
621 private RelativeActions GetRelativeActions(WixActionTuple action)
622 {
623 if (!this.RelativeActionsForActions.TryGetValue(action.Id.Id, out var relativeActions))
624 {
625 relativeActions = new RelativeActions();
626 this.RelativeActionsForActions.Add(action.Id.Id, relativeActions);
627 }
628
629 return relativeActions;
630 }
631
632 private RelativeActions GetAllRelativeActionsForSequenceType(SequenceTable sequenceType, WixActionTuple action)
633 {
634 var relativeActions = new RelativeActions();
635
636 if (this.RelativeActionsForActions.TryGetValue(action.Id.Id, out var actionRelatives))
637 {
638 this.RecurseRelativeActionsForSequenceType(sequenceType, actionRelatives.PreviousActions, relativeActions.PreviousActions);
639
640 this.RecurseRelativeActionsForSequenceType(sequenceType, actionRelatives.NextActions, relativeActions.NextActions);
641 }
642
643 return relativeActions;
644 }
645
646 private void RecurseRelativeActionsForSequenceType(SequenceTable sequenceType, List<WixActionTuple> actions, List<WixActionTuple> visitedActions)
647 {
648 foreach (var action in actions.Where(a => a.SequenceTable == sequenceType))
649 {
650 if (this.RelativeActionsForActions.TryGetValue(action.Id.Id, out var actionRelatives))
651 {
652 this.RecurseRelativeActionsForSequenceType(sequenceType, actionRelatives.PreviousActions, visitedActions);
653 }
654
655 visitedActions.Add(action);
656
657 if (actionRelatives != null)
658 {
659 this.RecurseRelativeActionsForSequenceType(sequenceType, actionRelatives.NextActions, visitedActions);
660 }
661 }
662 }
663
664 private class RelativeActions
665 {
666 public List<WixActionTuple> PreviousActions { get; } = new List<WixActionTuple>();
667
668 public List<WixActionTuple> NextActions { get; } = new List<WixActionTuple>();
669 }
670 }
671}
diff --git a/src/WixToolset.Core.WindowsInstaller/Bind/UpdateControlTextCommand.cs b/src/WixToolset.Core.WindowsInstaller/Bind/UpdateControlTextCommand.cs
index 7da32206..9579e0f8 100644
--- a/src/WixToolset.Core.WindowsInstaller/Bind/UpdateControlTextCommand.cs
+++ b/src/WixToolset.Core.WindowsInstaller/Bind/UpdateControlTextCommand.cs
@@ -1,6 +1,6 @@
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
3namespace WixToolset.Core.WindowsInstaller.Databases 3namespace WixToolset.Core.WindowsInstaller.Bind
4{ 4{
5 using System; 5 using System;
6 using System.IO; 6 using System.IO;
diff --git a/src/WixToolset.Core.WindowsInstaller/Bind/UpdateFileFacadesCommand.cs b/src/WixToolset.Core.WindowsInstaller/Bind/UpdateFileFacadesCommand.cs
index 20058597..030bc4cc 100644
--- a/src/WixToolset.Core.WindowsInstaller/Bind/UpdateFileFacadesCommand.cs
+++ b/src/WixToolset.Core.WindowsInstaller/Bind/UpdateFileFacadesCommand.cs
@@ -1,10 +1,9 @@
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
3namespace WixToolset.Core.WindowsInstaller.Databases 3namespace WixToolset.Core.WindowsInstaller.Bind
4{ 4{
5 using System; 5 using System;
6 using System.Collections.Generic; 6 using System.Collections.Generic;
7 using System.Collections.Specialized;
8 using System.ComponentModel; 7 using System.ComponentModel;
9 using System.Globalization; 8 using System.Globalization;
10 using System.IO; 9 using System.IO;
@@ -14,7 +13,6 @@ namespace WixToolset.Core.WindowsInstaller.Databases
14 using WixToolset.Clr.Interop; 13 using WixToolset.Clr.Interop;
15 using WixToolset.Core.Bind; 14 using WixToolset.Core.Bind;
16 using WixToolset.Data; 15 using WixToolset.Data;
17 using WixToolset.Data.Rows;
18 using WixToolset.Data.Tuples; 16 using WixToolset.Data.Tuples;
19 using WixToolset.Msi; 17 using WixToolset.Msi;
20 18
@@ -23,13 +21,16 @@ namespace WixToolset.Core.WindowsInstaller.Databases
23 /// </summary> 21 /// </summary>
24 internal class UpdateFileFacadesCommand 22 internal class UpdateFileFacadesCommand
25 { 23 {
26 public IEnumerable<FileFacade> FileFacades { private get; set; } 24 public UpdateFileFacadesCommand(IntermediateSection section)
25 {
26 this.Section = section;
27 }
27 28
28 public IEnumerable<FileFacade> UpdateFileFacades { private get; set; } 29 private IntermediateSection Section { get; }
29 30
30 public string ModularizationGuid { private get; set; } 31 public IEnumerable<FileFacade> FileFacades { private get; set; }
31 32
32 public Output Output { private get; set; } 33 public IEnumerable<FileFacade> UpdateFileFacades { private get; set; }
33 34
34 public bool OverwriteHash { private get; set; } 35 public bool OverwriteHash { private get; set; }
35 36
@@ -47,6 +48,12 @@ namespace WixToolset.Core.WindowsInstaller.Databases
47 48
48 private void UpdateFileFacade(FileFacade file) 49 private void UpdateFileFacade(FileFacade file)
49 { 50 {
51 var assemblyNameTuples = new Dictionary<string, MsiAssemblyNameTuple>();
52 foreach (var assemblyTuple in this.Section.Tuples.OfType<MsiAssemblyNameTuple>())
53 {
54 assemblyNameTuples.Add(assemblyTuple.Component_ + "/" + assemblyTuple.Name, assemblyTuple);
55 }
56
50 FileInfo fileInfo = null; 57 FileInfo fileInfo = null;
51 try 58 try
52 { 59 {
@@ -149,9 +156,8 @@ namespace WixToolset.Core.WindowsInstaller.Databases
149 156
150 if (null == file.Hash) 157 if (null == file.Hash)
151 { 158 {
152 //Table msiFileHashTable = this.Output.EnsureTable(this.TableDefinitions["MsiFileHash"]); 159 file.Hash = new MsiFileHashTuple(file.File.SourceLineNumbers, file.File.Id);
153 //file.Hash = msiFileHashTable.CreateRow(file.File.SourceLineNumbers); 160 this.Section.Tuples.Add(file.Hash);
154 throw new NotImplementedException();
155 } 161 }
156 162
157 file.Hash.File_ = file.File.File; 163 file.Hash.File_ = file.File.File;
@@ -198,13 +204,13 @@ namespace WixToolset.Core.WindowsInstaller.Databases
198 { 204 {
199 if (!String.IsNullOrEmpty(file.File.Version)) 205 if (!String.IsNullOrEmpty(file.File.Version))
200 { 206 {
201 string key = String.Format(CultureInfo.InvariantCulture, "fileversion.{0}", Common.Demodularize(this.Output.Type, this.ModularizationGuid, file.File.File)); 207 var key = String.Format(CultureInfo.InvariantCulture, "fileversion.{0}", file.File.File);
202 this.VariableCache[key] = file.File.Version; 208 this.VariableCache[key] = file.File.Version;
203 } 209 }
204 210
205 if (!String.IsNullOrEmpty(file.File.Language)) 211 if (!String.IsNullOrEmpty(file.File.Language))
206 { 212 {
207 string key = String.Format(CultureInfo.InvariantCulture, "filelanguage.{0}", Common.Demodularize(this.Output.Type, ModularizationGuid, file.File.File)); 213 var key = String.Format(CultureInfo.InvariantCulture, "filelanguage.{0}", file.File.File);
208 this.VariableCache[key] = file.File.Language; 214 this.VariableCache[key] = file.File.Language;
209 } 215 }
210 } 216 }
@@ -214,14 +220,13 @@ namespace WixToolset.Core.WindowsInstaller.Databases
214 if (FileAssemblyType.DotNetAssembly == file.WixFile.AssemblyType) 220 if (FileAssemblyType.DotNetAssembly == file.WixFile.AssemblyType)
215 { 221 {
216 bool targetNetfx1 = false; 222 bool targetNetfx1 = false;
217 StringDictionary assemblyNameValues = new StringDictionary(); 223 var assemblyNameValues = new Dictionary<string, string>();
218 224
219 ClrInterop.IReferenceIdentity referenceIdentity = null;
220 Guid referenceIdentityGuid = ClrInterop.ReferenceIdentityGuid; 225 Guid referenceIdentityGuid = ClrInterop.ReferenceIdentityGuid;
221 uint result = ClrInterop.GetAssemblyIdentityFromFile(fileInfo.FullName, ref referenceIdentityGuid, out referenceIdentity); 226 var result = ClrInterop.GetAssemblyIdentityFromFile(fileInfo.FullName, ref referenceIdentityGuid, out var referenceIdentity);
222 if (0 == result && null != referenceIdentity) 227 if (0 == result && null != referenceIdentity)
223 { 228 {
224 string imageRuntimeVersion = referenceIdentity.GetAttribute(null, "ImageRuntimeVersion"); 229 var imageRuntimeVersion = referenceIdentity.GetAttribute(null, "ImageRuntimeVersion");
225 if (null != imageRuntimeVersion) 230 if (null != imageRuntimeVersion)
226 { 231 {
227 targetNetfx1 = imageRuntimeVersion.StartsWith("v1", StringComparison.OrdinalIgnoreCase); 232 targetNetfx1 = imageRuntimeVersion.StartsWith("v1", StringComparison.OrdinalIgnoreCase);
@@ -269,15 +274,14 @@ namespace WixToolset.Core.WindowsInstaller.Databases
269 return; 274 return;
270 } 275 }
271 276
272 Table assemblyNameTable = this.Output.EnsureTable(this.TableDefinitions["MsiAssemblyName"]); 277 if (assemblyNameValues.TryGetValue("name", out var value))
273 if (assemblyNameValues.ContainsKey("name"))
274 { 278 {
275 this.SetMsiAssemblyName(assemblyNameTable, file, "name", assemblyNameValues["name"]); 279 this.SetMsiAssemblyName(assemblyNameTuples, file, "name", value);
276 } 280 }
277 281
278 if (!String.IsNullOrEmpty(version)) 282 if (!String.IsNullOrEmpty(version))
279 { 283 {
280 this.SetMsiAssemblyName(assemblyNameTable, file, "fileVersion", version); 284 this.SetMsiAssemblyName(assemblyNameTuples, file, "fileVersion", version);
281 } 285 }
282 286
283 if (assemblyNameValues.ContainsKey("version")) 287 if (assemblyNameValues.ContainsKey("version"))
@@ -303,33 +307,33 @@ namespace WixToolset.Core.WindowsInstaller.Databases
303 } 307 }
304 } 308 }
305 309
306 this.SetMsiAssemblyName(assemblyNameTable, file, "version", assemblyVersion); 310 this.SetMsiAssemblyName(assemblyNameTuples, file, "version", assemblyVersion);
307 } 311 }
308 312
309 if (assemblyNameValues.ContainsKey("culture")) 313 if (assemblyNameValues.ContainsKey("culture"))
310 { 314 {
311 this.SetMsiAssemblyName(assemblyNameTable, file, "culture", assemblyNameValues["culture"]); 315 this.SetMsiAssemblyName(assemblyNameTuples, file, "culture", assemblyNameValues["culture"]);
312 } 316 }
313 317
314 if (assemblyNameValues.ContainsKey("publicKeyToken")) 318 if (assemblyNameValues.ContainsKey("publicKeyToken"))
315 { 319 {
316 this.SetMsiAssemblyName(assemblyNameTable, file, "publicKeyToken", assemblyNameValues["publicKeyToken"]); 320 this.SetMsiAssemblyName(assemblyNameTuples, file, "publicKeyToken", assemblyNameValues["publicKeyToken"]);
317 } 321 }
318 322
319 if (!String.IsNullOrEmpty(file.WixFile.ProcessorArchitecture)) 323 if (!String.IsNullOrEmpty(file.WixFile.ProcessorArchitecture))
320 { 324 {
321 this.SetMsiAssemblyName(assemblyNameTable, file, "processorArchitecture", file.WixFile.ProcessorArchitecture); 325 this.SetMsiAssemblyName(assemblyNameTuples, file, "processorArchitecture", file.WixFile.ProcessorArchitecture);
322 } 326 }
323 327
324 if (assemblyNameValues.ContainsKey("processorArchitecture")) 328 if (assemblyNameValues.ContainsKey("processorArchitecture"))
325 { 329 {
326 this.SetMsiAssemblyName(assemblyNameTable, file, "processorArchitecture", assemblyNameValues["processorArchitecture"]); 330 this.SetMsiAssemblyName(assemblyNameTuples, file, "processorArchitecture", assemblyNameValues["processorArchitecture"]);
327 } 331 }
328 332
329 // add the assembly name to the information cache 333 // add the assembly name to the information cache
330 if (null != this.VariableCache) 334 if (null != this.VariableCache)
331 { 335 {
332 string fileId = Common.Demodularize(this.Output.Type, this.ModularizationGuid, file.File.File); 336 string fileId = file.File.File;
333 string key = String.Concat("assemblyfullname.", fileId); 337 string key = String.Concat("assemblyfullname.", fileId);
334 string assemblyName = String.Concat(assemblyNameValues["name"], ", version=", assemblyNameValues["version"], ", culture=", assemblyNameValues["culture"], ", publicKeyToken=", String.IsNullOrEmpty(assemblyNameValues["publicKeyToken"]) ? "null" : assemblyNameValues["publicKeyToken"]); 338 string assemblyName = String.Concat(assemblyNameValues["name"], ", version=", assemblyNameValues["version"], ", culture=", assemblyNameValues["culture"], ", publicKeyToken=", String.IsNullOrEmpty(assemblyNameValues["publicKeyToken"]) ? "null" : assemblyNameValues["publicKeyToken"]);
335 if (assemblyNameValues.ContainsKey("processorArchitecture")) 339 if (assemblyNameValues.ContainsKey("processorArchitecture"))
@@ -437,30 +441,29 @@ namespace WixToolset.Core.WindowsInstaller.Databases
437 Messaging.Instance.OnMessage(WixErrors.InvalidXml(new SourceLineNumber(fileManifest.WixFile.Source), "manifest", xe.Message)); 441 Messaging.Instance.OnMessage(WixErrors.InvalidXml(new SourceLineNumber(fileManifest.WixFile.Source), "manifest", xe.Message));
438 } 442 }
439 443
440 Table assemblyNameTable = this.Output.EnsureTable(this.TableDefinitions["MsiAssemblyName"]);
441 if (!String.IsNullOrEmpty(win32Name)) 444 if (!String.IsNullOrEmpty(win32Name))
442 { 445 {
443 this.SetMsiAssemblyName(assemblyNameTable, file, "name", win32Name); 446 this.SetMsiAssemblyName(assemblyNameTuples, file, "name", win32Name);
444 } 447 }
445 448
446 if (!String.IsNullOrEmpty(win32Version)) 449 if (!String.IsNullOrEmpty(win32Version))
447 { 450 {
448 this.SetMsiAssemblyName(assemblyNameTable, file, "version", win32Version); 451 this.SetMsiAssemblyName(assemblyNameTuples, file, "version", win32Version);
449 } 452 }
450 453
451 if (!String.IsNullOrEmpty(win32Type)) 454 if (!String.IsNullOrEmpty(win32Type))
452 { 455 {
453 this.SetMsiAssemblyName(assemblyNameTable, file, "type", win32Type); 456 this.SetMsiAssemblyName(assemblyNameTuples, file, "type", win32Type);
454 } 457 }
455 458
456 if (!String.IsNullOrEmpty(win32ProcessorArchitecture)) 459 if (!String.IsNullOrEmpty(win32ProcessorArchitecture))
457 { 460 {
458 this.SetMsiAssemblyName(assemblyNameTable, file, "processorArchitecture", win32ProcessorArchitecture); 461 this.SetMsiAssemblyName(assemblyNameTuples, file, "processorArchitecture", win32ProcessorArchitecture);
459 } 462 }
460 463
461 if (!String.IsNullOrEmpty(win32PublicKeyToken)) 464 if (!String.IsNullOrEmpty(win32PublicKeyToken))
462 { 465 {
463 this.SetMsiAssemblyName(assemblyNameTable, file, "publicKeyToken", win32PublicKeyToken); 466 this.SetMsiAssemblyName(assemblyNameTuples, file, "publicKeyToken", win32PublicKeyToken);
464 } 467 }
465 } 468 }
466 } 469 }
@@ -469,11 +472,11 @@ namespace WixToolset.Core.WindowsInstaller.Databases
469 /// Set an MsiAssemblyName row. If it was directly authored, override the value, otherwise 472 /// Set an MsiAssemblyName row. If it was directly authored, override the value, otherwise
470 /// create a new row. 473 /// create a new row.
471 /// </summary> 474 /// </summary>
472 /// <param name="assemblyNameTable">MsiAssemblyName table.</param> 475 /// <param name="assemblyNameTuples">MsiAssemblyName table.</param>
473 /// <param name="file">FileFacade containing the assembly read for the MsiAssemblyName row.</param> 476 /// <param name="file">FileFacade containing the assembly read for the MsiAssemblyName row.</param>
474 /// <param name="name">MsiAssemblyName name.</param> 477 /// <param name="name">MsiAssemblyName name.</param>
475 /// <param name="value">MsiAssemblyName value.</param> 478 /// <param name="value">MsiAssemblyName value.</param>
476 private void SetMsiAssemblyName(Table assemblyNameTable, FileFacade file, string name, string value) 479 private void SetMsiAssemblyName(Dictionary<string, MsiAssemblyNameTuple> assemblyNameTuples, FileFacade file, string name, string value)
477 { 480 {
478 // check for null value (this can occur when grabbing the file version from an assembly without one) 481 // check for null value (this can occur when grabbing the file version from an assembly without one)
479 if (String.IsNullOrEmpty(value)) 482 if (String.IsNullOrEmpty(value))
@@ -482,18 +485,6 @@ namespace WixToolset.Core.WindowsInstaller.Databases
482 } 485 }
483 else 486 else
484 { 487 {
485 Row assemblyNameRow = null;
486
487 // override directly authored value
488 foreach (Row row in assemblyNameTable.Rows)
489 {
490 if ((string)row[0] == file.File.Component_ && (string)row[1] == name)
491 {
492 assemblyNameRow = row;
493 break;
494 }
495 }
496
497 // 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. 488 // 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.
498 if ("name" == name && FileAssemblyType.DotNetAssembly == file.WixFile.AssemblyType && 489 if ("name" == name && FileAssemblyType.DotNetAssembly == file.WixFile.AssemblyType &&
499 String.IsNullOrEmpty(file.WixFile.File_AssemblyApplication) && 490 String.IsNullOrEmpty(file.WixFile.File_AssemblyApplication) &&
@@ -502,17 +493,14 @@ namespace WixToolset.Core.WindowsInstaller.Databases
502 Messaging.Instance.OnMessage(WixErrors.GACAssemblyIdentityWarning(file.File.SourceLineNumbers, Path.GetFileNameWithoutExtension(file.File.LongFileName), value)); 493 Messaging.Instance.OnMessage(WixErrors.GACAssemblyIdentityWarning(file.File.SourceLineNumbers, Path.GetFileNameWithoutExtension(file.File.LongFileName), value));
503 } 494 }
504 495
505 if (null == assemblyNameRow) 496 // override directly authored value
497 var lookup = String.Concat(file.File.Component_, "/", name);
498 if (assemblyNameTuples.TryGetValue(lookup, out var assemblyNameRow))
506 { 499 {
507 throw new NotImplementedException(); 500 assemblyNameRow = new MsiAssemblyNameTuple(file.File.SourceLineNumbers);
508#if TODO 501 assemblyNameRow.Component_ = file.File.Component_;
509 assemblyNameRow = assemblyNameTable.CreateRow(file.File.SourceLineNumbers); 502 assemblyNameRow.Name = name;
510 assemblyNameRow[0] = file.File.Component_; 503 assemblyNameRow.Value = value;
511 assemblyNameRow[1] = name;
512 assemblyNameRow[2] = value;
513
514 // put the MsiAssemblyName row in the same section as the related File row
515 assemblyNameRow.SectionId = file.File.SectionId;
516 504
517 if (null == file.AssemblyNames) 505 if (null == file.AssemblyNames)
518 { 506 {
@@ -520,17 +508,15 @@ namespace WixToolset.Core.WindowsInstaller.Databases
520 } 508 }
521 509
522 file.AssemblyNames.Add(assemblyNameRow); 510 file.AssemblyNames.Add(assemblyNameRow);
523#endif 511 this.Section.Tuples.Add(assemblyNameRow);
524 }
525 else
526 {
527 assemblyNameRow[2] = value;
528 } 512 }
529 513
514 assemblyNameRow.Value = value;
515
530 if (this.VariableCache != null) 516 if (this.VariableCache != null)
531 { 517 {
532 string key = String.Format(CultureInfo.InvariantCulture, "assembly{0}.{1}", name, Common.Demodularize(this.Output.Type, this.ModularizationGuid, file.File.File)).ToLowerInvariant(); 518 var key = String.Format(CultureInfo.InvariantCulture, "assembly{0}.{1}", name, file.File.File).ToLowerInvariant();
533 this.VariableCache[key] = (string)assemblyNameRow[2]; 519 this.VariableCache[key] = value;
534 } 520 }
535 } 521 }
536 } 522 }
diff --git a/src/WixToolset.Core.WindowsInstaller/Bind/UpdateMediaSequencesCommand.cs b/src/WixToolset.Core.WindowsInstaller/Bind/UpdateMediaSequencesCommand.cs
new file mode 100644
index 00000000..db74eda5
--- /dev/null
+++ b/src/WixToolset.Core.WindowsInstaller/Bind/UpdateMediaSequencesCommand.cs
@@ -0,0 +1,126 @@
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
3namespace WixToolset.Core.WindowsInstaller.Bind
4{
5 using System;
6 using System.Collections.Generic;
7 using System.Linq;
8 using WixToolset.Core.Bind;
9 using WixToolset.Data;
10 using WixToolset.Data.Rows;
11 using WixToolset.Data.Tuples;
12
13 internal class UpdateMediaSequencesCommand
14 {
15 public UpdateMediaSequencesCommand(Output output, List<FileFacade> fileFacades, Dictionary<int, MediaTuple> assignedMediaRows)
16 {
17 this.Output = output;
18 this.FileFacades = fileFacades;
19 }
20
21 private Output Output { get; }
22
23 private List<FileFacade> FileFacades { get; }
24
25 public void Execute()
26 {
27 var fileRows = new RowDictionary<FileRow>(this.Output.Tables["File"]);
28 var mediaRows = new RowDictionary<MediaRow>(this.Output.Tables["Media"]);
29
30 // Calculate sequence numbers and media disk id layout for all file media information objects.
31 if (OutputType.Module == this.Output.Type)
32 {
33 var lastSequence = 0;
34
35 // Order by Component to group the files by directory.
36 var optimized = this.OptimizedFileFacades();
37 foreach (var fileId in optimized.Select(f => f.File.File))
38 {
39 var fileRow = fileRows.Get(fileId);
40 fileRow.Sequence = ++lastSequence;
41 }
42 }
43 else
44 {
45 int lastSequence = 0;
46 MediaRow mediaRow = null;
47 Dictionary<int, List<FileFacade>> patchGroups = new Dictionary<int, List<FileFacade>>();
48
49 // sequence the non-patch-added files
50 var optimized = this.OptimizedFileFacades();
51 foreach (FileFacade facade in optimized)
52 {
53 if (null == mediaRow)
54 {
55 mediaRow = mediaRows.Get(facade.WixFile.DiskId);
56 if (OutputType.Patch == this.Output.Type)
57 {
58 // patch Media cannot start at zero
59 lastSequence = mediaRow.LastSequence;
60 }
61 }
62 else if (mediaRow.DiskId != facade.WixFile.DiskId)
63 {
64 mediaRow.LastSequence = lastSequence;
65 mediaRow = mediaRows.Get(facade.WixFile.DiskId);
66 }
67
68 if (0 < facade.WixFile.PatchGroup)
69 {
70 if (patchGroups.TryGetValue(facade.WixFile.PatchGroup, out var patchGroup))
71 {
72 patchGroup = new List<FileFacade>();
73 patchGroups.Add(facade.WixFile.PatchGroup, patchGroup);
74 }
75
76 patchGroup.Add(facade);
77 }
78 else
79 {
80 var fileRow = fileRows.Get(facade.File.File);
81 fileRow.Sequence = ++lastSequence;
82 }
83 }
84
85 if (null != mediaRow)
86 {
87 mediaRow.LastSequence = lastSequence;
88 mediaRow = null;
89 }
90
91 // sequence the patch-added files
92 foreach (var patchGroup in patchGroups.Values)
93 {
94 foreach (var facade in patchGroup)
95 {
96 if (null == mediaRow)
97 {
98 mediaRow = mediaRows.Get(facade.WixFile.DiskId);
99 }
100 else if (mediaRow.DiskId != facade.WixFile.DiskId)
101 {
102 mediaRow.LastSequence = lastSequence;
103 mediaRow = mediaRows.Get(facade.WixFile.DiskId);
104 }
105
106 var fileRow = fileRows.Get(facade.File.File);
107 fileRow.Sequence = ++lastSequence;
108 }
109 }
110
111 if (null != mediaRow)
112 {
113 mediaRow.LastSequence = lastSequence;
114 }
115 }
116 }
117
118 private IEnumerable<FileFacade> OptimizedFileFacades()
119 {
120 // TODO: Sort these facades even smarter by directory path and component id
121 // and maybe file size or file extension and other creative ideas to
122 // get optimal install speed out of MSI.
123 return this.FileFacades.OrderBy(f => f.File.Component_);
124 }
125 }
126}
diff --git a/src/WixToolset.Core.WindowsInstaller/Inscribe/InscribeMsiPackageCommand.cs b/src/WixToolset.Core.WindowsInstaller/Inscribe/InscribeMsiPackageCommand.cs
index 72c876e0..58384325 100644
--- a/src/WixToolset.Core.WindowsInstaller/Inscribe/InscribeMsiPackageCommand.cs
+++ b/src/WixToolset.Core.WindowsInstaller/Inscribe/InscribeMsiPackageCommand.cs
@@ -18,7 +18,7 @@ namespace WixToolset.Core.WindowsInstaller.Inscribe
18 public InscribeMsiPackageCommand(IInscribeContext context) 18 public InscribeMsiPackageCommand(IInscribeContext context)
19 { 19 {
20 this.Context = context; 20 this.Context = context;
21 this.TableDefinitions = WindowsInstallerStandard.GetTableDefinitions(); 21 this.TableDefinitions = WindowsInstallerStandardInternal.GetTableDefinitions();
22 } 22 }
23 23
24 private IInscribeContext Context { get; } 24 private IInscribeContext Context { get; }
diff --git a/src/WixToolset.Core.WindowsInstaller/MstBackend.cs b/src/WixToolset.Core.WindowsInstaller/MstBackend.cs
index 3e40a51f..17617dbc 100644
--- a/src/WixToolset.Core.WindowsInstaller/MstBackend.cs
+++ b/src/WixToolset.Core.WindowsInstaller/MstBackend.cs
@@ -3,7 +3,6 @@
3namespace WixToolset.Core.WindowsInstaller 3namespace WixToolset.Core.WindowsInstaller
4{ 4{
5 using System; 5 using System;
6 using WixToolset.Core.WindowsInstaller.Databases;
7 using WixToolset.Core.WindowsInstaller.Unbind; 6 using WixToolset.Core.WindowsInstaller.Unbind;
8 using WixToolset.Data; 7 using WixToolset.Data;
9 using WixToolset.Data.Bind; 8 using WixToolset.Data.Bind;
diff --git a/src/WixToolset.Core.WindowsInstaller/Patch.cs b/src/WixToolset.Core.WindowsInstaller/Patch.cs
index f4dac6e6..8e617fdb 100644
--- a/src/WixToolset.Core.WindowsInstaller/Patch.cs
+++ b/src/WixToolset.Core.WindowsInstaller/Patch.cs
@@ -29,7 +29,7 @@ namespace WixToolset.Data
29 public Patch() 29 public Patch()
30 { 30 {
31 this.inspectorExtensions = new List<IInspectorExtension>(); 31 this.inspectorExtensions = new List<IInspectorExtension>();
32 this.tableDefinitions = new TableDefinitionCollection(WindowsInstallerStandard.GetTableDefinitions()); 32 this.tableDefinitions = new TableDefinitionCollection(WindowsInstallerStandardInternal.GetTableDefinitions());
33 } 33 }
34 34
35 /// <summary> 35 /// <summary>
diff --git a/src/WixToolset.Core.WindowsInstaller/Unbind/UnbindDatabaseCommand.cs b/src/WixToolset.Core.WindowsInstaller/Unbind/UnbindDatabaseCommand.cs
index 15445bc8..9cd7b775 100644
--- a/src/WixToolset.Core.WindowsInstaller/Unbind/UnbindDatabaseCommand.cs
+++ b/src/WixToolset.Core.WindowsInstaller/Unbind/UnbindDatabaseCommand.cs
@@ -27,7 +27,7 @@ namespace WixToolset.Core.WindowsInstaller.Unbind
27 this.SuppressDemodularization = suppressDemodularization; 27 this.SuppressDemodularization = suppressDemodularization;
28 this.SkipSummaryInfo = skipSummaryInfo; 28 this.SkipSummaryInfo = skipSummaryInfo;
29 29
30 this.TableDefinitions = WindowsInstallerStandard.GetTableDefinitions(); 30 this.TableDefinitions = WindowsInstallerStandardInternal.GetTableDefinitions();
31 } 31 }
32 32
33 public Messaging Messaging { get; } 33 public Messaging Messaging { get; }
diff --git a/src/WixToolset.Core.WindowsInstaller/Unbind/UnbindTranformCommand.cs b/src/WixToolset.Core.WindowsInstaller/Unbind/UnbindTranformCommand.cs
index c0eda9c7..7eb81ac7 100644
--- a/src/WixToolset.Core.WindowsInstaller/Unbind/UnbindTranformCommand.cs
+++ b/src/WixToolset.Core.WindowsInstaller/Unbind/UnbindTranformCommand.cs
@@ -4,16 +4,13 @@ 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;
8 using System.ComponentModel; 7 using System.ComponentModel;
9 using System.Globalization; 8 using System.Globalization;
10 using System.IO; 9 using System.IO;
11 using System.Linq; 10 using System.Linq;
12 using System.Text.RegularExpressions;
13 using WixToolset.Core.Native; 11 using WixToolset.Core.Native;
14 using WixToolset.Core.WindowsInstaller.Databases; 12 using WixToolset.Core.WindowsInstaller.Bind;
15 using WixToolset.Data; 13 using WixToolset.Data;
16 using WixToolset.Data.Rows;
17 using WixToolset.Extensibility; 14 using WixToolset.Extensibility;
18 using WixToolset.Msi; 15 using WixToolset.Msi;
19 16
@@ -26,7 +23,7 @@ namespace WixToolset.Core.WindowsInstaller.Unbind
26 this.ExportBasePath = exportBasePath; 23 this.ExportBasePath = exportBasePath;
27 this.IntermediateFolder = intermediateFolder; 24 this.IntermediateFolder = intermediateFolder;
28 25
29 this.TableDefinitions = WindowsInstallerStandard.GetTableDefinitions(); 26 this.TableDefinitions = WindowsInstallerStandardInternal.GetTableDefinitions();
30 } 27 }
31 28
32 private Messaging Messaging { get; } 29 private Messaging Messaging { get; }
diff --git a/src/WixToolset.Core/Bind/ExtractEmbeddedFilesCommand.cs b/src/WixToolset.Core/Bind/ExtractEmbeddedFilesCommand.cs
index 7de40fb8..7e7c21b1 100644
--- a/src/WixToolset.Core/Bind/ExtractEmbeddedFilesCommand.cs
+++ b/src/WixToolset.Core/Bind/ExtractEmbeddedFilesCommand.cs
@@ -11,7 +11,12 @@ namespace WixToolset.Core.Bind
11 11
12 public class ExtractEmbeddedFilesCommand 12 public class ExtractEmbeddedFilesCommand
13 { 13 {
14 public IEnumerable<IExpectedExtractFile> FilesWithEmbeddedFiles { private get; set; } 14 public ExtractEmbeddedFilesCommand(IEnumerable<IExpectedExtractFile> embeddedFiles)
15 {
16 this.FilesWithEmbeddedFiles = embeddedFiles;
17 }
18
19 private IEnumerable<IExpectedExtractFile> FilesWithEmbeddedFiles { get; }
15 20
16 public void Execute() 21 public void Execute()
17 { 22 {
@@ -28,10 +33,10 @@ namespace WixToolset.Core.Bind
28 // a .wixlib embedded in a WixExtension). 33 // a .wixlib embedded in a WixExtension).
29 if ("embeddedresource" == baseUri.Scheme) 34 if ("embeddedresource" == baseUri.Scheme)
30 { 35 {
31 string assemblyPath = Path.GetFullPath(baseUri.LocalPath); 36 var assemblyPath = Path.GetFullPath(baseUri.LocalPath);
32 string resourceName = baseUri.Fragment.TrimStart('#'); 37 var resourceName = baseUri.Fragment.TrimStart('#');
33 38
34 Assembly assembly = Assembly.LoadFile(assemblyPath); 39 var assembly = Assembly.LoadFile(assemblyPath);
35 stream = assembly.GetManifestResourceStream(resourceName); 40 stream = assembly.GetManifestResourceStream(resourceName);
36 } 41 }
37 else // normal file (usually a binary .wixlib on disk). 42 else // normal file (usually a binary .wixlib on disk).
@@ -39,7 +44,7 @@ namespace WixToolset.Core.Bind
39 stream = File.OpenRead(baseUri.LocalPath); 44 stream = File.OpenRead(baseUri.LocalPath);
40 } 45 }
41 46
42 using (FileStructure fs = FileStructure.Read(stream)) 47 using (var fs = FileStructure.Read(stream))
43 { 48 {
44 var uniqueIndicies = new SortedSet<int>(); 49 var uniqueIndicies = new SortedSet<int>();
45 50
diff --git a/src/WixToolset.Core/Bind/ResolveDelayedFieldsCommand.cs b/src/WixToolset.Core/Bind/ResolveDelayedFieldsCommand.cs
index d05135cf..4585b71a 100644
--- a/src/WixToolset.Core/Bind/ResolveDelayedFieldsCommand.cs
+++ b/src/WixToolset.Core/Bind/ResolveDelayedFieldsCommand.cs
@@ -12,25 +12,28 @@ namespace WixToolset.Core.Bind
12 /// Resolves the fields which had variables that needed to be resolved after the file information 12 /// Resolves the fields which had variables that needed to be resolved after the file information
13 /// was loaded. 13 /// was loaded.
14 /// </summary> 14 /// </summary>
15 public class ResolveDelayedFieldsCommand : ICommand 15 public class ResolveDelayedFieldsCommand
16 { 16 {
17 public OutputType OutputType { private get; set;} 17 /// <summary>
18 18 /// Resolve delayed fields.
19 public IEnumerable<IDelayedField> DelayedFields { private get; set;} 19 /// </summary>
20 /// <param name="delayedFields">The fields which had resolution delayed.</param>
21 /// <param name="variableCache">The file information to use when resolving variables.</param>
22 public ResolveDelayedFieldsCommand(IEnumerable<IDelayedField> delayedFields, Dictionary<string, string> variableCache)
23 {
24 this.DelayedFields = delayedFields;
25 this.VariableCache = variableCache;
26 }
20 27
21 public IDictionary<string, string> VariableCache { private get; set; } 28 private IEnumerable<IDelayedField> DelayedFields { get;}
22 29
23 public string ModularizationGuid { private get; set; } 30 private IDictionary<string, string> VariableCache { get; }
24 31
25 /// <param name="output">Internal representation of the msi database to operate upon.</param>
26 /// <param name="delayedFields">The fields which had resolution delayed.</param>
27 /// <param name="variableCache">The file information to use when resolving variables.</param>
28 /// <param name="modularizationGuid">The modularization guid (used in case of a merge module).</param>
29 public void Execute() 32 public void Execute()
30 { 33 {
31 var deferredFields = new List<IDelayedField>(); 34 var deferredFields = new List<IDelayedField>();
32 35
33 foreach (IDelayedField delayedField in this.DelayedFields) 36 foreach (var delayedField in this.DelayedFields)
34 { 37 {
35 try 38 try
36 { 39 {
@@ -42,7 +45,7 @@ namespace WixToolset.Core.Bind
42 var value = WixVariableResolver.ResolveDelayedVariables(propertyRow.SourceLineNumbers, delayedField.Field.AsString(), this.VariableCache); 45 var value = WixVariableResolver.ResolveDelayedVariables(propertyRow.SourceLineNumbers, delayedField.Field.AsString(), this.VariableCache);
43 46
44 // update the variable cache with the new value 47 // update the variable cache with the new value
45 var key = String.Concat("property.", Common.Demodularize(this.OutputType, this.ModularizationGuid, (string)propertyRow[0])); 48 var key = String.Concat("property.", propertyRow.AsString(0));
46 this.VariableCache[key] = value; 49 this.VariableCache[key] = value;
47 50
48 // update the field data 51 // update the field data
@@ -62,43 +65,31 @@ namespace WixToolset.Core.Bind
62 65
63 // add specialization for ProductVersion fields 66 // add specialization for ProductVersion fields
64 string keyProductVersion = "property.ProductVersion"; 67 string keyProductVersion = "property.ProductVersion";
65 if (this.VariableCache.ContainsKey(keyProductVersion)) 68 if (this.VariableCache.TryGetValue(keyProductVersion, out var versionValue) && Version.TryParse(versionValue, out Version productVersion))
66 { 69 {
67 string value = this.VariableCache[keyProductVersion]; 70 // Don't add the variable if it already exists (developer defined a property with the same name).
68 Version productVersion = null; 71 string fieldKey = String.Concat(keyProductVersion, ".Major");
69 72 if (!this.VariableCache.ContainsKey(fieldKey))
70 try
71 { 73 {
72 productVersion = new Version(value); 74 this.VariableCache[fieldKey] = productVersion.Major.ToString(CultureInfo.InvariantCulture);
73 75 }
74 // Don't add the variable if it already exists (developer defined a property with the same name).
75 string fieldKey = String.Concat(keyProductVersion, ".Major");
76 if (!this.VariableCache.ContainsKey(fieldKey))
77 {
78 this.VariableCache[fieldKey] = productVersion.Major.ToString(CultureInfo.InvariantCulture);
79 }
80
81 fieldKey = String.Concat(keyProductVersion, ".Minor");
82 if (!this.VariableCache.ContainsKey(fieldKey))
83 {
84 this.VariableCache[fieldKey] = productVersion.Minor.ToString(CultureInfo.InvariantCulture);
85 }
86 76
87 fieldKey = String.Concat(keyProductVersion, ".Build"); 77 fieldKey = String.Concat(keyProductVersion, ".Minor");
88 if (!this.VariableCache.ContainsKey(fieldKey)) 78 if (!this.VariableCache.ContainsKey(fieldKey))
89 { 79 {
90 this.VariableCache[fieldKey] = productVersion.Build.ToString(CultureInfo.InvariantCulture); 80 this.VariableCache[fieldKey] = productVersion.Minor.ToString(CultureInfo.InvariantCulture);
91 } 81 }
92 82
93 fieldKey = String.Concat(keyProductVersion, ".Revision"); 83 fieldKey = String.Concat(keyProductVersion, ".Build");
94 if (!this.VariableCache.ContainsKey(fieldKey)) 84 if (!this.VariableCache.ContainsKey(fieldKey))
95 { 85 {
96 this.VariableCache[fieldKey] = productVersion.Revision.ToString(CultureInfo.InvariantCulture); 86 this.VariableCache[fieldKey] = productVersion.Build.ToString(CultureInfo.InvariantCulture);
97 }
98 } 87 }
99 catch 88
89 fieldKey = String.Concat(keyProductVersion, ".Revision");
90 if (!this.VariableCache.ContainsKey(fieldKey))
100 { 91 {
101 // Ignore the error introduced by new behavior. 92 this.VariableCache[fieldKey] = productVersion.Revision.ToString(CultureInfo.InvariantCulture);
102 } 93 }
103 } 94 }
104 95
@@ -113,7 +104,6 @@ namespace WixToolset.Core.Bind
113 catch (WixException we) 104 catch (WixException we)
114 { 105 {
115 Messaging.Instance.OnMessage(we.Error); 106 Messaging.Instance.OnMessage(we.Error);
116 continue;
117 } 107 }
118 } 108 }
119 } 109 }
diff --git a/src/WixToolset.Core/Bind/ResolvedDirectory.cs b/src/WixToolset.Core/Bind/ResolvedDirectory.cs
index fca706d8..9d07fc93 100644
--- a/src/WixToolset.Core/Bind/ResolvedDirectory.cs
+++ b/src/WixToolset.Core/Bind/ResolvedDirectory.cs
@@ -7,15 +7,6 @@ namespace WixToolset.Bind
7 /// </summary> 7 /// </summary>
8 public struct ResolvedDirectory 8 public struct ResolvedDirectory
9 { 9 {
10 /// <summary>The directory parent.</summary>
11 public string DirectoryParent;
12
13 /// <summary>The name of this directory.</summary>
14 public string Name;
15
16 /// <summary>The path of this directory.</summary>
17 public string Path;
18
19 /// <summary> 10 /// <summary>
20 /// Constructor for ResolvedDirectory. 11 /// Constructor for ResolvedDirectory.
21 /// </summary> 12 /// </summary>
@@ -27,5 +18,14 @@ namespace WixToolset.Bind
27 this.Name = name; 18 this.Name = name;
28 this.Path = null; 19 this.Path = null;
29 } 20 }
21
22 /// <summary>The directory parent.</summary>
23 public string DirectoryParent { get; set; }
24
25 /// <summary>The name of this directory.</summary>
26 public string Name { get; set; }
27
28 /// <summary>The path of this directory.</summary>
29 public string Path { get; set; }
30 } 30 }
31} 31}
diff --git a/src/WixToolset.Core/Binder.cs b/src/WixToolset.Core/Binder.cs
index 07a92d02..e282ead8 100644
--- a/src/WixToolset.Core/Binder.cs
+++ b/src/WixToolset.Core/Binder.cs
@@ -609,9 +609,9 @@ namespace WixToolset.Core
609 /// <param name="directory">Directory identifier.</param> 609 /// <param name="directory">Directory identifier.</param>
610 /// <param name="canonicalize">Canonicalize the path for standard directories.</param> 610 /// <param name="canonicalize">Canonicalize the path for standard directories.</param>
611 /// <returns>Source path of a directory.</returns> 611 /// <returns>Source path of a directory.</returns>
612 public static string GetDirectoryPath(Hashtable directories, Hashtable componentIdGenSeeds, string directory, bool canonicalize) 612 public static string GetDirectoryPath(Dictionary<string, ResolvedDirectory> directories, Dictionary<string, string> componentIdGenSeeds, string directory, bool canonicalize)
613 { 613 {
614 if (!directories.Contains(directory)) 614 if (!directories.ContainsKey(directory))
615 { 615 {
616 throw new WixException(WixErrors.ExpectedDirectory(directory)); 616 throw new WixException(WixErrors.ExpectedDirectory(directory));
617 } 617 }
@@ -620,7 +620,7 @@ namespace WixToolset.Core
620 620
621 if (null == resolvedDirectory.Path) 621 if (null == resolvedDirectory.Path)
622 { 622 {
623 if (null != componentIdGenSeeds && componentIdGenSeeds.Contains(directory)) 623 if (null != componentIdGenSeeds && componentIdGenSeeds.ContainsKey(directory))
624 { 624 {
625 resolvedDirectory.Path = (string)componentIdGenSeeds[directory]; 625 resolvedDirectory.Path = (string)componentIdGenSeeds[directory];
626 } 626 }
@@ -670,7 +670,7 @@ namespace WixToolset.Core
670 /// <param name="compressed">Specifies the package is compressed.</param> 670 /// <param name="compressed">Specifies the package is compressed.</param>
671 /// <param name="useLongName">Specifies the package uses long file names.</param> 671 /// <param name="useLongName">Specifies the package uses long file names.</param>
672 /// <returns>Source path of file relative to package directory.</returns> 672 /// <returns>Source path of file relative to package directory.</returns>
673 public static string GetFileSourcePath(Hashtable directories, string directoryId, string fileName, bool compressed, bool useLongName) 673 public static string GetFileSourcePath(Dictionary<string, ResolvedDirectory> directories, string directoryId, string fileName, bool compressed, bool useLongName)
674 { 674 {
675 string fileSourcePath = Common.GetName(fileName, true, useLongName); 675 string fileSourcePath = Common.GetName(fileName, true, useLongName);
676 676
diff --git a/src/WixToolset.Core/Compiler.cs b/src/WixToolset.Core/Compiler.cs
index e0475baa..903aae61 100644
--- a/src/WixToolset.Core/Compiler.cs
+++ b/src/WixToolset.Core/Compiler.cs
@@ -7137,7 +7137,7 @@ namespace WixToolset
7137 } 7137 }
7138 7138
7139 // finally, schedule RemoveExistingProducts 7139 // finally, schedule RemoveExistingProducts
7140 row = this.Core.CreateRow(sourceLineNumbers, TupleDefinitionType.WixAction); 7140 row = this.Core.CreateRow(sourceLineNumbers, TupleDefinitionType.WixAction, new Identifier("InstallExecuteSequence/RemoveExistingProducts", AccessModifier.Public));
7141 row.Set(0, "InstallExecuteSequence"); 7141 row.Set(0, "InstallExecuteSequence");
7142 row.Set(1, "RemoveExistingProducts"); 7142 row.Set(1, "RemoveExistingProducts");
7143 // row.Set(2, condition); 7143 // row.Set(2, condition);
@@ -7641,11 +7641,11 @@ namespace WixToolset
7641 row.Set(4, diskId); 7641 row.Set(4, diskId);
7642 if (YesNoType.Yes == fileCompression) 7642 if (YesNoType.Yes == fileCompression)
7643 { 7643 {
7644 row.Set(5, 1); 7644 row.Set(5, true);
7645 } 7645 }
7646 else if (YesNoType.No == fileCompression) 7646 else if (YesNoType.No == fileCompression)
7647 { 7647 {
7648 row.Set(5, 0); 7648 row.Set(5, false);
7649 } 7649 }
7650 else // YesNoType.NotSet == fileCompression 7650 else // YesNoType.NotSet == fileCompression
7651 { 7651 {
@@ -9525,7 +9525,7 @@ namespace WixToolset
9525 var patchIdRow = this.Core.CreateRow(sourceLineNumbers, TupleDefinitionType.WixPatchId); 9525 var patchIdRow = this.Core.CreateRow(sourceLineNumbers, TupleDefinitionType.WixPatchId);
9526 patchIdRow.Set(0, patchId); 9526 patchIdRow.Set(0, patchId);
9527 patchIdRow.Set(1, clientPatchId); 9527 patchIdRow.Set(1, clientPatchId);
9528 patchIdRow.Set(2, optimizePatchSizeForLargeFiles ? 1 : 0); 9528 patchIdRow.Set(2, optimizePatchSizeForLargeFiles);
9529 patchIdRow.Set(3, apiPatchingSymbolFlags); 9529 patchIdRow.Set(3, apiPatchingSymbolFlags);
9530 9530
9531 if (allowRemoval) 9531 if (allowRemoval)
diff --git a/src/WixToolset.Core/Linker.cs b/src/WixToolset.Core/Linker.cs
index 59481387..7faf69ba 100644
--- a/src/WixToolset.Core/Linker.cs
+++ b/src/WixToolset.Core/Linker.cs
@@ -23,9 +23,6 @@ namespace WixToolset.Core
23 private static readonly string emptyGuid = Guid.Empty.ToString("B"); 23 private static readonly string emptyGuid = Guid.Empty.ToString("B");
24 24
25 private bool sectionIdOnRows; 25 private bool sectionIdOnRows;
26 //private WixActionRowCollection standardActions;
27 //private Output activeOutput;
28 //private TableDefinitionCollection tableDefinitions;
29 26
30 /// <summary> 27 /// <summary>
31 /// Creates a linker. 28 /// Creates a linker.
@@ -34,9 +31,6 @@ namespace WixToolset.Core
34 { 31 {
35 this.sectionIdOnRows = true; // TODO: what is the correct value for this? 32 this.sectionIdOnRows = true; // TODO: what is the correct value for this?
36 33
37 //this.standardActions = WindowsInstallerStandard.GetStandardActions();
38 //this.tableDefinitions = new TableDefinitionCollection(WindowsInstallerStandard.GetTableDefinitions());
39
40 //this.extensionData = new List<IExtensionData>(); 34 //this.extensionData = new List<IExtensionData>();
41 //this.inspectorExtensions = new List<InspectorExtension>(); 35 //this.inspectorExtensions = new List<InspectorExtension>();
42 } 36 }
@@ -108,11 +102,6 @@ namespace WixToolset.Core
108 102
109 //this.activeOutput = null; 103 //this.activeOutput = null;
110 104
111#if MOVE_TO_BACKEND
112 var actionRows = new List<IntermediateTuple>();
113 var suppressActionRows = new List<IntermediateTuple>();
114#endif
115
116 //TableDefinitionCollection customTableDefinitions = new TableDefinitionCollection(); 105 //TableDefinitionCollection customTableDefinitions = new TableDefinitionCollection();
117 //IntermediateTuple customRows = new List<IntermediateTuple>(); 106 //IntermediateTuple customRows = new List<IntermediateTuple>();
118 107
@@ -232,7 +221,7 @@ namespace WixToolset.Core
232 } 221 }
233 222
234 // Report duplicates that would ultimately end up being primary key collisions. 223 // Report duplicates that would ultimately end up being primary key collisions.
235 ReportConflictingSymbolsCommand reportDupes = new ReportConflictingSymbolsCommand(find.PossiblyConflictingSymbols, resolve.ResolvedSections); 224 var reportDupes = new ReportConflictingSymbolsCommand(find.PossiblyConflictingSymbols, resolve.ResolvedSections);
236 reportDupes.Execute(); 225 reportDupes.Execute();
237 226
238 if (Messaging.Instance.EncounteredError) 227 if (Messaging.Instance.EncounteredError)
@@ -394,19 +383,6 @@ namespace WixToolset.Core
394 } 383 }
395 break; 384 break;
396 385
397#if MOVE_TO_BACKEND
398 case TupleDefinitionType.WixAction:
399 //if (this.sectionIdOnRows)
400 //{
401 // foreach (Row row in table.Rows)
402 // {
403 // row.SectionId = sectionId;
404 // }
405 //}
406 actionRows.Add(tuple);
407 break;
408#endif
409
410#if SOLVE_CUSTOM_TABLE 386#if SOLVE_CUSTOM_TABLE
411 case "WixCustomTable": 387 case "WixCustomTable":
412 this.LinkCustomTable(table, customTableDefinitions); 388 this.LinkCustomTable(table, customTableDefinitions);
@@ -455,12 +431,6 @@ namespace WixToolset.Core
455 } 431 }
456 break; 432 break;
457 433
458#if MOVE_TO_BACKEND
459 case TupleDefinitionType.WixSuppressAction:
460 suppressActionRows.Add(tuple);
461 break;
462#endif
463
464 case TupleDefinitionType.WixComplexReference: 434 case TupleDefinitionType.WixComplexReference:
465 copyTuple = false; 435 copyTuple = false;
466 break; 436 break;
@@ -539,11 +509,6 @@ namespace WixToolset.Core
539#endif 509#endif
540 510
541#if MOVE_TO_BACKEND 511#if MOVE_TO_BACKEND
542 // sequence all the actions
543 this.SequenceActions(actionRows, suppressActionRows);
544#endif
545
546#if MOVE_TO_BACKEND
547 // check for missing table and add them or display an error as appropriate 512 // check for missing table and add them or display an error as appropriate
548 switch (this.activeOutput.Type) 513 switch (this.activeOutput.Type)
549 { 514 {
@@ -1140,6 +1105,7 @@ namespace WixToolset.Core
1140 } 1105 }
1141 } 1106 }
1142#endif 1107#endif
1108
1143 /// <summary> 1109 /// <summary>
1144 /// Sends a message to the message delegate if there is one. 1110 /// Sends a message to the message delegate if there is one.
1145 /// </summary> 1111 /// </summary>
@@ -1726,638 +1692,6 @@ namespace WixToolset.Core
1726 } 1692 }
1727#endif 1693#endif
1728 1694
1729#if MOVE_TO_BACKEND
1730 /// <summary>
1731 /// Set sequence numbers for all the actions and create rows in the output object.
1732 /// </summary>
1733 /// <param name="actionRows">Collection of actions to schedule.</param>
1734 /// <param name="suppressActionRows">Collection of actions to suppress.</param>
1735 private void SequenceActions(List<IntermediateTuple> actionRows, List<IntermediateTuple> suppressActionRows)
1736 {
1737 var overridableActionRows = new WixActionRowCollection();
1738 var requiredActionRows = new WixActionRowCollection();
1739 ArrayList scheduledActionRows = new ArrayList();
1740
1741 // gather the required actions for the output type
1742 if (OutputType.Product == this.activeOutput.Type)
1743 {
1744 // AdminExecuteSequence table
1745 overridableActionRows.Add(this.standardActions[SequenceTable.AdminExecuteSequence, "CostFinalize"]);
1746 overridableActionRows.Add(this.standardActions[SequenceTable.AdminExecuteSequence, "CostInitialize"]);
1747 overridableActionRows.Add(this.standardActions[SequenceTable.AdminExecuteSequence, "FileCost"]);
1748 overridableActionRows.Add(this.standardActions[SequenceTable.AdminExecuteSequence, "InstallAdminPackage"]);
1749 overridableActionRows.Add(this.standardActions[SequenceTable.AdminExecuteSequence, "InstallFiles"]);
1750 overridableActionRows.Add(this.standardActions[SequenceTable.AdminExecuteSequence, "InstallFinalize"]);
1751 overridableActionRows.Add(this.standardActions[SequenceTable.AdminExecuteSequence, "InstallInitialize"]);
1752 overridableActionRows.Add(this.standardActions[SequenceTable.AdminExecuteSequence, "InstallValidate"]);
1753
1754 // AdminUISequence table
1755 overridableActionRows.Add(this.standardActions[SequenceTable.AdminUISequence, "CostFinalize"]);
1756 overridableActionRows.Add(this.standardActions[SequenceTable.AdminUISequence, "CostInitialize"]);
1757 overridableActionRows.Add(this.standardActions[SequenceTable.AdminUISequence, "ExecuteAction"]);
1758 overridableActionRows.Add(this.standardActions[SequenceTable.AdminUISequence, "FileCost"]);
1759
1760 // AdvtExecuteSequence table
1761 overridableActionRows.Add(this.standardActions[SequenceTable.AdvtExecuteSequence, "CostFinalize"]);
1762 overridableActionRows.Add(this.standardActions[SequenceTable.AdvtExecuteSequence, "CostInitialize"]);
1763 overridableActionRows.Add(this.standardActions[SequenceTable.AdvtExecuteSequence, "InstallFinalize"]);
1764 overridableActionRows.Add(this.standardActions[SequenceTable.AdvtExecuteSequence, "InstallInitialize"]);
1765 overridableActionRows.Add(this.standardActions[SequenceTable.AdvtExecuteSequence, "InstallValidate"]);
1766 overridableActionRows.Add(this.standardActions[SequenceTable.AdvtExecuteSequence, "PublishFeatures"]);
1767 overridableActionRows.Add(this.standardActions[SequenceTable.AdvtExecuteSequence, "PublishProduct"]);
1768
1769 // InstallExecuteSequence table
1770 overridableActionRows.Add(this.standardActions[SequenceTable.InstallExecuteSequence, "CostFinalize"]);
1771 overridableActionRows.Add(this.standardActions[SequenceTable.InstallExecuteSequence, "CostInitialize"]);
1772 overridableActionRows.Add(this.standardActions[SequenceTable.InstallExecuteSequence, "FileCost"]);
1773 overridableActionRows.Add(this.standardActions[SequenceTable.InstallExecuteSequence, "InstallFinalize"]);
1774 overridableActionRows.Add(this.standardActions[SequenceTable.InstallExecuteSequence, "InstallInitialize"]);
1775 overridableActionRows.Add(this.standardActions[SequenceTable.InstallExecuteSequence, "InstallValidate"]);
1776 overridableActionRows.Add(this.standardActions[SequenceTable.InstallExecuteSequence, "ProcessComponents"]);
1777 overridableActionRows.Add(this.standardActions[SequenceTable.InstallExecuteSequence, "PublishFeatures"]);
1778 overridableActionRows.Add(this.standardActions[SequenceTable.InstallExecuteSequence, "PublishProduct"]);
1779 overridableActionRows.Add(this.standardActions[SequenceTable.InstallExecuteSequence, "RegisterProduct"]);
1780 overridableActionRows.Add(this.standardActions[SequenceTable.InstallExecuteSequence, "RegisterUser"]);
1781 overridableActionRows.Add(this.standardActions[SequenceTable.InstallExecuteSequence, "UnpublishFeatures"]);
1782 overridableActionRows.Add(this.standardActions[SequenceTable.InstallExecuteSequence, "ValidateProductID"]);
1783
1784 // InstallUISequence table
1785 overridableActionRows.Add(this.standardActions[SequenceTable.InstallUISequence, "CostFinalize"]);
1786 overridableActionRows.Add(this.standardActions[SequenceTable.InstallUISequence, "CostInitialize"]);
1787 overridableActionRows.Add(this.standardActions[SequenceTable.InstallUISequence, "ExecuteAction"]);
1788 overridableActionRows.Add(this.standardActions[SequenceTable.InstallUISequence, "FileCost"]);
1789 overridableActionRows.Add(this.standardActions[SequenceTable.InstallUISequence, "ValidateProductID"]);
1790 }
1791
1792 // gather the required actions for each table
1793 foreach (Table table in this.activeOutput.Tables)
1794 {
1795 switch (table.Name)
1796 {
1797 case "AppSearch":
1798 overridableActionRows.Add(this.standardActions[SequenceTable.InstallExecuteSequence, "AppSearch"], true);
1799 overridableActionRows.Add(this.standardActions[SequenceTable.InstallUISequence, "AppSearch"], true);
1800 break;
1801 case "BindImage":
1802 overridableActionRows.Add(this.standardActions[SequenceTable.InstallExecuteSequence, "BindImage"], true);
1803 break;
1804 case "CCPSearch":
1805 overridableActionRows.Add(this.standardActions[SequenceTable.InstallExecuteSequence, "AppSearch"], true);
1806 overridableActionRows.Add(this.standardActions[SequenceTable.InstallExecuteSequence, "CCPSearch"], true);
1807 overridableActionRows.Add(this.standardActions[SequenceTable.InstallExecuteSequence, "RMCCPSearch"], true);
1808 overridableActionRows.Add(this.standardActions[SequenceTable.InstallUISequence, "AppSearch"], true);
1809 overridableActionRows.Add(this.standardActions[SequenceTable.InstallUISequence, "CCPSearch"], true);
1810 overridableActionRows.Add(this.standardActions[SequenceTable.InstallUISequence, "RMCCPSearch"], true);
1811 break;
1812 case "Class":
1813 overridableActionRows.Add(this.standardActions[SequenceTable.AdvtExecuteSequence, "RegisterClassInfo"], true);
1814 overridableActionRows.Add(this.standardActions[SequenceTable.InstallExecuteSequence, "RegisterClassInfo"], true);
1815 overridableActionRows.Add(this.standardActions[SequenceTable.InstallExecuteSequence, "UnregisterClassInfo"], true);
1816 break;
1817 case "Complus":
1818 overridableActionRows.Add(this.standardActions[SequenceTable.InstallExecuteSequence, "RegisterComPlus"], true);
1819 overridableActionRows.Add(this.standardActions[SequenceTable.InstallExecuteSequence, "UnregisterComPlus"], true);
1820 break;
1821 case "CreateFolder":
1822 overridableActionRows.Add(this.standardActions[SequenceTable.InstallExecuteSequence, "CreateFolders"], true);
1823 overridableActionRows.Add(this.standardActions[SequenceTable.InstallExecuteSequence, "RemoveFolders"], true);
1824 break;
1825 case "DuplicateFile":
1826 overridableActionRows.Add(this.standardActions[SequenceTable.InstallExecuteSequence, "DuplicateFiles"], true);
1827 overridableActionRows.Add(this.standardActions[SequenceTable.InstallExecuteSequence, "RemoveDuplicateFiles"], true);
1828 break;
1829 case "Environment":
1830 overridableActionRows.Add(this.standardActions[SequenceTable.InstallExecuteSequence, "WriteEnvironmentStrings"], true);
1831 overridableActionRows.Add(this.standardActions[SequenceTable.InstallExecuteSequence, "RemoveEnvironmentStrings"], true);
1832 break;
1833 case "Extension":
1834 overridableActionRows.Add(this.standardActions[SequenceTable.AdvtExecuteSequence, "RegisterExtensionInfo"], true);
1835 overridableActionRows.Add(this.standardActions[SequenceTable.InstallExecuteSequence, "RegisterExtensionInfo"], true);
1836 overridableActionRows.Add(this.standardActions[SequenceTable.InstallExecuteSequence, "UnregisterExtensionInfo"], true);
1837 break;
1838 case "File":
1839 overridableActionRows.Add(this.standardActions[SequenceTable.InstallExecuteSequence, "InstallFiles"], true);
1840 overridableActionRows.Add(this.standardActions[SequenceTable.InstallExecuteSequence, "RemoveFiles"], true);
1841 break;
1842 case "Font":
1843 overridableActionRows.Add(this.standardActions[SequenceTable.InstallExecuteSequence, "RegisterFonts"], true);
1844 overridableActionRows.Add(this.standardActions[SequenceTable.InstallExecuteSequence, "UnregisterFonts"], true);
1845 break;
1846 case "IniFile":
1847 case "RemoveIniFile":
1848 overridableActionRows.Add(this.standardActions[SequenceTable.InstallExecuteSequence, "WriteIniValues"], true);
1849 overridableActionRows.Add(this.standardActions[SequenceTable.InstallExecuteSequence, "RemoveIniValues"], true);
1850 break;
1851 case "IsolatedComponent":
1852 overridableActionRows.Add(this.standardActions[SequenceTable.InstallExecuteSequence, "IsolateComponents"], true);
1853 break;
1854 case "LaunchCondition":
1855 overridableActionRows.Add(this.standardActions[SequenceTable.InstallExecuteSequence, "LaunchConditions"], true);
1856 overridableActionRows.Add(this.standardActions[SequenceTable.InstallUISequence, "LaunchConditions"], true);
1857 break;
1858 case "MIME":
1859 overridableActionRows.Add(this.standardActions[SequenceTable.AdvtExecuteSequence, "RegisterMIMEInfo"], true);
1860 overridableActionRows.Add(this.standardActions[SequenceTable.InstallExecuteSequence, "RegisterMIMEInfo"], true);
1861 overridableActionRows.Add(this.standardActions[SequenceTable.InstallExecuteSequence, "UnregisterMIMEInfo"], true);
1862 break;
1863 case "MoveFile":
1864 overridableActionRows.Add(this.standardActions[SequenceTable.InstallExecuteSequence, "MoveFiles"], true);
1865 break;
1866 case "MsiAssembly":
1867 overridableActionRows.Add(this.standardActions[SequenceTable.AdvtExecuteSequence, "MsiPublishAssemblies"], true);
1868 overridableActionRows.Add(this.standardActions[SequenceTable.InstallExecuteSequence, "MsiPublishAssemblies"], true);
1869 overridableActionRows.Add(this.standardActions[SequenceTable.InstallExecuteSequence, "MsiUnpublishAssemblies"], true);
1870 break;
1871 case "MsiServiceConfig":
1872 case "MsiServiceConfigFailureActions":
1873 overridableActionRows.Add(this.standardActions[SequenceTable.InstallExecuteSequence, "MsiConfigureServices"], true);
1874 break;
1875 case "ODBCDataSource":
1876 case "ODBCTranslator":
1877 case "ODBCDriver":
1878 overridableActionRows.Add(this.standardActions[SequenceTable.InstallExecuteSequence, "SetODBCFolders"], true);
1879 overridableActionRows.Add(this.standardActions[SequenceTable.InstallExecuteSequence, "InstallODBC"], true);
1880 overridableActionRows.Add(this.standardActions[SequenceTable.InstallExecuteSequence, "RemoveODBC"], true);
1881 break;
1882 case "ProgId":
1883 overridableActionRows.Add(this.standardActions[SequenceTable.AdvtExecuteSequence, "RegisterProgIdInfo"], true);
1884 overridableActionRows.Add(this.standardActions[SequenceTable.InstallExecuteSequence, "RegisterProgIdInfo"], true);
1885 overridableActionRows.Add(this.standardActions[SequenceTable.InstallExecuteSequence, "UnregisterProgIdInfo"], true);
1886 break;
1887 case "PublishComponent":
1888 overridableActionRows.Add(this.standardActions[SequenceTable.AdvtExecuteSequence, "PublishComponents"], true);
1889 overridableActionRows.Add(this.standardActions[SequenceTable.InstallExecuteSequence, "PublishComponents"], true);
1890 overridableActionRows.Add(this.standardActions[SequenceTable.InstallExecuteSequence, "UnpublishComponents"], true);
1891 break;
1892 case "Registry":
1893 case "RemoveRegistry":
1894 overridableActionRows.Add(this.standardActions[SequenceTable.InstallExecuteSequence, "WriteRegistryValues"], true);
1895 overridableActionRows.Add(this.standardActions[SequenceTable.InstallExecuteSequence, "RemoveRegistryValues"], true);
1896 break;
1897 case "RemoveFile":
1898 overridableActionRows.Add(this.standardActions[SequenceTable.InstallExecuteSequence, "RemoveFiles"], true);
1899 break;
1900 case "SelfReg":
1901 overridableActionRows.Add(this.standardActions[SequenceTable.InstallExecuteSequence, "SelfRegModules"], true);
1902 overridableActionRows.Add(this.standardActions[SequenceTable.InstallExecuteSequence, "SelfUnregModules"], true);
1903 break;
1904 case "ServiceControl":
1905 overridableActionRows.Add(this.standardActions[SequenceTable.InstallExecuteSequence, "StartServices"], true);
1906 overridableActionRows.Add(this.standardActions[SequenceTable.InstallExecuteSequence, "StopServices"], true);
1907 overridableActionRows.Add(this.standardActions[SequenceTable.InstallExecuteSequence, "DeleteServices"], true);
1908 break;
1909 case "ServiceInstall":
1910 overridableActionRows.Add(this.standardActions[SequenceTable.InstallExecuteSequence, "InstallServices"], true);
1911 break;
1912 case "Shortcut":
1913 overridableActionRows.Add(this.standardActions[SequenceTable.AdvtExecuteSequence, "CreateShortcuts"], true);
1914 overridableActionRows.Add(this.standardActions[SequenceTable.InstallExecuteSequence, "CreateShortcuts"], true);
1915 overridableActionRows.Add(this.standardActions[SequenceTable.InstallExecuteSequence, "RemoveShortcuts"], true);
1916 break;
1917 case "TypeLib":
1918 overridableActionRows.Add(this.standardActions[SequenceTable.InstallExecuteSequence, "RegisterTypeLibraries"], true);
1919 overridableActionRows.Add(this.standardActions[SequenceTable.InstallExecuteSequence, "UnregisterTypeLibraries"], true);
1920 break;
1921 case "Upgrade":
1922 overridableActionRows.Add(this.standardActions[SequenceTable.InstallExecuteSequence, "FindRelatedProducts"], true);
1923 overridableActionRows.Add(this.standardActions[SequenceTable.InstallUISequence, "FindRelatedProducts"], true);
1924 // Only add the MigrateFeatureStates action if MigrateFeature attribute is set to yes on at least one UpgradeVersion element.
1925 foreach (Row row in table.Rows)
1926 {
1927 int options = (int)row[4];
1928 if (MsiInterop.MsidbUpgradeAttributesMigrateFeatures == (options & MsiInterop.MsidbUpgradeAttributesMigrateFeatures))
1929 {
1930 overridableActionRows.Add(this.standardActions[SequenceTable.InstallExecuteSequence, "MigrateFeatureStates"], true);
1931 overridableActionRows.Add(this.standardActions[SequenceTable.InstallUISequence, "MigrateFeatureStates"], true);
1932 break;
1933 }
1934 }
1935 break;
1936 }
1937 }
1938
1939 // index all the action rows (look for collisions)
1940 foreach (WixActionRow actionRow in actionRows)
1941 {
1942 if (actionRow.Overridable) // overridable action
1943 {
1944 WixActionRow collidingActionRow = overridableActionRows[actionRow.SequenceTable, actionRow.Action];
1945
1946 if (null != collidingActionRow)
1947 {
1948 this.OnMessage(WixErrors.OverridableActionCollision(actionRow.SourceLineNumbers, actionRow.SequenceTable.ToString(), actionRow.Action));
1949 if (null != collidingActionRow.SourceLineNumbers)
1950 {
1951 this.OnMessage(WixErrors.OverridableActionCollision2(collidingActionRow.SourceLineNumbers));
1952 }
1953 }
1954 else
1955 {
1956 overridableActionRows.Add(actionRow);
1957 }
1958 }
1959 else // unscheduled/scheduled action
1960 {
1961 // unscheduled action (allowed for certain standard actions)
1962 if (null == actionRow.Before && null == actionRow.After && 0 == actionRow.Sequence)
1963 {
1964 WixActionRow standardAction = this.standardActions[actionRow.SequenceTable, actionRow.Action];
1965
1966 if (null != standardAction)
1967 {
1968 // populate the sequence from the standard action
1969 actionRow.Sequence = standardAction.Sequence;
1970 }
1971 else // not a supported unscheduled action
1972 {
1973 throw new InvalidOperationException(WixStrings.EXP_FoundActionRowWithNoSequenceBeforeOrAfterColumnSet);
1974 }
1975 }
1976
1977 WixActionRow collidingActionRow = requiredActionRows[actionRow.SequenceTable, actionRow.Action];
1978
1979 if (null != collidingActionRow)
1980 {
1981 this.OnMessage(WixErrors.ActionCollision(actionRow.SourceLineNumbers, actionRow.SequenceTable.ToString(), actionRow.Action));
1982 if (null != collidingActionRow.SourceLineNumbers)
1983 {
1984 this.OnMessage(WixErrors.ActionCollision2(collidingActionRow.SourceLineNumbers));
1985 }
1986 }
1987 else
1988 {
1989 requiredActionRows.Add(actionRow.Clone());
1990 }
1991 }
1992 }
1993
1994 // add the overridable action rows that are not overridden to the required action rows
1995 foreach (WixActionRow actionRow in overridableActionRows)
1996 {
1997 if (null == requiredActionRows[actionRow.SequenceTable, actionRow.Action])
1998 {
1999 requiredActionRows.Add(actionRow.Clone());
2000 }
2001 }
2002
2003 // suppress the required actions that are overridable
2004 foreach (Row suppressActionRow in suppressActionRows)
2005 {
2006 SequenceTable sequenceTable = (SequenceTable)Enum.Parse(typeof(SequenceTable), (string)suppressActionRow[0]);
2007 string action = (string)suppressActionRow[1];
2008
2009 // get the action being suppressed (if it exists)
2010 WixActionRow requiredActionRow = requiredActionRows[sequenceTable, action];
2011
2012 // if there is an overridable row to suppress; suppress it
2013 // there is no warning if there is no action to suppress because the action may be suppressed from a merge module in the binder
2014 if (null != requiredActionRow)
2015 {
2016 if (requiredActionRow.Overridable)
2017 {
2018 this.OnMessage(WixWarnings.SuppressAction(suppressActionRow.SourceLineNumbers, action, sequenceTable.ToString()));
2019 if (null != requiredActionRow.SourceLineNumbers)
2020 {
2021 this.OnMessage(WixWarnings.SuppressAction2(requiredActionRow.SourceLineNumbers));
2022 }
2023 requiredActionRows.Remove(sequenceTable, action);
2024 }
2025 else // suppressing a non-overridable action row
2026 {
2027 this.OnMessage(WixErrors.SuppressNonoverridableAction(suppressActionRow.SourceLineNumbers, sequenceTable.ToString(), action));
2028 if (null != requiredActionRow.SourceLineNumbers)
2029 {
2030 this.OnMessage(WixErrors.SuppressNonoverridableAction2(requiredActionRow.SourceLineNumbers));
2031 }
2032 }
2033 }
2034 }
2035
2036 // create a copy of the required action rows so that new rows can be added while enumerating
2037 WixActionRow[] copyOfRequiredActionRows = new WixActionRow[requiredActionRows.Count];
2038 requiredActionRows.CopyTo(copyOfRequiredActionRows, 0);
2039
2040 // build up dependency trees of the relatively scheduled actions
2041 foreach (WixActionRow actionRow in copyOfRequiredActionRows)
2042 {
2043 if (0 == actionRow.Sequence)
2044 {
2045 // check for standard actions that don't have a sequence number in a merge module
2046 if (OutputType.Module == this.activeOutput.Type && WindowsInstallerStandard.IsStandardAction(actionRow.Action))
2047 {
2048 this.OnMessage(WixErrors.StandardActionRelativelyScheduledInModule(actionRow.SourceLineNumbers, actionRow.SequenceTable.ToString(), actionRow.Action));
2049 }
2050
2051 this.SequenceActionRow(actionRow, requiredActionRows);
2052 }
2053 else if (OutputType.Module == this.activeOutput.Type && 0 < actionRow.Sequence && !WindowsInstallerStandard.IsStandardAction(actionRow.Action)) // check for custom actions and dialogs that have a sequence number
2054 {
2055 this.OnMessage(WixErrors.CustomActionSequencedInModule(actionRow.SourceLineNumbers, actionRow.SequenceTable.ToString(), actionRow.Action));
2056 }
2057 }
2058
2059 // look for standard actions with sequence restrictions that aren't necessarily scheduled based on the presence of a particular table
2060 if (requiredActionRows.Contains(SequenceTable.InstallExecuteSequence, "DuplicateFiles") && !requiredActionRows.Contains(SequenceTable.InstallExecuteSequence, "InstallFiles"))
2061 {
2062 requiredActionRows.Add(this.standardActions[SequenceTable.InstallExecuteSequence, "InstallFiles"], true);
2063 }
2064
2065 // schedule actions
2066 if (OutputType.Module == this.activeOutput.Type)
2067 {
2068 // add the action row to the list of scheduled action rows
2069 scheduledActionRows.AddRange(requiredActionRows);
2070 }
2071 else
2072 {
2073 // process each sequence table individually
2074 foreach (SequenceTable sequenceTable in Enum.GetValues(typeof(SequenceTable)))
2075 {
2076 // create a collection of just the action rows in this sequence
2077 WixActionRowCollection sequenceActionRows = new WixActionRowCollection();
2078 foreach (WixActionRow actionRow in requiredActionRows)
2079 {
2080 if (sequenceTable == actionRow.SequenceTable)
2081 {
2082 sequenceActionRows.Add(actionRow);
2083 }
2084 }
2085
2086 // schedule the absolutely scheduled actions (by sorting them by their sequence numbers)
2087 ArrayList absoluteActionRows = new ArrayList();
2088 foreach (WixActionRow actionRow in sequenceActionRows)
2089 {
2090 if (0 != actionRow.Sequence)
2091 {
2092 // look for sequence number collisions
2093 foreach (WixActionRow sequenceScheduledActionRow in absoluteActionRows)
2094 {
2095 if (sequenceScheduledActionRow.Sequence == actionRow.Sequence)
2096 {
2097 this.OnMessage(WixWarnings.ActionSequenceCollision(actionRow.SourceLineNumbers, actionRow.SequenceTable.ToString(), actionRow.Action, sequenceScheduledActionRow.Action, actionRow.Sequence));
2098 if (null != sequenceScheduledActionRow.SourceLineNumbers)
2099 {
2100 this.OnMessage(WixWarnings.ActionSequenceCollision2(sequenceScheduledActionRow.SourceLineNumbers));
2101 }
2102 }
2103 }
2104
2105 absoluteActionRows.Add(actionRow);
2106 }
2107 }
2108 absoluteActionRows.Sort();
2109
2110 // schedule the relatively scheduled actions (by resolving the dependency trees)
2111 int previousUsedSequence = 0;
2112 ArrayList relativeActionRows = new ArrayList();
2113 for (int j = 0; j < absoluteActionRows.Count; j++)
2114 {
2115 WixActionRow absoluteActionRow = (WixActionRow)absoluteActionRows[j];
2116 int unusedSequence;
2117
2118 // get all the relatively scheduled action rows occuring before this absolutely scheduled action row
2119 RowIndexedList<WixActionRow> allPreviousActionRows = new RowIndexedList<WixActionRow>();
2120 absoluteActionRow.GetAllPreviousActionRows(sequenceTable, allPreviousActionRows);
2121
2122 // get all the relatively scheduled action rows occuring after this absolutely scheduled action row
2123 RowIndexedList<WixActionRow> allNextActionRows = new RowIndexedList<WixActionRow>();
2124 absoluteActionRow.GetAllNextActionRows(sequenceTable, allNextActionRows);
2125
2126 // check for relatively scheduled actions occuring before/after a special action (these have a negative sequence number)
2127 if (0 > absoluteActionRow.Sequence && (0 < allPreviousActionRows.Count || 0 < allNextActionRows.Count))
2128 {
2129 // create errors for all the before actions
2130 foreach (WixActionRow actionRow in allPreviousActionRows)
2131 {
2132 this.OnMessage(WixErrors.ActionScheduledRelativeToTerminationAction(actionRow.SourceLineNumbers, actionRow.SequenceTable.ToString(), actionRow.Action, absoluteActionRow.Action));
2133 }
2134
2135 // create errors for all the after actions
2136 foreach (WixActionRow actionRow in allNextActionRows)
2137 {
2138 this.OnMessage(WixErrors.ActionScheduledRelativeToTerminationAction(actionRow.SourceLineNumbers, actionRow.SequenceTable.ToString(), actionRow.Action, absoluteActionRow.Action));
2139 }
2140
2141 // if there is source line information for the absolutely scheduled action display it
2142 if (null != absoluteActionRow.SourceLineNumbers)
2143 {
2144 this.OnMessage(WixErrors.ActionScheduledRelativeToTerminationAction2(absoluteActionRow.SourceLineNumbers));
2145 }
2146
2147 continue;
2148 }
2149
2150 // schedule the action rows before this one
2151 unusedSequence = absoluteActionRow.Sequence - 1;
2152 for (int i = allPreviousActionRows.Count - 1; i >= 0; i--)
2153 {
2154 WixActionRow relativeActionRow = (WixActionRow)allPreviousActionRows[i];
2155
2156 // look for collisions
2157 if (unusedSequence == previousUsedSequence)
2158 {
2159 this.OnMessage(WixErrors.NoUniqueActionSequenceNumber(relativeActionRow.SourceLineNumbers, relativeActionRow.SequenceTable.ToString(), relativeActionRow.Action, absoluteActionRow.Action));
2160 if (null != absoluteActionRow.SourceLineNumbers)
2161 {
2162 this.OnMessage(WixErrors.NoUniqueActionSequenceNumber2(absoluteActionRow.SourceLineNumbers));
2163 }
2164
2165 unusedSequence++;
2166 }
2167
2168 relativeActionRow.Sequence = unusedSequence;
2169 relativeActionRows.Add(relativeActionRow);
2170
2171 unusedSequence--;
2172 }
2173
2174 // determine the next used action sequence number
2175 int nextUsedSequence;
2176 if (absoluteActionRows.Count > j + 1)
2177 {
2178 nextUsedSequence = ((WixActionRow)absoluteActionRows[j + 1]).Sequence;
2179 }
2180 else
2181 {
2182 nextUsedSequence = short.MaxValue + 1;
2183 }
2184
2185 // schedule the action rows after this one
2186 unusedSequence = absoluteActionRow.Sequence + 1;
2187 for (int i = 0; i < allNextActionRows.Count; i++)
2188 {
2189 WixActionRow relativeActionRow = (WixActionRow)allNextActionRows[i];
2190
2191 if (unusedSequence == nextUsedSequence)
2192 {
2193 this.OnMessage(WixErrors.NoUniqueActionSequenceNumber(relativeActionRow.SourceLineNumbers, relativeActionRow.SequenceTable.ToString(), relativeActionRow.Action, absoluteActionRow.Action));
2194 if (null != absoluteActionRow.SourceLineNumbers)
2195 {
2196 this.OnMessage(WixErrors.NoUniqueActionSequenceNumber2(absoluteActionRow.SourceLineNumbers));
2197 }
2198
2199 unusedSequence--;
2200 }
2201
2202 relativeActionRow.Sequence = unusedSequence;
2203 relativeActionRows.Add(relativeActionRow);
2204
2205 unusedSequence++;
2206 }
2207
2208 // keep track of this sequence number as the previous used sequence number for the next iteration
2209 previousUsedSequence = absoluteActionRow.Sequence;
2210 }
2211
2212 // add the absolutely and relatively scheduled actions to the list of scheduled actions
2213 scheduledActionRows.AddRange(absoluteActionRows);
2214 scheduledActionRows.AddRange(relativeActionRows);
2215 }
2216 }
2217
2218 // create the action rows for sequences that are not suppressed
2219 foreach (WixActionRow actionRow in scheduledActionRows)
2220 {
2221 // get the table definition for the action (and ensure the proper table exists for a module)
2222 TableDefinition sequenceTableDefinition = null;
2223 switch (actionRow.SequenceTable)
2224 {
2225 case SequenceTable.AdminExecuteSequence:
2226 if (OutputType.Module == this.activeOutput.Type)
2227 {
2228 this.activeOutput.EnsureTable(this.tableDefinitions["AdminExecuteSequence"]);
2229 sequenceTableDefinition = this.tableDefinitions["ModuleAdminExecuteSequence"];
2230 }
2231 else
2232 {
2233 sequenceTableDefinition = this.tableDefinitions["AdminExecuteSequence"];
2234 }
2235 break;
2236 case SequenceTable.AdminUISequence:
2237 if (OutputType.Module == this.activeOutput.Type)
2238 {
2239 this.activeOutput.EnsureTable(this.tableDefinitions["AdminUISequence"]);
2240 sequenceTableDefinition = this.tableDefinitions["ModuleAdminUISequence"];
2241 }
2242 else
2243 {
2244 sequenceTableDefinition = this.tableDefinitions["AdminUISequence"];
2245 }
2246 break;
2247 case SequenceTable.AdvtExecuteSequence:
2248 if (OutputType.Module == this.activeOutput.Type)
2249 {
2250 this.activeOutput.EnsureTable(this.tableDefinitions["AdvtExecuteSequence"]);
2251 sequenceTableDefinition = this.tableDefinitions["ModuleAdvtExecuteSequence"];
2252 }
2253 else
2254 {
2255 sequenceTableDefinition = this.tableDefinitions["AdvtExecuteSequence"];
2256 }
2257 break;
2258 case SequenceTable.InstallExecuteSequence:
2259 if (OutputType.Module == this.activeOutput.Type)
2260 {
2261 this.activeOutput.EnsureTable(this.tableDefinitions["InstallExecuteSequence"]);
2262 sequenceTableDefinition = this.tableDefinitions["ModuleInstallExecuteSequence"];
2263 }
2264 else
2265 {
2266 sequenceTableDefinition = this.tableDefinitions["InstallExecuteSequence"];
2267 }
2268 break;
2269 case SequenceTable.InstallUISequence:
2270 if (OutputType.Module == this.activeOutput.Type)
2271 {
2272 this.activeOutput.EnsureTable(this.tableDefinitions["InstallUISequence"]);
2273 sequenceTableDefinition = this.tableDefinitions["ModuleInstallUISequence"];
2274 }
2275 else
2276 {
2277 sequenceTableDefinition = this.tableDefinitions["InstallUISequence"];
2278 }
2279 break;
2280 }
2281
2282 // create the action sequence row in the output
2283 Table sequenceTable = this.activeOutput.EnsureTable(sequenceTableDefinition);
2284 Row row = sequenceTable.CreateRow(actionRow.SourceLineNumbers);
2285 if (this.sectionIdOnRows)
2286 {
2287 row.SectionId = actionRow.SectionId;
2288 }
2289
2290 if (OutputType.Module == this.activeOutput.Type)
2291 {
2292 row[0] = actionRow.Action;
2293 if (0 != actionRow.Sequence)
2294 {
2295 row[1] = actionRow.Sequence;
2296 }
2297 else
2298 {
2299 bool after = (null == actionRow.Before);
2300 row[2] = after ? actionRow.After : actionRow.Before;
2301 row[3] = after ? 1 : 0;
2302 }
2303 row[4] = actionRow.Condition;
2304 }
2305 else
2306 {
2307 row[0] = actionRow.Action;
2308 row[1] = actionRow.Condition;
2309 row[2] = actionRow.Sequence;
2310 }
2311 }
2312 }
2313
2314 /// <summary>
2315 /// Sequence an action before or after a standard action.
2316 /// </summary>
2317 /// <param name="actionRow">The action row to be sequenced.</param>
2318 /// <param name="requiredActionRows">Collection of actions which must be included.</param>
2319 [SuppressMessage("Microsoft.Globalization", "CA1303:DoNotPassLiteralsAsLocalizedParameters", MessageId = "System.InvalidOperationException.#ctor(System.String)")]
2320 private void SequenceActionRow(WixActionRow actionRow, WixActionRowCollection requiredActionRows)
2321 {
2322 bool after = false;
2323 if (actionRow.After != null)
2324 {
2325 after = true;
2326 }
2327 else if (actionRow.Before == null)
2328 {
2329 throw new InvalidOperationException(WixStrings.EXP_FoundActionRowWithNoSequenceBeforeOrAfterColumnSet);
2330 }
2331
2332 string parentActionName = (after ? actionRow.After : actionRow.Before);
2333 WixActionRow parentActionRow = requiredActionRows[actionRow.SequenceTable, parentActionName];
2334
2335 if (null == parentActionRow)
2336 {
2337 parentActionRow = this.standardActions[actionRow.SequenceTable, parentActionName];
2338
2339 // if the missing parent action is a standard action (with a suggested sequence number), add it
2340 if (null != parentActionRow)
2341 {
2342 // Create a clone to avoid modifying the static copy of the object.
2343 parentActionRow = parentActionRow.Clone();
2344 requiredActionRows.Add(parentActionRow);
2345 }
2346 else
2347 {
2348 throw new InvalidOperationException(String.Format(CultureInfo.CurrentUICulture, WixStrings.EXP_FoundActionRowWinNonExistentAction, (after ? "After" : "Before"), parentActionName));
2349 }
2350 }
2351 else if (actionRow == parentActionRow || actionRow.ContainsChildActionRow(parentActionRow)) // cycle detected
2352 {
2353 throw new WixException(WixErrors.ActionCircularDependency(actionRow.SourceLineNumbers, actionRow.SequenceTable.ToString(), actionRow.Action, parentActionRow.Action));
2354 }
2355
2356 // Add this action to the appropriate list of dependent action rows.
2357 WixActionRowCollection relatedRows = (after ? parentActionRow.NextActionRows : parentActionRow.PreviousActionRows);
2358 relatedRows.Add(actionRow);
2359 }
2360#endif
2361 1695
2362 /// <summary> 1696 /// <summary>
2363 /// Resolve features for columns that have null guid placeholders. 1697 /// Resolve features for columns that have null guid placeholders.
diff --git a/src/WixToolset.Core/WindowsInstallerStandard.cs b/src/WixToolset.Core/WindowsInstallerStandard.cs
deleted file mode 100644
index 90a53e6a..00000000
--- a/src/WixToolset.Core/WindowsInstallerStandard.cs
+++ /dev/null
@@ -1,260 +0,0 @@
1// Copyright (c) .NET Foundation and contributors. All rights reserved. Licensed under the Microsoft Reciprocal License. See LICENSE.TXT file in the project root for full license information.
2
3namespace WixToolset.Core
4{
5 using System.Collections.Generic;
6 using WixToolset.Data;
7 using WixToolset.Data.Tuples;
8
9 internal class WindowsInstallerStandard
10 {
11 private static readonly HashSet<string> standardActionNames = new HashSet<string>
12 {
13 "AllocateRegistrySpace",
14 "AppSearch",
15 "BindImage",
16 "CCPSearch",
17 "CostFinalize",
18 "CostInitialize",
19 "CreateFolders",
20 "CreateShortcuts",
21 "DeleteServices",
22 "DisableRollback",
23 "DuplicateFiles",
24 "ExecuteAction",
25 "FileCost",
26 "FindRelatedProducts",
27 "ForceReboot",
28 "InstallAdminPackage",
29 "InstallExecute",
30 "InstallExecuteAgain",
31 "InstallFiles",
32 "InstallFinalize",
33 "InstallInitialize",
34 "InstallODBC",
35 "InstallServices",
36 "InstallSFPCatalogFile",
37 "InstallValidate",
38 "IsolateComponents",
39 "LaunchConditions",
40 "MigrateFeatureStates",
41 "MoveFiles",
42 "MsiConfigureServices",
43 "MsiPublishAssemblies",
44 "MsiUnpublishAssemblies",
45 "PatchFiles",
46 "ProcessComponents",
47 "PublishComponents",
48 "PublishFeatures",
49 "PublishProduct",
50 "RegisterClassInfo",
51 "RegisterComPlus",
52 "RegisterExtensionInfo",
53 "RegisterFonts",
54 "RegisterMIMEInfo",
55 "RegisterProduct",
56 "RegisterProgIdInfo",
57 "RegisterTypeLibraries",
58 "RegisterUser",
59 "RemoveDuplicateFiles",
60 "RemoveEnvironmentStrings",
61 "RemoveExistingProducts",
62 "RemoveFiles",
63 "RemoveFolders",
64 "RemoveIniValues",
65 "RemoveODBC",
66 "RemoveRegistryValues",
67 "RemoveShortcuts",
68 "ResolveSource",
69 "RMCCPSearch",
70 "ScheduleReboot",
71 "SelfRegModules",
72 "SelfUnregModules",
73 "SetODBCFolders",
74 "StartServices",
75 "StopServices",
76 "UnpublishComponents",
77 "UnpublishFeatures",
78 "UnregisterClassInfo",
79 "UnregisterComPlus",
80 "UnregisterExtensionInfo",
81 "UnregisterFonts",
82 "UnregisterMIMEInfo",
83 "UnregisterProgIdInfo",
84 "UnregisterTypeLibraries",
85 "ValidateProductID",
86 "WriteEnvironmentStrings",
87 "WriteIniValues",
88 "WriteRegistryValues",
89 };
90
91 private static readonly WixActionTuple[] standardActions = new[]
92 {
93 new WixActionTuple(null, new Identifier("AdminExecuteSequence/InstallInitialize", AccessModifier.Public)) { Action="InstallInitialize", Sequence=1500, SequenceTable=SequenceTable.AdminExecuteSequence },
94 new WixActionTuple(null, new Identifier("AdvtExecuteSequence/InstallInitialize", AccessModifier.Public)) { Action="InstallInitialize", Sequence=1500, SequenceTable=SequenceTable.AdvtExecuteSequence },
95 new WixActionTuple(null, new Identifier("InstallExecuteSequence/InstallInitialize", AccessModifier.Public)) { Action="InstallInitialize", Sequence=1500, SequenceTable=SequenceTable.InstallExecuteSequence },
96
97 new WixActionTuple(null, new Identifier("InstallExecuteSequence/InstallExecute", AccessModifier.Public)) { Action="InstallExecute", Sequence=6500, SequenceTable=SequenceTable.InstallExecuteSequence, Condition="NOT Installed" },
98
99 new WixActionTuple(null, new Identifier("InstallExecuteSequence/InstallExecuteAgain", AccessModifier.Public)) { Action="InstallExecuteAgain", Sequence=6550, SequenceTable=SequenceTable.InstallExecuteSequence, Condition="NOT Installed" },
100
101 new WixActionTuple(null, new Identifier("AdminExecuteSequence/InstallFinalize", AccessModifier.Public)) { Action="InstallFinalize", Sequence=6600, SequenceTable=SequenceTable.AdminExecuteSequence },
102 new WixActionTuple(null, new Identifier("AdvtExecuteSequence/InstallFinalize", AccessModifier.Public)) { Action="InstallFinalize", Sequence=6600, SequenceTable=SequenceTable.AdvtExecuteSequence },
103 new WixActionTuple(null, new Identifier("InstallExecuteSequence/InstallFinalize", AccessModifier.Public)) { Action="InstallFinalize", Sequence=6600, SequenceTable=SequenceTable.InstallExecuteSequence },
104
105 new WixActionTuple(null, new Identifier("AdminExecuteSequence/InstallFiles", AccessModifier.Public)) { Action="InstallFiles", Sequence=4000, SequenceTable=SequenceTable.AdminExecuteSequence },
106 new WixActionTuple(null, new Identifier("InstallExecuteSequence/InstallFiles", AccessModifier.Public)) { Action="InstallFiles", Sequence=4000, SequenceTable=SequenceTable.InstallExecuteSequence },
107
108 new WixActionTuple(null, new Identifier("AdminExecuteSequence/InstallAdminPackage", AccessModifier.Public)) { Action="InstallAdminPackage", Sequence=3900, SequenceTable=SequenceTable.AdminExecuteSequence },
109
110 new WixActionTuple(null, new Identifier("AdminExecuteSequence/FileCost", AccessModifier.Public)) { Action="FileCost", Sequence=900, SequenceTable=SequenceTable.AdminExecuteSequence },
111 new WixActionTuple(null, new Identifier("InstallExecuteSequence/FileCost", AccessModifier.Public)) { Action="FileCost", Sequence=900, SequenceTable=SequenceTable.InstallExecuteSequence },
112 new WixActionTuple(null, new Identifier("InstallUISequence/FileCost", AccessModifier.Public)) { Action="FileCost", Sequence=900, SequenceTable=SequenceTable.InstallUISequence },
113
114 new WixActionTuple(null, new Identifier("AdminExecuteSequence/CostInitialize", AccessModifier.Public)) { Action="CostInitialize", Sequence=800, SequenceTable=SequenceTable.AdminExecuteSequence },
115 new WixActionTuple(null, new Identifier("AdminUISequence/CostInitialize", AccessModifier.Public)) { Action="CostInitialize", Sequence=800, SequenceTable=SequenceTable.AdminUISequence },
116 new WixActionTuple(null, new Identifier("AdvtExecuteSequence/CostInitialize", AccessModifier.Public)) { Action="CostInitialize", Sequence=800, SequenceTable=SequenceTable.AdvtExecuteSequence },
117 new WixActionTuple(null, new Identifier("InstallExecuteSequence/CostInitialize", AccessModifier.Public)) { Action="CostInitialize", Sequence=800, SequenceTable=SequenceTable.InstallExecuteSequence },
118 new WixActionTuple(null, new Identifier("InstallUISequence/CostInitialize", AccessModifier.Public)) { Action="CostInitialize", Sequence=800, SequenceTable=SequenceTable.InstallUISequence },
119
120 new WixActionTuple(null, new Identifier("AdminExecuteSequence/CostFinalize", AccessModifier.Public)) { Action="CostFinalize", Sequence=1000, SequenceTable=SequenceTable.AdminExecuteSequence },
121 new WixActionTuple(null, new Identifier("AdminUISequence/CostFinalize", AccessModifier.Public)) { Action="CostFinalize", Sequence=1000, SequenceTable=SequenceTable.AdminUISequence },
122 new WixActionTuple(null, new Identifier("AdvtExecuteSequence/CostFinalize", AccessModifier.Public)) { Action="CostFinalize", Sequence=1000, SequenceTable=SequenceTable.AdvtExecuteSequence },
123 new WixActionTuple(null, new Identifier("InstallExecuteSequence/CostFinalize", AccessModifier.Public)) { Action="CostFinalize", Sequence=1000, SequenceTable=SequenceTable.InstallExecuteSequence },
124 new WixActionTuple(null, new Identifier("InstallUISequence/CostFinalize", AccessModifier.Public)) { Action="CostFinalize", Sequence=1000, SequenceTable=SequenceTable.InstallUISequence },
125
126 new WixActionTuple(null, new Identifier("AdminExecuteSequence/InstallValidate", AccessModifier.Public)) { Action="InstallValidate", Sequence=1400, SequenceTable=SequenceTable.AdminExecuteSequence },
127 new WixActionTuple(null, new Identifier("AdvtExecuteSequence/InstallValidate", AccessModifier.Public)) { Action="InstallValidate", Sequence=1400, SequenceTable=SequenceTable.AdvtExecuteSequence },
128 new WixActionTuple(null, new Identifier("InstallExecuteSequence/InstallValidate", AccessModifier.Public)) { Action="InstallValidate", Sequence=1400, SequenceTable=SequenceTable.InstallExecuteSequence },
129
130 new WixActionTuple(null, new Identifier("AdminUISequence/ExecuteAction", AccessModifier.Public)) { Action="ExecuteAction", Sequence=1300, SequenceTable=SequenceTable.AdminUISequence },
131 new WixActionTuple(null, new Identifier("InstallUISequence/ExecuteAction", AccessModifier.Public)) { Action="ExecuteAction", Sequence=1300, SequenceTable=SequenceTable.InstallUISequence },
132
133 new WixActionTuple(null, new Identifier("AdvtExecuteSequence/CreateShortcuts", AccessModifier.Public)) { Action="CreateShortcuts", Sequence=4500, SequenceTable=SequenceTable.AdvtExecuteSequence },
134 new WixActionTuple(null, new Identifier("InstallExecuteSequence/CreateShortcuts", AccessModifier.Public)) { Action="CreateShortcuts", Sequence=4500, SequenceTable=SequenceTable.InstallExecuteSequence },
135
136 new WixActionTuple(null, new Identifier("AdvtExecuteSequence/MsiPublishAssemblies", AccessModifier.Public)) { Action="MsiPublishAssemblies", Sequence=6250, SequenceTable=SequenceTable.AdvtExecuteSequence },
137 new WixActionTuple(null, new Identifier("InstallExecuteSequence/MsiPublishAssemblies", AccessModifier.Public)) { Action="MsiPublishAssemblies", Sequence=6250, SequenceTable=SequenceTable.InstallExecuteSequence },
138
139 //<action name="PublishComponents" sequence="6200" AdvtExecuteSequence="yes" InstallExecuteSequence="yes" />
140 //<action name="PublishFeatures" sequence="6300" AdvtExecuteSequence="yes" InstallExecuteSequence="yes" />
141 //<action name="PublishProduct" sequence="6400" AdvtExecuteSequence="yes" InstallExecuteSequence="yes" />
142 //<action name="RegisterClassInfo" sequence="4600" AdvtExecuteSequence="yes" InstallExecuteSequence="yes" />
143 //<action name="RegisterExtensionInfo" sequence="4700" AdvtExecuteSequence="yes" InstallExecuteSequence="yes" />
144 //<action name="RegisterMIMEInfo" sequence="4900" AdvtExecuteSequence="yes" InstallExecuteSequence="yes" />
145 //<action name="RegisterProgIdInfo" sequence="4800" AdvtExecuteSequence="yes" InstallExecuteSequence="yes" />
146 //<action name="AllocateRegistrySpace" condition="NOT Installed" sequence="1550" InstallExecuteSequence="yes" />
147 //<action name="AppSearch" sequence="50" InstallExecuteSequence="yes" InstallUISequence="yes" />
148 //<action name="BindImage" sequence="4300" InstallExecuteSequence="yes" />
149 //<action name="CreateFolders" sequence="3700" InstallExecuteSequence="yes" />
150 //<action name="DuplicateFiles" sequence="4210" InstallExecuteSequence="yes" />
151 //<action name="FindRelatedProducts" sequence="25" InstallExecuteSequence="yes" InstallUISequence="yes" />
152 //<action name="InstallODBC" sequence="5400" InstallExecuteSequence="yes" />
153 //<action name="InstallServices" condition="VersionNT" sequence="5800" InstallExecuteSequence="yes" />
154 //<action name="MsiConfigureServices" condition="VersionNT>=600" sequence="5850" InstallExecuteSequence="yes" />
155 //<action name="IsolateComponents" sequence="950" InstallExecuteSequence="yes" InstallUISequence="yes" />
156 //<action name="LaunchConditions" sequence="100" AdminExecuteSequence="yes" AdminUISequence="yes" InstallExecuteSequence="yes" InstallUISequence="yes" />
157 //<action name="MigrateFeatureStates" sequence="1200" InstallExecuteSequence="yes" InstallUISequence="yes" />
158 //<action name="MoveFiles" sequence="3800" InstallExecuteSequence="yes" />
159 //<action name="PatchFiles" sequence="4090" AdminExecuteSequence="yes" InstallExecuteSequence="yes" />
160 //<action name="ProcessComponents" sequence="1600" InstallExecuteSequence="yes" />
161 //<action name="RegisterComPlus" sequence="5700" InstallExecuteSequence="yes" />
162 //<action name="RegisterFonts" sequence="5300" InstallExecuteSequence="yes" />
163 //<action name="RegisterProduct" sequence="6100" InstallExecuteSequence="yes" />
164 //<action name="RegisterTypeLibraries" sequence="5500" InstallExecuteSequence="yes" />
165 //<action name="RegisterUser" sequence="6000" InstallExecuteSequence="yes" />
166 //<action name="RemoveDuplicateFiles" sequence="3400" InstallExecuteSequence="yes" />
167 //<action name="RemoveEnvironmentStrings" sequence="3300" InstallExecuteSequence="yes" />
168 //<action name="RemoveFiles" sequence="3500" InstallExecuteSequence="yes" />
169 //<action name="RemoveFolders" sequence="3600" InstallExecuteSequence="yes" />
170 //<action name="RemoveIniValues" sequence="3100" InstallExecuteSequence="yes" />
171 //<action name="RemoveODBC" sequence="2400" InstallExecuteSequence="yes" />
172 //<action name="RemoveRegistryValues" sequence="2600" InstallExecuteSequence="yes" />
173 //<action name="RemoveShortcuts" sequence="3200" InstallExecuteSequence="yes" />
174 //<action name="SelfRegModules" sequence="5600" InstallExecuteSequence="yes" />
175 //<action name="SelfUnregModules" sequence="2200" InstallExecuteSequence="yes" />
176 //<action name="SetODBCFolders" sequence="1100" InstallExecuteSequence="yes" />
177
178
179 new WixActionTuple(null, new Identifier("InstallExecuteSequence/CCPSearch", AccessModifier.Public)) { Action="CCPSearch", Sequence=500, SequenceTable=SequenceTable.InstallExecuteSequence, Condition="NOT Installed" },
180 new WixActionTuple(null, new Identifier("InstallUISequence/CCPSearch", AccessModifier.Public)) { Action="CCPSearch", Sequence=500, SequenceTable=SequenceTable.InstallUISequence, Condition="NOT Installed" },
181
182 new WixActionTuple(null, new Identifier("InstallExecuteSequence/DeleteServices", AccessModifier.Public)) { Action="DeleteServices", Sequence=2000, SequenceTable=SequenceTable.InstallExecuteSequence, Condition="VersionNT" },
183
184 new WixActionTuple(null, new Identifier("InstallExecuteSequence/RMCCPSearch", AccessModifier.Public)) { Action="RMCCPSearch", Sequence=600, SequenceTable=SequenceTable.InstallExecuteSequence, Condition="NOT Installed" },
185 new WixActionTuple(null, new Identifier("InstallUISequence/RMCCPSearch", AccessModifier.Public)) { Action="RMCCPSearch", Sequence=600, SequenceTable=SequenceTable.InstallUISequence, Condition="NOT Installed" },
186
187 new WixActionTuple(null, new Identifier("InstallExecuteSequence/StartServices", AccessModifier.Public)) { Action="StartServices", Sequence=5900, SequenceTable=SequenceTable.InstallExecuteSequence, Condition="VersionNT" },
188 new WixActionTuple(null, new Identifier("InstallExecuteSequence/StopServices", AccessModifier.Public)) { Action="StopServices", Sequence=1900, SequenceTable=SequenceTable.InstallExecuteSequence, Condition="VersionNT" },
189
190 new WixActionTuple(null, new Identifier("InstallExecuteSequence/MsiUnpublishAssemblies", AccessModifier.Public)) { Action="MsiUnpublishAssemblies", Sequence=1750, SequenceTable=SequenceTable.InstallExecuteSequence },
191 new WixActionTuple(null, new Identifier("InstallExecuteSequence/UnpublishComponents", AccessModifier.Public)) { Action="UnpublishComponents", Sequence=1700, SequenceTable=SequenceTable.InstallExecuteSequence },
192 new WixActionTuple(null, new Identifier("InstallExecuteSequence/UnpublishFeatures", AccessModifier.Public)) { Action="UnpublishFeatures", Sequence=1800, SequenceTable=SequenceTable.InstallExecuteSequence },
193 new WixActionTuple(null, new Identifier("InstallExecuteSequence/UnregisterClassInfo", AccessModifier.Public)) { Action="UnregisterClassInfo", Sequence=2700, SequenceTable=SequenceTable.InstallExecuteSequence },
194 new WixActionTuple(null, new Identifier("InstallExecuteSequence/UnregisterComPlus", AccessModifier.Public)) { Action="UnregisterComPlus", Sequence=2100, SequenceTable=SequenceTable.InstallExecuteSequence },
195 new WixActionTuple(null, new Identifier("InstallExecuteSequence/UnregisterExtensionInfo", AccessModifier.Public)) { Action="UnregisterExtensionInfo", Sequence=2800, SequenceTable=SequenceTable.InstallExecuteSequence },
196 new WixActionTuple(null, new Identifier("InstallExecuteSequence/UnregisterFonts", AccessModifier.Public)) { Action="UnregisterFonts", Sequence=2500, SequenceTable=SequenceTable.InstallExecuteSequence },
197 new WixActionTuple(null, new Identifier("InstallExecuteSequence/UnregisterMIMEInfo", AccessModifier.Public)) { Action="UnregisterMIMEInfo", Sequence=3000, SequenceTable=SequenceTable.InstallExecuteSequence },
198 new WixActionTuple(null, new Identifier("InstallExecuteSequence/UnregisterProgIdInfo", AccessModifier.Public)) { Action="UnregisterProgIdInfo", Sequence=2900, SequenceTable=SequenceTable.InstallExecuteSequence },
199 new WixActionTuple(null, new Identifier("InstallExecuteSequence/UnregisterTypeLibraries", AccessModifier.Public)) { Action="UnregisterTypeLibraries", Sequence=2300, SequenceTable=SequenceTable.InstallExecuteSequence },
200 new WixActionTuple(null, new Identifier("InstallExecuteSequence/ValidateProductID", AccessModifier.Public)) { Action="ValidateProductID", Sequence=700, SequenceTable=SequenceTable.InstallExecuteSequence },
201 new WixActionTuple(null, new Identifier("InstallExecuteSequence/WriteEnvironmentStrings", AccessModifier.Public)) { Action="WriteEnvironmentStrings", Sequence=5200, SequenceTable=SequenceTable.InstallExecuteSequence },
202 new WixActionTuple(null, new Identifier("InstallExecuteSequence/WriteIniValues", AccessModifier.Public)) { Action="WriteIniValues", Sequence=5100, SequenceTable=SequenceTable.InstallExecuteSequence },
203 new WixActionTuple(null, new Identifier("InstallExecuteSequence/WriteRegistryValues", AccessModifier.Public)) { Action="WriteRegistryValues", Sequence=5000, SequenceTable=SequenceTable.InstallExecuteSequence },
204 };
205
206 private static readonly HashSet<string> standardDirectories = new HashSet<string>
207 {
208 "TARGETDIR",
209 "AdminToolsFolder",
210 "AppDataFolder",
211 "CommonAppDataFolder",
212 "CommonFilesFolder",
213 "DesktopFolder",
214 "FavoritesFolder",
215 "FontsFolder",
216 "LocalAppDataFolder",
217 "MyPicturesFolder",
218 "PersonalFolder",
219 "ProgramFilesFolder",
220 "ProgramMenuFolder",
221 "SendToFolder",
222 "StartMenuFolder",
223 "StartupFolder",
224 "System16Folder",
225 "SystemFolder",
226 "TempFolder",
227 "TemplateFolder",
228 "WindowsFolder",
229 "CommonFiles64Folder",
230 "ProgramFiles64Folder",
231 "System64Folder",
232 "NetHoodFolder",
233 "PrintHoodFolder",
234 "RecentFolder",
235 "WindowsVolume",
236 };
237
238 /// <summary>
239 /// Find out if an action is a standard action.
240 /// </summary>
241 /// <param name="actionName">Name of the action.</param>
242 /// <returns>true if the action is standard, false otherwise.</returns>
243 public static bool IsStandardAction(string actionName)
244 {
245 return standardActionNames.Contains(actionName);
246 }
247
248 public static WixActionTuple[] StandardActions() => standardActions;
249
250 /// <summary>
251 /// Find out if a directory is a standard directory.
252 /// </summary>
253 /// <param name="directoryName">Name of the directory.</param>
254 /// <returns>true if the directory is standard, false otherwise.</returns>
255 public static bool IsStandardDirectory(string directoryName)
256 {
257 return standardDirectories.Contains(directoryName);
258 }
259 }
260}
diff --git a/src/WixToolset.Core/WixStrings.Designer.cs b/src/WixToolset.Core/WixStrings.Designer.cs
index 75e2b908..36e64267 100644
--- a/src/WixToolset.Core/WixStrings.Designer.cs
+++ b/src/WixToolset.Core/WixStrings.Designer.cs
@@ -163,7 +163,7 @@ namespace WixToolset {
163 /// <summary> 163 /// <summary>
164 /// Looks up a localized string similar to Found an ActionRow with a non-existent {0} action: {1}.. 164 /// Looks up a localized string similar to Found an ActionRow with a non-existent {0} action: {1}..
165 /// </summary> 165 /// </summary>
166 internal static string EXP_FoundActionRowWinNonExistentAction { 166 public static string EXP_FoundActionRowWinNonExistentAction {
167 get { 167 get {
168 return ResourceManager.GetString("EXP_FoundActionRowWinNonExistentAction", resourceCulture); 168 return ResourceManager.GetString("EXP_FoundActionRowWinNonExistentAction", resourceCulture);
169 } 169 }
@@ -172,7 +172,7 @@ namespace WixToolset {
172 /// <summary> 172 /// <summary>
173 /// Looks up a localized string similar to Found an ActionRow with no Sequence, Before, or After column set.. 173 /// Looks up a localized string similar to Found an ActionRow with no Sequence, Before, or After column set..
174 /// </summary> 174 /// </summary>
175 internal static string EXP_FoundActionRowWithNoSequenceBeforeOrAfterColumnSet { 175 public static string EXP_FoundActionRowWithNoSequenceBeforeOrAfterColumnSet {
176 get { 176 get {
177 return ResourceManager.GetString("EXP_FoundActionRowWithNoSequenceBeforeOrAfterColumnSet", resourceCulture); 177 return ResourceManager.GetString("EXP_FoundActionRowWithNoSequenceBeforeOrAfterColumnSet", resourceCulture);
178 } 178 }
diff --git a/src/WixToolset.Data.WindowsInstaller/Data/tables.xml b/src/WixToolset.Data.WindowsInstaller/Data/tables.xml
index 280d87a8..e4b5e954 100644
--- a/src/WixToolset.Data.WindowsInstaller/Data/tables.xml
+++ b/src/WixToolset.Data.WindowsInstaller/Data/tables.xml
@@ -1468,7 +1468,7 @@
1468 <columnDefinition name="PatchGroup" type="number" length="4" /> 1468 <columnDefinition name="PatchGroup" type="number" length="4" />
1469 <columnDefinition name="Attributes" type="number" length="4"/> 1469 <columnDefinition name="Attributes" type="number" length="4"/>
1470 <columnDefinition name="PatchAttributes" type="number" length="4" nullable="yes"/> 1470 <columnDefinition name="PatchAttributes" type="number" length="4" nullable="yes"/>
1471 <columnDefinition name="DeltaPatchHeaderSource" type="string" length="0"/> 1471 <columnDefinition name="DeltaPatchHeaderSource" type="string" length="0" nullable="yes"/>
1472 </tableDefinition> 1472 </tableDefinition>
1473 <tableDefinition name="WixBindUpdatedFiles" unreal="yes"> 1473 <tableDefinition name="WixBindUpdatedFiles" unreal="yes">
1474 <columnDefinition name="File_" type="string" length="0" primaryKey="yes" modularize="column" keyTable="File" keyColumn="1" category="identifier" /> 1474 <columnDefinition name="File_" type="string" length="0" primaryKey="yes" modularize="column" keyTable="File" keyColumn="1" category="identifier" />
diff --git a/src/WixToolset.Data.WindowsInstaller/RowDictionary.cs b/src/WixToolset.Data.WindowsInstaller/RowDictionary.cs
index a0cc5302..5756db71 100644
--- a/src/WixToolset.Data.WindowsInstaller/RowDictionary.cs
+++ b/src/WixToolset.Data.WindowsInstaller/RowDictionary.cs
@@ -77,8 +77,7 @@ namespace WixToolset.Data
77 /// <returns>Row or null if key is not found.</returns> 77 /// <returns>Row or null if key is not found.</returns>
78 public T Get(string key) 78 public T Get(string key)
79 { 79 {
80 T result; 80 return this.TryGetValue(key, out var result) ? result : null;
81 return this.TryGetValue(key, out result) ? result : null;
82 } 81 }
83 } 82 }
84} 83}
diff --git a/src/WixToolset.Data.WindowsInstaller/Rows/SymbolPathType.cs b/src/WixToolset.Data.WindowsInstaller/Rows/SymbolPathType.cs
deleted file mode 100644
index 964e1caa..00000000
--- a/src/WixToolset.Data.WindowsInstaller/Rows/SymbolPathType.cs
+++ /dev/null
@@ -1,17 +0,0 @@
1// Copyright (c) .NET Foundation and contributors. All rights reserved. Licensed under the Microsoft Reciprocal License. See LICENSE.TXT file in the project root for full license information.
2
3namespace WixToolset.Data.Rows
4{
5 /// <summary>
6 /// The types that the WixDeltaPatchSymbolPaths table can hold.
7 /// </summary>
8 /// <remarks>The order of these values is important since WixDeltaPatchSymbolPaths are sorted by this type.</remarks>
9 public enum SymbolPathType
10 {
11 File,
12 Component,
13 Directory,
14 Media,
15 Product
16 };
17}
diff --git a/src/WixToolset.Data.WindowsInstaller/Rows/WixActionRow.cs b/src/WixToolset.Data.WindowsInstaller/Rows/WixActionRow.cs
index 3009e59d..d1be706e 100644
--- a/src/WixToolset.Data.WindowsInstaller/Rows/WixActionRow.cs
+++ b/src/WixToolset.Data.WindowsInstaller/Rows/WixActionRow.cs
@@ -8,27 +8,28 @@ namespace WixToolset.Data.Rows
8 using System.Globalization; 8 using System.Globalization;
9 using System.Xml; 9 using System.Xml;
10 using System.Xml.Schema; 10 using System.Xml.Schema;
11 using WixToolset.Data.Tuples;
11 12
12 /// <summary> 13 /// <summary>
13 /// The Sequence tables that actions may belong to. 14 /// The Sequence tables that actions may belong to.
14 /// </summary> 15 /// </summary>
15 public enum SequenceTable 16 //public enum SequenceTable
16 { 17 //{
17 /// <summary>AdminUISequence</summary> 18 // /// <summary>AdminUISequence</summary>
18 AdminUISequence, 19 // AdminUISequence,
19 20
20 /// <summary>AdminExecuteSequence</summary> 21 // /// <summary>AdminExecuteSequence</summary>
21 AdminExecuteSequence, 22 // AdminExecuteSequence,
22 23
23 /// <summary>AdvtExecuteSequence</summary> 24 // /// <summary>AdvtExecuteSequence</summary>
24 AdvtExecuteSequence, 25 // AdvtExecuteSequence,
25 26
26 /// <summary>InstallUISequence</summary> 27 // /// <summary>InstallUISequence</summary>
27 InstallUISequence, 28 // InstallUISequence,
28 29
29 /// <summary>InstallExecuteSequence</summary> 30 // /// <summary>InstallExecuteSequence</summary>
30 InstallExecuteSequence 31 // InstallExecuteSequence
31 } 32 //}
32 33
33 /// <summary> 34 /// <summary>
34 /// Specialization of a row for the sequence tables. 35 /// Specialization of a row for the sequence tables.
@@ -55,15 +56,15 @@ namespace WixToolset.Data.Rows
55 /// <param name="action">The name of the standard action.</param> 56 /// <param name="action">The name of the standard action.</param>
56 /// <param name="condition">The condition of the standard action.</param> 57 /// <param name="condition">The condition of the standard action.</param>
57 /// <param name="sequence">The suggested sequence number of the standard action.</param> 58 /// <param name="sequence">The suggested sequence number of the standard action.</param>
58 private WixActionRow(SequenceTable sequenceTable, string action, string condition, int sequence) : 59 //private WixActionRow(SequenceTable sequenceTable, string action, string condition, int sequence) :
59 base(null, WindowsInstallerStandard.GetTableDefinitions()["WixAction"]) 60 // base(null, WindowsInstallerStandard.GetTableDefinitions()["WixAction"])
60 { 61 //{
61 this.SequenceTable = sequenceTable; 62 // this.SequenceTable = sequenceTable;
62 this.Action = action; 63 // this.Action = action;
63 this.Condition = condition; 64 // this.Condition = condition;
64 this.Sequence = sequence; 65 // this.Sequence = sequence;
65 this.Overridable = true; // all standard actions are overridable by default 66 // this.Overridable = true; // all standard actions are overridable by default
66 } 67 //}
67 68
68 /// <summary> 69 /// <summary>
69 /// Instantiates an ActionRow by copying data from another ActionRow. 70 /// Instantiates an ActionRow by copying data from another ActionRow.
@@ -296,13 +297,15 @@ namespace WixToolset.Data.Rows
296 WixActionRow[] actionRows = new WixActionRow[sequenceCount]; 297 WixActionRow[] actionRows = new WixActionRow[sequenceCount];
297 for (int i = 0; i < sequenceCount; i++) 298 for (int i = 0; i < sequenceCount; i++)
298 { 299 {
299 WixActionRow actionRow = new WixActionRow(sequenceTables[i], id, condition, sequence); 300 //WixActionRow actionRow = new WixActionRow(sequenceTables[i], id, condition, sequence);
300 actionRows[i] = actionRow; 301 //actionRows[i] = actionRow;
302 throw new NotImplementedException();
301 } 303 }
302 304
303 return actionRows; 305 return actionRows;
304 } 306 }
305 307
308#if DEAD_CODE
306 /// <summary> 309 /// <summary>
307 /// Determines whether this ActionRow contains the specified ActionRow as a child in its dependency tree. 310 /// Determines whether this ActionRow contains the specified ActionRow as a child in its dependency tree.
308 /// </summary> 311 /// </summary>
@@ -370,5 +373,6 @@ namespace WixToolset.Data.Rows
370 } 373 }
371 } 374 }
372 } 375 }
376#endif
373 } 377 }
374} 378}
diff --git a/src/WixToolset.Data.WindowsInstaller/Rows/WixActionRowCollection.cs b/src/WixToolset.Data.WindowsInstaller/Rows/WixActionRowCollection.cs
index 513a104f..9fab6b5d 100644
--- a/src/WixToolset.Data.WindowsInstaller/Rows/WixActionRowCollection.cs
+++ b/src/WixToolset.Data.WindowsInstaller/Rows/WixActionRowCollection.cs
@@ -6,6 +6,7 @@ namespace WixToolset.Data.Rows
6 using System.Collections; 6 using System.Collections;
7 using System.Diagnostics; 7 using System.Diagnostics;
8 using System.Xml; 8 using System.Xml;
9 using WixToolset.Data.Tuples;
9 10
10 /// <summary> 11 /// <summary>
11 /// A collection of action rows sorted by their sequence table and action name. 12 /// A collection of action rows sorted by their sequence table and action name.
diff --git a/src/WixToolset.Data.WindowsInstaller/Rows/WixDeltaPatchSymbolPathsRow.cs b/src/WixToolset.Data.WindowsInstaller/Rows/WixDeltaPatchSymbolPathsRow.cs
index b6c0b840..3be5a56d 100644
--- a/src/WixToolset.Data.WindowsInstaller/Rows/WixDeltaPatchSymbolPathsRow.cs
+++ b/src/WixToolset.Data.WindowsInstaller/Rows/WixDeltaPatchSymbolPathsRow.cs
@@ -2,6 +2,8 @@
2 2
3namespace WixToolset.Data.Rows 3namespace WixToolset.Data.Rows
4{ 4{
5 using WixToolset.Data.Tuples;
6
5 /// <summary> 7 /// <summary>
6 /// Specialization of a row for the WixDeltaPatchSymbolPaths table. 8 /// Specialization of a row for the WixDeltaPatchSymbolPaths table.
7 /// </summary> 9 /// </summary>
diff --git a/src/WixToolset.Data.WindowsInstaller/TableDefinitionCollection.cs b/src/WixToolset.Data.WindowsInstaller/TableDefinitionCollection.cs
index 553f0eaa..26d56387 100644
--- a/src/WixToolset.Data.WindowsInstaller/TableDefinitionCollection.cs
+++ b/src/WixToolset.Data.WindowsInstaller/TableDefinitionCollection.cs
@@ -69,6 +69,17 @@ namespace WixToolset.Data
69 } 69 }
70 70
71 /// <summary> 71 /// <summary>
72 /// Tries to get a table definition by name.
73 /// </summary>
74 /// <param name="tableName">Name of table to locate.</param>
75 /// <param name="table">Table definition if found.</param>
76 /// <returns>True if table definition was found otherwise false.</returns>
77 public bool TryGet(string tableName, out TableDefinition table)
78 {
79 return this.collection.TryGetValue(tableName, out table);
80 }
81
82 /// <summary>
72 /// Load a table definition collection from an XmlReader. 83 /// Load a table definition collection from an XmlReader.
73 /// </summary> 84 /// </summary>
74 /// <param name="reader">Reader to get data from.</param> 85 /// <param name="reader">Reader to get data from.</param>
diff --git a/src/WixToolset.Data.WindowsInstaller/WindowsInstallerStandard.cs b/src/WixToolset.Data.WindowsInstaller/WindowsInstallerStandard.cs
deleted file mode 100644
index ee4a5103..00000000
--- a/src/WixToolset.Data.WindowsInstaller/WindowsInstallerStandard.cs
+++ /dev/null
@@ -1,442 +0,0 @@
1// Copyright (c) .NET Foundation and contributors. All rights reserved. Licensed under the Microsoft Reciprocal License. See LICENSE.TXT file in the project root for full license information.
2
3namespace WixToolset.Data
4{
5 using System.Collections.Generic;
6 using System.Reflection;
7 using System.Xml;
8 using WixToolset.Data.Rows;
9
10 /// <summary>
11 /// Represents the Windows Installer standard objects.
12 /// </summary>
13 public static class WindowsInstallerStandard
14 {
15 private static readonly object lockObject = new object();
16
17 private static TableDefinitionCollection tableDefinitions;
18 private static WixActionRowCollection standardActions;
19
20 private static HashSet<string> standardActionNames;
21 private static HashSet<string> standardDirectories;
22 private static HashSet<string> standardProperties;
23
24 /// <summary>
25 /// Gets the table definitions stored in this assembly.
26 /// </summary>
27 /// <returns>Table definition collection for tables stored in this assembly.</returns>
28 public static TableDefinitionCollection GetTableDefinitions()
29 {
30 lock (lockObject)
31 {
32 if (null == WindowsInstallerStandard.tableDefinitions)
33 {
34 using (XmlReader reader = XmlReader.Create(Assembly.GetExecutingAssembly().GetManifestResourceStream("WixToolset.Data.WindowsInstaller.Data.tables.xml")))
35 {
36 tableDefinitions = TableDefinitionCollection.Load(reader);
37 }
38 }
39 }
40
41 return WindowsInstallerStandard.tableDefinitions;
42 }
43
44 /// <summary>
45 /// Gets the standard actions stored in this assembly.
46 /// </summary>
47 /// <returns>Collection of standard actions in this assembly.</returns>
48 public static WixActionRowCollection GetStandardActions()
49 {
50 lock (lockObject)
51 {
52 if (null == standardActions)
53 {
54 using (XmlReader reader = XmlReader.Create(Assembly.GetExecutingAssembly().GetManifestResourceStream("WixToolset.Data.WindowsInstaller.Data.actions.xml")))
55 {
56 standardActions = WixActionRowCollection.Load(reader);
57 }
58 }
59 }
60
61 return standardActions;
62 }
63
64 /// <summary>
65 /// Gets (and loads if not yet loaded) the list of standard MSI directories.
66 /// </summary>
67 /// <value>The list of standard MSI directories.</value>
68 public static HashSet<string> GetStandardDirectories()
69 {
70 lock (lockObject)
71 {
72 if (null == standardDirectories)
73 {
74 LoadStandardDirectories();
75 }
76 }
77
78 return standardDirectories;
79 }
80
81 /// <summary>
82 /// Find out if an action is a standard action.
83 /// </summary>
84 /// <param name="actionName">Name of the action.</param>
85 /// <returns>true if the action is standard, false otherwise.</returns>
86 public static bool IsStandardAction(string actionName)
87 {
88 lock (lockObject)
89 {
90 if (null == standardActionNames)
91 {
92 standardActionNames = new HashSet<string>();
93 standardActionNames.Add("AllocateRegistrySpace");
94 standardActionNames.Add("AppSearch");
95 standardActionNames.Add("BindImage");
96 standardActionNames.Add("CCPSearch");
97 standardActionNames.Add("CostFinalize");
98 standardActionNames.Add("CostInitialize");
99 standardActionNames.Add("CreateFolders");
100 standardActionNames.Add("CreateShortcuts");
101 standardActionNames.Add("DeleteServices");
102 standardActionNames.Add("DisableRollback");
103 standardActionNames.Add("DuplicateFiles");
104 standardActionNames.Add("ExecuteAction");
105 standardActionNames.Add("FileCost");
106 standardActionNames.Add("FindRelatedProducts");
107 standardActionNames.Add("ForceReboot");
108 standardActionNames.Add("InstallAdminPackage");
109 standardActionNames.Add("InstallExecute");
110 standardActionNames.Add("InstallExecuteAgain");
111 standardActionNames.Add("InstallFiles");
112 standardActionNames.Add("InstallFinalize");
113 standardActionNames.Add("InstallInitialize");
114 standardActionNames.Add("InstallODBC");
115 standardActionNames.Add("InstallServices");
116 standardActionNames.Add("InstallSFPCatalogFile");
117 standardActionNames.Add("InstallValidate");
118 standardActionNames.Add("IsolateComponents");
119 standardActionNames.Add("LaunchConditions");
120 standardActionNames.Add("MigrateFeatureStates");
121 standardActionNames.Add("MoveFiles");
122 standardActionNames.Add("MsiConfigureServices");
123 standardActionNames.Add("MsiPublishAssemblies");
124 standardActionNames.Add("MsiUnpublishAssemblies");
125 standardActionNames.Add("PatchFiles");
126 standardActionNames.Add("ProcessComponents");
127 standardActionNames.Add("PublishComponents");
128 standardActionNames.Add("PublishFeatures");
129 standardActionNames.Add("PublishProduct");
130 standardActionNames.Add("RegisterClassInfo");
131 standardActionNames.Add("RegisterComPlus");
132 standardActionNames.Add("RegisterExtensionInfo");
133 standardActionNames.Add("RegisterFonts");
134 standardActionNames.Add("RegisterMIMEInfo");
135 standardActionNames.Add("RegisterProduct");
136 standardActionNames.Add("RegisterProgIdInfo");
137 standardActionNames.Add("RegisterTypeLibraries");
138 standardActionNames.Add("RegisterUser");
139 standardActionNames.Add("RemoveDuplicateFiles");
140 standardActionNames.Add("RemoveEnvironmentStrings");
141 standardActionNames.Add("RemoveExistingProducts");
142 standardActionNames.Add("RemoveFiles");
143 standardActionNames.Add("RemoveFolders");
144 standardActionNames.Add("RemoveIniValues");
145 standardActionNames.Add("RemoveODBC");
146 standardActionNames.Add("RemoveRegistryValues");
147 standardActionNames.Add("RemoveShortcuts");
148 standardActionNames.Add("ResolveSource");
149 standardActionNames.Add("RMCCPSearch");
150 standardActionNames.Add("ScheduleReboot");
151 standardActionNames.Add("SelfRegModules");
152 standardActionNames.Add("SelfUnregModules");
153 standardActionNames.Add("SetODBCFolders");
154 standardActionNames.Add("StartServices");
155 standardActionNames.Add("StopServices");
156 standardActionNames.Add("UnpublishComponents");
157 standardActionNames.Add("UnpublishFeatures");
158 standardActionNames.Add("UnregisterClassInfo");
159 standardActionNames.Add("UnregisterComPlus");
160 standardActionNames.Add("UnregisterExtensionInfo");
161 standardActionNames.Add("UnregisterFonts");
162 standardActionNames.Add("UnregisterMIMEInfo");
163 standardActionNames.Add("UnregisterProgIdInfo");
164 standardActionNames.Add("UnregisterTypeLibraries");
165 standardActionNames.Add("ValidateProductID");
166 standardActionNames.Add("WriteEnvironmentStrings");
167 standardActionNames.Add("WriteIniValues");
168 standardActionNames.Add("WriteRegistryValues");
169 }
170 }
171
172 return standardActionNames.Contains(actionName);
173 }
174
175 /// <summary>
176 /// Find out if a directory is a standard directory.
177 /// </summary>
178 /// <param name="directoryName">Name of the directory.</param>
179 /// <returns>true if the directory is standard, false otherwise.</returns>
180 public static bool IsStandardDirectory(string directoryName)
181 {
182 lock (lockObject)
183 {
184 if (null == standardDirectories)
185 {
186 LoadStandardDirectories();
187 }
188 }
189
190 return standardDirectories.Contains(directoryName);
191 }
192
193 /// <summary>
194 /// Find out if a property is a standard property.
195 /// References:
196 /// Title: Property Reference [Windows Installer]:
197 /// URL: http://msdn.microsoft.com/library/en-us/msi/setup/property_reference.asp
198 /// </summary>
199 /// <param name="propertyName">Name of the property.</param>
200 /// <returns>true if a property is standard, false otherwise.</returns>
201 public static bool IsStandardProperty(string propertyName)
202 {
203 lock (lockObject)
204 {
205 if (null == standardProperties)
206 {
207 standardProperties = new HashSet<string>();
208 standardProperties.Add("~"); // REG_MULTI_SZ/NULL marker
209 standardProperties.Add("ACTION");
210 standardProperties.Add("ADDDEFAULT");
211 standardProperties.Add("ADDLOCAL");
212 standardProperties.Add("ADDDSOURCE");
213 standardProperties.Add("AdminProperties");
214 standardProperties.Add("AdminUser");
215 standardProperties.Add("ADVERTISE");
216 standardProperties.Add("AFTERREBOOT");
217 standardProperties.Add("AllowProductCodeMismatches");
218 standardProperties.Add("AllowProductVersionMajorMismatches");
219 standardProperties.Add("ALLUSERS");
220 standardProperties.Add("Alpha");
221 standardProperties.Add("ApiPatchingSymbolFlags");
222 standardProperties.Add("ARPAUTHORIZEDCDFPREFIX");
223 standardProperties.Add("ARPCOMMENTS");
224 standardProperties.Add("ARPCONTACT");
225 standardProperties.Add("ARPHELPLINK");
226 standardProperties.Add("ARPHELPTELEPHONE");
227 standardProperties.Add("ARPINSTALLLOCATION");
228 standardProperties.Add("ARPNOMODIFY");
229 standardProperties.Add("ARPNOREMOVE");
230 standardProperties.Add("ARPNOREPAIR");
231 standardProperties.Add("ARPPRODUCTIONICON");
232 standardProperties.Add("ARPREADME");
233 standardProperties.Add("ARPSIZE");
234 standardProperties.Add("ARPSYSTEMCOMPONENT");
235 standardProperties.Add("ARPULRINFOABOUT");
236 standardProperties.Add("ARPURLUPDATEINFO");
237 standardProperties.Add("AVAILABLEFREEREG");
238 standardProperties.Add("BorderSize");
239 standardProperties.Add("BorderTop");
240 standardProperties.Add("CaptionHeight");
241 standardProperties.Add("CCP_DRIVE");
242 standardProperties.Add("ColorBits");
243 standardProperties.Add("COMPADDLOCAL");
244 standardProperties.Add("COMPADDSOURCE");
245 standardProperties.Add("COMPANYNAME");
246 standardProperties.Add("ComputerName");
247 standardProperties.Add("CostingComplete");
248 standardProperties.Add("Date");
249 standardProperties.Add("DefaultUIFont");
250 standardProperties.Add("DISABLEADVTSHORTCUTS");
251 standardProperties.Add("DISABLEMEDIA");
252 standardProperties.Add("DISABLEROLLBACK");
253 standardProperties.Add("DiskPrompt");
254 standardProperties.Add("DontRemoveTempFolderWhenFinished");
255 standardProperties.Add("EnableUserControl");
256 standardProperties.Add("EXECUTEACTION");
257 standardProperties.Add("EXECUTEMODE");
258 standardProperties.Add("FASTOEM");
259 standardProperties.Add("FILEADDDEFAULT");
260 standardProperties.Add("FILEADDLOCAL");
261 standardProperties.Add("FILEADDSOURCE");
262 standardProperties.Add("IncludeWholeFilesOnly");
263 standardProperties.Add("Installed");
264 standardProperties.Add("INSTALLLEVEL");
265 standardProperties.Add("Intel");
266 standardProperties.Add("Intel64");
267 standardProperties.Add("IsAdminPackage");
268 standardProperties.Add("LeftUnit");
269 standardProperties.Add("LIMITUI");
270 standardProperties.Add("ListOfPatchGUIDsToReplace");
271 standardProperties.Add("ListOfTargetProductCode");
272 standardProperties.Add("LOGACTION");
273 standardProperties.Add("LogonUser");
274 standardProperties.Add("Manufacturer");
275 standardProperties.Add("MEDIAPACKAGEPATH");
276 standardProperties.Add("MediaSourceDir");
277 standardProperties.Add("MinimumRequiredMsiVersion");
278 standardProperties.Add("MsiAMD64");
279 standardProperties.Add("MSIAPRSETTINGSIDENTIFIER");
280 standardProperties.Add("MSICHECKCRCS");
281 standardProperties.Add("MSIDISABLERMRESTART");
282 standardProperties.Add("MSIENFORCEUPGRADECOMPONENTRULES");
283 standardProperties.Add("MSIFASTINSTALL");
284 standardProperties.Add("MsiFileToUseToCreatePatchTables");
285 standardProperties.Add("MsiHiddenProperties");
286 standardProperties.Add("MSIINSTALLPERUSER");
287 standardProperties.Add("MSIINSTANCEGUID");
288 standardProperties.Add("MsiLogFileLocation");
289 standardProperties.Add("MsiLogging");
290 standardProperties.Add("MsiNetAssemblySupport");
291 standardProperties.Add("MSINEWINSTANCE");
292 standardProperties.Add("MSINODISABLEMEDIA");
293 standardProperties.Add("MsiNTProductType");
294 standardProperties.Add("MsiNTSuiteBackOffice");
295 standardProperties.Add("MsiNTSuiteDataCenter");
296 standardProperties.Add("MsiNTSuiteEnterprise");
297 standardProperties.Add("MsiNTSuiteSmallBusiness");
298 standardProperties.Add("MsiNTSuiteSmallBusinessRestricted");
299 standardProperties.Add("MsiNTSuiteWebServer");
300 standardProperties.Add("MsiNTSuitePersonal");
301 standardProperties.Add("MsiPatchRemovalList");
302 standardProperties.Add("MSIPATCHREMOVE");
303 standardProperties.Add("MSIRESTARTMANAGERCONTROL");
304 standardProperties.Add("MsiRestartManagerSessionKey");
305 standardProperties.Add("MSIRMSHUTDOWN");
306 standardProperties.Add("MsiRunningElevated");
307 standardProperties.Add("MsiUIHideCancel");
308 standardProperties.Add("MsiUIProgressOnly");
309 standardProperties.Add("MsiUISourceResOnly");
310 standardProperties.Add("MsiSystemRebootPending");
311 standardProperties.Add("MsiWin32AssemblySupport");
312 standardProperties.Add("NOCOMPANYNAME");
313 standardProperties.Add("NOUSERNAME");
314 standardProperties.Add("OLEAdvtSupport");
315 standardProperties.Add("OptimizePatchSizeForLargeFiles");
316 standardProperties.Add("OriginalDatabase");
317 standardProperties.Add("OutOfDiskSpace");
318 standardProperties.Add("OutOfNoRbDiskSpace");
319 standardProperties.Add("ParentOriginalDatabase");
320 standardProperties.Add("ParentProductCode");
321 standardProperties.Add("PATCH");
322 standardProperties.Add("PATCH_CACHE_DIR");
323 standardProperties.Add("PATCH_CACHE_ENABLED");
324 standardProperties.Add("PatchGUID");
325 standardProperties.Add("PATCHNEWPACKAGECODE");
326 standardProperties.Add("PATCHNEWSUMMARYCOMMENTS");
327 standardProperties.Add("PATCHNEWSUMMARYSUBJECT");
328 standardProperties.Add("PatchOutputPath");
329 standardProperties.Add("PatchSourceList");
330 standardProperties.Add("PhysicalMemory");
331 standardProperties.Add("PIDKEY");
332 standardProperties.Add("PIDTemplate");
333 standardProperties.Add("Preselected");
334 standardProperties.Add("PRIMARYFOLDER");
335 standardProperties.Add("PrimaryVolumePath");
336 standardProperties.Add("PrimaryVolumeSpaceAvailable");
337 standardProperties.Add("PrimaryVolumeSpaceRemaining");
338 standardProperties.Add("PrimaryVolumeSpaceRequired");
339 standardProperties.Add("Privileged");
340 standardProperties.Add("ProductCode");
341 standardProperties.Add("ProductID");
342 standardProperties.Add("ProductLanguage");
343 standardProperties.Add("ProductName");
344 standardProperties.Add("ProductState");
345 standardProperties.Add("ProductVersion");
346 standardProperties.Add("PROMPTROLLBACKCOST");
347 standardProperties.Add("REBOOT");
348 standardProperties.Add("REBOOTPROMPT");
349 standardProperties.Add("RedirectedDllSupport");
350 standardProperties.Add("REINSTALL");
351 standardProperties.Add("REINSTALLMODE");
352 standardProperties.Add("RemoveAdminTS");
353 standardProperties.Add("REMOVE");
354 standardProperties.Add("ReplacedInUseFiles");
355 standardProperties.Add("RestrictedUserControl");
356 standardProperties.Add("RESUME");
357 standardProperties.Add("RollbackDisabled");
358 standardProperties.Add("ROOTDRIVE");
359 standardProperties.Add("ScreenX");
360 standardProperties.Add("ScreenY");
361 standardProperties.Add("SecureCustomProperties");
362 standardProperties.Add("ServicePackLevel");
363 standardProperties.Add("ServicePackLevelMinor");
364 standardProperties.Add("SEQUENCE");
365 standardProperties.Add("SharedWindows");
366 standardProperties.Add("ShellAdvtSupport");
367 standardProperties.Add("SHORTFILENAMES");
368 standardProperties.Add("SourceDir");
369 standardProperties.Add("SOURCELIST");
370 standardProperties.Add("SystemLanguageID");
371 standardProperties.Add("TARGETDIR");
372 standardProperties.Add("TerminalServer");
373 standardProperties.Add("TextHeight");
374 standardProperties.Add("Time");
375 standardProperties.Add("TRANSFORMS");
376 standardProperties.Add("TRANSFORMSATSOURCE");
377 standardProperties.Add("TRANSFORMSSECURE");
378 standardProperties.Add("TTCSupport");
379 standardProperties.Add("UILevel");
380 standardProperties.Add("UpdateStarted");
381 standardProperties.Add("UpgradeCode");
382 standardProperties.Add("UPGRADINGPRODUCTCODE");
383 standardProperties.Add("UserLanguageID");
384 standardProperties.Add("USERNAME");
385 standardProperties.Add("UserSID");
386 standardProperties.Add("Version9X");
387 standardProperties.Add("VersionDatabase");
388 standardProperties.Add("VersionMsi");
389 standardProperties.Add("VersionNT");
390 standardProperties.Add("VersionNT64");
391 standardProperties.Add("VirtualMemory");
392 standardProperties.Add("WindowsBuild");
393 standardProperties.Add("WindowsVolume");
394 }
395 }
396
397 return standardProperties.Contains(propertyName);
398 }
399
400 /// <summary>
401 /// Sets up a hashtable with the set of standard MSI directories
402 /// </summary>
403 private static void LoadStandardDirectories()
404 {
405 lock (lockObject)
406 {
407 if (null == standardDirectories)
408 {
409 standardDirectories = new HashSet<string>();
410 standardDirectories.Add("TARGETDIR");
411 standardDirectories.Add("AdminToolsFolder");
412 standardDirectories.Add("AppDataFolder");
413 standardDirectories.Add("CommonAppDataFolder");
414 standardDirectories.Add("CommonFilesFolder");
415 standardDirectories.Add("DesktopFolder");
416 standardDirectories.Add("FavoritesFolder");
417 standardDirectories.Add("FontsFolder");
418 standardDirectories.Add("LocalAppDataFolder");
419 standardDirectories.Add("MyPicturesFolder");
420 standardDirectories.Add("PersonalFolder");
421 standardDirectories.Add("ProgramFilesFolder");
422 standardDirectories.Add("ProgramMenuFolder");
423 standardDirectories.Add("SendToFolder");
424 standardDirectories.Add("StartMenuFolder");
425 standardDirectories.Add("StartupFolder");
426 standardDirectories.Add("System16Folder");
427 standardDirectories.Add("SystemFolder");
428 standardDirectories.Add("TempFolder");
429 standardDirectories.Add("TemplateFolder");
430 standardDirectories.Add("WindowsFolder");
431 standardDirectories.Add("CommonFiles64Folder");
432 standardDirectories.Add("ProgramFiles64Folder");
433 standardDirectories.Add("System64Folder");
434 standardDirectories.Add("NetHoodFolder");
435 standardDirectories.Add("PrintHoodFolder");
436 standardDirectories.Add("RecentFolder");
437 standardDirectories.Add("WindowsVolume");
438 }
439 }
440 }
441 }
442}
diff --git a/src/WixToolset.Data.WindowsInstaller/WindowsInstallerStandardInternal.cs b/src/WixToolset.Data.WindowsInstaller/WindowsInstallerStandardInternal.cs
new file mode 100644
index 00000000..cc6754c3
--- /dev/null
+++ b/src/WixToolset.Data.WindowsInstaller/WindowsInstallerStandardInternal.cs
@@ -0,0 +1,59 @@
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
3namespace WixToolset.Data
4{
5 using System.Reflection;
6 using System.Xml;
7 using WixToolset.Data.Rows;
8
9 /// <summary>
10 /// Represents the Windows Installer standard objects.
11 /// </summary>
12 public static class WindowsInstallerStandardInternal
13 {
14 private static readonly object lockObject = new object();
15
16 private static TableDefinitionCollection tableDefinitions;
17 private static WixActionRowCollection standardActions;
18
19 /// <summary>
20 /// Gets the table definitions stored in this assembly.
21 /// </summary>
22 /// <returns>Table definition collection for tables stored in this assembly.</returns>
23 public static TableDefinitionCollection GetTableDefinitions()
24 {
25 lock (lockObject)
26 {
27 if (null == WindowsInstallerStandardInternal.tableDefinitions)
28 {
29 using (XmlReader reader = XmlReader.Create(Assembly.GetExecutingAssembly().GetManifestResourceStream("WixToolset.Data.WindowsInstaller.Data.tables.xml")))
30 {
31 WindowsInstallerStandardInternal.tableDefinitions = TableDefinitionCollection.Load(reader);
32 }
33 }
34 }
35
36 return WindowsInstallerStandardInternal.tableDefinitions;
37 }
38
39 /// <summary>
40 /// Gets the standard actions stored in this assembly.
41 /// </summary>
42 /// <returns>Collection of standard actions in this assembly.</returns>
43 public static WixActionRowCollection GetStandardActionRows()
44 {
45 lock (lockObject)
46 {
47 if (null == WindowsInstallerStandardInternal.standardActions)
48 {
49 using (XmlReader reader = XmlReader.Create(Assembly.GetExecutingAssembly().GetManifestResourceStream("WixToolset.Data.WindowsInstaller.Data.actions.xml")))
50 {
51 WindowsInstallerStandardInternal.standardActions = WixActionRowCollection.Load(reader);
52 }
53 }
54 }
55
56 return WindowsInstallerStandardInternal.standardActions;
57 }
58 }
59}
diff --git a/src/test/WixToolsetTest.CoreIntegrationFixture/ProgramFixture.cs b/src/test/WixToolsetTest.CoreIntegrationFixture/ProgramFixture.cs
index 46d15ed8..4b4daeda 100644
--- a/src/test/WixToolsetTest.CoreIntegrationFixture/ProgramFixture.cs
+++ b/src/test/WixToolsetTest.CoreIntegrationFixture/ProgramFixture.cs
@@ -26,18 +26,17 @@ namespace WixToolsetTest.CoreIntegrationFixture
26 var result = program.Run(new WixToolsetServiceProvider(), new[] { "build", "Package.wxs", "PackageComponents.wxs", "-loc", "Package.en-us.wxl", "-bindpath", "data", "-intermediateFolder", intermediateFolder, "-o", $@"{intermediateFolder}\bin\test.msi" }); 26 var result = program.Run(new WixToolsetServiceProvider(), new[] { "build", "Package.wxs", "PackageComponents.wxs", "-loc", "Package.en-us.wxl", "-bindpath", "data", "-intermediateFolder", intermediateFolder, "-o", $@"{intermediateFolder}\bin\test.msi" });
27 27
28 Assert.Equal(0, result); 28 Assert.Equal(0, result);
29#if FIXED_MSI_BACKEND 29
30 Assert.True(File.Exists(Path.Combine(intermediateFolder, @"bin\test.msi"))); 30 Assert.True(File.Exists(Path.Combine(intermediateFolder, @"bin\test.msi")));
31 Assert.True(File.Exists(Path.Combine(intermediateFolder, @"bin\test.wixpdb"))); 31 Assert.True(File.Exists(Path.Combine(intermediateFolder, @"bin\test.wixpdb")));
32 Assert.True(File.Exists(Path.Combine(intermediateFolder, @"bin\MsiPackage\test.txt"))); 32 Assert.True(File.Exists(Path.Combine(intermediateFolder, @"bin\MsiPackage\test.txt")));
33#else 33
34 var intermediate = Intermediate.Load(Path.Combine(intermediateFolder, @"bin\test.msi")); 34 var intermediate = Intermediate.Load(Path.Combine(intermediateFolder, @"bin\test.wir"));
35 Assert.Single(intermediate.Sections); 35 Assert.Single(intermediate.Sections);
36 36
37 var wixFile = intermediate.Sections.SelectMany(s => s.Tuples).OfType<WixFileTuple>().Single(); 37 var wixFile = intermediate.Sections.SelectMany(s => s.Tuples).OfType<WixFileTuple>().Single();
38 Assert.Equal(@"data\test.txt", wixFile[WixFileTupleFields.Source].AsPath().Path); 38 Assert.Equal(@"data\test.txt", wixFile[WixFileTupleFields.Source].AsPath().Path);
39 Assert.Equal(@"test.txt", wixFile[WixFileTupleFields.Source].PreviousValue.AsPath().Path); 39 Assert.Equal(@"test.txt", wixFile[WixFileTupleFields.Source].PreviousValue.AsPath().Path);
40#endif
41 } 40 }
42 } 41 }
43 } 42 }