@joebigelow / wix / commits / c237bb3b

Update decompiler to use XDocument rather than generated classes.

- Use CompareXml for diffing. - Change CustomAction/@ScriptFile to @ScriptSourceFile.

Bob Arnson committed Aug 24, 2020 at 17:25 UTC c237bb3bb00d36c50271a70baac68f49890e35e1
9 files changed +3467 -5270
src/WixToolset.Core.WindowsInstaller/Decompile/Decompiler.cs
+3283 -5014
@@ -3,9 +3,7 @@
3 namespace WixToolset.Core.WindowsInstaller
4 {
5 using System;
6 - using System.Collections;
6 using System.Collections.Generic;
8 - using System.Collections.Specialized;
7 using System.Globalization;
8 using System.IO;
9 using System.Linq;
@@ -13,13 +11,13 @@ namespace WixToolset.Core.WindowsInstaller
11 using System.Text.RegularExpressions;
12 using System.Xml.Linq;
13 using WixToolset.Core;
14 + using WixToolset.Core.WindowsInstaller.Decompile;
15 using WixToolset.Data;
16 using WixToolset.Data.Symbols;
17 using WixToolset.Data.WindowsInstaller;
18 using WixToolset.Data.WindowsInstaller.Rows;
19 using WixToolset.Extensibility;
20 using WixToolset.Extensibility.Services;
22 - using Wix = WixToolset.Data.Serialize;
21
22 /// <summary>
23 /// Decompiles an msi database into WiX source.
@@ -42,14 +40,7 @@ namespace WixToolset.Core.WindowsInstaller
40 private static readonly string[] IconControlAttributes = { "Image", null, null, null, "FixedSize", "Icon16", "Icon32" };
41 private static readonly string[] BitmapControlAttributes = { "Image", null, null, null, "FixedSize" };
42 private static readonly string[] CheckboxControlAttributes = { null, "PushLike", "Bitmap", "Icon", "FixedSize", "Icon16", "Icon32" };
45 -
46 - private bool compressed;
47 - private bool shortNames;
48 - private DecompilerCore core;
49 - private string modularizationGuid;
50 - private readonly Hashtable patchTargetFiles;
51 - private readonly Hashtable sequenceElements;
52 - private readonly TableDefinitionCollection tableDefinitions;
43 + private XElement uiElement;
44
45 /// <summary>
46 /// Creates a new decompiler object with a default set of table definitions.
@@ -58,7 +49,7 @@ namespace WixToolset.Core.WindowsInstaller
49 {
50 this.Messaging = messaging;
51 this.Extensions = extensions;
61 - this.BaseSourcePath = String.IsNullOrEmpty(baseSourcePath) ? "SourceDir" : baseSourcePath;
52 + this.BaseSourcePath = baseSourcePath ?? "SourceDir";
53 this.SuppressCustomTables = suppressCustomTables;
54 this.SuppressDroppingEmptyTables = suppressDroppingEmptyTables;
55 this.SuppressUI = suppressUI;
@@ -67,9 +58,7 @@ namespace WixToolset.Core.WindowsInstaller
58 this.ExtensionsByTableName = new Dictionary<string, IWindowsInstallerBackendDecompilerExtension>();
59 this.StandardActions = WindowsInstallerStandard.StandardActions().ToDictionary(a => a.Id.Id);
60
70 - this.patchTargetFiles = new Hashtable();
71 - this.sequenceElements = new Hashtable();
72 - this.tableDefinitions = new TableDefinitionCollection();
61 + this.TableDefinitions = new TableDefinitionCollection();
62 }
63
64 private IMessaging Messaging { get; }
@@ -94,6 +83,37 @@ namespace WixToolset.Core.WindowsInstaller
83
84 private Dictionary<string, WixActionSymbol> StandardActions { get; }
85
86 + private bool Compressed { get; set; }
87 +
88 + private XElement RootElement { get; set; }
89 +
90 + private TableDefinitionCollection TableDefinitions { get; }
91 +
92 + private bool ShortNames { get; set; }
93 +
94 + private string ModularizationGuid { get; set; }
95 +
96 + public XElement UIElement
97 + {
98 + get
99 + {
100 + if (null == this.uiElement)
101 + {
102 + this.uiElement = new XElement(Names.UIElement);
103 + this.RootElement.Add(this.uiElement);
104 + }
105 +
106 + return this.uiElement;
107 + }
108 + }
109 +
110 + public Dictionary<string, XElement> Singletons { get; } = new Dictionary<string, XElement>();
111 +
112 + public Dictionary<string, XElement> IndexedElements { get; } = new Dictionary<string, XElement>();
113 +
114 + public Dictionary<string, XElement> PatchTargetFiles { get; } = new Dictionary<string, XElement>();
115 +
116 +
117 /// <summary>
118 /// Decompile the database file.
119 /// </summary>
@@ -109,18 +129,18 @@ namespace WixToolset.Core.WindowsInstaller
129 this.OutputType = output.Type;
130
131 // collect the table definitions from the output
112 - this.tableDefinitions.Clear();
132 + this.TableDefinitions.Clear();
133 foreach (var table in output.Tables)
134 {
115 - this.tableDefinitions.Add(table.Definition);
135 + this.TableDefinitions.Add(table.Definition);
136 }
137
138 // add any missing standard and wix-specific table definitions
139 foreach (var tableDefinition in WindowsInstallerTableDefinitions.All)
140 {
121 - if (!this.tableDefinitions.Contains(tableDefinition.Name))
141 + if (!this.TableDefinitions.Contains(tableDefinition.Name))
142 {
123 - this.tableDefinitions.Add(tableDefinition);
143 + this.TableDefinitions.Add(tableDefinition);
144 }
145 }
146
@@ -132,62 +152,46 @@ namespace WixToolset.Core.WindowsInstaller
152 }
153 #endif
154
135 - var wixElement = new Wix.Wix();
136 - Wix.IParentElement rootElement;
137 -
155 switch (this.OutputType)
156 {
140 - case OutputType.Module:
141 - rootElement = new Wix.Module();
142 - break;
143 - case OutputType.PatchCreation:
144 - rootElement = new Wix.PatchCreation();
145 - break;
146 - case OutputType.Product:
147 - rootElement = new Wix.Product();
148 - break;
149 - default:
150 - throw new InvalidOperationException("Unknown output type.");
157 + case OutputType.Module:
158 + this.RootElement = new XElement(Names.ModuleElement);
159 + break;
160 + case OutputType.PatchCreation:
161 + this.RootElement = new XElement(Names.PatchCreationElement);
162 + break;
163 + case OutputType.Product:
164 + this.RootElement = new XElement(Names.ProductElement);
165 + break;
166 + default:
167 + throw new InvalidOperationException("Unknown output type.");
168 }
152 - wixElement.AddChild((Wix.ISchemaElement)rootElement);
169 +
170 + var xWix = new XElement(Names.WixElement, this.RootElement);
171
172 // try to decompile the database file
155 - try
173 + // stop processing if an error previously occurred
174 + if (this.Messaging.EncounteredError)
175 {
157 - this.core = new DecompilerCore(rootElement);
158 -
159 - // stop processing if an error previously occurred
160 - if (this.Messaging.EncounteredError)
161 - {
162 - return null;
163 - }
164 -
165 - this.InitializeDecompile(output.Tables, output.Codepage);
166 -
167 - // stop processing if an error previously occurred
168 - if (this.Messaging.EncounteredError)
169 - {
170 - return null;
171 - }
176 + return null;
177 + }
178
173 - // decompile the tables
174 - this.DecompileTables(output);
179 + this.InitializeDecompile(output.Tables, output.Codepage);
180
176 - // finalize the decompiler and its extensions
177 - this.FinalizeDecompile(output.Tables);
178 - }
179 - finally
181 + // stop processing if an error previously occurred
182 + if (this.Messaging.EncounteredError)
183 {
181 - this.core = null;
184 + return null;
185 }
186
184 - var document = new XDocument();
185 - using (var writer = document.CreateWriter())
186 - {
187 - wixElement.OutputXml(writer);
188 - }
187 + // decompile the tables
188 + this.DecompileTables(output);
189 +
190 + // finalize the decompiler and its extensions
191 + this.FinalizeDecompile(output.Tables);
192
193 // return the XML document only if decompilation completed successfully
194 + var document = new XDocument(xWix);
195 return this.Messaging.EncounteredError ? null : document;
196 }
197
@@ -211,51 +215,157 @@ namespace WixToolset.Core.WindowsInstaller
215 }
216 #endif
217
218 + /// <summary>
219 + /// Gets the element corresponding to the row it came from.
220 + /// </summary>
221 + /// <param name="row">The row corresponding to the element.</param>
222 + /// <returns>The indexed element.</returns>
223 + public XElement GetIndexedElement(WixToolset.Data.WindowsInstaller.Row row) => this.GetIndexedElement(row.TableDefinition.Name, row.GetPrimaryKey(DecompilerConstants.PrimaryKeyDelimiter));
224 +
225 + /// <summary>
226 + /// Gets the element corresponding to the primary key of the given table.
227 + /// </summary>
228 + /// <param name="table">The table corresponding to the element.</param>
229 + /// <param name="primaryKey">The primary key corresponding to the element.</param>
230 + /// <returns>The indexed element.</returns>
231 + public XElement GetIndexedElement(string table, params string[] primaryKey) => this.IndexedElements[String.Concat(table, ':', String.Join(DecompilerConstants.PrimaryKeyDelimiterString, primaryKey))];
232 +
233 + /// <summary>
234 + /// Gets the element corresponding to the primary key of the given table.
235 + /// </summary>
236 + /// <param name="table">The table corresponding to the element.</param>
237 + /// <param name="primaryKey">The primary key corresponding to the element.</param>
238 + /// <returns>The indexed element.</returns>
239 + public bool TryGetIndexedElement(WixToolset.Data.WindowsInstaller.Row row, out XElement xElement) => this.TryGetIndexedElement(row.TableDefinition.Name, out xElement, row.GetPrimaryKey(DecompilerConstants.PrimaryKeyDelimiter));
240 +
241 + /// <summary>
242 + /// Gets the element corresponding to the primary key of the given table.
243 + /// </summary>
244 + /// <param name="table">The table corresponding to the element.</param>
245 + /// <param name="primaryKey">The primary key corresponding to the element.</param>
246 + /// <returns>The indexed element.</returns>
247 + public bool TryGetIndexedElement(string table, out XElement xElement, params string[] primaryKey) => this.IndexedElements.TryGetValue(String.Concat(table, ':', String.Join(DecompilerConstants.PrimaryKeyDelimiterString, primaryKey)), out xElement);
248 +
249 + /// <summary>
250 + /// Index an element by its corresponding row.
251 + /// </summary>
252 + /// <param name="row">The row corresponding to the element.</param>
253 + /// <param name="element">The element to index.</param>
254 + public void IndexElement(WixToolset.Data.WindowsInstaller.Row row, XElement element)
255 + {
256 + this.IndexedElements.Add(String.Concat(row.TableDefinition.Name, ':', row.GetPrimaryKey(DecompilerConstants.PrimaryKeyDelimiter)), element);
257 + }
258 +
259 + /// <summary>
260 + /// Index an element by its corresponding row.
261 + /// </summary>
262 + /// <param name="row">The row corresponding to the element.</param>
263 + /// <param name="element">The element to index.</param>
264 + public void IndexElement(XElement element, string table, params string[] primaryKey)
265 + {
266 + this.IndexedElements.Add(String.Concat(table, ':', String.Join(DecompilerConstants.PrimaryKeyDelimiterString, primaryKey)), element);
267 + }
268 +
269 + private Dictionary<string, List<XElement>> IndexTableOneToMany(IEnumerable<Row> rows, int column = 0)
270 + {
271 + return rows
272 + .ToLookup(row => row.FieldAsString(column), row => this.GetIndexedElement(row))
273 + .ToDictionary(lookup => lookup.Key, lookup => lookup.ToList());
274 + }
275 +
276 + private Dictionary<string, List<XElement>> IndexTableOneToMany(TableIndexedCollection tables, string tableName, int column = 0) => this.IndexTableOneToMany(tables[tableName]?.Rows ?? Enumerable.Empty<Row>(), column);
277 +
278 + private Dictionary<string, List<XElement>> IndexTableOneToMany(Table table, int column = 0) => this.IndexTableOneToMany(table?.Rows ?? Enumerable.Empty<Row>(), column);
279 +
280 + private void AddChildToParent(string parentName, XElement xChild, Row row, int column)
281 + {
282 + var key = row.FieldAsString(column);
283 + if (this.TryGetIndexedElement(parentName, out var xParent, key))
284 + {
285 + xParent.Add(xChild);
286 + }
287 + else
288 + {
289 + this.Messaging.Write(WarningMessages.ExpectedForeignRow(row.SourceLineNumbers, row.Table.Name, row.GetPrimaryKey(DecompilerConstants.PrimaryKeyDelimiter), row.Fields[column].Column.Name, key, parentName));
290 + }
291 + }
292 +
293 + private static XAttribute XAttributeIfNotNull(string attributeName, Row row, int column) => row.IsColumnNull(column) ? null : new XAttribute(attributeName, row.FieldAsString(column));
294 +
295 + private static void SetAttributeIfNotNull(XElement xElement, string attributeName, string value)
296 + {
297 + if (!String.IsNullOrEmpty(value))
298 + {
299 + xElement.SetAttributeValue(attributeName, value);
300 + }
301 + }
302 +
303 + private static void SetAttributeIfNotNull(XElement xElement, string attributeName, int? value)
304 + {
305 + if (value.HasValue)
306 + {
307 + xElement.SetAttributeValue(attributeName, value);
308 + }
309 + }
310 +
311 + /// <summary>
312 + /// Convert an Int32 into a DateTime.
313 + /// </summary>
314 + /// <param name="value">The Int32 value.</param>
315 + /// <returns>The DateTime.</returns>
316 + private static DateTime ConvertIntegerToDateTime(int value)
317 + {
318 + var date = value / 65536;
319 + var time = value % 65536;
320 +
321 + return new DateTime(1980 + (date / 512), (date % 512) / 32, date % 32, time / 2048, (time % 2048) / 32, (time % 32) * 2);
322 + }
323 +
324 /// <summary>
325 /// Set the common control attributes in a control element.
326 /// </summary>
327 /// <param name="attributes">The control attributes.</param>
328 /// <param name="control">The control element.</param>
219 - private static void SetControlAttributes(int attributes, Wix.Control control)
329 + private static void SetControlAttributes(int attributes, XElement xControl)
330 {
331 if (0 == (attributes & WindowsInstallerConstants.MsidbControlAttributesEnabled))
332 {
223 - control.Disabled = Wix.YesNoType.yes;
333 + xControl.SetAttributeValue("Disabled", "yes");
334 }
335
336 if (WindowsInstallerConstants.MsidbControlAttributesIndirect == (attributes & WindowsInstallerConstants.MsidbControlAttributesIndirect))
337 {
228 - control.Indirect = Wix.YesNoType.yes;
338 + xControl.SetAttributeValue("Indirect", "yes");
339 }
340
341 if (WindowsInstallerConstants.MsidbControlAttributesInteger == (attributes & WindowsInstallerConstants.MsidbControlAttributesInteger))
342 {
233 - control.Integer = Wix.YesNoType.yes;
343 + xControl.SetAttributeValue("Integer", "yes");
344 }
345
346 if (WindowsInstallerConstants.MsidbControlAttributesLeftScroll == (attributes & WindowsInstallerConstants.MsidbControlAttributesLeftScroll))
347 {
238 - control.LeftScroll = Wix.YesNoType.yes;
348 + xControl.SetAttributeValue("LeftScroll", "yes");
349 }
350
351 if (WindowsInstallerConstants.MsidbControlAttributesRightAligned == (attributes & WindowsInstallerConstants.MsidbControlAttributesRightAligned))
352 {
243 - control.RightAligned = Wix.YesNoType.yes;
353 + xControl.SetAttributeValue("RightAligned", "yes");
354 }
355
356 if (WindowsInstallerConstants.MsidbControlAttributesRTLRO == (attributes & WindowsInstallerConstants.MsidbControlAttributesRTLRO))
357 {
248 - control.RightToLeft = Wix.YesNoType.yes;
358 + xControl.SetAttributeValue("RightToLeft", "yes");
359 }
360
361 if (WindowsInstallerConstants.MsidbControlAttributesSunken == (attributes & WindowsInstallerConstants.MsidbControlAttributesSunken))
362 {
253 - control.Sunken = Wix.YesNoType.yes;
363 + xControl.SetAttributeValue("Sunken", "yes");
364 }
365
366 if (0 == (attributes & WindowsInstallerConstants.MsidbControlAttributesVisible))
367 {
258 - control.Hidden = Wix.YesNoType.yes;
368 + xControl.SetAttributeValue("Hidden", "yes");
369 }
370 }
371
@@ -265,137 +375,93 @@ namespace WixToolset.Core.WindowsInstaller
375 /// <param name="actionSymbol">The action from which the element should be created.</param>
376 private void CreateActionElement(WixActionSymbol actionSymbol)
377 {
268 - Wix.ISchemaElement actionElement = null;
378 + XElement xAction;
379
270 - if (null != this.core.GetIndexedElement("CustomAction", actionSymbol.Action)) // custom action
380 + if (this.TryGetIndexedElement("CustomAction", out var _, actionSymbol.Action)) // custom action
381 {
272 - var custom = new Wix.Custom();
273 -
274 - custom.Action = actionSymbol.Action;
275 -
276 - if (null != actionSymbol.Condition)
277 - {
278 - custom.Content = actionSymbol.Condition;
279 - }
382 + xAction = new XElement(Names.CustomElement,
383 + new XAttribute("Action", actionSymbol.Action),
384 + String.IsNullOrEmpty(actionSymbol.Condition) ? null : new XAttribute("Condition", actionSymbol.Condition));
385
386 switch (actionSymbol.Sequence)
387 {
283 - case (-4):
284 - custom.OnExit = Wix.ExitType.suspend;
285 - break;
286 - case (-3):
287 - custom.OnExit = Wix.ExitType.error;
288 - break;
289 - case (-2):
290 - custom.OnExit = Wix.ExitType.cancel;
291 - break;
292 - case (-1):
293 - custom.OnExit = Wix.ExitType.success;
294 - break;
295 - default:
296 - if (null != actionSymbol.Before)
297 - {
298 - custom.Before = actionSymbol.Before;
299 - }
300 - else if (null != actionSymbol.After)
301 - {
302 - custom.After = actionSymbol.After;
303 - }
304 - else if (actionSymbol.Sequence.HasValue)
305 - {
306 - custom.Sequence = actionSymbol.Sequence.Value;
307 - }
308 - break;
388 + case (-4):
389 + xAction.SetAttributeValue("OnExit", "suspend");
390 + break;
391 + case (-3):
392 + xAction.SetAttributeValue("OnExit", "error");
393 + break;
394 + case (-2):
395 + xAction.SetAttributeValue("OnExit", "cancel");
396 + break;
397 + case (-1):
398 + xAction.SetAttributeValue("OnExit", "success");
399 + break;
400 + default:
401 + if (null != actionSymbol.Before)
402 + {
403 + xAction.SetAttributeValue("Before", actionSymbol.Before);
404 + }
405 + else if (null != actionSymbol.After)
406 + {
407 + xAction.SetAttributeValue("After", actionSymbol.After);
408 + }
409 + else if (actionSymbol.Sequence.HasValue)
410 + {
411 + xAction.SetAttributeValue("Sequence", actionSymbol.Sequence.Value);
412 + }
413 + break;
414 }
310 -
311 - actionElement = custom;
415 }
313 - else if (null != this.core.GetIndexedElement("Dialog", actionSymbol.Action)) // dialog
416 + else if (this.TryGetIndexedElement("Dialog", out var _, actionSymbol.Action)) // dialog
417 {
315 - var show = new Wix.Show();
316 -
317 - show.Dialog = actionSymbol.Action;
318 -
319 - if (null != actionSymbol.Condition)
320 - {
321 - show.Content = actionSymbol.Condition;
322 - }
418 + xAction = new XElement(Names.CustomElement,
419 + new XAttribute("Dialog", actionSymbol.Action),
420 + new XAttribute("Condition", actionSymbol.Condition));
421
422 switch (actionSymbol.Sequence)
423 {
326 - case (-4):
327 - show.OnExit = Wix.ExitType.suspend;
328 - break;
329 - case (-3):
330 - show.OnExit = Wix.ExitType.error;
331 - break;
332 - case (-2):
333 - show.OnExit = Wix.ExitType.cancel;
334 - break;
335 - case (-1):
336 - show.OnExit = Wix.ExitType.success;
337 - break;
338 - default:
339 - if (null != actionSymbol.Before)
340 - {
341 - show.Before = actionSymbol.Before;
342 - }
343 - else if (null != actionSymbol.After)
344 - {
345 - show.After = actionSymbol.After;
346 - }
347 - else if (actionSymbol.Sequence.HasValue)
348 - {
349 - show.Sequence = actionSymbol.Sequence.Value;
350 - }
351 - break;
424 + case (-4):
425 + xAction.SetAttributeValue("OnExit", "suspend");
426 + break;
427 + case (-3):
428 + xAction.SetAttributeValue("OnExit", "error");
429 + break;
430 + case (-2):
431 + xAction.SetAttributeValue("OnExit", "cancel");
432 + break;
433 + case (-1):
434 + xAction.SetAttributeValue("OnExit", "success");
435 + break;
436 + default:
437 + SetAttributeIfNotNull(xAction, "Before", actionSymbol.Before);
438 + SetAttributeIfNotNull(xAction, "After", actionSymbol.After);
439 + SetAttributeIfNotNull(xAction, "Sequence", actionSymbol.Sequence);
440 + break;
441 }
353 -
354 - actionElement = show;
442 }
443 else // possibly a standard action without suggested sequence information
444 {
358 - actionElement = this.CreateStandardActionElement(actionSymbol);
445 + xAction = this.CreateStandardActionElement(actionSymbol);
446 }
447
448 // add the action element to the appropriate sequence element
362 - if (null != actionElement)
449 + if (null != xAction)
450 {
451 var sequenceTable = actionSymbol.SequenceTable.ToString();
365 - var sequenceElement = (Wix.IParentElement)this.sequenceElements[sequenceTable];
366 -
367 - if (null == sequenceElement)
452 + if (!this.Singletons.TryGetValue(sequenceTable, out var xSequence))
453 {
369 - switch (actionSymbol.SequenceTable)
370 - {
371 - case SequenceTable.AdminExecuteSequence:
372 - sequenceElement = new Wix.AdminExecuteSequence();
373 - break;
374 - case SequenceTable.AdminUISequence:
375 - sequenceElement = new Wix.AdminUISequence();
376 - break;
377 - case SequenceTable.AdvertiseExecuteSequence:
378 - sequenceElement = new Wix.AdvertiseExecuteSequence();
379 - break;
380 - case SequenceTable.InstallExecuteSequence:
381 - sequenceElement = new Wix.InstallExecuteSequence();
382 - break;
383 - case SequenceTable.InstallUISequence:
384 - sequenceElement = new Wix.InstallUISequence();
385 - break;
386 - default:
387 - throw new InvalidOperationException("Unknown sequence table.");
388 - }
454 + xSequence = new XElement(Names.WxsNamespace + sequenceTable);
455
390 - this.core.RootElement.AddChild((Wix.ISchemaElement)sequenceElement);
391 - this.sequenceElements.Add(sequenceTable, sequenceElement);
456 + this.RootElement.Add(xSequence);
457 + this.Singletons.Add(sequenceTable, xSequence);
458 }
459
460 try
461 {
396 - sequenceElement.AddChild(actionElement);
462 + xSequence.Add(xAction);
463 }
398 - catch (System.ArgumentException) // action/dialog is not valid for this sequence
464 + catch (ArgumentException) // action/dialog is not valid for this sequence
465 {
466 this.Messaging.Write(WarningMessages.IllegalActionInSequence(actionSymbol.SourceLineNumbers, actionSymbol.SequenceTable.ToString(), actionSymbol.Action));
467 }
@@ -407,294 +473,129 @@ namespace WixToolset.Core.WindowsInstaller
473 /// </summary>
474 /// <param name="actionSymbol">The action row from which the element should be created.</param>
475 /// <returns>The created element.</returns>
410 - private Wix.ISchemaElement CreateStandardActionElement(WixActionSymbol actionSymbol)
476 + private XElement CreateStandardActionElement(WixActionSymbol actionSymbol)
477 {
412 - Wix.ActionSequenceType actionElement = null;
478 + XElement xStandardAction = null;
479
480 switch (actionSymbol.Action)
481 {
416 - case "AllocateRegistrySpace":
417 - actionElement = new Wix.AllocateRegistrySpace();
418 - break;
419 - case "AppSearch":
420 - this.StandardActions.TryGetValue(actionSymbol.Id.Id, out var appSearchActionRow);
421 -
422 - if (null != actionSymbol.Before || null != actionSymbol.After || (null != appSearchActionRow && actionSymbol.Sequence != appSearchActionRow.Sequence))
423 - {
424 - var appSearch = new Wix.AppSearch();
425 -
426 - if (null != actionSymbol.Condition)
427 - {
428 - appSearch.Content = actionSymbol.Condition;
429 - }
430 -
431 - if (null != actionSymbol.Before)
432 - {
433 - appSearch.Before = actionSymbol.Before;
434 - }
435 - else if (null != actionSymbol.After)
436 - {
437 - appSearch.After = actionSymbol.After;
438 - }
439 - else if (actionSymbol.Sequence.HasValue)
440 - {
441 - appSearch.Sequence = actionSymbol.Sequence.Value;
442 - }
443 -
444 - return appSearch;
445 - }
446 - break;
447 - case "BindImage":
448 - actionElement = new Wix.BindImage();
449 - break;
450 - case "CCPSearch":
451 - var ccpSearch = new Wix.CCPSearch();
452 - Decompiler.SequenceRelativeAction(actionSymbol, ccpSearch);
453 - return ccpSearch;
454 - case "CostFinalize":
455 - actionElement = new Wix.CostFinalize();
456 - break;
457 - case "CostInitialize":
458 - actionElement = new Wix.CostInitialize();
459 - break;
460 - case "CreateFolders":
461 - actionElement = new Wix.CreateFolders();
462 - break;
463 - case "CreateShortcuts":
464 - actionElement = new Wix.CreateShortcuts();
465 - break;
466 - case "DeleteServices":
467 - actionElement = new Wix.DeleteServices();
468 - break;
469 - case "DisableRollback":
470 - var disableRollback = new Wix.DisableRollback();
471 - Decompiler.SequenceRelativeAction(actionSymbol, disableRollback);
472 - return disableRollback;
473 - case "DuplicateFiles":
474 - actionElement = new Wix.DuplicateFiles();
475 - break;
476 - case "ExecuteAction":
477 - actionElement = new Wix.ExecuteAction();
478 - break;
479 - case "FileCost":
480 - actionElement = new Wix.FileCost();
481 - break;
482 - case "FindRelatedProducts":
483 - var findRelatedProducts = new Wix.FindRelatedProducts();
484 - Decompiler.SequenceRelativeAction(actionSymbol, findRelatedProducts);
485 - return findRelatedProducts;
486 - case "ForceReboot":
487 - var forceReboot = new Wix.ForceReboot();
488 - Decompiler.SequenceRelativeAction(actionSymbol, forceReboot);
489 - return forceReboot;
490 - case "InstallAdminPackage":
491 - actionElement = new Wix.InstallAdminPackage();
492 - break;
493 - case "InstallExecute":
494 - var installExecute = new Wix.InstallExecute();
495 - Decompiler.SequenceRelativeAction(actionSymbol, installExecute);
496 - return installExecute;
497 - case "InstallExecuteAgain":
498 - var installExecuteAgain = new Wix.InstallExecuteAgain();
499 - Decompiler.SequenceRelativeAction(actionSymbol, installExecuteAgain);
500 - return installExecuteAgain;
501 - case "InstallFiles":
502 - actionElement = new Wix.InstallFiles();
503 - break;
504 - case "InstallFinalize":
505 - actionElement = new Wix.InstallFinalize();
506 - break;
507 - case "InstallInitialize":
508 - actionElement = new Wix.InstallInitialize();
509 - break;
510 - case "InstallODBC":
511 - actionElement = new Wix.InstallODBC();
512 - break;
513 - case "InstallServices":
514 - actionElement = new Wix.InstallServices();
515 - break;
516 - case "InstallValidate":
517 - actionElement = new Wix.InstallValidate();
518 - break;
519 - case "IsolateComponents":
520 - actionElement = new Wix.IsolateComponents();
521 - break;
522 - case "LaunchConditions":
523 - var launchConditions = new Wix.LaunchConditions();
524 - Decompiler.SequenceRelativeAction(actionSymbol, launchConditions);
525 - return launchConditions;
526 - case "MigrateFeatureStates":
527 - actionElement = new Wix.MigrateFeatureStates();
528 - break;
529 - case "MoveFiles":
530 - actionElement = new Wix.MoveFiles();
531 - break;
532 - case "MsiPublishAssemblies":
533 - actionElement = new Wix.MsiPublishAssemblies();
534 - break;
535 - case "MsiUnpublishAssemblies":
536 - actionElement = new Wix.MsiUnpublishAssemblies();
537 - break;
538 - case "PatchFiles":
539 - actionElement = new Wix.PatchFiles();
540 - break;
541 - case "ProcessComponents":
542 - actionElement = new Wix.ProcessComponents();
543 - break;
544 - case "PublishComponents":
545 - actionElement = new Wix.PublishComponents();
546 - break;
547 - case "PublishFeatures":
548 - actionElement = new Wix.PublishFeatures();
549 - break;
550 - case "PublishProduct":
551 - actionElement = new Wix.PublishProduct();
552 - break;
553 - case "RegisterClassInfo":
554 - actionElement = new Wix.RegisterClassInfo();
555 - break;
556 - case "RegisterComPlus":
557 - actionElement = new Wix.RegisterComPlus();
558 - break;
559 - case "RegisterExtensionInfo":
560 - actionElement = new Wix.RegisterExtensionInfo();
561 - break;
562 - case "RegisterFonts":
563 - actionElement = new Wix.RegisterFonts();
564 - break;
565 - case "RegisterMIMEInfo":
566 - actionElement = new Wix.RegisterMIMEInfo();
567 - break;
568 - case "RegisterProduct":
569 - actionElement = new Wix.RegisterProduct();
570 - break;
571 - case "RegisterProgIdInfo":
572 - actionElement = new Wix.RegisterProgIdInfo();
573 - break;
574 - case "RegisterTypeLibraries":
575 - actionElement = new Wix.RegisterTypeLibraries();
576 - break;
577 - case "RegisterUser":
578 - actionElement = new Wix.RegisterUser();
579 - break;
580 - case "RemoveDuplicateFiles":
581 - actionElement = new Wix.RemoveDuplicateFiles();
582 - break;
583 - case "RemoveEnvironmentStrings":
584 - actionElement = new Wix.RemoveEnvironmentStrings();
585 - break;
586 - case "RemoveExistingProducts":
587 - var removeExistingProducts = new Wix.RemoveExistingProducts();
588 - Decompiler.SequenceRelativeAction(actionSymbol, removeExistingProducts);
589 - return removeExistingProducts;
590 - case "RemoveFiles":
591 - actionElement = new Wix.RemoveFiles();
592 - break;
593 - case "RemoveFolders":
594 - actionElement = new Wix.RemoveFolders();
595 - break;
596 - case "RemoveIniValues":
597 - actionElement = new Wix.RemoveIniValues();
598 - break;
599 - case "RemoveODBC":
600 - actionElement = new Wix.RemoveODBC();
601 - break;
602 - case "RemoveRegistryValues":
603 - actionElement = new Wix.RemoveRegistryValues();
604 - break;
605 - case "RemoveShortcuts":
606 - actionElement = new Wix.RemoveShortcuts();
607 - break;
608 - case "ResolveSource":
609 - var resolveSource = new Wix.ResolveSource();
610 - Decompiler.SequenceRelativeAction(actionSymbol, resolveSource);
611 - return resolveSource;
612 - case "RMCCPSearch":
613 - var rmccpSearch = new Wix.RMCCPSearch();
614 - Decompiler.SequenceRelativeAction(actionSymbol, rmccpSearch);
615 - return rmccpSearch;
616 - case "ScheduleReboot":
617 - var scheduleReboot = new Wix.ScheduleReboot();
618 - Decompiler.SequenceRelativeAction(actionSymbol, scheduleReboot);
619 - return scheduleReboot;
620 - case "SelfRegModules":
621 - actionElement = new Wix.SelfRegModules();
622 - break;
623 - case "SelfUnregModules":
624 - actionElement = new Wix.SelfUnregModules();
625 - break;
626 - case "SetODBCFolders":
627 - actionElement = new Wix.SetODBCFolders();
628 - break;
629 - case "StartServices":
630 - actionElement = new Wix.StartServices();
631 - break;
632 - case "StopServices":
633 - actionElement = new Wix.StopServices();
634 - break;
635 - case "UnpublishComponents":
636 - actionElement = new Wix.UnpublishComponents();
637 - break;
638 - case "UnpublishFeatures":
639 - actionElement = new Wix.UnpublishFeatures();
640 - break;
641 - case "UnregisterClassInfo":
642 - actionElement = new Wix.UnregisterClassInfo();
643 - break;
644 - case "UnregisterComPlus":
645 - actionElement = new Wix.UnregisterComPlus();
646 - break;
647 - case "UnregisterExtensionInfo":
648 - actionElement = new Wix.UnregisterExtensionInfo();
649 - break;
650 - case "UnregisterFonts":
651 - actionElement = new Wix.UnregisterFonts();
652 - break;
653 - case "UnregisterMIMEInfo":
654 - actionElement = new Wix.UnregisterMIMEInfo();
655 - break;
656 - case "UnregisterProgIdInfo":
657 - actionElement = new Wix.UnregisterProgIdInfo();
658 - break;
659 - case "UnregisterTypeLibraries":
660 - actionElement = new Wix.UnregisterTypeLibraries();
661 - break;
662 - case "ValidateProductID":
663 - actionElement = new Wix.ValidateProductID();
664 - break;
665 - case "WriteEnvironmentStrings":
666 - actionElement = new Wix.WriteEnvironmentStrings();
667 - break;
668 - case "WriteIniValues":
669 - actionElement = new Wix.WriteIniValues();
670 - break;
671 - case "WriteRegistryValues":
672 - actionElement = new Wix.WriteRegistryValues();
673 - break;
674 - default:
675 - this.Messaging.Write(WarningMessages.UnknownAction(actionSymbol.SourceLineNumbers, actionSymbol.SequenceTable.ToString(), actionSymbol.Action));
676 - return null;
482 + case "AllocateRegistrySpace":
483 + case "BindImage":
484 + case "CostFinalize":
485 + case "CostInitialize":
486 + case "CreateFolders":
487 + case "CreateShortcuts":
488 + case "DeleteServices":
489 + case "DuplicateFiles":
490 + case "ExecuteAction":
491 + case "FileCost":
492 + case "InstallAdminPackage":
493 + case "InstallFiles":
494 + case "InstallFinalize":
495 + case "InstallInitialize":
496 + case "InstallODBC":
497 + case "InstallServices":
498 + case "InstallValidate":
499 + case "IsolateComponents":
500 + case "MigrateFeatureStates":
501 + case "MoveFiles":
502 + case "MsiPublishAssemblies":
503 + case "MsiUnpublishAssemblies":
504 + case "PatchFiles":
505 + case "ProcessComponents":
506 + case "PublishComponents":
507 + case "PublishFeatures":
508 + case "PublishProduct":
509 + case "RegisterClassInfo":
510 + case "RegisterComPlus":
511 + case "RegisterExtensionInfo":
512 + case "RegisterFonts":
513 + case "RegisterMIMEInfo":
514 + case "RegisterProduct":
515 + case "RegisterProgIdInfo":
516 + case "RegisterTypeLibraries":
517 + case "RegisterUser":
518 + case "RemoveDuplicateFiles":
519 + case "RemoveEnvironmentStrings":
520 + case "RemoveFiles":
521 + case "RemoveFolders":
522 + case "RemoveIniValues":
523 + case "RemoveODBC":
524 + case "RemoveRegistryValues":
525 + case "RemoveShortcuts":
526 + case "SelfRegModules":
527 + case "SelfUnregModules":
528 + case "SetODBCFolders":
529 + case "StartServices":
530 + case "StopServices":
531 + case "UnpublishComponents":
532 + case "UnpublishFeatures":
533 + case "UnregisterClassInfo":
534 + case "UnregisterComPlus":
535 + case "UnregisterExtensionInfo":
536 + case "UnregisterFonts":
537 + case "UnregisterMIMEInfo":
538 + case "UnregisterProgIdInfo":
539 + case "UnregisterTypeLibraries":
540 + case "ValidateProductID":
541 + case "WriteEnvironmentStrings":
542 + case "WriteIniValues":
543 + case "WriteRegistryValues":
544 + xStandardAction = new XElement(Names.WxsNamespace + actionSymbol.Action);
545 + break;
546 +
547 + case "AppSearch":
548 + this.StandardActions.TryGetValue(actionSymbol.Id.Id, out var appSearchActionRow);
549 +
550 + if (null != actionSymbol.Before || null != actionSymbol.After || (null != appSearchActionRow && actionSymbol.Sequence != appSearchActionRow.Sequence))
551 + {
552 + xStandardAction = new XElement(Names.AppSearchElement);
553 +
554 + SetAttributeIfNotNull(xStandardAction, "Condition", actionSymbol.Condition);
555 + SetAttributeIfNotNull(xStandardAction, "Before", actionSymbol.Before);
556 + SetAttributeIfNotNull(xStandardAction, "After", actionSymbol.After);
557 + SetAttributeIfNotNull(xStandardAction, "Sequence", actionSymbol.Sequence);
558 +
559 + return xStandardAction;
560 + }
561 + break;
562 +
563 + case "CCPSearch":
564 + case "DisableRollback":
565 + case "FindRelatedProducts":
566 + case "ForceReboot":
567 + case "InstallExecute":
568 + case "InstallExecuteAgain":
569 + case "LaunchConditions":
570 + case "RemoveExistingProducts":
571 + case "ResolveSource":
572 + case "RMCCPSearch":
573 + case "ScheduleReboot":
574 + xStandardAction = new XElement(Names.WxsNamespace + actionSymbol.Action);
575 + Decompiler.SequenceRelativeAction(actionSymbol, xStandardAction);
576 + return xStandardAction;
577 +
578 + default:
579 + this.Messaging.Write(WarningMessages.UnknownAction(actionSymbol.SourceLineNumbers, actionSymbol.SequenceTable.ToString(), actionSymbol.Action));
580 + return null;
581 }
582
679 - if (actionElement != null)
583 + if (xStandardAction != null)
584 {
681 - this.SequenceStandardAction(actionSymbol, actionElement);
585 + this.SequenceStandardAction(actionSymbol, xStandardAction);
586 }
587
684 - return actionElement;
588 + return xStandardAction;
589 }
590
591 /// <summary>
688 - /// Applies the condition and sequence to a standard action element based on the action row data.
592 + /// Applies the condition and sequence to a standard action element based on the action symbol data.
593 /// </summary>
594 /// <param name="actionSymbol">Action data from the database.</param>
691 - /// <param name="actionElement">Element to be sequenced.</param>
692 - private void SequenceStandardAction(WixActionSymbol actionSymbol, Wix.ActionSequenceType actionElement)
595 + /// <param name="xAction">Element to be sequenced.</param>
596 + private void SequenceStandardAction(WixActionSymbol actionSymbol, XElement xAction)
597 {
694 - if (null != actionSymbol.Condition)
695 - {
696 - actionElement.Content = actionSymbol.Condition;
697 - }
598 + xAction.SetAttributeValue("Condition", actionSymbol.Condition);
599
600 if ((null != actionSymbol.Before || null != actionSymbol.After) && 0 == actionSymbol.Sequence)
601 {
@@ -702,7 +603,7 @@ namespace WixToolset.Core.WindowsInstaller
603 }
604 else if (actionSymbol.Sequence.HasValue)
605 {
705 - actionElement.Sequence = actionSymbol.Sequence.Value;
606 + xAction.SetAttributeValue("Sequence", actionSymbol.Sequence.Value);
607 }
608 }
609
@@ -710,26 +611,13 @@ namespace WixToolset.Core.WindowsInstaller
611 /// Applies the condition and relative sequence to an action element based on the action row data.
612 /// </summary>
613 /// <param name="actionSymbol">Action data from the database.</param>
713 - /// <param name="actionElement">Element to be sequenced.</param>
714 - private static void SequenceRelativeAction(WixActionSymbol actionSymbol, Wix.ActionModuleSequenceType actionElement)
614 + /// <param name="xAction">Element to be sequenced.</param>
615 + private static void SequenceRelativeAction(WixActionSymbol actionSymbol, XElement xAction)
616 {
716 - if (null != actionSymbol.Condition)
717 - {
718 - actionElement.Content = actionSymbol.Condition;
719 - }
720 -
721 - if (null != actionSymbol.Before)
722 - {
723 - actionElement.Before = actionSymbol.Before;
724 - }
725 - else if (null != actionSymbol.After)
726 - {
727 - actionElement.After = actionSymbol.After;
728 - }
729 - else if (actionSymbol.Sequence.HasValue)
730 - {
731 - actionElement.Sequence = actionSymbol.Sequence.Value;
732 - }
617 + SetAttributeIfNotNull(xAction, "Condition", actionSymbol.Condition);
618 + SetAttributeIfNotNull(xAction, "Before", actionSymbol.Before);
619 + SetAttributeIfNotNull(xAction, "After", actionSymbol.After);
620 + SetAttributeIfNotNull(xAction, "Sequence", actionSymbol.Sequence);
621 }
622
623 /// <summary>
@@ -737,24 +625,19 @@ namespace WixToolset.Core.WindowsInstaller
625 /// </summary>
626 /// <param name="id">The identifier of the property.</param>
627 /// <returns>The property element.</returns>
740 - private Wix.Property EnsureProperty(string id)
628 + private XElement EnsureProperty(string id)
629 {
742 - var property = (Wix.Property)this.core.GetIndexedElement("Property", id);
630 + XElement xProperty;
631
744 - if (null == property)
632 + if (!this.TryGetIndexedElement("Property", out xProperty, id))
633 {
746 - property = new Wix.Property();
747 - property.Id = id;
634 + xProperty = new XElement(Names.PropertyElement, new XAttribute("Id", id));
635
749 - // create a dummy row for indexing
750 - var row = this.tableDefinitions["Property"].CreateRow(null);
751 - row[0] = id;
752 -
753 - this.core.RootElement.AddChild(property);
754 - this.core.IndexElement(row, property);
636 + this.RootElement.Add(xProperty);
637 + this.IndexElement(xProperty, "Property", id);
638 }
639
757 - return property;
640 + return xProperty;
641 }
642
643 /// <summary>
@@ -809,51 +692,37 @@ namespace WixToolset.Core.WindowsInstaller
692 var checkBoxTable = tables["CheckBox"];
693 var controlTable = tables["Control"];
694
812 - var checkBoxes = new Hashtable();
813 - var checkBoxProperties = new Hashtable();
814 -
815 - // index the CheckBox table
816 - if (null != checkBoxTable)
817 - {
818 - foreach (var row in checkBoxTable.Rows)
819 - {
820 - checkBoxes.Add(row.GetPrimaryKey(DecompilerConstants.PrimaryKeyDelimiter), row);
821 - checkBoxProperties.Add(row.GetPrimaryKey(DecompilerConstants.PrimaryKeyDelimiter), false);
822 - }
823 - }
695 + var checkBoxes = checkBoxTable?.Rows.ToDictionary(row => row.GetPrimaryKey(DecompilerConstants.PrimaryKeyDelimiter));
696 + var checkBoxProperties = checkBoxTable?.Rows.ToDictionary(row => row.GetPrimaryKey(DecompilerConstants.PrimaryKeyDelimiter), row => false);
697
698 // enumerate through the Control table, adding CheckBox values where appropriate
699 if (null != controlTable)
700 {
701 foreach (var row in controlTable.Rows)
702 {
830 - var control = (Wix.Control)this.core.GetIndexedElement(row);
703 + var xControl = this.GetIndexedElement(row);
704
832 - if ("CheckBox" == Convert.ToString(row[2]) && null != row[8])
705 + if ("CheckBox" == row.FieldAsString(2))
706 {
834 - var checkBoxRow = (Row)checkBoxes[row[8]];
835 -
836 - if (null == checkBoxRow)
837 - {
838 - this.Messaging.Write(WarningMessages.ExpectedForeignRow(row.SourceLineNumbers, "Control", row.GetPrimaryKey(DecompilerConstants.PrimaryKeyDelimiter), "Property", Convert.ToString(row[8]), "CheckBox"));
839 - }
840 - else
707 + var property = row.FieldAsString(8);
708 + if (!String.IsNullOrEmpty(property) && checkBoxes.TryGetValue(property, out var checkBoxRow))
709 {
710 // if we've seen this property already, create a reference to it
843 - if (Convert.ToBoolean(checkBoxProperties[row[8]]))
711 + if (checkBoxProperties.TryGetValue(property, out var seen) && seen)
712 {
845 - control.CheckBoxPropertyRef = Convert.ToString(row[8]);
713 + xControl.SetAttributeValue("CheckBoxPropertyRef", property);
714 }
715 else
716 {
849 - control.Property = Convert.ToString(row[8]);
850 - checkBoxProperties[row[8]] = true;
717 + xControl.SetAttributeValue("Property", property);
718 + checkBoxProperties[property] = true;
719 }
720
853 - if (null != checkBoxRow[1])
854 - {
855 - control.CheckBoxValue = Convert.ToString(checkBoxRow[1]);
856 - }
721 + xControl.SetAttributeValue("CheckBoxValue", checkBoxRow.FieldAsString(1));
722 + }
723 + else
724 + {
725 + this.Messaging.Write(WarningMessages.ExpectedForeignRow(row.SourceLineNumbers, "Control", row.GetPrimaryKey(DecompilerConstants.PrimaryKeyDelimiter), "Property", row.FieldAsString(8), "CheckBox"));
726 }
727 }
728 }
@@ -879,60 +748,52 @@ namespace WixToolset.Core.WindowsInstaller
748 {
749 foreach (var row in componentTable.Rows)
750 {
882 - var attributes = Convert.ToInt32(row[3]);
751 + var attributes = row.FieldAsInteger(3);
752 + var keyPath = row.FieldAsString(5);
753
884 - if (null == row[5])
754 + if (String.IsNullOrEmpty(keyPath))
755 {
886 - var component = (Wix.Component)this.core.GetIndexedElement("Component", Convert.ToString(row[0]));
887 -
888 - component.KeyPath = Wix.YesNoType.yes;
756 + var xComponent = this.GetIndexedElement("Component", row.FieldAsString(0));
757 + xComponent.SetAttributeValue("KeyPath", "yes");
758 }
759 else if (WindowsInstallerConstants.MsidbComponentAttributesRegistryKeyPath == (attributes & WindowsInstallerConstants.MsidbComponentAttributesRegistryKeyPath))
760 {
892 - object registryObject = this.core.GetIndexedElement("Registry", Convert.ToString(row[5]));
893 -
894 - if (null != registryObject)
761 + if (this.TryGetIndexedElement("Registry", out var xRegistry, keyPath))
762 {
896 - var registryValue = registryObject as Wix.RegistryValue;
897 -
898 - if (null != registryValue)
763 + if (xRegistry.Name.LocalName == "RegistryValue")
764 {
900 - registryValue.KeyPath = Wix.YesNoType.yes;
765 + xRegistry.SetAttributeValue("KeyPath", "yes");
766 }
767 else
768 {
904 - this.Messaging.Write(WarningMessages.IllegalRegistryKeyPath(row.SourceLineNumbers, "Component", Convert.ToString(row[5])));
769 + this.Messaging.Write(WarningMessages.IllegalRegistryKeyPath(row.SourceLineNumbers, "Component", keyPath));
770 }
771 }
772 else
773 {
909 - this.Messaging.Write(WarningMessages.ExpectedForeignRow(row.SourceLineNumbers, "Component", row.GetPrimaryKey(DecompilerConstants.PrimaryKeyDelimiter), "KeyPath", Convert.ToString(row[5]), "Registry"));
774 + this.Messaging.Write(WarningMessages.ExpectedForeignRow(row.SourceLineNumbers, "Component", row.GetPrimaryKey(DecompilerConstants.PrimaryKeyDelimiter), "KeyPath", keyPath, "Registry"));
775 }
776 }
777 else if (WindowsInstallerConstants.MsidbComponentAttributesODBCDataSource == (attributes & WindowsInstallerConstants.MsidbComponentAttributesODBCDataSource))
778 {
914 - var odbcDataSource = (Wix.ODBCDataSource)this.core.GetIndexedElement("ODBCDataSource", Convert.ToString(row[5]));
915 -
916 - if (null != odbcDataSource)
779 + if (this.TryGetIndexedElement("ODBCDataSource", out var xOdbcDataSource, keyPath))
780 {
918 - odbcDataSource.KeyPath = Wix.YesNoType.yes;
781 + xOdbcDataSource.SetAttributeValue("KeyPath", "yes");
782 }
783 else
784 {
922 - this.Messaging.Write(WarningMessages.ExpectedForeignRow(row.SourceLineNumbers, "Component", row.GetPrimaryKey(DecompilerConstants.PrimaryKeyDelimiter), "KeyPath", Convert.ToString(row[5]), "ODBCDataSource"));
785 + this.Messaging.Write(WarningMessages.ExpectedForeignRow(row.SourceLineNumbers, "Component", row.GetPrimaryKey(DecompilerConstants.PrimaryKeyDelimiter), "KeyPath", keyPath, "ODBCDataSource"));
786 }
787 }
788 else
789 {
927 - var file = (Wix.File)this.core.GetIndexedElement("File", Convert.ToString(row[5]));
928 -
929 - if (null != file)
790 + if (this.TryGetIndexedElement("File", out var xFile, keyPath))
791 {
931 - file.KeyPath = Wix.YesNoType.yes;
792 + xFile.SetAttributeValue("KeyPath", "yes");
793 }
794 else
795 {
935 - this.Messaging.Write(WarningMessages.ExpectedForeignRow(row.SourceLineNumbers, "Component", row.GetPrimaryKey(DecompilerConstants.PrimaryKeyDelimiter), "KeyPath", Convert.ToString(row[5]), "File"));
796 + this.Messaging.Write(WarningMessages.ExpectedForeignRow(row.SourceLineNumbers, "Component", row.GetPrimaryKey(DecompilerConstants.PrimaryKeyDelimiter), "KeyPath", keyPath, "File"));
797 }
798 }
799 }
@@ -943,12 +804,10 @@ namespace WixToolset.Core.WindowsInstaller
804 {
805 foreach (FileRow fileRow in fileTable.Rows)
806 {
946 - var component = (Wix.Component)this.core.GetIndexedElement("Component", fileRow.Component);
947 - var file = (Wix.File)this.core.GetIndexedElement(fileRow);
948 -
949 - if (null != component)
807 + if (this.TryGetIndexedElement("Component", out var xComponent, fileRow.Component)
808 + && this.TryGetIndexedElement(fileRow, out var xFile))
809 {
951 - component.AddChild(file);
810 + xComponent.Add(xFile);
811 }
812 else
813 {
@@ -962,16 +821,14 @@ namespace WixToolset.Core.WindowsInstaller
821 {
822 foreach (var row in odbcDataSourceTable.Rows)
823 {
965 - var component = (Wix.Component)this.core.GetIndexedElement("Component", Convert.ToString(row[1]));
966 - var odbcDataSource = (Wix.ODBCDataSource)this.core.GetIndexedElement(row);
967 -
968 - if (null != component)
824 + if (this.TryGetIndexedElement("Component", out var xComponent, row.FieldAsString(1))
825 + && this.TryGetIndexedElement(row, out var xOdbcDataSource))
826 {
970 - component.AddChild(odbcDataSource);
827 + xComponent.Add(xOdbcDataSource);
828 }
829 else
830 {
974 - this.Messaging.Write(WarningMessages.ExpectedForeignRow(row.SourceLineNumbers, "ODBCDataSource", row.GetPrimaryKey(DecompilerConstants.PrimaryKeyDelimiter), "Component_", Convert.ToString(row[1]), "Component"));
831 + this.Messaging.Write(WarningMessages.ExpectedForeignRow(row.SourceLineNumbers, "ODBCDataSource", row.GetPrimaryKey(DecompilerConstants.PrimaryKeyDelimiter), "Component_", row.FieldAsString(1), "Component"));
832 }
833 }
834 }
@@ -981,16 +838,14 @@ namespace WixToolset.Core.WindowsInstaller
838 {
839 foreach (var row in registryTable.Rows)
840 {
984 - var component = (Wix.Component)this.core.GetIndexedElement("Component", Convert.ToString(row[5]));
985 - var registryElement = this.core.GetIndexedElement(row);
986 -
987 - if (null != component)
841 + if (this.TryGetIndexedElement("Component", out var xComponent, row.FieldAsString(5))
842 + && this.TryGetIndexedElement(row, out var xRegistry))
843 {
989 - component.AddChild(registryElement);
844 + xComponent.Add(xRegistry);
845 }
846 else
847 {
993 - this.Messaging.Write(WarningMessages.ExpectedForeignRow(row.SourceLineNumbers, "Registry", row.GetPrimaryKey(DecompilerConstants.PrimaryKeyDelimiter), "Component_", Convert.ToString(row[5]), "Component"));
848 + this.Messaging.Write(WarningMessages.ExpectedForeignRow(row.SourceLineNumbers, "Registry", row.GetPrimaryKey(DecompilerConstants.PrimaryKeyDelimiter), "Component_", row.FieldAsString(5), "Component"));
849 }
850 }
851 }
@@ -1012,92 +867,82 @@ namespace WixToolset.Core.WindowsInstaller
867 return;
868 }
869
1015 - var controlTable = tables["Control"];
1016 - var dialogTable = tables["Dialog"];
1017 -
1018 - var addedControls = new Hashtable();
1019 - var controlRows = new Hashtable();
870 + var addedControls = new HashSet<XElement>();
871
1021 - // index the rows in the control rows (because we need the Control_Next value)
1022 - if (null != controlTable)
1023 - {
1024 - foreach (var row in controlTable.Rows)
1025 - {
1026 - controlRows.Add(row.GetPrimaryKey(DecompilerConstants.PrimaryKeyDelimiter), row);
1027 - }
1028 - }
872 + var controlTable = tables["Control"];
873 + var controlRows = controlTable?.Rows.ToDictionary(row => row.GetPrimaryKey(DecompilerConstants.PrimaryKeyDelimiter));
874
875 + var dialogTable = tables["Dialog"];
876 if (null != dialogTable)
877 {
1032 - foreach (var row in dialogTable.Rows)
878 + foreach (var dialogRow in dialogTable.Rows)
879 {
1034 - var dialog = (Wix.Dialog)this.core.GetIndexedElement(row);
1035 - var dialogId = Convert.ToString(row[0]);
880 + var xDialog = this.GetIndexedElement(dialogRow);
881 + var dialogId = dialogRow.FieldAsString(0);
882
1037 - var control = (Wix.Control)this.core.GetIndexedElement("Control", dialogId, Convert.ToString(row[7]));
1038 - if (null == control)
883 + if (!this.TryGetIndexedElement("Control", out var xControl, dialogId, dialogRow.FieldAsString(7)))
884 {
1040 - this.Messaging.Write(WarningMessages.ExpectedForeignRow(row.SourceLineNumbers, "Dialog", row.GetPrimaryKey(DecompilerConstants.PrimaryKeyDelimiter), "Dialog", dialogId, "Control_First", Convert.ToString(row[7]), "Control"));
885 + this.Messaging.Write(WarningMessages.ExpectedForeignRow(dialogRow.SourceLineNumbers, "Dialog", dialogRow.GetPrimaryKey(DecompilerConstants.PrimaryKeyDelimiter), "Dialog", dialogId, "Control_First", dialogRow.FieldAsString(7), "Control"));
886 }
887
888 // add tabbable controls
1044 - while (null != control)
889 + while (null != xControl)
890 {
1046 - var controlRow = (Row)controlRows[String.Concat(dialogId, DecompilerConstants.PrimaryKeyDelimiter, control.Id)];
891 + var controlId = xControl.Attribute("Id");
892 + var controlRow = controlRows[String.Concat(dialogId, DecompilerConstants.PrimaryKeyDelimiter, controlId)];
893
1048 - control.TabSkip = Wix.YesNoType.no;
1049 - dialog.AddChild(control);
1050 - addedControls.Add(control, null);
894 + xControl.SetAttributeValue("TabSkip", "no");
895
1052 - if (null != controlRow[10])
896 + xDialog.Add(xControl);
897 + addedControls.Add(xControl);
898 +
899 + var controlNext = controlRow.FieldAsString(10);
900 + if (!String.IsNullOrEmpty(controlNext))
901 {
1054 - control = (Wix.Control)this.core.GetIndexedElement("Control", dialogId, Convert.ToString(controlRow[10]));
1055 - if (null != control)
902 + if (this.TryGetIndexedElement("Control", out xControl, dialogId, controlNext))
903 {
904 // looped back to the first control in the dialog
1058 - if (addedControls.Contains(control))
905 + if (addedControls.Contains(xControl))
906 {
1060 - control = null;
907 + xControl = null;
908 }
909 }
910 else
911 {
1065 - this.Messaging.Write(WarningMessages.ExpectedForeignRow(controlRow.SourceLineNumbers, "Control", controlRow.GetPrimaryKey(DecompilerConstants.PrimaryKeyDelimiter), "Dialog_", dialogId, "Control_Next", Convert.ToString(controlRow[10]), "Control"));
912 + this.Messaging.Write(WarningMessages.ExpectedForeignRow(controlRow.SourceLineNumbers, "Control", controlRow.GetPrimaryKey(DecompilerConstants.PrimaryKeyDelimiter), "Dialog_", dialogId, "Control_Next", controlNext, "Control"));
913 }
914 }
915 else
916 {
1070 - control = null;
917 + xControl = null;
918 }
919 }
920
921 // set default control
1075 - if (null != row[8])
922 + var controlDefault = dialogRow.FieldAsString(8);
923 + if (!String.IsNullOrEmpty(controlDefault))
924 {
1077 - var defaultControl = (Wix.Control)this.core.GetIndexedElement("Control", dialogId, Convert.ToString(row[8]));
1078 -
1079 - if (null != defaultControl)
925 + if (this.TryGetIndexedElement("Control", out var xDefaultControl, dialogId, controlDefault))
926 {
1081 - defaultControl.Default = Wix.YesNoType.yes;
927 + xDefaultControl.SetAttributeValue("Default", "yes");
928 }
929 else
930 {
1085 - this.Messaging.Write(WarningMessages.ExpectedForeignRow(row.SourceLineNumbers, "Dialog", row.GetPrimaryKey(DecompilerConstants.PrimaryKeyDelimiter), "Dialog", dialogId, "Control_Default", Convert.ToString(row[8]), "Control"));
931 + this.Messaging.Write(WarningMessages.ExpectedForeignRow(dialogRow.SourceLineNumbers, "Dialog", dialogRow.GetPrimaryKey(DecompilerConstants.PrimaryKeyDelimiter), "Dialog", dialogId, "Control_Default", Convert.ToString(dialogRow[8]), "Control"));
932 }
933 }
934
935 // set cancel control
1090 - if (null != row[9])
936 + var controlCancel = dialogRow.FieldAsString(8);
937 + if (!String.IsNullOrEmpty(controlCancel))
938 {
1092 - var cancelControl = (Wix.Control)this.core.GetIndexedElement("Control", dialogId, Convert.ToString(row[9]));
1093 -
1094 - if (null != cancelControl)
939 + if (this.TryGetIndexedElement("Control", out var xCancelControl, dialogId, controlCancel))
940 {
1096 - cancelControl.Cancel = Wix.YesNoType.yes;
941 + xCancelControl.SetAttributeValue("Cancel", "yes");
942 }
943 else
944 {
1100 - this.Messaging.Write(WarningMessages.ExpectedForeignRow(row.SourceLineNumbers, "Dialog", row.GetPrimaryKey(DecompilerConstants.PrimaryKeyDelimiter), "Dialog", dialogId, "Control_Cancel", Convert.ToString(row[9]), "Control"));
945 + this.Messaging.Write(WarningMessages.ExpectedForeignRow(dialogRow.SourceLineNumbers, "Dialog", dialogRow.GetPrimaryKey(DecompilerConstants.PrimaryKeyDelimiter), "Dialog", dialogId, "Control_Cancel", Convert.ToString(dialogRow[9]), "Control"));
946 }
947 }
948 }
@@ -1106,21 +951,20 @@ namespace WixToolset.Core.WindowsInstaller
951 // add the non-tabbable controls to the dialog
952 if (null != controlTable)
953 {
1109 - foreach (var row in controlTable.Rows)
954 + foreach (var controlRow in controlTable.Rows)
955 {
1111 - var control = (Wix.Control)this.core.GetIndexedElement(row);
1112 - var dialog = (Wix.Dialog)this.core.GetIndexedElement("Dialog", Convert.ToString(row[0]));
1113 -
1114 - if (null == dialog)
956 + var dialogId = controlRow.FieldAsString(0);
957 + if (!this.TryGetIndexedElement("Dialog", out var xDialog, dialogId))
958 {
1116 - this.Messaging.Write(WarningMessages.ExpectedForeignRow(row.SourceLineNumbers, "Control", row.GetPrimaryKey(DecompilerConstants.PrimaryKeyDelimiter), "Dialog_", Convert.ToString(row[0]), "Dialog"));
959 + this.Messaging.Write(WarningMessages.ExpectedForeignRow(controlRow.SourceLineNumbers, "Control", controlRow.GetPrimaryKey(DecompilerConstants.PrimaryKeyDelimiter), "Dialog_", dialogId, "Dialog"));
960 continue;
961 }
962
1120 - if (!addedControls.Contains(control))
963 + var xControl = this.GetIndexedElement(controlRow);
964 + if (!addedControls.Contains(xControl))
965 {
1122 - control.TabSkip = Wix.YesNoType.yes;
1123 - dialog.AddChild(control);
966 + xControl.SetAttributeValue("TabSkip", "yes");
967 + xDialog.Add(xControl);
968 }
969 }
970 }
@@ -1137,53 +981,53 @@ namespace WixToolset.Core.WindowsInstaller
981 private void FinalizeDuplicateMoveFileTables(TableIndexedCollection tables)
982 {
983 var duplicateFileTable = tables["DuplicateFile"];
1140 - var moveFileTable = tables["MoveFile"];
1141 -
984 if (null != duplicateFileTable)
985 {
986 foreach (var row in duplicateFileTable.Rows)
987 {
1146 - var copyFile = (Wix.CopyFile)this.core.GetIndexedElement(row);
1147 -
1148 - if (null != row[4])
988 + var xCopyFile = this.GetIndexedElement(row);
989 + var destination = row.FieldAsString(4);
990 + if (!String.IsNullOrEmpty(destination))
991 {
1150 - if (null != this.core.GetIndexedElement("Directory", Convert.ToString(row[4])))
992 + if (this.TryGetIndexedElement("Directory", out var _, destination))
993 {
1152 - copyFile.DestinationDirectory = Convert.ToString(row[4]);
994 + xCopyFile.SetAttributeValue("DestinationDirectory", destination);
995 }
996 else
997 {
1156 - copyFile.DestinationProperty = Convert.ToString(row[4]);
998 + xCopyFile.SetAttributeValue("DestinationProperty", destination);
999 }
1000 }
1001 }
1002 }
1003
1004 + var moveFileTable = tables["MoveFile"];
1005 if (null != moveFileTable)
1006 {
1007 foreach (var row in moveFileTable.Rows)
1008 {
1166 - var copyFile = (Wix.CopyFile)this.core.GetIndexedElement(row);
1167 -
1168 - if (null != row[4])
1009 + var xCopyFile = this.GetIndexedElement(row);
1010 + var source = row.FieldAsString(4);
1011 + if (!String.IsNullOrEmpty(source))
1012 {
1170 - if (null != this.core.GetIndexedElement("Directory", Convert.ToString(row[4])))
1013 + if (this.TryGetIndexedElement("Directory", out var _, source))
1014 {
1172 - copyFile.SourceDirectory = Convert.ToString(row[4]);
1015 + xCopyFile.SetAttributeValue("SourceDirectory", source);
1016 }
1017 else
1018 {
1176 - copyFile.SourceProperty = Convert.ToString(row[4]);
1019 + xCopyFile.SetAttributeValue("SourceProperty", source);
1020 }
1021 }
1022
1180 - if (null != this.core.GetIndexedElement("Directory", Convert.ToString(row[5])))
1023 + var destination = row.FieldAsString(5);
1024 + if (this.TryGetIndexedElement("Directory", out var _, destination))
1025 {
1182 - copyFile.DestinationDirectory = Convert.ToString(row[5]);
1026 + xCopyFile.SetAttributeValue("DestinationDirectory", destination);
1027 }
1028 else
1029 {
1186 - copyFile.DestinationProperty = Convert.ToString(row[5]);
1030 + xCopyFile.SetAttributeValue("DestinationProperty", destination);
1031 }
1032 }
1033 }
@@ -1195,22 +1039,17 @@ namespace WixToolset.Core.WindowsInstaller
1039 /// <param name="tables">The collection of all tables.</param>
1040 private void FinalizeFamilyFileRangesTable(TableIndexedCollection tables)
1041 {
1198 - var externalFilesTable = tables["ExternalFiles"];
1042 var familyFileRangesTable = tables["FamilyFileRanges"];
1200 - var targetFiles_OptionalDataTable = tables["TargetFiles_OptionalData"];
1201 -
1202 - var usedProtectRanges = new Hashtable();
1203 -
1043 if (null != familyFileRangesTable)
1044 {
1045 foreach (var row in familyFileRangesTable.Rows)
1046 {
1208 - var protectRange = new Wix.ProtectRange();
1047 + var xProtectRange = new XElement(Names.ProtectRangeElement);
1048
1210 - if (null != row[2] && null != row[3])
1049 + if (!row.IsColumnNull(2) && !row.IsColumnNull(3))
1050 {
1212 - var retainOffsets = (Convert.ToString(row[2])).Split(',');
1213 - var retainLengths = (Convert.ToString(row[3])).Split(',');
1051 + var retainOffsets = row.FieldAsString(2).Split(',');
1052 + var retainLengths = row.FieldAsString(3).Split(',');
1053
1054 if (retainOffsets.Length == retainLengths.Length)
1055 {
@@ -1218,20 +1057,20 @@ namespace WixToolset.Core.WindowsInstaller
1057 {
1058 if (retainOffsets[i].StartsWith("0x", StringComparison.Ordinal))
1059 {
1221 - protectRange.Offset = Convert.ToInt32(retainOffsets[i].Substring(2), 16);
1060 + xProtectRange.SetAttributeValue("Offset", Convert.ToInt32(retainOffsets[i].Substring(2), 16));
1061 }
1062 else
1063 {
1225 - protectRange.Offset = Convert.ToInt32(retainOffsets[i], CultureInfo.InvariantCulture);
1064 + xProtectRange.SetAttributeValue("Offset", Convert.ToInt32(retainOffsets[i], CultureInfo.InvariantCulture));
1065 }
1066
1067 if (retainLengths[i].StartsWith("0x", StringComparison.Ordinal))
1068 {
1230 - protectRange.Length = Convert.ToInt32(retainLengths[i].Substring(2), 16);
1069 + xProtectRange.SetAttributeValue("Length", Convert.ToInt32(retainLengths[i].Substring(2), 16));
1070 }
1071 else
1072 {
1234 - protectRange.Length = Convert.ToInt32(retainLengths[i], CultureInfo.InvariantCulture);
1073 + xProtectRange.SetAttributeValue("Length", Convert.ToInt32(retainLengths[i], CultureInfo.InvariantCulture));
1074 }
1075 }
1076 }
@@ -1240,79 +1079,59 @@ namespace WixToolset.Core.WindowsInstaller
1079 // TODO: warn
1080 }
1081 }
1243 - else if (null != row[2] || null != row[3])
1082 + else if (!row.IsColumnNull(2) || !row.IsColumnNull(3))
1083 {
1084 // TODO: warn about mismatch between columns
1085 }
1086
1248 - this.core.IndexElement(row, protectRange);
1087 + this.IndexElement(row, xProtectRange);
1088 }
1089 }
1090
1091 + var usedProtectRanges = new HashSet<XElement>();
1092 + var externalFilesTable = tables["ExternalFiles"];
1093 if (null != externalFilesTable)
1094 {
1095 foreach (var row in externalFilesTable.Rows)
1096 {
1256 - var externalFile = (Wix.ExternalFile)this.core.GetIndexedElement(row);
1257 -
1258 - var protectRange = (Wix.ProtectRange)this.core.GetIndexedElement("FamilyFileRanges", Convert.ToString(row[0]), Convert.ToString(row[1]));
1259 - if (null != protectRange)
1097 + if (this.TryGetIndexedElement(row, out var xExternalFile)
1098 + && this.TryGetIndexedElement("FamilyFileRanges", out var xProtectRange, row.FieldAsString(0), row.FieldAsString(0)))
1099 {
1261 - externalFile.AddChild(protectRange);
1262 - usedProtectRanges[protectRange] = null;
1100 + xExternalFile.Add(xProtectRange);
1101 + usedProtectRanges.Add(xProtectRange);
1102 }
1103 }
1104 }
1105
1106 + var targetFiles_OptionalDataTable = tables["TargetFiles_OptionalData"];
1107 if (null != targetFiles_OptionalDataTable)
1108 {
1109 var targetImagesTable = tables["TargetImages"];
1270 - var upgradedImagesTable = tables["UpgradedImages"];
1271 -
1272 - var targetImageRows = new Hashtable();
1273 - var upgradedImagesRows = new Hashtable();
1274 -
1275 - // index the TargetImages table
1276 - if (null != targetImagesTable)
1277 - {
1278 - foreach (var row in targetImagesTable.Rows)
1279 - {
1280 - targetImageRows.Add(row[0], row);
1281 - }
1282 - }
1110 + var targetImageRows = targetImagesTable?.Rows.ToDictionary(row => row.FieldAsString(0));
1111
1284 - // index the UpgradedImages table
1285 - if (null != upgradedImagesTable)
1286 - {
1287 - foreach (var row in upgradedImagesTable.Rows)
1288 - {
1289 - upgradedImagesRows.Add(row[0], row);
1290 - }
1291 - }
1112 + var upgradedImagesTable = tables["UpgradedImages"];
1113 + var upgradedImagesRows = upgradedImagesTable?.Rows.ToDictionary(row => row.FieldAsString(0));
1114
1115 foreach (var row in targetFiles_OptionalDataTable.Rows)
1116 {
1295 - var targetFile = (Wix.TargetFile)this.patchTargetFiles[row.GetPrimaryKey(DecompilerConstants.PrimaryKeyDelimiter)];
1117 + var xTargetFile = this.PatchTargetFiles[row.GetPrimaryKey(DecompilerConstants.PrimaryKeyDelimiter)];
1118
1297 - var targetImageRow = (Row)targetImageRows[row[0]];
1298 - if (null == targetImageRow)
1119 + if (!targetImageRows.TryGetValue(row.FieldAsString(0), out var targetImageRow))
1120 {
1300 - this.Messaging.Write(WarningMessages.ExpectedForeignRow(row.SourceLineNumbers, targetFiles_OptionalDataTable.Name, row.GetPrimaryKey(DecompilerConstants.PrimaryKeyDelimiter), "Target", Convert.ToString(row[0]), "TargetImages"));
1121 + this.Messaging.Write(WarningMessages.ExpectedForeignRow(row.SourceLineNumbers, targetFiles_OptionalDataTable.Name, row.GetPrimaryKey(DecompilerConstants.PrimaryKeyDelimiter), "Target", row.FieldAsString(0), "TargetImages"));
1122 continue;
1123 }
1124
1304 - var upgradedImagesRow = (Row)upgradedImagesRows[targetImageRow[3]];
1305 - if (null == upgradedImagesRow)
1125 + if (!upgradedImagesRows.TryGetValue(row.FieldAsString(3), out var upgradedImagesRow))
1126 {
1307 - this.Messaging.Write(WarningMessages.ExpectedForeignRow(targetImageRow.SourceLineNumbers, targetImageRow.Table.Name, targetImageRow.GetPrimaryKey(DecompilerConstants.PrimaryKeyDelimiter), "Upgraded", Convert.ToString(row[3]), "UpgradedImages"));
1127 + this.Messaging.Write(WarningMessages.ExpectedForeignRow(targetImageRow.SourceLineNumbers, targetImageRow.Table.Name, targetImageRow.GetPrimaryKey(DecompilerConstants.PrimaryKeyDelimiter), "Upgraded", row.FieldAsString(3), "UpgradedImages"));
1128 continue;
1129 }
1130
1311 - var protectRange = (Wix.ProtectRange)this.core.GetIndexedElement("FamilyFileRanges", Convert.ToString(upgradedImagesRow[4]), Convert.ToString(row[1]));
1312 - if (null != protectRange)
1131 + if (this.TryGetIndexedElement("FamilyFileRanges", out var xProtectRange, upgradedImagesRow.FieldAsString(4), row.FieldAsString(1)))
1132 {
1314 - targetFile.AddChild(protectRange);
1315 - usedProtectRanges[protectRange] = null;
1133 + xTargetFile.Add(xProtectRange);
1134 + usedProtectRanges.Add(xProtectRange);
1135 }
1136 }
1137 }
@@ -1321,25 +1140,14 @@ namespace WixToolset.Core.WindowsInstaller
1140 {
1141 foreach (var row in familyFileRangesTable.Rows)
1142 {
1324 - var protectRange = (Wix.ProtectRange)this.core.GetIndexedElement(row);
1143 + var xProtectRange = this.GetIndexedElement(row);
1144
1326 - if (!usedProtectRanges.Contains(protectRange))
1145 + if (!usedProtectRanges.Contains(xProtectRange))
1146 {
1328 - var protectFile = new Wix.ProtectFile();
1329 -
1330 - protectFile.File = Convert.ToString(row[1]);
1147 + var xProtectFile = new XElement(Names.ProtectFileElement, new XAttribute("File", row.FieldAsString(1)));
1148 + xProtectFile.Add(xProtectRange);
1149
1332 - protectFile.AddChild(protectRange);
1333 -
1334 - var family = (Wix.Family)this.core.GetIndexedElement("ImageFamilies", Convert.ToString(row[0]));
1335 - if (null != family)
1336 - {
1337 - family.AddChild(protectFile);
1338 - }
1339 - else
1340 - {
1341 - this.Messaging.Write(WarningMessages.ExpectedForeignRow(row.SourceLineNumbers, familyFileRangesTable.Name, row.GetPrimaryKey(DecompilerConstants.PrimaryKeyDelimiter), "Family", Convert.ToString(row[0]), "ImageFamilies"));
1342 - }
1150 + this.AddChildToParent("ImageFamilies", xProtectFile, row, 0);
1151 }
1152 }
1153 }
@@ -1357,11 +1165,6 @@ namespace WixToolset.Core.WindowsInstaller
1165 private void FinalizeFeatureComponentsTable(TableIndexedCollection tables)
1166 {
1167 var classTable = tables["Class"];
1360 - var extensionTable = tables["Extension"];
1361 - var msiAssemblyTable = tables["MsiAssembly"];
1362 - var publishComponentTable = tables["PublishComponent"];
1363 - var typeLibTable = tables["TypeLib"];
1364 -
1168 if (null != classTable)
1169 {
1170 foreach (var row in classTable.Rows)
@@ -1370,6 +1173,7 @@ namespace WixToolset.Core.WindowsInstaller
1173 }
1174 }
1175
1176 + var extensionTable = tables["Extension"];
1177 if (null != extensionTable)
1178 {
1179 foreach (var row in extensionTable.Rows)
@@ -1378,6 +1182,7 @@ namespace WixToolset.Core.WindowsInstaller
1182 }
1183 }
1184
1185 + var msiAssemblyTable = tables["MsiAssembly"];
1186 if (null != msiAssemblyTable)
1187 {
1188 foreach (var row in msiAssemblyTable.Rows)
@@ -1386,6 +1191,7 @@ namespace WixToolset.Core.WindowsInstaller
1191 }
1192 }
1193
1194 + var publishComponentTable = tables["PublishComponent"];
1195 if (null != publishComponentTable)
1196 {
1197 foreach (var row in publishComponentTable.Rows)
@@ -1394,6 +1200,7 @@ namespace WixToolset.Core.WindowsInstaller
1200 }
1201 }
1202
1203 + var typeLibTable = tables["TypeLib"];
1204 if (null != typeLibTable)
1205 {
1206 foreach (var row in typeLibTable.Rows)
@@ -1412,132 +1219,89 @@ namespace WixToolset.Core.WindowsInstaller
1219 /// </remarks>
1220 private void FinalizeFileTable(TableIndexedCollection tables)
1221 {
1415 - var fileTable = tables["File"];
1416 - var mediaTable = tables["Media"];
1417 - var msiAssemblyTable = tables["MsiAssembly"];
1418 - var typeLibTable = tables["TypeLib"];
1419 -
1222 // index the media table by media id
1421 - RowDictionary<MediaRow> mediaRows;
1422 - if (null != mediaTable)
1423 - {
1424 - mediaRows = new RowDictionary<MediaRow>(mediaTable);
1425 - }
1223 + var mediaTable = tables["Media"];
1224 + var mediaRows = new RowDictionary<MediaRow>(mediaTable);
1225
1226 // set the disk identifiers and sources for files
1428 - if (null != fileTable)
1227 + foreach (var fileRow in tables["File"]?.Rows.Cast<FileRow>() ?? Enumerable.Empty<FileRow>())
1228 {
1430 - foreach (FileRow fileRow in fileTable.Rows)
1431 - {
1432 - var file = (Wix.File)this.core.GetIndexedElement("File", fileRow.File);
1229 + var xFile = this.GetIndexedElement("File", fileRow.File);
1230
1434 - // Don't bother processing files that are orphaned (and won't show up in the output anyway)
1435 - if (null != file.ParentElement)
1231 + // Don't bother processing files that are orphaned (and won't show up in the output anyway)
1232 + if (null != xFile.Parent)
1233 + {
1234 + // set the diskid
1235 + if (null != mediaTable)
1236 {
1437 - // set the diskid
1438 - if (null != mediaTable)
1237 + foreach (MediaRow mediaRow in mediaTable.Rows)
1238 {
1440 - foreach (MediaRow mediaRow in mediaTable.Rows)
1239 + if (fileRow.Sequence <= mediaRow.LastSequence && mediaRow.DiskId != 1)
1240 {
1442 - if (fileRow.Sequence <= mediaRow.LastSequence && mediaRow.DiskId != 1)
1443 - {
1444 - file.DiskId = Convert.ToString(mediaRow.DiskId);
1445 - break;
1446 - }
1241 + xFile.SetAttributeValue("DiskId", mediaRow.DiskId);
1242 + break;
1243 }
1244 }
1245 + }
1246
1450 - // set the source (done here because it requires information from the Directory table)
1451 - if (OutputType.Module == this.OutputType)
1452 - {
1453 - file.Source = String.Concat(this.BaseSourcePath, Path.DirectorySeparatorChar, "File", Path.DirectorySeparatorChar, file.Id, '.', this.modularizationGuid.Substring(1, 36).Replace('-', '_'));
1454 - }
1455 - else if (Wix.YesNoDefaultType.yes == file.Compressed || (Wix.YesNoDefaultType.no != file.Compressed && this.compressed) || (OutputType.Product == this.OutputType && this.TreatProductAsModule))
1247 + var fileId = xFile?.Attribute("Id")?.Value;
1248 + var fileCompressed = xFile?.Attribute("Compressed")?.Value;
1249 + var fileShortName = xFile?.Attribute("ShortName")?.Value;
1250 + var fileName = xFile?.Attribute("Name")?.Value;
1251 +
1252 + // set the source (done here because it requires information from the Directory table)
1253 + if (OutputType.Module == this.OutputType)
1254 + {
1255 + xFile.SetAttributeValue("Source", String.Concat(this.BaseSourcePath, Path.DirectorySeparatorChar, "File", Path.DirectorySeparatorChar, fileId, '.', this.ModularizationGuid.Substring(1, 36).Replace('-', '_')));
1256 + }
1257 + else if (fileCompressed == "yes" || (fileCompressed != "no" && this.Compressed) || (OutputType.Product == this.OutputType && this.TreatProductAsModule))
1258 + {
1259 + xFile.SetAttributeValue("Source", String.Concat(this.BaseSourcePath, Path.DirectorySeparatorChar, "File", Path.DirectorySeparatorChar, fileId));
1260 + }
1261 + else // uncompressed
1262 + {
1263 + var name = (!this.ShortNames && !String.IsNullOrEmpty(fileName)) ? fileName : fileShortName ?? fileName;
1264 +
1265 + if (this.Compressed) // uncompressed at the root of the source image
1266 {
1457 - file.Source = String.Concat(this.BaseSourcePath, Path.DirectorySeparatorChar, "File", Path.DirectorySeparatorChar, file.Id);
1267 + xFile.SetAttributeValue("Source", String.Concat("SourceDir", Path.DirectorySeparatorChar, name));
1268 }
1459 - else // uncompressed
1269 + else
1270 {
1461 - var fileName = (null != file.ShortName ? file.ShortName : file.Name);
1462 -
1463 - if (!this.shortNames && null != file.Name)
1464 - {
1465 - fileName = file.Name;
1466 - }
1467 -
1468 - if (this.compressed) // uncompressed at the root of the source image
1469 - {
1470 - file.Source = String.Concat("SourceDir", Path.DirectorySeparatorChar, fileName);
1471 - }
1472 - else
1473 - {
1474 - var sourcePath = this.GetSourcePath(file);
1475 -
1476 - file.Source = Path.Combine(sourcePath, fileName);
1477 - }
1271 + var sourcePath = this.GetSourcePath(xFile);
1272 + xFile.SetAttributeValue("Source", Path.Combine(sourcePath, name));
1273 }
1274 }
1275 }
1276 }
1277
1278 // set the file assemblies and manifests
1484 - if (null != msiAssemblyTable)
1279 + foreach (var row in tables["MsiAssembly"]?.Rows ?? Enumerable.Empty<Row>())
1280 {
1486 - foreach (var row in msiAssemblyTable.Rows)
1281 + if (this.TryGetIndexedElement("Component", out var xComponent, row.FieldAsString(0)))
1282 {
1488 - var component = (Wix.Component)this.core.GetIndexedElement("Component", Convert.ToString(row[0]));
1489 -
1490 - if (null == component)
1491 - {
1492 - this.Messaging.Write(WarningMessages.ExpectedForeignRow(row.SourceLineNumbers, "MsiAssembly", row.GetPrimaryKey(DecompilerConstants.PrimaryKeyDelimiter), "Component_", Convert.ToString(row[0]), "Component"));
1493 - }
1494 - else
1283 + foreach (var xFile in xComponent.Elements(Names.FileElement).Where(x => x.Attribute("KeyPath")?.Value == "yes"))
1284 {
1496 - foreach (Wix.ISchemaElement element in component.Children)
1497 - {
1498 - var file = element as Wix.File;
1499 -
1500 - if (null != file && Wix.YesNoType.yes == file.KeyPath)
1501 - {
1502 - if (null != row[2])
1503 - {
1504 - file.AssemblyManifest = Convert.ToString(row[2]);
1505 - }
1506 -
1507 - if (null != row[3])
1508 - {
1509 - file.AssemblyApplication = Convert.ToString(row[3]);
1510 - }
1511 -
1512 - if (null == row[4] || 0 == Convert.ToInt32(row[4]))
1513 - {
1514 - file.Assembly = Wix.File.AssemblyType.net;
1515 - }
1516 - else
1517 - {
1518 - file.Assembly = Wix.File.AssemblyType.win32;
1519 - }
1520 - }
1521 - }
1285 + xFile.SetAttributeValue("AssemblyManifest", row.FieldAsString(2));
1286 + xFile.SetAttributeValue("AssemblyApplication", row.FieldAsString(3));
1287 + xFile.SetAttributeValue("Assembly", row.FieldAsInteger(4) == 0 ? ".net" : "win32");
1288 }
1289 }
1290 + else
1291 + {
1292 + this.Messaging.Write(WarningMessages.ExpectedForeignRow(row.SourceLineNumbers, "MsiAssembly", row.GetPrimaryKey(DecompilerConstants.PrimaryKeyDelimiter), "Component_", row.FieldAsString(0), "Component"));
1293 + }
1294 }
1295
1296 // nest the TypeLib elements
1527 - if (null != typeLibTable)
1297 + foreach (var row in tables["TypeLib"]?.Rows ?? Enumerable.Empty<Row>())
1298 {
1529 - foreach (var row in typeLibTable.Rows)
1530 - {
1531 - var component = (Wix.Component)this.core.GetIndexedElement("Component", Convert.ToString(row[2]));
1532 - var typeLib = (Wix.TypeLib)this.core.GetIndexedElement(row);
1299 + var xComponent = this.GetIndexedElement("Component", row.FieldAsString(2));
1300 + var xTypeLib = this.GetIndexedElement(row);
1301
1534 - foreach (Wix.ISchemaElement element in component.Children)
1535 - {
1536 - if (element is Wix.File file && Wix.YesNoType.yes == file.KeyPath)
1537 - {
1538 - file.AddChild(typeLib);
1539 - }
1540 - }
1302 + foreach (var xFile in xComponent.Elements(Names.FileElement).Where(x => x.Attribute("KeyPath")?.Value == "yes"))
1303 + {
1304 + xFile.Add(xTypeLib);
1305 }
1306 }
1307 }
@@ -1553,61 +1317,41 @@ namespace WixToolset.Core.WindowsInstaller
1317 /// </remarks>
1318 private void FinalizeMIMETable(TableIndexedCollection tables)
1319 {
1556 - var extensionTable = tables["Extension"];
1557 - var mimeTable = tables["MIME"];
1558 -
1559 - var comExtensions = new Hashtable();
1560 -
1561 - if (null != extensionTable)
1320 + var extensionRows = tables["Extension"]?.Rows ?? Enumerable.Empty<Row>();
1321 + foreach (var row in extensionRows)
1322 {
1563 - foreach (var row in extensionTable.Rows)
1323 + // set the default MIME element for this extension
1324 + var mimeRef = row.FieldAsString(3);
1325 + if (null != mimeRef)
1326 {
1565 - var extension = (Wix.Extension)this.core.GetIndexedElement(row);
1566 -
1567 - // index the extension
1568 - if (!comExtensions.Contains(row[0]))
1327 + if (this.TryGetIndexedElement("MIME", out var xMime, mimeRef))
1328 {
1570 - comExtensions.Add(row[0], new ArrayList());
1329 + xMime.SetAttributeValue("Default", "yes");
1330 }
1572 - ((ArrayList)comExtensions[row[0]]).Add(extension);
1573 -
1574 - // set the default MIME element for this extension
1575 - if (null != row[3])
1331 + else
1332 {
1577 - var mime = (Wix.MIME)this.core.GetIndexedElement("MIME", Convert.ToString(row[3]));
1578 -
1579 - if (null != mime)
1580 - {
1581 - mime.Default = Wix.YesNoType.yes;
1582 - }
1583 - else
1584 - {
1585 - this.Messaging.Write(WarningMessages.ExpectedForeignRow(row.SourceLineNumbers, "Extension", row.GetPrimaryKey(DecompilerConstants.PrimaryKeyDelimiter), "MIME_", Convert.ToString(row[3]), "MIME"));
1586 - }
1333 + this.Messaging.Write(WarningMessages.ExpectedForeignRow(row.SourceLineNumbers, "Extension", row.GetPrimaryKey(DecompilerConstants.PrimaryKeyDelimiter), "MIME_", row.FieldAsString(3), "MIME"));
1334 }
1335 }
1336 }
1337
1591 - if (null != mimeTable)
1592 - {
1593 - foreach (var row in mimeTable.Rows)
1594 - {
1595 - var mime = (Wix.MIME)this.core.GetIndexedElement(row);
1338 + var extensionsByExtensionId = this.IndexTableOneToMany(extensionRows);
1339
1597 - if (comExtensions.Contains(row[1]))
1598 - {
1599 - var extensionElements = (ArrayList)comExtensions[row[1]];
1340 + foreach (var row in tables["MIME"]?.Rows ?? Enumerable.Empty<Row>())
1341 + {
1342 + var xMime = this.GetIndexedElement(row);
1343
1601 - foreach (Wix.Extension extension in extensionElements)
1602 - {
1603 - extension.AddChild(mime);
1604 - }
1605 - }
1606 - else
1344 + if (extensionsByExtensionId.TryGetValue(row.FieldAsString(1), out var xExtensions))
1345 + {
1346 + foreach (var extension in xExtensions)
1347 {
1608 - this.Messaging.Write(WarningMessages.ExpectedForeignRow(row.SourceLineNumbers, "MIME", row.GetPrimaryKey(DecompilerConstants.PrimaryKeyDelimiter), "Extension_", Convert.ToString(row[1]), "Extension"));
1348 + extension.Add(xMime);
1349 }
1350 }
1351 + else
1352 + {
1353 + this.Messaging.Write(WarningMessages.ExpectedForeignRow(row.SourceLineNumbers, "MIME", row.GetPrimaryKey(DecompilerConstants.PrimaryKeyDelimiter), "Extension_", row.FieldAsString(1), "Extension"));
1354 + }
1355 }
1356 }
1357
@@ -1623,125 +1367,80 @@ namespace WixToolset.Core.WindowsInstaller
1367 /// </remarks>
1368 private void FinalizeProgIdTable(TableIndexedCollection tables)
1369 {
1626 - var classTable = tables["Class"];
1627 - var progIdTable = tables["ProgId"];
1628 - var extensionTable = tables["Extension"];
1629 - var componentTable = tables["Component"];
1370 + // add the default ProgIds for each class (and index the class table)
1371 + var classRows = tables["Class"]?.Rows?.Where(row => row.FieldAsString(3) != null) ?? Enumerable.Empty<Row>();
1372
1631 - var addedProgIds = new Hashtable();
1632 - var classes = new Hashtable();
1633 - var components = new Hashtable();
1373 + var classesByCLSID = this.IndexTableOneToMany(classRows);
1374
1635 - // add the default ProgIds for each class (and index the class table)
1636 - if (null != classTable)
1375 + var addedProgIds = new Dictionary<XElement, string>();
1376 +
1377 + foreach (var row in classRows)
1378 {
1638 - foreach (var row in classTable.Rows)
1639 - {
1640 - var wixClass = (Wix.Class)this.core.GetIndexedElement(row);
1379 + var clsid = row.FieldAsString(0);
1380 + var xClass = this.GetIndexedElement(row);
1381
1642 - if (null != row[3])
1382 + if (this.TryGetIndexedElement("ProgId", out var xProgId, row.FieldAsString(3)))
1383 + {
1384 + if (addedProgIds.TryGetValue(xProgId, out var progid))
1385 {
1644 - var progId = (Wix.ProgId)this.core.GetIndexedElement("ProgId", Convert.ToString(row[3]));
1645 -
1646 - if (null != progId)
1647 - {
1648 - if (addedProgIds.Contains(progId))
1649 - {
1650 - this.Messaging.Write(WarningMessages.TooManyProgIds(row.SourceLineNumbers, Convert.ToString(row[0]), Convert.ToString(row[3]), Convert.ToString(addedProgIds[progId])));
1651 - }
1652 - else
1653 - {
1654 - wixClass.AddChild(progId);
1655 - addedProgIds.Add(progId, wixClass.Id);
1656 - }
1657 - }
1658 - else
1659 - {
1660 - this.Messaging.Write(WarningMessages.ExpectedForeignRow(row.SourceLineNumbers, "Class", row.GetPrimaryKey(DecompilerConstants.PrimaryKeyDelimiter), "ProgId_Default", Convert.ToString(row[3]), "ProgId"));
1661 - }
1386 + this.Messaging.Write(WarningMessages.TooManyProgIds(row.SourceLineNumbers, row.FieldAsString(0), row.FieldAsString(3), progid));
1387 }
1663 -
1664 - // index the Class elements for nesting of ProgId elements (which don't use the full Class primary key)
1665 - if (!classes.Contains(wixClass.Id))
1388 + else
1389 {
1667 - classes.Add(wixClass.Id, new ArrayList());
1390 + xClass.Add(xProgId);
1391 + addedProgIds.Add(xProgId, clsid);
1392 }
1669 - ((ArrayList)classes[wixClass.Id]).Add(wixClass);
1393 + }
1394 + else
1395 + {
1396 + this.Messaging.Write(WarningMessages.ExpectedForeignRow(row.SourceLineNumbers, "Class", row.GetPrimaryKey(DecompilerConstants.PrimaryKeyDelimiter), "ProgId_Default", row.FieldAsString(3), "ProgId"));
1397 }
1398 }
1399
1400 // add the remaining non-default ProgId entries for each class
1674 - if (null != progIdTable)
1401 + foreach (var row in tables["ProgId"]?.Rows ?? Enumerable.Empty<Row>())
1402 {
1676 - foreach (var row in progIdTable.Rows)
1677 - {
1678 - var progId = (Wix.ProgId)this.core.GetIndexedElement(row);
1403 + var clsid = row.FieldAsString(2);
1404 + var xProgId = this.GetIndexedElement(row);
1405
1680 - if (!addedProgIds.Contains(progId) && null != row[2] && null == progId.ParentElement)
1406 + if (!addedProgIds.ContainsKey(xProgId) && null != clsid && null == xProgId.Parent)
1407 + {
1408 + if (classesByCLSID.TryGetValue(clsid, out var xClasses))
1409 {
1682 - var classElements = (ArrayList)classes[row[2]];
1683 -
1684 - if (null != classElements)
1685 - {
1686 - foreach (Wix.Class wixClass in classElements)
1687 - {
1688 - wixClass.AddChild(progId);
1689 - addedProgIds.Add(progId, wixClass.Id);
1690 - }
1691 - }
1692 - else
1410 + foreach (var xClass in xClasses)
1411 {
1694 - this.Messaging.Write(WarningMessages.ExpectedForeignRow(row.SourceLineNumbers, "ProgId", row.GetPrimaryKey(DecompilerConstants.PrimaryKeyDelimiter), "Class_", Convert.ToString(row[2]), "Class"));
1412 + xClass.Add(xProgId);
1413 + addedProgIds.Add(xProgId, clsid);
1414 }
1415 }
1697 - }
1698 - }
1699 -
1700 - if (null != componentTable)
1701 - {
1702 - foreach (var row in componentTable.Rows)
1703 - {
1704 - var wixComponent = (Wix.Component)this.core.GetIndexedElement(row);
1705 -
1706 - // index the Class elements for nesting of ProgId elements (which don't use the full Class primary key)
1707 - if (!components.Contains(wixComponent.Id))
1416 + else
1417 {
1709 - components.Add(wixComponent.Id, new ArrayList());
1418 + this.Messaging.Write(WarningMessages.ExpectedForeignRow(row.SourceLineNumbers, "ProgId", row.GetPrimaryKey(DecompilerConstants.PrimaryKeyDelimiter), "Class_", row.FieldAsString(2), "Class"));
1419 }
1711 - ((ArrayList)components[wixComponent.Id]).Add(wixComponent);
1420 }
1421 }
1422
1423 // Check for any progIds that are not hooked up to a class and hook them up to the component specified by the extension
1716 - if (null != extensionTable)
1717 - {
1718 - foreach (var row in extensionTable.Rows)
1719 - {
1720 - // ignore the extension if it isn't associated with a progId
1721 - if (null == row[2])
1722 - {
1723 - continue;
1724 - }
1424 + var componentsById = this.IndexTableOneToMany(tables, "Component");
1425
1726 - var progId = (Wix.ProgId)this.core.GetIndexedElement("ProgId", Convert.ToString(row[2]));
1426 + foreach (var row in tables["Extension"]?.Rows?.Where(row => row.FieldAsString(2) != null) ?? Enumerable.Empty<Row>())
1427 + {
1428 + var xProgId = this.GetIndexedElement("ProgId", row.FieldAsString(2));
1429
1728 - // Haven't added the progId yet and it doesn't have a parent progId
1729 - if (!addedProgIds.Contains(progId) && null == progId.ParentElement)
1430 + // Haven't added the progId yet and it doesn't have a parent progId
1431 + if (!addedProgIds.ContainsKey(xProgId) && null == xProgId.Parent)
1432 + {
1433 + if (componentsById.TryGetValue(row.FieldAsString(1), out var xComponents))
1434 {
1731 - var componentElements = (ArrayList)components[row[1]];
1732 -
1733 - if (null != componentElements)
1734 - {
1735 - foreach (Wix.Component wixComponent in componentElements)
1736 - {
1737 - wixComponent.AddChild(progId);
1738 - }
1739 - }
1740 - else
1435 + foreach (var xComponent in xComponents)
1436 {
1742 - this.Messaging.Write(WarningMessages.ExpectedForeignRow(row.SourceLineNumbers, "Extension", row.GetPrimaryKey(DecompilerConstants.PrimaryKeyDelimiter), "Component_", Convert.ToString(row[1]), "Component"));
1437 + xComponent.Add(xProgId);
1438 }
1439 }
1440 + else
1441 + {
1442 + this.Messaging.Write(WarningMessages.ExpectedForeignRow(row.SourceLineNumbers, "Extension", row.GetPrimaryKey(DecompilerConstants.PrimaryKeyDelimiter), "Component_", row.FieldAsString(1), "Component"));
1443 + }
1444 }
1445 }
1446 }
@@ -1755,25 +1454,18 @@ namespace WixToolset.Core.WindowsInstaller
1454 /// </remarks>
1455 private void FinalizePropertyTable(TableIndexedCollection tables)
1456 {
1758 - var propertyTable = tables["Property"];
1759 - var customActionTable = tables["CustomAction"];
1760 -
1761 - if (null != propertyTable && null != customActionTable)
1457 + foreach (var row in tables["CustomAction"]?.Rows ?? Enumerable.Empty<Row>())
1458 {
1763 - foreach (var row in customActionTable.Rows)
1459 + // If no other fields on the property are set we must have created it in the backend.
1460 + var bits = row.FieldAsInteger(1);
1461 + if (WindowsInstallerConstants.MsidbCustomActionTypeHideTarget == (bits & WindowsInstallerConstants.MsidbCustomActionTypeHideTarget)
1462 + && WindowsInstallerConstants.MsidbCustomActionTypeInScript == (bits & WindowsInstallerConstants.MsidbCustomActionTypeInScript)
1463 + && this.TryGetIndexedElement("Property", out var xProperty, row.FieldAsString(0))
1464 + && String.IsNullOrEmpty(xProperty.Attribute("Value")?.Value)
1465 + && xProperty.Attribute("Secure")?.Value != "yes"
1466 + && xProperty.Attribute("SuppressModularization")?.Value != "yes")
1467 {
1765 - var bits = Convert.ToInt32(row[1]);
1766 - if (WindowsInstallerConstants.MsidbCustomActionTypeHideTarget == (bits & WindowsInstallerConstants.MsidbCustomActionTypeHideTarget) &&
1767 - WindowsInstallerConstants.MsidbCustomActionTypeInScript == (bits & WindowsInstallerConstants.MsidbCustomActionTypeInScript))
1768 - {
1769 - var property = (Wix.Property)this.core.GetIndexedElement("Property", Convert.ToString(row[0]));
1770 -
1771 - // If no other fields on the property are set we must have created it during link
1772 - if (null != property && null == property.Value && Wix.YesNoType.yes != property.Secure && Wix.YesNoType.yes != property.SuppressModularization)
1773 - {
1774 - this.core.RootElement.RemoveChild(property);
1775 - }
1776 - }
1468 + xProperty.Remove();
1469 }
1470 }
1471 }
@@ -1787,126 +1479,79 @@ namespace WixToolset.Core.WindowsInstaller
1479 /// </remarks>
1480 private void FinalizeRemoveFileTable(TableIndexedCollection tables)
1481 {
1790 - var removeFileTable = tables["RemoveFile"];
1791 -
1792 - if (null != removeFileTable)
1482 + foreach (var row in tables["RemoveFile"]?.Rows ?? Enumerable.Empty<Row>())
1483 {
1794 - foreach (var row in removeFileTable.Rows)
1795 - {
1796 - var isDirectory = false;
1797 - var property = Convert.ToString(row[3]);
1798 -
1799 - // determine if the property is actually authored as a directory
1800 - if (null != this.core.GetIndexedElement("Directory", property))
1801 - {
1802 - isDirectory = true;
1803 - }
1804 -
1805 - var element = this.core.GetIndexedElement(row);
1806 -
1807 - var removeFile = element as Wix.RemoveFile;
1808 - if (null != removeFile)
1809 - {
1810 - if (isDirectory)
1811 - {
1812 - removeFile.Directory = property;
1813 - }
1814 - else
1815 - {
1816 - removeFile.Property = property;
1817 - }
1818 - }
1819 - else
1820 - {
1821 - var removeFolder = (Wix.RemoveFolder)element;
1484 + var xRemove = this.GetIndexedElement(row);
1485 + var property = row.FieldAsString(3);
1486
1823 - if (isDirectory)
1824 - {
1825 - removeFolder.Directory = property;
1826 - }
1827 - else
1828 - {
1829 - removeFolder.Property = property;
1830 - }
1831 - }
1487 + if (this.TryGetIndexedElement("Directory", out var _, property))
1488 + {
1489 + xRemove.SetAttributeValue("Directory", property);
1490 + }
1491 + else
1492 + {
1493 + xRemove.SetAttributeValue("Property", property);
1494 }
1495 }
1496 }
1497
1498 /// <summary>
1837 - /// Finalize the LockPermissions table.
1499 + /// Finalize the LockPermissions or MsiLockPermissionsEx table.
1500 /// </summary>
1501 /// <param name="tables">The collection of all tables.</param>
1502 + /// <param name="tableName">Which table to finalize.</param>
1503 /// <remarks>
1504 /// Nests the Permission elements below their parent elements. There are no declared foreign
1505 /// keys for the parents of the LockPermissions table.
1506 /// </remarks>
1844 - private void FinalizeLockPermissionsTable(TableIndexedCollection tables)
1507 + private void FinalizePermissionsTable(TableIndexedCollection tables, string tableName)
1508 {
1846 - var createFolderTable = tables["CreateFolder"];
1847 - var lockPermissionsTable = tables["LockPermissions"];
1848 -
1849 - var createFolders = new Hashtable();
1509 + var createFoldersById = this.IndexTableOneToMany(tables, tableName);
1510
1851 - // index the CreateFolder table because the foreign key to this table from the
1852 - // LockPermissions table is only part of the primary key of this table
1853 - if (null != createFolderTable)
1511 + foreach (var row in tables[tableName]?.Rows ?? Enumerable.Empty<Row>())
1512 {
1855 - foreach (var row in createFolderTable.Rows)
1856 - {
1857 - var createFolder = (Wix.CreateFolder)this.core.GetIndexedElement(row);
1858 - var directoryId = Convert.ToString(row[0]);
1513 + var id = row.FieldAsString(0);
1514 + var table = row.FieldAsString(1);
1515 + var xPermission = this.GetIndexedElement(row);
1516
1860 - if (!createFolders.Contains(directoryId))
1517 + if ("CreateFolder" == table)
1518 + {
1519 + if (createFoldersById.TryGetValue(id, out var xCreateFolders))
1520 + {
1521 + foreach (var xCreateFolder in xCreateFolders)
1522 + {
1523 + xCreateFolder.Add(xPermission);
1524 + }
1525 + }
1526 + else
1527 {
1862 - createFolders.Add(directoryId, new ArrayList());
1528 + this.Messaging.Write(WarningMessages.ExpectedForeignRow(row.SourceLineNumbers, tableName, row.GetPrimaryKey(DecompilerConstants.PrimaryKeyDelimiter), "LockObject", id, table));
1529 }
1864 - ((ArrayList)createFolders[directoryId]).Add(createFolder);
1530 }
1866 - }
1867 -
1868 - if (null != lockPermissionsTable)
1869 - {
1870 - foreach (var row in lockPermissionsTable.Rows)
1531 + else
1532 {
1872 - var id = Convert.ToString(row[0]);
1873 - var table = Convert.ToString(row[1]);
1874 -
1875 - var permission = (Wix.Permission)this.core.GetIndexedElement(row);
1876 -
1877 - if ("CreateFolder" == table)
1533 + if (this.TryGetIndexedElement(table, out var xParent, id))
1534 {
1879 - var createFolderElements = (ArrayList)createFolders[id];
1880 -
1881 - if (null != createFolderElements)
1882 - {
1883 - foreach (Wix.CreateFolder createFolder in createFolderElements)
1884 - {
1885 - createFolder.AddChild(permission);
1886 - }
1887 - }
1888 - else
1889 - {
1890 - this.Messaging.Write(WarningMessages.ExpectedForeignRow(row.SourceLineNumbers, "LockPermissions", row.GetPrimaryKey(DecompilerConstants.PrimaryKeyDelimiter), "LockObject", id, table));
1891 - }
1535 + xParent.Add(xPermission);
1536 }
1537 else
1538 {
1895 - var parentElement = (Wix.IParentElement)this.core.GetIndexedElement(table, id);
1896 -
1897 - if (null != parentElement)
1898 - {
1899 - parentElement.AddChild(permission);
1900 - }
1901 - else
1902 - {
1903 - this.Messaging.Write(WarningMessages.ExpectedForeignRow(row.SourceLineNumbers, "LockPermissions", row.GetPrimaryKey(DecompilerConstants.PrimaryKeyDelimiter), "LockObject", id, table));
1904 - }
1539 + this.Messaging.Write(WarningMessages.ExpectedForeignRow(row.SourceLineNumbers, tableName, row.GetPrimaryKey(DecompilerConstants.PrimaryKeyDelimiter), "LockObject", id, table));
1540 }
1541 }
1542 }
1543 }
1544
1545 + /// <summary>
1546 + /// Finalize the LockPermissions table.
1547 + /// </summary>
1548 + /// <param name="tables">The collection of all tables.</param>
1549 + /// <remarks>
1550 + /// Nests the Permission elements below their parent elements. There are no declared foreign
1551 + /// keys for the parents of the LockPermissions table.
1552 + /// </remarks>
1553 + private void FinalizeLockPermissionsTable(TableIndexedCollection tables) => this.FinalizePermissionsTable(tables, "LockPermissions");
1554 +
1555 /// <summary>
1556 /// Finalize the MsiLockPermissionsEx table.
1557 /// </summary>
@@ -1915,164 +1560,58 @@ namespace WixToolset.Core.WindowsInstaller
1560 /// Nests the PermissionEx elements below their parent elements. There are no declared foreign
1561 /// keys for the parents of the MsiLockPermissionsEx table.
1562 /// </remarks>
1918 - private void FinalizeMsiLockPermissionsExTable(TableIndexedCollection tables)
1563 + private void FinalizeMsiLockPermissionsExTable(TableIndexedCollection tables) => this.FinalizePermissionsTable(tables, "MsiLockPermissionsEx");
1564 +
1565 + private static Dictionary<string, List<string>> IndexTable(Table table, int keyColumn, int? dataColumn)
1566 {
1920 - var createFolderTable = tables["CreateFolder"];
1921 - var msiLockPermissionsExTable = tables["MsiLockPermissionsEx"];
1567 + if (table == null)
1568 + {
1569 + return new Dictionary<string, List<string>>();
1570 + }
1571
1923 - var createFolders = new Hashtable();
1572 + return table.Rows
1573 + .ToLookup(row => row.FieldAsString(keyColumn), row => dataColumn.HasValue ? row.FieldAsString(dataColumn.Value) : null)
1574 + .ToDictionary(lookup => lookup.Key, lookup => lookup.ToList());
1575 + }
1576
1925 - // index the CreateFolder table because the foreign key to this table from the
1926 - // MsiLockPermissionsEx table is only part of the primary key of this table
1927 - if (null != createFolderTable)
1577 + private static XElement FindComplianceDrive(XElement xSearch)
1578 + {
1579 + var xComplianceDrive = xSearch.Element(Names.ComplianceDriveElement);
1580 + if (null == xComplianceDrive)
1581 {
1929 - foreach (var row in createFolderTable.Rows)
1930 - {
1931 - var createFolder = (Wix.CreateFolder)this.core.GetIndexedElement(row);
1932 - var directoryId = Convert.ToString(row[0]);
1933 -
1934 - if (!createFolders.Contains(directoryId))
1935 - {
1936 - createFolders.Add(directoryId, new ArrayList());
1937 - }
1938 - ((ArrayList)createFolders[directoryId]).Add(createFolder);
1939 - }
1582 + xComplianceDrive = new XElement(Names.ComplianceDriveElement);
1583 + xSearch.Add(xComplianceDrive);
1584 }
1585
1942 - if (null != msiLockPermissionsExTable)
1943 - {
1944 - foreach (var row in msiLockPermissionsExTable.Rows)
1945 - {
1946 - var id = Convert.ToString(row[1]);
1947 - var table = Convert.ToString(row[2]);
1586 + return xComplianceDrive;
1587 + }
1588
1949 - var permissionEx = (Wix.PermissionEx)this.core.GetIndexedElement(row);
1589 + /// <summary>
1590 + /// Finalize the search tables.
1591 + /// </summary>
1592 + /// <param name="tables">The collection of all tables.</param>
1593 + /// <remarks>Does all the complex linking required for the search tables.</remarks>
1594 + private void FinalizeSearchTables(TableIndexedCollection tables)
1595 + {
1596 + var appSearches = IndexTable(tables["AppSearch"], keyColumn: 1, dataColumn: 0);
1597 + var ccpSearches = IndexTable(tables["CCPSearch"], keyColumn: 0, dataColumn: null);
1598 + var drLocators = tables["DrLocator"]?.Rows.ToDictionary(row => this.GetIndexedElement(row), row => row);
1599
1951 - if ("CreateFolder" == table)
1952 - {
1953 - var createFolderElements = (ArrayList)createFolders[id];
1954 -
1955 - if (null != createFolderElements)
1956 - {
1957 - foreach (Wix.CreateFolder createFolder in createFolderElements)
1958 - {
1959 - createFolder.AddChild(permissionEx);
1960 - }
1961 - }
1962 - else
1963 - {
1964 - this.Messaging.Write(WarningMessages.ExpectedForeignRow(row.SourceLineNumbers, "MsiLockPermissionsEx", row.GetPrimaryKey(DecompilerConstants.PrimaryKeyDelimiter), "LockObject", id, table));
1965 - }
1966 - }
1967 - else
1968 - {
1969 - var parentElement = (Wix.IParentElement)this.core.GetIndexedElement(table, id);
1970 -
1971 - if (null != parentElement)
1972 - {
1973 - parentElement.AddChild(permissionEx);
1974 - }
1975 - else
1976 - {
1977 - this.Messaging.Write(WarningMessages.ExpectedForeignRow(row.SourceLineNumbers, "MsiLockPermissionsEx", row.GetPrimaryKey(DecompilerConstants.PrimaryKeyDelimiter), "LockObject", id, table));
1978 - }
1979 - }
1980 - }
1981 - }
1982 - }
1983 -
1984 - /// <summary>
1985 - /// Finalize the search tables.
1986 - /// </summary>
1987 - /// <param name="tables">The collection of all tables.</param>
1988 - /// <remarks>Does all the complex linking required for the search tables.</remarks>
1989 - private void FinalizeSearchTables(TableIndexedCollection tables)
1990 - {
1991 - var appSearchTable = tables["AppSearch"];
1992 - var ccpSearchTable = tables["CCPSearch"];
1993 - var drLocatorTable = tables["DrLocator"];
1994 -
1995 - var appSearches = new Hashtable();
1996 - var ccpSearches = new Hashtable();
1997 - var drLocators = new Hashtable();
1998 - var locators = new Hashtable();
1999 - var usedSearchElements = new Hashtable();
2000 - var unusedSearchElements = new Dictionary<string, Wix.IParentElement>();
2001 -
2002 - Wix.ComplianceCheck complianceCheck = null;
2003 -
2004 - // index the AppSearch table by signatures
2005 - if (null != appSearchTable)
2006 - {
2007 - foreach (var row in appSearchTable.Rows)
2008 - {
2009 - var property = Convert.ToString(row[0]);
2010 - var signature = Convert.ToString(row[1]);
2011 -
2012 - if (!appSearches.Contains(signature))
2013 - {
2014 - appSearches.Add(signature, new StringCollection());
2015 - }
2016 -
2017 - ((StringCollection)appSearches[signature]).Add(property);
2018 - }
2019 - }
2020 -
2021 - // index the CCPSearch table by signatures
2022 - if (null != ccpSearchTable)
2023 - {
2024 - foreach (var row in ccpSearchTable.Rows)
2025 - {
2026 - var signature = Convert.ToString(row[0]);
2027 -
2028 - if (!ccpSearches.Contains(signature))
2029 - {
2030 - ccpSearches.Add(signature, new StringCollection());
2031 - }
2032 -
2033 - ((StringCollection)ccpSearches[signature]).Add(null);
2034 -
2035 - if (null == complianceCheck && !appSearches.Contains(signature))
2036 - {
2037 - complianceCheck = new Wix.ComplianceCheck();
2038 - this.core.RootElement.AddChild(complianceCheck);
2039 - }
2040 - }
2041 - }
2042 -
2043 - // index the directory searches by their search elements (to get back the original row)
2044 - if (null != drLocatorTable)
1600 + var xComplianceCheck = new XElement(Names.ComplianceCheckElement);
1601 + if (ccpSearches.Keys.Any(ccpSignature => !appSearches.ContainsKey(ccpSignature)))
1602 {
2046 - foreach (var row in drLocatorTable.Rows)
2047 - {
2048 - drLocators.Add(this.core.GetIndexedElement(row), row);
2049 - }
1603 + this.RootElement.Add(xComplianceCheck);
1604 }
1605
1606 // index the locator tables by their signatures
2053 - var locatorTableNames = new string[] { "CompLocator", "RegLocator", "IniLocator", "DrLocator", "Signature" };
2054 - foreach (var locatorTableName in locatorTableNames)
2055 - {
2056 - var locatorTable = tables[locatorTableName];
2057 -
2058 - if (null != locatorTable)
2059 - {
2060 - foreach (var row in locatorTable.Rows)
2061 - {
2062 - var signature = Convert.ToString(row[0]);
2063 -
2064 - if (!locators.Contains(signature))
2065 - {
2066 - locators.Add(signature, new ArrayList());
2067 - }
2068 -
2069 - ((ArrayList)locators[signature]).Add(row);
2070 - }
2071 - }
2072 - }
1607 + var locators =
1608 + new[] { "CompLocator", "RegLocator", "IniLocator", "DrLocator", "Signature" }
1609 + .SelectMany(table => tables[table]?.Rows ?? Enumerable.Empty<Row>())
1610 + .ToLookup(row => row.FieldAsString(0), row => row)
1611 + .ToDictionary(lookup => lookup.Key, lookup => lookup.ToList());
1612
1613 // move the DrLocator rows with a parent of CCP_DRIVE first to ensure they get FileSearch children (not FileSearchRef)
2075 - foreach (ArrayList locatorRows in locators.Values)
1614 + foreach (var locatorRows in locators.Values)
1615 {
1616 var firstDrLocator = -1;
1617
@@ -2097,205 +1636,142 @@ namespace WixToolset.Core.WindowsInstaller
1636 }
1637 }
1638
2100 - foreach (string signature in locators.Keys)
1639 + var xUsedSearches = new HashSet<XElement>();
1640 + var xUnusedSearches = new Dictionary<string, XElement>();
1641 +
1642 + foreach (var signature in locators.Keys)
1643 {
2102 - var locatorRows = (ArrayList)locators[signature];
2103 - var signatureSearchElements = new ArrayList();
1644 + var locatorRows = locators[signature];
1645 + var xSignatureSearches = new List<XElement>();
1646
2105 - foreach (Row locatorRow in locatorRows)
1647 + foreach (var locatorRow in locatorRows)
1648 {
1649 var used = true;
2108 - var searchElement = this.core.GetIndexedElement(locatorRow);
1650 + var xSearch = this.GetIndexedElement(locatorRow);
1651
2110 - if ("Signature" == locatorRow.TableDefinition.Name && 0 < signatureSearchElements.Count)
1652 + if ("Signature" == locatorRow.TableDefinition.Name && 0 < xSignatureSearches.Count)
1653 {
2112 - foreach (Wix.IParentElement searchParentElement in signatureSearchElements)
1654 + foreach (var xSearchParent in xSignatureSearches)
1655 {
2114 - if (!usedSearchElements.Contains(searchElement))
1656 + if (!xUsedSearches.Contains(xSearch))
1657 {
2116 - searchParentElement.AddChild(searchElement);
2117 - usedSearchElements[searchElement] = null;
1658 + xSearchParent.Add(xSearch);
1659 + xUsedSearches.Add(xSearch);
1660 }
1661 else
1662 {
2121 - var fileSearchRef = new Wix.FileSearchRef();
1663 + var xFileSearchRef = new XElement(Names.FileSearchRefElement,
1664 + new XAttribute("Id", signature));
1665
2123 - fileSearchRef.Id = signature;
2124 -
2125 - searchParentElement.AddChild(fileSearchRef);
1666 + xSearchParent.Add(xFileSearchRef);
1667 }
1668 }
1669 }
2129 - else if ("DrLocator" == locatorRow.TableDefinition.Name && null != locatorRow[1])
1670 + else if ("DrLocator" == locatorRow.TableDefinition.Name && !locatorRow.IsColumnNull(1))
1671 {
2131 - var drSearchElement = (Wix.DirectorySearch)searchElement;
2132 - var parentSignature = Convert.ToString(locatorRow[1]);
1672 + var parentSignature = locatorRow.FieldAsString(1);
1673
1674 if ("CCP_DRIVE" == parentSignature)
1675 {
2136 - if (appSearches.Contains(signature))
1676 + if (appSearches.ContainsKey(signature)
1677 + && appSearches.TryGetValue(signature, out var appSearchPropertyIds))
1678 {
2138 - var appSearchPropertyIds = (StringCollection)appSearches[signature];
2139 -
1679 foreach (var propertyId in appSearchPropertyIds)
1680 {
2142 - var property = this.EnsureProperty(propertyId);
2143 - Wix.ComplianceDrive complianceDrive = null;
2144 -
2145 - if (ccpSearches.Contains(signature))
2146 - {
2147 - property.ComplianceCheck = Wix.YesNoType.yes;
2148 - }
1681 + var xProperty = this.EnsureProperty(propertyId);
1682
2150 - foreach (Wix.ISchemaElement element in property.Children)
1683 + if (ccpSearches.ContainsKey(signature))
1684 {
2152 - complianceDrive = element as Wix.ComplianceDrive;
2153 - if (null != complianceDrive)
2154 - {
2155 - break;
2156 - }
1685 + xProperty.SetAttributeValue("ComplianceCheck", "yes");
1686 }
1687
2159 - if (null == complianceDrive)
2160 - {
2161 - complianceDrive = new Wix.ComplianceDrive();
2162 - property.AddChild(complianceDrive);
2163 - }
1688 + var xComplianceDrive = FindComplianceDrive(xProperty);
1689
2165 - if (!usedSearchElements.Contains(searchElement))
1690 + if (!xUsedSearches.Contains(xSearch))
1691 {
2167 - complianceDrive.AddChild(searchElement);
2168 - usedSearchElements[searchElement] = null;
1692 + xComplianceDrive.Add(xSearch);
1693 + xUsedSearches.Add(xSearch);
1694 }
1695 else
1696 {
2172 - var directorySearchRef = new Wix.DirectorySearchRef();
2173 -
2174 - directorySearchRef.Id = signature;
2175 -
2176 - if (null != locatorRow[1])
2177 - {
2178 - directorySearchRef.Parent = Convert.ToString(locatorRow[1]);
2179 - }
2180 -
2181 - if (null != locatorRow[2])
2182 - {
2183 - directorySearchRef.Path = Convert.ToString(locatorRow[2]);
2184 - }
1697 + var directorySearchRef = new XElement(Names.DirectorySearchRefElement,
1698 + new XAttribute("Id", signature),
1699 + XAttributeIfNotNull("Parent", locatorRow, 1),
1700 + XAttributeIfNotNull("Path", locatorRow, 2));
1701
2186 - complianceDrive.AddChild(directorySearchRef);
2187 - signatureSearchElements.Add(directorySearchRef);
1702 + xComplianceDrive.Add(directorySearchRef);
1703 + xSignatureSearches.Add(directorySearchRef);
1704 }
1705 }
1706 }
2191 - else if (ccpSearches.Contains(signature))
1707 + else if (ccpSearches.ContainsKey(signature))
1708 {
2193 - Wix.ComplianceDrive complianceDrive = null;
2194 -
2195 - foreach (Wix.ISchemaElement element in complianceCheck.Children)
2196 - {
2197 - complianceDrive = element as Wix.ComplianceDrive;
2198 - if (null != complianceDrive)
2199 - {
2200 - break;
2201 - }
2202 - }
2203 -
2204 - if (null == complianceDrive)
2205 - {
2206 - complianceDrive = new Wix.ComplianceDrive();
2207 - complianceCheck.AddChild(complianceDrive);
2208 - }
1709 + var xComplianceDrive = FindComplianceDrive(xComplianceCheck);
1710
2210 - if (!usedSearchElements.Contains(searchElement))
1711 + if (!xUsedSearches.Contains(xSearch))
1712 {
2212 - complianceDrive.AddChild(searchElement);
2213 - usedSearchElements[searchElement] = null;
1713 + xComplianceDrive.Add(xSearch);
1714 + xUsedSearches.Add(xSearch);
1715 }
1716 else
1717 {
2217 - var directorySearchRef = new Wix.DirectorySearchRef();
2218 -
2219 - directorySearchRef.Id = signature;
2220 -
2221 - if (null != locatorRow[1])
2222 - {
2223 - directorySearchRef.Parent = Convert.ToString(locatorRow[1]);
2224 - }
2225 -
2226 - if (null != locatorRow[2])
2227 - {
2228 - directorySearchRef.Path = Convert.ToString(locatorRow[2]);
2229 - }
1718 + var directorySearchRef = new XElement(Names.DirectorySearchRefElement,
1719 + new XAttribute("Id", signature),
1720 + XAttributeIfNotNull("Parent", locatorRow, 1),
1721 + XAttributeIfNotNull("Path", locatorRow, 2));
1722
2231 - complianceDrive.AddChild(directorySearchRef);
2232 - signatureSearchElements.Add(directorySearchRef);
1723 + xComplianceDrive.Add(directorySearchRef);
1724 + xSignatureSearches.Add(directorySearchRef);
1725 }
1726 }
1727 }
1728 else
1729 {
1730 var usedDrLocator = false;
2239 - var parentLocatorRows = (ArrayList)locators[parentSignature];
1731
2241 - if (null != parentLocatorRows)
1732 + if (locators.TryGetValue(parentSignature, out var parentLocatorRows))
1733 {
2243 - foreach (Row parentLocatorRow in parentLocatorRows)
1734 + foreach (var parentLocatorRow in parentLocatorRows)
1735 {
1736 if ("DrLocator" == parentLocatorRow.TableDefinition.Name)
1737 {
2247 - var parentSearchElement = (Wix.IParentElement)this.core.GetIndexedElement(parentLocatorRow);
1738 + var xParentSearch = this.GetIndexedElement(parentLocatorRow);
1739
2249 - if (parentSearchElement.Children.GetEnumerator().MoveNext())
1740 + if (xParentSearch.HasElements)
1741 {
2251 - var parentDrLocatorRow = (Row)drLocators[parentSearchElement];
2252 - var directorySeachRef = new Wix.DirectorySearchRef();
2253 -
2254 - directorySeachRef.Id = parentSignature;
2255 -
2256 - if (null != parentDrLocatorRow[1])
2257 - {
2258 - directorySeachRef.Parent = Convert.ToString(parentDrLocatorRow[1]);
2259 - }
2260 -
2261 - if (null != parentDrLocatorRow[2])
2262 - {
2263 - directorySeachRef.Path = Convert.ToString(parentDrLocatorRow[2]);
2264 - }
2265 -
2266 - parentSearchElement = directorySeachRef;
2267 - unusedSearchElements.Add(directorySeachRef.Id, directorySeachRef);
1742 + var parentDrLocatorRow = drLocators[xParentSearch];
1743 + var xDirectorySearchRef = new XElement(Names.DirectorySearchRefElement,
1744 + new XAttribute("Id", parentSignature),
1745 + XAttributeIfNotNull("Parent", parentDrLocatorRow, 1),
1746 + XAttributeIfNotNull("Path", parentDrLocatorRow, 2));
1747 +
1748 + xParentSearch = xDirectorySearchRef;
1749 + xUnusedSearches.Add(parentSignature, xDirectorySearchRef);
1750 }
1751
2270 - if (!usedSearchElements.Contains(searchElement))
1752 + if (!xUsedSearches.Contains(xSearch))
1753 {
2272 - parentSearchElement.AddChild(searchElement);
2273 - usedSearchElements[searchElement] = null;
1754 + xParentSearch.Add(xSearch);
1755 + xUsedSearches.Add(xSearch);
1756 usedDrLocator = true;
1757 }
1758 else
1759 {
2278 - var directorySearchRef = new Wix.DirectorySearchRef();
2279 -
2280 - directorySearchRef.Id = signature;
2281 -
2282 - directorySearchRef.Parent = parentSignature;
2283 -
2284 - if (null != locatorRow[2])
2285 - {
2286 - directorySearchRef.Path = Convert.ToString(locatorRow[2]);
2287 - }
1760 + var xDirectorySearchRef = new XElement(Names.DirectorySearchRefElement,
1761 + new XAttribute("Id", signature),
1762 + new XAttribute("Parent", parentSignature),
1763 + XAttributeIfNotNull("Path", locatorRow, 2));
1764
2289 - parentSearchElement.AddChild(searchElement);
1765 + xParentSearch.Add(xSearch);
1766 usedDrLocator = true;
1767 }
1768 }
1769 else if ("RegLocator" == parentLocatorRow.TableDefinition.Name)
1770 {
2295 - var parentSearchElement = (Wix.IParentElement)this.core.GetIndexedElement(parentLocatorRow);
1771 + var xParentSearch = this.GetIndexedElement(parentLocatorRow);
1772
2297 - parentSearchElement.AddChild(searchElement);
2298 - usedSearchElements[searchElement] = null;
1773 + xParentSearch.Add(xSearch);
1774 + xUsedSearches.Add(xSearch);
1775 usedDrLocator = true;
1776 }
1777 }
@@ -2303,7 +1779,7 @@ namespace WixToolset.Core.WindowsInstaller
1779 // keep track of unused DrLocator rows
1780 if (!usedDrLocator)
1781 {
2306 - unusedSearchElements.Add(drSearchElement.Id, drSearchElement);
1782 + xUnusedSearches.Add(xSearch.Attribute("Id").Value, xSearch);
1783 }
1784 }
1785 else
@@ -2312,32 +1788,30 @@ namespace WixToolset.Core.WindowsInstaller
1788 }
1789 }
1790 }
2315 - else if (appSearches.Contains(signature))
1791 + else if (appSearches.ContainsKey(signature)
1792 + && appSearches.TryGetValue(signature, out var appSearchPropertyIds))
1793 {
2317 - var appSearchPropertyIds = (StringCollection)appSearches[signature];
2318 -
1794 foreach (var propertyId in appSearchPropertyIds)
1795 {
2321 - var property = this.EnsureProperty(propertyId);
1796 + var xProperty = this.EnsureProperty(propertyId);
1797
2323 - if (ccpSearches.Contains(signature))
1798 + if (ccpSearches.ContainsKey(signature))
1799 {
2325 - property.ComplianceCheck = Wix.YesNoType.yes;
1800 + xProperty.SetAttributeValue("ComplianceCheck", "yes");
1801 }
1802
2328 - if (!usedSearchElements.Contains(searchElement))
1803 + if (!xUsedSearches.Contains(xSearch))
1804 {
2330 - property.AddChild(searchElement);
2331 - usedSearchElements[searchElement] = null;
1805 + xProperty.Add(xSearch);
1806 + xUsedSearches.Add(xSearch);
1807 }
1808 else if ("RegLocator" == locatorRow.TableDefinition.Name)
1809 {
2335 - var registrySearchRef = new Wix.RegistrySearchRef();
2336 -
2337 - registrySearchRef.Id = signature;
1810 + var xRegistrySearchRef = new XElement(Names.RegistrySearchRefElement,
1811 + new XAttribute("Id", signature));
1812
2339 - property.AddChild(registrySearchRef);
2340 - signatureSearchElements.Add(registrySearchRef);
1813 + xProperty.Add(xRegistrySearchRef);
1814 + xSignatureSearches.Add(xRegistrySearchRef);
1815 }
1816 else
1817 {
@@ -2345,21 +1819,20 @@ namespace WixToolset.Core.WindowsInstaller
1819 }
1820 }
1821 }
2348 - else if (ccpSearches.Contains(signature))
1822 + else if (ccpSearches.ContainsKey(signature))
1823 {
2350 - if (!usedSearchElements.Contains(searchElement))
1824 + if (!xUsedSearches.Contains(xSearch))
1825 {
2352 - complianceCheck.AddChild(searchElement);
2353 - usedSearchElements[searchElement] = null;
1826 + xComplianceCheck.Add(xSearch);
1827 + xUsedSearches.Add(xSearch);
1828 }
1829 else if ("RegLocator" == locatorRow.TableDefinition.Name)
1830 {
2357 - var registrySearchRef = new Wix.RegistrySearchRef();
1831 + var xRegistrySearchRef = new XElement(Names.RegistrySearchRefElement,
1832 + new XAttribute("Id", signature));
1833
2359 - registrySearchRef.Id = signature;
2360 -
2361 - complianceCheck.AddChild(registrySearchRef);
2362 - signatureSearchElements.Add(registrySearchRef);
1834 + xComplianceCheck.Add(xRegistrySearchRef);
1835 + xSignatureSearches.Add(xRegistrySearchRef);
1836 }
1837 else
1838 {
@@ -2368,13 +1841,9 @@ namespace WixToolset.Core.WindowsInstaller
1841 }
1842 else
1843 {
2371 - if (searchElement is Wix.DirectorySearch directorySearch)
2372 - {
2373 - unusedSearchElements.Add(directorySearch.Id, directorySearch);
2374 - }
2375 - else if (searchElement is Wix.RegistrySearch registrySearch)
1844 + if (xSearch.Name.LocalName == "DirectorySearch" || xSearch.Name.LocalName == "RegistrySearch")
1845 {
2377 - unusedSearchElements.Add(registrySearch.Id, registrySearch);
1846 + xUnusedSearches.Add(xSearch.Attribute("Id").Value, xSearch);
1847 }
1848 else
1849 {
@@ -2386,51 +1855,44 @@ namespace WixToolset.Core.WindowsInstaller
1855 // keep track of the search elements for this signature so that nested searches go in the proper parents
1856 if (used)
1857 {
2389 - signatureSearchElements.Add(searchElement);
1858 + xSignatureSearches.Add(xSearch);
1859 }
1860 }
1861 }
1862
1863 // Iterate through the unused elements through a sorted list of their ids so the output is deterministic.
2395 - var unusedSearchElementKeys = unusedSearchElements.Keys.ToList();
2396 - unusedSearchElementKeys.Sort();
2397 - foreach (var unusedSearchElementKey in unusedSearchElementKeys)
1864 + foreach (var unusedSearch in xUnusedSearches.OrderBy(kvp => kvp.Key))
1865 {
2399 - var unusedSearchElement = unusedSearchElements[unusedSearchElementKey];
1866 var used = false;
1867
2402 - Wix.DirectorySearch leafDirectorySearch = null;
2403 - var parentElement = unusedSearchElement;
1868 + XElement xLeafDirectorySearch = null;
1869 + var xUnusedSearch = unusedSearch.Value;
1870 + var xParent = xUnusedSearch;
1871 var updatedLeaf = true;
1872 while (updatedLeaf)
1873 {
1874 updatedLeaf = false;
2408 - foreach (var schemaElement in parentElement.Children)
1875 +
1876 + var xDirectorySearch = xParent.Element(Names.DirectorySearchElement);
1877 + if (xDirectorySearch != null)
1878 {
2410 - if (schemaElement is Wix.DirectorySearch directorySearch)
2411 - {
2412 - parentElement = leafDirectorySearch = directorySearch;
2413 - updatedLeaf = true;
2414 - break;
2415 - }
1879 + xParent = xLeafDirectorySearch = xDirectorySearch;
1880 + updatedLeaf = true;
1881 }
1882 }
1883
2419 - if (leafDirectorySearch != null)
1884 + if (xLeafDirectorySearch != null)
1885 {
2421 - var appSearchProperties = (StringCollection)appSearches[leafDirectorySearch.Id];
2422 -
2423 - var unusedSearchSchemaElement = unusedSearchElement as Wix.ISchemaElement;
2424 - if (null != appSearchProperties)
1886 + var leafDirectorySearchId = xLeafDirectorySearch.Attribute("Id").Value;
1887 + if (appSearches.TryGetValue(leafDirectorySearchId, out var appSearchPropertyIds))
1888 {
2426 - var property = this.EnsureProperty(appSearchProperties[0]);
2427 -
2428 - property.AddChild(unusedSearchSchemaElement);
1889 + var xProperty = this.EnsureProperty(appSearchPropertyIds[0]);
1890 + xProperty.Add(xUnusedSearch);
1891 used = true;
1892 }
2431 - else if (ccpSearches.Contains(leafDirectorySearch.Id))
1893 + else if (ccpSearches.ContainsKey(leafDirectorySearchId))
1894 {
2433 - complianceCheck.AddChild(unusedSearchSchemaElement);
1895 + xComplianceCheck.Add(xUnusedSearch);
1896 used = true;
1897 }
1898 else
@@ -2464,18 +1926,19 @@ namespace WixToolset.Core.WindowsInstaller
1926
1927 foreach (var row in shortcutTable.Rows)
1928 {
2467 - var shortcut = (Wix.Shortcut)this.core.GetIndexedElement(row);
2468 - var target = Convert.ToString(row[4]);
2469 - var feature = this.core.GetIndexedElement("Feature", target);
2470 - if (feature == null)
1929 + var xShortcut = this.GetIndexedElement(row);
1930 +
1931 + var target = row.FieldAsString(4);
1932 +
1933 + if (this.TryGetIndexedElement("Feature", out var _, target))
1934 {
2472 - // TODO: use this value to do a "more-correct" nesting under the indicated File or CreateDirectory element
2473 - shortcut.Target = target;
1935 + xShortcut.SetAttributeValue("Advertise", "yes");
1936 + this.SetPrimaryFeature(row, 4, 3);
1937 }
1938 else
1939 {
2477 - shortcut.Advertise = Wix.YesNoType.yes;
2478 - this.SetPrimaryFeature(row, 4, 3);
1940 + // TODO: use this value to do a "more-correct" nesting under the indicated File or CreateDirectory element
1941 + xShortcut.SetAttributeValue("Target", target);
1942 }
1943 }
1944 }
@@ -2520,12 +1983,12 @@ namespace WixToolset.Core.WindowsInstaller
1983
1984 actionSymbol.Action = action;
1985
2523 - if (null != row[1])
1986 + if (!row.IsColumnNull(1))
1987 {
2525 - actionSymbol.Condition = Convert.ToString(row[1]);
1988 + actionSymbol.Condition = row.FieldAsString(1);
1989 }
1990
2528 - actionSymbol.Sequence = Convert.ToInt32(row[2]);
1991 + actionSymbol.Sequence = row.FieldAsInteger(2);
1992
1993 actionSymbol.SequenceTable = sequenceTable;
1994
@@ -2654,30 +2117,30 @@ namespace WixToolset.Core.WindowsInstaller
2117
2118 actionRow.Action = row.FieldAsString(0);
2119
2657 - if (null != row[1])
2120 + if (!row.IsColumnNull(1))
2121 {
2659 - actionRow.Sequence = Convert.ToInt32(row[1]);
2122 + actionRow.Sequence = row.FieldAsInteger(1);
2123 }
2124
2662 - if (null != row[2] && null != row[3])
2125 + if (!row.IsColumnNull(2) && !row.IsColumnNull(3))
2126 {
2664 - switch (Convert.ToInt32(row[3]))
2127 + switch (row.FieldAsInteger(3))
2128 {
2666 - case 0:
2667 - actionRow.Before = Convert.ToString(row[2]);
2668 - break;
2669 - case 1:
2670 - actionRow.After = Convert.ToString(row[2]);
2671 - break;
2672 - default:
2673 - this.Messaging.Write(WarningMessages.IllegalColumnValue(row.SourceLineNumbers, table.Name, row.Fields[3].Column.Name, row[3]));
2674 - break;
2129 + case 0:
2130 + actionRow.Before = row.FieldAsString(2);
2131 + break;
2132 + case 1:
2133 + actionRow.After = row.FieldAsString(2);
2134 + break;
2135 + default:
2136 + this.Messaging.Write(WarningMessages.IllegalColumnValue(row.SourceLineNumbers, table.Name, row.Fields[3].Column.Name, row[3]));
2137 + break;
2138 }
2139 }
2140
2678 - if (null != row[4])
2141 + if (!row.IsColumnNull(4))
2142 {
2680 - actionRow.Condition = Convert.ToString(row[4]);
2143 + actionRow.Condition = row.FieldAsString(4);
2144 }
2145
2146 actionRow.SequenceTable = sequenceTable;
@@ -2707,7 +2170,6 @@ namespace WixToolset.Core.WindowsInstaller
2170 var upgradeTable = tables["Upgrade"];
2171 string downgradeErrorMessage = null;
2172 string disallowUpgradeErrorMessage = null;
2710 - var majorUpgrade = new Wix.MajorUpgrade();
2173
2174 // find the DowngradePreventedCondition launch condition message
2175 if (null != launchConditionTable && 0 < launchConditionTable.Rows.Count)
@@ -2727,65 +2189,63 @@ namespace WixToolset.Core.WindowsInstaller
2189
2190 if (null != upgradeTable && 0 < upgradeTable.Rows.Count)
2191 {
2730 - var hasMajorUpgrade = false;
2192 + XElement xMajorUpgrade = null;
2193
2732 - foreach (var row in upgradeTable.Rows)
2194 + foreach (UpgradeRow upgradeRow in upgradeTable.Rows)
2195 {
2734 - var upgradeRow = (UpgradeRow)row;
2735 -
2196 if (Common.UpgradeDetectedProperty == upgradeRow.ActionProperty)
2197 {
2738 - hasMajorUpgrade = true;
2198 var attr = upgradeRow.Attributes;
2199 var removeFeatures = upgradeRow.Remove;
2200 + xMajorUpgrade = xMajorUpgrade ?? new XElement(Names.MajorUpgradeElement);
2201
2202 if (WindowsInstallerConstants.MsidbUpgradeAttributesVersionMaxInclusive == (attr & WindowsInstallerConstants.MsidbUpgradeAttributesVersionMaxInclusive))
2203 {
2744 - majorUpgrade.AllowSameVersionUpgrades = Wix.YesNoType.yes;
2204 + xMajorUpgrade.SetAttributeValue("AllowSameVersionUpgrades", "yes");
2205 }
2206
2207 if (WindowsInstallerConstants.MsidbUpgradeAttributesMigrateFeatures != (attr & WindowsInstallerConstants.MsidbUpgradeAttributesMigrateFeatures))
2208 {
2749 - majorUpgrade.MigrateFeatures = Wix.YesNoType.no;
2209 + xMajorUpgrade.SetAttributeValue("MigrateFeatures", "no");
2210 }
2211
2212 if (WindowsInstallerConstants.MsidbUpgradeAttributesIgnoreRemoveFailure == (attr & WindowsInstallerConstants.MsidbUpgradeAttributesIgnoreRemoveFailure))
2213 {
2754 - majorUpgrade.IgnoreRemoveFailure = Wix.YesNoType.yes;
2214 + xMajorUpgrade.SetAttributeValue("IgnoreRemoveFailure", "yes");
2215 }
2216
2217 if (!String.IsNullOrEmpty(removeFeatures))
2218 {
2759 - majorUpgrade.RemoveFeatures = removeFeatures;
2219 + xMajorUpgrade.SetAttributeValue("RemoveFeatures", removeFeatures);
2220 }
2221 }
2222 else if (Common.DowngradeDetectedProperty == upgradeRow.ActionProperty)
2223 {
2764 - hasMajorUpgrade = true;
2765 - majorUpgrade.DowngradeErrorMessage = downgradeErrorMessage;
2224 + xMajorUpgrade = xMajorUpgrade ?? new XElement(Names.MajorUpgradeElement);
2225 + xMajorUpgrade.SetAttributeValue("DowngradeErrorMessage", downgradeErrorMessage);
2226 }
2227 }
2228
2769 - if (hasMajorUpgrade)
2229 + if (xMajorUpgrade != null)
2230 {
2231 if (String.IsNullOrEmpty(downgradeErrorMessage))
2232 {
2773 - majorUpgrade.AllowDowngrades = Wix.YesNoType.yes;
2233 + xMajorUpgrade.SetAttributeValue("AllowDowngrades", "yes");
2234 }
2235
2236 if (!String.IsNullOrEmpty(disallowUpgradeErrorMessage))
2237 {
2778 - majorUpgrade.Disallow = Wix.YesNoType.yes;
2779 - majorUpgrade.DisallowUpgradeErrorMessage = disallowUpgradeErrorMessage;
2238 + xMajorUpgrade.SetAttributeValue("Disallow", "yes");
2239 + xMajorUpgrade.SetAttributeValue("DisallowUpgradeErrorMessage", disallowUpgradeErrorMessage);
2240 }
2241
2242 var scheduledType = DetermineMajorUpgradeScheduling(tables);
2783 - if (Wix.MajorUpgrade.ScheduleType.afterInstallValidate != scheduledType)
2243 + if (scheduledType != "afterInstallValidate")
2244 {
2785 - majorUpgrade.Schedule = scheduledType;
2245 + xMajorUpgrade.SetAttributeValue("Schedule", scheduledType);
2246 }
2247
2788 - this.core.RootElement.AddChild(majorUpgrade);
2248 + this.RootElement.Add(xMajorUpgrade);
2249 }
2250 }
2251 }
@@ -2801,43 +2261,25 @@ namespace WixToolset.Core.WindowsInstaller
2261 /// </remarks>
2262 private void FinalizeVerbTable(TableIndexedCollection tables)
2263 {
2804 - var extensionTable = tables["Extension"];
2805 - var verbTable = tables["Verb"];
2806 -
2807 - var extensionElements = new Hashtable();
2808 -
2809 - if (null != extensionTable)
2810 - {
2811 - foreach (var row in extensionTable.Rows)
2812 - {
2813 - var extension = (Wix.Extension)this.core.GetIndexedElement(row);
2814 -
2815 - if (!extensionElements.Contains(row[0]))
2816 - {
2817 - extensionElements.Add(row[0], new ArrayList());
2818 - }
2819 -
2820 - ((ArrayList)extensionElements[row[0]]).Add(extension);
2821 - }
2822 - }
2264 + var xExtensions = this.IndexTableOneToMany(tables["Extension"]);
2265
2266 + var verbTable = tables["Verb"];
2267 if (null != verbTable)
2268 {
2269 foreach (var row in verbTable.Rows)
2270 {
2828 - var verb = (Wix.Verb)this.core.GetIndexedElement(row);
2829 -
2830 - var extensionsArray = (ArrayList)extensionElements[row[0]];
2831 - if (null != extensionsArray)
2271 + if (xExtensions.TryGetValue(row.FieldAsString(0), out var xVerbExtensions))
2272 {
2833 - foreach (Wix.Extension extension in extensionsArray)
2273 + var xVerb = this.GetIndexedElement(row);
2274 +
2275 + foreach (var xVerbExtension in xVerbExtensions)
2276 {
2835 - extension.AddChild(verb);
2277 + xVerbExtension.Add(xVerb);
2278 }
2279 }
2280 else
2281 {
2840 - this.Messaging.Write(WarningMessages.ExpectedForeignRow(row.SourceLineNumbers, verbTable.Name, row.GetPrimaryKey(DecompilerConstants.PrimaryKeyDelimiter), "Extension_", Convert.ToString(row[0]), "Extension"));
2282 + this.Messaging.Write(WarningMessages.ExpectedForeignRow(row.SourceLineNumbers, verbTable.Name, row.GetPrimaryKey(DecompilerConstants.PrimaryKeyDelimiter), "Extension_", row.FieldAsString(0), "Extension"));
2283 }
2284 }
2285 }
@@ -2846,33 +2288,38 @@ namespace WixToolset.Core.WindowsInstaller
2288 /// <summary>
2289 /// Get the path to a file in the source image.
2290 /// </summary>
2849 - /// <param name="file">The file.</param>
2291 + /// <param name="xFile">The file.</param>
2292 /// <returns>The path to the file in the source image.</returns>
2851 - private string GetSourcePath(Wix.File file)
2293 + private string GetSourcePath(XElement xFile)
2294 {
2295 var sourcePath = new StringBuilder();
2296
2855 - var component = (Wix.Component)file.ParentElement;
2297 + var component = xFile.Parent;
2298
2857 - for (var directory = (Wix.Directory)component.ParentElement; null != directory; directory = directory.ParentElement as Wix.Directory)
2299 + for (var xDirectory = component.Parent; null != xDirectory && xDirectory.Name.LocalName == "Directory"; xDirectory = xDirectory.Parent)
2300 {
2301 string name;
2302
2861 - if (!this.shortNames && null != directory.SourceName)
2303 + var dirSourceName = xDirectory.Attribute("SourceName")?.Value;
2304 + var dirShortSourceName = xDirectory.Attribute("ShortSourceName")?.Value;
2305 + var dirShortName = xDirectory.Attribute("ShortName")?.Value;
2306 + var dirName = xDirectory.Attribute("Name")?.Value;
2307 +
2308 + if (!this.ShortNames && null != dirSourceName)
2309 {
2863 - name = directory.SourceName;
2310 + name = dirSourceName;
2311 }
2865 - else if (null != directory.ShortSourceName)
2312 + else if (null != dirShortSourceName)
2313 {
2867 - name = directory.ShortSourceName;
2314 + name = dirShortSourceName;
2315 }
2869 - else if (!this.shortNames || null == directory.ShortName)
2316 + else if (!this.ShortNames || null == dirShortName)
2317 {
2871 - name = directory.Name;
2318 + name = dirName;
2319 }
2320 else
2321 {
2875 - name = directory.ShortName;
2322 + name = dirShortName;
2323 }
2324
2325 if (0 == sourcePath.Length)
@@ -2895,11 +2342,11 @@ namespace WixToolset.Core.WindowsInstaller
2342 /// <param name="tableName">The name of the table to resolve.</param>
2343 /// <param name="unsortedTableNames">The unsorted table names.</param>
2344 /// <param name="sortedTableNames">The sorted table names.</param>
2898 - private void ResolveTableDependencies(string tableName, SortedList unsortedTableNames, StringCollection sortedTableNames)
2345 + private void ResolveTableDependencies(string tableName, List<string> unsortedTableNames, HashSet<string> sortedTableNames)
2346 {
2347 unsortedTableNames.Remove(tableName);
2348
2902 - foreach (var columnDefinition in this.tableDefinitions[tableName].Columns)
2349 + foreach (var columnDefinition in this.TableDefinitions[tableName].Columns)
2350 {
2351 // no dependency to resolve because this column doesn't reference another table
2352 if (null == columnDefinition.KeyTable)
@@ -2917,7 +2364,7 @@ namespace WixToolset.Core.WindowsInstaller
2364 {
2365 continue; // dependent table has already been sorted
2366 }
2920 - else if (!this.tableDefinitions.Contains(keyTable))
2367 + else if (!this.TableDefinitions.Contains(keyTable))
2368 {
2369 this.Messaging.Write(ErrorMessages.MissingTableDefinition(keyTable));
2370 }
@@ -2941,24 +2388,18 @@ namespace WixToolset.Core.WindowsInstaller
2388 /// Get the names of the tables to process in the order they should be processed, according to their dependencies.
2389 /// </summary>
2390 /// <returns>A StringCollection containing the ordered table names.</returns>
2944 - private StringCollection GetSortedTableNames()
2391 + private HashSet<string> GetOrderedTableNames()
2392 {
2946 - var sortedTableNames = new StringCollection();
2947 - var unsortedTableNames = new SortedList();
2948 -
2949 - // index the table names
2950 - foreach (var tableDefinition in this.tableDefinitions)
2951 - {
2952 - unsortedTableNames.Add(tableDefinition.Name, tableDefinition.Name);
2953 - }
2393 + var orderedTableNames = new HashSet<string>();
2394 + var unsortedTableNames = new List<string>(this.TableDefinitions.Select(t => t.Name));
2395
2396 // resolve the dependencies for each table
2397 while (0 < unsortedTableNames.Count)
2398 {
2958 - this.ResolveTableDependencies(Convert.ToString(unsortedTableNames.GetByIndex(0)), unsortedTableNames, sortedTableNames);
2399 + this.ResolveTableDependencies(unsortedTableNames[0], unsortedTableNames, orderedTableNames);
2400 }
2401
2961 - return sortedTableNames;
2402 + return orderedTableNames;
2403 }
2404
2405 /// <summary>
@@ -2968,26 +2409,17 @@ namespace WixToolset.Core.WindowsInstaller
2409 private void InitializeDecompile(TableIndexedCollection tables, int codepage)
2410 {
2411 // reset all the state information
2971 - this.compressed = false;
2972 - this.patchTargetFiles.Clear();
2973 - this.sequenceElements.Clear();
2974 - this.shortNames = false;
2412 + this.Compressed = false;
2413 + this.ShortNames = false;
2414 +
2415 + this.Singletons.Clear();
2416 + this.IndexedElements.Clear();
2417 + this.PatchTargetFiles.Clear();
2418
2419 // set the codepage if its not neutral (0)
2420 if (0 != codepage)
2421 {
2979 - switch (this.OutputType)
2980 - {
2981 - case OutputType.Module:
2982 - ((Wix.Module)this.core.RootElement).Codepage = codepage.ToString(CultureInfo.InvariantCulture);
2983 - break;
2984 - case OutputType.PatchCreation:
2985 - ((Wix.PatchCreation)this.core.RootElement).Codepage = codepage.ToString(CultureInfo.InvariantCulture);
2986 - break;
2987 - case OutputType.Product:
2988 - ((Wix.Product)this.core.RootElement).Codepage = codepage.ToString(CultureInfo.InvariantCulture);
2989 - break;
2990 - }
2422 + this.RootElement.SetAttributeValue("Codepage", codepage);
2423 }
2424
2425 // index the rows from the extension libraries
@@ -3011,15 +2443,15 @@ namespace WixToolset.Core.WindowsInstaller
2443 // the Actions table needs to be handled specially
2444 if ("WixAction" == table.Name)
2445 {
3014 - primaryKey = Convert.ToString(row[1]);
2446 + primaryKey = row.FieldAsString(1);
2447
2448 if (OutputType.Module == this.outputType)
2449 {
3018 - tableName = String.Concat("Module", Convert.ToString(row[0]));
2450 + tableName = String.Concat("Module", row.FieldAsString(0));
2451 }
2452 else
2453 {
3022 - tableName = Convert.ToString(row[0]);
2454 + tableName = row.FieldAsString(0);
2455 }
2456 }
2457 else
@@ -3077,9 +2509,8 @@ namespace WixToolset.Core.WindowsInstaller
2509 /// <param name="output">The output being decompiled.</param>
2510 private void DecompileTables(WindowsInstallerData output)
2511 {
3080 - var sortedTableNames = this.GetSortedTableNames();
3081 -
3082 - foreach (var tableName in sortedTableNames)
2512 + var orderedTableNames = this.GetOrderedTableNames();
2513 + foreach (var tableName in orderedTableNames)
2514 {
2515 var table = output.Tables[tableName];
2516
@@ -3094,328 +2525,326 @@ namespace WixToolset.Core.WindowsInstaller
2525 // empty tables may be kept with EnsureTable if the user set the proper option
2526 if (0 == table.Rows.Count && this.SuppressDroppingEmptyTables)
2527 {
3097 - var ensureTable = new Wix.EnsureTable();
3098 - ensureTable.Id = table.Name;
3099 - this.core.RootElement.AddChild(ensureTable);
2528 + this.RootElement.Add(new XElement(Names.EnsureTableElement, new XAttribute("Id", table.Name)));
2529 }
2530
2531 switch (table.Name)
2532 {
3104 - case "_SummaryInformation":
3105 - this.Decompile_SummaryInformationTable(table);
3106 - break;
3107 - case "AdminExecuteSequence":
3108 - case "AdminUISequence":
3109 - case "AdvtExecuteSequence":
3110 - case "InstallExecuteSequence":
3111 - case "InstallUISequence":
3112 - case "ModuleAdminExecuteSequence":
3113 - case "ModuleAdminUISequence":
3114 - case "ModuleAdvtExecuteSequence":
3115 - case "ModuleInstallExecuteSequence":
3116 - case "ModuleInstallUISequence":
3117 - // handled in FinalizeSequenceTables
3118 - break;
3119 - case "ActionText":
3120 - this.DecompileActionTextTable(table);
3121 - break;
3122 - case "AdvtUISequence":
3123 - this.Messaging.Write(WarningMessages.DeprecatedTable(table.Name));
3124 - break;
3125 - case "AppId":
3126 - this.DecompileAppIdTable(table);
3127 - break;
3128 - case "AppSearch":
3129 - // handled in FinalizeSearchTables
3130 - break;
3131 - case "BBControl":
3132 - this.DecompileBBControlTable(table);
3133 - break;
3134 - case "Billboard":
3135 - this.DecompileBillboardTable(table);
3136 - break;
3137 - case "Binary":
3138 - this.DecompileBinaryTable(table);
3139 - break;
3140 - case "BindImage":
3141 - this.DecompileBindImageTable(table);
3142 - break;
3143 - case "CCPSearch":
3144 - // handled in FinalizeSearchTables
3145 - break;
3146 - case "CheckBox":
3147 - // handled in FinalizeCheckBoxTable
3148 - break;
3149 - case "Class":
3150 - this.DecompileClassTable(table);
3151 - break;
3152 - case "ComboBox":
3153 - this.DecompileComboBoxTable(table);
3154 - break;
3155 - case "Control":
3156 - this.DecompileControlTable(table);
3157 - break;
3158 - case "ControlCondition":
3159 - this.DecompileControlConditionTable(table);
3160 - break;
3161 - case "ControlEvent":
3162 - this.DecompileControlEventTable(table);
3163 - break;
3164 - case "CreateFolder":
3165 - this.DecompileCreateFolderTable(table);
3166 - break;
3167 - case "CustomAction":
3168 - this.DecompileCustomActionTable(table);
3169 - break;
3170 - case "CompLocator":
3171 - this.DecompileCompLocatorTable(table);
3172 - break;
3173 - case "Complus":
3174 - this.DecompileComplusTable(table);
3175 - break;
3176 - case "Component":
3177 - this.DecompileComponentTable(table);
3178 - break;
3179 - case "Condition":
3180 - this.DecompileConditionTable(table);
3181 - break;
3182 - case "Dialog":
3183 - this.DecompileDialogTable(table);
3184 - break;
3185 - case "Directory":
3186 - this.DecompileDirectoryTable(table);
3187 - break;
3188 - case "DrLocator":
3189 - this.DecompileDrLocatorTable(table);
3190 - break;
3191 - case "DuplicateFile":
3192 - this.DecompileDuplicateFileTable(table);
3193 - break;
3194 - case "Environment":
3195 - this.DecompileEnvironmentTable(table);
3196 - break;
3197 - case "Error":
3198 - this.DecompileErrorTable(table);
3199 - break;
3200 - case "EventMapping":
3201 - this.DecompileEventMappingTable(table);
3202 - break;
3203 - case "Extension":
3204 - this.DecompileExtensionTable(table);
3205 - break;
3206 - case "ExternalFiles":
3207 - this.DecompileExternalFilesTable(table);
3208 - break;
3209 - case "FamilyFileRanges":
3210 - // handled in FinalizeFamilyFileRangesTable
3211 - break;
3212 - case "Feature":
3213 - this.DecompileFeatureTable(table);
3214 - break;
3215 - case "FeatureComponents":
3216 - this.DecompileFeatureComponentsTable(table);
3217 - break;
3218 - case "File":
3219 - this.DecompileFileTable(table);
3220 - break;
3221 - case "FileSFPCatalog":
3222 - this.DecompileFileSFPCatalogTable(table);
3223 - break;
3224 - case "Font":
3225 - this.DecompileFontTable(table);
3226 - break;
3227 - case "Icon":
3228 - this.DecompileIconTable(table);
3229 - break;
3230 - case "ImageFamilies":
3231 - this.DecompileImageFamiliesTable(table);
3232 - break;
3233 - case "IniFile":
3234 - this.DecompileIniFileTable(table);
3235 - break;
3236 - case "IniLocator":
3237 - this.DecompileIniLocatorTable(table);
3238 - break;
3239 - case "IsolatedComponent":
3240 - this.DecompileIsolatedComponentTable(table);
3241 - break;
3242 - case "LaunchCondition":
3243 - this.DecompileLaunchConditionTable(table);
3244 - break;
3245 - case "ListBox":
3246 - this.DecompileListBoxTable(table);
3247 - break;
3248 - case "ListView":
3249 - this.DecompileListViewTable(table);
3250 - break;
3251 - case "LockPermissions":
3252 - this.DecompileLockPermissionsTable(table);
3253 - break;
3254 - case "Media":
3255 - this.DecompileMediaTable(table);
3256 - break;
3257 - case "MIME":
3258 - this.DecompileMIMETable(table);
3259 - break;
3260 - case "ModuleAdvtUISequence":
3261 - this.Messaging.Write(WarningMessages.DeprecatedTable(table.Name));
3262 - break;
3263 - case "ModuleComponents":
3264 - // handled by DecompileComponentTable (since the ModuleComponents table
3265 - // rows are created by nesting components under the Module element)
3266 - break;
3267 - case "ModuleConfiguration":
3268 - this.DecompileModuleConfigurationTable(table);
3269 - break;
3270 - case "ModuleDependency":
3271 - this.DecompileModuleDependencyTable(table);
3272 - break;
3273 - case "ModuleExclusion":
3274 - this.DecompileModuleExclusionTable(table);
3275 - break;
3276 - case "ModuleIgnoreTable":
3277 - this.DecompileModuleIgnoreTableTable(table);
3278 - break;
3279 - case "ModuleSignature":
3280 - this.DecompileModuleSignatureTable(table);
3281 - break;
3282 - case "ModuleSubstitution":
3283 - this.DecompileModuleSubstitutionTable(table);
3284 - break;
3285 - case "MoveFile":
3286 - this.DecompileMoveFileTable(table);
3287 - break;
3288 - case "MsiAssembly":
3289 - // handled in FinalizeFileTable
3290 - break;
3291 - case "MsiDigitalCertificate":
3292 - this.DecompileMsiDigitalCertificateTable(table);
3293 - break;
3294 - case "MsiDigitalSignature":
3295 - this.DecompileMsiDigitalSignatureTable(table);
3296 - break;
3297 - case "MsiEmbeddedChainer":
3298 - this.DecompileMsiEmbeddedChainerTable(table);
3299 - break;
3300 - case "MsiEmbeddedUI":
3301 - this.DecompileMsiEmbeddedUITable(table);
3302 - break;
3303 - case "MsiLockPermissionsEx":
3304 - this.DecompileMsiLockPermissionsExTable(table);
3305 - break;
3306 - case "MsiPackageCertificate":
3307 - this.DecompileMsiPackageCertificateTable(table);
3308 - break;
3309 - case "MsiPatchCertificate":
3310 - this.DecompileMsiPatchCertificateTable(table);
3311 - break;
3312 - case "MsiShortcutProperty":
3313 - this.DecompileMsiShortcutPropertyTable(table);
3314 - break;
3315 - case "ODBCAttribute":
3316 - this.DecompileODBCAttributeTable(table);
3317 - break;
3318 - case "ODBCDataSource":
3319 - this.DecompileODBCDataSourceTable(table);
3320 - break;
3321 - case "ODBCDriver":
3322 - this.DecompileODBCDriverTable(table);
3323 - break;
3324 - case "ODBCSourceAttribute":
3325 - this.DecompileODBCSourceAttributeTable(table);
3326 - break;
3327 - case "ODBCTranslator":
3328 - this.DecompileODBCTranslatorTable(table);
3329 - break;
3330 - case "PatchMetadata":
3331 - this.DecompilePatchMetadataTable(table);
3332 - break;
3333 - case "PatchSequence":
3334 - this.DecompilePatchSequenceTable(table);
3335 - break;
3336 - case "ProgId":
3337 - this.DecompileProgIdTable(table);
3338 - break;
3339 - case "Properties":
3340 - this.DecompilePropertiesTable(table);
3341 - break;
3342 - case "Property":
3343 - this.DecompilePropertyTable(table);
3344 - break;
3345 - case "PublishComponent":
3346 - this.DecompilePublishComponentTable(table);
3347 - break;
3348 - case "RadioButton":
3349 - this.DecompileRadioButtonTable(table);
3350 - break;
3351 - case "Registry":
3352 - this.DecompileRegistryTable(table);
3353 - break;
3354 - case "RegLocator":
3355 - this.DecompileRegLocatorTable(table);
3356 - break;
3357 - case "RemoveFile":
3358 - this.DecompileRemoveFileTable(table);
3359 - break;
3360 - case "RemoveIniFile":
3361 - this.DecompileRemoveIniFileTable(table);
3362 - break;
3363 - case "RemoveRegistry":
3364 - this.DecompileRemoveRegistryTable(table);
3365 - break;
3366 - case "ReserveCost":
3367 - this.DecompileReserveCostTable(table);
3368 - break;
3369 - case "SelfReg":
3370 - this.DecompileSelfRegTable(table);
3371 - break;
3372 - case "ServiceControl":
3373 - this.DecompileServiceControlTable(table);
3374 - break;
3375 - case "ServiceInstall":
3376 - this.DecompileServiceInstallTable(table);
3377 - break;
3378 - case "SFPCatalog":
3379 - this.DecompileSFPCatalogTable(table);
3380 - break;
3381 - case "Shortcut":
3382 - this.DecompileShortcutTable(table);
3383 - break;
3384 - case "Signature":
3385 - this.DecompileSignatureTable(table);
3386 - break;
3387 - case "TargetFiles_OptionalData":
3388 - this.DecompileTargetFiles_OptionalDataTable(table);
3389 - break;
3390 - case "TargetImages":
3391 - this.DecompileTargetImagesTable(table);
3392 - break;
3393 - case "TextStyle":
3394 - this.DecompileTextStyleTable(table);
3395 - break;
3396 - case "TypeLib":
3397 - this.DecompileTypeLibTable(table);
3398 - break;
3399 - case "Upgrade":
3400 - this.DecompileUpgradeTable(table);
3401 - break;
3402 - case "UpgradedFiles_OptionalData":
3403 - this.DecompileUpgradedFiles_OptionalDataTable(table);
3404 - break;
3405 - case "UpgradedFilesToIgnore":
3406 - this.DecompileUpgradedFilesToIgnoreTable(table);
3407 - break;
3408 - case "UpgradedImages":
3409 - this.DecompileUpgradedImagesTable(table);
3410 - break;
3411 - case "UIText":
3412 - this.DecompileUITextTable(table);
3413 - break;
3414 - case "Verb":
3415 - this.DecompileVerbTable(table);
3416 - break;
2533 + case "_SummaryInformation":
2534 + this.Decompile_SummaryInformationTable(table);
2535 + break;
2536 + case "AdminExecuteSequence":
2537 + case "AdminUISequence":
2538 + case "AdvtExecuteSequence":
2539 + case "InstallExecuteSequence":
2540 + case "InstallUISequence":
2541 + case "ModuleAdminExecuteSequence":
2542 + case "ModuleAdminUISequence":
2543 + case "ModuleAdvtExecuteSequence":
2544 + case "ModuleInstallExecuteSequence":
2545 + case "ModuleInstallUISequence":
2546 + // handled in FinalizeSequenceTables
2547 + break;
2548 + case "ActionText":
2549 + this.DecompileActionTextTable(table);
2550 + break;
2551 + case "AdvtUISequence":
2552 + this.Messaging.Write(WarningMessages.DeprecatedTable(table.Name));
2553 + break;
2554 + case "AppId":
2555 + this.DecompileAppIdTable(table);
2556 + break;
2557 + case "AppSearch":
2558 + // handled in FinalizeSearchTables
2559 + break;
2560 + case "BBControl":
2561 + this.DecompileBBControlTable(table);
2562 + break;
2563 + case "Billboard":
2564 + this.DecompileBillboardTable(table);
2565 + break;
2566 + case "Binary":
2567 + this.DecompileBinaryTable(table);
2568 + break;
2569 + case "BindImage":
2570 + this.DecompileBindImageTable(table);
2571 + break;
2572 + case "CCPSearch":
2573 + // handled in FinalizeSearchTables
2574 + break;
2575 + case "CheckBox":
2576 + // handled in FinalizeCheckBoxTable
2577 + break;
2578 + case "Class":
2579 + this.DecompileClassTable(table);
2580 + break;
2581 + case "ComboBox":
2582 + this.DecompileComboBoxTable(table);
2583 + break;
2584 + case "Control":
2585 + this.DecompileControlTable(table);
2586 + break;
2587 + case "ControlCondition":
2588 + this.DecompileControlConditionTable(table);
2589 + break;
2590 + case "ControlEvent":
2591 + this.DecompileControlEventTable(table);
2592 + break;
2593 + case "CreateFolder":
2594 + this.DecompileCreateFolderTable(table);
2595 + break;
2596 + case "CustomAction":
2597 + this.DecompileCustomActionTable(table);
2598 + break;
2599 + case "CompLocator":
2600 + this.DecompileCompLocatorTable(table);
2601 + break;
2602 + case "Complus":
2603 + this.DecompileComplusTable(table);
2604 + break;
2605 + case "Component":
2606 + this.DecompileComponentTable(table);
2607 + break;
2608 + case "Condition":
2609 + this.DecompileConditionTable(table);
2610 + break;
2611 + case "Dialog":
2612 + this.DecompileDialogTable(table);
2613 + break;
2614 + case "Directory":
2615 + this.DecompileDirectoryTable(table);
2616 + break;
2617 + case "DrLocator":
2618 + this.DecompileDrLocatorTable(table);
2619 + break;
2620 + case "DuplicateFile":
2621 + this.DecompileDuplicateFileTable(table);
2622 + break;
2623 + case "Environment":
2624 + this.DecompileEnvironmentTable(table);
2625 + break;
2626 + case "Error":
2627 + this.DecompileErrorTable(table);
2628 + break;
2629 + case "EventMapping":
2630 + this.DecompileEventMappingTable(table);
2631 + break;
2632 + case "Extension":
2633 + this.DecompileExtensionTable(table);
2634 + break;
2635 + case "ExternalFiles":
2636 + this.DecompileExternalFilesTable(table);
2637 + break;
2638 + case "FamilyFileRanges":
2639 + // handled in FinalizeFamilyFileRangesTable
2640 + break;
2641 + case "Feature":
2642 + this.DecompileFeatureTable(table);
2643 + break;
2644 + case "FeatureComponents":
2645 + this.DecompileFeatureComponentsTable(table);
2646 + break;
2647 + case "File":
2648 + this.DecompileFileTable(table);
2649 + break;
2650 + case "FileSFPCatalog":
2651 + this.DecompileFileSFPCatalogTable(table);
2652 + break;
2653 + case "Font":
2654 + this.DecompileFontTable(table);
2655 + break;
2656 + case "Icon":
2657 + this.DecompileIconTable(table);
2658 + break;
2659 + case "ImageFamilies":
2660 + this.DecompileImageFamiliesTable(table);
2661 + break;
2662 + case "IniFile":
2663 + this.DecompileIniFileTable(table);
2664 + break;
2665 + case "IniLocator":
2666 + this.DecompileIniLocatorTable(table);
2667 + break;
2668 + case "IsolatedComponent":
2669 + this.DecompileIsolatedComponentTable(table);
2670 + break;
2671 + case "LaunchCondition":
2672 + this.DecompileLaunchConditionTable(table);
2673 + break;
2674 + case "ListBox":
2675 + this.DecompileListBoxTable(table);
2676 + break;
2677 + case "ListView":
2678 + this.DecompileListViewTable(table);
2679 + break;
2680 + case "LockPermissions":
2681 + this.DecompileLockPermissionsTable(table);
2682 + break;
2683 + case "Media":
2684 + this.DecompileMediaTable(table);
2685 + break;
2686 + case "MIME":
2687 + this.DecompileMIMETable(table);
2688 + break;
2689 + case "ModuleAdvtUISequence":
2690 + this.Messaging.Write(WarningMessages.DeprecatedTable(table.Name));
2691 + break;
2692 + case "ModuleComponents":
2693 + // handled by DecompileComponentTable (since the ModuleComponents table
2694 + // rows are created by nesting components under the Module element)
2695 + break;
2696 + case "ModuleConfiguration":
2697 + this.DecompileModuleConfigurationTable(table);
2698 + break;
2699 + case "ModuleDependency":
2700 + this.DecompileModuleDependencyTable(table);
2701 + break;
2702 + case "ModuleExclusion":
2703 + this.DecompileModuleExclusionTable(table);
2704 + break;
2705 + case "ModuleIgnoreTable":
2706 + this.DecompileModuleIgnoreTableTable(table);
2707 + break;
2708 + case "ModuleSignature":
2709 + this.DecompileModuleSignatureTable(table);
2710 + break;
2711 + case "ModuleSubstitution":
2712 + this.DecompileModuleSubstitutionTable(table);
2713 + break;
2714 + case "MoveFile":
2715 + this.DecompileMoveFileTable(table);
2716 + break;
2717 + case "MsiAssembly":
2718 + // handled in FinalizeFileTable
2719 + break;
2720 + case "MsiDigitalCertificate":
2721 + this.DecompileMsiDigitalCertificateTable(table);
2722 + break;
2723 + case "MsiDigitalSignature":
2724 + this.DecompileMsiDigitalSignatureTable(table);
2725 + break;
2726 + case "MsiEmbeddedChainer":
2727 + this.DecompileMsiEmbeddedChainerTable(table);
2728 + break;
2729 + case "MsiEmbeddedUI":
2730 + this.DecompileMsiEmbeddedUITable(table);
2731 + break;
2732 + case "MsiLockPermissionsEx":
2733 + this.DecompileMsiLockPermissionsExTable(table);
2734 + break;
2735 + case "MsiPackageCertificate":
2736 + this.DecompileMsiPackageCertificateTable(table);
2737 + break;
2738 + case "MsiPatchCertificate":
2739 + this.DecompileMsiPatchCertificateTable(table);
2740 + break;
2741 + case "MsiShortcutProperty":
2742 + this.DecompileMsiShortcutPropertyTable(table);
2743 + break;
2744 + case "ODBCAttribute":
2745 + this.DecompileODBCAttributeTable(table);
2746 + break;
2747 + case "ODBCDataSource":
2748 + this.DecompileODBCDataSourceTable(table);
2749 + break;
2750 + case "ODBCDriver":
2751 + this.DecompileODBCDriverTable(table);
2752 + break;
2753 + case "ODBCSourceAttribute":
2754 + this.DecompileODBCSourceAttributeTable(table);
2755 + break;
2756 + case "ODBCTranslator":
2757 + this.DecompileODBCTranslatorTable(table);
2758 + break;
2759 + case "PatchMetadata":
2760 + this.DecompilePatchMetadataTable(table);
2761 + break;
2762 + case "PatchSequence":
2763 + this.DecompilePatchSequenceTable(table);
2764 + break;
2765 + case "ProgId":
2766 + this.DecompileProgIdTable(table);
2767 + break;
2768 + case "Properties":
2769 + this.DecompilePropertiesTable(table);
2770 + break;
2771 + case "Property":
2772 + this.DecompilePropertyTable(table);
2773 + break;
2774 + case "PublishComponent":
2775 + this.DecompilePublishComponentTable(table);
2776 + break;
2777 + case "RadioButton":
2778 + this.DecompileRadioButtonTable(table);
2779 + break;
2780 + case "Registry":
2781 + this.DecompileRegistryTable(table);
2782 + break;
2783 + case "RegLocator":
2784 + this.DecompileRegLocatorTable(table);
2785 + break;
2786 + case "RemoveFile":
2787 + this.DecompileRemoveFileTable(table);
2788 + break;
2789 + case "RemoveIniFile":
2790 + this.DecompileRemoveIniFileTable(table);
2791 + break;
2792 + case "RemoveRegistry":
2793 + this.DecompileRemoveRegistryTable(table);
2794 + break;
2795 + case "ReserveCost":
2796 + this.DecompileReserveCostTable(table);
2797 + break;
2798 + case "SelfReg":
2799 + this.DecompileSelfRegTable(table);
2800 + break;
2801 + case "ServiceControl":
2802 + this.DecompileServiceControlTable(table);
2803 + break;
2804 + case "ServiceInstall":
2805 + this.DecompileServiceInstallTable(table);
2806 + break;
2807 + case "SFPCatalog":
2808 + this.DecompileSFPCatalogTable(table);
2809 + break;
2810 + case "Shortcut":
2811 + this.DecompileShortcutTable(table);
2812 + break;
2813 + case "Signature":
2814 + this.DecompileSignatureTable(table);
2815 + break;
2816 + case "TargetFiles_OptionalData":
2817 + this.DecompileTargetFiles_OptionalDataTable(table);
2818 + break;
2819 + case "TargetImages":
2820 + this.DecompileTargetImagesTable(table);
2821 + break;
2822 + case "TextStyle":
2823 + this.DecompileTextStyleTable(table);
2824 + break;
2825 + case "TypeLib":
2826 + this.DecompileTypeLibTable(table);
2827 + break;
2828 + case "Upgrade":
2829 + this.DecompileUpgradeTable(table);
2830 + break;
2831 + case "UpgradedFiles_OptionalData":
2832 + this.DecompileUpgradedFiles_OptionalDataTable(table);
2833 + break;
2834 + case "UpgradedFilesToIgnore":
2835 + this.DecompileUpgradedFilesToIgnoreTable(table);
2836 + break;
2837 + case "UpgradedImages":
2838 + this.DecompileUpgradedImagesTable(table);
2839 + break;
2840 + case "UIText":
2841 + this.DecompileUITextTable(table);
2842 + break;
2843 + case "Verb":
2844 + this.DecompileVerbTable(table);
2845 + break;
2846
3418 - default:
2847 + default:
2848 #if TODO_DECOMPILER_EXTENSIONS
2849 if (this.ExtensionsByTableName.TryGetValue(table.Name, out var extension)
2850 {
@@ -3423,11 +2852,11 @@ namespace WixToolset.Core.WindowsInstaller
2852 }
2853 else
2854 #endif
3426 - if (!this.SuppressCustomTables)
3427 - {
3428 - this.DecompileCustomTable(table);
3429 - }
3430 - break;
2855 + if (!this.SuppressCustomTables)
2856 + {
2857 + this.DecompileCustomTable(table);
2858 + }
2859 + break;
2860 }
2861 }
2862 }
@@ -3442,87 +2871,87 @@ namespace WixToolset.Core.WindowsInstaller
2871 {
2872 switch (tableName)
2873 {
3445 - case "ActionText":
3446 - case "BBControl":
3447 - case "Billboard":
3448 - case "CheckBox":
3449 - case "Control":
3450 - case "ControlCondition":
3451 - case "ControlEvent":
3452 - case "Dialog":
3453 - case "Error":
3454 - case "EventMapping":
3455 - case "RadioButton":
3456 - case "TextStyle":
3457 - case "UIText":
3458 - return !this.SuppressUI;
3459 - case "ModuleAdminExecuteSequence":
3460 - case "ModuleAdminUISequence":
3461 - case "ModuleAdvtExecuteSequence":
3462 - case "ModuleAdvtUISequence":
3463 - case "ModuleComponents":
3464 - case "ModuleConfiguration":
3465 - case "ModuleDependency":
3466 - case "ModuleIgnoreTable":
3467 - case "ModuleInstallExecuteSequence":
3468 - case "ModuleInstallUISequence":
3469 - case "ModuleExclusion":
3470 - case "ModuleSignature":
3471 - case "ModuleSubstitution":
3472 - if (OutputType.Module != output.Type)
3473 - {
3474 - this.Messaging.Write(WarningMessages.SkippingMergeModuleTable(output.SourceLineNumbers, tableName));
3475 - return false;
3476 - }
3477 - else
3478 - {
3479 - return true;
3480 - }
3481 - case "ExternalFiles":
3482 - case "FamilyFileRanges":
3483 - case "ImageFamilies":
3484 - case "PatchMetadata":
3485 - case "PatchSequence":
3486 - case "Properties":
3487 - case "TargetFiles_OptionalData":
3488 - case "TargetImages":
3489 - case "UpgradedFiles_OptionalData":
3490 - case "UpgradedFilesToIgnore":
3491 - case "UpgradedImages":
3492 - if (OutputType.PatchCreation != output.Type)
3493 - {
3494 - this.Messaging.Write(WarningMessages.SkippingPatchCreationTable(output.SourceLineNumbers, tableName));
2874 + case "ActionText":
2875 + case "BBControl":
2876 + case "Billboard":
2877 + case "CheckBox":
2878 + case "Control":
2879 + case "ControlCondition":
2880 + case "ControlEvent":
2881 + case "Dialog":
2882 + case "Error":
2883 + case "EventMapping":
2884 + case "RadioButton":
2885 + case "TextStyle":
2886 + case "UIText":
2887 + return !this.SuppressUI;
2888 + case "ModuleAdminExecuteSequence":
2889 + case "ModuleAdminUISequence":
2890 + case "ModuleAdvtExecuteSequence":
2891 + case "ModuleAdvtUISequence":
2892 + case "ModuleComponents":
2893 + case "ModuleConfiguration":
2894 + case "ModuleDependency":
2895 + case "ModuleIgnoreTable":
2896 + case "ModuleInstallExecuteSequence":
2897 + case "ModuleInstallUISequence":
2898 + case "ModuleExclusion":
2899 + case "ModuleSignature":
2900 + case "ModuleSubstitution":
2901 + if (OutputType.Module != output.Type)
2902 + {
2903 + this.Messaging.Write(WarningMessages.SkippingMergeModuleTable(output.SourceLineNumbers, tableName));
2904 + return false;
2905 + }
2906 + else
2907 + {
2908 + return true;
2909 + }
2910 + case "ExternalFiles":
2911 + case "FamilyFileRanges":
2912 + case "ImageFamilies":
2913 + case "PatchMetadata":
2914 + case "PatchSequence":
2915 + case "Properties":
2916 + case "TargetFiles_OptionalData":
2917 + case "TargetImages":
2918 + case "UpgradedFiles_OptionalData":
2919 + case "UpgradedFilesToIgnore":
2920 + case "UpgradedImages":
2921 + if (OutputType.PatchCreation != output.Type)
2922 + {
2923 + this.Messaging.Write(WarningMessages.SkippingPatchCreationTable(output.SourceLineNumbers, tableName));
2924 + return false;
2925 + }
2926 + else
2927 + {
2928 + return true;
2929 + }
2930 + case "MsiPatchHeaders":
2931 + case "MsiPatchMetadata":
2932 + case "MsiPatchOldAssemblyName":
2933 + case "MsiPatchOldAssemblyFile":
2934 + case "MsiPatchSequence":
2935 + case "Patch":
2936 + case "PatchPackage":
2937 + this.Messaging.Write(WarningMessages.PatchTable(output.SourceLineNumbers, tableName));
2938 return false;
3496 - }
3497 - else
3498 - {
2939 + case "_SummaryInformation":
2940 return true;
3500 - }
3501 - case "MsiPatchHeaders":
3502 - case "MsiPatchMetadata":
3503 - case "MsiPatchOldAssemblyName":
3504 - case "MsiPatchOldAssemblyFile":
3505 - case "MsiPatchSequence":
3506 - case "Patch":
3507 - case "PatchPackage":
3508 - this.Messaging.Write(WarningMessages.PatchTable(output.SourceLineNumbers, tableName));
3509 - return false;
3510 - case "_SummaryInformation":
3511 - return true;
3512 - case "_Validation":
3513 - case "MsiAssemblyName":
3514 - case "MsiFileHash":
3515 - return false;
3516 - default: // all other tables are allowed in any output except for a patch creation package
3517 - if (OutputType.PatchCreation == output.Type)
3518 - {
3519 - this.Messaging.Write(WarningMessages.IllegalPatchCreationTable(output.SourceLineNumbers, tableName));
2941 + case "_Validation":
2942 + case "MsiAssemblyName":
2943 + case "MsiFileHash":
2944 return false;
3521 - }
3522 - else
3523 - {
3524 - return true;
3525 - }
2945 + default: // all other tables are allowed in any output except for a patch creation package
2946 + if (OutputType.PatchCreation == output.Type)
2947 + {
2948 + this.Messaging.Write(WarningMessages.IllegalPatchCreationTable(output.SourceLineNumbers, tableName));
2949 + return false;
2950 + }
2951 + else
2952 + {
2953 + return true;
2954 + }
2955 }
2956 }
2957
@@ -3534,200 +2963,177 @@ namespace WixToolset.Core.WindowsInstaller
2963 {
2964 if (OutputType.Module == this.OutputType || OutputType.Product == this.OutputType)
2965 {
3537 - var package = new Wix.Package();
2966 + var xPackage = new XElement(Names.PackageElement);
2967
2968 foreach (var row in table.Rows)
2969 {
3541 - var value = Convert.ToString(row[1]);
2970 + var value = row.FieldAsString(1);
2971
3543 - if (null != value && 0 < value.Length)
2972 + if (!String.IsNullOrEmpty(value))
2973 {
3545 - switch (Convert.ToInt32(row[0]))
2974 + switch (row.FieldAsInteger(0))
2975 {
3547 - case 1:
3548 - if ("1252" != value)
3549 - {
3550 - package.SummaryCodepage = value;
3551 - }
3552 - break;
3553 - case 3:
3554 - package.Description = value;
3555 - break;
3556 - case 4:
3557 - package.Manufacturer = value;
3558 - break;
3559 - case 5:
3560 - if ("Installer" != value)
3561 - {
3562 - package.Keywords = value;
3563 - }
3564 - break;
3565 - case 6:
3566 - if (!value.StartsWith("This installer database contains the logic and data required to install "))
3567 - {
3568 - package.Comments = value;
3569 - }
3570 - break;
3571 - case 7:
3572 - var template = value.Split(';');
3573 - if (0 < template.Length && 0 < template[template.Length - 1].Length)
3574 - {
3575 - package.Languages = template[template.Length - 1];
3576 - }
3577 -
3578 - if (1 < template.Length && null != template[0] && 0 < template[0].Length)
3579 - {
3580 - switch (template[0])
2976 + case 1:
2977 + if ("1252" != value)
2978 {
3582 - case "Intel":
3583 - package.Platform = WixToolset.Data.Serialize.Package.PlatformType.x86;
3584 - break;
3585 - case "Intel64":
3586 - package.Platform = WixToolset.Data.Serialize.Package.PlatformType.ia64;
3587 - break;
3588 - case "x64":
3589 - package.Platform = WixToolset.Data.Serialize.Package.PlatformType.x64;
3590 - break;
2979 + xPackage.SetAttributeValue("SummaryCodepage", value);
2980 + }
2981 + break;
2982 + case 3:
2983 + xPackage.SetAttributeValue("Description", value);
2984 + break;
2985 + case 4:
2986 + xPackage.SetAttributeValue("Manufacturer", value);
2987 + break;
2988 + case 5:
2989 + if ("Installer" != value)
2990 + {
2991 + xPackage.SetAttributeValue("Keywords", value);
2992 + }
2993 + break;
2994 + case 6:
2995 + if (!value.StartsWith("This installer database contains the logic and data required to install "))
2996 + {
2997 + xPackage.SetAttributeValue("Comments", value);
2998 + }
2999 + break;
3000 + case 7:
3001 + var template = value.Split(';');
3002 + if (0 < template.Length && 0 < template[template.Length - 1].Length)
3003 + {
3004 + xPackage.SetAttributeValue("Languages", template[template.Length - 1]);
3005 }
3592 - }
3593 - break;
3594 - case 9:
3595 - if (OutputType.Module == this.OutputType)
3596 - {
3597 - this.modularizationGuid = value;
3598 - package.Id = value;
3599 - }
3600 - break;
3601 - case 14:
3602 - package.InstallerVersion = Convert.ToInt32(row[1], CultureInfo.InvariantCulture);
3603 - break;
3604 - case 15:
3605 - var wordCount = Convert.ToInt32(row[1], CultureInfo.InvariantCulture);
3606 - if (0x1 == (wordCount & 0x1))
3607 - {
3608 - this.shortNames = true;
3609 - package.ShortNames = Wix.YesNoType.yes;
3610 - }
3006
3612 - if (0x2 == (wordCount & 0x2))
3613 - {
3614 - this.compressed = true;
3007 + if (1 < template.Length && null != template[0] && 0 < template[0].Length)
3008 + {
3009 + switch (template[0])
3010 + {
3011 + case "Intel":
3012 + xPackage.SetAttributeValue("Platform", "x86");
3013 + break;
3014 + case "Intel64":
3015 + xPackage.SetAttributeValue("Platform", "ia64");
3016 + break;
3017 + case "x64":
3018 + xPackage.SetAttributeValue("Platform", "x64");
3019 + break;
3020 + case "Arm":
3021 + xPackage.SetAttributeValue("Platform", "arm");
3022 + break;
3023 + case "Arm64":
3024 + xPackage.SetAttributeValue("Platform", "arm64");
3025 + break;
3026 + }
3027 + }
3028 + break;
3029 + case 9:
3030 + if (OutputType.Module == this.OutputType)
3031 + {
3032 + this.ModularizationGuid = value;
3033 + xPackage.SetAttributeValue("Id", value);
3034 + }
3035 + break;
3036 + case 14:
3037 + xPackage.SetAttributeValue("InstallerVersion", row.FieldAsInteger(1));
3038 + break;
3039 + case 15:
3040 + var wordCount = row.FieldAsInteger(1);
3041 + if (0x1 == (wordCount & 0x1))
3042 + {
3043 + this.ShortNames = true;
3044 + xPackage.SetAttributeValue("ShortNames", "yes");
3045 + }
3046
3616 - if (OutputType.Product == this.OutputType)
3047 + if (0x2 == (wordCount & 0x2))
3048 {
3618 - package.Compressed = Wix.YesNoType.yes;
3049 + this.Compressed = true;
3050 +
3051 + if (OutputType.Product == this.OutputType)
3052 + {
3053 + xPackage.SetAttributeValue("Compressed", "yes");
3054 + }
3055 }
3620 - }
3056
3622 - if (0x4 == (wordCount & 0x4))
3623 - {
3624 - package.AdminImage = Wix.YesNoType.yes;
3625 - }
3057 + if (0x4 == (wordCount & 0x4))
3058 + {
3059 + xPackage.SetAttributeValue("AdminImage", "yes");
3060 + }
3061
3627 - if (0x8 == (wordCount & 0x8))
3628 - {
3629 - package.InstallPrivileges = Wix.Package.InstallPrivilegesType.limited;
3630 - }
3062 + if (0x8 == (wordCount & 0x8))
3063 + {
3064 + xPackage.SetAttributeValue("InstallPrivileges", "limited");
3065 + }
3066
3632 - break;
3633 - case 19:
3634 - var security = Convert.ToInt32(row[1], CultureInfo.InvariantCulture);
3635 - switch (security)
3636 - {
3637 - case 0:
3638 - package.ReadOnly = Wix.YesNoDefaultType.no;
3067 break;
3640 - case 4:
3641 - package.ReadOnly = Wix.YesNoDefaultType.yes;
3068 + case 19:
3069 + var security = row.FieldAsInteger(1);
3070 + switch (security)
3071 + {
3072 + case 0:
3073 + xPackage.SetAttributeValue("ReadOnly", "no");
3074 + break;
3075 + case 4:
3076 + xPackage.SetAttributeValue("ReadOnly", "yes");
3077 + break;
3078 + }
3079 break;
3643 - }
3644 - break;
3080 }
3081 }
3082 }
3083
3649 - this.core.RootElement.AddChild(package);
3084 + this.RootElement.Add(xPackage);
3085 }
3086 else
3087 {
3653 - var patchInformation = new Wix.PatchInformation();
3088 + var xPatchInformation = new XElement(Names.PatchInformationElement);
3089
3090 foreach (var row in table.Rows)
3091 {
3657 - var propertyId = Convert.ToInt32(row[0]);
3658 - var value = Convert.ToString(row[1]);
3092 + var propertyId = row.FieldAsInteger(0);
3093 + var value = row.FieldAsString(1);
3094
3660 - if (null != row[1] && 0 < value.Length)
3095 + if (!String.IsNullOrEmpty(value))
3096 {
3097 switch (propertyId)
3098 {
3664 - case 1:
3665 - if ("1252" != value)
3666 - {
3667 - patchInformation.SummaryCodepage = value;
3668 - }
3669 - break;
3670 - case 3:
3671 - patchInformation.Description = value;
3672 - break;
3673 - case 4:
3674 - patchInformation.Manufacturer = value;
3675 - break;
3676 - case 5:
3677 - if ("Installer,Patching,PCP,Database" != value)
3678 - {
3679 - patchInformation.Keywords = value;
3680 - }
3681 - break;
3682 - case 6:
3683 - patchInformation.Comments = value;
3684 - break;
3685 - case 7:
3686 - var template = value.Split(';');
3687 - if (0 < template.Length && 0 < template[template.Length - 1].Length)
3688 - {
3689 - patchInformation.Languages = template[template.Length - 1];
3690 - }
3691 -
3692 - if (1 < template.Length && null != template[0] && 0 < template[0].Length)
3693 - {
3694 - patchInformation.Platforms = template[0];
3695 - }
3696 - break;
3697 - case 15:
3698 - var wordCount = Convert.ToInt32(value, CultureInfo.InvariantCulture);
3699 - if (0x1 == (wordCount & 0x1))
3700 - {
3701 - patchInformation.ShortNames = Wix.YesNoType.yes;
3702 - }
3703 -
3704 - if (0x2 == (wordCount & 0x2))
3705 - {
3706 - patchInformation.Compressed = Wix.YesNoType.yes;
3707 - }
3708 -
3709 - if (0x4 == (wordCount & 0x4))
3710 - {
3711 - patchInformation.AdminImage = Wix.YesNoType.yes;
3712 - }
3713 - break;
3714 - case 19:
3715 - var security = Convert.ToInt32(value, CultureInfo.InvariantCulture);
3716 - switch (security)
3717 - {
3718 - case 0:
3719 - patchInformation.ReadOnly = Wix.YesNoDefaultType.no;
3099 + case 1:
3100 + if ("1252" != value)
3101 + {
3102 + xPatchInformation.SetAttributeValue("SummaryCodepage", value);
3103 + }
3104 + break;
3105 + case 3:
3106 + xPatchInformation.SetAttributeValue("Description", value);
3107 break;
3108 case 4:
3722 - patchInformation.ReadOnly = Wix.YesNoDefaultType.yes;
3109 + xPatchInformation.SetAttributeValue("Manufacturer", value);
3110 + break;
3111 + case 5:
3112 + if ("Installer,Patching,PCP,Database" != value)
3113 + {
3114 + xPatchInformation.SetAttributeValue("Keywords", value);
3115 + }
3116 + break;
3117 + case 6:
3118 + xPatchInformation.SetAttributeValue("Comments", value);
3119 + break;
3120 + case 19:
3121 + var security = Convert.ToInt32(value, CultureInfo.InvariantCulture);
3122 + switch (security)
3123 + {
3124 + case 0:
3125 + xPatchInformation.SetAttributeValue("ReadOnly", "no");
3126 + break;
3127 + case 4:
3128 + xPatchInformation.SetAttributeValue("ReadOnly", "yes");
3129 + break;
3130 + }
3131 break;
3724 - }
3725 - break;
3132 }
3133 }
3134 }
3135
3730 - this.core.RootElement.AddChild(patchInformation);
3136 + this.RootElement.Add(xPatchInformation);
3137 }
3138 }
3139
@@ -3739,21 +3145,12 @@ namespace WixToolset.Core.WindowsInstaller
3145 {
3146 foreach (var row in table.Rows)
3147 {
3742 - var progressText = new Wix.ProgressText();
3148 + var progressText = new XElement(Names.ProgressTextElement,
3149 + new XAttribute("Action", row.FieldAsString(0)),
3150 + row.IsColumnNull(1) ? null : new XAttribute("Content", row.FieldAsString(1)),
3151 + row.IsColumnNull(2) ? null : new XAttribute("Template", row.FieldAsString(2)));
3152
3744 - progressText.Action = Convert.ToString(row[0]);
3745 -
3746 - if (null != row[1])
3747 - {
3748 - progressText.Content = Convert.ToString(row[1]);
3749 - }
3750 -
3751 - if (null != row[2])
3752 - {
3753 - progressText.Template = Convert.ToString(row[2]);
3754 - }
3755 -
3756 - this.core.UIElement.AddChild(progressText);
3153 + this.UIElement.Add(progressText);
3154 }
3155 }
3156
@@ -3765,44 +3162,18 @@ namespace WixToolset.Core.WindowsInstaller
3162 {
3163 foreach (var row in table.Rows)
3164 {
3768 - var appId = new Wix.AppId();
3769 -
3770 - appId.Advertise = Wix.YesNoType.yes;
3771 -
3772 - appId.Id = Convert.ToString(row[0]);
3773 -
3774 - if (null != row[1])
3775 - {
3776 - appId.RemoteServerName = Convert.ToString(row[1]);
3777 - }
3778 -
3779 - if (null != row[2])
3780 - {
3781 - appId.LocalService = Convert.ToString(row[2]);
3782 - }
3165 + var appId = new XElement(Names.AppIdElement,
3166 + new XAttribute("Advertise", "yes"),
3167 + new XAttribute("Id", row.FieldAsString(0)),
3168 + row.IsColumnNull(1) ? null : new XAttribute("RemoteServerName", row.FieldAsString(1)),
3169 + row.IsColumnNull(2) ? null : new XAttribute("LocalService", row.FieldAsString(2)),
3170 + row.IsColumnNull(3) ? null : new XAttribute("ServiceParameters", row.FieldAsString(3)),
3171 + row.IsColumnNull(4) ? null : new XAttribute("DllSurrogate", row.FieldAsString(4)),
3172 + row.IsColumnNull(5) || row.FieldAsInteger(5) != 1 ? null : new XAttribute("ActivateAtStorage", "yes"),
3173 + row.IsColumnNull(6) || row.FieldAsInteger(6) != 1 ? null : new XAttribute("RunAsInteractiveUser", "yes"));
3174
3784 - if (null != row[3])
3785 - {
3786 - appId.ServiceParameters = Convert.ToString(row[3]);
3787 - }
3788 -
3789 - if (null != row[4])
3790 - {
3791 - appId.DllSurrogate = Convert.ToString(row[4]);
3792 - }
3793 -
3794 - if (null != row[5] && Int32.Equals(row[5], 1))
3795 - {
3796 - appId.ActivateAtStorage = Wix.YesNoType.yes;
3797 - }
3798 -
3799 - if (null != row[6] && Int32.Equals(row[6], 1))
3800 - {
3801 - appId.RunAsInteractiveUser = Wix.YesNoType.yes;
3802 - }
3803 -
3804 - this.core.RootElement.AddChild(appId);
3805 - this.core.IndexElement(row, appId);
3175 + this.RootElement.Add(appId);
3176 + this.IndexElement(row, appId);
3177 }
3178 }
3179
@@ -3814,34 +3185,23 @@ namespace WixToolset.Core.WindowsInstaller
3185 {
3186 foreach (BBControlRow bbControlRow in table.Rows)
3187 {
3817 - var control = new Wix.Control();
3818 -
3819 - control.Id = bbControlRow.BBControl;
3820 -
3821 - control.Type = bbControlRow.Type;
3822 -
3823 - control.X = bbControlRow.X;
3824 -
3825 - control.Y = bbControlRow.Y;
3826 -
3827 - control.Width = bbControlRow.Width;
3828 -
3829 - control.Height = bbControlRow.Height;
3188 + var xControl = new XElement(Names.ControlElement,
3189 + new XAttribute("Id", bbControlRow.BBControl),
3190 + new XAttribute("Type", bbControlRow.Type),
3191 + new XAttribute("X", bbControlRow.X),
3192 + new XAttribute("Y", bbControlRow.Y),
3193 + new XAttribute("Width", bbControlRow.Width),
3194 + new XAttribute("Height", bbControlRow.Height),
3195 + null == bbControlRow.Text ? null : new XAttribute("Text", bbControlRow.Text));
3196
3197 if (null != bbControlRow[7])
3198 {
3833 - SetControlAttributes(bbControlRow.Attributes, control);
3199 + SetControlAttributes(bbControlRow.Attributes, xControl);
3200 }
3201
3836 - if (null != bbControlRow.Text)
3202 + if (this.TryGetIndexedElement("Billboard", out var xBillboard, bbControlRow.Billboard))
3203 {
3838 - control.Text = bbControlRow.Text;
3839 - }
3840 -
3841 - var billboard = (Wix.Billboard)this.core.GetIndexedElement("Billboard", bbControlRow.Billboard);
3842 - if (null != billboard)
3843 - {
3844 - billboard.AddChild(control);
3204 + xBillboard.Add(xControl);
3205 }
3206 else
3207 {
@@ -3856,37 +3216,34 @@ namespace WixToolset.Core.WindowsInstaller
3216 /// <param name="table">The table to decompile.</param>
3217 private void DecompileBillboardTable(Table table)
3218 {
3859 - var billboardActions = new Hashtable();
3860 - var billboards = new SortedList();
3219 + var billboards = new SortedList<string, Row>();
3220
3221 foreach (var row in table.Rows)
3863 - {
3864 - var billboard = new Wix.Billboard();
3865 -
3866 - billboard.Id = Convert.ToString(row[0]);
3867 -
3868 - billboard.Feature = Convert.ToString(row[1]);
3222 + {
3223 + var xBillboard = new XElement(Names.BillboardElement,
3224 + new XAttribute("Id", row.FieldAsString(0)),
3225 + new XAttribute("Feature", row.FieldAsString(1)));
3226
3870 - this.core.IndexElement(row, billboard);
3227 + this.IndexElement(row, xBillboard);
3228 billboards.Add(String.Format(CultureInfo.InvariantCulture, "{0}|{1:0000000000}", row[0], row[3]), row);
3229 }
3230
3874 - foreach (Row row in billboards.Values)
3231 + var billboardActions = new Dictionary<string, XElement>();
3232 +
3233 + foreach (var row in billboards.Values)
3234 {
3876 - var billboard = (Wix.Billboard)this.core.GetIndexedElement(row);
3877 - var billboardAction = (Wix.BillboardAction)billboardActions[row[2]];
3235 + var xBillboard = this.GetIndexedElement(row);
3236
3879 - if (null == billboardAction)
3237 + if (!billboardActions.TryGetValue(row.FieldAsString(2), out var xBillboardAction))
3238 {
3881 - billboardAction = new Wix.BillboardAction();
3239 + xBillboardAction = new XElement(Names.BillboardActionElement,
3240 + new XAttribute("Id", row.FieldAsString(2)));
3241
3883 - billboardAction.Id = Convert.ToString(row[2]);
3884 -
3885 - this.core.UIElement.AddChild(billboardAction);
3886 - billboardActions.Add(row[2], billboardAction);
3242 + this.UIElement.Add(xBillboardAction);
3243 + billboardActions.Add(row.FieldAsString(2), xBillboardAction);
3244 }
3245
3889 - billboardAction.AddChild(billboard);
3246 + xBillboardAction.Add(xBillboard);
3247 }
3248 }
3249
@@ -3898,13 +3255,11 @@ namespace WixToolset.Core.WindowsInstaller
3255 {
3256 foreach (var row in table.Rows)
3257 {
3901 - var binary = new Wix.Binary();
3902 -
3903 - binary.Id = Convert.ToString(row[0]);
3258 + var xBinary = new XElement(Names.BinaryElement,
3259 + new XAttribute("Id", row.FieldAsString(0)),
3260 + new XAttribute("SourceFile", row.FieldAsString(1)));
3261
3905 - binary.SourceFile = Convert.ToString(row[1]);
3906 -
3907 - this.core.RootElement.AddChild(binary);
3262 + this.RootElement.Add(xBinary);
3263 }
3264 }
3265
@@ -3916,15 +3271,13 @@ namespace WixToolset.Core.WindowsInstaller
3271 {
3272 foreach (var row in table.Rows)
3273 {
3919 - var file = (Wix.File)this.core.GetIndexedElement("File", Convert.ToString(row[0]));
3920 -
3921 - if (null != file)
3274 + if (this.TryGetIndexedElement("File", out var xFile, row.FieldAsString(0)))
3275 {
3923 - file.BindPath = Convert.ToString(row[1]);
3276 + xFile.SetAttributeValue("BindPath", row.FieldAsString(1));
3277 }
3278 else
3279 {
3927 - this.Messaging.Write(WarningMessages.ExpectedForeignRow(row.SourceLineNumbers, table.Name, row.GetPrimaryKey(DecompilerConstants.PrimaryKeyDelimiter), "File_", Convert.ToString(row[0]), "File"));
3280 + this.Messaging.Write(WarningMessages.ExpectedForeignRow(row.SourceLineNumbers, table.Name, row.GetPrimaryKey(DecompilerConstants.PrimaryKeyDelimiter), "File_", row.FieldAsString(0), "File"));
3281 }
3282 }
3283 }
@@ -3937,46 +3290,20 @@ namespace WixToolset.Core.WindowsInstaller
3290 {
3291 foreach (var row in table.Rows)
3292 {
3940 - var wixClass = new Wix.Class();
3941 -
3942 - wixClass.Advertise = Wix.YesNoType.yes;
3943 -
3944 - wixClass.Id = Convert.ToString(row[0]);
3945 -
3946 - switch (Convert.ToString(row[1]))
3947 - {
3948 - case "LocalServer":
3949 - wixClass.Context = Wix.Class.ContextType.LocalServer;
3950 - break;
3951 - case "LocalServer32":
3952 - wixClass.Context = Wix.Class.ContextType.LocalServer32;
3953 - break;
3954 - case "InprocServer":
3955 - wixClass.Context = Wix.Class.ContextType.InprocServer;
3956 - break;
3957 - case "InprocServer32":
3958 - wixClass.Context = Wix.Class.ContextType.InprocServer32;
3959 - break;
3960 - default:
3961 - this.Messaging.Write(WarningMessages.IllegalColumnValue(row.SourceLineNumbers, table.Name, row.Fields[1].Column.Name, row[1]));
3962 - break;
3963 - }
3964 -
3965 - // ProgId children are handled in FinalizeProgIdTable
3966 -
3967 - if (null != row[4])
3968 - {
3969 - wixClass.Description = Convert.ToString(row[4]);
3970 - }
3971 -
3972 - if (null != row[5])
3973 - {
3974 - wixClass.AppId = Convert.ToString(row[5]);
3975 - }
3293 + var xClass = new XElement(Names.ClassElement,
3294 + new XAttribute("Id", row.FieldAsString(0)),
3295 + new XAttribute("Advertise", "yes"),
3296 + new XAttribute("Context", row.FieldAsString(1)),
3297 + row.IsColumnNull(4) ? null : new XAttribute("Description", row.FieldAsString(4)),
3298 + row.IsColumnNull(5) ? null : new XAttribute("AppId", row.FieldAsString(5)),
3299 + row.IsColumnNull(7) ? null : new XAttribute("Icon", row.FieldAsString(7)),
3300 + row.IsColumnNull(8) ? null : new XAttribute("IconIndex", row.FieldAsString(8)),
3301 + row.IsColumnNull(9) ? null : new XAttribute("Handler", row.FieldAsString(9)),
3302 + row.IsColumnNull(10) ? null : new XAttribute("Argument", row.FieldAsString(10)));
3303
3977 - if (null != row[6])
3304 + if (!row.IsColumnNull(6))
3305 {
3979 - var fileTypeMaskStrings = (Convert.ToString(row[6])).Split(';');
3306 + var fileTypeMaskStrings = row.FieldAsString(6).Split(';');
3307
3308 try
3309 {
@@ -3986,15 +3313,12 @@ namespace WixToolset.Core.WindowsInstaller
3313
3314 if (4 == fileTypeMaskParts.Length)
3315 {
3989 - var fileTypeMask = new Wix.FileTypeMask();
3316 + var xFileTypeMask = new XElement(Names.FileTypeMaskElement,
3317 + new XAttribute("Offset", Convert.ToInt32(fileTypeMaskParts[0], CultureInfo.InvariantCulture)),
3318 + new XAttribute("Mask", fileTypeMaskParts[2]),
3319 + new XAttribute("Value", fileTypeMaskParts[3]));
3320
3991 - fileTypeMask.Offset = Convert.ToInt32(fileTypeMaskParts[0], CultureInfo.InvariantCulture);
3992 -
3993 - fileTypeMask.Mask = fileTypeMaskParts[2];
3994 -
3995 - fileTypeMask.Value = fileTypeMaskParts[3];
3996 -
3997 - wixClass.AddChild(fileTypeMask);
3321 + xClass.Add(xFileTypeMask);
3322 }
3323 else
3324 {
@@ -4012,31 +3336,11 @@ namespace WixToolset.Core.WindowsInstaller
3336 }
3337 }
3338
4015 - if (null != row[7])
4016 - {
4017 - wixClass.Icon = Convert.ToString(row[7]);
4018 - }
4019 -
4020 - if (null != row[8])
4021 - {
4022 - wixClass.IconIndex = Convert.ToInt32(row[8]);
4023 - }
4024 -
4025 - if (null != row[9])
4026 - {
4027 - wixClass.Handler = Convert.ToString(row[9]);
4028 - }
4029 -
4030 - if (null != row[10])
3339 + if (!row.IsColumnNull(12))
3340 {
4032 - wixClass.Argument = Convert.ToString(row[10]);
4033 - }
4034 -
4035 - if (null != row[12])
4036 - {
4037 - if (1 == Convert.ToInt32(row[12]))
3341 + if (1 == row.FieldAsInteger(12))
3342 {
4039 - wixClass.RelativePath = Wix.YesNoType.yes;
3343 + xClass.SetAttributeValue("RelativePath", "yes");
3344 }
3345 else
3346 {
@@ -4044,17 +3348,8 @@ namespace WixToolset.Core.WindowsInstaller
3348 }
3349 }
3350
4047 - var component = (Wix.Component)this.core.GetIndexedElement("Component", Convert.ToString(row[2]));
4048 - if (null != component)
4049 - {
4050 - component.AddChild(wixClass);
4051 - }
4052 - else
4053 - {
4054 - this.Messaging.Write(WarningMessages.ExpectedForeignRow(row.SourceLineNumbers, table.Name, row.GetPrimaryKey(DecompilerConstants.PrimaryKeyDelimiter), "Component_", Convert.ToString(row[2]), "Component"));
4055 - }
4056 -
4057 - this.core.IndexElement(row, wixClass);
3351 + this.AddChildToParent("Component", xClass, row, 2);
3352 + this.IndexElement(row, xClass);
3353 }
3354 }
3355
@@ -4064,36 +3359,27 @@ namespace WixToolset.Core.WindowsInstaller
3359 /// <param name="table">The table to decompile.</param>
3360 private void DecompileComboBoxTable(Table table)
3361 {
4067 - Wix.ComboBox comboBox = null;
4068 - var comboBoxRows = new SortedList();
4069 -
3362 // sort the combo boxes by their property and order
4071 - foreach (var row in table.Rows)
4072 - {
4073 - comboBoxRows.Add(String.Concat("{0}|{1:0000000000}", row[0], row[1]), row);
4074 - }
3363 + var comboBoxRows = table.Rows.Select(row => row).OrderBy(row => String.Format("{0}|{1:0000000000}", row.FieldAsString(0), row.FieldAsInteger(1)));
3364
4076 - foreach (Row row in comboBoxRows.Values)
3365 + XElement xComboBox = null;
3366 + string property = null;
3367 + foreach (var row in comboBoxRows)
3368 {
4078 - if (null == comboBox || Convert.ToString(row[0]) != comboBox.Property)
3369 + if (null == xComboBox || row.FieldAsString(0) != property)
3370 {
4080 - comboBox = new Wix.ComboBox();
4081 -
4082 - comboBox.Property = Convert.ToString(row[0]);
4083 -
4084 - this.core.UIElement.AddChild(comboBox);
4085 - }
3371 + property = row.FieldAsString(0);
3372
4087 - var listItem = new Wix.ListItem();
3373 + xComboBox = new XElement(Names.ComboBoxElement,
3374 + new XAttribute("Property", property));
3375
4089 - listItem.Value = Convert.ToString(row[2]);
4090 -
4091 - if (null != row[3])
4092 - {
4093 - listItem.Text = Convert.ToString(row[3]);
3376 + this.UIElement.Add(xComboBox);
3377 }
3378
4096 - comboBox.AddChild(listItem);
3379 + var xListItem = new XElement(Names.ListItemElement,
3380 + new XAttribute("Value", row.FieldAsString(2)),
3381 + row.IsColumnNull(3) ? null : new XAttribute("Text", row.FieldAsString(3)));
3382 + xComboBox.Add(xListItem);
3383 }
3384 }
3385
@@ -4105,80 +3391,75 @@ namespace WixToolset.Core.WindowsInstaller
3391 {
3392 foreach (ControlRow controlRow in table.Rows)
3393 {
4108 - var control = new Wix.Control();
4109 -
4110 - control.Id = controlRow.Control;
4111 -
4112 - control.Type = controlRow.Type;
3394 + var xControl = new XElement(Names.ControlElement,
3395 + new XAttribute("Id", controlRow.Control),
3396 + new XAttribute("Type", controlRow.Type),
3397 + new XAttribute("X", controlRow.X),
3398 + new XAttribute("Y", controlRow.Y),
3399 + new XAttribute("Width", controlRow.Width),
3400 + new XAttribute("Height", controlRow.Height),
3401 + new XAttribute("Text", controlRow.Text));
3402
4114 - control.X = controlRow.X;
4115 -
4116 - control.Y = controlRow.Y;
4117 -
4118 - control.Width = controlRow.Width;
4119 -
4120 - control.Height = controlRow.Height;
4121 -
4122 - if (null != controlRow[7])
3403 + if (!controlRow.IsColumnNull(7))
3404 {
3405 string[] specialAttributes;
3406
3407 // sets various common attributes like Disabled, Indirect, Integer, ...
4127 - SetControlAttributes(controlRow.Attributes, control);
3408 + SetControlAttributes(controlRow.Attributes, xControl);

This file is too large to show in full.

src/WixToolset.Core.WindowsInstaller/Decompile/DecompilerCore.cs deleted
-110
@@ -1,110 +0,0 @@
1 -// Copyright (c) .NET Foundation and contributors. All rights reserved. Licensed under the Microsoft Reciprocal License. See LICENSE.TXT file in the project root for full license information.
2 -
3 -namespace WixToolset
4 -{
5 - using System;
6 - using System.Collections;
7 - using WixToolset.Data.WindowsInstaller;
8 - using WixToolset.Extensibility;
9 - using Wix = WixToolset.Data.Serialize;
10 -
11 - /// <summary>
12 - /// The base of the decompiler. Holds some variables used by the decompiler and extensions,
13 - /// as well as some utility methods.
14 - /// </summary>
15 - internal class DecompilerCore
16 - {
17 - private readonly Hashtable elements;
18 - private Wix.UI uiElement;
19 -
20 - /// <summary>
21 - /// Instantiate a new decompiler core.
22 - /// </summary>
23 - /// <param name="rootElement">The root element of the decompiled database.</param>
24 - /// <param name="messageHandler">The message handler.</param>
25 - internal DecompilerCore(Wix.IParentElement rootElement)
26 - {
27 - this.elements = new Hashtable();
28 - this.RootElement = rootElement;
29 - }
30 -
31 - /// <summary>
32 - /// Gets the root element of the decompiled output.
33 - /// </summary>
34 - /// <value>The root element of the decompiled output.</value>
35 - public Wix.IParentElement RootElement { get; }
36 -
37 - /// <summary>
38 - /// Gets the UI element.
39 - /// </summary>
40 - /// <value>The UI element.</value>
41 - public Wix.UI UIElement
42 - {
43 - get
44 - {
45 - if (null == this.uiElement)
46 - {
47 - this.uiElement = new Wix.UI();
48 - this.RootElement.AddChild(this.uiElement);
49 - }
50 -
51 - return this.uiElement;
52 - }
53 - }
54 -
55 - /// <summary>
56 - /// Verifies if a filename is a valid short filename.
57 - /// </summary>
58 - /// <param name="filename">Filename to verify.</param>
59 - /// <param name="allowWildcards">true if wildcards are allowed in the filename.</param>
60 - /// <returns>True if the filename is a valid short filename</returns>
61 - public virtual bool IsValidShortFilename(string filename, bool allowWildcards)
62 - {
63 - return false;
64 - }
65 -
66 - /// <summary>
67 - /// Convert an Int32 into a DateTime.
68 - /// </summary>
69 - /// <param name="value">The Int32 value.</param>
70 - /// <returns>The DateTime.</returns>
71 - public DateTime ConvertIntegerToDateTime(int value)
72 - {
73 - var date = value / 65536;
74 - var time = value % 65536;
75 -
76 - return new DateTime(1980 + (date / 512), (date % 512) / 32, date % 32, time / 2048, (time % 2048) / 32, (time % 32) * 2);
77 - }
78 -
79 - /// <summary>
80 - /// Gets the element corresponding to the row it came from.
81 - /// </summary>
82 - /// <param name="row">The row corresponding to the element.</param>
83 - /// <returns>The indexed element.</returns>
84 - public Wix.ISchemaElement GetIndexedElement(Row row)
85 - {
86 - return this.GetIndexedElement(row.TableDefinition.Name, row.GetPrimaryKey(DecompilerConstants.PrimaryKeyDelimiter));
87 - }
88 -
89 - /// <summary>
90 - /// Gets the element corresponding to the primary key of the given table.
91 - /// </summary>
92 - /// <param name="table">The table corresponding to the element.</param>
93 - /// <param name="primaryKey">The primary key corresponding to the element.</param>
94 - /// <returns>The indexed element.</returns>
95 - public Wix.ISchemaElement GetIndexedElement(string table, params string[] primaryKey)
96 - {
97 - return (Wix.ISchemaElement)this.elements[String.Concat(table, ':', String.Join(DecompilerConstants.PrimaryKeyDelimiterString, primaryKey))];
98 - }
99 -
100 - /// <summary>
101 - /// Index an element by its corresponding row.
102 - /// </summary>
103 - /// <param name="row">The row corresponding to the element.</param>
104 - /// <param name="element">The element to index.</param>
105 - public void IndexElement(Row row, Wix.ISchemaElement element)
106 - {
107 - this.elements.Add(String.Concat(row.TableDefinition.Name, ':', row.GetPrimaryKey(DecompilerConstants.PrimaryKeyDelimiter)), element);
108 - }
109 - }
110 -}
src/WixToolset.Core.WindowsInstaller/Decompile/Names.cs new
+158
@@ -0,0 +1,158 @@
1 +namespace WixToolset.Core.WindowsInstaller.Decompile
2 +{
3 + using System.Xml.Linq;
4 +
5 + internal static class Names
6 + {
7 + public static readonly XNamespace WxsNamespace = "http://wixtoolset.org/schemas/v4/wxs";
8 +
9 + public static readonly XName WixElement = WxsNamespace + "Wix";
10 +
11 + public static readonly XName ProductElement = WxsNamespace + "Product";
12 + public static readonly XName ModuleElement = WxsNamespace + "Module";
13 + public static readonly XName PatchCreationElement = WxsNamespace + "PatchCreation";
14 +
15 + public static readonly XName CustomElement = WxsNamespace + "Custom";
16 +
17 + public static readonly XName AdminExecuteSequenceElement = WxsNamespace + "AdminExecuteSequence";
18 + public static readonly XName AdminUISequenceElement = WxsNamespace + "AdminUISequence";
19 + public static readonly XName AdvertiseExecuteSequenceElement = WxsNamespace + "AdvertiseExecuteSequence";
20 + public static readonly XName InstallExecuteSequenceElement = WxsNamespace + "InstallExecuteSequence";
21 + public static readonly XName InstallUISequenceElement = WxsNamespace + "InstallUISequence";
22 +
23 + public static readonly XName AppSearchElement = WxsNamespace + "AppSearch";
24 +
25 + public static readonly XName PropertyElement = WxsNamespace + "Property";
26 +
27 + public static readonly XName ProtectRangeElement = WxsNamespace + "ProtectRange";
28 + public static readonly XName ProtectFileElement = WxsNamespace + "ProtectFile";
29 +
30 + public static readonly XName FileElement = WxsNamespace + "File";
31 +
32 + public static readonly XName EnsureTableElement = WxsNamespace + "EnsureTable";
33 + public static readonly XName PackageElement = WxsNamespace + "Package";
34 + public static readonly XName PatchInformationElement = WxsNamespace + "PatchInformation";
35 +
36 + public static readonly XName ProgressTextElement = WxsNamespace + "ProgressText";
37 + public static readonly XName UIElement = WxsNamespace + "UI";
38 +
39 + public static readonly XName AppIdElement = WxsNamespace + "AppId";
40 +
41 + public static readonly XName ControlElement = WxsNamespace + "Control";
42 +
43 + public static readonly XName BillboardElement = WxsNamespace + "Billboard";
44 + public static readonly XName BillboardActionElement = WxsNamespace + "BillboardAction";
45 +
46 + public static readonly XName BinaryElement = WxsNamespace + "Binary";
47 +
48 + public static readonly XName ClassElement = WxsNamespace + "Class";
49 +
50 + public static readonly XName FileTypeMaskElement = WxsNamespace + "FileTypeMask";
51 +
52 + public static readonly XName ComboBoxElement = WxsNamespace + "ComboBox";
53 +
54 + public static readonly XName ListItemElement = WxsNamespace + "ListItem";
55 +
56 + public static readonly XName ConditionElement = WxsNamespace + "Condition";
57 + public static readonly XName PublishElement = WxsNamespace + "Publish";
58 + public static readonly XName CustomTableElement = WxsNamespace + "CustomTable";
59 + public static readonly XName ColumnElement = WxsNamespace + "Column";
60 + public static readonly XName RowElement = WxsNamespace + "Row";
61 + public static readonly XName DataElement = WxsNamespace + "Data";
62 + public static readonly XName CreateFolderElement = WxsNamespace + "CreateFolder";
63 +
64 + public static readonly XName CustomActionElement = WxsNamespace + "CustomAction";
65 +
66 + public static readonly XName ComponentSearchElement = WxsNamespace + "ComponentSearch";
67 + public static readonly XName ComponentElement = WxsNamespace + "Component";
68 +
69 + public static readonly XName LevelElement = WxsNamespace + "Level";
70 + public static readonly XName DialogElement = WxsNamespace + "Dialog";
71 + public static readonly XName DirectoryElement = WxsNamespace + "Directory";
72 + public static readonly XName DirectorySearchElement = WxsNamespace + "DirectorySearch";
73 + public static readonly XName CopyFileElement = WxsNamespace + "CopyFile";
74 + public static readonly XName EnvironmentElement = WxsNamespace + "Environment";
75 + public static readonly XName ErrorElement = WxsNamespace + "Error";
76 + public static readonly XName SubscribeElement = WxsNamespace + "Subscribe";
77 + public static readonly XName ExtensionElement = WxsNamespace + "Extension";
78 + public static readonly XName ExternalFileElement = WxsNamespace + "ExternalFile";
79 + public static readonly XName SymbolPathElement = WxsNamespace + "SymbolPath";
80 + public static readonly XName IgnoreRangeElement = WxsNamespace + "IgnoreRange";
81 +
82 + public static readonly XName FeatureElement = WxsNamespace + "Feature";
83 + public static readonly XName ComponentRefElement = WxsNamespace + "ComponentRef";
84 + public static readonly XName SFPFileElement = WxsNamespace + "SFPFile";
85 + public static readonly XName IconElement = WxsNamespace + "Icon";
86 + public static readonly XName FamilyElement = WxsNamespace + "Family";
87 + public static readonly XName IniFileElement = WxsNamespace + "IniFile";
88 + public static readonly XName IniFileSearchElement = WxsNamespace + "IniFileSearch";
89 + public static readonly XName IsolateComponentElement = WxsNamespace + "IsolateComponent";
90 + public static readonly XName LaunchElement = WxsNamespace + "Launch";
91 + public static readonly XName ListBoxElement = WxsNamespace + "ListBox";
92 + public static readonly XName ListViewElement = WxsNamespace + "ListView";
93 + public static readonly XName PermissionElement = WxsNamespace + "Permission";
94 + public static readonly XName MediaElement = WxsNamespace + "Media";
95 + public static readonly XName MIMEElement = WxsNamespace + "MIME";
96 + public static readonly XName ConfigurationElement = WxsNamespace + "Configuration";
97 + public static readonly XName DependencyElement = WxsNamespace + "Dependency";
98 + public static readonly XName ExclusionElement = WxsNamespace + "Exclusion";
99 + public static readonly XName IgnoreTableElement = WxsNamespace + "IgnoreTable";
100 + public static readonly XName SubstitutionElement = WxsNamespace + "Substitution";
101 + public static readonly XName DigitalCertificateElement = WxsNamespace + "DigitalCertificate";
102 + public static readonly XName DigitalSignatureElement = WxsNamespace + "DigitalSignature";
103 + public static readonly XName EmbeddedChainerElement = WxsNamespace + "EmbeddedChainer";
104 + public static readonly XName EmbeddedUIElement = WxsNamespace + "EmbeddedUI";
105 + public static readonly XName EmbeddedUIResourceElement = WxsNamespace + "EmbeddedUIResource";
106 + public static readonly XName PermissionExElement = WxsNamespace + "PermissionEx";
107 + public static readonly XName PackageCertificatesElement = WxsNamespace + "PackageCertificates";
108 + public static readonly XName PatchCertificatesElement = WxsNamespace + "PatchCertificates";
109 + public static readonly XName ShortcutPropertyElement = WxsNamespace + "ShortcutProperty";
110 + public static readonly XName ODBCDataSourceElement = WxsNamespace + "ODBCDataSource";
111 + public static readonly XName ODBCDriverElement = WxsNamespace + "ODBCDriver";
112 + public static readonly XName ODBCTranslatorElement = WxsNamespace + "ODBCTranslator";
113 + public static readonly XName PatchMetadataElement = WxsNamespace + "PatchMetadata";
114 + public static readonly XName OptimizeCustomActionsElement = WxsNamespace + "OptimizeCustomActions";
115 + public static readonly XName CustomPropertyElement = WxsNamespace + "CustomProperty";
116 + public static readonly XName PatchSequenceElement = WxsNamespace + "PatchSequence";
117 + public static readonly XName ProgIdElement = WxsNamespace + "ProgId";
118 + public static readonly XName ReplacePatchElement = WxsNamespace + "ReplacePatch";
119 + public static readonly XName TargetProductCodeElement = WxsNamespace + "TargetProductCode";
120 + public static readonly XName PatchPropertyElement = WxsNamespace + "PatchProperty";
121 + public static readonly XName CategoryElement = WxsNamespace + "Category";
122 + public static readonly XName RadioButtonElement = WxsNamespace + "RadioButton";
123 + public static readonly XName RadioButtonGroupElement = WxsNamespace + "RadioButtonGroup";
124 + public static readonly XName RegistryKeyElement = WxsNamespace + "RegistryKey";
125 + public static readonly XName RegistryValueElement = WxsNamespace + "RegistryValue";
126 + public static readonly XName MultiStringElement = WxsNamespace + "MultiString";
127 + public static readonly XName RegistrySearchElement = WxsNamespace + "RegistrySearch";
128 + public static readonly XName RemoveFolderElement = WxsNamespace + "RemoveFolder";
129 + public static readonly XName RemoveFileElement = WxsNamespace + "RemoveFile";
130 + public static readonly XName RemoveRegistryKeyElement = WxsNamespace + "RemoveRegistryKey";
131 + public static readonly XName RemoveRegistryValueElement = WxsNamespace + "RemoveRegistryValue";
132 + public static readonly XName ReserveCostElement = WxsNamespace + "ReserveCost";
133 + public static readonly XName ServiceControlElement = WxsNamespace + "ServiceControl";
134 + public static readonly XName ServiceArgumentElement = WxsNamespace + "ServiceArgument";
135 + public static readonly XName ServiceInstallElement = WxsNamespace + "ServiceInstall";
136 + public static readonly XName ServiceDependencyElement = WxsNamespace + "ServiceDependency";
137 + public static readonly XName SFPCatalogElement = WxsNamespace + "SFPCatalog";
138 + public static readonly XName ShortcutElement = WxsNamespace + "Shortcut";
139 + public static readonly XName FileSearchElement = WxsNamespace + "FileSearch";
140 + public static readonly XName TargetFileElement = WxsNamespace + "TargetFile";
141 + public static readonly XName TargetImageElement = WxsNamespace + "TargetImage";
142 + public static readonly XName TextStyleElement = WxsNamespace + "TextStyle";
143 + public static readonly XName TypeLibElement = WxsNamespace + "TypeLib";
144 + public static readonly XName UpgradeElement = WxsNamespace + "Upgrade";
145 + public static readonly XName UpgradeVersionElement = WxsNamespace + "UpgradeVersion";
146 + public static readonly XName UpgradeFileElement = WxsNamespace + "UpgradeFile";
147 + public static readonly XName UpgradeImageElement = WxsNamespace + "UpgradeImage";
148 + public static readonly XName UITextElement = WxsNamespace + "UIText";
149 + public static readonly XName VerbElement = WxsNamespace + "Verb";
150 + public static readonly XName ComplianceCheckElement = WxsNamespace + "ComplianceCheck";
151 + public static readonly XName FileSearchRefElement = WxsNamespace + "FileSearchRef";
152 + public static readonly XName ComplianceDriveElement = WxsNamespace + "ComplianceDrive";
153 + public static readonly XName DirectorySearchRefElement = WxsNamespace + "DirectorySearchRef";
154 + public static readonly XName RegistrySearchRefElement = WxsNamespace + "RegistrySearchRef";
155 + public static readonly XName MajorUpgradeElement = WxsNamespace + "MajorUpgrade";
156 + //public static readonly XName Element = WxsNamespace + "";
157 + }
158 +}
src/WixToolset.Core.WindowsInstaller/Melter.cs
-1
@@ -12,7 +12,6 @@ namespace WixToolset
12 using System.Text;
13 using System.Text.RegularExpressions;
14 using WixToolset.Data;
15 - using Wix = WixToolset.Data.Serialize;
15
16 /// <summary>
17 /// Converts a wixout representation of an MSM database into a ComponentGroup the form of WiX source.
src/WixToolset.Core/CommandLine/DecompileCommand.cs
+1 -1
@@ -33,7 +33,7 @@ namespace WixToolset.Core.CommandLine
33
34 public Task<int> ExecuteAsync(CancellationToken _)
35 {
36 - if (this.commandLine.ShowHelp)
36 + if (this.commandLine.ShowHelp || String.IsNullOrEmpty(this.commandLine.DecompileFilePath))
37 {
38 Console.WriteLine("TODO: Show decompile command help");
39 return Task.FromResult(-1);
src/WixToolset.Core/Compiler.cs
+3 -3
@@ -3331,7 +3331,7 @@ namespace WixToolset.Core
3331 break;
3332 }
3333 break;
3334 - case "ScriptFile":
3334 + case "ScriptSourceFile":
3335 scriptFile = this.Core.GetAttributeValue(sourceLineNumbers, attrib);
3336 break;
3337 case "SuppressModularization":
@@ -3387,7 +3387,7 @@ namespace WixToolset.Core
3387 {
3388 if (String.IsNullOrEmpty(scriptFile))
3389 {
3390 - this.Core.Write(ErrorMessages.ExpectedAttribute(sourceLineNumbers, node.Name.LocalName, "ScriptFile", "Script"));
3390 + this.Core.Write(ErrorMessages.ExpectedAttribute(sourceLineNumbers, node.Name.LocalName, "ScriptSourceFile", "Script"));
3391 }
3392 }
3393 else if (CustomActionTargetType.VBScript == targetType) // non-inline vbscript
@@ -3426,7 +3426,7 @@ namespace WixToolset.Core
3426
3427 if (!inlineScript && !String.IsNullOrEmpty(scriptFile))
3428 {
3429 - this.Core.Write(ErrorMessages.IllegalAttributeWithoutOtherAttributes(sourceLineNumbers, node.Name.LocalName, "ScriptFile", "Script"));
3429 + this.Core.Write(ErrorMessages.IllegalAttributeWithoutOtherAttributes(sourceLineNumbers, node.Name.LocalName, "ScriptSourceFile", "Script"));
3430 }
3431
3432 if (win64 && CustomActionTargetType.VBScript != targetType && CustomActionTargetType.JScript != targetType)
src/test/WixToolsetTest.CoreIntegration/CustomTableFixture.cs
+2 -13
@@ -3,6 +3,7 @@
3 namespace WixToolsetTest.CoreIntegration
4 {
5 using System.IO;
6 + using System.Xml.Linq;
7 using WixBuildTools.TestSupport;
8 using WixToolset.Core.TestPackage;
9 using Xunit;
@@ -224,20 +225,8 @@ namespace WixToolsetTest.CoreIntegration
225
226 result.AssertSuccess();
227
227 - CompareLineByLine(expectedFile, decompiledWxsPath);
228 + WixAssert.CompareXml(expectedFile, decompiledWxsPath);
229 }
230 }
230 -
231 - private static void CompareLineByLine(string expectedFile, string actualFile)
232 - {
233 - var expectedLines = File.ReadAllLines(expectedFile);
234 - var actualLines = File.ReadAllLines(actualFile);
235 - for (var i = 0; i < expectedLines.Length; ++i)
236 - {
237 - Assert.True(actualLines.Length > i, $"{i}: Expected file longer than actual file");
238 - Assert.Equal($"{i}: {expectedLines[i]}", $"{i}: {actualLines[i]}");
239 - }
240 - Assert.True(expectedLines.Length == actualLines.Length, "Actual file longer than expected file");
241 - }
231 }
232 }
src/test/WixToolsetTest.CoreIntegration/DecompileFixture.cs
+16 -124
@@ -6,14 +6,14 @@ namespace WixToolsetTest.CoreIntegration
6 using System.Xml.Linq;
7 using WixBuildTools.TestSupport;
8 using WixToolset.Core.TestPackage;
9 + using WixToolset.Extensibility.Services;
10 using Xunit;
11
12 public class DecompileFixture
13 {
13 - [Fact]
14 - public void CanDecompileSingleFileCompressed()
14 + private static void DecompileAndCompare(string sourceFolder, string msiName, string expectedWxsName)
15 {
16 - var folder = TestData.Get(@"TestData\DecompileSingleFileCompressed");
16 + var folder = TestData.Get(sourceFolder);
17
18 using (var fs = new DisposableFileSystem())
19 {
@@ -23,75 +23,33 @@ namespace WixToolsetTest.CoreIntegration
23 var result = WixRunner.Execute(new[]
24 {
25 "decompile",
26 - Path.Combine(folder, "example.msi"),
26 + Path.Combine(folder, msiName),
27 "-intermediateFolder", intermediateFolder,
28 "-o", outputPath
29 });
30
31 result.AssertSuccess();
32
33 - var actual = File.ReadAllText(outputPath);
34 - var actualFormatted = XDocument.Parse(actual, LoadOptions.PreserveWhitespace | LoadOptions.SetBaseUri | LoadOptions.SetLineInfo).ToString();
35 - var expected = XDocument.Load(Path.Combine(folder, "Expected.wxs"), LoadOptions.PreserveWhitespace | LoadOptions.SetBaseUri | LoadOptions.SetLineInfo).ToString();
36 -
37 - Assert.Equal(expected, actualFormatted);
33 + WixAssert.CompareXml(Path.Combine(folder, expectedWxsName), outputPath);
34 }
35 }
36
37 [Fact]
42 - public void CanDecompile64BitSingleFileCompressed()
38 + public void CanDecompileSingleFileCompressed()
39 {
44 - var folder = TestData.Get(@"TestData\DecompileSingleFileCompressed64");
45 -
46 - using (var fs = new DisposableFileSystem())
47 - {
48 - var intermediateFolder = fs.GetFolder();
49 - var outputPath = Path.Combine(intermediateFolder, @"Actual.wxs");
50 -
51 - var result = WixRunner.Execute(new[]
52 - {
53 - "decompile",
54 - Path.Combine(folder, "example.msi"),
55 - "-intermediateFolder", intermediateFolder,
56 - "-o", outputPath
57 - });
58 -
59 - result.AssertSuccess();
60 -
61 - var actual = File.ReadAllText(outputPath);
62 - var actualFormatted = XDocument.Parse(actual, LoadOptions.PreserveWhitespace | LoadOptions.SetBaseUri | LoadOptions.SetLineInfo).ToString();
63 - var expected = XDocument.Load(Path.Combine(folder, "Expected.wxs"), LoadOptions.PreserveWhitespace | LoadOptions.SetBaseUri | LoadOptions.SetLineInfo).ToString();
40 + DecompileAndCompare(@"TestData\DecompileSingleFileCompressed", "example.msi", "Expected.wxs");
41 + }
42
65 - Assert.Equal(expected, actualFormatted);
66 - }
43 + [Fact]
44 + public void CanDecompile64BitSingleFileCompressed()
45 + {
46 + DecompileAndCompare(@"TestData\DecompileSingleFileCompressed64", "example.msi", "Expected.wxs");
47 }
48
49 [Fact]
50 public void CanDecompileNestedDirSearchUnderRegSearch()
51 {
72 - var folder = TestData.Get(@"TestData\AppSearch");
73 -
74 - using (var fs = new DisposableFileSystem())
75 - {
76 - var intermediateFolder = fs.GetFolder();
77 - var outputPath = Path.Combine(intermediateFolder, @"Actual.wxs");
78 -
79 - var result = WixRunner.Execute(new[]
80 - {
81 - "decompile",
82 - Path.Combine(folder, "NestedDirSearchUnderRegSearch.msi"),
83 - "-intermediateFolder", intermediateFolder,
84 - "-o", outputPath
85 - });
86 -
87 - result.AssertSuccess();
88 -
89 - var actual = File.ReadAllText(outputPath);
90 - var actualFormatted = XDocument.Parse(actual, LoadOptions.PreserveWhitespace | LoadOptions.SetBaseUri | LoadOptions.SetLineInfo).ToString();
91 - var expected = XDocument.Load(Path.Combine(folder, "DecompiledNestedDirSearchUnderRegSearch.wxs"), LoadOptions.PreserveWhitespace | LoadOptions.SetBaseUri | LoadOptions.SetLineInfo).ToString();
92 -
93 - Assert.Equal(expected, actualFormatted);
94 - }
52 + DecompileAndCompare(@"TestData\AppSearch", "NestedDirSearchUnderRegSearch.msi", "DecompiledNestedDirSearchUnderRegSearch.wxs");
53 }
54
55 [Fact]
@@ -100,85 +58,19 @@ namespace WixToolsetTest.CoreIntegration
58 // The input MSI was not created using standard methods, it is an example of a real world database that needs to be decompiled.
59 // The Class/@Feature_ column has length of 32, the File/@Attributes has length of 2,
60 // and numerous foreign key relationships are missing.
103 - var folder = TestData.Get(@"TestData\Class");
104 -
105 - using (var fs = new DisposableFileSystem())
106 - {
107 - var intermediateFolder = fs.GetFolder();
108 - var outputPath = Path.Combine(intermediateFolder, @"Actual.wxs");
109 -
110 - var result = WixRunner.Execute(new[]
111 - {
112 - "decompile",
113 - Path.Combine(folder, "OldClassTableDef.msi"),
114 - "-intermediateFolder", intermediateFolder,
115 - "-o", outputPath
116 - });
117 -
118 - result.AssertSuccess();
119 -
120 - var actual = File.ReadAllText(outputPath);
121 - var actualFormatted = XDocument.Parse(actual, LoadOptions.PreserveWhitespace | LoadOptions.SetBaseUri | LoadOptions.SetLineInfo).ToString();
122 - var expected = XDocument.Load(Path.Combine(folder, "DecompiledOldClassTableDef.wxs"), LoadOptions.PreserveWhitespace | LoadOptions.SetBaseUri | LoadOptions.SetLineInfo).ToString();
123 -
124 - Assert.Equal(expected, actualFormatted);
125 - }
61 + DecompileAndCompare(@"TestData\Class", "OldClassTableDef.msi", "DecompiledOldClassTableDef.wxs");
62 }
63
64 [Fact]
65 public void CanDecompileSequenceTables()
66 {
131 - var folder = TestData.Get(@"TestData\SequenceTables");
132 -
133 - using (var fs = new DisposableFileSystem())
134 - {
135 - var intermediateFolder = fs.GetFolder();
136 - var outputPath = Path.Combine(intermediateFolder, @"Actual.wxs");
137 -
138 - var result = WixRunner.Execute(new[]
139 - {
140 - "decompile",
141 - Path.Combine(folder, "SequenceTables.msi"),
142 - "-intermediateFolder", intermediateFolder,
143 - "-o", outputPath
144 - });
145 -
146 - result.AssertSuccess();
147 -
148 - var actual = File.ReadAllText(outputPath);
149 - var actualFormatted = XDocument.Parse(actual, LoadOptions.PreserveWhitespace | LoadOptions.SetBaseUri | LoadOptions.SetLineInfo).ToString();
150 - var expected = XDocument.Load(Path.Combine(folder, "DecompiledSequenceTables.wxs"), LoadOptions.PreserveWhitespace | LoadOptions.SetBaseUri | LoadOptions.SetLineInfo).ToString();
151 -
152 - Assert.Equal(expected, actualFormatted);
153 - }
67 + DecompileAndCompare(@"TestData\SequenceTables", "SequenceTables.msi", "DecompiledSequenceTables.wxs");
68 }
69
70 [Fact]
71 public void CanDecompileShortcuts()
72 {
159 - var folder = TestData.Get(@"TestData\Shortcut");
160 -
161 - using (var fs = new DisposableFileSystem())
162 - {
163 - var intermediateFolder = fs.GetFolder();
164 - var outputPath = Path.Combine(intermediateFolder, @"Actual.wxs");
165 -
166 - var result = WixRunner.Execute(new[]
167 - {
168 - "decompile",
169 - Path.Combine(folder, "shortcuts.msi"),
170 - "-intermediateFolder", intermediateFolder,
171 - "-o", outputPath
172 - });
173 -
174 - result.AssertSuccess();
175 -
176 - var actual = File.ReadAllText(outputPath);
177 - var actualFormatted = XDocument.Parse(actual, LoadOptions.PreserveWhitespace | LoadOptions.SetBaseUri | LoadOptions.SetLineInfo).ToString();
178 - var expected = XDocument.Load(Path.Combine(folder, "DecompiledShortcuts.wxs"), LoadOptions.PreserveWhitespace | LoadOptions.SetBaseUri | LoadOptions.SetLineInfo).ToString();
179 -
180 - Assert.Equal(expected, actualFormatted);
181 - }
73 + DecompileAndCompare(@"TestData\Shortcut", "shortcuts.msi", "DecompiledShortcuts.wxs");
74 }
75 }
76 }
src/test/WixToolsetTest.CoreIntegration/TestData/CustomTable/CustomTable-Expected.wxs
+4 -4
@@ -6,12 +6,12 @@
6 <Column Id="Column1" PrimaryKey="yes" Type="string" Width="0" Category="text" Description="The first custom column." />
7 <Column Id="Component_" Type="string" Width="72" KeyTable="Component" KeyColumn="1" Description="The custom table's Component reference" />
8 <Row>
9 - <Data Column="Column1">Row1</Data>
10 - <Data Column="Component_">test.txt</Data>
9 + <Data Column="Column1" Value="Row1" />
10 + <Data Column="Component_" Value="test.txt" />
11 </Row>
12 <Row>
13 - <Data Column="Column1">Row2</Data>
14 - <Data Column="Component_">test.txt</Data>
13 + <Data Column="Column1" Value="Row2" />
14 + <Data Column="Component_" Value="test.txt" />
15 </Row>
16 </CustomTable>
17 <Directory Id="TARGETDIR" Name="SourceDir">