aboutsummaryrefslogtreecommitdiff
path: root/src/WixToolset.Core/Compiler.cs
diff options
context:
space:
mode:
Diffstat (limited to 'src/WixToolset.Core/Compiler.cs')
-rw-r--r--src/WixToolset.Core/Compiler.cs20667
1 files changed, 20667 insertions, 0 deletions
diff --git a/src/WixToolset.Core/Compiler.cs b/src/WixToolset.Core/Compiler.cs
new file mode 100644
index 00000000..00618152
--- /dev/null
+++ b/src/WixToolset.Core/Compiler.cs
@@ -0,0 +1,20667 @@
1// Copyright (c) .NET Foundation and contributors. All rights reserved. Licensed under the Microsoft Reciprocal License. See LICENSE.TXT file in the project root for full license information.
2
3namespace WixToolset
4{
5 using System;
6 using System.Collections;
7 using System.Collections.Generic;
8 using System.Diagnostics;
9 using System.Diagnostics.CodeAnalysis;
10 using System.Globalization;
11 using System.IO;
12 using System.Text.RegularExpressions;
13 using System.Xml.Linq;
14 using WixToolset.Data;
15 using WixToolset.Data.Rows;
16 using WixToolset.Extensibility;
17 using WixToolset.Msi;
18 using WixToolset.Core.Native;
19 using Wix = WixToolset.Data.Serialize;
20
21 /// <summary>
22 /// Compiler of the WiX toolset.
23 /// </summary>
24 [SuppressMessage("Microsoft.Naming", "CA1724:TypeNamesShouldNotMatchNamespaces")]
25 public sealed class Compiler
26 {
27 public const string UpgradeDetectedProperty = "WIX_UPGRADE_DETECTED";
28 public const string UpgradePreventedCondition = "NOT WIX_UPGRADE_DETECTED";
29 public const string DowngradeDetectedProperty = "WIX_DOWNGRADE_DETECTED";
30 public const string DowngradePreventedCondition = "NOT WIX_DOWNGRADE_DETECTED";
31 public const string DefaultComponentIdPlaceholderFormat = "WixComponentIdPlaceholder{0}";
32 public const string DefaultComponentIdPlaceholderWixVariableFormat = "!(wix.{0})";
33 public const string BurnUXContainerId = "WixUXContainer";
34 public const string BurnDefaultAttachedContainerId = "WixAttachedContainer";
35
36 // The following constants must stay in sync with src\burn\engine\core.h
37 private const string BURN_BUNDLE_NAME = "WixBundleName";
38 private const string BURN_BUNDLE_ORIGINAL_SOURCE = "WixBundleOriginalSource";
39 private const string BURN_BUNDLE_ORIGINAL_SOURCE_FOLDER = "WixBundleOriginalSourceFolder";
40 private const string BURN_BUNDLE_LAST_USED_SOURCE = "WixBundleLastUsedSource";
41
42 private TableDefinitionCollection tableDefinitions;
43 private Dictionary<XNamespace, ICompilerExtension> extensions;
44 private List<InspectorExtension> inspectorExtensions;
45 private CompilerCore core;
46 private bool showPedanticMessages;
47
48 // if these are true you know you are building a module or product
49 // but if they are false you cannot not be sure they will not end
50 // up a product or module. Use these flags carefully.
51 private bool compilingModule;
52 private bool compilingProduct;
53
54 private bool useShortFileNames;
55 private string activeName;
56 private string activeLanguage;
57
58 private WixVariableResolver componentIdPlaceholdersResolver;
59
60 /// <summary>
61 /// Creates a new compiler object with a default set of table definitions.
62 /// </summary>
63 public Compiler()
64 {
65 this.tableDefinitions = new TableDefinitionCollection(WindowsInstallerStandard.GetTableDefinitions());
66 this.extensions = new Dictionary<XNamespace, ICompilerExtension>();
67 this.inspectorExtensions = new List<InspectorExtension>();
68
69 this.CurrentPlatform = Platform.X86;
70 }
71
72 /// <summary>
73 /// Type of RadioButton element in a group.
74 /// </summary>
75 private enum RadioButtonType
76 {
77 /// <summary>Not set, yet.</summary>
78 NotSet,
79
80 /// <summary>Text</summary>
81 Text,
82
83 /// <summary>Bitmap</summary>
84 Bitmap,
85
86 /// <summary>Icon</summary>
87 Icon,
88 }
89
90 /// <summary>
91 /// Gets or sets the platform which the compiler will use when defaulting 64-bit attributes and elements.
92 /// </summary>
93 /// <value>The platform which the compiler will use when defaulting 64-bit attributes and elements.</value>
94 public Platform CurrentPlatform { get; set; }
95
96 /// <summary>
97 /// Gets or sets the option to show pedantic messages.
98 /// </summary>
99 /// <value>The option to show pedantic messages.</value>
100 public bool ShowPedanticMessages
101 {
102 get { return this.showPedanticMessages; }
103 set { this.showPedanticMessages = value; }
104 }
105
106 /// <summary>
107 /// Adds a compiler extension.
108 /// </summary>
109 /// <param name="extension">The extension to add.</param>
110 public void AddExtension(ICompilerExtension extension)
111 {
112 // Check if this extension is adding a schema namespace that already exists.
113 ICompilerExtension collidingExtension;
114 if (!this.extensions.TryGetValue(extension.Namespace, out collidingExtension))
115 {
116 this.extensions.Add(extension.Namespace, extension);
117 }
118 else
119 {
120 Messaging.Instance.OnMessage(WixErrors.DuplicateExtensionXmlSchemaNamespace(extension.GetType().ToString(), extension.Namespace.NamespaceName, collidingExtension.GetType().ToString()));
121 }
122
123 //if (null != extension.InspectorExtension)
124 //{
125 // this.inspectorExtensions.Add(extension.InspectorExtension);
126 //}
127 }
128
129 /// <summary>
130 /// Adds table definitions from an extension
131 /// </summary>
132 /// <param name="extension">Extension with table definitions.</param>
133 public void AddExtensionData(IExtensionData extension)
134 {
135 if (null != extension.TableDefinitions)
136 {
137 foreach (TableDefinition tableDefinition in extension.TableDefinitions)
138 {
139 if (!this.tableDefinitions.Contains(tableDefinition.Name))
140 {
141 this.tableDefinitions.Add(tableDefinition);
142 }
143 else
144 {
145 Messaging.Instance.OnMessage(WixErrors.DuplicateExtensionTable(extension.GetType().ToString(), tableDefinition.Name));
146 }
147 }
148 }
149 }
150
151 /// <summary>
152 /// Compiles the provided Xml document into an intermediate object
153 /// </summary>
154 /// <param name="source">Source xml document to compile. The BaseURI property
155 /// should be properly set to get messages containing source line information.</param>
156 /// <returns>Intermediate object representing compiled source document.</returns>
157 /// <remarks>This method is not thread-safe.</remarks>
158 [SuppressMessage("Microsoft.Design", "CA1059:MembersShouldNotExposeCertainConcreteTypes")]
159 public Intermediate Compile(XDocument source)
160 {
161 if (null == source)
162 {
163 throw new ArgumentNullException("source");
164 }
165
166 bool encounteredError = false;
167
168 // create the intermediate
169 Intermediate target = new Intermediate();
170
171 // try to compile it
172 try
173 {
174 this.core = new CompilerCore(target, this.tableDefinitions, this.extensions);
175 this.core.ShowPedanticMessages = this.showPedanticMessages;
176 this.core.CurrentPlatform = this.CurrentPlatform;
177 this.componentIdPlaceholdersResolver = new WixVariableResolver();
178
179 foreach (CompilerExtension extension in this.extensions.Values)
180 {
181 extension.Core = this.core;
182 extension.Initialize();
183 }
184
185 // parse the document
186 SourceLineNumber sourceLineNumbers = Preprocessor.GetSourceLineNumbers(source.Root);
187 if ("Wix" == source.Root.Name.LocalName)
188 {
189 if (CompilerCore.WixNamespace == source.Root.Name.Namespace)
190 {
191 this.ParseWixElement(source.Root);
192 }
193 else // invalid or missing namespace
194 {
195 if (String.IsNullOrEmpty(source.Root.Name.NamespaceName))
196 {
197 this.core.OnMessage(WixErrors.InvalidWixXmlNamespace(sourceLineNumbers, "Wix", CompilerCore.WixNamespace.ToString()));
198 }
199 else
200 {
201 this.core.OnMessage(WixErrors.InvalidWixXmlNamespace(sourceLineNumbers, "Wix", source.Root.Name.NamespaceName, CompilerCore.WixNamespace.ToString()));
202 }
203 }
204 }
205 else
206 {
207 this.core.OnMessage(WixErrors.InvalidDocumentElement(sourceLineNumbers, source.Root.Name.LocalName, "source", "Wix"));
208 }
209
210 // Resolve any Component Id placeholders compiled into the intermediate.
211 if (0 < this.componentIdPlaceholdersResolver.VariableCount)
212 {
213 foreach (var section in target.Sections)
214 {
215 foreach (Table table in section.Tables)
216 {
217 foreach (Row row in table.Rows)
218 {
219 foreach (Field field in row.Fields)
220 {
221 if (field.Data is string)
222 {
223 bool isDefault = false;
224 bool delayedResolve = false;
225 field.Data = this.componentIdPlaceholdersResolver.ResolveVariables(row.SourceLineNumbers, (string)field.Data, false, false, ref isDefault, ref delayedResolve);
226 }
227 }
228 }
229 }
230 }
231 }
232
233 // inspect the document
234 InspectorCore inspectorCore = new InspectorCore();
235 foreach (InspectorExtension inspectorExtension in this.inspectorExtensions)
236 {
237 inspectorExtension.Core = inspectorCore;
238 inspectorExtension.InspectIntermediate(target);
239
240 // reset
241 inspectorExtension.Core = null;
242 }
243
244 if (inspectorCore.EncounteredError)
245 {
246 encounteredError = true;
247 }
248 }
249 finally
250 {
251 if (this.core.EncounteredError)
252 {
253 encounteredError = true;
254 }
255
256 foreach (CompilerExtension extension in this.extensions.Values)
257 {
258 extension.Finish();
259 extension.Core = null;
260 }
261 this.core = null;
262 }
263
264 // return the compiled intermediate only if it completed successfully
265 return (encounteredError ? null : target);
266 }
267
268 /// <summary>
269 /// Uppercases the first character of a string.
270 /// </summary>
271 /// <param name="s">String to uppercase first character of.</param>
272 /// <returns>String with first character uppercased.</returns>
273 private static string UppercaseFirstChar(string s)
274 {
275 if (0 == s.Length)
276 {
277 return s;
278 }
279
280 return String.Concat(s.Substring(0, 1).ToUpper(CultureInfo.InvariantCulture), s.Substring(1));
281 }
282
283 /// <summary>
284 /// Lowercases the string if present.
285 /// </summary>
286 /// <param name="s">String to lowercase.</param>
287 /// <returns>Null if the string is null, otherwise returns the lowercase.</returns>
288 private static string LowercaseOrNull(string s)
289 {
290 return (null == s) ? s : s.ToLowerInvariant();
291 }
292
293 /// <summary>
294 /// Given a possible short and long file name, create an msi filename value.
295 /// </summary>
296 /// <param name="shortName">The short file name.</param>
297 /// <param name="longName">Possibly the long file name.</param>
298 /// <returns>The value in the msi filename data type.</returns>
299 private string GetMsiFilenameValue(string shortName, string longName)
300 {
301 if (null != shortName && null != longName && !String.Equals(shortName, longName, StringComparison.OrdinalIgnoreCase))
302 {
303 return String.Format(CultureInfo.InvariantCulture, "{0}|{1}", shortName, longName);
304 }
305 else
306 {
307 if (this.core.IsValidShortFilename(longName, false))
308 {
309 return longName;
310 }
311 else
312 {
313 return shortName;
314 }
315 }
316 }
317
318 /// <summary>
319 /// Adds a search property to the active section.
320 /// </summary>
321 /// <param name="sourceLineNumbers">Current source/line number of processing.</param>
322 /// <param name="property">Property to add to search.</param>
323 /// <param name="signature">Signature for search.</param>
324 private void AddAppSearch(SourceLineNumber sourceLineNumbers, Identifier property, string signature)
325 {
326 if (!this.core.EncounteredError)
327 {
328 if (property.Id != property.Id.ToUpperInvariant())
329 {
330 this.core.OnMessage(WixErrors.SearchPropertyNotUppercase(sourceLineNumbers, "Property", "Id", property.Id));
331 }
332
333 Row row = this.core.CreateRow(sourceLineNumbers, "AppSearch", property);
334 row[1] = signature;
335 }
336 }
337
338 /// <summary>
339 /// Adds a property to the active section.
340 /// </summary>
341 /// <param name="sourceLineNumbers">Current source/line number of processing.</param>
342 /// <param name="property">Name of property to add.</param>
343 /// <param name="value">Value of property.</param>
344 /// <param name="admin">Flag if property is an admin property.</param>
345 /// <param name="secure">Flag if property is a secure property.</param>
346 /// <param name="hidden">Flag if property is to be hidden.</param>
347 /// <param name="fragment">Adds the property to a new section.</param>
348 private void AddProperty(SourceLineNumber sourceLineNumbers, Identifier property, string value, bool admin, bool secure, bool hidden, bool fragment)
349 {
350 // properties without a valid identifier should not be processed any further
351 if (null == property || String.IsNullOrEmpty(property.Id))
352 {
353 return;
354 }
355
356 if (!String.IsNullOrEmpty(value))
357 {
358 Regex regex = new Regex(@"\[(?<identifier>[a-zA-Z_][a-zA-Z0-9_\.]*)]", RegexOptions.Compiled | RegexOptions.Singleline | RegexOptions.ExplicitCapture);
359 MatchCollection matches = regex.Matches(value);
360
361 foreach (Match match in matches)
362 {
363 Group group = match.Groups["identifier"];
364 if (group.Success)
365 {
366 this.core.OnMessage(WixWarnings.PropertyValueContainsPropertyReference(sourceLineNumbers, property.Id, group.Value));
367 }
368 }
369 }
370
371 if (!this.core.EncounteredError)
372 {
373 Section section = this.core.ActiveSection;
374
375 // Add the row to a separate section if requested.
376 if (fragment)
377 {
378 string id = String.Concat(this.core.ActiveSection.Id, ".", property.Id);
379
380 section = this.core.CreateSection(id, SectionType.Fragment, this.core.ActiveSection.Codepage);
381
382 // Reference the property in the active section.
383 this.core.CreateSimpleReference(sourceLineNumbers, "Property", property.Id);
384 }
385
386 Row row = this.core.CreateRow(sourceLineNumbers, "Property", section, property);
387
388 // Allow row to exist with no value so that PropertyRefs can be made for *Search elements
389 // the linker will remove these rows before the final output is created.
390 if (null != value)
391 {
392 row[1] = value;
393 }
394
395 if (admin || hidden || secure)
396 {
397 this.AddWixPropertyRow(sourceLineNumbers, property, admin, secure, hidden, section);
398 }
399 }
400 }
401
402 private WixPropertyRow AddWixPropertyRow(SourceLineNumber sourceLineNumbers, Identifier property, bool admin, bool secure, bool hidden, Section section = null)
403 {
404 if (secure && property.Id != property.Id.ToUpperInvariant())
405 {
406 this.core.OnMessage(WixErrors.SecurePropertyNotUppercase(sourceLineNumbers, "Property", "Id", property.Id));
407 }
408
409 if (null == section)
410 {
411 section = this.core.ActiveSection;
412
413 this.core.EnsureTable(sourceLineNumbers, "Property"); // Property table is always required when using WixProperty table.
414 }
415
416 WixPropertyRow row = (WixPropertyRow)this.core.CreateRow(sourceLineNumbers, "WixProperty", section, property);
417 row.Admin = admin;
418 row.Hidden = hidden;
419 row.Secure = secure;
420
421 return row;
422 }
423
424 /// <summary>
425 /// Adds a "implemented category" registry key to active section.
426 /// </summary>
427 /// <param name="sourceLineNumbers">Current source/line number of processing.</param>
428 /// <param name="categoryId">GUID for category.</param>
429 /// <param name="classId">ClassId for to mark "implemented".</param>
430 /// <param name="componentId">Identifier of parent component.</param>
431 private void RegisterImplementedCategories(SourceLineNumber sourceLineNumbers, string categoryId, string classId, string componentId)
432 {
433 this.core.CreateRegistryRow(sourceLineNumbers, MsiInterop.MsidbRegistryRootClassesRoot, String.Concat("CLSID\\", classId, "\\Implemented Categories\\", categoryId), "*", null, componentId);
434 }
435
436 /// <summary>
437 /// Parses an application identifer element.
438 /// </summary>
439 /// <param name="node">Element to parse.</param>
440 /// <param name="componentId">Identifier of parent component.</param>
441 /// <param name="advertise">The required advertise state (set depending upon the parent).</param>
442 /// <param name="fileServer">Optional file identifier for CLSID when not advertised.</param>
443 /// <param name="typeLibId">Optional TypeLib GUID for CLSID.</param>
444 /// <param name="typeLibVersion">Optional TypeLib Version for CLSID Interfaces (if any).</param>
445 private void ParseAppIdElement(XElement node, string componentId, YesNoType advertise, string fileServer, string typeLibId, string typeLibVersion)
446 {
447 SourceLineNumber sourceLineNumbers = Preprocessor.GetSourceLineNumbers(node);
448 string appId = null;
449 string remoteServerName = null;
450 string localService = null;
451 string serviceParameters = null;
452 string dllSurrogate = null;
453 YesNoType activateAtStorage = YesNoType.NotSet;
454 YesNoType appIdAdvertise = YesNoType.NotSet;
455 YesNoType runAsInteractiveUser = YesNoType.NotSet;
456 string description = null;
457
458 foreach (XAttribute attrib in node.Attributes())
459 {
460 if (String.IsNullOrEmpty(attrib.Name.NamespaceName) || CompilerCore.WixNamespace == attrib.Name.Namespace)
461 {
462 switch (attrib.Name.LocalName)
463 {
464 case "Id":
465 appId = this.core.GetAttributeGuidValue(sourceLineNumbers, attrib, false);
466 break;
467 case "ActivateAtStorage":
468 activateAtStorage = this.core.GetAttributeYesNoValue(sourceLineNumbers, attrib);
469 break;
470 case "Advertise":
471 appIdAdvertise = this.core.GetAttributeYesNoValue(sourceLineNumbers, attrib);
472 break;
473 case "Description": description = this.core.GetAttributeValue(sourceLineNumbers, attrib);
474 break;
475 case "DllSurrogate":
476 dllSurrogate = this.core.GetAttributeValue(sourceLineNumbers, attrib, EmptyRule.CanBeEmpty);
477 break;
478 case "LocalService":
479 localService = this.core.GetAttributeValue(sourceLineNumbers, attrib);
480 break;
481 case "RemoteServerName":
482 remoteServerName = this.core.GetAttributeValue(sourceLineNumbers, attrib);
483 break;
484 case "RunAsInteractiveUser":
485 runAsInteractiveUser = this.core.GetAttributeYesNoValue(sourceLineNumbers, attrib);
486 break;
487 case "ServiceParameters":
488 serviceParameters = this.core.GetAttributeValue(sourceLineNumbers, attrib);
489 break;
490 default:
491 this.core.UnexpectedAttribute(node, attrib);
492 break;
493 }
494 }
495 else
496 {
497 this.core.ParseExtensionAttribute(node, attrib);
498 }
499 }
500
501 if (null == appId)
502 {
503 this.core.OnMessage(WixErrors.ExpectedAttribute(sourceLineNumbers, node.Name.LocalName, "Id"));
504 }
505
506 if ((YesNoType.No == advertise && YesNoType.Yes == appIdAdvertise) || (YesNoType.Yes == advertise && YesNoType.No == appIdAdvertise))
507 {
508 this.core.OnMessage(WixErrors.AppIdIncompatibleAdvertiseState(sourceLineNumbers, node.Name.LocalName, "Advertise", appIdAdvertise.ToString(), advertise.ToString()));
509 }
510 else
511 {
512 advertise = appIdAdvertise;
513 }
514
515 // if the advertise state has not been set, default to non-advertised
516 if (YesNoType.NotSet == advertise)
517 {
518 advertise = YesNoType.No;
519 }
520
521 foreach (XElement child in node.Elements())
522 {
523 if (CompilerCore.WixNamespace == child.Name.Namespace)
524 {
525 switch (child.Name.LocalName)
526 {
527 case "Class":
528 this.ParseClassElement(child, componentId, advertise, fileServer, typeLibId, typeLibVersion, appId);
529 break;
530 default:
531 this.core.UnexpectedElement(node, child);
532 break;
533 }
534 }
535 else
536 {
537 this.core.ParseExtensionElement(node, child);
538 }
539 }
540
541 if (YesNoType.Yes == advertise)
542 {
543 if (null != description)
544 {
545 this.core.OnMessage(WixErrors.IllegalAttributeWhenAdvertised(sourceLineNumbers, node.Name.LocalName, "Description"));
546 }
547
548 if (!this.core.EncounteredError)
549 {
550 Row row = this.core.CreateRow(sourceLineNumbers, "AppId");
551 row[0] = appId;
552 row[1] = remoteServerName;
553 row[2] = localService;
554 row[3] = serviceParameters;
555 row[4] = dllSurrogate;
556 if (YesNoType.Yes == activateAtStorage)
557 {
558 row[5] = 1;
559 }
560
561 if (YesNoType.Yes == runAsInteractiveUser)
562 {
563 row[6] = 1;
564 }
565 }
566 }
567 else if (YesNoType.No == advertise)
568 {
569 if (null != description)
570 {
571 this.core.CreateRegistryRow(sourceLineNumbers, MsiInterop.MsidbRegistryRootClassesRoot, String.Concat("AppID\\", appId), null, description, componentId);
572 }
573 else
574 {
575 this.core.CreateRegistryRow(sourceLineNumbers, MsiInterop.MsidbRegistryRootClassesRoot, String.Concat("AppID\\", appId), "+", null, componentId);
576 }
577
578 if (null != remoteServerName)
579 {
580 this.core.CreateRegistryRow(sourceLineNumbers, MsiInterop.MsidbRegistryRootClassesRoot, String.Concat("AppID\\", appId), "RemoteServerName", remoteServerName, componentId);
581 }
582
583 if (null != localService)
584 {
585 this.core.CreateRegistryRow(sourceLineNumbers, MsiInterop.MsidbRegistryRootClassesRoot, String.Concat("AppID\\", appId), "LocalService", localService, componentId);
586 }
587
588 if (null != serviceParameters)
589 {
590 this.core.CreateRegistryRow(sourceLineNumbers, MsiInterop.MsidbRegistryRootClassesRoot, String.Concat("AppID\\", appId), "ServiceParameters", serviceParameters, componentId);
591 }
592
593 if (null != dllSurrogate)
594 {
595 this.core.CreateRegistryRow(sourceLineNumbers, MsiInterop.MsidbRegistryRootClassesRoot, String.Concat("AppID\\", appId), "DllSurrogate", dllSurrogate, componentId);
596 }
597
598 if (YesNoType.Yes == activateAtStorage)
599 {
600 this.core.CreateRegistryRow(sourceLineNumbers, MsiInterop.MsidbRegistryRootClassesRoot, String.Concat("AppID\\", appId), "ActivateAtStorage", "Y", componentId);
601 }
602
603 if (YesNoType.Yes == runAsInteractiveUser)
604 {
605 this.core.CreateRegistryRow(sourceLineNumbers, MsiInterop.MsidbRegistryRootClassesRoot, String.Concat("AppID\\", appId), "RunAs", "Interactive User", componentId);
606 }
607 }
608 }
609
610 /// <summary>
611 /// Parses an AssemblyName element.
612 /// </summary>
613 /// <param name="node">File element to parse.</param>
614 /// <param name="componentId">Parent's component id.</param>
615 private void ParseAssemblyName(XElement node, string componentId)
616 {
617 SourceLineNumber sourceLineNumbers = Preprocessor.GetSourceLineNumbers(node);
618 string id = null;
619 string value = null;
620
621 foreach (XAttribute attrib in node.Attributes())
622 {
623 if (String.IsNullOrEmpty(attrib.Name.NamespaceName) || CompilerCore.WixNamespace == attrib.Name.Namespace)
624 {
625 switch (attrib.Name.LocalName)
626 {
627 case "Id":
628 id = this.core.GetAttributeValue(sourceLineNumbers, attrib);
629 break;
630 case "Value":
631 value = this.core.GetAttributeValue(sourceLineNumbers, attrib);
632 break;
633 default:
634 this.core.UnexpectedAttribute(node, attrib);
635 break;
636 }
637 }
638 else
639 {
640 this.core.ParseExtensionAttribute(node, attrib);
641 }
642 }
643
644 if (null == id)
645 {
646 this.core.OnMessage(WixErrors.ExpectedAttribute(sourceLineNumbers, node.Name.LocalName, "Id"));
647 }
648
649 this.core.ParseForExtensionElements(node);
650
651 if (!this.core.EncounteredError)
652 {
653 Row row = this.core.CreateRow(sourceLineNumbers, "MsiAssemblyName");
654 row[0] = componentId;
655 row[1] = id;
656 row[2] = value;
657 }
658 }
659
660 /// <summary>
661 /// Parses a binary element.
662 /// </summary>
663 /// <param name="node">Element to parse.</param>
664 /// <returns>Identifier for the new row.</returns>
665 [SuppressMessage("Microsoft.Performance", "CA1800:DoNotCastUnnecessarily")]
666 private Identifier ParseBinaryElement(XElement node)
667 {
668 SourceLineNumber sourceLineNumbers = Preprocessor.GetSourceLineNumbers(node);
669 Identifier id = null;
670 string sourceFile = null;
671 YesNoType suppressModularization = YesNoType.NotSet;
672
673 foreach (XAttribute attrib in node.Attributes())
674 {
675 if (String.IsNullOrEmpty(attrib.Name.NamespaceName) || CompilerCore.WixNamespace == attrib.Name.Namespace)
676 {
677 switch (attrib.Name.LocalName)
678 {
679 case "Id":
680 id = this.core.GetAttributeIdentifier(sourceLineNumbers, attrib);
681 break;
682 case "SourceFile":
683 case "src":
684 if (null != sourceFile)
685 {
686 this.core.OnMessage(WixErrors.IllegalAttributeWithOtherAttribute(sourceLineNumbers, node.Name.LocalName, "SourceFile", "src"));
687 }
688
689 if ("src" == attrib.Name.LocalName)
690 {
691 this.core.OnMessage(WixWarnings.DeprecatedAttribute(sourceLineNumbers, node.Name.LocalName, attrib.Name.LocalName, "SourceFile"));
692 }
693 sourceFile = this.core.GetAttributeValue(sourceLineNumbers, attrib);
694 break;
695 case "SuppressModularization":
696 suppressModularization = this.core.GetAttributeYesNoValue(sourceLineNumbers, attrib);
697 break;
698 default:
699 this.core.UnexpectedAttribute(node, attrib);
700 break;
701 }
702 }
703 else
704 {
705 this.core.ParseExtensionAttribute(node, attrib);
706 }
707 }
708
709 if (null == id)
710 {
711 this.core.OnMessage(WixErrors.ExpectedAttribute(sourceLineNumbers, node.Name.LocalName, "Id"));
712 id = Identifier.Invalid;
713 }
714 else if (!String.IsNullOrEmpty(id.Id)) // only check legal values
715 {
716 if (55 < id.Id.Length)
717 {
718 this.core.OnMessage(WixErrors.StreamNameTooLong(sourceLineNumbers, node.Name.LocalName, "Id", id.Id, id.Id.Length, 55));
719 }
720 else if (!this.compilingProduct) // if we're not doing a product then we can't be sure that a binary identifier will fit when modularized
721 {
722 if (18 < id.Id.Length)
723 {
724 this.core.OnMessage(WixWarnings.IdentifierCannotBeModularized(sourceLineNumbers, node.Name.LocalName, "Id", id.Id, id.Id.Length, 18));
725 }
726 }
727 }
728
729 if (null == sourceFile)
730 {
731 this.core.OnMessage(WixErrors.ExpectedAttribute(sourceLineNumbers, node.Name.LocalName, "SourceFile"));
732 }
733
734 this.core.ParseForExtensionElements(node);
735
736 if (!this.core.EncounteredError)
737 {
738 Row row = this.core.CreateRow(sourceLineNumbers, "Binary", id);
739 row[1] = sourceFile;
740
741 if (YesNoType.Yes == suppressModularization)
742 {
743 Row wixSuppressModularizationRow = this.core.CreateRow(sourceLineNumbers, "WixSuppressModularization");
744 wixSuppressModularizationRow[0] = id;
745 }
746 }
747
748 return id;
749 }
750
751 /// <summary>
752 /// Parses an icon element.
753 /// </summary>
754 /// <param name="node">Element to parse.</param>
755 /// <returns>Identifier for the new row.</returns>
756 private string ParseIconElement(XElement node)
757 {
758 SourceLineNumber sourceLineNumbers = Preprocessor.GetSourceLineNumbers(node);
759 Identifier id = null;
760 string sourceFile = null;
761
762 foreach (XAttribute attrib in node.Attributes())
763 {
764 if (String.IsNullOrEmpty(attrib.Name.NamespaceName) || CompilerCore.WixNamespace == attrib.Name.Namespace)
765 {
766 switch (attrib.Name.LocalName)
767 {
768 case "Id":
769 id = this.core.GetAttributeIdentifier(sourceLineNumbers, attrib);
770 break;
771 case "SourceFile":
772 sourceFile = this.core.GetAttributeValue(sourceLineNumbers, attrib);
773 break;
774 default:
775 this.core.UnexpectedAttribute(node, attrib);
776 break;
777 }
778 }
779 else
780 {
781 this.core.ParseExtensionAttribute(node, attrib);
782 }
783 }
784
785 if (null == id)
786 {
787 this.core.OnMessage(WixErrors.ExpectedAttribute(sourceLineNumbers, node.Name.LocalName, "Id"));
788 id = Identifier.Invalid;
789 }
790 else if (!String.IsNullOrEmpty(id.Id)) // only check legal values
791 {
792 if (57 < id.Id.Length)
793 {
794 this.core.OnMessage(WixErrors.StreamNameTooLong(sourceLineNumbers, node.Name.LocalName, "Id", id.Id, id.Id.Length, 57));
795 }
796 else if (!this.compilingProduct) // if we're not doing a product then we can't be sure that a binary identifier will fit when modularized
797 {
798 if (20 < id.Id.Length)
799 {
800 this.core.OnMessage(WixWarnings.IdentifierCannotBeModularized(sourceLineNumbers, node.Name.LocalName, "Id", id.Id, id.Id.Length, 20));
801 }
802 }
803 }
804
805 if (null == sourceFile)
806 {
807 this.core.OnMessage(WixErrors.ExpectedAttribute(sourceLineNumbers, node.Name.LocalName, "SourceFile"));
808 }
809
810 this.core.ParseForExtensionElements(node);
811
812 if (!this.core.EncounteredError)
813 {
814 Row row = this.core.CreateRow(sourceLineNumbers, "Icon", id);
815 row[1] = sourceFile;
816 }
817
818 return id.Id;
819 }
820
821 /// <summary>
822 /// Parses an InstanceTransforms element.
823 /// </summary>
824 /// <param name="node">Element to parse.</param>
825 private void ParseInstanceTransformsElement(XElement node)
826 {
827 SourceLineNumber sourceLineNumbers = Preprocessor.GetSourceLineNumbers(node);
828 string property = null;
829
830 foreach (XAttribute attrib in node.Attributes())
831 {
832 if (String.IsNullOrEmpty(attrib.Name.NamespaceName) || CompilerCore.WixNamespace == attrib.Name.Namespace)
833 {
834 switch (attrib.Name.LocalName)
835 {
836 case "Property":
837 property = this.core.GetAttributeIdentifierValue(sourceLineNumbers, attrib);
838 this.core.CreateSimpleReference(sourceLineNumbers, "Property", property);
839 break;
840 default:
841 this.core.UnexpectedAttribute(node, attrib);
842 break;
843 }
844 }
845 else
846 {
847 this.core.ParseExtensionAttribute(node, attrib);
848 }
849 }
850
851 if (null == property)
852 {
853 this.core.OnMessage(WixErrors.ExpectedAttribute(sourceLineNumbers, node.Name.LocalName, "Property"));
854 }
855
856 // find unexpected child elements
857 foreach (XElement child in node.Elements())
858 {
859 if (CompilerCore.WixNamespace == child.Name.Namespace)
860 {
861 switch (child.Name.LocalName)
862 {
863 case "Instance":
864 ParseInstanceElement(child, property);
865 break;
866 default:
867 this.core.UnexpectedElement(node, child);
868 break;
869 }
870 }
871 else
872 {
873 this.core.ParseExtensionElement(node, child);
874 }
875 }
876 }
877
878 /// <summary>
879 /// Parses an instance element.
880 /// </summary>
881 /// <param name="node">Element to parse.</param>
882 /// <param name="componentId">Identifier of instance property.</param>
883 private void ParseInstanceElement(XElement node, string propertyId)
884 {
885 SourceLineNumber sourceLineNumbers = Preprocessor.GetSourceLineNumbers(node);
886 string id = null;
887 string productCode = null;
888 string productName = null;
889 string upgradeCode = null;
890
891 foreach (XAttribute attrib in node.Attributes())
892 {
893 if (String.IsNullOrEmpty(attrib.Name.NamespaceName) || CompilerCore.WixNamespace == attrib.Name.Namespace)
894 {
895 switch (attrib.Name.LocalName)
896 {
897 case "Id":
898 id = this.core.GetAttributeIdentifierValue(sourceLineNumbers, attrib);
899 break;
900 case "ProductCode":
901 productCode = this.core.GetAttributeGuidValue(sourceLineNumbers, attrib, true);
902 break;
903 case "ProductName":
904 productName = this.core.GetAttributeValue(sourceLineNumbers, attrib);
905 break;
906 case "UpgradeCode":
907 upgradeCode = this.core.GetAttributeGuidValue(sourceLineNumbers, attrib, false);
908 break;
909 default:
910 this.core.UnexpectedAttribute(node, attrib);
911 break;
912 }
913 }
914 else
915 {
916 this.core.ParseExtensionAttribute(node, attrib);
917 }
918 }
919
920 if (null == id)
921 {
922 this.core.OnMessage(WixErrors.ExpectedAttribute(sourceLineNumbers, node.Name.LocalName, "Id"));
923 }
924
925 if (null == productCode)
926 {
927 this.core.OnMessage(WixErrors.ExpectedAttribute(sourceLineNumbers, node.Name.LocalName, "ProductCode"));
928 }
929
930 this.core.ParseForExtensionElements(node);
931
932 if (!this.core.EncounteredError)
933 {
934 Row row = this.core.CreateRow(sourceLineNumbers, "WixInstanceTransforms");
935 row[0] = id;
936 row[1] = propertyId;
937 row[2] = productCode;
938 if (null != productName)
939 {
940 row[3] = productName;
941 }
942 if (null != upgradeCode)
943 {
944 row[4] = upgradeCode;
945 }
946 }
947 }
948
949 /// <summary>
950 /// Parses a category element.
951 /// </summary>
952 /// <param name="node">Element to parse.</param>
953 /// <param name="componentId">Identifier of parent component.</param>
954 private void ParseCategoryElement(XElement node, string componentId)
955 {
956 SourceLineNumber sourceLineNumbers = Preprocessor.GetSourceLineNumbers(node);
957 string id = null;
958 string appData = null;
959 string feature = null;
960 string qualifier = null;
961
962 foreach (XAttribute attrib in node.Attributes())
963 {
964 if (String.IsNullOrEmpty(attrib.Name.NamespaceName) || CompilerCore.WixNamespace == attrib.Name.Namespace)
965 {
966 switch (attrib.Name.LocalName)
967 {
968 case "Id":
969 id = this.core.GetAttributeGuidValue(sourceLineNumbers, attrib, false);
970 break;
971 case "AppData":
972 appData = this.core.GetAttributeValue(sourceLineNumbers, attrib);
973 break;
974 case "Feature":
975 feature = this.core.GetAttributeIdentifierValue(sourceLineNumbers, attrib);
976 this.core.CreateSimpleReference(sourceLineNumbers, "Feature", feature);
977 break;
978 case "Qualifier":
979 qualifier = this.core.GetAttributeValue(sourceLineNumbers, attrib);
980 break;
981 default:
982 this.core.UnexpectedAttribute(node, attrib);
983 break;
984 }
985 }
986 else
987 {
988 this.core.ParseExtensionAttribute(node, attrib);
989 }
990 }
991
992 if (null == id)
993 {
994 this.core.OnMessage(WixErrors.ExpectedAttribute(sourceLineNumbers, node.Name.LocalName, "Id"));
995 }
996
997 if (null == qualifier)
998 {
999 this.core.OnMessage(WixErrors.ExpectedAttribute(sourceLineNumbers, node.Name.LocalName, "Qualifier"));
1000 }
1001
1002 this.core.ParseForExtensionElements(node);
1003
1004 if (!this.core.EncounteredError)
1005 {
1006 Row row = this.core.CreateRow(sourceLineNumbers, "PublishComponent");
1007 row[0] = id;
1008 row[1] = qualifier;
1009 row[2] = componentId;
1010 row[3] = appData;
1011 if (null == feature)
1012 {
1013 row[4] = Guid.Empty.ToString("B");
1014 }
1015 else
1016 {
1017 row[4] = feature;
1018 }
1019 }
1020 }
1021
1022 /// <summary>
1023 /// Parses a class element.
1024 /// </summary>
1025 /// <param name="node">Element to parse.</param>
1026 /// <param name="componentId">Identifier of parent component.</param>
1027 /// <param name="advertise">Optional Advertise State for the parent AppId element (if any).</param>
1028 /// <param name="fileServer">Optional file identifier for CLSID when not advertised.</param>
1029 /// <param name="typeLibId">Optional TypeLib GUID for CLSID.</param>
1030 /// <param name="typeLibVersion">Optional TypeLib Version for CLSID Interfaces (if any).</param>
1031 /// <param name="parentAppId">Optional parent AppId.</param>
1032 private void ParseClassElement(XElement node, string componentId, YesNoType advertise, string fileServer, string typeLibId, string typeLibVersion, string parentAppId)
1033 {
1034 SourceLineNumber sourceLineNumbers = Preprocessor.GetSourceLineNumbers(node);
1035
1036 string appId = null;
1037 string argument = null;
1038 bool class16bit = false;
1039 bool class32bit = false;
1040 string classId = null;
1041 YesNoType classAdvertise = YesNoType.NotSet;
1042 string[] contexts = null;
1043 string formattedContextString = null;
1044 bool control = false;
1045 string defaultInprocHandler = null;
1046 string defaultProgId = null;
1047 string description = null;
1048 string fileTypeMask = null;
1049 string foreignServer = null;
1050 string icon = null;
1051 int iconIndex = CompilerConstants.IntegerNotSet;
1052 string insertable = null;
1053 string localFileServer = null;
1054 bool programmable = false;
1055 YesNoType relativePath = YesNoType.NotSet;
1056 bool safeForInit = false;
1057 bool safeForScripting = false;
1058 bool shortServerPath = false;
1059 string threadingModel = null;
1060 string version = null;
1061
1062 foreach (XAttribute attrib in node.Attributes())
1063 {
1064 if (String.IsNullOrEmpty(attrib.Name.NamespaceName) || CompilerCore.WixNamespace == attrib.Name.Namespace)
1065 {
1066 switch (attrib.Name.LocalName)
1067 {
1068 case "Id":
1069 classId = this.core.GetAttributeGuidValue(sourceLineNumbers, attrib, false);
1070 break;
1071 case "Advertise":
1072 classAdvertise = this.core.GetAttributeYesNoValue(sourceLineNumbers, attrib);
1073 break;
1074 case "AppId":
1075 appId = this.core.GetAttributeGuidValue(sourceLineNumbers, attrib, false);
1076 break;
1077 case "Argument":
1078 argument = this.core.GetAttributeValue(sourceLineNumbers, attrib);
1079 break;
1080 case "Context":
1081 contexts = this.core.GetAttributeValue(sourceLineNumbers, attrib).Split("\r\n\t ".ToCharArray(), StringSplitOptions.RemoveEmptyEntries);
1082 break;
1083 case "Control":
1084 control = YesNoType.Yes == this.core.GetAttributeYesNoValue(sourceLineNumbers, attrib);
1085 break;
1086 case "Description":
1087 description = this.core.GetAttributeValue(sourceLineNumbers, attrib);
1088 break;
1089 case "Handler":
1090 defaultInprocHandler = this.core.GetAttributeValue(sourceLineNumbers, attrib);
1091 break;
1092 case "Icon":
1093 icon = this.core.GetAttributeIdentifierValue(sourceLineNumbers, attrib);
1094 break;
1095 case "IconIndex":
1096 iconIndex = this.core.GetAttributeIntegerValue(sourceLineNumbers, attrib, short.MinValue + 1, short.MaxValue);
1097 break;
1098 case "RelativePath":
1099 relativePath = this.core.GetAttributeYesNoValue(sourceLineNumbers, attrib);
1100 break;
1101
1102 // The following attributes result in rows always added to the Registry table rather than the Class table
1103 case "Insertable":
1104 insertable = (YesNoType.Yes == this.core.GetAttributeYesNoValue(sourceLineNumbers, attrib)) ? "Insertable" : "NotInsertable";
1105 break;
1106 case "Programmable":
1107 programmable = YesNoType.Yes == this.core.GetAttributeYesNoValue(sourceLineNumbers, attrib);
1108 break;
1109 case "SafeForInitializing":
1110 safeForInit = YesNoType.Yes == this.core.GetAttributeYesNoValue(sourceLineNumbers, attrib);
1111 break;
1112 case "SafeForScripting":
1113 safeForScripting = YesNoType.Yes == this.core.GetAttributeYesNoValue(sourceLineNumbers, attrib);
1114 break;
1115 case "ForeignServer":
1116 foreignServer = this.core.GetAttributeValue(sourceLineNumbers, attrib);
1117 break;
1118 case "Server":
1119 localFileServer = this.core.GetAttributeValue(sourceLineNumbers, attrib);
1120 break;
1121 case "ShortPath":
1122 shortServerPath = YesNoType.Yes == this.core.GetAttributeYesNoValue(sourceLineNumbers, attrib);
1123 break;
1124 case "ThreadingModel":
1125 threadingModel = this.core.GetAttributeValue(sourceLineNumbers, attrib);
1126 break;
1127 case "Version":
1128 version = this.core.GetAttributeValue(sourceLineNumbers, attrib);
1129 break;
1130 default:
1131 this.core.UnexpectedAttribute(node, attrib);
1132 break;
1133 }
1134 }
1135 else
1136 {
1137 this.core.ParseExtensionAttribute(node, attrib);
1138 }
1139 }
1140
1141 if (null == classId)
1142 {
1143 this.core.OnMessage(WixErrors.ExpectedAttribute(sourceLineNumbers, node.Name.LocalName, "Id"));
1144 }
1145
1146 HashSet<string> uniqueContexts = new HashSet<string>();
1147 foreach (string context in contexts)
1148 {
1149 if (uniqueContexts.Contains(context))
1150 {
1151 this.core.OnMessage(WixErrors.DuplicateContextValue(sourceLineNumbers, context));
1152 }
1153 else
1154 {
1155 uniqueContexts.Add(context);
1156 }
1157
1158 if (context.EndsWith("32", StringComparison.Ordinal))
1159 {
1160 class32bit = true;
1161 }
1162 else
1163 {
1164 class16bit = true;
1165 }
1166 }
1167
1168 if ((YesNoType.No == advertise && YesNoType.Yes == classAdvertise) || (YesNoType.Yes == advertise && YesNoType.No == classAdvertise))
1169 {
1170 this.core.OnMessage(WixErrors.AdvertiseStateMustMatch(sourceLineNumbers, classAdvertise.ToString(), advertise.ToString()));
1171 }
1172 else
1173 {
1174 advertise = classAdvertise;
1175 }
1176
1177 // If the advertise state has not been set, default to non-advertised.
1178 if (YesNoType.NotSet == advertise)
1179 {
1180 advertise = YesNoType.No;
1181 }
1182
1183 if (YesNoType.Yes == advertise && 0 == contexts.Length)
1184 {
1185 this.core.OnMessage(WixErrors.ExpectedAttribute(sourceLineNumbers, node.Name.LocalName, "Context", "Advertise", "yes"));
1186 }
1187
1188 if (!String.IsNullOrEmpty(parentAppId) && !String.IsNullOrEmpty(appId))
1189 {
1190 this.core.OnMessage(WixErrors.IllegalAttributeWhenNested(sourceLineNumbers, node.Name.LocalName, "AppId", node.Parent.Name.LocalName));
1191 }
1192
1193 if (!String.IsNullOrEmpty(localFileServer))
1194 {
1195 this.core.CreateSimpleReference(sourceLineNumbers, "File", localFileServer);
1196 }
1197
1198 // Local variables used strictly for child node processing.
1199 int fileTypeMaskIndex = 0;
1200 YesNoType firstProgIdForClass = YesNoType.Yes;
1201
1202 foreach (XElement child in node.Elements())
1203 {
1204 if (CompilerCore.WixNamespace == child.Name.Namespace)
1205 {
1206 switch (child.Name.LocalName)
1207 {
1208 case "FileTypeMask":
1209 if (YesNoType.Yes == advertise)
1210 {
1211 fileTypeMask = String.Concat(fileTypeMask, null == fileTypeMask ? String.Empty : ";", this.ParseFileTypeMaskElement(child));
1212 }
1213 else if (YesNoType.No == advertise)
1214 {
1215 SourceLineNumber childSourceLineNumbers = Preprocessor.GetSourceLineNumbers(child);
1216 this.core.CreateRegistryRow(childSourceLineNumbers, MsiInterop.MsidbRegistryRootClassesRoot, String.Concat("FileType\\", classId, "\\", fileTypeMaskIndex.ToString()), String.Empty, this.ParseFileTypeMaskElement(child), componentId);
1217 fileTypeMaskIndex++;
1218 }
1219 break;
1220 case "Interface":
1221 this.ParseInterfaceElement(child, componentId, class16bit ? classId : null, class32bit ? classId : null, typeLibId, typeLibVersion);
1222 break;
1223 case "ProgId":
1224 {
1225 bool foundExtension = false;
1226 string progId = this.ParseProgIdElement(child, componentId, advertise, classId, description, null, ref foundExtension, firstProgIdForClass);
1227 if (null == defaultProgId)
1228 {
1229 defaultProgId = progId;
1230 }
1231 firstProgIdForClass = YesNoType.No;
1232 }
1233 break;
1234 default:
1235 this.core.UnexpectedElement(node, child);
1236 break;
1237 }
1238 }
1239 else
1240 {
1241 this.core.ParseExtensionElement(node, child);
1242 }
1243 }
1244
1245 // If this Class is being advertised.
1246 if (YesNoType.Yes == advertise)
1247 {
1248 if (null != fileServer || null != localFileServer)
1249 {
1250 this.core.OnMessage(WixErrors.IllegalAttributeWithOtherAttribute(sourceLineNumbers, node.Name.LocalName, "Server", "Advertise", "yes"));
1251 }
1252
1253 if (null != foreignServer)
1254 {
1255 this.core.OnMessage(WixErrors.IllegalAttributeWithOtherAttribute(sourceLineNumbers, node.Name.LocalName, "ForeignServer", "Advertise", "yes"));
1256 }
1257
1258 if (null == appId && null != parentAppId)
1259 {
1260 appId = parentAppId;
1261 }
1262
1263 // add a Class row for each context
1264 if (!this.core.EncounteredError)
1265 {
1266 foreach (string context in contexts)
1267 {
1268 Row row = this.core.CreateRow(sourceLineNumbers, "Class");
1269 row[0] = classId;
1270 row[1] = context;
1271 row[2] = componentId;
1272 row[3] = defaultProgId;
1273 row[4] = description;
1274 if (null != appId)
1275 {
1276 row[5] = appId;
1277 this.core.CreateSimpleReference(sourceLineNumbers, "AppId", appId);
1278 }
1279 row[6] = fileTypeMask;
1280 if (null != icon)
1281 {
1282 row[7] = icon;
1283 this.core.CreateSimpleReference(sourceLineNumbers, "Icon", icon);
1284 }
1285 if (CompilerConstants.IntegerNotSet != iconIndex)
1286 {
1287 row[8] = iconIndex;
1288 }
1289 row[9] = defaultInprocHandler;
1290 row[10] = argument;
1291 row[11] = Guid.Empty.ToString("B");
1292 if (YesNoType.Yes == relativePath)
1293 {
1294 row[12] = MsiInterop.MsidbClassAttributesRelativePath;
1295 }
1296 }
1297 }
1298 }
1299 else if (YesNoType.No == advertise)
1300 {
1301 if (null == fileServer && null == localFileServer && null == foreignServer)
1302 {
1303 this.core.OnMessage(WixErrors.ExpectedAttributes(sourceLineNumbers, node.Name.LocalName, "ForeignServer", "Server"));
1304 }
1305
1306 if (null != fileServer && null != foreignServer)
1307 {
1308 this.core.OnMessage(WixErrors.IllegalAttributeWhenNested(sourceLineNumbers, node.Name.LocalName, "ForeignServer", "File"));
1309 }
1310 else if (null != localFileServer && null != foreignServer)
1311 {
1312 this.core.OnMessage(WixErrors.IllegalAttributeWithOtherAttribute(sourceLineNumbers, node.Name.LocalName, "ForeignServer", "Server"));
1313 }
1314 else if (null == fileServer)
1315 {
1316 fileServer = localFileServer;
1317 }
1318
1319 if (null != appId) // need to use nesting (not a reference) for the unadvertised Class elements
1320 {
1321 this.core.OnMessage(WixErrors.IllegalAttributeWithOtherAttribute(sourceLineNumbers, node.Name.LocalName, "AppId", "Advertise", "no"));
1322 }
1323
1324 // add the core registry keys for each context in the class
1325 foreach (string context in contexts)
1326 {
1327 if (context.StartsWith("InprocServer", StringComparison.Ordinal)) // dll server
1328 {
1329 if (null != argument)
1330 {
1331 this.core.OnMessage(WixErrors.IllegalAttributeWithOtherAttribute(sourceLineNumbers, node.Name.LocalName, "Arguments", "Context", context));
1332 }
1333
1334 if (null != fileServer)
1335 {
1336 formattedContextString = String.Concat("[", shortServerPath ? "!" : "#", fileServer, "]");
1337 }
1338 else if (null != foreignServer)
1339 {
1340 formattedContextString = foreignServer;
1341 }
1342 }
1343 else if (context.StartsWith("LocalServer", StringComparison.Ordinal)) // exe server (quote the long path)
1344 {
1345 if (null != fileServer)
1346 {
1347 if (shortServerPath)
1348 {
1349 formattedContextString = String.Concat("[!", fileServer, "]");
1350 }
1351 else
1352 {
1353 formattedContextString = String.Concat("\"[#", fileServer, "]\"");
1354 }
1355 }
1356 else if (null != foreignServer)
1357 {
1358 formattedContextString = foreignServer;
1359 }
1360
1361 if (null != argument)
1362 {
1363 formattedContextString = String.Concat(formattedContextString, " ", argument);
1364 }
1365 }
1366 else
1367 {
1368 this.core.OnMessage(WixErrors.IllegalAttributeValue(sourceLineNumbers, node.Name.LocalName, "Context", context, "InprocServer", "InprocServer32", "LocalServer", "LocalServer32"));
1369 }
1370
1371 this.core.CreateRegistryRow(sourceLineNumbers, MsiInterop.MsidbRegistryRootClassesRoot, String.Concat("CLSID\\", classId, "\\", context), String.Empty, formattedContextString, componentId); // ClassId context
1372
1373 if (null != icon) // ClassId default icon
1374 {
1375 this.core.CreateSimpleReference(sourceLineNumbers, "File", icon);
1376
1377 icon = String.Format(CultureInfo.InvariantCulture, "\"[#{0}]\"", icon);
1378
1379 if (CompilerConstants.IntegerNotSet != iconIndex)
1380 {
1381 icon = String.Concat(icon, ",", iconIndex);
1382 }
1383 this.core.CreateRegistryRow(sourceLineNumbers, MsiInterop.MsidbRegistryRootClassesRoot, String.Concat("CLSID\\", classId, "\\", context, "\\DefaultIcon"), String.Empty, icon, componentId);
1384 }
1385 }
1386
1387 if (null != parentAppId) // ClassId AppId (must be specified via nesting, not with the AppId attribute)
1388 {
1389 this.core.CreateRegistryRow(sourceLineNumbers, MsiInterop.MsidbRegistryRootClassesRoot, String.Concat("CLSID\\", classId), "AppID", parentAppId, componentId);
1390 }
1391
1392 if (null != description) // ClassId description
1393 {
1394 this.core.CreateRegistryRow(sourceLineNumbers, MsiInterop.MsidbRegistryRootClassesRoot, String.Concat("CLSID\\", classId), String.Empty, description, componentId);
1395 }
1396
1397 if (null != defaultInprocHandler)
1398 {
1399 switch (defaultInprocHandler) // ClassId Default Inproc Handler
1400 {
1401 case "1":
1402 this.core.CreateRegistryRow(sourceLineNumbers, MsiInterop.MsidbRegistryRootClassesRoot, String.Concat("CLSID\\", classId, "\\InprocHandler"), String.Empty, "ole.dll", componentId);
1403 break;
1404 case "2":
1405 this.core.CreateRegistryRow(sourceLineNumbers, MsiInterop.MsidbRegistryRootClassesRoot, String.Concat("CLSID\\", classId, "\\InprocHandler32"), String.Empty, "ole32.dll", componentId);
1406 break;
1407 case "3":
1408 this.core.CreateRegistryRow(sourceLineNumbers, MsiInterop.MsidbRegistryRootClassesRoot, String.Concat("CLSID\\", classId, "\\InprocHandler"), String.Empty, "ole.dll", componentId);
1409 this.core.CreateRegistryRow(sourceLineNumbers, MsiInterop.MsidbRegistryRootClassesRoot, String.Concat("CLSID\\", classId, "\\InprocHandler32"), String.Empty, "ole32.dll", componentId);
1410 break;
1411 default:
1412 this.core.CreateRegistryRow(sourceLineNumbers, MsiInterop.MsidbRegistryRootClassesRoot, String.Concat("CLSID\\", classId, "\\InprocHandler32"), String.Empty, defaultInprocHandler, componentId);
1413 break;
1414 }
1415 }
1416
1417 if (YesNoType.NotSet != relativePath) // ClassId's RelativePath
1418 {
1419 this.core.OnMessage(WixErrors.RelativePathForRegistryElement(sourceLineNumbers));
1420 }
1421 }
1422
1423 if (null != threadingModel)
1424 {
1425 threadingModel = Compiler.UppercaseFirstChar(threadingModel);
1426
1427 // add a threading model for each context in the class
1428 foreach (string context in contexts)
1429 {
1430 this.core.CreateRegistryRow(sourceLineNumbers, MsiInterop.MsidbRegistryRootClassesRoot, String.Concat("CLSID\\", classId, "\\", context), "ThreadingModel", threadingModel, componentId);
1431 }
1432 }
1433
1434 if (null != typeLibId)
1435 {
1436 this.core.CreateRegistryRow(sourceLineNumbers, MsiInterop.MsidbRegistryRootClassesRoot, String.Concat("CLSID\\", classId, "\\TypeLib"), null, typeLibId, componentId);
1437 }
1438
1439 if (null != version)
1440 {
1441 this.core.CreateRegistryRow(sourceLineNumbers, MsiInterop.MsidbRegistryRootClassesRoot, String.Concat("CLSID\\", classId, "\\Version"), null, version, componentId);
1442 }
1443
1444 if (null != insertable)
1445 {
1446 // Add "*" for name so that any subkeys (shouldn't be any) are removed on uninstall.
1447 this.core.CreateRegistryRow(sourceLineNumbers, MsiInterop.MsidbRegistryRootClassesRoot, String.Concat("CLSID\\", classId, "\\", insertable), "*", null, componentId);
1448 }
1449
1450 if (control)
1451 {
1452 // Add "*" for name so that any subkeys (shouldn't be any) are removed on uninstall.
1453 this.core.CreateRegistryRow(sourceLineNumbers, MsiInterop.MsidbRegistryRootClassesRoot, String.Concat("CLSID\\", classId, "\\Control"), "*", null, componentId);
1454 }
1455
1456 if (programmable)
1457 {
1458 // Add "*" for name so that any subkeys (shouldn't be any) are removed on uninstall.
1459 this.core.CreateRegistryRow(sourceLineNumbers, MsiInterop.MsidbRegistryRootClassesRoot, String.Concat("CLSID\\", classId, "\\Programmable"), "*", null, componentId);
1460 }
1461
1462 if (safeForInit)
1463 {
1464 this.RegisterImplementedCategories(sourceLineNumbers, "{7DD95802-9882-11CF-9FA9-00AA006C42C4}", classId, componentId);
1465 }
1466
1467 if (safeForScripting)
1468 {
1469 this.RegisterImplementedCategories(sourceLineNumbers, "{7DD95801-9882-11CF-9FA9-00AA006C42C4}", classId, componentId);
1470 }
1471 }
1472
1473 /// <summary>
1474 /// Parses an Interface element.
1475 /// </summary>
1476 /// <param name="node">Element to parse.</param>
1477 /// <param name="componentId">Identifier of parent component.</param>
1478 /// <param name="proxyId">16-bit proxy for interface.</param>
1479 /// <param name="proxyId32">32-bit proxy for interface.</param>
1480 /// <param name="typeLibId">Optional TypeLib GUID for CLSID.</param>
1481 /// <param name="typelibVersion">Version of the TypeLib to which this interface belongs. Required if typeLibId is specified</param>
1482 private void ParseInterfaceElement(XElement node, string componentId, string proxyId, string proxyId32, string typeLibId, string typelibVersion)
1483 {
1484 SourceLineNumber sourceLineNumbers = Preprocessor.GetSourceLineNumbers(node);
1485 string baseInterface = null;
1486 string interfaceId = null;
1487 string name = null;
1488 int numMethods = CompilerConstants.IntegerNotSet;
1489 bool versioned = true;
1490
1491 foreach (XAttribute attrib in node.Attributes())
1492 {
1493 if (String.IsNullOrEmpty(attrib.Name.NamespaceName) || CompilerCore.WixNamespace == attrib.Name.Namespace)
1494 {
1495 switch (attrib.Name.LocalName)
1496 {
1497 case "Id":
1498 interfaceId = this.core.GetAttributeGuidValue(sourceLineNumbers, attrib, false);
1499 break;
1500 case "BaseInterface":
1501 baseInterface = this.core.GetAttributeGuidValue(sourceLineNumbers, attrib, false);
1502 break;
1503 case "Name":
1504 name = this.core.GetAttributeValue(sourceLineNumbers, attrib);
1505 break;
1506 case "NumMethods":
1507 numMethods = this.core.GetAttributeIntegerValue(sourceLineNumbers, attrib, 0, int.MaxValue);
1508 break;
1509 case "ProxyStubClassId":
1510 proxyId = this.core.GetAttributeValue(sourceLineNumbers, attrib);
1511 break;
1512 case "ProxyStubClassId32":
1513 proxyId32 = this.core.GetAttributeGuidValue(sourceLineNumbers, attrib, false);
1514 break;
1515 case "Versioned":
1516 versioned = YesNoType.Yes == this.core.GetAttributeYesNoValue(sourceLineNumbers, attrib);
1517 break;
1518 default:
1519 this.core.UnexpectedAttribute(node, attrib);
1520 break;
1521 }
1522 }
1523 else
1524 {
1525 this.core.ParseExtensionAttribute(node, attrib);
1526 }
1527 }
1528
1529 if (null == interfaceId)
1530 {
1531 this.core.OnMessage(WixErrors.ExpectedAttribute(sourceLineNumbers, node.Name.LocalName, "Id"));
1532 }
1533
1534 if (null == name)
1535 {
1536 this.core.OnMessage(WixErrors.ExpectedAttribute(sourceLineNumbers, node.Name.LocalName, "Name"));
1537 }
1538
1539 this.core.ParseForExtensionElements(node);
1540
1541 this.core.CreateRegistryRow(sourceLineNumbers, MsiInterop.MsidbRegistryRootClassesRoot, String.Concat("Interface\\", interfaceId), null, name, componentId);
1542 if (null != typeLibId)
1543 {
1544 this.core.CreateRegistryRow(sourceLineNumbers, MsiInterop.MsidbRegistryRootClassesRoot, String.Concat("Interface\\", interfaceId, "\\TypeLib"), null, typeLibId, componentId);
1545 if (versioned)
1546 {
1547 this.core.CreateRegistryRow(sourceLineNumbers, MsiInterop.MsidbRegistryRootClassesRoot, String.Concat("Interface\\", interfaceId, "\\TypeLib"), "Version", typelibVersion, componentId);
1548 }
1549 }
1550
1551 if (null != baseInterface)
1552 {
1553 this.core.CreateRegistryRow(sourceLineNumbers, MsiInterop.MsidbRegistryRootClassesRoot, String.Concat("Interface\\", interfaceId, "\\BaseInterface"), null, baseInterface, componentId);
1554 }
1555
1556 if (CompilerConstants.IntegerNotSet != numMethods)
1557 {
1558 this.core.CreateRegistryRow(sourceLineNumbers, MsiInterop.MsidbRegistryRootClassesRoot, String.Concat("Interface\\", interfaceId, "\\NumMethods"), null, numMethods.ToString(), componentId);
1559 }
1560
1561 if (null != proxyId)
1562 {
1563 this.core.CreateRegistryRow(sourceLineNumbers, MsiInterop.MsidbRegistryRootClassesRoot, String.Concat("Interface\\", interfaceId, "\\ProxyStubClsid"), null, proxyId, componentId);
1564 }
1565
1566 if (null != proxyId32)
1567 {
1568 this.core.CreateRegistryRow(sourceLineNumbers, MsiInterop.MsidbRegistryRootClassesRoot, String.Concat("Interface\\", interfaceId, "\\ProxyStubClsid32"), null, proxyId32, componentId);
1569 }
1570 }
1571
1572 /// <summary>
1573 /// Parses a CLSID's file type mask element.
1574 /// </summary>
1575 /// <param name="node">Element to parse.</param>
1576 /// <returns>String representing the file type mask elements.</returns>
1577 private string ParseFileTypeMaskElement(XElement node)
1578 {
1579 SourceLineNumber sourceLineNumbers = Preprocessor.GetSourceLineNumbers(node);
1580 int cb = 0;
1581 int offset = CompilerConstants.IntegerNotSet;
1582 string mask = null;
1583 string value = null;
1584
1585 foreach (XAttribute attrib in node.Attributes())
1586 {
1587 if (String.IsNullOrEmpty(attrib.Name.NamespaceName) || CompilerCore.WixNamespace == attrib.Name.Namespace)
1588 {
1589 switch (attrib.Name.LocalName)
1590 {
1591 case "Mask":
1592 mask = this.core.GetAttributeValue(sourceLineNumbers, attrib);
1593 break;
1594 case "Offset":
1595 offset = this.core.GetAttributeIntegerValue(sourceLineNumbers, attrib, 0, int.MaxValue);
1596 break;
1597 case "Value":
1598 value = this.core.GetAttributeValue(sourceLineNumbers, attrib);
1599 break;
1600 default:
1601 this.core.UnexpectedAttribute(node, attrib);
1602 break;
1603 }
1604 }
1605 else
1606 {
1607 this.core.ParseExtensionAttribute(node, attrib);
1608 }
1609 }
1610
1611
1612 if (null == mask)
1613 {
1614 this.core.OnMessage(WixErrors.ExpectedAttribute(sourceLineNumbers, node.Name.LocalName, "Mask"));
1615 }
1616
1617 if (CompilerConstants.IntegerNotSet == offset)
1618 {
1619 this.core.OnMessage(WixErrors.ExpectedAttribute(sourceLineNumbers, node.Name.LocalName, "Offset"));
1620 }
1621
1622 if (null == value)
1623 {
1624 this.core.OnMessage(WixErrors.ExpectedAttribute(sourceLineNumbers, node.Name.LocalName, "Value"));
1625 }
1626
1627 this.core.ParseForExtensionElements(node);
1628
1629 if (!this.core.EncounteredError)
1630 {
1631 if (mask.Length != value.Length)
1632 {
1633 this.core.OnMessage(WixErrors.ValueAndMaskMustBeSameLength(sourceLineNumbers));
1634 }
1635 cb = mask.Length / 2;
1636 }
1637
1638 return String.Concat(offset.ToString(CultureInfo.InvariantCulture.NumberFormat), ",", cb.ToString(CultureInfo.InvariantCulture.NumberFormat), ",", mask, ",", value);
1639 }
1640
1641 /// <summary>
1642 /// Parses a product search element.
1643 /// </summary>
1644 /// <param name="node">Element to parse.</param>
1645 /// <returns>Signature for search element.</returns>
1646 private void ParseProductSearchElement(XElement node, string propertyId)
1647 {
1648 SourceLineNumber sourceLineNumbers = Preprocessor.GetSourceLineNumbers(node);
1649
1650 string upgradeCode = null;
1651 string language = null;
1652 string maximum = null;
1653 string minimum = null;
1654 int options = MsiInterop.MsidbUpgradeAttributesVersionMinInclusive | MsiInterop.MsidbUpgradeAttributesOnlyDetect;
1655
1656 foreach (XAttribute attrib in node.Attributes())
1657 {
1658 if (String.IsNullOrEmpty(attrib.Name.NamespaceName) || CompilerCore.WixNamespace == attrib.Name.Namespace)
1659 {
1660 switch (attrib.Name.LocalName)
1661 {
1662 case "ExcludeLanguages":
1663 if (YesNoType.Yes == this.core.GetAttributeYesNoValue(sourceLineNumbers, attrib))
1664 {
1665 options |= MsiInterop.MsidbUpgradeAttributesLanguagesExclusive;
1666 }
1667 break;
1668 case "IncludeMaximum":
1669 if (YesNoType.Yes == this.core.GetAttributeYesNoValue(sourceLineNumbers, attrib))
1670 {
1671 options |= MsiInterop.MsidbUpgradeAttributesVersionMaxInclusive;
1672 }
1673 break;
1674 case "IncludeMinimum": // this is "yes" by default
1675 if (YesNoType.No == this.core.GetAttributeYesNoValue(sourceLineNumbers, attrib))
1676 {
1677 options &= ~MsiInterop.MsidbUpgradeAttributesVersionMinInclusive;
1678 }
1679 break;
1680 case "Language":
1681 language = this.core.GetAttributeValue(sourceLineNumbers, attrib);
1682 break;
1683 case "Minimum":
1684 minimum = this.core.GetAttributeValue(sourceLineNumbers, attrib);
1685 break;
1686 case "Maximum":
1687 maximum = this.core.GetAttributeValue(sourceLineNumbers, attrib);
1688 break;
1689 case "UpgradeCode":
1690 upgradeCode = this.core.GetAttributeGuidValue(sourceLineNumbers, attrib, false);
1691 break;
1692 default:
1693 this.core.UnexpectedAttribute(node, attrib);
1694 break;
1695 }
1696 }
1697 else
1698 {
1699 this.core.ParseExtensionAttribute(node, attrib);
1700 }
1701 }
1702
1703 if (null == minimum && null == maximum)
1704 {
1705 this.core.OnMessage(WixErrors.ExpectedAttributes(sourceLineNumbers, node.Name.LocalName, "Minimum", "Maximum"));
1706 }
1707
1708 this.core.ParseForExtensionElements(node);
1709
1710 if (!this.core.EncounteredError)
1711 {
1712 Row row = this.core.CreateRow(sourceLineNumbers, "Upgrade");
1713 row[0] = upgradeCode;
1714 row[1] = minimum;
1715 row[2] = maximum;
1716 row[3] = language;
1717 row[4] = options;
1718 row[6] = propertyId;
1719 }
1720 }
1721
1722 /// <summary>
1723 /// Parses a registry search element.
1724 /// </summary>
1725 /// <param name="node">Element to parse.</param>
1726 /// <returns>Signature for search element.</returns>
1727 private string ParseRegistrySearchElement(XElement node)
1728 {
1729 SourceLineNumber sourceLineNumbers = Preprocessor.GetSourceLineNumbers(node);
1730 bool explicitWin64 = false;
1731 Identifier id = null;
1732 string key = null;
1733 string name = null;
1734 string signature = null;
1735 int root = CompilerConstants.IntegerNotSet;
1736 int type = CompilerConstants.IntegerNotSet;
1737 bool search64bit = false;
1738
1739 foreach (XAttribute attrib in node.Attributes())
1740 {
1741 if (String.IsNullOrEmpty(attrib.Name.NamespaceName) || CompilerCore.WixNamespace == attrib.Name.Namespace)
1742 {
1743 switch (attrib.Name.LocalName)
1744 {
1745 case "Id":
1746 id = this.core.GetAttributeIdentifier(sourceLineNumbers, attrib);
1747 break;
1748 case "Key":
1749 key = this.core.GetAttributeValue(sourceLineNumbers, attrib);
1750 break;
1751 case "Name":
1752 name = this.core.GetAttributeValue(sourceLineNumbers, attrib);
1753 break;
1754 case "Root":
1755 root = this.core.GetAttributeMsidbRegistryRootValue(sourceLineNumbers, attrib, false);
1756 break;
1757 case "Type":
1758 string typeValue = this.core.GetAttributeValue(sourceLineNumbers, attrib);
1759 if (0 < typeValue.Length)
1760 {
1761 Wix.RegistrySearch.TypeType typeType = Wix.RegistrySearch.ParseTypeType(typeValue);
1762 switch (typeType)
1763 {
1764 case Wix.RegistrySearch.TypeType.directory:
1765 type = 0;
1766 break;
1767 case Wix.RegistrySearch.TypeType.file:
1768 type = 1;
1769 break;
1770 case Wix.RegistrySearch.TypeType.raw:
1771 type = 2;
1772 break;
1773 default:
1774 this.core.OnMessage(WixErrors.IllegalAttributeValue(sourceLineNumbers, node.Name.LocalName, "Type", typeValue, "directory", "file", "raw"));
1775 break;
1776 }
1777 }
1778 break;
1779 case "Win64":
1780 explicitWin64 = true;
1781 search64bit = YesNoType.Yes == this.core.GetAttributeYesNoValue(sourceLineNumbers, attrib);
1782 break;
1783 default:
1784 this.core.UnexpectedAttribute(node, attrib);
1785 break;
1786 }
1787 }
1788 else
1789 {
1790 this.core.ParseExtensionAttribute(node, attrib);
1791 }
1792 }
1793
1794 if (!explicitWin64 && (Platform.IA64 == this.CurrentPlatform || Platform.X64 == this.CurrentPlatform))
1795 {
1796 search64bit = true;
1797 }
1798
1799 if (null == id)
1800 {
1801 id = this.core.CreateIdentifier("reg", root.ToString(), key, name, type.ToString(), search64bit.ToString());
1802 }
1803
1804 if (null == key)
1805 {
1806 this.core.OnMessage(WixErrors.ExpectedAttribute(sourceLineNumbers, node.Name.LocalName, "Key"));
1807 }
1808
1809 if (CompilerConstants.IntegerNotSet == root)
1810 {
1811 this.core.OnMessage(WixErrors.ExpectedAttribute(sourceLineNumbers, node.Name.LocalName, "Root"));
1812 }
1813
1814 if (CompilerConstants.IntegerNotSet == type)
1815 {
1816 this.core.OnMessage(WixErrors.ExpectedAttribute(sourceLineNumbers, node.Name.LocalName, "Type"));
1817 }
1818
1819 signature = id.Id;
1820 bool oneChild = false;
1821 foreach (XElement child in node.Elements())
1822 {
1823 if (CompilerCore.WixNamespace == child.Name.Namespace)
1824 {
1825 switch (child.Name.LocalName)
1826 {
1827 case "DirectorySearch":
1828 if (oneChild)
1829 {
1830 this.core.OnMessage(WixErrors.TooManySearchElements(sourceLineNumbers, node.Name.LocalName));
1831 }
1832 oneChild = true;
1833
1834 // directorysearch parentage should work like directory element, not the rest of the signature type because of the DrLocator.Parent column
1835 signature = this.ParseDirectorySearchElement(child, id.Id);
1836 break;
1837 case "DirectorySearchRef":
1838 if (oneChild)
1839 {
1840 this.core.OnMessage(WixErrors.TooManySearchElements(sourceLineNumbers, node.Name.LocalName));
1841 }
1842 oneChild = true;
1843 signature = this.ParseDirectorySearchRefElement(child, id.Id);
1844 break;
1845 case "FileSearch":
1846 if (oneChild)
1847 {
1848 this.core.OnMessage(WixErrors.TooManySearchElements(sourceLineNumbers, node.Name.LocalName));
1849 }
1850 oneChild = true;
1851 signature = this.ParseFileSearchElement(child, id.Id, false, CompilerConstants.IntegerNotSet);
1852 id = new Identifier(signature, AccessModifier.Private); // FileSearch signatures override parent signatures
1853 break;
1854 case "FileSearchRef":
1855 if (oneChild)
1856 {
1857 this.core.OnMessage(WixErrors.TooManySearchElements(sourceLineNumbers, node.Name.LocalName));
1858 }
1859 oneChild = true;
1860 string newId = this.ParseSimpleRefElement(child, "Signature"); // FileSearch signatures override parent signatures
1861 id = new Identifier(newId, AccessModifier.Private);
1862 signature = null;
1863 break;
1864 default:
1865 this.core.UnexpectedElement(node, child);
1866 break;
1867 }
1868 }
1869 else
1870 {
1871 this.core.ParseExtensionElement(node, child);
1872 }
1873 }
1874
1875
1876 if (!this.core.EncounteredError)
1877 {
1878 Row row = this.core.CreateRow(sourceLineNumbers, "RegLocator", id);
1879 row[1] = root;
1880 row[2] = key;
1881 row[3] = name;
1882 row[4] = search64bit ? (type | 16) : type;
1883 }
1884
1885 return signature;
1886 }
1887
1888 /// <summary>
1889 /// Parses a registry search reference element.
1890 /// </summary>
1891 /// <param name="node">Element to parse.</param>
1892 /// <returns>Signature of referenced search element.</returns>
1893 private string ParseRegistrySearchRefElement(XElement node)
1894 {
1895 SourceLineNumber sourceLineNumbers = Preprocessor.GetSourceLineNumbers(node);
1896 string id = null;
1897
1898 foreach (XAttribute attrib in node.Attributes())
1899 {
1900 if (String.IsNullOrEmpty(attrib.Name.NamespaceName) || CompilerCore.WixNamespace == attrib.Name.Namespace)
1901 {
1902 switch (attrib.Name.LocalName)
1903 {
1904 case "Id":
1905 id = this.core.GetAttributeIdentifierValue(sourceLineNumbers, attrib);
1906 this.core.CreateSimpleReference(sourceLineNumbers, "RegLocator", id);
1907 break;
1908 default:
1909 this.core.UnexpectedAttribute(node, attrib);
1910 break;
1911 }
1912 }
1913 else
1914 {
1915 this.core.ParseExtensionAttribute(node, attrib);
1916 }
1917 }
1918
1919 if (null == id)
1920 {
1921 this.core.OnMessage(WixErrors.ExpectedAttribute(sourceLineNumbers, node.Name.LocalName, "Id"));
1922 }
1923
1924 this.core.ParseForExtensionElements(node);
1925
1926 return id; // the id of the RegistrySearchRef element is its signature
1927 }
1928
1929 /// <summary>
1930 /// Parses child elements for search signatures.
1931 /// </summary>
1932 /// <param name="node">Node whose children we are parsing.</param>
1933 /// <returns>Returns list of string signatures.</returns>
1934 private List<string> ParseSearchSignatures(XElement node)
1935 {
1936 List<string> signatures = new List<string>();
1937
1938 foreach (XElement child in node.Elements())
1939 {
1940 string signature = null;
1941 if (CompilerCore.WixNamespace == child.Name.Namespace)
1942 {
1943 switch (child.Name.LocalName)
1944 {
1945 case "ComplianceDrive":
1946 signature = this.ParseComplianceDriveElement(child);
1947 break;
1948 case "ComponentSearch":
1949 signature = this.ParseComponentSearchElement(child);
1950 break;
1951 case "DirectorySearch":
1952 signature = this.ParseDirectorySearchElement(child, String.Empty);
1953 break;
1954 case "DirectorySearchRef":
1955 signature = this.ParseDirectorySearchRefElement(child, String.Empty);
1956 break;
1957 case "IniFileSearch":
1958 signature = this.ParseIniFileSearchElement(child);
1959 break;
1960 case "ProductSearch":
1961 // handled in ParsePropertyElement
1962 break;
1963 case "RegistrySearch":
1964 signature = this.ParseRegistrySearchElement(child);
1965 break;
1966 case "RegistrySearchRef":
1967 signature = this.ParseRegistrySearchRefElement(child);
1968 break;
1969 default:
1970 this.core.UnexpectedElement(node, child);
1971 break;
1972 }
1973 }
1974 else
1975 {
1976 this.core.ParseExtensionElement(node, child);
1977 }
1978
1979
1980 if (!String.IsNullOrEmpty(signature))
1981 {
1982 signatures.Add(signature);
1983 }
1984 }
1985
1986 return signatures;
1987 }
1988
1989 /// <summary>
1990 /// Parses a compliance drive element.
1991 /// </summary>
1992 /// <param name="node">Element to parse.</param>
1993 /// <returns>Signature of nested search elements.</returns>
1994 private string ParseComplianceDriveElement(XElement node)
1995 {
1996 SourceLineNumber sourceLineNumbers = Preprocessor.GetSourceLineNumbers(node);
1997 string signature = null;
1998
1999 bool oneChild = false;
2000 foreach (XElement child in node.Elements())
2001 {
2002 if (CompilerCore.WixNamespace == child.Name.Namespace)
2003 {
2004 SourceLineNumber childSourceLineNumbers = Preprocessor.GetSourceLineNumbers(node);
2005 switch (child.Name.LocalName)
2006 {
2007 case "DirectorySearch":
2008 if (oneChild)
2009 {
2010 this.core.OnMessage(WixErrors.TooManySearchElements(childSourceLineNumbers, node.Name.LocalName));
2011 }
2012 oneChild = true;
2013 signature = this.ParseDirectorySearchElement(child, "CCP_DRIVE");
2014 break;
2015 case "DirectorySearchRef":
2016 if (oneChild)
2017 {
2018 this.core.OnMessage(WixErrors.TooManySearchElements(childSourceLineNumbers, node.Name.LocalName));
2019 }
2020 oneChild = true;
2021 signature = this.ParseDirectorySearchRefElement(child, "CCP_DRIVE");
2022 break;
2023 default:
2024 this.core.UnexpectedElement(node, child);
2025 break;
2026 }
2027 }
2028 else
2029 {
2030 this.core.ParseExtensionElement(node, child);
2031 }
2032 }
2033
2034 if (null == signature)
2035 {
2036 this.core.OnMessage(WixErrors.SearchElementRequired(sourceLineNumbers, node.Name.LocalName));
2037 }
2038
2039 return signature;
2040 }
2041
2042 /// <summary>
2043 /// Parses a compilance check element.
2044 /// </summary>
2045 /// <param name="node">Element to parse.</param>
2046 private void ParseComplianceCheckElement(XElement node)
2047 {
2048 SourceLineNumber sourceLineNumbers = Preprocessor.GetSourceLineNumbers(node);
2049
2050 foreach (XAttribute attrib in node.Attributes())
2051 {
2052 if (String.IsNullOrEmpty(attrib.Name.NamespaceName) || CompilerCore.WixNamespace == attrib.Name.Namespace)
2053 {
2054 switch (attrib.Name.LocalName)
2055 {
2056 default:
2057 this.core.UnexpectedAttribute(node, attrib);
2058 break;
2059 }
2060 }
2061 else
2062 {
2063 this.core.ParseExtensionAttribute(node, attrib);
2064 }
2065 }
2066
2067 string signature = null;
2068
2069 // see if this property is used for appSearch
2070 List<string> signatures = this.ParseSearchSignatures(node);
2071 foreach (string sig in signatures)
2072 {
2073 // if we haven't picked a signature for this ComplianceCheck pick
2074 // this one
2075 if (null == signature)
2076 {
2077 signature = sig;
2078 }
2079 else if (signature != sig)
2080 {
2081 // all signatures under a ComplianceCheck must be the same
2082 this.core.OnMessage(WixErrors.MultipleIdentifiersFound(sourceLineNumbers, node.Name.LocalName, sig, signature));
2083 }
2084 }
2085
2086 if (null == signature)
2087 {
2088 this.core.OnMessage(WixErrors.SearchElementRequired(sourceLineNumbers, node.Name.LocalName));
2089 }
2090
2091 if (!this.core.EncounteredError)
2092 {
2093 Row row = this.core.CreateRow(sourceLineNumbers, "CCPSearch");
2094 row[0] = signature;
2095 }
2096 }
2097
2098 /// <summary>
2099 /// Parses a component element.
2100 /// </summary>
2101 /// <param name="node">Element to parse.</param>
2102 /// <param name="parentType">Type of component's complex reference parent. Will be Uknown if there is no parent.</param>
2103 /// <param name="parentId">Optional identifier for component's primary parent.</param>
2104 /// <param name="parentLanguage">Optional string for component's parent's language.</param>
2105 /// <param name="diskId">Optional disk id inherited from parent directory.</param>
2106 /// <param name="directoryId">Optional identifier for component's directory.</param>
2107 /// <param name="srcPath">Optional source path for files up to this point.</param>
2108 [SuppressMessage("Microsoft.Performance", "CA1800:DoNotCastUnnecessarily")]
2109 private void ParseComponentElement(XElement node, ComplexReferenceParentType parentType, string parentId, string parentLanguage, int diskId, string directoryId, string srcPath)
2110 {
2111 SourceLineNumber sourceLineNumbers = Preprocessor.GetSourceLineNumbers(node);
2112
2113 int bits = 0;
2114 int comPlusBits = CompilerConstants.IntegerNotSet;
2115 string condition = null;
2116 bool encounteredODBCDataSource = false;
2117 bool explicitWin64 = false;
2118 int files = 0;
2119 string guid = "*";
2120 string componentIdPlaceholder = String.Format(Compiler.DefaultComponentIdPlaceholderFormat, this.componentIdPlaceholdersResolver.VariableCount); // placeholder id for defaulting Component/@Id to keypath id.
2121 string componentIdPlaceholderWixVariable = String.Format(Compiler.DefaultComponentIdPlaceholderWixVariableFormat, componentIdPlaceholder);
2122 Identifier id = new Identifier(componentIdPlaceholderWixVariable, AccessModifier.Private);
2123 int keyBits = 0;
2124 bool keyFound = false;
2125 string keyPath = null;
2126 bool shouldAddCreateFolder = false;
2127 bool win64 = false;
2128 bool multiInstance = false;
2129 List<string> symbols = new List<string>();
2130 string feature = null;
2131
2132 foreach (XAttribute attrib in node.Attributes())
2133 {
2134 if (String.IsNullOrEmpty(attrib.Name.NamespaceName) || CompilerCore.WixNamespace == attrib.Name.Namespace)
2135 {
2136 switch (attrib.Name.LocalName)
2137 {
2138 case "Id":
2139 id = this.core.GetAttributeIdentifier(sourceLineNumbers, attrib);
2140 break;
2141 case "ComPlusFlags":
2142 comPlusBits = this.core.GetAttributeIntegerValue(sourceLineNumbers, attrib, 0, short.MaxValue);
2143 break;
2144 case "DisableRegistryReflection":
2145 if (YesNoType.Yes == this.core.GetAttributeYesNoValue(sourceLineNumbers, attrib))
2146 {
2147 bits |= MsiInterop.MsidbComponentAttributesDisableRegistryReflection;
2148 }
2149 break;
2150 case "Directory":
2151 directoryId = this.core.CreateDirectoryReferenceFromInlineSyntax(sourceLineNumbers, attrib, directoryId);
2152 break;
2153 case "DiskId":
2154 diskId = this.core.GetAttributeIntegerValue(sourceLineNumbers, attrib, 1, short.MaxValue);
2155 break;
2156 case "Feature":
2157 feature = this.core.GetAttributeIdentifierValue(sourceLineNumbers, attrib);
2158 break;
2159 case "Guid":
2160 guid = this.core.GetAttributeGuidValue(sourceLineNumbers, attrib, true, true);
2161 break;
2162 case "KeyPath":
2163 if (YesNoType.Yes == this.core.GetAttributeYesNoValue(sourceLineNumbers, attrib))
2164 {
2165 keyFound = true;
2166 keyPath = null;
2167 keyBits = 0;
2168 shouldAddCreateFolder = true;
2169 }
2170 break;
2171 case "Location":
2172 string location = this.core.GetAttributeValue(sourceLineNumbers, attrib);
2173 if (0 < location.Length)
2174 {
2175 Wix.Component.LocationType locationType = Wix.Component.ParseLocationType(location);
2176 switch (locationType)
2177 {
2178 case Wix.Component.LocationType.either:
2179 bits |= MsiInterop.MsidbComponentAttributesOptional;
2180 break;
2181 case Wix.Component.LocationType.local: // this is the default
2182 break;
2183 case Wix.Component.LocationType.source:
2184 bits |= MsiInterop.MsidbComponentAttributesSourceOnly;
2185 break;
2186 default:
2187 this.core.OnMessage(WixErrors.IllegalAttributeValue(sourceLineNumbers, node.Name.LocalName, attrib.Name.LocalName, "either", "local", "source"));
2188 break;
2189 }
2190 }
2191 break;
2192 case "MultiInstance":
2193 multiInstance = YesNoType.Yes == this.core.GetAttributeYesNoValue(sourceLineNumbers, attrib);
2194 break;
2195 case "NeverOverwrite":
2196 if (YesNoType.Yes == this.core.GetAttributeYesNoValue(sourceLineNumbers, attrib))
2197 {
2198 bits |= MsiInterop.MsidbComponentAttributesNeverOverwrite;
2199 }
2200 break;
2201 case "Permanent":
2202 if (YesNoType.Yes == this.core.GetAttributeYesNoValue(sourceLineNumbers, attrib))
2203 {
2204 bits |= MsiInterop.MsidbComponentAttributesPermanent;
2205 }
2206 break;
2207 case "Shared":
2208 if (YesNoType.Yes == this.core.GetAttributeYesNoValue(sourceLineNumbers, attrib))
2209 {
2210 bits |= MsiInterop.MsidbComponentAttributesShared;
2211 }
2212 break;
2213 case "SharedDllRefCount":
2214 if (YesNoType.Yes == this.core.GetAttributeYesNoValue(sourceLineNumbers, attrib))
2215 {
2216 bits |= MsiInterop.MsidbComponentAttributesSharedDllRefCount;
2217 }
2218 break;
2219 case "Transitive":
2220 if (YesNoType.Yes == this.core.GetAttributeYesNoValue(sourceLineNumbers, attrib))
2221 {
2222 bits |= MsiInterop.MsidbComponentAttributesTransitive;
2223 }
2224 break;
2225 case "UninstallWhenSuperseded":
2226 if (YesNoType.Yes == this.core.GetAttributeYesNoValue(sourceLineNumbers, attrib))
2227 {
2228 bits |= MsiInterop.MsidbComponentAttributesUninstallOnSupersedence;
2229 }
2230 break;
2231 case "Win64":
2232 explicitWin64 = true;
2233 if (YesNoType.Yes == this.core.GetAttributeYesNoValue(sourceLineNumbers, attrib))
2234 {
2235 bits |= MsiInterop.MsidbComponentAttributes64bit;
2236 win64 = true;
2237 }
2238 break;
2239 default:
2240 this.core.UnexpectedAttribute(node, attrib);
2241 break;
2242 }
2243 }
2244 else
2245 {
2246 this.core.ParseExtensionAttribute(node, attrib);
2247 }
2248 }
2249
2250 if (!explicitWin64 && (Platform.IA64 == this.CurrentPlatform || Platform.X64 == this.CurrentPlatform))
2251 {
2252 bits |= MsiInterop.MsidbComponentAttributes64bit;
2253 win64 = true;
2254 }
2255
2256 if (null == directoryId)
2257 {
2258 this.core.OnMessage(WixErrors.ExpectedAttribute(sourceLineNumbers, node.Name.LocalName, "Directory"));
2259 }
2260
2261 if (String.IsNullOrEmpty(guid) && MsiInterop.MsidbComponentAttributesShared == (bits & MsiInterop.MsidbComponentAttributesShared))
2262 {
2263 this.core.OnMessage(WixErrors.IllegalAttributeValueWithOtherAttribute(sourceLineNumbers, node.Name.LocalName, "Shared", "yes", "Guid", ""));
2264 }
2265
2266 if (String.IsNullOrEmpty(guid) && MsiInterop.MsidbComponentAttributesPermanent == (bits & MsiInterop.MsidbComponentAttributesPermanent))
2267 {
2268 this.core.OnMessage(WixErrors.IllegalAttributeValueWithOtherAttribute(sourceLineNumbers, node.Name.LocalName, "Permanent", "yes", "Guid", ""));
2269 }
2270
2271 if (null != feature)
2272 {
2273 if (this.compilingModule)
2274 {
2275 this.core.OnMessage(WixErrors.IllegalAttributeInMergeModule(sourceLineNumbers, node.Name.LocalName, "Feature"));
2276 }
2277 else
2278 {
2279 if (ComplexReferenceParentType.Feature == parentType || ComplexReferenceParentType.FeatureGroup == parentType)
2280 {
2281 this.core.OnMessage(WixErrors.IllegalAttributeWhenNested(sourceLineNumbers, node.Name.LocalName, "Feature", node.Parent.Name.LocalName));
2282 }
2283 else
2284 {
2285 this.core.CreateComplexReference(sourceLineNumbers, ComplexReferenceParentType.Feature, feature, null, ComplexReferenceChildType.Component, id.Id, true);
2286 }
2287 }
2288 }
2289
2290 foreach (XElement child in node.Elements())
2291 {
2292 YesNoType keyPathSet = YesNoType.NotSet;
2293 string keyPossible = null;
2294 int keyBit = 0;
2295
2296 if (CompilerCore.WixNamespace == child.Name.Namespace)
2297 {
2298 switch (child.Name.LocalName)
2299 {
2300 case "AppId":
2301 this.ParseAppIdElement(child, id.Id, YesNoType.NotSet, null, null, null);
2302 break;
2303 case "Category":
2304 this.ParseCategoryElement(child, id.Id);
2305 break;
2306 case "Class":
2307 this.ParseClassElement(child, id.Id, YesNoType.NotSet, null, null, null, null);
2308 break;
2309 case "Condition":
2310 if (null != condition)
2311 {
2312 SourceLineNumber childSourceLineNumbers = Preprocessor.GetSourceLineNumbers(node);
2313 this.core.OnMessage(WixErrors.TooManyChildren(childSourceLineNumbers, node.Name.LocalName, child.Name.LocalName));
2314 }
2315 condition = this.ParseConditionElement(child, node.Name.LocalName, null, null);
2316 break;
2317 case "CopyFile":
2318 this.ParseCopyFileElement(child, id.Id, null);
2319 break;
2320 case "CreateFolder":
2321 string createdFolder = this.ParseCreateFolderElement(child, id.Id, directoryId, win64);
2322 if (directoryId == createdFolder)
2323 {
2324 shouldAddCreateFolder = false;
2325 }
2326 break;
2327 case "Environment":
2328 this.ParseEnvironmentElement(child, id.Id);
2329 break;
2330 case "Extension":
2331 this.ParseExtensionElement(child, id.Id, YesNoType.NotSet, null);
2332 break;
2333 case "File":
2334 keyPathSet = this.ParseFileElement(child, id.Id, directoryId, diskId, srcPath, out keyPossible, win64, guid);
2335 if (null != keyPossible)
2336 {
2337 keyBit = 0;
2338 }
2339 files++;
2340 break;
2341 case "IniFile":
2342 this.ParseIniFileElement(child, id.Id);
2343 break;
2344 case "Interface":
2345 this.ParseInterfaceElement(child, id.Id, null, null, null, null);
2346 break;
2347 case "IsolateComponent":
2348 this.ParseIsolateComponentElement(child, id.Id);
2349 break;
2350 case "ODBCDataSource":
2351 keyPathSet = this.ParseODBCDataSource(child, id.Id, null, out keyPossible);
2352 keyBit = MsiInterop.MsidbComponentAttributesODBCDataSource;
2353 encounteredODBCDataSource = true;
2354 break;
2355 case "ODBCDriver":
2356 this.ParseODBCDriverOrTranslator(child, id.Id, null, this.tableDefinitions["ODBCDriver"]);
2357 break;
2358 case "ODBCTranslator":
2359 this.ParseODBCDriverOrTranslator(child, id.Id, null, this.tableDefinitions["ODBCTranslator"]);
2360 break;
2361 case "ProgId":
2362 bool foundExtension = false;
2363 this.ParseProgIdElement(child, id.Id, YesNoType.NotSet, null, null, null, ref foundExtension, YesNoType.NotSet);
2364 break;
2365 case "RegistryKey":
2366 keyPathSet = this.ParseRegistryKeyElement(child, id.Id, CompilerConstants.IntegerNotSet, null, win64, out keyPossible);
2367 keyBit = MsiInterop.MsidbComponentAttributesRegistryKeyPath;
2368 break;
2369 case "RegistryValue":
2370 keyPathSet = this.ParseRegistryValueElement(child, id.Id, CompilerConstants.IntegerNotSet, null, win64, out keyPossible);
2371 keyBit = MsiInterop.MsidbComponentAttributesRegistryKeyPath;
2372 break;
2373 case "RemoveFile":
2374 this.ParseRemoveFileElement(child, id.Id, directoryId);
2375 break;
2376 case "RemoveFolder":
2377 this.ParseRemoveFolderElement(child, id.Id, directoryId);
2378 break;
2379 case "RemoveRegistryKey":
2380 this.ParseRemoveRegistryKeyElement(child, id.Id);
2381 break;
2382 case "RemoveRegistryValue":
2383 this.ParseRemoveRegistryValueElement(child, id.Id);
2384 break;
2385 case "ReserveCost":
2386 this.ParseReserveCostElement(child, id.Id, directoryId);
2387 break;
2388 case "ServiceConfig":
2389 this.ParseServiceConfigElement(child, id.Id, null);
2390 break;
2391 case "ServiceConfigFailureActions":
2392 this.ParseServiceConfigFailureActionsElement(child, id.Id, null);
2393 break;
2394 case "ServiceControl":
2395 this.ParseServiceControlElement(child, id.Id);
2396 break;
2397 case "ServiceInstall":
2398 this.ParseServiceInstallElement(child, id.Id, win64);
2399 break;
2400 case "Shortcut":
2401 this.ParseShortcutElement(child, id.Id, node.Name.LocalName, directoryId, YesNoType.No);
2402 break;
2403 case "SymbolPath":
2404 symbols.Add(this.ParseSymbolPathElement(child));
2405 break;
2406 case "TypeLib":
2407 this.ParseTypeLibElement(child, id.Id, null, win64);
2408 break;
2409 default:
2410 this.core.UnexpectedElement(node, child);
2411 break;
2412 }
2413 }
2414 else
2415 {
2416 Dictionary<string, string> context = new Dictionary<string, string>() { { "ComponentId", id.Id }, { "DirectoryId", directoryId }, { "Win64", win64.ToString() }, };
2417 ComponentKeyPath possibleKeyPath = this.core.ParsePossibleKeyPathExtensionElement(node, child, context);
2418 if (null != possibleKeyPath)
2419 {
2420 if (ComponentKeyPathType.None == possibleKeyPath.Type)
2421 {
2422 keyPathSet = YesNoType.No;
2423 }
2424 else
2425 {
2426 keyPathSet = possibleKeyPath.Explicit ? YesNoType.Yes : YesNoType.NotSet;
2427
2428 if (!String.IsNullOrEmpty(possibleKeyPath.Id))
2429 {
2430 keyPossible = possibleKeyPath.Id;
2431 }
2432
2433 if (ComponentKeyPathType.Registry == possibleKeyPath.Type || ComponentKeyPathType.RegistryFormatted == possibleKeyPath.Type)
2434 {
2435 keyBit = MsiInterop.MsidbComponentAttributesRegistryKeyPath;
2436 }
2437 }
2438 }
2439 }
2440
2441 // Verify that either the key path is not set, or it is set along with a key path ID.
2442 Debug.Assert(YesNoType.Yes != keyPathSet || (YesNoType.Yes == keyPathSet && null != keyPossible));
2443
2444 if (keyFound && YesNoType.Yes == keyPathSet)
2445 {
2446 this.core.OnMessage(WixErrors.ComponentMultipleKeyPaths(sourceLineNumbers, node.Name.LocalName, "KeyPath", "yes", "File", "RegistryValue", "ODBCDataSource"));
2447 }
2448
2449 // if a possible KeyPath has been found and that value was explicitly set as
2450 // the KeyPath of the component, set it now. Alternatively, if a possible
2451 // KeyPath has been found and no KeyPath has been previously set, use this
2452 // value as the default KeyPath of the component
2453 if (!String.IsNullOrEmpty(keyPossible) && (YesNoType.Yes == keyPathSet || (YesNoType.NotSet == keyPathSet && String.IsNullOrEmpty(keyPath) && !keyFound)))
2454 {
2455 keyFound = YesNoType.Yes == keyPathSet;
2456 keyPath = keyPossible;
2457 keyBits = keyBit;
2458 }
2459 }
2460
2461
2462 if (shouldAddCreateFolder)
2463 {
2464 Row row = this.core.CreateRow(sourceLineNumbers, "CreateFolder");
2465 row[0] = directoryId;
2466 row[1] = id.Id;
2467 }
2468
2469 // check for conditions that exclude this component from using generated guids
2470 bool isGeneratableGuidOk = "*" == guid;
2471 if (isGeneratableGuidOk)
2472 {
2473 if (encounteredODBCDataSource)
2474 {
2475 this.core.OnMessage(WixErrors.IllegalComponentWithAutoGeneratedGuid(sourceLineNumbers));
2476 isGeneratableGuidOk = false;
2477 }
2478
2479 if (0 != files && MsiInterop.MsidbComponentAttributesRegistryKeyPath == keyBits)
2480 {
2481 this.core.OnMessage(WixErrors.IllegalComponentWithAutoGeneratedGuid(sourceLineNumbers, true));
2482 isGeneratableGuidOk = false;
2483 }
2484 }
2485
2486 // check for implicit KeyPath which can easily be accidentally changed
2487 if (this.showPedanticMessages && !keyFound && !isGeneratableGuidOk)
2488 {
2489 this.core.OnMessage(WixErrors.ImplicitComponentKeyPath(sourceLineNumbers, id.Id));
2490 }
2491
2492 // if there isn't an @Id attribute value, replace the placeholder with the id of the keypath.
2493 // either an explicit KeyPath="yes" attribute must be specified or requirements for
2494 // generatable guid must be met.
2495 if (componentIdPlaceholderWixVariable == id.Id)
2496 {
2497 if (isGeneratableGuidOk || keyFound && !String.IsNullOrEmpty(keyPath))
2498 {
2499 this.componentIdPlaceholdersResolver.AddVariable(componentIdPlaceholder, keyPath);
2500
2501 id = new Identifier(keyPath, AccessModifier.Private);
2502 }
2503 else
2504 {
2505 this.core.OnMessage(WixErrors.CannotDefaultComponentId(sourceLineNumbers));
2506 }
2507 }
2508
2509 // If an id was not determined by now, we have to error.
2510 if (null == id)
2511 {
2512 this.core.OnMessage(WixErrors.ExpectedAttribute(sourceLineNumbers, node.Name.LocalName, "Id"));
2513 }
2514
2515 // finally add the Component table row
2516 if (!this.core.EncounteredError)
2517 {
2518 Row row = this.core.CreateRow(sourceLineNumbers, "Component", id);
2519 row[1] = guid;
2520 row[2] = directoryId;
2521 row[3] = bits | keyBits;
2522 row[4] = condition;
2523 row[5] = keyPath;
2524
2525 if (multiInstance)
2526 {
2527 Row instanceComponentRow = this.core.CreateRow(sourceLineNumbers, "WixInstanceComponent");
2528 instanceComponentRow[0] = id;
2529 }
2530
2531 if (0 < symbols.Count)
2532 {
2533 WixDeltaPatchSymbolPathsRow symbolRow = (WixDeltaPatchSymbolPathsRow)this.core.CreateRow(sourceLineNumbers, "WixDeltaPatchSymbolPaths", id);
2534 symbolRow.Type = SymbolPathType.Component;
2535 symbolRow.SymbolPaths = String.Join(";", symbols);
2536 }
2537
2538 // Complus
2539 if (CompilerConstants.IntegerNotSet != comPlusBits)
2540 {
2541 row = this.core.CreateRow(sourceLineNumbers, "Complus");
2542 row[0] = id;
2543 row[1] = comPlusBits;
2544 }
2545
2546 // if this is a module, automatically add this component to the references to ensure it gets in the ModuleComponents table
2547 if (this.compilingModule)
2548 {
2549 this.core.CreateComplexReference(sourceLineNumbers, ComplexReferenceParentType.Module, this.activeName, this.activeLanguage, ComplexReferenceChildType.Component, id.Id, false);
2550 }
2551 else if (ComplexReferenceParentType.Unknown != parentType && null != parentId) // if parent was provided, add a complex reference to that.
2552 {
2553 // If the Component is defined directly under a feature, then mark the complex reference primary.
2554 this.core.CreateComplexReference(sourceLineNumbers, parentType, parentId, parentLanguage, ComplexReferenceChildType.Component, id.Id, ComplexReferenceParentType.Feature == parentType);
2555 }
2556 }
2557 }
2558
2559 /// <summary>
2560 /// Parses a component group element.
2561 /// </summary>
2562 /// <param name="node">Element to parse.</param>
2563 [SuppressMessage("Microsoft.Performance", "CA1800:DoNotCastUnnecessarily")]
2564 private void ParseComponentGroupElement(XElement node, ComplexReferenceParentType parentType, string parentId)
2565 {
2566 SourceLineNumber sourceLineNumbers = Preprocessor.GetSourceLineNumbers(node);
2567 Identifier id = null;
2568 string directoryId = null;
2569 string source = null;
2570
2571 foreach (XAttribute attrib in node.Attributes())
2572 {
2573 if (String.IsNullOrEmpty(attrib.Name.NamespaceName) || CompilerCore.WixNamespace == attrib.Name.Namespace)
2574 {
2575 switch (attrib.Name.LocalName)
2576 {
2577 case "Id":
2578 id = this.core.GetAttributeIdentifier(sourceLineNumbers, attrib);
2579 break;
2580 case "Directory":
2581 // If the inline syntax is invalid it returns null. Use a static error identifier so the null
2582 // directory identifier here doesn't trickle down false errors into child elements.
2583 directoryId = this.core.CreateDirectoryReferenceFromInlineSyntax(sourceLineNumbers, attrib, null) ?? "ErrorParsingInlineSyntax";
2584 break;
2585 case "Source":
2586 source = this.core.GetAttributeValue(sourceLineNumbers, attrib);
2587 break;
2588 default:
2589 this.core.UnexpectedAttribute(node, attrib);
2590 break;
2591 }
2592 }
2593 else
2594 {
2595 this.core.ParseExtensionAttribute(node, attrib);
2596 }
2597 }
2598
2599 if (null == id)
2600 {
2601 this.core.OnMessage(WixErrors.ExpectedAttribute(sourceLineNumbers, node.Name.LocalName, "Id"));
2602 id = Identifier.Invalid;
2603 }
2604
2605 if (!String.IsNullOrEmpty(source) && !source.EndsWith(Path.DirectorySeparatorChar.ToString(), StringComparison.Ordinal))
2606 {
2607 source = String.Concat(source, Path.DirectorySeparatorChar);
2608 }
2609
2610 foreach (XElement child in node.Elements())
2611 {
2612 if (CompilerCore.WixNamespace == child.Name.Namespace)
2613 {
2614 switch (child.Name.LocalName)
2615 {
2616 case "ComponentGroupRef":
2617 this.ParseComponentGroupRefElement(child, ComplexReferenceParentType.ComponentGroup, id.Id, null);
2618 break;
2619 case "ComponentRef":
2620 this.ParseComponentRefElement(child, ComplexReferenceParentType.ComponentGroup, id.Id, null);
2621 break;
2622 case "Component":
2623 this.ParseComponentElement(child, ComplexReferenceParentType.ComponentGroup, id.Id, null, CompilerConstants.IntegerNotSet, directoryId, source);
2624 break;
2625 default:
2626 this.core.UnexpectedElement(node, child);
2627 break;
2628 }
2629 }
2630 else
2631 {
2632 this.core.ParseExtensionElement(node, child);
2633 }
2634 }
2635
2636 if (!this.core.EncounteredError)
2637 {
2638 Row row = this.core.CreateRow(sourceLineNumbers, "WixComponentGroup", id);
2639
2640 // Add this componentGroup and its parent in WixGroup.
2641 this.core.CreateWixGroupRow(sourceLineNumbers, parentType, parentId, ComplexReferenceChildType.ComponentGroup, id.Id);
2642 }
2643 }
2644
2645 /// <summary>
2646 /// Parses a component group reference element.
2647 /// </summary>
2648 /// <param name="node">Element to parse.</param>
2649 /// <param name="parentType">ComplexReferenceParentType of parent element.</param>
2650 /// <param name="parentId">Identifier of parent element (usually a Feature or Module).</param>
2651 /// <param name="parentLanguage">Optional language of parent (only useful for Modules).</param>
2652 private void ParseComponentGroupRefElement(XElement node, ComplexReferenceParentType parentType, string parentId, string parentLanguage)
2653 {
2654 Debug.Assert(ComplexReferenceParentType.ComponentGroup == parentType || ComplexReferenceParentType.FeatureGroup == parentType || ComplexReferenceParentType.Feature == parentType || ComplexReferenceParentType.Module == parentType);
2655
2656 SourceLineNumber sourceLineNumbers = Preprocessor.GetSourceLineNumbers(node);
2657 string id = null;
2658 YesNoType primary = YesNoType.NotSet;
2659
2660 foreach (XAttribute attrib in node.Attributes())
2661 {
2662 if (String.IsNullOrEmpty(attrib.Name.NamespaceName) || CompilerCore.WixNamespace == attrib.Name.Namespace)
2663 {
2664 switch (attrib.Name.LocalName)
2665 {
2666 case "Id":
2667 id = this.core.GetAttributeIdentifierValue(sourceLineNumbers, attrib);
2668 this.core.CreateSimpleReference(sourceLineNumbers, "WixComponentGroup", id);
2669 break;
2670 case "Primary":
2671 primary = this.core.GetAttributeYesNoValue(sourceLineNumbers, attrib);
2672 break;
2673 default:
2674 this.core.UnexpectedAttribute(node, attrib);
2675 break;
2676 }
2677 }
2678 else
2679 {
2680 this.core.ParseExtensionAttribute(node, attrib);
2681 }
2682 }
2683
2684 if (null == id)
2685 {
2686 this.core.OnMessage(WixErrors.ExpectedAttribute(sourceLineNumbers, node.Name.LocalName, "Id"));
2687 }
2688
2689 this.core.ParseForExtensionElements(node);
2690
2691 this.core.CreateComplexReference(sourceLineNumbers, parentType, parentId, parentLanguage, ComplexReferenceChildType.ComponentGroup, id, (YesNoType.Yes == primary));
2692 }
2693
2694 /// <summary>
2695 /// Parses a component reference element.
2696 /// </summary>
2697 /// <param name="node">Element to parse.</param>
2698 /// <param name="parentType">ComplexReferenceParentType of parent element.</param>
2699 /// <param name="parentId">Identifier of parent element (usually a Feature or Module).</param>
2700 /// <param name="parentLanguage">Optional language of parent (only useful for Modules).</param>
2701 private void ParseComponentRefElement(XElement node, ComplexReferenceParentType parentType, string parentId, string parentLanguage)
2702 {
2703 Debug.Assert(ComplexReferenceParentType.FeatureGroup == parentType || ComplexReferenceParentType.ComponentGroup == parentType || ComplexReferenceParentType.Feature == parentType || ComplexReferenceParentType.Module == parentType);
2704
2705 SourceLineNumber sourceLineNumbers = Preprocessor.GetSourceLineNumbers(node);
2706 string id = null;
2707 YesNoType primary = YesNoType.NotSet;
2708
2709 foreach (XAttribute attrib in node.Attributes())
2710 {
2711 if (String.IsNullOrEmpty(attrib.Name.NamespaceName) || CompilerCore.WixNamespace == attrib.Name.Namespace)
2712 {
2713 switch (attrib.Name.LocalName)
2714 {
2715 case "Id":
2716 id = this.core.GetAttributeIdentifierValue(sourceLineNumbers, attrib);
2717 this.core.CreateSimpleReference(sourceLineNumbers, "Component", id);
2718 break;
2719 case "Primary":
2720 primary = this.core.GetAttributeYesNoValue(sourceLineNumbers, attrib);
2721 break;
2722 default:
2723 this.core.UnexpectedAttribute(node, attrib);
2724 break;
2725 }
2726 }
2727 else
2728 {
2729 this.core.ParseExtensionAttribute(node, attrib);
2730 }
2731 }
2732
2733 if (null == id)
2734 {
2735 this.core.OnMessage(WixErrors.ExpectedAttribute(sourceLineNumbers, node.Name.LocalName, "Id"));
2736 }
2737
2738 this.core.ParseForExtensionElements(node);
2739
2740 this.core.CreateComplexReference(sourceLineNumbers, parentType, parentId, parentLanguage, ComplexReferenceChildType.Component, id, (YesNoType.Yes == primary));
2741 }
2742
2743 /// <summary>
2744 /// Parses a component search element.
2745 /// </summary>
2746 /// <param name="node">Element to parse.</param>
2747 /// <returns>Signature for search element.</returns>
2748 private string ParseComponentSearchElement(XElement node)
2749 {
2750 SourceLineNumber sourceLineNumbers = Preprocessor.GetSourceLineNumbers(node);
2751 Identifier id = null;
2752 string componentId = null;
2753 int type = MsiInterop.MsidbLocatorTypeFileName;
2754 string signature = null;
2755
2756 foreach (XAttribute attrib in node.Attributes())
2757 {
2758 if (String.IsNullOrEmpty(attrib.Name.NamespaceName) || CompilerCore.WixNamespace == attrib.Name.Namespace)
2759 {
2760 switch (attrib.Name.LocalName)
2761 {
2762 case "Id":
2763 id = this.core.GetAttributeIdentifier(sourceLineNumbers, attrib);
2764 break;
2765 case "Guid":
2766 componentId = this.core.GetAttributeGuidValue(sourceLineNumbers, attrib, false);
2767 break;
2768 case "Type":
2769 string typeValue = this.core.GetAttributeValue(sourceLineNumbers, attrib);
2770 if (0 < typeValue.Length)
2771 {
2772 Wix.ComponentSearch.TypeType typeType = Wix.ComponentSearch.ParseTypeType(typeValue);
2773 switch (typeType)
2774 {
2775 case Wix.ComponentSearch.TypeType.directory:
2776 type = MsiInterop.MsidbLocatorTypeDirectory;
2777 break;
2778 case Wix.ComponentSearch.TypeType.file:
2779 type = MsiInterop.MsidbLocatorTypeFileName;
2780 break;
2781 default:
2782 this.core.OnMessage(WixErrors.IllegalAttributeValue(sourceLineNumbers, node.Name.LocalName, attrib.Name.LocalName, typeValue, "directory", "file"));
2783 break;
2784 }
2785 }
2786 break;
2787 default:
2788 this.core.UnexpectedAttribute(node, attrib);
2789 break;
2790 }
2791 }
2792 else
2793 {
2794 this.core.ParseExtensionAttribute(node, attrib);
2795 }
2796 }
2797
2798 if (null == id)
2799 {
2800 id = this.core.CreateIdentifier("cmp", componentId, type.ToString());
2801 }
2802
2803 signature = id.Id;
2804 bool oneChild = false;
2805 foreach (XElement child in node.Elements())
2806 {
2807 if (CompilerCore.WixNamespace == child.Name.Namespace)
2808 {
2809 switch (child.Name.LocalName)
2810 {
2811 case "DirectorySearch":
2812 if (oneChild)
2813 {
2814 this.core.OnMessage(WixErrors.TooManySearchElements(sourceLineNumbers, node.Name.LocalName));
2815 }
2816 oneChild = true;
2817
2818 // directorysearch parentage should work like directory element, not the rest of the signature type because of the DrLocator.Parent column
2819 signature = this.ParseDirectorySearchElement(child, id.Id);
2820 break;
2821 case "DirectorySearchRef":
2822 if (oneChild)
2823 {
2824 this.core.OnMessage(WixErrors.TooManySearchElements(sourceLineNumbers, node.Name.LocalName));
2825 }
2826 oneChild = true;
2827 signature = this.ParseDirectorySearchRefElement(child, id.Id);
2828 break;
2829 case "FileSearch":
2830 if (oneChild)
2831 {
2832 this.core.OnMessage(WixErrors.TooManySearchElements(sourceLineNumbers, node.Name.LocalName));
2833 }
2834 oneChild = true;
2835 signature = this.ParseFileSearchElement(child, id.Id, false, CompilerConstants.IntegerNotSet);
2836 id = new Identifier(signature, AccessModifier.Private); // FileSearch signatures override parent signatures
2837 break;
2838 case "FileSearchRef":
2839 if (oneChild)
2840 {
2841 this.core.OnMessage(WixErrors.TooManySearchElements(sourceLineNumbers, node.Name.LocalName));
2842 }
2843 oneChild = true;
2844 string newId = this.ParseSimpleRefElement(child, "Signature"); // FileSearch signatures override parent signatures
2845 id = new Identifier(newId, AccessModifier.Private);
2846 signature = null;
2847 break;
2848 default:
2849 this.core.UnexpectedElement(node, child);
2850 break;
2851 }
2852 }
2853 else
2854 {
2855 this.core.ParseExtensionElement(node, child);
2856 }
2857 }
2858
2859 if (!this.core.EncounteredError)
2860 {
2861 Row row = this.core.CreateRow(sourceLineNumbers, "CompLocator", id);
2862 row[1] = componentId;
2863 row[2] = type;
2864 }
2865
2866 return signature;
2867 }
2868
2869 /// <summary>
2870 /// Parses a create folder element.
2871 /// </summary>
2872 /// <param name="node">Element to parse.</param>
2873 /// <param name="componentId">Identifier for parent component.</param>
2874 /// <param name="directoryId">Default identifier for directory to create.</param>
2875 /// <param name="win64Component">true if the component is 64-bit.</param>
2876 /// <returns>Identifier for the directory that will be created</returns>
2877 private string ParseCreateFolderElement(XElement node, string componentId, string directoryId, bool win64Component)
2878 {
2879 SourceLineNumber sourceLineNumbers = Preprocessor.GetSourceLineNumbers(node);
2880 foreach (XAttribute attrib in node.Attributes())
2881 {
2882 if (String.IsNullOrEmpty(attrib.Name.NamespaceName) || CompilerCore.WixNamespace == attrib.Name.Namespace)
2883 {
2884 switch (attrib.Name.LocalName)
2885 {
2886 case "Directory":
2887 directoryId = this.core.CreateDirectoryReferenceFromInlineSyntax(sourceLineNumbers, attrib, directoryId);
2888 break;
2889 default:
2890 this.core.UnexpectedAttribute(node, attrib);
2891 break;
2892 }
2893 }
2894 else
2895 {
2896 this.core.ParseExtensionAttribute(node, attrib);
2897 }
2898 }
2899
2900 foreach (XElement child in node.Elements())
2901 {
2902 if (CompilerCore.WixNamespace == child.Name.Namespace)
2903 {
2904 switch (child.Name.LocalName)
2905 {
2906 case "Shortcut":
2907 this.ParseShortcutElement(child, componentId, node.Name.LocalName, directoryId, YesNoType.No);
2908 break;
2909 case "Permission":
2910 this.ParsePermissionElement(child, directoryId, "CreateFolder");
2911 break;
2912 case "PermissionEx":
2913 this.ParsePermissionExElement(child, directoryId, "CreateFolder");
2914 break;
2915 default:
2916 this.core.UnexpectedElement(node, child);
2917 break;
2918 }
2919 }
2920 else
2921 {
2922 Dictionary<string, string> context = new Dictionary<string, string>() { { "DirectoryId", directoryId }, { "ComponentId", componentId }, { "Win64", win64Component.ToString() } };
2923 this.core.ParseExtensionElement(node, child, context);
2924 }
2925 }
2926
2927 if (!this.core.EncounteredError)
2928 {
2929 Row row = this.core.CreateRow(sourceLineNumbers, "CreateFolder");
2930 row[0] = directoryId;
2931 row[1] = componentId;
2932 }
2933
2934 return directoryId;
2935 }
2936
2937 /// <summary>
2938 /// Parses a copy file element.
2939 /// </summary>
2940 /// <param name="node">Element to parse.</param>
2941 /// <param name="componentId">Identifier of parent component.</param>
2942 /// <param name="fileId">Identifier of file to copy (null if moving the file).</param>
2943 private void ParseCopyFileElement(XElement node, string componentId, string fileId)
2944 {
2945 SourceLineNumber sourceLineNumbers = Preprocessor.GetSourceLineNumbers(node);
2946 Identifier id = null;
2947 bool delete = false;
2948 string destinationDirectory = null;
2949 string destinationName = null;
2950 string destinationShortName = null;
2951 string destinationProperty = null;
2952 string sourceDirectory = null;
2953 string sourceFolder = null;
2954 string sourceName = null;
2955 string sourceProperty = null;
2956
2957 foreach (XAttribute attrib in node.Attributes())
2958 {
2959 if (String.IsNullOrEmpty(attrib.Name.NamespaceName) || CompilerCore.WixNamespace == attrib.Name.Namespace)
2960 {
2961 switch (attrib.Name.LocalName)
2962 {
2963 case "Id":
2964 id = this.core.GetAttributeIdentifier(sourceLineNumbers, attrib);
2965 break;
2966 case "Delete":
2967 delete = YesNoType.Yes == this.core.GetAttributeYesNoValue(sourceLineNumbers, attrib);
2968 break;
2969 case "DestinationDirectory":
2970 destinationDirectory = this.core.CreateDirectoryReferenceFromInlineSyntax(sourceLineNumbers, attrib, null);
2971 break;
2972 case "DestinationName":
2973 destinationName = this.core.GetAttributeLongFilename(sourceLineNumbers, attrib, false);
2974 break;
2975 case "DestinationProperty":
2976 destinationProperty = this.core.GetAttributeIdentifierValue(sourceLineNumbers, attrib);
2977 break;
2978 case "DestinationShortName":
2979 destinationShortName = this.core.GetAttributeShortFilename(sourceLineNumbers, attrib, false);
2980 break;
2981 case "FileId":
2982 if (null != fileId)
2983 {
2984 this.core.OnMessage(WixErrors.IllegalAttributeWhenNested(sourceLineNumbers, node.Name.LocalName, attrib.Name.LocalName, node.Parent.Name.LocalName));
2985 }
2986 fileId = this.core.GetAttributeIdentifierValue(sourceLineNumbers, attrib);
2987 this.core.CreateSimpleReference(sourceLineNumbers, "File", fileId);
2988 break;
2989 case "SourceDirectory":
2990 sourceDirectory = this.core.CreateDirectoryReferenceFromInlineSyntax(sourceLineNumbers, attrib, null);
2991 break;
2992 case "SourceName":
2993 sourceName = this.core.GetAttributeValue(sourceLineNumbers, attrib);
2994 break;
2995 case "SourceProperty":
2996 sourceProperty = this.core.GetAttributeIdentifierValue(sourceLineNumbers, attrib);
2997 break;
2998 default:
2999 this.core.UnexpectedAttribute(node, attrib);
3000 break;
3001 }
3002 }
3003 else
3004 {
3005 this.core.ParseExtensionAttribute(node, attrib);
3006 }
3007 }
3008
3009 if (null != sourceFolder && null != sourceDirectory) // SourceFolder and SourceDirectory cannot coexist
3010 {
3011 this.core.OnMessage(WixErrors.IllegalAttributeWithOtherAttribute(sourceLineNumbers, node.Name.LocalName, "SourceFolder", "SourceDirectory"));
3012 }
3013
3014 if (null != sourceFolder && null != sourceProperty) // SourceFolder and SourceProperty cannot coexist
3015 {
3016 this.core.OnMessage(WixErrors.IllegalAttributeWithOtherAttribute(sourceLineNumbers, node.Name.LocalName, "SourceFolder", "SourceProperty"));
3017 }
3018
3019 if (null != sourceDirectory && null != sourceProperty) // SourceDirectory and SourceProperty cannot coexist
3020 {
3021 this.core.OnMessage(WixErrors.IllegalAttributeWithOtherAttribute(sourceLineNumbers, node.Name.LocalName, "SourceProperty", "SourceDirectory"));
3022 }
3023
3024 if (null != destinationDirectory && null != destinationProperty) // DestinationDirectory and DestinationProperty cannot coexist
3025 {
3026 this.core.OnMessage(WixErrors.IllegalAttributeWithOtherAttribute(sourceLineNumbers, node.Name.LocalName, "DestinationProperty", "DestinationDirectory"));
3027 }
3028
3029 // generate a short file name
3030 if (null == destinationShortName && (null != destinationName && !this.core.IsValidShortFilename(destinationName, false)))
3031 {
3032 destinationShortName = this.core.CreateShortName(destinationName, true, false, node.Name.LocalName, componentId);
3033 }
3034
3035 if (null == id)
3036 {
3037 id = this.core.CreateIdentifier("cf", sourceFolder, sourceDirectory, sourceProperty, destinationDirectory, destinationProperty, destinationName);
3038 }
3039
3040 this.core.ParseForExtensionElements(node);
3041
3042 if (null == fileId)
3043 {
3044 // DestinationDirectory or DestinationProperty must be specified
3045 if (null == destinationDirectory && null == destinationProperty)
3046 {
3047 this.core.OnMessage(WixErrors.ExpectedAttributesWithoutOtherAttribute(sourceLineNumbers, node.Name.LocalName, "DestinationDirectory", "DestinationProperty", "FileId"));
3048 }
3049
3050 if (!this.core.EncounteredError)
3051 {
3052 Row row = this.core.CreateRow(sourceLineNumbers, "MoveFile", id);
3053 row[1] = componentId;
3054 row[2] = sourceName;
3055 row[3] = String.IsNullOrEmpty(destinationShortName) && String.IsNullOrEmpty(destinationName) ? null : GetMsiFilenameValue(destinationShortName, destinationName);
3056 if (null != sourceDirectory)
3057 {
3058 row[4] = sourceDirectory;
3059 }
3060 else if (null != sourceProperty)
3061 {
3062 row[4] = sourceProperty;
3063 }
3064 else
3065 {
3066 row[4] = sourceFolder;
3067 }
3068
3069 if (null != destinationDirectory)
3070 {
3071 row[5] = destinationDirectory;
3072 }
3073 else
3074 {
3075 row[5] = destinationProperty;
3076 }
3077 row[6] = delete ? 1 : 0;
3078 }
3079 }
3080 else // copy the file
3081 {
3082 if (null != sourceDirectory)
3083 {
3084 this.core.OnMessage(WixErrors.IllegalAttributeWithOtherAttribute(sourceLineNumbers, node.Name.LocalName, "SourceDirectory", "FileId"));
3085 }
3086
3087 if (null != sourceFolder)
3088 {
3089 this.core.OnMessage(WixErrors.IllegalAttributeWithOtherAttribute(sourceLineNumbers, node.Name.LocalName, "SourceFolder", "FileId"));
3090 }
3091
3092 if (null != sourceName)
3093 {
3094 this.core.OnMessage(WixErrors.IllegalAttributeWithOtherAttribute(sourceLineNumbers, node.Name.LocalName, "SourceName", "FileId"));
3095 }
3096
3097 if (null != sourceProperty)
3098 {
3099 this.core.OnMessage(WixErrors.IllegalAttributeWithOtherAttribute(sourceLineNumbers, node.Name.LocalName, "SourceProperty", "FileId"));
3100 }
3101
3102 if (delete)
3103 {
3104 this.core.OnMessage(WixErrors.IllegalAttributeWithOtherAttribute(sourceLineNumbers, node.Name.LocalName, "Delete", "FileId"));
3105 }
3106
3107 if (null == destinationName && null == destinationDirectory && null == destinationProperty)
3108 {
3109 this.core.OnMessage(WixWarnings.CopyFileFileIdUseless(sourceLineNumbers));
3110 }
3111
3112 if (!this.core.EncounteredError)
3113 {
3114 Row row = this.core.CreateRow(sourceLineNumbers, "DuplicateFile", id);
3115 row[1] = componentId;
3116 row[2] = fileId;
3117 row[3] = String.IsNullOrEmpty(destinationShortName) && String.IsNullOrEmpty(destinationName) ? null : GetMsiFilenameValue(destinationShortName, destinationName);
3118 if (null != destinationDirectory)
3119 {
3120 row[4] = destinationDirectory;
3121 }
3122 else
3123 {
3124 row[4] = destinationProperty;
3125 }
3126 }
3127 }
3128 }
3129
3130 /// <summary>
3131 /// Parses a CustomAction element.
3132 /// </summary>
3133 /// <param name="node">Element to parse.</param>
3134 private void ParseCustomActionElement(XElement node)
3135 {
3136 SourceLineNumber sourceLineNumbers = Preprocessor.GetSourceLineNumbers(node);
3137 Identifier id = null;
3138 int bits = 0;
3139 int extendedBits = 0;
3140 bool inlineScript = false;
3141 string innerText = null;
3142 string source = null;
3143 int sourceBits = 0;
3144 YesNoType suppressModularization = YesNoType.NotSet;
3145 string target = null;
3146 int targetBits = 0;
3147 bool explicitWin64 = false;
3148
3149 foreach (XAttribute attrib in node.Attributes())
3150 {
3151 if (String.IsNullOrEmpty(attrib.Name.NamespaceName) || CompilerCore.WixNamespace == attrib.Name.Namespace)
3152 {
3153 switch (attrib.Name.LocalName)
3154 {
3155 case "Id":
3156 id = this.core.GetAttributeIdentifier(sourceLineNumbers, attrib);
3157 break;
3158 case "BinaryKey":
3159 if (null != source)
3160 {
3161 this.core.OnMessage(WixErrors.CustomActionMultipleSources(sourceLineNumbers, node.Name.LocalName, attrib.Name.LocalName, "BinaryKey", "Directory", "FileKey", "Property", "Script"));
3162 }
3163 source = this.core.GetAttributeIdentifierValue(sourceLineNumbers, attrib);
3164 sourceBits = MsiInterop.MsidbCustomActionTypeBinaryData;
3165 this.core.CreateSimpleReference(sourceLineNumbers, "Binary", source); // add a reference to the appropriate Binary
3166 break;
3167 case "Directory":
3168 if (null != source)
3169 {
3170 this.core.OnMessage(WixErrors.CustomActionMultipleSources(sourceLineNumbers, node.Name.LocalName, attrib.Name.LocalName, "BinaryKey", "Directory", "FileKey", "Property", "Script"));
3171 }
3172 source = this.core.CreateDirectoryReferenceFromInlineSyntax(sourceLineNumbers, attrib, null);
3173 sourceBits = MsiInterop.MsidbCustomActionTypeDirectory;
3174 break;
3175 case "DllEntry":
3176 if (null != target)
3177 {
3178 this.core.OnMessage(WixErrors.CustomActionMultipleTargets(sourceLineNumbers, node.Name.LocalName, attrib.Name.LocalName, "DllEntry", "Error", "ExeCommand", "JScriptCall", "Script", "Value", "VBScriptCall"));
3179 }
3180 target = this.core.GetAttributeValue(sourceLineNumbers, attrib);
3181 targetBits = MsiInterop.MsidbCustomActionTypeDll;
3182 break;
3183 case "Error":
3184 if (null != target)
3185 {
3186 this.core.OnMessage(WixErrors.CustomActionMultipleTargets(sourceLineNumbers, node.Name.LocalName, attrib.Name.LocalName, "DllEntry", "Error", "ExeCommand", "JScriptCall", "Script", "Value", "VBScriptCall"));
3187 }
3188 target = this.core.GetAttributeValue(sourceLineNumbers, attrib);
3189 targetBits = MsiInterop.MsidbCustomActionTypeTextData | MsiInterop.MsidbCustomActionTypeSourceFile;
3190
3191 bool errorReference = true;
3192
3193 try
3194 {
3195 // The target can be either a formatted error string or a literal
3196 // error number. Try to convert to error number to determine whether
3197 // to add a reference. No need to look at the value.
3198 Convert.ToInt32(target, CultureInfo.InvariantCulture.NumberFormat);
3199 }
3200 catch (FormatException)
3201 {
3202 errorReference = false;
3203 }
3204 catch (OverflowException)
3205 {
3206 errorReference = false;
3207 }
3208
3209 if (errorReference)
3210 {
3211 this.core.CreateSimpleReference(sourceLineNumbers, "Error", target);
3212 }
3213 break;
3214 case "ExeCommand":
3215 if (null != target)
3216 {
3217 this.core.OnMessage(WixErrors.CustomActionMultipleTargets(sourceLineNumbers, node.Name.LocalName, attrib.Name.LocalName, "DllEntry", "Error", "ExeCommand", "JScriptCall", "Script", "Value", "VBScriptCall"));
3218 }
3219 target = this.core.GetAttributeValue(sourceLineNumbers, attrib, EmptyRule.CanBeEmpty); // one of the few cases where an empty string value is valid
3220 targetBits = MsiInterop.MsidbCustomActionTypeExe;
3221 break;
3222 case "Execute":
3223 string execute = this.core.GetAttributeValue(sourceLineNumbers, attrib);
3224 if (0 < execute.Length)
3225 {
3226 Wix.CustomAction.ExecuteType executeType = Wix.CustomAction.ParseExecuteType(execute);
3227 switch (executeType)
3228 {
3229 case Wix.CustomAction.ExecuteType.commit:
3230 bits |= MsiInterop.MsidbCustomActionTypeInScript | MsiInterop.MsidbCustomActionTypeCommit;
3231 break;
3232 case Wix.CustomAction.ExecuteType.deferred:
3233 bits |= MsiInterop.MsidbCustomActionTypeInScript;
3234 break;
3235 case Wix.CustomAction.ExecuteType.firstSequence:
3236 bits |= MsiInterop.MsidbCustomActionTypeFirstSequence;
3237 break;
3238 case Wix.CustomAction.ExecuteType.immediate:
3239 break;
3240 case Wix.CustomAction.ExecuteType.oncePerProcess:
3241 bits |= MsiInterop.MsidbCustomActionTypeOncePerProcess;
3242 break;
3243 case Wix.CustomAction.ExecuteType.rollback:
3244 bits |= MsiInterop.MsidbCustomActionTypeInScript | MsiInterop.MsidbCustomActionTypeRollback;
3245 break;
3246 case Wix.CustomAction.ExecuteType.secondSequence:
3247 bits |= MsiInterop.MsidbCustomActionTypeClientRepeat;
3248 break;
3249 default:
3250 this.core.OnMessage(WixErrors.IllegalAttributeValue(sourceLineNumbers, node.Name.LocalName, attrib.Name.LocalName, execute, "commit", "deferred", "firstSequence", "immediate", "oncePerProcess", "rollback", "secondSequence"));
3251 break;
3252 }
3253 }
3254 break;
3255 case "FileKey":
3256 if (null != source)
3257 {
3258 this.core.OnMessage(WixErrors.CustomActionMultipleSources(sourceLineNumbers, node.Name.LocalName, attrib.Name.LocalName, "BinaryKey", "Directory", "FileKey", "Property", "Script"));
3259 }
3260 source = this.core.GetAttributeIdentifierValue(sourceLineNumbers, attrib);
3261 sourceBits = MsiInterop.MsidbCustomActionTypeSourceFile;
3262 this.core.CreateSimpleReference(sourceLineNumbers, "File", source); // add a reference to the appropriate File
3263 break;
3264 case "HideTarget":
3265 if (YesNoType.Yes == this.core.GetAttributeYesNoValue(sourceLineNumbers, attrib))
3266 {
3267 bits |= MsiInterop.MsidbCustomActionTypeHideTarget;
3268 }
3269 break;
3270 case "Impersonate":
3271 if (YesNoType.No == this.core.GetAttributeYesNoValue(sourceLineNumbers, attrib))
3272 {
3273 bits |= MsiInterop.MsidbCustomActionTypeNoImpersonate;
3274 }
3275 break;
3276 case "JScriptCall":
3277 if (null != target)
3278 {
3279 this.core.OnMessage(WixErrors.CustomActionMultipleTargets(sourceLineNumbers, node.Name.LocalName, attrib.Name.LocalName, "DllEntry", "Error", "ExeCommand", "JScriptCall", "Script", "Value", "VBScriptCall"));
3280 }
3281 target = this.core.GetAttributeValue(sourceLineNumbers, attrib, EmptyRule.CanBeEmpty); // one of the few cases where an empty string value is valid
3282 targetBits = MsiInterop.MsidbCustomActionTypeJScript;
3283 break;
3284 case "PatchUninstall":
3285 if (YesNoType.Yes == this.core.GetAttributeYesNoValue(sourceLineNumbers, attrib))
3286 {
3287 extendedBits |= MsiInterop.MsidbCustomActionTypePatchUninstall;
3288 }
3289 break;
3290 case "Property":
3291 if (null != source)
3292 {
3293 this.core.OnMessage(WixErrors.CustomActionMultipleSources(sourceLineNumbers, node.Name.LocalName, attrib.Name.LocalName, "BinaryKey", "Directory", "FileKey", "Property", "Script"));
3294 }
3295 source = this.core.GetAttributeValue(sourceLineNumbers, attrib);
3296 sourceBits = MsiInterop.MsidbCustomActionTypeProperty;
3297 break;
3298 case "Return":
3299 string returnValue = this.core.GetAttributeValue(sourceLineNumbers, attrib);
3300 if (0 < returnValue.Length)
3301 {
3302 Wix.CustomAction.ReturnType returnType = Wix.CustomAction.ParseReturnType(returnValue);
3303 switch (returnType)
3304 {
3305 case Wix.CustomAction.ReturnType.asyncNoWait:
3306 bits |= MsiInterop.MsidbCustomActionTypeAsync | MsiInterop.MsidbCustomActionTypeContinue;
3307 break;
3308 case Wix.CustomAction.ReturnType.asyncWait:
3309 bits |= MsiInterop.MsidbCustomActionTypeAsync;
3310 break;
3311 case Wix.CustomAction.ReturnType.check:
3312 break;
3313 case Wix.CustomAction.ReturnType.ignore:
3314 bits |= MsiInterop.MsidbCustomActionTypeContinue;
3315 break;
3316 default:
3317 this.core.OnMessage(WixErrors.IllegalAttributeValue(sourceLineNumbers, node.Name.LocalName, attrib.Name.LocalName, returnValue, "asyncNoWait", "asyncWait", "check", "ignore"));
3318 break;
3319 }
3320 }
3321 break;
3322 case "Script":
3323 if (null != source)
3324 {
3325 this.core.OnMessage(WixErrors.CustomActionMultipleSources(sourceLineNumbers, node.Name.LocalName, attrib.Name.LocalName, "BinaryKey", "Directory", "FileKey", "Property", "Script"));
3326 }
3327
3328 if (null != target)
3329 {
3330 this.core.OnMessage(WixErrors.CustomActionMultipleTargets(sourceLineNumbers, node.Name.LocalName, attrib.Name.LocalName, "DllEntry", "Error", "ExeCommand", "JScriptCall", "Script", "Value", "VBScriptCall"));
3331 }
3332
3333 // set the source and target to empty string for error messages when the user sets multiple sources or targets
3334 source = string.Empty;
3335 target = string.Empty;
3336
3337 inlineScript = true;
3338
3339 string script = this.core.GetAttributeValue(sourceLineNumbers, attrib);
3340 if (0 < script.Length)
3341 {
3342 Wix.CustomAction.ScriptType scriptType = Wix.CustomAction.ParseScriptType(script);
3343 switch (scriptType)
3344 {
3345 case Wix.CustomAction.ScriptType.jscript:
3346 sourceBits = MsiInterop.MsidbCustomActionTypeDirectory;
3347 targetBits = MsiInterop.MsidbCustomActionTypeJScript;
3348 break;
3349 case Wix.CustomAction.ScriptType.vbscript:
3350 sourceBits = MsiInterop.MsidbCustomActionTypeDirectory;
3351 targetBits = MsiInterop.MsidbCustomActionTypeVBScript;
3352 break;
3353 default:
3354 this.core.OnMessage(WixErrors.IllegalAttributeValue(sourceLineNumbers, node.Name.LocalName, attrib.Name.LocalName, script, "jscript", "vbscript"));
3355 break;
3356 }
3357 }
3358 break;
3359 case "SuppressModularization":
3360 suppressModularization = this.core.GetAttributeYesNoValue(sourceLineNumbers, attrib);
3361 break;
3362 case "TerminalServerAware":
3363 if (YesNoType.Yes == this.core.GetAttributeYesNoValue(sourceLineNumbers, attrib))
3364 {
3365 bits |= MsiInterop.MsidbCustomActionTypeTSAware;
3366 }
3367 break;
3368 case "Value":
3369 if (null != target)
3370 {
3371 this.core.OnMessage(WixErrors.CustomActionMultipleTargets(sourceLineNumbers, node.Name.LocalName, attrib.Name.LocalName, "DllEntry", "Error", "ExeCommand", "JScriptCall", "Script", "Value", "VBScriptCall"));
3372 }
3373 target = this.core.GetAttributeValue(sourceLineNumbers, attrib, EmptyRule.CanBeEmpty); // one of the few cases where an empty string value is valid
3374 targetBits = MsiInterop.MsidbCustomActionTypeTextData;
3375 break;
3376 case "VBScriptCall":
3377 if (null != target)
3378 {
3379 this.core.OnMessage(WixErrors.CustomActionMultipleTargets(sourceLineNumbers, node.Name.LocalName, attrib.Name.LocalName, "DllEntry", "Error", "ExeCommand", "JScriptCall", "Script", "Value", "VBScriptCall"));
3380 }
3381 target = this.core.GetAttributeValue(sourceLineNumbers, attrib, EmptyRule.CanBeEmpty); // one of the few cases where an empty string value is valid
3382 targetBits = MsiInterop.MsidbCustomActionTypeVBScript;
3383 break;
3384 case "Win64":
3385 explicitWin64 = true;
3386 if (YesNoType.Yes == this.core.GetAttributeYesNoValue(sourceLineNumbers, attrib))
3387 {
3388 bits |= MsiInterop.MsidbCustomActionType64BitScript;
3389 }
3390 break;
3391 default:
3392 this.core.UnexpectedAttribute(node, attrib);
3393 break;
3394 }
3395 }
3396 else
3397 {
3398 this.core.ParseExtensionAttribute(node, attrib);
3399 }
3400 }
3401
3402 if (null == id)
3403 {
3404 this.core.OnMessage(WixErrors.ExpectedAttribute(sourceLineNumbers, node.Name.LocalName, "Id"));
3405 id = Identifier.Invalid;
3406 }
3407
3408 if (!explicitWin64 && (MsiInterop.MsidbCustomActionTypeVBScript == targetBits || MsiInterop.MsidbCustomActionTypeJScript == targetBits) && (Platform.IA64 == this.CurrentPlatform || Platform.X64 == this.CurrentPlatform))
3409 {
3410 bits |= MsiInterop.MsidbCustomActionType64BitScript;
3411 }
3412
3413 // get the inner text if any exists
3414 innerText = this.core.GetTrimmedInnerText(node);
3415
3416 // if we have an in-lined Script CustomAction ensure no source or target attributes were provided
3417 if (inlineScript)
3418 {
3419 target = innerText;
3420 }
3421 else if (MsiInterop.MsidbCustomActionTypeVBScript == targetBits) // non-inline vbscript
3422 {
3423 if (null == source)
3424 {
3425 this.core.OnMessage(WixErrors.IllegalAttributeWithoutOtherAttributes(sourceLineNumbers, node.Name.LocalName, "VBScriptCall", "BinaryKey", "FileKey", "Property"));
3426 }
3427 else if (MsiInterop.MsidbCustomActionTypeDirectory == sourceBits)
3428 {
3429 this.core.OnMessage(WixErrors.IllegalAttributeWithOtherAttribute(sourceLineNumbers, node.Name.LocalName, "VBScriptCall", "Directory"));
3430 }
3431 }
3432 else if (MsiInterop.MsidbCustomActionTypeJScript == targetBits) // non-inline jscript
3433 {
3434 if (null == source)
3435 {
3436 this.core.OnMessage(WixErrors.IllegalAttributeWithoutOtherAttributes(sourceLineNumbers, node.Name.LocalName, "JScriptCall", "BinaryKey", "FileKey", "Property"));
3437 }
3438 else if (MsiInterop.MsidbCustomActionTypeDirectory == sourceBits)
3439 {
3440 this.core.OnMessage(WixErrors.IllegalAttributeWithOtherAttribute(sourceLineNumbers, node.Name.LocalName, "JScriptCall", "Directory"));
3441 }
3442 }
3443 else if (MsiInterop.MsidbCustomActionTypeExe == targetBits) // exe-command
3444 {
3445 if (null == source)
3446 {
3447 this.core.OnMessage(WixErrors.IllegalAttributeWithoutOtherAttributes(sourceLineNumbers, node.Name.LocalName, "ExeCommand", "BinaryKey", "Directory", "FileKey", "Property"));
3448 }
3449 }
3450 else if (MsiInterop.MsidbCustomActionTypeTextData == (bits | sourceBits | targetBits))
3451 {
3452 this.core.OnMessage(WixErrors.IllegalAttributeWithoutOtherAttributes(sourceLineNumbers, node.Name.LocalName, "Value", "Directory", "Property"));
3453 }
3454 else if (!String.IsNullOrEmpty(innerText)) // inner text cannot be specified with non-script CAs
3455 {
3456 this.core.OnMessage(WixErrors.CustomActionIllegalInnerText(sourceLineNumbers, node.Name.LocalName, innerText, "Script"));
3457 }
3458
3459 if (MsiInterop.MsidbCustomActionType64BitScript == (bits & MsiInterop.MsidbCustomActionType64BitScript) && MsiInterop.MsidbCustomActionTypeVBScript != targetBits && MsiInterop.MsidbCustomActionTypeJScript != targetBits)
3460 {
3461 this.core.OnMessage(WixErrors.IllegalAttributeWithoutOtherAttributes(sourceLineNumbers, node.Name.LocalName, "Win64", "Script", "VBScriptCall", "JScriptCall"));
3462 }
3463
3464 if ((MsiInterop.MsidbCustomActionTypeAsync | MsiInterop.MsidbCustomActionTypeContinue) == (bits & (MsiInterop.MsidbCustomActionTypeAsync | MsiInterop.MsidbCustomActionTypeContinue)) && MsiInterop.MsidbCustomActionTypeExe != targetBits)
3465 {
3466 this.core.OnMessage(WixErrors.IllegalAttributeValueWithoutOtherAttribute(sourceLineNumbers, node.Name.LocalName, "Return", "asyncNoWait", "ExeCommand"));
3467 }
3468
3469 if (MsiInterop.MsidbCustomActionTypeTSAware == (bits & MsiInterop.MsidbCustomActionTypeTSAware))
3470 {
3471 // TS-aware CAs are valid only when deferred so require the in-script Type bit...
3472 if (0 == (bits & MsiInterop.MsidbCustomActionTypeInScript))
3473 {
3474 this.core.OnMessage(WixErrors.IllegalTerminalServerCustomActionAttributes(sourceLineNumbers));
3475 }
3476 }
3477
3478 // MSI doesn't support in-script property setting, so disallow it
3479 if (MsiInterop.MsidbCustomActionTypeProperty == sourceBits &&
3480 MsiInterop.MsidbCustomActionTypeTextData == targetBits &&
3481 0 != (bits & MsiInterop.MsidbCustomActionTypeInScript))
3482 {
3483 this.core.OnMessage(WixErrors.IllegalPropertyCustomActionAttributes(sourceLineNumbers));
3484 }
3485
3486 if (0 == targetBits)
3487 {
3488 this.core.OnMessage(WixErrors.ExpectedAttributes(sourceLineNumbers, node.Name.LocalName, "DllEntry", "Error", "ExeCommand", "JScriptCall", "Script", "Value", "VBScriptCall"));
3489 }
3490
3491 this.core.ParseForExtensionElements(node);
3492
3493 if (!this.core.EncounteredError)
3494 {
3495 Row row = this.core.CreateRow(sourceLineNumbers, "CustomAction", id);
3496 row[1] = bits | sourceBits | targetBits;
3497 row[2] = source;
3498 row[3] = target;
3499 if (0 != extendedBits)
3500 {
3501 row[4] = extendedBits;
3502 }
3503
3504 if (YesNoType.Yes == suppressModularization)
3505 {
3506 this.core.CreateRow(sourceLineNumbers, "WixSuppressModularization", id);
3507 }
3508
3509 // For deferred CAs that specify HideTarget we should also hide the CA data property for the action.
3510 if (MsiInterop.MsidbCustomActionTypeHideTarget == (bits & MsiInterop.MsidbCustomActionTypeHideTarget) &&
3511 MsiInterop.MsidbCustomActionTypeInScript == (bits & MsiInterop.MsidbCustomActionTypeInScript))
3512 {
3513 this.AddWixPropertyRow(sourceLineNumbers, id, false, false, true);
3514 }
3515 }
3516 }
3517
3518 /// <summary>
3519 /// Parses a simple reference element.
3520 /// </summary>
3521 /// <param name="node">Element to parse.</param>
3522 /// <param name="table">Table which contains the target of the simple reference.</param>
3523 /// <returns>Id of the referenced element.</returns>
3524 private string ParseSimpleRefElement(XElement node, string table)
3525 {
3526 SourceLineNumber sourceLineNumbers = Preprocessor.GetSourceLineNumbers(node);
3527 string id = null;
3528
3529 foreach (XAttribute attrib in node.Attributes())
3530 {
3531 if (String.IsNullOrEmpty(attrib.Name.NamespaceName) || CompilerCore.WixNamespace == attrib.Name.Namespace)
3532 {
3533 switch (attrib.Name.LocalName)
3534 {
3535 case "Id":
3536 id = this.core.GetAttributeIdentifierValue(sourceLineNumbers, attrib);
3537 this.core.CreateSimpleReference(sourceLineNumbers, table, id);
3538 break;
3539 default:
3540 this.core.UnexpectedAttribute(node, attrib);
3541 break;
3542 }
3543 }
3544 else
3545 {
3546 this.core.ParseExtensionAttribute(node, attrib);
3547 }
3548 }
3549
3550 if (null == id)
3551 {
3552 this.core.OnMessage(WixErrors.ExpectedAttribute(sourceLineNumbers, node.Name.LocalName, "Id"));
3553 }
3554
3555 this.core.ParseForExtensionElements(node);
3556
3557 return id;
3558 }
3559
3560 /// <summary>
3561 /// Parses a PatchFamilyRef element.
3562 /// </summary>
3563 /// <param name="node">Element to parse.</param>
3564 /// <param name="parentType">The parent type.</param>
3565 /// <param name="parentId">The ID of the parent.</param>
3566 /// <returns>Id of the referenced element.</returns>
3567 private void ParsePatchFamilyRefElement(XElement node, ComplexReferenceParentType parentType, string parentId)
3568 {
3569 SourceLineNumber sourceLineNumbers = Preprocessor.GetSourceLineNumbers(node);
3570 string[] primaryKeys = new string[2];
3571
3572 foreach (XAttribute attrib in node.Attributes())
3573 {
3574 if (String.IsNullOrEmpty(attrib.Name.NamespaceName) || CompilerCore.WixNamespace == attrib.Name.Namespace)
3575 {
3576 switch (attrib.Name.LocalName)
3577 {
3578 case "Id":
3579 primaryKeys[0] = this.core.GetAttributeIdentifierValue(sourceLineNumbers, attrib);
3580 break;
3581 case "ProductCode":
3582 primaryKeys[1] = this.core.GetAttributeIdentifierValue(sourceLineNumbers, attrib);
3583 break;
3584 default:
3585 this.core.UnexpectedAttribute(node, attrib);
3586 break;
3587 }
3588 }
3589 else
3590 {
3591 this.core.ParseExtensionAttribute(node, attrib);
3592 }
3593 }
3594
3595 if (null == primaryKeys[0])
3596 {
3597 this.core.OnMessage(WixErrors.ExpectedAttribute(sourceLineNumbers, node.Name.LocalName, "Id"));
3598 }
3599
3600 this.core.CreateSimpleReference(sourceLineNumbers, "MsiPatchSequence", primaryKeys);
3601
3602 this.core.ParseForExtensionElements(node);
3603
3604 if (!this.core.EncounteredError)
3605 {
3606 this.core.CreateComplexReference(sourceLineNumbers, parentType, parentId, null, ComplexReferenceChildType.PatchFamily, primaryKeys[0], true);
3607 }
3608 }
3609
3610 /// <summary>
3611 /// Parses a PatchFamilyGroup element.
3612 /// </summary>
3613 /// <param name="node">Element to parse.</param>
3614 [System.Diagnostics.CodeAnalysis.SuppressMessage("Microsoft.Performance", "CA1800:DoNotCastUnnecessarily")]
3615 private void ParsePatchFamilyGroupElement(XElement node, ComplexReferenceParentType parentType, string parentId)
3616 {
3617 SourceLineNumber sourceLineNumbers = Preprocessor.GetSourceLineNumbers(node);
3618 Identifier id = null;
3619
3620 foreach (XAttribute attrib in node.Attributes())
3621 {
3622 if (String.IsNullOrEmpty(attrib.Name.NamespaceName) || CompilerCore.WixNamespace == attrib.Name.Namespace)
3623 {
3624 switch (attrib.Name.LocalName)
3625 {
3626 case "Id":
3627 id = this.core.GetAttributeIdentifier(sourceLineNumbers, attrib);
3628 break;
3629 default:
3630 this.core.UnexpectedAttribute(node, attrib);
3631 break;
3632 }
3633 }
3634 else
3635 {
3636 this.core.ParseExtensionAttribute(node, attrib);
3637 }
3638 }
3639
3640 if (null == id)
3641 {
3642 this.core.OnMessage(WixErrors.ExpectedAttribute(sourceLineNumbers, node.Name.LocalName, "Id"));
3643 id = Identifier.Invalid;
3644 }
3645
3646 foreach (XElement child in node.Elements())
3647 {
3648 if (CompilerCore.WixNamespace == child.Name.Namespace)
3649 {
3650 switch (child.Name.LocalName)
3651 {
3652 case "PatchFamily":
3653 this.ParsePatchFamilyElement(child, ComplexReferenceParentType.PatchFamilyGroup, id.Id);
3654 break;
3655 case "PatchFamilyRef":
3656 this.ParsePatchFamilyRefElement(child, ComplexReferenceParentType.PatchFamilyGroup, id.Id);
3657 break;
3658 case "PatchFamilyGroupRef":
3659 this.ParsePatchFamilyGroupRefElement(child, ComplexReferenceParentType.PatchFamilyGroup, id.Id);
3660 break;
3661 default:
3662 this.core.UnexpectedElement(node, child);
3663 break;
3664 }
3665 }
3666 else
3667 {
3668 this.core.ParseExtensionElement(node, child);
3669 }
3670 }
3671
3672 if (!this.core.EncounteredError)
3673 {
3674 Row row = this.core.CreateRow(sourceLineNumbers, "WixPatchFamilyGroup", id);
3675
3676 //Add this PatchFamilyGroup and its parent in WixGroup.
3677 this.core.CreateWixGroupRow(sourceLineNumbers, parentType, parentId, ComplexReferenceChildType.PatchFamilyGroup, id.Id);
3678 }
3679 }
3680
3681 /// <summary>
3682 /// Parses a PatchFamilyGroup reference element.
3683 /// </summary>
3684 /// <param name="node">Element to parse.</param>
3685 /// <param name="parentType">The type of parent.</param>
3686 /// <param name="parentId">Identifier of parent element.</param>
3687 private void ParsePatchFamilyGroupRefElement(XElement node, ComplexReferenceParentType parentType, string parentId)
3688 {
3689 Debug.Assert(ComplexReferenceParentType.PatchFamilyGroup == parentType || ComplexReferenceParentType.Patch == parentType);
3690
3691 SourceLineNumber sourceLineNumbers = Preprocessor.GetSourceLineNumbers(node);
3692 string id = null;
3693
3694 foreach (XAttribute attrib in node.Attributes())
3695 {
3696 if (String.IsNullOrEmpty(attrib.Name.NamespaceName) || CompilerCore.WixNamespace == attrib.Name.Namespace)
3697 {
3698 switch (attrib.Name.LocalName)
3699 {
3700 case "Id":
3701 id = this.core.GetAttributeIdentifierValue(sourceLineNumbers, attrib);
3702 this.core.CreateSimpleReference(sourceLineNumbers, "WixPatchFamilyGroup", id);
3703 break;
3704 default:
3705 this.core.UnexpectedAttribute(node, attrib);
3706 break;
3707 }
3708 }
3709 else
3710 {
3711 this.core.ParseExtensionAttribute(node, attrib);
3712 }
3713 }
3714
3715 if (null == id)
3716 {
3717 this.core.OnMessage(WixErrors.ExpectedAttribute(sourceLineNumbers, node.Name.LocalName, "Id"));
3718 }
3719
3720 this.core.ParseForExtensionElements(node);
3721
3722 if (!this.core.EncounteredError)
3723 {
3724 this.core.CreateComplexReference(sourceLineNumbers, parentType, parentId, null, ComplexReferenceChildType.PatchFamilyGroup, id, true);
3725 }
3726 }
3727
3728 /// <summary>
3729 /// Parses an ensure table element.
3730 /// </summary>
3731 /// <param name="node">Element to parse.</param>
3732 private void ParseEnsureTableElement(XElement node)
3733 {
3734 SourceLineNumber sourceLineNumbers = Preprocessor.GetSourceLineNumbers(node);
3735 string id = null;
3736
3737 foreach (XAttribute attrib in node.Attributes())
3738 {
3739 if (String.IsNullOrEmpty(attrib.Name.NamespaceName) || CompilerCore.WixNamespace == attrib.Name.Namespace)
3740 {
3741 switch (attrib.Name.LocalName)
3742 {
3743 case "Id":
3744 id = this.core.GetAttributeIdentifierValue(sourceLineNumbers, attrib);
3745 break;
3746 default:
3747 this.core.UnexpectedAttribute(node, attrib);
3748 break;
3749 }
3750 }
3751 else
3752 {
3753 this.core.ParseExtensionAttribute(node, attrib);
3754 }
3755 }
3756
3757 if (null == id)
3758 {
3759 this.core.OnMessage(WixErrors.ExpectedAttribute(sourceLineNumbers, node.Name.LocalName, "Id"));
3760 }
3761 else if (31 < id.Length)
3762 {
3763 this.core.OnMessage(WixErrors.TableNameTooLong(sourceLineNumbers, node.Name.LocalName, "Id", id));
3764 }
3765
3766 this.core.ParseForExtensionElements(node);
3767
3768 this.core.EnsureTable(sourceLineNumbers, id);
3769 }
3770
3771 /// <summary>
3772 /// Parses a custom table element.
3773 /// </summary>
3774 /// <param name="node">Element to parse.</param>
3775 /// <remarks>not cleaned</remarks>
3776 [SuppressMessage("Microsoft.Globalization", "CA1308:NormalizeStringsToUppercase", Justification = "Changing the way this string normalizes would result " +
3777 "in a change to the way the WixCustomTable table is generated. Furthermore, there is no security hole here, as the strings won't need to " +
3778 "make a round trip")]
3779 private void ParseCustomTableElement(XElement node)
3780 {
3781 SourceLineNumber sourceLineNumbers = Preprocessor.GetSourceLineNumbers(node);
3782 string tableId = null;
3783
3784 string categories = null;
3785 int columnCount = 0;
3786 string columnNames = null;
3787 string columnTypes = null;
3788 string descriptions = null;
3789 string keyColumns = null;
3790 string keyTables = null;
3791 string maxValues = null;
3792 string minValues = null;
3793 string modularizations = null;
3794 string primaryKeys = null;
3795 string sets = null;
3796 bool bootstrapperApplicationData = false;
3797
3798 foreach (XAttribute attrib in node.Attributes())
3799 {
3800 if (String.IsNullOrEmpty(attrib.Name.NamespaceName) || CompilerCore.WixNamespace == attrib.Name.Namespace)
3801 {
3802 switch (attrib.Name.LocalName)
3803 {
3804 case "Id":
3805 tableId = this.core.GetAttributeIdentifierValue(sourceLineNumbers, attrib);
3806 break;
3807 case "BootstrapperApplicationData":
3808 bootstrapperApplicationData = YesNoType.Yes == this.core.GetAttributeYesNoValue(sourceLineNumbers, attrib);
3809 break;
3810 default:
3811 this.core.UnexpectedAttribute(node, attrib);
3812 break;
3813 }
3814 }
3815 else
3816 {
3817 this.core.ParseExtensionAttribute(node, attrib);
3818 }
3819 }
3820
3821 if (null == tableId)
3822 {
3823 this.core.OnMessage(WixErrors.ExpectedAttribute(sourceLineNumbers, node.Name.LocalName, "Id"));
3824 }
3825 else if (31 < tableId.Length)
3826 {
3827 this.core.OnMessage(WixErrors.CustomTableNameTooLong(sourceLineNumbers, node.Name.LocalName, "Id", tableId));
3828 }
3829
3830 foreach (XElement child in node.Elements())
3831 {
3832 if (CompilerCore.WixNamespace == child.Name.Namespace)
3833 {
3834 SourceLineNumber childSourceLineNumbers = Preprocessor.GetSourceLineNumbers(child);
3835 switch (child.Name.LocalName)
3836 {
3837 case "Column":
3838 ++columnCount;
3839
3840 string category = String.Empty;
3841 string columnName = null;
3842 string columnType = null;
3843 string description = String.Empty;
3844 int keyColumn = CompilerConstants.IntegerNotSet;
3845 string keyTable = String.Empty;
3846 bool localizable = false;
3847 long maxValue = CompilerConstants.LongNotSet;
3848 long minValue = CompilerConstants.LongNotSet;
3849 string modularization = "None";
3850 bool nullable = false;
3851 bool primaryKey = false;
3852 string setValues = String.Empty;
3853 string typeName = null;
3854 int width = 0;
3855
3856 foreach (XAttribute childAttrib in child.Attributes())
3857 {
3858 switch (childAttrib.Name.LocalName)
3859 {
3860 case "Id":
3861 columnName = this.core.GetAttributeIdentifierValue(childSourceLineNumbers, childAttrib);
3862 break;
3863 case "Category":
3864 category = this.core.GetAttributeValue(childSourceLineNumbers, childAttrib);
3865 break;
3866 case "Description":
3867 description = this.core.GetAttributeValue(childSourceLineNumbers, childAttrib);
3868 break;
3869 case "KeyColumn":
3870 keyColumn = this.core.GetAttributeIntegerValue(childSourceLineNumbers, childAttrib, 1, 32);
3871 break;
3872 case "KeyTable":
3873 keyTable = this.core.GetAttributeValue(childSourceLineNumbers, childAttrib);
3874 break;
3875 case "Localizable":
3876 localizable = YesNoType.Yes == this.core.GetAttributeYesNoValue(childSourceLineNumbers, childAttrib);
3877 break;
3878 case "MaxValue":
3879 maxValue = this.core.GetAttributeLongValue(childSourceLineNumbers, childAttrib, int.MinValue + 1, int.MaxValue);
3880 break;
3881 case "MinValue":
3882 minValue = this.core.GetAttributeLongValue(childSourceLineNumbers, childAttrib, int.MinValue + 1, int.MaxValue);
3883 break;
3884 case "Modularize":
3885 modularization = this.core.GetAttributeValue(childSourceLineNumbers, childAttrib);
3886 break;
3887 case "Nullable":
3888 nullable = YesNoType.Yes == this.core.GetAttributeYesNoValue(childSourceLineNumbers, childAttrib);
3889 break;
3890 case "PrimaryKey":
3891 primaryKey = YesNoType.Yes == this.core.GetAttributeYesNoValue(childSourceLineNumbers, childAttrib);
3892 break;
3893 case "Set":
3894 setValues = this.core.GetAttributeValue(childSourceLineNumbers, childAttrib);
3895 break;
3896 case "Type":
3897 string typeValue = this.core.GetAttributeValue(childSourceLineNumbers, childAttrib);
3898 if (0 < typeValue.Length)
3899 {
3900 Wix.Column.TypeType typeType = Wix.Column.ParseTypeType(typeValue);
3901 switch (typeType)
3902 {
3903 case Wix.Column.TypeType.binary:
3904 typeName = "OBJECT";
3905 break;
3906 case Wix.Column.TypeType.@int:
3907 typeName = "SHORT";
3908 break;
3909 case Wix.Column.TypeType.@string:
3910 typeName = "CHAR";
3911 break;
3912 default:
3913 this.core.OnMessage(WixErrors.IllegalAttributeValue(childSourceLineNumbers, child.Name.LocalName, "Type", typeValue, "binary", "int", "string"));
3914 break;
3915 }
3916 }
3917 break;
3918 case "Width":
3919 width = this.core.GetAttributeIntegerValue(childSourceLineNumbers, childAttrib, 0, int.MaxValue);
3920 break;
3921 default:
3922 this.core.UnexpectedAttribute(child, childAttrib);
3923 break;
3924 }
3925 }
3926
3927 if (null == columnName)
3928 {
3929 this.core.OnMessage(WixErrors.ExpectedAttribute(childSourceLineNumbers, child.Name.LocalName, "Id"));
3930 }
3931
3932 if (null == typeName)
3933 {
3934 this.core.OnMessage(WixErrors.ExpectedAttribute(childSourceLineNumbers, child.Name.LocalName, "Type"));
3935 }
3936 else if ("SHORT" == typeName)
3937 {
3938 if (2 != width && 4 != width)
3939 {
3940 this.core.OnMessage(WixErrors.CustomTableIllegalColumnWidth(childSourceLineNumbers, child.Name.LocalName, "Width", width));
3941 }
3942 columnType = String.Concat(nullable ? "I" : "i", width);
3943 }
3944 else if ("CHAR" == typeName)
3945 {
3946 string typeChar = localizable ? "l" : "s";
3947 columnType = String.Concat(nullable ? typeChar.ToUpper(CultureInfo.InvariantCulture) : typeChar.ToLower(CultureInfo.InvariantCulture), width);
3948 }
3949 else if ("OBJECT" == typeName)
3950 {
3951 if ("Binary" != category)
3952 {
3953 this.core.OnMessage(WixErrors.ExpectedBinaryCategory(childSourceLineNumbers));
3954 }
3955 columnType = String.Concat(nullable ? "V" : "v", width);
3956 }
3957
3958 this.core.ParseForExtensionElements(child);
3959
3960 columnNames = String.Concat(columnNames, null == columnNames ? String.Empty : "\t", columnName);
3961 columnTypes = String.Concat(columnTypes, null == columnTypes ? String.Empty : "\t", columnType);
3962 if (primaryKey)
3963 {
3964 primaryKeys = String.Concat(primaryKeys, null == primaryKeys ? String.Empty : "\t", columnName);
3965 }
3966
3967 minValues = String.Concat(minValues, null == minValues ? String.Empty : "\t", CompilerConstants.LongNotSet != minValue ? minValue.ToString(CultureInfo.InvariantCulture) : String.Empty);
3968 maxValues = String.Concat(maxValues, null == maxValues ? String.Empty : "\t", CompilerConstants.LongNotSet != maxValue ? maxValue.ToString(CultureInfo.InvariantCulture) : String.Empty);
3969 keyTables = String.Concat(keyTables, null == keyTables ? String.Empty : "\t", keyTable);
3970 keyColumns = String.Concat(keyColumns, null == keyColumns ? String.Empty : "\t", CompilerConstants.IntegerNotSet != keyColumn ? keyColumn.ToString(CultureInfo.InvariantCulture) : String.Empty);
3971 categories = String.Concat(categories, null == categories ? String.Empty : "\t", category);
3972 sets = String.Concat(sets, null == sets ? String.Empty : "\t", setValues);
3973 descriptions = String.Concat(descriptions, null == descriptions ? String.Empty : "\t", description);
3974 modularizations = String.Concat(modularizations, null == modularizations ? String.Empty : "\t", modularization);
3975
3976 break;
3977 case "Row":
3978 string dataValue = null;
3979
3980 foreach (XAttribute childAttrib in child.Attributes())
3981 {
3982 this.core.ParseExtensionAttribute(child, childAttrib);
3983 }
3984
3985 foreach (XElement data in child.Elements())
3986 {
3987 SourceLineNumber dataSourceLineNumbers = Preprocessor.GetSourceLineNumbers(data);
3988 switch (data.Name.LocalName)
3989 {
3990 case "Data":
3991 columnName = null;
3992 foreach (XAttribute dataAttrib in data.Attributes())
3993 {
3994 switch (dataAttrib.Name.LocalName)
3995 {
3996 case "Column":
3997 columnName = this.core.GetAttributeValue(dataSourceLineNumbers, dataAttrib);
3998 break;
3999 default:
4000 this.core.UnexpectedAttribute(data, dataAttrib);
4001 break;
4002 }
4003 }
4004
4005 if (null == columnName)
4006 {
4007 this.core.OnMessage(WixErrors.ExpectedAttribute(dataSourceLineNumbers, data.Name.LocalName, "Column"));
4008 }
4009
4010 dataValue = String.Concat(dataValue, null == dataValue ? String.Empty : Common.CustomRowFieldSeparator.ToString(), columnName, ":", Common.GetInnerText(data));
4011 break;
4012 }
4013 }
4014
4015 this.core.CreateSimpleReference(sourceLineNumbers, "WixCustomTable", tableId);
4016
4017 if (!this.core.EncounteredError)
4018 {
4019 Row rowRow = this.core.CreateRow(childSourceLineNumbers, "WixCustomRow");
4020 rowRow[0] = tableId;
4021 rowRow[1] = dataValue;
4022 }
4023 break;
4024 default:
4025 this.core.UnexpectedElement(node, child);
4026 break;
4027 }
4028 }
4029 else
4030 {
4031 this.core.ParseExtensionElement(node, child);
4032 }
4033 }
4034
4035 if (0 < columnCount)
4036 {
4037 if (null == primaryKeys || 0 == primaryKeys.Length)
4038 {
4039 this.core.OnMessage(WixErrors.CustomTableMissingPrimaryKey(sourceLineNumbers));
4040 }
4041
4042 if (!this.core.EncounteredError)
4043 {
4044 Row row = this.core.CreateRow(sourceLineNumbers, "WixCustomTable");
4045 row[0] = tableId;
4046 row[1] = columnCount;
4047 row[2] = columnNames;
4048 row[3] = columnTypes;
4049 row[4] = primaryKeys;
4050 row[5] = minValues;
4051 row[6] = maxValues;
4052 row[7] = keyTables;
4053 row[8] = keyColumns;
4054 row[9] = categories;
4055 row[10] = sets;
4056 row[11] = descriptions;
4057 row[12] = modularizations;
4058 row[13] = bootstrapperApplicationData ? 1 : 0;
4059 }
4060 }
4061 }
4062
4063 /// <summary>
4064 /// Parses a directory element.
4065 /// </summary>
4066 /// <param name="node">Element to parse.</param>
4067 /// <param name="parentId">Optional identifier of parent directory.</param>
4068 /// <param name="diskId">Disk id inherited from parent directory.</param>
4069 /// <param name="fileSource">Path to source file as of yet.</param>
4070 [SuppressMessage("Microsoft.Performance", "CA1820:TestForEmptyStringsUsingStringLength")]
4071 private void ParseDirectoryElement(XElement node, string parentId, int diskId, string fileSource)
4072 {
4073 SourceLineNumber sourceLineNumbers = Preprocessor.GetSourceLineNumbers(node);
4074 Identifier id = null;
4075 string componentGuidGenerationSeed = null;
4076 bool fileSourceAttribSet = false;
4077 bool nameHasValue = false;
4078 string name = "."; // default to parent directory.
4079 string[] inlineSyntax = null;
4080 string shortName = null;
4081 string sourceName = null;
4082 string shortSourceName = null;
4083 string defaultDir = null;
4084 string symbols = null;
4085
4086 foreach (XAttribute attrib in node.Attributes())
4087 {
4088 if (String.IsNullOrEmpty(attrib.Name.NamespaceName) || CompilerCore.WixNamespace == attrib.Name.Namespace)
4089 {
4090 switch (attrib.Name.LocalName)
4091 {
4092 case "Id":
4093 id = this.core.GetAttributeIdentifier(sourceLineNumbers, attrib);
4094 break;
4095 case "ComponentGuidGenerationSeed":
4096 componentGuidGenerationSeed = this.core.GetAttributeGuidValue(sourceLineNumbers, attrib, false);
4097 break;
4098 case "DiskId":
4099 diskId = this.core.GetAttributeIntegerValue(sourceLineNumbers, attrib, 1, short.MaxValue);
4100 break;
4101 case "FileSource":
4102 fileSource = this.core.GetAttributeValue(sourceLineNumbers, attrib);
4103 fileSourceAttribSet = true;
4104 break;
4105 case "Name":
4106 nameHasValue = true;
4107 if (attrib.Value.Equals("."))
4108 {
4109 name = attrib.Value;
4110 }
4111 else
4112 {
4113 inlineSyntax = this.core.GetAttributeInlineDirectorySyntax(sourceLineNumbers, attrib);
4114 }
4115 break;
4116 case "ShortName":
4117 shortName = this.core.GetAttributeShortFilename(sourceLineNumbers, attrib, false);
4118 break;
4119 case "ShortSourceName":
4120 shortSourceName = this.core.GetAttributeShortFilename(sourceLineNumbers, attrib, false);
4121 break;
4122 case "SourceName":
4123 if ("." == attrib.Value)
4124 {
4125 sourceName = attrib.Value;
4126 }
4127 else
4128 {
4129 sourceName = this.core.GetAttributeLongFilename(sourceLineNumbers, attrib, false);
4130 }
4131 break;
4132 default:
4133 this.core.UnexpectedAttribute(node, attrib);
4134 break;
4135 }
4136 }
4137 else
4138 {
4139 this.core.ParseExtensionAttribute(node, attrib);
4140 }
4141 }
4142
4143 // Create the directory rows for the inline.
4144 if (null != inlineSyntax)
4145 {
4146 // Special case the single entry in the inline syntax since it is the most common case
4147 // and needs no extra processing. It's just the name of the directory.
4148 if (1 == inlineSyntax.Length)
4149 {
4150 name = inlineSyntax[0];
4151 }
4152 else
4153 {
4154 int pathStartsAt = 0;
4155 if (inlineSyntax[0].EndsWith(":"))
4156 {
4157 parentId = inlineSyntax[0].TrimEnd(':');
4158 this.core.CreateSimpleReference(sourceLineNumbers, "Directory", parentId);
4159
4160 pathStartsAt = 1;
4161 }
4162
4163 for (int i = pathStartsAt; i < inlineSyntax.Length - 1; ++i)
4164 {
4165 Identifier inlineId = this.core.CreateDirectoryRow(sourceLineNumbers, null, parentId, inlineSyntax[i]);
4166 parentId = inlineId.Id;
4167 }
4168
4169 name = inlineSyntax[inlineSyntax.Length - 1];
4170 }
4171 }
4172
4173 if (!nameHasValue)
4174 {
4175 if (!String.IsNullOrEmpty(shortName))
4176 {
4177 this.core.OnMessage(WixErrors.IllegalAttributeWithoutOtherAttributes(sourceLineNumbers, node.Name.LocalName, "ShortName", "Name"));
4178 }
4179
4180 if (null == parentId)
4181 {
4182 this.core.OnMessage(WixErrors.DirectoryRootWithoutName(sourceLineNumbers, node.Name.LocalName, "Name"));
4183 }
4184 }
4185 else if (!String.IsNullOrEmpty(name))
4186 {
4187 if (String.IsNullOrEmpty(shortName))
4188 {
4189 if (!name.Equals(".") && !name.Equals("SourceDir") && !this.core.IsValidShortFilename(name, false))
4190 {
4191 shortName = this.core.CreateShortName(name, false, false, "Directory", parentId);
4192 }
4193 }
4194 else if (name.Equals("."))
4195 {
4196 this.core.OnMessage(WixErrors.IllegalAttributeWithOtherAttribute(sourceLineNumbers, node.Name.LocalName, "ShortName", "Name", name));
4197 }
4198 else if (name.Equals(shortName))
4199 {
4200 this.core.OnMessage(WixWarnings.DirectoryRedundantNames(sourceLineNumbers, node.Name.LocalName, "Name", "ShortName", name));
4201 }
4202 }
4203
4204 if (String.IsNullOrEmpty(sourceName))
4205 {
4206 if (!String.IsNullOrEmpty(shortSourceName))
4207 {
4208 this.core.OnMessage(WixErrors.IllegalAttributeWithoutOtherAttributes(sourceLineNumbers, node.Name.LocalName, "ShortSourceName", "SourceName"));
4209 }
4210 }
4211 else
4212 {
4213 if (String.IsNullOrEmpty(shortSourceName))
4214 {
4215 if (!sourceName.Equals(".") && !this.core.IsValidShortFilename(sourceName, false))
4216 {
4217 shortSourceName = this.core.CreateShortName(sourceName, false, false, "Directory", parentId);
4218 }
4219 }
4220 else if (sourceName.Equals("."))
4221 {
4222 this.core.OnMessage(WixErrors.IllegalAttributeWithOtherAttribute(sourceLineNumbers, node.Name.LocalName, "ShortSourceName", "SourceName", sourceName));
4223 }
4224 else if (sourceName.Equals(shortSourceName))
4225 {
4226 this.core.OnMessage(WixWarnings.DirectoryRedundantNames(sourceLineNumbers, node.Name.LocalName, "SourceName", "ShortSourceName", sourceName));
4227 }
4228 }
4229
4230 // Update the file source path appropriately.
4231 if (fileSourceAttribSet)
4232 {
4233 if (!fileSource.EndsWith(Path.DirectorySeparatorChar.ToString(), StringComparison.Ordinal))
4234 {
4235 fileSource = String.Concat(fileSource, Path.DirectorySeparatorChar);
4236 }
4237 }
4238 else // add the appropriate part of this directory element to the file source.
4239 {
4240 string append = null;
4241 if (this.useShortFileNames)
4242 {
4243 append = !String.IsNullOrEmpty(shortSourceName) ? shortSourceName : shortName;
4244 }
4245
4246 if (String.IsNullOrEmpty(append))
4247 {
4248 append = !String.IsNullOrEmpty(sourceName) ? sourceName : name;
4249 }
4250
4251 if (!String.IsNullOrEmpty(append))
4252 {
4253 fileSource = String.Concat(fileSource, append, Path.DirectorySeparatorChar);
4254 }
4255 }
4256
4257 if (null == id)
4258 {
4259 id = this.core.CreateIdentifier("dir", parentId, name, shortName, sourceName, shortSourceName);
4260 }
4261
4262 // Calculate the DefaultDir for the directory row.
4263 defaultDir = String.IsNullOrEmpty(shortName) ? name : String.Concat(shortName, "|", name);
4264 if (!String.IsNullOrEmpty(sourceName))
4265 {
4266 defaultDir = String.Concat(defaultDir, ":", String.IsNullOrEmpty(shortSourceName) ? sourceName : String.Concat(shortSourceName, "|", sourceName));
4267 }
4268
4269 if ("TARGETDIR".Equals(id.Id) && !"SourceDir".Equals(defaultDir))
4270 {
4271 this.core.OnMessage(WixErrors.IllegalTargetDirDefaultDir(sourceLineNumbers, defaultDir));
4272 }
4273
4274 foreach (XElement child in node.Elements())
4275 {
4276 if (CompilerCore.WixNamespace == child.Name.Namespace)
4277 {
4278 switch (child.Name.LocalName)
4279 {
4280 case "Component":
4281 this.ParseComponentElement(child, ComplexReferenceParentType.Unknown, null, null, diskId, id.Id, fileSource);
4282 break;
4283 case "Directory":
4284 this.ParseDirectoryElement(child, id.Id, diskId, fileSource);
4285 break;
4286 case "Merge":
4287 this.ParseMergeElement(child, id.Id, diskId);
4288 break;
4289 case "SymbolPath":
4290 if (null != symbols)
4291 {
4292 symbols += ";" + this.ParseSymbolPathElement(child);
4293 }
4294 else
4295 {
4296 symbols = this.ParseSymbolPathElement(child);
4297 }
4298 break;
4299 default:
4300 this.core.UnexpectedElement(node, child);
4301 break;
4302 }
4303 }
4304 else
4305 {
4306 this.core.ParseExtensionElement(node, child);
4307 }
4308 }
4309
4310 if (!this.core.EncounteredError)
4311 {
4312 Row row = this.core.CreateRow(sourceLineNumbers, "Directory", id);
4313 row[1] = parentId;
4314 row[2] = defaultDir;
4315
4316 if (null != componentGuidGenerationSeed)
4317 {
4318 Row wixRow = this.core.CreateRow(sourceLineNumbers, "WixDirectory");
4319 wixRow[0] = id.Id;
4320 wixRow[1] = componentGuidGenerationSeed;
4321 }
4322
4323 if (null != symbols)
4324 {
4325 WixDeltaPatchSymbolPathsRow symbolRow = (WixDeltaPatchSymbolPathsRow)this.core.CreateRow(sourceLineNumbers, "WixDeltaPatchSymbolPaths", id);
4326 symbolRow.Type = SymbolPathType.Directory;
4327 symbolRow.SymbolPaths = symbols;
4328 }
4329 }
4330 }
4331
4332 /// <summary>
4333 /// Parses a directory reference element.
4334 /// </summary>
4335 /// <param name="node">Element to parse.</param>
4336 [System.Diagnostics.CodeAnalysis.SuppressMessage("Microsoft.Performance", "CA1800:DoNotCastUnnecessarily")]
4337 private void ParseDirectoryRefElement(XElement node)
4338 {
4339 SourceLineNumber sourceLineNumbers = Preprocessor.GetSourceLineNumbers(node);
4340 string id = null;
4341 int diskId = CompilerConstants.IntegerNotSet;
4342 string fileSource = String.Empty;
4343
4344 foreach (XAttribute attrib in node.Attributes())
4345 {
4346 if (String.IsNullOrEmpty(attrib.Name.NamespaceName) || CompilerCore.WixNamespace == attrib.Name.Namespace)
4347 {
4348 switch (attrib.Name.LocalName)
4349 {
4350 case "Id":
4351 id = this.core.GetAttributeIdentifierValue(sourceLineNumbers, attrib);
4352 this.core.CreateSimpleReference(sourceLineNumbers, "Directory", id);
4353 break;
4354 case "DiskId":
4355 diskId = this.core.GetAttributeIntegerValue(sourceLineNumbers, attrib, 1, short.MaxValue);
4356 break;
4357 case "FileSource":
4358 fileSource = this.core.GetAttributeValue(sourceLineNumbers, attrib);
4359 break;
4360 default:
4361 this.core.UnexpectedAttribute(node, attrib);
4362 break;
4363 }
4364 }
4365 else
4366 {
4367 this.core.ParseExtensionAttribute(node, attrib);
4368 }
4369 }
4370
4371 if (null == id)
4372 {
4373 this.core.OnMessage(WixErrors.ExpectedAttribute(sourceLineNumbers, node.Name.LocalName, "Id"));
4374 }
4375
4376 if (!String.IsNullOrEmpty(fileSource) && !fileSource.EndsWith(Path.DirectorySeparatorChar.ToString(), StringComparison.Ordinal))
4377 {
4378 fileSource = String.Concat(fileSource, Path.DirectorySeparatorChar);
4379 }
4380
4381 foreach (XElement child in node.Elements())
4382 {
4383 if (CompilerCore.WixNamespace == child.Name.Namespace)
4384 {
4385 switch (child.Name.LocalName)
4386 {
4387 case "Component":
4388 this.ParseComponentElement(child, ComplexReferenceParentType.Unknown, null, null, diskId, id, fileSource);
4389 break;
4390 case "Directory":
4391 this.ParseDirectoryElement(child, id, diskId, fileSource);
4392 break;
4393 case "Merge":
4394 this.ParseMergeElement(child, id, diskId);
4395 break;
4396 default:
4397 this.core.UnexpectedElement(node, child);
4398 break;
4399 }
4400 }
4401 else
4402 {
4403 this.core.ParseExtensionElement(node, child);
4404 }
4405 }
4406 }
4407
4408 /// <summary>
4409 /// Parses a directory search element.
4410 /// </summary>
4411 /// <param name="node">Element to parse.</param>
4412 /// <param name="parentSignature">Signature of parent search element.</param>
4413 /// <returns>Signature of search element.</returns>
4414 private string ParseDirectorySearchElement(XElement node, string parentSignature)
4415 {
4416 SourceLineNumber sourceLineNumbers = Preprocessor.GetSourceLineNumbers(node);
4417 Identifier id = null;
4418 int depth = CompilerConstants.IntegerNotSet;
4419 string path = null;
4420 bool assignToProperty = false;
4421 string signature = null;
4422
4423 foreach (XAttribute attrib in node.Attributes())
4424 {
4425 if (String.IsNullOrEmpty(attrib.Name.NamespaceName) || CompilerCore.WixNamespace == attrib.Name.Namespace)
4426 {
4427 switch (attrib.Name.LocalName)
4428 {
4429 case "Id":
4430 id = this.core.GetAttributeIdentifier(sourceLineNumbers, attrib);
4431 break;
4432 case "Depth":
4433 depth = this.core.GetAttributeIntegerValue(sourceLineNumbers, attrib, 0, short.MaxValue);
4434 break;
4435 case "Path":
4436 path = this.core.GetAttributeValue(sourceLineNumbers, attrib);
4437 break;
4438 case "AssignToProperty":
4439 assignToProperty = YesNoType.Yes == this.core.GetAttributeYesNoValue(sourceLineNumbers, attrib);
4440 break;
4441 default:
4442 this.core.UnexpectedAttribute(node, attrib);
4443 break;
4444 }
4445 }
4446 else
4447 {
4448 this.core.ParseExtensionAttribute(node, attrib);
4449 }
4450 }
4451
4452 if (null == id)
4453 {
4454 id = this.core.CreateIdentifier("dir", path, depth.ToString());
4455 }
4456
4457 signature = id.Id;
4458
4459 bool oneChild = false;
4460 bool hasFileSearch = false;
4461 foreach (XElement child in node.Elements())
4462 {
4463 if (CompilerCore.WixNamespace == child.Name.Namespace)
4464 {
4465 SourceLineNumber childSourceLineNumbers = Preprocessor.GetSourceLineNumbers(child);
4466 switch (child.Name.LocalName)
4467 {
4468 case "DirectorySearch":
4469 if (oneChild)
4470 {
4471 this.core.OnMessage(WixErrors.TooManySearchElements(childSourceLineNumbers, node.Name.LocalName));
4472 }
4473 oneChild = true;
4474 signature = this.ParseDirectorySearchElement(child, id.Id);
4475 break;
4476 case "DirectorySearchRef":
4477 if (oneChild)
4478 {
4479 this.core.OnMessage(WixErrors.TooManySearchElements(childSourceLineNumbers, node.Name.LocalName));
4480 }
4481 oneChild = true;
4482 signature = this.ParseDirectorySearchRefElement(child, id.Id);
4483 break;
4484 case "FileSearch":
4485 if (oneChild)
4486 {
4487 this.core.OnMessage(WixErrors.TooManySearchElements(sourceLineNumbers, node.Name.LocalName));
4488 }
4489 oneChild = true;
4490 hasFileSearch = true;
4491 signature = this.ParseFileSearchElement(child, id.Id, assignToProperty, depth);
4492 break;
4493 case "FileSearchRef":
4494 if (oneChild)
4495 {
4496 this.core.OnMessage(WixErrors.TooManySearchElements(sourceLineNumbers, node.Name.LocalName));
4497 }
4498 oneChild = true;
4499 signature = this.ParseSimpleRefElement(child, "Signature");
4500 break;
4501 default:
4502 this.core.UnexpectedElement(node, child);
4503 break;
4504 }
4505
4506 // If AssignToProperty is set, only a FileSearch
4507 // or no child element can be nested.
4508 if (assignToProperty)
4509 {
4510 if (!hasFileSearch)
4511 {
4512 this.core.OnMessage(WixErrors.IllegalParentAttributeWhenNested(sourceLineNumbers, node.Name.LocalName, "AssignToProperty", child.Name.LocalName));
4513 }
4514 else if (!oneChild)
4515 {
4516 // This a normal directory search.
4517 assignToProperty = false;
4518 }
4519 }
4520 }
4521 else
4522 {
4523 this.core.ParseExtensionElement(node, child);
4524 }
4525 }
4526
4527 if (!this.core.EncounteredError)
4528 {
4529 Identifier rowId = id;
4530
4531 // If AssignToProperty is set, the DrLocator row created by
4532 // ParseFileSearchElement creates the directory entry to return
4533 // and the row created here is for the file search.
4534 if (assignToProperty)
4535 {
4536 rowId = new Identifier(signature, AccessModifier.Private);
4537
4538 // The property should be set to the directory search Id.
4539 signature = id.Id;
4540 }
4541
4542 Row row = this.core.CreateRow(sourceLineNumbers, "DrLocator", rowId);
4543 row[1] = parentSignature;
4544 row[2] = path;
4545 if (CompilerConstants.IntegerNotSet != depth)
4546 {
4547 row[3] = depth;
4548 }
4549 }
4550
4551 return signature;
4552 }
4553
4554 /// <summary>
4555 /// Parses a directory search reference element.
4556 /// </summary>
4557 /// <param name="node">Element to parse.</param>
4558 /// <param name="parentSignature">Signature of parent search element.</param>
4559 /// <returns>Signature of search element.</returns>
4560 private string ParseDirectorySearchRefElement(XElement node, string parentSignature)
4561 {
4562 SourceLineNumber sourceLineNumbers = Preprocessor.GetSourceLineNumbers(node);
4563 Identifier id = null;
4564 Identifier parent = null;
4565 string path = null;
4566 string signature = null;
4567
4568 foreach (XAttribute attrib in node.Attributes())
4569 {
4570 if (String.IsNullOrEmpty(attrib.Name.NamespaceName) || CompilerCore.WixNamespace == attrib.Name.Namespace)
4571 {
4572 switch (attrib.Name.LocalName)
4573 {
4574 case "Id":
4575 id = this.core.GetAttributeIdentifier(sourceLineNumbers, attrib);
4576 break;
4577 case "Parent":
4578 parent = this.core.GetAttributeIdentifier(sourceLineNumbers, attrib);
4579 break;
4580 case "Path":
4581 path = this.core.GetAttributeValue(sourceLineNumbers, attrib);
4582 break;
4583 default:
4584 this.core.UnexpectedAttribute(node, attrib);
4585 break;
4586 }
4587 }
4588 else
4589 {
4590 this.core.ParseExtensionAttribute(node, attrib);
4591 }
4592 }
4593
4594 if (null != parent)
4595 {
4596 if (!String.IsNullOrEmpty(parentSignature))
4597 {
4598 this.core.OnMessage(WixErrors.CanNotHaveTwoParents(sourceLineNumbers, id.Id, parent.Id, parentSignature));
4599 }
4600 else
4601 {
4602 parentSignature = parent.Id;
4603 }
4604 }
4605
4606 if (null == id)
4607 {
4608 id = this.core.CreateIdentifier("dsr", parentSignature, path);
4609 }
4610
4611 signature = id.Id;
4612
4613 bool oneChild = false;
4614 foreach (XElement child in node.Elements())
4615 {
4616 if (CompilerCore.WixNamespace == child.Name.Namespace)
4617 {
4618 SourceLineNumber childSourceLineNumbers = Preprocessor.GetSourceLineNumbers(child);
4619 switch (child.Name.LocalName)
4620 {
4621 case "DirectorySearch":
4622 if (oneChild)
4623 {
4624 this.core.OnMessage(WixErrors.TooManySearchElements(childSourceLineNumbers, node.Name.LocalName));
4625 }
4626 oneChild = true;
4627 signature = this.ParseDirectorySearchElement(child, id.Id);
4628 break;
4629 case "DirectorySearchRef":
4630 if (oneChild)
4631 {
4632 this.core.OnMessage(WixErrors.TooManySearchElements(childSourceLineNumbers, node.Name.LocalName));
4633 }
4634 oneChild = true;
4635 signature = this.ParseDirectorySearchRefElement(child, id.Id);
4636 break;
4637 case "FileSearch":
4638 if (oneChild)
4639 {
4640 this.core.OnMessage(WixErrors.TooManySearchElements(childSourceLineNumbers, node.Name.LocalName));
4641 }
4642 oneChild = true;
4643 signature = this.ParseFileSearchElement(child, id.Id, false, CompilerConstants.IntegerNotSet);
4644 break;
4645 case "FileSearchRef":
4646 if (oneChild)
4647 {
4648 this.core.OnMessage(WixErrors.TooManySearchElements(sourceLineNumbers, node.Name.LocalName));
4649 }
4650 oneChild = true;
4651 signature = this.ParseSimpleRefElement(child, "Signature");
4652 break;
4653 default:
4654 this.core.UnexpectedElement(node, child);
4655 break;
4656 }
4657 }
4658 else
4659 {
4660 this.core.ParseExtensionElement(node, child);
4661 }
4662 }
4663
4664
4665 this.core.CreateSimpleReference(sourceLineNumbers, "DrLocator", id.Id, parentSignature, path);
4666
4667 return signature;
4668 }
4669
4670 /// <summary>
4671 /// Parses a feature element.
4672 /// </summary>
4673 /// <param name="node">Element to parse.</param>
4674 /// <param name="parentType">The type of parent.</param>
4675 /// <param name="parentId">Optional identifer for parent feature.</param>
4676 /// <param name="lastDisplay">Display value for last feature used to get the features to display in the same order as specified
4677 /// in the source code.</param>
4678 [SuppressMessage("Microsoft.Performance", "CA1800:DoNotCastUnnecessarily")]
4679 private void ParseFeatureElement(XElement node, ComplexReferenceParentType parentType, string parentId, ref int lastDisplay)
4680 {
4681 SourceLineNumber sourceLineNumbers = Preprocessor.GetSourceLineNumbers(node);
4682 Identifier id = null;
4683 string allowAdvertise = null;
4684 int bits = 0;
4685 string configurableDirectory = null;
4686 string description = null;
4687 string display = "collapse";
4688 YesNoType followParent = YesNoType.NotSet;
4689 string installDefault = null;
4690 int level = 1;
4691 string title = null;
4692 string typicalDefault = null;
4693
4694 foreach (XAttribute attrib in node.Attributes())
4695 {
4696 if (String.IsNullOrEmpty(attrib.Name.NamespaceName) || CompilerCore.WixNamespace == attrib.Name.Namespace)
4697 {
4698 switch (attrib.Name.LocalName)
4699 {
4700 case "Id":
4701 id = this.core.GetAttributeIdentifier(sourceLineNumbers, attrib);
4702 break;
4703 case "Absent":
4704 string absent = this.core.GetAttributeValue(sourceLineNumbers, attrib);
4705 if (0 < absent.Length)
4706 {
4707 Wix.Feature.AbsentType absentType = Wix.Feature.ParseAbsentType(absent);
4708 switch (absentType)
4709 {
4710 case Wix.Feature.AbsentType.allow: // this is the default
4711 break;
4712 case Wix.Feature.AbsentType.disallow:
4713 bits = bits | MsiInterop.MsidbFeatureAttributesUIDisallowAbsent;
4714 break;
4715 default:
4716 this.core.OnMessage(WixErrors.IllegalAttributeValue(sourceLineNumbers, node.Name.LocalName, attrib.Name.LocalName, absent, "allow", "disallow"));
4717 break;
4718 }
4719 }
4720 break;
4721 case "AllowAdvertise":
4722 allowAdvertise = this.core.GetAttributeValue(sourceLineNumbers, attrib);
4723 if (0 < allowAdvertise.Length)
4724 {
4725 Wix.Feature.AllowAdvertiseType allowAdvertiseType = Wix.Feature.ParseAllowAdvertiseType(allowAdvertise);
4726 switch (allowAdvertiseType)
4727 {
4728 case Wix.Feature.AllowAdvertiseType.no:
4729 bits |= MsiInterop.MsidbFeatureAttributesDisallowAdvertise;
4730 break;
4731 case Wix.Feature.AllowAdvertiseType.system:
4732 bits |= MsiInterop.MsidbFeatureAttributesNoUnsupportedAdvertise;
4733 break;
4734 case Wix.Feature.AllowAdvertiseType.yes: // this is the default
4735 break;
4736 default:
4737 this.core.OnMessage(WixErrors.IllegalAttributeValue(sourceLineNumbers, node.Name.LocalName, attrib.Name.LocalName, allowAdvertise, "no", "system", "yes"));
4738 break;
4739 }
4740 }
4741 break;
4742 case "ConfigurableDirectory":
4743 configurableDirectory = this.core.CreateDirectoryReferenceFromInlineSyntax(sourceLineNumbers, attrib, null);
4744 break;
4745 case "Description":
4746 description = this.core.GetAttributeValue(sourceLineNumbers, attrib);
4747 break;
4748 case "Display":
4749 display = this.core.GetAttributeValue(sourceLineNumbers, attrib);
4750 break;
4751 case "InstallDefault":
4752 installDefault = this.core.GetAttributeValue(sourceLineNumbers, attrib);
4753 if (0 < installDefault.Length)
4754 {
4755 Wix.Feature.InstallDefaultType installDefaultType = Wix.Feature.ParseInstallDefaultType(installDefault);
4756 switch (installDefaultType)
4757 {
4758 case Wix.Feature.InstallDefaultType.followParent:
4759 if (ComplexReferenceParentType.Product == parentType)
4760 {
4761 this.core.OnMessage(WixErrors.RootFeatureCannotFollowParent(sourceLineNumbers));
4762 }
4763 bits = bits | MsiInterop.MsidbFeatureAttributesFollowParent;
4764 break;
4765 case Wix.Feature.InstallDefaultType.local: // this is the default
4766 break;
4767 case Wix.Feature.InstallDefaultType.source:
4768 bits = bits | MsiInterop.MsidbFeatureAttributesFavorSource;
4769 break;
4770 default:
4771 this.core.OnMessage(WixErrors.IllegalAttributeValue(sourceLineNumbers, node.Name.LocalName, attrib.Name.LocalName, installDefault, "followParent", "local", "source"));
4772 break;
4773 }
4774 }
4775 break;
4776 case "Level":
4777 level = this.core.GetAttributeIntegerValue(sourceLineNumbers, attrib, 0, short.MaxValue);
4778 break;
4779 case "Title":
4780 title = this.core.GetAttributeValue(sourceLineNumbers, attrib);
4781 if ("PUT-FEATURE-TITLE-HERE" == title)
4782 {
4783 this.core.OnMessage(WixWarnings.PlaceholderValue(sourceLineNumbers, node.Name.LocalName, attrib.Name.LocalName, title));
4784 }
4785 break;
4786 case "TypicalDefault":
4787 typicalDefault = this.core.GetAttributeValue(sourceLineNumbers, attrib);
4788 if (0 < typicalDefault.Length)
4789 {
4790 Wix.Feature.TypicalDefaultType typicalDefaultType = Wix.Feature.ParseTypicalDefaultType(typicalDefault);
4791 switch (typicalDefaultType)
4792 {
4793 case Wix.Feature.TypicalDefaultType.advertise:
4794 bits = bits | MsiInterop.MsidbFeatureAttributesFavorAdvertise;
4795 break;
4796 case Wix.Feature.TypicalDefaultType.install: // this is the default
4797 break;
4798 default:
4799 this.core.OnMessage(WixErrors.IllegalAttributeValue(sourceLineNumbers, node.Name.LocalName, attrib.Name.LocalName, typicalDefault, "advertise", "install"));
4800 break;
4801 }
4802 }
4803 break;
4804 default:
4805 this.core.UnexpectedAttribute(node, attrib);
4806 break;
4807 }
4808 }
4809 else
4810 {
4811 this.core.ParseExtensionAttribute(node, attrib);
4812 }
4813 }
4814
4815 if (null == id)
4816 {
4817 this.core.OnMessage(WixErrors.ExpectedAttribute(sourceLineNumbers, node.Name.LocalName, "Id"));
4818 id = Identifier.Invalid;
4819 }
4820 else if (38 < id.Id.Length)
4821 {
4822 this.core.OnMessage(WixErrors.FeatureNameTooLong(sourceLineNumbers, node.Name.LocalName, "Id", id.Id));
4823 }
4824
4825 if (null != configurableDirectory && configurableDirectory.ToUpper(CultureInfo.InvariantCulture) != configurableDirectory)
4826 {
4827 this.core.OnMessage(WixErrors.FeatureConfigurableDirectoryNotUppercase(sourceLineNumbers, node.Name.LocalName, "ConfigurableDirectory", configurableDirectory));
4828 }
4829
4830 if ("advertise" == typicalDefault && "no" == allowAdvertise)
4831 {
4832 this.core.OnMessage(WixErrors.FeatureCannotFavorAndDisallowAdvertise(sourceLineNumbers, node.Name.LocalName, "TypicalDefault", typicalDefault, "AllowAdvertise", allowAdvertise));
4833 }
4834
4835 if (YesNoType.Yes == followParent && ("local" == installDefault || "source" == installDefault))
4836 {
4837 this.core.OnMessage(WixErrors.FeatureCannotFollowParentAndFavorLocalOrSource(sourceLineNumbers, node.Name.LocalName, "InstallDefault", "FollowParent", "yes"));
4838 }
4839
4840 int childDisplay = 0;
4841 foreach (XElement child in node.Elements())
4842 {
4843 if (CompilerCore.WixNamespace == child.Name.Namespace)
4844 {
4845 switch (child.Name.LocalName)
4846 {
4847 case "ComponentGroupRef":
4848 this.ParseComponentGroupRefElement(child, ComplexReferenceParentType.Feature, id.Id, null);
4849 break;
4850 case "ComponentRef":
4851 this.ParseComponentRefElement(child, ComplexReferenceParentType.Feature, id.Id, null);
4852 break;
4853 case "Component":
4854 this.ParseComponentElement(child, ComplexReferenceParentType.Feature, id.Id, null, CompilerConstants.IntegerNotSet, null, null);
4855 break;
4856 case "Condition":
4857 this.ParseConditionElement(child, node.Name.LocalName, id.Id, null);
4858 break;
4859 case "Feature":
4860 this.ParseFeatureElement(child, ComplexReferenceParentType.Feature, id.Id, ref childDisplay);
4861 break;
4862 case "FeatureGroupRef":
4863 this.ParseFeatureGroupRefElement(child, ComplexReferenceParentType.Feature, id.Id);
4864 break;
4865 case "FeatureRef":
4866 this.ParseFeatureRefElement(child, ComplexReferenceParentType.Feature, id.Id);
4867 break;
4868 case "MergeRef":
4869 this.ParseMergeRefElement(child, ComplexReferenceParentType.Feature, id.Id);
4870 break;
4871 default:
4872 this.core.UnexpectedElement(node, child);
4873 break;
4874 }
4875 }
4876 else
4877 {
4878 this.core.ParseExtensionElement(node, child);
4879 }
4880 }
4881
4882 if (!this.core.EncounteredError)
4883 {
4884 Row row = this.core.CreateRow(sourceLineNumbers, "Feature", id);
4885 row[1] = null; // this column is set in the linker
4886 row[2] = title;
4887 row[3] = description;
4888 if (0 < display.Length)
4889 {
4890 switch (display)
4891 {
4892 case "collapse":
4893 lastDisplay = (lastDisplay | 1) + 1;
4894 row[4] = lastDisplay;
4895 break;
4896 case "expand":
4897 lastDisplay = (lastDisplay + 1) | 1;
4898 row[4] = lastDisplay;
4899 break;
4900 case "hidden":
4901 row[4] = 0;
4902 break;
4903 default:
4904 int value;
4905 if (!Int32.TryParse(display, NumberStyles.Integer, CultureInfo.InvariantCulture, out value))
4906 {
4907 this.core.OnMessage(WixErrors.IllegalAttributeValue(sourceLineNumbers, node.Name.LocalName, "Display", display, "collapse", "expand", "hidden"));
4908 }
4909 else
4910 {
4911 row[4] = value;
4912 // save the display value of this row (if its not hidden) for subsequent rows
4913 if (0 != (int)row[4])
4914 {
4915 lastDisplay = (int)row[4];
4916 }
4917 }
4918 break;
4919 }
4920 }
4921 row[5] = level;
4922 row[6] = configurableDirectory;
4923 row[7] = bits;
4924
4925 if (ComplexReferenceParentType.Unknown != parentType)
4926 {
4927 this.core.CreateComplexReference(sourceLineNumbers, parentType, parentId, null, ComplexReferenceChildType.Feature, id.Id, false);
4928 }
4929 }
4930 }
4931
4932 /// <summary>
4933 /// Parses a feature reference element.
4934 /// </summary>
4935 /// <param name="node">Element to parse.</param>
4936 /// <param name="parentType">The type of parent.</param>
4937 /// <param name="parentId">Optional identifier for parent feature.</param>
4938 [System.Diagnostics.CodeAnalysis.SuppressMessage("Microsoft.Performance", "CA1800:DoNotCastUnnecessarily")]
4939 private void ParseFeatureRefElement(XElement node, ComplexReferenceParentType parentType, string parentId)
4940 {
4941 SourceLineNumber sourceLineNumbers = Preprocessor.GetSourceLineNumbers(node);
4942 string id = null;
4943 YesNoType ignoreParent = YesNoType.NotSet;
4944
4945 foreach (XAttribute attrib in node.Attributes())
4946 {
4947 if (String.IsNullOrEmpty(attrib.Name.NamespaceName) || CompilerCore.WixNamespace == attrib.Name.Namespace)
4948 {
4949 switch (attrib.Name.LocalName)
4950 {
4951 case "Id":
4952 id = this.core.GetAttributeIdentifierValue(sourceLineNumbers, attrib);
4953 this.core.CreateSimpleReference(sourceLineNumbers, "Feature", id);
4954 break;
4955 case "IgnoreParent":
4956 ignoreParent = this.core.GetAttributeYesNoValue(sourceLineNumbers, attrib);
4957 break;
4958 default:
4959 this.core.UnexpectedAttribute(node, attrib);
4960 break;
4961 }
4962 }
4963 else
4964 {
4965 this.core.ParseExtensionAttribute(node, attrib);
4966 }
4967 }
4968
4969
4970 if (null == id)
4971 {
4972 this.core.OnMessage(WixErrors.ExpectedAttribute(sourceLineNumbers, node.Name.LocalName, "Id"));
4973 }
4974
4975 int lastDisplay = 0;
4976 foreach (XElement child in node.Elements())
4977 {
4978 if (CompilerCore.WixNamespace == child.Name.Namespace)
4979 {
4980 switch (child.Name.LocalName)
4981 {
4982 case "ComponentGroupRef":
4983 this.ParseComponentGroupRefElement(child, ComplexReferenceParentType.Feature, id, null);
4984 break;
4985 case "ComponentRef":
4986 this.ParseComponentRefElement(child, ComplexReferenceParentType.Feature, id, null);
4987 break;
4988 case "Component":
4989 this.ParseComponentElement(child, ComplexReferenceParentType.Feature, id, null, CompilerConstants.IntegerNotSet, null, null);
4990 break;
4991 case "Feature":
4992 this.ParseFeatureElement(child, ComplexReferenceParentType.Feature, id, ref lastDisplay);
4993 break;
4994 case "FeatureGroup":
4995 this.ParseFeatureGroupElement(child, ComplexReferenceParentType.Feature, id);
4996 break;
4997 case "FeatureGroupRef":
4998 this.ParseFeatureGroupRefElement(child, ComplexReferenceParentType.Feature, id);
4999 break;
5000 case "FeatureRef":
5001 this.ParseFeatureRefElement(child, ComplexReferenceParentType.Feature, id);
5002 break;
5003 case "MergeRef":
5004 this.ParseMergeRefElement(child, ComplexReferenceParentType.Feature, id);
5005 break;
5006 default:
5007 this.core.UnexpectedElement(node, child);
5008 break;
5009 }
5010 }
5011 else
5012 {
5013 this.core.ParseExtensionElement(node, child);
5014 }
5015 }
5016
5017 if (!this.core.EncounteredError)
5018 {
5019 if (ComplexReferenceParentType.Unknown != parentType && YesNoType.Yes != ignoreParent)
5020 {
5021 this.core.CreateComplexReference(sourceLineNumbers, parentType, parentId, null, ComplexReferenceChildType.Feature, id, false);
5022 }
5023 }
5024 }
5025
5026 /// <summary>
5027 /// Parses a feature group element.
5028 /// </summary>
5029 /// <param name="node">Element to parse.</param>
5030 [System.Diagnostics.CodeAnalysis.SuppressMessage("Microsoft.Performance", "CA1800:DoNotCastUnnecessarily")]
5031 private void ParseFeatureGroupElement(XElement node, ComplexReferenceParentType parentType, string parentId)
5032 {
5033 SourceLineNumber sourceLineNumbers = Preprocessor.GetSourceLineNumbers(node);
5034 Identifier id = null;
5035
5036 foreach (XAttribute attrib in node.Attributes())
5037 {
5038 if (String.IsNullOrEmpty(attrib.Name.NamespaceName) || CompilerCore.WixNamespace == attrib.Name.Namespace)
5039 {
5040 switch (attrib.Name.LocalName)
5041 {
5042 case "Id":
5043 id = this.core.GetAttributeIdentifier(sourceLineNumbers, attrib);
5044 break;
5045 default:
5046 this.core.UnexpectedAttribute(node, attrib);
5047 break;
5048 }
5049 }
5050 else
5051 {
5052 this.core.ParseExtensionAttribute(node, attrib);
5053 }
5054 }
5055
5056 if (null == id)
5057 {
5058 this.core.OnMessage(WixErrors.ExpectedAttribute(sourceLineNumbers, node.Name.LocalName, "Id"));
5059 id = Identifier.Invalid;
5060 }
5061
5062 int lastDisplay = 0;
5063 foreach (XElement child in node.Elements())
5064 {
5065 if (CompilerCore.WixNamespace == child.Name.Namespace)
5066 {
5067 switch (child.Name.LocalName)
5068 {
5069 case "ComponentGroupRef":
5070 this.ParseComponentGroupRefElement(child, ComplexReferenceParentType.FeatureGroup, id.Id, null);
5071 break;
5072 case "ComponentRef":
5073 this.ParseComponentRefElement(child, ComplexReferenceParentType.FeatureGroup, id.Id, null);
5074 break;
5075 case "Component":
5076 this.ParseComponentElement(child, ComplexReferenceParentType.FeatureGroup, id.Id, null, CompilerConstants.IntegerNotSet, null, null);
5077 break;
5078 case "Feature":
5079 this.ParseFeatureElement(child, ComplexReferenceParentType.FeatureGroup, id.Id, ref lastDisplay);
5080 break;
5081 case "FeatureGroupRef":
5082 this.ParseFeatureGroupRefElement(child, ComplexReferenceParentType.FeatureGroup, id.Id);
5083 break;
5084 case "FeatureRef":
5085 this.ParseFeatureRefElement(child, ComplexReferenceParentType.FeatureGroup, id.Id);
5086 break;
5087 case "MergeRef":
5088 this.ParseMergeRefElement(child, ComplexReferenceParentType.FeatureGroup, id.Id);
5089 break;
5090 default:
5091 this.core.UnexpectedElement(node, child);
5092 break;
5093 }
5094 }
5095 else
5096 {
5097 this.core.ParseExtensionElement(node, child);
5098 }
5099 }
5100
5101 if (!this.core.EncounteredError)
5102 {
5103 Row row = this.core.CreateRow(sourceLineNumbers, "WixFeatureGroup", id);
5104
5105 //Add this FeatureGroup and its parent in WixGroup.
5106 this.core.CreateWixGroupRow(sourceLineNumbers, parentType, parentId, ComplexReferenceChildType.FeatureGroup, id.Id);
5107 }
5108 }
5109
5110 /// <summary>
5111 /// Parses a feature group reference element.
5112 /// </summary>
5113 /// <param name="node">Element to parse.</param>
5114 /// <param name="parentType">The type of parent.</param>
5115 /// <param name="parentId">Identifier of parent element.</param>
5116 private void ParseFeatureGroupRefElement(XElement node, ComplexReferenceParentType parentType, string parentId)
5117 {
5118 Debug.Assert(ComplexReferenceParentType.Feature == parentType || ComplexReferenceParentType.FeatureGroup == parentType || ComplexReferenceParentType.ComponentGroup == parentType || ComplexReferenceParentType.Product == parentType);
5119
5120 SourceLineNumber sourceLineNumbers = Preprocessor.GetSourceLineNumbers(node);
5121 string id = null;
5122 YesNoType ignoreParent = YesNoType.NotSet;
5123 YesNoType primary = YesNoType.NotSet;
5124
5125 foreach (XAttribute attrib in node.Attributes())
5126 {
5127 if (String.IsNullOrEmpty(attrib.Name.NamespaceName) || CompilerCore.WixNamespace == attrib.Name.Namespace)
5128 {
5129 switch (attrib.Name.LocalName)
5130 {
5131 case "Id":
5132 id = this.core.GetAttributeValue(sourceLineNumbers, attrib);
5133 this.core.CreateSimpleReference(sourceLineNumbers, "WixFeatureGroup", id);
5134 break;
5135 case "IgnoreParent":
5136 ignoreParent = this.core.GetAttributeYesNoValue(sourceLineNumbers, attrib);
5137 break;
5138 case "Primary":
5139 primary = this.core.GetAttributeYesNoValue(sourceLineNumbers, attrib);
5140 break;
5141 default:
5142 this.core.UnexpectedAttribute(node, attrib);
5143 break;
5144 }
5145 }
5146 else
5147 {
5148 this.core.ParseExtensionAttribute(node, attrib);
5149 }
5150 }
5151
5152 if (null == id)
5153 {
5154 this.core.OnMessage(WixErrors.ExpectedAttribute(sourceLineNumbers, node.Name.LocalName, "Id"));
5155 }
5156
5157 this.core.ParseForExtensionElements(node);
5158
5159 if (!this.core.EncounteredError)
5160 {
5161 if (YesNoType.Yes != ignoreParent)
5162 {
5163 this.core.CreateComplexReference(sourceLineNumbers, parentType, parentId, null, ComplexReferenceChildType.FeatureGroup, id, (YesNoType.Yes == primary));
5164 }
5165 }
5166 }
5167
5168 /// <summary>
5169 /// Parses an environment element.
5170 /// </summary>
5171 /// <param name="node">Element to parse.</param>
5172 /// <param name="componentId">Identifier of parent component.</param>
5173 private void ParseEnvironmentElement(XElement node, string componentId)
5174 {
5175 SourceLineNumber sourceLineNumbers = Preprocessor.GetSourceLineNumbers(node);
5176 Identifier id = null;
5177 string action = null;
5178 string name = null;
5179 Wix.Environment.PartType partType = Wix.Environment.PartType.NotSet;
5180 string part = null;
5181 bool permanent = false;
5182 string separator = ";"; // default to ';'
5183 bool system = false;
5184 string text = null;
5185 string uninstall = "-"; // default to remove at uninstall
5186
5187 foreach (XAttribute attrib in node.Attributes())
5188 {
5189 if (String.IsNullOrEmpty(attrib.Name.NamespaceName) || CompilerCore.WixNamespace == attrib.Name.Namespace)
5190 {
5191 switch (attrib.Name.LocalName)
5192 {
5193 case "Id":
5194 id = this.core.GetAttributeIdentifier(sourceLineNumbers, attrib);
5195 break;
5196 case "Action":
5197 string value = this.core.GetAttributeValue(sourceLineNumbers, attrib);
5198 if (0 < value.Length)
5199 {
5200 Wix.Environment.ActionType actionType = Wix.Environment.ParseActionType(value);
5201 switch (actionType)
5202 {
5203 case Wix.Environment.ActionType.create:
5204 action = "+";
5205 break;
5206 case Wix.Environment.ActionType.set:
5207 action = "=";
5208 break;
5209 case Wix.Environment.ActionType.remove:
5210 action = "!";
5211 break;
5212 default:
5213 this.core.OnMessage(WixErrors.IllegalAttributeValue(sourceLineNumbers, node.Name.LocalName, attrib.Name.LocalName, value, "create", "set", "remove"));
5214 break;
5215 }
5216 }
5217 break;
5218 case "Name":
5219 name = this.core.GetAttributeValue(sourceLineNumbers, attrib);
5220 break;
5221 case "Part":
5222 part = this.core.GetAttributeValue(sourceLineNumbers, attrib);
5223 if (!Wix.Environment.TryParsePartType(part, out partType))
5224 {
5225 this.core.OnMessage(WixErrors.IllegalAttributeValue(sourceLineNumbers, node.Name.LocalName, "Part", part, "all", "first", "last"));
5226 }
5227 break;
5228 case "Permanent":
5229 permanent = YesNoType.Yes == this.core.GetAttributeYesNoValue(sourceLineNumbers, attrib);
5230 break;
5231 case "Separator":
5232 separator = this.core.GetAttributeValue(sourceLineNumbers, attrib);
5233 break;
5234 case "System":
5235 system = YesNoType.Yes == this.core.GetAttributeYesNoValue(sourceLineNumbers, attrib);
5236 break;
5237 case "Value":
5238 text = this.core.GetAttributeValue(sourceLineNumbers, attrib);
5239 break;
5240 default:
5241 this.core.UnexpectedAttribute(node, attrib);
5242 break;
5243 }
5244 }
5245 else
5246 {
5247 this.core.ParseExtensionAttribute(node, attrib);
5248 }
5249 }
5250
5251 if (null == id)
5252 {
5253 id = this.core.CreateIdentifier("env", action, name, part, system.ToString());
5254 }
5255
5256 if (null == name)
5257 {
5258 this.core.OnMessage(WixErrors.ExpectedAttribute(sourceLineNumbers, node.Name.LocalName, "Name"));
5259 }
5260
5261 if (Wix.Environment.PartType.NotSet != partType)
5262 {
5263 if ("+" == action)
5264 {
5265 this.core.OnMessage(WixErrors.IllegalAttributeWithOtherAttribute(sourceLineNumbers, node.Name.LocalName, "Part", "Action", "create"));
5266 }
5267
5268 switch (partType)
5269 {
5270 case Wix.Environment.PartType.all:
5271 break;
5272 case Wix.Environment.PartType.first:
5273 text = String.Concat(text, separator, "[~]");
5274 break;
5275 case Wix.Environment.PartType.last:
5276 text = String.Concat("[~]", separator, text);
5277 break;
5278 }
5279 }
5280
5281 if (permanent)
5282 {
5283 uninstall = null;
5284 }
5285
5286 this.core.ParseForExtensionElements(node);
5287
5288 if (!this.core.EncounteredError)
5289 {
5290 Row row = this.core.CreateRow(sourceLineNumbers, "Environment", id);
5291 row[1] = String.Concat(action, uninstall, system ? "*" : String.Empty, name);
5292 row[2] = text;
5293 row[3] = componentId;
5294 }
5295 }
5296
5297 /// <summary>
5298 /// Parses an error element.
5299 /// </summary>
5300 /// <param name="node">Element to parse.</param>
5301 private void ParseErrorElement(XElement node)
5302 {
5303 SourceLineNumber sourceLineNumbers = Preprocessor.GetSourceLineNumbers(node);
5304 int id = CompilerConstants.IntegerNotSet;
5305
5306 foreach (XAttribute attrib in node.Attributes())
5307 {
5308 if (String.IsNullOrEmpty(attrib.Name.NamespaceName) || CompilerCore.WixNamespace == attrib.Name.Namespace)
5309 {
5310 switch (attrib.Name.LocalName)
5311 {
5312 case "Id":
5313 id = this.core.GetAttributeIntegerValue(sourceLineNumbers, attrib, 0, short.MaxValue);
5314 break;
5315 default:
5316 this.core.UnexpectedAttribute(node, attrib);
5317 break;
5318 }
5319 }
5320 else
5321 {
5322 this.core.ParseExtensionAttribute(node, attrib);
5323 }
5324 }
5325
5326 if (CompilerConstants.IntegerNotSet == id)
5327 {
5328 this.core.OnMessage(WixErrors.ExpectedAttribute(sourceLineNumbers, node.Name.LocalName, "Id"));
5329 id = CompilerConstants.IllegalInteger;
5330 }
5331
5332 this.core.ParseForExtensionElements(node);
5333
5334 if (!this.core.EncounteredError)
5335 {
5336 Row row = this.core.CreateRow(sourceLineNumbers, "Error");
5337 row[0] = id;
5338 row[1] = Common.GetInnerText(node); // TODO: *
5339 }
5340 }
5341
5342 /// <summary>
5343 /// Parses an extension element.
5344 /// </summary>
5345 /// <param name="node">Element to parse.</param>
5346 /// <param name="componentId">Identifier of parent component.</param>
5347 /// <param name="advertise">Flag if this extension is advertised.</param>
5348 /// <param name="progId">ProgId for extension.</param>
5349 private void ParseExtensionElement(XElement node, string componentId, YesNoType advertise, string progId)
5350 {
5351 SourceLineNumber sourceLineNumbers = Preprocessor.GetSourceLineNumbers(node);
5352 string extension = null;
5353 string mime = null;
5354
5355 foreach (XAttribute attrib in node.Attributes())
5356 {
5357 if (String.IsNullOrEmpty(attrib.Name.NamespaceName) || CompilerCore.WixNamespace == attrib.Name.Namespace)
5358 {
5359 switch (attrib.Name.LocalName)
5360 {
5361 case "Id":
5362 extension = this.core.GetAttributeValue(sourceLineNumbers, attrib);
5363 break;
5364 case "Advertise":
5365 YesNoType extensionAdvertise = this.core.GetAttributeYesNoValue(sourceLineNumbers, attrib);
5366 if ((YesNoType.No == advertise && YesNoType.Yes == extensionAdvertise) || (YesNoType.Yes == advertise && YesNoType.No == extensionAdvertise))
5367 {
5368 this.core.OnMessage(WixErrors.AdvertiseStateMustMatch(sourceLineNumbers, extensionAdvertise.ToString(), advertise.ToString()));
5369 }
5370 advertise = extensionAdvertise;
5371 break;
5372 case "ContentType":
5373 mime = this.core.GetAttributeValue(sourceLineNumbers, attrib);
5374 break;
5375 default:
5376 this.core.UnexpectedAttribute(node, attrib);
5377 break;
5378 }
5379 }
5380 else
5381 {
5382 Dictionary<string, string> context = new Dictionary<string, string>() { { "ProgId", progId }, { "ComponentId", componentId } };
5383 this.core.ParseExtensionAttribute(node, attrib, context);
5384 }
5385 }
5386
5387 if (YesNoType.NotSet == advertise)
5388 {
5389 advertise = YesNoType.No;
5390 }
5391
5392 foreach (XElement child in node.Elements())
5393 {
5394 if (CompilerCore.WixNamespace == child.Name.Namespace)
5395 {
5396 switch (child.Name.LocalName)
5397 {
5398 case "Verb":
5399 this.ParseVerbElement(child, extension, progId, componentId, advertise);
5400 break;
5401 case "MIME":
5402 string newMime = this.ParseMIMEElement(child, extension, componentId, advertise);
5403 if (null != newMime && null == mime)
5404 {
5405 mime = newMime;
5406 }
5407 break;
5408 default:
5409 this.core.UnexpectedElement(node, child);
5410 break;
5411 }
5412 }
5413 else
5414 {
5415 this.core.ParseExtensionElement(node, child);
5416 }
5417 }
5418
5419
5420 if (YesNoType.Yes == advertise)
5421 {
5422 if (!this.core.EncounteredError)
5423 {
5424 Row row = this.core.CreateRow(sourceLineNumbers, "Extension");
5425 row[0] = extension;
5426 row[1] = componentId;
5427 row[2] = progId;
5428 row[3] = mime;
5429 row[4] = Guid.Empty.ToString("B");
5430
5431 this.core.EnsureTable(sourceLineNumbers, "Verb");
5432 }
5433 }
5434 else if (YesNoType.No == advertise)
5435 {
5436 this.core.CreateRegistryRow(sourceLineNumbers, MsiInterop.MsidbRegistryRootClassesRoot, String.Concat(".", extension), String.Empty, progId, componentId); // Extension
5437 if (null != mime)
5438 {
5439 this.core.CreateRegistryRow(sourceLineNumbers, MsiInterop.MsidbRegistryRootClassesRoot, String.Concat(".", extension), "Content Type", mime, componentId); // Extension's MIME ContentType
5440 }
5441 }
5442 }
5443
5444
5445 /// <summary>
5446 /// Parses a file element.
5447 /// </summary>
5448 /// <param name="node">File element to parse.</param>
5449 /// <param name="componentId">Parent's component id.</param>
5450 /// <param name="directoryId">Ancestor's directory id.</param>
5451 /// <param name="diskId">Disk id inherited from parent component.</param>
5452 /// <param name="sourcePath">Default source path of parent directory.</param>
5453 /// <param name="possibleKeyPath">This will be set with the possible keyPath for the parent component.</param>
5454 /// <param name="win64Component">true if the component is 64-bit.</param>
5455 /// <returns>Yes if this element was marked as the parent component's key path, No if explicitly marked as not being a key path, or NotSet otherwise.</returns>
5456 [System.Diagnostics.CodeAnalysis.SuppressMessage("Microsoft.Performance", "CA1800:DoNotCastUnnecessarily")]
5457 private YesNoType ParseFileElement(XElement node, string componentId, string directoryId, int diskId, string sourcePath, out string possibleKeyPath, bool win64Component, string componentGuid)
5458 {
5459 SourceLineNumber sourceLineNumbers = Preprocessor.GetSourceLineNumbers(node);
5460 Identifier id = null;
5461 FileAssemblyType assemblyType = FileAssemblyType.NotAnAssembly;
5462 string assemblyApplication = null;
5463 string assemblyManifest = null;
5464 string bindPath = null;
5465 int bits = MsiInterop.MsidbFileAttributesVital; // assume all files are vital.
5466 string companionFile = null;
5467 string defaultLanguage = null;
5468 int defaultSize = 0;
5469 string defaultVersion = null;
5470 string fontTitle = null;
5471 bool generatedShortFileName = false;
5472 YesNoType keyPath = YesNoType.NotSet;
5473 string name = null;
5474 int patchGroup = CompilerConstants.IntegerNotSet;
5475 bool patchIgnore = false;
5476 bool patchIncludeWholeFile = false;
5477 bool patchAllowIgnoreOnError = false;
5478
5479 string ignoreLengths = null;
5480 string ignoreOffsets = null;
5481 string protectLengths = null;
5482 string protectOffsets = null;
5483 string symbols = null;
5484
5485 string procArch = null;
5486 int selfRegCost = CompilerConstants.IntegerNotSet;
5487 string shortName = null;
5488 string source = sourcePath; // assume we'll use the parents as the source for this file
5489 bool sourceSet = false;
5490
5491 foreach (XAttribute attrib in node.Attributes())
5492 {
5493 if (String.IsNullOrEmpty(attrib.Name.NamespaceName) || CompilerCore.WixNamespace == attrib.Name.Namespace)
5494 {
5495 switch (attrib.Name.LocalName)
5496 {
5497 case "Id":
5498 id = this.core.GetAttributeIdentifier(sourceLineNumbers, attrib);
5499 break;
5500 case "Assembly":
5501 string assemblyValue = this.core.GetAttributeValue(sourceLineNumbers, attrib);
5502 if (0 < assemblyValue.Length)
5503 {
5504 Wix.File.AssemblyType parsedAssemblyType = Wix.File.ParseAssemblyType(assemblyValue);
5505 switch (parsedAssemblyType)
5506 {
5507 case Wix.File.AssemblyType.net:
5508 assemblyType = FileAssemblyType.DotNetAssembly;
5509 break;
5510 case Wix.File.AssemblyType.no:
5511 assemblyType = FileAssemblyType.NotAnAssembly;
5512 break;
5513 case Wix.File.AssemblyType.win32:
5514 assemblyType = FileAssemblyType.Win32Assembly;
5515 break;
5516 default:
5517 this.core.OnMessage(WixErrors.IllegalAttributeValue(sourceLineNumbers, "File", "Assembly", assemblyValue, "no", "win32", ".net"));
5518 break;
5519 }
5520 }
5521 break;
5522 case "AssemblyApplication":
5523 assemblyApplication = this.core.GetAttributeIdentifierValue(sourceLineNumbers, attrib);
5524 this.core.CreateSimpleReference(sourceLineNumbers, "File", assemblyApplication);
5525 break;
5526 case "AssemblyManifest":
5527 assemblyManifest = this.core.GetAttributeIdentifierValue(sourceLineNumbers, attrib);
5528 this.core.CreateSimpleReference(sourceLineNumbers, "File", assemblyManifest);
5529 break;
5530 case "BindPath":
5531 bindPath = this.core.GetAttributeValue(sourceLineNumbers, attrib, EmptyRule.CanBeEmpty);
5532 break;
5533 case "Checksum":
5534 if (YesNoType.Yes == this.core.GetAttributeYesNoValue(sourceLineNumbers, attrib))
5535 {
5536 bits |= MsiInterop.MsidbFileAttributesChecksum;
5537 }
5538 break;
5539 case "CompanionFile":
5540 companionFile = this.core.GetAttributeIdentifierValue(sourceLineNumbers, attrib);
5541 this.core.CreateSimpleReference(sourceLineNumbers, "File", companionFile);
5542 break;
5543 case "Compressed":
5544 YesNoDefaultType compressed = this.core.GetAttributeYesNoDefaultValue(sourceLineNumbers, attrib);
5545 if (YesNoDefaultType.Yes == compressed)
5546 {
5547 bits |= MsiInterop.MsidbFileAttributesCompressed;
5548 }
5549 else if (YesNoDefaultType.No == compressed)
5550 {
5551 bits |= MsiInterop.MsidbFileAttributesNoncompressed;
5552 }
5553 break;
5554 case "DefaultLanguage":
5555 defaultLanguage = this.core.GetAttributeValue(sourceLineNumbers, attrib);
5556 break;
5557 case "DefaultSize":
5558 defaultSize = this.core.GetAttributeIntegerValue(sourceLineNumbers, attrib, 0, int.MaxValue);
5559 break;
5560 case "DefaultVersion":
5561 defaultVersion = this.core.GetAttributeValue(sourceLineNumbers, attrib);
5562 break;
5563 case "DiskId":
5564 diskId = this.core.GetAttributeIntegerValue(sourceLineNumbers, attrib, 1, short.MaxValue);
5565 break;
5566 case "FontTitle":
5567 fontTitle = this.core.GetAttributeValue(sourceLineNumbers, attrib);
5568 break;
5569 case "Hidden":
5570 if (YesNoType.Yes == this.core.GetAttributeYesNoValue(sourceLineNumbers, attrib))
5571 {
5572 bits |= MsiInterop.MsidbFileAttributesHidden;
5573 }
5574 break;
5575 case "KeyPath":
5576 keyPath = this.core.GetAttributeYesNoValue(sourceLineNumbers, attrib);
5577 break;
5578 case "Name":
5579 name = this.core.GetAttributeLongFilename(sourceLineNumbers, attrib, false);
5580 break;
5581 case "PatchGroup":
5582 patchGroup = this.core.GetAttributeIntegerValue(sourceLineNumbers, attrib, 1, int.MaxValue);
5583 break;
5584 case "PatchIgnore":
5585 patchIgnore = YesNoType.Yes == this.core.GetAttributeYesNoValue(sourceLineNumbers, attrib);
5586 break;
5587 case "PatchWholeFile":
5588 patchIncludeWholeFile = YesNoType.Yes == this.core.GetAttributeYesNoValue(sourceLineNumbers, attrib);
5589 break;
5590 case "PatchAllowIgnoreOnError":
5591 patchAllowIgnoreOnError = YesNoType.Yes == this.core.GetAttributeYesNoValue(sourceLineNumbers, attrib);
5592 break;
5593 case "ProcessorArchitecture":
5594 string procArchValue = this.core.GetAttributeValue(sourceLineNumbers, attrib);
5595 if (0 < procArchValue.Length)
5596 {
5597 Wix.File.ProcessorArchitectureType procArchType = Wix.File.ParseProcessorArchitectureType(procArchValue);
5598 switch (procArchType)
5599 {
5600 case Wix.File.ProcessorArchitectureType.msil:
5601 procArch = "MSIL";
5602 break;
5603 case Wix.File.ProcessorArchitectureType.x86:
5604 procArch = "x86";
5605 break;
5606 case Wix.File.ProcessorArchitectureType.x64:
5607 procArch = "amd64";
5608 break;
5609 case Wix.File.ProcessorArchitectureType.ia64:
5610 procArch = "ia64";
5611 break;
5612 default:
5613 this.core.OnMessage(WixErrors.IllegalAttributeValue(sourceLineNumbers, "File", "ProcessorArchitecture", procArchValue, "msil", "x86", "x64", "ia64"));
5614 break;
5615 }
5616 }
5617 break;
5618 case "ReadOnly":
5619 if (YesNoType.Yes == this.core.GetAttributeYesNoValue(sourceLineNumbers, attrib))
5620 {
5621 bits |= MsiInterop.MsidbFileAttributesReadOnly;
5622 }
5623 break;
5624 case "SelfRegCost":
5625 selfRegCost = this.core.GetAttributeIntegerValue(sourceLineNumbers, attrib, 0, short.MaxValue);
5626 break;
5627 case "ShortName":
5628 shortName = this.core.GetAttributeShortFilename(sourceLineNumbers, attrib, false);
5629 break;
5630 case "Source":
5631 source = this.core.GetAttributeValue(sourceLineNumbers, attrib);
5632 sourceSet = true;
5633 break;
5634 case "System":
5635 if (YesNoType.Yes == this.core.GetAttributeYesNoValue(sourceLineNumbers, attrib))
5636 {
5637 bits |= MsiInterop.MsidbFileAttributesSystem;
5638 }
5639 break;
5640 case "TrueType":
5641 if (YesNoType.Yes == this.core.GetAttributeYesNoValue(sourceLineNumbers, attrib))
5642 {
5643 fontTitle = String.Empty;
5644 }
5645 break;
5646 case "Vital":
5647 YesNoType isVital = this.core.GetAttributeYesNoValue(sourceLineNumbers, attrib);
5648 if (YesNoType.Yes == isVital)
5649 {
5650 bits |= MsiInterop.MsidbFileAttributesVital;
5651 }
5652 else if (YesNoType.No == isVital)
5653 {
5654 bits &= ~MsiInterop.MsidbFileAttributesVital;
5655 }
5656 break;
5657 default:
5658 this.core.UnexpectedAttribute(node, attrib);
5659 break;
5660 }
5661 }
5662 else
5663 {
5664 this.core.ParseExtensionAttribute(node, attrib);
5665 }
5666 }
5667
5668 if (null != companionFile)
5669 {
5670 // the companion file cannot be the key path of a component
5671 if (YesNoType.Yes == keyPath)
5672 {
5673 this.core.OnMessage(WixErrors.IllegalAttributeWithOtherAttribute(sourceLineNumbers, node.Name.LocalName, "CompanionFile", "KeyPath", "yes"));
5674 }
5675 }
5676
5677 if (sourceSet && !source.EndsWith(Path.DirectorySeparatorChar.ToString(), StringComparison.Ordinal) && null == name)
5678 {
5679 name = Path.GetFileName(source);
5680 if (!this.core.IsValidLongFilename(name, false))
5681 {
5682 this.core.OnMessage(WixErrors.IllegalLongFilename(sourceLineNumbers, node.Name.LocalName, "Source", name));
5683 }
5684 }
5685
5686 // generate a short file name
5687 if (null == shortName && (null != name && !this.core.IsValidShortFilename(name, false)))
5688 {
5689 shortName = this.core.CreateShortName(name, true, false, node.Name.LocalName, directoryId);
5690 generatedShortFileName = true;
5691 }
5692
5693 if (null == id)
5694 {
5695 id = this.core.CreateIdentifier("fil", directoryId, name ?? shortName);
5696 }
5697
5698 if (!this.compilingModule && CompilerConstants.IntegerNotSet == diskId)
5699 {
5700 diskId = 1; // default to first Media
5701 }
5702
5703 if (null != defaultVersion && null != companionFile)
5704 {
5705 this.core.OnMessage(WixErrors.IllegalAttributeWithOtherAttribute(sourceLineNumbers, node.Name.LocalName, "DefaultVersion", "CompanionFile", companionFile));
5706 }
5707
5708 if (FileAssemblyType.NotAnAssembly == assemblyType)
5709 {
5710 if (null != assemblyManifest)
5711 {
5712 this.core.OnMessage(WixErrors.ExpectedAttribute(sourceLineNumbers, node.Name.LocalName, "Assembly", "AssemblyManifest"));
5713 }
5714
5715 if (null != assemblyApplication)
5716 {
5717 this.core.OnMessage(WixErrors.ExpectedAttribute(sourceLineNumbers, node.Name.LocalName, "Assembly", "AssemblyApplication"));
5718 }
5719 }
5720 else
5721 {
5722 if (FileAssemblyType.Win32Assembly == assemblyType && null == assemblyManifest)
5723 {
5724 this.core.OnMessage(WixErrors.ExpectedAttribute(sourceLineNumbers, node.Name.LocalName, "AssemblyManifest", "Assembly", "win32"));
5725 }
5726
5727 // allow "*" guid components to omit explicit KeyPath as they can have only one file and therefore this file is the keypath
5728 if (YesNoType.Yes != keyPath && "*" != componentGuid)
5729 {
5730 this.core.OnMessage(WixErrors.IllegalAttributeValueWithoutOtherAttribute(sourceLineNumbers, node.Name.LocalName, "Assembly", (FileAssemblyType.DotNetAssembly == assemblyType ? ".net" : "win32"), "KeyPath", "yes"));
5731 }
5732 }
5733
5734 foreach (XElement child in node.Elements())
5735 {
5736 if (CompilerCore.WixNamespace == child.Name.Namespace)
5737 {
5738 switch (child.Name.LocalName)
5739 {
5740 case "AppId":
5741 this.ParseAppIdElement(child, componentId, YesNoType.NotSet, id.Id, null, null);
5742 break;
5743 case "AssemblyName":
5744 this.ParseAssemblyName(child, componentId);
5745 break;
5746 case "Class":
5747 this.ParseClassElement(child, componentId, YesNoType.NotSet, id.Id, null, null, null);
5748 break;
5749 case "CopyFile":
5750 this.ParseCopyFileElement(child, componentId, id.Id);
5751 break;
5752 case "IgnoreRange":
5753 this.ParseRangeElement(child, ref ignoreOffsets, ref ignoreLengths);
5754 break;
5755 case "ODBCDriver":
5756 this.ParseODBCDriverOrTranslator(child, componentId, id.Id, this.tableDefinitions["ODBCDriver"]);
5757 break;
5758 case "ODBCTranslator":
5759 this.ParseODBCDriverOrTranslator(child, componentId, id.Id, this.tableDefinitions["ODBCTranslator"]);
5760 break;
5761 case "Permission":
5762 this.ParsePermissionElement(child, id.Id, "File");
5763 break;
5764 case "PermissionEx":
5765 this.ParsePermissionExElement(child, id.Id, "File");
5766 break;
5767 case "ProtectRange":
5768 this.ParseRangeElement(child, ref protectOffsets, ref protectLengths);
5769 break;
5770 case "Shortcut":
5771 this.ParseShortcutElement(child, componentId, node.Name.LocalName, id.Id, keyPath);
5772 break;
5773 case "SymbolPath":
5774 if (null != symbols)
5775 {
5776 symbols += ";" + this.ParseSymbolPathElement(child);
5777 }
5778 else
5779 {
5780 symbols = this.ParseSymbolPathElement(child);
5781 }
5782 break;
5783 case "TypeLib":
5784 this.ParseTypeLibElement(child, componentId, id.Id, win64Component);
5785 break;
5786 default:
5787 this.core.UnexpectedElement(node, child);
5788 break;
5789 }
5790 }
5791 else
5792 {
5793 Dictionary<string, string> context = new Dictionary<string, string>() { { "FileId", id.Id }, { "ComponentId", componentId }, { "Win64", win64Component.ToString() } };
5794 this.core.ParseExtensionElement(node, child, context);
5795 }
5796 }
5797
5798
5799 if (!this.core.EncounteredError)
5800 {
5801 PatchAttributeType patchAttributes = PatchAttributeType.None;
5802 if (patchIgnore)
5803 {
5804 patchAttributes |= PatchAttributeType.Ignore;
5805 }
5806 if (patchIncludeWholeFile)
5807 {
5808 patchAttributes |= PatchAttributeType.IncludeWholeFile;
5809 }
5810 if (patchAllowIgnoreOnError)
5811 {
5812 patchAttributes |= PatchAttributeType.AllowIgnoreOnError;
5813 }
5814
5815 if (String.IsNullOrEmpty(source))
5816 {
5817 if (!this.useShortFileNames && null != name)
5818 {
5819 source = name;
5820 }
5821 else
5822 {
5823 source = shortName;
5824 }
5825 }
5826 else if (source.EndsWith(Path.DirectorySeparatorChar.ToString(), StringComparison.Ordinal)) // if source relies on parent directories, append the file name
5827 {
5828 if (!this.useShortFileNames && null != name)
5829 {
5830 source = Path.Combine(source, name);
5831 }
5832 else
5833 {
5834 source = Path.Combine(source, shortName);
5835 }
5836 }
5837
5838 FileRow fileRow = (FileRow)this.core.CreateRow(sourceLineNumbers, "File", id);
5839 fileRow[1] = componentId;
5840 fileRow[2] = GetMsiFilenameValue(shortName, name);
5841 fileRow[3] = defaultSize;
5842 if (null != companionFile)
5843 {
5844 fileRow[4] = companionFile;
5845 }
5846 else if (null != defaultVersion)
5847 {
5848 fileRow[4] = defaultVersion;
5849 }
5850 fileRow[5] = defaultLanguage;
5851 fileRow[6] = bits;
5852
5853 // the Sequence row is set in the binder
5854
5855 WixFileRow wixFileRow = (WixFileRow)this.core.CreateRow(sourceLineNumbers, "WixFile", id);
5856 wixFileRow.AssemblyType = assemblyType;
5857 wixFileRow.AssemblyManifest = assemblyManifest;
5858 wixFileRow.AssemblyApplication = assemblyApplication;
5859 wixFileRow.Directory = directoryId;
5860 wixFileRow.DiskId = (CompilerConstants.IntegerNotSet == diskId) ? 0 : diskId;
5861 wixFileRow.Source = source;
5862 wixFileRow.ProcessorArchitecture = procArch;
5863 wixFileRow.PatchGroup = (CompilerConstants.IntegerNotSet != patchGroup ? patchGroup : -1);
5864 wixFileRow.Attributes = (generatedShortFileName ? 0x1 : 0x0);
5865 wixFileRow.PatchAttributes = patchAttributes;
5866
5867 // Always create a delta patch row for this file since other elements (like Component and Media) may
5868 // want to add symbol paths to it.
5869 WixDeltaPatchFileRow deltaPatchFileRow = (WixDeltaPatchFileRow)this.core.CreateRow(sourceLineNumbers, "WixDeltaPatchFile", id);
5870 deltaPatchFileRow.RetainLengths = protectLengths;
5871 deltaPatchFileRow.IgnoreOffsets = ignoreOffsets;
5872 deltaPatchFileRow.IgnoreLengths = ignoreLengths;
5873 deltaPatchFileRow.RetainOffsets = protectOffsets;
5874
5875 if (null != symbols)
5876 {
5877 WixDeltaPatchSymbolPathsRow symbolRow = (WixDeltaPatchSymbolPathsRow)this.core.CreateRow(sourceLineNumbers, "WixDeltaPatchSymbolPaths", id);
5878 symbolRow.Type = SymbolPathType.File;
5879 symbolRow.SymbolPaths = symbols;
5880 }
5881
5882 if (FileAssemblyType.NotAnAssembly != assemblyType)
5883 {
5884 Row row = this.core.CreateRow(sourceLineNumbers, "MsiAssembly");
5885 row[0] = componentId;
5886 row[1] = Guid.Empty.ToString("B");
5887 row[2] = assemblyManifest;
5888 row[3] = assemblyApplication;
5889 row[4] = (FileAssemblyType.DotNetAssembly == assemblyType) ? 0 : 1;
5890 }
5891
5892 if (null != bindPath)
5893 {
5894 Row row = this.core.CreateRow(sourceLineNumbers, "BindImage");
5895 row[0] = id.Id;
5896 row[1] = bindPath;
5897
5898 // TODO: technically speaking each of the properties in the "bindPath" should be added as references, but how much do we really care about BindImage?
5899 }
5900
5901 if (CompilerConstants.IntegerNotSet != selfRegCost)
5902 {
5903 Row row = this.core.CreateRow(sourceLineNumbers, "SelfReg");
5904 row[0] = id.Id;
5905 row[1] = selfRegCost;
5906 }
5907
5908 if (null != fontTitle)
5909 {
5910 Row row = this.core.CreateRow(sourceLineNumbers, "Font");
5911 row[0] = id.Id;
5912 row[1] = fontTitle;
5913 }
5914 }
5915
5916 this.core.CreateSimpleReference(sourceLineNumbers, "Media", diskId.ToString(CultureInfo.InvariantCulture.NumberFormat));
5917
5918 // If this component does not have a companion file this file is a possible keypath.
5919 possibleKeyPath = null;
5920 if (null == companionFile)
5921 {
5922 possibleKeyPath = id.Id;
5923 }
5924
5925 return keyPath;
5926 }
5927
5928 /// <summary>
5929 /// Parses a file search element.
5930 /// </summary>
5931 /// <param name="node">Element to parse.</param>
5932 /// <param name="parentSignature">Signature of parent search element.</param>
5933 /// <param name="parentDirectorySearch">Whether this search element is used to search for the parent directory.</param>
5934 /// <param name="parentDepth">The depth specified by the parent search element.</param>
5935 /// <returns>Signature of search element.</returns>
5936 private string ParseFileSearchElement(XElement node, string parentSignature, bool parentDirectorySearch, int parentDepth)
5937 {
5938 SourceLineNumber sourceLineNumbers = Preprocessor.GetSourceLineNumbers(node);
5939 Identifier id = null;
5940 string languages = null;
5941 int minDate = CompilerConstants.IntegerNotSet;
5942 int maxDate = CompilerConstants.IntegerNotSet;
5943 int maxSize = CompilerConstants.IntegerNotSet;
5944 int minSize = CompilerConstants.IntegerNotSet;
5945 string maxVersion = null;
5946 string minVersion = null;
5947 string name = null;
5948 string shortName = null;
5949
5950 foreach (XAttribute attrib in node.Attributes())
5951 {
5952 if (String.IsNullOrEmpty(attrib.Name.NamespaceName) || CompilerCore.WixNamespace == attrib.Name.Namespace)
5953 {
5954 switch (attrib.Name.LocalName)
5955 {
5956 case "Id":
5957 id = this.core.GetAttributeIdentifier(sourceLineNumbers, attrib);
5958 break;
5959 case "Name":
5960 name = this.core.GetAttributeLongFilename(sourceLineNumbers, attrib, false);
5961 break;
5962 case "MinVersion":
5963 minVersion = this.core.GetAttributeValue(sourceLineNumbers, attrib);
5964 break;
5965 case "MaxVersion":
5966 maxVersion = this.core.GetAttributeValue(sourceLineNumbers, attrib);
5967 break;
5968 case "MinSize":
5969 minSize = this.core.GetAttributeIntegerValue(sourceLineNumbers, attrib, 0, int.MaxValue);
5970 break;
5971 case "MaxSize":
5972 maxSize = this.core.GetAttributeIntegerValue(sourceLineNumbers, attrib, 0, int.MaxValue);
5973 break;
5974 case "MinDate":
5975 minDate = this.core.GetAttributeDateTimeValue(sourceLineNumbers, attrib);
5976 break;
5977 case "MaxDate":
5978 maxDate = this.core.GetAttributeDateTimeValue(sourceLineNumbers, attrib);
5979 break;
5980 case "Languages":
5981 languages = this.core.GetAttributeValue(sourceLineNumbers, attrib);
5982 break;
5983 case "ShortName":
5984 shortName = this.core.GetAttributeShortFilename(sourceLineNumbers, attrib, false);
5985 break;
5986 default:
5987 this.core.UnexpectedAttribute(node, attrib);
5988 break;
5989 }
5990 }
5991 else
5992 {
5993 this.core.ParseExtensionAttribute(node, attrib);
5994 }
5995 }
5996
5997 // Using both ShortName and Name will not always work due to a Windows Installer bug.
5998 if (null != shortName && null != name)
5999 {
6000 this.core.OnMessage(WixWarnings.FileSearchFileNameIssue(sourceLineNumbers, node.Name.LocalName, "ShortName", "Name"));
6001 }
6002 else if (null == shortName && null == name) // at least one name must be specified.
6003 {
6004 this.core.OnMessage(WixErrors.ExpectedAttribute(sourceLineNumbers, node.Name.LocalName, "Name"));
6005 }
6006
6007 if (this.core.IsValidShortFilename(name, false))
6008 {
6009 if (null == shortName)
6010 {
6011 shortName = name;
6012 name = null;
6013 }
6014 else
6015 {
6016 this.core.OnMessage(WixErrors.IllegalAttributeValueWithOtherAttribute(sourceLineNumbers, node.Name.LocalName, "Name", name, "ShortName"));
6017 }
6018 }
6019
6020 if (null == id)
6021 {
6022 if (String.IsNullOrEmpty(parentSignature))
6023 {
6024 id = this.core.CreateIdentifier("fs", name ?? shortName);
6025 }
6026 else // reuse parent signature in the Signature table
6027 {
6028 id = new Identifier(parentSignature, AccessModifier.Private);
6029 }
6030 }
6031
6032 bool isSameId = String.Equals(id.Id, parentSignature, StringComparison.Ordinal);
6033 if (parentDirectorySearch)
6034 {
6035 // If searching for the parent directory, the Id attribute
6036 // value must be specified and unique.
6037 if (isSameId)
6038 {
6039 this.core.OnMessage(WixErrors.UniqueFileSearchIdRequired(sourceLineNumbers, parentSignature, node.Name.LocalName));
6040 }
6041 }
6042 else if (parentDepth > 1)
6043 {
6044 // Otherwise, if the depth > 1 the Id must be absent or the same
6045 // as the parent DirectorySearch if AssignToProperty is not set.
6046 if (!isSameId)
6047 {
6048 this.core.OnMessage(WixErrors.IllegalSearchIdForParentDepth(sourceLineNumbers, id.Id, parentSignature));
6049 }
6050 }
6051
6052 this.core.ParseForExtensionElements(node);
6053
6054 if (!this.core.EncounteredError)
6055 {
6056 Row row = this.core.CreateRow(sourceLineNumbers, "Signature", id);
6057 row[1] = name ?? shortName;
6058 row[2] = minVersion;
6059 row[3] = maxVersion;
6060
6061 if (CompilerConstants.IntegerNotSet != minSize)
6062 {
6063 row[4] = minSize;
6064 }
6065 if (CompilerConstants.IntegerNotSet != maxSize)
6066 {
6067 row[5] = maxSize;
6068 }
6069 if (CompilerConstants.IntegerNotSet != minDate)
6070 {
6071 row[6] = minDate;
6072 }
6073 if (CompilerConstants.IntegerNotSet != maxDate)
6074 {
6075 row[7] = maxDate;
6076 }
6077 row[8] = languages;
6078
6079 // Create a DrLocator row to associate the file with a directory
6080 // when a different identifier is specified for the FileSearch.
6081 if (!isSameId)
6082 {
6083 if (parentDirectorySearch)
6084 {
6085 // Creates the DrLocator row for the directory search while
6086 // the parent DirectorySearch creates the file locator row.
6087 row = this.core.CreateRow(sourceLineNumbers, "DrLocator");
6088 row[0] = parentSignature;
6089 row[1] = id;
6090 }
6091 else
6092 {
6093 row = this.core.CreateRow(sourceLineNumbers, "DrLocator", id);
6094 row[1] = parentSignature;
6095 }
6096 }
6097 }
6098
6099 return id.Id; // the id of the FileSearch element is its signature
6100 }
6101
6102
6103 /// <summary>
6104 /// Parses a fragment element.
6105 /// </summary>
6106 /// <param name="node">Element to parse.</param>
6107 private void ParseFragmentElement(XElement node)
6108 {
6109 SourceLineNumber sourceLineNumbers = Preprocessor.GetSourceLineNumbers(node);
6110 string id = null;
6111
6112 this.activeName = null;
6113 this.activeLanguage = null;
6114
6115 foreach (XAttribute attrib in node.Attributes())
6116 {
6117 if (String.IsNullOrEmpty(attrib.Name.NamespaceName) || CompilerCore.WixNamespace == attrib.Name.Namespace)
6118 {
6119 switch (attrib.Name.LocalName)
6120 {
6121 case "Id":
6122 id = this.core.GetAttributeIdentifierValue(sourceLineNumbers, attrib);
6123 break;
6124 default:
6125 this.core.UnexpectedAttribute(node, attrib);
6126 break;
6127 }
6128 }
6129 else
6130 {
6131 this.core.ParseExtensionAttribute(node, attrib);
6132 }
6133 }
6134
6135 // NOTE: Id is not required for Fragments, this is a departure from the normal run of the mill processing.
6136
6137 this.core.CreateActiveSection(id, SectionType.Fragment, 0);
6138
6139 int featureDisplay = 0;
6140 foreach (XElement child in node.Elements())
6141 {
6142 if (CompilerCore.WixNamespace == child.Name.Namespace)
6143 {
6144 switch (child.Name.LocalName)
6145 {
6146 case "_locDefinition":
6147 break;
6148 case "AdminExecuteSequence":
6149 this.ParseSequenceElement(child, child.Name.LocalName);
6150 break;
6151 case "AdminUISequence":
6152 this.ParseSequenceElement(child, child.Name.LocalName);
6153 break;
6154 case "AdvertiseExecuteSequence":
6155 this.ParseSequenceElement(child, child.Name.LocalName);
6156 break;
6157 case "InstallExecuteSequence":
6158 this.ParseSequenceElement(child, child.Name.LocalName);
6159 break;
6160 case "InstallUISequence":
6161 this.ParseSequenceElement(child, child.Name.LocalName);
6162 break;
6163 case "AppId":
6164 this.ParseAppIdElement(child, null, YesNoType.Yes, null, null, null);
6165 break;
6166 case "Binary":
6167 this.ParseBinaryElement(child);
6168 break;
6169 case "BootstrapperApplication":
6170 this.ParseBootstrapperApplicationElement(child);
6171 break;
6172 case "BootstrapperApplicationRef":
6173 this.ParseBootstrapperApplicationRefElement(child);
6174 break;
6175 case "ComplianceCheck":
6176 this.ParseComplianceCheckElement(child);
6177 break;
6178 case "Component":
6179 this.ParseComponentElement(child, ComplexReferenceParentType.Unknown, null, null, CompilerConstants.IntegerNotSet, null, null);
6180 break;
6181 case "ComponentGroup":
6182 this.ParseComponentGroupElement(child, ComplexReferenceParentType.Unknown, id);
6183 break;
6184 case "Condition":
6185 this.ParseConditionElement(child, node.Name.LocalName, null, null);
6186 break;
6187 case "Container":
6188 this.ParseContainerElement(child);
6189 break;
6190 case "CustomAction":
6191 this.ParseCustomActionElement(child);
6192 break;
6193 case "CustomActionRef":
6194 this.ParseSimpleRefElement(child, "CustomAction");
6195 break;
6196 case "CustomTable":
6197 this.ParseCustomTableElement(child);
6198 break;
6199 case "Directory":
6200 this.ParseDirectoryElement(child, null, CompilerConstants.IntegerNotSet, String.Empty);
6201 break;
6202 case "DirectoryRef":
6203 this.ParseDirectoryRefElement(child);
6204 break;
6205 case "EmbeddedChainer":
6206 this.ParseEmbeddedChainerElement(child);
6207 break;
6208 case "EmbeddedChainerRef":
6209 this.ParseSimpleRefElement(child, "MsiEmbeddedChainer");
6210 break;
6211 case "EnsureTable":
6212 this.ParseEnsureTableElement(child);
6213 break;
6214 case "Feature":
6215 this.ParseFeatureElement(child, ComplexReferenceParentType.Unknown, null, ref featureDisplay);
6216 break;
6217 case "FeatureGroup":
6218 this.ParseFeatureGroupElement(child, ComplexReferenceParentType.Unknown, id);
6219 break;
6220 case "FeatureRef":
6221 this.ParseFeatureRefElement(child, ComplexReferenceParentType.Unknown, null);
6222 break;
6223 case "Icon":
6224 this.ParseIconElement(child);
6225 break;
6226 case "IgnoreModularization":
6227 this.ParseIgnoreModularizationElement(child);
6228 break;
6229 case "Media":
6230 this.ParseMediaElement(child, null);
6231 break;
6232 case "MediaTemplate":
6233 this.ParseMediaTemplateElement(child, null);
6234 break;
6235 case "PackageGroup":
6236 this.ParsePackageGroupElement(child);
6237 break;
6238 case "PackageCertificates":
6239 case "PatchCertificates":
6240 this.ParseCertificatesElement(child);
6241 break;
6242 case "PatchFamily":
6243 this.ParsePatchFamilyElement(child, ComplexReferenceParentType.Unknown, id);
6244 break;
6245 case "PatchFamilyGroup":
6246 this.ParsePatchFamilyGroupElement(child, ComplexReferenceParentType.Unknown, id);
6247 break;
6248 case "PatchFamilyGroupRef":
6249 this.ParsePatchFamilyGroupRefElement(child, ComplexReferenceParentType.Unknown, id);
6250 break;
6251 case "PayloadGroup":
6252 this.ParsePayloadGroupElement(child, ComplexReferenceParentType.Unknown, null);
6253 break;
6254 case "Property":
6255 this.ParsePropertyElement(child);
6256 break;
6257 case "PropertyRef":
6258 this.ParseSimpleRefElement(child, "Property");
6259 break;
6260 case "RelatedBundle":
6261 this.ParseRelatedBundleElement(child);
6262 break;
6263 case "SetDirectory":
6264 this.ParseSetDirectoryElement(child);
6265 break;
6266 case "SetProperty":
6267 this.ParseSetPropertyElement(child);
6268 break;
6269 case "SFPCatalog":
6270 string parentName = null;
6271 this.ParseSFPCatalogElement(child, ref parentName);
6272 break;
6273 case "UI":
6274 this.ParseUIElement(child);
6275 break;
6276 case "UIRef":
6277 this.ParseSimpleRefElement(child, "WixUI");
6278 break;
6279 case "Upgrade":
6280 this.ParseUpgradeElement(child);
6281 break;
6282 case "Variable":
6283 this.ParseVariableElement(child);
6284 break;
6285 case "WixVariable":
6286 this.ParseWixVariableElement(child);
6287 break;
6288 default:
6289 this.core.UnexpectedElement(node, child);
6290 break;
6291 }
6292 }
6293 else
6294 {
6295 this.core.ParseExtensionElement(node, child);
6296 }
6297 }
6298
6299 if (!this.core.EncounteredError && null != id)
6300 {
6301 Row row = this.core.CreateRow(sourceLineNumbers, "WixFragment");
6302 row[0] = id;
6303 }
6304 }
6305
6306
6307 /// <summary>
6308 /// Parses a condition element.
6309 /// </summary>
6310 /// <param name="node">Element to parse.</param>
6311 /// <param name="parentElementLocalName">LocalName of the parent element.</param>
6312 /// <param name="id">Id of the parent element.</param>
6313 /// <param name="dialog">Dialog of the parent element if its a Control.</param>
6314 /// <returns>The condition if one was found.</returns>
6315 private string ParseConditionElement(XElement node, string parentElementLocalName, string id, string dialog)
6316 {
6317 SourceLineNumber sourceLineNumbers = Preprocessor.GetSourceLineNumbers(node);
6318 string action = null;
6319 string condition = null;
6320 int level = CompilerConstants.IntegerNotSet;
6321 string message = null;
6322
6323 foreach (XAttribute attrib in node.Attributes())
6324 {
6325 if (String.IsNullOrEmpty(attrib.Name.NamespaceName) || CompilerCore.WixNamespace == attrib.Name.Namespace)
6326 {
6327 switch (attrib.Name.LocalName)
6328 {
6329 case "Action":
6330 if ("Control" == parentElementLocalName)
6331 {
6332 action = this.core.GetAttributeValue(sourceLineNumbers, attrib);
6333 if (0 < action.Length)
6334 {
6335 Wix.Condition.ActionType actionType;
6336 if (Wix.Condition.TryParseActionType(action, out actionType))
6337 {
6338 action = Compiler.UppercaseFirstChar(action);
6339 }
6340 else
6341 {
6342 this.core.OnMessage(WixErrors.IllegalAttributeValue(sourceLineNumbers, node.Name.LocalName, attrib.Name.LocalName, action, "default", "disable", "enable", "hide", "show"));
6343 }
6344 }
6345 }
6346 else
6347 {
6348 this.core.UnexpectedAttribute(node, attrib);
6349 }
6350 break;
6351 case "Level":
6352 if ("Feature" == parentElementLocalName)
6353 {
6354 level = this.core.GetAttributeIntegerValue(sourceLineNumbers, attrib, 0, short.MaxValue);
6355 }
6356 else
6357 {
6358 this.core.UnexpectedAttribute(node, attrib);
6359 }
6360 break;
6361 case "Message":
6362 if ("Fragment" == parentElementLocalName || "Product" == parentElementLocalName)
6363 {
6364 message = this.core.GetAttributeValue(sourceLineNumbers, attrib);
6365 }
6366 else
6367 {
6368 this.core.UnexpectedAttribute(node, attrib);
6369 }
6370 break;
6371 default:
6372 this.core.UnexpectedAttribute(node, attrib);
6373 break;
6374 }
6375 }
6376 else
6377 {
6378 this.core.ParseExtensionAttribute(node, attrib);
6379 }
6380 }
6381
6382 // get the condition from the inner text of the element
6383 condition = this.core.GetConditionInnerText(node);
6384
6385 this.core.ParseForExtensionElements(node);
6386
6387 // the condition should not be empty
6388 if (null == condition || 0 == condition.Length)
6389 {
6390 condition = null;
6391 this.core.OnMessage(WixErrors.ConditionExpected(sourceLineNumbers, node.Name.LocalName));
6392 }
6393
6394 switch (parentElementLocalName)
6395 {
6396 case "Control":
6397 if (null == action)
6398 {
6399 this.core.OnMessage(WixErrors.ExpectedAttribute(sourceLineNumbers, node.Name.LocalName, "Action"));
6400 }
6401
6402 if (!this.core.EncounteredError)
6403 {
6404 Row row = this.core.CreateRow(sourceLineNumbers, "ControlCondition");
6405 row[0] = dialog;
6406 row[1] = id;
6407 row[2] = action;
6408 row[3] = condition;
6409 }
6410 break;
6411 case "Feature":
6412 if (CompilerConstants.IntegerNotSet == level)
6413 {
6414 this.core.OnMessage(WixErrors.ExpectedAttribute(sourceLineNumbers, node.Name.LocalName, "Level"));
6415 level = CompilerConstants.IllegalInteger;
6416 }
6417
6418 if (!this.core.EncounteredError)
6419 {
6420 Row row = this.core.CreateRow(sourceLineNumbers, "Condition");
6421 row[0] = id;
6422 row[1] = level;
6423 row[2] = condition;
6424 }
6425 break;
6426 case "Fragment":
6427 case "Product":
6428 if (null == message)
6429 {
6430 this.core.OnMessage(WixErrors.ExpectedAttribute(sourceLineNumbers, node.Name.LocalName, "Message"));
6431 }
6432
6433 if (!this.core.EncounteredError)
6434 {
6435 Row row = this.core.CreateRow(sourceLineNumbers, "LaunchCondition");
6436 row[0] = condition;
6437 row[1] = message;
6438 }
6439 break;
6440 }
6441
6442 return condition;
6443 }
6444
6445 /// <summary>
6446 /// Parses a IniFile element.
6447 /// </summary>
6448 /// <param name="node">Element to parse.</param>
6449 /// <param name="componentId">Identifier of the parent component.</param>
6450 private void ParseIniFileElement(XElement node, string componentId)
6451 {
6452 SourceLineNumber sourceLineNumbers = Preprocessor.GetSourceLineNumbers(node);
6453 Identifier id = null;
6454 int action = CompilerConstants.IntegerNotSet;
6455 string directory = null;
6456 string key = null;
6457 string name = null;
6458 string section = null;
6459 string shortName = null;
6460 string tableName = null;
6461 string value = null;
6462
6463 foreach (XAttribute attrib in node.Attributes())
6464 {
6465 if (String.IsNullOrEmpty(attrib.Name.NamespaceName) || CompilerCore.WixNamespace == attrib.Name.Namespace)
6466 {
6467 switch (attrib.Name.LocalName)
6468 {
6469 case "Id":
6470 id = this.core.GetAttributeIdentifier(sourceLineNumbers, attrib);
6471 break;
6472 case "Action":
6473 string actionValue = this.core.GetAttributeValue(sourceLineNumbers, attrib);
6474 if (0 < actionValue.Length)
6475 {
6476 Wix.IniFile.ActionType actionType = Wix.IniFile.ParseActionType(actionValue);
6477 switch (actionType)
6478 {
6479 case Wix.IniFile.ActionType.addLine:
6480 action = MsiInterop.MsidbIniFileActionAddLine;
6481 break;
6482 case Wix.IniFile.ActionType.addTag:
6483 action = MsiInterop.MsidbIniFileActionAddTag;
6484 break;
6485 case Wix.IniFile.ActionType.createLine:
6486 action = MsiInterop.MsidbIniFileActionCreateLine;
6487 break;
6488 case Wix.IniFile.ActionType.removeLine:
6489 action = MsiInterop.MsidbIniFileActionRemoveLine;
6490 break;
6491 case Wix.IniFile.ActionType.removeTag:
6492 action = MsiInterop.MsidbIniFileActionRemoveTag;
6493 break;
6494 default:
6495 this.core.OnMessage(WixErrors.IllegalAttributeValue(sourceLineNumbers, node.Name.LocalName, "Action", actionValue, "addLine", "addTag", "createLine", "removeLine", "removeTag"));
6496 break;
6497 }
6498 }
6499 break;
6500 case "Directory":
6501 directory = this.core.GetAttributeIdentifierValue(sourceLineNumbers, attrib);
6502 break;
6503 case "Key":
6504 key = this.core.GetAttributeValue(sourceLineNumbers, attrib);
6505 break;
6506 case "Name":
6507 name = this.core.GetAttributeLongFilename(sourceLineNumbers, attrib, false);
6508 break;
6509 case "Section":
6510 section = this.core.GetAttributeValue(sourceLineNumbers, attrib);
6511 break;
6512 case "ShortName":
6513 shortName = this.core.GetAttributeShortFilename(sourceLineNumbers, attrib, false);
6514 break;
6515 case "Value":
6516 value = this.core.GetAttributeValue(sourceLineNumbers, attrib);
6517 break;
6518 default:
6519 this.core.UnexpectedAttribute(node, attrib);
6520 break;
6521 }
6522 }
6523 else
6524 {
6525 this.core.ParseExtensionAttribute(node, attrib);
6526 }
6527 }
6528
6529 if (CompilerConstants.IntegerNotSet == action)
6530 {
6531 this.core.OnMessage(WixErrors.ExpectedAttribute(sourceLineNumbers, node.Name.LocalName, "Action"));
6532 action = CompilerConstants.IllegalInteger;
6533 }
6534
6535 if (null == key)
6536 {
6537 this.core.OnMessage(WixErrors.ExpectedAttribute(sourceLineNumbers, node.Name.LocalName, "Key"));
6538 }
6539
6540 if (null == name)
6541 {
6542 this.core.OnMessage(WixErrors.ExpectedAttribute(sourceLineNumbers, node.Name.LocalName, "Name"));
6543 }
6544 else if (0 < name.Length)
6545 {
6546 if (this.core.IsValidShortFilename(name, false))
6547 {
6548 if (null == shortName)
6549 {
6550 shortName = name;
6551 name = null;
6552 }
6553 else
6554 {
6555 this.core.OnMessage(WixErrors.IllegalAttributeValueWithOtherAttribute(sourceLineNumbers, node.Name.LocalName, "Name", name, "ShortName"));
6556 }
6557 }
6558 else // generate a short file name.
6559 {
6560 if (null == shortName)
6561 {
6562 shortName = this.core.CreateShortName(name, true, false, node.Name.LocalName, componentId);
6563 }
6564 }
6565 }
6566
6567 if (null == section)
6568 {
6569 this.core.OnMessage(WixErrors.ExpectedAttribute(sourceLineNumbers, node.Name.LocalName, "Section"));
6570 }
6571
6572 if (null == id)
6573 {
6574 id = this.core.CreateIdentifier("ini", directory, name ?? shortName, section, key, name);
6575 }
6576
6577 this.core.ParseForExtensionElements(node);
6578
6579 if (MsiInterop.MsidbIniFileActionRemoveLine == action || MsiInterop.MsidbIniFileActionRemoveTag == action)
6580 {
6581 tableName = "RemoveIniFile";
6582 }
6583 else
6584 {
6585 if (null == value)
6586 {
6587 this.core.OnMessage(WixErrors.ExpectedAttribute(sourceLineNumbers, node.Name.LocalName, "Value"));
6588 }
6589
6590 tableName = "IniFile";
6591 }
6592
6593 if (!this.core.EncounteredError)
6594 {
6595 Row row = this.core.CreateRow(sourceLineNumbers, tableName, id);
6596 row[1] = GetMsiFilenameValue(shortName, name);
6597 row[2] = directory;
6598 row[3] = section;
6599 row[4] = key;
6600 row[5] = value;
6601 row[6] = action;
6602 row[7] = componentId;
6603 }
6604 }
6605
6606 /// <summary>
6607 /// Parses an IniFile search element.
6608 /// </summary>
6609 /// <param name="node">Element to parse.</param>
6610 /// <returns>Signature for search element.</returns>
6611 private string ParseIniFileSearchElement(XElement node)
6612 {
6613 SourceLineNumber sourceLineNumbers = Preprocessor.GetSourceLineNumbers(node);
6614 Identifier id = null;
6615 int field = CompilerConstants.IntegerNotSet;
6616 string key = null;
6617 string name = null;
6618 string section = null;
6619 string shortName = null;
6620 string signature = null;
6621 int type = 1; // default is file
6622
6623 foreach (XAttribute attrib in node.Attributes())
6624 {
6625 if (String.IsNullOrEmpty(attrib.Name.NamespaceName) || CompilerCore.WixNamespace == attrib.Name.Namespace)
6626 {
6627 switch (attrib.Name.LocalName)
6628 {
6629 case "Id":
6630 id = this.core.GetAttributeIdentifier(sourceLineNumbers, attrib);
6631 break;
6632 case "Field":
6633 field = this.core.GetAttributeIntegerValue(sourceLineNumbers, attrib, 0, short.MaxValue);
6634 break;
6635 case "Key":
6636 key = this.core.GetAttributeValue(sourceLineNumbers, attrib);
6637 break;
6638 case "Name":
6639 name = this.core.GetAttributeLongFilename(sourceLineNumbers, attrib, false);
6640 break;
6641 case "Section":
6642 section = this.core.GetAttributeValue(sourceLineNumbers, attrib);
6643 break;
6644 case "ShortName":
6645 shortName = this.core.GetAttributeShortFilename(sourceLineNumbers, attrib, false);
6646 break;
6647 case "Type":
6648 string typeValue = this.core.GetAttributeValue(sourceLineNumbers, attrib);
6649 if (0 < typeValue.Length)
6650 {
6651 Wix.IniFileSearch.TypeType typeType = Wix.IniFileSearch.ParseTypeType(typeValue);
6652 switch (typeType)
6653 {
6654 case Wix.IniFileSearch.TypeType.directory:
6655 type = 0;
6656 break;
6657 case Wix.IniFileSearch.TypeType.file:
6658 type = 1;
6659 break;
6660 case Wix.IniFileSearch.TypeType.raw:
6661 type = 2;
6662 break;
6663 default:
6664 this.core.OnMessage(WixErrors.IllegalAttributeValue(sourceLineNumbers, node.Name.LocalName, "Type", typeValue, "directory", "file", "registry"));
6665 break;
6666 }
6667 }
6668 break;
6669 default:
6670 this.core.UnexpectedAttribute(node, attrib);
6671 break;
6672 }
6673 }
6674 else
6675 {
6676 this.core.ParseExtensionAttribute(node, attrib);
6677 }
6678 }
6679
6680 if (null == key)
6681 {
6682 this.core.OnMessage(WixErrors.ExpectedAttribute(sourceLineNumbers, node.Name.LocalName, "Key"));
6683 }
6684
6685 if (null == name)
6686 {
6687 this.core.OnMessage(WixErrors.ExpectedAttribute(sourceLineNumbers, node.Name.LocalName, "Name"));
6688 }
6689 else if (0 < name.Length)
6690 {
6691 if (this.core.IsValidShortFilename(name, false))
6692 {
6693 if (null == shortName)
6694 {
6695 shortName = name;
6696 name = null;
6697 }
6698 else
6699 {
6700 this.core.OnMessage(WixErrors.IllegalAttributeValueWithOtherAttribute(sourceLineNumbers, node.Name.LocalName, "Name", name, "ShortName"));
6701 }
6702 }
6703 else if (null == shortName) // generate a short file name.
6704 {
6705 shortName = this.core.CreateShortName(name, true, false, node.Name.LocalName);
6706 }
6707 }
6708
6709 if (null == section)
6710 {
6711 this.core.OnMessage(WixErrors.ExpectedAttribute(sourceLineNumbers, node.Name.LocalName, "Section"));
6712 }
6713
6714 if (null == id)
6715 {
6716 id = this.core.CreateIdentifier("ini", name, section, key, field.ToString(), type.ToString());
6717 }
6718
6719 signature = id.Id;
6720
6721 bool oneChild = false;
6722 foreach (XElement child in node.Elements())
6723 {
6724 if (CompilerCore.WixNamespace == child.Name.Namespace)
6725 {
6726 SourceLineNumber childSourceLineNumbers = Preprocessor.GetSourceLineNumbers(child);
6727 switch (child.Name.LocalName)
6728 {
6729 case "DirectorySearch":
6730 if (oneChild)
6731 {
6732 this.core.OnMessage(WixErrors.TooManySearchElements(childSourceLineNumbers, node.Name.LocalName));
6733 }
6734 oneChild = true;
6735
6736 // directorysearch parentage should work like directory element, not the rest of the signature type because of the DrLocator.Parent column
6737 signature = this.ParseDirectorySearchElement(child, id.Id);
6738 break;
6739 case "DirectorySearchRef":
6740 if (oneChild)
6741 {
6742 this.core.OnMessage(WixErrors.TooManySearchElements(childSourceLineNumbers, node.Name.LocalName));
6743 }
6744 oneChild = true;
6745 signature = this.ParseDirectorySearchRefElement(child, id.Id);
6746 break;
6747 case "FileSearch":
6748 if (oneChild)
6749 {
6750 this.core.OnMessage(WixErrors.TooManySearchElements(sourceLineNumbers, node.Name.LocalName));
6751 }
6752 oneChild = true;
6753 signature = this.ParseFileSearchElement(child, id.Id, false, CompilerConstants.IntegerNotSet);
6754 id = new Identifier(signature, AccessModifier.Private); // FileSearch signatures override parent signatures
6755 break;
6756 case "FileSearchRef":
6757 if (oneChild)
6758 {
6759 this.core.OnMessage(WixErrors.TooManySearchElements(sourceLineNumbers, node.Name.LocalName));
6760 }
6761 oneChild = true;
6762 string newId = this.ParseSimpleRefElement(child, "Signature"); // FileSearch signatures override parent signatures
6763 id = new Identifier(newId, AccessModifier.Private);
6764 signature = null;
6765 break;
6766 default:
6767 this.core.UnexpectedElement(node, child);
6768 break;
6769 }
6770 }
6771 else
6772 {
6773 this.core.ParseExtensionElement(node, child);
6774 }
6775 }
6776
6777 if (!this.core.EncounteredError)
6778 {
6779 Row row = this.core.CreateRow(sourceLineNumbers, "IniLocator", id);
6780 row[1] = GetMsiFilenameValue(shortName, name);
6781 row[2] = section;
6782 row[3] = key;
6783 if (CompilerConstants.IntegerNotSet != field)
6784 {
6785 row[4] = field;
6786 }
6787 row[5] = type;
6788 }
6789
6790 return signature;
6791 }
6792
6793 /// <summary>
6794 /// Parses an isolated component element.
6795 /// </summary>
6796 /// <param name="node">Element to parse.</param>
6797 /// <param name="componentId">Identifier of parent component.</param>
6798 private void ParseIsolateComponentElement(XElement node, string componentId)
6799 {
6800 SourceLineNumber sourceLineNumbers = Preprocessor.GetSourceLineNumbers(node);
6801 string shared = null;
6802
6803 foreach (XAttribute attrib in node.Attributes())
6804 {
6805 if (String.IsNullOrEmpty(attrib.Name.NamespaceName) || CompilerCore.WixNamespace == attrib.Name.Namespace)
6806 {
6807 switch (attrib.Name.LocalName)
6808 {
6809 case "Shared":
6810 shared = this.core.GetAttributeIdentifierValue(sourceLineNumbers, attrib);
6811 this.core.CreateSimpleReference(sourceLineNumbers, "Component", shared);
6812 break;
6813 default:
6814 this.core.UnexpectedAttribute(node, attrib);
6815 break;
6816 }
6817 }
6818 else
6819 {
6820 this.core.ParseExtensionAttribute(node, attrib);
6821 }
6822 }
6823
6824 if (null == shared)
6825 {
6826 this.core.OnMessage(WixErrors.ExpectedAttribute(sourceLineNumbers, node.Name.LocalName, "Shared"));
6827 }
6828
6829 this.core.ParseForExtensionElements(node);
6830
6831 if (!this.core.EncounteredError)
6832 {
6833 Row row = this.core.CreateRow(sourceLineNumbers, "IsolatedComponent");
6834 row[0] = shared;
6835 row[1] = componentId;
6836 }
6837 }
6838
6839 /// <summary>
6840 /// Parses a PatchCertificates or PackageCertificates element.
6841 /// </summary>
6842 /// <param name="node">The element to parse.</param>
6843 private void ParseCertificatesElement(XElement node)
6844 {
6845 SourceLineNumber sourceLineNumbers = Preprocessor.GetSourceLineNumbers(node);
6846
6847 // no attributes are supported for this element
6848 foreach (XAttribute attrib in node.Attributes())
6849 {
6850 if (String.IsNullOrEmpty(attrib.Name.NamespaceName) || CompilerCore.WixNamespace == attrib.Name.Namespace)
6851 {
6852 this.core.UnexpectedAttribute(node, attrib);
6853 }
6854 else
6855 {
6856 this.core.ParseExtensionAttribute(node, attrib);
6857 }
6858 }
6859
6860 foreach (XElement child in node.Elements())
6861 {
6862 if (CompilerCore.WixNamespace == child.Name.Namespace)
6863 {
6864 switch (child.Name.LocalName)
6865 {
6866 case "DigitalCertificate":
6867 string name = this.ParseDigitalCertificateElement(child);
6868
6869 if (!this.core.EncounteredError)
6870 {
6871 Row row = this.core.CreateRow(sourceLineNumbers, "PatchCertificates" == node.Name.LocalName ? "MsiPatchCertificate" : "MsiPackageCertificate");
6872 row[0] = name;
6873 row[1] = name;
6874 }
6875 break;
6876 default:
6877 this.core.UnexpectedElement(node, child);
6878 break;
6879 }
6880 }
6881 else
6882 {
6883 this.core.ParseExtensionElement(node, child);
6884 }
6885 }
6886 }
6887
6888 /// <summary>
6889 /// Parses an digital certificate element.
6890 /// </summary>
6891 /// <param name="node">Element to parse.</param>
6892 /// <returns>The identifier of the certificate.</returns>
6893 private string ParseDigitalCertificateElement(XElement node)
6894 {
6895 SourceLineNumber sourceLineNumbers = Preprocessor.GetSourceLineNumbers(node);
6896 Identifier id = null;
6897 string sourceFile = null;
6898
6899 foreach (XAttribute attrib in node.Attributes())
6900 {
6901 if (String.IsNullOrEmpty(attrib.Name.NamespaceName) || CompilerCore.WixNamespace == attrib.Name.Namespace)
6902 {
6903 switch (attrib.Name.LocalName)
6904 {
6905 case "Id":
6906 id = this.core.GetAttributeIdentifier(sourceLineNumbers, attrib);
6907 break;
6908 case "SourceFile":
6909 sourceFile = this.core.GetAttributeValue(sourceLineNumbers, attrib);
6910 break;
6911 default:
6912 this.core.UnexpectedAttribute(node, attrib);
6913 break;
6914 }
6915 }
6916 else
6917 {
6918 this.core.ParseExtensionAttribute(node, attrib);
6919 }
6920 }
6921
6922 if (null == id)
6923 {
6924 this.core.OnMessage(WixErrors.ExpectedAttribute(sourceLineNumbers, node.Name.LocalName, "Id"));
6925 id = Identifier.Invalid;
6926 }
6927 else if (40 < id.Id.Length)
6928 {
6929 this.core.OnMessage(WixErrors.StreamNameTooLong(sourceLineNumbers, node.Name.LocalName, "Id", id.Id, id.Id.Length, 40));
6930
6931 // No need to check for modularization problems since DigitalSignature and thus DigitalCertificate
6932 // currently have no usage in merge modules.
6933 }
6934
6935 if (null == sourceFile)
6936 {
6937 this.core.OnMessage(WixErrors.ExpectedAttribute(sourceLineNumbers, node.Name.LocalName, "SourceFile"));
6938 }
6939
6940 this.core.ParseForExtensionElements(node);
6941
6942 if (!this.core.EncounteredError)
6943 {
6944 Row row = this.core.CreateRow(sourceLineNumbers, "MsiDigitalCertificate", id);
6945 row[1] = sourceFile;
6946 }
6947
6948 return id.Id;
6949 }
6950
6951 /// <summary>
6952 /// Parses an digital signature element.
6953 /// </summary>
6954 /// <param name="node">Element to parse.</param>
6955 /// <param name="diskId">Disk id inherited from parent media.</param>
6956 private void ParseDigitalSignatureElement(XElement node, string diskId)
6957 {
6958 SourceLineNumber sourceLineNumbers = Preprocessor.GetSourceLineNumbers(node);
6959 string certificateId = null;
6960 string sourceFile = null;
6961
6962 foreach (XAttribute attrib in node.Attributes())
6963 {
6964 if (String.IsNullOrEmpty(attrib.Name.NamespaceName) || CompilerCore.WixNamespace == attrib.Name.Namespace)
6965 {
6966 switch (attrib.Name.LocalName)
6967 {
6968 case "SourceFile":
6969 sourceFile = this.core.GetAttributeValue(sourceLineNumbers, attrib);
6970 break;
6971 default:
6972 this.core.UnexpectedAttribute(node, attrib);
6973 break;
6974 }
6975 }
6976 else
6977 {
6978 this.core.ParseExtensionAttribute(node, attrib);
6979 }
6980 }
6981
6982 // sanity check for debug to ensure the stream name will not be a problem
6983 if (null != sourceFile)
6984 {
6985 Debug.Assert(62 >= "MsiDigitalSignature.Media.".Length + diskId.Length);
6986 }
6987
6988 foreach (XElement child in node.Elements())
6989 {
6990 if (CompilerCore.WixNamespace == child.Name.Namespace)
6991 {
6992 switch (child.Name.LocalName)
6993 {
6994 case "DigitalCertificate":
6995 certificateId = this.ParseDigitalCertificateElement(child);
6996 break;
6997 default:
6998 this.core.UnexpectedElement(node, child);
6999 break;
7000 }
7001 }
7002 else
7003 {
7004 this.core.ParseExtensionElement(node, child);
7005 }
7006 }
7007
7008 if (null == certificateId)
7009 {
7010 this.core.OnMessage(WixErrors.ExpectedElement(sourceLineNumbers, node.Name.LocalName, "DigitalCertificate"));
7011 }
7012
7013 if (!this.core.EncounteredError)
7014 {
7015 Row row = this.core.CreateRow(sourceLineNumbers, "MsiDigitalSignature");
7016 row[0] = "Media";
7017 row[1] = diskId;
7018 row[2] = certificateId;
7019 row[3] = sourceFile;
7020 }
7021 }
7022
7023 /// <summary>
7024 /// Parses a MajorUpgrade element.
7025 /// </summary>
7026 /// <param name="node">The element to parse.</param>
7027 /// <param name="parentElement">The parent element.</param>
7028 private void ParseMajorUpgradeElement(XElement node, IDictionary<string, string> contextValues)
7029 {
7030 SourceLineNumber sourceLineNumbers = Preprocessor.GetSourceLineNumbers(node);
7031 int options = MsiInterop.MsidbUpgradeAttributesMigrateFeatures;
7032 bool allowDowngrades = false;
7033 bool allowSameVersionUpgrades = false;
7034 bool blockUpgrades = false;
7035 string downgradeErrorMessage = null;
7036 string disallowUpgradeErrorMessage = null;
7037 string removeFeatures = null;
7038 string schedule = null;
7039
7040 string upgradeCode = contextValues["UpgradeCode"];
7041 if (String.IsNullOrEmpty(upgradeCode))
7042 {
7043 this.core.OnMessage(WixErrors.ParentElementAttributeRequired(sourceLineNumbers, "Product", "UpgradeCode", node.Name.LocalName));
7044 }
7045
7046 string productVersion = contextValues["ProductVersion"];
7047 if (String.IsNullOrEmpty(productVersion))
7048 {
7049 this.core.OnMessage(WixErrors.ParentElementAttributeRequired(sourceLineNumbers, "Product", "Version", node.Name.LocalName));
7050 }
7051
7052 string productLanguage = contextValues["ProductLanguage"];
7053
7054 foreach (XAttribute attrib in node.Attributes())
7055 {
7056 if (String.IsNullOrEmpty(attrib.Name.NamespaceName) || CompilerCore.WixNamespace == attrib.Name.Namespace)
7057 {
7058 switch (attrib.Name.LocalName)
7059 {
7060 case "AllowDowngrades":
7061 allowDowngrades = YesNoType.Yes == this.core.GetAttributeYesNoValue(sourceLineNumbers, attrib);
7062 break;
7063 case "AllowSameVersionUpgrades":
7064 allowSameVersionUpgrades = YesNoType.Yes == this.core.GetAttributeYesNoValue(sourceLineNumbers, attrib);
7065 break;
7066 case "Disallow":
7067 blockUpgrades = YesNoType.Yes == this.core.GetAttributeYesNoValue(sourceLineNumbers, attrib);
7068 break;
7069 case "DowngradeErrorMessage":
7070 downgradeErrorMessage = this.core.GetAttributeValue(sourceLineNumbers, attrib);
7071 break;
7072 case "DisallowUpgradeErrorMessage":
7073 disallowUpgradeErrorMessage = this.core.GetAttributeValue(sourceLineNumbers, attrib);
7074 break;
7075 case "MigrateFeatures":
7076 if (YesNoType.No == this.core.GetAttributeYesNoValue(sourceLineNumbers, attrib))
7077 {
7078 options &= ~MsiInterop.MsidbUpgradeAttributesMigrateFeatures;
7079 }
7080 break;
7081 case "IgnoreLanguage":
7082 if (YesNoType.Yes == this.core.GetAttributeYesNoValue(sourceLineNumbers, attrib))
7083 {
7084 productLanguage = null;
7085 }
7086 break;
7087 case "IgnoreRemoveFailure":
7088 if (YesNoType.Yes == this.core.GetAttributeYesNoValue(sourceLineNumbers, attrib))
7089 {
7090 options |= MsiInterop.MsidbUpgradeAttributesIgnoreRemoveFailure;
7091 }
7092 break;
7093 case "RemoveFeatures":
7094 removeFeatures = this.core.GetAttributeValue(sourceLineNumbers, attrib);
7095 break;
7096 case "Schedule":
7097 schedule = this.core.GetAttributeValue(sourceLineNumbers, attrib);
7098 break;
7099 default:
7100 this.core.UnexpectedAttribute(node, attrib);
7101 break;
7102 }
7103 }
7104 else
7105 {
7106 this.core.ParseExtensionAttribute(node, attrib);
7107 }
7108 }
7109
7110 this.core.ParseForExtensionElements(node);
7111
7112 if (!allowDowngrades && String.IsNullOrEmpty(downgradeErrorMessage))
7113 {
7114 this.core.OnMessage(WixErrors.ExpectedAttribute(sourceLineNumbers, node.Name.LocalName, "DowngradeErrorMessage", "AllowDowngrades", "yes", true));
7115 }
7116
7117 if (allowDowngrades && !String.IsNullOrEmpty(downgradeErrorMessage))
7118 {
7119 this.core.OnMessage(WixErrors.IllegalAttributeWithOtherAttribute(sourceLineNumbers, node.Name.LocalName, "DowngradeErrorMessage", "AllowDowngrades", "yes"));
7120 }
7121
7122 if (allowDowngrades && allowSameVersionUpgrades)
7123 {
7124 this.core.OnMessage(WixErrors.IllegalAttributeWithOtherAttribute(sourceLineNumbers, node.Name.LocalName, "AllowSameVersionUpgrades", "AllowDowngrades", "yes"));
7125 }
7126
7127 if (blockUpgrades && String.IsNullOrEmpty(disallowUpgradeErrorMessage))
7128 {
7129 this.core.OnMessage(WixErrors.ExpectedAttribute(sourceLineNumbers, node.Name.LocalName, "DisallowUpgradeErrorMessage", "Disallow", "yes", true));
7130 }
7131
7132 if (!blockUpgrades && !String.IsNullOrEmpty(disallowUpgradeErrorMessage))
7133 {
7134 this.core.OnMessage(WixErrors.IllegalAttributeWithOtherAttribute(sourceLineNumbers, node.Name.LocalName, "DisallowUpgradeErrorMessage", "Disallow", "yes"));
7135 }
7136
7137 if (!this.core.EncounteredError)
7138 {
7139 // create the row that performs the upgrade (or downgrade)
7140 Row row = this.core.CreateRow(sourceLineNumbers, "Upgrade");
7141 row[0] = upgradeCode;
7142 if (allowDowngrades)
7143 {
7144 row[1] = "0"; // let any version satisfy
7145 // row[2] = maximum version; omit so we don't have to fake a version like "255.255.65535";
7146 row[3] = productLanguage;
7147 row[4] = options | MsiInterop.MsidbUpgradeAttributesVersionMinInclusive;
7148 }
7149 else
7150 {
7151 // row[1] = minimum version; skip it so we detect all prior versions.
7152 row[2] = productVersion;
7153 row[3] = productLanguage;
7154 row[4] = allowSameVersionUpgrades ? (options | MsiInterop.MsidbUpgradeAttributesVersionMaxInclusive) : options;
7155 }
7156
7157 row[5] = removeFeatures;
7158 row[6] = Compiler.UpgradeDetectedProperty;
7159
7160 // Ensure the action property is secure.
7161 this.AddWixPropertyRow(sourceLineNumbers, new Identifier(Compiler.UpgradeDetectedProperty, AccessModifier.Public), false, true, false);
7162
7163 // Add launch condition that blocks upgrades
7164 if (blockUpgrades)
7165 {
7166 row = this.core.CreateRow(sourceLineNumbers, "LaunchCondition");
7167 row[0] = Compiler.UpgradePreventedCondition;
7168 row[1] = disallowUpgradeErrorMessage;
7169 }
7170
7171 // now create the Upgrade row and launch conditions to prevent downgrades (unless explicitly permitted)
7172 if (!allowDowngrades)
7173 {
7174 row = this.core.CreateRow(sourceLineNumbers, "Upgrade");
7175 row[0] = upgradeCode;
7176 row[1] = productVersion;
7177 // row[2] = maximum version; skip it so we detect all future versions.
7178 row[3] = productLanguage;
7179 row[4] = MsiInterop.MsidbUpgradeAttributesOnlyDetect;
7180 // row[5] = removeFeatures;
7181 row[6] = Compiler.DowngradeDetectedProperty;
7182
7183 // Ensure the action property is secure.
7184 this.AddWixPropertyRow(sourceLineNumbers, new Identifier(Compiler.DowngradeDetectedProperty, AccessModifier.Public), false, true, false);
7185
7186 row = this.core.CreateRow(sourceLineNumbers, "LaunchCondition");
7187 row[0] = Compiler.DowngradePreventedCondition;
7188 row[1] = downgradeErrorMessage;
7189 }
7190
7191 // finally, schedule RemoveExistingProducts
7192 row = this.core.CreateRow(sourceLineNumbers, "WixAction");
7193 row[0] = "InstallExecuteSequence";
7194 row[1] = "RemoveExistingProducts";
7195 // row[2] = condition;
7196 // row[3] = sequence;
7197 row[6] = 0; // overridable
7198
7199 switch (schedule)
7200 {
7201 case null:
7202 case "afterInstallValidate":
7203 // row[4] = beforeAction;
7204 row[5] = "InstallValidate";
7205 break;
7206 case "afterInstallInitialize":
7207 // row[4] = beforeAction;
7208 row[5] = "InstallInitialize";
7209 break;
7210 case "afterInstallExecute":
7211 // row[4] = beforeAction;
7212 row[5] = "InstallExecute";
7213 break;
7214 case "afterInstallExecuteAgain":
7215 // row[4] = beforeAction;
7216 row[5] = "InstallExecuteAgain";
7217 break;
7218 case "afterInstallFinalize":
7219 // row[4] = beforeAction;
7220 row[5] = "InstallFinalize";
7221 break;
7222 }
7223 }
7224 }
7225
7226 /// <summary>
7227 /// Parses a media element.
7228 /// </summary>
7229 /// <param name="node">Element to parse.</param>
7230 /// <param name="patchId">Set to the PatchId if parsing Patch/Media element otherwise null.</param>
7231 private void ParseMediaElement(XElement node, string patchId)
7232 {
7233 SourceLineNumber sourceLineNumbers = Preprocessor.GetSourceLineNumbers(node);
7234 int id = CompilerConstants.IntegerNotSet;
7235 string cabinet = null;
7236 CompressionLevel? compressionLevel = null;
7237 string diskPrompt = null;
7238 string layout = null;
7239 bool patch = null != patchId;
7240 string volumeLabel = null;
7241 string source = null;
7242 string symbols = null;
7243
7244 YesNoType embedCab = patch ? YesNoType.Yes : YesNoType.NotSet;
7245
7246 foreach (XAttribute attrib in node.Attributes())
7247 {
7248 if (String.IsNullOrEmpty(attrib.Name.NamespaceName) || CompilerCore.WixNamespace == attrib.Name.Namespace)
7249 {
7250 switch (attrib.Name.LocalName)
7251 {
7252 case "Id":
7253 id = this.core.GetAttributeIntegerValue(sourceLineNumbers, attrib, 1, short.MaxValue);
7254 break;
7255 case "Cabinet":
7256 cabinet = this.core.GetAttributeValue(sourceLineNumbers, attrib);
7257 break;
7258 case "CompressionLevel":
7259 string compressionLevelString = this.core.GetAttributeValue(sourceLineNumbers, attrib);
7260 if (0 < compressionLevelString.Length)
7261 {
7262 Wix.CompressionLevelType compressionLevelType;
7263 if (!Wix.Enums.TryParseCompressionLevelType(compressionLevelString, out compressionLevelType))
7264 {
7265 this.core.OnMessage(WixErrors.IllegalAttributeValue(sourceLineNumbers, node.Name.LocalName, attrib.Name.LocalName, compressionLevelString, "high", "low", "medium", "mszip", "none"));
7266 }
7267 else
7268 {
7269 compressionLevel = (CompressionLevel)Enum.Parse(typeof(CompressionLevel), compressionLevelString, true);
7270 }
7271 }
7272 break;
7273 case "DiskPrompt":
7274 diskPrompt = this.core.GetAttributeValue(sourceLineNumbers, attrib);
7275 this.core.CreateSimpleReference(sourceLineNumbers, "Property", "DiskPrompt"); // ensure the output has a DiskPrompt Property defined
7276 break;
7277 case "EmbedCab":
7278 embedCab = this.core.GetAttributeYesNoValue(sourceLineNumbers, attrib);
7279 break;
7280 case "Layout":
7281 case "src":
7282 if (null != layout)
7283 {
7284 this.core.OnMessage(WixErrors.IllegalAttributeWithOtherAttribute(sourceLineNumbers, node.Name.LocalName, "Layout", "src"));
7285 }
7286
7287 if ("src" == attrib.Name.LocalName)
7288 {
7289 this.core.OnMessage(WixWarnings.DeprecatedAttribute(sourceLineNumbers, node.Name.LocalName, attrib.Name.LocalName, "Layout"));
7290 }
7291 layout = this.core.GetAttributeValue(sourceLineNumbers, attrib);
7292 break;
7293 case "VolumeLabel":
7294 volumeLabel = this.core.GetAttributeValue(sourceLineNumbers, attrib);
7295 break;
7296 case "Source":
7297 source = this.core.GetAttributeValue(sourceLineNumbers, attrib);
7298 break;
7299 default:
7300 this.core.UnexpectedAttribute(node, attrib);
7301 break;
7302 }
7303 }
7304 else
7305 {
7306 this.core.ParseExtensionAttribute(node, attrib);
7307 }
7308 }
7309
7310 if (CompilerConstants.IntegerNotSet == id)
7311 {
7312 this.core.OnMessage(WixErrors.ExpectedAttribute(sourceLineNumbers, node.Name.LocalName, "Id"));
7313 id = CompilerConstants.IllegalInteger;
7314 }
7315
7316 if (YesNoType.IllegalValue != embedCab)
7317 {
7318 if (YesNoType.Yes == embedCab)
7319 {
7320 if (null == cabinet)
7321 {
7322 this.core.OnMessage(WixErrors.ExpectedAttribute(sourceLineNumbers, node.Name.LocalName, "Cabinet", "EmbedCab", "yes"));
7323 }
7324 else
7325 {
7326 if (62 < cabinet.Length)
7327 {
7328 this.core.OnMessage(WixErrors.MediaEmbeddedCabinetNameTooLong(sourceLineNumbers, node.Name.LocalName, "Cabinet", cabinet, cabinet.Length));
7329 }
7330
7331 cabinet = String.Concat("#", cabinet);
7332 }
7333 }
7334 else // external cabinet file
7335 {
7336 // external cabinet files must use 8.3 filenames
7337 if (!String.IsNullOrEmpty(cabinet) && !this.core.IsValidShortFilename(cabinet, false))
7338 {
7339 // WiX variables in the name will trip the "not a valid 8.3 name" switch, so let them through
7340 if (!Common.WixVariableRegex.Match(cabinet).Success)
7341 {
7342 this.core.OnMessage(WixWarnings.MediaExternalCabinetFilenameIllegal(sourceLineNumbers, node.Name.LocalName, "Cabinet", cabinet));
7343 }
7344 }
7345 }
7346 }
7347
7348 if (null != compressionLevel && null == cabinet)
7349 {
7350 this.core.OnMessage(WixErrors.ExpectedAttribute(sourceLineNumbers, node.Name.LocalName, "Cabinet", "CompressionLevel"));
7351 }
7352
7353 if (patch)
7354 {
7355 // Default Source to a form of the Patch Id if none is specified.
7356 if (null == source)
7357 {
7358 source = String.Concat("_", new Guid(patchId).ToString("N", CultureInfo.InvariantCulture).ToUpper(CultureInfo.InvariantCulture));
7359 }
7360 }
7361
7362 foreach (XElement child in node.Elements())
7363 {
7364 SourceLineNumber childSourceLineNumbers = Preprocessor.GetSourceLineNumbers(child);
7365 if (CompilerCore.WixNamespace == child.Name.Namespace)
7366 {
7367 switch (child.Name.LocalName)
7368 {
7369 case "DigitalSignature":
7370 if (YesNoType.Yes == embedCab)
7371 {
7372 this.core.OnMessage(WixErrors.SignedEmbeddedCabinet(childSourceLineNumbers));
7373 }
7374 else if (null == cabinet)
7375 {
7376 this.core.OnMessage(WixErrors.ExpectedSignedCabinetName(childSourceLineNumbers));
7377 }
7378 else
7379 {
7380 this.ParseDigitalSignatureElement(child, id.ToString(CultureInfo.InvariantCulture.NumberFormat));
7381 }
7382 break;
7383 case "PatchBaseline":
7384 if (patch)
7385 {
7386 this.ParsePatchBaselineElement(child, id);
7387 }
7388 else
7389 {
7390 this.core.UnexpectedElement(node, child);
7391 }
7392 break;
7393 case "SymbolPath":
7394 if (null != symbols)
7395 {
7396 symbols += "" + this.ParseSymbolPathElement(child);
7397 }
7398 else
7399 {
7400 symbols = this.ParseSymbolPathElement(child);
7401 }
7402 break;
7403 default:
7404 this.core.UnexpectedElement(node, child);
7405 break;
7406 }
7407 }
7408 else
7409 {
7410 this.core.ParseExtensionElement(node, child);
7411 }
7412 }
7413
7414
7415
7416 // add the row to the section
7417 if (!this.core.EncounteredError)
7418 {
7419 MediaRow mediaRow = (MediaRow)this.core.CreateRow(sourceLineNumbers, "Media");
7420 mediaRow.DiskId = id;
7421 mediaRow.LastSequence = 0; // this is set in the binder
7422 mediaRow.DiskPrompt = diskPrompt;
7423 mediaRow.Cabinet = cabinet;
7424 mediaRow.VolumeLabel = volumeLabel;
7425 mediaRow.Source = source;
7426
7427 // the Source column is only set when creating a patch
7428
7429 if (null != compressionLevel || null != layout)
7430 {
7431 WixMediaRow row = (WixMediaRow)this.core.CreateRow(sourceLineNumbers, "WixMedia");
7432 row.DiskId = id;
7433 row.CompressionLevel = compressionLevel;
7434 row.Layout = layout;
7435 }
7436
7437 if (null != symbols)
7438 {
7439 WixDeltaPatchSymbolPathsRow symbolRow = (WixDeltaPatchSymbolPathsRow)this.core.CreateRow(sourceLineNumbers, "WixDeltaPatchSymbolPaths");
7440 symbolRow.Id = id.ToString(CultureInfo.InvariantCulture);
7441 symbolRow.Type = SymbolPathType.Media;
7442 symbolRow.SymbolPaths = symbols;
7443 }
7444 }
7445 }
7446
7447 /// <summary>
7448 /// Parses a media template element.
7449 /// </summary>
7450 /// <param name="node">Element to parse.</param>
7451 /// <param name="patchId">Set to the PatchId if parsing Patch/Media element otherwise null.</param>
7452 private void ParseMediaTemplateElement(XElement node, string patchId)
7453 {
7454 SourceLineNumber sourceLineNumbers = Preprocessor.GetSourceLineNumbers(node);
7455 string cabinetTemplate = "cab{0}.cab";
7456 string compressionLevel = null; // this defaults to mszip in Binder
7457 string diskPrompt = null;
7458 bool patch = null != patchId;
7459 string volumeLabel = null;
7460 int maximumUncompressedMediaSize = CompilerConstants.IntegerNotSet;
7461 int maximumCabinetSizeForLargeFileSplitting = CompilerConstants.IntegerNotSet;
7462 Wix.CompressionLevelType compressionLevelType = Wix.CompressionLevelType.NotSet;
7463
7464 YesNoType embedCab = patch ? YesNoType.Yes : YesNoType.NotSet;
7465
7466 foreach (XAttribute attrib in node.Attributes())
7467 {
7468 if (String.IsNullOrEmpty(attrib.Name.NamespaceName) || CompilerCore.WixNamespace == attrib.Name.Namespace)
7469 {
7470 switch (attrib.Name.LocalName)
7471 {
7472 case "CabinetTemplate":
7473 string authoredCabinetTemplateValue = this.core.GetAttributeValue(sourceLineNumbers, attrib, EmptyRule.CanBeEmpty);
7474 if (!String.IsNullOrEmpty(authoredCabinetTemplateValue))
7475 {
7476 cabinetTemplate = authoredCabinetTemplateValue;
7477 }
7478
7479 // Create an example cabinet name using the maximum number of cabinets supported, 999.
7480 string exampleCabinetName = String.Format(cabinetTemplate, "###");
7481 if (!this.core.IsValidLocIdentifier(exampleCabinetName))
7482 {
7483 // The example name should not match the authored template since that would nullify the
7484 // reason for having multiple cabients. External cabinet files must also be valid file names.
7485 if (exampleCabinetName.Equals(authoredCabinetTemplateValue) || !this.core.IsValidLongFilename(exampleCabinetName, false))
7486 {
7487 this.core.OnMessage(WixErrors.InvalidCabinetTemplate(sourceLineNumbers, cabinetTemplate));
7488 }
7489 else if (!this.core.IsValidShortFilename(exampleCabinetName, false) && !Common.WixVariableRegex.Match(exampleCabinetName).Success) // ignore short names with wix variables because it rarely works out.
7490 {
7491 this.core.OnMessage(WixWarnings.MediaExternalCabinetFilenameIllegal(sourceLineNumbers, node.Name.LocalName, "CabinetTemplate", cabinetTemplate));
7492 }
7493 }
7494 break;
7495 case "CompressionLevel":
7496 compressionLevel = this.core.GetAttributeValue(sourceLineNumbers, attrib);
7497 if (0 < compressionLevel.Length)
7498 {
7499 if (!Wix.Enums.TryParseCompressionLevelType(compressionLevel, out compressionLevelType))
7500 {
7501 this.core.OnMessage(WixErrors.IllegalAttributeValue(sourceLineNumbers, node.Name.LocalName, attrib.Name.LocalName, compressionLevel, "high", "low", "medium", "mszip", "none"));
7502 }
7503 }
7504 break;
7505 case "DiskPrompt":
7506 diskPrompt = this.core.GetAttributeValue(sourceLineNumbers, attrib);
7507 this.core.CreateSimpleReference(sourceLineNumbers, "Property", "DiskPrompt"); // ensure the output has a DiskPrompt Property defined
7508 this.core.OnMessage(WixWarnings.ReservedAttribute(sourceLineNumbers, node.Name.LocalName, attrib.Name.LocalName));
7509 break;
7510 case "EmbedCab":
7511 embedCab = this.core.GetAttributeYesNoValue(sourceLineNumbers, attrib);
7512 break;
7513 case "VolumeLabel":
7514 volumeLabel = this.core.GetAttributeValue(sourceLineNumbers, attrib);
7515 this.core.OnMessage(WixWarnings.ReservedAttribute(sourceLineNumbers, node.Name.LocalName, attrib.Name.LocalName));
7516 break;
7517 case "MaximumUncompressedMediaSize":
7518 maximumUncompressedMediaSize = this.core.GetAttributeIntegerValue(sourceLineNumbers, attrib, 1, int.MaxValue);
7519 break;
7520 case "MaximumCabinetSizeForLargeFileSplitting":
7521 maximumCabinetSizeForLargeFileSplitting = this.core.GetAttributeIntegerValue(sourceLineNumbers, attrib, CompilerCore.MinValueOfMaxCabSizeForLargeFileSplitting, CompilerCore.MaxValueOfMaxCabSizeForLargeFileSplitting);
7522 break;
7523 default:
7524 this.core.UnexpectedAttribute(node, attrib);
7525 break;
7526 }
7527 }
7528 else
7529 {
7530 this.core.ParseExtensionAttribute(node, attrib);
7531 }
7532 }
7533
7534 if (YesNoType.IllegalValue != embedCab)
7535 {
7536 if (YesNoType.Yes == embedCab)
7537 {
7538 cabinetTemplate = String.Concat("#", cabinetTemplate);
7539 }
7540 }
7541
7542 if (!this.core.EncounteredError)
7543 {
7544 MediaRow temporaryMediaRow = (MediaRow)this.core.CreateRow(sourceLineNumbers, "Media");
7545 temporaryMediaRow.DiskId = 1;
7546 WixMediaTemplateRow mediaTemplateRow = (WixMediaTemplateRow)this.core.CreateRow(sourceLineNumbers, "WixMediaTemplate");
7547 mediaTemplateRow.CabinetTemplate = cabinetTemplate;
7548 mediaTemplateRow.VolumeLabel = volumeLabel;
7549 mediaTemplateRow.DiskPrompt = diskPrompt;
7550 mediaTemplateRow.VolumeLabel = volumeLabel;
7551
7552 if (maximumUncompressedMediaSize != CompilerConstants.IntegerNotSet)
7553 {
7554 mediaTemplateRow.MaximumUncompressedMediaSize = maximumUncompressedMediaSize;
7555 }
7556 else
7557 {
7558 mediaTemplateRow.MaximumUncompressedMediaSize = CompilerCore.DefaultMaximumUncompressedMediaSize;
7559 }
7560
7561 if (maximumCabinetSizeForLargeFileSplitting != CompilerConstants.IntegerNotSet)
7562 {
7563 mediaTemplateRow.MaximumCabinetSizeForLargeFileSplitting = maximumCabinetSizeForLargeFileSplitting;
7564 }
7565 else
7566 {
7567 mediaTemplateRow.MaximumCabinetSizeForLargeFileSplitting = 0; // Default value of 0 corresponds to max size of 2048 MB (i.e. 2 GB)
7568 }
7569
7570 switch (compressionLevelType)
7571 {
7572 case Wix.CompressionLevelType.high:
7573 mediaTemplateRow.CompressionLevel = CompressionLevel.High;
7574 break;
7575 case Wix.CompressionLevelType.low:
7576 mediaTemplateRow.CompressionLevel = CompressionLevel.Low;
7577 break;
7578 case Wix.CompressionLevelType.medium:
7579 mediaTemplateRow.CompressionLevel = CompressionLevel.Medium;
7580 break;
7581 case Wix.CompressionLevelType.none:
7582 mediaTemplateRow.CompressionLevel = CompressionLevel.None;
7583 break;
7584 case Wix.CompressionLevelType.mszip:
7585 mediaTemplateRow.CompressionLevel = CompressionLevel.Mszip;
7586 break;
7587 }
7588 }
7589 }
7590
7591 /// <summary>
7592 /// Parses a merge element.
7593 /// </summary>
7594 /// <param name="node">Element to parse.</param>
7595 /// <param name="directoryId">Identifier for parent directory.</param>
7596 /// <param name="diskId">Disk id inherited from parent directory.</param>
7597 private void ParseMergeElement(XElement node, string directoryId, int diskId)
7598 {
7599 SourceLineNumber sourceLineNumbers = Preprocessor.GetSourceLineNumbers(node);
7600 Identifier id = null;
7601 string configData = String.Empty;
7602 YesNoType fileCompression = YesNoType.NotSet;
7603 string language = null;
7604 string sourceFile = null;
7605
7606 foreach (XAttribute attrib in node.Attributes())
7607 {
7608 if (String.IsNullOrEmpty(attrib.Name.NamespaceName) || CompilerCore.WixNamespace == attrib.Name.Namespace)
7609 {
7610 switch (attrib.Name.LocalName)
7611 {
7612 case "Id":
7613 id = this.core.GetAttributeIdentifier(sourceLineNumbers, attrib);
7614 break;
7615 case "DiskId":
7616 diskId = this.core.GetAttributeIntegerValue(sourceLineNumbers, attrib, 1, short.MaxValue);
7617 this.core.CreateSimpleReference(sourceLineNumbers, "Media", diskId.ToString(CultureInfo.InvariantCulture.NumberFormat));
7618 break;
7619 case "FileCompression":
7620 fileCompression = this.core.GetAttributeYesNoValue(sourceLineNumbers, attrib);
7621 break;
7622 case "Language":
7623 language = this.core.GetAttributeLocalizableIntegerValue(sourceLineNumbers, attrib, 0, short.MaxValue);
7624 break;
7625 case "SourceFile":
7626 sourceFile = this.core.GetAttributeValue(sourceLineNumbers, attrib);
7627 break;
7628 default:
7629 this.core.UnexpectedAttribute(node, attrib);
7630 break;
7631 }
7632 }
7633 else
7634 {
7635 this.core.ParseExtensionAttribute(node, attrib);
7636 }
7637 }
7638
7639 if (null == id)
7640 {
7641 this.core.OnMessage(WixErrors.ExpectedAttribute(sourceLineNumbers, node.Name.LocalName, "Id"));
7642 }
7643
7644 if (null == language)
7645 {
7646 this.core.OnMessage(WixErrors.ExpectedAttribute(sourceLineNumbers, node.Name.LocalName, "Language"));
7647 }
7648
7649 if (null == sourceFile)
7650 {
7651 this.core.OnMessage(WixErrors.ExpectedAttribute(sourceLineNumbers, node.Name.LocalName, "SourceFile"));
7652 }
7653
7654 if (CompilerConstants.IntegerNotSet == diskId)
7655 {
7656 this.core.OnMessage(WixErrors.ExpectedAttributeInElementOrParent(sourceLineNumbers, node.Name.LocalName, "DiskId", "Directory"));
7657 diskId = CompilerConstants.IllegalInteger;
7658 }
7659
7660 foreach (XElement child in node.Elements())
7661 {
7662 if (CompilerCore.WixNamespace == child.Name.Namespace)
7663 {
7664 switch (child.Name.LocalName)
7665 {
7666 case "ConfigurationData":
7667 if (0 == configData.Length)
7668 {
7669 configData = this.ParseConfigurationDataElement(child);
7670 }
7671 else
7672 {
7673 configData = String.Concat(configData, ",", this.ParseConfigurationDataElement(child));
7674 }
7675 break;
7676 default:
7677 this.core.UnexpectedElement(node, child);
7678 break;
7679 }
7680 }
7681 else
7682 {
7683 this.core.ParseExtensionElement(node, child);
7684 }
7685 }
7686
7687 if (!this.core.EncounteredError)
7688 {
7689 Row row = this.core.CreateRow(sourceLineNumbers, "WixMerge", id);
7690 row[1] = language;
7691 row[2] = directoryId;
7692 row[3] = sourceFile;
7693 row[4] = diskId;
7694 if (YesNoType.Yes == fileCompression)
7695 {
7696 row[5] = 1;
7697 }
7698 else if (YesNoType.No == fileCompression)
7699 {
7700 row[5] = 0;
7701 }
7702 else // YesNoType.NotSet == fileCompression
7703 {
7704 // and we leave the column null
7705 }
7706 row[6] = configData;
7707 row[7] = Guid.Empty.ToString("B");
7708 }
7709 }
7710
7711 /// <summary>
7712 /// Parses a configuration data element.
7713 /// </summary>
7714 /// <param name="node">Element to parse.</param>
7715 /// <returns>String in format "name=value" with '%', ',' and '=' hex encoded.</returns>
7716 private string ParseConfigurationDataElement(XElement node)
7717 {
7718 SourceLineNumber sourceLineNumbers = Preprocessor.GetSourceLineNumbers(node);
7719 string name = null;
7720 string value = null;
7721
7722 foreach (XAttribute attrib in node.Attributes())
7723 {
7724 if (String.IsNullOrEmpty(attrib.Name.NamespaceName) || CompilerCore.WixNamespace == attrib.Name.Namespace)
7725 {
7726 switch (attrib.Name.LocalName)
7727 {
7728 case "Name":
7729 name = this.core.GetAttributeValue(sourceLineNumbers, attrib);
7730 break;
7731 case "Value":
7732 value = this.core.GetAttributeValue(sourceLineNumbers, attrib);
7733 break;
7734 default:
7735 this.core.UnexpectedAttribute(node, attrib);
7736 break;
7737 }
7738 }
7739 else
7740 {
7741 this.core.ParseExtensionAttribute(node, attrib);
7742 }
7743 }
7744
7745 if (null == name)
7746 {
7747 this.core.OnMessage(WixErrors.ExpectedAttribute(sourceLineNumbers, node.Name.LocalName, "Name"));
7748 }
7749 else // need to hex encode these characters
7750 {
7751 name = name.Replace("%", "%25");
7752 name = name.Replace("=", "%3D");
7753 name = name.Replace(",", "%2C");
7754 }
7755
7756 if (null == value)
7757 {
7758 this.core.OnMessage(WixErrors.ExpectedAttribute(sourceLineNumbers, node.Name.LocalName, "Value"));
7759 }
7760 else // need to hex encode these characters
7761 {
7762 value = value.Replace("%", "%25");
7763 value = value.Replace("=", "%3D");
7764 value = value.Replace(",", "%2C");
7765 }
7766
7767 this.core.ParseForExtensionElements(node);
7768
7769 return String.Concat(name, "=", value);
7770 }
7771
7772 /// <summary>
7773 /// Parses a merge reference element.
7774 /// </summary>
7775 /// <param name="node">Element to parse.</param>
7776 /// <param name="parentType">Parents complex reference type.</param>
7777 /// <param name="parentId">Identifier for parent feature or feature group.</param>
7778 private void ParseMergeRefElement(XElement node, ComplexReferenceParentType parentType, string parentId)
7779 {
7780 SourceLineNumber sourceLineNumbers = Preprocessor.GetSourceLineNumbers(node);
7781 string id = null;
7782 YesNoType primary = YesNoType.NotSet;
7783
7784 foreach (XAttribute attrib in node.Attributes())
7785 {
7786 if (String.IsNullOrEmpty(attrib.Name.NamespaceName) || CompilerCore.WixNamespace == attrib.Name.Namespace)
7787 {
7788 switch (attrib.Name.LocalName)
7789 {
7790 case "Id":
7791 id = this.core.GetAttributeIdentifierValue(sourceLineNumbers, attrib);
7792 this.core.CreateSimpleReference(sourceLineNumbers, "WixMerge", id);
7793 break;
7794 case "Primary":
7795 primary = this.core.GetAttributeYesNoValue(sourceLineNumbers, attrib);
7796 break;
7797 default:
7798 this.core.UnexpectedAttribute(node, attrib);
7799 break;
7800 }
7801 }
7802 else
7803 {
7804 this.core.ParseExtensionAttribute(node, attrib);
7805 }
7806 }
7807
7808 if (null == id)
7809 {
7810 this.core.OnMessage(WixErrors.ExpectedAttribute(sourceLineNumbers, node.Name.LocalName, "Id"));
7811 }
7812
7813 this.core.ParseForExtensionElements(node);
7814
7815 this.core.CreateComplexReference(sourceLineNumbers, parentType, parentId, null, ComplexReferenceChildType.Module, id, (YesNoType.Yes == primary));
7816 }
7817
7818 /// <summary>
7819 /// Parses a mime element.
7820 /// </summary>
7821 /// <param name="node">Element to parse.</param>
7822 /// <param name="extension">Identifier for parent extension.</param>
7823 /// <param name="componentId">Identifier for parent component.</param>
7824 /// <param name="parentAdvertised">Flag if the parent element is advertised.</param>
7825 /// <returns>Content type if this is the default for the MIME type.</returns>
7826 private string ParseMIMEElement(XElement node, string extension, string componentId, YesNoType parentAdvertised)
7827 {
7828 SourceLineNumber sourceLineNumbers = Preprocessor.GetSourceLineNumbers(node);
7829 string classId = null;
7830 string contentType = null;
7831 YesNoType advertise = parentAdvertised;
7832 YesNoType returnContentType = YesNoType.NotSet;
7833
7834 foreach (XAttribute attrib in node.Attributes())
7835 {
7836 if (String.IsNullOrEmpty(attrib.Name.NamespaceName) || CompilerCore.WixNamespace == attrib.Name.Namespace)
7837 {
7838 switch (attrib.Name.LocalName)
7839 {
7840 case "Advertise":
7841 advertise = this.core.GetAttributeYesNoValue(sourceLineNumbers, attrib);
7842 break;
7843 case "Class":
7844 classId = this.core.GetAttributeGuidValue(sourceLineNumbers, attrib, false);
7845 break;
7846 case "ContentType":
7847 contentType = this.core.GetAttributeValue(sourceLineNumbers, attrib);
7848 break;
7849 case "Default":
7850 returnContentType = this.core.GetAttributeYesNoValue(sourceLineNumbers, attrib);
7851 break;
7852 default:
7853 this.core.UnexpectedAttribute(node, attrib);
7854 break;
7855 }
7856 }
7857 else
7858 {
7859 this.core.ParseExtensionAttribute(node, attrib);
7860 }
7861 }
7862
7863 if (null == contentType)
7864 {
7865 this.core.OnMessage(WixErrors.ExpectedAttribute(sourceLineNumbers, node.Name.LocalName, "ContentType"));
7866 }
7867
7868 // if the advertise state has not been set, default to non-advertised
7869 if (YesNoType.NotSet == advertise)
7870 {
7871 advertise = YesNoType.No;
7872 }
7873
7874 this.core.ParseForExtensionElements(node);
7875
7876 if (YesNoType.Yes == advertise)
7877 {
7878 if (YesNoType.Yes != parentAdvertised)
7879 {
7880 this.core.OnMessage(WixErrors.AdvertiseStateMustMatch(sourceLineNumbers, advertise.ToString(), parentAdvertised.ToString()));
7881 }
7882
7883 if (!this.core.EncounteredError)
7884 {
7885 Row row = this.core.CreateRow(sourceLineNumbers, "MIME");
7886 row[0] = contentType;
7887 row[1] = extension;
7888 row[2] = classId;
7889 }
7890 }
7891 else if (YesNoType.No == advertise)
7892 {
7893 if (YesNoType.Yes == returnContentType && YesNoType.Yes == parentAdvertised)
7894 {
7895 this.core.OnMessage(WixErrors.CannotDefaultMismatchedAdvertiseStates(sourceLineNumbers));
7896 }
7897
7898 this.core.CreateRegistryRow(sourceLineNumbers, MsiInterop.MsidbRegistryRootClassesRoot, String.Concat("MIME\\Database\\Content Type\\", contentType), "Extension", String.Concat(".", extension), componentId);
7899 if (null != classId)
7900 {
7901 this.core.CreateRegistryRow(sourceLineNumbers, MsiInterop.MsidbRegistryRootClassesRoot, String.Concat("MIME\\Database\\Content Type\\", contentType), "CLSID", classId, componentId);
7902 }
7903 }
7904
7905 return YesNoType.Yes == returnContentType ? contentType : null;
7906 }
7907
7908 /// <summary>
7909 /// Parses a module element.
7910 /// </summary>
7911 /// <param name="node">Element to parse.</param>
7912 private void ParseModuleElement(XElement node)
7913 {
7914 SourceLineNumber sourceLineNumbers = Preprocessor.GetSourceLineNumbers(node);
7915 int codepage = 0;
7916 string moduleId = null;
7917 string version = null;
7918
7919 this.activeName = null;
7920 this.activeLanguage = null;
7921
7922 foreach (XAttribute attrib in node.Attributes())
7923 {
7924 if (String.IsNullOrEmpty(attrib.Name.NamespaceName) || CompilerCore.WixNamespace == attrib.Name.Namespace)
7925 {
7926 switch (attrib.Name.LocalName)
7927 {
7928 case "Id":
7929 this.activeName = this.core.GetAttributeValue(sourceLineNumbers, attrib);
7930 if ("PUT-MODULE-NAME-HERE" == this.activeName)
7931 {
7932 this.core.OnMessage(WixWarnings.PlaceholderValue(sourceLineNumbers, node.Name.LocalName, attrib.Name.LocalName, this.activeName));
7933 }
7934 else
7935 {
7936 this.activeName = this.core.GetAttributeIdentifierValue(sourceLineNumbers, attrib);
7937 }
7938 break;
7939 case "Codepage":
7940 codepage = this.core.GetAttributeCodePageValue(sourceLineNumbers, attrib);
7941 break;
7942 case "Guid":
7943 moduleId = this.core.GetAttributeGuidValue(sourceLineNumbers, attrib, false);
7944 this.core.OnMessage(WixWarnings.DeprecatedModuleGuidAttribute(sourceLineNumbers));
7945 break;
7946 case "Language":
7947 this.activeLanguage = this.core.GetAttributeLocalizableIntegerValue(sourceLineNumbers, attrib, 0, short.MaxValue);
7948 break;
7949 case "Version":
7950 version = this.core.GetAttributeVersionValue(sourceLineNumbers, attrib);
7951 break;
7952 default:
7953 this.core.UnexpectedAttribute(node, attrib);
7954 break;
7955 }
7956 }
7957 else
7958 {
7959 this.core.ParseExtensionAttribute(node, attrib);
7960 }
7961 }
7962
7963 if (null == this.activeName)
7964 {
7965 this.core.OnMessage(WixErrors.ExpectedAttribute(sourceLineNumbers, node.Name.LocalName, "Id"));
7966 }
7967
7968 if (null == this.activeLanguage)
7969 {
7970 this.core.OnMessage(WixErrors.ExpectedAttribute(sourceLineNumbers, node.Name.LocalName, "Language"));
7971 }
7972
7973 if (null == version)
7974 {
7975 this.core.OnMessage(WixErrors.ExpectedAttribute(sourceLineNumbers, node.Name.LocalName, "Version"));
7976 }
7977 else if (!CompilerCore.IsValidModuleOrBundleVersion(version))
7978 {
7979 this.core.OnMessage(WixWarnings.InvalidModuleOrBundleVersion(sourceLineNumbers, "Module", version));
7980 }
7981
7982 try
7983 {
7984 this.compilingModule = true; // notice that we are actually building a Merge Module here
7985 this.core.CreateActiveSection(this.activeName, SectionType.Module, codepage);
7986
7987 foreach (XElement child in node.Elements())
7988 {
7989 if (CompilerCore.WixNamespace == child.Name.Namespace)
7990 {
7991 switch (child.Name.LocalName)
7992 {
7993 case "AdminExecuteSequence":
7994 case "AdminUISequence":
7995 case "AdvertiseExecuteSequence":
7996 case "InstallExecuteSequence":
7997 case "InstallUISequence":
7998 this.ParseSequenceElement(child, child.Name.LocalName);
7999 break;
8000 case "AppId":
8001 this.ParseAppIdElement(child, null, YesNoType.Yes, null, null, null);
8002 break;
8003 case "Binary":
8004 this.ParseBinaryElement(child);
8005 break;
8006 case "Component":
8007 this.ParseComponentElement(child, ComplexReferenceParentType.Module, this.activeName, this.activeLanguage, CompilerConstants.IntegerNotSet, null, null);
8008 break;
8009 case "ComponentGroupRef":
8010 this.ParseComponentGroupRefElement(child, ComplexReferenceParentType.Module, this.activeName, this.activeLanguage);
8011 break;
8012 case "ComponentRef":
8013 this.ParseComponentRefElement(child, ComplexReferenceParentType.Module, this.activeName, this.activeLanguage);
8014 break;
8015 case "Configuration":
8016 this.ParseConfigurationElement(child);
8017 break;
8018 case "CustomAction":
8019 this.ParseCustomActionElement(child);
8020 break;
8021 case "CustomActionRef":
8022 this.ParseSimpleRefElement(child, "CustomAction");
8023 break;
8024 case "CustomTable":
8025 this.ParseCustomTableElement(child);
8026 break;
8027 case "Dependency":
8028 this.ParseDependencyElement(child);
8029 break;
8030 case "Directory":
8031 this.ParseDirectoryElement(child, null, CompilerConstants.IntegerNotSet, String.Empty);
8032 break;
8033 case "DirectoryRef":
8034 this.ParseDirectoryRefElement(child);
8035 break;
8036 case "EmbeddedChainer":
8037 this.ParseEmbeddedChainerElement(child);
8038 break;
8039 case "EmbeddedChainerRef":
8040 this.ParseSimpleRefElement(child, "MsiEmbeddedChainer");
8041 break;
8042 case "EnsureTable":
8043 this.ParseEnsureTableElement(child);
8044 break;
8045 case "Exclusion":
8046 this.ParseExclusionElement(child);
8047 break;
8048 case "Icon":
8049 this.ParseIconElement(child);
8050 break;
8051 case "IgnoreModularization":
8052 this.ParseIgnoreModularizationElement(child);
8053 break;
8054 case "IgnoreTable":
8055 this.ParseIgnoreTableElement(child);
8056 break;
8057 case "Package":
8058 this.ParsePackageElement(child, null, moduleId);
8059 break;
8060 case "Property":
8061 this.ParsePropertyElement(child);
8062 break;
8063 case "PropertyRef":
8064 this.ParseSimpleRefElement(child, "Property");
8065 break;
8066 case "SetDirectory":
8067 this.ParseSetDirectoryElement(child);
8068 break;
8069 case "SetProperty":
8070 this.ParseSetPropertyElement(child);
8071 break;
8072 case "SFPCatalog":
8073 string parentName = null;
8074 this.ParseSFPCatalogElement(child, ref parentName);
8075 break;
8076 case "Substitution":
8077 this.ParseSubstitutionElement(child);
8078 break;
8079 case "UI":
8080 this.ParseUIElement(child);
8081 break;
8082 case "UIRef":
8083 this.ParseSimpleRefElement(child, "WixUI");
8084 break;
8085 case "WixVariable":
8086 this.ParseWixVariableElement(child);
8087 break;
8088 default:
8089 this.core.UnexpectedElement(node, child);
8090 break;
8091 }
8092 }
8093 else
8094 {
8095 this.core.ParseExtensionElement(node, child);
8096 }
8097 }
8098
8099
8100 if (!this.core.EncounteredError)
8101 {
8102 Row row = this.core.CreateRow(sourceLineNumbers, "ModuleSignature");
8103 row[0] = this.activeName;
8104 row[1] = this.activeLanguage;
8105 row[2] = version;
8106 }
8107 }
8108 finally
8109 {
8110 this.compilingModule = false; // notice that we are no longer building a Merge Module here
8111 }
8112 }
8113
8114 /// <summary>
8115 /// Parses a patch creation element.
8116 /// </summary>
8117 /// <param name="node">The element to parse.</param>
8118 private void ParsePatchCreationElement(XElement node)
8119 {
8120 SourceLineNumber sourceLineNumbers = Preprocessor.GetSourceLineNumbers(node);
8121 bool clean = true; // Default is to clean
8122 int codepage = 0;
8123 string outputPath = null;
8124 bool productMismatches = false;
8125 string replaceGuids = String.Empty;
8126 string sourceList = null;
8127 string symbolFlags = null;
8128 string targetProducts = String.Empty;
8129 bool versionMismatches = false;
8130 bool wholeFiles = false;
8131
8132 foreach (XAttribute attrib in node.Attributes())
8133 {
8134 if (String.IsNullOrEmpty(attrib.Name.NamespaceName) || CompilerCore.WixNamespace == attrib.Name.Namespace)
8135 {
8136 switch (attrib.Name.LocalName)
8137 {
8138 case "Id":
8139 this.activeName = this.core.GetAttributeGuidValue(sourceLineNumbers, attrib, false);
8140 break;
8141 case "AllowMajorVersionMismatches":
8142 versionMismatches = YesNoType.Yes == this.core.GetAttributeYesNoValue(sourceLineNumbers, attrib);
8143 break;
8144 case "AllowProductCodeMismatches":
8145 productMismatches = YesNoType.Yes == this.core.GetAttributeYesNoValue(sourceLineNumbers, attrib);
8146 break;
8147 case "CleanWorkingFolder":
8148 clean = YesNoType.Yes == this.core.GetAttributeYesNoValue(sourceLineNumbers, attrib);
8149 break;
8150 case "Codepage":
8151 codepage = this.core.GetAttributeCodePageValue(sourceLineNumbers, attrib);
8152 break;
8153 case "OutputPath":
8154 outputPath = this.core.GetAttributeValue(sourceLineNumbers, attrib);
8155 break;
8156 case "SourceList":
8157 sourceList = this.core.GetAttributeValue(sourceLineNumbers, attrib);
8158 break;
8159 case "SymbolFlags":
8160 symbolFlags = String.Format(CultureInfo.InvariantCulture, "0x{0:x8}", this.core.GetAttributeLongValue(sourceLineNumbers, attrib, 0, uint.MaxValue));
8161 break;
8162 case "WholeFilesOnly":
8163 wholeFiles = YesNoType.Yes == this.core.GetAttributeYesNoValue(sourceLineNumbers, attrib);
8164 break;
8165 default:
8166 this.core.UnexpectedAttribute(node, attrib);
8167 break;
8168 }
8169 }
8170 else
8171 {
8172 this.core.ParseExtensionAttribute(node, attrib);
8173 }
8174 }
8175
8176 if (null == this.activeName)
8177 {
8178 this.core.OnMessage(WixErrors.ExpectedAttribute(sourceLineNumbers, node.Name.LocalName, "Id"));
8179 }
8180
8181 this.core.CreateActiveSection(this.activeName, SectionType.PatchCreation, codepage);
8182
8183 foreach (XElement child in node.Elements())
8184 {
8185 if (CompilerCore.WixNamespace == child.Name.Namespace)
8186 {
8187 switch (child.Name.LocalName)
8188 {
8189 case "Family":
8190 this.ParseFamilyElement(child);
8191 break;
8192 case "PatchInformation":
8193 this.ParsePatchInformationElement(child);
8194 break;
8195 case "PatchMetadata":
8196 this.ParsePatchMetadataElement(child);
8197 break;
8198 case "PatchProperty":
8199 this.ParsePatchPropertyElement(child, false);
8200 break;
8201 case "PatchSequence":
8202 this.ParsePatchSequenceElement(child);
8203 break;
8204 case "ReplacePatch":
8205 replaceGuids = String.Concat(replaceGuids, this.ParseReplacePatchElement(child));
8206 break;
8207 case "TargetProductCode":
8208 string targetProduct = this.ParseTargetProductCodeElement(child);
8209 if (0 < targetProducts.Length)
8210 {
8211 targetProducts = String.Concat(targetProducts, ";");
8212 }
8213 targetProducts = String.Concat(targetProducts, targetProduct);
8214 break;
8215 default:
8216 this.core.UnexpectedElement(node, child);
8217 break;
8218 }
8219 }
8220 else
8221 {
8222 this.core.ParseExtensionElement(node, child);
8223 }
8224 }
8225
8226 this.ProcessProperties(sourceLineNumbers, "PatchGUID", this.activeName);
8227 this.ProcessProperties(sourceLineNumbers, "AllowProductCodeMismatches", productMismatches ? "1" : "0");
8228 this.ProcessProperties(sourceLineNumbers, "AllowProductVersionMajorMismatches", versionMismatches ? "1" : "0");
8229 this.ProcessProperties(sourceLineNumbers, "DontRemoveTempFolderWhenFinished", clean ? "0" : "1");
8230 this.ProcessProperties(sourceLineNumbers, "IncludeWholeFilesOnly", wholeFiles ? "1" : "0");
8231
8232 if (null != symbolFlags)
8233 {
8234 this.ProcessProperties(sourceLineNumbers, "ApiPatchingSymbolFlags", symbolFlags);
8235 }
8236
8237 if (0 < replaceGuids.Length)
8238 {
8239 this.ProcessProperties(sourceLineNumbers, "ListOfPatchGUIDsToReplace", replaceGuids);
8240 }
8241
8242 if (0 < targetProducts.Length)
8243 {
8244 this.ProcessProperties(sourceLineNumbers, "ListOfTargetProductCodes", targetProducts);
8245 }
8246
8247 if (null != outputPath)
8248 {
8249 this.ProcessProperties(sourceLineNumbers, "PatchOutputPath", outputPath);
8250 }
8251
8252 if (null != sourceList)
8253 {
8254 this.ProcessProperties(sourceLineNumbers, "PatchSourceList", sourceList);
8255 }
8256 }
8257
8258 /// <summary>
8259 /// Parses a family element.
8260 /// </summary>
8261 /// <param name="node">The element to parse.</param>
8262 private void ParseFamilyElement(XElement node)
8263 {
8264 SourceLineNumber sourceLineNumbers = Preprocessor.GetSourceLineNumbers(node);
8265 int diskId = CompilerConstants.IntegerNotSet;
8266 string diskPrompt = null;
8267 string mediaSrcProp = null;
8268 string name = null;
8269 int sequenceStart = CompilerConstants.IntegerNotSet;
8270 string volumeLabel = null;
8271
8272 foreach (XAttribute attrib in node.Attributes())
8273 {
8274 if (String.IsNullOrEmpty(attrib.Name.NamespaceName) || CompilerCore.WixNamespace == attrib.Name.Namespace)
8275 {
8276 switch (attrib.Name.LocalName)
8277 {
8278 case "DiskId":
8279 diskId = this.core.GetAttributeIntegerValue(sourceLineNumbers, attrib, 1, short.MaxValue);
8280 break;
8281 case "DiskPrompt":
8282 diskPrompt = this.core.GetAttributeValue(sourceLineNumbers, attrib);
8283 break;
8284 case "MediaSrcProp":
8285 mediaSrcProp = this.core.GetAttributeValue(sourceLineNumbers, attrib);
8286 break;
8287 case "Name":
8288 name = this.core.GetAttributeValue(sourceLineNumbers, attrib);
8289 break;
8290 case "SequenceStart":
8291 sequenceStart = this.core.GetAttributeIntegerValue(sourceLineNumbers, attrib, 1, int.MaxValue);
8292 break;
8293 case "VolumeLabel":
8294 volumeLabel = this.core.GetAttributeValue(sourceLineNumbers, attrib);
8295 break;
8296 default:
8297 this.core.UnexpectedAttribute(node, attrib);
8298 break;
8299 }
8300 }
8301 else
8302 {
8303 this.core.ParseExtensionAttribute(node, attrib);
8304 }
8305 }
8306
8307 if (null == name)
8308 {
8309 this.core.OnMessage(WixErrors.ExpectedAttribute(sourceLineNumbers, node.Name.LocalName, "Name"));
8310 }
8311 else if (0 < name.Length)
8312 {
8313 if (8 < name.Length) // check the length
8314 {
8315 this.core.OnMessage(WixErrors.FamilyNameTooLong(sourceLineNumbers, node.Name.LocalName, "Name", name, name.Length));
8316 }
8317 else // check for illegal characters
8318 {
8319 foreach (char character in name)
8320 {
8321 if (!Char.IsLetterOrDigit(character) && '_' != character)
8322 {
8323 this.core.OnMessage(WixErrors.IllegalFamilyName(sourceLineNumbers, node.Name.LocalName, "Name", name));
8324 }
8325 }
8326 }
8327 }
8328
8329 foreach (XElement child in node.Elements())
8330 {
8331 if (CompilerCore.WixNamespace == child.Name.Namespace)
8332 {
8333 switch (child.Name.LocalName)
8334 {
8335 case "UpgradeImage":
8336 this.ParseUpgradeImageElement(child, name);
8337 break;
8338 case "ExternalFile":
8339 this.ParseExternalFileElement(child, name);
8340 break;
8341 case "ProtectFile":
8342 this.ParseProtectFileElement(child, name);
8343 break;
8344 default:
8345 this.core.UnexpectedElement(node, child);
8346 break;
8347 }
8348 }
8349 else
8350 {
8351 this.core.ParseExtensionElement(node, child);
8352 }
8353 }
8354
8355 if (!this.core.EncounteredError)
8356 {
8357 Row row = this.core.CreateRow(sourceLineNumbers, "ImageFamilies");
8358 row[0] = name;
8359 row[1] = mediaSrcProp;
8360 if (CompilerConstants.IntegerNotSet != diskId)
8361 {
8362 row[2] = diskId;
8363 }
8364
8365 if (CompilerConstants.IntegerNotSet != sequenceStart)
8366 {
8367 row[3] = sequenceStart;
8368 }
8369 row[4] = diskPrompt;
8370 row[5] = volumeLabel;
8371 }
8372 }
8373
8374 /// <summary>
8375 /// Parses an upgrade image element.
8376 /// </summary>
8377 /// <param name="node">The element to parse.</param>
8378 /// <param name="family">The family for this element.</param>
8379 private void ParseUpgradeImageElement(XElement node, string family)
8380 {
8381 SourceLineNumber sourceLineNumbers = Preprocessor.GetSourceLineNumbers(node);
8382 string sourceFile = null;
8383 string sourcePatch = null;
8384 List<string> symbols = new List<string>();
8385 string upgrade = null;
8386
8387 foreach (XAttribute attrib in node.Attributes())
8388 {
8389 if (String.IsNullOrEmpty(attrib.Name.NamespaceName) || CompilerCore.WixNamespace == attrib.Name.Namespace)
8390 {
8391 switch (attrib.Name.LocalName)
8392 {
8393 case "Id":
8394 upgrade = this.core.GetAttributeValue(sourceLineNumbers, attrib);
8395 if (13 < upgrade.Length)
8396 {
8397 this.core.OnMessage(WixErrors.IdentifierTooLongError(sourceLineNumbers, node.Name.LocalName, "Id", upgrade, 13));
8398 }
8399 break;
8400 case "SourceFile":
8401 case "src":
8402 if (null != sourceFile)
8403 {
8404 this.core.OnMessage(WixErrors.IllegalAttributeWithOtherAttribute(sourceLineNumbers, node.Name.LocalName, "src", "SourceFile"));
8405 }
8406
8407 if ("src" == attrib.Name.LocalName)
8408 {
8409 this.core.OnMessage(WixWarnings.DeprecatedAttribute(sourceLineNumbers, node.Name.LocalName, attrib.Name.LocalName, "SourceFile"));
8410 }
8411 sourceFile = this.core.GetAttributeValue(sourceLineNumbers, attrib);
8412 break;
8413 case "SourcePatch":
8414 case "srcPatch":
8415 if (null != sourcePatch)
8416 {
8417 this.core.OnMessage(WixErrors.IllegalAttributeWithOtherAttribute(sourceLineNumbers, node.Name.LocalName, "srcPatch", "SourcePatch"));
8418 }
8419
8420 if ("srcPatch" == attrib.Name.LocalName)
8421 {
8422 this.core.OnMessage(WixWarnings.DeprecatedAttribute(sourceLineNumbers, node.Name.LocalName, attrib.Name.LocalName, "SourcePatch"));
8423 }
8424 sourcePatch = this.core.GetAttributeValue(sourceLineNumbers, attrib);
8425 break;
8426 default:
8427 this.core.UnexpectedAttribute(node, attrib);
8428 break;
8429 }
8430 }
8431 else
8432 {
8433 this.core.ParseExtensionAttribute(node, attrib);
8434 }
8435 }
8436
8437 if (null == upgrade)
8438 {
8439 this.core.OnMessage(WixErrors.ExpectedAttribute(sourceLineNumbers, node.Name.LocalName, "Id"));
8440 }
8441
8442 if (null == sourceFile)
8443 {
8444 this.core.OnMessage(WixErrors.ExpectedAttribute(sourceLineNumbers, node.Name.LocalName, "SourceFile"));
8445 }
8446
8447 foreach (XElement child in node.Elements())
8448 {
8449 if (CompilerCore.WixNamespace == child.Name.Namespace)
8450 {
8451 switch (child.Name.LocalName)
8452 {
8453 case "SymbolPath":
8454 symbols.Add(this.ParseSymbolPathElement(child));
8455 break;
8456 case "TargetImage":
8457 this.ParseTargetImageElement(child, upgrade, family);
8458 break;
8459 case "UpgradeFile":
8460 this.ParseUpgradeFileElement(child, upgrade);
8461 break;
8462 default:
8463 this.core.UnexpectedElement(node, child);
8464 break;
8465 }
8466 }
8467 else
8468 {
8469 this.core.ParseExtensionElement(node, child);
8470 }
8471 }
8472
8473 if (!this.core.EncounteredError)
8474 {
8475 Row row = this.core.CreateRow(sourceLineNumbers, "UpgradedImages");
8476 row[0] = upgrade;
8477 row[1] = sourceFile;
8478 row[2] = sourcePatch;
8479 row[3] = String.Join(";", symbols);
8480 row[4] = family;
8481 }
8482 }
8483
8484 /// <summary>
8485 /// Parses an upgrade file element.
8486 /// </summary>
8487 /// <param name="node">The element to parse.</param>
8488 /// <param name="upgrade">The upgrade key for this element.</param>
8489 private void ParseUpgradeFileElement(XElement node, string upgrade)
8490 {
8491 SourceLineNumber sourceLineNumbers = Preprocessor.GetSourceLineNumbers(node);
8492 bool allowIgnoreOnError = false;
8493 string file = null;
8494 bool ignore = false;
8495 List<string> symbols = new List<string>();
8496 bool wholeFile = false;
8497
8498 foreach (XAttribute attrib in node.Attributes())
8499 {
8500 if (String.IsNullOrEmpty(attrib.Name.NamespaceName) || CompilerCore.WixNamespace == attrib.Name.Namespace)
8501 {
8502 switch (attrib.Name.LocalName)
8503 {
8504 case "AllowIgnoreOnError":
8505 allowIgnoreOnError = YesNoType.Yes == this.core.GetAttributeYesNoValue(sourceLineNumbers, attrib);
8506 break;
8507 case "File":
8508 file = this.core.GetAttributeValue(sourceLineNumbers, attrib);
8509 break;
8510 case "Ignore":
8511 ignore = YesNoType.Yes == this.core.GetAttributeYesNoValue(sourceLineNumbers, attrib);
8512 break;
8513 case "WholeFile":
8514 wholeFile = YesNoType.Yes == this.core.GetAttributeYesNoValue(sourceLineNumbers, attrib);
8515 break;
8516 default:
8517 this.core.UnexpectedAttribute(node, attrib);
8518 break;
8519 }
8520 }
8521 else
8522 {
8523 this.core.ParseExtensionAttribute(node, attrib);
8524 }
8525 }
8526
8527 if (null == file)
8528 {
8529 this.core.OnMessage(WixErrors.ExpectedAttribute(sourceLineNumbers, node.Name.LocalName, "File"));
8530 }
8531
8532 foreach (XElement child in node.Elements())
8533 {
8534 if (CompilerCore.WixNamespace == child.Name.Namespace)
8535 {
8536 switch (child.Name.LocalName)
8537 {
8538 case "SymbolPath":
8539 symbols.Add(this.ParseSymbolPathElement(child));
8540 break;
8541 default:
8542 this.core.UnexpectedElement(node, child);
8543 break;
8544 }
8545 }
8546 else
8547 {
8548 this.core.ParseExtensionElement(node, child);
8549 }
8550 }
8551
8552 if (!this.core.EncounteredError)
8553 {
8554 if (ignore)
8555 {
8556 Row row = this.core.CreateRow(sourceLineNumbers, "UpgradedFilesToIgnore");
8557 row[0] = upgrade;
8558 row[1] = file;
8559 }
8560 else
8561 {
8562 Row row = this.core.CreateRow(sourceLineNumbers, "UpgradedFiles_OptionalData");
8563 row[0] = upgrade;
8564 row[1] = file;
8565 row[2] = String.Join(";", symbols);
8566 row[3] = allowIgnoreOnError ? 1 : 0;
8567 row[4] = wholeFile ? 1 : 0;
8568 }
8569 }
8570 }
8571
8572 /// <summary>
8573 /// Parses a target image element.
8574 /// </summary>
8575 /// <param name="node">The element to parse.</param>
8576 /// <param name="upgrade">The upgrade key for this element.</param>
8577 /// <param name="family">The family key for this element.</param>
8578 private void ParseTargetImageElement(XElement node, string upgrade, string family)
8579 {
8580 SourceLineNumber sourceLineNumbers = Preprocessor.GetSourceLineNumbers(node);
8581 bool ignore = false;
8582 int order = CompilerConstants.IntegerNotSet;
8583 string sourceFile = null;
8584 string symbols = null;
8585 string target = null;
8586 string validation = null;
8587
8588 foreach (XAttribute attrib in node.Attributes())
8589 {
8590 if (String.IsNullOrEmpty(attrib.Name.NamespaceName) || CompilerCore.WixNamespace == attrib.Name.Namespace)
8591 {
8592 switch (attrib.Name.LocalName)
8593 {
8594 case "Id":
8595 target = this.core.GetAttributeValue(sourceLineNumbers, attrib);
8596 if (target.Length > 13)
8597 {
8598 this.core.OnMessage(WixErrors.IdentifierTooLongError(sourceLineNumbers, node.Name.LocalName, "Id", target, 13));
8599 }
8600 break;
8601 case "IgnoreMissingFiles":
8602 ignore = YesNoType.Yes == this.core.GetAttributeYesNoValue(sourceLineNumbers, attrib);
8603 break;
8604 case "Order":
8605 order = this.core.GetAttributeIntegerValue(sourceLineNumbers, attrib, int.MinValue + 2, int.MaxValue);
8606 break;
8607 case "SourceFile":
8608 case "src":
8609 if (null != sourceFile)
8610 {
8611 this.core.OnMessage(WixErrors.IllegalAttributeWithOtherAttribute(sourceLineNumbers, node.Name.LocalName, "src", "SourceFile"));
8612 }
8613
8614 if ("src" == attrib.Name.LocalName)
8615 {
8616 this.core.OnMessage(WixWarnings.DeprecatedAttribute(sourceLineNumbers, node.Name.LocalName, attrib.Name.LocalName, "SourceFile"));
8617 }
8618 sourceFile = this.core.GetAttributeValue(sourceLineNumbers, attrib);
8619 break;
8620 case "Validation":
8621 validation = this.core.GetAttributeValue(sourceLineNumbers, attrib);
8622 break;
8623 default:
8624 this.core.UnexpectedAttribute(node, attrib);
8625 break;
8626 }
8627 }
8628 else
8629 {
8630 this.core.ParseExtensionAttribute(node, attrib);
8631 }
8632 }
8633
8634 if (null == target)
8635 {
8636 this.core.OnMessage(WixErrors.ExpectedAttribute(sourceLineNumbers, node.Name.LocalName, "Id"));
8637 }
8638
8639 if (null == sourceFile)
8640 {
8641 this.core.OnMessage(WixErrors.ExpectedAttribute(sourceLineNumbers, node.Name.LocalName, "SourceFile"));
8642 }
8643
8644 if (CompilerConstants.IntegerNotSet == order)
8645 {
8646 this.core.OnMessage(WixErrors.ExpectedAttribute(sourceLineNumbers, node.Name.LocalName, "Order"));
8647 }
8648
8649 foreach (XElement child in node.Elements())
8650 {
8651 if (CompilerCore.WixNamespace == child.Name.Namespace)
8652 {
8653 switch (child.Name.LocalName)
8654 {
8655 case "SymbolPath":
8656 if (null != symbols)
8657 {
8658 symbols = String.Concat(symbols, ";", this.ParseSymbolPathElement(child));
8659 }
8660 else
8661 {
8662 symbols = this.ParseSymbolPathElement(child);
8663 }
8664 break;
8665 case "TargetFile":
8666 this.ParseTargetFileElement(child, target, family);
8667 break;
8668 default:
8669 this.core.UnexpectedElement(node, child);
8670 break;
8671 }
8672 }
8673 else
8674 {
8675 this.core.ParseExtensionElement(node, child);
8676 }
8677 }
8678
8679 if (!this.core.EncounteredError)
8680 {
8681 Row row = this.core.CreateRow(sourceLineNumbers, "TargetImages");
8682 row[0] = target;
8683 row[1] = sourceFile;
8684 row[2] = symbols;
8685 row[3] = upgrade;
8686 row[4] = order;
8687 row[5] = validation;
8688 row[6] = ignore ? 1 : 0;
8689 }
8690 }
8691
8692 /// <summary>
8693 /// Parses an upgrade file element.
8694 /// </summary>
8695 /// <param name="node">The element to parse.</param>
8696 /// <param name="target">The upgrade key for this element.</param>
8697 /// <param name="family">The family key for this element.</param>
8698 private void ParseTargetFileElement(XElement node, string target, string family)
8699 {
8700 SourceLineNumber sourceLineNumbers = Preprocessor.GetSourceLineNumbers(node);
8701 string file = null;
8702 string ignoreLengths = null;
8703 string ignoreOffsets = null;
8704 string protectLengths = null;
8705 string protectOffsets = null;
8706 string symbols = null;
8707
8708 foreach (XAttribute attrib in node.Attributes())
8709 {
8710 if (String.IsNullOrEmpty(attrib.Name.NamespaceName) || CompilerCore.WixNamespace == attrib.Name.Namespace)
8711 {
8712 switch (attrib.Name.LocalName)
8713 {
8714 case "Id":
8715 file = this.core.GetAttributeValue(sourceLineNumbers, attrib);
8716 break;
8717 default:
8718 this.core.UnexpectedAttribute(node, attrib);
8719 break;
8720 }
8721 }
8722 else
8723 {
8724 this.core.ParseExtensionAttribute(node, attrib);
8725 }
8726 }
8727
8728 if (null == file)
8729 {
8730 this.core.OnMessage(WixErrors.ExpectedAttribute(sourceLineNumbers, node.Name.LocalName, "Id"));
8731 }
8732
8733 foreach (XElement child in node.Elements())
8734 {
8735 if (CompilerCore.WixNamespace == child.Name.Namespace)
8736 {
8737 switch (child.Name.LocalName)
8738 {
8739 case "IgnoreRange":
8740 this.ParseRangeElement(child, ref ignoreOffsets, ref ignoreLengths);
8741 break;
8742 case "ProtectRange":
8743 this.ParseRangeElement(child, ref protectOffsets, ref protectLengths);
8744 break;
8745 case "SymbolPath":
8746 symbols = this.ParseSymbolPathElement(child);
8747 break;
8748 default:
8749 this.core.UnexpectedElement(node, child);
8750 break;
8751 }
8752 }
8753 else
8754 {
8755 this.core.ParseExtensionElement(node, child);
8756 }
8757 }
8758
8759 if (!this.core.EncounteredError)
8760 {
8761 Row row = this.core.CreateRow(sourceLineNumbers, "TargetFiles_OptionalData");
8762 row[0] = target;
8763 row[1] = file;
8764 row[2] = symbols;
8765 row[3] = ignoreOffsets;
8766 row[4] = ignoreLengths;
8767
8768 if (null != protectOffsets)
8769 {
8770 row[5] = protectOffsets;
8771
8772 Row row2 = this.core.CreateRow(sourceLineNumbers, "FamilyFileRanges");
8773 row2[0] = family;
8774 row2[1] = file;
8775 row2[2] = protectOffsets;
8776 row2[3] = protectLengths;
8777 }
8778 }
8779 }
8780
8781 /// <summary>
8782 /// Parses an external file element.
8783 /// </summary>
8784 /// <param name="node">The element to parse.</param>
8785 /// <param name="family">The family for this element.</param>
8786 private void ParseExternalFileElement(XElement node, string family)
8787 {
8788 SourceLineNumber sourceLineNumbers = Preprocessor.GetSourceLineNumbers(node);
8789 string file = null;
8790 string ignoreLengths = null;
8791 string ignoreOffsets = null;
8792 int order = CompilerConstants.IntegerNotSet;
8793 string protectLengths = null;
8794 string protectOffsets = null;
8795 string source = null;
8796 string symbols = null;
8797
8798 foreach (XAttribute attrib in node.Attributes())
8799 {
8800 if (String.IsNullOrEmpty(attrib.Name.NamespaceName) || CompilerCore.WixNamespace == attrib.Name.Namespace)
8801 {
8802 switch (attrib.Name.LocalName)
8803 {
8804 case "File":
8805 file = this.core.GetAttributeValue(sourceLineNumbers, attrib);
8806 break;
8807 case "Order":
8808 order = this.core.GetAttributeIntegerValue(sourceLineNumbers, attrib, int.MinValue + 2, int.MaxValue);
8809 break;
8810 case "Source":
8811 case "src":
8812 if (null != source)
8813 {
8814 this.core.OnMessage(WixErrors.IllegalAttributeWithOtherAttribute(sourceLineNumbers, node.Name.LocalName, "src", "Source"));
8815 }
8816
8817 if ("src" == attrib.Name.LocalName)
8818 {
8819 this.core.OnMessage(WixWarnings.DeprecatedAttribute(sourceLineNumbers, node.Name.LocalName, attrib.Name.LocalName, "Source"));
8820 }
8821 source = this.core.GetAttributeValue(sourceLineNumbers, attrib);
8822 break;
8823 default:
8824 this.core.UnexpectedAttribute(node, attrib);
8825 break;
8826 }
8827 }
8828 else
8829 {
8830 this.core.ParseExtensionAttribute(node, attrib);
8831 }
8832 }
8833
8834 if (null == file)
8835 {
8836 this.core.OnMessage(WixErrors.ExpectedAttribute(sourceLineNumbers, node.Name.LocalName, "File"));
8837 }
8838
8839 if (null == source)
8840 {
8841 this.core.OnMessage(WixErrors.ExpectedAttribute(sourceLineNumbers, node.Name.LocalName, "Source"));
8842 }
8843
8844 if (CompilerConstants.IntegerNotSet == order)
8845 {
8846 this.core.OnMessage(WixErrors.ExpectedAttribute(sourceLineNumbers, node.Name.LocalName, "Order"));
8847 }
8848
8849 foreach (XElement child in node.Elements())
8850 {
8851 if (CompilerCore.WixNamespace == child.Name.Namespace)
8852 {
8853 switch (child.Name.LocalName)
8854 {
8855 case "IgnoreRange":
8856 this.ParseRangeElement(child, ref ignoreOffsets, ref ignoreLengths);
8857 break;
8858 case "ProtectRange":
8859 this.ParseRangeElement(child, ref protectOffsets, ref protectLengths);
8860 break;
8861 case "SymbolPath":
8862 symbols = this.ParseSymbolPathElement(child);
8863 break;
8864 default:
8865 this.core.UnexpectedElement(node, child);
8866 break;
8867 }
8868 }
8869 else
8870 {
8871 this.core.ParseExtensionElement(node, child);
8872 }
8873 }
8874
8875 if (!this.core.EncounteredError)
8876 {
8877 Row row = this.core.CreateRow(sourceLineNumbers, "ExternalFiles");
8878 row[0] = family;
8879 row[1] = file;
8880 row[2] = source;
8881 row[3] = symbols;
8882 row[4] = ignoreOffsets;
8883 row[5] = ignoreLengths;
8884 if (null != protectOffsets)
8885 {
8886 row[6] = protectOffsets;
8887 }
8888
8889 if (CompilerConstants.IntegerNotSet != order)
8890 {
8891 row[7] = order;
8892 }
8893
8894 if (null != protectOffsets)
8895 {
8896 Row row2 = this.core.CreateRow(sourceLineNumbers, "FamilyFileRanges");
8897 row2[0] = family;
8898 row2[1] = file;
8899 row2[2] = protectOffsets;
8900 row2[3] = protectLengths;
8901 }
8902 }
8903 }
8904
8905 /// <summary>
8906 /// Parses a protect file element.
8907 /// </summary>
8908 /// <param name="node">The element to parse.</param>
8909 /// <param name="family">The family for this element.</param>
8910 private void ParseProtectFileElement(XElement node, string family)
8911 {
8912 SourceLineNumber sourceLineNumbers = Preprocessor.GetSourceLineNumbers(node);
8913 string file = null;
8914 string protectLengths = null;
8915 string protectOffsets = null;
8916
8917 foreach (XAttribute attrib in node.Attributes())
8918 {
8919 if (String.IsNullOrEmpty(attrib.Name.NamespaceName) || CompilerCore.WixNamespace == attrib.Name.Namespace)
8920 {
8921 switch (attrib.Name.LocalName)
8922 {
8923 case "File":
8924 file = this.core.GetAttributeValue(sourceLineNumbers, attrib);
8925 break;
8926 default:
8927 this.core.UnexpectedAttribute(node, attrib);
8928 break;
8929 }
8930 }
8931 else
8932 {
8933 this.core.ParseExtensionAttribute(node, attrib);
8934 }
8935 }
8936
8937 if (null == file)
8938 {
8939 this.core.OnMessage(WixErrors.ExpectedAttribute(sourceLineNumbers, node.Name.LocalName, "File"));
8940 }
8941
8942 foreach (XElement child in node.Elements())
8943 {
8944 if (CompilerCore.WixNamespace == child.Name.Namespace)
8945 {
8946 switch (child.Name.LocalName)
8947 {
8948 case "ProtectRange":
8949 this.ParseRangeElement(child, ref protectOffsets, ref protectLengths);
8950 break;
8951 default:
8952 this.core.UnexpectedElement(node, child);
8953 break;
8954 }
8955 }
8956 else
8957 {
8958 this.core.ParseExtensionElement(node, child);
8959 }
8960 }
8961
8962 if (null == protectOffsets || null == protectLengths)
8963 {
8964 this.core.OnMessage(WixErrors.ExpectedElement(sourceLineNumbers, node.Name.LocalName, "ProtectRange"));
8965 }
8966
8967 if (!this.core.EncounteredError)
8968 {
8969 Row row = this.core.CreateRow(sourceLineNumbers, "FamilyFileRanges");
8970 row[0] = family;
8971 row[1] = file;
8972 row[2] = protectOffsets;
8973 row[3] = protectLengths;
8974 }
8975 }
8976
8977 /// <summary>
8978 /// Parses a range element (ProtectRange, IgnoreRange, etc).
8979 /// </summary>
8980 /// <param name="node">The element to parse.</param>
8981 /// <param name="offsets">Reference to the offsets string.</param>
8982 /// <param name="lengths">Reference to the lengths string.</param>
8983 private void ParseRangeElement(XElement node, ref string offsets, ref string lengths)
8984 {
8985 SourceLineNumber sourceLineNumbers = Preprocessor.GetSourceLineNumbers(node);
8986 string length = null;
8987 string offset = null;
8988
8989 foreach (XAttribute attrib in node.Attributes())
8990 {
8991 if (String.IsNullOrEmpty(attrib.Name.NamespaceName) || CompilerCore.WixNamespace == attrib.Name.Namespace)
8992 {
8993 switch (attrib.Name.LocalName)
8994 {
8995 case "Length":
8996 length = this.core.GetAttributeValue(sourceLineNumbers, attrib);
8997 break;
8998 case "Offset":
8999 offset = this.core.GetAttributeValue(sourceLineNumbers, attrib);
9000 break;
9001 default:
9002 this.core.UnexpectedAttribute(node, attrib);
9003 break;
9004 }
9005 }
9006 else
9007 {
9008 this.core.ParseExtensionAttribute(node, attrib);
9009 }
9010 }
9011
9012 if (null == length)
9013 {
9014 this.core.OnMessage(WixErrors.ExpectedAttribute(sourceLineNumbers, node.Name.LocalName, "Length"));
9015 }
9016
9017 if (null == offset)
9018 {
9019 this.core.OnMessage(WixErrors.ExpectedAttribute(sourceLineNumbers, node.Name.LocalName, "Offset"));
9020 }
9021
9022 this.core.ParseForExtensionElements(node);
9023
9024 if (null != lengths)
9025 {
9026 lengths = String.Concat(lengths, ",", length);
9027 }
9028 else
9029 {
9030 lengths = length;
9031 }
9032
9033 if (null != offsets)
9034 {
9035 offsets = String.Concat(offsets, ",", offset);
9036 }
9037 else
9038 {
9039 offsets = offset;
9040 }
9041 }
9042
9043 /// <summary>
9044 /// Parses a patch property element.
9045 /// </summary>
9046 /// <param name="node">The element to parse.</param>
9047 /// <param name="patch">True if parsing an patch element.</param>
9048 private void ParsePatchPropertyElement(XElement node, bool patch)
9049 {
9050 SourceLineNumber sourceLineNumbers = Preprocessor.GetSourceLineNumbers(node);
9051 string name = null;
9052 string company = null;
9053 string value = null;
9054
9055 foreach (XAttribute attrib in node.Attributes())
9056 {
9057 if (String.IsNullOrEmpty(attrib.Name.NamespaceName) || CompilerCore.WixNamespace == attrib.Name.Namespace)
9058 {
9059 switch (attrib.Name.LocalName)
9060 {
9061 case "Id":
9062 case "Name":
9063 name = this.core.GetAttributeValue(sourceLineNumbers, attrib);
9064 break;
9065 case "Company":
9066 company = this.core.GetAttributeValue(sourceLineNumbers, attrib);
9067 break;
9068 case "Value":
9069 value = this.core.GetAttributeValue(sourceLineNumbers, attrib);
9070 break;
9071 default:
9072 this.core.UnexpectedAttribute(node, attrib);
9073 break;
9074 }
9075 }
9076 else
9077 {
9078 this.core.ParseExtensionAttribute(node, attrib);
9079 }
9080 }
9081
9082 if (null == name)
9083 {
9084 this.core.OnMessage(WixErrors.ExpectedAttribute(sourceLineNumbers, node.Name.LocalName, "Name"));
9085 }
9086
9087 if (null == value)
9088 {
9089 this.core.OnMessage(WixErrors.ExpectedAttribute(sourceLineNumbers, node.Name.LocalName, "Value"));
9090 }
9091
9092 this.core.ParseForExtensionElements(node);
9093
9094 if (patch)
9095 {
9096 // /Patch/PatchProperty goes directly into MsiPatchMetadata table
9097 Row row = this.core.CreateRow(sourceLineNumbers, "MsiPatchMetadata");
9098 row[0] = company;
9099 row[1] = name;
9100 row[2] = value;
9101 }
9102 else
9103 {
9104 if (null != company)
9105 {
9106 this.core.OnMessage(WixErrors.UnexpectedAttribute(sourceLineNumbers, node.Name.LocalName, "Company"));
9107 }
9108 this.ProcessProperties(sourceLineNumbers, name, value);
9109 }
9110 }
9111
9112 /// <summary>
9113 /// Parses a patch sequence element.
9114 /// </summary>
9115 /// <param name="node">The element to parse.</param>
9116 private void ParsePatchSequenceElement(XElement node)
9117 {
9118 SourceLineNumber sourceLineNumbers = Preprocessor.GetSourceLineNumbers(node);
9119 string family = null;
9120 string target = null;
9121 string sequence = null;
9122 int attributes = 0;
9123
9124 foreach (XAttribute attrib in node.Attributes())
9125 {
9126 if (String.IsNullOrEmpty(attrib.Name.NamespaceName) || CompilerCore.WixNamespace == attrib.Name.Namespace)
9127 {
9128 switch (attrib.Name.LocalName)
9129 {
9130 case "PatchFamily":
9131 family = this.core.GetAttributeIdentifierValue(sourceLineNumbers, attrib);
9132 break;
9133 case "ProductCode":
9134 if (null != target)
9135 {
9136 this.core.OnMessage(WixErrors.IllegalAttributeWithOtherAttributes(sourceLineNumbers, node.Name.LocalName, attrib.Name.LocalName, "Target", "TargetImage"));
9137 }
9138 target = this.core.GetAttributeGuidValue(sourceLineNumbers, attrib, false);
9139 break;
9140 case "Target":
9141 if (null != target)
9142 {
9143 this.core.OnMessage(WixErrors.IllegalAttributeWithOtherAttributes(sourceLineNumbers, node.Name.LocalName, attrib.Name.LocalName, "TargetImage", "ProductCode"));
9144 }
9145 this.core.OnMessage(WixWarnings.DeprecatedPatchSequenceTargetAttribute(sourceLineNumbers, node.Name.LocalName, attrib.Name.LocalName));
9146 target = this.core.GetAttributeValue(sourceLineNumbers, attrib);
9147 break;
9148 case "TargetImage":
9149 if (null != target)
9150 {
9151 this.core.OnMessage(WixErrors.IllegalAttributeWithOtherAttributes(sourceLineNumbers, node.Name.LocalName, attrib.Name.LocalName, "Target", "ProductCode"));
9152 }
9153 target = this.core.GetAttributeValue(sourceLineNumbers, attrib);
9154 this.core.CreateSimpleReference(sourceLineNumbers, "TargetImages", target);
9155 break;
9156 case "Sequence":
9157 sequence = this.core.GetAttributeVersionValue(sourceLineNumbers, attrib);
9158 break;
9159 case "Supersede":
9160 if (YesNoType.Yes == this.core.GetAttributeYesNoValue(sourceLineNumbers, attrib))
9161 {
9162 attributes |= 0x1;
9163 }
9164 break;
9165 default:
9166 this.core.UnexpectedAttribute(node, attrib);
9167 break;
9168 }
9169 }
9170 else
9171 {
9172 this.core.ParseExtensionAttribute(node, attrib);
9173 }
9174 }
9175
9176 if (null == family)
9177 {
9178 this.core.OnMessage(WixErrors.ExpectedAttribute(sourceLineNumbers, node.Name.LocalName, "PatchFamily"));
9179 }
9180
9181 this.core.ParseForExtensionElements(node);
9182
9183 if (!this.core.EncounteredError)
9184 {
9185 Row row = this.core.CreateRow(sourceLineNumbers, "PatchSequence");
9186 row[0] = family;
9187 row[1] = target;
9188 if (!String.IsNullOrEmpty(sequence))
9189 {
9190 row[2] = sequence;
9191 }
9192 row[3] = attributes;
9193 }
9194 }
9195
9196 /// <summary>
9197 /// Parses a TargetProductCode element.
9198 /// </summary>
9199 /// <param name="node">The element to parse.</param>
9200 /// <returns>The id from the node.</returns>
9201 private string ParseTargetProductCodeElement(XElement node)
9202 {
9203 SourceLineNumber sourceLineNumbers = Preprocessor.GetSourceLineNumbers(node);
9204 string id = null;
9205
9206 foreach (XAttribute attrib in node.Attributes())
9207 {
9208 if (String.IsNullOrEmpty(attrib.Name.NamespaceName) || CompilerCore.WixNamespace == attrib.Name.Namespace)
9209 {
9210 switch (attrib.Name.LocalName)
9211 {
9212 case "Id":
9213 id = this.core.GetAttributeValue(sourceLineNumbers, attrib);
9214 if (id.Length > 0 && "*" != id)
9215 {
9216 id = this.core.GetAttributeGuidValue(sourceLineNumbers, attrib, false);
9217 }
9218 break;
9219 default:
9220 this.core.UnexpectedAttribute(node, attrib);
9221 break;
9222 }
9223 }
9224 else
9225 {
9226 this.core.ParseExtensionAttribute(node, attrib);
9227 }
9228 }
9229
9230 if (null == id)
9231 {
9232 this.core.OnMessage(WixErrors.ExpectedAttribute(sourceLineNumbers, node.Name.LocalName, "Id"));
9233 }
9234
9235 this.core.ParseForExtensionElements(node);
9236
9237 return id;
9238 }
9239
9240 /// <summary>
9241 /// Parses a TargetProductCodes element.
9242 /// </summary>
9243 /// <param name="node">The element to parse.</param>
9244 private void ParseTargetProductCodesElement(XElement node)
9245 {
9246 SourceLineNumber sourceLineNumbers = Preprocessor.GetSourceLineNumbers(node);
9247 bool replace = false;
9248 List<string> targetProductCodes = new List<string>();
9249
9250 foreach (XAttribute attrib in node.Attributes())
9251 {
9252 if (String.IsNullOrEmpty(attrib.Name.NamespaceName) || CompilerCore.WixNamespace == attrib.Name.Namespace)
9253 {
9254 switch (attrib.Name.LocalName)
9255 {
9256 case "Replace":
9257 replace = YesNoType.Yes == this.core.GetAttributeYesNoValue(sourceLineNumbers, attrib);
9258 break;
9259 default:
9260 this.core.UnexpectedAttribute(node, attrib);
9261 break;
9262 }
9263 }
9264 else
9265 {
9266 this.core.ParseExtensionAttribute(node, attrib);
9267 }
9268 }
9269
9270 foreach (XElement child in node.Elements())
9271 {
9272 if (CompilerCore.WixNamespace == child.Name.Namespace)
9273 {
9274 switch (child.Name.LocalName)
9275 {
9276 case "TargetProductCode":
9277 string id = this.ParseTargetProductCodeElement(child);
9278 if (0 == String.CompareOrdinal("*", id))
9279 {
9280 this.core.OnMessage(WixErrors.IllegalAttributeValueWhenNested(sourceLineNumbers, child.Name.LocalName, "Id", id, node.Name.LocalName));
9281 }
9282 else
9283 {
9284 targetProductCodes.Add(id);
9285 }
9286 break;
9287 default:
9288 this.core.UnexpectedElement(node, child);
9289 break;
9290 }
9291 }
9292 else
9293 {
9294 this.core.ParseExtensionElement(node, child);
9295 }
9296 }
9297
9298 if (!this.core.EncounteredError)
9299 {
9300 // By default, target ProductCodes should be added.
9301 if (!replace)
9302 {
9303 Row row = this.core.CreateRow(sourceLineNumbers, "WixPatchTarget");
9304 row[0] = "*";
9305 }
9306
9307 foreach (string targetProductCode in targetProductCodes)
9308 {
9309 Row row = this.core.CreateRow(sourceLineNumbers, "WixPatchTarget");
9310 row[0] = targetProductCode;
9311 }
9312 }
9313 }
9314
9315 /// <summary>
9316 /// Parses a ReplacePatch element.
9317 /// </summary>
9318 /// <param name="node">The element to parse.</param>
9319 /// <returns>The id from the node.</returns>
9320 private string ParseReplacePatchElement(XElement node)
9321 {
9322 SourceLineNumber sourceLineNumbers = Preprocessor.GetSourceLineNumbers(node);
9323 string id = null;
9324
9325 foreach (XAttribute attrib in node.Attributes())
9326 {
9327 if (String.IsNullOrEmpty(attrib.Name.NamespaceName) || CompilerCore.WixNamespace == attrib.Name.Namespace)
9328 {
9329 switch (attrib.Name.LocalName)
9330 {
9331 case "Id":
9332 id = this.core.GetAttributeGuidValue(sourceLineNumbers, attrib, false);
9333 break;
9334 default:
9335 this.core.UnexpectedAttribute(node, attrib);
9336 break;
9337 }
9338 }
9339 else
9340 {
9341 this.core.ParseExtensionAttribute(node, attrib);
9342 }
9343 }
9344
9345 if (null == id)
9346 {
9347 this.core.OnMessage(WixErrors.ExpectedAttribute(sourceLineNumbers, node.Name.LocalName, "Id"));
9348 }
9349
9350 this.core.ParseForExtensionElements(node);
9351
9352 return id;
9353 }
9354
9355 /// <summary>
9356 /// Parses a symbol path element.
9357 /// </summary>
9358 /// <param name="node">The element to parse.</param>
9359 /// <returns>The path from the node.</returns>
9360 private string ParseSymbolPathElement(XElement node)
9361 {
9362 SourceLineNumber sourceLineNumbers = Preprocessor.GetSourceLineNumbers(node);
9363 string path = null;
9364
9365 foreach (XAttribute attrib in node.Attributes())
9366 {
9367 if (String.IsNullOrEmpty(attrib.Name.NamespaceName) || CompilerCore.WixNamespace == attrib.Name.Namespace)
9368 {
9369 switch (attrib.Name.LocalName)
9370 {
9371 case "Path":
9372 path = this.core.GetAttributeValue(sourceLineNumbers, attrib);
9373 break;
9374 default:
9375 this.core.UnexpectedAttribute(node, attrib);
9376 break;
9377 }
9378 }
9379 else
9380 {
9381 this.core.ParseExtensionAttribute(node, attrib);
9382 }
9383 }
9384
9385 if (null == path)
9386 {
9387 this.core.OnMessage(WixErrors.ExpectedAttribute(sourceLineNumbers, node.Name.LocalName, "Path"));
9388 }
9389
9390 this.core.ParseForExtensionElements(node);
9391
9392 return path;
9393 }
9394
9395 /// <summary>
9396 /// Parses an patch element.
9397 /// </summary>
9398 /// <param name="node">The element to parse.</param>
9399 private void ParsePatchElement(XElement node)
9400 {
9401 SourceLineNumber sourceLineNumbers = Preprocessor.GetSourceLineNumbers(node);
9402 string patchId = null;
9403 int codepage = 0;
9404 ////bool versionMismatches = false;
9405 ////bool productMismatches = false;
9406 bool allowRemoval = false;
9407 string classification = null;
9408 string clientPatchId = null;
9409 string description = null;
9410 string displayName = null;
9411 string comments = null;
9412 string manufacturer = null;
9413 YesNoType minorUpdateTargetRTM = YesNoType.NotSet;
9414 string moreInfoUrl = null;
9415 int optimizeCA = CompilerConstants.IntegerNotSet;
9416 YesNoType optimizedInstallMode = YesNoType.NotSet;
9417 string targetProductName = null;
9418 // string replaceGuids = String.Empty;
9419 int apiPatchingSymbolFlags = 0;
9420 bool optimizePatchSizeForLargeFiles = false;
9421
9422 foreach (XAttribute attrib in node.Attributes())
9423 {
9424 if (String.IsNullOrEmpty(attrib.Name.NamespaceName) || CompilerCore.WixNamespace == attrib.Name.Namespace)
9425 {
9426 switch (attrib.Name.LocalName)
9427 {
9428 case "Id":
9429 patchId = this.core.GetAttributeGuidValue(sourceLineNumbers, attrib, true);
9430 break;
9431 case "Codepage":
9432 codepage = this.core.GetAttributeCodePageValue(sourceLineNumbers, attrib);
9433 break;
9434 case "AllowMajorVersionMismatches":
9435 ////versionMismatches = (YesNoType.Yes == this.core.GetAttributeYesNoValue(sourceLineNumbers, attrib));
9436 break;
9437 case "AllowProductCodeMismatches":
9438 ////productMismatches = (YesNoType.Yes == this.core.GetAttributeYesNoValue(sourceLineNumbers, attrib));
9439 break;
9440 case "AllowRemoval":
9441 allowRemoval = (YesNoType.Yes == this.core.GetAttributeYesNoValue(sourceLineNumbers, attrib));
9442 break;
9443 case "Classification":
9444 classification = this.core.GetAttributeValue(sourceLineNumbers, attrib);
9445 break;
9446 case "ClientPatchId":
9447 clientPatchId = this.core.GetAttributeValue(sourceLineNumbers, attrib);
9448 break;
9449 case "Description":
9450 description = this.core.GetAttributeValue(sourceLineNumbers, attrib);
9451 break;
9452 case "DisplayName":
9453 displayName = this.core.GetAttributeValue(sourceLineNumbers, attrib);
9454 break;
9455 case "Comments":
9456 comments = this.core.GetAttributeValue(sourceLineNumbers, attrib);
9457 break;
9458 case "Manufacturer":
9459 manufacturer = this.core.GetAttributeValue(sourceLineNumbers, attrib);
9460 break;
9461 case "MinorUpdateTargetRTM":
9462 minorUpdateTargetRTM = this.core.GetAttributeYesNoValue(sourceLineNumbers, attrib);
9463 break;
9464 case "MoreInfoURL":
9465 moreInfoUrl = this.core.GetAttributeValue(sourceLineNumbers, attrib);
9466 break;
9467 case "OptimizedInstallMode":
9468 optimizedInstallMode = this.core.GetAttributeYesNoValue(sourceLineNumbers, attrib);
9469 break;
9470 case "TargetProductName":
9471 targetProductName = this.core.GetAttributeValue(sourceLineNumbers, attrib);
9472 break;
9473 case "ApiPatchingSymbolNoImagehlpFlag":
9474 apiPatchingSymbolFlags |= (YesNoType.Yes == this.core.GetAttributeYesNoValue(sourceLineNumbers, attrib)) ? (int)PatchAPI.PatchInterop.PatchSymbolFlagsType.PATCH_SYMBOL_NO_IMAGEHLP : 0;
9475 break;
9476 case "ApiPatchingSymbolNoFailuresFlag":
9477 apiPatchingSymbolFlags |= (YesNoType.Yes == this.core.GetAttributeYesNoValue(sourceLineNumbers, attrib)) ? (int)PatchAPI.PatchInterop.PatchSymbolFlagsType.PATCH_SYMBOL_NO_FAILURES : 0;
9478 break;
9479 case "ApiPatchingSymbolUndecoratedTooFlag":
9480 apiPatchingSymbolFlags |= (YesNoType.Yes == this.core.GetAttributeYesNoValue(sourceLineNumbers, attrib)) ? (int)PatchAPI.PatchInterop.PatchSymbolFlagsType.PATCH_SYMBOL_UNDECORATED_TOO : 0;
9481 break;
9482 case "OptimizePatchSizeForLargeFiles":
9483 optimizePatchSizeForLargeFiles = (YesNoType.Yes == this.core.GetAttributeYesNoValue(sourceLineNumbers, attrib));
9484 break;
9485 default:
9486 this.core.UnexpectedAttribute(node, attrib);
9487 break;
9488 }
9489 }
9490 else
9491 {
9492 this.core.ParseExtensionAttribute(node, attrib);
9493 }
9494 }
9495
9496 if (patchId == null || patchId == "*")
9497 {
9498 // auto-generate at compile time, since this value gets dispersed to several locations
9499 patchId = Common.GenerateGuid();
9500 }
9501 this.activeName = patchId;
9502
9503 if (null == this.activeName)
9504 {
9505 this.core.OnMessage(WixErrors.ExpectedAttribute(sourceLineNumbers, node.Name.LocalName, "Id"));
9506 }
9507 if (null == classification)
9508 {
9509 this.core.OnMessage(WixErrors.ExpectedAttribute(sourceLineNumbers, node.Name.LocalName, "Classification"));
9510 }
9511 if (null == clientPatchId)
9512 {
9513 clientPatchId = String.Concat("_", new Guid(patchId).ToString("N", CultureInfo.InvariantCulture).ToUpper(CultureInfo.InvariantCulture));
9514 }
9515 if (null == description)
9516 {
9517 this.core.OnMessage(WixErrors.ExpectedAttribute(sourceLineNumbers, node.Name.LocalName, "Description"));
9518 }
9519 if (null == displayName)
9520 {
9521 this.core.OnMessage(WixErrors.ExpectedAttribute(sourceLineNumbers, node.Name.LocalName, "DisplayName"));
9522 }
9523 if (null == manufacturer)
9524 {
9525 this.core.OnMessage(WixErrors.ExpectedAttribute(sourceLineNumbers, node.Name.LocalName, "Manufacturer"));
9526 }
9527
9528 this.core.CreateActiveSection(this.activeName, SectionType.Patch, codepage);
9529
9530 foreach (XElement child in node.Elements())
9531 {
9532 if (CompilerCore.WixNamespace == child.Name.Namespace)
9533 {
9534 switch (child.Name.LocalName)
9535 {
9536 case "PatchInformation":
9537 this.ParsePatchInformationElement(child);
9538 break;
9539 case "Media":
9540 this.ParseMediaElement(child, patchId);
9541 break;
9542 case "OptimizeCustomActions":
9543 optimizeCA = this.ParseOptimizeCustomActionsElement(child);
9544 break;
9545 case "PatchFamily":
9546 this.ParsePatchFamilyElement(child, ComplexReferenceParentType.Patch, patchId);
9547 break;
9548 case "PatchFamilyRef":
9549 this.ParsePatchFamilyRefElement(child, ComplexReferenceParentType.Patch, patchId);
9550 break;
9551 case "PatchFamilyGroup":
9552 this.ParsePatchFamilyGroupElement(child, ComplexReferenceParentType.Patch, patchId);
9553 break;
9554 case "PatchFamilyGroupRef":
9555 this.ParsePatchFamilyGroupRefElement(child, ComplexReferenceParentType.Patch, patchId);
9556 break;
9557 case "PatchProperty":
9558 this.ParsePatchPropertyElement(child, true);
9559 break;
9560 case "TargetProductCodes":
9561 this.ParseTargetProductCodesElement(child);
9562 break;
9563 default:
9564 this.core.UnexpectedElement(node, child);
9565 break;
9566 }
9567 }
9568 else
9569 {
9570 this.core.ParseExtensionElement(node, child);
9571 }
9572 }
9573
9574
9575 if (!this.core.EncounteredError)
9576 {
9577 Row patchIdRow = this.core.CreateRow(sourceLineNumbers, "WixPatchId");
9578 patchIdRow[0] = patchId;
9579 patchIdRow[1] = clientPatchId;
9580 patchIdRow[2] = optimizePatchSizeForLargeFiles ? 1 : 0;
9581 patchIdRow[3] = apiPatchingSymbolFlags;
9582
9583 if (allowRemoval)
9584 {
9585 Row row = this.core.CreateRow(sourceLineNumbers, "MsiPatchMetadata");
9586 row[0] = null;
9587 row[1] = "AllowRemoval";
9588 row[2] = allowRemoval ? "1" : "0";
9589 }
9590
9591 if (null != classification)
9592 {
9593 Row row = this.core.CreateRow(sourceLineNumbers, "MsiPatchMetadata");
9594 row[0] = null;
9595 row[1] = "Classification";
9596 row[2] = classification;
9597 }
9598
9599 // always generate the CreationTimeUTC
9600 {
9601 Row row = this.core.CreateRow(sourceLineNumbers, "MsiPatchMetadata");
9602 row[0] = null;
9603 row[1] = "CreationTimeUTC";
9604 row[2] = DateTime.UtcNow.ToString("MM-dd-yy HH:mm", CultureInfo.InvariantCulture);
9605 }
9606
9607 if (null != description)
9608 {
9609 Row row = this.core.CreateRow(sourceLineNumbers, "MsiPatchMetadata");
9610 row[0] = null;
9611 row[1] = "Description";
9612 row[2] = description;
9613 }
9614
9615 if (null != displayName)
9616 {
9617 Row row = this.core.CreateRow(sourceLineNumbers, "MsiPatchMetadata");
9618 row[0] = null;
9619 row[1] = "DisplayName";
9620 row[2] = displayName;
9621 }
9622
9623 if (null != manufacturer)
9624 {
9625 Row row = this.core.CreateRow(sourceLineNumbers, "MsiPatchMetadata");
9626 row[0] = null;
9627 row[1] = "ManufacturerName";
9628 row[2] = manufacturer;
9629 }
9630
9631 if (YesNoType.NotSet != minorUpdateTargetRTM)
9632 {
9633 Row row = this.core.CreateRow(sourceLineNumbers, "MsiPatchMetadata");
9634 row[0] = null;
9635 row[1] = "MinorUpdateTargetRTM";
9636 row[2] = YesNoType.Yes == minorUpdateTargetRTM ? "1" : "0";
9637 }
9638
9639 if (null != moreInfoUrl)
9640 {
9641 Row row = this.core.CreateRow(sourceLineNumbers, "MsiPatchMetadata");
9642 row[0] = null;
9643 row[1] = "MoreInfoURL";
9644 row[2] = moreInfoUrl;
9645 }
9646
9647 if (CompilerConstants.IntegerNotSet != optimizeCA)
9648 {
9649 Row row = this.core.CreateRow(sourceLineNumbers, "MsiPatchMetadata");
9650 row[0] = null;
9651 row[1] = "OptimizeCA";
9652 row[2] = optimizeCA.ToString(CultureInfo.InvariantCulture);
9653 }
9654
9655 if (YesNoType.NotSet != optimizedInstallMode)
9656 {
9657 Row row = this.core.CreateRow(sourceLineNumbers, "MsiPatchMetadata");
9658 row[0] = null;
9659 row[1] = "OptimizedInstallMode";
9660 row[2] = YesNoType.Yes == optimizedInstallMode ? "1" : "0";
9661 }
9662
9663 if (null != targetProductName)
9664 {
9665 Row row = this.core.CreateRow(sourceLineNumbers, "MsiPatchMetadata");
9666 row[0] = null;
9667 row[1] = "TargetProductName";
9668 row[2] = targetProductName;
9669 }
9670
9671 if (null != comments)
9672 {
9673 Row row = this.core.CreateRow(sourceLineNumbers, "WixPatchMetadata");
9674 row[0] = "Comments";
9675 row[1] = comments;
9676 }
9677 }
9678 // TODO: do something with versionMismatches and productMismatches
9679 }
9680
9681 /// <summary>
9682 /// Parses a PatchFamily element.
9683 /// </summary>
9684 /// <param name="node">The element to parse.</param>
9685 private void ParsePatchFamilyElement(XElement node, ComplexReferenceParentType parentType, string parentId)
9686 {
9687 SourceLineNumber sourceLineNumbers = Preprocessor.GetSourceLineNumbers(node);
9688 Identifier id = null;
9689 string productCode = null;
9690 string version = null;
9691 int attributes = 0;
9692
9693 foreach (XAttribute attrib in node.Attributes())
9694 {
9695 if (String.IsNullOrEmpty(attrib.Name.NamespaceName) || CompilerCore.WixNamespace == attrib.Name.Namespace)
9696 {
9697 switch (attrib.Name.LocalName)
9698 {
9699 case "Id":
9700 id = this.core.GetAttributeIdentifier(sourceLineNumbers, attrib);
9701 break;
9702 case "ProductCode":
9703 productCode = this.core.GetAttributeGuidValue(sourceLineNumbers, attrib, false);
9704 break;
9705 case "Version":
9706 version = this.core.GetAttributeVersionValue(sourceLineNumbers, attrib);
9707 break;
9708 case "Supersede":
9709 if (YesNoType.Yes == this.core.GetAttributeYesNoValue(sourceLineNumbers, attrib))
9710 {
9711 attributes |= 0x1;
9712 }
9713 break;
9714 default:
9715 this.core.UnexpectedAttribute(node, attrib);
9716 break;
9717 }
9718 }
9719 else
9720 {
9721 this.core.ParseExtensionAttribute(node, attrib);
9722 }
9723 }
9724
9725 if (null == id)
9726 {
9727 this.core.OnMessage(WixErrors.ExpectedAttribute(sourceLineNumbers, node.Name.LocalName, "Id"));
9728 id = Identifier.Invalid;
9729 }
9730
9731 if (String.IsNullOrEmpty(version))
9732 {
9733 this.core.OnMessage(WixErrors.ExpectedAttribute(sourceLineNumbers, node.Name.LocalName, "Version"));
9734 }
9735 else if (!CompilerCore.IsValidProductVersion(version))
9736 {
9737 this.core.OnMessage(WixErrors.InvalidProductVersion(sourceLineNumbers, version));
9738 }
9739
9740 // find unexpected child elements
9741 foreach (XElement child in node.Elements())
9742 {
9743 if (CompilerCore.WixNamespace == child.Name.Namespace)
9744 {
9745 switch (child.Name.LocalName)
9746 {
9747 case "All":
9748 this.ParseAllElement(child);
9749 break;
9750 case "BinaryRef":
9751 this.ParsePatchChildRefElement(child, "Binary");
9752 break;
9753 case "ComponentRef":
9754 this.ParsePatchChildRefElement(child, "Component");
9755 break;
9756 case "CustomActionRef":
9757 this.ParsePatchChildRefElement(child, "CustomAction");
9758 break;
9759 case "DirectoryRef":
9760 this.ParsePatchChildRefElement(child, "Directory");
9761 break;
9762 case "DigitalCertificateRef":
9763 this.ParsePatchChildRefElement(child, "MsiDigitalCertificate");
9764 break;
9765 case "FeatureRef":
9766 this.ParsePatchChildRefElement(child, "Feature");
9767 break;
9768 case "IconRef":
9769 this.ParsePatchChildRefElement(child, "Icon");
9770 break;
9771 case "PropertyRef":
9772 this.ParsePatchChildRefElement(child, "Property");
9773 break;
9774 case "UIRef":
9775 this.ParsePatchChildRefElement(child, "WixUI");
9776 break;
9777 default:
9778 this.core.UnexpectedElement(node, child);
9779 break;
9780 }
9781 }
9782 else
9783 {
9784 this.core.ParseExtensionElement(node, child);
9785 }
9786 }
9787
9788
9789 if (!this.core.EncounteredError)
9790 {
9791 Row row = this.core.CreateRow(sourceLineNumbers, "MsiPatchSequence", id);
9792 row[1] = productCode;
9793 row[2] = version;
9794 row[3] = attributes;
9795
9796 if (ComplexReferenceParentType.Unknown != parentType)
9797 {
9798 this.core.CreateComplexReference(sourceLineNumbers, parentType, parentId, null, ComplexReferenceChildType.PatchFamily, id.Id, ComplexReferenceParentType.Patch == parentType);
9799 }
9800 }
9801 }
9802
9803 /// <summary>
9804 /// Parses the All element under a PatchFamily.
9805 /// </summary>
9806 /// <param name="node">The element to parse.</param>
9807 private void ParseAllElement(XElement node)
9808 {
9809 SourceLineNumber sourceLineNumbers = Preprocessor.GetSourceLineNumbers(node);
9810
9811 // find unexpected attributes
9812 foreach (XAttribute attrib in node.Attributes())
9813 {
9814 if (String.IsNullOrEmpty(attrib.Name.NamespaceName) || CompilerCore.WixNamespace == attrib.Name.Namespace)
9815 {
9816 this.core.UnexpectedAttribute(node, attrib);
9817 }
9818 else
9819 {
9820 this.core.ParseExtensionAttribute(node, attrib);
9821 }
9822 }
9823
9824 this.core.ParseForExtensionElements(node);
9825
9826 // Always warn when using the All element.
9827 this.core.OnMessage(WixWarnings.AllChangesIncludedInPatch(sourceLineNumbers));
9828
9829 if (!this.core.EncounteredError)
9830 {
9831 this.core.CreatePatchFamilyChildReference(sourceLineNumbers, "*", "*");
9832 }
9833 }
9834
9835 /// <summary>
9836 /// Parses all reference elements under a PatchFamily.
9837 /// </summary>
9838 /// <param name="node">The element to parse.</param>
9839 /// <param name="tableName">Table that reference was made to.</param>
9840 private void ParsePatchChildRefElement(XElement node, string tableName)
9841 {
9842 SourceLineNumber sourceLineNumbers = Preprocessor.GetSourceLineNumbers(node);
9843 string id = null;
9844
9845 foreach (XAttribute attrib in node.Attributes())
9846 {
9847 if (String.IsNullOrEmpty(attrib.Name.NamespaceName) || CompilerCore.WixNamespace == attrib.Name.Namespace)
9848 {
9849 switch (attrib.Name.LocalName)
9850 {
9851 case "Id":
9852 id = this.core.GetAttributeIdentifierValue(sourceLineNumbers, attrib);
9853 break;
9854 default:
9855 this.core.UnexpectedAttribute(node, attrib);
9856 break;
9857 }
9858 }
9859 else
9860 {
9861 this.core.ParseExtensionAttribute(node, attrib);
9862 }
9863 }
9864
9865 if (null == id)
9866 {
9867 this.core.OnMessage(WixErrors.ExpectedAttribute(sourceLineNumbers, node.Name.LocalName, "Id"));
9868 }
9869
9870 this.core.ParseForExtensionElements(node);
9871
9872 if (!this.core.EncounteredError)
9873 {
9874 this.core.CreatePatchFamilyChildReference(sourceLineNumbers, tableName, id);
9875 }
9876 }
9877
9878 /// <summary>
9879 /// Parses a PatchBaseline element.
9880 /// </summary>
9881 /// <param name="node">The element to parse.</param>
9882 /// <param name="diskId">Media index from parent element.</param>
9883 private void ParsePatchBaselineElement(XElement node, int diskId)
9884 {
9885 SourceLineNumber sourceLineNumbers = Preprocessor.GetSourceLineNumbers(node);
9886 Identifier id = null;
9887 bool parsedValidate = false;
9888 TransformFlags validationFlags = TransformFlags.PatchTransformDefault;
9889
9890 foreach (XAttribute attrib in node.Attributes())
9891 {
9892 if (String.IsNullOrEmpty(attrib.Name.NamespaceName) || CompilerCore.WixNamespace == attrib.Name.Namespace)
9893 {
9894 switch (attrib.Name.LocalName)
9895 {
9896 case "Id":
9897 id = this.core.GetAttributeIdentifier(sourceLineNumbers, attrib);
9898 break;
9899 default:
9900 this.core.UnexpectedAttribute(node, attrib);
9901 break;
9902 }
9903 }
9904 else
9905 {
9906 this.core.ParseExtensionAttribute(node, attrib);
9907 }
9908 }
9909
9910 if (null == id)
9911 {
9912 this.core.OnMessage(WixErrors.ExpectedAttribute(sourceLineNumbers, node.Name.LocalName, "Id"));
9913 id = Identifier.Invalid;
9914 }
9915 else if (27 < id.Id.Length)
9916 {
9917 this.core.OnMessage(WixErrors.IdentifierTooLongError(sourceLineNumbers, node.Name.LocalName, "Id", id.Id, 27));
9918 }
9919
9920 foreach (XElement child in node.Elements())
9921 {
9922 if (CompilerCore.WixNamespace == child.Name.Namespace)
9923 {
9924 switch (child.Name.LocalName)
9925 {
9926 case "Validate":
9927 if (parsedValidate)
9928 {
9929 SourceLineNumber childSourceLineNumbers = Preprocessor.GetSourceLineNumbers(child);
9930 this.core.OnMessage(WixErrors.TooManyChildren(childSourceLineNumbers, node.Name.LocalName, child.Name.LocalName));
9931 }
9932 else
9933 {
9934 this.ParseValidateElement(child, ref validationFlags);
9935 parsedValidate = true;
9936 }
9937 break;
9938 default:
9939 this.core.UnexpectedElement(node, child);
9940 break;
9941 }
9942 }
9943 else
9944 {
9945 this.core.ParseExtensionElement(node, child);
9946 }
9947 }
9948
9949 if (!this.core.EncounteredError)
9950 {
9951 Row row = this.core.CreateRow(sourceLineNumbers, "WixPatchBaseline", id);
9952 row[1] = diskId;
9953 row[2] = (int)validationFlags;
9954 }
9955 }
9956
9957 /// <summary>
9958 /// Parses a Validate element.
9959 /// </summary>
9960 /// <param name="node">The element to parse.</param>
9961 /// <param name="validationFlags">TransformValidation flags to use when creating the authoring patch transform.</param>
9962 private void ParseValidateElement(XElement node, ref TransformFlags validationFlags)
9963 {
9964 SourceLineNumber sourceLineNumbers = Preprocessor.GetSourceLineNumbers(node);
9965
9966 foreach (XAttribute attrib in node.Attributes())
9967 {
9968 if (String.IsNullOrEmpty(attrib.Name.NamespaceName) || CompilerCore.WixNamespace == attrib.Name.Namespace)
9969 {
9970 switch (attrib.Name.LocalName)
9971 {
9972 case "ProductId":
9973 if (YesNoType.Yes == this.core.GetAttributeYesNoValue(sourceLineNumbers, attrib))
9974 {
9975 validationFlags |= TransformFlags.ValidateProduct;
9976 }
9977 else
9978 {
9979 validationFlags &= ~TransformFlags.ValidateProduct;
9980 }
9981 break;
9982 case "ProductLanguage":
9983 if (YesNoType.Yes == this.core.GetAttributeYesNoValue(sourceLineNumbers, attrib))
9984 {
9985 validationFlags |= TransformFlags.ValidateLanguage;
9986 }
9987 else
9988 {
9989 validationFlags &= ~TransformFlags.ValidateLanguage;
9990 }
9991 break;
9992 case "ProductVersion":
9993 string check = this.core.GetAttributeValue(sourceLineNumbers, attrib);
9994 validationFlags &= ~TransformFlags.ProductVersionMask;
9995 Wix.Validate.ProductVersionType productVersionType = Wix.Validate.ParseProductVersionType(check);
9996 switch (productVersionType)
9997 {
9998 case Wix.Validate.ProductVersionType.Major:
9999 validationFlags |= TransformFlags.ValidateMajorVersion;
10000 break;
10001 case Wix.Validate.ProductVersionType.Minor:
10002 validationFlags |= TransformFlags.ValidateMinorVersion;
10003 break;
10004 case Wix.Validate.ProductVersionType.Update:
10005 validationFlags |= TransformFlags.ValidateUpdateVersion;
10006 break;
10007 default:
10008 this.core.OnMessage(WixErrors.IllegalAttributeValue(sourceLineNumbers, node.Name.LocalName, "Version", check, "Major", "Minor", "Update"));
10009 break;
10010 }
10011 break;
10012 case "ProductVersionOperator":
10013 string op = this.core.GetAttributeValue(sourceLineNumbers, attrib);
10014 validationFlags &= ~TransformFlags.ProductVersionOperatorMask;
10015 Wix.Validate.ProductVersionOperatorType opType = Wix.Validate.ParseProductVersionOperatorType(op);
10016 switch (opType)
10017 {
10018 case Wix.Validate.ProductVersionOperatorType.Lesser:
10019 validationFlags |= TransformFlags.ValidateNewLessBaseVersion;
10020 break;
10021 case Wix.Validate.ProductVersionOperatorType.LesserOrEqual:
10022 validationFlags |= TransformFlags.ValidateNewLessEqualBaseVersion;
10023 break;
10024 case Wix.Validate.ProductVersionOperatorType.Equal:
10025 validationFlags |= TransformFlags.ValidateNewEqualBaseVersion;
10026 break;
10027 case Wix.Validate.ProductVersionOperatorType.GreaterOrEqual:
10028 validationFlags |= TransformFlags.ValidateNewGreaterEqualBaseVersion;
10029 break;
10030 case Wix.Validate.ProductVersionOperatorType.Greater:
10031 validationFlags |= TransformFlags.ValidateNewGreaterBaseVersion;
10032 break;
10033 default:
10034 this.core.OnMessage(WixErrors.IllegalAttributeValue(sourceLineNumbers, node.Name.LocalName, "Operator", op, "Lesser", "LesserOrEqual", "Equal", "GreaterOrEqual", "Greater"));
10035 break;
10036 }
10037 break;
10038 case "UpgradeCode":
10039 if (YesNoType.Yes == this.core.GetAttributeYesNoValue(sourceLineNumbers, attrib))
10040 {
10041 validationFlags |= TransformFlags.ValidateUpgradeCode;
10042 }
10043 else
10044 {
10045 validationFlags &= ~TransformFlags.ValidateUpgradeCode;
10046 }
10047 break;
10048 case "IgnoreAddExistingRow":
10049 if (YesNoType.Yes == this.core.GetAttributeYesNoValue(sourceLineNumbers, attrib))
10050 {
10051 validationFlags |= TransformFlags.ErrorAddExistingRow;
10052 }
10053 else
10054 {
10055 validationFlags &= ~TransformFlags.ErrorAddExistingRow;
10056 }
10057 break;
10058 case "IgnoreAddExistingTable":
10059 if (YesNoType.Yes == this.core.GetAttributeYesNoValue(sourceLineNumbers, attrib))
10060 {
10061 validationFlags |= TransformFlags.ErrorAddExistingTable;
10062 }
10063 else
10064 {
10065 validationFlags &= ~TransformFlags.ErrorAddExistingTable;
10066 }
10067 break;
10068 case "IgnoreDeleteMissingRow":
10069 if (YesNoType.Yes == this.core.GetAttributeYesNoValue(sourceLineNumbers, attrib))
10070 {
10071 validationFlags |= TransformFlags.ErrorDeleteMissingRow;
10072 }
10073 else
10074 {
10075 validationFlags &= ~TransformFlags.ErrorDeleteMissingRow;
10076 }
10077 break;
10078 case "IgnoreDeleteMissingTable":
10079 if (YesNoType.Yes == this.core.GetAttributeYesNoValue(sourceLineNumbers, attrib))
10080 {
10081 validationFlags |= TransformFlags.ErrorDeleteMissingTable;
10082 }
10083 else
10084 {
10085 validationFlags &= ~TransformFlags.ErrorDeleteMissingTable;
10086 }
10087 break;
10088 case "IgnoreUpdateMissingRow":
10089 if (YesNoType.Yes == this.core.GetAttributeYesNoValue(sourceLineNumbers, attrib))
10090 {
10091 validationFlags |= TransformFlags.ErrorUpdateMissingRow;
10092 }
10093 else
10094 {
10095 validationFlags &= ~TransformFlags.ErrorUpdateMissingRow;
10096 }
10097 break;
10098 case "IgnoreChangingCodePage":
10099 if (YesNoType.Yes == this.core.GetAttributeYesNoValue(sourceLineNumbers, attrib))
10100 {
10101 validationFlags |= TransformFlags.ErrorChangeCodePage;
10102 }
10103 else
10104 {
10105 validationFlags &= ~TransformFlags.ErrorChangeCodePage;
10106 }
10107 break;
10108 default:
10109 this.core.UnexpectedAttribute(node, attrib);
10110 break;
10111 }
10112 }
10113 else
10114 {
10115 this.core.ParseExtensionAttribute(node, attrib);
10116 }
10117 }
10118
10119 }
10120
10121 /// <summary>
10122 /// Adds a row to the properties table.
10123 /// </summary>
10124 /// <param name="sourceLineNumbers">Source line numbers.</param>
10125 /// <param name="name">Name of the property.</param>
10126 /// <param name="value">Value of the property.</param>
10127 private void ProcessProperties(SourceLineNumber sourceLineNumbers, string name, string value)
10128 {
10129 if (!this.core.EncounteredError)
10130 {
10131 Row row = this.core.CreateRow(sourceLineNumbers, "Properties");
10132 row[0] = name;
10133 row[1] = value;
10134 }
10135 }
10136
10137 /// <summary>
10138 /// Parses a dependency element.
10139 /// </summary>
10140 /// <param name="node">Element to parse.</param>
10141 private void ParseDependencyElement(XElement node)
10142 {
10143 SourceLineNumber sourceLineNumbers = Preprocessor.GetSourceLineNumbers(node);
10144 string requiredId = null;
10145 int requiredLanguage = CompilerConstants.IntegerNotSet;
10146 string requiredVersion = null;
10147
10148 foreach (XAttribute attrib in node.Attributes())
10149 {
10150 if (String.IsNullOrEmpty(attrib.Name.NamespaceName) || CompilerCore.WixNamespace == attrib.Name.Namespace)
10151 {
10152 switch (attrib.Name.LocalName)
10153 {
10154 case "RequiredId":
10155 requiredId = this.core.GetAttributeIdentifierValue(sourceLineNumbers, attrib);
10156 break;
10157 case "RequiredLanguage":
10158 requiredLanguage = this.core.GetAttributeIntegerValue(sourceLineNumbers, attrib, 0, short.MaxValue);
10159 break;
10160 case "RequiredVersion":
10161 requiredVersion = this.core.GetAttributeValue(sourceLineNumbers, attrib);
10162 break;
10163 default:
10164 this.core.UnexpectedAttribute(node, attrib);
10165 break;
10166 }
10167 }
10168 else
10169 {
10170 this.core.ParseExtensionAttribute(node, attrib);
10171 }
10172 }
10173
10174 if (null == requiredId)
10175 {
10176 this.core.OnMessage(WixErrors.ExpectedAttribute(sourceLineNumbers, node.Name.LocalName, "RequiredId"));
10177 requiredId = String.Empty;
10178 }
10179
10180 if (CompilerConstants.IntegerNotSet == requiredLanguage)
10181 {
10182 this.core.OnMessage(WixErrors.ExpectedAttribute(sourceLineNumbers, node.Name.LocalName, "RequiredLanguage"));
10183 requiredLanguage = CompilerConstants.IllegalInteger;
10184 }
10185
10186 this.core.ParseForExtensionElements(node);
10187
10188 if (!this.core.EncounteredError)
10189 {
10190 Row row = this.core.CreateRow(sourceLineNumbers, "ModuleDependency");
10191 row[0] = this.activeName;
10192 row[1] = this.activeLanguage;
10193 row[2] = requiredId;
10194 row[3] = requiredLanguage.ToString(CultureInfo.InvariantCulture);
10195 row[4] = requiredVersion;
10196 }
10197 }
10198
10199 /// <summary>
10200 /// Parses an exclusion element.
10201 /// </summary>
10202 /// <param name="node">Element to parse.</param>
10203 private void ParseExclusionElement(XElement node)
10204 {
10205 SourceLineNumber sourceLineNumbers = Preprocessor.GetSourceLineNumbers(node);
10206 string excludedId = null;
10207 int excludeExceptLanguage = CompilerConstants.IntegerNotSet;
10208 int excludeLanguage = CompilerConstants.IntegerNotSet;
10209 string excludedLanguageField = "0";
10210 string excludedMaxVersion = null;
10211 string excludedMinVersion = null;
10212
10213 foreach (XAttribute attrib in node.Attributes())
10214 {
10215 if (String.IsNullOrEmpty(attrib.Name.NamespaceName) || CompilerCore.WixNamespace == attrib.Name.Namespace)
10216 {
10217 switch (attrib.Name.LocalName)
10218 {
10219 case "ExcludedId":
10220 excludedId = this.core.GetAttributeIdentifierValue(sourceLineNumbers, attrib);
10221 break;
10222 case "ExcludeExceptLanguage":
10223 excludeExceptLanguage = this.core.GetAttributeIntegerValue(sourceLineNumbers, attrib, 0, short.MaxValue);
10224 break;
10225 case "ExcludeLanguage":
10226 excludeLanguage = this.core.GetAttributeIntegerValue(sourceLineNumbers, attrib, 0, short.MaxValue);
10227 break;
10228 case "ExcludedMaxVersion":
10229 excludedMaxVersion = this.core.GetAttributeValue(sourceLineNumbers, attrib);
10230 break;
10231 case "ExcludedMinVersion":
10232 excludedMinVersion = this.core.GetAttributeValue(sourceLineNumbers, attrib);
10233 break;
10234 default:
10235 this.core.UnexpectedAttribute(node, attrib);
10236 break;
10237 }
10238 }
10239 else
10240 {
10241 this.core.ParseExtensionAttribute(node, attrib);
10242 }
10243 }
10244
10245 if (null == excludedId)
10246 {
10247 this.core.OnMessage(WixErrors.ExpectedAttribute(sourceLineNumbers, node.Name.LocalName, "ExcludedId"));
10248 excludedId = String.Empty;
10249 }
10250
10251 if (CompilerConstants.IntegerNotSet != excludeExceptLanguage && CompilerConstants.IntegerNotSet != excludeLanguage)
10252 {
10253 this.core.OnMessage(WixErrors.IllegalModuleExclusionLanguageAttributes(sourceLineNumbers));
10254 }
10255 else if (CompilerConstants.IntegerNotSet != excludeExceptLanguage)
10256 {
10257 excludedLanguageField = Convert.ToString(-excludeExceptLanguage, CultureInfo.InvariantCulture);
10258 }
10259 else if (CompilerConstants.IntegerNotSet != excludeLanguage)
10260 {
10261 excludedLanguageField = Convert.ToString(excludeLanguage, CultureInfo.InvariantCulture);
10262 }
10263
10264 this.core.ParseForExtensionElements(node);
10265
10266 if (!this.core.EncounteredError)
10267 {
10268 Row row = this.core.CreateRow(sourceLineNumbers, "ModuleExclusion");
10269 row[0] = this.activeName;
10270 row[1] = this.activeLanguage;
10271 row[2] = excludedId;
10272 row[3] = excludedLanguageField;
10273 row[4] = excludedMinVersion;
10274 row[5] = excludedMaxVersion;
10275 }
10276 }
10277
10278 /// <summary>
10279 /// Parses a configuration element for a configurable merge module.
10280 /// </summary>
10281 /// <param name="node">Element to parse.</param>
10282 private void ParseConfigurationElement(XElement node)
10283 {
10284 SourceLineNumber sourceLineNumbers = Preprocessor.GetSourceLineNumbers(node);
10285 int attributes = 0;
10286 string contextData = null;
10287 string defaultValue = null;
10288 string description = null;
10289 string displayName = null;
10290 int format = CompilerConstants.IntegerNotSet;
10291 string helpKeyword = null;
10292 string helpLocation = null;
10293 string name = null;
10294 string type = null;
10295
10296 foreach (XAttribute attrib in node.Attributes())
10297 {
10298 if (String.IsNullOrEmpty(attrib.Name.NamespaceName) || CompilerCore.WixNamespace == attrib.Name.Namespace)
10299 {
10300 switch (attrib.Name.LocalName)
10301 {
10302 case "Name":
10303 name = this.core.GetAttributeIdentifierValue(sourceLineNumbers, attrib);
10304 break;
10305 case "ContextData":
10306 contextData = this.core.GetAttributeValue(sourceLineNumbers, attrib);
10307 break;
10308 case "Description":
10309 description = this.core.GetAttributeValue(sourceLineNumbers, attrib);
10310 break;
10311 case "DefaultValue":
10312 defaultValue = this.core.GetAttributeValue(sourceLineNumbers, attrib);
10313 break;
10314 case "DisplayName":
10315 displayName = this.core.GetAttributeValue(sourceLineNumbers, attrib);
10316 break;
10317 case "Format":
10318 string formatStr = this.core.GetAttributeValue(sourceLineNumbers, attrib);
10319 if (0 < formatStr.Length)
10320 {
10321 Wix.Configuration.FormatType formatType = Wix.Configuration.ParseFormatType(formatStr);
10322 switch (formatType)
10323 {
10324 case Wix.Configuration.FormatType.Text:
10325 format = 0;
10326 break;
10327 case Wix.Configuration.FormatType.Key:
10328 format = 1;
10329 break;
10330 case Wix.Configuration.FormatType.Integer:
10331 format = 2;
10332 break;
10333 case Wix.Configuration.FormatType.Bitfield:
10334 format = 3;
10335 break;
10336 default:
10337 this.core.OnMessage(WixErrors.IllegalAttributeValue(sourceLineNumbers, node.Name.LocalName, "Format", formatStr, "Text", "Key", "Integer", "Bitfield"));
10338 break;
10339 }
10340 }
10341 break;
10342 case "HelpKeyword":
10343 helpKeyword = this.core.GetAttributeValue(sourceLineNumbers, attrib);
10344 break;
10345 case "HelpLocation":
10346 helpLocation = this.core.GetAttributeValue(sourceLineNumbers, attrib);
10347 break;
10348 case "KeyNoOrphan":
10349 if (YesNoType.Yes == this.core.GetAttributeYesNoValue(sourceLineNumbers, attrib))
10350 {
10351 attributes |= MsiInterop.MsidbMsmConfigurableOptionKeyNoOrphan;
10352 }
10353 break;
10354 case "NonNullable":
10355 if (YesNoType.Yes == this.core.GetAttributeYesNoValue(sourceLineNumbers, attrib))
10356 {
10357 attributes |= MsiInterop.MsidbMsmConfigurableOptionNonNullable;
10358 }
10359 break;
10360 case "Type":
10361 type = this.core.GetAttributeValue(sourceLineNumbers, attrib);
10362 break;
10363 default:
10364 this.core.UnexpectedAttribute(node, attrib);
10365 break;
10366 }
10367 }
10368 else
10369 {
10370 this.core.ParseExtensionAttribute(node, attrib);
10371 }
10372 }
10373
10374 if (null == name)
10375 {
10376 this.core.OnMessage(WixErrors.ExpectedAttribute(sourceLineNumbers, node.Name.LocalName, "Name"));
10377 name = String.Empty;
10378 }
10379
10380 if (CompilerConstants.IntegerNotSet == format)
10381 {
10382 this.core.OnMessage(WixErrors.ExpectedAttribute(sourceLineNumbers, node.Name.LocalName, "Format"));
10383 format = CompilerConstants.IllegalInteger;
10384 }
10385
10386 this.core.ParseForExtensionElements(node);
10387
10388 if (!this.core.EncounteredError)
10389 {
10390 Row row = this.core.CreateRow(sourceLineNumbers, "ModuleConfiguration");
10391 row[0] = name;
10392 row[1] = format;
10393 row[2] = type;
10394 row[3] = contextData;
10395 row[4] = defaultValue;
10396 row[5] = attributes;
10397 row[6] = displayName;
10398 row[7] = description;
10399 row[8] = helpLocation;
10400 row[9] = helpKeyword;
10401 }
10402 }
10403
10404 /// <summary>
10405 /// Parses a substitution element for a configurable merge module.
10406 /// </summary>
10407 /// <param name="node">Element to parse.</param>
10408 private void ParseSubstitutionElement(XElement node)
10409 {
10410 SourceLineNumber sourceLineNumbers = Preprocessor.GetSourceLineNumbers(node);
10411 string column = null;
10412 string rowKeys = null;
10413 string table = null;
10414 string value = null;
10415
10416 foreach (XAttribute attrib in node.Attributes())
10417 {
10418 if (String.IsNullOrEmpty(attrib.Name.NamespaceName) || CompilerCore.WixNamespace == attrib.Name.Namespace)
10419 {
10420 switch (attrib.Name.LocalName)
10421 {
10422 case "Column":
10423 column = this.core.GetAttributeIdentifierValue(sourceLineNumbers, attrib);
10424 break;
10425 case "Row":
10426 rowKeys = this.core.GetAttributeValue(sourceLineNumbers, attrib);
10427 break;
10428 case "Table":
10429 table = this.core.GetAttributeIdentifierValue(sourceLineNumbers, attrib);
10430 break;
10431 case "Value":
10432 value = this.core.GetAttributeValue(sourceLineNumbers, attrib);
10433 break;
10434 default:
10435 this.core.UnexpectedAttribute(node, attrib);
10436 break;
10437 }
10438 }
10439 else
10440 {
10441 this.core.ParseExtensionAttribute(node, attrib);
10442 }
10443 }
10444
10445 if (null == column)
10446 {
10447 this.core.OnMessage(WixErrors.ExpectedAttribute(sourceLineNumbers, node.Name.LocalName, "Column"));
10448 column = String.Empty;
10449 }
10450
10451 if (null == table)
10452 {
10453 this.core.OnMessage(WixErrors.ExpectedAttribute(sourceLineNumbers, node.Name.LocalName, "Table"));
10454 table = String.Empty;
10455 }
10456
10457 if (null == rowKeys)
10458 {
10459 this.core.OnMessage(WixErrors.ExpectedAttribute(sourceLineNumbers, node.Name.LocalName, "Row"));
10460 }
10461
10462 this.core.ParseForExtensionElements(node);
10463
10464 if (!this.core.EncounteredError)
10465 {
10466 Row row = this.core.CreateRow(sourceLineNumbers, "ModuleSubstitution");
10467 row[0] = table;
10468 row[1] = rowKeys;
10469 row[2] = column;
10470 row[3] = value;
10471 }
10472 }
10473
10474 /// <summary>
10475 /// Parses an IgnoreTable element.
10476 /// </summary>
10477 /// <param name="node">Element to parse.</param>
10478 private void ParseIgnoreTableElement(XElement node)
10479 {
10480 SourceLineNumber sourceLineNumbers = Preprocessor.GetSourceLineNumbers(node);
10481 string id = null;
10482
10483 foreach (XAttribute attrib in node.Attributes())
10484 {
10485 if (String.IsNullOrEmpty(attrib.Name.NamespaceName) || CompilerCore.WixNamespace == attrib.Name.Namespace)
10486 {
10487 switch (attrib.Name.LocalName)
10488 {
10489 case "Id":
10490 id = this.core.GetAttributeIdentifierValue(sourceLineNumbers, attrib);
10491 break;
10492 default:
10493 this.core.UnexpectedAttribute(node, attrib);
10494 break;
10495 }
10496 }
10497 else
10498 {
10499 this.core.ParseExtensionAttribute(node, attrib);
10500 }
10501 }
10502
10503 if (null == id)
10504 {
10505 this.core.OnMessage(WixErrors.ExpectedAttribute(sourceLineNumbers, node.Name.LocalName, "Id"));
10506 }
10507
10508 this.core.ParseForExtensionElements(node);
10509
10510 if (!this.core.EncounteredError)
10511 {
10512 Row row = this.core.CreateRow(sourceLineNumbers, "ModuleIgnoreTable");
10513 row[0] = id;
10514 }
10515 }
10516
10517 /// <summary>
10518 /// Parses an odbc driver or translator element.
10519 /// </summary>
10520 /// <param name="node">Element to parse.</param>
10521 /// <param name="componentId">Identifier of parent component.</param>
10522 /// <param name="fileId">Default identifer for driver/translator file.</param>
10523 /// <param name="table">Table we're processing for.</param>
10524 private void ParseODBCDriverOrTranslator(XElement node, string componentId, string fileId, TableDefinition table)
10525 {
10526 SourceLineNumber sourceLineNumbers = Preprocessor.GetSourceLineNumbers(node);
10527 Identifier id = null;
10528 string driver = fileId;
10529 string name = null;
10530 string setup = fileId;
10531
10532 foreach (XAttribute attrib in node.Attributes())
10533 {
10534 if (String.IsNullOrEmpty(attrib.Name.NamespaceName) || CompilerCore.WixNamespace == attrib.Name.Namespace)
10535 {
10536 switch (attrib.Name.LocalName)
10537 {
10538 case "Id":
10539 id = this.core.GetAttributeIdentifier(sourceLineNumbers, attrib);
10540 break;
10541 case "File":
10542 driver = this.core.GetAttributeIdentifierValue(sourceLineNumbers, attrib);
10543 this.core.CreateSimpleReference(sourceLineNumbers, "File", driver);
10544 break;
10545 case "Name":
10546 name = this.core.GetAttributeValue(sourceLineNumbers, attrib);
10547 break;
10548 case "SetupFile":
10549 setup = this.core.GetAttributeIdentifierValue(sourceLineNumbers, attrib);
10550 this.core.CreateSimpleReference(sourceLineNumbers, "File", setup);
10551 break;
10552 default:
10553 this.core.UnexpectedAttribute(node, attrib);
10554 break;
10555 }
10556 }
10557 else
10558 {
10559 this.core.ParseExtensionAttribute(node, attrib);
10560 }
10561 }
10562
10563 if (null == name)
10564 {
10565 this.core.OnMessage(WixErrors.ExpectedAttribute(sourceLineNumbers, node.Name.LocalName, "Name"));
10566 }
10567
10568 if (null == id)
10569 {
10570 id = this.core.CreateIdentifier("odb", name, fileId, setup);
10571 }
10572
10573 // drivers have a few possible children
10574 if ("ODBCDriver" == table.Name)
10575 {
10576 // process any data sources for the driver
10577 foreach (XElement child in node.Elements())
10578 {
10579 if (CompilerCore.WixNamespace == child.Name.Namespace)
10580 {
10581 switch (child.Name.LocalName)
10582 {
10583 case "ODBCDataSource":
10584 string ignoredKeyPath = null;
10585 this.ParseODBCDataSource(child, componentId, name, out ignoredKeyPath);
10586 break;
10587 case "Property":
10588 this.ParseODBCProperty(child, id.Id, "ODBCAttribute");
10589 break;
10590 default:
10591 this.core.UnexpectedElement(node, child);
10592 break;
10593 }
10594 }
10595 else
10596 {
10597 this.core.ParseExtensionElement(node, child);
10598 }
10599 }
10600 }
10601 else
10602 {
10603 this.core.ParseForExtensionElements(node);
10604 }
10605
10606 if (!this.core.EncounteredError)
10607 {
10608 Row row = this.core.CreateRow(sourceLineNumbers, table.Name, id);
10609 row[1] = componentId;
10610 row[2] = name;
10611 row[3] = driver;
10612 row[4] = setup;
10613 }
10614 }
10615
10616 /// <summary>
10617 /// Parses a Property element underneath an ODBC driver or translator.
10618 /// </summary>
10619 /// <param name="node">Element to parse.</param>
10620 /// <param name="parentId">Identifier of parent driver or translator.</param>
10621 /// <param name="tableName">Name of the table to create property in.</param>
10622 private void ParseODBCProperty(XElement node, string parentId, string tableName)
10623 {
10624 SourceLineNumber sourceLineNumbers = Preprocessor.GetSourceLineNumbers(node);
10625 string id = null;
10626 string propertyValue = null;
10627
10628 foreach (XAttribute attrib in node.Attributes())
10629 {
10630 if (String.IsNullOrEmpty(attrib.Name.NamespaceName) || CompilerCore.WixNamespace == attrib.Name.Namespace)
10631 {
10632 switch (attrib.Name.LocalName)
10633 {
10634 case "Id":
10635 id = this.core.GetAttributeValue(sourceLineNumbers, attrib);
10636 break;
10637 case "Value":
10638 propertyValue = this.core.GetAttributeValue(sourceLineNumbers, attrib);
10639 break;
10640 default:
10641 this.core.UnexpectedAttribute(node, attrib);
10642 break;
10643 }
10644 }
10645 else
10646 {
10647 this.core.ParseExtensionAttribute(node, attrib);
10648 }
10649 }
10650
10651 if (null == id)
10652 {
10653 this.core.OnMessage(WixErrors.ExpectedAttribute(sourceLineNumbers, node.Name.LocalName, "Id"));
10654 }
10655
10656 this.core.ParseForExtensionElements(node);
10657
10658 if (!this.core.EncounteredError)
10659 {
10660 Row row = this.core.CreateRow(sourceLineNumbers, tableName);
10661 row[0] = parentId;
10662 row[1] = id;
10663 row[2] = propertyValue;
10664 }
10665 }
10666
10667 /// <summary>
10668 /// Parse an odbc data source element.
10669 /// </summary>
10670 /// <param name="node">Element to parse.</param>
10671 /// <param name="componentId">Identifier of parent component.</param>
10672 /// <param name="driverName">Default name of driver.</param>
10673 /// <param name="possibleKeyPath">Identifier of this element in case it is a keypath.</param>
10674 /// <returns>Yes if this element was marked as the parent component's key path, No if explicitly marked as not being a key path, or NotSet otherwise.</returns>
10675 private YesNoType ParseODBCDataSource(XElement node, string componentId, string driverName, out string possibleKeyPath)
10676 {
10677 SourceLineNumber sourceLineNumbers = Preprocessor.GetSourceLineNumbers(node);
10678 Identifier id = null;
10679 YesNoType keyPath = YesNoType.NotSet;
10680 string name = null;
10681 int registration = CompilerConstants.IntegerNotSet;
10682
10683 foreach (XAttribute attrib in node.Attributes())
10684 {
10685 if (String.IsNullOrEmpty(attrib.Name.NamespaceName) || CompilerCore.WixNamespace == attrib.Name.Namespace)
10686 {
10687 switch (attrib.Name.LocalName)
10688 {
10689 case "Id":
10690 id = this.core.GetAttributeIdentifier(sourceLineNumbers, attrib);
10691 break;
10692 case "DriverName":
10693 driverName = this.core.GetAttributeValue(sourceLineNumbers, attrib);
10694 break;
10695 case "KeyPath":
10696 keyPath = this.core.GetAttributeYesNoValue(sourceLineNumbers, attrib);
10697 break;
10698 case "Name":
10699 name = this.core.GetAttributeValue(sourceLineNumbers, attrib);
10700 break;
10701 case "Registration":
10702 string registrationValue = this.core.GetAttributeValue(sourceLineNumbers, attrib);
10703 if (0 < registrationValue.Length)
10704 {
10705 Wix.ODBCDataSource.RegistrationType registrationType = Wix.ODBCDataSource.ParseRegistrationType(registrationValue);
10706 switch (registrationType)
10707 {
10708 case Wix.ODBCDataSource.RegistrationType.machine:
10709 registration = 0;
10710 break;
10711 case Wix.ODBCDataSource.RegistrationType.user:
10712 registration = 1;
10713 break;
10714 default:
10715 this.core.OnMessage(WixErrors.IllegalAttributeValue(sourceLineNumbers, node.Name.LocalName, "Registration", registrationValue, "machine", "user"));
10716 break;
10717 }
10718 }
10719 break;
10720 default:
10721 this.core.UnexpectedAttribute(node, attrib);
10722 break;
10723 }
10724 }
10725 else
10726 {
10727 this.core.ParseExtensionAttribute(node, attrib);
10728 }
10729 }
10730
10731 if (CompilerConstants.IntegerNotSet == registration)
10732 {
10733 this.core.OnMessage(WixErrors.ExpectedAttribute(sourceLineNumbers, node.Name.LocalName, "Registration"));
10734 registration = CompilerConstants.IllegalInteger;
10735 }
10736
10737 if (null == id)
10738 {
10739 id = this.core.CreateIdentifier("odc", name, driverName, registration.ToString());
10740 }
10741
10742 foreach (XElement child in node.Elements())
10743 {
10744 if (CompilerCore.WixNamespace == child.Name.Namespace)
10745 {
10746 switch (child.Name.LocalName)
10747 {
10748 case "Property":
10749 this.ParseODBCProperty(child, id.Id, "ODBCSourceAttribute");
10750 break;
10751 default:
10752 this.core.UnexpectedElement(node, child);
10753 break;
10754 }
10755 }
10756 else
10757 {
10758 this.core.ParseExtensionElement(node, child);
10759 }
10760 }
10761
10762 if (!this.core.EncounteredError)
10763 {
10764 Row row = this.core.CreateRow(sourceLineNumbers, "ODBCDataSource", id);
10765 row[1] = componentId;
10766 row[2] = name;
10767 row[3] = driverName;
10768 row[4] = registration;
10769 }
10770
10771 possibleKeyPath = id.Id;
10772 return keyPath;
10773 }
10774
10775 /// <summary>
10776 /// Parses a package element.
10777 /// </summary>
10778 /// <param name="node">Element to parse.</param>
10779 /// <param name="productAuthor">Default package author.</param>
10780 /// <param name="moduleId">The module guid - this is necessary until Module/@Guid is removed.</param>
10781 private void ParsePackageElement(XElement node, string productAuthor, string moduleId)
10782 {
10783 SourceLineNumber sourceLineNumbers = Preprocessor.GetSourceLineNumbers(node);
10784 string codepage = "65001";
10785 string comments = String.Format(CultureInfo.InvariantCulture, "This installer database contains the logic and data required to install {0}.", this.activeName);
10786 string keywords = "Installer";
10787 int msiVersion = 100; // lowest released version, really should be specified
10788 string packageAuthor = productAuthor;
10789 string packageCode = null;
10790 string packageLanguages = this.activeLanguage;
10791 string packageName = this.activeName;
10792 string platform = null;
10793 string platformValue = null;
10794 YesNoDefaultType security = YesNoDefaultType.Default;
10795 int sourceBits = (this.compilingModule ? 2 : 0);
10796 Row row;
10797 bool installPrivilegeSeen = false;
10798 bool installScopeSeen = false;
10799
10800 switch (this.CurrentPlatform)
10801 {
10802 case Platform.X86:
10803 platform = "Intel";
10804 break;
10805 case Platform.X64:
10806 platform = "x64";
10807 msiVersion = 200;
10808 break;
10809 case Platform.IA64:
10810 platform = "Intel64";
10811 msiVersion = 200;
10812 break;
10813 case Platform.ARM:
10814 platform = "Arm";
10815 msiVersion = 500;
10816 break;
10817 default:
10818 throw new ArgumentException(WixStrings.EXP_UnknownPlatformEnum, this.CurrentPlatform.ToString());
10819 }
10820
10821 foreach (XAttribute attrib in node.Attributes())
10822 {
10823 if (String.IsNullOrEmpty(attrib.Name.NamespaceName) || CompilerCore.WixNamespace == attrib.Name.Namespace)
10824 {
10825 switch (attrib.Name.LocalName)
10826 {
10827 case "Id":
10828 packageCode = this.core.GetAttributeGuidValue(sourceLineNumbers, attrib, this.compilingProduct);
10829 break;
10830 case "AdminImage":
10831 if (YesNoType.Yes == this.core.GetAttributeYesNoValue(sourceLineNumbers, attrib))
10832 {
10833 sourceBits = sourceBits | 4;
10834 }
10835 break;
10836 case "Comments":
10837 comments = this.core.GetAttributeValue(sourceLineNumbers, attrib);
10838 break;
10839 case "Compressed":
10840 // merge modules must always be compressed, so this attribute is invalid
10841 if (this.compilingModule)
10842 {
10843 this.core.OnMessage(WixWarnings.DeprecatedPackageCompressedAttribute(sourceLineNumbers));
10844 // this.core.OnMessage(WixErrors.IllegalAttributeWhenNested(sourceLineNumbers, node.Name.LocalName, "Compressed", "Module"));
10845 }
10846 else if (YesNoType.Yes == this.core.GetAttributeYesNoValue(sourceLineNumbers, attrib))
10847 {
10848 sourceBits = sourceBits | 2;
10849 }
10850 break;
10851 case "Description":
10852 packageName = this.core.GetAttributeValue(sourceLineNumbers, attrib);
10853 break;
10854 case "InstallPrivileges":
10855 string installPrivileges = this.core.GetAttributeValue(sourceLineNumbers, attrib);
10856 if (0 < installPrivileges.Length)
10857 {
10858 installPrivilegeSeen = true;
10859 Wix.Package.InstallPrivilegesType installPrivilegesType = Wix.Package.ParseInstallPrivilegesType(installPrivileges);
10860 switch (installPrivilegesType)
10861 {
10862 case Wix.Package.InstallPrivilegesType.elevated:
10863 // this is the default setting
10864 break;
10865 case Wix.Package.InstallPrivilegesType.limited:
10866 sourceBits = sourceBits | 8;
10867 break;
10868 default:
10869 this.core.OnMessage(WixErrors.IllegalAttributeValue(sourceLineNumbers, node.Name.LocalName, attrib.Name.LocalName, installPrivileges, "elevated", "limited"));
10870 break;
10871 }
10872 }
10873 break;
10874 case "InstallScope":
10875 string installScope = this.core.GetAttributeValue(sourceLineNumbers, attrib);
10876 if (0 < installScope.Length)
10877 {
10878 installScopeSeen = true;
10879 Wix.Package.InstallScopeType installScopeType = Wix.Package.ParseInstallScopeType(installScope);
10880 switch (installScopeType)
10881 {
10882 case Wix.Package.InstallScopeType.perMachine:
10883 row = this.core.CreateRow(sourceLineNumbers, "Property");
10884 row[0] = "ALLUSERS";
10885 row[1] = "1";
10886 break;
10887 case Wix.Package.InstallScopeType.perUser:
10888 sourceBits = sourceBits | 8;
10889 break;
10890 default:
10891 this.core.OnMessage(WixErrors.IllegalAttributeValue(sourceLineNumbers, node.Name.LocalName, attrib.Name.LocalName, installScope, "perMachine", "perUser"));
10892 break;
10893 }
10894 }
10895 break;
10896 case "InstallerVersion":
10897 msiVersion = this.core.GetAttributeIntegerValue(sourceLineNumbers, attrib, 0, int.MaxValue);
10898 break;
10899 case "Keywords":
10900 keywords = this.core.GetAttributeValue(sourceLineNumbers, attrib);
10901 break;
10902 case "Languages":
10903 packageLanguages = this.core.GetAttributeValue(sourceLineNumbers, attrib);
10904 break;
10905 case "Manufacturer":
10906 packageAuthor = this.core.GetAttributeValue(sourceLineNumbers, attrib);
10907 if ("PUT-COMPANY-NAME-HERE" == packageAuthor)
10908 {
10909 this.core.OnMessage(WixWarnings.PlaceholderValue(sourceLineNumbers, node.Name.LocalName, attrib.Name.LocalName, packageAuthor));
10910 }
10911 break;
10912 case "Platform":
10913 if (null != platformValue)
10914 {
10915 this.core.OnMessage(WixErrors.IllegalAttributeWithOtherAttribute(sourceLineNumbers, node.Name.LocalName, attrib.Name.LocalName, "Platforms"));
10916 }
10917
10918 platformValue = this.core.GetAttributeValue(sourceLineNumbers, attrib);
10919 Wix.Package.PlatformType platformType = Wix.Package.ParsePlatformType(platformValue);
10920 switch (platformType)
10921 {
10922 case Wix.Package.PlatformType.intel:
10923 this.core.OnMessage(WixWarnings.DeprecatedAttributeValue(sourceLineNumbers, platformValue, node.Name.LocalName, attrib.Name.LocalName, "x86"));
10924 goto case Wix.Package.PlatformType.x86;
10925 case Wix.Package.PlatformType.x86:
10926 platform = "Intel";
10927 break;
10928 case Wix.Package.PlatformType.x64:
10929 platform = "x64";
10930 break;
10931 case Wix.Package.PlatformType.intel64:
10932 this.core.OnMessage(WixWarnings.DeprecatedAttributeValue(sourceLineNumbers, platformValue, node.Name.LocalName, attrib.Name.LocalName, "ia64"));
10933 goto case Wix.Package.PlatformType.ia64;
10934 case Wix.Package.PlatformType.ia64:
10935 platform = "Intel64";
10936 break;
10937 case Wix.Package.PlatformType.arm:
10938 platform = "Arm";
10939 break;
10940 default:
10941 this.core.OnMessage(WixErrors.InvalidPlatformValue(sourceLineNumbers, platformValue));
10942 break;
10943 }
10944 break;
10945 case "Platforms":
10946 if (null != platformValue)
10947 {
10948 this.core.OnMessage(WixErrors.IllegalAttributeWithOtherAttribute(sourceLineNumbers, node.Name.LocalName, attrib.Name.LocalName, "Platform"));
10949 }
10950
10951 this.core.OnMessage(WixWarnings.DeprecatedAttribute(sourceLineNumbers, node.Name.LocalName, attrib.Name.LocalName, "Platform"));
10952 platformValue = this.core.GetAttributeValue(sourceLineNumbers, attrib);
10953 platform = platformValue;
10954 break;
10955 case "ReadOnly":
10956 security = this.core.GetAttributeYesNoDefaultValue(sourceLineNumbers, attrib);
10957 break;
10958 case "ShortNames":
10959 if (YesNoType.Yes == this.core.GetAttributeYesNoValue(sourceLineNumbers, attrib))
10960 {
10961 sourceBits = sourceBits | 1;
10962 this.useShortFileNames = true;
10963 }
10964 break;
10965 case "SummaryCodepage":
10966 codepage = this.core.GetAttributeLocalizableCodePageValue(sourceLineNumbers, attrib, true);
10967 break;
10968 default:
10969 this.core.UnexpectedAttribute(node, attrib);
10970 break;
10971 }
10972 }
10973 else
10974 {
10975 this.core.ParseExtensionAttribute(node, attrib);
10976 }
10977 }
10978
10979 if (installPrivilegeSeen && installScopeSeen)
10980 {
10981 this.core.OnMessage(WixErrors.IllegalAttributeWithOtherAttribute(sourceLineNumbers, node.Name.LocalName, "InstallPrivileges", "InstallScope"));
10982 }
10983
10984 if ((0 != String.Compare(platform, "Intel", StringComparison.OrdinalIgnoreCase)) && 200 > msiVersion)
10985 {
10986 msiVersion = 200;
10987 this.core.OnMessage(WixWarnings.RequiresMsi200for64bitPackage(sourceLineNumbers));
10988 }
10989
10990 if ((0 == String.Compare(platform, "Arm", StringComparison.OrdinalIgnoreCase)) && 500 > msiVersion)
10991 {
10992 msiVersion = 500;
10993 this.core.OnMessage(WixWarnings.RequiresMsi500forArmPackage(sourceLineNumbers));
10994 }
10995
10996 if (null == packageAuthor)
10997 {
10998 this.core.OnMessage(WixErrors.ExpectedAttribute(sourceLineNumbers, node.Name.LocalName, "Manufacturer"));
10999 }
11000
11001 if (this.compilingModule)
11002 {
11003 if (null == packageCode)
11004 {
11005 this.core.OnMessage(WixErrors.ExpectedAttribute(sourceLineNumbers, node.Name.LocalName, "Id"));
11006 }
11007
11008 // merge modules use the modularization guid as the package code
11009 if (null != moduleId)
11010 {
11011 packageCode = moduleId;
11012 }
11013
11014 // merge modules are always compressed
11015 sourceBits = 2;
11016 }
11017 else // product
11018 {
11019 if (null == packageCode)
11020 {
11021 packageCode = "*";
11022 }
11023
11024 if ("*" != packageCode)
11025 {
11026 this.core.OnMessage(WixWarnings.PackageCodeSet(sourceLineNumbers));
11027 }
11028 }
11029
11030 this.core.ParseForExtensionElements(node);
11031
11032 if (!this.core.EncounteredError)
11033 {
11034 row = this.core.CreateRow(sourceLineNumbers, "_SummaryInformation");
11035 row[0] = 1;
11036 row[1] = codepage;
11037
11038 row = this.core.CreateRow(sourceLineNumbers, "_SummaryInformation");
11039 row[0] = 2;
11040 row[1] = "Installation Database";
11041
11042 row = this.core.CreateRow(sourceLineNumbers, "_SummaryInformation");
11043 row[0] = 3;
11044 row[1] = packageName;
11045
11046 row = this.core.CreateRow(sourceLineNumbers, "_SummaryInformation");
11047 row[0] = 4;
11048 row[1] = packageAuthor;
11049
11050 row = this.core.CreateRow(sourceLineNumbers, "_SummaryInformation");
11051 row[0] = 5;
11052 row[1] = keywords;
11053
11054 row = this.core.CreateRow(sourceLineNumbers, "_SummaryInformation");
11055 row[0] = 6;
11056 row[1] = comments;
11057
11058 row = this.core.CreateRow(sourceLineNumbers, "_SummaryInformation");
11059 row[0] = 7;
11060 row[1] = String.Format(CultureInfo.InvariantCulture, "{0};{1}", platform, packageLanguages);
11061
11062 row = this.core.CreateRow(sourceLineNumbers, "_SummaryInformation");
11063 row[0] = 9;
11064 row[1] = packageCode;
11065
11066 row = this.core.CreateRow(sourceLineNumbers, "_SummaryInformation");
11067 row[0] = 14;
11068 row[1] = msiVersion.ToString(CultureInfo.InvariantCulture);
11069
11070 row = this.core.CreateRow(sourceLineNumbers, "_SummaryInformation");
11071 row[0] = 15;
11072 row[1] = sourceBits.ToString(CultureInfo.InvariantCulture);
11073
11074 row = this.core.CreateRow(sourceLineNumbers, "_SummaryInformation");
11075 row[0] = 19;
11076 switch (security)
11077 {
11078 case YesNoDefaultType.No: // no restriction
11079 row[1] = "0";
11080 break;
11081 case YesNoDefaultType.Default: // read-only recommended
11082 row[1] = "2";
11083 break;
11084 case YesNoDefaultType.Yes: // read-only enforced
11085 row[1] = "4";
11086 break;
11087 }
11088 }
11089 }
11090
11091 /// <summary>
11092 /// Parses a patch metadata element.
11093 /// </summary>
11094 /// <param name="node">Element to parse.</param>
11095 private void ParsePatchMetadataElement(XElement node)
11096 {
11097 SourceLineNumber sourceLineNumbers = Preprocessor.GetSourceLineNumbers(node);
11098 YesNoType allowRemoval = YesNoType.NotSet;
11099 string classification = null;
11100 string creationTimeUtc = null;
11101 string description = null;
11102 string displayName = null;
11103 string manufacturerName = null;
11104 string minorUpdateTargetRTM = null;
11105 string moreInfoUrl = null;
11106 int optimizeCA = CompilerConstants.IntegerNotSet;
11107 YesNoType optimizedInstallMode = YesNoType.NotSet;
11108 string targetProductName = null;
11109
11110 foreach (XAttribute attrib in node.Attributes())
11111 {
11112 if (String.IsNullOrEmpty(attrib.Name.NamespaceName) || CompilerCore.WixNamespace == attrib.Name.Namespace)
11113 {
11114 switch (attrib.Name.LocalName)
11115 {
11116 case "AllowRemoval":
11117 allowRemoval = this.core.GetAttributeYesNoValue(sourceLineNumbers, attrib);
11118 break;
11119 case "Classification":
11120 classification = this.core.GetAttributeValue(sourceLineNumbers, attrib);
11121 break;
11122 case "CreationTimeUTC":
11123 creationTimeUtc = this.core.GetAttributeValue(sourceLineNumbers, attrib);
11124 break;
11125 case "Description":
11126 description = this.core.GetAttributeValue(sourceLineNumbers, attrib);
11127 break;
11128 case "DisplayName":
11129 displayName = this.core.GetAttributeValue(sourceLineNumbers, attrib);
11130 break;
11131 case "ManufacturerName":
11132 manufacturerName = this.core.GetAttributeValue(sourceLineNumbers, attrib);
11133 break;
11134 case "MinorUpdateTargetRTM":
11135 minorUpdateTargetRTM = this.core.GetAttributeValue(sourceLineNumbers, attrib);
11136 break;
11137 case "MoreInfoURL":
11138 moreInfoUrl = this.core.GetAttributeValue(sourceLineNumbers, attrib);
11139 break;
11140 case "OptimizedInstallMode":
11141 optimizedInstallMode = this.core.GetAttributeYesNoValue(sourceLineNumbers, attrib);
11142 break;
11143 case "TargetProductName":
11144 targetProductName = this.core.GetAttributeValue(sourceLineNumbers, attrib);
11145 break;
11146 default:
11147 this.core.UnexpectedAttribute(node, attrib);
11148 break;
11149 }
11150 }
11151 else
11152 {
11153 this.core.ParseExtensionAttribute(node, attrib);
11154 }
11155 }
11156
11157 if (YesNoType.NotSet == allowRemoval)
11158 {
11159 this.core.OnMessage(WixErrors.ExpectedAttribute(sourceLineNumbers, node.Name.LocalName, "AllowRemoval"));
11160 }
11161
11162 if (null == classification)
11163 {
11164 this.core.OnMessage(WixErrors.ExpectedAttribute(sourceLineNumbers, node.Name.LocalName, "Classification"));
11165 }
11166
11167 if (null == description)
11168 {
11169 this.core.OnMessage(WixErrors.ExpectedAttribute(sourceLineNumbers, node.Name.LocalName, "Description"));
11170 }
11171
11172 if (null == displayName)
11173 {
11174 this.core.OnMessage(WixErrors.ExpectedAttribute(sourceLineNumbers, node.Name.LocalName, "DisplayName"));
11175 }
11176
11177 if (null == manufacturerName)
11178 {
11179 this.core.OnMessage(WixErrors.ExpectedAttribute(sourceLineNumbers, node.Name.LocalName, "ManufacturerName"));
11180 }
11181
11182 if (null == moreInfoUrl)
11183 {
11184 this.core.OnMessage(WixErrors.ExpectedAttribute(sourceLineNumbers, node.Name.LocalName, "MoreInfoURL"));
11185 }
11186
11187 if (null == targetProductName)
11188 {
11189 this.core.OnMessage(WixErrors.ExpectedAttribute(sourceLineNumbers, node.Name.LocalName, "TargetProductName"));
11190 }
11191
11192 foreach (XElement child in node.Elements())
11193 {
11194 if (CompilerCore.WixNamespace == child.Name.Namespace)
11195 {
11196 switch (child.Name.LocalName)
11197 {
11198 case "CustomProperty":
11199 this.ParseCustomPropertyElement(child);
11200 break;
11201 case "OptimizeCustomActions":
11202 optimizeCA = this.ParseOptimizeCustomActionsElement(child);
11203 break;
11204 default:
11205 this.core.UnexpectedElement(node, child);
11206 break;
11207 }
11208 }
11209 else
11210 {
11211 this.core.ParseExtensionElement(node, child);
11212 }
11213 }
11214
11215 if (!this.core.EncounteredError)
11216 {
11217 if (YesNoType.NotSet != allowRemoval)
11218 {
11219 Row row = this.core.CreateRow(sourceLineNumbers, "PatchMetadata");
11220 row[0] = null;
11221 row[1] = "AllowRemoval";
11222 row[2] = YesNoType.Yes == allowRemoval ? "1" : "0";
11223 }
11224
11225 if (null != classification)
11226 {
11227 Row row = this.core.CreateRow(sourceLineNumbers, "PatchMetadata");
11228 row[0] = null;
11229 row[1] = "Classification";
11230 row[2] = classification;
11231 }
11232
11233 if (null != creationTimeUtc)
11234 {
11235 Row row = this.core.CreateRow(sourceLineNumbers, "PatchMetadata");
11236 row[0] = null;
11237 row[1] = "CreationTimeUTC";
11238 row[2] = creationTimeUtc;
11239 }
11240
11241 if (null != description)
11242 {
11243 Row row = this.core.CreateRow(sourceLineNumbers, "PatchMetadata");
11244 row[0] = null;
11245 row[1] = "Description";
11246 row[2] = description;
11247 }
11248
11249 if (null != displayName)
11250 {
11251 Row row = this.core.CreateRow(sourceLineNumbers, "PatchMetadata");
11252 row[0] = null;
11253 row[1] = "DisplayName";
11254 row[2] = displayName;
11255 }
11256
11257 if (null != manufacturerName)
11258 {
11259 Row row = this.core.CreateRow(sourceLineNumbers, "PatchMetadata");
11260 row[0] = null;
11261 row[1] = "ManufacturerName";
11262 row[2] = manufacturerName;
11263 }
11264
11265 if (null != minorUpdateTargetRTM)
11266 {
11267 Row row = this.core.CreateRow(sourceLineNumbers, "PatchMetadata");
11268 row[0] = null;
11269 row[1] = "MinorUpdateTargetRTM";
11270 row[2] = minorUpdateTargetRTM;
11271 }
11272
11273 if (null != moreInfoUrl)
11274 {
11275 Row row = this.core.CreateRow(sourceLineNumbers, "PatchMetadata");
11276 row[0] = null;
11277 row[1] = "MoreInfoURL";
11278 row[2] = moreInfoUrl;
11279 }
11280
11281 if (CompilerConstants.IntegerNotSet != optimizeCA)
11282 {
11283 Row row = this.core.CreateRow(sourceLineNumbers, "PatchMetadata");
11284 row[0] = null;
11285 row[1] = "OptimizeCA";
11286 row[2] = optimizeCA.ToString(CultureInfo.InvariantCulture);
11287 }
11288
11289 if (YesNoType.NotSet != optimizedInstallMode)
11290 {
11291 Row row = this.core.CreateRow(sourceLineNumbers, "PatchMetadata");
11292 row[0] = null;
11293 row[1] = "OptimizedInstallMode";
11294 row[2] = YesNoType.Yes == optimizedInstallMode ? "1" : "0";
11295 }
11296
11297 if (null != targetProductName)
11298 {
11299 Row row = this.core.CreateRow(sourceLineNumbers, "PatchMetadata");
11300 row[0] = null;
11301 row[1] = "TargetProductName";
11302 row[2] = targetProductName;
11303 }
11304 }
11305 }
11306
11307 /// <summary>
11308 /// Parses a custom property element for the PatchMetadata table.
11309 /// </summary>
11310 /// <param name="node">Element to parse.</param>
11311 private void ParseCustomPropertyElement(XElement node)
11312 {
11313 SourceLineNumber sourceLineNumbers = Preprocessor.GetSourceLineNumbers(node);
11314 string company = null;
11315 string property = null;
11316 string value = null;
11317
11318 foreach (XAttribute attrib in node.Attributes())
11319 {
11320 if (String.IsNullOrEmpty(attrib.Name.NamespaceName) || CompilerCore.WixNamespace == attrib.Name.Namespace)
11321 {
11322 switch (attrib.Name.LocalName)
11323 {
11324 case "Company":
11325 company = this.core.GetAttributeValue(sourceLineNumbers, attrib);
11326 break;
11327 case "Property":
11328 property = this.core.GetAttributeValue(sourceLineNumbers, attrib);
11329 break;
11330 case "Value":
11331 value = this.core.GetAttributeValue(sourceLineNumbers, attrib);
11332 break;
11333 default:
11334 this.core.UnexpectedAttribute(node, attrib);
11335 break;
11336 }
11337 }
11338 else
11339 {
11340 this.core.ParseExtensionAttribute(node, attrib);
11341 }
11342 }
11343
11344 if (null == company)
11345 {
11346 this.core.OnMessage(WixErrors.ExpectedAttribute(sourceLineNumbers, node.Name.LocalName, "Company"));
11347 }
11348
11349 if (null == property)
11350 {
11351 this.core.OnMessage(WixErrors.ExpectedAttribute(sourceLineNumbers, node.Name.LocalName, "Property"));
11352 }
11353
11354 if (null == value)
11355 {
11356 this.core.OnMessage(WixErrors.ExpectedAttribute(sourceLineNumbers, node.Name.LocalName, "Value"));
11357 }
11358
11359 this.core.ParseForExtensionElements(node);
11360
11361 if (!this.core.EncounteredError)
11362 {
11363 Row row = this.core.CreateRow(sourceLineNumbers, "PatchMetadata");
11364 row[0] = company;
11365 row[1] = property;
11366 row[2] = value;
11367 }
11368 }
11369
11370 /// <summary>
11371 /// Parses the OptimizeCustomActions element.
11372 /// </summary>
11373 /// <param name="node">Element to parse.</param>
11374 /// <returns>The combined integer value for callers to store as appropriate.</returns>
11375 private int ParseOptimizeCustomActionsElement(XElement node)
11376 {
11377 SourceLineNumber sourceLineNumbers = Preprocessor.GetSourceLineNumbers(node);
11378 OptimizeCA optimizeCA = OptimizeCA.None;
11379
11380 foreach (XAttribute attrib in node.Attributes())
11381 {
11382 if (String.IsNullOrEmpty(attrib.Name.NamespaceName) || CompilerCore.WixNamespace == attrib.Name.Namespace)
11383 {
11384 switch (attrib.Name.LocalName)
11385 {
11386 case "SkipAssignment":
11387 if (YesNoType.Yes == this.core.GetAttributeYesNoValue(sourceLineNumbers, attrib))
11388 {
11389 optimizeCA |= OptimizeCA.SkipAssignment;
11390 }
11391 break;
11392 case "SkipImmediate":
11393 if (YesNoType.Yes == this.core.GetAttributeYesNoValue(sourceLineNumbers, attrib))
11394 {
11395 optimizeCA |= OptimizeCA.SkipImmediate;
11396 }
11397 break;
11398 case "SkipDeferred":
11399 if (YesNoType.Yes == this.core.GetAttributeYesNoValue(sourceLineNumbers, attrib))
11400 {
11401 optimizeCA |= OptimizeCA.SkipDeferred;
11402 }
11403 break;
11404 default:
11405 this.core.UnexpectedAttribute(node, attrib);
11406 break;
11407 }
11408 }
11409 else
11410 {
11411 this.core.ParseExtensionAttribute(node, attrib);
11412 }
11413 }
11414
11415 return (int)optimizeCA;
11416 }
11417
11418 /// <summary>
11419 /// Parses a patch information element.
11420 /// </summary>
11421 /// <param name="node">Element to parse.</param>
11422 private void ParsePatchInformationElement(XElement node)
11423 {
11424 SourceLineNumber sourceLineNumbers = Preprocessor.GetSourceLineNumbers(node);
11425 string codepage = "65001";
11426 string comments = null;
11427 string keywords = "Installer,Patching,PCP,Database";
11428 int msiVersion = 1; // Should always be 1 for patches
11429 string packageAuthor = null;
11430 string packageName = this.activeName;
11431 YesNoDefaultType security = YesNoDefaultType.Default;
11432
11433 foreach (XAttribute attrib in node.Attributes())
11434 {
11435 if (String.IsNullOrEmpty(attrib.Name.NamespaceName) || CompilerCore.WixNamespace == attrib.Name.Namespace)
11436 {
11437 switch (attrib.Name.LocalName)
11438 {
11439 case "AdminImage":
11440 this.core.OnMessage(WixWarnings.DeprecatedAttribute(sourceLineNumbers, node.Name.LocalName, attrib.Name.LocalName));
11441 break;
11442 case "Comments":
11443 comments = this.core.GetAttributeValue(sourceLineNumbers, attrib);
11444 break;
11445 case "Compressed":
11446 this.core.OnMessage(WixWarnings.DeprecatedAttribute(sourceLineNumbers, node.Name.LocalName, attrib.Name.LocalName));
11447 break;
11448 case "Description":
11449 packageName = this.core.GetAttributeValue(sourceLineNumbers, attrib);
11450 break;
11451 case "Keywords":
11452 keywords = this.core.GetAttributeValue(sourceLineNumbers, attrib);
11453 break;
11454 case "Languages":
11455 this.core.OnMessage(WixWarnings.DeprecatedAttribute(sourceLineNumbers, node.Name.LocalName, attrib.Name.LocalName));
11456 break;
11457 case "Manufacturer":
11458 packageAuthor = this.core.GetAttributeValue(sourceLineNumbers, attrib);
11459 break;
11460 case "Platforms":
11461 this.core.OnMessage(WixWarnings.DeprecatedAttribute(sourceLineNumbers, node.Name.LocalName, attrib.Name.LocalName));
11462 break;
11463 case "ReadOnly":
11464 security = this.core.GetAttributeYesNoDefaultValue(sourceLineNumbers, attrib);
11465 break;
11466 case "ShortNames":
11467 this.core.OnMessage(WixWarnings.DeprecatedAttribute(sourceLineNumbers, node.Name.LocalName, attrib.Name.LocalName));
11468 break;
11469 case "SummaryCodepage":
11470 codepage = this.core.GetAttributeLocalizableCodePageValue(sourceLineNumbers, attrib);
11471 break;
11472 default:
11473 this.core.UnexpectedAttribute(node, attrib);
11474 break;
11475 }
11476 }
11477 else
11478 {
11479 this.core.ParseExtensionAttribute(node, attrib);
11480 }
11481 }
11482
11483 this.core.ParseForExtensionElements(node);
11484
11485 if (!this.core.EncounteredError)
11486 {
11487 // PID_CODEPAGE
11488 Row row = this.core.CreateRow(sourceLineNumbers, "_SummaryInformation");
11489 row[0] = 1;
11490 row[1] = codepage;
11491
11492 // PID_TITLE
11493 row = this.core.CreateRow(sourceLineNumbers, "_SummaryInformation");
11494 row[0] = 2;
11495 row[1] = "Patch";
11496
11497 // PID_SUBJECT
11498 if (null != packageName)
11499 {
11500 row = this.core.CreateRow(sourceLineNumbers, "_SummaryInformation");
11501 row[0] = 3;
11502 row[1] = packageName;
11503 }
11504
11505 // PID_AUTHOR
11506 if (null != packageAuthor)
11507 {
11508 row = this.core.CreateRow(sourceLineNumbers, "_SummaryInformation");
11509 row[0] = 4;
11510 row[1] = packageAuthor;
11511 }
11512
11513 // PID_KEYWORDS
11514 if (null != keywords)
11515 {
11516 row = this.core.CreateRow(sourceLineNumbers, "_SummaryInformation");
11517 row[0] = 5;
11518 row[1] = keywords;
11519 }
11520
11521 // PID_COMMENTS
11522 if (null != comments)
11523 {
11524 row = this.core.CreateRow(sourceLineNumbers, "_SummaryInformation");
11525 row[0] = 6;
11526 row[1] = comments;
11527 }
11528
11529 // PID_PAGECOUNT
11530 row = this.core.CreateRow(sourceLineNumbers, "_SummaryInformation");
11531 row[0] = 14;
11532 row[1] = msiVersion.ToString(CultureInfo.InvariantCulture);
11533
11534 // PID_WORDCOUNT
11535 row = this.core.CreateRow(sourceLineNumbers, "_SummaryInformation");
11536 row[0] = 15;
11537 row[1] = "0";
11538
11539 // PID_SECURITY
11540 row = this.core.CreateRow(sourceLineNumbers, "_SummaryInformation");
11541 row[0] = 19;
11542 switch (security)
11543 {
11544 case YesNoDefaultType.No: // no restriction
11545 row[1] = "0";
11546 break;
11547 case YesNoDefaultType.Default: // read-only recommended
11548 row[1] = "2";
11549 break;
11550 case YesNoDefaultType.Yes: // read-only enforced
11551 row[1] = "4";
11552 break;
11553 }
11554 }
11555 }
11556
11557 /// <summary>
11558 /// Parses an ignore modularization element.
11559 /// </summary>
11560 /// <param name="node">XmlNode on an IgnoreModulatization element.</param>
11561 private void ParseIgnoreModularizationElement(XElement node)
11562 {
11563 SourceLineNumber sourceLineNumbers = Preprocessor.GetSourceLineNumbers(node);
11564 string name = null;
11565
11566 this.core.OnMessage(WixWarnings.DeprecatedIgnoreModularizationElement(sourceLineNumbers));
11567
11568 foreach (XAttribute attrib in node.Attributes())
11569 {
11570 if (String.IsNullOrEmpty(attrib.Name.NamespaceName) || CompilerCore.WixNamespace == attrib.Name.Namespace)
11571 {
11572 switch (attrib.Name.LocalName)
11573 {
11574 case "Name":
11575 name = this.core.GetAttributeIdentifierValue(sourceLineNumbers, attrib);
11576 break;
11577 case "Type":
11578 // this is actually not used
11579 break;
11580 default:
11581 this.core.UnexpectedAttribute(node, attrib);
11582 break;
11583 }
11584 }
11585 else
11586 {
11587 this.core.ParseExtensionAttribute(node, attrib);
11588 }
11589 }
11590
11591 if (null == name)
11592 {
11593 this.core.OnMessage(WixErrors.ExpectedAttribute(sourceLineNumbers, node.Name.LocalName, "Name"));
11594 }
11595
11596 this.core.ParseForExtensionElements(node);
11597
11598 if (!this.core.EncounteredError)
11599 {
11600 Row row = this.core.CreateRow(sourceLineNumbers, "WixSuppressModularization");
11601 row[0] = name;
11602 }
11603 }
11604
11605 /// <summary>
11606 /// Parses a permission element.
11607 /// </summary>
11608 /// <param name="node">Element to parse.</param>
11609 /// <param name="objectId">Identifier of object to be secured.</param>
11610 /// <param name="tableName">Name of table that contains objectId.</param>
11611 private void ParsePermissionElement(XElement node, string objectId, string tableName)
11612 {
11613 SourceLineNumber sourceLineNumbers = Preprocessor.GetSourceLineNumbers(node);
11614 BitArray bits = new BitArray(32);
11615 string domain = null;
11616 int permission = 0;
11617 string[] specialPermissions = null;
11618 string user = null;
11619
11620 switch (tableName)
11621 {
11622 case "CreateFolder":
11623 specialPermissions = Common.FolderPermissions;
11624 break;
11625 case "File":
11626 specialPermissions = Common.FilePermissions;
11627 break;
11628 case "Registry":
11629 specialPermissions = Common.RegistryPermissions;
11630 break;
11631 default:
11632 this.core.UnexpectedElement(node.Parent, node);
11633 return; // stop processing this element since no valid permissions are available
11634 }
11635
11636 foreach (XAttribute attrib in node.Attributes())
11637 {
11638 if (String.IsNullOrEmpty(attrib.Name.NamespaceName) || CompilerCore.WixNamespace == attrib.Name.Namespace)
11639 {
11640 switch (attrib.Name.LocalName)
11641 {
11642 case "Domain":
11643 domain = this.core.GetAttributeValue(sourceLineNumbers, attrib);
11644 break;
11645 case "User":
11646 user = this.core.GetAttributeValue(sourceLineNumbers, attrib);
11647 break;
11648 case "FileAllRights":
11649 // match the WinNT.h mask FILE_ALL_ACCESS for value 0x001F01FF (aka 1 1111 0000 0001 1111 1111 or 2032127)
11650 bits[0] = bits[1] = bits[2] = bits[3] = bits[4] = bits[5] = bits[6] = bits[7] = bits[8] = bits[16] = bits[17] = bits[18] = bits[19] = bits[20] = true;
11651 break;
11652 case "SpecificRightsAll":
11653 // match the WinNT.h mask SPECIFIC_RIGHTS_ALL for value 0x0000FFFF (aka 1111 1111 1111 1111)
11654 bits[0] = bits[1] = bits[2] = bits[3] = bits[4] = bits[5] = bits[6] = bits[7] = bits[8] = bits[9] = bits[10] = bits[11] = bits[12] = bits[13] = bits[14] = bits[15] = true;
11655 break;
11656 default:
11657 YesNoType attribValue = this.core.GetAttributeYesNoValue(sourceLineNumbers, attrib);
11658 if (!this.core.TrySetBitFromName(Common.StandardPermissions, attrib.Name.LocalName, attribValue, bits, 16))
11659 {
11660 if (!this.core.TrySetBitFromName(Common.GenericPermissions, attrib.Name.LocalName, attribValue, bits, 28))
11661 {
11662 if (!this.core.TrySetBitFromName(specialPermissions, attrib.Name.LocalName, attribValue, bits, 0))
11663 {
11664 this.core.UnexpectedAttribute(node, attrib);
11665 break;
11666 }
11667 }
11668 }
11669 break;
11670 }
11671 }
11672 else
11673 {
11674 this.core.ParseExtensionAttribute(node, attrib);
11675 }
11676 }
11677
11678 permission = this.core.CreateIntegerFromBitArray(bits);
11679
11680 if (null == user)
11681 {
11682 this.core.OnMessage(WixErrors.ExpectedAttribute(sourceLineNumbers, node.Name.LocalName, "User"));
11683 }
11684
11685 if (int.MinValue == permission) // just GENERIC_READ, which is MSI_NULL
11686 {
11687 this.core.OnMessage(WixErrors.GenericReadNotAllowed(sourceLineNumbers));
11688 }
11689
11690 this.core.ParseForExtensionElements(node);
11691
11692 if (!this.core.EncounteredError)
11693 {
11694 Row row = this.core.CreateRow(sourceLineNumbers, "LockPermissions");
11695 row[0] = objectId;
11696 row[1] = tableName;
11697 row[2] = domain;
11698 row[3] = user;
11699 row[4] = permission;
11700 }
11701 }
11702
11703 /// <summary>
11704 /// Parses an extended permission element.
11705 /// </summary>
11706 /// <param name="node">Element to parse.</param>
11707 /// <param name="objectId">Identifier of object to be secured.</param>
11708 /// <param name="tableName">Name of table that contains objectId.</param>
11709 private void ParsePermissionExElement(XElement node, string objectId, string tableName)
11710 {
11711 SourceLineNumber sourceLineNumbers = Preprocessor.GetSourceLineNumbers(node);
11712 string condition = null;
11713 Identifier id = null;
11714 string sddl = null;
11715
11716 switch (tableName)
11717 {
11718 case "CreateFolder":
11719 case "File":
11720 case "Registry":
11721 case "ServiceInstall":
11722 break;
11723 default:
11724 this.core.UnexpectedElement(node.Parent, node);
11725 return; // stop processing this element since nothing will be valid.
11726 }
11727
11728 foreach (XAttribute attrib in node.Attributes())
11729 {
11730 if (String.IsNullOrEmpty(attrib.Name.NamespaceName) || CompilerCore.WixNamespace == attrib.Name.Namespace)
11731 {
11732 switch (attrib.Name.LocalName)
11733 {
11734 case "Id":
11735 id = this.core.GetAttributeIdentifier(sourceLineNumbers, attrib);
11736 break;
11737 case "Sddl":
11738 sddl = this.core.GetAttributeValue(sourceLineNumbers, attrib);
11739 break;
11740 default:
11741 this.core.UnexpectedAttribute(node, attrib);
11742 break;
11743 }
11744 }
11745 else
11746 {
11747 this.core.ParseExtensionAttribute(node, attrib);
11748 }
11749 }
11750
11751 if (null == sddl)
11752 {
11753 this.core.OnMessage(WixErrors.ExpectedAttribute(sourceLineNumbers, node.Name.LocalName, "Sddl"));
11754 }
11755
11756 if (null == id)
11757 {
11758 id = this.core.CreateIdentifier("pme", objectId, tableName, sddl);
11759 }
11760
11761 foreach (XElement child in node.Elements())
11762 {
11763 if (CompilerCore.WixNamespace == child.Name.Namespace)
11764 {
11765 switch (child.Name.LocalName)
11766 {
11767 case "Condition":
11768 if (null != condition)
11769 {
11770 SourceLineNumber childSourceLineNumbers = Preprocessor.GetSourceLineNumbers(node);
11771 this.core.OnMessage(WixErrors.TooManyChildren(childSourceLineNumbers, node.Name.LocalName, child.Name.LocalName));
11772 }
11773
11774 condition = this.ParseConditionElement(child, node.Name.LocalName, null, null);
11775 break;
11776 default:
11777 this.core.UnexpectedElement(node, child);
11778 break;
11779 }
11780 }
11781 else
11782 {
11783 this.core.ParseExtensionElement(node, child);
11784 }
11785 }
11786
11787 if (!this.core.EncounteredError)
11788 {
11789 Row row = this.core.CreateRow(sourceLineNumbers, "MsiLockPermissionsEx", id);
11790 row[1] = objectId;
11791 row[2] = tableName;
11792 row[3] = sddl;
11793 row[4] = condition;
11794 }
11795 }
11796
11797 /// <summary>
11798 /// Parses a product element.
11799 /// </summary>
11800 /// <param name="node">Element to parse.</param>
11801 [System.Diagnostics.CodeAnalysis.SuppressMessage("Microsoft.Performance", "CA1800:DoNotCastUnnecessarily")]
11802 private void ParseProductElement(XElement node)
11803 {
11804 SourceLineNumber sourceLineNumbers = Preprocessor.GetSourceLineNumbers(node);
11805 int codepage = 0;
11806 string productCode = null;
11807 string upgradeCode = null;
11808 string manufacturer = null;
11809 string version = null;
11810 string symbols = null;
11811
11812 this.activeName = null;
11813 this.activeLanguage = null;
11814
11815 foreach (XAttribute attrib in node.Attributes())
11816 {
11817 if (String.IsNullOrEmpty(attrib.Name.NamespaceName) || CompilerCore.WixNamespace == attrib.Name.Namespace)
11818 {
11819 switch (attrib.Name.LocalName)
11820 {
11821 case "Id":
11822 productCode = this.core.GetAttributeGuidValue(sourceLineNumbers, attrib, true);
11823 break;
11824 case "Codepage":
11825 codepage = this.core.GetAttributeCodePageValue(sourceLineNumbers, attrib);
11826 break;
11827 case "Language":
11828 this.activeLanguage = this.core.GetAttributeLocalizableIntegerValue(sourceLineNumbers, attrib, 0, short.MaxValue);
11829 break;
11830 case "Manufacturer":
11831 manufacturer = this.core.GetAttributeValue(sourceLineNumbers, attrib, EmptyRule.MustHaveNonWhitespaceCharacters);
11832 if ("PUT-COMPANY-NAME-HERE" == manufacturer)
11833 {
11834 this.core.OnMessage(WixWarnings.PlaceholderValue(sourceLineNumbers, node.Name.LocalName, attrib.Name.LocalName, manufacturer));
11835 }
11836 break;
11837 case "Name":
11838 this.activeName = this.core.GetAttributeValue(sourceLineNumbers, attrib, EmptyRule.MustHaveNonWhitespaceCharacters);
11839 if ("PUT-PRODUCT-NAME-HERE" == this.activeName)
11840 {
11841 this.core.OnMessage(WixWarnings.PlaceholderValue(sourceLineNumbers, node.Name.LocalName, attrib.Name.LocalName, this.activeName));
11842 }
11843 break;
11844 case "UpgradeCode":
11845 upgradeCode = this.core.GetAttributeGuidValue(sourceLineNumbers, attrib, false);
11846 break;
11847 case "Version": // if the attribute is valid version, use the attribute value as is (so "1.0000.01.01" would *not* get translated to "1.0.1.1").
11848 string verifiedVersion = this.core.GetAttributeVersionValue(sourceLineNumbers, attrib);
11849 if (!String.IsNullOrEmpty(verifiedVersion))
11850 {
11851 version = attrib.Value;
11852 }
11853 break;
11854 default:
11855 this.core.UnexpectedAttribute(node, attrib);
11856 break;
11857 }
11858 }
11859 else
11860 {
11861 this.core.ParseExtensionAttribute(node, attrib);
11862 }
11863 }
11864
11865 if (null == productCode)
11866 {
11867 this.core.OnMessage(WixErrors.ExpectedAttribute(sourceLineNumbers, node.Name.LocalName, "Id"));
11868 }
11869
11870 if (null == this.activeLanguage)
11871 {
11872 this.core.OnMessage(WixErrors.ExpectedAttribute(sourceLineNumbers, node.Name.LocalName, "Language"));
11873 }
11874
11875 if (null == manufacturer)
11876 {
11877 this.core.OnMessage(WixErrors.ExpectedAttribute(sourceLineNumbers, node.Name.LocalName, "Manufacturer"));
11878 }
11879
11880 if (null == this.activeName)
11881 {
11882 this.core.OnMessage(WixErrors.ExpectedAttribute(sourceLineNumbers, node.Name.LocalName, "Name"));
11883 }
11884
11885 if (null == upgradeCode)
11886 {
11887 this.core.OnMessage(WixWarnings.MissingUpgradeCode(sourceLineNumbers));
11888 }
11889
11890 if (null == version)
11891 {
11892 this.core.OnMessage(WixErrors.ExpectedAttribute(sourceLineNumbers, node.Name.LocalName, "Version"));
11893 }
11894 else if (!CompilerCore.IsValidProductVersion(version))
11895 {
11896 this.core.OnMessage(WixErrors.InvalidProductVersion(sourceLineNumbers, version));
11897 }
11898
11899 if (this.core.EncounteredError)
11900 {
11901 return;
11902 }
11903
11904 try
11905 {
11906 this.compilingProduct = true;
11907 this.core.CreateActiveSection(productCode, SectionType.Product, codepage);
11908
11909 this.AddProperty(sourceLineNumbers, new Identifier("Manufacturer", AccessModifier.Public), manufacturer, false, false, false, true);
11910 this.AddProperty(sourceLineNumbers, new Identifier("ProductCode", AccessModifier.Public), productCode, false, false, false, true);
11911 this.AddProperty(sourceLineNumbers, new Identifier("ProductLanguage", AccessModifier.Public), this.activeLanguage, false, false, false, true);
11912 this.AddProperty(sourceLineNumbers, new Identifier("ProductName", AccessModifier.Public), this.activeName, false, false, false, true);
11913 this.AddProperty(sourceLineNumbers, new Identifier("ProductVersion", AccessModifier.Public), version, false, false, false, true);
11914 if (null != upgradeCode)
11915 {
11916 this.AddProperty(sourceLineNumbers, new Identifier("UpgradeCode", AccessModifier.Public), upgradeCode, false, false, false, true);
11917 }
11918
11919 Dictionary<string, string> contextValues = new Dictionary<string, string>();
11920 contextValues["ProductLanguage"] = this.activeLanguage;
11921 contextValues["ProductVersion"] = version;
11922 contextValues["UpgradeCode"] = upgradeCode;
11923
11924 int featureDisplay = 0;
11925 foreach (XElement child in node.Elements())
11926 {
11927 if (CompilerCore.WixNamespace == child.Name.Namespace)
11928 {
11929 switch (child.Name.LocalName)
11930 {
11931 case "_locDefinition":
11932 break;
11933 case "AdminExecuteSequence":
11934 case "AdminUISequence":
11935 case "AdvertiseExecuteSequence":
11936 case "InstallExecuteSequence":
11937 case "InstallUISequence":
11938 this.ParseSequenceElement(child, child.Name.LocalName);
11939 break;
11940 case "AppId":
11941 this.ParseAppIdElement(child, null, YesNoType.Yes, null, null, null);
11942 break;
11943 case "Binary":
11944 this.ParseBinaryElement(child);
11945 break;
11946 case "ComplianceCheck":
11947 this.ParseComplianceCheckElement(child);
11948 break;
11949 case "Component":
11950 this.ParseComponentElement(child, ComplexReferenceParentType.Unknown, null, null, CompilerConstants.IntegerNotSet, null, null);
11951 break;
11952 case "ComponentGroup":
11953 this.ParseComponentGroupElement(child, ComplexReferenceParentType.Unknown, null);
11954 break;
11955 case "Condition":
11956 this.ParseConditionElement(child, node.Name.LocalName, null, null);
11957 break;
11958 case "CustomAction":
11959 this.ParseCustomActionElement(child);
11960 break;
11961 case "CustomActionRef":
11962 this.ParseSimpleRefElement(child, "CustomAction");
11963 break;
11964 case "CustomTable":
11965 this.ParseCustomTableElement(child);
11966 break;
11967 case "Directory":
11968 this.ParseDirectoryElement(child, null, CompilerConstants.IntegerNotSet, String.Empty);
11969 break;
11970 case "DirectoryRef":
11971 this.ParseDirectoryRefElement(child);
11972 break;
11973 case "EmbeddedChainer":
11974 this.ParseEmbeddedChainerElement(child);
11975 break;
11976 case "EmbeddedChainerRef":
11977 this.ParseSimpleRefElement(child, "MsiEmbeddedChainer");
11978 break;
11979 case "EnsureTable":
11980 this.ParseEnsureTableElement(child);
11981 break;
11982 case "Feature":
11983 this.ParseFeatureElement(child, ComplexReferenceParentType.Product, productCode, ref featureDisplay);
11984 break;
11985 case "FeatureRef":
11986 this.ParseFeatureRefElement(child, ComplexReferenceParentType.Product, productCode);
11987 break;
11988 case "FeatureGroupRef":
11989 this.ParseFeatureGroupRefElement(child, ComplexReferenceParentType.Product, productCode);
11990 break;
11991 case "Icon":
11992 this.ParseIconElement(child);
11993 break;
11994 case "InstanceTransforms":
11995 this.ParseInstanceTransformsElement(child);
11996 break;
11997 case "MajorUpgrade":
11998 this.ParseMajorUpgradeElement(child, contextValues);
11999 break;
12000 case "Media":
12001 this.ParseMediaElement(child, null);
12002 break;
12003 case "MediaTemplate":
12004 this.ParseMediaTemplateElement(child, null);
12005 break;
12006 case "Package":
12007 this.ParsePackageElement(child, manufacturer, null);
12008 break;
12009 case "PackageCertificates":
12010 case "PatchCertificates":
12011 this.ParseCertificatesElement(child);
12012 break;
12013 case "Property":
12014 this.ParsePropertyElement(child);
12015 break;
12016 case "PropertyRef":
12017 this.ParseSimpleRefElement(child, "Property");
12018 break;
12019 case "SetDirectory":
12020 this.ParseSetDirectoryElement(child);
12021 break;
12022 case "SetProperty":
12023 this.ParseSetPropertyElement(child);
12024 break;
12025 case "SFPCatalog":
12026 string parentName = null;
12027 this.ParseSFPCatalogElement(child, ref parentName);
12028 break;
12029 case "SymbolPath":
12030 if (null != symbols)
12031 {
12032 symbols += ";" + this.ParseSymbolPathElement(child);
12033 }
12034 else
12035 {
12036 symbols = this.ParseSymbolPathElement(child);
12037 }
12038 break;
12039 case "UI":
12040 this.ParseUIElement(child);
12041 break;
12042 case "UIRef":
12043 this.ParseSimpleRefElement(child, "WixUI");
12044 break;
12045 case "Upgrade":
12046 this.ParseUpgradeElement(child);
12047 break;
12048 case "WixVariable":
12049 this.ParseWixVariableElement(child);
12050 break;
12051 default:
12052 this.core.UnexpectedElement(node, child);
12053 break;
12054 }
12055 }
12056 else
12057 {
12058 this.core.ParseExtensionElement(node, child);
12059 }
12060 }
12061
12062 if (!this.core.EncounteredError)
12063 {
12064 if (null != symbols)
12065 {
12066 WixDeltaPatchSymbolPathsRow symbolRow = (WixDeltaPatchSymbolPathsRow)this.core.CreateRow(sourceLineNumbers, "WixDeltaPatchSymbolPaths");
12067 symbolRow.Id = productCode;
12068 symbolRow.Type = SymbolPathType.Product;
12069 symbolRow.SymbolPaths = symbols;
12070 }
12071 }
12072 }
12073 finally
12074 {
12075 this.compilingProduct = false;
12076 }
12077 }
12078
12079 /// <summary>
12080 /// Parses a progid element
12081 /// </summary>
12082 /// <param name="node">Element to parse.</param>
12083 /// <param name="componentId">Identifier of parent component.</param>
12084 /// <param name="advertise">Flag if progid is advertised.</param>
12085 /// <param name="classId">CLSID related to ProgId.</param>
12086 /// <param name="description">Default description of ProgId</param>
12087 /// <param name="parent">Optional parent ProgId</param>
12088 /// <param name="foundExtension">Set to true if an extension is found; used for error-checking.</param>
12089 /// <param name="firstProgIdForClass">Whether or not this ProgId is the first one found in the parent class.</param>
12090 /// <returns>This element's Id.</returns>
12091 private string ParseProgIdElement(XElement node, string componentId, YesNoType advertise, string classId, string description, string parent, ref bool foundExtension, YesNoType firstProgIdForClass)
12092 {
12093 SourceLineNumber sourceLineNumbers = Preprocessor.GetSourceLineNumbers(node);
12094 string icon = null;
12095 int iconIndex = CompilerConstants.IntegerNotSet;
12096 string noOpen = null;
12097 string progId = null;
12098 YesNoType progIdAdvertise = YesNoType.NotSet;
12099
12100 foreach (XAttribute attrib in node.Attributes())
12101 {
12102 if (String.IsNullOrEmpty(attrib.Name.NamespaceName) || CompilerCore.WixNamespace == attrib.Name.Namespace)
12103 {
12104 switch (attrib.Name.LocalName)
12105 {
12106 case "Id":
12107 progId = this.core.GetAttributeValue(sourceLineNumbers, attrib);
12108 break;
12109 case "Advertise":
12110 progIdAdvertise = this.core.GetAttributeYesNoValue(sourceLineNumbers, attrib);
12111 break;
12112 case "Description":
12113 description = this.core.GetAttributeValue(sourceLineNumbers, attrib, EmptyRule.CanBeEmpty);
12114 break;
12115 case "Icon":
12116 icon = this.core.GetAttributeIdentifierValue(sourceLineNumbers, attrib);
12117 break;
12118 case "IconIndex":
12119 iconIndex = this.core.GetAttributeIntegerValue(sourceLineNumbers, attrib, short.MinValue + 1, short.MaxValue);
12120 break;
12121 case "NoOpen":
12122 noOpen = this.core.GetAttributeValue(sourceLineNumbers, attrib, EmptyRule.CanBeEmpty);
12123 break;
12124 default:
12125 this.core.UnexpectedAttribute(node, attrib);
12126 break;
12127 }
12128 }
12129 else
12130 {
12131 this.core.ParseExtensionAttribute(node, attrib);
12132 }
12133 }
12134
12135 if ((YesNoType.No == advertise && YesNoType.Yes == progIdAdvertise) || (YesNoType.Yes == advertise && YesNoType.No == progIdAdvertise))
12136 {
12137 this.core.OnMessage(WixErrors.AdvertiseStateMustMatch(sourceLineNumbers, advertise.ToString(), progIdAdvertise.ToString()));
12138 }
12139 else
12140 {
12141 advertise = progIdAdvertise;
12142 }
12143
12144 if (YesNoType.NotSet == advertise)
12145 {
12146 advertise = YesNoType.No;
12147 }
12148
12149 if (null != parent && (null != icon || CompilerConstants.IntegerNotSet != iconIndex))
12150 {
12151 this.core.OnMessage(WixErrors.VersionIndependentProgIdsCannotHaveIcons(sourceLineNumbers));
12152 }
12153
12154 YesNoType firstProgIdForNestedClass = YesNoType.Yes;
12155 foreach (XElement child in node.Elements())
12156 {
12157 if (CompilerCore.WixNamespace == child.Name.Namespace)
12158 {
12159 switch (child.Name.LocalName)
12160 {
12161 case "Extension":
12162 this.ParseExtensionElement(child, componentId, advertise, progId);
12163 foundExtension = true;
12164 break;
12165 case "ProgId":
12166 // Only allow one nested ProgId. If we have a child, we should not have a parent.
12167 if (null == parent)
12168 {
12169 if (YesNoType.Yes == advertise)
12170 {
12171 this.ParseProgIdElement(child, componentId, advertise, null, description, progId, ref foundExtension, firstProgIdForNestedClass);
12172 }
12173 else if (YesNoType.No == advertise)
12174 {
12175 this.ParseProgIdElement(child, componentId, advertise, classId, description, progId, ref foundExtension, firstProgIdForNestedClass);
12176 }
12177
12178 firstProgIdForNestedClass = YesNoType.No; // any ProgId after this one is definitely not the first.
12179 }
12180 else
12181 {
12182 SourceLineNumber childSourceLineNumbers = Preprocessor.GetSourceLineNumbers(child);
12183 this.core.OnMessage(WixErrors.ProgIdNestedTooDeep(childSourceLineNumbers));
12184 }
12185 break;
12186 default:
12187 this.core.UnexpectedElement(node, child);
12188 break;
12189 }
12190 }
12191 else
12192 {
12193 this.core.ParseExtensionElement(node, child);
12194 }
12195 }
12196
12197 if (YesNoType.Yes == advertise)
12198 {
12199 if (!this.core.EncounteredError)
12200 {
12201 Row row = this.core.CreateRow(sourceLineNumbers, "ProgId");
12202 row[0] = progId;
12203 row[1] = parent;
12204 row[2] = classId;
12205 row[3] = description;
12206 if (null != icon)
12207 {
12208 row[4] = icon;
12209 this.core.CreateSimpleReference(sourceLineNumbers, "Icon", icon);
12210 }
12211
12212 if (CompilerConstants.IntegerNotSet != iconIndex)
12213 {
12214 row[5] = iconIndex;
12215 }
12216
12217 this.core.EnsureTable(sourceLineNumbers, "Class");
12218 }
12219 }
12220 else if (YesNoType.No == advertise)
12221 {
12222 this.core.CreateRegistryRow(sourceLineNumbers, MsiInterop.MsidbRegistryRootClassesRoot, progId, String.Empty, description, componentId);
12223 if (null != classId)
12224 {
12225 this.core.CreateRegistryRow(sourceLineNumbers, MsiInterop.MsidbRegistryRootClassesRoot, String.Concat(progId, "\\CLSID"), String.Empty, classId, componentId);
12226 if (null != parent) // if this is a version independent ProgId
12227 {
12228 if (YesNoType.Yes == firstProgIdForClass)
12229 {
12230 this.core.CreateRegistryRow(sourceLineNumbers, MsiInterop.MsidbRegistryRootClassesRoot, String.Concat("CLSID\\", classId, "\\VersionIndependentProgID"), String.Empty, progId, componentId);
12231 }
12232
12233 this.core.CreateRegistryRow(sourceLineNumbers, MsiInterop.MsidbRegistryRootClassesRoot, String.Concat(progId, "\\CurVer"), String.Empty, parent, componentId);
12234 }
12235 else
12236 {
12237 if (YesNoType.Yes == firstProgIdForClass)
12238 {
12239 this.core.CreateRegistryRow(sourceLineNumbers, MsiInterop.MsidbRegistryRootClassesRoot, String.Concat("CLSID\\", classId, "\\ProgID"), String.Empty, progId, componentId);
12240 }
12241 }
12242 }
12243
12244 if (null != icon) // ProgId's Default Icon
12245 {
12246 this.core.CreateSimpleReference(sourceLineNumbers, "File", icon);
12247
12248 icon = String.Format(CultureInfo.InvariantCulture, "\"[#{0}]\"", icon);
12249
12250 if (CompilerConstants.IntegerNotSet != iconIndex)
12251 {
12252 icon = String.Concat(icon, ",", iconIndex);
12253 }
12254
12255 this.core.CreateRegistryRow(sourceLineNumbers, MsiInterop.MsidbRegistryRootClassesRoot, String.Concat(progId, "\\DefaultIcon"), String.Empty, icon, componentId);
12256 }
12257 }
12258
12259 if (null != noOpen)
12260 {
12261 this.core.CreateRegistryRow(sourceLineNumbers, MsiInterop.MsidbRegistryRootClassesRoot, progId, "NoOpen", noOpen, componentId); // ProgId NoOpen name
12262 }
12263
12264 // raise an error for an orphaned ProgId
12265 if (YesNoType.Yes == advertise && !foundExtension && null == parent && null == classId)
12266 {
12267 this.core.OnMessage(WixWarnings.OrphanedProgId(sourceLineNumbers, progId));
12268 }
12269
12270 return progId;
12271 }
12272
12273 /// <summary>
12274 /// Parses a property element.
12275 /// </summary>
12276 /// <param name="node">Element to parse.</param>
12277 private void ParsePropertyElement(XElement node)
12278 {
12279 SourceLineNumber sourceLineNumbers = Preprocessor.GetSourceLineNumbers(node);
12280 Identifier id = null;
12281 bool admin = false;
12282 bool complianceCheck = false;
12283 bool hidden = false;
12284 bool secure = false;
12285 YesNoType suppressModularization = YesNoType.NotSet;
12286 string value = null;
12287
12288 foreach (XAttribute attrib in node.Attributes())
12289 {
12290 if (String.IsNullOrEmpty(attrib.Name.NamespaceName) || CompilerCore.WixNamespace == attrib.Name.Namespace)
12291 {
12292 switch (attrib.Name.LocalName)
12293 {
12294 case "Id":
12295 id = this.core.GetAttributeIdentifier(sourceLineNumbers, attrib);
12296 break;
12297 case "Admin":
12298 admin = YesNoType.Yes == this.core.GetAttributeYesNoValue(sourceLineNumbers, attrib);
12299 break;
12300 case "ComplianceCheck":
12301 complianceCheck = YesNoType.Yes == this.core.GetAttributeYesNoValue(sourceLineNumbers, attrib);
12302 break;
12303 case "Hidden":
12304 hidden = YesNoType.Yes == this.core.GetAttributeYesNoValue(sourceLineNumbers, attrib);
12305 break;
12306 case "Secure":
12307 secure = YesNoType.Yes == this.core.GetAttributeYesNoValue(sourceLineNumbers, attrib);
12308 break;
12309 case "SuppressModularization":
12310 suppressModularization = this.core.GetAttributeYesNoValue(sourceLineNumbers, attrib);
12311 break;
12312 case "Value":
12313 value = this.core.GetAttributeValue(sourceLineNumbers, attrib);
12314 break;
12315 default:
12316 this.core.UnexpectedAttribute(node, attrib);
12317 break;
12318 }
12319 }
12320 else
12321 {
12322 this.core.ParseExtensionAttribute(node, attrib);
12323 }
12324 }
12325
12326 if (null == id)
12327 {
12328 this.core.OnMessage(WixErrors.ExpectedAttribute(sourceLineNumbers, node.Name.LocalName, "Id"));
12329 id = Identifier.Invalid;
12330 }
12331 else if ("ProductID" == id.Id)
12332 {
12333 this.core.OnMessage(WixWarnings.ProductIdAuthored(sourceLineNumbers));
12334 }
12335 else if ("SecureCustomProperties" == id.Id || "AdminProperties" == id.Id || "MsiHiddenProperties" == id.Id)
12336 {
12337 this.core.OnMessage(WixErrors.CannotAuthorSpecialProperties(sourceLineNumbers, id.Id));
12338 }
12339
12340 string innerText = this.core.GetTrimmedInnerText(node);
12341 if (null != value)
12342 {
12343 // cannot specify both the value attribute and inner text
12344 if (!String.IsNullOrEmpty(innerText))
12345 {
12346 this.core.OnMessage(WixErrors.IllegalAttributeWithInnerText(sourceLineNumbers, node.Name.LocalName, "Value"));
12347 }
12348 }
12349 else // value attribute not specified, use inner text if any.
12350 {
12351 value = innerText;
12352 }
12353
12354 if ("ErrorDialog" == id.Id)
12355 {
12356 this.core.CreateSimpleReference(sourceLineNumbers, "Dialog", value);
12357 }
12358
12359 foreach (XElement child in node.Elements())
12360 {
12361 if (CompilerCore.WixNamespace == child.Name.Namespace)
12362 {
12363 {
12364 switch (child.Name.LocalName)
12365 {
12366 case "ProductSearch":
12367 this.ParseProductSearchElement(child, id.Id);
12368 secure = true;
12369 break;
12370 default:
12371 // let ParseSearchSignatures handle standard AppSearch children and unknown elements
12372 break;
12373 }
12374 }
12375 }
12376 }
12377
12378 // see if this property is used for appSearch
12379 List<string> signatures = this.ParseSearchSignatures(node);
12380
12381 // If we're doing CCP then there must be a signature.
12382 if (complianceCheck && 0 == signatures.Count)
12383 {
12384 this.core.OnMessage(WixErrors.SearchElementRequiredWithAttribute(sourceLineNumbers, node.Name.LocalName, "ComplianceCheck", "yes"));
12385 }
12386
12387 foreach (string sig in signatures)
12388 {
12389 if (complianceCheck && !this.core.EncounteredError)
12390 {
12391 this.core.CreateRow(sourceLineNumbers, "CCPSearch", new Identifier(sig, AccessModifier.Private));
12392 }
12393
12394 this.AddAppSearch(sourceLineNumbers, id, sig);
12395 }
12396
12397 // If we're doing AppSearch get that setup.
12398 if (0 < signatures.Count)
12399 {
12400 this.AddProperty(sourceLineNumbers, id, value, admin, secure, hidden, false);
12401 }
12402 else // just a normal old property.
12403 {
12404 // If the property value is empty and none of the flags are set, print out a warning that we're ignoring
12405 // the element.
12406 if (String.IsNullOrEmpty(value) && !admin && !secure && !hidden)
12407 {
12408 this.core.OnMessage(WixWarnings.PropertyUseless(sourceLineNumbers, id.Id));
12409 }
12410 else // there is a value and/or a flag set, do that.
12411 {
12412 this.AddProperty(sourceLineNumbers, id, value, admin, secure, hidden, false);
12413 }
12414 }
12415
12416 if (!this.core.EncounteredError && YesNoType.Yes == suppressModularization)
12417 {
12418 this.core.OnMessage(WixWarnings.PropertyModularizationSuppressed(sourceLineNumbers));
12419
12420 this.core.CreateRow(sourceLineNumbers, "WixSuppressModularization", id);
12421 }
12422 }
12423
12424 /// <summary>
12425 /// Parses a RegistryKey element.
12426 /// </summary>
12427 /// <param name="node">Element to parse.</param>
12428 /// <param name="componentId">Identifier for parent component.</param>
12429 /// <param name="root">Root specified when element is nested under another Registry element, otherwise CompilerConstants.IntegerNotSet.</param>
12430 /// <param name="parentKey">Parent key for this Registry element when nested.</param>
12431 /// <param name="win64Component">true if the component is 64-bit.</param>
12432 /// <param name="possibleKeyPath">Identifier of this registry key since it could be the component's keypath.</param>
12433 /// <returns>Yes if this element was marked as the parent component's key path, No if explicitly marked as not being a key path, or NotSet otherwise.</returns>
12434 [SuppressMessage("Microsoft.Globalization", "CA1308:NormalizeStringsToUppercase", Justification = "Changing the way this string normalizes would result " +
12435 "in a change to the way the Registry table is generated, potentially causing extra churn in patches on an MSI built from an older version of WiX. " +
12436 "Furthermore, there is no security hole here, as the strings won't need to make a round trip")]
12437 private YesNoType ParseRegistryKeyElement(XElement node, string componentId, int root, string parentKey, bool win64Component, out string possibleKeyPath)
12438 {
12439 SourceLineNumber sourceLineNumbers = Preprocessor.GetSourceLineNumbers(node);
12440 Identifier id = null;
12441 string key = parentKey; // default to parent key path
12442 string action = null;
12443 bool forceCreateOnInstall = false;
12444 bool forceDeleteOnUninstall = false;
12445 Wix.RegistryKey.ActionType actionType = Wix.RegistryKey.ActionType.NotSet;
12446 YesNoType keyPath = YesNoType.NotSet;
12447
12448 possibleKeyPath = null;
12449
12450 foreach (XAttribute attrib in node.Attributes())
12451 {
12452 if (String.IsNullOrEmpty(attrib.Name.NamespaceName) || CompilerCore.WixNamespace == attrib.Name.Namespace)
12453 {
12454 switch (attrib.Name.LocalName)
12455 {
12456 case "Id":
12457 id = this.core.GetAttributeIdentifier(sourceLineNumbers, attrib);
12458 break;
12459 case "Action":
12460 this.core.OnMessage(WixWarnings.DeprecatedRegistryKeyActionAttribute(sourceLineNumbers));
12461 action = this.core.GetAttributeValue(sourceLineNumbers, attrib);
12462 if (0 < action.Length)
12463 {
12464 actionType = Wix.RegistryKey.ParseActionType(action);
12465 switch (actionType)
12466 {
12467 case Wix.RegistryKey.ActionType.create:
12468 forceCreateOnInstall = true;
12469 break;
12470 case Wix.RegistryKey.ActionType.createAndRemoveOnUninstall:
12471 forceCreateOnInstall = true;
12472 forceDeleteOnUninstall = true;
12473 break;
12474 case Wix.RegistryKey.ActionType.none:
12475 break;
12476 default:
12477 this.core.OnMessage(WixErrors.IllegalAttributeValue(sourceLineNumbers, node.Name.LocalName, attrib.Name.LocalName, action, "create", "createAndRemoveOnUninstall", "none"));
12478 break;
12479 }
12480 }
12481 break;
12482 case "ForceCreateOnInstall":
12483 forceCreateOnInstall = YesNoType.Yes == this.core.GetAttributeYesNoValue(sourceLineNumbers, attrib);
12484 break;
12485 case "ForceDeleteOnUninstall":
12486 forceDeleteOnUninstall = YesNoType.Yes == this.core.GetAttributeYesNoValue(sourceLineNumbers, attrib);
12487 break;
12488 case "Key":
12489 key = this.core.GetAttributeValue(sourceLineNumbers, attrib);
12490 if (null != parentKey)
12491 {
12492 key = Path.Combine(parentKey, key);
12493 }
12494 break;
12495 case "Root":
12496 if (CompilerConstants.IntegerNotSet != root)
12497 {
12498 this.core.OnMessage(WixErrors.RegistryRootInvalid(sourceLineNumbers));
12499 }
12500
12501 root = this.core.GetAttributeMsidbRegistryRootValue(sourceLineNumbers, attrib, true);
12502 break;
12503 default:
12504 this.core.UnexpectedAttribute(node, attrib);
12505 break;
12506 }
12507 }
12508 else
12509 {
12510 this.core.ParseExtensionAttribute(node, attrib);
12511 }
12512 }
12513
12514 string name = forceCreateOnInstall ? (forceDeleteOnUninstall ? "*" : "+") : (forceDeleteOnUninstall ? "-" : null);
12515
12516 if (forceCreateOnInstall || forceDeleteOnUninstall) // generates a Registry row, so an Id must be present
12517 {
12518 // generate the identifier if it wasn't provided
12519 if (null == id)
12520 {
12521 id = this.core.CreateIdentifier("reg", componentId, root.ToString(CultureInfo.InvariantCulture.NumberFormat), LowercaseOrNull(key), LowercaseOrNull(name));
12522 }
12523 }
12524 else // does not generate a Registry row, so no Id should be present
12525 {
12526 if (null != id)
12527 {
12528 this.core.OnMessage(WixErrors.IllegalAttributeWithoutOtherAttributes(sourceLineNumbers, node.Name.LocalName, "Id", "ForceCreateOnInstall", "ForceDeleteOnUninstall", "yes", true));
12529 }
12530 }
12531
12532 if (CompilerConstants.IntegerNotSet == root)
12533 {
12534 this.core.OnMessage(WixErrors.ExpectedAttribute(sourceLineNumbers, node.Name.LocalName, "Root"));
12535 root = CompilerConstants.IllegalInteger;
12536 }
12537
12538 if (null == key)
12539 {
12540 this.core.OnMessage(WixErrors.ExpectedAttribute(sourceLineNumbers, node.Name.LocalName, "Key"));
12541 key = String.Empty; // set the key to something to prevent null reference exceptions
12542 }
12543
12544 foreach (XElement child in node.Elements())
12545 {
12546 if (CompilerCore.WixNamespace == child.Name.Namespace)
12547 {
12548 string possibleChildKeyPath = null;
12549
12550 switch (child.Name.LocalName)
12551 {
12552 case "RegistryKey":
12553 if (YesNoType.Yes == this.ParseRegistryKeyElement(child, componentId, root, key, win64Component, out possibleChildKeyPath))
12554 {
12555 if (YesNoType.Yes == keyPath)
12556 {
12557 this.core.OnMessage(WixErrors.ComponentMultipleKeyPaths(sourceLineNumbers, child.Name.LocalName, "KeyPath", "yes", "File", "RegistryValue", "ODBCDataSource"));
12558 }
12559
12560 possibleKeyPath = possibleChildKeyPath; // the child is the key path
12561 keyPath = YesNoType.Yes;
12562 }
12563 else if (null == possibleKeyPath && null != possibleChildKeyPath)
12564 {
12565 possibleKeyPath = possibleChildKeyPath;
12566 }
12567 break;
12568 case "RegistryValue":
12569 if (YesNoType.Yes == this.ParseRegistryValueElement(child, componentId, root, key, win64Component, out possibleChildKeyPath))
12570 {
12571 if (YesNoType.Yes == keyPath)
12572 {
12573 this.core.OnMessage(WixErrors.ComponentMultipleKeyPaths(sourceLineNumbers, child.Name.LocalName, "KeyPath", "yes", "File", "RegistryValue", "ODBCDataSource"));
12574 }
12575
12576 possibleKeyPath = possibleChildKeyPath; // the child is the key path
12577 keyPath = YesNoType.Yes;
12578 }
12579 else if (null == possibleKeyPath && null != possibleChildKeyPath)
12580 {
12581 possibleKeyPath = possibleChildKeyPath;
12582 }
12583 break;
12584 case "Permission":
12585 if (!forceCreateOnInstall)
12586 {
12587 this.core.OnMessage(WixErrors.UnexpectedElementWithAttributeValue(sourceLineNumbers, node.Name.LocalName, child.Name.LocalName, "ForceCreateOnInstall", "yes"));
12588 }
12589 this.ParsePermissionElement(child, id.Id, "Registry");
12590 break;
12591 case "PermissionEx":
12592 if (!forceCreateOnInstall)
12593 {
12594 this.core.OnMessage(WixErrors.UnexpectedElementWithAttributeValue(sourceLineNumbers, node.Name.LocalName, child.Name.LocalName, "ForceCreateOnInstall", "yes"));
12595 }
12596 this.ParsePermissionExElement(child, id.Id, "Registry");
12597 break;
12598 default:
12599 this.core.UnexpectedElement(node, child);
12600 break;
12601 }
12602 }
12603 else
12604 {
12605 Dictionary<string, string> context = new Dictionary<string, string>() { { "RegistryId", id.Id }, { "ComponentId", componentId }, { "Win64", win64Component.ToString() } };
12606 this.core.ParseExtensionElement(node, child, context);
12607 }
12608 }
12609
12610
12611 if (!this.core.EncounteredError && null != name)
12612 {
12613 Row row = this.core.CreateRow(sourceLineNumbers, "Registry", id);
12614 row[1] = root;
12615 row[2] = key;
12616 row[3] = name;
12617 row[4] = null;
12618 row[5] = componentId;
12619 }
12620
12621 return keyPath;
12622 }
12623
12624 /// <summary>
12625 /// Parses a RegistryValue element.
12626 /// </summary>
12627 /// <param name="node">Element to parse.</param>
12628 /// <param name="componentId">Identifier for parent component.</param>
12629 /// <param name="root">Root specified when element is nested under a RegistryKey element, otherwise CompilerConstants.IntegerNotSet.</param>
12630 /// <param name="parentKey">Root specified when element is nested under a RegistryKey element, otherwise CompilerConstants.IntegerNotSet.</param>
12631 /// <param name="win64Component">true if the component is 64-bit.</param>
12632 /// <param name="possibleKeyPath">Identifier of this registry key since it could be the component's keypath.</param>
12633 /// <returns>Yes if this element was marked as the parent component's key path, No if explicitly marked as not being a key path, or NotSet otherwise.</returns>
12634 [SuppressMessage("Microsoft.Globalization", "CA1308:NormalizeStringsToUppercase", Justification = "Changing the way this string normalizes would result " +
12635 "in a change to the way the Registry table is generated, potentially causing extra churn in patches on an MSI built from an older version of WiX. " +
12636 "Furthermore, there is no security hole here, as the strings won't need to make a round trip")]
12637 private YesNoType ParseRegistryValueElement(XElement node, string componentId, int root, string parentKey, bool win64Component, out string possibleKeyPath)
12638 {
12639 SourceLineNumber sourceLineNumbers = Preprocessor.GetSourceLineNumbers(node);
12640 Identifier id = null;
12641 string key = parentKey; // default to parent key path
12642 string name = null;
12643 string value = null;
12644 string type = null;
12645 Wix.RegistryValue.TypeType typeType = Wix.RegistryValue.TypeType.NotSet;
12646 string action = null;
12647 Wix.RegistryValue.ActionType actionType = Wix.RegistryValue.ActionType.NotSet;
12648 YesNoType keyPath = YesNoType.NotSet;
12649 bool couldBeKeyPath = true; // assume that this is a regular registry key that could become the key path
12650
12651 possibleKeyPath = null;
12652
12653 foreach (XAttribute attrib in node.Attributes())
12654 {
12655 if (String.IsNullOrEmpty(attrib.Name.NamespaceName) || CompilerCore.WixNamespace == attrib.Name.Namespace)
12656 {
12657 switch (attrib.Name.LocalName)
12658 {
12659 case "Id":
12660 id = this.core.GetAttributeIdentifier(sourceLineNumbers, attrib);
12661 break;
12662 case "Action":
12663 action = this.core.GetAttributeValue(sourceLineNumbers, attrib);
12664 if (0 < action.Length)
12665 {
12666 if (!Wix.RegistryValue.TryParseActionType(action, out actionType))
12667 {
12668 this.core.OnMessage(WixErrors.IllegalAttributeValue(sourceLineNumbers, node.Name.LocalName, attrib.Name.LocalName, action, "append", "prepend", "write"));
12669 }
12670 }
12671 break;
12672 case "Key":
12673 key = this.core.GetAttributeValue(sourceLineNumbers, attrib);
12674 if (null != parentKey)
12675 {
12676 if (parentKey.EndsWith("\\", StringComparison.Ordinal))
12677 {
12678 key = String.Concat(parentKey, key);
12679 }
12680 else
12681 {
12682 key = String.Concat(parentKey, "\\", key);
12683 }
12684 }
12685 break;
12686 case "KeyPath":
12687 keyPath = this.core.GetAttributeYesNoValue(sourceLineNumbers, attrib);
12688 break;
12689 case "Name":
12690 name = this.core.GetAttributeValue(sourceLineNumbers, attrib);
12691 break;
12692 case "Root":
12693 if (CompilerConstants.IntegerNotSet != root)
12694 {
12695 this.core.OnMessage(WixErrors.RegistryRootInvalid(sourceLineNumbers));
12696 }
12697
12698 root = this.core.GetAttributeMsidbRegistryRootValue(sourceLineNumbers, attrib, true);
12699 break;
12700 case "Type":
12701 type = this.core.GetAttributeValue(sourceLineNumbers, attrib);
12702 if (0 < type.Length)
12703 {
12704 if (!Wix.RegistryValue.TryParseTypeType(type, out typeType))
12705 {
12706 this.core.OnMessage(WixErrors.IllegalAttributeValue(sourceLineNumbers, node.Name.LocalName, attrib.Name.LocalName, type, "binary", "expandable", "integer", "multiString", "string"));
12707 }
12708 }
12709 break;
12710 case "Value":
12711 value = this.core.GetAttributeValue(sourceLineNumbers, attrib, EmptyRule.CanBeEmpty);
12712 break;
12713 default:
12714 this.core.UnexpectedAttribute(node, attrib);
12715 break;
12716 }
12717 }
12718 else
12719 {
12720 this.core.ParseExtensionAttribute(node, attrib);
12721 }
12722 }
12723
12724 // generate the identifier if it wasn't provided
12725 if (null == id)
12726 {
12727 id = this.core.CreateIdentifier("reg", componentId, root.ToString(CultureInfo.InvariantCulture.NumberFormat), LowercaseOrNull(key), LowercaseOrNull(name));
12728 }
12729
12730 if ((Wix.RegistryValue.ActionType.append == actionType || Wix.RegistryValue.ActionType.prepend == actionType) &&
12731 Wix.RegistryValue.TypeType.multiString != typeType)
12732 {
12733 this.core.OnMessage(WixErrors.IllegalAttributeValueWithoutOtherAttribute(sourceLineNumbers, node.Name.LocalName, "Action", action, "Type", "multiString"));
12734 }
12735
12736 if (null == key)
12737 {
12738 this.core.OnMessage(WixErrors.ExpectedAttribute(sourceLineNumbers, node.Name.LocalName, "Key"));
12739 }
12740
12741 if (CompilerConstants.IntegerNotSet == root)
12742 {
12743 this.core.OnMessage(WixErrors.ExpectedAttribute(sourceLineNumbers, node.Name.LocalName, "Root"));
12744 }
12745
12746 if (null == type)
12747 {
12748 this.core.OnMessage(WixErrors.ExpectedAttribute(sourceLineNumbers, node.Name.LocalName, "Type"));
12749 }
12750
12751 foreach (XElement child in node.Elements())
12752 {
12753 if (CompilerCore.WixNamespace == child.Name.Namespace)
12754 {
12755 switch (child.Name.LocalName)
12756 {
12757 case "MultiStringValue":
12758 if (Wix.RegistryValue.TypeType.multiString != typeType && null != value)
12759 {
12760 this.core.OnMessage(WixErrors.RegistryMultipleValuesWithoutMultiString(sourceLineNumbers, node.Name.LocalName, "Value", child.Name.LocalName, "Type"));
12761 }
12762 else if (null == value)
12763 {
12764 value = Common.GetInnerText(child);
12765 }
12766 else
12767 {
12768 value = String.Concat(value, "[~]", Common.GetInnerText(child));
12769 }
12770 break;
12771 case "Permission":
12772 this.ParsePermissionElement(child, id.Id, "Registry");
12773 break;
12774 case "PermissionEx":
12775 this.ParsePermissionExElement(child, id.Id, "Registry");
12776 break;
12777 default:
12778 this.core.UnexpectedElement(node, child);
12779 break;
12780 }
12781 }
12782 else
12783 {
12784 Dictionary<string, string> context = new Dictionary<string, string>() { { "RegistryId", id.Id }, { "ComponentId", componentId }, { "Win64", win64Component.ToString() } };
12785 this.core.ParseExtensionElement(node, child, context);
12786 }
12787 }
12788
12789
12790 switch (typeType)
12791 {
12792 case Wix.RegistryValue.TypeType.binary:
12793 value = String.Concat("#x", value);
12794 break;
12795 case Wix.RegistryValue.TypeType.expandable:
12796 value = String.Concat("#%", value);
12797 break;
12798 case Wix.RegistryValue.TypeType.integer:
12799 value = String.Concat("#", value);
12800 break;
12801 case Wix.RegistryValue.TypeType.multiString:
12802 switch (actionType)
12803 {
12804 case Wix.RegistryValue.ActionType.append:
12805 value = String.Concat("[~]", value);
12806 break;
12807 case Wix.RegistryValue.ActionType.prepend:
12808 value = String.Concat(value, "[~]");
12809 break;
12810 case Wix.RegistryValue.ActionType.write:
12811 default:
12812 if (null != value && -1 == value.IndexOf("[~]", StringComparison.Ordinal))
12813 {
12814 value = String.Format(CultureInfo.InvariantCulture, "[~]{0}[~]", value);
12815 }
12816 break;
12817 }
12818 break;
12819 case Wix.RegistryValue.TypeType.@string:
12820 // escape the leading '#' character for string registry keys
12821 if (null != value && value.StartsWith("#", StringComparison.Ordinal))
12822 {
12823 value = String.Concat("#", value);
12824 }
12825 break;
12826 }
12827
12828 // value may be set by child MultiStringValue elements, so it must be checked here
12829 if (null == value)
12830 {
12831 this.core.OnMessage(WixErrors.ExpectedAttribute(sourceLineNumbers, node.Name.LocalName, "Value"));
12832 }
12833 else if (0 == value.Length && ("+" == name || "-" == name || "*" == name)) // prevent accidental authoring of special name values
12834 {
12835 this.core.OnMessage(WixErrors.RegistryNameValueIncorrect(sourceLineNumbers, node.Name.LocalName, "Name", name));
12836 }
12837
12838 if (!this.core.EncounteredError)
12839 {
12840 Row row = this.core.CreateRow(sourceLineNumbers, "Registry", id);
12841 row[1] = root;
12842 row[2] = key;
12843 row[3] = name;
12844 row[4] = value;
12845 row[5] = componentId;
12846 }
12847
12848 // If this was just a regular registry key (that could be the key path)
12849 // and no child registry key set the possible key path, let's make this
12850 // Registry/@Id a possible key path.
12851 if (couldBeKeyPath && null == possibleKeyPath)
12852 {
12853 possibleKeyPath = id.Id;
12854 }
12855
12856 return keyPath;
12857 }
12858
12859 /// <summary>
12860 /// Parses a RemoveRegistryKey element.
12861 /// </summary>
12862 /// <param name="node">The element to parse.</param>
12863 /// <param name="componentId">The component identifier of the parent element.</param>
12864 [SuppressMessage("Microsoft.Globalization", "CA1308:NormalizeStringsToUppercase", Justification = "Changing the way this string normalizes would result " +
12865 "in a change to the way the Registry table is generated, potentially causing extra churn in patches on an MSI built from an older version of WiX. " +
12866 "Furthermore, there is no security hole here, as the strings won't need to make a round trip")]
12867 private void ParseRemoveRegistryKeyElement(XElement node, string componentId)
12868 {
12869 SourceLineNumber sourceLineNumbers = Preprocessor.GetSourceLineNumbers(node);
12870 Identifier id = null;
12871 string action = null;
12872 Wix.RemoveRegistryKey.ActionType actionType = Wix.RemoveRegistryKey.ActionType.NotSet;
12873 string key = null;
12874 string name = "-";
12875 int root = CompilerConstants.IntegerNotSet;
12876
12877 foreach (XAttribute attrib in node.Attributes())
12878 {
12879 if (String.IsNullOrEmpty(attrib.Name.NamespaceName) || CompilerCore.WixNamespace == attrib.Name.Namespace)
12880 {
12881 switch (attrib.Name.LocalName)
12882 {
12883 case "Id":
12884 id = this.core.GetAttributeIdentifier(sourceLineNumbers, attrib);
12885 break;
12886 case "Action":
12887 action = this.core.GetAttributeValue(sourceLineNumbers, attrib);
12888 if (0 < action.Length)
12889 {
12890 if (!Wix.RemoveRegistryKey.TryParseActionType(action, out actionType))
12891 {
12892 this.core.OnMessage(WixErrors.IllegalAttributeValue(sourceLineNumbers, node.Name.LocalName, attrib.Name.LocalName, action, "removeOnInstall", "removeOnUninstall"));
12893 }
12894 }
12895 break;
12896 case "Key":
12897 key = this.core.GetAttributeValue(sourceLineNumbers, attrib);
12898 break;
12899 case "Root":
12900 root = this.core.GetAttributeMsidbRegistryRootValue(sourceLineNumbers, attrib, true);
12901 break;
12902 default:
12903 this.core.UnexpectedAttribute(node, attrib);
12904 break;
12905 }
12906 }
12907 else
12908 {
12909 this.core.ParseExtensionAttribute(node, attrib);
12910 }
12911 }
12912
12913 // generate the identifier if it wasn't provided
12914 if (null == id)
12915 {
12916 id = this.core.CreateIdentifier("reg", componentId, root.ToString(CultureInfo.InvariantCulture.NumberFormat), LowercaseOrNull(key), LowercaseOrNull(name));
12917 }
12918
12919 if (null == action)
12920 {
12921 this.core.OnMessage(WixErrors.ExpectedAttribute(sourceLineNumbers, node.Name.LocalName, "Action"));
12922 }
12923
12924 if (null == key)
12925 {
12926 this.core.OnMessage(WixErrors.ExpectedAttribute(sourceLineNumbers, node.Name.LocalName, "Key"));
12927 }
12928
12929 if (CompilerConstants.IntegerNotSet == root)
12930 {
12931 this.core.OnMessage(WixErrors.ExpectedAttribute(sourceLineNumbers, node.Name.LocalName, "Root"));
12932 }
12933
12934 this.core.ParseForExtensionElements(node);
12935
12936 if (!this.core.EncounteredError)
12937 {
12938 Row row = this.core.CreateRow(sourceLineNumbers, (Wix.RemoveRegistryKey.ActionType.removeOnUninstall == actionType ? "Registry" : "RemoveRegistry"), id);
12939 row[1] = root;
12940 row[2] = key;
12941 row[3] = name;
12942 if (Wix.RemoveRegistryKey.ActionType.removeOnUninstall == actionType) // Registry table
12943 {
12944 row[4] = null;
12945 row[5] = componentId;
12946 }
12947 else // RemoveRegistry table
12948 {
12949 row[4] = componentId;
12950 }
12951 }
12952 }
12953
12954 /// <summary>
12955 /// Parses a RemoveRegistryValue element.
12956 /// </summary>
12957 /// <param name="node">The element to parse.</param>
12958 /// <param name="componentId">The component identifier of the parent element.</param>
12959 [SuppressMessage("Microsoft.Globalization", "CA1308:NormalizeStringsToUppercase", Justification = "Changing the way this string normalizes would result " +
12960 "in a change to the way the Registry table is generated, potentially causing extra churn in patches on an MSI built from an older version of WiX. " +
12961 "Furthermore, there is no security hole here, as the strings won't need to make a round trip")]
12962 private void ParseRemoveRegistryValueElement(XElement node, string componentId)
12963 {
12964 SourceLineNumber sourceLineNumbers = Preprocessor.GetSourceLineNumbers(node);
12965 Identifier id = null;
12966 string key = null;
12967 string name = null;
12968 int root = CompilerConstants.IntegerNotSet;
12969
12970 foreach (XAttribute attrib in node.Attributes())
12971 {
12972 if (String.IsNullOrEmpty(attrib.Name.NamespaceName) || CompilerCore.WixNamespace == attrib.Name.Namespace)
12973 {
12974 switch (attrib.Name.LocalName)
12975 {
12976 case "Id":
12977 id = this.core.GetAttributeIdentifier(sourceLineNumbers, attrib);
12978 break;
12979 case "Key":
12980 key = this.core.GetAttributeValue(sourceLineNumbers, attrib);
12981 break;
12982 case "Name":
12983 name = this.core.GetAttributeValue(sourceLineNumbers, attrib);
12984 break;
12985 case "Root":
12986 root = this.core.GetAttributeMsidbRegistryRootValue(sourceLineNumbers, attrib, true);
12987 break;
12988 default:
12989 this.core.UnexpectedAttribute(node, attrib);
12990 break;
12991 }
12992 }
12993 else
12994 {
12995 this.core.ParseExtensionAttribute(node, attrib);
12996 }
12997 }
12998
12999 // generate the identifier if it wasn't provided
13000 if (null == id)
13001 {
13002 id = this.core.CreateIdentifier("reg", componentId, root.ToString(CultureInfo.InvariantCulture.NumberFormat), LowercaseOrNull(key), LowercaseOrNull(name));
13003 }
13004
13005 if (null == key)
13006 {
13007 this.core.OnMessage(WixErrors.ExpectedAttribute(sourceLineNumbers, node.Name.LocalName, "Key"));
13008 }
13009
13010 if (CompilerConstants.IntegerNotSet == root)
13011 {
13012 this.core.OnMessage(WixErrors.ExpectedAttribute(sourceLineNumbers, node.Name.LocalName, "Root"));
13013 }
13014
13015 this.core.ParseForExtensionElements(node);
13016
13017 if (!this.core.EncounteredError)
13018 {
13019 Row row = this.core.CreateRow(sourceLineNumbers, "RemoveRegistry", id);
13020 row[1] = root;
13021 row[2] = key;
13022 row[3] = name;
13023 row[4] = componentId;
13024 }
13025 }
13026
13027 /// <summary>
13028 /// Parses a remove file element.
13029 /// </summary>
13030 /// <param name="node">Element to parse.</param>
13031 /// <param name="componentId">Identifier of parent component.</param>
13032 /// <param name="parentDirectory">Identifier of the parent component's directory.</param>
13033 private void ParseRemoveFileElement(XElement node, string componentId, string parentDirectory)
13034 {
13035 SourceLineNumber sourceLineNumbers = Preprocessor.GetSourceLineNumbers(node);
13036 Identifier id = null;
13037 string directory = null;
13038 string name = null;
13039 int on = CompilerConstants.IntegerNotSet;
13040 string property = null;
13041 string shortName = null;
13042
13043 foreach (XAttribute attrib in node.Attributes())
13044 {
13045 if (String.IsNullOrEmpty(attrib.Name.NamespaceName) || CompilerCore.WixNamespace == attrib.Name.Namespace)
13046 {
13047 switch (attrib.Name.LocalName)
13048 {
13049 case "Id":
13050 id = this.core.GetAttributeIdentifier(sourceLineNumbers, attrib);
13051 break;
13052 case "Directory":
13053 directory = this.core.CreateDirectoryReferenceFromInlineSyntax(sourceLineNumbers, attrib, parentDirectory);
13054 break;
13055 case "Name":
13056 name = this.core.GetAttributeLongFilename(sourceLineNumbers, attrib, true);
13057 break;
13058 case "On":
13059 Wix.InstallUninstallType onValue = this.core.GetAttributeInstallUninstallValue(sourceLineNumbers, attrib);
13060 switch (onValue)
13061 {
13062 case Wix.InstallUninstallType.install:
13063 on = 1;
13064 break;
13065 case Wix.InstallUninstallType.uninstall:
13066 on = 2;
13067 break;
13068 case Wix.InstallUninstallType.both:
13069 on = 3;
13070 break;
13071 default:
13072 on = CompilerConstants.IllegalInteger;
13073 break;
13074 }
13075 break;
13076 case "Property":
13077 property = this.core.GetAttributeIdentifierValue(sourceLineNumbers, attrib);
13078 break;
13079 case "ShortName":
13080 shortName = this.core.GetAttributeShortFilename(sourceLineNumbers, attrib, true);
13081 break;
13082 default:
13083 this.core.UnexpectedAttribute(node, attrib);
13084 break;
13085 }
13086 }
13087 else
13088 {
13089 this.core.ParseExtensionAttribute(node, attrib);
13090 }
13091 }
13092
13093 if (null == name)
13094 {
13095 this.core.OnMessage(WixErrors.ExpectedAttribute(sourceLineNumbers, node.Name.LocalName, "Name"));
13096 }
13097 else if (0 < name.Length)
13098 {
13099 if (this.core.IsValidShortFilename(name, true))
13100 {
13101 if (null == shortName)
13102 {
13103 shortName = name;
13104 name = null;
13105 }
13106 else
13107 {
13108 this.core.OnMessage(WixErrors.IllegalAttributeValueWithOtherAttribute(sourceLineNumbers, node.Name.LocalName, "Name", name, "ShortName"));
13109 }
13110 }
13111 else if (null == shortName) // generate a short file name.
13112 {
13113 shortName = this.core.CreateShortName(name, true, true, node.Name.LocalName, componentId);
13114 }
13115 }
13116
13117 if (CompilerConstants.IntegerNotSet == on)
13118 {
13119 this.core.OnMessage(WixErrors.ExpectedAttribute(sourceLineNumbers, node.Name.LocalName, "On"));
13120 on = CompilerConstants.IllegalInteger;
13121 }
13122
13123 if (null != directory && null != property)
13124 {
13125 this.core.OnMessage(WixErrors.IllegalAttributeWithOtherAttribute(sourceLineNumbers, node.Name.LocalName, "Property", "Directory", directory));
13126 }
13127
13128 if (null == id)
13129 {
13130 id = this.core.CreateIdentifier("rmf", directory ?? property ?? parentDirectory, LowercaseOrNull(shortName), LowercaseOrNull(name), on.ToString());
13131 }
13132
13133 this.core.ParseForExtensionElements(node);
13134
13135 if (!this.core.EncounteredError)
13136 {
13137 Row row = this.core.CreateRow(sourceLineNumbers, "RemoveFile", id);
13138 row[1] = componentId;
13139 row[2] = GetMsiFilenameValue(shortName, name);
13140 if (null != directory)
13141 {
13142 row[3] = directory;
13143 }
13144 else if (null != property)
13145 {
13146 row[3] = property;
13147 }
13148 else
13149 {
13150 row[3] = parentDirectory;
13151 }
13152 row[4] = on;
13153 }
13154 }
13155
13156 /// <summary>
13157 /// Parses a RemoveFolder element.
13158 /// </summary>
13159 /// <param name="node">Element to parse.</param>
13160 /// <param name="componentId">Identifier of parent component.</param>
13161 /// <param name="parentDirectory">Identifier of parent component's directory.</param>
13162 private void ParseRemoveFolderElement(XElement node, string componentId, string parentDirectory)
13163 {
13164 SourceLineNumber sourceLineNumbers = Preprocessor.GetSourceLineNumbers(node);
13165 Identifier id = null;
13166 string directory = null;
13167 int on = CompilerConstants.IntegerNotSet;
13168 string property = null;
13169
13170 foreach (XAttribute attrib in node.Attributes())
13171 {
13172 if (String.IsNullOrEmpty(attrib.Name.NamespaceName) || CompilerCore.WixNamespace == attrib.Name.Namespace)
13173 {
13174 switch (attrib.Name.LocalName)
13175 {
13176 case "Id":
13177 id = this.core.GetAttributeIdentifier(sourceLineNumbers, attrib);
13178 break;
13179 case "Directory":
13180 directory = this.core.CreateDirectoryReferenceFromInlineSyntax(sourceLineNumbers, attrib, parentDirectory);
13181 break;
13182 case "On":
13183 Wix.InstallUninstallType onValue = this.core.GetAttributeInstallUninstallValue(sourceLineNumbers, attrib);
13184 switch (onValue)
13185 {
13186 case Wix.InstallUninstallType.install:
13187 on = 1;
13188 break;
13189 case Wix.InstallUninstallType.uninstall:
13190 on = 2;
13191 break;
13192 case Wix.InstallUninstallType.both:
13193 on = 3;
13194 break;
13195 default:
13196 on = CompilerConstants.IllegalInteger;
13197 break;
13198 }
13199 break;
13200 case "Property":
13201 property = this.core.GetAttributeValue(sourceLineNumbers, attrib);
13202 break;
13203 default:
13204 this.core.UnexpectedAttribute(node, attrib);
13205 break;
13206 }
13207 }
13208 else
13209 {
13210 this.core.ParseExtensionAttribute(node, attrib);
13211 }
13212 }
13213
13214 if (CompilerConstants.IntegerNotSet == on)
13215 {
13216 this.core.OnMessage(WixErrors.ExpectedAttribute(sourceLineNumbers, node.Name.LocalName, "On"));
13217 on = CompilerConstants.IllegalInteger;
13218 }
13219
13220 if (null != directory && null != property)
13221 {
13222 this.core.OnMessage(WixErrors.IllegalAttributeWithOtherAttribute(sourceLineNumbers, node.Name.LocalName, "Property", "Directory", directory));
13223 }
13224
13225 if (null == id)
13226 {
13227 id = this.core.CreateIdentifier("rmf", directory ?? property ?? parentDirectory, on.ToString());
13228 }
13229
13230 this.core.ParseForExtensionElements(node);
13231
13232 if (!this.core.EncounteredError)
13233 {
13234 Row row = this.core.CreateRow(sourceLineNumbers, "RemoveFile", id);
13235 row[1] = componentId;
13236 row[2] = null;
13237 if (null != directory)
13238 {
13239 row[3] = directory;
13240 }
13241 else if (null != property)
13242 {
13243 row[3] = property;
13244 }
13245 else
13246 {
13247 row[3] = parentDirectory;
13248 }
13249 row[4] = on;
13250 }
13251 }
13252
13253 /// <summary>
13254 /// Parses a reserve cost element.
13255 /// </summary>
13256 /// <param name="node">Element to parse.</param>
13257 /// <param name="componentId">Identifier of parent component.</param>
13258 /// <param name="directoryId">Optional and default identifier of referenced directory.</param>
13259 private void ParseReserveCostElement(XElement node, string componentId, string directoryId)
13260 {
13261 SourceLineNumber sourceLineNumbers = Preprocessor.GetSourceLineNumbers(node);
13262 Identifier id = null;
13263 int runFromSource = CompilerConstants.IntegerNotSet;
13264 int runLocal = CompilerConstants.IntegerNotSet;
13265
13266 foreach (XAttribute attrib in node.Attributes())
13267 {
13268 if (String.IsNullOrEmpty(attrib.Name.NamespaceName) || CompilerCore.WixNamespace == attrib.Name.Namespace)
13269 {
13270 switch (attrib.Name.LocalName)
13271 {
13272 case "Id":
13273 id = this.core.GetAttributeIdentifier(sourceLineNumbers, attrib);
13274 break;
13275 case "Directory":
13276 directoryId = this.core.CreateDirectoryReferenceFromInlineSyntax(sourceLineNumbers, attrib, directoryId);
13277 break;
13278 case "RunFromSource":
13279 runFromSource = this.core.GetAttributeIntegerValue(sourceLineNumbers, attrib, 0, int.MaxValue);
13280 break;
13281 case "RunLocal":
13282 runLocal = this.core.GetAttributeIntegerValue(sourceLineNumbers, attrib, 0, int.MaxValue);
13283 break;
13284 default:
13285 this.core.UnexpectedAttribute(node, attrib);
13286 break;
13287 }
13288 }
13289 else
13290 {
13291 this.core.ParseExtensionAttribute(node, attrib);
13292 }
13293 }
13294
13295 if (null == id)
13296 {
13297 id = this.core.CreateIdentifier("rc", componentId, directoryId);
13298 }
13299
13300 if (CompilerConstants.IntegerNotSet == runFromSource)
13301 {
13302 this.core.OnMessage(WixErrors.ExpectedAttribute(sourceLineNumbers, node.Name.LocalName, "RunFromSource"));
13303 }
13304
13305 if (CompilerConstants.IntegerNotSet == runLocal)
13306 {
13307 this.core.OnMessage(WixErrors.ExpectedAttribute(sourceLineNumbers, node.Name.LocalName, "RunLocal"));
13308 }
13309
13310 this.core.ParseForExtensionElements(node);
13311
13312 if (!this.core.EncounteredError)
13313 {
13314 Row row = this.core.CreateRow(sourceLineNumbers, "ReserveCost", id);
13315 row[1] = componentId;
13316 row[2] = directoryId;
13317 row[3] = runLocal;
13318 row[4] = runFromSource;
13319 }
13320 }
13321
13322 /// <summary>
13323 /// Parses a sequence element.
13324 /// </summary>
13325 /// <param name="node">Element to parse.</param>
13326 /// <param name="sequenceTable">Name of sequence table.</param>
13327 private void ParseSequenceElement(XElement node, string sequenceTable)
13328 {
13329 // use the proper table name internally
13330 if ("AdvertiseExecuteSequence" == sequenceTable)
13331 {
13332 sequenceTable = "AdvtExecuteSequence";
13333 }
13334
13335 // Parse each action in the sequence.
13336 foreach (XElement child in node.Elements())
13337 {
13338 SourceLineNumber childSourceLineNumbers = Preprocessor.GetSourceLineNumbers(child);
13339 string actionName = child.Name.LocalName;
13340 string afterAction = null;
13341 string beforeAction = null;
13342 string condition = null;
13343 bool customAction = "Custom" == actionName;
13344 bool overridable = false;
13345 int exitSequence = CompilerConstants.IntegerNotSet;
13346 int sequence = CompilerConstants.IntegerNotSet;
13347 bool showDialog = "Show" == actionName;
13348 bool specialAction = "InstallExecute" == actionName || "InstallExecuteAgain" == actionName || "RemoveExistingProducts" == actionName || "DisableRollback" == actionName || "ScheduleReboot" == actionName || "ForceReboot" == actionName || "ResolveSource" == actionName;
13349 bool specialStandardAction = "AppSearch" == actionName || "CCPSearch" == actionName || "RMCCPSearch" == actionName || "LaunchConditions" == actionName || "FindRelatedProducts" == actionName;
13350 bool suppress = false;
13351
13352 foreach (XAttribute attrib in child.Attributes())
13353 {
13354 if (String.IsNullOrEmpty(attrib.Name.NamespaceName) || CompilerCore.WixNamespace == attrib.Name.Namespace)
13355 {
13356 switch (attrib.Name.LocalName)
13357 {
13358 case "Action":
13359 if (customAction)
13360 {
13361 actionName = this.core.GetAttributeIdentifierValue(childSourceLineNumbers, attrib);
13362 this.core.CreateSimpleReference(childSourceLineNumbers, "CustomAction", actionName);
13363 }
13364 else
13365 {
13366 this.core.UnexpectedAttribute(child, attrib);
13367 }
13368 break;
13369 case "After":
13370 if (customAction || showDialog || specialAction || specialStandardAction)
13371 {
13372 afterAction = this.core.GetAttributeIdentifierValue(childSourceLineNumbers, attrib);
13373 this.core.CreateSimpleReference(childSourceLineNumbers, "WixAction", sequenceTable, afterAction);
13374 }
13375 else
13376 {
13377 this.core.UnexpectedAttribute(child, attrib);
13378 }
13379 break;
13380 case "Before":
13381 if (customAction || showDialog || specialAction || specialStandardAction)
13382 {
13383 beforeAction = this.core.GetAttributeIdentifierValue(childSourceLineNumbers, attrib);
13384 this.core.CreateSimpleReference(childSourceLineNumbers, "WixAction", sequenceTable, beforeAction);
13385 }
13386 else
13387 {
13388 this.core.UnexpectedAttribute(child, attrib);
13389 }
13390 break;
13391 case "Dialog":
13392 if (showDialog)
13393 {
13394 actionName = this.core.GetAttributeIdentifierValue(childSourceLineNumbers, attrib);
13395 this.core.CreateSimpleReference(childSourceLineNumbers, "Dialog", actionName);
13396 }
13397 else
13398 {
13399 this.core.UnexpectedAttribute(child, attrib);
13400 }
13401 break;
13402 case "OnExit":
13403 if (customAction || showDialog || specialAction)
13404 {
13405 Wix.ExitType exitValue = this.core.GetAttributeExitValue(childSourceLineNumbers, attrib);
13406 switch (exitValue)
13407 {
13408 case Wix.ExitType.success:
13409 exitSequence = -1;
13410 break;
13411 case Wix.ExitType.cancel:
13412 exitSequence = -2;
13413 break;
13414 case Wix.ExitType.error:
13415 exitSequence = -3;
13416 break;
13417 case Wix.ExitType.suspend:
13418 exitSequence = -4;
13419 break;
13420 }
13421 }
13422 else
13423 {
13424 this.core.UnexpectedAttribute(child, attrib);
13425 }
13426 break;
13427 case "Overridable":
13428 overridable = YesNoType.Yes == this.core.GetAttributeYesNoValue(childSourceLineNumbers, attrib);
13429 break;
13430 case "Sequence":
13431 sequence = this.core.GetAttributeIntegerValue(childSourceLineNumbers, attrib, 1, short.MaxValue);
13432 break;
13433 case "Suppress":
13434 suppress = YesNoType.Yes == this.core.GetAttributeYesNoValue(childSourceLineNumbers, attrib);
13435 break;
13436 default:
13437 this.core.UnexpectedAttribute(node, attrib);
13438 break;
13439 }
13440 }
13441 else
13442 {
13443 this.core.ParseExtensionAttribute(node, attrib);
13444 }
13445 }
13446
13447
13448 // Get the condition from the inner text of the element.
13449 condition = this.core.GetConditionInnerText(child);
13450
13451 if (customAction && "Custom" == actionName)
13452 {
13453 this.core.OnMessage(WixErrors.ExpectedAttribute(childSourceLineNumbers, child.Name.LocalName, "Action"));
13454 }
13455 else if (showDialog && "Show" == actionName)
13456 {
13457 this.core.OnMessage(WixErrors.ExpectedAttribute(childSourceLineNumbers, child.Name.LocalName, "Dialog"));
13458 }
13459
13460 if (CompilerConstants.IntegerNotSet != sequence)
13461 {
13462 if (CompilerConstants.IntegerNotSet != exitSequence)
13463 {
13464 this.core.OnMessage(WixErrors.IllegalAttributeWithOtherAttribute(childSourceLineNumbers, child.Name.LocalName, "Sequence", "OnExit"));
13465 }
13466 else if (null != beforeAction || null != afterAction)
13467 {
13468 this.core.OnMessage(WixErrors.IllegalAttributeWithOtherAttribute(childSourceLineNumbers, child.Name.LocalName, "Sequence", "Before", "After"));
13469 }
13470 }
13471 else // sequence not specified use OnExit (which may also be not set).
13472 {
13473 sequence = exitSequence;
13474 }
13475
13476 if (null != beforeAction && null != afterAction)
13477 {
13478 this.core.OnMessage(WixErrors.IllegalAttributeWithOtherAttribute(childSourceLineNumbers, child.Name.LocalName, "After", "Before"));
13479 }
13480 else if ((customAction || showDialog || specialAction) && !suppress && CompilerConstants.IntegerNotSet == sequence && null == beforeAction && null == afterAction)
13481 {
13482 this.core.OnMessage(WixErrors.NeedSequenceBeforeOrAfter(childSourceLineNumbers, child.Name.LocalName));
13483 }
13484
13485 // action that is scheduled to occur before/after itself
13486 if (beforeAction == actionName)
13487 {
13488 this.core.OnMessage(WixErrors.ActionScheduledRelativeToItself(childSourceLineNumbers, child.Name.LocalName, "Before", beforeAction));
13489 }
13490 else if (afterAction == actionName)
13491 {
13492 this.core.OnMessage(WixErrors.ActionScheduledRelativeToItself(childSourceLineNumbers, child.Name.LocalName, "After", afterAction));
13493 }
13494
13495 // normal standard actions cannot be set overridable by the user (since they are overridable by default)
13496 if (overridable && WindowsInstallerStandard.IsStandardAction(actionName) && !specialAction)
13497 {
13498 this.core.OnMessage(WixErrors.UnexpectedAttribute(childSourceLineNumbers, child.Name.LocalName, "Overridable"));
13499 }
13500
13501 // suppress cannot be specified at the same time as Before, After, or Sequence
13502 if (suppress && (null != afterAction || null != beforeAction || CompilerConstants.IntegerNotSet != sequence || overridable))
13503 {
13504 this.core.OnMessage(WixErrors.IllegalAttributeWithOtherAttributes(childSourceLineNumbers, child.Name.LocalName, "Suppress", "Before", "After", "Sequence", "Overridable"));
13505 }
13506
13507 this.core.ParseForExtensionElements(child);
13508
13509 // add the row and any references needed
13510 if (!this.core.EncounteredError)
13511 {
13512 if (suppress)
13513 {
13514 Row row = this.core.CreateRow(childSourceLineNumbers, "WixSuppressAction");
13515 row[0] = sequenceTable;
13516 row[1] = actionName;
13517 }
13518 else
13519 {
13520 Row row = this.core.CreateRow(childSourceLineNumbers, "WixAction");
13521 row[0] = sequenceTable;
13522 row[1] = actionName;
13523 row[2] = condition;
13524 if (CompilerConstants.IntegerNotSet != sequence)
13525 {
13526 row[3] = sequence;
13527 }
13528 row[4] = beforeAction;
13529 row[5] = afterAction;
13530 row[6] = overridable ? 1 : 0;
13531 }
13532 }
13533 }
13534 }
13535
13536
13537 /// <summary>
13538 /// Parses a service config element.
13539 /// </summary>
13540 /// <param name="node">Element to parse.</param>
13541 /// <param name="componentId">Identifier of parent component.</param>
13542 /// <param name="serviceName">Optional element containing parent's service name.</param>
13543 private void ParseServiceConfigElement(XElement node, string componentId, string serviceName)
13544 {
13545 SourceLineNumber sourceLineNumbers = Preprocessor.GetSourceLineNumbers(node);
13546 Identifier id = null;
13547 string delayedAutoStart = null;
13548 string failureActionsWhen = null;
13549 int events = 0;
13550 string name = serviceName;
13551 string preShutdownDelay = null;
13552 string requiredPrivileges = null;
13553 string sid = null;
13554
13555 this.core.OnMessage(WixWarnings.ServiceConfigFamilyNotSupported(sourceLineNumbers, node.Name.LocalName));
13556
13557 foreach (XAttribute attrib in node.Attributes())
13558 {
13559 if (String.IsNullOrEmpty(attrib.Name.NamespaceName) || CompilerCore.WixNamespace == attrib.Name.Namespace)
13560 {
13561 switch (attrib.Name.LocalName)
13562 {
13563 case "Id":
13564 id = this.core.GetAttributeIdentifier(sourceLineNumbers, attrib);
13565 break;
13566 case "DelayedAutoStart":
13567 delayedAutoStart = this.core.GetAttributeValue(sourceLineNumbers, attrib);
13568 if (0 < delayedAutoStart.Length)
13569 {
13570 switch (delayedAutoStart)
13571 {
13572 case "no":
13573 delayedAutoStart = "0";
13574 break;
13575 case "yes":
13576 delayedAutoStart = "1";
13577 break;
13578 default:
13579 // allow everything else to pass through that are hopefully "formatted" Properties.
13580 break;
13581 }
13582 }
13583 break;
13584 case "FailureActionsWhen":
13585 failureActionsWhen = this.core.GetAttributeValue(sourceLineNumbers, attrib);
13586 if (0 < failureActionsWhen.Length)
13587 {
13588 switch (failureActionsWhen)
13589 {
13590 case "failedToStop":
13591 failureActionsWhen = "0";
13592 break;
13593 case "failedToStopOrReturnedError":
13594 failureActionsWhen = "1";
13595 break;
13596 default:
13597 // allow everything else to pass through that are hopefully "formatted" Properties.
13598 break;
13599 }
13600 }
13601 break;
13602 case "OnInstall":
13603 YesNoType install = this.core.GetAttributeYesNoValue(sourceLineNumbers, attrib);
13604 if (YesNoType.Yes == install)
13605 {
13606 events |= MsiInterop.MsidbServiceConfigEventInstall;
13607 }
13608 break;
13609 case "OnReinstall":
13610 YesNoType reinstall = this.core.GetAttributeYesNoValue(sourceLineNumbers, attrib);
13611 if (YesNoType.Yes == reinstall)
13612 {
13613 events |= MsiInterop.MsidbServiceConfigEventReinstall;
13614 }
13615 break;
13616 case "OnUninstall":
13617 YesNoType uninstall = this.core.GetAttributeYesNoValue(sourceLineNumbers, attrib);
13618 if (YesNoType.Yes == uninstall)
13619 {
13620 events |= MsiInterop.MsidbServiceConfigEventUninstall;
13621 }
13622 break;
13623 default:
13624 this.core.UnexpectedAttribute(node, attrib);
13625 break;
13626 case "PreShutdownDelay":
13627 preShutdownDelay = this.core.GetAttributeValue(sourceLineNumbers, attrib, EmptyRule.CanBeEmpty);
13628 break;
13629 case "ServiceName":
13630 if (!String.IsNullOrEmpty(serviceName))
13631 {
13632 this.core.OnMessage(WixErrors.IllegalAttributeWhenNested(sourceLineNumbers, node.Name.LocalName, attrib.Name.LocalName, "ServiceInstall"));
13633 }
13634
13635 name = this.core.GetAttributeValue(sourceLineNumbers, attrib);
13636 break;
13637 case "ServiceSid":
13638 sid = this.core.GetAttributeValue(sourceLineNumbers, attrib);
13639 if (0 < sid.Length)
13640 {
13641 switch (sid)
13642 {
13643 case "none":
13644 sid = "0";
13645 break;
13646 case "restricted":
13647 sid = "3";
13648 break;
13649 case "unrestricted":
13650 sid = "1";
13651 break;
13652 default:
13653 // allow everything else to pass through that are hopefully "formatted" Properties.
13654 break;
13655 }
13656 }
13657 break;
13658 }
13659 }
13660 else
13661 {
13662 this.core.ParseExtensionAttribute(node, attrib);
13663 }
13664 }
13665
13666 // Get the ServiceConfig required privilegs.
13667 foreach (XElement child in node.Elements())
13668 {
13669 if (CompilerCore.WixNamespace == child.Name.Namespace)
13670 {
13671 switch (child.Name.LocalName)
13672 {
13673 case "RequiredPrivilege":
13674 string privilege = this.core.GetTrimmedInnerText(child);
13675 switch (privilege)
13676 {
13677 case "assignPrimaryToken":
13678 privilege = "SeAssignPrimaryTokenPrivilege";
13679 break;
13680 case "audit":
13681 privilege = "SeAuditPrivilege";
13682 break;
13683 case "backup":
13684 privilege = "SeBackupPrivilege";
13685 break;
13686 case "changeNotify":
13687 privilege = "SeChangeNotifyPrivilege";
13688 break;
13689 case "createGlobal":
13690 privilege = "SeCreateGlobalPrivilege";
13691 break;
13692 case "createPagefile":
13693 privilege = "SeCreatePagefilePrivilege";
13694 break;
13695 case "createPermanent":
13696 privilege = "SeCreatePermanentPrivilege";
13697 break;
13698 case "createSymbolicLink":
13699 privilege = "SeCreateSymbolicLinkPrivilege";
13700 break;
13701 case "createToken":
13702 privilege = "SeCreateTokenPrivilege";
13703 break;
13704 case "debug":
13705 privilege = "SeDebugPrivilege";
13706 break;
13707 case "enableDelegation":
13708 privilege = "SeEnableDelegationPrivilege";
13709 break;
13710 case "impersonate":
13711 privilege = "SeImpersonatePrivilege";
13712 break;
13713 case "increaseBasePriority":
13714 privilege = "SeIncreaseBasePriorityPrivilege";
13715 break;
13716 case "increaseQuota":
13717 privilege = "SeIncreaseQuotaPrivilege";
13718 break;
13719 case "increaseWorkingSet":
13720 privilege = "SeIncreaseWorkingSetPrivilege";
13721 break;
13722 case "loadDriver":
13723 privilege = "SeLoadDriverPrivilege";
13724 break;
13725 case "lockMemory":
13726 privilege = "SeLockMemoryPrivilege";
13727 break;
13728 case "machineAccount":
13729 privilege = "SeMachineAccountPrivilege";
13730 break;
13731 case "manageVolume":
13732 privilege = "SeManageVolumePrivilege";
13733 break;
13734 case "profileSingleProcess":
13735 privilege = "SeProfileSingleProcessPrivilege";
13736 break;
13737 case "relabel":
13738 privilege = "SeRelabelPrivilege";
13739 break;
13740 case "remoteShutdown":
13741 privilege = "SeRemoteShutdownPrivilege";
13742 break;
13743 case "restore":
13744 privilege = "SeRestorePrivilege";
13745 break;
13746 case "security":
13747 privilege = "SeSecurityPrivilege";
13748 break;
13749 case "shutdown":
13750 privilege = "SeShutdownPrivilege";
13751 break;
13752 case "syncAgent":
13753 privilege = "SeSyncAgentPrivilege";
13754 break;
13755 case "systemEnvironment":
13756 privilege = "SeSystemEnvironmentPrivilege";
13757 break;
13758 case "systemProfile":
13759 privilege = "SeSystemProfilePrivilege";
13760 break;
13761 case "systemTime":
13762 case "modifySystemTime":
13763 privilege = "SeSystemtimePrivilege";
13764 break;
13765 case "takeOwnership":
13766 privilege = "SeTakeOwnershipPrivilege";
13767 break;
13768 case "tcb":
13769 case "trustedComputerBase":
13770 privilege = "SeTcbPrivilege";
13771 break;
13772 case "timeZone":
13773 case "modifyTimeZone":
13774 privilege = "SeTimeZonePrivilege";
13775 break;
13776 case "trustedCredManAccess":
13777 case "trustedCredentialManagerAccess":
13778 privilege = "SeTrustedCredManAccessPrivilege";
13779 break;
13780 case "undock":
13781 privilege = "SeUndockPrivilege";
13782 break;
13783 case "unsolicitedInput":
13784 privilege = "SeUnsolicitedInputPrivilege";
13785 break;
13786 default:
13787 // allow everything else to pass through that are hopefully "formatted" Properties.
13788 break;
13789 }
13790
13791 if (null != requiredPrivileges)
13792 {
13793 requiredPrivileges = String.Concat(requiredPrivileges, "[~]");
13794 }
13795 requiredPrivileges = String.Concat(requiredPrivileges, privilege);
13796 break;
13797 default:
13798 this.core.UnexpectedElement(node, child);
13799 break;
13800 }
13801 }
13802 else
13803 {
13804 this.core.ParseExtensionElement(node, child);
13805 }
13806 }
13807
13808 if (String.IsNullOrEmpty(name))
13809 {
13810 this.core.OnMessage(WixErrors.ExpectedAttribute(sourceLineNumbers, node.Name.LocalName, "ServiceName"));
13811 }
13812 else if (null == id)
13813 {
13814 id = this.core.CreateIdentifierFromFilename(name);
13815 }
13816
13817 if (0 == events)
13818 {
13819 this.core.OnMessage(WixErrors.ExpectedAttributes(sourceLineNumbers, node.Name.LocalName, "OnInstall", "OnReinstall", "OnUninstall"));
13820 }
13821
13822 if (String.IsNullOrEmpty(delayedAutoStart) && String.IsNullOrEmpty(failureActionsWhen) && String.IsNullOrEmpty(preShutdownDelay) && String.IsNullOrEmpty(requiredPrivileges) && String.IsNullOrEmpty(sid))
13823 {
13824 this.core.OnMessage(WixErrors.ExpectedAttributes(sourceLineNumbers, node.Name.LocalName, "DelayedAutoStart", "FailureActionsWhen", "PreShutdownDelay", "ServiceSid", "RequiredPrivilege"));
13825 }
13826
13827 if (!this.core.EncounteredError)
13828 {
13829 if (!String.IsNullOrEmpty(delayedAutoStart))
13830 {
13831 Row row = this.core.CreateRow(sourceLineNumbers, "MsiServiceConfig", new Identifier(String.Concat(id.Id, ".DS"), id.Access));
13832 row[1] = name;
13833 row[2] = events;
13834 row[3] = 3;
13835 row[4] = delayedAutoStart;
13836 row[5] = componentId;
13837 }
13838
13839 if (!String.IsNullOrEmpty(failureActionsWhen))
13840 {
13841 Row row = this.core.CreateRow(sourceLineNumbers, "MsiServiceConfig", new Identifier(String.Concat(id.Id, ".FA"), id.Access));
13842 row[1] = name;
13843 row[2] = events;
13844 row[3] = 4;
13845 row[4] = failureActionsWhen;
13846 row[5] = componentId;
13847 }
13848
13849 if (!String.IsNullOrEmpty(sid))
13850 {
13851 Row row = this.core.CreateRow(sourceLineNumbers, "MsiServiceConfig", new Identifier(String.Concat(id.Id, ".SS"), id.Access));
13852 row[1] = name;
13853 row[2] = events;
13854 row[3] = 5;
13855 row[4] = sid;
13856 row[5] = componentId;
13857 }
13858
13859 if (!String.IsNullOrEmpty(requiredPrivileges))
13860 {
13861 Row row = this.core.CreateRow(sourceLineNumbers, "MsiServiceConfig", new Identifier(String.Concat(id.Id, ".RP"), id.Access));
13862 row[1] = name;
13863 row[2] = events;
13864 row[3] = 6;
13865 row[4] = requiredPrivileges;
13866 row[5] = componentId;
13867 }
13868
13869 if (!String.IsNullOrEmpty(preShutdownDelay))
13870 {
13871 Row row = this.core.CreateRow(sourceLineNumbers, "MsiServiceConfig", new Identifier(String.Concat(id.Id, ".PD"), id.Access));
13872 row[1] = name;
13873 row[2] = events;
13874 row[3] = 7;
13875 row[4] = preShutdownDelay;
13876 row[5] = componentId;
13877 }
13878 }
13879 }
13880
13881 /// <summary>
13882 /// Parses a service config failure actions element.
13883 /// </summary>
13884 /// <param name="node">Element to parse.</param>
13885 /// <param name="componentId">Identifier of parent component.</param>
13886 /// <param name="serviceName">Optional element containing parent's service name.</param>
13887 private void ParseServiceConfigFailureActionsElement(XElement node, string componentId, string serviceName)
13888 {
13889 SourceLineNumber sourceLineNumbers = Preprocessor.GetSourceLineNumbers(node);
13890 Identifier id = null;
13891 int events = 0;
13892 string name = serviceName;
13893 int resetPeriod = CompilerConstants.IntegerNotSet;
13894 string rebootMessage = null;
13895 string command = null;
13896 string actions = null;
13897 string actionsDelays = null;
13898
13899 this.core.OnMessage(WixWarnings.ServiceConfigFamilyNotSupported(sourceLineNumbers, node.Name.LocalName));
13900
13901 foreach (XAttribute attrib in node.Attributes())
13902 {
13903 if (String.IsNullOrEmpty(attrib.Name.NamespaceName) || CompilerCore.WixNamespace == attrib.Name.Namespace)
13904 {
13905 switch (attrib.Name.LocalName)
13906 {
13907 case "Id":
13908 id = this.core.GetAttributeIdentifier(sourceLineNumbers, attrib);
13909 break;
13910 case "Command":
13911 command = this.core.GetAttributeValue(sourceLineNumbers, attrib, EmptyRule.CanBeEmpty);
13912 break;
13913 case "OnInstall":
13914 YesNoType install = this.core.GetAttributeYesNoValue(sourceLineNumbers, attrib);
13915 if (YesNoType.Yes == install)
13916 {
13917 events |= MsiInterop.MsidbServiceConfigEventInstall;
13918 }
13919 break;
13920 case "OnReinstall":
13921 YesNoType reinstall = this.core.GetAttributeYesNoValue(sourceLineNumbers, attrib);
13922 if (YesNoType.Yes == reinstall)
13923 {
13924 events |= MsiInterop.MsidbServiceConfigEventReinstall;
13925 }
13926 break;
13927 case "OnUninstall":
13928 YesNoType uninstall = this.core.GetAttributeYesNoValue(sourceLineNumbers, attrib);
13929 if (YesNoType.Yes == uninstall)
13930 {
13931 events |= MsiInterop.MsidbServiceConfigEventUninstall;
13932 }
13933 break;
13934 case "RebootMessage":
13935 rebootMessage = this.core.GetAttributeValue(sourceLineNumbers, attrib, EmptyRule.CanBeEmpty);
13936 break;
13937 case "ResetPeriod":
13938 resetPeriod = this.core.GetAttributeIntegerValue(sourceLineNumbers, attrib, 0, int.MaxValue);
13939 break;
13940 case "ServiceName":
13941 if (!String.IsNullOrEmpty(serviceName))
13942 {
13943 this.core.OnMessage(WixErrors.IllegalAttributeWhenNested(sourceLineNumbers, node.Name.LocalName, attrib.Name.LocalName, "ServiceInstall"));
13944 }
13945
13946 name = this.core.GetAttributeValue(sourceLineNumbers, attrib);
13947 break;
13948 default:
13949 this.core.UnexpectedAttribute(node, attrib);
13950 break;
13951 }
13952 }
13953 else
13954 {
13955 this.core.ParseExtensionAttribute(node, attrib);
13956 }
13957 }
13958
13959 // Get the ServiceConfigFailureActions actions.
13960 foreach (XElement child in node.Elements())
13961 {
13962 if (CompilerCore.WixNamespace == child.Name.Namespace)
13963 {
13964 switch (child.Name.LocalName)
13965 {
13966 case "Failure":
13967 string action = null;
13968 string delay = null;
13969 SourceLineNumber childSourceLineNumbers = Preprocessor.GetSourceLineNumbers(child);
13970
13971 foreach (XAttribute childAttrib in child.Attributes())
13972 {
13973 if (String.IsNullOrEmpty(childAttrib.Name.NamespaceName) || CompilerCore.WixNamespace == childAttrib.Name.Namespace)
13974 {
13975 switch (childAttrib.Name.LocalName)
13976 {
13977 case "Action":
13978 action = this.core.GetAttributeValue(childSourceLineNumbers, childAttrib);
13979 switch (action)
13980 {
13981 case "none":
13982 action = "0";
13983 break;
13984 case "restartComputer":
13985 action = "2";
13986 break;
13987 case "restartService":
13988 action = "1";
13989 break;
13990 case "runCommand":
13991 action = "3";
13992 break;
13993 default:
13994 // allow everything else to pass through that are hopefully "formatted" Properties.
13995 break;
13996 }
13997 break;
13998 case "Delay":
13999 delay = this.core.GetAttributeValue(childSourceLineNumbers, childAttrib);
14000 break;
14001 default:
14002 this.core.UnexpectedAttribute(child, childAttrib);
14003 break;
14004 }
14005 }
14006 }
14007
14008 if (String.IsNullOrEmpty(action))
14009 {
14010 this.core.OnMessage(WixErrors.ExpectedAttribute(sourceLineNumbers, child.Name.LocalName, "Action"));
14011 }
14012
14013 if (String.IsNullOrEmpty(delay))
14014 {
14015 this.core.OnMessage(WixErrors.ExpectedAttribute(sourceLineNumbers, child.Name.LocalName, "Delay"));
14016 }
14017
14018 if (!String.IsNullOrEmpty(actions))
14019 {
14020 actions = String.Concat(actions, "[~]");
14021 }
14022 actions = String.Concat(actions, action);
14023
14024 if (!String.IsNullOrEmpty(actionsDelays))
14025 {
14026 actionsDelays = String.Concat(actionsDelays, "[~]");
14027 }
14028 actionsDelays = String.Concat(actionsDelays, delay);
14029 break;
14030 default:
14031 this.core.UnexpectedElement(node, child);
14032 break;
14033 }
14034 }
14035 else
14036 {
14037 this.core.ParseExtensionElement(node, child);
14038 }
14039 }
14040
14041 if (String.IsNullOrEmpty(name))
14042 {
14043 this.core.OnMessage(WixErrors.ExpectedAttribute(sourceLineNumbers, node.Name.LocalName, "ServiceName"));
14044 }
14045 else if (null == id)
14046 {
14047 id = this.core.CreateIdentifierFromFilename(name);
14048 }
14049
14050 if (0 == events)
14051 {
14052 this.core.OnMessage(WixErrors.ExpectedAttributes(sourceLineNumbers, node.Name.LocalName, "OnInstall", "OnReinstall", "OnUninstall"));
14053 }
14054
14055 if (!this.core.EncounteredError)
14056 {
14057 Row row = this.core.CreateRow(sourceLineNumbers, "MsiServiceConfigFailureActions", id);
14058 row[1] = name;
14059 row[2] = events;
14060 if (CompilerConstants.IntegerNotSet != resetPeriod)
14061 {
14062 row[3] = resetPeriod;
14063 }
14064 row[4] = rebootMessage ?? "[~]";
14065 row[5] = command ?? "[~]";
14066 row[6] = actions;
14067 row[7] = actionsDelays;
14068 row[8] = componentId;
14069 }
14070 }
14071
14072 /// <summary>
14073 /// Parses a service control element.
14074 /// </summary>
14075 /// <param name="node">Element to parse.</param>
14076 /// <param name="componentId">Identifier of parent component.</param>
14077 private void ParseServiceControlElement(XElement node, string componentId)
14078 {
14079 SourceLineNumber sourceLineNumbers = Preprocessor.GetSourceLineNumbers(node);
14080 string arguments = null;
14081 int events = 0; // default is to do nothing
14082 Identifier id = null;
14083 string name = null;
14084 YesNoType wait = YesNoType.NotSet;
14085
14086 foreach (XAttribute attrib in node.Attributes())
14087 {
14088 if (String.IsNullOrEmpty(attrib.Name.NamespaceName) || CompilerCore.WixNamespace == attrib.Name.Namespace)
14089 {
14090 switch (attrib.Name.LocalName)
14091 {
14092 case "Id":
14093 id = this.core.GetAttributeIdentifier(sourceLineNumbers, attrib);
14094 break;
14095 case "Name":
14096 name = this.core.GetAttributeValue(sourceLineNumbers, attrib);
14097 break;
14098 case "Remove":
14099 Wix.InstallUninstallType removeValue = this.core.GetAttributeInstallUninstallValue(sourceLineNumbers, attrib);
14100 switch (removeValue)
14101 {
14102 case Wix.InstallUninstallType.install:
14103 events |= MsiInterop.MsidbServiceControlEventDelete;
14104 break;
14105 case Wix.InstallUninstallType.uninstall:
14106 events |= MsiInterop.MsidbServiceControlEventUninstallDelete;
14107 break;
14108 case Wix.InstallUninstallType.both:
14109 events |= MsiInterop.MsidbServiceControlEventDelete | MsiInterop.MsidbServiceControlEventUninstallDelete;
14110 break;
14111 }
14112 break;
14113 case "Start":
14114 Wix.InstallUninstallType startValue = this.core.GetAttributeInstallUninstallValue(sourceLineNumbers, attrib);
14115 switch (startValue)
14116 {
14117 case Wix.InstallUninstallType.install:
14118 events |= MsiInterop.MsidbServiceControlEventStart;
14119 break;
14120 case Wix.InstallUninstallType.uninstall:
14121 events |= MsiInterop.MsidbServiceControlEventUninstallStart;
14122 break;
14123 case Wix.InstallUninstallType.both:
14124 events |= MsiInterop.MsidbServiceControlEventStart | MsiInterop.MsidbServiceControlEventUninstallStart;
14125 break;
14126 }
14127 break;
14128 case "Stop":
14129 Wix.InstallUninstallType stopValue = this.core.GetAttributeInstallUninstallValue(sourceLineNumbers, attrib);
14130 switch (stopValue)
14131 {
14132 case Wix.InstallUninstallType.install:
14133 events |= MsiInterop.MsidbServiceControlEventStop;
14134 break;
14135 case Wix.InstallUninstallType.uninstall:
14136 events |= MsiInterop.MsidbServiceControlEventUninstallStop;
14137 break;
14138 case Wix.InstallUninstallType.both:
14139 events |= MsiInterop.MsidbServiceControlEventStop | MsiInterop.MsidbServiceControlEventUninstallStop;
14140 break;
14141 }
14142 break;
14143 case "Wait":
14144 wait = this.core.GetAttributeYesNoValue(sourceLineNumbers, attrib);
14145 break;
14146 default:
14147 this.core.UnexpectedAttribute(node, attrib);
14148 break;
14149 }
14150 }
14151 else
14152 {
14153 this.core.ParseExtensionAttribute(node, attrib);
14154 }
14155 }
14156
14157 if (null == id)
14158 {
14159 id = this.core.CreateIdentifierFromFilename(name);
14160 }
14161
14162 if (null == name)
14163 {
14164 this.core.OnMessage(WixErrors.ExpectedAttribute(sourceLineNumbers, node.Name.LocalName, "Name"));
14165 }
14166
14167 // get the ServiceControl arguments
14168 foreach (XElement child in node.Elements())
14169 {
14170 if (CompilerCore.WixNamespace == child.Name.Namespace)
14171 {
14172 switch (child.Name.LocalName)
14173 {
14174 case "ServiceArgument":
14175 if (null != arguments)
14176 {
14177 arguments = String.Concat(arguments, "[~]");
14178 }
14179 arguments = String.Concat(arguments, this.core.GetTrimmedInnerText(child));
14180 break;
14181 default:
14182 this.core.UnexpectedElement(node, child);
14183 break;
14184 }
14185 }
14186 else
14187 {
14188 this.core.ParseExtensionElement(node, child);
14189 }
14190 }
14191
14192 if (!this.core.EncounteredError)
14193 {
14194 Row row = this.core.CreateRow(sourceLineNumbers, "ServiceControl", id);
14195 row[1] = name;
14196 row[2] = events;
14197 row[3] = arguments;
14198 if (YesNoType.NotSet != wait)
14199 {
14200 row[4] = YesNoType.Yes == wait ? 1 : 0;
14201 }
14202 row[5] = componentId;
14203 }
14204 }
14205
14206 /// <summary>
14207 /// Parses a service dependency element.
14208 /// </summary>
14209 /// <param name="node">Element to parse.</param>
14210 /// <returns>Parsed sevice dependency name.</returns>
14211 private string ParseServiceDependencyElement(XElement node)
14212 {
14213 SourceLineNumber sourceLineNumbers = Preprocessor.GetSourceLineNumbers(node);
14214 string dependency = null;
14215 bool group = false;
14216
14217 foreach (XAttribute attrib in node.Attributes())
14218 {
14219 if (String.IsNullOrEmpty(attrib.Name.NamespaceName) || CompilerCore.WixNamespace == attrib.Name.Namespace)
14220 {
14221 switch (attrib.Name.LocalName)
14222 {
14223 case "Id":
14224 dependency = this.core.GetAttributeValue(sourceLineNumbers, attrib);
14225 break;
14226 case "Group":
14227 group = YesNoType.Yes == this.core.GetAttributeYesNoValue(sourceLineNumbers, attrib);
14228 break;
14229 default:
14230 this.core.UnexpectedAttribute(node, attrib);
14231 break;
14232 }
14233 }
14234 else
14235 {
14236 this.core.ParseExtensionAttribute(node, attrib);
14237 }
14238 }
14239
14240 if (null == dependency)
14241 {
14242 this.core.OnMessage(WixErrors.ExpectedAttribute(sourceLineNumbers, node.Name.LocalName, "Id"));
14243 }
14244
14245 this.core.ParseForExtensionElements(node);
14246
14247 return group ? String.Concat("+", dependency) : dependency;
14248 }
14249
14250 /// <summary>
14251 /// Parses a service install element.
14252 /// </summary>
14253 /// <param name="node">Element to parse.</param>
14254 /// <param name="componentId">Identifier of parent component.</param>
14255 private void ParseServiceInstallElement(XElement node, string componentId, bool win64Component)
14256 {
14257 SourceLineNumber sourceLineNumbers = Preprocessor.GetSourceLineNumbers(node);
14258 Identifier id = null;
14259 string account = null;
14260 string arguments = null;
14261 string dependencies = null;
14262 string description = null;
14263 string displayName = null;
14264 bool eraseDescription = false;
14265 int errorbits = 0;
14266 string loadOrderGroup = null;
14267 string name = null;
14268 string password = null;
14269 int startType = 0;
14270 int typebits = 0;
14271
14272 foreach (XAttribute attrib in node.Attributes())
14273 {
14274 if (String.IsNullOrEmpty(attrib.Name.NamespaceName) || CompilerCore.WixNamespace == attrib.Name.Namespace)
14275 {
14276 switch (attrib.Name.LocalName)
14277 {
14278 case "Id":
14279 id = this.core.GetAttributeIdentifier(sourceLineNumbers, attrib);
14280 break;
14281 case "Account":
14282 account = this.core.GetAttributeValue(sourceLineNumbers, attrib);
14283 break;
14284 case "Arguments":
14285 arguments = this.core.GetAttributeValue(sourceLineNumbers, attrib);
14286 break;
14287 case "Description":
14288 description = this.core.GetAttributeValue(sourceLineNumbers, attrib);
14289 break;
14290 case "DisplayName":
14291 displayName = this.core.GetAttributeValue(sourceLineNumbers, attrib);
14292 break;
14293 case "EraseDescription":
14294 eraseDescription = YesNoType.Yes == this.core.GetAttributeYesNoValue(sourceLineNumbers, attrib);
14295 break;
14296 case "ErrorControl":
14297 string errorControlValue = this.core.GetAttributeValue(sourceLineNumbers, attrib);
14298 if (0 < errorControlValue.Length)
14299 {
14300 Wix.ServiceInstall.ErrorControlType errorControlType = Wix.ServiceInstall.ParseErrorControlType(errorControlValue);
14301 switch (errorControlType)
14302 {
14303 case Wix.ServiceInstall.ErrorControlType.ignore:
14304 errorbits |= MsiInterop.MsidbServiceInstallErrorIgnore;
14305 break;
14306 case Wix.ServiceInstall.ErrorControlType.normal:
14307 errorbits |= MsiInterop.MsidbServiceInstallErrorNormal;
14308 break;
14309 case Wix.ServiceInstall.ErrorControlType.critical:
14310 errorbits |= MsiInterop.MsidbServiceInstallErrorCritical;
14311 break;
14312 default:
14313 this.core.OnMessage(WixErrors.IllegalAttributeValue(sourceLineNumbers, node.Name.LocalName, attrib.Name.LocalName, errorControlValue, "ignore", "normal", "critical"));
14314 break;
14315 }
14316 }
14317 break;
14318 case "Interactive":
14319 if (YesNoType.Yes == this.core.GetAttributeYesNoValue(sourceLineNumbers, attrib))
14320 {
14321 typebits |= MsiInterop.MsidbServiceInstallInteractive;
14322 }
14323 break;
14324 case "LoadOrderGroup":
14325 loadOrderGroup = this.core.GetAttributeValue(sourceLineNumbers, attrib);
14326 break;
14327 case "Name":
14328 name = this.core.GetAttributeValue(sourceLineNumbers, attrib);
14329 break;
14330 case "Password":
14331 password = this.core.GetAttributeValue(sourceLineNumbers, attrib);
14332 break;
14333 case "Start":
14334 string startValue = this.core.GetAttributeValue(sourceLineNumbers, attrib);
14335 if (0 < startValue.Length)
14336 {
14337 Wix.ServiceInstall.StartType start = Wix.ServiceInstall.ParseStartType(startValue);
14338 switch (start)
14339 {
14340 case Wix.ServiceInstall.StartType.auto:
14341 startType = MsiInterop.MsidbServiceInstallAutoStart;
14342 break;
14343 case Wix.ServiceInstall.StartType.demand:
14344 startType = MsiInterop.MsidbServiceInstallDemandStart;
14345 break;
14346 case Wix.ServiceInstall.StartType.disabled:
14347 startType = MsiInterop.MsidbServiceInstallDisabled;
14348 break;
14349 case Wix.ServiceInstall.StartType.boot:
14350 case Wix.ServiceInstall.StartType.system:
14351 this.core.OnMessage(WixErrors.ValueNotSupported(sourceLineNumbers, node.Name.LocalName, attrib.Name.LocalName, startValue));
14352 break;
14353 default:
14354 this.core.OnMessage(WixErrors.IllegalAttributeValue(sourceLineNumbers, node.Name.LocalName, attrib.Name.LocalName, startValue, "auto", "demand", "disabled"));
14355 break;
14356 }
14357 }
14358 break;
14359 case "Type":
14360 string typeValue = this.core.GetAttributeValue(sourceLineNumbers, attrib);
14361 if (0 < typeValue.Length)
14362 {
14363 Wix.ServiceInstall.TypeType typeType = Wix.ServiceInstall.ParseTypeType(typeValue);
14364 switch (typeType)
14365 {
14366 case Wix.ServiceInstall.TypeType.ownProcess:
14367 typebits |= MsiInterop.MsidbServiceInstallOwnProcess;
14368 break;
14369 case Wix.ServiceInstall.TypeType.shareProcess:
14370 typebits |= MsiInterop.MsidbServiceInstallShareProcess;
14371 break;
14372 case Wix.ServiceInstall.TypeType.kernelDriver:
14373 case Wix.ServiceInstall.TypeType.systemDriver:
14374 this.core.OnMessage(WixErrors.ValueNotSupported(sourceLineNumbers, node.Name.LocalName, attrib.Name.LocalName, typeValue));
14375 break;
14376 default:
14377 this.core.OnMessage(WixErrors.IllegalAttributeValue(sourceLineNumbers, node.Name.LocalName, node.Name.LocalName, typeValue, "ownProcess", "shareProcess"));
14378 break;
14379 }
14380 }
14381 break;
14382 case "Vital":
14383 if (YesNoType.Yes == this.core.GetAttributeYesNoValue(sourceLineNumbers, attrib))
14384 {
14385 errorbits |= MsiInterop.MsidbServiceInstallErrorControlVital;
14386 }
14387 break;
14388 default:
14389 this.core.UnexpectedAttribute(node, attrib);
14390 break;
14391 }
14392 }
14393 else
14394 {
14395 this.core.ParseExtensionAttribute(node, attrib);
14396 }
14397 }
14398
14399 if (String.IsNullOrEmpty(name))
14400 {
14401 this.core.OnMessage(WixErrors.ExpectedAttribute(sourceLineNumbers, node.Name.LocalName, "Name"));
14402 }
14403 else if (null == id)
14404 {
14405 id = this.core.CreateIdentifierFromFilename(name);
14406 }
14407
14408 if (0 == startType)
14409 {
14410 this.core.OnMessage(WixErrors.ExpectedAttribute(sourceLineNumbers, node.Name.LocalName, "Start"));
14411 }
14412
14413 if (eraseDescription)
14414 {
14415 description = "[~]";
14416 }
14417
14418 // get the ServiceInstall dependencies and config
14419 foreach (XElement child in node.Elements())
14420 {
14421 if (CompilerCore.WixNamespace == child.Name.Namespace)
14422 {
14423 switch (child.Name.LocalName)
14424 {
14425 case "PermissionEx":
14426 this.ParsePermissionExElement(child, id.Id, "ServiceInstall");
14427 break;
14428 case "ServiceConfig":
14429 this.ParseServiceConfigElement(child, componentId, name);
14430 break;
14431 case "ServiceConfigFailureActions":
14432 this.ParseServiceConfigFailureActionsElement(child, componentId, name);
14433 break;
14434 case "ServiceDependency":
14435 dependencies = String.Concat(dependencies, this.ParseServiceDependencyElement(child), "[~]");
14436 break;
14437 default:
14438 this.core.UnexpectedElement(node, child);
14439 break;
14440 }
14441 }
14442 else
14443 {
14444 Dictionary<string, string> context = new Dictionary<string, string>() { { "ServiceInstallId", id.Id }, { "ServiceInstallName", name }, { "ServiceInstallComponentId", componentId }, { "Win64", win64Component.ToString() } };
14445 this.core.ParseExtensionElement(node, child, context);
14446 }
14447 }
14448
14449 if (null != dependencies)
14450 {
14451 dependencies = String.Concat(dependencies, "[~]");
14452 }
14453
14454 if (!this.core.EncounteredError)
14455 {
14456 Row row = this.core.CreateRow(sourceLineNumbers, "ServiceInstall", id);
14457 row[1] = name;
14458 row[2] = displayName;
14459 row[3] = typebits;
14460 row[4] = startType;
14461 row[5] = errorbits;
14462 row[6] = loadOrderGroup;
14463 row[7] = dependencies;
14464 row[8] = account;
14465 row[9] = password;
14466 row[10] = arguments;
14467 row[11] = componentId;
14468 row[12] = description;
14469 }
14470 }
14471
14472 /// <summary>
14473 /// Parses a SetDirectory element.
14474 /// </summary>
14475 /// <param name="node">Element to parse.</param>
14476 private void ParseSetDirectoryElement(XElement node)
14477 {
14478 SourceLineNumber sourceLineNumbers = Preprocessor.GetSourceLineNumbers(node);
14479 string actionName = null;
14480 string id = null;
14481 string condition = null;
14482 string[] sequences = new string[] { "InstallUISequence", "InstallExecuteSequence" }; // default to "both"
14483 int extraBits = 0;
14484 string value = null;
14485
14486 foreach (XAttribute attrib in node.Attributes())
14487 {
14488 if (String.IsNullOrEmpty(attrib.Name.NamespaceName) || CompilerCore.WixNamespace == attrib.Name.Namespace)
14489 {
14490 switch (attrib.Name.LocalName)
14491 {
14492 case "Action":
14493 actionName = this.core.GetAttributeIdentifierValue(sourceLineNumbers, attrib);
14494 break;
14495 case "Id":
14496 id = this.core.GetAttributeIdentifierValue(sourceLineNumbers, attrib);
14497 this.core.CreateSimpleReference(sourceLineNumbers, "Directory", id);
14498 break;
14499 case "Sequence":
14500 string sequenceValue = this.core.GetAttributeValue(sourceLineNumbers, attrib);
14501 if (0 < sequenceValue.Length)
14502 {
14503 Wix.SequenceType sequenceType = Wix.Enums.ParseSequenceType(sequenceValue);
14504 switch (sequenceType)
14505 {
14506 case Wix.SequenceType.execute:
14507 sequences = new string[] { "InstallExecuteSequence" };
14508 break;
14509 case Wix.SequenceType.ui:
14510 sequences = new string[] { "InstallUISequence" };
14511 break;
14512 case Wix.SequenceType.first:
14513 extraBits = MsiInterop.MsidbCustomActionTypeFirstSequence;
14514 // default puts it in both sequence which is what we want
14515 break;
14516 case Wix.SequenceType.both:
14517 // default so no work necessary.
14518 break;
14519 default:
14520 this.core.OnMessage(WixErrors.IllegalAttributeValue(sourceLineNumbers, node.Name.LocalName, attrib.Name.LocalName, sequenceValue, "execute", "ui", "both"));
14521 break;
14522 }
14523 }
14524 break;
14525 case "Value":
14526 value = this.core.GetAttributeValue(sourceLineNumbers, attrib);
14527 break;
14528 default:
14529 this.core.UnexpectedAttribute(node, attrib);
14530 break;
14531 }
14532 }
14533 else
14534 {
14535 this.core.ParseExtensionAttribute(node, attrib);
14536 }
14537 }
14538
14539 condition = this.core.GetConditionInnerText(node);
14540
14541 if (null == id)
14542 {
14543 this.core.OnMessage(WixErrors.ExpectedAttribute(sourceLineNumbers, node.Name.LocalName, "Id"));
14544 }
14545 else if (String.IsNullOrEmpty(actionName))
14546 {
14547 actionName = String.Concat("Set", id);
14548 }
14549
14550 if (null == value)
14551 {
14552 this.core.OnMessage(WixErrors.ExpectedAttribute(sourceLineNumbers, node.Name.LocalName, "Value"));
14553 }
14554
14555 this.core.ParseForExtensionElements(node);
14556
14557 // add the row and any references needed
14558 if (!this.core.EncounteredError)
14559 {
14560 Row row = this.core.CreateRow(sourceLineNumbers, "CustomAction");
14561 row[0] = actionName;
14562 row[1] = MsiInterop.MsidbCustomActionTypeProperty | MsiInterop.MsidbCustomActionTypeTextData | extraBits;
14563 row[2] = id;
14564 row[3] = value;
14565
14566 foreach (string sequence in sequences)
14567 {
14568 Row sequenceRow = this.core.CreateRow(sourceLineNumbers, "WixAction");
14569 sequenceRow[0] = sequence;
14570 sequenceRow[1] = actionName;
14571 sequenceRow[2] = condition;
14572 // no explicit sequence
14573 // no before action
14574 sequenceRow[5] = "CostInitialize";
14575 sequenceRow[6] = 0; // not overridable
14576 }
14577 }
14578 }
14579
14580 /// <summary>
14581 /// Parses a SetProperty element.
14582 /// </summary>
14583 /// <param name="node">Element to parse.</param>
14584 private void ParseSetPropertyElement(XElement node)
14585 {
14586 SourceLineNumber sourceLineNumbers = Preprocessor.GetSourceLineNumbers(node);
14587 string actionName = null;
14588 string id = null;
14589 string afterAction = null;
14590 string beforeAction = null;
14591 string condition = null;
14592 string[] sequences = new string[] { "InstallUISequence", "InstallExecuteSequence" }; // default to "both"
14593 int extraBits = 0;
14594 string value = null;
14595
14596 foreach (XAttribute attrib in node.Attributes())
14597 {
14598 if (String.IsNullOrEmpty(attrib.Name.NamespaceName) || CompilerCore.WixNamespace == attrib.Name.Namespace)
14599 {
14600 switch (attrib.Name.LocalName)
14601 {
14602 case "Action":
14603 actionName = this.core.GetAttributeIdentifierValue(sourceLineNumbers, attrib);
14604 break;
14605 case "Id":
14606 id = this.core.GetAttributeIdentifierValue(sourceLineNumbers, attrib);
14607 break;
14608 case "After":
14609 afterAction = this.core.GetAttributeIdentifierValue(sourceLineNumbers, attrib);
14610 break;
14611 case "Before":
14612 beforeAction = this.core.GetAttributeIdentifierValue(sourceLineNumbers, attrib);
14613 break;
14614 case "Sequence":
14615 string sequenceValue = this.core.GetAttributeValue(sourceLineNumbers, attrib);
14616 if (0 < sequenceValue.Length)
14617 {
14618 Wix.SequenceType sequenceType = Wix.Enums.ParseSequenceType(sequenceValue);
14619 switch (sequenceType)
14620 {
14621 case Wix.SequenceType.execute:
14622 sequences = new string[] { "InstallExecuteSequence" };
14623 break;
14624 case Wix.SequenceType.ui:
14625 sequences = new string[] { "InstallUISequence" };
14626 break;
14627 case Wix.SequenceType.first:
14628 extraBits = MsiInterop.MsidbCustomActionTypeFirstSequence;
14629 // default puts it in both sequence which is what we want
14630 break;
14631 case Wix.SequenceType.both:
14632 // default so no work necessary.
14633 break;
14634 default:
14635 this.core.OnMessage(WixErrors.IllegalAttributeValue(sourceLineNumbers, node.Name.LocalName, attrib.Name.LocalName, sequenceValue, "execute", "ui", "both"));
14636 break;
14637 }
14638 }
14639 break;
14640 case "Value":
14641 value = this.core.GetAttributeValue(sourceLineNumbers, attrib, EmptyRule.CanBeEmpty);
14642 break;
14643 default:
14644 this.core.UnexpectedAttribute(node, attrib);
14645 break;
14646 }
14647 }
14648 else
14649 {
14650 this.core.ParseExtensionAttribute(node, attrib);
14651 }
14652 }
14653
14654 condition = this.core.GetConditionInnerText(node);
14655
14656 if (null == id)
14657 {
14658 this.core.OnMessage(WixErrors.ExpectedAttribute(sourceLineNumbers, node.Name.LocalName, "Id"));
14659 }
14660 else if (String.IsNullOrEmpty(actionName))
14661 {
14662 actionName = String.Concat("Set", id);
14663 }
14664
14665 if (null == value)
14666 {
14667 this.core.OnMessage(WixErrors.ExpectedAttribute(sourceLineNumbers, node.Name.LocalName, "Value"));
14668 }
14669
14670 if (null != beforeAction && null != afterAction)
14671 {
14672 this.core.OnMessage(WixErrors.IllegalAttributeWithOtherAttribute(sourceLineNumbers, node.Name.LocalName, "After", "Before"));
14673 }
14674 else if (null == beforeAction && null == afterAction)
14675 {
14676 this.core.OnMessage(WixErrors.ExpectedAttributesWithOtherAttribute(sourceLineNumbers, node.Name.LocalName, "After", "Before", "Id"));
14677 }
14678
14679 this.core.ParseForExtensionElements(node);
14680
14681 // add the row and any references needed
14682 if (!this.core.EncounteredError)
14683 {
14684 // action that is scheduled to occur before/after itself
14685 if (beforeAction == actionName)
14686 {
14687 this.core.OnMessage(WixErrors.ActionScheduledRelativeToItself(sourceLineNumbers, node.Name.LocalName, "Before", beforeAction));
14688 }
14689 else if (afterAction == actionName)
14690 {
14691 this.core.OnMessage(WixErrors.ActionScheduledRelativeToItself(sourceLineNumbers, node.Name.LocalName, "After", afterAction));
14692 }
14693
14694 Row row = this.core.CreateRow(sourceLineNumbers, "CustomAction");
14695 row[0] = actionName;
14696 row[1] = MsiInterop.MsidbCustomActionTypeProperty | MsiInterop.MsidbCustomActionTypeTextData | extraBits;
14697 row[2] = id;
14698 row[3] = value;
14699
14700 foreach (string sequence in sequences)
14701 {
14702 Row sequenceRow = this.core.CreateRow(sourceLineNumbers, "WixAction");
14703 sequenceRow[0] = sequence;
14704 sequenceRow[1] = actionName;
14705 sequenceRow[2] = condition;
14706 // no explicit sequence
14707 sequenceRow[4] = beforeAction;
14708 sequenceRow[5] = afterAction;
14709 sequenceRow[6] = 0; // not overridable
14710
14711 if (null != beforeAction)
14712 {
14713 if (WindowsInstallerStandard.IsStandardAction(beforeAction))
14714 {
14715 this.core.CreateSimpleReference(sourceLineNumbers, "WixAction", sequence, beforeAction);
14716 }
14717 else
14718 {
14719 this.core.CreateSimpleReference(sourceLineNumbers, "CustomAction", beforeAction);
14720 }
14721 }
14722
14723 if (null != afterAction)
14724 {
14725 if (WindowsInstallerStandard.IsStandardAction(afterAction))
14726 {
14727 this.core.CreateSimpleReference(sourceLineNumbers, "WixAction", sequence, afterAction);
14728 }
14729 else
14730 {
14731 this.core.CreateSimpleReference(sourceLineNumbers, "CustomAction", afterAction);
14732 }
14733 }
14734 }
14735 }
14736 }
14737
14738 /// <summary>
14739 /// Parses a SFP catalog element.
14740 /// </summary>
14741 /// <param name="node">Element to parse.</param>
14742 /// <param name="parentSFPCatalog">Parent SFPCatalog.</param>
14743 private void ParseSFPFileElement(XElement node, string parentSFPCatalog)
14744 {
14745 SourceLineNumber sourceLineNumbers = Preprocessor.GetSourceLineNumbers(node);
14746 string id = null;
14747
14748 foreach (XAttribute attrib in node.Attributes())
14749 {
14750 if (String.IsNullOrEmpty(attrib.Name.NamespaceName) || CompilerCore.WixNamespace == attrib.Name.Namespace)
14751 {
14752 switch (attrib.Name.LocalName)
14753 {
14754 case "Id":
14755 id = this.core.GetAttributeIdentifierValue(sourceLineNumbers, attrib);
14756 break;
14757 default:
14758 this.core.UnexpectedAttribute(node, attrib);
14759 break;
14760 }
14761 }
14762 else
14763 {
14764 this.core.ParseExtensionAttribute(node, attrib);
14765 }
14766 }
14767
14768 if (null == id)
14769 {
14770 this.core.OnMessage(WixErrors.ExpectedAttribute(sourceLineNumbers, node.Name.LocalName, "Id"));
14771 }
14772
14773 this.core.ParseForExtensionElements(node);
14774
14775 if (!this.core.EncounteredError)
14776 {
14777 Row row = this.core.CreateRow(sourceLineNumbers, "FileSFPCatalog");
14778 row[0] = id;
14779 row[1] = parentSFPCatalog;
14780 }
14781 }
14782
14783 /// <summary>
14784 /// Parses a SFP catalog element.
14785 /// </summary>
14786 /// <param name="node">Element to parse.</param>
14787 /// <param name="parentSFPCatalog">Parent SFPCatalog.</param>
14788 private void ParseSFPCatalogElement(XElement node, ref string parentSFPCatalog)
14789 {
14790 SourceLineNumber sourceLineNumbers = Preprocessor.GetSourceLineNumbers(node);
14791 string parentName = null;
14792 string dependency = null;
14793 string name = null;
14794 string sourceFile = null;
14795
14796 foreach (XAttribute attrib in node.Attributes())
14797 {
14798 if (String.IsNullOrEmpty(attrib.Name.NamespaceName) || CompilerCore.WixNamespace == attrib.Name.Namespace)
14799 {
14800 switch (attrib.Name.LocalName)
14801 {
14802 case "Dependency":
14803 dependency = this.core.GetAttributeValue(sourceLineNumbers, attrib);
14804 break;
14805 case "Name":
14806 name = this.core.GetAttributeShortFilename(sourceLineNumbers, attrib, false);
14807 parentSFPCatalog = name;
14808 break;
14809 case "SourceFile":
14810 sourceFile = this.core.GetAttributeValue(sourceLineNumbers, attrib);
14811 break;
14812 default:
14813 this.core.UnexpectedAttribute(node, attrib);
14814 break;
14815 }
14816 }
14817 else
14818 {
14819 this.core.ParseExtensionAttribute(node, attrib);
14820 }
14821 }
14822
14823 if (null == name)
14824 {
14825 this.core.OnMessage(WixErrors.ExpectedAttribute(sourceLineNumbers, node.Name.LocalName, "Name"));
14826 }
14827
14828 if (null == sourceFile)
14829 {
14830 this.core.OnMessage(WixErrors.ExpectedAttribute(sourceLineNumbers, node.Name.LocalName, "SourceFile"));
14831 }
14832
14833 foreach (XElement child in node.Elements())
14834 {
14835 if (CompilerCore.WixNamespace == child.Name.Namespace)
14836 {
14837 switch (child.Name.LocalName)
14838 {
14839 case "SFPCatalog":
14840 this.ParseSFPCatalogElement(child, ref parentName);
14841 if (null != dependency && parentName == dependency)
14842 {
14843 this.core.OnMessage(WixErrors.ExpectedAttribute(sourceLineNumbers, node.Name.LocalName, "Dependency"));
14844 }
14845 dependency = parentName;
14846 break;
14847 case "SFPFile":
14848 this.ParseSFPFileElement(child, name);
14849 break;
14850 default:
14851 this.core.UnexpectedElement(node, child);
14852 break;
14853 }
14854 }
14855 else
14856 {
14857 this.core.ParseExtensionElement(node, child);
14858 }
14859 }
14860
14861 if (null == dependency)
14862 {
14863 this.core.OnMessage(WixErrors.ExpectedAttribute(sourceLineNumbers, node.Name.LocalName, "Dependency"));
14864 }
14865
14866 if (!this.core.EncounteredError)
14867 {
14868 Row row = this.core.CreateRow(sourceLineNumbers, "SFPCatalog");
14869 row[0] = name;
14870 row[1] = sourceFile;
14871 row[2] = dependency;
14872 }
14873 }
14874
14875 /// <summary>
14876 /// Parses a shortcut element.
14877 /// </summary>
14878 /// <param name="node">Element to parse.</param>
14879 /// <param name="componentId">Identifer for parent component.</param>
14880 /// <param name="parentElementLocalName">Local name of parent element.</param>
14881 /// <param name="defaultTarget">Default identifier of parent (which is usually the target).</param>
14882 /// <param name="parentKeyPath">Flag to indicate whether the parent element is the keypath of a component or not (will only be true for file parent elements).</param>
14883 private void ParseShortcutElement(XElement node, string componentId, string parentElementLocalName, string defaultTarget, YesNoType parentKeyPath)
14884 {
14885 SourceLineNumber sourceLineNumbers = Preprocessor.GetSourceLineNumbers(node);
14886 Identifier id = null;
14887 bool advertise = false;
14888 string arguments = null;
14889 string description = null;
14890 string descriptionResourceDll = null;
14891 int descriptionResourceId = CompilerConstants.IntegerNotSet;
14892 string directory = null;
14893 string displayResourceDll = null;
14894 int displayResourceId = CompilerConstants.IntegerNotSet;
14895 int hotkey = CompilerConstants.IntegerNotSet;
14896 string icon = null;
14897 int iconIndex = CompilerConstants.IntegerNotSet;
14898 string name = null;
14899 string shortName = null;
14900 int show = CompilerConstants.IntegerNotSet;
14901 string target = null;
14902 string workingDirectory = null;
14903
14904 foreach (XAttribute attrib in node.Attributes())
14905 {
14906 if (String.IsNullOrEmpty(attrib.Name.NamespaceName) || CompilerCore.WixNamespace == attrib.Name.Namespace)
14907 {
14908 switch (attrib.Name.LocalName)
14909 {
14910 case "Id":
14911 id = this.core.GetAttributeIdentifier(sourceLineNumbers, attrib);
14912 break;
14913 case "Advertise":
14914 advertise = YesNoType.Yes == this.core.GetAttributeYesNoValue(sourceLineNumbers, attrib);
14915 break;
14916 case "Arguments":
14917 arguments = this.core.GetAttributeValue(sourceLineNumbers, attrib);
14918 break;
14919 case "Description":
14920 description = this.core.GetAttributeValue(sourceLineNumbers, attrib);
14921 break;
14922 case "DescriptionResourceDll":
14923 descriptionResourceDll = this.core.GetAttributeValue(sourceLineNumbers, attrib);
14924 break;
14925 case "DescriptionResourceId":
14926 descriptionResourceId = this.core.GetAttributeIntegerValue(sourceLineNumbers, attrib, 0, short.MaxValue);
14927 break;
14928 case "Directory":
14929 directory = this.core.CreateDirectoryReferenceFromInlineSyntax(sourceLineNumbers, attrib, null);
14930 break;
14931 case "DisplayResourceDll":
14932 displayResourceDll = this.core.GetAttributeValue(sourceLineNumbers, attrib);
14933 break;
14934 case "DisplayResourceId":
14935 displayResourceId = this.core.GetAttributeIntegerValue(sourceLineNumbers, attrib, 0, short.MaxValue);
14936 break;
14937 case "Hotkey":
14938 hotkey = this.core.GetAttributeIntegerValue(sourceLineNumbers, attrib, 0, short.MaxValue);
14939 break;
14940 case "Icon":
14941 icon = this.core.GetAttributeIdentifierValue(sourceLineNumbers, attrib);
14942 this.core.CreateSimpleReference(sourceLineNumbers, "Icon", icon);
14943 break;
14944 case "IconIndex":
14945 iconIndex = this.core.GetAttributeIntegerValue(sourceLineNumbers, attrib, short.MinValue + 1, short.MaxValue);
14946 break;
14947 case "Name":
14948 name = this.core.GetAttributeLongFilename(sourceLineNumbers, attrib, false);
14949 break;
14950 case "ShortName":
14951 shortName = this.core.GetAttributeShortFilename(sourceLineNumbers, attrib, false);
14952 break;
14953 case "Show":
14954 string showValue = this.core.GetAttributeValue(sourceLineNumbers, attrib);
14955 if (showValue.Length == 0)
14956 {
14957 show = CompilerConstants.IllegalInteger;
14958 }
14959 else
14960 {
14961 Wix.Shortcut.ShowType showType = Wix.Shortcut.ParseShowType(showValue);
14962 switch (showType)
14963 {
14964 case Wix.Shortcut.ShowType.normal:
14965 show = 1;
14966 break;
14967 case Wix.Shortcut.ShowType.maximized:
14968 show = 3;
14969 break;
14970 case Wix.Shortcut.ShowType.minimized:
14971 show = 7;
14972 break;
14973 default:
14974 this.core.OnMessage(WixErrors.IllegalAttributeValue(sourceLineNumbers, node.Name.LocalName, "Show", showValue, "normal", "maximized", "minimized"));
14975 show = CompilerConstants.IllegalInteger;
14976 break;
14977 }
14978 }
14979 break;
14980 case "Target":
14981 target = this.core.GetAttributeValue(sourceLineNumbers, attrib);
14982 break;
14983 case "WorkingDirectory":
14984 workingDirectory = this.core.GetAttributeIdentifierValue(sourceLineNumbers, attrib);
14985 break;
14986 default:
14987 this.core.UnexpectedAttribute(node, attrib);
14988 break;
14989 }
14990 }
14991 else
14992 {
14993 this.core.ParseExtensionAttribute(node, attrib);
14994 }
14995 }
14996
14997 if (advertise && null != target)
14998 {
14999 this.core.OnMessage(WixErrors.IllegalAttributeWithOtherAttribute(sourceLineNumbers, node.Name.LocalName, "Target", "Advertise", "yes"));
15000 }
15001
15002 if (null == directory)
15003 {
15004 if ("Component" == parentElementLocalName)
15005 {
15006 directory = defaultTarget;
15007 }
15008 else
15009 {
15010 this.core.OnMessage(WixErrors.ExpectedAttributeWhenElementNotUnderElement(sourceLineNumbers, node.Name.LocalName, "Directory", "Component"));
15011 }
15012 }
15013
15014 if (null != descriptionResourceDll)
15015 {
15016 if (CompilerConstants.IntegerNotSet == descriptionResourceId)
15017 {
15018 this.core.OnMessage(WixErrors.ExpectedAttribute(sourceLineNumbers, node.Name.LocalName, "DescriptionResourceDll", "DescriptionResourceId"));
15019 }
15020 }
15021 else
15022 {
15023 if (CompilerConstants.IntegerNotSet != descriptionResourceId)
15024 {
15025 this.core.OnMessage(WixErrors.ExpectedAttribute(sourceLineNumbers, node.Name.LocalName, "DescriptionResourceId", "DescriptionResourceDll"));
15026 }
15027 }
15028
15029 if (null != displayResourceDll)
15030 {
15031 if (CompilerConstants.IntegerNotSet == displayResourceId)
15032 {
15033 this.core.OnMessage(WixErrors.ExpectedAttribute(sourceLineNumbers, node.Name.LocalName, "DisplayResourceDll", "DisplayResourceId"));
15034 }
15035 }
15036 else
15037 {
15038 if (CompilerConstants.IntegerNotSet != displayResourceId)
15039 {
15040 this.core.OnMessage(WixErrors.ExpectedAttribute(sourceLineNumbers, node.Name.LocalName, "DisplayResourceId", "DisplayResourceDll"));
15041 }
15042 }
15043
15044 if (null == name)
15045 {
15046 this.core.OnMessage(WixErrors.ExpectedAttribute(sourceLineNumbers, node.Name.LocalName, "Name"));
15047 }
15048 else if (0 < name.Length)
15049 {
15050 if (this.core.IsValidShortFilename(name, false))
15051 {
15052 if (null == shortName)
15053 {
15054 shortName = name;
15055 name = null;
15056 }
15057 else
15058 {
15059 this.core.OnMessage(WixErrors.IllegalAttributeValueWithOtherAttribute(sourceLineNumbers, node.Name.LocalName, "Name", name, "ShortName"));
15060 }
15061 }
15062 else if (null == shortName) // generate a short file name.
15063 {
15064 shortName = this.core.CreateShortName(name, true, false, node.Name.LocalName, componentId, directory);
15065 }
15066 }
15067
15068 if ("Component" != parentElementLocalName && null != target)
15069 {
15070 this.core.OnMessage(WixErrors.IllegalAttributeWhenNested(sourceLineNumbers, node.Name.LocalName, "Target", parentElementLocalName));
15071 }
15072
15073 if (null == id)
15074 {
15075 id = this.core.CreateIdentifier("sct", directory, LowercaseOrNull(name) ?? LowercaseOrNull(shortName));
15076 }
15077
15078 foreach (XElement child in node.Elements())
15079 {
15080 if (CompilerCore.WixNamespace == child.Name.Namespace)
15081 {
15082 switch (child.Name.LocalName)
15083 {
15084 case "Icon":
15085 icon = this.ParseIconElement(child);
15086 break;
15087 case "ShortcutProperty":
15088 this.ParseShortcutPropertyElement(child, id.Id);
15089 break;
15090 default:
15091 this.core.UnexpectedElement(node, child);
15092 break;
15093 }
15094 }
15095 else
15096 {
15097 this.core.ParseExtensionElement(node, child);
15098 }
15099 }
15100
15101 if (!this.core.EncounteredError)
15102 {
15103 Row row = this.core.CreateRow(sourceLineNumbers, "Shortcut", id);
15104 row[1] = directory;
15105 row[2] = GetMsiFilenameValue(shortName, name);
15106 row[3] = componentId;
15107 if (advertise)
15108 {
15109 if (YesNoType.Yes != parentKeyPath && "Component" != parentElementLocalName)
15110 {
15111 this.core.OnMessage(WixWarnings.UnclearShortcut(sourceLineNumbers, id.Id, componentId, defaultTarget));
15112 }
15113 row[4] = Guid.Empty.ToString("B");
15114 }
15115 else if (null != target)
15116 {
15117 row[4] = target;
15118 }
15119 else if ("Component" == parentElementLocalName || "CreateFolder" == parentElementLocalName)
15120 {
15121 row[4] = String.Format(CultureInfo.InvariantCulture, "[{0}]", defaultTarget);
15122 }
15123 else if ("File" == parentElementLocalName)
15124 {
15125 row[4] = String.Format(CultureInfo.InvariantCulture, "[#{0}]", defaultTarget);
15126 }
15127 row[5] = arguments;
15128 row[6] = description;
15129 if (CompilerConstants.IntegerNotSet != hotkey)
15130 {
15131 row[7] = hotkey;
15132 }
15133 row[8] = icon;
15134 if (CompilerConstants.IntegerNotSet != iconIndex)
15135 {
15136 row[9] = iconIndex;
15137 }
15138
15139 if (CompilerConstants.IntegerNotSet != show)
15140 {
15141 row[10] = show;
15142 }
15143 row[11] = workingDirectory;
15144 row[12] = displayResourceDll;
15145 if (CompilerConstants.IntegerNotSet != displayResourceId)
15146 {
15147 row[13] = displayResourceId;
15148 }
15149 row[14] = descriptionResourceDll;
15150 if (CompilerConstants.IntegerNotSet != descriptionResourceId)
15151 {
15152 row[15] = descriptionResourceId;
15153 }
15154 }
15155 }
15156
15157 /// <summary>
15158 /// Parses a shortcut property element.
15159 /// </summary>
15160 /// <param name="node">Element to parse.</param>
15161 private void ParseShortcutPropertyElement(XElement node, string shortcutId)
15162 {
15163 SourceLineNumber sourceLineNumbers = Preprocessor.GetSourceLineNumbers(node);
15164 Identifier id = null;
15165 string key = null;
15166 string value = null;
15167
15168 foreach (XAttribute attrib in node.Attributes())
15169 {
15170 if (String.IsNullOrEmpty(attrib.Name.NamespaceName) || CompilerCore.WixNamespace == attrib.Name.Namespace)
15171 {
15172 switch (attrib.Name.LocalName)
15173 {
15174 case "Id":
15175 id = this.core.GetAttributeIdentifier(sourceLineNumbers, attrib);
15176 break;
15177 case "Key":
15178 key = this.core.GetAttributeValue(sourceLineNumbers, attrib);
15179 break;
15180 case "Value":
15181 value = this.core.GetAttributeValue(sourceLineNumbers, attrib);
15182 break;
15183 default:
15184 this.core.UnexpectedAttribute(node, attrib);
15185 break;
15186 }
15187 }
15188 else
15189 {
15190 this.core.ParseExtensionAttribute(node, attrib);
15191 }
15192 }
15193
15194 if (String.IsNullOrEmpty(key))
15195 {
15196 this.core.OnMessage(WixErrors.ExpectedAttribute(sourceLineNumbers, node.Name.LocalName, "Key"));
15197 }
15198 else if (null == id)
15199 {
15200 id = this.core.CreateIdentifier("scp", shortcutId, key.ToUpperInvariant());
15201 }
15202
15203 string innerText = this.core.GetTrimmedInnerText(node);
15204 if (!String.IsNullOrEmpty(innerText))
15205 {
15206 if (String.IsNullOrEmpty(value))
15207 {
15208 value = innerText;
15209 }
15210 else // cannot specify both the value attribute and inner text
15211 {
15212 this.core.OnMessage(WixErrors.IllegalAttributeWithInnerText(sourceLineNumbers, node.Name.LocalName, "Value"));
15213 }
15214 }
15215
15216 if (String.IsNullOrEmpty(value))
15217 {
15218 this.core.OnMessage(WixErrors.ExpectedAttribute(sourceLineNumbers, node.Name.LocalName, "Value"));
15219 }
15220
15221 this.core.ParseForExtensionElements(node);
15222
15223 if (!this.core.EncounteredError)
15224 {
15225 Row row = this.core.CreateRow(sourceLineNumbers, "MsiShortcutProperty", id);
15226 row[1] = shortcutId;
15227 row[2] = key;
15228 row[3] = value;
15229 }
15230 }
15231
15232 /// <summary>
15233 /// Parses a typelib element.
15234 /// </summary>
15235 /// <param name="node">Element to parse.</param>
15236 /// <param name="componentId">Identifier of parent component.</param>
15237 /// <param name="fileServer">Identifier of file that acts as typelib server.</param>
15238 /// <param name="win64Component">true if the component is 64-bit.</param>
15239 private void ParseTypeLibElement(XElement node, string componentId, string fileServer, bool win64Component)
15240 {
15241 SourceLineNumber sourceLineNumbers = Preprocessor.GetSourceLineNumbers(node);
15242 string id = null;
15243 YesNoType advertise = YesNoType.NotSet;
15244 int cost = CompilerConstants.IntegerNotSet;
15245 string description = null;
15246 int flags = 0;
15247 string helpDirectory = null;
15248 int language = CompilerConstants.IntegerNotSet;
15249 int majorVersion = CompilerConstants.IntegerNotSet;
15250 int minorVersion = CompilerConstants.IntegerNotSet;
15251 long resourceId = CompilerConstants.LongNotSet;
15252
15253 foreach (XAttribute attrib in node.Attributes())
15254 {
15255 if (String.IsNullOrEmpty(attrib.Name.NamespaceName) || CompilerCore.WixNamespace == attrib.Name.Namespace)
15256 {
15257 switch (attrib.Name.LocalName)
15258 {
15259 case "Id":
15260 id = this.core.GetAttributeGuidValue(sourceLineNumbers, attrib, false);
15261 break;
15262 case "Advertise":
15263 advertise = this.core.GetAttributeYesNoValue(sourceLineNumbers, attrib);
15264 break;
15265 case "Control":
15266 if (YesNoType.Yes == this.core.GetAttributeYesNoValue(sourceLineNumbers, attrib))
15267 {
15268 flags |= 2;
15269 }
15270 break;
15271 case "Cost":
15272 cost = this.core.GetAttributeIntegerValue(sourceLineNumbers, attrib, 0, int.MaxValue);
15273 break;
15274 case "Description":
15275 description = this.core.GetAttributeValue(sourceLineNumbers, attrib);
15276 break;
15277 case "HasDiskImage":
15278 if (YesNoType.Yes == this.core.GetAttributeYesNoValue(sourceLineNumbers, attrib))
15279 {
15280 flags |= 8;
15281 }
15282 break;
15283 case "HelpDirectory":
15284 helpDirectory = this.core.CreateDirectoryReferenceFromInlineSyntax(sourceLineNumbers, attrib, null);
15285 break;
15286 case "Hidden":
15287 if (YesNoType.Yes == this.core.GetAttributeYesNoValue(sourceLineNumbers, attrib))
15288 {
15289 flags |= 4;
15290 }
15291 break;
15292 case "Language":
15293 language = this.core.GetAttributeIntegerValue(sourceLineNumbers, attrib, 0, short.MaxValue);
15294 break;
15295 case "MajorVersion":
15296 majorVersion = this.core.GetAttributeIntegerValue(sourceLineNumbers, attrib, 0, ushort.MaxValue);
15297 break;
15298 case "MinorVersion":
15299 minorVersion = this.core.GetAttributeIntegerValue(sourceLineNumbers, attrib, 0, byte.MaxValue);
15300 break;
15301 case "ResourceId":
15302 resourceId = this.core.GetAttributeLongValue(sourceLineNumbers, attrib, int.MinValue, int.MaxValue);
15303 break;
15304 case "Restricted":
15305 if (YesNoType.Yes == this.core.GetAttributeYesNoValue(sourceLineNumbers, attrib))
15306 {
15307 flags |= 1;
15308 }
15309 break;
15310 default:
15311 this.core.UnexpectedAttribute(node, attrib);
15312 break;
15313 }
15314 }
15315 else
15316 {
15317 this.core.ParseExtensionAttribute(node, attrib);
15318 }
15319 }
15320
15321 if (null == id)
15322 {
15323 this.core.OnMessage(WixErrors.ExpectedAttribute(sourceLineNumbers, node.Name.LocalName, "Id"));
15324 }
15325
15326 if (CompilerConstants.IntegerNotSet == language)
15327 {
15328 this.core.OnMessage(WixErrors.ExpectedAttribute(sourceLineNumbers, node.Name.LocalName, "Language"));
15329 language = CompilerConstants.IllegalInteger;
15330 }
15331
15332 // build up the typelib version string for the registry if the major or minor version was specified
15333 string registryVersion = null;
15334 if (CompilerConstants.IntegerNotSet != majorVersion || CompilerConstants.IntegerNotSet != minorVersion)
15335 {
15336 if (CompilerConstants.IntegerNotSet != majorVersion)
15337 {
15338 registryVersion = majorVersion.ToString("x", CultureInfo.InvariantCulture.NumberFormat);
15339 }
15340 else
15341 {
15342 registryVersion = "0";
15343 }
15344
15345 if (CompilerConstants.IntegerNotSet != minorVersion)
15346 {
15347 registryVersion = String.Concat(registryVersion, ".", minorVersion.ToString("x", CultureInfo.InvariantCulture.NumberFormat));
15348 }
15349 else
15350 {
15351 registryVersion = String.Concat(registryVersion, ".0");
15352 }
15353 }
15354
15355 // if the advertise state has not been set, default to non-advertised
15356 if (YesNoType.NotSet == advertise)
15357 {
15358 advertise = YesNoType.No;
15359 }
15360
15361 foreach (XElement child in node.Elements())
15362 {
15363 if (CompilerCore.WixNamespace == child.Name.Namespace)
15364 {
15365 switch (child.Name.LocalName)
15366 {
15367 case "AppId":
15368 this.ParseAppIdElement(child, componentId, YesNoType.NotSet, fileServer, id, registryVersion);
15369 break;
15370 case "Class":
15371 this.ParseClassElement(child, componentId, YesNoType.NotSet, fileServer, id, registryVersion, null);
15372 break;
15373 case "Interface":
15374 this.ParseInterfaceElement(child, componentId, null, null, id, registryVersion);
15375 break;
15376 default:
15377 this.core.UnexpectedElement(node, child);
15378 break;
15379 }
15380 }
15381 else
15382 {
15383 this.core.ParseExtensionElement(node, child);
15384 }
15385 }
15386
15387
15388 if (YesNoType.Yes == advertise)
15389 {
15390 if (CompilerConstants.LongNotSet != resourceId)
15391 {
15392 this.core.OnMessage(WixErrors.IllegalAttributeWhenAdvertised(sourceLineNumbers, node.Name.LocalName, "ResourceId"));
15393 }
15394
15395 if (0 != flags)
15396 {
15397 if (0x1 == (flags & 0x1))
15398 {
15399 this.core.OnMessage(WixErrors.IllegalAttributeWithOtherAttribute(sourceLineNumbers, node.Name.LocalName, "Restricted", "Advertise", "yes"));
15400 }
15401
15402 if (0x2 == (flags & 0x2))
15403 {
15404 this.core.OnMessage(WixErrors.IllegalAttributeWithOtherAttribute(sourceLineNumbers, node.Name.LocalName, "Control", "Advertise", "yes"));
15405 }
15406
15407 if (0x4 == (flags & 0x4))
15408 {
15409 this.core.OnMessage(WixErrors.IllegalAttributeWithOtherAttribute(sourceLineNumbers, node.Name.LocalName, "Hidden", "Advertise", "yes"));
15410 }
15411
15412 if (0x8 == (flags & 0x8))
15413 {
15414 this.core.OnMessage(WixErrors.IllegalAttributeWithOtherAttribute(sourceLineNumbers, node.Name.LocalName, "HasDiskImage", "Advertise", "yes"));
15415 }
15416 }
15417
15418 if (!this.core.EncounteredError)
15419 {
15420 Row row = this.core.CreateRow(sourceLineNumbers, "TypeLib");
15421 row[0] = id;
15422 row[1] = language;
15423 row[2] = componentId;
15424 if (CompilerConstants.IntegerNotSet != majorVersion || CompilerConstants.IntegerNotSet != minorVersion)
15425 {
15426 row[3] = (CompilerConstants.IntegerNotSet != majorVersion ? majorVersion * 256 : 0) + (CompilerConstants.IntegerNotSet != minorVersion ? minorVersion : 0);
15427 }
15428 row[4] = description;
15429 row[5] = helpDirectory;
15430 row[6] = Guid.Empty.ToString("B");
15431 if (CompilerConstants.IntegerNotSet != cost)
15432 {
15433 row[7] = cost;
15434 }
15435 }
15436 }
15437 else if (YesNoType.No == advertise)
15438 {
15439 if (CompilerConstants.IntegerNotSet != cost && CompilerConstants.IllegalInteger != cost)
15440 {
15441 this.core.OnMessage(WixErrors.IllegalAttributeWithOtherAttribute(sourceLineNumbers, node.Name.LocalName, "Cost", "Advertise", "no"));
15442 }
15443
15444 if (null == fileServer)
15445 {
15446 this.core.OnMessage(WixErrors.MissingTypeLibFile(sourceLineNumbers, node.Name.LocalName, "File"));
15447 }
15448
15449 if (null == registryVersion)
15450 {
15451 this.core.OnMessage(WixErrors.ExpectedAttributesWithOtherAttribute(sourceLineNumbers, node.Name.LocalName, "MajorVersion", "MinorVersion", "Advertise", "no"));
15452 }
15453
15454 // HKCR\TypeLib\[ID]\[MajorVersion].[MinorVersion], (Default) = [Description]
15455 this.core.CreateRegistryRow(sourceLineNumbers, MsiInterop.MsidbRegistryRootClassesRoot, String.Format(CultureInfo.InvariantCulture, @"TypeLib\{0}\{1}", id, registryVersion), null, description, componentId);
15456
15457 // HKCR\TypeLib\[ID]\[MajorVersion].[MinorVersion]\[Language]\[win16|win32|win64], (Default) = [TypeLibPath]\[ResourceId]
15458 string path = String.Concat("[#", fileServer, "]");
15459 if (CompilerConstants.LongNotSet != resourceId)
15460 {
15461 path = String.Concat(path, Path.DirectorySeparatorChar, resourceId.ToString(CultureInfo.InvariantCulture.NumberFormat));
15462 }
15463 this.core.CreateRegistryRow(sourceLineNumbers, MsiInterop.MsidbRegistryRootClassesRoot, String.Format(CultureInfo.InvariantCulture, @"TypeLib\{0}\{1}\{2}\{3}", id, registryVersion, language, (win64Component ? "win64" : "win32")), null, path, componentId);
15464
15465 // HKCR\TypeLib\[ID]\[MajorVersion].[MinorVersion]\FLAGS, (Default) = [TypeLibFlags]
15466 this.core.CreateRegistryRow(sourceLineNumbers, MsiInterop.MsidbRegistryRootClassesRoot, String.Format(CultureInfo.InvariantCulture, @"TypeLib\{0}\{1}\FLAGS", id, registryVersion), null, flags.ToString(CultureInfo.InvariantCulture.NumberFormat), componentId);
15467
15468 if (null != helpDirectory)
15469 {
15470 // HKCR\TypeLib\[ID]\[MajorVersion].[MinorVersion]\HELPDIR, (Default) = [HelpDirectory]
15471 this.core.CreateRegistryRow(sourceLineNumbers, MsiInterop.MsidbRegistryRootClassesRoot, String.Format(CultureInfo.InvariantCulture, @"TypeLib\{0}\{1}\HELPDIR", id, registryVersion), null, String.Concat("[", helpDirectory, "]"), componentId);
15472 }
15473 }
15474 }
15475
15476 /// <summary>
15477 /// Parses an EmbeddedChaniner element.
15478 /// </summary>
15479 /// <param name="node">Element to parse.</param>
15480 private void ParseEmbeddedChainerElement(XElement node)
15481 {
15482 SourceLineNumber sourceLineNumbers = Preprocessor.GetSourceLineNumbers(node);
15483 Identifier id = null;
15484 string commandLine = null;
15485 string condition = null;
15486 string source = null;
15487 int type = 0;
15488
15489 foreach (XAttribute attrib in node.Attributes())
15490 {
15491 if (String.IsNullOrEmpty(attrib.Name.NamespaceName) || CompilerCore.WixNamespace == attrib.Name.Namespace)
15492 {
15493 switch (attrib.Name.LocalName)
15494 {
15495 case "Id":
15496 id = this.core.GetAttributeIdentifier(sourceLineNumbers, attrib);
15497 break;
15498 case "BinarySource":
15499 if (null != source)
15500 {
15501 this.core.OnMessage(WixErrors.IllegalAttributeWithOtherAttributes(sourceLineNumbers, node.Name.LocalName, attrib.Name.LocalName, "FileSource", "PropertySource"));
15502 }
15503 source = this.core.GetAttributeIdentifierValue(sourceLineNumbers, attrib);
15504 type = MsiInterop.MsidbCustomActionTypeExe + MsiInterop.MsidbCustomActionTypeBinaryData;
15505 this.core.CreateSimpleReference(sourceLineNumbers, "Binary", source); // add a reference to the appropriate Binary
15506 break;
15507 case "CommandLine":
15508 commandLine = this.core.GetAttributeValue(sourceLineNumbers, attrib);
15509 break;
15510 case "FileSource":
15511 if (null != source)
15512 {
15513 this.core.OnMessage(WixErrors.IllegalAttributeWithOtherAttributes(sourceLineNumbers, node.Name.LocalName, attrib.Name.LocalName, "BinarySource", "PropertySource"));
15514 }
15515 source = this.core.GetAttributeIdentifierValue(sourceLineNumbers, attrib);
15516 type = MsiInterop.MsidbCustomActionTypeExe + MsiInterop.MsidbCustomActionTypeSourceFile;
15517 this.core.CreateSimpleReference(sourceLineNumbers, "File", source); // add a reference to the appropriate File
15518 break;
15519 case "PropertySource":
15520 if (null != source)
15521 {
15522 this.core.OnMessage(WixErrors.IllegalAttributeWithOtherAttributes(sourceLineNumbers, node.Name.LocalName, attrib.Name.LocalName, "BinarySource", "FileSource"));
15523 }
15524 source = this.core.GetAttributeIdentifierValue(sourceLineNumbers, attrib);
15525 type = MsiInterop.MsidbCustomActionTypeExe + MsiInterop.MsidbCustomActionTypeProperty;
15526 // cannot add a reference to a Property because it may be created at runtime.
15527 break;
15528 default:
15529 this.core.UnexpectedAttribute(node, attrib);
15530 break;
15531 }
15532 }
15533 else
15534 {
15535 this.core.ParseExtensionAttribute(node, attrib);
15536 }
15537 }
15538
15539 // Get the condition from the inner text of the element.
15540 condition = this.core.GetConditionInnerText(node);
15541
15542 if (null == id)
15543 {
15544 id = this.core.CreateIdentifier("mec", source, type.ToString());
15545 }
15546
15547 if (null == source)
15548 {
15549 this.core.OnMessage(WixErrors.ExpectedAttributes(sourceLineNumbers, node.Name.LocalName, "BinarySource", "FileSource", "PropertySource"));
15550 }
15551
15552 if (!this.core.EncounteredError)
15553 {
15554 Row row = this.core.CreateRow(sourceLineNumbers, "MsiEmbeddedChainer", id);
15555 row[1] = condition;
15556 row[2] = commandLine;
15557 row[3] = source;
15558 row[4] = type;
15559 }
15560 }
15561
15562 /// <summary>
15563 /// Parses UI elements.
15564 /// </summary>
15565 /// <param name="node">Element to parse.</param>
15566 private void ParseUIElement(XElement node)
15567 {
15568 SourceLineNumber sourceLineNumbers = Preprocessor.GetSourceLineNumbers(node);
15569 Identifier id = null;
15570 int embeddedUICount = 0;
15571
15572 foreach (XAttribute attrib in node.Attributes())
15573 {
15574 if (String.IsNullOrEmpty(attrib.Name.NamespaceName) || CompilerCore.WixNamespace == attrib.Name.Namespace)
15575 {
15576 switch (attrib.Name.LocalName)
15577 {
15578 case "Id":
15579 id = this.core.GetAttributeIdentifier(sourceLineNumbers, attrib);
15580 break;
15581 default:
15582 this.core.UnexpectedAttribute(node, attrib);
15583 break;
15584 }
15585 }
15586 else
15587 {
15588 this.core.ParseExtensionAttribute(node, attrib);
15589 }
15590 }
15591
15592 foreach (XElement child in node.Elements())
15593 {
15594 if (CompilerCore.WixNamespace == child.Name.Namespace)
15595 {
15596 switch (child.Name.LocalName)
15597 {
15598 case "BillboardAction":
15599 this.ParseBillboardActionElement(child);
15600 break;
15601 case "ComboBox":
15602 this.ParseControlGroupElement(child, this.tableDefinitions["ComboBox"], "ListItem");
15603 break;
15604 case "Dialog":
15605 this.ParseDialogElement(child);
15606 break;
15607 case "DialogRef":
15608 this.ParseSimpleRefElement(child, "Dialog");
15609 break;
15610 case "EmbeddedUI":
15611 if (0 < embeddedUICount) // there can be only one embedded UI
15612 {
15613 SourceLineNumber childSourceLineNumbers = Preprocessor.GetSourceLineNumbers(child);
15614 this.core.OnMessage(WixErrors.TooManyChildren(childSourceLineNumbers, node.Name.LocalName, child.Name.LocalName));
15615 }
15616 this.ParseEmbeddedUIElement(child);
15617 ++embeddedUICount;
15618 break;
15619 case "Error":
15620 this.ParseErrorElement(child);
15621 break;
15622 case "ListBox":
15623 this.ParseControlGroupElement(child, this.tableDefinitions["ListBox"], "ListItem");
15624 break;
15625 case "ListView":
15626 this.ParseControlGroupElement(child, this.tableDefinitions["ListView"], "ListItem");
15627 break;
15628 case "ProgressText":
15629 this.ParseActionTextElement(child);
15630 break;
15631 case "Publish":
15632 int order = 0;
15633 this.ParsePublishElement(child, null, null, ref order);
15634 break;
15635 case "RadioButtonGroup":
15636 RadioButtonType radioButtonType = this.ParseRadioButtonGroupElement(child, null, RadioButtonType.NotSet);
15637 if (RadioButtonType.Bitmap == radioButtonType || RadioButtonType.Icon == radioButtonType)
15638 {
15639 SourceLineNumber childSourceLineNumbers = Preprocessor.GetSourceLineNumbers(child);
15640 this.core.OnMessage(WixErrors.RadioButtonBitmapAndIconDisallowed(childSourceLineNumbers));
15641 }
15642 break;
15643 case "TextStyle":
15644 this.ParseTextStyleElement(child);
15645 break;
15646 case "UIText":
15647 this.ParseUITextElement(child);
15648 break;
15649
15650 // the following are available indentically under the UI and Product elements for document organization use only
15651 case "AdminUISequence":
15652 case "InstallUISequence":
15653 this.ParseSequenceElement(child, child.Name.LocalName);
15654 break;
15655 case "Binary":
15656 this.ParseBinaryElement(child);
15657 break;
15658 case "Property":
15659 this.ParsePropertyElement(child);
15660 break;
15661 case "PropertyRef":
15662 this.ParseSimpleRefElement(child, "Property");
15663 break;
15664 case "UIRef":
15665 this.ParseSimpleRefElement(child, "WixUI");
15666 break;
15667
15668 default:
15669 this.core.UnexpectedElement(node, child);
15670 break;
15671 }
15672 }
15673 else
15674 {
15675 this.core.ParseExtensionElement(node, child);
15676 }
15677 }
15678
15679 if (null != id && !this.core.EncounteredError)
15680 {
15681 this.core.CreateRow(sourceLineNumbers, "WixUI", id);
15682 }
15683 }
15684
15685 /// <summary>
15686 /// Parses a list item element.
15687 /// </summary>
15688 /// <param name="node">Element to parse.</param>
15689 /// <param name="table">Table to add row to.</param>
15690 /// <param name="property">Identifier of property referred to by list item.</param>
15691 /// <param name="order">Relative order of list items.</param>
15692 private void ParseListItemElement(XElement node, TableDefinition table, string property, ref int order)
15693 {
15694 SourceLineNumber sourceLineNumbers = Preprocessor.GetSourceLineNumbers(node);
15695 string icon = null;
15696 string text = null;
15697 string value = null;
15698
15699 foreach (XAttribute attrib in node.Attributes())
15700 {
15701 if (String.IsNullOrEmpty(attrib.Name.NamespaceName) || CompilerCore.WixNamespace == attrib.Name.Namespace)
15702 {
15703 switch (attrib.Name.LocalName)
15704 {
15705 case "Icon":
15706 if ("ListView" == table.Name)
15707 {
15708 icon = this.core.GetAttributeIdentifierValue(sourceLineNumbers, attrib);
15709 this.core.CreateSimpleReference(sourceLineNumbers, "Binary", icon);
15710 }
15711 else
15712 {
15713 this.core.OnMessage(WixErrors.IllegalAttributeExceptOnElement(sourceLineNumbers, node.Name.LocalName, attrib.Name.LocalName, "ListView"));
15714 }
15715 break;
15716 case "Text":
15717 text = this.core.GetAttributeValue(sourceLineNumbers, attrib);
15718 break;
15719 case "Value":
15720 value = this.core.GetAttributeValue(sourceLineNumbers, attrib);
15721 break;
15722 default:
15723 this.core.UnexpectedAttribute(node, attrib);
15724 break;
15725 }
15726 }
15727 else
15728 {
15729 this.core.ParseExtensionAttribute(node, attrib);
15730 }
15731 }
15732
15733 if (null == value)
15734 {
15735 this.core.OnMessage(WixErrors.ExpectedAttribute(sourceLineNumbers, node.Name.LocalName, "Value"));
15736 }
15737
15738 this.core.ParseForExtensionElements(node);
15739
15740 if (!this.core.EncounteredError)
15741 {
15742 Row row = this.core.CreateRow(sourceLineNumbers, table.Name);
15743 row[0] = property;
15744 row[1] = ++order;
15745 row[2] = value;
15746 row[3] = text;
15747 if (null != icon)
15748 {
15749 row[4] = icon;
15750 }
15751 }
15752 }
15753
15754 /// <summary>
15755 /// Parses a radio button element.
15756 /// </summary>
15757 /// <param name="node">Element to parse.</param>
15758 /// <param name="property">Identifier of property referred to by radio button.</param>
15759 /// <param name="order">Relative order of radio buttons.</param>
15760 /// <returns>Type of this radio button.</returns>
15761 private RadioButtonType ParseRadioButtonElement(XElement node, string property, ref int order)
15762 {
15763 SourceLineNumber sourceLineNumbers = Preprocessor.GetSourceLineNumbers(node);
15764 RadioButtonType type = RadioButtonType.NotSet;
15765 string value = null;
15766 string x = null;
15767 string y = null;
15768 string width = null;
15769 string height = null;
15770 string text = null;
15771 string tooltip = null;
15772 string help = null;
15773
15774 foreach (XAttribute attrib in node.Attributes())
15775 {
15776 if (String.IsNullOrEmpty(attrib.Name.NamespaceName) || CompilerCore.WixNamespace == attrib.Name.Namespace)
15777 {
15778 switch (attrib.Name.LocalName)
15779 {
15780 case "Bitmap":
15781 if (RadioButtonType.NotSet != type)
15782 {
15783 this.core.OnMessage(WixErrors.IllegalAttributeWithOtherAttributes(sourceLineNumbers, node.Name.LocalName, attrib.Name.LocalName, "Icon", "Text"));
15784 }
15785 text = this.core.GetAttributeIdentifierValue(sourceLineNumbers, attrib);
15786 this.core.CreateSimpleReference(sourceLineNumbers, "Binary", text);
15787 type = RadioButtonType.Bitmap;
15788 break;
15789 case "Height":
15790 height = this.core.GetAttributeLocalizableIntegerValue(sourceLineNumbers, attrib, 0, short.MaxValue);
15791 break;
15792 case "Help":
15793 help = this.core.GetAttributeValue(sourceLineNumbers, attrib);
15794 break;
15795 case "Icon":
15796 if (RadioButtonType.NotSet != type)
15797 {
15798 this.core.OnMessage(WixErrors.IllegalAttributeWithOtherAttributes(sourceLineNumbers, node.Name.LocalName, attrib.Name.LocalName, "Bitmap", "Text"));
15799 }
15800 text = this.core.GetAttributeIdentifierValue(sourceLineNumbers, attrib);
15801 this.core.CreateSimpleReference(sourceLineNumbers, "Binary", text);
15802 type = RadioButtonType.Icon;
15803 break;
15804 case "Text":
15805 if (RadioButtonType.NotSet != type)
15806 {
15807 this.core.OnMessage(WixErrors.IllegalAttributeWithOtherAttribute(sourceLineNumbers, node.Name.LocalName, attrib.Name.LocalName, "Bitmap", "Icon"));
15808 }
15809 text = this.core.GetAttributeValue(sourceLineNumbers, attrib);
15810 type = RadioButtonType.Text;
15811 break;
15812 case "ToolTip":
15813 tooltip = this.core.GetAttributeValue(sourceLineNumbers, attrib);
15814 break;
15815 case "Value":
15816 value = this.core.GetAttributeValue(sourceLineNumbers, attrib);
15817 break;
15818 case "Width":
15819 width = this.core.GetAttributeLocalizableIntegerValue(sourceLineNumbers, attrib, 0, short.MaxValue);
15820 break;
15821 case "X":
15822 x = this.core.GetAttributeLocalizableIntegerValue(sourceLineNumbers, attrib, 0, short.MaxValue);
15823 break;
15824 case "Y":
15825 y = this.core.GetAttributeLocalizableIntegerValue(sourceLineNumbers, attrib, 0, short.MaxValue);
15826 break;
15827 default:
15828 this.core.UnexpectedAttribute(node, attrib);
15829 break;
15830 }
15831 }
15832 else
15833 {
15834 this.core.ParseExtensionAttribute(node, attrib);
15835 }
15836 }
15837
15838 if (null == value)
15839 {
15840 this.core.OnMessage(WixErrors.ExpectedAttribute(sourceLineNumbers, node.Name.LocalName, "Value"));
15841 }
15842
15843 if (null == x)
15844 {
15845 this.core.OnMessage(WixErrors.ExpectedAttribute(sourceLineNumbers, node.Name.LocalName, "X"));
15846 }
15847
15848 if (null == y)
15849 {
15850 this.core.OnMessage(WixErrors.ExpectedAttribute(sourceLineNumbers, node.Name.LocalName, "Y"));
15851 }
15852
15853 if (null == width)
15854 {
15855 this.core.OnMessage(WixErrors.ExpectedAttribute(sourceLineNumbers, node.Name.LocalName, "Width"));
15856 }
15857
15858 if (null == height)
15859 {
15860 this.core.OnMessage(WixErrors.ExpectedAttribute(sourceLineNumbers, node.Name.LocalName, "Height"));
15861 }
15862
15863 this.core.ParseForExtensionElements(node);
15864
15865 if (!this.core.EncounteredError)
15866 {
15867 Row row = this.core.CreateRow(sourceLineNumbers, "RadioButton");
15868 row[0] = property;
15869 row[1] = ++order;
15870 row[2] = value;
15871 row[3] = x;
15872 row[4] = y;
15873 row[5] = width;
15874 row[6] = height;
15875 row[7] = text;
15876 if (null != tooltip || null != help)
15877 {
15878 row[8] = String.Concat(tooltip, "|", help);
15879 }
15880 }
15881
15882 return type;
15883 }
15884
15885 /// <summary>
15886 /// Parses a billboard element.
15887 /// </summary>
15888 /// <param name="node">Element to parse.</param>
15889 private void ParseBillboardActionElement(XElement node)
15890 {
15891 SourceLineNumber sourceLineNumbers = Preprocessor.GetSourceLineNumbers(node);
15892 string action = null;
15893 int order = 0;
15894
15895 foreach (XAttribute attrib in node.Attributes())
15896 {
15897 if (String.IsNullOrEmpty(attrib.Name.NamespaceName) || CompilerCore.WixNamespace == attrib.Name.Namespace)
15898 {
15899 switch (attrib.Name.LocalName)
15900 {
15901 case "Id":
15902 action = this.core.GetAttributeIdentifierValue(sourceLineNumbers, attrib);
15903 this.core.CreateSimpleReference(sourceLineNumbers, "WixAction", "InstallExecuteSequence", action);
15904 break;
15905 default:
15906 this.core.UnexpectedAttribute(node, attrib);
15907 break;
15908 }
15909 }
15910 else
15911 {
15912 this.core.ParseExtensionAttribute(node, attrib);
15913 }
15914 }
15915
15916 if (null == action)
15917 {
15918 this.core.OnMessage(WixErrors.ExpectedAttribute(sourceLineNumbers, node.Name.LocalName, "Id"));
15919 }
15920
15921 foreach (XElement child in node.Elements())
15922 {
15923 if (CompilerCore.WixNamespace == child.Name.Namespace)
15924 {
15925 switch (child.Name.LocalName)
15926 {
15927 case "Billboard":
15928 order = order + 1;
15929 this.ParseBillboardElement(child, action, order);
15930 break;
15931 default:
15932 this.core.UnexpectedElement(node, child);
15933 break;
15934 }
15935 }
15936 else
15937 {
15938 this.core.ParseExtensionElement(node, child);
15939 }
15940 }
15941 }
15942
15943 /// <summary>
15944 /// Parses a billboard element.
15945 /// </summary>
15946 /// <param name="node">Element to parse.</param>
15947 /// <param name="action">Action for the billboard.</param>
15948 /// <param name="order">Order of the billboard.</param>
15949 private void ParseBillboardElement(XElement node, string action, int order)
15950 {
15951 SourceLineNumber sourceLineNumbers = Preprocessor.GetSourceLineNumbers(node);
15952 Identifier id = null;
15953 string feature = null;
15954
15955 foreach (XAttribute attrib in node.Attributes())
15956 {
15957 if (String.IsNullOrEmpty(attrib.Name.NamespaceName) || CompilerCore.WixNamespace == attrib.Name.Namespace)
15958 {
15959 switch (attrib.Name.LocalName)
15960 {
15961 case "Id":
15962 id = this.core.GetAttributeIdentifier(sourceLineNumbers, attrib);
15963 break;
15964 case "Feature":
15965 feature = this.core.GetAttributeIdentifierValue(sourceLineNumbers, attrib);
15966 this.core.CreateSimpleReference(sourceLineNumbers, "Feature", feature);
15967 break;
15968 default:
15969 this.core.UnexpectedAttribute(node, attrib);
15970 break;
15971 }
15972 }
15973 else
15974 {
15975 this.core.ParseExtensionAttribute(node, attrib);
15976 }
15977 }
15978
15979 if (null == id)
15980 {
15981 id = this.core.CreateIdentifier("bil", action, order.ToString(), feature);
15982 }
15983
15984 foreach (XElement child in node.Elements())
15985 {
15986 if (CompilerCore.WixNamespace == child.Name.Namespace)
15987 {
15988 switch (child.Name.LocalName)
15989 {
15990 case "Control":
15991 // These are all thrown away.
15992 Row lastTabRow = null;
15993 string firstControl = null;
15994 string defaultControl = null;
15995 string cancelControl = null;
15996
15997 this.ParseControlElement(child, id.Id, this.tableDefinitions["BBControl"], ref lastTabRow, ref firstControl, ref defaultControl, ref cancelControl, false);
15998 break;
15999 default:
16000 this.core.UnexpectedElement(node, child);
16001 break;
16002 }
16003 }
16004 else
16005 {
16006 this.core.ParseExtensionElement(node, child);
16007 }
16008 }
16009
16010
16011 if (!this.core.EncounteredError)
16012 {
16013 Row row = this.core.CreateRow(sourceLineNumbers, "Billboard", id);
16014 row[1] = feature;
16015 row[2] = action;
16016 row[3] = order;
16017 }
16018 }
16019
16020 /// <summary>
16021 /// Parses a control group element.
16022 /// </summary>
16023 /// <param name="node">Element to parse.</param>
16024 /// <param name="table">Table referred to by control group.</param>
16025 /// <param name="childTag">Expected child elements.</param>
16026 private void ParseControlGroupElement(XElement node, TableDefinition table, string childTag)
16027 {
16028 SourceLineNumber sourceLineNumbers = Preprocessor.GetSourceLineNumbers(node);
16029 int order = 0;
16030 string property = null;
16031
16032 foreach (XAttribute attrib in node.Attributes())
16033 {
16034 if (String.IsNullOrEmpty(attrib.Name.NamespaceName) || CompilerCore.WixNamespace == attrib.Name.Namespace)
16035 {
16036 switch (attrib.Name.LocalName)
16037 {
16038 case "Property":
16039 property = this.core.GetAttributeIdentifierValue(sourceLineNumbers, attrib);
16040 break;
16041 default:
16042 this.core.UnexpectedAttribute(node, attrib);
16043 break;
16044 }
16045 }
16046 else
16047 {
16048 this.core.ParseExtensionAttribute(node, attrib);
16049 }
16050 }
16051
16052 if (null == property)
16053 {
16054 this.core.OnMessage(WixErrors.ExpectedAttribute(sourceLineNumbers, node.Name.LocalName, "Property"));
16055 }
16056
16057 foreach (XElement child in node.Elements())
16058 {
16059 if (CompilerCore.WixNamespace == child.Name.Namespace)
16060 {
16061 if (childTag != child.Name.LocalName)
16062 {
16063 this.core.UnexpectedElement(node, child);
16064 }
16065
16066 switch (child.Name.LocalName)
16067 {
16068 case "ListItem":
16069 this.ParseListItemElement(child, table, property, ref order);
16070 break;
16071 case "Property":
16072 this.ParsePropertyElement(child);
16073 break;
16074 default:
16075 this.core.UnexpectedElement(node, child);
16076 break;
16077 }
16078 }
16079 else
16080 {
16081 this.core.ParseExtensionElement(node, child);
16082 }
16083 }
16084
16085 }
16086
16087 /// <summary>
16088 /// Parses a radio button control group element.
16089 /// </summary>
16090 /// <param name="node">Element to parse.</param>
16091 /// <param name="property">Property associated with this radio button group.</param>
16092 /// <param name="groupType">Specifies the current type of radio buttons in the group.</param>
16093 /// <returns>The current type of radio buttons in the group.</returns>
16094 private RadioButtonType ParseRadioButtonGroupElement(XElement node, string property, RadioButtonType groupType)
16095 {
16096 SourceLineNumber sourceLineNumbers = Preprocessor.GetSourceLineNumbers(node);
16097 int order = 0;
16098
16099 foreach (XAttribute attrib in node.Attributes())
16100 {
16101 if (String.IsNullOrEmpty(attrib.Name.NamespaceName) || CompilerCore.WixNamespace == attrib.Name.Namespace)
16102 {
16103 switch (attrib.Name.LocalName)
16104 {
16105 case "Property":
16106 property = this.core.GetAttributeIdentifierValue(sourceLineNumbers, attrib);
16107 this.core.CreateSimpleReference(sourceLineNumbers, "Property", property);
16108 break;
16109 default:
16110 this.core.UnexpectedAttribute(node, attrib);
16111 break;
16112 }
16113 }
16114 else
16115 {
16116 this.core.ParseExtensionAttribute(node, attrib);
16117 }
16118 }
16119
16120 if (null == property)
16121 {
16122 this.core.OnMessage(WixErrors.ExpectedAttribute(sourceLineNumbers, node.Name.LocalName, "Property"));
16123 }
16124
16125 foreach (XElement child in node.Elements())
16126 {
16127 if (CompilerCore.WixNamespace == child.Name.Namespace)
16128 {
16129 switch (child.Name.LocalName)
16130 {
16131 case "RadioButton":
16132 RadioButtonType type = this.ParseRadioButtonElement(child, property, ref order);
16133 if (RadioButtonType.NotSet == groupType)
16134 {
16135 groupType = type;
16136 }
16137 else if (groupType != type)
16138 {
16139 SourceLineNumber childSourceLineNumbers = Preprocessor.GetSourceLineNumbers(child);
16140 this.core.OnMessage(WixErrors.RadioButtonTypeInconsistent(childSourceLineNumbers));
16141 }
16142 break;
16143 default:
16144 this.core.UnexpectedElement(node, child);
16145 break;
16146 }
16147 }
16148 else
16149 {
16150 this.core.ParseExtensionElement(node, child);
16151 }
16152 }
16153
16154
16155 return groupType;
16156 }
16157
16158 /// <summary>
16159 /// Parses an action text element.
16160 /// </summary>
16161 /// <param name="node">Element to parse.</param>
16162 private void ParseActionTextElement(XElement node)
16163 {
16164 SourceLineNumber sourceLineNumbers = Preprocessor.GetSourceLineNumbers(node);
16165 string action = null;
16166 string template = null;
16167
16168 foreach (XAttribute attrib in node.Attributes())
16169 {
16170 if (String.IsNullOrEmpty(attrib.Name.NamespaceName) || CompilerCore.WixNamespace == attrib.Name.Namespace)
16171 {
16172 switch (attrib.Name.LocalName)
16173 {
16174 case "Action":
16175 action = this.core.GetAttributeValue(sourceLineNumbers, attrib);
16176 break;
16177 case "Template":
16178 template = this.core.GetAttributeValue(sourceLineNumbers, attrib);
16179 break;
16180 default:
16181 this.core.UnexpectedAttribute(node, attrib);
16182 break;
16183 }
16184 }
16185 else
16186 {
16187 this.core.ParseExtensionAttribute(node, attrib);
16188 }
16189 }
16190
16191 if (null == action)
16192 {
16193 this.core.OnMessage(WixErrors.ExpectedAttribute(sourceLineNumbers, node.Name.LocalName, "Action"));
16194 }
16195
16196 this.core.ParseForExtensionElements(node);
16197
16198 if (!this.core.EncounteredError)
16199 {
16200 Row row = this.core.CreateRow(sourceLineNumbers, "ActionText");
16201 row[0] = action;
16202 row[1] = Common.GetInnerText(node);
16203 row[2] = template;
16204 }
16205 }
16206
16207 /// <summary>
16208 /// Parses an ui text element.
16209 /// </summary>
16210 /// <param name="node">Element to parse.</param>
16211 private void ParseUITextElement(XElement node)
16212 {
16213 SourceLineNumber sourceLineNumbers = Preprocessor.GetSourceLineNumbers(node);
16214 Identifier id = null;
16215 string text = null;
16216
16217 foreach (XAttribute attrib in node.Attributes())
16218 {
16219 if (String.IsNullOrEmpty(attrib.Name.NamespaceName) || CompilerCore.WixNamespace == attrib.Name.Namespace)
16220 {
16221 switch (attrib.Name.LocalName)
16222 {
16223 case "Id":
16224 id = this.core.GetAttributeIdentifier(sourceLineNumbers, attrib);
16225 break;
16226 default:
16227 this.core.UnexpectedAttribute(node, attrib);
16228 break;
16229 }
16230 }
16231 else
16232 {
16233 this.core.ParseExtensionAttribute(node, attrib);
16234 }
16235 }
16236
16237 text = Common.GetInnerText(node);
16238
16239 if (null == id)
16240 {
16241 id = this.core.CreateIdentifier("txt", text);
16242 }
16243
16244 this.core.ParseForExtensionElements(node);
16245
16246 if (!this.core.EncounteredError)
16247 {
16248 Row row = this.core.CreateRow(sourceLineNumbers, "UIText", id);
16249 row[1] = text;
16250 }
16251 }
16252
16253 /// <summary>
16254 /// Parses a text style element.
16255 /// </summary>
16256 /// <param name="node">Element to parse.</param>
16257 private void ParseTextStyleElement(XElement node)
16258 {
16259 SourceLineNumber sourceLineNumbers = Preprocessor.GetSourceLineNumbers(node);
16260 Identifier id = null;
16261 int bits = 0;
16262 int color = CompilerConstants.IntegerNotSet;
16263 string faceName = null;
16264 string size = "0";
16265
16266 foreach (XAttribute attrib in node.Attributes())
16267 {
16268 if (String.IsNullOrEmpty(attrib.Name.NamespaceName) || CompilerCore.WixNamespace == attrib.Name.Namespace)
16269 {
16270 switch (attrib.Name.LocalName)
16271 {
16272 case "Id":
16273 id = this.core.GetAttributeIdentifier(sourceLineNumbers, attrib);
16274 break;
16275
16276 // RGB Values
16277 case "Red":
16278 int redColor = this.core.GetAttributeIntegerValue(sourceLineNumbers, attrib, 0, byte.MaxValue);
16279 if (CompilerConstants.IllegalInteger != redColor)
16280 {
16281 if (CompilerConstants.IntegerNotSet == color)
16282 {
16283 color = redColor;
16284 }
16285 else
16286 {
16287 color += redColor;
16288 }
16289 }
16290 break;
16291 case "Green":
16292 int greenColor = this.core.GetAttributeIntegerValue(sourceLineNumbers, attrib, 0, byte.MaxValue);
16293 if (CompilerConstants.IllegalInteger != greenColor)
16294 {
16295 if (CompilerConstants.IntegerNotSet == color)
16296 {
16297 color = greenColor * 256;
16298 }
16299 else
16300 {
16301 color += greenColor * 256;
16302 }
16303 }
16304 break;
16305 case "Blue":
16306 int blueColor = this.core.GetAttributeIntegerValue(sourceLineNumbers, attrib, 0, byte.MaxValue);
16307 if (CompilerConstants.IllegalInteger != blueColor)
16308 {
16309 if (CompilerConstants.IntegerNotSet == color)
16310 {
16311 color = blueColor * 65536;
16312 }
16313 else
16314 {
16315 color += blueColor * 65536;
16316 }
16317 }
16318 break;
16319
16320 // Style values
16321 case "Bold":
16322 if (YesNoType.Yes == this.core.GetAttributeYesNoValue(sourceLineNumbers, attrib))
16323 {
16324 bits |= MsiInterop.MsidbTextStyleStyleBitsBold;
16325 }
16326 break;
16327 case "Italic":
16328 if (YesNoType.Yes == this.core.GetAttributeYesNoValue(sourceLineNumbers, attrib))
16329 {
16330 bits |= MsiInterop.MsidbTextStyleStyleBitsItalic;
16331 }
16332 break;
16333 case "Strike":
16334 if (YesNoType.Yes == this.core.GetAttributeYesNoValue(sourceLineNumbers, attrib))
16335 {
16336 bits |= MsiInterop.MsidbTextStyleStyleBitsStrike;
16337 }
16338 break;
16339 case "Underline":
16340 if (YesNoType.Yes == this.core.GetAttributeYesNoValue(sourceLineNumbers, attrib))
16341 {
16342 bits |= MsiInterop.MsidbTextStyleStyleBitsUnderline;
16343 }
16344 break;
16345
16346 // Font values
16347 case "FaceName":
16348 faceName = this.core.GetAttributeValue(sourceLineNumbers, attrib);
16349 break;
16350 case "Size":
16351 size = this.core.GetAttributeValue(sourceLineNumbers, attrib);
16352 break;
16353
16354 default:
16355 this.core.UnexpectedAttribute(node, attrib);
16356 break;
16357 }
16358 }
16359 else
16360 {
16361 this.core.ParseExtensionAttribute(node, attrib);
16362 }
16363 }
16364
16365 if (null == id)
16366 {
16367 this.core.CreateIdentifier("txs", faceName, size.ToString(), color.ToString(), bits.ToString());
16368 }
16369
16370 if (null == faceName)
16371 {
16372 this.core.OnMessage(WixErrors.ExpectedAttribute(sourceLineNumbers, node.Name.LocalName, "FaceName"));
16373 }
16374
16375 this.core.ParseForExtensionElements(node);
16376
16377 if (!this.core.EncounteredError)
16378 {
16379 Row row = this.core.CreateRow(sourceLineNumbers, "TextStyle", id);
16380 row[1] = faceName;
16381 row[2] = size;
16382 if (0 <= color)
16383 {
16384 row[3] = color;
16385 }
16386
16387 if (0 < bits)
16388 {
16389 row[4] = bits;
16390 }
16391 }
16392 }
16393
16394 /// <summary>
16395 /// Parses a dialog element.
16396 /// </summary>
16397 /// <param name="node">Element to parse.</param>
16398 private void ParseDialogElement(XElement node)
16399 {
16400 SourceLineNumber sourceLineNumbers = Preprocessor.GetSourceLineNumbers(node);
16401 Identifier id = null;
16402 int bits = MsiInterop.MsidbDialogAttributesVisible | MsiInterop.MsidbDialogAttributesModal | MsiInterop.MsidbDialogAttributesMinimize;
16403 int height = 0;
16404 string title = null;
16405 bool trackDiskSpace = false;
16406 int width = 0;
16407 int x = 50;
16408 int y = 50;
16409
16410 foreach (XAttribute attrib in node.Attributes())
16411 {
16412 if (String.IsNullOrEmpty(attrib.Name.NamespaceName) || CompilerCore.WixNamespace == attrib.Name.Namespace)
16413 {
16414 switch (attrib.Name.LocalName)
16415 {
16416 case "Id":
16417 id = this.core.GetAttributeIdentifier(sourceLineNumbers, attrib);
16418 break;
16419 case "Height":
16420 height = this.core.GetAttributeIntegerValue(sourceLineNumbers, attrib, 0, short.MaxValue);
16421 break;
16422 case "Title":
16423 title = this.core.GetAttributeValue(sourceLineNumbers, attrib);
16424 break;
16425 case "Width":
16426 width = this.core.GetAttributeIntegerValue(sourceLineNumbers, attrib, 0, short.MaxValue);
16427 break;
16428 case "X":
16429 x = this.core.GetAttributeIntegerValue(sourceLineNumbers, attrib, 0, 100);
16430 break;
16431 case "Y":
16432 y = this.core.GetAttributeIntegerValue(sourceLineNumbers, attrib, 0, 100);
16433 break;
16434
16435 case "CustomPalette":
16436 if (YesNoType.Yes == this.core.GetAttributeYesNoValue(sourceLineNumbers, attrib))
16437 {
16438 bits ^= MsiInterop.MsidbDialogAttributesUseCustomPalette;
16439 }
16440 break;
16441 case "ErrorDialog":
16442 if (YesNoType.Yes == this.core.GetAttributeYesNoValue(sourceLineNumbers, attrib))
16443 {
16444 bits ^= MsiInterop.MsidbDialogAttributesError;
16445 }
16446 break;
16447 case "Hidden":
16448 if (YesNoType.Yes == this.core.GetAttributeYesNoValue(sourceLineNumbers, attrib))
16449 {
16450 bits ^= MsiInterop.MsidbDialogAttributesVisible;
16451 }
16452 break;
16453 case "KeepModeless":
16454 if (YesNoType.Yes == this.core.GetAttributeYesNoValue(sourceLineNumbers, attrib))
16455 {
16456 bits ^= MsiInterop.MsidbDialogAttributesKeepModeless;
16457 }
16458 break;
16459 case "LeftScroll":
16460 if (YesNoType.Yes == this.core.GetAttributeYesNoValue(sourceLineNumbers, attrib))
16461 {
16462 bits ^= MsiInterop.MsidbDialogAttributesLeftScroll;
16463 }
16464 break;
16465 case "Modeless":
16466 if (YesNoType.Yes == this.core.GetAttributeYesNoValue(sourceLineNumbers, attrib))
16467 {
16468 bits ^= MsiInterop.MsidbDialogAttributesModal;
16469 }
16470 break;
16471 case "NoMinimize":
16472 if (YesNoType.Yes == this.core.GetAttributeYesNoValue(sourceLineNumbers, attrib))
16473 {
16474 bits ^= MsiInterop.MsidbDialogAttributesMinimize;
16475 }
16476 break;
16477 case "RightAligned":
16478 if (YesNoType.Yes == this.core.GetAttributeYesNoValue(sourceLineNumbers, attrib))
16479 {
16480 bits ^= MsiInterop.MsidbDialogAttributesRightAligned;
16481 }
16482 break;
16483 case "RightToLeft":
16484 if (YesNoType.Yes == this.core.GetAttributeYesNoValue(sourceLineNumbers, attrib))
16485 {
16486 bits ^= MsiInterop.MsidbDialogAttributesRTLRO;
16487 }
16488 break;
16489 case "SystemModal":
16490 if (YesNoType.Yes == this.core.GetAttributeYesNoValue(sourceLineNumbers, attrib))
16491 {
16492 bits ^= MsiInterop.MsidbDialogAttributesSysModal;
16493 }
16494 break;
16495 case "TrackDiskSpace":
16496 if (YesNoType.Yes == this.core.GetAttributeYesNoValue(sourceLineNumbers, attrib))
16497 {
16498 bits ^= MsiInterop.MsidbDialogAttributesTrackDiskSpace;
16499 trackDiskSpace = true;
16500 }
16501 break;
16502
16503 default:
16504 this.core.UnexpectedAttribute(node, attrib);
16505 break;
16506 }
16507 }
16508 else
16509 {
16510 this.core.ParseExtensionAttribute(node, attrib);
16511 }
16512 }
16513
16514 if (null == id)
16515 {
16516 this.core.OnMessage(WixErrors.ExpectedAttribute(sourceLineNumbers, node.Name.LocalName, "Id"));
16517 id = Identifier.Invalid;
16518 }
16519
16520 Row lastTabRow = null;
16521 string cancelControl = null;
16522 string defaultControl = null;
16523 string firstControl = null;
16524
16525 foreach (XElement child in node.Elements())
16526 {
16527 if (CompilerCore.WixNamespace == child.Name.Namespace)
16528 {
16529 switch (child.Name.LocalName)
16530 {
16531 case "Control":
16532 this.ParseControlElement(child, id.Id, this.tableDefinitions["Control"], ref lastTabRow, ref firstControl, ref defaultControl, ref cancelControl, trackDiskSpace);
16533 break;
16534 default:
16535 this.core.UnexpectedElement(node, child);
16536 break;
16537 }
16538 }
16539 else
16540 {
16541 this.core.ParseExtensionElement(node, child);
16542 }
16543 }
16544
16545
16546 if (null != lastTabRow && null != lastTabRow[1])
16547 {
16548 if (firstControl != lastTabRow[1].ToString())
16549 {
16550 lastTabRow[10] = firstControl;
16551 }
16552 }
16553
16554 if (null == firstControl)
16555 {
16556 this.core.OnMessage(WixErrors.NoFirstControlSpecified(sourceLineNumbers, id.Id));
16557 }
16558
16559 if (!this.core.EncounteredError)
16560 {
16561 Row row = this.core.CreateRow(sourceLineNumbers, "Dialog", id);
16562 row[1] = x;
16563 row[2] = y;
16564 row[3] = width;
16565 row[4] = height;
16566 row[5] = bits;
16567 row[6] = title;
16568 row[7] = firstControl;
16569 row[8] = defaultControl;
16570 row[9] = cancelControl;
16571 }
16572 }
16573
16574 /// <summary>
16575 /// Parses an EmbeddedUI element.
16576 /// </summary>
16577 /// <param name="node">Element to parse.</param>
16578 private void ParseEmbeddedUIElement(XElement node)
16579 {
16580 SourceLineNumber sourceLineNumbers = Preprocessor.GetSourceLineNumbers(node);
16581 Identifier id = null;
16582 string name = null;
16583 int attributes = MsiInterop.MsidbEmbeddedUI; // by default this is the primary DLL that does not support basic UI.
16584 int messageFilter = MsiInterop.INSTALLLOGMODE_FATALEXIT | MsiInterop.INSTALLLOGMODE_ERROR | MsiInterop.INSTALLLOGMODE_WARNING | MsiInterop.INSTALLLOGMODE_USER
16585 | MsiInterop.INSTALLLOGMODE_INFO | MsiInterop.INSTALLLOGMODE_FILESINUSE | MsiInterop.INSTALLLOGMODE_RESOLVESOURCE
16586 | MsiInterop.INSTALLLOGMODE_OUTOFDISKSPACE | MsiInterop.INSTALLLOGMODE_ACTIONSTART | MsiInterop.INSTALLLOGMODE_ACTIONDATA
16587 | MsiInterop.INSTALLLOGMODE_PROGRESS | MsiInterop.INSTALLLOGMODE_COMMONDATA | MsiInterop.INSTALLLOGMODE_INITIALIZE
16588 | MsiInterop.INSTALLLOGMODE_TERMINATE | MsiInterop.INSTALLLOGMODE_SHOWDIALOG | MsiInterop.INSTALLLOGMODE_RMFILESINUSE
16589 | MsiInterop.INSTALLLOGMODE_INSTALLSTART | MsiInterop.INSTALLLOGMODE_INSTALLEND;
16590 string sourceFile = null;
16591
16592 foreach (XAttribute attrib in node.Attributes())
16593 {
16594 if (String.IsNullOrEmpty(attrib.Name.NamespaceName) || CompilerCore.WixNamespace == attrib.Name.Namespace)
16595 {
16596 switch (attrib.Name.LocalName)
16597 {
16598 case "Id":
16599 id = this.core.GetAttributeIdentifier(sourceLineNumbers, attrib);
16600 break;
16601 case "Name":
16602 name = this.core.GetAttributeLongFilename(sourceLineNumbers, attrib, false);
16603 break;
16604 case "IgnoreFatalExit":
16605 if (YesNoType.Yes == this.core.GetAttributeYesNoValue(sourceLineNumbers, attrib))
16606 {
16607 messageFilter ^= MsiInterop.INSTALLLOGMODE_FATALEXIT;
16608 }
16609 break;
16610 case "IgnoreError":
16611 if (YesNoType.Yes == this.core.GetAttributeYesNoValue(sourceLineNumbers, attrib))
16612 {
16613 messageFilter ^= MsiInterop.INSTALLLOGMODE_ERROR;
16614 }
16615 break;
16616 case "IgnoreWarning":
16617 if (YesNoType.Yes == this.core.GetAttributeYesNoValue(sourceLineNumbers, attrib))
16618 {
16619 messageFilter ^= MsiInterop.INSTALLLOGMODE_WARNING;
16620 }
16621 break;
16622 case "IgnoreUser":
16623 if (YesNoType.Yes == this.core.GetAttributeYesNoValue(sourceLineNumbers, attrib))
16624 {
16625 messageFilter ^= MsiInterop.INSTALLLOGMODE_USER;
16626 }
16627 break;
16628 case "IgnoreInfo":
16629 if (YesNoType.Yes == this.core.GetAttributeYesNoValue(sourceLineNumbers, attrib))
16630 {
16631 messageFilter ^= MsiInterop.INSTALLLOGMODE_INFO;
16632 }
16633 break;
16634 case "IgnoreFilesInUse":
16635 if (YesNoType.Yes == this.core.GetAttributeYesNoValue(sourceLineNumbers, attrib))
16636 {
16637 messageFilter ^= MsiInterop.INSTALLLOGMODE_FILESINUSE;
16638 }
16639 break;
16640 case "IgnoreResolveSource":
16641 if (YesNoType.Yes == this.core.GetAttributeYesNoValue(sourceLineNumbers, attrib))
16642 {
16643 messageFilter ^= MsiInterop.INSTALLLOGMODE_RESOLVESOURCE;
16644 }
16645 break;
16646 case "IgnoreOutOfDiskSpace":
16647 if (YesNoType.Yes == this.core.GetAttributeYesNoValue(sourceLineNumbers, attrib))
16648 {
16649 messageFilter ^= MsiInterop.INSTALLLOGMODE_OUTOFDISKSPACE;
16650 }
16651 break;
16652 case "IgnoreActionStart":
16653 if (YesNoType.Yes == this.core.GetAttributeYesNoValue(sourceLineNumbers, attrib))
16654 {
16655 messageFilter ^= MsiInterop.INSTALLLOGMODE_ACTIONSTART;
16656 }
16657 break;
16658 case "IgnoreActionData":
16659 if (YesNoType.Yes == this.core.GetAttributeYesNoValue(sourceLineNumbers, attrib))
16660 {
16661 messageFilter ^= MsiInterop.INSTALLLOGMODE_ACTIONDATA;
16662 }
16663 break;
16664 case "IgnoreProgress":
16665 if (YesNoType.Yes == this.core.GetAttributeYesNoValue(sourceLineNumbers, attrib))
16666 {
16667 messageFilter ^= MsiInterop.INSTALLLOGMODE_PROGRESS;
16668 }
16669 break;
16670 case "IgnoreCommonData":
16671 if (YesNoType.Yes == this.core.GetAttributeYesNoValue(sourceLineNumbers, attrib))
16672 {
16673 messageFilter ^= MsiInterop.INSTALLLOGMODE_COMMONDATA;
16674 }
16675 break;
16676 case "IgnoreInitialize":
16677 if (YesNoType.Yes == this.core.GetAttributeYesNoValue(sourceLineNumbers, attrib))
16678 {
16679 messageFilter ^= MsiInterop.INSTALLLOGMODE_INITIALIZE;
16680 }
16681 break;
16682 case "IgnoreTerminate":
16683 if (YesNoType.Yes == this.core.GetAttributeYesNoValue(sourceLineNumbers, attrib))
16684 {
16685 messageFilter ^= MsiInterop.INSTALLLOGMODE_TERMINATE;
16686 }
16687 break;
16688 case "IgnoreShowDialog":
16689 if (YesNoType.Yes == this.core.GetAttributeYesNoValue(sourceLineNumbers, attrib))
16690 {
16691 messageFilter ^= MsiInterop.INSTALLLOGMODE_SHOWDIALOG;
16692 }
16693 break;
16694 case "IgnoreRMFilesInUse":
16695 if (YesNoType.Yes == this.core.GetAttributeYesNoValue(sourceLineNumbers, attrib))
16696 {
16697 messageFilter ^= MsiInterop.INSTALLLOGMODE_RMFILESINUSE;
16698 }
16699 break;
16700 case "IgnoreInstallStart":
16701 if (YesNoType.Yes == this.core.GetAttributeYesNoValue(sourceLineNumbers, attrib))
16702 {
16703 messageFilter ^= MsiInterop.INSTALLLOGMODE_INSTALLSTART;
16704 }
16705 break;
16706 case "IgnoreInstallEnd":
16707 if (YesNoType.Yes == this.core.GetAttributeYesNoValue(sourceLineNumbers, attrib))
16708 {
16709 messageFilter ^= MsiInterop.INSTALLLOGMODE_INSTALLEND;
16710 }
16711 break;
16712 case "SourceFile":
16713 sourceFile = this.core.GetAttributeValue(sourceLineNumbers, attrib);
16714 break;
16715 case "SupportBasicUI":
16716 if (YesNoType.Yes == this.core.GetAttributeYesNoValue(sourceLineNumbers, attrib))
16717 {
16718 attributes |= MsiInterop.MsidbEmbeddedHandlesBasic;
16719 }
16720 break;
16721 default:
16722 this.core.UnexpectedAttribute(node, attrib);
16723 break;
16724 }
16725 }
16726 else
16727 {
16728 this.core.ParseExtensionAttribute(node, attrib);
16729 }
16730 }
16731
16732 if (String.IsNullOrEmpty(sourceFile))
16733 {
16734 this.core.OnMessage(WixErrors.ExpectedAttribute(sourceLineNumbers, node.Name.LocalName, "SourceFile"));
16735 }
16736 else if (String.IsNullOrEmpty(name))
16737 {
16738 name = Path.GetFileName(sourceFile);
16739 if (!this.core.IsValidLongFilename(name, false))
16740 {
16741 this.core.OnMessage(WixErrors.IllegalLongFilename(sourceLineNumbers, node.Name.LocalName, "Source", name));
16742 }
16743 }
16744
16745 if (null == id)
16746 {
16747 if (!String.IsNullOrEmpty(name))
16748 {
16749 id = this.core.CreateIdentifierFromFilename(name);
16750 }
16751
16752 if (null == id)
16753 {
16754 this.core.OnMessage(WixErrors.ExpectedAttribute(sourceLineNumbers, node.Name.LocalName, "Id"));
16755 }
16756 else if (!Common.IsIdentifier(id.Id))
16757 {
16758 this.core.OnMessage(WixErrors.IllegalIdentifier(sourceLineNumbers, node.Name.LocalName, "Id", id.Id));
16759 }
16760 }
16761 else if (String.IsNullOrEmpty(name))
16762 {
16763 name = id.Id;
16764 }
16765
16766 if (!name.Contains("."))
16767 {
16768 this.core.OnMessage(WixErrors.InvalidEmbeddedUIFileName(sourceLineNumbers, name));
16769 }
16770
16771 foreach (XElement child in node.Elements())
16772 {
16773 if (CompilerCore.WixNamespace == child.Name.Namespace)
16774 {
16775 switch (child.Name.LocalName)
16776 {
16777 case "EmbeddedUIResource":
16778 this.ParseEmbeddedUIResourceElement(child);
16779 break;
16780 default:
16781 this.core.UnexpectedElement(node, child);
16782 break;
16783 }
16784 }
16785 else
16786 {
16787 this.core.ParseExtensionElement(node, child);
16788 }
16789 }
16790
16791 if (!this.core.EncounteredError)
16792 {
16793 Row row = this.core.CreateRow(sourceLineNumbers, "MsiEmbeddedUI", id);
16794 row[1] = name;
16795 row[2] = attributes;
16796 row[3] = messageFilter;
16797 row[4] = sourceFile;
16798 }
16799 }
16800
16801 /// <summary>
16802 /// Parses a embedded UI resource element.
16803 /// </summary>
16804 /// <param name="node">Element to parse.</param>
16805 /// <param name="parentId">Identifier of parent EmbeddedUI element.</param>
16806 private void ParseEmbeddedUIResourceElement(XElement node)
16807 {
16808 SourceLineNumber sourceLineNumbers = Preprocessor.GetSourceLineNumbers(node);
16809 Identifier id = null;
16810 string name = null;
16811 string sourceFile = null;
16812
16813 foreach (XAttribute attrib in node.Attributes())
16814 {
16815 if (String.IsNullOrEmpty(attrib.Name.NamespaceName) || CompilerCore.WixNamespace == attrib.Name.Namespace)
16816 {
16817 switch (attrib.Name.LocalName)
16818 {
16819 case "Id":
16820 id = this.core.GetAttributeIdentifier(sourceLineNumbers, attrib);
16821 break;
16822 case "Name":
16823 name = this.core.GetAttributeLongFilename(sourceLineNumbers, attrib, false);
16824 break;
16825 case "SourceFile":
16826 sourceFile = this.core.GetAttributeValue(sourceLineNumbers, attrib);
16827 break;
16828 default:
16829 this.core.UnexpectedAttribute(node, attrib);
16830 break;
16831 }
16832 }
16833 else
16834 {
16835 this.core.ParseExtensionAttribute(node, attrib);
16836 }
16837 }
16838
16839 if (String.IsNullOrEmpty(sourceFile))
16840 {
16841 this.core.OnMessage(WixErrors.ExpectedAttribute(sourceLineNumbers, node.Name.LocalName, "SourceFile"));
16842 }
16843 else if (String.IsNullOrEmpty(name))
16844 {
16845 name = Path.GetFileName(sourceFile);
16846 if (!this.core.IsValidLongFilename(name, false))
16847 {
16848 this.core.OnMessage(WixErrors.IllegalLongFilename(sourceLineNumbers, node.Name.LocalName, "Source", name));
16849 }
16850 }
16851
16852 if (null == id)
16853 {
16854 if (!String.IsNullOrEmpty(name))
16855 {
16856 id = this.core.CreateIdentifierFromFilename(name);
16857 }
16858
16859 if (null == id)
16860 {
16861 this.core.OnMessage(WixErrors.ExpectedAttribute(sourceLineNumbers, node.Name.LocalName, "Id"));
16862 }
16863 else if (!Common.IsIdentifier(id.Id))
16864 {
16865 this.core.OnMessage(WixErrors.IllegalIdentifier(sourceLineNumbers, node.Name.LocalName, "Id", id.Id));
16866 }
16867 }
16868 else if (String.IsNullOrEmpty(name))
16869 {
16870 name = id.Id;
16871 }
16872
16873 this.core.ParseForExtensionElements(node);
16874
16875 if (!this.core.EncounteredError)
16876 {
16877 Row row = this.core.CreateRow(sourceLineNumbers, "MsiEmbeddedUI", id);
16878 row[1] = name;
16879 row[2] = 0; // embedded UI resources always set this to 0
16880 row[3] = null;
16881 row[4] = sourceFile;
16882 }
16883 }
16884
16885 /// <summary>
16886 /// Parses a control element.
16887 /// </summary>
16888 /// <param name="node">Element to parse.</param>
16889 /// <param name="dialog">Identifier for parent dialog.</param>
16890 /// <param name="table">Table control belongs in.</param>
16891 /// <param name="lastTabRow">Last row in the tab order.</param>
16892 /// <param name="firstControl">Name of the first control in the tab order.</param>
16893 /// <param name="defaultControl">Name of the default control.</param>
16894 /// <param name="cancelControl">Name of the candle control.</param>
16895 /// <param name="trackDiskSpace">True if the containing dialog tracks disk space.</param>
16896 private void ParseControlElement(XElement node, string dialog, TableDefinition table, ref Row lastTabRow, ref string firstControl, ref string defaultControl, ref string cancelControl, bool trackDiskSpace)
16897 {
16898 SourceLineNumber sourceLineNumbers = Preprocessor.GetSourceLineNumbers(node);
16899 Identifier id = null;
16900 BitArray bits = new BitArray(32);
16901 int attributes = 0;
16902 string checkBoxPropertyRef = null;
16903 string checkboxValue = null;
16904 string controlType = null;
16905 bool disabled = false;
16906 string height = null;
16907 string help = null;
16908 bool isCancel = false;
16909 bool isDefault = false;
16910 bool notTabbable = false;
16911 string property = null;
16912 int publishOrder = 0;
16913 string[] specialAttributes = null;
16914 string sourceFile = null;
16915 string text = null;
16916 string tooltip = null;
16917 RadioButtonType radioButtonsType = RadioButtonType.NotSet;
16918 string width = null;
16919 string x = null;
16920 string y = null;
16921
16922 // The rest of the method relies on the control's Type, so we have to get that first.
16923 XAttribute typeAttribute = node.Attribute("Type");
16924 if (null == typeAttribute)
16925 {
16926 this.core.OnMessage(WixErrors.ExpectedAttribute(sourceLineNumbers, node.Name.LocalName, "Type"));
16927 }
16928 else
16929 {
16930 controlType = this.core.GetAttributeValue(sourceLineNumbers, typeAttribute);
16931 }
16932
16933 switch (controlType)
16934 {
16935 case "Billboard":
16936 specialAttributes = null;
16937 notTabbable = true;
16938 disabled = true;
16939
16940 this.core.EnsureTable(sourceLineNumbers, "Billboard");
16941 break;
16942 case "Bitmap":
16943 specialAttributes = MsiInterop.BitmapControlAttributes;
16944 notTabbable = true;
16945 disabled = true;
16946 break;
16947 case "CheckBox":
16948 specialAttributes = MsiInterop.CheckboxControlAttributes;
16949 break;
16950 case "ComboBox":
16951 specialAttributes = MsiInterop.ComboboxControlAttributes;
16952 break;
16953 case "DirectoryCombo":
16954 specialAttributes = MsiInterop.VolumeControlAttributes;
16955 break;
16956 case "DirectoryList":
16957 specialAttributes = null;
16958 break;
16959 case "Edit":
16960 specialAttributes = MsiInterop.EditControlAttributes;
16961 break;
16962 case "GroupBox":
16963 specialAttributes = null;
16964 notTabbable = true;
16965 break;
16966 case "Hyperlink":
16967 specialAttributes = MsiInterop.HyperlinkControlAttributes;
16968 break;
16969 case "Icon":
16970 specialAttributes = MsiInterop.IconControlAttributes;
16971 notTabbable = true;
16972 disabled = true;
16973 break;
16974 case "Line":
16975 specialAttributes = null;
16976 notTabbable = true;
16977 disabled = true;
16978 break;
16979 case "ListBox":
16980 specialAttributes = MsiInterop.ListboxControlAttributes;
16981 break;
16982 case "ListView":
16983 specialAttributes = MsiInterop.ListviewControlAttributes;
16984 break;
16985 case "MaskedEdit":
16986 specialAttributes = MsiInterop.EditControlAttributes;
16987 break;
16988 case "PathEdit":
16989 specialAttributes = MsiInterop.EditControlAttributes;
16990 break;
16991 case "ProgressBar":
16992 specialAttributes = MsiInterop.ProgressControlAttributes;
16993 notTabbable = true;
16994 disabled = true;
16995 break;
16996 case "PushButton":
16997 specialAttributes = MsiInterop.ButtonControlAttributes;
16998 break;
16999 case "RadioButtonGroup":
17000 specialAttributes = MsiInterop.RadioControlAttributes;
17001 break;
17002 case "ScrollableText":
17003 specialAttributes = null;
17004 break;
17005 case "SelectionTree":
17006 specialAttributes = null;
17007 break;
17008 case "Text":
17009 specialAttributes = MsiInterop.TextControlAttributes;
17010 notTabbable = true;
17011 break;
17012 case "VolumeCostList":
17013 specialAttributes = MsiInterop.VolumeControlAttributes;
17014 notTabbable = true;
17015 break;
17016 case "VolumeSelectCombo":
17017 specialAttributes = MsiInterop.VolumeControlAttributes;
17018 break;
17019 default:
17020 specialAttributes = null;
17021 notTabbable = true;
17022 break;
17023 }
17024
17025 foreach (XAttribute attrib in node.Attributes())
17026 {
17027 if (String.IsNullOrEmpty(attrib.Name.NamespaceName) || CompilerCore.WixNamespace == attrib.Name.Namespace)
17028 {
17029 switch (attrib.Name.LocalName)
17030 {
17031 case "Id":
17032 id = this.core.GetAttributeIdentifier(sourceLineNumbers, attrib);
17033 break;
17034 case "Type": // already processed
17035 break;
17036 case "Cancel":
17037 isCancel = YesNoType.Yes == this.core.GetAttributeYesNoValue(sourceLineNumbers, attrib);
17038 break;
17039 case "CheckBoxPropertyRef":
17040 checkBoxPropertyRef = this.core.GetAttributeValue(sourceLineNumbers, attrib);
17041 break;
17042 case "CheckBoxValue":
17043 checkboxValue = this.core.GetAttributeValue(sourceLineNumbers, attrib);
17044 break;
17045 case "Default":
17046 isDefault = YesNoType.Yes == this.core.GetAttributeYesNoValue(sourceLineNumbers, attrib);
17047 break;
17048 case "Height":
17049 height = this.core.GetAttributeLocalizableIntegerValue(sourceLineNumbers, attrib, 0, short.MaxValue);
17050 break;
17051 case "Help":
17052 help = this.core.GetAttributeValue(sourceLineNumbers, attrib);
17053 break;
17054 case "IconSize":
17055 string iconSizeValue = this.core.GetAttributeValue(sourceLineNumbers, attrib);
17056 if (null != specialAttributes)
17057 {
17058 if (0 < iconSizeValue.Length)
17059 {
17060 Wix.Control.IconSizeType iconsSizeType = Wix.Control.ParseIconSizeType(iconSizeValue);
17061 switch (iconsSizeType)
17062 {
17063 case Wix.Control.IconSizeType.Item16:
17064 this.core.TrySetBitFromName(specialAttributes, "Icon16", YesNoType.Yes, bits, 16);
17065 break;
17066 case Wix.Control.IconSizeType.Item32:
17067 this.core.TrySetBitFromName(specialAttributes, "Icon32", YesNoType.Yes, bits, 16);
17068 break;
17069 case Wix.Control.IconSizeType.Item48:
17070 this.core.TrySetBitFromName(specialAttributes, "Icon16", YesNoType.Yes, bits, 16);
17071 this.core.TrySetBitFromName(specialAttributes, "Icon32", YesNoType.Yes, bits, 16);
17072 break;
17073 default:
17074 this.core.OnMessage(WixErrors.IllegalAttributeValue(sourceLineNumbers, node.Name.LocalName, attrib.Name.LocalName, iconSizeValue, "16", "32", "48"));
17075 break;
17076 }
17077 }
17078 }
17079 else
17080 {
17081 this.core.OnMessage(WixErrors.IllegalAttributeValueWithOtherAttribute(sourceLineNumbers, node.Name.LocalName, attrib.Name.LocalName, iconSizeValue, "Type"));
17082 }
17083 break;
17084 case "Property":
17085 property = this.core.GetAttributeValue(sourceLineNumbers, attrib);
17086 break;
17087 case "TabSkip":
17088 notTabbable = YesNoType.Yes == this.core.GetAttributeYesNoValue(sourceLineNumbers, attrib);
17089 break;
17090 case "Text":
17091 text = this.core.GetAttributeValue(sourceLineNumbers, attrib);
17092 break;
17093 case "ToolTip":
17094 tooltip = this.core.GetAttributeValue(sourceLineNumbers, attrib);
17095 break;
17096 case "Width":
17097 width = this.core.GetAttributeLocalizableIntegerValue(sourceLineNumbers, attrib, 0, short.MaxValue);
17098 break;
17099 case "X":
17100 x = this.core.GetAttributeLocalizableIntegerValue(sourceLineNumbers, attrib, 0, short.MaxValue);
17101 break;
17102 case "Y":
17103 y = this.core.GetAttributeLocalizableIntegerValue(sourceLineNumbers, attrib, 0, short.MaxValue);
17104 break;
17105 default:
17106 YesNoType attribValue = this.core.GetAttributeYesNoValue(sourceLineNumbers, attrib);
17107 if (!this.core.TrySetBitFromName(MsiInterop.CommonControlAttributes, attrib.Name.LocalName, attribValue, bits, 0))
17108 {
17109 if (null == specialAttributes || !this.core.TrySetBitFromName(specialAttributes, attrib.Name.LocalName, attribValue, bits, 16))
17110 {
17111 this.core.UnexpectedAttribute(node, attrib);
17112 }
17113 }
17114 break;
17115 }
17116 }
17117 else
17118 {
17119 this.core.ParseExtensionAttribute(node, attrib);
17120 }
17121 }
17122
17123 attributes = this.core.CreateIntegerFromBitArray(bits);
17124
17125 if (disabled)
17126 {
17127 attributes |= MsiInterop.MsidbControlAttributesEnabled; // bit will be inverted when stored
17128 }
17129
17130 if (null == height)
17131 {
17132 this.core.OnMessage(WixErrors.ExpectedAttribute(sourceLineNumbers, node.Name.LocalName, "Height"));
17133 }
17134
17135 if (null == width)
17136 {
17137 this.core.OnMessage(WixErrors.ExpectedAttribute(sourceLineNumbers, node.Name.LocalName, "Width"));
17138 }
17139
17140 if (null == x)
17141 {
17142 this.core.OnMessage(WixErrors.ExpectedAttribute(sourceLineNumbers, node.Name.LocalName, "X"));
17143 }
17144
17145 if (null == y)
17146 {
17147 this.core.OnMessage(WixErrors.ExpectedAttribute(sourceLineNumbers, node.Name.LocalName, "Y"));
17148 }
17149
17150 if (null == id)
17151 {
17152 id = this.core.CreateIdentifier("ctl", dialog, x, y, height, width);
17153 }
17154
17155 if (isCancel)
17156 {
17157 cancelControl = id.Id;
17158 }
17159
17160 if (isDefault)
17161 {
17162 defaultControl = id.Id;
17163 }
17164
17165 foreach (XElement child in node.Elements())
17166 {
17167 if (CompilerCore.WixNamespace == child.Name.Namespace)
17168 {
17169 SourceLineNumber childSourceLineNumbers = Preprocessor.GetSourceLineNumbers(child);
17170 switch (child.Name.LocalName)
17171 {
17172 case "Binary":
17173 this.ParseBinaryElement(child);
17174 break;
17175 case "ComboBox":
17176 this.ParseControlGroupElement(child, this.tableDefinitions["ComboBox"], "ListItem");
17177 break;
17178 case "Condition":
17179 this.ParseConditionElement(child, node.Name.LocalName, id.Id, dialog);
17180 break;
17181 case "ListBox":
17182 this.ParseControlGroupElement(child, this.tableDefinitions["ListBox"], "ListItem");
17183 break;
17184 case "ListView":
17185 this.ParseControlGroupElement(child, this.tableDefinitions["ListView"], "ListItem");
17186 break;
17187 case "Property":
17188 this.ParsePropertyElement(child);
17189 break;
17190 case "Publish":
17191 this.ParsePublishElement(child, dialog ?? String.Empty, id.Id, ref publishOrder);
17192 break;
17193 case "RadioButtonGroup":
17194 radioButtonsType = this.ParseRadioButtonGroupElement(child, property, radioButtonsType);
17195 break;
17196 case "Subscribe":
17197 this.ParseSubscribeElement(child, dialog, id.Id);
17198 break;
17199 case "Text":
17200 foreach (XAttribute attrib in child.Attributes())
17201 {
17202 if (String.IsNullOrEmpty(attrib.Name.NamespaceName) || CompilerCore.WixNamespace == attrib.Name.Namespace)
17203 {
17204 switch (attrib.Name.LocalName)
17205 {
17206 case "SourceFile":
17207 sourceFile = this.core.GetAttributeValue(childSourceLineNumbers, attrib);
17208 break;
17209 default:
17210 this.core.UnexpectedAttribute(child, attrib);
17211 break;
17212 }
17213 }
17214 else
17215 {
17216 this.core.ParseExtensionAttribute(child, attrib);
17217 }
17218 }
17219
17220 text = Common.GetInnerText(child);
17221 if (!String.IsNullOrEmpty(text) && null != sourceFile)
17222 {
17223 this.core.OnMessage(WixErrors.IllegalAttributeWithInnerText(childSourceLineNumbers, child.Name.LocalName, "SourceFile"));
17224 }
17225 break;
17226 default:
17227 this.core.UnexpectedElement(node, child);
17228 break;
17229 }
17230 }
17231 else
17232 {
17233 this.core.ParseExtensionElement(node, child);
17234 }
17235 }
17236
17237 // If the radio buttons have icons, then we need to add the icon attribute.
17238 switch (radioButtonsType)
17239 {
17240 case RadioButtonType.Bitmap:
17241 attributes |= MsiInterop.MsidbControlAttributesBitmap;
17242 break;
17243 case RadioButtonType.Icon:
17244 attributes |= MsiInterop.MsidbControlAttributesIcon;
17245 break;
17246 case RadioButtonType.Text:
17247 // Text is the default so nothing needs to be added bits
17248 break;
17249 }
17250
17251 // If we're tracking disk space, and this is a non-FormatSize Text control, and the text attribute starts with
17252 // '[' and ends with ']', add a space. It is not necessary for the whole string to be a property, just
17253 // those two characters matter.
17254 if (trackDiskSpace && "Text" == controlType &&
17255 MsiInterop.MsidbControlAttributesFormatSize != (attributes & MsiInterop.MsidbControlAttributesFormatSize) &&
17256 null != text && text.StartsWith("[", StringComparison.Ordinal) && text.EndsWith("]", StringComparison.Ordinal))
17257 {
17258 text = String.Concat(text, " ");
17259 }
17260
17261 // the logic for creating control rows is a little tricky because of the way tabable controls are set
17262 Row row = null;
17263 if (!this.core.EncounteredError)
17264 {
17265 if ("CheckBox" == controlType)
17266 {
17267 if (String.IsNullOrEmpty(property) && String.IsNullOrEmpty(checkBoxPropertyRef))
17268 {
17269 this.core.OnMessage(WixErrors.ExpectedAttribute(sourceLineNumbers, node.Name.LocalName, "Property", "CheckBoxPropertyRef", true));
17270 }
17271 else if (!String.IsNullOrEmpty(property) && !String.IsNullOrEmpty(checkBoxPropertyRef))
17272 {
17273 this.core.OnMessage(WixErrors.IllegalAttributeWithOtherAttribute(sourceLineNumbers, node.Name.LocalName, "Property", "CheckBoxPropertyRef"));
17274 }
17275 else if (!String.IsNullOrEmpty(property))
17276 {
17277 row = this.core.CreateRow(sourceLineNumbers, "CheckBox");
17278 row[0] = property;
17279 row[1] = checkboxValue;
17280 }
17281 else
17282 {
17283 this.core.CreateSimpleReference(sourceLineNumbers, "CheckBox", checkBoxPropertyRef);
17284 }
17285 }
17286
17287 row = this.core.CreateRow(sourceLineNumbers, table.Name);
17288 row.Access = id.Access;
17289 row[0] = dialog;
17290 row[1] = id.Id;
17291 row[2] = controlType;
17292 row[3] = x;
17293 row[4] = y;
17294 row[5] = width;
17295 row[6] = height;
17296 row[7] = attributes ^ (MsiInterop.MsidbControlAttributesVisible | MsiInterop.MsidbControlAttributesEnabled);
17297 if ("BBControl" == table.Name)
17298 {
17299 row[8] = text; // BBControl.Text
17300
17301 if (null != sourceFile)
17302 {
17303 Row wixBBControlRow = this.core.CreateRow(sourceLineNumbers, "WixBBControl");
17304 wixBBControlRow.Access = id.Access;
17305 wixBBControlRow[0] = dialog;
17306 wixBBControlRow[1] = id.Id;
17307 wixBBControlRow[2] = sourceFile;
17308 }
17309 }
17310 else
17311 {
17312 row[8] = !String.IsNullOrEmpty(property) ? property : checkBoxPropertyRef;
17313 row[9] = text;
17314 if (null != tooltip || null != help)
17315 {
17316 row[11] = String.Concat(tooltip, "|", help); // Separator is required, even if only one is non-null.
17317 }
17318
17319 if (null != sourceFile)
17320 {
17321 Row wixControlRow = this.core.CreateRow(sourceLineNumbers, "WixControl");
17322 wixControlRow.Access = id.Access;
17323 wixControlRow[0] = dialog;
17324 wixControlRow[1] = id.Id;
17325 wixControlRow[2] = sourceFile;
17326 }
17327 }
17328 }
17329
17330 if (!notTabbable)
17331 {
17332 if ("BBControl" == table.Name)
17333 {
17334 this.core.OnMessage(WixErrors.TabbableControlNotAllowedInBillboard(sourceLineNumbers, node.Name.LocalName, controlType));
17335 }
17336
17337 if (null == firstControl)
17338 {
17339 firstControl = id.Id;
17340 }
17341
17342 if (null != lastTabRow)
17343 {
17344 lastTabRow[10] = id.Id;
17345 }
17346 lastTabRow = row;
17347 }
17348
17349 // bitmap and icon controls contain a foreign key into the binary table in the text column;
17350 // add a reference if the identifier of the binary entry is known during compilation
17351 if (("Bitmap" == controlType || "Icon" == controlType) && Common.IsIdentifier(text))
17352 {
17353 this.core.CreateSimpleReference(sourceLineNumbers, "Binary", text);
17354 }
17355 }
17356
17357 /// <summary>
17358 /// Parses a publish control event element.
17359 /// </summary>
17360 /// <param name="node">Element to parse.</param>
17361 /// <param name="dialog">Identifier of parent dialog.</param>
17362 /// <param name="control">Identifier of parent control.</param>
17363 /// <param name="order">Relative order of controls.</param>
17364 private void ParsePublishElement(XElement node, string dialog, string control, ref int order)
17365 {
17366 SourceLineNumber sourceLineNumbers = Preprocessor.GetSourceLineNumbers(node);
17367 string argument = null;
17368 string condition = null;
17369 string controlEvent = null;
17370 string property = null;
17371
17372 // give this control event a unique ordering
17373 order++;
17374
17375 foreach (XAttribute attrib in node.Attributes())
17376 {
17377 if (String.IsNullOrEmpty(attrib.Name.NamespaceName) || CompilerCore.WixNamespace == attrib.Name.Namespace)
17378 {
17379 switch (attrib.Name.LocalName)
17380 {
17381 case "Control":
17382 if (null != control)
17383 {
17384 this.core.OnMessage(WixErrors.IllegalAttributeWhenNested(sourceLineNumbers, node.Name.LocalName, attrib.Name.LocalName, node.Parent.Name.LocalName));
17385 }
17386 control = this.core.GetAttributeIdentifierValue(sourceLineNumbers, attrib);
17387 break;
17388 case "Dialog":
17389 if (null != dialog)
17390 {
17391 this.core.OnMessage(WixErrors.IllegalAttributeWhenNested(sourceLineNumbers, node.Name.LocalName, attrib.Name.LocalName, node.Parent.Name.LocalName));
17392 }
17393 dialog = this.core.GetAttributeIdentifierValue(sourceLineNumbers, attrib);
17394 this.core.CreateSimpleReference(sourceLineNumbers, "Dialog", dialog);
17395 break;
17396 case "Event":
17397 controlEvent = Compiler.UppercaseFirstChar(this.core.GetAttributeValue(sourceLineNumbers, attrib));
17398 break;
17399 case "Order":
17400 order = this.core.GetAttributeIntegerValue(sourceLineNumbers, attrib, 0, 2147483647);
17401 break;
17402 case "Property":
17403 property = String.Concat("[", this.core.GetAttributeValue(sourceLineNumbers, attrib), "]");
17404 break;
17405 case "Value":
17406 argument = this.core.GetAttributeValue(sourceLineNumbers, attrib);
17407 break;
17408 default:
17409 this.core.UnexpectedAttribute(node, attrib);
17410 break;
17411 }
17412 }
17413 else
17414 {
17415 this.core.ParseExtensionAttribute(node, attrib);
17416 }
17417 }
17418
17419 condition = this.core.GetConditionInnerText(node);
17420
17421 if (null == control)
17422 {
17423 this.core.OnMessage(WixErrors.ExpectedAttribute(sourceLineNumbers, node.Name.LocalName, "Control"));
17424 }
17425
17426 if (null == dialog)
17427 {
17428 this.core.OnMessage(WixErrors.ExpectedAttribute(sourceLineNumbers, node.Name.LocalName, "Dialog"));
17429 }
17430
17431 if (null == controlEvent && null == property) // need to specify at least one
17432 {
17433 this.core.OnMessage(WixErrors.ExpectedAttributes(sourceLineNumbers, node.Name.LocalName, "Event", "Property"));
17434 }
17435 else if (null != controlEvent && null != property) // cannot specify both
17436 {
17437 this.core.OnMessage(WixErrors.IllegalAttributeWithOtherAttribute(sourceLineNumbers, node.Name.LocalName, "Event", "Property"));
17438 }
17439
17440 if (null == argument)
17441 {
17442 if (null != controlEvent)
17443 {
17444 this.core.OnMessage(WixErrors.ExpectedAttribute(sourceLineNumbers, node.Name.LocalName, "Value", "Event"));
17445 }
17446 else if (null != property)
17447 {
17448 // if this is setting a property to null, put a special value in the argument column
17449 argument = "{}";
17450 }
17451 }
17452
17453 this.core.ParseForExtensionElements(node);
17454
17455 if (!this.core.EncounteredError)
17456 {
17457 Row row = this.core.CreateRow(sourceLineNumbers, "ControlEvent");
17458 row[0] = dialog;
17459 row[1] = control;
17460 row[2] = (null != controlEvent ? controlEvent : property);
17461 row[3] = argument;
17462 row[4] = condition;
17463 row[5] = order;
17464 }
17465
17466 if ("DoAction" == controlEvent && null != argument)
17467 {
17468 // if we're not looking at a standard action or a formatted string then create a reference
17469 // to the custom action.
17470 if (!WindowsInstallerStandard.IsStandardAction(argument) && !Common.ContainsProperty(argument))
17471 {
17472 this.core.CreateSimpleReference(sourceLineNumbers, "CustomAction", argument);
17473 }
17474 }
17475
17476 // if we're referring to a dialog but not through a property, add it to the references
17477 if (("NewDialog" == controlEvent || "SpawnDialog" == controlEvent || "SpawnWaitDialog" == controlEvent || "SelectionBrowse" == controlEvent) && Common.IsIdentifier(argument))
17478 {
17479 this.core.CreateSimpleReference(sourceLineNumbers, "Dialog", argument);
17480 }
17481 }
17482
17483 /// <summary>
17484 /// Parses a control subscription element.
17485 /// </summary>
17486 /// <param name="node">Element to parse.</param>
17487 /// <param name="dialog">Identifier of dialog.</param>
17488 /// <param name="control">Identifier of control.</param>
17489 private void ParseSubscribeElement(XElement node, string dialog, string control)
17490 {
17491 SourceLineNumber sourceLineNumbers = Preprocessor.GetSourceLineNumbers(node);
17492 string controlAttribute = null;
17493 string eventMapping = null;
17494
17495 foreach (XAttribute attrib in node.Attributes())
17496 {
17497 if (String.IsNullOrEmpty(attrib.Name.NamespaceName) || CompilerCore.WixNamespace == attrib.Name.Namespace)
17498 {
17499 switch (attrib.Name.LocalName)
17500 {
17501 case "Attribute":
17502 controlAttribute = Compiler.UppercaseFirstChar(this.core.GetAttributeIdentifierValue(sourceLineNumbers, attrib));
17503 break;
17504 case "Event":
17505 eventMapping = Compiler.UppercaseFirstChar(this.core.GetAttributeIdentifierValue(sourceLineNumbers, attrib));
17506 break;
17507 default:
17508 this.core.UnexpectedAttribute(node, attrib);
17509 break;
17510 }
17511 }
17512 else
17513 {
17514 this.core.ParseExtensionAttribute(node, attrib);
17515 }
17516 }
17517
17518 this.core.ParseForExtensionElements(node);
17519
17520 if (!this.core.EncounteredError)
17521 {
17522 Row row = this.core.CreateRow(sourceLineNumbers, "EventMapping");
17523 row[0] = dialog;
17524 row[1] = control;
17525 row[2] = eventMapping;
17526 row[3] = controlAttribute;
17527 }
17528 }
17529
17530 /// <summary>
17531 /// Parses an upgrade element.
17532 /// </summary>
17533 /// <param name="node">Element to parse.</param>
17534 private void ParseUpgradeElement(XElement node)
17535 {
17536 SourceLineNumber sourceLineNumbers = Preprocessor.GetSourceLineNumbers(node);
17537 string id = null;
17538
17539 foreach (XAttribute attrib in node.Attributes())
17540 {
17541 if (String.IsNullOrEmpty(attrib.Name.NamespaceName) || CompilerCore.WixNamespace == attrib.Name.Namespace)
17542 {
17543 switch (attrib.Name.LocalName)
17544 {
17545 case "Id":
17546 id = this.core.GetAttributeGuidValue(sourceLineNumbers, attrib, false);
17547 break;
17548 default:
17549 this.core.UnexpectedAttribute(node, attrib);
17550 break;
17551 }
17552 }
17553 else
17554 {
17555 this.core.ParseExtensionAttribute(node, attrib);
17556 }
17557 }
17558
17559 if (null == id)
17560 {
17561 this.core.OnMessage(WixErrors.ExpectedAttribute(sourceLineNumbers, node.Name.LocalName, "Id"));
17562 }
17563
17564 // process the UpgradeVersion children here
17565 foreach (XElement child in node.Elements())
17566 {
17567 if (CompilerCore.WixNamespace == child.Name.Namespace)
17568 {
17569 SourceLineNumber childSourceLineNumbers = Preprocessor.GetSourceLineNumbers(child);
17570
17571 switch (child.Name.LocalName)
17572 {
17573 case "Property":
17574 this.ParsePropertyElement(child);
17575 this.core.OnMessage(WixWarnings.DeprecatedUpgradeProperty(childSourceLineNumbers));
17576 break;
17577 case "UpgradeVersion":
17578 this.ParseUpgradeVersionElement(child, id);
17579 break;
17580 default:
17581 this.core.UnexpectedElement(node, child);
17582 break;
17583 }
17584 }
17585 else
17586 {
17587 this.core.ParseExtensionElement(node, child);
17588 }
17589 }
17590
17591
17592 // No rows created here. All row creation is done in ParseUpgradeVersionElement.
17593 }
17594
17595 /// <summary>
17596 /// Parse upgrade version element.
17597 /// </summary>
17598 /// <param name="node">Element to parse.</param>
17599 /// <param name="upgradeId">Upgrade code.</param>
17600 private void ParseUpgradeVersionElement(XElement node, string upgradeId)
17601 {
17602 SourceLineNumber sourceLineNumbers = Preprocessor.GetSourceLineNumbers(node);
17603
17604 string actionProperty = null;
17605 string language = null;
17606 string maximum = null;
17607 string minimum = null;
17608 int options = 256;
17609 string removeFeatures = null;
17610
17611 foreach (XAttribute attrib in node.Attributes())
17612 {
17613 if (String.IsNullOrEmpty(attrib.Name.NamespaceName) || CompilerCore.WixNamespace == attrib.Name.Namespace)
17614 {
17615 switch (attrib.Name.LocalName)
17616 {
17617 case "ExcludeLanguages":
17618 if (YesNoType.Yes == this.core.GetAttributeYesNoValue(sourceLineNumbers, attrib))
17619 {
17620 options |= MsiInterop.MsidbUpgradeAttributesLanguagesExclusive;
17621 }
17622 break;
17623 case "IgnoreRemoveFailure":
17624 if (YesNoType.Yes == this.core.GetAttributeYesNoValue(sourceLineNumbers, attrib))
17625 {
17626 options |= MsiInterop.MsidbUpgradeAttributesIgnoreRemoveFailure;
17627 }
17628 break;
17629 case "IncludeMaximum":
17630 if (YesNoType.Yes == this.core.GetAttributeYesNoValue(sourceLineNumbers, attrib))
17631 {
17632 options |= MsiInterop.MsidbUpgradeAttributesVersionMaxInclusive;
17633 }
17634 break;
17635 case "IncludeMinimum": // this is "yes" by default
17636 if (YesNoType.No == this.core.GetAttributeYesNoValue(sourceLineNumbers, attrib))
17637 {
17638 options &= ~MsiInterop.MsidbUpgradeAttributesVersionMinInclusive;
17639 }
17640 break;
17641 case "Language":
17642 language = this.core.GetAttributeValue(sourceLineNumbers, attrib);
17643 break;
17644 case "Minimum":
17645 minimum = this.core.GetAttributeVersionValue(sourceLineNumbers, attrib);
17646 break;
17647 case "Maximum":
17648 maximum = this.core.GetAttributeVersionValue(sourceLineNumbers, attrib);
17649 break;
17650 case "MigrateFeatures":
17651 if (YesNoType.Yes == this.core.GetAttributeYesNoValue(sourceLineNumbers, attrib))
17652 {
17653 options |= MsiInterop.MsidbUpgradeAttributesMigrateFeatures;
17654 }
17655 break;
17656 case "OnlyDetect":
17657 if (YesNoType.Yes == this.core.GetAttributeYesNoValue(sourceLineNumbers, attrib))
17658 {
17659 options |= MsiInterop.MsidbUpgradeAttributesOnlyDetect;
17660 }
17661 break;
17662 case "Property":
17663 actionProperty = this.core.GetAttributeIdentifierValue(sourceLineNumbers, attrib);
17664 break;
17665 case "RemoveFeatures":
17666 removeFeatures = this.core.GetAttributeValue(sourceLineNumbers, attrib);
17667 break;
17668 default:
17669 this.core.UnexpectedAttribute(node, attrib);
17670 break;
17671 }
17672 }
17673 else
17674 {
17675 this.core.ParseExtensionAttribute(node, attrib);
17676 }
17677 }
17678
17679 if (null == actionProperty)
17680 {
17681 this.core.OnMessage(WixErrors.ExpectedAttribute(sourceLineNumbers, node.Name.LocalName, "Property"));
17682 }
17683 else if (actionProperty.ToUpper(CultureInfo.InvariantCulture) != actionProperty)
17684 {
17685 this.core.OnMessage(WixErrors.SecurePropertyNotUppercase(sourceLineNumbers, node.Name.LocalName, "Property", actionProperty));
17686 }
17687
17688 if (null == minimum && null == maximum)
17689 {
17690 this.core.OnMessage(WixErrors.ExpectedAttributes(sourceLineNumbers, node.Name.LocalName, "Minimum", "Maximum"));
17691 }
17692
17693 this.core.ParseForExtensionElements(node);
17694
17695 if (!this.core.EncounteredError)
17696 {
17697 Row row = this.core.CreateRow(sourceLineNumbers, "Upgrade");
17698 row[0] = upgradeId;
17699 row[1] = minimum;
17700 row[2] = maximum;
17701 row[3] = language;
17702 row[4] = options;
17703 row[5] = removeFeatures;
17704 row[6] = actionProperty;
17705
17706 // Ensure the action property is secure.
17707 this.AddWixPropertyRow(sourceLineNumbers, new Identifier(actionProperty, AccessModifier.Private), false, true, false);
17708
17709 // Ensure that RemoveExistingProducts is authored in InstallExecuteSequence
17710 // if at least one row in Upgrade table lacks the OnlyDetect attribute.
17711 if (0 == (options & MsiInterop.MsidbUpgradeAttributesOnlyDetect))
17712 {
17713 this.core.CreateSimpleReference(sourceLineNumbers, "WixAction", "InstallExecuteSequence", "RemoveExistingProducts");
17714 }
17715 }
17716 }
17717
17718 /// <summary>
17719 /// Parses a verb element.
17720 /// </summary>
17721 /// <param name="node">Element to parse.</param>
17722 /// <param name="extension">Extension verb is releated to.</param>
17723 /// <param name="progId">Optional progId for extension.</param>
17724 /// <param name="componentId">Identifier for parent component.</param>
17725 /// <param name="advertise">Flag if verb is advertised.</param>
17726 private void ParseVerbElement(XElement node, string extension, string progId, string componentId, YesNoType advertise)
17727 {
17728 SourceLineNumber sourceLineNumbers = Preprocessor.GetSourceLineNumbers(node);
17729 string id = null;
17730 string argument = null;
17731 string command = null;
17732 int sequence = CompilerConstants.IntegerNotSet;
17733 string target = null;
17734 string targetFile = null;
17735 string targetProperty = null;
17736
17737 foreach (XAttribute attrib in node.Attributes())
17738 {
17739 if (String.IsNullOrEmpty(attrib.Name.NamespaceName) || CompilerCore.WixNamespace == attrib.Name.Namespace)
17740 {
17741 switch (attrib.Name.LocalName)
17742 {
17743 case "Id":
17744 id = this.core.GetAttributeValue(sourceLineNumbers, attrib);
17745 break;
17746 case "Argument":
17747 argument = this.core.GetAttributeValue(sourceLineNumbers, attrib);
17748 break;
17749 case "Command":
17750 command = this.core.GetAttributeValue(sourceLineNumbers, attrib);
17751 break;
17752 case "Sequence":
17753 sequence = this.core.GetAttributeIntegerValue(sourceLineNumbers, attrib, 1, short.MaxValue);
17754 break;
17755 case "Target":
17756 target = this.core.GetAttributeValue(sourceLineNumbers, attrib);
17757 this.core.OnMessage(WixWarnings.DeprecatedAttribute(sourceLineNumbers, node.Name.LocalName, attrib.Name.LocalName, "TargetFile", "TargetProperty"));
17758 break;
17759 case "TargetFile":
17760 targetFile = this.core.GetAttributeValue(sourceLineNumbers, attrib);
17761 this.core.CreateSimpleReference(sourceLineNumbers, "File", targetFile);
17762 break;
17763 case "TargetProperty":
17764 targetProperty = this.core.GetAttributeValue(sourceLineNumbers, attrib);
17765 break;
17766 default:
17767 this.core.UnexpectedAttribute(node, attrib);
17768 break;
17769 }
17770 }
17771 else
17772 {
17773 this.core.ParseExtensionAttribute(node, attrib);
17774 }
17775 }
17776
17777 if (null == id)
17778 {
17779 this.core.OnMessage(WixErrors.ExpectedAttribute(sourceLineNumbers, node.Name.LocalName, "Id"));
17780 }
17781
17782 if (null != target && null != targetFile)
17783 {
17784 this.core.OnMessage(WixErrors.IllegalAttributeWithOtherAttribute(sourceLineNumbers, node.Name.LocalName, "Target", "TargetFile"));
17785 }
17786
17787 if (null != target && null != targetProperty)
17788 {
17789 this.core.OnMessage(WixErrors.IllegalAttributeWithOtherAttribute(sourceLineNumbers, node.Name.LocalName, "Target", "TargetProperty"));
17790 }
17791
17792 if (null != targetFile && null != targetProperty)
17793 {
17794 this.core.OnMessage(WixErrors.IllegalAttributeWithOtherAttribute(sourceLineNumbers, node.Name.LocalName, "TargetFile", "TargetProperty"));
17795 }
17796
17797 this.core.ParseForExtensionElements(node);
17798
17799 if (YesNoType.Yes == advertise)
17800 {
17801 if (null != target)
17802 {
17803 this.core.OnMessage(WixErrors.IllegalAttributeWhenAdvertised(sourceLineNumbers, node.Name.LocalName, "Target"));
17804 }
17805
17806 if (null != targetFile)
17807 {
17808 this.core.OnMessage(WixErrors.IllegalAttributeWhenAdvertised(sourceLineNumbers, node.Name.LocalName, "TargetFile"));
17809 }
17810
17811 if (null != targetProperty)
17812 {
17813 this.core.OnMessage(WixErrors.IllegalAttributeWhenAdvertised(sourceLineNumbers, node.Name.LocalName, "TargetProperty"));
17814 }
17815
17816 if (!this.core.EncounteredError)
17817 {
17818 Row row = this.core.CreateRow(sourceLineNumbers, "Verb");
17819 row[0] = extension;
17820 row[1] = id;
17821 if (CompilerConstants.IntegerNotSet != sequence)
17822 {
17823 row[2] = sequence;
17824 }
17825 row[3] = command;
17826 row[4] = argument;
17827 }
17828 }
17829 else if (YesNoType.No == advertise)
17830 {
17831 if (CompilerConstants.IntegerNotSet != sequence)
17832 {
17833 this.core.OnMessage(WixErrors.IllegalAttributeWithOtherAttribute(sourceLineNumbers, node.Name.LocalName, "Sequence", "Advertise", "no"));
17834 }
17835
17836 if (null == target && null == targetFile && null == targetProperty)
17837 {
17838 this.core.OnMessage(WixErrors.ExpectedAttributesWithOtherAttribute(sourceLineNumbers, node.Name.LocalName, "TargetFile", "TargetProperty", "Advertise", "no"));
17839 }
17840
17841 if (null == target)
17842 {
17843 if (null != targetFile)
17844 {
17845 target = String.Concat("\"[#", targetFile, "]\"");
17846 }
17847
17848 if (null != targetProperty)
17849 {
17850 target = String.Concat("\"[", targetProperty, "]\"");
17851 }
17852 }
17853
17854 if (null != argument)
17855 {
17856 target = String.Concat(target, " ", argument);
17857 }
17858
17859 string prefix = (null != progId ? progId : String.Concat(".", extension));
17860
17861 if (null != command)
17862 {
17863 this.core.CreateRegistryRow(sourceLineNumbers, MsiInterop.MsidbRegistryRootClassesRoot, String.Concat(prefix, "\\shell\\", id), String.Empty, command, componentId);
17864 }
17865
17866 this.core.CreateRegistryRow(sourceLineNumbers, MsiInterop.MsidbRegistryRootClassesRoot, String.Concat(prefix, "\\shell\\", id, "\\command"), String.Empty, target, componentId);
17867 }
17868 }
17869
17870
17871 /// <summary>
17872 /// Parses an ApprovedExeForElevation element.
17873 /// </summary>
17874 /// <param name="node">Element to parse</param>
17875 private void ParseApprovedExeForElevation(XElement node)
17876 {
17877 SourceLineNumber sourceLineNumbers = Preprocessor.GetSourceLineNumbers(node);
17878 Identifier id = null;
17879 string key = null;
17880 string valueName = null;
17881 YesNoType win64 = YesNoType.NotSet;
17882
17883 foreach (XAttribute attrib in node.Attributes())
17884 {
17885 if (String.IsNullOrEmpty(attrib.Name.NamespaceName) || CompilerCore.WixNamespace == attrib.Name.Namespace)
17886 {
17887 switch (attrib.Name.LocalName)
17888 {
17889 case "Id":
17890 id = this.core.GetAttributeIdentifier(sourceLineNumbers, attrib);
17891 break;
17892 case "Key":
17893 key = this.core.GetAttributeValue(sourceLineNumbers, attrib);
17894 break;
17895 case "Value":
17896 valueName = this.core.GetAttributeValue(sourceLineNumbers, attrib);
17897 break;
17898 case "Win64":
17899 win64 = this.core.GetAttributeYesNoValue(sourceLineNumbers, attrib);
17900 break;
17901 default:
17902 this.core.UnexpectedAttribute(node, attrib);
17903 break;
17904 }
17905 }
17906 else
17907 {
17908 this.core.ParseExtensionAttribute(node, attrib);
17909 }
17910 }
17911
17912 if (null == id)
17913 {
17914 this.core.OnMessage(WixErrors.ExpectedAttribute(sourceLineNumbers, node.Name.LocalName, "Id"));
17915 }
17916
17917 if (null == key)
17918 {
17919 this.core.OnMessage(WixErrors.ExpectedAttribute(sourceLineNumbers, node.Name.LocalName, "Key"));
17920 }
17921
17922 BundleApprovedExeForElevationAttributes attributes = BundleApprovedExeForElevationAttributes.None;
17923
17924 if (win64 == YesNoType.Yes)
17925 {
17926 attributes |= BundleApprovedExeForElevationAttributes.Win64;
17927 }
17928
17929 this.core.ParseForExtensionElements(node);
17930
17931 if (!this.core.EncounteredError)
17932 {
17933 WixApprovedExeForElevationRow wixApprovedExeForElevationRow = (WixApprovedExeForElevationRow)this.core.CreateRow(sourceLineNumbers, "WixApprovedExeForElevation", id);
17934 wixApprovedExeForElevationRow.Key = key;
17935 wixApprovedExeForElevationRow.ValueName = valueName;
17936 wixApprovedExeForElevationRow.Attributes = attributes;
17937 }
17938 }
17939
17940 /// <summary>
17941 /// Parses a Bundle element.
17942 /// </summary>
17943 /// <param name="node">Element to parse</param>
17944 private void ParseBundleElement(XElement node)
17945 {
17946 SourceLineNumber sourceLineNumbers = Preprocessor.GetSourceLineNumbers(node);
17947 string copyright = null;
17948 string aboutUrl = null;
17949 YesNoDefaultType compressed = YesNoDefaultType.Default;
17950 int disableModify = -1;
17951 YesNoType disableRemove = YesNoType.NotSet;
17952 string helpTelephone = null;
17953 string helpUrl = null;
17954 string manufacturer = null;
17955 string name = null;
17956 string tag = null;
17957 string updateUrl = null;
17958 string upgradeCode = null;
17959 string version = null;
17960 string condition = null;
17961 string parentName = null;
17962
17963 string fileSystemSafeBundleName = null;
17964 string logVariablePrefixAndExtension = null;
17965 string iconSourceFile = null;
17966 string splashScreenSourceFile = null;
17967
17968 // Process only standard attributes until the active section is initialized.
17969 foreach (XAttribute attrib in node.Attributes())
17970 {
17971 if (String.IsNullOrEmpty(attrib.Name.NamespaceName) || CompilerCore.WixNamespace == attrib.Name.Namespace)
17972 {
17973 switch (attrib.Name.LocalName)
17974 {
17975 case "AboutUrl":
17976 aboutUrl = this.core.GetAttributeValue(sourceLineNumbers, attrib);
17977 break;
17978 case "Compressed":
17979 compressed = this.core.GetAttributeYesNoDefaultValue(sourceLineNumbers, attrib);
17980 break;
17981 case "Condition":
17982 condition = this.core.GetAttributeValue(sourceLineNumbers, attrib);
17983 break;
17984 case "Copyright":
17985 copyright = this.core.GetAttributeValue(sourceLineNumbers, attrib);
17986 break;
17987 case "DisableModify":
17988 string value = this.core.GetAttributeValue(sourceLineNumbers, attrib);
17989 switch (value)
17990 {
17991 case "button":
17992 disableModify = 2;
17993 break;
17994 case "yes":
17995 disableModify = 1;
17996 break;
17997 case "no":
17998 disableModify = 0;
17999 break;
18000 default:
18001 this.core.OnMessage(WixErrors.IllegalAttributeValue(sourceLineNumbers, node.Name.LocalName, attrib.Name.LocalName, value, "button", "yes", "no"));
18002 break;
18003 }
18004 break;
18005 case "DisableRemove":
18006 disableRemove = this.core.GetAttributeYesNoValue(sourceLineNumbers, attrib);
18007 break;
18008 case "DisableRepair":
18009 this.core.OnMessage(WixWarnings.DeprecatedAttribute(sourceLineNumbers, node.Name.LocalName, attrib.Name.LocalName));
18010 break;
18011 case "HelpTelephone":
18012 helpTelephone = this.core.GetAttributeValue(sourceLineNumbers, attrib);
18013 break;
18014 case "HelpUrl":
18015 helpUrl = this.core.GetAttributeValue(sourceLineNumbers, attrib);
18016 break;
18017 case "Manufacturer":
18018 manufacturer = this.core.GetAttributeValue(sourceLineNumbers, attrib);
18019 break;
18020 case "IconSourceFile":
18021 iconSourceFile = this.core.GetAttributeValue(sourceLineNumbers, attrib);
18022 break;
18023 case "Name":
18024 name = this.core.GetAttributeValue(sourceLineNumbers, attrib);
18025 break;
18026 case "ParentName":
18027 parentName = this.core.GetAttributeValue(sourceLineNumbers, attrib);
18028 break;
18029 case "SplashScreenSourceFile":
18030 splashScreenSourceFile = this.core.GetAttributeValue(sourceLineNumbers, attrib);
18031 break;
18032 case "Tag":
18033 tag = this.core.GetAttributeValue(sourceLineNumbers, attrib);
18034 break;
18035 case "UpdateUrl":
18036 updateUrl = this.core.GetAttributeValue(sourceLineNumbers, attrib);
18037 break;
18038 case "UpgradeCode":
18039 upgradeCode = this.core.GetAttributeGuidValue(sourceLineNumbers, attrib, false);
18040 break;
18041 case "Version":
18042 version = this.core.GetAttributeVersionValue(sourceLineNumbers, attrib);
18043 break;
18044 default:
18045 this.core.UnexpectedAttribute(node, attrib);
18046 break;
18047 }
18048 }
18049 }
18050
18051 if (String.IsNullOrEmpty(version))
18052 {
18053 this.core.OnMessage(WixErrors.ExpectedAttribute(sourceLineNumbers, node.Name.LocalName, "Version"));
18054 }
18055 else if (!CompilerCore.IsValidModuleOrBundleVersion(version))
18056 {
18057 this.core.OnMessage(WixWarnings.InvalidModuleOrBundleVersion(sourceLineNumbers, "Bundle", version));
18058 }
18059
18060 if (String.IsNullOrEmpty(upgradeCode))
18061 {
18062 this.core.OnMessage(WixErrors.ExpectedAttribute(sourceLineNumbers, node.Name.LocalName, "UpgradeCode"));
18063 }
18064
18065 if (String.IsNullOrEmpty(copyright))
18066 {
18067 if (String.IsNullOrEmpty(manufacturer))
18068 {
18069 copyright = "Copyright (c). All rights reserved.";
18070 }
18071 else
18072 {
18073 copyright = String.Format("Copyright (c) {0}. All rights reserved.", manufacturer);
18074 }
18075 }
18076
18077 if (String.IsNullOrEmpty(name))
18078 {
18079 logVariablePrefixAndExtension = String.Concat("WixBundleLog:Setup.log");
18080 }
18081 else
18082 {
18083 // Ensure only allowable path characters are in "name" (and change spaces to underscores).
18084 fileSystemSafeBundleName = CompilerCore.MakeValidLongFileName(name.Replace(' ', '_'), "_");
18085 logVariablePrefixAndExtension = String.Concat("WixBundleLog:", fileSystemSafeBundleName, ".log");
18086 }
18087
18088 this.activeName = String.IsNullOrEmpty(name) ? Common.GenerateGuid() : name;
18089 this.core.CreateActiveSection(this.activeName, SectionType.Bundle, 0);
18090
18091 // Now that the active section is initialized, process only extension attributes.
18092 foreach (XAttribute attrib in node.Attributes())
18093 {
18094 if (!String.IsNullOrEmpty(attrib.Name.NamespaceName) && CompilerCore.WixNamespace != attrib.Name.Namespace)
18095 {
18096 this.core.ParseExtensionAttribute(node, attrib);
18097 }
18098 }
18099
18100 bool baSeen = false;
18101 bool chainSeen = false;
18102 bool logSeen = false;
18103
18104 foreach (XElement child in node.Elements())
18105 {
18106 if (CompilerCore.WixNamespace == child.Name.Namespace)
18107 {
18108 switch (child.Name.LocalName)
18109 {
18110 case "ApprovedExeForElevation":
18111 this.ParseApprovedExeForElevation(child);
18112 break;
18113 case "BootstrapperApplication":
18114 if (baSeen)
18115 {
18116 SourceLineNumber childSourceLineNumbers = Preprocessor.GetSourceLineNumbers(child);
18117 this.core.OnMessage(WixErrors.TooManyChildren(childSourceLineNumbers, node.Name.LocalName, "BootstrapperApplication"));
18118 }
18119 this.ParseBootstrapperApplicationElement(child);
18120 baSeen = true;
18121 break;
18122 case "BootstrapperApplicationRef":
18123 this.ParseBootstrapperApplicationRefElement(child);
18124 break;
18125 case "OptionalUpdateRegistration":
18126 this.ParseOptionalUpdateRegistrationElement(child, manufacturer, parentName, name);
18127 break;
18128 case "Catalog":
18129 this.ParseCatalogElement(child);
18130 break;
18131 case "Chain":
18132 if (chainSeen)
18133 {
18134 SourceLineNumber childSourceLineNumbers = Preprocessor.GetSourceLineNumbers(child);
18135 this.core.OnMessage(WixErrors.TooManyChildren(childSourceLineNumbers, node.Name.LocalName, "Chain"));
18136 }
18137 this.ParseChainElement(child);
18138 chainSeen = true;
18139 break;
18140 case "Container":
18141 this.ParseContainerElement(child);
18142 break;
18143 case "ContainerRef":
18144 this.ParseSimpleRefElement(child, "WixBundleContainer");
18145 break;
18146 case "Log":
18147 if (logSeen)
18148 {
18149 SourceLineNumber childSourceLineNumbers = Preprocessor.GetSourceLineNumbers(child);
18150 this.core.OnMessage(WixErrors.TooManyChildren(childSourceLineNumbers, node.Name.LocalName, "Log"));
18151 }
18152 logVariablePrefixAndExtension = this.ParseLogElement(child, fileSystemSafeBundleName);
18153 logSeen = true;
18154 break;
18155 case "PayloadGroup":
18156 this.ParsePayloadGroupElement(child, ComplexReferenceParentType.Layout, "BundleLayoutOnlyPayloads");
18157 break;
18158 case "PayloadGroupRef":
18159 this.ParsePayloadGroupRefElement(child, ComplexReferenceParentType.Layout, "BundleLayoutOnlyPayloads", ComplexReferenceChildType.Unknown, null);
18160 break;
18161 case "RelatedBundle":
18162 this.ParseRelatedBundleElement(child);
18163 break;
18164 case "Update":
18165 this.ParseUpdateElement(child);
18166 break;
18167 case "Variable":
18168 this.ParseVariableElement(child);
18169 break;
18170 case "WixVariable":
18171 this.ParseWixVariableElement(child);
18172 break;
18173 default:
18174 this.core.UnexpectedElement(node, child);
18175 break;
18176 }
18177 }
18178 else
18179 {
18180 this.core.ParseExtensionElement(node, child);
18181 }
18182 }
18183
18184
18185 if (!chainSeen)
18186 {
18187 this.core.OnMessage(WixErrors.ExpectedElement(sourceLineNumbers, node.Name.LocalName, "Chain"));
18188 }
18189
18190 if (!this.core.EncounteredError)
18191 {
18192 if (null != upgradeCode)
18193 {
18194 Row relatedBundleRow = this.core.CreateRow(sourceLineNumbers, "WixRelatedBundle");
18195 relatedBundleRow[0] = upgradeCode;
18196 relatedBundleRow[1] = (int)Wix.RelatedBundle.ActionType.Upgrade;
18197 }
18198
18199 WixBundleContainerRow containerRow = (WixBundleContainerRow)this.core.CreateRow(sourceLineNumbers, "WixBundleContainer");
18200 containerRow.Id = Compiler.BurnDefaultAttachedContainerId;
18201 containerRow.Name = "bundle-attached.cab";
18202 containerRow.Type = ContainerType.Attached;
18203
18204 Row row = this.core.CreateRow(sourceLineNumbers, "WixBundle");
18205 row[0] = version;
18206 row[1] = copyright;
18207 row[2] = name;
18208 row[3] = aboutUrl;
18209 if (-1 != disableModify)
18210 {
18211 row[4] = disableModify;
18212 }
18213 if (YesNoType.NotSet != disableRemove)
18214 {
18215 row[5] = (YesNoType.Yes == disableRemove) ? 1 : 0;
18216 }
18217 // row[6] - (deprecated) "disable repair"
18218 row[7] = helpTelephone;
18219 row[8] = helpUrl;
18220 row[9] = manufacturer;
18221 row[10] = updateUrl;
18222 if (YesNoDefaultType.Default != compressed)
18223 {
18224 row[11] = (YesNoDefaultType.Yes == compressed) ? 1 : 0;
18225 }
18226
18227 row[12] = logVariablePrefixAndExtension;
18228 row[13] = iconSourceFile;
18229 row[14] = splashScreenSourceFile;
18230 row[15] = condition;
18231 row[16] = tag;
18232 row[17] = this.CurrentPlatform.ToString();
18233 row[18] = parentName;
18234 row[19] = upgradeCode;
18235
18236 // Ensure that the bundle stores the well-known persisted values.
18237 WixBundleVariableRow bundleNameWellKnownVariable = (WixBundleVariableRow)this.core.CreateRow(sourceLineNumbers, "WixBundleVariable");
18238 bundleNameWellKnownVariable.Id = Compiler.BURN_BUNDLE_NAME;
18239 bundleNameWellKnownVariable.Hidden = false;
18240 bundleNameWellKnownVariable.Persisted = true;
18241
18242 WixBundleVariableRow bundleOriginalSourceWellKnownVariable = (WixBundleVariableRow)this.core.CreateRow(sourceLineNumbers, "WixBundleVariable");
18243 bundleOriginalSourceWellKnownVariable.Id = Compiler.BURN_BUNDLE_ORIGINAL_SOURCE;
18244 bundleOriginalSourceWellKnownVariable.Hidden = false;
18245 bundleOriginalSourceWellKnownVariable.Persisted = true;
18246
18247 WixBundleVariableRow bundleOriginalSourceFolderWellKnownVariable = (WixBundleVariableRow)this.core.CreateRow(sourceLineNumbers, "WixBundleVariable");
18248 bundleOriginalSourceFolderWellKnownVariable.Id = Compiler.BURN_BUNDLE_ORIGINAL_SOURCE_FOLDER;
18249 bundleOriginalSourceFolderWellKnownVariable.Hidden = false;
18250 bundleOriginalSourceFolderWellKnownVariable.Persisted = true;
18251
18252 WixBundleVariableRow bundleLastUsedSourceWellKnownVariable = (WixBundleVariableRow)this.core.CreateRow(sourceLineNumbers, "WixBundleVariable");
18253 bundleLastUsedSourceWellKnownVariable.Id = Compiler.BURN_BUNDLE_LAST_USED_SOURCE;
18254 bundleLastUsedSourceWellKnownVariable.Hidden = false;
18255 bundleLastUsedSourceWellKnownVariable.Persisted = true;
18256 }
18257 }
18258
18259 /// <summary>
18260 /// Parse a Container element.
18261 /// </summary>
18262 /// <param name="node">Element to parse</param>
18263 private string ParseLogElement(XElement node, string fileSystemSafeBundleName)
18264 {
18265 SourceLineNumber sourceLineNumbers = Preprocessor.GetSourceLineNumbers(node);
18266 YesNoType disableLog = YesNoType.NotSet;
18267 string variable = "WixBundleLog";
18268 string logPrefix = fileSystemSafeBundleName ?? "Setup";
18269 string logExtension = ".log";
18270
18271 foreach (XAttribute attrib in node.Attributes())
18272 {
18273 if (String.IsNullOrEmpty(attrib.Name.NamespaceName) || CompilerCore.WixNamespace == attrib.Name.Namespace)
18274 {
18275 switch (attrib.Name.LocalName)
18276 {
18277 case "Disable":
18278 disableLog = this.core.GetAttributeYesNoValue(sourceLineNumbers, attrib);
18279 break;
18280 case "PathVariable":
18281 variable = this.core.GetAttributeValue(sourceLineNumbers, attrib, EmptyRule.CanBeEmpty);
18282 break;
18283 case "Prefix":
18284 logPrefix = this.core.GetAttributeValue(sourceLineNumbers, attrib);
18285 break;
18286 case "Extension":
18287 logExtension = this.core.GetAttributeValue(sourceLineNumbers, attrib);
18288 break;
18289 default:
18290 this.core.UnexpectedAttribute(node, attrib);
18291 break;
18292 }
18293 }
18294 else
18295 {
18296 this.core.ParseExtensionAttribute(node, attrib);
18297 }
18298 }
18299
18300 if (!logExtension.StartsWith(".", StringComparison.Ordinal))
18301 {
18302 logExtension = String.Concat(".", logExtension);
18303 }
18304
18305 this.core.ParseForExtensionElements(node);
18306
18307 return YesNoType.Yes == disableLog ? null : String.Concat(variable, ":", logPrefix, logExtension);
18308 }
18309
18310 /// <summary>
18311 /// Parse a Catalog element.
18312 /// </summary>
18313 /// <param name="node">Element to parse</param>
18314 private void ParseCatalogElement(XElement node)
18315 {
18316 SourceLineNumber sourceLineNumbers = Preprocessor.GetSourceLineNumbers(node);
18317 Identifier id = null;
18318 string sourceFile = null;
18319
18320 foreach (XAttribute attrib in node.Attributes())
18321 {
18322 if (String.IsNullOrEmpty(attrib.Name.NamespaceName) || CompilerCore.WixNamespace == attrib.Name.Namespace)
18323 {
18324 switch (attrib.Name.LocalName)
18325 {
18326 case "Id":
18327 id = this.core.GetAttributeIdentifier(sourceLineNumbers, attrib);
18328 break;
18329 case "SourceFile":
18330 sourceFile = this.core.GetAttributeValue(sourceLineNumbers, attrib);
18331 break;
18332 default:
18333 this.core.UnexpectedAttribute(node, attrib);
18334 break;
18335 }
18336 }
18337 }
18338
18339 if (null == id)
18340 {
18341 this.core.OnMessage(WixErrors.ExpectedAttribute(sourceLineNumbers, node.Name.LocalName, "Id"));
18342 }
18343
18344 if (null == sourceFile)
18345 {
18346 this.core.OnMessage(WixErrors.ExpectedAttribute(sourceLineNumbers, node.Name.LocalName, "SourceFile"));
18347 }
18348
18349 this.core.ParseForExtensionElements(node);
18350
18351 // Create catalog row
18352 if (!this.core.EncounteredError)
18353 {
18354 this.CreatePayloadRow(sourceLineNumbers, id, Path.GetFileName(sourceFile), sourceFile, null, ComplexReferenceParentType.Container, Compiler.BurnUXContainerId, ComplexReferenceChildType.Unknown, null, YesNoDefaultType.Yes, YesNoType.Yes, null, null, null);
18355
18356 WixBundleCatalogRow wixCatalogRow = (WixBundleCatalogRow)this.core.CreateRow(sourceLineNumbers, "WixBundleCatalog", id);
18357 wixCatalogRow.Payload = id.Id;
18358 }
18359 }
18360
18361 /// <summary>
18362 /// Parse a Container element.
18363 /// </summary>
18364 /// <param name="node">Element to parse</param>
18365 private void ParseContainerElement(XElement node)
18366 {
18367 SourceLineNumber sourceLineNumbers = Preprocessor.GetSourceLineNumbers(node);
18368 Identifier id = null;
18369 string downloadUrl = null;
18370 string name = null;
18371 ContainerType type = ContainerType.Detached;
18372
18373 foreach (XAttribute attrib in node.Attributes())
18374 {
18375 if (String.IsNullOrEmpty(attrib.Name.NamespaceName) || CompilerCore.WixNamespace == attrib.Name.Namespace)
18376 {
18377 switch (attrib.Name.LocalName)
18378 {
18379 case "Id":
18380 id = this.core.GetAttributeIdentifier(sourceLineNumbers, attrib);
18381 break;
18382 case "DownloadUrl":
18383 downloadUrl = this.core.GetAttributeValue(sourceLineNumbers, attrib);
18384 break;
18385 case "Name":
18386 name = this.core.GetAttributeValue(sourceLineNumbers, attrib);
18387 break;
18388 case "Type":
18389 string typeString = this.core.GetAttributeValue(sourceLineNumbers, attrib);
18390 if (!Enum.TryParse<ContainerType>(typeString, out type))
18391 {
18392 this.core.OnMessage(WixErrors.IllegalAttributeValueWithLegalList(sourceLineNumbers, node.Name.LocalName, "Type", typeString, "attached, detached"));
18393 }
18394 break;
18395 default:
18396 this.core.UnexpectedAttribute(node, attrib);
18397 break;
18398 }
18399 }
18400 else
18401 {
18402 this.core.ParseExtensionAttribute(node, attrib);
18403 }
18404 }
18405
18406 if (null == id)
18407 {
18408 if (!String.IsNullOrEmpty(name))
18409 {
18410 id = this.core.CreateIdentifierFromFilename(name);
18411 }
18412
18413 if (null == id)
18414 {
18415 this.core.OnMessage(WixErrors.ExpectedAttribute(sourceLineNumbers, node.Name.LocalName, "Id"));
18416 id = Identifier.Invalid;
18417 }
18418 else if (!Common.IsIdentifier(id.Id))
18419 {
18420 this.core.OnMessage(WixErrors.IllegalIdentifier(sourceLineNumbers, node.Name.LocalName, "Id", id.Id));
18421 }
18422 }
18423 else if (null == name)
18424 {
18425 name = id.Id;
18426 }
18427
18428 if (!String.IsNullOrEmpty(downloadUrl) && ContainerType.Detached != type)
18429 {
18430 this.core.OnMessage(WixErrors.IllegalAttributeWithOtherAttribute(sourceLineNumbers, node.Name.LocalName, "DownloadUrl", "Type", "attached"));
18431 }
18432
18433 foreach (XElement child in node.Elements())
18434 {
18435 if (CompilerCore.WixNamespace == child.Name.Namespace)
18436 {
18437 switch (child.Name.LocalName)
18438 {
18439 case "PackageGroupRef":
18440 this.ParsePackageGroupRefElement(child, ComplexReferenceParentType.Container, id.Id);
18441 break;
18442 default:
18443 this.core.UnexpectedElement(node, child);
18444 break;
18445 }
18446 }
18447 else
18448 {
18449 this.core.ParseExtensionElement(node, child);
18450 }
18451 }
18452
18453
18454 if (!this.core.EncounteredError)
18455 {
18456 WixBundleContainerRow row = (WixBundleContainerRow)this.core.CreateRow(sourceLineNumbers, "WixBundleContainer", id);
18457 row.Name = name;
18458 row.Type = type;
18459 row.DownloadUrl = downloadUrl;
18460 }
18461 }
18462
18463 /// <summary>
18464 /// Parse the BoostrapperApplication element.
18465 /// </summary>
18466 /// <param name="node">Element to parse</param>
18467 private void ParseBootstrapperApplicationElement(XElement node)
18468 {
18469 SourceLineNumber sourceLineNumbers = Preprocessor.GetSourceLineNumbers(node);
18470 string id = null;
18471 string previousId = null;
18472 ComplexReferenceChildType previousType = ComplexReferenceChildType.Unknown;
18473
18474 // The BootstrapperApplication element acts like a Payload element so delegate to the "Payload" attribute parsing code to parse and create a Payload entry.
18475 id = this.ParsePayloadElementContent(node, ComplexReferenceParentType.Container, Compiler.BurnUXContainerId, previousType, previousId, false);
18476 if (null != id)
18477 {
18478 previousId = id;
18479 previousType = ComplexReferenceChildType.Payload;
18480 }
18481
18482 foreach (XElement child in node.Elements())
18483 {
18484 if (CompilerCore.WixNamespace == child.Name.Namespace)
18485 {
18486 switch (child.Name.LocalName)
18487 {
18488 case "Payload":
18489 previousId = this.ParsePayloadElement(child, ComplexReferenceParentType.Container, Compiler.BurnUXContainerId, previousType, previousId);
18490 previousType = ComplexReferenceChildType.Payload;
18491 break;
18492 case "PayloadGroupRef":
18493 previousId = this.ParsePayloadGroupRefElement(child, ComplexReferenceParentType.Container, Compiler.BurnUXContainerId, previousType, previousId);
18494 previousType = ComplexReferenceChildType.PayloadGroup;
18495 break;
18496 default:
18497 this.core.UnexpectedElement(node, child);
18498 break;
18499 }
18500 }
18501 else
18502 {
18503 this.core.ParseExtensionElement(node, child);
18504 }
18505 }
18506
18507 if (null == previousId)
18508 {
18509 // We need *either* <Payload> or <PayloadGroupRef> or even just @SourceFile on the BA...
18510 // but we just say there's a missing <Payload>.
18511 // TODO: Is there a better message for this?
18512 this.core.OnMessage(WixErrors.ExpectedElement(sourceLineNumbers, node.Name.LocalName, "Payload"));
18513 }
18514
18515 // Add the application as an attached container and if an Id was provided add that too.
18516 if (!this.core.EncounteredError)
18517 {
18518 WixBundleContainerRow containerRow = (WixBundleContainerRow)this.core.CreateRow(sourceLineNumbers, "WixBundleContainer");
18519 containerRow.Id = Compiler.BurnUXContainerId;
18520 containerRow.Name = "bundle-ux.cab";
18521 containerRow.Type = ContainerType.Attached;
18522
18523 if (!String.IsNullOrEmpty(id))
18524 {
18525 Row row = this.core.CreateRow(sourceLineNumbers, "WixBootstrapperApplication");
18526 row[0] = id;
18527 }
18528 }
18529 }
18530
18531 /// <summary>
18532 /// Parse the BoostrapperApplicationRef element.
18533 /// </summary>
18534 /// <param name="node">Element to parse</param>
18535 private void ParseBootstrapperApplicationRefElement(XElement node)
18536 {
18537 SourceLineNumber sourceLineNumbers = Preprocessor.GetSourceLineNumbers(node);
18538 string id = null;
18539 string previousId = null;
18540 ComplexReferenceChildType previousType = ComplexReferenceChildType.Unknown;
18541
18542 foreach (XAttribute attrib in node.Attributes())
18543 {
18544 if (String.IsNullOrEmpty(attrib.Name.NamespaceName) || CompilerCore.WixNamespace == attrib.Name.Namespace)
18545 {
18546 switch (attrib.Name.LocalName)
18547 {
18548 case "Id":
18549 id = this.core.GetAttributeIdentifierValue(sourceLineNumbers, attrib);
18550 break;
18551 default:
18552 this.core.UnexpectedAttribute(node, attrib);
18553 break;
18554 }
18555 }
18556 else
18557 {
18558 this.core.ParseExtensionAttribute(node, attrib);
18559 }
18560 }
18561
18562 foreach (XElement child in node.Elements())
18563 {
18564 if (CompilerCore.WixNamespace == child.Name.Namespace)
18565 {
18566 switch (child.Name.LocalName)
18567 {
18568 case "Payload":
18569 previousId = this.ParsePayloadElement(child, ComplexReferenceParentType.Container, Compiler.BurnUXContainerId, previousType, previousId);
18570 previousType = ComplexReferenceChildType.Payload;
18571 break;
18572 case "PayloadGroupRef":
18573 previousId = this.ParsePayloadGroupRefElement(child, ComplexReferenceParentType.Container, Compiler.BurnUXContainerId, previousType, previousId);
18574 previousType = ComplexReferenceChildType.PayloadGroup;
18575 break;
18576 default:
18577 this.core.UnexpectedElement(node, child);
18578 break;
18579 }
18580 }
18581 else
18582 {
18583 this.core.ParseExtensionElement(node, child);
18584 }
18585 }
18586
18587
18588 if (String.IsNullOrEmpty(id))
18589 {
18590 this.core.OnMessage(WixErrors.ExpectedAttribute(sourceLineNumbers, node.Name.LocalName, "Id"));
18591 }
18592 else
18593 {
18594 this.core.CreateSimpleReference(sourceLineNumbers, "WixBootstrapperApplication", id);
18595 }
18596 }
18597
18598 /// <summary>
18599 /// Parse the OptionalUpdateRegistration element.
18600 /// </summary>
18601 /// <param name="node">The element to parse.</param>
18602 /// <param name="defaultManufacturer">The manufacturer.</param>
18603 /// <param name="defaultProductFamily">The product family.</param>
18604 /// <param name="defaultName">The bundle name.</param>
18605 private void ParseOptionalUpdateRegistrationElement(XElement node, string defaultManufacturer, string defaultProductFamily, string defaultName)
18606 {
18607 const string defaultClassification = "Update";
18608
18609 SourceLineNumber sourceLineNumbers = Preprocessor.GetSourceLineNumbers(node);
18610 string manufacturer = null;
18611 string department = null;
18612 string productFamily = null;
18613 string name = null;
18614 string classification = defaultClassification;
18615
18616 foreach (XAttribute attrib in node.Attributes())
18617 {
18618 if (String.IsNullOrEmpty(attrib.Name.NamespaceName) || CompilerCore.WixNamespace == attrib.Name.Namespace)
18619 {
18620 switch (attrib.Name.LocalName)
18621 {
18622 case "Manufacturer":
18623 manufacturer = this.core.GetAttributeValue(sourceLineNumbers, attrib);
18624 break;
18625 case "Department":
18626 department = this.core.GetAttributeValue(sourceLineNumbers, attrib);
18627 break;
18628 case "ProductFamily":
18629 productFamily = this.core.GetAttributeValue(sourceLineNumbers, attrib);
18630 break;
18631 case "Name":
18632 name = this.core.GetAttributeValue(sourceLineNumbers, attrib);
18633 break;
18634 case "Classification":
18635 classification = this.core.GetAttributeValue(sourceLineNumbers, attrib);
18636 break;
18637 default:
18638 this.core.UnexpectedAttribute(node, attrib);
18639 break;
18640 }
18641 }
18642 else
18643 {
18644 this.core.ParseExtensionAttribute(node, attrib);
18645 }
18646 }
18647
18648 if (String.IsNullOrEmpty(manufacturer))
18649 {
18650 if (!String.IsNullOrEmpty(defaultManufacturer))
18651 {
18652 manufacturer = defaultManufacturer;
18653 }
18654 else
18655 {
18656 this.core.OnMessage(WixErrors.ExpectedAttributeInElementOrParent(sourceLineNumbers, node.Name.LocalName, "Manufacturer", node.Parent.Name.LocalName));
18657 }
18658 }
18659
18660 if (String.IsNullOrEmpty(productFamily))
18661 {
18662 if (!String.IsNullOrEmpty(defaultProductFamily))
18663 {
18664 productFamily = defaultProductFamily;
18665 }
18666 }
18667
18668 if (String.IsNullOrEmpty(name))
18669 {
18670 if (!String.IsNullOrEmpty(defaultName))
18671 {
18672 name = defaultName;
18673 }
18674 else
18675 {
18676 this.core.OnMessage(WixErrors.ExpectedAttributeInElementOrParent(sourceLineNumbers, node.Name.LocalName, "Name", node.Parent.Name.LocalName));
18677 }
18678 }
18679
18680 if (String.IsNullOrEmpty(classification))
18681 {
18682 this.core.OnMessage(WixErrors.IllegalEmptyAttributeValue(sourceLineNumbers, node.Name.LocalName, "Classification", defaultClassification));
18683 }
18684
18685 this.core.ParseForExtensionElements(node);
18686
18687 if (!this.core.EncounteredError)
18688 {
18689 Row row = this.core.CreateRow(sourceLineNumbers, "WixUpdateRegistration");
18690 row[0] = manufacturer;
18691 row[1] = department;
18692 row[2] = productFamily;
18693 row[3] = name;
18694 row[4] = classification;
18695 }
18696 }
18697
18698 /// <summary>
18699 /// Parse Payload element.
18700 /// </summary>
18701 /// <param name="node">Element to parse</param>
18702 /// <param name="parentType">ComplexReferenceParentType of parent element. (BA or PayloadGroup)</param>
18703 /// <param name="parentId">Identifier of parent element.</param>
18704 private string ParsePayloadElement(XElement node, ComplexReferenceParentType parentType, string parentId, ComplexReferenceChildType previousType, string previousId)
18705 {
18706 Debug.Assert(ComplexReferenceParentType.PayloadGroup == parentType || ComplexReferenceParentType.Package == parentType || ComplexReferenceParentType.Container == parentType);
18707 Debug.Assert(ComplexReferenceChildType.Unknown == previousType || ComplexReferenceChildType.PayloadGroup == previousType || ComplexReferenceChildType.Payload == previousType);
18708
18709 string id = ParsePayloadElementContent(node, parentType, parentId, previousType, previousId, true);
18710 Dictionary<string, string> context = new Dictionary<string, string>();
18711 context["Id"] = id;
18712
18713 foreach (XElement child in node.Elements())
18714 {
18715 if (CompilerCore.WixNamespace == child.Name.Namespace)
18716 {
18717 switch (child.Name.LocalName)
18718 {
18719 default:
18720 this.core.UnexpectedElement(node, child);
18721 break;
18722 }
18723 }
18724 else
18725 {
18726 this.core.ParseExtensionElement(node, child, context);
18727 }
18728 }
18729
18730 return id;
18731 }
18732
18733 /// <summary>
18734 /// Parse the attributes of the Payload element.
18735 /// </summary>
18736 /// <param name="node">Element to parse</param>
18737 /// <param name="parentType">ComplexReferenceParentType of parent element.</param>
18738 /// <param name="parentId">Identifier of parent element.</param>
18739 private string ParsePayloadElementContent(XElement node, ComplexReferenceParentType parentType, string parentId, ComplexReferenceChildType previousType, string previousId, bool required)
18740 {
18741 Debug.Assert(ComplexReferenceParentType.PayloadGroup == parentType || ComplexReferenceParentType.Package == parentType || ComplexReferenceParentType.Container == parentType);
18742
18743 SourceLineNumber sourceLineNumbers = Preprocessor.GetSourceLineNumbers(node);
18744 YesNoDefaultType compressed = YesNoDefaultType.Default;
18745 YesNoType enableSignatureVerification = YesNoType.No;
18746 Identifier id = null;
18747 string name = null;
18748 string sourceFile = null;
18749 string downloadUrl = null;
18750 Wix.RemotePayload remotePayload = null;
18751
18752 // This list lets us evaluate extension attributes *after* all core attributes
18753 // have been parsed and dealt with, regardless of authoring order.
18754 List<XAttribute> extensionAttributes = new List<XAttribute>();
18755
18756 foreach (XAttribute attrib in node.Attributes())
18757 {
18758 if (String.IsNullOrEmpty(attrib.Name.NamespaceName) || CompilerCore.WixNamespace == attrib.Name.Namespace)
18759 {
18760 switch (attrib.Name.LocalName)
18761 {
18762 case "Id":
18763 id = this.core.GetAttributeIdentifier(sourceLineNumbers, attrib);
18764 break;
18765 case "Compressed":
18766 compressed = this.core.GetAttributeYesNoDefaultValue(sourceLineNumbers, attrib);
18767 break;
18768 case "Name":
18769 name = this.core.GetAttributeLongFilename(sourceLineNumbers, attrib, false, true);
18770 break;
18771 case "SourceFile":
18772 sourceFile = this.core.GetAttributeValue(sourceLineNumbers, attrib);
18773 break;
18774 case "DownloadUrl":
18775 downloadUrl = this.core.GetAttributeValue(sourceLineNumbers, attrib);
18776 break;
18777 case "EnableSignatureVerification":
18778 enableSignatureVerification = this.core.GetAttributeYesNoValue(sourceLineNumbers, attrib);
18779 break;
18780 default:
18781 this.core.UnexpectedAttribute(node, attrib);
18782 break;
18783 }
18784 }
18785 else
18786 {
18787 extensionAttributes.Add(attrib);
18788 }
18789 }
18790
18791 if (!required && null == sourceFile)
18792 {
18793 // Nothing left to do!
18794 return null;
18795 }
18796
18797 if (null == id)
18798 {
18799 id = this.core.CreateIdentifier("pay", (null != sourceFile) ? sourceFile.ToUpperInvariant() : String.Empty);
18800 }
18801
18802 // Now that the PayloadId is known, we can parse the extension attributes.
18803 Dictionary<string, string> context = new Dictionary<string, string>();
18804 context["Id"] = id.Id;
18805
18806 foreach (XAttribute extensionAttribute in extensionAttributes)
18807 {
18808 this.core.ParseExtensionAttribute(node, extensionAttribute, context);
18809 }
18810
18811 // We only handle the elements we care about. Let caller handle other children.
18812 foreach (XElement child in node.Elements(CompilerCore.WixNamespace + "RemotePayload"))
18813 {
18814 SourceLineNumber childSourceLineNumbers = Preprocessor.GetSourceLineNumbers(child);
18815
18816 if (CompilerCore.WixNamespace == node.Name.Namespace && node.Name.LocalName != "ExePackage")
18817 {
18818 this.core.OnMessage(WixErrors.RemotePayloadUnsupported(childSourceLineNumbers));
18819 continue;
18820 }
18821
18822 if (null != remotePayload)
18823 {
18824 this.core.OnMessage(WixErrors.TooManyChildren(childSourceLineNumbers, node.Name.LocalName, child.Name.LocalName));
18825 }
18826
18827 remotePayload = this.ParseRemotePayloadElement(child);
18828 }
18829
18830 if (null != sourceFile && null != remotePayload)
18831 {
18832 this.core.OnMessage(WixErrors.UnexpectedElementWithAttribute(sourceLineNumbers, node.Name.LocalName, "RemotePayload", "SourceFile"));
18833 }
18834 else if (null == sourceFile && null == remotePayload)
18835 {
18836 this.core.OnMessage(WixErrors.ExpectedAttributeOrElement(sourceLineNumbers, node.Name.LocalName, "SourceFile", "RemotePayload"));
18837 }
18838 else if (null == sourceFile)
18839 {
18840 sourceFile = String.Empty;
18841 }
18842
18843 if (null == downloadUrl && null != remotePayload)
18844 {
18845 this.core.OnMessage(WixErrors.ExpectedAttributeWithElement(sourceLineNumbers, node.Name.LocalName, "DownloadUrl", "RemotePayload"));
18846 }
18847
18848 if (Compiler.BurnUXContainerId == parentId)
18849 {
18850 if (compressed == YesNoDefaultType.No)
18851 {
18852 core.OnMessage(WixWarnings.UxPayloadsOnlySupportEmbedding(sourceLineNumbers, sourceFile));
18853 }
18854
18855 compressed = YesNoDefaultType.Yes;
18856 }
18857
18858 this.CreatePayloadRow(sourceLineNumbers, id, name, sourceFile, downloadUrl, parentType, parentId, previousType, previousId, compressed, enableSignatureVerification, null, null, remotePayload);
18859
18860 return id.Id;
18861 }
18862
18863 private Wix.RemotePayload ParseRemotePayloadElement(XElement node)
18864 {
18865 SourceLineNumber sourceLineNumbers = Preprocessor.GetSourceLineNumbers(node);
18866 Wix.RemotePayload remotePayload = new Wix.RemotePayload();
18867
18868 foreach (XAttribute attrib in node.Attributes())
18869 {
18870 if (String.IsNullOrEmpty(attrib.Name.NamespaceName) || CompilerCore.WixNamespace == attrib.Name.Namespace)
18871 {
18872 switch (attrib.Name.LocalName)
18873 {
18874 case "CertificatePublicKey":
18875 remotePayload.CertificatePublicKey = this.core.GetAttributeValue(sourceLineNumbers, attrib);
18876 break;
18877 case "CertificateThumbprint":
18878 remotePayload.CertificateThumbprint = this.core.GetAttributeValue(sourceLineNumbers, attrib);
18879 break;
18880 case "Description":
18881 remotePayload.Description = this.core.GetAttributeValue(sourceLineNumbers, attrib);
18882 break;
18883 case "Hash":
18884 remotePayload.Hash = this.core.GetAttributeValue(sourceLineNumbers, attrib);
18885 break;
18886 case "ProductName":
18887 remotePayload.ProductName = this.core.GetAttributeValue(sourceLineNumbers, attrib);
18888 break;
18889 case "Size":
18890 remotePayload.Size = this.core.GetAttributeIntegerValue(sourceLineNumbers, attrib, 0, int.MaxValue);
18891 break;
18892 case "Version":
18893 remotePayload.Version = this.core.GetAttributeValue(sourceLineNumbers, attrib);
18894 break;
18895 default:
18896 this.core.UnexpectedAttribute(node, attrib);
18897 break;
18898 }
18899 }
18900 else
18901 {
18902 this.core.ParseExtensionAttribute(node, attrib);
18903 }
18904 }
18905
18906 if (String.IsNullOrEmpty(remotePayload.ProductName))
18907 {
18908 this.core.OnMessage(WixErrors.ExpectedAttribute(sourceLineNumbers, node.Name.LocalName, "ProductName"));
18909 }
18910
18911 if (String.IsNullOrEmpty(remotePayload.Description))
18912 {
18913 this.core.OnMessage(WixErrors.ExpectedAttribute(sourceLineNumbers, node.Name.LocalName, "Description"));
18914 }
18915
18916 if (String.IsNullOrEmpty(remotePayload.Hash))
18917 {
18918 this.core.OnMessage(WixErrors.ExpectedAttribute(sourceLineNumbers, node.Name.LocalName, "Hash"));
18919 }
18920
18921 if (0 == remotePayload.Size)
18922 {
18923 this.core.OnMessage(WixErrors.ExpectedAttribute(sourceLineNumbers, node.Name.LocalName, "Size"));
18924 }
18925
18926 if (String.IsNullOrEmpty(remotePayload.Version))
18927 {
18928 this.core.OnMessage(WixErrors.ExpectedAttribute(sourceLineNumbers, node.Name.LocalName, "Version"));
18929 }
18930
18931 return remotePayload;
18932 }
18933
18934 /// <summary>
18935 /// Creates the row for a Payload.
18936 /// </summary>
18937 /// <param name="node">Element to parse</param>
18938 /// <param name="parentType">ComplexReferenceParentType of parent element</param>
18939 /// <param name="parentId">Identifier of parent element.</param>
18940 private WixBundlePayloadRow CreatePayloadRow(SourceLineNumber sourceLineNumbers, Identifier id, string name, string sourceFile, string downloadUrl, ComplexReferenceParentType parentType,
18941 string parentId, ComplexReferenceChildType previousType, string previousId, YesNoDefaultType compressed, YesNoType enableSignatureVerification, string displayName, string description,
18942 Wix.RemotePayload remotePayload)
18943 {
18944 WixBundlePayloadRow row = null;
18945
18946 if (!this.core.EncounteredError)
18947 {
18948 row = (WixBundlePayloadRow)this.core.CreateRow(sourceLineNumbers, "WixBundlePayload", id);
18949 row.Name = String.IsNullOrEmpty(name) ? Path.GetFileName(sourceFile) : name;
18950 row.SourceFile = sourceFile;
18951 row.DownloadUrl = downloadUrl;
18952 row.Compressed = compressed;
18953 row.UnresolvedSourceFile = sourceFile; // duplicate of sourceFile but in a string column so it won't get resolved to a full path during binding.
18954 row.DisplayName = displayName;
18955 row.Description = description;
18956 row.EnableSignatureValidation = (YesNoType.Yes == enableSignatureVerification);
18957
18958 if (null != remotePayload)
18959 {
18960 row.Description = remotePayload.Description;
18961 row.DisplayName = remotePayload.ProductName;
18962 row.Hash = remotePayload.Hash;
18963 row.PublicKey = remotePayload.CertificatePublicKey;
18964 row.Thumbprint = remotePayload.CertificateThumbprint;
18965 row.FileSize = remotePayload.Size;
18966 row.Version = remotePayload.Version;
18967 }
18968
18969 this.CreateGroupAndOrderingRows(sourceLineNumbers, parentType, parentId, ComplexReferenceChildType.Payload, id.Id, previousType, previousId);
18970 }
18971
18972 return row;
18973 }
18974
18975 /// <summary>
18976 /// Parse PayloadGroup element.
18977 /// </summary>
18978 /// <param name="node">Element to parse</param>
18979 /// <param name="parentType">Optional ComplexReferenceParentType of parent element. (typically another PayloadGroup)</param>
18980 /// <param name="parentId">Identifier of parent element.</param>
18981 private void ParsePayloadGroupElement(XElement node, ComplexReferenceParentType parentType, string parentId)
18982 {
18983 Debug.Assert(ComplexReferenceParentType.Unknown == parentType || ComplexReferenceParentType.Layout == parentType || ComplexReferenceParentType.PayloadGroup == parentType);
18984
18985 SourceLineNumber sourceLineNumbers = Preprocessor.GetSourceLineNumbers(node);
18986 Identifier id = null;
18987
18988 foreach (XAttribute attrib in node.Attributes())
18989 {
18990 if (String.IsNullOrEmpty(attrib.Name.NamespaceName) || CompilerCore.WixNamespace == attrib.Name.Namespace)
18991 {
18992 switch (attrib.Name.LocalName)
18993 {
18994 case "Id":
18995 id = this.core.GetAttributeIdentifier(sourceLineNumbers, attrib);
18996 break;
18997 default:
18998 this.core.UnexpectedAttribute(node, attrib);
18999 break;
19000 }
19001 }
19002 else
19003 {
19004 this.core.ParseExtensionAttribute(node, attrib);
19005 }
19006 }
19007
19008 if (null == id)
19009 {
19010 this.core.OnMessage(WixErrors.ExpectedAttribute(sourceLineNumbers, node.Name.LocalName, "Id"));
19011 id = Identifier.Invalid;
19012 }
19013
19014 ComplexReferenceChildType previousType = ComplexReferenceChildType.Unknown;
19015 string previousId = null;
19016 foreach (XElement child in node.Elements())
19017 {
19018 if (CompilerCore.WixNamespace == child.Name.Namespace)
19019 {
19020 switch (child.Name.LocalName)
19021 {
19022 case "Payload":
19023 previousId = this.ParsePayloadElement(child, ComplexReferenceParentType.PayloadGroup, id.Id, previousType, previousId);
19024 previousType = ComplexReferenceChildType.Payload;
19025 break;
19026 case "PayloadGroupRef":
19027 previousId = this.ParsePayloadGroupRefElement(child, ComplexReferenceParentType.PayloadGroup, id.Id, previousType, previousId);
19028 previousType = ComplexReferenceChildType.PayloadGroup;
19029 break;
19030 default:
19031 this.core.UnexpectedElement(node, child);
19032 break;
19033 }
19034 }
19035 else
19036 {
19037 this.core.ParseExtensionElement(node, child);
19038 }
19039 }
19040
19041
19042 if (!this.core.EncounteredError)
19043 {
19044 this.core.CreateRow(sourceLineNumbers, "WixBundlePayloadGroup", id);
19045
19046 this.CreateGroupAndOrderingRows(sourceLineNumbers, parentType, parentId, ComplexReferenceChildType.PayloadGroup, id.Id, ComplexReferenceChildType.Unknown, null);
19047 }
19048 }
19049
19050 /// <summary>
19051 /// Parses a payload group reference element.
19052 /// </summary>
19053 /// <param name="node">Element to parse.</param>
19054 /// <param name="parentType">ComplexReferenceParentType of parent element (BA or PayloadGroup).</param>
19055 /// <param name="parentId">Identifier of parent element.</param>
19056 private string ParsePayloadGroupRefElement(XElement node, ComplexReferenceParentType parentType, string parentId, ComplexReferenceChildType previousType, string previousId)
19057 {
19058 Debug.Assert(ComplexReferenceParentType.Layout == parentType || ComplexReferenceParentType.PayloadGroup == parentType || ComplexReferenceParentType.Package == parentType || ComplexReferenceParentType.Container == parentType);
19059 Debug.Assert(ComplexReferenceChildType.Unknown == previousType || ComplexReferenceChildType.PayloadGroup == previousType || ComplexReferenceChildType.Payload == previousType);
19060
19061 SourceLineNumber sourceLineNumbers = Preprocessor.GetSourceLineNumbers(node);
19062 string id = null;
19063
19064 foreach (XAttribute attrib in node.Attributes())
19065 {
19066 if (String.IsNullOrEmpty(attrib.Name.NamespaceName) || CompilerCore.WixNamespace == attrib.Name.Namespace)
19067 {
19068 switch (attrib.Name.LocalName)
19069 {
19070 case "Id":
19071 id = this.core.GetAttributeIdentifierValue(sourceLineNumbers, attrib);
19072 this.core.CreateSimpleReference(sourceLineNumbers, "WixBundlePayloadGroup", id);
19073 break;
19074 default:
19075 this.core.UnexpectedAttribute(node, attrib);
19076 break;
19077 }
19078 }
19079 else
19080 {
19081 this.core.ParseExtensionAttribute(node, attrib);
19082 }
19083 }
19084
19085 if (null == id)
19086 {
19087 this.core.OnMessage(WixErrors.ExpectedAttribute(sourceLineNumbers, node.Name.LocalName, "Id"));
19088 }
19089
19090 this.core.ParseForExtensionElements(node);
19091
19092 this.CreateGroupAndOrderingRows(sourceLineNumbers, parentType, parentId, ComplexReferenceChildType.PayloadGroup, id, previousType, previousId);
19093
19094 return id;
19095 }
19096
19097 /// <summary>
19098 /// Creates group and ordering information.
19099 /// </summary>
19100 /// <param name="sourceLineNumbers">Source line numbers.</param>
19101 /// <param name="parentType">Type of parent group, if known.</param>
19102 /// <param name="parentId">Identifier of parent group, if known.</param>
19103 /// <param name="type">Type of this item.</param>
19104 /// <param name="id">Identifier for this item.</param>
19105 /// <param name="previousType">Type of previous item, if known.</param>
19106 /// <param name="previousId">Identifier of previous item, if known</param>
19107 private void CreateGroupAndOrderingRows(SourceLineNumber sourceLineNumbers,
19108 ComplexReferenceParentType parentType, string parentId,
19109 ComplexReferenceChildType type, string id,
19110 ComplexReferenceChildType previousType, string previousId)
19111 {
19112 if (ComplexReferenceParentType.Unknown != parentType && null != parentId)
19113 {
19114 this.core.CreateWixGroupRow(sourceLineNumbers, parentType, parentId, type, id);
19115 }
19116
19117 if (ComplexReferenceChildType.Unknown != previousType && null != previousId)
19118 {
19119 this.CreateWixOrderingRow(sourceLineNumbers, type, id, previousType, previousId);
19120 }
19121 }
19122
19123 /// <summary>
19124 /// Parse ExitCode element.
19125 /// </summary>
19126 /// <param name="node">Element to parse</param>
19127 /// <param name="packageId">Id of parent element</param>
19128 private void ParseExitCodeElement(XElement node, string packageId)
19129 {
19130 SourceLineNumber sourceLineNumbers = Preprocessor.GetSourceLineNumbers(node);
19131 int value = CompilerConstants.IntegerNotSet;
19132 ExitCodeBehaviorType behavior = ExitCodeBehaviorType.NotSet;
19133
19134 foreach (XAttribute attrib in node.Attributes())
19135 {
19136 if (String.IsNullOrEmpty(attrib.Name.NamespaceName) || CompilerCore.WixNamespace == attrib.Name.Namespace)
19137 {
19138 switch (attrib.Name.LocalName)
19139 {
19140 case "Value":
19141 value = this.core.GetAttributeIntegerValue(sourceLineNumbers, attrib, int.MinValue + 2, int.MaxValue);
19142 break;
19143 case "Behavior":
19144 string behaviorString = this.core.GetAttributeValue(sourceLineNumbers, attrib);
19145 if (!Enum.TryParse<ExitCodeBehaviorType>(behaviorString, true, out behavior))
19146 {
19147 this.core.OnMessage(WixErrors.IllegalAttributeValueWithLegalList(sourceLineNumbers, node.Name.LocalName, "Behavior", behaviorString, "success, error, scheduleReboot, forceReboot"));
19148 }
19149 break;
19150 default:
19151 this.core.UnexpectedAttribute(node, attrib);
19152 break;
19153 }
19154 }
19155 else
19156 {
19157 this.core.ParseExtensionAttribute(node, attrib);
19158 }
19159 }
19160
19161 if (ExitCodeBehaviorType.NotSet == behavior)
19162 {
19163 this.core.OnMessage(WixErrors.ExpectedAttribute(sourceLineNumbers, node.Name.LocalName, "Behavior"));
19164 }
19165
19166 this.core.ParseForExtensionElements(node);
19167
19168 if (!this.core.EncounteredError)
19169 {
19170 WixBundlePackageExitCodeRow row = (WixBundlePackageExitCodeRow)this.core.CreateRow(sourceLineNumbers, "WixBundlePackageExitCode");
19171 row.ChainPackageId = packageId;
19172 row.Code = value;
19173 row.Behavior = behavior;
19174 }
19175 }
19176
19177 /// <summary>
19178 /// Parse Chain element.
19179 /// </summary>
19180 /// <param name="node">Element to parse</param>
19181 private void ParseChainElement(XElement node)
19182 {
19183 SourceLineNumber sourceLineNumbers = Preprocessor.GetSourceLineNumbers(node);
19184 WixChainAttributes attributes = WixChainAttributes.None;
19185
19186 foreach (XAttribute attrib in node.Attributes())
19187 {
19188 if (String.IsNullOrEmpty(attrib.Name.NamespaceName) || CompilerCore.WixNamespace == attrib.Name.Namespace)
19189 {
19190 switch (attrib.Name.LocalName)
19191 {
19192 case "DisableRollback":
19193 if (YesNoType.Yes == this.core.GetAttributeYesNoValue(sourceLineNumbers, attrib))
19194 {
19195 attributes |= WixChainAttributes.DisableRollback;
19196 }
19197 break;
19198 case "DisableSystemRestore":
19199 if (YesNoType.Yes == this.core.GetAttributeYesNoValue(sourceLineNumbers, attrib))
19200 {
19201 attributes |= WixChainAttributes.DisableSystemRestore;
19202 }
19203 break;
19204 case "ParallelCache":
19205 if (YesNoType.Yes == this.core.GetAttributeYesNoValue(sourceLineNumbers, attrib))
19206 {
19207 attributes |= WixChainAttributes.ParallelCache;
19208 }
19209 break;
19210 default:
19211 this.core.UnexpectedAttribute(node, attrib);
19212 break;
19213 }
19214 }
19215 else
19216 {
19217 this.core.ParseExtensionAttribute(node, attrib);
19218 }
19219 }
19220
19221 // Ensure there is always a rollback boundary at the beginning of the chain.
19222 this.CreateRollbackBoundary(sourceLineNumbers, new Identifier("WixDefaultBoundary", AccessModifier.Public), YesNoType.Yes, YesNoType.No, ComplexReferenceParentType.PackageGroup, "WixChain", ComplexReferenceChildType.Unknown, null);
19223
19224 string previousId = "WixDefaultBoundary";
19225 ComplexReferenceChildType previousType = ComplexReferenceChildType.Package;
19226
19227 foreach (XElement child in node.Elements())
19228 {
19229 if (CompilerCore.WixNamespace == child.Name.Namespace)
19230 {
19231 switch (child.Name.LocalName)
19232 {
19233 case "MsiPackage":
19234 previousId = this.ParseMsiPackageElement(child, ComplexReferenceParentType.PackageGroup, "WixChain", previousType, previousId);
19235 previousType = ComplexReferenceChildType.Package;
19236 break;
19237 case "MspPackage":
19238 previousId = this.ParseMspPackageElement(child, ComplexReferenceParentType.PackageGroup, "WixChain", previousType, previousId);
19239 previousType = ComplexReferenceChildType.Package;
19240 break;
19241 case "MsuPackage":
19242 previousId = this.ParseMsuPackageElement(child, ComplexReferenceParentType.PackageGroup, "WixChain", previousType, previousId);
19243 previousType = ComplexReferenceChildType.Package;
19244 break;
19245 case "ExePackage":
19246 previousId = this.ParseExePackageElement(child, ComplexReferenceParentType.PackageGroup, "WixChain", previousType, previousId);
19247 previousType = ComplexReferenceChildType.Package;
19248 break;
19249 case "RollbackBoundary":
19250 previousId = this.ParseRollbackBoundaryElement(child, ComplexReferenceParentType.PackageGroup, "WixChain", previousType, previousId);
19251 previousType = ComplexReferenceChildType.Package;
19252 break;
19253 case "PackageGroupRef":
19254 previousId = this.ParsePackageGroupRefElement(child, ComplexReferenceParentType.PackageGroup, "WixChain", previousType, previousId);
19255 previousType = ComplexReferenceChildType.PackageGroup;
19256 break;
19257 default:
19258 this.core.UnexpectedElement(node, child);
19259 break;
19260 }
19261 }
19262 else
19263 {
19264 this.core.ParseExtensionElement(node, child);
19265 }
19266 }
19267
19268
19269 if (null == previousId)
19270 {
19271 this.core.OnMessage(WixErrors.ExpectedElement(sourceLineNumbers, node.Name.LocalName, "MsiPackage", "ExePackage", "PackageGroupRef"));
19272 }
19273
19274 if (!this.core.EncounteredError)
19275 {
19276 WixChainRow row = (WixChainRow)this.core.CreateRow(sourceLineNumbers, "WixChain");
19277 row.Attributes = attributes;
19278 }
19279 }
19280
19281 /// <summary>
19282 /// Parse MsiPackage element
19283 /// </summary>
19284 /// <param name="node">Element to parse</param>
19285 /// <param name="parentType">Type of parent group, if known.</param>
19286 /// <param name="parentId">Identifier of parent group, if known.</param>
19287 /// <param name="previousType">Type of previous item, if known.</param>
19288 /// <param name="previousId">Identifier of previous item, if known</param>
19289 /// <returns>Identifier for package element.</returns>
19290 private string ParseMsiPackageElement(XElement node, ComplexReferenceParentType parentType, string parentId, ComplexReferenceChildType previousType, string previousId)
19291 {
19292 return ParseChainPackage(node, WixBundlePackageType.Msi, parentType, parentId, previousType, previousId);
19293 }
19294
19295 /// <summary>
19296 /// Parse MspPackage element
19297 /// </summary>
19298 /// <param name="node">Element to parse</param>
19299 /// <param name="parentType">Type of parent group, if known.</param>
19300 /// <param name="parentId">Identifier of parent group, if known.</param>
19301 /// <param name="previousType">Type of previous item, if known.</param>
19302 /// <param name="previousId">Identifier of previous item, if known</param>
19303 /// <returns>Identifier for package element.</returns>
19304 private string ParseMspPackageElement(XElement node, ComplexReferenceParentType parentType, string parentId, ComplexReferenceChildType previousType, string previousId)
19305 {
19306 return ParseChainPackage(node, WixBundlePackageType.Msp, parentType, parentId, previousType, previousId);
19307 }
19308
19309 /// <summary>
19310 /// Parse MsuPackage element
19311 /// </summary>
19312 /// <param name="node">Element to parse</param>
19313 /// <param name="parentType">Type of parent group, if known.</param>
19314 /// <param name="parentId">Identifier of parent group, if known.</param>
19315 /// <param name="previousType">Type of previous item, if known.</param>
19316 /// <param name="previousId">Identifier of previous item, if known</param>
19317 /// <returns>Identifier for package element.</returns>
19318 private string ParseMsuPackageElement(XElement node, ComplexReferenceParentType parentType, string parentId, ComplexReferenceChildType previousType, string previousId)
19319 {
19320 return ParseChainPackage(node, WixBundlePackageType.Msu, parentType, parentId, previousType, previousId);
19321 }
19322
19323 /// <summary>
19324 /// Parse ExePackage element
19325 /// </summary>
19326 /// <param name="node">Element to parse</param>
19327 /// <param name="parentType">Type of parent group, if known.</param>
19328 /// <param name="parentId">Identifier of parent group, if known.</param>
19329 /// <param name="previousType">Type of previous item, if known.</param>
19330 /// <param name="previousId">Identifier of previous item, if known</param>
19331 /// <returns>Identifier for package element.</returns>
19332 private string ParseExePackageElement(XElement node, ComplexReferenceParentType parentType, string parentId, ComplexReferenceChildType previousType, string previousId)
19333 {
19334 return ParseChainPackage(node, WixBundlePackageType.Exe, parentType, parentId, previousType, previousId);
19335 }
19336
19337 /// <summary>
19338 /// Parse RollbackBoundary element
19339 /// </summary>
19340 /// <param name="node">Element to parse</param>
19341 /// <param name="parentType">Type of parent group, if known.</param>
19342 /// <param name="parentId">Identifier of parent group, if known.</param>
19343 /// <param name="previousType">Type of previous item, if known.</param>
19344 /// <param name="previousId">Identifier of previous item, if known</param>
19345 /// <returns>Identifier for package element.</returns>
19346 private string ParseRollbackBoundaryElement(XElement node, ComplexReferenceParentType parentType, string parentId, ComplexReferenceChildType previousType, string previousId)
19347 {
19348 Debug.Assert(ComplexReferenceParentType.PackageGroup == parentType);
19349 Debug.Assert(ComplexReferenceChildType.Unknown == previousType || ComplexReferenceChildType.PackageGroup == previousType || ComplexReferenceChildType.Package == previousType);
19350
19351 SourceLineNumber sourceLineNumbers = Preprocessor.GetSourceLineNumbers(node);
19352 Identifier id = null;
19353 YesNoType vital = YesNoType.Yes;
19354 YesNoType transaction = YesNoType.No;
19355
19356 // This list lets us evaluate extension attributes *after* all core attributes
19357 // have been parsed and dealt with, regardless of authoring order.
19358 List<XAttribute> extensionAttributes = new List<XAttribute>();
19359
19360 foreach (XAttribute attrib in node.Attributes())
19361 {
19362 if (String.IsNullOrEmpty(attrib.Name.NamespaceName) || CompilerCore.WixNamespace == attrib.Name.Namespace)
19363 {
19364 bool allowed = true;
19365 switch (attrib.Name.LocalName)
19366 {
19367 case "Id":
19368 id = this.core.GetAttributeIdentifier(sourceLineNumbers, attrib);
19369 break;
19370 case "Vital":
19371 vital = this.core.GetAttributeYesNoValue(sourceLineNumbers, attrib);
19372 break;
19373 case "Transaction":
19374 transaction = this.core.GetAttributeYesNoValue(sourceLineNumbers, attrib);
19375 break;
19376 default:
19377 allowed = false;
19378 break;
19379 }
19380
19381 if (!allowed)
19382 {
19383 this.core.UnexpectedAttribute(node, attrib);
19384 }
19385 }
19386 else
19387 {
19388 // Save the extension attributes for later...
19389 extensionAttributes.Add(attrib);
19390 }
19391 }
19392
19393 if (null == id)
19394 {
19395 if (!String.IsNullOrEmpty(previousId))
19396 {
19397 id = this.core.CreateIdentifier("rba", previousId);
19398 }
19399
19400 if (null == id)
19401 {
19402 this.core.OnMessage(WixErrors.ExpectedAttribute(sourceLineNumbers, node.Name.LocalName, "Id"));
19403 id = Identifier.Invalid;
19404 }
19405 else if (!Common.IsIdentifier(id.Id))
19406 {
19407 this.core.OnMessage(WixErrors.IllegalIdentifier(sourceLineNumbers, node.Name.LocalName, "Id", id.Id));
19408 }
19409 }
19410
19411 // Now that the rollback identifier is known, we can parse the extension attributes...
19412 Dictionary<string, string> contextValues = new Dictionary<string, string>();
19413 contextValues["RollbackBoundaryId"] = id.Id;
19414 foreach (XAttribute attribute in extensionAttributes)
19415 {
19416 this.core.ParseExtensionAttribute(node, attribute, contextValues);
19417 }
19418
19419 this.core.ParseForExtensionElements(node);
19420
19421 if (!this.core.EncounteredError)
19422 {
19423 this.CreateRollbackBoundary(sourceLineNumbers, id, vital, transaction, parentType, parentId, previousType, previousId);
19424 }
19425
19426 return id.Id;
19427 }
19428
19429 /// <summary>
19430 /// Parses one of the ChainPackage elements
19431 /// </summary>
19432 /// <param name="node">Element to parse</param>
19433 /// <param name="packageType">Type of package to parse</param>
19434 /// <param name="parentType">Type of parent group, if known.</param>
19435 /// <param name="parentId">Identifier of parent group, if known.</param>
19436 /// <param name="previousType">Type of previous item, if known.</param>
19437 /// <param name="previousId">Identifier of previous item, if known</param>
19438 /// <returns>Identifier for package element.</returns>
19439 /// <remarks>This method contains the shared logic for parsing all of the ChainPackage
19440 /// types, as there is more in common between them than different.</remarks>
19441 private string ParseChainPackage(XElement node, WixBundlePackageType packageType, ComplexReferenceParentType parentType, string parentId, ComplexReferenceChildType previousType, string previousId)
19442 {
19443 Debug.Assert(ComplexReferenceParentType.PackageGroup == parentType);
19444 Debug.Assert(ComplexReferenceChildType.Unknown == previousType || ComplexReferenceChildType.PackageGroup == previousType || ComplexReferenceChildType.Package == previousType);
19445
19446 SourceLineNumber sourceLineNumbers = Preprocessor.GetSourceLineNumbers(node);
19447 Identifier id = null;
19448 string name = null;
19449 string sourceFile = null;
19450 string downloadUrl = null;
19451 string after = null;
19452 string installCondition = null;
19453 YesNoAlwaysType cache = YesNoAlwaysType.Yes; // the default is to cache everything in tradeoff for stability over disk space.
19454 string cacheId = null;
19455 string description = null;
19456 string displayName = null;
19457 string logPathVariable = (packageType == WixBundlePackageType.Msu) ? String.Empty : null;
19458 string rollbackPathVariable = (packageType == WixBundlePackageType.Msu) ? String.Empty : null;
19459 YesNoType permanent = YesNoType.NotSet;
19460 YesNoType visible = YesNoType.NotSet;
19461 YesNoType vital = YesNoType.Yes;
19462 string installCommand = null;
19463 string repairCommand = null;
19464 YesNoType repairable = YesNoType.NotSet;
19465 string uninstallCommand = null;
19466 YesNoDefaultType perMachine = YesNoDefaultType.NotSet;
19467 string detectCondition = null;
19468 string protocol = null;
19469 int installSize = CompilerConstants.IntegerNotSet;
19470 string msuKB = null;
19471 YesNoType suppressLooseFilePayloadGeneration = YesNoType.NotSet;
19472 YesNoType enableSignatureVerification = YesNoType.No;
19473 YesNoDefaultType compressed = YesNoDefaultType.Default;
19474 YesNoType displayInternalUI = YesNoType.NotSet;
19475 YesNoType enableFeatureSelection = YesNoType.NotSet;
19476 YesNoType forcePerMachine = YesNoType.NotSet;
19477 Wix.RemotePayload remotePayload = null;
19478 YesNoType slipstream = YesNoType.NotSet;
19479
19480 string[] expectedNetFx4Args = new string[] { "/q", "/norestart", "/chainingpackage" };
19481
19482 // This list lets us evaluate extension attributes *after* all core attributes
19483 // have been parsed and dealt with, regardless of authoring order.
19484 List<XAttribute> extensionAttributes = new List<XAttribute>();
19485
19486 foreach (XAttribute attrib in node.Attributes())
19487 {
19488 if (String.IsNullOrEmpty(attrib.Name.NamespaceName) || CompilerCore.WixNamespace == attrib.Name.Namespace)
19489 {
19490 bool allowed = true;
19491 switch (attrib.Name.LocalName)
19492 {
19493 case "Id":
19494 id = this.core.GetAttributeIdentifier(sourceLineNumbers, attrib);
19495 break;
19496 case "Name":
19497 name = this.core.GetAttributeLongFilename(sourceLineNumbers, attrib, false, true);
19498 if (!this.core.IsValidLongFilename(name, false, true))
19499 {
19500 this.core.OnMessage(WixErrors.IllegalLongFilename(sourceLineNumbers, node.Name.LocalName, "Name", name));
19501 }
19502 break;
19503 case "SourceFile":
19504 sourceFile = this.core.GetAttributeValue(sourceLineNumbers, attrib);
19505 break;
19506 case "DownloadUrl":
19507 downloadUrl = this.core.GetAttributeValue(sourceLineNumbers, attrib);
19508 break;
19509 case "After":
19510 after = this.core.GetAttributeValue(sourceLineNumbers, attrib);
19511 break;
19512 case "InstallCondition":
19513 installCondition = this.core.GetAttributeValue(sourceLineNumbers, attrib);
19514 break;
19515 case "Cache":
19516 cache = this.core.GetAttributeYesNoAlwaysValue(sourceLineNumbers, attrib);
19517 break;
19518 case "CacheId":
19519 cacheId = this.core.GetAttributeValue(sourceLineNumbers, attrib);
19520 break;
19521 case "Description":
19522 description = this.core.GetAttributeValue(sourceLineNumbers, attrib);
19523 break;
19524 case "DisplayName":
19525 displayName = this.core.GetAttributeValue(sourceLineNumbers, attrib);
19526 break;
19527 case "DisplayInternalUI":
19528 displayInternalUI = this.core.GetAttributeYesNoValue(sourceLineNumbers, attrib);
19529 allowed = (packageType == WixBundlePackageType.Msi || packageType == WixBundlePackageType.Msp);
19530 break;
19531 case "EnableFeatureSelection":
19532 enableFeatureSelection = this.core.GetAttributeYesNoValue(sourceLineNumbers, attrib);
19533 allowed = (packageType == WixBundlePackageType.Msi);
19534 break;
19535 case "ForcePerMachine":
19536 forcePerMachine = this.core.GetAttributeYesNoValue(sourceLineNumbers, attrib);
19537 allowed = (packageType == WixBundlePackageType.Msi);
19538 break;
19539 case "LogPathVariable":
19540 logPathVariable = this.core.GetAttributeValue(sourceLineNumbers, attrib, EmptyRule.CanBeEmpty);
19541 break;
19542 case "RollbackLogPathVariable":
19543 rollbackPathVariable = this.core.GetAttributeValue(sourceLineNumbers, attrib, EmptyRule.CanBeEmpty);
19544 break;
19545 case "Permanent":
19546 permanent = this.core.GetAttributeYesNoValue(sourceLineNumbers, attrib);
19547 break;
19548 case "Visible":
19549 visible = this.core.GetAttributeYesNoValue(sourceLineNumbers, attrib);
19550 allowed = (packageType == WixBundlePackageType.Msi);
19551 break;
19552 case "Vital":
19553 vital = this.core.GetAttributeYesNoValue(sourceLineNumbers, attrib);
19554 break;
19555 case "InstallCommand":
19556 installCommand = this.core.GetAttributeValue(sourceLineNumbers, attrib);
19557 allowed = (packageType == WixBundlePackageType.Exe);
19558 break;
19559 case "RepairCommand":
19560 repairCommand = this.core.GetAttributeValue(sourceLineNumbers, attrib, EmptyRule.CanBeEmpty);
19561 repairable = YesNoType.Yes;
19562 allowed = (packageType == WixBundlePackageType.Exe);
19563 break;
19564 case "UninstallCommand":
19565 uninstallCommand = this.core.GetAttributeValue(sourceLineNumbers, attrib);
19566 allowed = (packageType == WixBundlePackageType.Exe);
19567 break;
19568 case "PerMachine":
19569 perMachine = this.core.GetAttributeYesNoDefaultValue(sourceLineNumbers, attrib);
19570 allowed = (packageType == WixBundlePackageType.Exe || packageType == WixBundlePackageType.Msp);
19571 break;
19572 case "DetectCondition":
19573 detectCondition = this.core.GetAttributeValue(sourceLineNumbers, attrib);
19574 allowed = (packageType == WixBundlePackageType.Exe || packageType == WixBundlePackageType.Msu);
19575 break;
19576 case "Protocol":
19577 protocol = this.core.GetAttributeValue(sourceLineNumbers, attrib);
19578 allowed = (packageType == WixBundlePackageType.Exe);
19579 break;
19580 case "InstallSize":
19581 installSize = this.core.GetAttributeIntegerValue(sourceLineNumbers, attrib, 0, int.MaxValue);
19582 break;
19583 case "KB":
19584 msuKB = this.core.GetAttributeValue(sourceLineNumbers, attrib);
19585 allowed = (packageType == WixBundlePackageType.Msu);
19586 break;
19587 case "Compressed":
19588 compressed = this.core.GetAttributeYesNoDefaultValue(sourceLineNumbers, attrib);
19589 break;
19590 case "SuppressLooseFilePayloadGeneration":
19591 this.core.OnMessage(WixWarnings.DeprecatedAttribute(sourceLineNumbers, node.Name.LocalName, attrib.Name.LocalName));
19592 suppressLooseFilePayloadGeneration = this.core.GetAttributeYesNoValue(sourceLineNumbers, attrib);
19593 allowed = (packageType == WixBundlePackageType.Msi);
19594 break;
19595 case "EnableSignatureVerification":
19596 enableSignatureVerification = this.core.GetAttributeYesNoValue(sourceLineNumbers, attrib);
19597 break;
19598 case "Slipstream":
19599 slipstream = this.core.GetAttributeYesNoValue(sourceLineNumbers, attrib);
19600 allowed = (packageType == WixBundlePackageType.Msp);
19601 break;
19602 default:
19603 allowed = false;
19604 break;
19605 }
19606
19607 if (!allowed)
19608 {
19609 this.core.UnexpectedAttribute(node, attrib);
19610 }
19611 }
19612 else
19613 {
19614 // Save the extension attributes for later...
19615 extensionAttributes.Add(attrib);
19616 }
19617 }
19618
19619 // We need to handle RemotePayload up front because it effects value of sourceFile which is used in Id generation. Id is needed by other child elements.
19620 foreach (XElement child in node.Elements(CompilerCore.WixNamespace + "RemotePayload"))
19621 {
19622 SourceLineNumber childSourceLineNumbers = Preprocessor.GetSourceLineNumbers(child);
19623
19624 if (CompilerCore.WixNamespace == node.Name.Namespace && node.Name.LocalName != "ExePackage" && node.Name.LocalName != "MsuPackage")
19625 {
19626 this.core.OnMessage(WixErrors.RemotePayloadUnsupported(childSourceLineNumbers));
19627 continue;
19628 }
19629
19630 if (null != remotePayload)
19631 {
19632 this.core.OnMessage(WixErrors.TooManyChildren(childSourceLineNumbers, node.Name.LocalName, child.Name.LocalName));
19633 }
19634
19635 remotePayload = this.ParseRemotePayloadElement(child);
19636 }
19637
19638 if (String.IsNullOrEmpty(sourceFile))
19639 {
19640 if (String.IsNullOrEmpty(name))
19641 {
19642 this.core.OnMessage(WixErrors.ExpectedAttributesWithOtherAttribute(sourceLineNumbers, node.Name.LocalName, "Name", "SourceFile"));
19643 }
19644 else if (null == remotePayload)
19645 {
19646 sourceFile = Path.Combine("SourceDir", name);
19647 }
19648 else
19649 {
19650 sourceFile = String.Empty; // SourceFile is required it cannot be null.
19651 }
19652 }
19653 else if (null != remotePayload)
19654 {
19655 this.core.OnMessage(WixErrors.UnexpectedElementWithAttribute(sourceLineNumbers, node.Name.LocalName, "RemotePayload", "SourceFile"));
19656 }
19657 else if (sourceFile.EndsWith(Path.DirectorySeparatorChar.ToString(), StringComparison.Ordinal))
19658 {
19659 if (String.IsNullOrEmpty(name))
19660 {
19661 this.core.OnMessage(WixErrors.ExpectedAttribute(sourceLineNumbers, node.Name.LocalName, "Name", "SourceFile", sourceFile));
19662 }
19663 else
19664 {
19665 sourceFile = Path.Combine(sourceFile, Path.GetFileName(name));
19666 }
19667 }
19668
19669 if (null == downloadUrl && null != remotePayload)
19670 {
19671 this.core.OnMessage(WixErrors.ExpectedAttributeWithElement(sourceLineNumbers, node.Name.LocalName, "DownloadUrl", "RemotePayload"));
19672 }
19673
19674 if (YesNoDefaultType.No != compressed && null != remotePayload)
19675 {
19676 compressed = YesNoDefaultType.No;
19677 this.core.OnMessage(WixWarnings.RemotePayloadsMustNotAlsoBeCompressed(sourceLineNumbers, node.Name.LocalName));
19678 }
19679
19680 if (null == id)
19681 {
19682 if (!String.IsNullOrEmpty(name))
19683 {
19684 id = this.core.CreateIdentifierFromFilename(Path.GetFileName(name));
19685 }
19686 else if (!String.IsNullOrEmpty(sourceFile))
19687 {
19688 id = this.core.CreateIdentifierFromFilename(Path.GetFileName(sourceFile));
19689 }
19690
19691 if (null == id)
19692 {
19693 this.core.OnMessage(WixErrors.ExpectedAttribute(sourceLineNumbers, node.Name.LocalName, "Id"));
19694 id = Identifier.Invalid;
19695 }
19696 else if (!Common.IsIdentifier(id.Id))
19697 {
19698 this.core.OnMessage(WixErrors.IllegalIdentifier(sourceLineNumbers, node.Name.LocalName, "Id", id.Id));
19699 }
19700 }
19701
19702 if (null == logPathVariable)
19703 {
19704 logPathVariable = String.Concat("WixBundleLog_", id.Id);
19705 }
19706
19707 if (null == rollbackPathVariable)
19708 {
19709 rollbackPathVariable = String.Concat("WixBundleRollbackLog_", id.Id);
19710 }
19711
19712 if (!String.IsNullOrEmpty(protocol) && !protocol.Equals("burn", StringComparison.Ordinal) && !protocol.Equals("netfx4", StringComparison.Ordinal) && !protocol.Equals("none", StringComparison.Ordinal))
19713 {
19714 this.core.OnMessage(WixErrors.IllegalAttributeValueWithLegalList(sourceLineNumbers, node.Name.LocalName, "Protocol", protocol, "none, burn, netfx4"));
19715 }
19716
19717 if (!String.IsNullOrEmpty(protocol) && protocol.Equals("netfx4", StringComparison.Ordinal))
19718 {
19719 foreach (string expectedArgument in expectedNetFx4Args)
19720 {
19721 if (null == installCommand || -1 == installCommand.IndexOf(expectedArgument, StringComparison.OrdinalIgnoreCase))
19722 {
19723 this.core.OnMessage(WixWarnings.AttributeShouldContain(sourceLineNumbers, node.Name.LocalName, "InstallCommand", installCommand, expectedArgument, "Protocol", "netfx4"));
19724 }
19725
19726 if (null == repairCommand || -1 == repairCommand.IndexOf(expectedArgument, StringComparison.OrdinalIgnoreCase))
19727 {
19728 this.core.OnMessage(WixWarnings.AttributeShouldContain(sourceLineNumbers, node.Name.LocalName, "RepairCommand", repairCommand, expectedArgument, "Protocol", "netfx4"));
19729 }
19730
19731 if (null == uninstallCommand || -1 == uninstallCommand.IndexOf(expectedArgument, StringComparison.OrdinalIgnoreCase))
19732 {
19733 this.core.OnMessage(WixWarnings.AttributeShouldContain(sourceLineNumbers, node.Name.LocalName, "UninstallCommand", uninstallCommand, expectedArgument, "Protocol", "netfx4"));
19734 }
19735 }
19736 }
19737
19738 // Only set default scope for EXEs and MSPs if not already set.
19739 if ((WixBundlePackageType.Exe == packageType || WixBundlePackageType.Msp == packageType) && YesNoDefaultType.NotSet == perMachine)
19740 {
19741 perMachine = YesNoDefaultType.Default;
19742 }
19743
19744 // Now that the package ID is known, we can parse the extension attributes...
19745 Dictionary<string, string> contextValues = new Dictionary<string, string>() { { "PackageId", id.Id } };
19746 foreach (XAttribute attribute in extensionAttributes)
19747 {
19748 this.core.ParseExtensionAttribute(node, attribute, contextValues);
19749 }
19750
19751 foreach (XElement child in node.Elements())
19752 {
19753 if (CompilerCore.WixNamespace == child.Name.Namespace)
19754 {
19755 bool allowed = true;
19756 switch (child.Name.LocalName)
19757 {
19758 case "SlipstreamMsp":
19759 allowed = (packageType == WixBundlePackageType.Msi);
19760 if (allowed)
19761 {
19762 this.ParseSlipstreamMspElement(child, id.Id);
19763 }
19764 break;
19765 case "MsiProperty":
19766 allowed = (packageType == WixBundlePackageType.Msi || packageType == WixBundlePackageType.Msp);
19767 if (allowed)
19768 {
19769 this.ParseMsiPropertyElement(child, id.Id);
19770 }
19771 break;
19772 case "Payload":
19773 this.ParsePayloadElement(child, ComplexReferenceParentType.Package, id.Id, ComplexReferenceChildType.Unknown, null);
19774 break;
19775 case "PayloadGroupRef":
19776 this.ParsePayloadGroupRefElement(child, ComplexReferenceParentType.Package, id.Id, ComplexReferenceChildType.Unknown, null);
19777 break;
19778 case "ExitCode":
19779 allowed = (packageType == WixBundlePackageType.Exe);
19780 if (allowed)
19781 {
19782 this.ParseExitCodeElement(child, id.Id);
19783 }
19784 break;
19785 case "CommandLine":
19786 allowed = (packageType == WixBundlePackageType.Exe);
19787 if (allowed)
19788 {
19789 this.ParseCommandLineElement(child, id.Id);
19790 }
19791 break;
19792 case "RemotePayload":
19793 // Handled previously
19794 break;
19795 default:
19796 allowed = false;
19797 break;
19798 }
19799
19800 if (!allowed)
19801 {
19802 this.core.UnexpectedElement(node, child);
19803 }
19804 }
19805 else
19806 {
19807 Dictionary<string, string> context = new Dictionary<string, string>() { { "Id", id.Id } };
19808 this.core.ParseExtensionElement(node, child, context);
19809 }
19810 }
19811
19812 if (!this.core.EncounteredError)
19813 {
19814 // We create the package contents as a payload with this package as the parent
19815 this.CreatePayloadRow(sourceLineNumbers, id, name, sourceFile, downloadUrl, ComplexReferenceParentType.Package, id.Id,
19816 ComplexReferenceChildType.Unknown, null, compressed, enableSignatureVerification, displayName, description, remotePayload);
19817
19818 WixChainItemRow chainItemRow = (WixChainItemRow)this.core.CreateRow(sourceLineNumbers, "WixChainItem", id);
19819
19820 WixBundlePackageAttributes attributes = 0;
19821 attributes |= (YesNoType.Yes == permanent) ? WixBundlePackageAttributes.Permanent : 0;
19822 attributes |= (YesNoType.Yes == visible) ? WixBundlePackageAttributes.Visible : 0;
19823
19824 WixBundlePackageRow chainPackageRow = (WixBundlePackageRow)this.core.CreateRow(sourceLineNumbers, "WixBundlePackage", id);
19825 chainPackageRow.Type = packageType;
19826 chainPackageRow.PackagePayload = id.Id;
19827 chainPackageRow.Attributes = attributes;
19828
19829 chainPackageRow.InstallCondition = installCondition;
19830
19831 if (YesNoAlwaysType.NotSet != cache)
19832 {
19833 chainPackageRow.Cache = cache;
19834 }
19835
19836 chainPackageRow.CacheId = cacheId;
19837
19838 if (YesNoType.NotSet != vital)
19839 {
19840 chainPackageRow.Vital = vital;
19841 }
19842
19843 if (YesNoDefaultType.NotSet != perMachine)
19844 {
19845 chainPackageRow.PerMachine = perMachine;
19846 }
19847
19848 chainPackageRow.LogPathVariable = logPathVariable;
19849 chainPackageRow.RollbackLogPathVariable = rollbackPathVariable;
19850
19851 if (CompilerConstants.IntegerNotSet != installSize)
19852 {
19853 chainPackageRow.InstallSize = installSize;
19854 }
19855
19856 switch (packageType)
19857 {
19858 case WixBundlePackageType.Exe:
19859 WixBundleExePackageAttributes exeAttributes = 0;
19860 exeAttributes |= (YesNoType.Yes == repairable) ? WixBundleExePackageAttributes.Repairable : 0;
19861
19862 WixBundleExePackageRow exeRow = (WixBundleExePackageRow)this.core.CreateRow(sourceLineNumbers, "WixBundleExePackage", id);
19863 exeRow.Attributes = exeAttributes;
19864 exeRow.DetectCondition = detectCondition;
19865 exeRow.InstallCommand = installCommand;
19866 exeRow.RepairCommand = repairCommand;
19867 exeRow.UninstallCommand = uninstallCommand;
19868 exeRow.ExeProtocol = protocol;
19869 break;
19870
19871 case WixBundlePackageType.Msi:
19872 WixBundleMsiPackageAttributes msiAttributes = 0;
19873 msiAttributes |= (YesNoType.Yes == displayInternalUI) ? WixBundleMsiPackageAttributes.DisplayInternalUI : 0;
19874 msiAttributes |= (YesNoType.Yes == enableFeatureSelection) ? WixBundleMsiPackageAttributes.EnableFeatureSelection : 0;
19875 msiAttributes |= (YesNoType.Yes == forcePerMachine) ? WixBundleMsiPackageAttributes.ForcePerMachine : 0;
19876 msiAttributes |= (YesNoType.Yes == suppressLooseFilePayloadGeneration) ? WixBundleMsiPackageAttributes.SuppressLooseFilePayloadGeneration : 0;
19877
19878 WixBundleMsiPackageRow msiRow = (WixBundleMsiPackageRow)this.core.CreateRow(sourceLineNumbers, "WixBundleMsiPackage", id);
19879 msiRow.Attributes = msiAttributes;
19880 break;
19881
19882 case WixBundlePackageType.Msp:
19883 WixBundleMspPackageAttributes mspAttributes = 0;
19884 mspAttributes |= (YesNoType.Yes == displayInternalUI) ? WixBundleMspPackageAttributes.DisplayInternalUI : 0;
19885 mspAttributes |= (YesNoType.Yes == slipstream) ? WixBundleMspPackageAttributes.Slipstream : 0;
19886
19887 WixBundleMspPackageRow mspRow = (WixBundleMspPackageRow)this.core.CreateRow(sourceLineNumbers, "WixBundleMspPackage", id);
19888 mspRow.Attributes = mspAttributes;
19889 break;
19890
19891 case WixBundlePackageType.Msu:
19892 WixBundleMsuPackageRow msuRow = (WixBundleMsuPackageRow)this.core.CreateRow(sourceLineNumbers, "WixBundleMsuPackage", id);
19893 msuRow.DetectCondition = detectCondition;
19894 msuRow.MsuKB = msuKB;
19895 break;
19896 }
19897
19898 this.CreateChainPackageMetaRows(sourceLineNumbers, parentType, parentId, ComplexReferenceChildType.Package, id.Id, previousType, previousId, after);
19899 }
19900
19901 return id.Id;
19902 }
19903
19904 /// <summary>
19905 /// Parse CommandLine element.
19906 /// </summary>
19907 /// <param name="node">Element to parse</param>
19908 private void ParseCommandLineElement(XElement node, string packageId)
19909 {
19910 SourceLineNumber sourceLineNumbers = Preprocessor.GetSourceLineNumbers(node);
19911 string installArgument = null;
19912 string uninstallArgument = null;
19913 string repairArgument = null;
19914 string condition = null;
19915
19916 foreach (XAttribute attrib in node.Attributes())
19917 {
19918 if (String.IsNullOrEmpty(attrib.Name.NamespaceName) || CompilerCore.WixNamespace == attrib.Name.Namespace)
19919 {
19920 switch (attrib.Name.LocalName)
19921 {
19922 case "InstallArgument":
19923 installArgument = this.core.GetAttributeValue(sourceLineNumbers, attrib);
19924 break;
19925 case "UninstallArgument":
19926 uninstallArgument = this.core.GetAttributeValue(sourceLineNumbers, attrib);
19927 break;
19928 case "RepairArgument":
19929 repairArgument = this.core.GetAttributeValue(sourceLineNumbers, attrib);
19930 break;
19931 case "Condition":
19932 condition = this.core.GetAttributeValue(sourceLineNumbers, attrib);
19933 break;
19934 default:
19935 this.core.UnexpectedAttribute(node, attrib);
19936 break;
19937 }
19938 }
19939 else
19940 {
19941 this.core.ParseExtensionAttribute(node, attrib);
19942 }
19943 }
19944
19945 if (String.IsNullOrEmpty(condition))
19946 {
19947 this.core.OnMessage(WixErrors.ExpectedAttribute(sourceLineNumbers, node.Name.LocalName, "Condition"));
19948 }
19949
19950 this.core.ParseForExtensionElements(node);
19951
19952 if (!this.core.EncounteredError)
19953 {
19954 WixBundlePackageCommandLineRow row = (WixBundlePackageCommandLineRow)this.core.CreateRow(sourceLineNumbers, "WixBundlePackageCommandLine");
19955 row.ChainPackageId = packageId;
19956 row.InstallArgument = installArgument;
19957 row.UninstallArgument = uninstallArgument;
19958 row.RepairArgument = repairArgument;
19959 row.Condition = condition;
19960 }
19961 }
19962
19963 /// <summary>
19964 /// Parse PackageGroup element.
19965 /// </summary>
19966 /// <param name="node">Element to parse</param>
19967 private void ParsePackageGroupElement(XElement node)
19968 {
19969 SourceLineNumber sourceLineNumbers = Preprocessor.GetSourceLineNumbers(node);
19970 Identifier id = null;
19971
19972 foreach (XAttribute attrib in node.Attributes())
19973 {
19974 if (String.IsNullOrEmpty(attrib.Name.NamespaceName) || CompilerCore.WixNamespace == attrib.Name.Namespace)
19975 {
19976 switch (attrib.Name.LocalName)
19977 {
19978 case "Id":
19979 id = this.core.GetAttributeIdentifier(sourceLineNumbers, attrib);
19980 break;
19981 default:
19982 this.core.UnexpectedAttribute(node, attrib);
19983 break;
19984 }
19985 }
19986 else
19987 {
19988 this.core.ParseExtensionAttribute(node, attrib);
19989 }
19990 }
19991
19992 if (null == id)
19993 {
19994 this.core.OnMessage(WixErrors.ExpectedAttribute(sourceLineNumbers, node.Name.LocalName, "Id"));
19995 id = Identifier.Invalid;
19996 }
19997
19998 ComplexReferenceChildType previousType = ComplexReferenceChildType.Unknown;
19999 string previousId = null;
20000 foreach (XElement child in node.Elements())
20001 {
20002 if (CompilerCore.WixNamespace == child.Name.Namespace)
20003 {
20004 switch (child.Name.LocalName)
20005 {
20006 case "MsiPackage":
20007 previousId = this.ParseMsiPackageElement(child, ComplexReferenceParentType.PackageGroup, id.Id, previousType, previousId);
20008 previousType = ComplexReferenceChildType.Package;
20009 break;
20010 case "MspPackage":
20011 previousId = this.ParseMspPackageElement(child, ComplexReferenceParentType.PackageGroup, id.Id, previousType, previousId);
20012 previousType = ComplexReferenceChildType.Package;
20013 break;
20014 case "MsuPackage":
20015 previousId = this.ParseMsuPackageElement(child, ComplexReferenceParentType.PackageGroup, id.Id, previousType, previousId);
20016 previousType = ComplexReferenceChildType.Package;
20017 break;
20018 case "ExePackage":
20019 previousId = this.ParseExePackageElement(child, ComplexReferenceParentType.PackageGroup, id.Id, previousType, previousId);
20020 previousType = ComplexReferenceChildType.Package;
20021 break;
20022 case "RollbackBoundary":
20023 previousId = this.ParseRollbackBoundaryElement(child, ComplexReferenceParentType.PackageGroup, id.Id, previousType, previousId);
20024 previousType = ComplexReferenceChildType.Package;
20025 break;
20026 case "PackageGroupRef":
20027 previousId = this.ParsePackageGroupRefElement(child, ComplexReferenceParentType.PackageGroup, id.Id, previousType, previousId);
20028 previousType = ComplexReferenceChildType.PackageGroup;
20029 break;
20030 default:
20031 this.core.UnexpectedElement(node, child);
20032 break;
20033 }
20034 }
20035 else
20036 {
20037 this.core.ParseExtensionElement(node, child);
20038 }
20039 }
20040
20041
20042 if (!this.core.EncounteredError)
20043 {
20044 this.core.CreateRow(sourceLineNumbers, "WixBundlePackageGroup", id);
20045 }
20046 }
20047
20048 /// <summary>
20049 /// Parses a package group reference element.
20050 /// </summary>
20051 /// <param name="node">Element to parse.</param>
20052 /// <param name="parentType">ComplexReferenceParentType of parent element (Unknown or PackageGroup).</param>
20053 /// <param name="parentId">Identifier of parent element.</param>
20054 /// <returns>Identifier for package group element.</rereturns>
20055 private string ParsePackageGroupRefElement(XElement node, ComplexReferenceParentType parentType, string parentId)
20056 {
20057 return this.ParsePackageGroupRefElement(node, parentType, parentId, ComplexReferenceChildType.Unknown, null);
20058 }
20059
20060 /// <summary>
20061 /// Parses a package group reference element.
20062 /// </summary>
20063 /// <param name="node">Element to parse.</param>
20064 /// <param name="parentType">ComplexReferenceParentType of parent element (Unknown or PackageGroup).</param>
20065 /// <param name="parentId">Identifier of parent element.</param>
20066 /// <param name="parentType">ComplexReferenceParentType of previous element (Unknown, Package, or PackageGroup).</param>
20067 /// <param name="parentId">Identifier of parent element.</param>
20068 /// <returns>Identifier for package group element.</rereturns>
20069 private string ParsePackageGroupRefElement(XElement node, ComplexReferenceParentType parentType, string parentId, ComplexReferenceChildType previousType, string previousId)
20070 {
20071 Debug.Assert(ComplexReferenceParentType.Unknown == parentType || ComplexReferenceParentType.PackageGroup == parentType || ComplexReferenceParentType.Container == parentType);
20072 Debug.Assert(ComplexReferenceChildType.Unknown == previousType || ComplexReferenceChildType.PackageGroup == previousType || ComplexReferenceChildType.Package == previousType);
20073
20074 SourceLineNumber sourceLineNumbers = Preprocessor.GetSourceLineNumbers(node);
20075 string id = null;
20076 string after = null;
20077
20078 foreach (XAttribute attrib in node.Attributes())
20079 {
20080 if (String.IsNullOrEmpty(attrib.Name.NamespaceName) || CompilerCore.WixNamespace == attrib.Name.Namespace)
20081 {
20082 switch (attrib.Name.LocalName)
20083 {
20084 case "Id":
20085 id = this.core.GetAttributeIdentifierValue(sourceLineNumbers, attrib);
20086 this.core.CreateSimpleReference(sourceLineNumbers, "WixBundlePackageGroup", id);
20087 break;
20088 case "After":
20089 after = this.core.GetAttributeIdentifierValue(sourceLineNumbers, attrib);
20090 break;
20091 default:
20092 this.core.UnexpectedAttribute(node, attrib);
20093 break;
20094 }
20095 }
20096 else
20097 {
20098 this.core.ParseExtensionAttribute(node, attrib);
20099
20100 }
20101 }
20102
20103 if (null == id)
20104 {
20105 this.core.OnMessage(WixErrors.ExpectedAttribute(sourceLineNumbers, node.Name.LocalName, "Id"));
20106 }
20107
20108 if (null != after && ComplexReferenceParentType.Container == parentType)
20109 {
20110 this.core.OnMessage(WixErrors.IllegalAttributeWhenNested(sourceLineNumbers, node.Name.LocalName, "After", parentId));
20111 }
20112
20113 this.core.ParseForExtensionElements(node);
20114
20115 if (ComplexReferenceParentType.Container == parentType)
20116 {
20117 this.core.CreateWixGroupRow(sourceLineNumbers, ComplexReferenceParentType.Container, parentId, ComplexReferenceChildType.PackageGroup, id);
20118 }
20119 else
20120 {
20121 this.CreateChainPackageMetaRows(sourceLineNumbers, parentType, parentId, ComplexReferenceChildType.PackageGroup, id, previousType, previousId, after);
20122 }
20123
20124 return id;
20125 }
20126
20127 /// <summary>
20128 /// Creates rollback boundary.
20129 /// </summary>
20130 /// <param name="sourceLineNumbers">Source line numbers.</param>
20131 /// <param name="id">Identifier for the rollback boundary.</param>
20132 /// <param name="vital">Indicates whether the rollback boundary is vital or not.</param>
20133 /// <param name="parentType">Type of parent group.</param>
20134 /// <param name="parentId">Identifier of parent group.</param>
20135 /// <param name="previousType">Type of previous item, if any.</param>
20136 /// <param name="previousId">Identifier of previous item, if any.</param>
20137 private void CreateRollbackBoundary(SourceLineNumber sourceLineNumbers, Identifier id, YesNoType vital, YesNoType transaction, ComplexReferenceParentType parentType, string parentId, ComplexReferenceChildType previousType, string previousId)
20138 {
20139 WixChainItemRow row = (WixChainItemRow)this.core.CreateRow(sourceLineNumbers, "WixChainItem", id);
20140
20141 WixBundleRollbackBoundaryRow rollbackBoundary = (WixBundleRollbackBoundaryRow)this.core.CreateRow(sourceLineNumbers, "WixBundleRollbackBoundary", id);
20142
20143 if (YesNoType.NotSet != vital)
20144 {
20145 rollbackBoundary.Vital = vital;
20146 }
20147 if (YesNoType.NotSet != transaction)
20148 {
20149 rollbackBoundary.Transaction = transaction;
20150 }
20151
20152 this.CreateChainPackageMetaRows(sourceLineNumbers, parentType, parentId, ComplexReferenceChildType.Package, id.Id, previousType, previousId, null);
20153 }
20154
20155 /// <summary>
20156 /// Creates group and ordering information for packages
20157 /// </summary>
20158 /// <param name="sourceLineNumbers">Source line numbers.</param>
20159 /// <param name="parentType">Type of parent group, if known.</param>
20160 /// <param name="parentId">Identifier of parent group, if known.</param>
20161 /// <param name="type">Type of this item.</param>
20162 /// <param name="id">Identifier for this item.</param>
20163 /// <param name="previousType">Type of previous item, if known.</param>
20164 /// <param name="previousId">Identifier of previous item, if known</param>
20165 /// <param name="afterId">Identifier of explicit 'After' attribute, if given.</param>
20166 private void CreateChainPackageMetaRows(SourceLineNumber sourceLineNumbers,
20167 ComplexReferenceParentType parentType, string parentId,
20168 ComplexReferenceChildType type, string id,
20169 ComplexReferenceChildType previousType, string previousId, string afterId)
20170 {
20171 // If there's an explicit 'After' attribute, it overrides the inferred previous item.
20172 if (null != afterId)
20173 {
20174 previousType = ComplexReferenceChildType.Package;
20175 previousId = afterId;
20176 }
20177
20178 this.CreateGroupAndOrderingRows(sourceLineNumbers, parentType, parentId, type, id, previousType, previousId);
20179 }
20180
20181 // TODO: Should we define our own enum for this, just to ensure there's no "cross-contamination"?
20182 // TODO: Also, we could potentially include an 'Attributes' field to track things like
20183 // 'before' vs. 'after', and explicit vs. inferred dependencies.
20184 private void CreateWixOrderingRow(SourceLineNumber sourceLineNumbers,
20185 ComplexReferenceChildType itemType, string itemId,
20186 ComplexReferenceChildType dependsOnType, string dependsOnId)
20187 {
20188 if (!this.core.EncounteredError)
20189 {
20190 Row row = this.core.CreateRow(sourceLineNumbers, "WixOrdering");
20191 row[0] = itemType.ToString();
20192 row[1] = itemId;
20193 row[2] = dependsOnType.ToString();
20194 row[3] = dependsOnId;
20195 }
20196 }
20197
20198 /// <summary>
20199 /// Parse MsiProperty element
20200 /// </summary>
20201 /// <param name="node">Element to parse</param>
20202 /// <param name="packageId">Id of parent element</param>
20203 private void ParseMsiPropertyElement(XElement node, string packageId)
20204 {
20205 SourceLineNumber sourceLineNumbers = Preprocessor.GetSourceLineNumbers(node);
20206 string name = null;
20207 string value = null;
20208 string condition = null;
20209
20210 foreach (XAttribute attrib in node.Attributes())
20211 {
20212 if (String.IsNullOrEmpty(attrib.Name.NamespaceName) || CompilerCore.WixNamespace == attrib.Name.Namespace)
20213 {
20214 switch (attrib.Name.LocalName)
20215 {
20216 case "Name":
20217 name = this.core.GetAttributeMsiPropertyNameValue(sourceLineNumbers, attrib);
20218 break;
20219 case "Value":
20220 value = this.core.GetAttributeValue(sourceLineNumbers, attrib);
20221 break;
20222 case "Condition":
20223 condition = this.core.GetAttributeValue(sourceLineNumbers, attrib);
20224 break;
20225 default:
20226 this.core.UnexpectedAttribute(node, attrib);
20227 break;
20228 }
20229 }
20230 else
20231 {
20232 this.core.ParseExtensionAttribute(node, attrib);
20233 }
20234 }
20235
20236 if (null == name)
20237 {
20238 this.core.OnMessage(WixErrors.ExpectedAttribute(sourceLineNumbers, node.Name.LocalName, "Name"));
20239 }
20240
20241 if (null == value)
20242 {
20243 this.core.OnMessage(WixErrors.ExpectedAttribute(sourceLineNumbers, node.Name.LocalName, "Value"));
20244 }
20245
20246 this.core.ParseForExtensionElements(node);
20247
20248 if (!this.core.EncounteredError)
20249 {
20250 WixBundleMsiPropertyRow row = (WixBundleMsiPropertyRow)this.core.CreateRow(sourceLineNumbers, "WixBundleMsiProperty");
20251 row.ChainPackageId = packageId;
20252 row.Name = name;
20253 row.Value = value;
20254
20255 if (!String.IsNullOrEmpty(condition))
20256 {
20257 row.Condition = condition;
20258 }
20259 }
20260 }
20261
20262 /// <summary>
20263 /// Parse SlipstreamMsp element
20264 /// </summary>
20265 /// <param name="node">Element to parse</param>
20266 /// <param name="packageId">Id of parent element</param>
20267 private void ParseSlipstreamMspElement(XElement node, string packageId)
20268 {
20269 SourceLineNumber sourceLineNumbers = Preprocessor.GetSourceLineNumbers(node);
20270 string id = null;
20271
20272 foreach (XAttribute attrib in node.Attributes())
20273 {
20274 if (String.IsNullOrEmpty(attrib.Name.NamespaceName) || CompilerCore.WixNamespace == attrib.Name.Namespace)
20275 {
20276 switch (attrib.Name.LocalName)
20277 {
20278 case "Id":
20279 id = this.core.GetAttributeIdentifierValue(sourceLineNumbers, attrib);
20280 this.core.CreateSimpleReference(sourceLineNumbers, "WixBundlePackage", id);
20281 break;
20282 default:
20283 this.core.UnexpectedAttribute(node, attrib);
20284 break;
20285 }
20286 }
20287 else
20288 {
20289 this.core.ParseExtensionAttribute(node, attrib);
20290 }
20291 }
20292
20293 if (null == id)
20294 {
20295 this.core.OnMessage(WixErrors.ExpectedAttribute(sourceLineNumbers, node.Name.LocalName, "Id"));
20296 }
20297
20298 this.core.ParseForExtensionElements(node);
20299
20300 if (!this.core.EncounteredError)
20301 {
20302 WixBundleSlipstreamMspRow row = (WixBundleSlipstreamMspRow)this.core.CreateRow(sourceLineNumbers, "WixBundleSlipstreamMsp");
20303 row.ChainPackageId = packageId;
20304 row.MspPackageId = id;
20305 }
20306 }
20307
20308 /// <summary>
20309 /// Parse RelatedBundle element
20310 /// </summary>
20311 /// <param name="node">Element to parse</param>
20312 private void ParseRelatedBundleElement(XElement node)
20313 {
20314 SourceLineNumber sourceLineNumbers = Preprocessor.GetSourceLineNumbers(node);
20315 string id = null;
20316 string action = null;
20317 Wix.RelatedBundle.ActionType actionType = Wix.RelatedBundle.ActionType.Detect;
20318
20319 foreach (XAttribute attrib in node.Attributes())
20320 {
20321 if (String.IsNullOrEmpty(attrib.Name.NamespaceName) || CompilerCore.WixNamespace == attrib.Name.Namespace)
20322 {
20323 switch (attrib.Name.LocalName)
20324 {
20325 case "Id":
20326 id = this.core.GetAttributeGuidValue(sourceLineNumbers, attrib, false);
20327 break;
20328 case "Action":
20329 action = this.core.GetAttributeValue(sourceLineNumbers, attrib);
20330 break;
20331 default:
20332 this.core.UnexpectedAttribute(node, attrib);
20333 break;
20334 }
20335 }
20336 else
20337 {
20338 this.core.ParseExtensionAttribute(node, attrib);
20339 }
20340 }
20341
20342 if (null == id)
20343 {
20344 this.core.OnMessage(WixErrors.ExpectedAttribute(sourceLineNumbers, node.Name.LocalName, "Id"));
20345 }
20346
20347 if (!String.IsNullOrEmpty(action))
20348 {
20349 actionType = Wix.RelatedBundle.ParseActionType(action);
20350 switch (actionType)
20351 {
20352 case Wix.RelatedBundle.ActionType.Detect:
20353 break;
20354 case Wix.RelatedBundle.ActionType.Upgrade:
20355 break;
20356 case Wix.RelatedBundle.ActionType.Addon:
20357 break;
20358 case Wix.RelatedBundle.ActionType.Patch:
20359 break;
20360 default:
20361 this.core.OnMessage(WixErrors.IllegalAttributeValue(sourceLineNumbers, node.Name.LocalName, "Action", action, "Detect", "Upgrade", "Addon", "Patch"));
20362 break;
20363 }
20364 }
20365
20366 this.core.ParseForExtensionElements(node);
20367
20368 if (!this.core.EncounteredError)
20369 {
20370 Row row = this.core.CreateRow(sourceLineNumbers, "WixRelatedBundle");
20371 row[0] = id;
20372 row[1] = (int)actionType;
20373 }
20374 }
20375
20376 /// <summary>
20377 /// Parse Update element
20378 /// </summary>
20379 /// <param name="node">Element to parse</param>
20380 private void ParseUpdateElement(XElement node)
20381 {
20382 SourceLineNumber sourceLineNumbers = Preprocessor.GetSourceLineNumbers(node);
20383 string location = null;
20384
20385 foreach (XAttribute attrib in node.Attributes())
20386 {
20387 if (String.IsNullOrEmpty(attrib.Name.NamespaceName) || CompilerCore.WixNamespace == attrib.Name.Namespace)
20388 {
20389 switch (attrib.Name.LocalName)
20390 {
20391 case "Location":
20392 location = this.core.GetAttributeValue(sourceLineNumbers, attrib);
20393 break;
20394 default:
20395 this.core.UnexpectedAttribute(node, attrib);
20396 break;
20397 }
20398 }
20399 else
20400 {
20401 this.core.ParseExtensionAttribute(node, attrib);
20402 }
20403 }
20404
20405 if (null == location)
20406 {
20407 this.core.OnMessage(WixErrors.ExpectedAttribute(sourceLineNumbers, node.Name.LocalName, "Location"));
20408 }
20409
20410 this.core.ParseForExtensionElements(node);
20411
20412 if (!this.core.EncounteredError)
20413 {
20414 Row row = this.core.CreateRow(sourceLineNumbers, "WixBundleUpdate");
20415 row[0] = location;
20416 }
20417 }
20418
20419 /// <summary>
20420 /// Parse Variable element
20421 /// </summary>
20422 /// <param name="node">Element to parse</param>
20423 private void ParseVariableElement(XElement node)
20424 {
20425 SourceLineNumber sourceLineNumbers = Preprocessor.GetSourceLineNumbers(node);
20426 bool hidden = false;
20427 string name = null;
20428 bool persisted = false;
20429 string value = null;
20430 string type = null;
20431
20432 foreach (XAttribute attrib in node.Attributes())
20433 {
20434 if (String.IsNullOrEmpty(attrib.Name.NamespaceName) || CompilerCore.WixNamespace == attrib.Name.Namespace)
20435 {
20436 switch (attrib.Name.LocalName)
20437 {
20438 case "Hidden":
20439 if (YesNoType.Yes == this.core.GetAttributeYesNoValue(sourceLineNumbers, attrib))
20440 {
20441 hidden = true;
20442 }
20443 break;
20444 case "Name":
20445 name = this.core.GetAttributeBundleVariableValue(sourceLineNumbers, attrib);
20446 break;
20447 case "Persisted":
20448 if (YesNoType.Yes == this.core.GetAttributeYesNoValue(sourceLineNumbers, attrib))
20449 {
20450 persisted = true;
20451 }
20452 break;
20453 case "Value":
20454 value = this.core.GetAttributeValue(sourceLineNumbers, attrib, EmptyRule.CanBeEmpty);
20455 break;
20456 case "Type":
20457 type = this.core.GetAttributeValue(sourceLineNumbers, attrib);
20458 break;
20459 default:
20460 this.core.UnexpectedAttribute(node, attrib);
20461 break;
20462 }
20463 }
20464 else
20465 {
20466 this.core.ParseExtensionAttribute(node, attrib);
20467 }
20468 }
20469
20470 if (null == name)
20471 {
20472 this.core.OnMessage(WixErrors.ExpectedAttribute(sourceLineNumbers, node.Name.LocalName, "Name"));
20473 }
20474 else if (name.StartsWith("Wix", StringComparison.OrdinalIgnoreCase))
20475 {
20476 this.core.OnMessage(WixErrors.ReservedNamespaceViolation(sourceLineNumbers, node.Name.LocalName, "Name", "Wix"));
20477 }
20478
20479 if (null == type && null != value)
20480 {
20481 // Infer the type from the current value...
20482 if (value.StartsWith("v", StringComparison.OrdinalIgnoreCase))
20483 {
20484 // Version constructor does not support simple "v#" syntax so check to see if the value is
20485 // non-negative real quick.
20486 Int32 number;
20487 if (Int32.TryParse(value.Substring(1), NumberStyles.None, CultureInfo.InvariantCulture.NumberFormat, out number))
20488 {
20489 type = "version";
20490 }
20491 else
20492 {
20493 // Sadly, Version doesn't have a TryParse() method until .NET 4, so we have to try/catch to see if it parses.
20494 try
20495 {
20496 Version version = new Version(value.Substring(1));
20497 type = "version";
20498 }
20499 catch (Exception)
20500 {
20501 }
20502 }
20503 }
20504
20505 // Not a version, check for numeric.
20506 if (null == type)
20507 {
20508 Int64 number;
20509 if (Int64.TryParse(value, NumberStyles.Integer, CultureInfo.InvariantCulture.NumberFormat, out number))
20510 {
20511 type = "numeric";
20512 }
20513 else
20514 {
20515 type = "string";
20516 }
20517 }
20518 }
20519
20520 if (null == value && null != type)
20521 {
20522 this.core.OnMessage(WixErrors.ExpectedAttribute(sourceLineNumbers, "Variable", "Value", "Type"));
20523 }
20524
20525 this.core.ParseForExtensionElements(node);
20526
20527 if (!this.core.EncounteredError)
20528 {
20529 WixBundleVariableRow row = (WixBundleVariableRow)this.core.CreateRow(sourceLineNumbers, "WixBundleVariable");
20530 row.Id = name;
20531 row.Value = value;
20532 row.Type = type;
20533 row.Hidden = hidden;
20534 row.Persisted = persisted;
20535 }
20536 }
20537
20538
20539
20540 /// <summary>
20541 /// Parses a Wix element.
20542 /// </summary>
20543 /// <param name="node">Element to parse.</param>
20544 private void ParseWixElement(XElement node)
20545 {
20546 SourceLineNumber sourceLineNumbers = Preprocessor.GetSourceLineNumbers(node);
20547 string requiredVersion = null;
20548
20549 foreach (XAttribute attrib in node.Attributes())
20550 {
20551 if (String.IsNullOrEmpty(attrib.Name.NamespaceName) || CompilerCore.WixNamespace == attrib.Name.Namespace)
20552 {
20553 switch (attrib.Name.LocalName)
20554 {
20555 case "RequiredVersion":
20556 requiredVersion = this.core.GetAttributeVersionValue(sourceLineNumbers, attrib);
20557 break;
20558 default:
20559 this.core.UnexpectedAttribute(node, attrib);
20560 break;
20561 }
20562 }
20563 else
20564 {
20565 this.core.ParseExtensionAttribute(node, attrib);
20566 }
20567 }
20568
20569 if (null != requiredVersion)
20570 {
20571 this.core.VerifyRequiredVersion(sourceLineNumbers, requiredVersion);
20572 }
20573
20574 foreach (XElement child in node.Elements())
20575 {
20576 if (CompilerCore.WixNamespace == child.Name.Namespace)
20577 {
20578 switch (child.Name.LocalName)
20579 {
20580 case "Bundle":
20581 this.ParseBundleElement(child);
20582 break;
20583 case "Fragment":
20584 this.ParseFragmentElement(child);
20585 break;
20586 case "Module":
20587 this.ParseModuleElement(child);
20588 break;
20589 case "PatchCreation":
20590 this.ParsePatchCreationElement(child);
20591 break;
20592 case "Product":
20593 this.ParseProductElement(child);
20594 break;
20595 case "Patch":
20596 this.ParsePatchElement(child);
20597 break;
20598 default:
20599 this.core.UnexpectedElement(node, child);
20600 break;
20601 }
20602 }
20603 else
20604 {
20605 this.core.ParseExtensionElement(node, child);
20606 }
20607 }
20608 }
20609
20610 /// <summary>
20611 /// Parses a WixVariable element.
20612 /// </summary>
20613 /// <param name="node">Element to parse.</param>
20614 private void ParseWixVariableElement(XElement node)
20615 {
20616 SourceLineNumber sourceLineNumbers = Preprocessor.GetSourceLineNumbers(node);
20617 Identifier id = null;
20618 bool overridable = false;
20619 string value = null;
20620
20621 foreach (XAttribute attrib in node.Attributes())
20622 {
20623 if (String.IsNullOrEmpty(attrib.Name.NamespaceName) || CompilerCore.WixNamespace == attrib.Name.Namespace)
20624 {
20625 switch (attrib.Name.LocalName)
20626 {
20627 case "Id":
20628 id = this.core.GetAttributeIdentifier(sourceLineNumbers, attrib);
20629 break;
20630 case "Overridable":
20631 overridable = (YesNoType.Yes == this.core.GetAttributeYesNoValue(sourceLineNumbers, attrib));
20632 break;
20633 case "Value":
20634 value = this.core.GetAttributeValue(sourceLineNumbers, attrib, EmptyRule.CanBeEmpty);
20635 break;
20636 default:
20637 this.core.UnexpectedAttribute(node, attrib);
20638 break;
20639 }
20640 }
20641 else
20642 {
20643 this.core.ParseExtensionAttribute(node, attrib);
20644 }
20645 }
20646
20647 if (null == id)
20648 {
20649 this.core.OnMessage(WixErrors.ExpectedAttribute(sourceLineNumbers, node.Name.LocalName, "Id"));
20650 }
20651
20652 if (null == value)
20653 {
20654 this.core.OnMessage(WixErrors.ExpectedAttribute(sourceLineNumbers, node.Name.LocalName, "Value"));
20655 }
20656
20657 this.core.ParseForExtensionElements(node);
20658
20659 if (!this.core.EncounteredError)
20660 {
20661 WixVariableRow wixVariableRow = (WixVariableRow)this.core.CreateRow(sourceLineNumbers, "WixVariable", id);
20662 wixVariableRow.Value = value;
20663 wixVariableRow.Overridable = overridable;
20664 }
20665 }
20666 }
20667}