main
cs 5,069 lines 225 KB
Raw
1 // Copyright (c) .NET Foundation and contributors. All rights reserved. Licensed under the Microsoft Reciprocal License. See LICENSE.TXT file in the project root for full license information.
2
3 namespace WixToolset.Core
4 {
5 using System;
6 using System.Collections;
7 using System.Collections.Generic;
8 using System.Globalization;
9 using System.IO;
10 using System.Xml.Linq;
11 using WixToolset.Data;
12 using WixToolset.Data.Symbols;
13 using WixToolset.Data.WindowsInstaller;
14 using WixToolset.Extensibility;
15
16 /// <summary>
17 /// Compiler of the WiX toolset.
18 /// </summary>
19 internal partial class Compiler : ICompiler
20 {
21 /// <summary>
22 /// Parses a product element.
23 /// </summary>
24 /// <param name="node">Element to parse.</param>
25 private void ParsePackageElement(XElement node)
26 {
27 var sourceLineNumbers = Preprocessor.GetSourceLineNumbers(node);
28 var compressed = YesNoDefaultType.Default;
29 var sourceBits = 0;
30 string codepage = null;
31 var productCode = "*";
32 string productLanguage = null;
33 var isPerMachine = true;
34 var isPerUserOrMachine = false;
35 string upgradeCode = null;
36 string manufacturer = null;
37 string version = null;
38 string symbols = null;
39 var isCodepageSet = false;
40 var isCommentsSet = false;
41 var isPackageNameSet = false;
42 var isKeywordsSet = false;
43 var isPackageAuthorSet = false;
44 var upgradeStrategy = WixPackageUpgradeStrategy.MajorUpgrade;
45
46 this.GetDefaultPlatformAndInstallerVersion(out var platform, out var msiVersion);
47
48 this.activeName = null;
49 this.activeLanguage = null;
50
51 foreach (var attrib in node.Attributes())
52 {
53 if (String.IsNullOrEmpty(attrib.Name.NamespaceName) || CompilerCore.WixNamespace == attrib.Name.Namespace)
54 {
55 switch (attrib.Name.LocalName)
56 {
57 case "Codepage":
58 codepage = this.Core.GetAttributeLocalizableCodePageValue(sourceLineNumbers, attrib);
59 break;
60 case "Compressed":
61 compressed = this.Core.GetAttributeYesNoDefaultValue(sourceLineNumbers, attrib);
62 break;
63 case "InstallerVersion":
64 msiVersion = this.Core.GetAttributeIntegerValue(sourceLineNumbers, attrib, 0, Int32.MaxValue);
65 break;
66 case "Language":
67 productLanguage = this.Core.GetAttributeLocalizableIntegerValue(sourceLineNumbers, attrib, 0, Int16.MaxValue);
68 break;
69 case "Manufacturer":
70 manufacturer = this.Core.GetAttributeValue(sourceLineNumbers, attrib, EmptyRule.MustHaveNonWhitespaceCharacters);
71 if ("PUT-COMPANY-NAME-HERE" == manufacturer)
72 {
73 this.Core.Write(WarningMessages.PlaceholderValue(sourceLineNumbers, node.Name.LocalName, attrib.Name.LocalName, manufacturer));
74 }
75 break;
76 case "Name":
77 this.activeName = this.Core.GetAttributeValue(sourceLineNumbers, attrib, EmptyRule.MustHaveNonWhitespaceCharacters);
78 if ("PUT-PRODUCT-NAME-HERE" == this.activeName)
79 {
80 this.Core.Write(WarningMessages.PlaceholderValue(sourceLineNumbers, node.Name.LocalName, attrib.Name.LocalName, this.activeName));
81 }
82 break;
83 case "ProductCode":
84 productCode = this.Core.GetAttributeGuidValue(sourceLineNumbers, attrib, true);
85 break;
86 case "Scope":
87 var installScope = this.Core.GetAttributeValue(sourceLineNumbers, attrib);
88 switch (installScope)
89 {
90 case "perMachine":
91 // handled below after we create the section.
92 break;
93 case "perUser":
94 isPerMachine = false;
95 sourceBits |= 8;
96 break;
97 case "perUserOrMachine":
98 isPerMachine = false;
99 isPerUserOrMachine = true;
100 break;
101 default:
102 this.Core.Write(ErrorMessages.IllegalAttributeValue(sourceLineNumbers, node.Name.LocalName, attrib.Name.LocalName, installScope, "perMachine", "perUser", "perUserOrMachine"));
103 break;
104 }
105 break;
106 case "ShortNames":
107 if (YesNoType.Yes == this.Core.GetAttributeYesNoValue(sourceLineNumbers, attrib))
108 {
109 sourceBits |= 1;
110 }
111 break;
112 case "UpgradeCode":
113 upgradeCode = this.Core.GetAttributeGuidValue(sourceLineNumbers, attrib, false);
114 break;
115 case "UpgradeStrategy":
116 var strategy = this.Core.GetAttributeValue(sourceLineNumbers, attrib);
117 switch (strategy)
118 {
119 case "majorUpgrade":
120 upgradeStrategy = WixPackageUpgradeStrategy.MajorUpgrade;
121 break;
122 case "none":
123 upgradeStrategy = WixPackageUpgradeStrategy.None;
124 break;
125 default:
126 this.Core.Write(ErrorMessages.IllegalAttributeValue(sourceLineNumbers, node.Name.LocalName, attrib.Name.LocalName, strategy, "majorUpgrade", "none"));
127 break;
128 }
129 break;
130 case "Version":
131 version = this.Core.GetAttributeVersionValue(sourceLineNumbers, attrib);
132 break;
133 default:
134 this.Core.UnexpectedAttribute(node, attrib);
135 break;
136 }
137 }
138 else
139 {
140 this.Core.ParseExtensionAttribute(node, attrib);
141 }
142 }
143
144 if (null == productCode)
145 {
146 this.Core.Write(ErrorMessages.ExpectedAttribute(sourceLineNumbers, node.Name.LocalName, "Id"));
147 }
148
149 if (null == manufacturer)
150 {
151 this.Core.Write(ErrorMessages.ExpectedAttribute(sourceLineNumbers, node.Name.LocalName, "Manufacturer"));
152 }
153
154 if (null == this.activeName)
155 {
156 this.Core.Write(ErrorMessages.ExpectedAttribute(sourceLineNumbers, node.Name.LocalName, "Name"));
157 }
158
159 if (null == upgradeCode)
160 {
161 this.Core.Write(WarningMessages.MissingUpgradeCode(sourceLineNumbers));
162 }
163
164 if (null == version)
165 {
166 this.Core.Write(ErrorMessages.ExpectedAttribute(sourceLineNumbers, node.Name.LocalName, "Version"));
167 }
168
169 if (compressed != YesNoDefaultType.No)
170 {
171 sourceBits |= 2;
172 }
173
174 if (this.Core.EncounteredError)
175 {
176 return;
177 }
178
179 try
180 {
181 this.compilingProduct = true;
182 this.Core.CreateActiveSection(productCode, SectionType.Package, this.Context.CompilationId);
183
184 this.AddProperty(sourceLineNumbers, new Identifier(AccessModifier.Global, "Manufacturer"), manufacturer, false, false, false, true);
185 this.AddProperty(sourceLineNumbers, new Identifier(AccessModifier.Global, "ProductCode"), productCode, false, false, false, true);
186 this.AddProperty(sourceLineNumbers, new Identifier(AccessModifier.Global, "ProductLanguage"), productLanguage, false, false, false, true);
187 this.AddProperty(sourceLineNumbers, new Identifier(AccessModifier.Global, "ProductName"), this.activeName, false, false, false, true);
188 this.AddProperty(sourceLineNumbers, new Identifier(AccessModifier.Global, "ProductVersion"), version, false, false, false, true);
189 if (null != upgradeCode)
190 {
191 this.AddProperty(sourceLineNumbers, new Identifier(AccessModifier.Global, "UpgradeCode"), upgradeCode, false, false, false, true);
192 }
193
194 if (isPerUserOrMachine)
195 {
196 this.AddProperty(sourceLineNumbers, new Identifier(AccessModifier.Global, "ALLUSERS"), "2", false, false, false, false);
197 this.AddProperty(sourceLineNumbers, new Identifier(AccessModifier.Global, "MSIINSTALLPERUSER"), "1", false, false, false, false);
198 }
199 else if (isPerMachine)
200 {
201 this.AddProperty(sourceLineNumbers, new Identifier(AccessModifier.Global, "ALLUSERS"), "1", false, false, false, false);
202 }
203
204 this.Core.AddSymbol(new SummaryInformationSymbol(sourceLineNumbers)
205 {
206 PropertyId = SummaryInformationType.Title,
207 Value = "Installation Database"
208 });
209
210 this.ValidateAndAddCommonSummaryInformationSymbols(sourceLineNumbers, msiVersion, platform, productLanguage);
211
212 this.Core.AddSymbol(new SummaryInformationSymbol(sourceLineNumbers)
213 {
214 PropertyId = SummaryInformationType.WordCount,
215 Value = sourceBits.ToString(CultureInfo.InvariantCulture)
216 });
217
218 var contextValues = new Dictionary<string, string>
219 {
220 ["ProductLanguage"] = productLanguage,
221 ["ProductVersion"] = version,
222 ["UpgradeCode"] = upgradeCode
223 };
224
225 var featureDisplay = 0;
226 foreach (var child in node.Elements())
227 {
228 if (CompilerCore.WixNamespace == child.Name.Namespace)
229 {
230 switch (child.Name.LocalName)
231 {
232 case "_locDefinition":
233 break;
234 case "AdminExecuteSequence":
235 this.ParseSequenceElement(child, SequenceTable.AdminExecuteSequence);
236 break;
237 case "AdminUISequence":
238 this.ParseSequenceElement(child, SequenceTable.AdminUISequence);
239 break;
240 case "AdvertiseExecuteSequence":
241 this.ParseSequenceElement(child, SequenceTable.AdvertiseExecuteSequence);
242 break;
243 case "InstallExecuteSequence":
244 this.ParseSequenceElement(child, SequenceTable.InstallExecuteSequence);
245 break;
246 case "InstallUISequence":
247 this.ParseSequenceElement(child, SequenceTable.InstallUISequence);
248 break;
249 case "AppId":
250 this.ParseAppIdElement(child, null, YesNoType.Yes, null, null, null);
251 break;
252 case "Binary":
253 this.ParseBinaryElement(child);
254 break;
255 case "ComplianceCheck":
256 this.ParseComplianceCheckElement(child);
257 break;
258 case "Component":
259 this.ParseComponentElement(child, ComplexReferenceParentType.Product, null, null, CompilerConstants.IntegerNotSet, null, null);
260 break;
261 case "ComponentRef":
262 this.ParseComponentRefElement(child, ComplexReferenceParentType.Product, null, null);
263 break;
264 case "ComponentGroup":
265 this.ParseComponentGroupElement(child, ComplexReferenceParentType.Product, null);
266 break;
267 case "ComponentGroupRef":
268 this.ParseComponentGroupRefElement(child, ComplexReferenceParentType.Product, null, null);
269 break;
270 case "CustomAction":
271 this.ParseCustomActionElement(child);
272 break;
273 case "CustomActionRef":
274 this.ParseSimpleRefElement(child, SymbolDefinitions.CustomAction);
275 break;
276 case "CustomTable":
277 this.ParseCustomTableElement(child);
278 break;
279 case "CustomTableRef":
280 this.ParseCustomTableRefElement(child);
281 break;
282 case "Directory":
283 this.ParseDirectoryElement(child, null, CompilerConstants.IntegerNotSet, String.Empty);
284 break;
285 case "DirectoryRef":
286 this.ParseDirectoryRefElement(child);
287 break;
288 case "EmbeddedChainer":
289 this.ParseEmbeddedChainerElement(child);
290 break;
291 case "EmbeddedChainerRef":
292 this.ParseSimpleRefElement(child, SymbolDefinitions.MsiEmbeddedChainer);
293 break;
294 case "EnsureTable":
295 this.ParseEnsureTableElement(child);
296 break;
297 case "Feature":
298 this.ParseFeatureElement(child, ComplexReferenceParentType.Product, productCode, ref featureDisplay);
299 break;
300 case "FeatureRef":
301 this.ParseFeatureRefElement(child, ComplexReferenceParentType.Product, productCode);
302 break;
303 case "FeatureGroupRef":
304 this.ParseFeatureGroupRefElement(child, ComplexReferenceParentType.Product, productCode);
305 break;
306 case "File":
307 this.ParseNakedFileElement(child, ComplexReferenceParentType.Product, productCode, null, null);
308 break;
309 case "Files":
310 this.ParseFilesElement(child, ComplexReferenceParentType.Unknown, null, null, null);
311 break;
312 case "Icon":
313 this.ParseIconElement(child);
314 break;
315 case "InstanceTransforms":
316 this.ParseInstanceTransformsElement(child);
317 break;
318 case "Launch":
319 this.ParseLaunchElement(child);
320 break;
321 case "MajorUpgrade":
322 this.ParseMajorUpgradeElement(child, contextValues);
323 break;
324 case "Media":
325 this.ParseMediaElement(child, null);
326 break;
327 case "MediaTemplate":
328 this.ParseMediaTemplateElement(child, null);
329 break;
330 case "PackageCertificates":
331 case "PatchCertificates":
332 this.ParseCertificatesElement(child);
333 break;
334 case "Property":
335 this.ParsePropertyElement(child);
336 break;
337 case "PropertyRef":
338 this.ParseSimpleRefElement(child, SymbolDefinitions.Property);
339 break;
340 case "Requires":
341 this.ParseRequiresElement(child, null);
342 break;
343 case "SetDirectory":
344 this.ParseSetDirectoryElement(child);
345 break;
346 case "SetProperty":
347 this.ParseSetPropertyElement(child);
348 break;
349 case "SFPCatalog":
350 string parentName = null;
351 this.ParseSFPCatalogElement(child, ref parentName);
352 break;
353 case "SoftwareTag":
354 this.ParsePackageTagElement(child);
355 break;
356 case "StandardDirectory":
357 this.ParseStandardDirectoryElement(child);
358 break;
359 case "SummaryInformation":
360 this.ParseSummaryInformationElement(child, ref isCodepageSet, ref isCommentsSet, ref isPackageNameSet, ref isKeywordsSet, ref isPackageAuthorSet);
361 break;
362 case "SymbolPath":
363 if (null != symbols)
364 {
365 symbols += ";" + this.ParseSymbolPathElement(child);
366 }
367 else
368 {
369 symbols = this.ParseSymbolPathElement(child);
370 }
371 break;
372 case "UI":
373 this.ParseUIElement(child);
374 break;
375 case "UIRef":
376 this.ParseSimpleRefElement(child, SymbolDefinitions.WixUI);
377 break;
378 case "Upgrade":
379 this.ParseUpgradeElement(child);
380 break;
381 case "WixVariable":
382 this.ParseWixVariableElement(child);
383 break;
384 default:
385 this.Core.UnexpectedElement(node, child);
386 break;
387 }
388 }
389 else
390 {
391 this.Core.ParseExtensionElement(node, child);
392 }
393 }
394
395 if (!this.Core.EncounteredError)
396 {
397 this.Core.AddSymbol(new WixPackageSymbol(sourceLineNumbers)
398 {
399 PackageId = productCode,
400 UpgradeCode = upgradeCode,
401 Name = this.activeName,
402 Language = productLanguage,
403 Version = version,
404 Manufacturer = manufacturer,
405 Attributes = isPerMachine ? WixPackageAttributes.PerMachine : WixPackageAttributes.None,
406 Codepage = codepage,
407 UpgradeStrategy = upgradeStrategy,
408 });
409
410 if (!isCommentsSet)
411 {
412 this.Core.AddSymbol(new SummaryInformationSymbol(sourceLineNumbers)
413 {
414 PropertyId = SummaryInformationType.Comments,
415 Value = String.Format(CultureInfo.InvariantCulture, "This installer database contains the logic and data required to install {0}.", this.activeName)
416 });
417 }
418
419 if (!isPackageNameSet)
420 {
421 this.Core.AddSymbol(new SummaryInformationSymbol(sourceLineNumbers)
422 {
423 PropertyId = SummaryInformationType.Subject,
424 Value = this.activeName
425 });
426 }
427
428 if (!isPackageAuthorSet)
429 {
430 this.Core.AddSymbol(new SummaryInformationSymbol(sourceLineNumbers)
431 {
432 PropertyId = SummaryInformationType.Author,
433 Value = manufacturer
434 });
435 }
436
437 if (!isKeywordsSet)
438 {
439 this.Core.AddSymbol(new SummaryInformationSymbol(sourceLineNumbers)
440 {
441 PropertyId = SummaryInformationType.Keywords,
442 Value = "Installer"
443 });
444 }
445
446 if (null != symbols)
447 {
448 this.Core.AddSymbol(new WixDeltaPatchSymbolPathsSymbol(sourceLineNumbers)
449 {
450 SymbolId = productCode,
451 SymbolType = SymbolPathType.Product,
452 SymbolPaths = symbols,
453 });
454 }
455
456 this.Core.CreateSimpleReference(sourceLineNumbers, SymbolDefinitions.WixFragment, WixStandardLibraryIdentifiers.WixStandardPackageReferences);
457 }
458 }
459 finally
460 {
461 this.compilingProduct = false;
462 }
463 }
464
465 private void GetDefaultPlatformAndInstallerVersion(out string platform, out int msiVersion)
466 {
467 // Let's default to a modern version of MSI. Users can override,
468 // of course, subject to platform-specific limitations.
469 msiVersion = 500;
470
471 switch (this.CurrentPlatform)
472 {
473 case Platform.X86:
474 platform = "Intel";
475 break;
476 case Platform.X64:
477 platform = "x64";
478 break;
479 case Platform.ARM64:
480 platform = "Arm64";
481 break;
482 default:
483 throw new ArgumentException("Unknown platform enumeration '{0}' encountered.", this.CurrentPlatform.ToString());
484 }
485 }
486
487 private void ValidateAndAddCommonSummaryInformationSymbols(SourceLineNumber sourceLineNumbers, int msiVersion, string platform, string language)
488 {
489 if (String.Equals(platform, "X64", StringComparison.OrdinalIgnoreCase) && 200 > msiVersion)
490 {
491 msiVersion = 200;
492 this.Core.Write(WarningMessages.RequiresMsi200for64bitPackage(sourceLineNumbers));
493 }
494
495 if (String.Equals(platform, "Arm64", StringComparison.OrdinalIgnoreCase) && 500 > msiVersion)
496 {
497 msiVersion = 500;
498 this.Core.Write(WarningMessages.RequiresMsi500forArmPackage(sourceLineNumbers));
499 }
500
501 this.Core.AddSymbol(new SummaryInformationSymbol(sourceLineNumbers)
502 {
503 PropertyId = SummaryInformationType.PlatformAndLanguage,
504 Value = $"{platform};{language}"
505 });
506
507 this.Core.AddSymbol(new SummaryInformationSymbol(sourceLineNumbers)
508 {
509 PropertyId = SummaryInformationType.WindowsInstallerVersion,
510 Value = msiVersion.ToString(CultureInfo.InvariantCulture)
511 });
512
513 this.Core.AddSymbol(new SummaryInformationSymbol(sourceLineNumbers)
514 {
515 PropertyId = SummaryInformationType.Security,
516 Value = "2"
517 });
518 }
519
520 /// <summary>
521 /// Parses an odbc driver or translator element.
522 /// </summary>
523 /// <param name="node">Element to parse.</param>
524 /// <param name="componentId">Identifier of parent component.</param>
525 /// <param name="fileId">Default identifer for driver/translator file.</param>
526 /// <param name="symbolDefinitionType">Symbol type we're processing for.</param>
527 private void ParseODBCDriverOrTranslator(XElement node, string componentId, string fileId, SymbolDefinitionType symbolDefinitionType)
528 {
529 var sourceLineNumbers = Preprocessor.GetSourceLineNumbers(node);
530 Identifier id = null;
531 var driver = fileId;
532 string name = null;
533 var setup = fileId;
534
535 foreach (var attrib in node.Attributes())
536 {
537 if (String.IsNullOrEmpty(attrib.Name.NamespaceName) || CompilerCore.WixNamespace == attrib.Name.Namespace)
538 {
539 switch (attrib.Name.LocalName)
540 {
541 case "Id":
542 id = this.Core.GetAttributeIdentifier(sourceLineNumbers, attrib);
543 break;
544 case "File":
545 driver = this.Core.GetAttributeIdentifierValue(sourceLineNumbers, attrib);
546 this.Core.CreateSimpleReference(sourceLineNumbers, SymbolDefinitions.File, driver);
547 break;
548 case "Name":
549 name = this.Core.GetAttributeValue(sourceLineNumbers, attrib);
550 break;
551 case "SetupFile":
552 setup = this.Core.GetAttributeIdentifierValue(sourceLineNumbers, attrib);
553 this.Core.CreateSimpleReference(sourceLineNumbers, SymbolDefinitions.File, setup);
554 break;
555 default:
556 this.Core.UnexpectedAttribute(node, attrib);
557 break;
558 }
559 }
560 else
561 {
562 this.Core.ParseExtensionAttribute(node, attrib);
563 }
564 }
565
566 if (null == name)
567 {
568 this.Core.Write(ErrorMessages.ExpectedAttribute(sourceLineNumbers, node.Name.LocalName, "Name"));
569 }
570
571 if (null == id)
572 {
573 id = this.Core.CreateIdentifier("odb", name, fileId, setup);
574 }
575
576 // drivers have a few possible children
577 if (SymbolDefinitionType.ODBCDriver == symbolDefinitionType)
578 {
579 // process any data sources for the driver
580 foreach (var child in node.Elements())
581 {
582 if (CompilerCore.WixNamespace == child.Name.Namespace)
583 {
584 switch (child.Name.LocalName)
585 {
586 case "ODBCDataSource":
587 this.ParseODBCDataSource(child, componentId, name, out _);
588 break;
589 case "Property":
590 this.ParseODBCProperty(child, id.Id, SymbolDefinitionType.ODBCAttribute);
591 break;
592 default:
593 this.Core.UnexpectedElement(node, child);
594 break;
595 }
596 }
597 else
598 {
599 this.Core.ParseExtensionElement(node, child);
600 }
601 }
602 }
603 else
604 {
605 this.Core.ParseForExtensionElements(node);
606 }
607
608 if (!this.Core.EncounteredError)
609 {
610 switch (symbolDefinitionType)
611 {
612 case SymbolDefinitionType.ODBCDriver:
613 this.Core.AddSymbol(new ODBCDriverSymbol(sourceLineNumbers, id)
614 {
615 ComponentRef = componentId,
616 Description = name,
617 FileRef = driver,
618 SetupFileRef = setup,
619 });
620 break;
621 case SymbolDefinitionType.ODBCTranslator:
622 this.Core.AddSymbol(new ODBCTranslatorSymbol(sourceLineNumbers, id)
623 {
624 ComponentRef = componentId,
625 Description = name,
626 FileRef = driver,
627 SetupFileRef = setup,
628 });
629 break;
630 default:
631 throw new ArgumentOutOfRangeException(nameof(symbolDefinitionType));
632 }
633 }
634 }
635
636 /// <summary>
637 /// Parses a Property element underneath an ODBC driver or translator.
638 /// </summary>
639 /// <param name="node">Element to parse.</param>
640 /// <param name="parentId">Identifier of parent driver or translator.</param>
641 /// <param name="symbolDefinitionType">Name of the table to create property in.</param>
642 private void ParseODBCProperty(XElement node, string parentId, SymbolDefinitionType symbolDefinitionType)
643 {
644 var sourceLineNumbers = Preprocessor.GetSourceLineNumbers(node);
645 string id = null;
646 string propertyValue = null;
647
648 foreach (var attrib in node.Attributes())
649 {
650 if (String.IsNullOrEmpty(attrib.Name.NamespaceName) || CompilerCore.WixNamespace == attrib.Name.Namespace)
651 {
652 switch (attrib.Name.LocalName)
653 {
654 case "Id":
655 id = this.Core.GetAttributeValue(sourceLineNumbers, attrib);
656 break;
657 case "Value":
658 propertyValue = this.Core.GetAttributeValue(sourceLineNumbers, attrib);
659 break;
660 default:
661 this.Core.UnexpectedAttribute(node, attrib);
662 break;
663 }
664 }
665 else
666 {
667 this.Core.ParseExtensionAttribute(node, attrib);
668 }
669 }
670
671 if (null == id)
672 {
673 this.Core.Write(ErrorMessages.ExpectedAttribute(sourceLineNumbers, node.Name.LocalName, "Id"));
674 }
675
676 this.Core.ParseForExtensionElements(node);
677
678 if (!this.Core.EncounteredError)
679 {
680 var identifier = new Identifier(AccessModifier.Section, parentId, id);
681 switch (symbolDefinitionType)
682 {
683 case SymbolDefinitionType.ODBCAttribute:
684 this.Core.AddSymbol(new ODBCAttributeSymbol(sourceLineNumbers, identifier)
685 {
686 DriverRef = parentId,
687 Attribute = id,
688 Value = propertyValue,
689 });
690 break;
691 case SymbolDefinitionType.ODBCSourceAttribute:
692 this.Core.AddSymbol(new ODBCSourceAttributeSymbol(sourceLineNumbers, identifier)
693 {
694 DataSourceRef = parentId,
695 Attribute = id,
696 Value = propertyValue,
697 });
698 break;
699 default:
700 throw new ArgumentOutOfRangeException(nameof(symbolDefinitionType));
701 }
702 }
703 }
704
705 /// <summary>
706 /// Parse an odbc data source element.
707 /// </summary>
708 /// <param name="node">Element to parse.</param>
709 /// <param name="componentId">Identifier of parent component.</param>
710 /// <param name="driverName">Default name of driver.</param>
711 /// <param name="possibleKeyPath">Identifier of this element in case it is a keypath.</param>
712 /// <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>
713 private YesNoType ParseODBCDataSource(XElement node, string componentId, string driverName, out Identifier possibleKeyPath)
714 {
715 var sourceLineNumbers = Preprocessor.GetSourceLineNumbers(node);
716 Identifier id = null;
717 var keyPath = YesNoType.NotSet;
718 string name = null;
719 var registration = CompilerConstants.IntegerNotSet;
720
721 foreach (var attrib in node.Attributes())
722 {
723 if (String.IsNullOrEmpty(attrib.Name.NamespaceName) || CompilerCore.WixNamespace == attrib.Name.Namespace)
724 {
725 switch (attrib.Name.LocalName)
726 {
727 case "Id":
728 id = this.Core.GetAttributeIdentifier(sourceLineNumbers, attrib);
729 break;
730 case "DriverName":
731 driverName = this.Core.GetAttributeValue(sourceLineNumbers, attrib);
732 break;
733 case "KeyPath":
734 keyPath = this.Core.GetAttributeYesNoValue(sourceLineNumbers, attrib);
735 break;
736 case "Name":
737 name = this.Core.GetAttributeValue(sourceLineNumbers, attrib);
738 break;
739 case "Registration":
740 var registrationValue = this.Core.GetAttributeValue(sourceLineNumbers, attrib);
741 switch (registrationValue)
742 {
743 case "machine":
744 registration = 0;
745 break;
746 case "user":
747 registration = 1;
748 break;
749 case "":
750 break;
751 default:
752 this.Core.Write(ErrorMessages.IllegalAttributeValue(sourceLineNumbers, node.Name.LocalName, "Registration", registrationValue, "machine", "user"));
753 break;
754 }
755 break;
756 default:
757 this.Core.UnexpectedAttribute(node, attrib);
758 break;
759 }
760 }
761 else
762 {
763 this.Core.ParseExtensionAttribute(node, attrib);
764 }
765 }
766
767 if (CompilerConstants.IntegerNotSet == registration)
768 {
769 this.Core.Write(ErrorMessages.ExpectedAttribute(sourceLineNumbers, node.Name.LocalName, "Registration"));
770 registration = CompilerConstants.IllegalInteger;
771 }
772
773 if (null == id)
774 {
775 id = this.Core.CreateIdentifier("odc", name, driverName, registration.ToString());
776 }
777
778 foreach (var child in node.Elements())
779 {
780 if (CompilerCore.WixNamespace == child.Name.Namespace)
781 {
782 switch (child.Name.LocalName)
783 {
784 case "Property":
785 this.ParseODBCProperty(child, id.Id, SymbolDefinitionType.ODBCSourceAttribute);
786 break;
787 default:
788 this.Core.UnexpectedElement(node, child);
789 break;
790 }
791 }
792 else
793 {
794 this.Core.ParseExtensionElement(node, child);
795 }
796 }
797
798 if (!this.Core.EncounteredError)
799 {
800 this.Core.AddSymbol(new ODBCDataSourceSymbol(sourceLineNumbers, id)
801 {
802 ComponentRef = componentId,
803 Description = name,
804 DriverDescription = driverName,
805 Registration = registration
806 });
807 }
808
809 possibleKeyPath = id;
810 return keyPath;
811 }
812
813 /// <summary>
814 /// Parses a package element.
815 /// </summary>
816 /// <param name="node">Element to parse.</param>
817 /// <param name="isCodepageSet"></param>
818 /// <param name="isCommentsSet"></param>
819 /// <param name="isPackageNameSet"></param>
820 /// <param name="isKeywordsSet"></param>
821 /// <param name="isPackageAuthorSet"></param>
822 private void ParseSummaryInformationElement(XElement node, ref bool isCodepageSet, ref bool isCommentsSet, ref bool isPackageNameSet, ref bool isKeywordsSet, ref bool isPackageAuthorSet)
823 {
824 var sourceLineNumbers = Preprocessor.GetSourceLineNumbers(node);
825 string codepage = null;
826 string comments = null;
827 string packageName = null;
828 string keywords = null;
829 string packageAuthor = null;
830
831 foreach (var attrib in node.Attributes())
832 {
833 if (String.IsNullOrEmpty(attrib.Name.NamespaceName) || CompilerCore.WixNamespace == attrib.Name.Namespace)
834 {
835 switch (attrib.Name.LocalName)
836 {
837 case "Codepage":
838 codepage = this.Core.GetAttributeLocalizableCodePageValue(sourceLineNumbers, attrib, true);
839 break;
840 case "Comments":
841 comments = this.Core.GetAttributeValue(sourceLineNumbers, attrib);
842 break;
843 case "Description":
844 packageName = this.Core.GetAttributeValue(sourceLineNumbers, attrib);
845 break;
846 case "Keywords":
847 keywords = this.Core.GetAttributeValue(sourceLineNumbers, attrib);
848 break;
849 case "Manufacturer":
850 packageAuthor = this.Core.GetAttributeValue(sourceLineNumbers, attrib);
851 if ("PUT-COMPANY-NAME-HERE" == packageAuthor)
852 {
853 this.Core.Write(WarningMessages.PlaceholderValue(sourceLineNumbers, node.Name.LocalName, attrib.Name.LocalName, packageAuthor));
854 }
855 break;
856 default:
857 this.Core.UnexpectedAttribute(node, attrib);
858 break;
859 }
860 }
861 else
862 {
863 this.Core.ParseExtensionAttribute(node, attrib);
864 }
865 }
866
867 this.Core.ParseForExtensionElements(node);
868
869 if (!this.Core.EncounteredError)
870 {
871 if (null != codepage)
872 {
873 isCodepageSet = true;
874 this.Core.AddSymbol(new SummaryInformationSymbol(sourceLineNumbers)
875 {
876 PropertyId = SummaryInformationType.Codepage,
877 Value = codepage
878 });
879 }
880
881 if (null != comments)
882 {
883 isCommentsSet = true;
884 this.Core.AddSymbol(new SummaryInformationSymbol(sourceLineNumbers)
885 {
886 PropertyId = SummaryInformationType.Comments,
887 Value = comments
888 });
889 }
890
891 if (null != packageName)
892 {
893 isPackageNameSet = true;
894 this.Core.AddSymbol(new SummaryInformationSymbol(sourceLineNumbers)
895 {
896 PropertyId = SummaryInformationType.Subject,
897 Value = packageName
898 });
899 }
900
901 if (null != packageAuthor)
902 {
903 isPackageAuthorSet = true;
904 this.Core.AddSymbol(new SummaryInformationSymbol(sourceLineNumbers)
905 {
906 PropertyId = SummaryInformationType.Author,
907 Value = packageAuthor
908 });
909 }
910
911 if (null != keywords)
912 {
913 isKeywordsSet = true;
914 this.Core.AddSymbol(new SummaryInformationSymbol(sourceLineNumbers)
915 {
916 PropertyId = SummaryInformationType.Keywords,
917 Value = keywords
918 });
919 }
920 }
921 }
922
923 /// <summary>
924 /// Parses a patch information element.
925 /// </summary>
926 /// <param name="node">Element to parse.</param>
927 private void ParsePatchInformationElement(XElement node)
928 {
929 var sourceLineNumbers = Preprocessor.GetSourceLineNumbers(node);
930 var codepage = "1252";
931 string comments = null;
932 var keywords = "Installer,Patching,PCP,Database";
933 var msiVersion = 1; // Should always be 1 for patches
934 string packageAuthor = null;
935 var packageName = this.activeName;
936 var security = YesNoDefaultType.Default;
937
938 foreach (var attrib in node.Attributes())
939 {
940 if (String.IsNullOrEmpty(attrib.Name.NamespaceName) || CompilerCore.WixNamespace == attrib.Name.Namespace)
941 {
942 switch (attrib.Name.LocalName)
943 {
944 case "AdminImage":
945 this.Core.Write(WarningMessages.DeprecatedAttribute(sourceLineNumbers, node.Name.LocalName, attrib.Name.LocalName));
946 break;
947 case "Comments":
948 comments = this.Core.GetAttributeValue(sourceLineNumbers, attrib);
949 break;
950 case "Compressed":
951 this.Core.Write(WarningMessages.DeprecatedAttribute(sourceLineNumbers, node.Name.LocalName, attrib.Name.LocalName));
952 break;
953 case "Description":
954 packageName = this.Core.GetAttributeValue(sourceLineNumbers, attrib);
955 break;
956 case "Keywords":
957 keywords = this.Core.GetAttributeValue(sourceLineNumbers, attrib);
958 break;
959 case "Languages":
960 this.Core.Write(WarningMessages.DeprecatedAttribute(sourceLineNumbers, node.Name.LocalName, attrib.Name.LocalName));
961 break;
962 case "Manufacturer":
963 packageAuthor = this.Core.GetAttributeValue(sourceLineNumbers, attrib);
964 break;
965 case "Platforms":
966 this.Core.Write(WarningMessages.DeprecatedAttribute(sourceLineNumbers, node.Name.LocalName, attrib.Name.LocalName));
967 break;
968 case "ReadOnly":
969 security = this.Core.GetAttributeYesNoDefaultValue(sourceLineNumbers, attrib);
970 break;
971 case "ShortNames":
972 this.Core.Write(WarningMessages.DeprecatedAttribute(sourceLineNumbers, node.Name.LocalName, attrib.Name.LocalName));
973 break;
974 case "SummaryCodepage":
975 codepage = this.Core.GetAttributeLocalizableCodePageValue(sourceLineNumbers, attrib);
976 break;
977 default:
978 this.Core.UnexpectedAttribute(node, attrib);
979 break;
980 }
981 }
982 else
983 {
984 this.Core.ParseExtensionAttribute(node, attrib);
985 }
986 }
987
988 this.Core.ParseForExtensionElements(node);
989
990 if (!this.Core.EncounteredError)
991 {
992 this.Core.AddSymbol(new SummaryInformationSymbol(sourceLineNumbers)
993 {
994 PropertyId = SummaryInformationType.Codepage,
995 Value = codepage
996 });
997
998 this.Core.AddSymbol(new SummaryInformationSymbol(sourceLineNumbers)
999 {
1000 PropertyId = SummaryInformationType.Title,
1001 Value = "Patch"
1002 });
1003
1004 if (null != packageName)
1005 {
1006 this.Core.AddSymbol(new SummaryInformationSymbol(sourceLineNumbers)
1007 {
1008 PropertyId = SummaryInformationType.Subject,
1009 Value = packageName
1010 });
1011 }
1012
1013 if (null != packageAuthor)
1014 {
1015 this.Core.AddSymbol(new SummaryInformationSymbol(sourceLineNumbers)
1016 {
1017 PropertyId = SummaryInformationType.Author,
1018 Value = packageAuthor
1019 });
1020 }
1021
1022 if (null != keywords)
1023 {
1024 this.Core.AddSymbol(new SummaryInformationSymbol(sourceLineNumbers)
1025 {
1026 PropertyId = SummaryInformationType.Keywords,
1027 Value = keywords
1028 });
1029 }
1030
1031 if (null != comments)
1032 {
1033 this.Core.AddSymbol(new SummaryInformationSymbol(sourceLineNumbers)
1034 {
1035 PropertyId = SummaryInformationType.Comments,
1036 Value = comments
1037 });
1038 }
1039
1040 this.Core.AddSymbol(new SummaryInformationSymbol(sourceLineNumbers)
1041 {
1042 PropertyId = SummaryInformationType.WindowsInstallerVersion,
1043 Value = msiVersion.ToString(CultureInfo.InvariantCulture)
1044 });
1045
1046 this.Core.AddSymbol(new SummaryInformationSymbol(sourceLineNumbers)
1047 {
1048 PropertyId = SummaryInformationType.WordCount,
1049 Value = "0"
1050 });
1051
1052 this.Core.AddSymbol(new SummaryInformationSymbol(sourceLineNumbers)
1053 {
1054 PropertyId = SummaryInformationType.Security,
1055 Value = YesNoDefaultType.No == security ? "0" : YesNoDefaultType.Yes == security ? "4" : "2"
1056 });
1057 }
1058 }
1059
1060 /// <summary>
1061 /// Parses a permission element.
1062 /// </summary>
1063 /// <param name="node">Element to parse.</param>
1064 /// <param name="objectId">Identifier of object to be secured.</param>
1065 /// <param name="tableName">Name of table that contains objectId.</param>
1066 private void ParsePermissionElement(XElement node, string objectId, string tableName)
1067 {
1068 var sourceLineNumbers = Preprocessor.GetSourceLineNumbers(node);
1069 var bits = new BitArray(32);
1070 string domain = null;
1071 string[] specialPermissions;
1072 string user = null;
1073
1074 switch (tableName)
1075 {
1076 case "CreateFolder":
1077 specialPermissions = LockPermissionConstants.FolderPermissions;
1078 break;
1079 case "File":
1080 specialPermissions = LockPermissionConstants.FilePermissions;
1081 break;
1082 case "Registry":
1083 specialPermissions = LockPermissionConstants.RegistryPermissions;
1084 break;
1085 default:
1086 this.Core.UnexpectedElement(node.Parent, node);
1087 return; // stop processing this element since no valid permissions are available
1088 }
1089
1090 foreach (var attrib in node.Attributes())
1091 {
1092 if (String.IsNullOrEmpty(attrib.Name.NamespaceName) || CompilerCore.WixNamespace == attrib.Name.Namespace)
1093 {
1094 switch (attrib.Name.LocalName)
1095 {
1096 case "Domain":
1097 domain = this.Core.GetAttributeValue(sourceLineNumbers, attrib);
1098 break;
1099 case "User":
1100 user = this.Core.GetAttributeValue(sourceLineNumbers, attrib);
1101 break;
1102 case "FileAllRights":
1103 // match the WinNT.h mask FILE_ALL_ACCESS for value 0x001F01FF (aka 1 1111 0000 0001 1111 1111 or 2032127)
1104 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;
1105 break;
1106 case "SpecificRightsAll":
1107 // match the WinNT.h mask SPECIFIC_RIGHTS_ALL for value 0x0000FFFF (aka 1111 1111 1111 1111)
1108 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;
1109 break;
1110 default:
1111 var attribValue = this.Core.GetAttributeYesNoValue(sourceLineNumbers, attrib);
1112 if (!this.Core.TrySetBitFromName(LockPermissionConstants.StandardPermissions, attrib.Name.LocalName, attribValue, bits, 16))
1113 {
1114 if (!this.Core.TrySetBitFromName(LockPermissionConstants.GenericPermissions, attrib.Name.LocalName, attribValue, bits, 28))
1115 {
1116 if (!this.Core.TrySetBitFromName(specialPermissions, attrib.Name.LocalName, attribValue, bits, 0))
1117 {
1118 this.Core.UnexpectedAttribute(node, attrib);
1119 break;
1120 }
1121 }
1122 }
1123 break;
1124 }
1125 }
1126 else
1127 {
1128 this.Core.ParseExtensionAttribute(node, attrib);
1129 }
1130 }
1131
1132 if (null == user)
1133 {
1134 this.Core.Write(ErrorMessages.ExpectedAttribute(sourceLineNumbers, node.Name.LocalName, "User"));
1135 }
1136
1137 var permission = this.Core.CreateIntegerFromBitArray(bits);
1138
1139 if (Int32.MinValue == permission) // just GENERIC_READ, which is MSI_NULL
1140 {
1141 this.Core.Write(ErrorMessages.GenericReadNotAllowed(sourceLineNumbers));
1142 }
1143
1144 this.Core.ParseForExtensionElements(node);
1145
1146 if (!this.Core.EncounteredError)
1147 {
1148 this.Core.AddSymbol(new LockPermissionsSymbol(sourceLineNumbers)
1149 {
1150 LockObject = objectId,
1151 Table = tableName,
1152 Domain = domain,
1153 User = user,
1154 Permission = permission
1155 });
1156 }
1157 }
1158
1159 /// <summary>
1160 /// Parses an extended permission element.
1161 /// </summary>
1162 /// <param name="node">Element to parse.</param>
1163 /// <param name="objectId">Identifier of object to be secured.</param>
1164 /// <param name="tableName">Name of table that contains objectId.</param>
1165 private void ParsePermissionExElement(XElement node, string objectId, string tableName)
1166 {
1167 var sourceLineNumbers = Preprocessor.GetSourceLineNumbers(node);
1168 string condition = null;
1169 Identifier id = null;
1170 string sddl = null;
1171
1172 switch (tableName)
1173 {
1174 case "CreateFolder":
1175 case "File":
1176 case "Registry":
1177 case "ServiceInstall":
1178 break;
1179 default:
1180 this.Core.UnexpectedElement(node.Parent, node);
1181 return; // stop processing this element since nothing will be valid.
1182 }
1183
1184 foreach (var attrib in node.Attributes())
1185 {
1186 if (String.IsNullOrEmpty(attrib.Name.NamespaceName) || CompilerCore.WixNamespace == attrib.Name.Namespace)
1187 {
1188 switch (attrib.Name.LocalName)
1189 {
1190 case "Id":
1191 id = this.Core.GetAttributeIdentifier(sourceLineNumbers, attrib);
1192 break;
1193 case "Condition":
1194 condition = this.Core.GetAttributeValue(sourceLineNumbers, attrib);
1195 break;
1196 case "Sddl":
1197 sddl = this.Core.GetAttributeValue(sourceLineNumbers, attrib);
1198 break;
1199 default:
1200 this.Core.UnexpectedAttribute(node, attrib);
1201 break;
1202 }
1203 }
1204 else
1205 {
1206 this.Core.ParseExtensionAttribute(node, attrib);
1207 }
1208 }
1209
1210 if (null == sddl)
1211 {
1212 this.Core.Write(ErrorMessages.ExpectedAttribute(sourceLineNumbers, node.Name.LocalName, "Sddl"));
1213 }
1214
1215 if (null == id)
1216 {
1217 id = this.Core.CreateIdentifier("pme", objectId, tableName, sddl);
1218 }
1219
1220 this.Core.ParseForExtensionElements(node);
1221
1222 if (!this.Core.EncounteredError)
1223 {
1224 this.Core.AddSymbol(new MsiLockPermissionsExSymbol(sourceLineNumbers, id)
1225 {
1226 LockObject = objectId,
1227 Table = tableName,
1228 SDDLText = sddl,
1229 Condition = condition
1230 });
1231 }
1232 }
1233
1234 /// <summary>
1235 /// Parses a progid element
1236 /// </summary>
1237 /// <param name="node">Element to parse.</param>
1238 /// <param name="componentId">Identifier of parent component.</param>
1239 /// <param name="advertise">Flag if progid is advertised.</param>
1240 /// <param name="classId">CLSID related to ProgId.</param>
1241 /// <param name="description">Default description of ProgId</param>
1242 /// <param name="parent">Optional parent ProgId</param>
1243 /// <param name="foundExtension">Set to true if an extension is found; used for error-checking.</param>
1244 /// <param name="firstProgIdForClass">Whether or not this ProgId is the first one found in the parent class.</param>
1245 /// <returns>This element's Id.</returns>
1246 private string ParseProgIdElement(XElement node, string componentId, YesNoType advertise, string classId, string description, string parent, ref bool foundExtension, YesNoType firstProgIdForClass)
1247 {
1248 var sourceLineNumbers = Preprocessor.GetSourceLineNumbers(node);
1249 string icon = null;
1250 var iconIndex = CompilerConstants.IntegerNotSet;
1251 string noOpen = null;
1252 string progId = null;
1253 var progIdAdvertise = YesNoType.NotSet;
1254
1255 foreach (var attrib in node.Attributes())
1256 {
1257 if (String.IsNullOrEmpty(attrib.Name.NamespaceName) || CompilerCore.WixNamespace == attrib.Name.Namespace)
1258 {
1259 switch (attrib.Name.LocalName)
1260 {
1261 case "Id":
1262 progId = this.Core.GetAttributeValue(sourceLineNumbers, attrib);
1263 break;
1264 case "Advertise":
1265 progIdAdvertise = this.Core.GetAttributeYesNoValue(sourceLineNumbers, attrib);
1266 break;
1267 case "Description":
1268 description = this.Core.GetAttributeValue(sourceLineNumbers, attrib, EmptyRule.CanBeEmpty);
1269 break;
1270 case "Icon":
1271 icon = this.Core.GetAttributeIdentifierValue(sourceLineNumbers, attrib);
1272 break;
1273 case "IconIndex":
1274 iconIndex = this.Core.GetAttributeIntegerValue(sourceLineNumbers, attrib, Int16.MinValue + 1, Int16.MaxValue);
1275 break;
1276 case "NoOpen":
1277 noOpen = this.Core.GetAttributeValue(sourceLineNumbers, attrib, EmptyRule.CanBeEmpty);
1278 break;
1279 default:
1280 this.Core.UnexpectedAttribute(node, attrib);
1281 break;
1282 }
1283 }
1284 else
1285 {
1286 this.Core.ParseExtensionAttribute(node, attrib);
1287 }
1288 }
1289
1290 if ((YesNoType.No == advertise && YesNoType.Yes == progIdAdvertise) || (YesNoType.Yes == advertise && YesNoType.No == progIdAdvertise))
1291 {
1292 this.Core.Write(ErrorMessages.AdvertiseStateMustMatch(sourceLineNumbers, advertise.ToString(), progIdAdvertise.ToString()));
1293 }
1294 else if (YesNoType.NotSet != progIdAdvertise)
1295 {
1296 advertise = progIdAdvertise;
1297 }
1298
1299 if (YesNoType.NotSet == advertise)
1300 {
1301 advertise = YesNoType.No;
1302 }
1303
1304 if (null != parent && (null != icon || CompilerConstants.IntegerNotSet != iconIndex))
1305 {
1306 this.Core.Write(ErrorMessages.VersionIndependentProgIdsCannotHaveIcons(sourceLineNumbers));
1307 }
1308
1309 var firstProgIdForNestedClass = YesNoType.Yes;
1310 foreach (var child in node.Elements())
1311 {
1312 if (CompilerCore.WixNamespace == child.Name.Namespace)
1313 {
1314 switch (child.Name.LocalName)
1315 {
1316 case "Extension":
1317 this.ParseExtensionElement(child, componentId, advertise, progId);
1318 foundExtension = true;
1319 break;
1320 case "ProgId":
1321 // Only allow one nested ProgId. If we have a child, we should not have a parent.
1322 if (null == parent)
1323 {
1324 if (YesNoType.Yes == advertise)
1325 {
1326 this.ParseProgIdElement(child, componentId, advertise, null, description, progId, ref foundExtension, firstProgIdForNestedClass);
1327 }
1328 else if (YesNoType.No == advertise)
1329 {
1330 this.ParseProgIdElement(child, componentId, advertise, classId, description, progId, ref foundExtension, firstProgIdForNestedClass);
1331 }
1332
1333 firstProgIdForNestedClass = YesNoType.No; // any ProgId after this one is definitely not the first.
1334 }
1335 else
1336 {
1337 var childSourceLineNumbers = Preprocessor.GetSourceLineNumbers(child);
1338 this.Core.Write(ErrorMessages.ProgIdNestedTooDeep(childSourceLineNumbers));
1339 }
1340 break;
1341 default:
1342 this.Core.UnexpectedElement(node, child);
1343 break;
1344 }
1345 }
1346 else
1347 {
1348 this.Core.ParseExtensionElement(node, child);
1349 }
1350 }
1351
1352 if (YesNoType.Yes == advertise)
1353 {
1354 if (!this.Core.EncounteredError)
1355 {
1356 var symbol = this.Core.AddSymbol(new ProgIdSymbol(sourceLineNumbers, new Identifier(AccessModifier.Global, progId))
1357 {
1358 ProgId = progId,
1359 ParentProgIdRef = parent,
1360 ClassRef = classId,
1361 Description = description,
1362 });
1363
1364 if (null != icon)
1365 {
1366 symbol.IconRef = icon;
1367 this.Core.CreateSimpleReference(sourceLineNumbers, SymbolDefinitions.Icon, icon);
1368 }
1369
1370 if (CompilerConstants.IntegerNotSet != iconIndex)
1371 {
1372 symbol.IconIndex = iconIndex;
1373 }
1374
1375 this.Core.EnsureTable(sourceLineNumbers, WindowsInstallerTableDefinitions.Class);
1376 }
1377 }
1378 else if (YesNoType.No == advertise)
1379 {
1380 this.Core.CreateRegistryStringSymbol(sourceLineNumbers, RegistryRootType.ClassesRoot, progId, String.Empty, description, componentId);
1381 if (null != classId)
1382 {
1383 this.Core.CreateRegistryStringSymbol(sourceLineNumbers, RegistryRootType.ClassesRoot, String.Concat(progId, "\\CLSID"), String.Empty, classId, componentId);
1384 if (null != parent) // if this is a version independent ProgId
1385 {
1386 if (YesNoType.Yes == firstProgIdForClass)
1387 {
1388 this.Core.CreateRegistryStringSymbol(sourceLineNumbers, RegistryRootType.ClassesRoot, String.Concat("CLSID\\", classId, "\\VersionIndependentProgID"), String.Empty, progId, componentId);
1389 }
1390
1391 this.Core.CreateRegistryStringSymbol(sourceLineNumbers, RegistryRootType.ClassesRoot, String.Concat(progId, "\\CurVer"), String.Empty, parent, componentId);
1392 }
1393 else
1394 {
1395 if (YesNoType.Yes == firstProgIdForClass)
1396 {
1397 this.Core.CreateRegistryStringSymbol(sourceLineNumbers, RegistryRootType.ClassesRoot, String.Concat("CLSID\\", classId, "\\ProgID"), String.Empty, progId, componentId);
1398 }
1399 }
1400 }
1401
1402 if (null != icon) // ProgId's Default Icon
1403 {
1404 this.Core.CreateSimpleReference(sourceLineNumbers, SymbolDefinitions.File, icon);
1405
1406 icon = String.Format(CultureInfo.InvariantCulture, "\"[#{0}]\"", icon);
1407
1408 if (CompilerConstants.IntegerNotSet != iconIndex)
1409 {
1410 icon = String.Concat(icon, ",", iconIndex);
1411 }
1412
1413 this.Core.CreateRegistryStringSymbol(sourceLineNumbers, RegistryRootType.ClassesRoot, String.Concat(progId, "\\DefaultIcon"), String.Empty, icon, componentId);
1414 }
1415 }
1416
1417 if (null != noOpen)
1418 {
1419 this.Core.CreateRegistryStringSymbol(sourceLineNumbers, RegistryRootType.ClassesRoot, progId, "NoOpen", noOpen, componentId); // ProgId NoOpen name
1420 }
1421
1422 // raise an error for an orphaned ProgId
1423 if (YesNoType.Yes == advertise && !foundExtension && null == parent && null == classId)
1424 {
1425 this.Core.Write(WarningMessages.OrphanedProgId(sourceLineNumbers, progId));
1426 }
1427
1428 return progId;
1429 }
1430
1431 /// <summary>
1432 /// Parses a property element.
1433 /// </summary>
1434 /// <param name="node">Element to parse.</param>
1435 private void ParsePropertyElement(XElement node)
1436 {
1437 var sourceLineNumbers = Preprocessor.GetSourceLineNumbers(node);
1438 Identifier id = null;
1439 var admin = false;
1440 var complianceCheck = false;
1441 var hidden = false;
1442 var secure = false;
1443 var suppressModularization = YesNoType.NotSet;
1444 string value = null;
1445
1446 foreach (var attrib in node.Attributes())
1447 {
1448 if (String.IsNullOrEmpty(attrib.Name.NamespaceName) || CompilerCore.WixNamespace == attrib.Name.Namespace)
1449 {
1450 switch (attrib.Name.LocalName)
1451 {
1452 case "Id":
1453 id = this.Core.GetAttributeIdentifier(sourceLineNumbers, attrib);
1454 break;
1455 case "Admin":
1456 admin = YesNoType.Yes == this.Core.GetAttributeYesNoValue(sourceLineNumbers, attrib);
1457 break;
1458 case "ComplianceCheck":
1459 complianceCheck = YesNoType.Yes == this.Core.GetAttributeYesNoValue(sourceLineNumbers, attrib);
1460 break;
1461 case "Hidden":
1462 hidden = YesNoType.Yes == this.Core.GetAttributeYesNoValue(sourceLineNumbers, attrib);
1463 break;
1464 case "Secure":
1465 secure = YesNoType.Yes == this.Core.GetAttributeYesNoValue(sourceLineNumbers, attrib);
1466 break;
1467 case "SuppressModularization":
1468 suppressModularization = this.Core.GetAttributeYesNoValue(sourceLineNumbers, attrib);
1469 break;
1470 case "Value":
1471 value = this.Core.GetAttributeValue(sourceLineNumbers, attrib);
1472 break;
1473 default:
1474 this.Core.UnexpectedAttribute(node, attrib);
1475 break;
1476 }
1477 }
1478 else
1479 {
1480 this.Core.ParseExtensionAttribute(node, attrib);
1481 }
1482 }
1483
1484 if (null == id)
1485 {
1486 this.Core.Write(ErrorMessages.ExpectedAttribute(sourceLineNumbers, node.Name.LocalName, "Id"));
1487 id = Identifier.Invalid;
1488 }
1489 else if ("ProductID" == id.Id)
1490 {
1491 this.Core.Write(WarningMessages.ProductIdAuthored(sourceLineNumbers));
1492 }
1493 else if ("SecureCustomProperties" == id.Id || "AdminProperties" == id.Id || "MsiHiddenProperties" == id.Id)
1494 {
1495 this.Core.Write(ErrorMessages.CannotAuthorSpecialProperties(sourceLineNumbers, id.Id));
1496 }
1497
1498 if ("ErrorDialog" == id.Id)
1499 {
1500 this.Core.CreateSimpleReference(sourceLineNumbers, SymbolDefinitions.Dialog, value);
1501 }
1502
1503 foreach (var child in node.Elements())
1504 {
1505 if (CompilerCore.WixNamespace == child.Name.Namespace)
1506 {
1507 {
1508 switch (child.Name.LocalName)
1509 {
1510 case "ProductSearch":
1511 this.ParseProductSearchElement(child, id.Id);
1512 secure = true;
1513 break;
1514 default:
1515 // let ParseSearchSignatures handle standard AppSearch children and unknown elements
1516 break;
1517 }
1518 }
1519 }
1520 }
1521
1522 this.Core.InnerTextDisallowed(node, "Value");
1523
1524 // see if this property is used for appSearch
1525 var signatures = this.ParseSearchSignatures(node);
1526
1527 // If we're doing CCP then there must be a signature.
1528 if (complianceCheck && 0 == signatures.Count)
1529 {
1530 this.Core.Write(ErrorMessages.SearchElementRequiredWithAttribute(sourceLineNumbers, node.Name.LocalName, "ComplianceCheck", "yes"));
1531 }
1532
1533 foreach (var sig in signatures)
1534 {
1535 if (complianceCheck && !this.Core.EncounteredError)
1536 {
1537 this.Core.AddSymbol(new CCPSearchSymbol(sourceLineNumbers, new Identifier(AccessModifier.Section, sig)));
1538 }
1539
1540 this.AddAppSearch(sourceLineNumbers, id, sig);
1541 }
1542
1543 // If we're doing AppSearch get that setup.
1544 if (0 < signatures.Count)
1545 {
1546 this.AddProperty(sourceLineNumbers, id, value, admin, secure, hidden, false);
1547 }
1548 else // just a normal old property.
1549 {
1550 // If the property value is empty and none of the flags are set, print out a warning that we're ignoring
1551 // the element.
1552 if (String.IsNullOrEmpty(value) && !admin && !secure && !hidden)
1553 {
1554 this.Core.Write(WarningMessages.PropertyUseless(sourceLineNumbers, id.Id));
1555 }
1556 else // there is a value and/or a flag set, do that.
1557 {
1558 this.AddProperty(sourceLineNumbers, id, value, admin, secure, hidden, false);
1559 }
1560 }
1561
1562 if (!this.Core.EncounteredError && YesNoType.Yes == suppressModularization)
1563 {
1564 this.Core.Write(WarningMessages.PropertyModularizationSuppressed(sourceLineNumbers));
1565
1566 this.Core.AddSymbol(new WixSuppressModularizationSymbol(sourceLineNumbers)
1567 {
1568 SuppressIdentifier = id.Id
1569 });
1570 }
1571 }
1572
1573 /// <summary>
1574 /// Parses a RegistryKey element.
1575 /// </summary>
1576 /// <param name="node">Element to parse.</param>
1577 /// <param name="componentId">Identifier for parent component.</param>
1578 /// <param name="root">Root specified when element is nested under another Registry element, otherwise CompilerConstants.IntegerNotSet.</param>
1579 /// <param name="parentKey">Parent key for this Registry element when nested.</param>
1580 /// <param name="win64Component">true if the component is 64-bit.</param>
1581 /// <param name="possibleKeyPath">Identifier of this registry key since it could be the component's keypath.</param>
1582 /// <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>
1583 private YesNoType ParseRegistryKeyElement(XElement node, string componentId, RegistryRootType? root, string parentKey, bool win64Component, out Identifier possibleKeyPath)
1584 {
1585 var sourceLineNumbers = Preprocessor.GetSourceLineNumbers(node);
1586 Identifier id = null;
1587 var key = parentKey; // default to parent key path
1588 var forceCreateOnInstall = false;
1589 var forceDeleteOnUninstall = false;
1590 var keyPath = YesNoType.NotSet;
1591
1592 possibleKeyPath = null;
1593
1594 foreach (var attrib in node.Attributes())
1595 {
1596 if (String.IsNullOrEmpty(attrib.Name.NamespaceName) || CompilerCore.WixNamespace == attrib.Name.Namespace)
1597 {
1598 switch (attrib.Name.LocalName)
1599 {
1600 case "Id":
1601 id = this.Core.GetAttributeIdentifier(sourceLineNumbers, attrib);
1602 break;
1603 case "ForceCreateOnInstall":
1604 forceCreateOnInstall = YesNoType.Yes == this.Core.GetAttributeYesNoValue(sourceLineNumbers, attrib);
1605 break;
1606 case "ForceDeleteOnUninstall":
1607 forceDeleteOnUninstall = YesNoType.Yes == this.Core.GetAttributeYesNoValue(sourceLineNumbers, attrib);
1608 break;
1609 case "Key":
1610 key = this.Core.GetAttributeValue(sourceLineNumbers, attrib);
1611 if (null != parentKey)
1612 {
1613 key = Path.Combine(parentKey, key);
1614 }
1615 key = key?.TrimEnd('\\');
1616 break;
1617 case "Root":
1618 if (root.HasValue)
1619 {
1620 this.Core.Write(ErrorMessages.RegistryRootInvalid(sourceLineNumbers));
1621 }
1622
1623 root = this.Core.GetAttributeRegistryRootValue(sourceLineNumbers, attrib, true);
1624 break;
1625 default:
1626 this.Core.UnexpectedAttribute(node, attrib);
1627 break;
1628 }
1629 }
1630 else
1631 {
1632 this.Core.ParseExtensionAttribute(node, attrib);
1633 }
1634 }
1635
1636 var name = forceCreateOnInstall ? (forceDeleteOnUninstall ? "*" : "+") : (forceDeleteOnUninstall ? "-" : null);
1637
1638 if (forceCreateOnInstall || forceDeleteOnUninstall) // generates a Registry row, so an Id must be present
1639 {
1640 // generate the identifier if it wasn't provided
1641 if (null == id)
1642 {
1643 id = this.Core.CreateIdentifier("reg", componentId, ((int)root).ToString(CultureInfo.InvariantCulture.NumberFormat), LowercaseOrNull(key), LowercaseOrNull(name));
1644 }
1645 }
1646 else // does not generate a Registry row, so no Id should be present
1647 {
1648 if (null != id)
1649 {
1650 this.Core.Write(ErrorMessages.IllegalAttributeWithoutOtherAttributes(sourceLineNumbers, node.Name.LocalName, "Id", "ForceCreateOnInstall", "ForceDeleteOnUninstall", "yes", true));
1651 }
1652 }
1653
1654 if (!root.HasValue)
1655 {
1656 this.Core.Write(ErrorMessages.ExpectedAttribute(sourceLineNumbers, node.Name.LocalName, "Root"));
1657 }
1658
1659 if (null == key)
1660 {
1661 this.Core.Write(ErrorMessages.ExpectedAttribute(sourceLineNumbers, node.Name.LocalName, "Key"));
1662 key = String.Empty; // set the key to something to prevent null reference exceptions
1663 }
1664
1665 foreach (var child in node.Elements())
1666 {
1667 if (CompilerCore.WixNamespace == child.Name.Namespace)
1668 {
1669 Identifier possibleChildKeyPath = null;
1670
1671 switch (child.Name.LocalName)
1672 {
1673 case "RegistryKey":
1674 if (YesNoType.Yes == this.ParseRegistryKeyElement(child, componentId, root, key, win64Component, out possibleChildKeyPath))
1675 {
1676 if (YesNoType.Yes == keyPath)
1677 {
1678 this.Core.Write(ErrorMessages.ComponentMultipleKeyPaths(sourceLineNumbers, child.Name.LocalName, "KeyPath", "yes", "File", "RegistryValue", "ODBCDataSource"));
1679 }
1680
1681 possibleKeyPath = possibleChildKeyPath; // the child is the key path
1682 keyPath = YesNoType.Yes;
1683 }
1684 else if (null == possibleKeyPath && null != possibleChildKeyPath)
1685 {
1686 possibleKeyPath = possibleChildKeyPath;
1687 }
1688 break;
1689 case "RegistryValue":
1690 if (YesNoType.Yes == this.ParseRegistryValueElement(child, componentId, root, key, win64Component, out possibleChildKeyPath))
1691 {
1692 if (YesNoType.Yes == keyPath)
1693 {
1694 this.Core.Write(ErrorMessages.ComponentMultipleKeyPaths(sourceLineNumbers, child.Name.LocalName, "KeyPath", "yes", "File", "RegistryValue", "ODBCDataSource"));
1695 }
1696
1697 possibleKeyPath = possibleChildKeyPath; // the child is the key path
1698 keyPath = YesNoType.Yes;
1699 }
1700 else if (null == possibleKeyPath && null != possibleChildKeyPath)
1701 {
1702 possibleKeyPath = possibleChildKeyPath;
1703 }
1704 break;
1705 case "Permission":
1706 if (!forceCreateOnInstall)
1707 {
1708 this.Core.Write(ErrorMessages.UnexpectedElementWithAttributeValue(sourceLineNumbers, node.Name.LocalName, child.Name.LocalName, "ForceCreateOnInstall", "yes"));
1709 }
1710 this.ParsePermissionElement(child, id.Id, "Registry");
1711 break;
1712 case "PermissionEx":
1713 if (!forceCreateOnInstall)
1714 {
1715 this.Core.Write(ErrorMessages.UnexpectedElementWithAttributeValue(sourceLineNumbers, node.Name.LocalName, child.Name.LocalName, "ForceCreateOnInstall", "yes"));
1716 }
1717 this.ParsePermissionExElement(child, id.Id, "Registry");
1718 break;
1719 default:
1720 this.Core.UnexpectedElement(node, child);
1721 break;
1722 }
1723 }
1724 else
1725 {
1726 var context = new Dictionary<string, string>() { { "RegistryId", id?.Id }, { "ComponentId", componentId }, { "Win64", win64Component.ToString() } };
1727 this.Core.ParseExtensionElement(node, child, context);
1728 }
1729 }
1730
1731 if (!this.Core.EncounteredError && null != name)
1732 {
1733 this.Core.AddSymbol(new RegistrySymbol(sourceLineNumbers, id)
1734 {
1735 Root = root.Value,
1736 Key = key,
1737 Name = name,
1738 ComponentRef = componentId,
1739 });
1740 }
1741
1742 return keyPath;
1743 }
1744
1745 /// <summary>
1746 /// Parses a RegistryValue element.
1747 /// </summary>
1748 /// <param name="node">Element to parse.</param>
1749 /// <param name="componentId">Identifier for parent component.</param>
1750 /// <param name="root">Root specified when element is nested under a RegistryKey element, otherwise CompilerConstants.IntegerNotSet.</param>
1751 /// <param name="parentKey">Root specified when element is nested under a RegistryKey element, otherwise CompilerConstants.IntegerNotSet.</param>
1752 /// <param name="win64Component">true if the component is 64-bit.</param>
1753 /// <param name="possibleKeyPath">Identifier of this registry key since it could be the component's keypath.</param>
1754 /// <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>
1755 private YesNoType ParseRegistryValueElement(XElement node, string componentId, RegistryRootType? root, string parentKey, bool win64Component, out Identifier possibleKeyPath)
1756 {
1757 var sourceLineNumbers = Preprocessor.GetSourceLineNumbers(node);
1758 Identifier id = null;
1759 var key = parentKey; // default to parent key path
1760 string name = null;
1761 string value = null;
1762 string action = null;
1763 var valueType = RegistryValueType.String;
1764 var actionType = RegistryValueActionType.Write;
1765 var keyPath = YesNoType.NotSet;
1766
1767 possibleKeyPath = null;
1768
1769 foreach (var attrib in node.Attributes())
1770 {
1771 if (String.IsNullOrEmpty(attrib.Name.NamespaceName) || CompilerCore.WixNamespace == attrib.Name.Namespace)
1772 {
1773 switch (attrib.Name.LocalName)
1774 {
1775 case "Id":
1776 id = this.Core.GetAttributeIdentifier(sourceLineNumbers, attrib);
1777 break;
1778 case "Action":
1779 var actionValue = this.Core.GetAttributeValue(sourceLineNumbers, attrib);
1780 switch (actionValue)
1781 {
1782 case "append":
1783 actionType = RegistryValueActionType.Append;
1784 break;
1785 case "prepend":
1786 actionType = RegistryValueActionType.Prepend;
1787 break;
1788 case "write":
1789 actionType = RegistryValueActionType.Write;
1790 break;
1791 case "":
1792 break;
1793 default:
1794 this.Core.Write(ErrorMessages.IllegalAttributeValue(sourceLineNumbers, node.Name.LocalName, attrib.Name.LocalName, actionValue, "append", "prepend", "write"));
1795 break;
1796 }
1797 break;
1798 case "Key":
1799 key = this.Core.GetAttributeValue(sourceLineNumbers, attrib);
1800 if (null != parentKey)
1801 {
1802 if (parentKey.EndsWith("\\", StringComparison.Ordinal))
1803 {
1804 key = String.Concat(parentKey, key);
1805 }
1806 else
1807 {
1808 key = String.Concat(parentKey, "\\", key);
1809 }
1810 }
1811 break;
1812 case "KeyPath":
1813 keyPath = this.Core.GetAttributeYesNoValue(sourceLineNumbers, attrib);
1814 break;
1815 case "Name":
1816 name = this.Core.GetAttributeValue(sourceLineNumbers, attrib);
1817 break;
1818 case "Root":
1819 if (root.HasValue)
1820 {
1821 this.Core.Write(ErrorMessages.RegistryRootInvalid(sourceLineNumbers));
1822 }
1823
1824 root = this.Core.GetAttributeRegistryRootValue(sourceLineNumbers, attrib, true);
1825 break;
1826 case "Type":
1827 var typeValue = this.Core.GetAttributeValue(sourceLineNumbers, attrib);
1828 switch (typeValue)
1829 {
1830 case "binary":
1831 valueType = RegistryValueType.Binary;
1832 break;
1833 case "expandable":
1834 valueType = RegistryValueType.Expandable;
1835 break;
1836 case "integer":
1837 valueType = RegistryValueType.Integer;
1838 break;
1839 case "multiString":
1840 valueType = RegistryValueType.MultiString;
1841 break;
1842 case "string":
1843 valueType = RegistryValueType.String;
1844 break;
1845 case "":
1846 break;
1847 default:
1848 this.Core.Write(ErrorMessages.IllegalAttributeValue(sourceLineNumbers, node.Name.LocalName, attrib.Name.LocalName, typeValue, "binary", "expandable", "integer", "multiString", "string"));
1849 break;
1850 }
1851 break;
1852 case "Value":
1853 value = this.Core.GetAttributeValue(sourceLineNumbers, attrib, EmptyRule.CanBeEmpty);
1854 break;
1855 default:
1856 this.Core.UnexpectedAttribute(node, attrib);
1857 break;
1858 }
1859 }
1860 else
1861 {
1862 this.Core.ParseExtensionAttribute(node, attrib);
1863 }
1864 }
1865
1866 // generate the identifier if it wasn't provided
1867 if (null == id)
1868 {
1869 id = this.Core.CreateIdentifier("reg", componentId, ((int)(root ?? RegistryRootType.Unknown)).ToString(), LowercaseOrNull(key), LowercaseOrNull(name));
1870 }
1871
1872 if (RegistryValueType.MultiString != valueType && (RegistryValueActionType.Append == actionType || RegistryValueActionType.Prepend == actionType))
1873 {
1874 this.Core.Write(ErrorMessages.IllegalAttributeValueWithoutOtherAttribute(sourceLineNumbers, node.Name.LocalName, "Action", action, "Type", "multiString"));
1875 }
1876
1877 if (null == key)
1878 {
1879 this.Core.Write(ErrorMessages.ExpectedAttribute(sourceLineNumbers, node.Name.LocalName, "Key"));
1880 }
1881
1882 if (!root.HasValue)
1883 {
1884 this.Core.Write(ErrorMessages.ExpectedAttribute(sourceLineNumbers, node.Name.LocalName, "Root"));
1885 }
1886
1887 foreach (var child in node.Elements())
1888 {
1889 if (CompilerCore.WixNamespace == child.Name.Namespace)
1890 {
1891 switch (child.Name.LocalName)
1892 {
1893 case "MultiString":
1894 case "MultiStringValue":
1895 if (RegistryValueType.MultiString != valueType && null != value)
1896 {
1897 this.Core.Write(ErrorMessages.RegistryMultipleValuesWithoutMultiString(sourceLineNumbers, node.Name.LocalName, "Value", child.Name.LocalName, "Type"));
1898 }
1899 else
1900 {
1901 value = this.ParseRegistryMultiStringElement(child, value);
1902 }
1903 break;
1904 case "Permission":
1905 this.ParsePermissionElement(child, id.Id, "Registry");
1906 break;
1907 case "PermissionEx":
1908 this.ParsePermissionExElement(child, id.Id, "Registry");
1909 break;
1910 default:
1911 this.Core.UnexpectedElement(node, child);
1912 break;
1913 }
1914 }
1915 else
1916 {
1917 var context = new Dictionary<string, string>() { { "RegistryId", id?.Id }, { "ComponentId", componentId }, { "Win64", win64Component.ToString() } };
1918 this.Core.ParseExtensionElement(node, child, context);
1919 }
1920 }
1921
1922 //switch (typeType)
1923 //{
1924 //case Wix.RegistryValue.TypeType.binary:
1925 // value = String.Concat("#x", value);
1926 // break;
1927 //case Wix.RegistryValue.TypeType.expandable:
1928 // value = String.Concat("#%", value);
1929 // break;
1930 //case Wix.RegistryValue.TypeType.integer:
1931 // value = String.Concat("#", value);
1932 // break;
1933 //case Wix.RegistryValue.TypeType.multiString:
1934 // switch (actionType)
1935 // {
1936 // case Wix.RegistryValue.ActionType.append:
1937 // value = String.Concat("[~]", value);
1938 // break;
1939 // case Wix.RegistryValue.ActionType.prepend:
1940 // value = String.Concat(value, "[~]");
1941 // break;
1942 // case Wix.RegistryValue.ActionType.write:
1943 // default:
1944 // if (null != value && -1 == value.IndexOf("[~]", StringComparison.Ordinal))
1945 // {
1946 // value = String.Format(CultureInfo.InvariantCulture, "[~]{0}[~]", value);
1947 // }
1948 // break;
1949 // }
1950 // break;
1951 //case Wix.RegistryValue.TypeType.@string:
1952 // // escape the leading '#' character for string registry keys
1953 // if (null != value && value.StartsWith("#", StringComparison.Ordinal))
1954 // {
1955 // value = String.Concat("#", value);
1956 // }
1957 // break;
1958 //}
1959
1960 // value may be set by child MultiStringValue elements, so it must be checked here
1961 if (null == value && valueType != RegistryValueType.Binary)
1962 {
1963 this.Core.Write(ErrorMessages.ExpectedAttribute(sourceLineNumbers, node.Name.LocalName, "Value"));
1964 }
1965 else if (0 == value?.Length && ("+" == name || "-" == name || "*" == name)) // prevent accidental authoring of special name values
1966 {
1967 this.Core.Write(ErrorMessages.RegistryNameValueIncorrect(sourceLineNumbers, node.Name.LocalName, "Name", name));
1968 }
1969
1970 if (!this.Core.EncounteredError)
1971 {
1972 this.Core.AddSymbol(new RegistrySymbol(sourceLineNumbers, id)
1973 {
1974 Root = root.Value,
1975 Key = key,
1976 Name = name,
1977 Value = value,
1978 ValueType = valueType,
1979 ValueAction = actionType,
1980 ComponentRef = componentId,
1981 });
1982 }
1983
1984 // If this was just a regular registry key (that could be the key path)
1985 // and no child registry key set the possible key path, let's make this
1986 // Registry/@Id a possible key path.
1987 if (null == possibleKeyPath)
1988 {
1989 possibleKeyPath = id;
1990 }
1991
1992 return keyPath;
1993 }
1994
1995 private string ParseRegistryMultiStringElement(XElement node, string value)
1996 {
1997 var sourceLineNumbers = Preprocessor.GetSourceLineNumbers(node);
1998 string multiStringValue = null;
1999
2000 foreach (var attrib in node.Attributes())
2001 {
2002 if (String.IsNullOrEmpty(attrib.Name.NamespaceName) || CompilerCore.WixNamespace == attrib.Name.Namespace)
2003 {
2004 switch (attrib.Name.LocalName)
2005 {
2006 case "Value":
2007 multiStringValue = this.Core.GetAttributeValue(sourceLineNumbers, attrib);
2008 break;
2009 default:
2010 this.Core.UnexpectedAttribute(node, attrib);
2011 break;
2012 }
2013 }
2014 }
2015
2016 this.Core.ParseForExtensionElements(node);
2017
2018 return null == value ? multiStringValue ?? "[~]" : String.Concat(value, "[~]", multiStringValue);
2019 }
2020
2021 /// <summary>
2022 /// Parses a RemoveRegistryKey element.
2023 /// </summary>
2024 /// <param name="node">The element to parse.</param>
2025 /// <param name="componentId">The component identifier of the parent element.</param>
2026 private void ParseRemoveRegistryKeyElement(XElement node, string componentId)
2027 {
2028 var sourceLineNumbers = Preprocessor.GetSourceLineNumbers(node);
2029 Identifier id = null;
2030 RemoveRegistryActionType? actionType = null;
2031 string key = null;
2032 var name = "-";
2033 RegistryRootType? root = null;
2034
2035 foreach (var attrib in node.Attributes())
2036 {
2037 if (String.IsNullOrEmpty(attrib.Name.NamespaceName) || CompilerCore.WixNamespace == attrib.Name.Namespace)
2038 {
2039 switch (attrib.Name.LocalName)
2040 {
2041 case "Id":
2042 id = this.Core.GetAttributeIdentifier(sourceLineNumbers, attrib);
2043 break;
2044 case "Action":
2045 var actionValue = this.Core.GetAttributeValue(sourceLineNumbers, attrib);
2046 switch (actionValue)
2047 {
2048 case "removeOnInstall":
2049 actionType = RemoveRegistryActionType.RemoveOnInstall;
2050 break;
2051 case "removeOnUninstall":
2052 actionType = RemoveRegistryActionType.RemoveOnUninstall;
2053 break;
2054 case "":
2055 break;
2056 default:
2057 this.Core.Write(ErrorMessages.IllegalAttributeValue(sourceLineNumbers, node.Name.LocalName, attrib.Name.LocalName, actionValue, "removeOnInstall", "removeOnUninstall"));
2058 break;
2059 }
2060 //if (0 < action.Length)
2061 //{
2062 // if (!Wix.RemoveRegistryKey.TryParseActionType(action, out actionType))
2063 // {
2064 // this.Core.Write(ErrorMessages.IllegalAttributeValue(sourceLineNumbers, node.Name.LocalName, attrib.Name.LocalName, action, "removeOnInstall", "removeOnUninstall"));
2065 // }
2066 //}
2067 break;
2068 case "Key":
2069 key = this.Core.GetAttributeValue(sourceLineNumbers, attrib);
2070 break;
2071 case "Root":
2072 root = this.Core.GetAttributeRegistryRootValue(sourceLineNumbers, attrib, true);
2073 break;
2074 default:
2075 this.Core.UnexpectedAttribute(node, attrib);
2076 break;
2077 }
2078 }
2079 else
2080 {
2081 this.Core.ParseExtensionAttribute(node, attrib);
2082 }
2083 }
2084
2085 // generate the identifier if it wasn't provided
2086 if (null == id)
2087 {
2088 id = this.Core.CreateIdentifier("reg", componentId, ((int)root).ToString(CultureInfo.InvariantCulture.NumberFormat), LowercaseOrNull(key), LowercaseOrNull(name));
2089 }
2090
2091 if (!root.HasValue)
2092 {
2093 this.Core.Write(ErrorMessages.ExpectedAttribute(sourceLineNumbers, node.Name.LocalName, "Root"));
2094 }
2095
2096 if (null == key)
2097 {
2098 this.Core.Write(ErrorMessages.ExpectedAttribute(sourceLineNumbers, node.Name.LocalName, "Key"));
2099 }
2100
2101 if (!actionType.HasValue)
2102 {
2103 this.Core.Write(ErrorMessages.ExpectedAttribute(sourceLineNumbers, node.Name.LocalName, "Action"));
2104 }
2105
2106 this.Core.ParseForExtensionElements(node);
2107
2108 if (!this.Core.EncounteredError)
2109 {
2110 this.Core.AddSymbol(new RemoveRegistrySymbol(sourceLineNumbers, id)
2111 {
2112 Root = root.Value,
2113 Key = key,
2114 Name = name,
2115 Action = actionType.Value,
2116 ComponentRef = componentId,
2117 });
2118 }
2119 }
2120
2121 /// <summary>
2122 /// Parses a RemoveRegistryValue element.
2123 /// </summary>
2124 /// <param name="node">The element to parse.</param>
2125 /// <param name="componentId">The component identifier of the parent element.</param>
2126 private void ParseRemoveRegistryValueElement(XElement node, string componentId)
2127 {
2128 var sourceLineNumbers = Preprocessor.GetSourceLineNumbers(node);
2129 Identifier id = null;
2130 string key = null;
2131 string name = null;
2132 RegistryRootType? root = null;
2133
2134 foreach (var attrib in node.Attributes())
2135 {
2136 if (String.IsNullOrEmpty(attrib.Name.NamespaceName) || CompilerCore.WixNamespace == attrib.Name.Namespace)
2137 {
2138 switch (attrib.Name.LocalName)
2139 {
2140 case "Id":
2141 id = this.Core.GetAttributeIdentifier(sourceLineNumbers, attrib);
2142 break;
2143 case "Key":
2144 key = this.Core.GetAttributeValue(sourceLineNumbers, attrib);
2145 break;
2146 case "Name":
2147 name = this.Core.GetAttributeValue(sourceLineNumbers, attrib);
2148 break;
2149 case "Root":
2150 root = this.Core.GetAttributeRegistryRootValue(sourceLineNumbers, attrib, true);
2151 break;
2152 default:
2153 this.Core.UnexpectedAttribute(node, attrib);
2154 break;
2155 }
2156 }
2157 else
2158 {
2159 this.Core.ParseExtensionAttribute(node, attrib);
2160 }
2161 }
2162
2163 // generate the identifier if it wasn't provided
2164 if (null == id)
2165 {
2166 id = this.Core.CreateIdentifier("reg", componentId, ((int)root).ToString(CultureInfo.InvariantCulture.NumberFormat), LowercaseOrNull(key), LowercaseOrNull(name));
2167 }
2168
2169 if (!root.HasValue)
2170 {
2171 this.Core.Write(ErrorMessages.ExpectedAttribute(sourceLineNumbers, node.Name.LocalName, "Root"));
2172 }
2173
2174 if (null == key)
2175 {
2176 this.Core.Write(ErrorMessages.ExpectedAttribute(sourceLineNumbers, node.Name.LocalName, "Key"));
2177 }
2178
2179 this.Core.ParseForExtensionElements(node);
2180
2181 if (!this.Core.EncounteredError)
2182 {
2183 this.Core.AddSymbol(new RemoveRegistrySymbol(sourceLineNumbers, id)
2184 {
2185 Root = root.Value,
2186 Key = key,
2187 Name = name,
2188 ComponentRef = componentId
2189 });
2190 }
2191 }
2192
2193 /// <summary>
2194 /// Parses a remove file element.
2195 /// </summary>
2196 /// <param name="node">Element to parse.</param>
2197 /// <param name="componentId">Identifier of parent component.</param>
2198 /// <param name="parentDirectory">Identifier of the parent component's directory.</param>
2199 private void ParseRemoveFileElement(XElement node, string componentId, string parentDirectory)
2200 {
2201 var sourceLineNumbers = Preprocessor.GetSourceLineNumbers(node);
2202 Identifier id = null;
2203 string directoryId = null;
2204 string subdirectory = null;
2205 string name = null;
2206 bool? onInstall = null;
2207 bool? onUninstall = null;
2208 string propertyId = null;
2209 string shortName = null;
2210
2211 foreach (var attrib in node.Attributes())
2212 {
2213 if (String.IsNullOrEmpty(attrib.Name.NamespaceName) || CompilerCore.WixNamespace == attrib.Name.Namespace)
2214 {
2215 switch (attrib.Name.LocalName)
2216 {
2217 case "Id":
2218 id = this.Core.GetAttributeIdentifier(sourceLineNumbers, attrib);
2219 break;
2220 case "Directory":
2221 directoryId = this.Core.GetAttributeIdentifierValue(sourceLineNumbers, attrib);
2222 this.Core.CreateSimpleReference(sourceLineNumbers, SymbolDefinitions.Directory, directoryId);
2223 break;
2224 case "Subdirectory":
2225 subdirectory = this.Core.GetAttributeLongFilename(sourceLineNumbers, attrib, allowRelative: true);
2226 break;
2227 case "Name":
2228 name = this.Core.GetAttributeLongFilename(sourceLineNumbers, attrib, true);
2229 break;
2230 case "On":
2231 var onValue = this.Core.GetAttributeValue(sourceLineNumbers, attrib);
2232 switch (onValue)
2233 {
2234 case "install":
2235 onInstall = true;
2236 break;
2237 case "uninstall":
2238 onUninstall = true;
2239 break;
2240 case "both":
2241 onInstall = true;
2242 onUninstall = true;
2243 break;
2244 }
2245 break;
2246 case "Property":
2247 propertyId = this.Core.GetAttributeIdentifierValue(sourceLineNumbers, attrib);
2248 break;
2249 case "ShortName":
2250 shortName = this.Core.GetAttributeShortFilename(sourceLineNumbers, attrib, true);
2251 break;
2252 default:
2253 this.Core.UnexpectedAttribute(node, attrib);
2254 break;
2255 }
2256 }
2257 else
2258 {
2259 this.Core.ParseExtensionAttribute(node, attrib);
2260 }
2261 }
2262
2263 if (null == name)
2264 {
2265 this.Core.Write(ErrorMessages.ExpectedAttribute(sourceLineNumbers, node.Name.LocalName, "Name"));
2266 }
2267
2268 if (!onInstall.HasValue && !onUninstall.HasValue)
2269 {
2270 this.Core.Write(ErrorMessages.ExpectedAttribute(sourceLineNumbers, node.Name.LocalName, "On"));
2271 }
2272
2273 if (String.IsNullOrEmpty(propertyId))
2274 {
2275 directoryId = this.HandleSubdirectory(sourceLineNumbers, node, directoryId ?? parentDirectory, subdirectory, "Directory", "Subdirectory");
2276 }
2277 else if (!String.IsNullOrEmpty(directoryId))
2278 {
2279 this.Core.Write(ErrorMessages.IllegalAttributeWithOtherAttribute(sourceLineNumbers, node.Name.LocalName, "Property", "Directory", directoryId));
2280 }
2281 else if (!String.IsNullOrEmpty(subdirectory))
2282 {
2283 this.Core.Write(ErrorMessages.IllegalAttributeWithOtherAttribute(sourceLineNumbers, node.Name.LocalName, "Property", "Subdirectory", subdirectory));
2284 }
2285
2286 if (null == id)
2287 {
2288 var on = (onInstall == true && onUninstall == true) ? 3 : (onUninstall == true) ? 2 : (onInstall == true) ? 1 : 0;
2289 id = this.Core.CreateIdentifier("rmf", directoryId ?? propertyId ?? parentDirectory, LowercaseOrNull(shortName), LowercaseOrNull(name), on.ToString());
2290 }
2291
2292 this.Core.ParseForExtensionElements(node);
2293
2294 if (!this.Core.EncounteredError)
2295 {
2296 this.Core.AddSymbol(new RemoveFileSymbol(sourceLineNumbers, id)
2297 {
2298 ComponentRef = componentId,
2299 FileName = name,
2300 ShortFileName = shortName,
2301 DirPropertyRef = directoryId ?? propertyId ?? parentDirectory,
2302 OnInstall = onInstall,
2303 OnUninstall = onUninstall,
2304 });
2305 }
2306 }
2307
2308 /// <summary>
2309 /// Parses a RemoveFolder element.
2310 /// </summary>
2311 /// <param name="node">Element to parse.</param>
2312 /// <param name="componentId">Identifier of parent component.</param>
2313 /// <param name="parentDirectory">Identifier of parent component's directory.</param>
2314 private void ParseRemoveFolderElement(XElement node, string componentId, string parentDirectory)
2315 {
2316 var sourceLineNumbers = Preprocessor.GetSourceLineNumbers(node);
2317 Identifier id = null;
2318 string directoryId = null;
2319 string subdirectory = null;
2320 bool? onInstall = null;
2321 bool? onUninstall = null;
2322 string propertyId = null;
2323
2324 foreach (var attrib in node.Attributes())
2325 {
2326 if (String.IsNullOrEmpty(attrib.Name.NamespaceName) || CompilerCore.WixNamespace == attrib.Name.Namespace)
2327 {
2328 switch (attrib.Name.LocalName)
2329 {
2330 case "Id":
2331 id = this.Core.GetAttributeIdentifier(sourceLineNumbers, attrib);
2332 break;
2333 case "Directory":
2334 directoryId = this.Core.GetAttributeIdentifierValue(sourceLineNumbers, attrib);
2335 this.Core.CreateSimpleReference(sourceLineNumbers, SymbolDefinitions.Directory, directoryId);
2336 break;
2337 case "Subdirectory":
2338 subdirectory = this.Core.GetAttributeLongFilename(sourceLineNumbers, attrib, allowRelative: true);
2339 break;
2340 case "On":
2341 var onValue = this.Core.GetAttributeValue(sourceLineNumbers, attrib);
2342 switch (onValue)
2343 {
2344 case "install":
2345 onInstall = true;
2346 break;
2347 case "uninstall":
2348 onUninstall = true;
2349 break;
2350 case "both":
2351 onInstall = true;
2352 onUninstall = true;
2353 break;
2354 }
2355 break;
2356 case "Property":
2357 propertyId = this.Core.GetAttributeIdentifierValue(sourceLineNumbers, attrib);
2358 break;
2359 default:
2360 this.Core.UnexpectedAttribute(node, attrib);
2361 break;
2362 }
2363 }
2364 else
2365 {
2366 this.Core.ParseExtensionAttribute(node, attrib);
2367 }
2368 }
2369
2370 if (!onInstall.HasValue && !onUninstall.HasValue)
2371 {
2372 this.Core.Write(ErrorMessages.ExpectedAttribute(sourceLineNumbers, node.Name.LocalName, "On"));
2373 }
2374
2375 if (String.IsNullOrEmpty(propertyId))
2376 {
2377 directoryId = this.HandleSubdirectory(sourceLineNumbers, node, directoryId ?? parentDirectory, subdirectory, "Directory", "Subdirectory");
2378 }
2379 else if (!String.IsNullOrEmpty(directoryId))
2380 {
2381 this.Core.Write(ErrorMessages.IllegalAttributeWithOtherAttribute(sourceLineNumbers, node.Name.LocalName, "Property", "Directory", directoryId));
2382 }
2383 else if (!String.IsNullOrEmpty(subdirectory))
2384 {
2385 this.Core.Write(ErrorMessages.IllegalAttributeWithOtherAttribute(sourceLineNumbers, node.Name.LocalName, "Property", "Subdirectory", subdirectory));
2386 }
2387
2388 if (null == id)
2389 {
2390 var on = (onInstall == true && onUninstall == true) ? 3 : (onUninstall == true) ? 2 : (onInstall == true) ? 1 : 0;
2391 id = this.Core.CreateIdentifier("rmf", directoryId ?? propertyId, on.ToString());
2392 }
2393
2394 this.Core.ParseForExtensionElements(node);
2395
2396 if (!this.Core.EncounteredError)
2397 {
2398 this.Core.AddSymbol(new RemoveFileSymbol(sourceLineNumbers, id)
2399 {
2400 ComponentRef = componentId,
2401 DirPropertyRef = directoryId ?? propertyId,
2402 OnInstall = onInstall,
2403 OnUninstall = onUninstall
2404 });
2405 }
2406 }
2407
2408 /// <summary>
2409 /// Parses a reserve cost element.
2410 /// </summary>
2411 /// <param name="node">Element to parse.</param>
2412 /// <param name="componentId">Identifier of parent component.</param>
2413 /// <param name="directoryId">Optional and default identifier of referenced directory.</param>
2414 private void ParseReserveCostElement(XElement node, string componentId, string directoryId)
2415 {
2416 var sourceLineNumbers = Preprocessor.GetSourceLineNumbers(node);
2417 Identifier id = null;
2418 string subdirectory = null;
2419 var runFromSource = CompilerConstants.IntegerNotSet;
2420 var runLocal = CompilerConstants.IntegerNotSet;
2421
2422 foreach (var attrib in node.Attributes())
2423 {
2424 if (String.IsNullOrEmpty(attrib.Name.NamespaceName) || CompilerCore.WixNamespace == attrib.Name.Namespace)
2425 {
2426 switch (attrib.Name.LocalName)
2427 {
2428 case "Id":
2429 id = this.Core.GetAttributeIdentifier(sourceLineNumbers, attrib);
2430 break;
2431 case "Directory":
2432 directoryId = this.Core.GetAttributeIdentifierValue(sourceLineNumbers, attrib);
2433 this.Core.CreateSimpleReference(sourceLineNumbers, SymbolDefinitions.Directory, directoryId);
2434 break;
2435 case "Subdirectory":
2436 subdirectory = this.Core.GetAttributeLongFilename(sourceLineNumbers, attrib, allowRelative: true);
2437 break;
2438 case "RunFromSource":
2439 runFromSource = this.Core.GetAttributeIntegerValue(sourceLineNumbers, attrib, 0, Int32.MaxValue);
2440 break;
2441 case "RunLocal":
2442 runLocal = this.Core.GetAttributeIntegerValue(sourceLineNumbers, attrib, 0, Int32.MaxValue);
2443 break;
2444 default:
2445 this.Core.UnexpectedAttribute(node, attrib);
2446 break;
2447 }
2448 }
2449 else
2450 {
2451 this.Core.ParseExtensionAttribute(node, attrib);
2452 }
2453 }
2454
2455 directoryId = this.HandleSubdirectory(sourceLineNumbers, node, directoryId, subdirectory, "Directory", "Subdirectory");
2456
2457 if (null == id)
2458 {
2459 id = this.Core.CreateIdentifier("rc", componentId, directoryId);
2460 }
2461
2462 if (CompilerConstants.IntegerNotSet == runFromSource)
2463 {
2464 this.Core.Write(ErrorMessages.ExpectedAttribute(sourceLineNumbers, node.Name.LocalName, "RunFromSource"));
2465 }
2466
2467 if (CompilerConstants.IntegerNotSet == runLocal)
2468 {
2469 this.Core.Write(ErrorMessages.ExpectedAttribute(sourceLineNumbers, node.Name.LocalName, "RunLocal"));
2470 }
2471
2472 this.Core.ParseForExtensionElements(node);
2473
2474 if (!this.Core.EncounteredError)
2475 {
2476 this.Core.AddSymbol(new ReserveCostSymbol(sourceLineNumbers, id)
2477 {
2478 ComponentRef = componentId,
2479 ReserveFolder = directoryId,
2480 ReserveLocal = runLocal,
2481 ReserveSource = runFromSource
2482 });
2483 }
2484 }
2485
2486 /// <summary>
2487 /// Parses a sequence element.
2488 /// </summary>
2489 /// <param name="node">Element to parse.</param>
2490 /// <param name="sequenceTable">Name of sequence table.</param>
2491 private void ParseSequenceElement(XElement node, SequenceTable sequenceTable)
2492 {
2493 // Parse each action in the sequence.
2494 foreach (var child in node.Elements())
2495 {
2496 var childSourceLineNumbers = Preprocessor.GetSourceLineNumbers(child);
2497 Identifier actionIdentifier = null;
2498 var actionName = child.Name.LocalName;
2499 string afterAction = null;
2500 string beforeAction = null;
2501 string condition = null;
2502 var customAction = "Custom" == actionName;
2503 var overridable = false;
2504 var exitSequence = CompilerConstants.IntegerNotSet;
2505 var sequence = CompilerConstants.IntegerNotSet;
2506 var showDialog = "Show" == actionName;
2507 var specialAction = "InstallExecute" == actionName || "InstallExecuteAgain" == actionName || "RemoveExistingProducts" == actionName || "DisableRollback" == actionName || "ScheduleReboot" == actionName || "ForceReboot" == actionName || "ResolveSource" == actionName; // these actions do NOT have default sequence numbers and MUST be scheduled.
2508 var specialStandardAction = "AppSearch" == actionName || "CCPSearch" == actionName || "RMCCPSearch" == actionName || "LaunchConditions" == actionName || "FindRelatedProducts" == actionName; // these standard actions have default sequence numbers so they do NOT have to be scheduled.
2509 var suppress = false;
2510
2511 foreach (var attrib in child.Attributes())
2512 {
2513 if (String.IsNullOrEmpty(attrib.Name.NamespaceName) || CompilerCore.WixNamespace == attrib.Name.Namespace)
2514 {
2515 switch (attrib.Name.LocalName)
2516 {
2517 case "Action":
2518 if (customAction)
2519 {
2520 actionIdentifier = this.Core.GetAttributeIdentifier(childSourceLineNumbers, attrib);
2521 actionName = actionIdentifier.Id;
2522 this.Core.CreateSimpleReference(childSourceLineNumbers, SymbolDefinitions.CustomAction, actionIdentifier.Id);
2523 }
2524 else
2525 {
2526 this.Core.UnexpectedAttribute(child, attrib);
2527 }
2528 break;
2529 case "After":
2530 if (customAction || showDialog || specialAction || specialStandardAction)
2531 {
2532 afterAction = this.Core.GetAttributeIdentifierValue(childSourceLineNumbers, attrib);
2533 this.Core.CreateSimpleReference(childSourceLineNumbers, SymbolDefinitions.WixAction, sequenceTable.ToString(), afterAction);
2534 }
2535 else
2536 {
2537 this.Core.UnexpectedAttribute(child, attrib);
2538 }
2539 break;
2540 case "Before":
2541 if (customAction || showDialog || specialAction || specialStandardAction)
2542 {
2543 beforeAction = this.Core.GetAttributeIdentifierValue(childSourceLineNumbers, attrib);
2544 this.Core.CreateSimpleReference(childSourceLineNumbers, SymbolDefinitions.WixAction, sequenceTable.ToString(), beforeAction);
2545 }
2546 else
2547 {
2548 this.Core.UnexpectedAttribute(child, attrib);
2549 }
2550 break;
2551 case "Condition":
2552 condition = this.Core.GetAttributeValue(childSourceLineNumbers, attrib);
2553 break;
2554 case "Dialog":
2555 if (showDialog)
2556 {
2557 actionIdentifier = this.Core.GetAttributeIdentifier(childSourceLineNumbers, attrib);
2558 actionName = actionIdentifier.Id;
2559 this.Core.CreateSimpleReference(childSourceLineNumbers, SymbolDefinitions.Dialog, actionName);
2560 }
2561 else
2562 {
2563 this.Core.UnexpectedAttribute(child, attrib);
2564 }
2565 break;
2566 case "OnExit":
2567 if (customAction || showDialog || specialAction)
2568 {
2569 var exitValue = this.Core.GetAttributeValue(childSourceLineNumbers, attrib);
2570 switch (exitValue)
2571 {
2572 case "success":
2573 exitSequence = -1;
2574 break;
2575 case "cancel":
2576 exitSequence = -2;
2577 break;
2578 case "error":
2579 exitSequence = -3;
2580 break;
2581 case "suspend":
2582 exitSequence = -4;
2583 break;
2584 }
2585 }
2586 else
2587 {
2588 this.Core.UnexpectedAttribute(child, attrib);
2589 }
2590 break;
2591 case "Overridable":
2592 overridable = YesNoType.Yes == this.Core.GetAttributeYesNoValue(childSourceLineNumbers, attrib);
2593 break;
2594 case "Sequence":
2595 sequence = this.Core.GetAttributeIntegerValue(childSourceLineNumbers, attrib, 1, Int16.MaxValue);
2596 break;
2597 case "Suppress":
2598 suppress = YesNoType.Yes == this.Core.GetAttributeYesNoValue(childSourceLineNumbers, attrib);
2599 break;
2600 default:
2601 this.Core.UnexpectedAttribute(node, attrib);
2602 break;
2603 }
2604 }
2605 else
2606 {
2607 this.Core.ParseExtensionAttribute(node, attrib);
2608 }
2609 }
2610
2611 var standardAction = WindowsInstallerStandard.IsStandardAction(actionName);
2612
2613 if (customAction && "Custom" == actionName)
2614 {
2615 this.Core.Write(ErrorMessages.ExpectedAttribute(childSourceLineNumbers, child.Name.LocalName, "Action"));
2616 }
2617 else if (showDialog && "Show" == actionName)
2618 {
2619 this.Core.Write(ErrorMessages.ExpectedAttribute(childSourceLineNumbers, child.Name.LocalName, "Dialog"));
2620 }
2621
2622 if (CompilerConstants.IntegerNotSet != sequence)
2623 {
2624 if (CompilerConstants.IntegerNotSet != exitSequence)
2625 {
2626 this.Core.Write(ErrorMessages.IllegalAttributeWithOtherAttribute(childSourceLineNumbers, child.Name.LocalName, "Sequence", "OnExit"));
2627 }
2628 else if (null != beforeAction || null != afterAction)
2629 {
2630 this.Core.Write(ErrorMessages.IllegalAttributeWithOtherAttribute(childSourceLineNumbers, child.Name.LocalName, "Sequence", "Before", "After"));
2631 }
2632 }
2633 else // sequence not specified use OnExit (which may also be not set).
2634 {
2635 sequence = exitSequence;
2636 }
2637
2638 if (null != beforeAction && null != afterAction)
2639 {
2640 this.Core.Write(ErrorMessages.IllegalAttributeWithOtherAttribute(childSourceLineNumbers, child.Name.LocalName, "After", "Before"));
2641 }
2642 else if ((customAction || showDialog || specialAction) && !suppress && CompilerConstants.IntegerNotSet == sequence && null == beforeAction && null == afterAction)
2643 {
2644 this.Core.Write(ErrorMessages.NeedSequenceBeforeOrAfter(childSourceLineNumbers, child.Name.LocalName));
2645 }
2646
2647 // action that is scheduled to occur before/after itself
2648 if (beforeAction == actionName)
2649 {
2650 this.Core.Write(ErrorMessages.ActionScheduledRelativeToItself(childSourceLineNumbers, child.Name.LocalName, "Before", beforeAction));
2651 }
2652 else if (afterAction == actionName)
2653 {
2654 this.Core.Write(ErrorMessages.ActionScheduledRelativeToItself(childSourceLineNumbers, child.Name.LocalName, "After", afterAction));
2655 }
2656
2657 // normal standard actions cannot be set overridable by the user (since they are overridable by default)
2658 if (overridable && standardAction && !specialAction)
2659 {
2660 this.Core.Write(ErrorMessages.UnexpectedAttribute(childSourceLineNumbers, child.Name.LocalName, "Overridable"));
2661 }
2662
2663 // suppress cannot be specified at the same time as Before, After, or Sequence
2664 if (suppress && (null != afterAction || null != beforeAction || CompilerConstants.IntegerNotSet != sequence || overridable))
2665 {
2666 this.Core.Write(ErrorMessages.IllegalAttributeWithOtherAttributes(childSourceLineNumbers, child.Name.LocalName, "Suppress", "Before", "After", "Sequence", "Overridable"));
2667 }
2668
2669 this.Core.ParseForExtensionElements(child);
2670
2671 // add the row and any references needed
2672 if (!this.Core.EncounteredError)
2673 {
2674 if (suppress)
2675 {
2676 this.Core.AddSymbol(new WixSuppressActionSymbol(childSourceLineNumbers, new Identifier(AccessModifier.Global, sequenceTable, actionName))
2677 {
2678 SequenceTable = sequenceTable,
2679 Action = actionName
2680 });
2681 }
2682 else
2683 {
2684 var access = AccessModifier.Global;
2685 if (overridable)
2686 {
2687 access = AccessModifier.Virtual;
2688 }
2689 else if (actionIdentifier != null)
2690 {
2691 access = actionIdentifier.Access;
2692 }
2693 else if (standardAction)
2694 {
2695 access = AccessModifier.Override;
2696 }
2697
2698 var symbol = this.Core.AddSymbol(new WixActionSymbol(childSourceLineNumbers, new Identifier(access, sequenceTable, actionName))
2699 {
2700 SequenceTable = sequenceTable,
2701 Action = actionName,
2702 Condition = condition,
2703 Before = beforeAction,
2704 After = afterAction,
2705 Overridable = overridable,
2706 });
2707
2708 if (CompilerConstants.IntegerNotSet != sequence)
2709 {
2710 symbol.Sequence = sequence;
2711 }
2712 }
2713 }
2714 }
2715
2716 this.Core.InnerTextDisallowed(node, "Condition");
2717 }
2718
2719
2720 /// <summary>
2721 /// Parses a service config element.
2722 /// </summary>
2723 /// <param name="node">Element to parse.</param>
2724 /// <param name="componentId">Identifier of parent component.</param>
2725 /// <param name="serviceName">Optional element containing parent's service name.</param>
2726 private void ParseServiceConfigElement(XElement node, string componentId, string serviceName)
2727 {
2728 var sourceLineNumbers = Preprocessor.GetSourceLineNumbers(node);
2729 Identifier id = null;
2730 string delayedAutoStart = null;
2731 string failureActionsWhen = null;
2732 var name = serviceName;
2733 var install = false;
2734 var reinstall = false;
2735 var uninstall = false;
2736 string preShutdownDelay = null;
2737 string requiredPrivileges = null;
2738 string sid = null;
2739
2740 this.Core.Write(WarningMessages.ServiceConfigFamilyNotSupported(sourceLineNumbers, node.Name.LocalName));
2741
2742 foreach (var attrib in node.Attributes())
2743 {
2744 if (String.IsNullOrEmpty(attrib.Name.NamespaceName) || CompilerCore.WixNamespace == attrib.Name.Namespace)
2745 {
2746 switch (attrib.Name.LocalName)
2747 {
2748 case "Id":
2749 id = this.Core.GetAttributeIdentifier(sourceLineNumbers, attrib);
2750 break;
2751 case "DelayedAutoStart":
2752 delayedAutoStart = this.Core.GetAttributeValue(sourceLineNumbers, attrib);
2753 switch (delayedAutoStart)
2754 {
2755 case "no":
2756 delayedAutoStart = "0";
2757 break;
2758 case "yes":
2759 delayedAutoStart = "1";
2760 break;
2761 default:
2762 // allow everything else to pass through that are hopefully "formatted" Properties.
2763 break;
2764 }
2765 break;
2766 case "FailureActionsWhen":
2767 failureActionsWhen = this.Core.GetAttributeValue(sourceLineNumbers, attrib);
2768 switch (failureActionsWhen)
2769 {
2770 case "failedToStop":
2771 failureActionsWhen = "0";
2772 break;
2773 case "failedToStopOrReturnedError":
2774 failureActionsWhen = "1";
2775 break;
2776 default:
2777 // allow everything else to pass through that are hopefully "formatted" Properties.
2778 break;
2779 }
2780 break;
2781 case "OnInstall":
2782 install = YesNoType.Yes == this.Core.GetAttributeYesNoValue(sourceLineNumbers, attrib);
2783 //if (YesNoType.Yes == install)
2784 //{
2785 // events |= MsiInterop.MsidbServiceConfigEventInstall;
2786 //}
2787 break;
2788 case "OnReinstall":
2789 reinstall = YesNoType.Yes == this.Core.GetAttributeYesNoValue(sourceLineNumbers, attrib);
2790 //if (YesNoType.Yes == reinstall)
2791 //{
2792 // events |= MsiInterop.MsidbServiceConfigEventReinstall;
2793 //}
2794 break;
2795 case "OnUninstall":
2796 uninstall = YesNoType.Yes == this.Core.GetAttributeYesNoValue(sourceLineNumbers, attrib);
2797 //if (YesNoType.Yes == uninstall)
2798 //{
2799 // events |= MsiInterop.MsidbServiceConfigEventUninstall;
2800 //}
2801 break;
2802 default:
2803 this.Core.UnexpectedAttribute(node, attrib);
2804 break;
2805 case "PreShutdownDelay":
2806 preShutdownDelay = this.Core.GetAttributeValue(sourceLineNumbers, attrib, EmptyRule.CanBeEmpty);
2807 break;
2808 case "ServiceName":
2809 if (!String.IsNullOrEmpty(serviceName))
2810 {
2811 this.Core.Write(ErrorMessages.IllegalAttributeWhenNested(sourceLineNumbers, node.Name.LocalName, attrib.Name.LocalName, "ServiceInstall"));
2812 }
2813
2814 name = this.Core.GetAttributeValue(sourceLineNumbers, attrib);
2815 break;
2816 case "ServiceSid":
2817 sid = this.Core.GetAttributeValue(sourceLineNumbers, attrib);
2818 switch (sid)
2819 {
2820 case "none":
2821 sid = "0";
2822 break;
2823 case "restricted":
2824 sid = "3";
2825 break;
2826 case "unrestricted":
2827 sid = "1";
2828 break;
2829 default:
2830 // allow everything else to pass through that are hopefully "formatted" Properties.
2831 break;
2832 }
2833 break;
2834 }
2835 }
2836 else
2837 {
2838 this.Core.ParseExtensionAttribute(node, attrib);
2839 }
2840 }
2841
2842 // Get the ServiceConfig required privilegs.
2843 foreach (var child in node.Elements())
2844 {
2845 if (CompilerCore.WixNamespace == child.Name.Namespace)
2846 {
2847 switch (child.Name.LocalName)
2848 {
2849 case "RequiredPrivilege":
2850 requiredPrivileges = this.ParseRequiredPrivilege(child, requiredPrivileges);
2851 break;
2852 default:
2853 this.Core.UnexpectedElement(node, child);
2854 break;
2855 }
2856 }
2857 else
2858 {
2859 this.Core.ParseExtensionElement(node, child);
2860 }
2861 }
2862
2863 if (String.IsNullOrEmpty(name))
2864 {
2865 this.Core.Write(ErrorMessages.ExpectedAttribute(sourceLineNumbers, node.Name.LocalName, "ServiceName"));
2866 }
2867 else if (null == id)
2868 {
2869 id = this.Core.CreateIdentifierFromFilename(name);
2870 }
2871
2872 if (!install && !reinstall && !uninstall)
2873 {
2874 this.Core.Write(ErrorMessages.ExpectedAttributes(sourceLineNumbers, node.Name.LocalName, "OnInstall", "OnReinstall", "OnUninstall"));
2875 }
2876
2877 if (String.IsNullOrEmpty(delayedAutoStart) && String.IsNullOrEmpty(failureActionsWhen) && String.IsNullOrEmpty(preShutdownDelay) && String.IsNullOrEmpty(requiredPrivileges) && String.IsNullOrEmpty(sid))
2878 {
2879 this.Core.Write(ErrorMessages.ExpectedAttributes(sourceLineNumbers, node.Name.LocalName, "DelayedAutoStart", "FailureActionsWhen", "PreShutdownDelay", "ServiceSid", "RequiredPrivilege"));
2880 }
2881
2882 if (!this.Core.EncounteredError)
2883 {
2884 if (!String.IsNullOrEmpty(delayedAutoStart))
2885 {
2886 this.Core.AddSymbol(new MsiServiceConfigSymbol(sourceLineNumbers, new Identifier(id.Access, String.Concat(id.Id, ".DS")))
2887 {
2888 Name = name,
2889 OnInstall = install,
2890 OnReinstall = reinstall,
2891 OnUninstall = uninstall,
2892 ConfigType = MsiServiceConfigType.DelayedAutoStart,
2893 Argument = delayedAutoStart,
2894 ComponentRef = componentId,
2895 });
2896 }
2897
2898 if (!String.IsNullOrEmpty(failureActionsWhen))
2899 {
2900 this.Core.AddSymbol(new MsiServiceConfigSymbol(sourceLineNumbers, new Identifier(id.Access, String.Concat(id.Id, ".FA")))
2901 {
2902 Name = name,
2903 OnInstall = install,
2904 OnReinstall = reinstall,
2905 OnUninstall = uninstall,
2906 ConfigType = MsiServiceConfigType.FailureActionsFlag,
2907 Argument = failureActionsWhen,
2908 ComponentRef = componentId,
2909 });
2910 }
2911
2912 if (!String.IsNullOrEmpty(sid))
2913 {
2914 this.Core.AddSymbol(new MsiServiceConfigSymbol(sourceLineNumbers, new Identifier(id.Access, String.Concat(id.Id, ".SS")))
2915 {
2916 Name = name,
2917 OnInstall = install,
2918 OnReinstall = reinstall,
2919 OnUninstall = uninstall,
2920 ConfigType = MsiServiceConfigType.ServiceSidInfo,
2921 Argument = sid,
2922 ComponentRef = componentId,
2923 });
2924 }
2925
2926 if (!String.IsNullOrEmpty(requiredPrivileges))
2927 {
2928 this.Core.AddSymbol(new MsiServiceConfigSymbol(sourceLineNumbers, new Identifier(id.Access, String.Concat(id.Id, ".RP")))
2929 {
2930 Name = name,
2931 OnInstall = install,
2932 OnReinstall = reinstall,
2933 OnUninstall = uninstall,
2934 ConfigType = MsiServiceConfigType.RequiredPrivilegesInfo,
2935 Argument = requiredPrivileges,
2936 ComponentRef = componentId,
2937 });
2938 }
2939
2940 if (!String.IsNullOrEmpty(preShutdownDelay))
2941 {
2942 this.Core.AddSymbol(new MsiServiceConfigSymbol(sourceLineNumbers, new Identifier(id.Access, String.Concat(id.Id, ".PD")))
2943 {
2944 Name = name,
2945 OnInstall = install,
2946 OnReinstall = reinstall,
2947 OnUninstall = uninstall,
2948 ConfigType = MsiServiceConfigType.PreshutdownInfo,
2949 Argument = preShutdownDelay,
2950 ComponentRef = componentId,
2951 });
2952 }
2953 }
2954 }
2955
2956 private string ParseRequiredPrivilege(XElement node, string requiredPrivileges)
2957 {
2958 var sourceLineNumbers = Preprocessor.GetSourceLineNumbers(node);
2959 string privilege = null;
2960
2961 foreach (var attrib in node.Attributes())
2962 {
2963 if (String.IsNullOrEmpty(attrib.Name.NamespaceName) || CompilerCore.WixNamespace == attrib.Name.Namespace)
2964 {
2965 switch (attrib.Name.LocalName)
2966 {
2967 case "Name":
2968 privilege = this.Core.GetAttributeValue(sourceLineNumbers, attrib);
2969 switch (privilege)
2970 {
2971 case "assignPrimaryToken":
2972 privilege = "SeAssignPrimaryTokenPrivilege";
2973 break;
2974 case "audit":
2975 privilege = "SeAuditPrivilege";
2976 break;
2977 case "backup":
2978 privilege = "SeBackupPrivilege";
2979 break;
2980 case "changeNotify":
2981 privilege = "SeChangeNotifyPrivilege";
2982 break;
2983 case "createGlobal":
2984 privilege = "SeCreateGlobalPrivilege";
2985 break;
2986 case "createPagefile":
2987 privilege = "SeCreatePagefilePrivilege";
2988 break;
2989 case "createPermanent":
2990 privilege = "SeCreatePermanentPrivilege";
2991 break;
2992 case "createSymbolicLink":
2993 privilege = "SeCreateSymbolicLinkPrivilege";
2994 break;
2995 case "createToken":
2996 privilege = "SeCreateTokenPrivilege";
2997 break;
2998 case "debug":
2999 privilege = "SeDebugPrivilege";
3000 break;
3001 case "enableDelegation":
3002 privilege = "SeEnableDelegationPrivilege";
3003 break;
3004 case "impersonate":
3005 privilege = "SeImpersonatePrivilege";
3006 break;
3007 case "increaseBasePriority":
3008 privilege = "SeIncreaseBasePriorityPrivilege";
3009 break;
3010 case "increaseQuota":
3011 privilege = "SeIncreaseQuotaPrivilege";
3012 break;
3013 case "increaseWorkingSet":
3014 privilege = "SeIncreaseWorkingSetPrivilege";
3015 break;
3016 case "loadDriver":
3017 privilege = "SeLoadDriverPrivilege";
3018 break;
3019 case "lockMemory":
3020 privilege = "SeLockMemoryPrivilege";
3021 break;
3022 case "machineAccount":
3023 privilege = "SeMachineAccountPrivilege";
3024 break;
3025 case "manageVolume":
3026 privilege = "SeManageVolumePrivilege";
3027 break;
3028 case "profileSingleProcess":
3029 privilege = "SeProfileSingleProcessPrivilege";
3030 break;
3031 case "relabel":
3032 privilege = "SeRelabelPrivilege";
3033 break;
3034 case "remoteShutdown":
3035 privilege = "SeRemoteShutdownPrivilege";
3036 break;
3037 case "restore":
3038 privilege = "SeRestorePrivilege";
3039 break;
3040 case "security":
3041 privilege = "SeSecurityPrivilege";
3042 break;
3043 case "shutdown":
3044 privilege = "SeShutdownPrivilege";
3045 break;
3046 case "syncAgent":
3047 privilege = "SeSyncAgentPrivilege";
3048 break;
3049 case "systemEnvironment":
3050 privilege = "SeSystemEnvironmentPrivilege";
3051 break;
3052 case "systemProfile":
3053 privilege = "SeSystemProfilePrivilege";
3054 break;
3055 case "systemTime":
3056 case "modifySystemTime":
3057 privilege = "SeSystemtimePrivilege";
3058 break;
3059 case "takeOwnership":
3060 privilege = "SeTakeOwnershipPrivilege";
3061 break;
3062 case "tcb":
3063 case "trustedComputerBase":
3064 privilege = "SeTcbPrivilege";
3065 break;
3066 case "timeZone":
3067 case "modifyTimeZone":
3068 privilege = "SeTimeZonePrivilege";
3069 break;
3070 case "trustedCredManAccess":
3071 case "trustedCredentialManagerAccess":
3072 privilege = "SeTrustedCredManAccessPrivilege";
3073 break;
3074 case "undock":
3075 privilege = "SeUndockPrivilege";
3076 break;
3077 case "unsolicitedInput":
3078 privilege = "SeUnsolicitedInputPrivilege";
3079 break;
3080 default:
3081 // allow everything else to pass through that are hopefully "formatted" Properties.
3082 break;
3083 }
3084 break;
3085 default:
3086 this.Core.UnexpectedAttribute(node, attrib);
3087 break;
3088 }
3089 }
3090 }
3091
3092 if (privilege == null)
3093 {
3094 this.Core.Write(ErrorMessages.ExpectedAttribute(sourceLineNumbers, node.Name.LocalName, "Name"));
3095 }
3096
3097 this.Core.ParseForExtensionElements(node);
3098
3099 return (requiredPrivileges == null) ? privilege : String.Concat(requiredPrivileges, "[~]", privilege);
3100 }
3101
3102 /// <summary>
3103 /// Parses a service config failure actions element.
3104 /// </summary>
3105 /// <param name="node">Element to parse.</param>
3106 /// <param name="componentId">Identifier of parent component.</param>
3107 /// <param name="serviceName">Optional element containing parent's service name.</param>
3108 private void ParseServiceConfigFailureActionsElement(XElement node, string componentId, string serviceName)
3109 {
3110 var sourceLineNumbers = Preprocessor.GetSourceLineNumbers(node);
3111 Identifier id = null;
3112 var name = serviceName;
3113 var install = false;
3114 var reinstall = false;
3115 var uninstall = false;
3116 int? resetPeriod = null;
3117 string rebootMessage = null;
3118 string command = null;
3119 string actions = null;
3120 string actionsDelays = null;
3121
3122 this.Core.Write(WarningMessages.ServiceConfigFamilyNotSupported(sourceLineNumbers, node.Name.LocalName));
3123
3124 foreach (var attrib in node.Attributes())
3125 {
3126 if (String.IsNullOrEmpty(attrib.Name.NamespaceName) || CompilerCore.WixNamespace == attrib.Name.Namespace)
3127 {
3128 switch (attrib.Name.LocalName)
3129 {
3130 case "Id":
3131 id = this.Core.GetAttributeIdentifier(sourceLineNumbers, attrib);
3132 break;
3133 case "Command":
3134 command = this.Core.GetAttributeValue(sourceLineNumbers, attrib, EmptyRule.CanBeEmpty);
3135 break;
3136 case "OnInstall":
3137 install = YesNoType.Yes == this.Core.GetAttributeYesNoValue(sourceLineNumbers, attrib);
3138 break;
3139 case "OnReinstall":
3140 reinstall = YesNoType.Yes == this.Core.GetAttributeYesNoValue(sourceLineNumbers, attrib);
3141 break;
3142 case "OnUninstall":
3143 uninstall = YesNoType.Yes == this.Core.GetAttributeYesNoValue(sourceLineNumbers, attrib);
3144 break;
3145 case "RebootMessage":
3146 rebootMessage = this.Core.GetAttributeValue(sourceLineNumbers, attrib, EmptyRule.CanBeEmpty);
3147 break;
3148 case "ResetPeriod":
3149 resetPeriod = this.Core.GetAttributeIntegerValue(sourceLineNumbers, attrib, 0, Int32.MaxValue);
3150 break;
3151 case "ServiceName":
3152 if (!String.IsNullOrEmpty(serviceName))
3153 {
3154 this.Core.Write(ErrorMessages.IllegalAttributeWhenNested(sourceLineNumbers, node.Name.LocalName, attrib.Name.LocalName, "ServiceInstall"));
3155 }
3156
3157 name = this.Core.GetAttributeValue(sourceLineNumbers, attrib);
3158 break;
3159 default:
3160 this.Core.UnexpectedAttribute(node, attrib);
3161 break;
3162 }
3163 }
3164 else
3165 {
3166 this.Core.ParseExtensionAttribute(node, attrib);
3167 }
3168 }
3169
3170 // Get the ServiceConfigFailureActions actions.
3171 foreach (var child in node.Elements())
3172 {
3173 if (CompilerCore.WixNamespace == child.Name.Namespace)
3174 {
3175 switch (child.Name.LocalName)
3176 {
3177 case "Failure":
3178 string action = null;
3179 string delay = null;
3180 var childSourceLineNumbers = Preprocessor.GetSourceLineNumbers(child);
3181
3182 foreach (var childAttrib in child.Attributes())
3183 {
3184 if (String.IsNullOrEmpty(childAttrib.Name.NamespaceName) || CompilerCore.WixNamespace == childAttrib.Name.Namespace)
3185 {
3186 switch (childAttrib.Name.LocalName)
3187 {
3188 case "Action":
3189 action = this.Core.GetAttributeValue(childSourceLineNumbers, childAttrib);
3190 switch (action)
3191 {
3192 case "none":
3193 action = "0";
3194 break;
3195 case "restartComputer":
3196 action = "2";
3197 break;
3198 case "restartService":
3199 action = "1";
3200 break;
3201 case "runCommand":
3202 action = "3";
3203 break;
3204 default:
3205 // allow everything else to pass through that are hopefully "formatted" Properties.
3206 break;
3207 }
3208 break;
3209 case "Delay":
3210 delay = this.Core.GetAttributeValue(childSourceLineNumbers, childAttrib);
3211 break;
3212 default:
3213 this.Core.UnexpectedAttribute(child, childAttrib);
3214 break;
3215 }
3216 }
3217 }
3218
3219 if (String.IsNullOrEmpty(action))
3220 {
3221 this.Core.Write(ErrorMessages.ExpectedAttribute(sourceLineNumbers, child.Name.LocalName, "Action"));
3222 }
3223
3224 if (String.IsNullOrEmpty(delay))
3225 {
3226 this.Core.Write(ErrorMessages.ExpectedAttribute(sourceLineNumbers, child.Name.LocalName, "Delay"));
3227 }
3228
3229 if (!String.IsNullOrEmpty(actions))
3230 {
3231 actions = String.Concat(actions, "[~]");
3232 }
3233 actions = String.Concat(actions, action);
3234
3235 if (!String.IsNullOrEmpty(actionsDelays))
3236 {
3237 actionsDelays = String.Concat(actionsDelays, "[~]");
3238 }
3239 actionsDelays = String.Concat(actionsDelays, delay);
3240 break;
3241 default:
3242 this.Core.UnexpectedElement(node, child);
3243 break;
3244 }
3245 }
3246 else
3247 {
3248 this.Core.ParseExtensionElement(node, child);
3249 }
3250 }
3251
3252 if (String.IsNullOrEmpty(name))
3253 {
3254 this.Core.Write(ErrorMessages.ExpectedAttribute(sourceLineNumbers, node.Name.LocalName, "ServiceName"));
3255 }
3256 else if (null == id)
3257 {
3258 id = this.Core.CreateIdentifierFromFilename(name);
3259 }
3260
3261 if (!install && !reinstall && !uninstall)
3262 {
3263 this.Core.Write(ErrorMessages.ExpectedAttributes(sourceLineNumbers, node.Name.LocalName, "OnInstall", "OnReinstall", "OnUninstall"));
3264 }
3265
3266 if (!this.Core.EncounteredError)
3267 {
3268 this.Core.AddSymbol(new MsiServiceConfigFailureActionsSymbol(sourceLineNumbers, id)
3269 {
3270 Name = name,
3271 OnInstall = install,
3272 OnReinstall = reinstall,
3273 OnUninstall = uninstall,
3274 ResetPeriod = resetPeriod,
3275 RebootMessage = rebootMessage,
3276 Command = command,
3277 Actions = actions,
3278 DelayActions = actionsDelays,
3279 ComponentRef = componentId,
3280 });
3281 }
3282 }
3283
3284 /// <summary>
3285 /// Parses a service control element.
3286 /// </summary>
3287 /// <param name="node">Element to parse.</param>
3288 /// <param name="componentId">Identifier of parent component.</param>
3289 private void ParseServiceControlElement(XElement node, string componentId)
3290 {
3291 var sourceLineNumbers = Preprocessor.GetSourceLineNumbers(node);
3292 string arguments = null;
3293 Identifier id = null;
3294 string name = null;
3295 var installRemove = false;
3296 var uninstallRemove = false;
3297 var installStart = false;
3298 var uninstallStart = false;
3299 var installStop = false;
3300 var uninstallStop = false;
3301 bool? wait = null;
3302
3303 foreach (var attrib in node.Attributes())
3304 {
3305 if (String.IsNullOrEmpty(attrib.Name.NamespaceName) || CompilerCore.WixNamespace == attrib.Name.Namespace)
3306 {
3307 switch (attrib.Name.LocalName)
3308 {
3309 case "Id":
3310 id = this.Core.GetAttributeIdentifier(sourceLineNumbers, attrib);
3311 break;
3312 case "Name":
3313 name = this.Core.GetAttributeValue(sourceLineNumbers, attrib);
3314 break;
3315 case "Remove":
3316 var removeValue = this.Core.GetAttributeValue(sourceLineNumbers, attrib);
3317 switch (removeValue)
3318 {
3319 case "install":
3320 installRemove = true;
3321 break;
3322 case "uninstall":
3323 uninstallRemove = true;
3324 break;
3325 case "both":
3326 installRemove = true;
3327 uninstallRemove = true;
3328 break;
3329 case "":
3330 break;
3331 }
3332 break;
3333 case "Start":
3334 var startValue = this.Core.GetAttributeValue(sourceLineNumbers, attrib);
3335 switch (startValue)
3336 {
3337 case "install":
3338 installStart = true;
3339 break;
3340 case "uninstall":
3341 uninstallStart = true;
3342 break;
3343 case "both":
3344 installStart = true;
3345 uninstallStart = true;
3346 break;
3347 case "":
3348 break;
3349 }
3350 break;
3351 case "Stop":
3352 var stopValue = this.Core.GetAttributeValue(sourceLineNumbers, attrib);
3353 switch (stopValue)
3354 {
3355 case "install":
3356 installStop = true;
3357 break;
3358 case "uninstall":
3359 uninstallStop = true;
3360 break;
3361 case "both":
3362 installStop = true;
3363 uninstallStop = true;
3364 break;
3365 case "":
3366 break;
3367 }
3368 break;
3369 case "Wait":
3370 wait = YesNoType.Yes == this.Core.GetAttributeYesNoValue(sourceLineNumbers, attrib);
3371 break;
3372 default:
3373 this.Core.UnexpectedAttribute(node, attrib);
3374 break;
3375 }
3376 }
3377 else
3378 {
3379 this.Core.ParseExtensionAttribute(node, attrib);
3380 }
3381 }
3382
3383 if (null == id)
3384 {
3385 id = this.Core.CreateIdentifierFromFilename(name);
3386 }
3387
3388 if (null == name)
3389 {
3390 this.Core.Write(ErrorMessages.ExpectedAttribute(sourceLineNumbers, node.Name.LocalName, "Name"));
3391 }
3392
3393 // get the ServiceControl arguments
3394 foreach (var child in node.Elements())
3395 {
3396 if (CompilerCore.WixNamespace == child.Name.Namespace)
3397 {
3398 switch (child.Name.LocalName)
3399 {
3400 case "ServiceArgument":
3401 arguments = this.ParseServiceArgument(child, arguments);
3402 break;
3403 default:
3404 this.Core.UnexpectedElement(node, child);
3405 break;
3406 }
3407 }
3408 else
3409 {
3410 this.Core.ParseExtensionElement(node, child);
3411 }
3412 }
3413
3414 if (!this.Core.EncounteredError)
3415 {
3416 this.Core.AddSymbol(new ServiceControlSymbol(sourceLineNumbers, id)
3417 {
3418 Name = name,
3419 InstallRemove = installRemove,
3420 UninstallRemove = uninstallRemove,
3421 InstallStart = installStart,
3422 UninstallStart = uninstallStart,
3423 InstallStop = installStop,
3424 UninstallStop = uninstallStop,
3425 Arguments = arguments,
3426 Wait = wait,
3427 ComponentRef = componentId
3428 });
3429 }
3430 }
3431
3432 private string ParseServiceArgument(XElement node, string arguments)
3433 {
3434 var sourceLineNumbers = Preprocessor.GetSourceLineNumbers(node);
3435 string argument = null;
3436
3437 foreach (var attrib in node.Attributes())
3438 {
3439 if (String.IsNullOrEmpty(attrib.Name.NamespaceName) || CompilerCore.WixNamespace == attrib.Name.Namespace)
3440 {
3441 switch (attrib.Name.LocalName)
3442 {
3443 case "Value":
3444 argument = this.Core.GetAttributeValue(sourceLineNumbers, attrib);
3445 break;
3446 default:
3447 this.Core.UnexpectedAttribute(node, attrib);
3448 break;
3449 }
3450 }
3451 }
3452
3453 if (argument == null)
3454 {
3455 this.Core.Write(ErrorMessages.ExpectedAttribute(sourceLineNumbers, node.Name.LocalName, "Value"));
3456 }
3457
3458 this.Core.ParseForExtensionElements(node);
3459
3460 return (arguments == null) ? argument : String.Concat(arguments, "[~]", argument);
3461 }
3462
3463 /// <summary>
3464 /// Parses a service dependency element.
3465 /// </summary>
3466 /// <param name="node">Element to parse.</param>
3467 /// <returns>Parsed sevice dependency name.</returns>
3468 private string ParseServiceDependencyElement(XElement node)
3469 {
3470 var sourceLineNumbers = Preprocessor.GetSourceLineNumbers(node);
3471 string dependency = null;
3472 var group = false;
3473
3474 foreach (var attrib in node.Attributes())
3475 {
3476 if (String.IsNullOrEmpty(attrib.Name.NamespaceName) || CompilerCore.WixNamespace == attrib.Name.Namespace)
3477 {
3478 switch (attrib.Name.LocalName)
3479 {
3480 case "Id":
3481 dependency = this.Core.GetAttributeValue(sourceLineNumbers, attrib);
3482 break;
3483 case "Group":
3484 group = YesNoType.Yes == this.Core.GetAttributeYesNoValue(sourceLineNumbers, attrib);
3485 break;
3486 default:
3487 this.Core.UnexpectedAttribute(node, attrib);
3488 break;
3489 }
3490 }
3491 else
3492 {
3493 this.Core.ParseExtensionAttribute(node, attrib);
3494 }
3495 }
3496
3497 if (null == dependency)
3498 {
3499 this.Core.Write(ErrorMessages.ExpectedAttribute(sourceLineNumbers, node.Name.LocalName, "Id"));
3500 }
3501
3502 this.Core.ParseForExtensionElements(node);
3503
3504 return group ? String.Concat("+", dependency) : dependency;
3505 }
3506
3507 /// <summary>
3508 /// Parses a service install element.
3509 /// </summary>
3510 /// <param name="node">Element to parse.</param>
3511 /// <param name="componentId">Identifier of parent component.</param>
3512 /// <param name="win64Component"></param>
3513 private void ParseServiceInstallElement(XElement node, string componentId, bool win64Component)
3514 {
3515 var sourceLineNumbers = Preprocessor.GetSourceLineNumbers(node);
3516 Identifier id = null;
3517 string account = null;
3518 string arguments = null;
3519 string dependencies = null;
3520 string description = null;
3521 string displayName = null;
3522 var eraseDescription = false;
3523 string loadOrderGroup = null;
3524 string name = null;
3525 string password = null;
3526
3527 var serviceType = ServiceType.OwnProcess;
3528 var startType = ServiceStartType.Demand;
3529 var errorControl = ServiceErrorControl.Normal;
3530 var interactive = false;
3531 var vital = false;
3532
3533 foreach (var attrib in node.Attributes())
3534 {
3535 if (String.IsNullOrEmpty(attrib.Name.NamespaceName) || CompilerCore.WixNamespace == attrib.Name.Namespace)
3536 {
3537 switch (attrib.Name.LocalName)
3538 {
3539 case "Id":
3540 id = this.Core.GetAttributeIdentifier(sourceLineNumbers, attrib);
3541 break;
3542 case "Account":
3543 account = this.Core.GetAttributeValue(sourceLineNumbers, attrib);
3544 break;
3545 case "Arguments":
3546 arguments = this.Core.GetAttributeValue(sourceLineNumbers, attrib);
3547 break;
3548 case "Description":
3549 description = this.Core.GetAttributeValue(sourceLineNumbers, attrib);
3550 break;
3551 case "DisplayName":
3552 displayName = this.Core.GetAttributeValue(sourceLineNumbers, attrib);
3553 break;
3554 case "EraseDescription":
3555 eraseDescription = YesNoType.Yes == this.Core.GetAttributeYesNoValue(sourceLineNumbers, attrib);
3556 break;
3557 case "ErrorControl":
3558 var errorControlValue = this.Core.GetAttributeValue(sourceLineNumbers, attrib);
3559 switch (errorControlValue)
3560 {
3561 case "ignore":
3562 errorControl = ServiceErrorControl.Ignore;
3563 break;
3564 case "normal":
3565 errorControl = ServiceErrorControl.Normal;
3566 break;
3567 case "critical":
3568 errorControl = ServiceErrorControl.Critical;
3569 break;
3570 case "": // error case handled by GetAttributeValue()
3571 break;
3572 default:
3573 this.Core.Write(ErrorMessages.IllegalAttributeValue(sourceLineNumbers, node.Name.LocalName, attrib.Name.LocalName, errorControlValue, "ignore", "normal", "critical"));
3574 break;
3575 }
3576 break;
3577 case "Interactive":
3578 interactive = YesNoType.Yes == this.Core.GetAttributeYesNoValue(sourceLineNumbers, attrib);
3579 break;
3580 case "LoadOrderGroup":
3581 loadOrderGroup = this.Core.GetAttributeValue(sourceLineNumbers, attrib);
3582 break;
3583 case "Name":
3584 name = this.Core.GetAttributeValue(sourceLineNumbers, attrib);
3585 break;
3586 case "Password":
3587 password = this.Core.GetAttributeValue(sourceLineNumbers, attrib);
3588 break;
3589 case "Start":
3590 var startValue = this.Core.GetAttributeValue(sourceLineNumbers, attrib);
3591 switch (startValue)
3592 {
3593 case "auto":
3594 startType = ServiceStartType.Auto;
3595 break;
3596 case "demand":
3597 startType = ServiceStartType.Demand;
3598 break;
3599 case "disabled":
3600 startType = ServiceStartType.Disabled;
3601 break;
3602 case "boot":
3603 case "system":
3604 this.Core.Write(ErrorMessages.ValueNotSupported(sourceLineNumbers, node.Name.LocalName, attrib.Name.LocalName, startValue));
3605 break;
3606 case "":
3607 break;
3608 default:
3609 this.Core.Write(ErrorMessages.IllegalAttributeValue(sourceLineNumbers, node.Name.LocalName, attrib.Name.LocalName, startValue, "auto", "demand", "disabled"));
3610 break;
3611 }
3612 break;
3613 case "Type":
3614 var typeValue = this.Core.GetAttributeValue(sourceLineNumbers, attrib);
3615 switch (typeValue)
3616 {
3617 case "ownProcess":
3618 serviceType = ServiceType.OwnProcess;
3619 break;
3620 case "shareProcess":
3621 serviceType = ServiceType.ShareProcess;
3622 break;
3623 case "kernelDriver":
3624 case "systemDriver":
3625 this.Core.Write(ErrorMessages.ValueNotSupported(sourceLineNumbers, node.Name.LocalName, attrib.Name.LocalName, typeValue));
3626 break;
3627 case "":
3628 break;
3629 default:
3630 this.Core.Write(ErrorMessages.IllegalAttributeValue(sourceLineNumbers, node.Name.LocalName, node.Name.LocalName, typeValue, "ownProcess", "shareProcess"));
3631 break;
3632 }
3633 break;
3634 case "Vital":
3635 vital = YesNoType.Yes == this.Core.GetAttributeYesNoValue(sourceLineNumbers, attrib);
3636 break;
3637 default:
3638 this.Core.UnexpectedAttribute(node, attrib);
3639 break;
3640 }
3641 }
3642 else
3643 {
3644 this.Core.ParseExtensionAttribute(node, attrib);
3645 }
3646 }
3647
3648 if (String.IsNullOrEmpty(name))
3649 {
3650 this.Core.Write(ErrorMessages.ExpectedAttribute(sourceLineNumbers, node.Name.LocalName, "Name"));
3651 }
3652 else if (null == id)
3653 {
3654 id = this.Core.CreateIdentifierFromFilename(name);
3655 }
3656
3657 if (0 == startType)
3658 {
3659 this.Core.Write(ErrorMessages.ExpectedAttribute(sourceLineNumbers, node.Name.LocalName, "Start"));
3660 }
3661
3662 if (eraseDescription)
3663 {
3664 description = "[~]";
3665 }
3666
3667 // get the ServiceInstall dependencies and config
3668 foreach (var child in node.Elements())
3669 {
3670 if (CompilerCore.WixNamespace == child.Name.Namespace)
3671 {
3672 switch (child.Name.LocalName)
3673 {
3674 case "PermissionEx":
3675 this.ParsePermissionExElement(child, id.Id, "ServiceInstall");
3676 break;
3677 case "ServiceConfig":
3678 this.ParseServiceConfigElement(child, componentId, name);
3679 break;
3680 case "ServiceConfigFailureActions":
3681 this.ParseServiceConfigFailureActionsElement(child, componentId, name);
3682 break;
3683 case "ServiceDependency":
3684 dependencies = String.Concat(dependencies, this.ParseServiceDependencyElement(child), "[~]");
3685 break;
3686 default:
3687 this.Core.UnexpectedElement(node, child);
3688 break;
3689 }
3690 }
3691 else
3692 {
3693 var context = new Dictionary<string, string>() { { "ServiceInstallId", id?.Id }, { "ServiceInstallName", name }, { "ServiceInstallComponentId", componentId }, { "Win64", win64Component.ToString() } };
3694 this.Core.ParseExtensionElement(node, child, context);
3695 }
3696 }
3697
3698 if (null != dependencies)
3699 {
3700 dependencies = String.Concat(dependencies, "[~]");
3701 }
3702
3703 if (!this.Core.EncounteredError)
3704 {
3705 this.Core.AddSymbol(new ServiceInstallSymbol(sourceLineNumbers, id)
3706 {
3707 Name = name,
3708 DisplayName = displayName,
3709 ServiceType = serviceType,
3710 StartType = startType,
3711 ErrorControl = errorControl,
3712 LoadOrderGroup = loadOrderGroup,
3713 Dependencies = dependencies,
3714 StartName = account,
3715 Password = password,
3716 Arguments = arguments,
3717 ComponentRef = componentId,
3718 Description = description,
3719 Interactive = interactive,
3720 Vital = vital
3721 });
3722 }
3723 }
3724
3725 /// <summary>
3726 /// Parses a SetDirectory element.
3727 /// </summary>
3728 /// <param name="node">Element to parse.</param>
3729 private void ParseSetDirectoryElement(XElement node)
3730 {
3731 var sourceLineNumbers = Preprocessor.GetSourceLineNumbers(node);
3732 string actionName = null;
3733 string id = null;
3734 string condition = null;
3735 var executionType = CustomActionExecutionType.Immediate;
3736 var sequences = new[] { SequenceTable.InstallUISequence, SequenceTable.InstallExecuteSequence }; // default to "both"
3737 string value = null;
3738
3739 foreach (var attrib in node.Attributes())
3740 {
3741 if (String.IsNullOrEmpty(attrib.Name.NamespaceName) || CompilerCore.WixNamespace == attrib.Name.Namespace)
3742 {
3743 switch (attrib.Name.LocalName)
3744 {
3745 case "Action":
3746 actionName = this.Core.GetAttributeIdentifierValue(sourceLineNumbers, attrib);
3747 break;
3748 case "Condition":
3749 condition = this.Core.GetAttributeValue(sourceLineNumbers, attrib);
3750 break;
3751 case "Id":
3752 id = this.Core.GetAttributeIdentifierValue(sourceLineNumbers, attrib);
3753 this.Core.CreateSimpleReference(sourceLineNumbers, SymbolDefinitions.Directory, id);
3754 break;
3755 case "Sequence":
3756 var sequenceValue = this.Core.GetAttributeValue(sourceLineNumbers, attrib);
3757 switch (sequenceValue)
3758 {
3759 case "execute":
3760 sequences = new[] { SequenceTable.InstallExecuteSequence };
3761 break;
3762 case "first":
3763 executionType = CustomActionExecutionType.FirstSequence;
3764 break;
3765 case "ui":
3766 sequences = new[] { SequenceTable.InstallUISequence };
3767 break;
3768 case "both":
3769 break;
3770 case "":
3771 break;
3772 default:
3773 this.Core.Write(ErrorMessages.IllegalAttributeValue(sourceLineNumbers, node.Name.LocalName, attrib.Name.LocalName, sequenceValue, "execute", "ui", "both"));
3774 break;
3775 }
3776 break;
3777 case "Value":
3778 value = this.Core.GetAttributeValue(sourceLineNumbers, attrib);
3779 break;
3780 default:
3781 this.Core.UnexpectedAttribute(node, attrib);
3782 break;
3783 }
3784 }
3785 else
3786 {
3787 this.Core.ParseExtensionAttribute(node, attrib);
3788 }
3789 }
3790
3791 if (null == id)
3792 {
3793 this.Core.Write(ErrorMessages.ExpectedAttribute(sourceLineNumbers, node.Name.LocalName, "Id"));
3794 }
3795 else if (String.IsNullOrEmpty(actionName))
3796 {
3797 actionName = String.Concat("Set", id);
3798 }
3799
3800 if (null == value)
3801 {
3802 this.Core.Write(ErrorMessages.ExpectedAttribute(sourceLineNumbers, node.Name.LocalName, "Value"));
3803 }
3804
3805 this.Core.ParseForExtensionElements(node);
3806
3807 if (!this.Core.EncounteredError)
3808 {
3809 this.Core.AddSymbol(new CustomActionSymbol(sourceLineNumbers, new Identifier(AccessModifier.Global, actionName))
3810 {
3811 ExecutionType = executionType,
3812 SourceType = CustomActionSourceType.Directory,
3813 TargetType = CustomActionTargetType.TextData,
3814 Source = id,
3815 Target = value
3816 });
3817
3818 foreach (var sequence in sequences)
3819 {
3820 this.Core.ScheduleActionSymbol(sourceLineNumbers, AccessModifier.Global, sequence, actionName, condition, afterAction: "CostFinalize");
3821 }
3822 }
3823 }
3824
3825 /// <summary>
3826 /// Parses a SetProperty element.
3827 /// </summary>
3828 /// <param name="node">Element to parse.</param>
3829 private void ParseSetPropertyElement(XElement node)
3830 {
3831 var sourceLineNumbers = Preprocessor.GetSourceLineNumbers(node);
3832 string actionName = null;
3833 string id = null;
3834 string condition = null;
3835 string afterAction = null;
3836 string beforeAction = null;
3837 var executionType = CustomActionExecutionType.Immediate;
3838 var sequences = new[] { SequenceTable.InstallUISequence, SequenceTable.InstallExecuteSequence }; // default to "both"
3839 string value = null;
3840
3841 foreach (var attrib in node.Attributes())
3842 {
3843 if (String.IsNullOrEmpty(attrib.Name.NamespaceName) || CompilerCore.WixNamespace == attrib.Name.Namespace)
3844 {
3845 switch (attrib.Name.LocalName)
3846 {
3847 case "Action":
3848 actionName = this.Core.GetAttributeIdentifierValue(sourceLineNumbers, attrib);
3849 break;
3850 case "Id":
3851 id = this.Core.GetAttributeIdentifierValue(sourceLineNumbers, attrib);
3852 break;
3853 case "Condition":
3854 condition = this.Core.GetAttributeValue(sourceLineNumbers, attrib);
3855 break;
3856 case "After":
3857 afterAction = this.Core.GetAttributeIdentifierValue(sourceLineNumbers, attrib);
3858 break;
3859 case "Before":
3860 beforeAction = this.Core.GetAttributeIdentifierValue(sourceLineNumbers, attrib);
3861 break;
3862 case "Sequence":
3863 var sequenceValue = this.Core.GetAttributeValue(sourceLineNumbers, attrib);
3864 switch (sequenceValue)
3865 {
3866 case "execute":
3867 sequences = new[] { SequenceTable.InstallExecuteSequence };
3868 break;
3869 case "first":
3870 executionType = CustomActionExecutionType.FirstSequence;
3871 break;
3872 case "ui":
3873 sequences = new[] { SequenceTable.InstallUISequence };
3874 break;
3875 case "both":
3876 break;
3877 case "":
3878 break;
3879 default:
3880 this.Core.Write(ErrorMessages.IllegalAttributeValue(sourceLineNumbers, node.Name.LocalName, attrib.Name.LocalName, sequenceValue, "execute", "ui", "both"));
3881 break;
3882 }
3883 break;
3884 case "Value":
3885 value = this.Core.GetAttributeValue(sourceLineNumbers, attrib, EmptyRule.CanBeEmpty);
3886 break;
3887 default:
3888 this.Core.UnexpectedAttribute(node, attrib);
3889 break;
3890 }
3891 }
3892 else
3893 {
3894 this.Core.ParseExtensionAttribute(node, attrib);
3895 }
3896 }
3897
3898 if (null == id)
3899 {
3900 this.Core.Write(ErrorMessages.ExpectedAttribute(sourceLineNumbers, node.Name.LocalName, "Id"));
3901 }
3902 else if (String.IsNullOrEmpty(actionName))
3903 {
3904 actionName = String.Concat("Set", id);
3905 }
3906
3907 if (null == value)
3908 {
3909 this.Core.Write(ErrorMessages.ExpectedAttribute(sourceLineNumbers, node.Name.LocalName, "Value"));
3910 }
3911
3912 if (null != beforeAction && null != afterAction)
3913 {
3914 this.Core.Write(ErrorMessages.IllegalAttributeWithOtherAttribute(sourceLineNumbers, node.Name.LocalName, "After", "Before"));
3915 }
3916 else if (null == beforeAction && null == afterAction)
3917 {
3918 this.Core.Write(ErrorMessages.ExpectedAttributesWithOtherAttribute(sourceLineNumbers, node.Name.LocalName, "After", "Before", "Id"));
3919 }
3920
3921 this.Core.ParseForExtensionElements(node);
3922
3923 // add the row and any references needed
3924 if (!this.Core.EncounteredError)
3925 {
3926 // action that is scheduled to occur before/after itself
3927 if (beforeAction == actionName)
3928 {
3929 this.Core.Write(ErrorMessages.ActionScheduledRelativeToItself(sourceLineNumbers, node.Name.LocalName, "Before", beforeAction));
3930 }
3931 else if (afterAction == actionName)
3932 {
3933 this.Core.Write(ErrorMessages.ActionScheduledRelativeToItself(sourceLineNumbers, node.Name.LocalName, "After", afterAction));
3934 }
3935
3936 this.Core.AddSymbol(new CustomActionSymbol(sourceLineNumbers, new Identifier(AccessModifier.Global, actionName))
3937 {
3938 ExecutionType = executionType,
3939 SourceType = CustomActionSourceType.Property,
3940 TargetType = CustomActionTargetType.TextData,
3941 Source = id,
3942 Target = value,
3943 });
3944
3945 foreach (var sequence in sequences)
3946 {
3947 this.Core.ScheduleActionSymbol(sourceLineNumbers, AccessModifier.Global, sequence, actionName, condition, beforeAction, afterAction);
3948 }
3949 }
3950 }
3951
3952 /// <summary>
3953 /// Parses a SFP catalog element.
3954 /// </summary>
3955 /// <param name="node">Element to parse.</param>
3956 /// <param name="parentSFPCatalog">Parent SFPCatalog.</param>
3957 private void ParseSFPFileElement(XElement node, string parentSFPCatalog)
3958 {
3959 var sourceLineNumbers = Preprocessor.GetSourceLineNumbers(node);
3960 string id = null;
3961
3962 foreach (var attrib in node.Attributes())
3963 {
3964 if (String.IsNullOrEmpty(attrib.Name.NamespaceName) || CompilerCore.WixNamespace == attrib.Name.Namespace)
3965 {
3966 switch (attrib.Name.LocalName)
3967 {
3968 case "Id":
3969 id = this.Core.GetAttributeIdentifierValue(sourceLineNumbers, attrib);
3970 break;
3971 default:
3972 this.Core.UnexpectedAttribute(node, attrib);
3973 break;
3974 }
3975 }
3976 else
3977 {
3978 this.Core.ParseExtensionAttribute(node, attrib);
3979 }
3980 }
3981
3982 if (null == id)
3983 {
3984 this.Core.Write(ErrorMessages.ExpectedAttribute(sourceLineNumbers, node.Name.LocalName, "Id"));
3985 }
3986
3987 this.Core.ParseForExtensionElements(node);
3988
3989 if (!this.Core.EncounteredError)
3990 {
3991 this.Core.AddSymbol(new FileSFPCatalogSymbol(sourceLineNumbers)
3992 {
3993 FileRef = id,
3994 SFPCatalogRef = parentSFPCatalog
3995 });
3996 }
3997 }
3998
3999 /// <summary>
4000 /// Parses a SFP catalog element.
4001 /// </summary>
4002 /// <param name="node">Element to parse.</param>
4003 /// <param name="parentSFPCatalog">Parent SFPCatalog.</param>
4004 private void ParseSFPCatalogElement(XElement node, ref string parentSFPCatalog)
4005 {
4006 var sourceLineNumbers = Preprocessor.GetSourceLineNumbers(node);
4007 string parentName = null;
4008 string dependency = null;
4009 string name = null;
4010 string sourceFile = null;
4011
4012 foreach (var attrib in node.Attributes())
4013 {
4014 if (String.IsNullOrEmpty(attrib.Name.NamespaceName) || CompilerCore.WixNamespace == attrib.Name.Namespace)
4015 {
4016 switch (attrib.Name.LocalName)
4017 {
4018 case "Dependency":
4019 dependency = this.Core.GetAttributeValue(sourceLineNumbers, attrib);
4020 break;
4021 case "Name":
4022 name = this.Core.GetAttributeShortFilename(sourceLineNumbers, attrib, false);
4023 parentSFPCatalog = name;
4024 break;
4025 case "SourceFile":
4026 sourceFile = this.Core.GetAttributeValue(sourceLineNumbers, attrib);
4027 break;
4028 default:
4029 this.Core.UnexpectedAttribute(node, attrib);
4030 break;
4031 }
4032 }
4033 else
4034 {
4035 this.Core.ParseExtensionAttribute(node, attrib);
4036 }
4037 }
4038
4039 if (null == name)
4040 {
4041 this.Core.Write(ErrorMessages.ExpectedAttribute(sourceLineNumbers, node.Name.LocalName, "Name"));
4042 }
4043
4044 if (null == sourceFile)
4045 {
4046 this.Core.Write(ErrorMessages.ExpectedAttribute(sourceLineNumbers, node.Name.LocalName, "SourceFile"));
4047 }
4048
4049 foreach (var child in node.Elements())
4050 {
4051 if (CompilerCore.WixNamespace == child.Name.Namespace)
4052 {
4053 switch (child.Name.LocalName)
4054 {
4055 case "SFPCatalog":
4056 this.ParseSFPCatalogElement(child, ref parentName);
4057 if (null != dependency && parentName == dependency)
4058 {
4059 this.Core.Write(ErrorMessages.ExpectedAttribute(sourceLineNumbers, node.Name.LocalName, "Dependency"));
4060 }
4061 dependency = parentName;
4062 break;
4063 case "SFPFile":
4064 this.ParseSFPFileElement(child, name);
4065 break;
4066 default:
4067 this.Core.UnexpectedElement(node, child);
4068 break;
4069 }
4070 }
4071 else
4072 {
4073 this.Core.ParseExtensionElement(node, child);
4074 }
4075 }
4076
4077 if (null == dependency)
4078 {
4079 this.Core.Write(ErrorMessages.ExpectedAttribute(sourceLineNumbers, node.Name.LocalName, "Dependency"));
4080 }
4081
4082 if (!this.Core.EncounteredError)
4083 {
4084 this.Core.AddSymbol(new SFPCatalogSymbol(sourceLineNumbers)
4085 {
4086 SFPCatalog = name,
4087 Catalog = sourceFile,
4088 Dependency = dependency
4089 });
4090 }
4091 }
4092
4093 /// <summary>
4094 /// Parses a shortcut element.
4095 /// </summary>
4096 /// <param name="node">Element to parse.</param>
4097 /// <param name="componentId">Identifer for parent component.</param>
4098 /// <param name="parentElementLocalName">Local name of parent element.</param>
4099 /// <param name="defaultTarget">Default identifier of parent (which is usually the target).</param>
4100 /// <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>
4101 private void ParseShortcutElement(XElement node, string componentId, string parentElementLocalName, string defaultTarget, YesNoType parentKeyPath)
4102 {
4103 var sourceLineNumbers = Preprocessor.GetSourceLineNumbers(node);
4104 Identifier id = null;
4105 var advertise = false;
4106 string arguments = null;
4107 string description = null;
4108 string descriptionResourceDll = null;
4109 int? descriptionResourceId = null;
4110 string directoryId = null;
4111 string subdirectory = null;
4112 string displayResourceDll = null;
4113 int? displayResourceId = null;
4114 int? hotkey = null;
4115 string icon = null;
4116 int? iconIndex = null;
4117 string name = null;
4118 string shortName = null;
4119 ShortcutShowType? show = null;
4120 string target = null;
4121 string workingDirectoryId = null;
4122 string workingSubdirectory = null;
4123
4124 foreach (var attrib in node.Attributes())
4125 {
4126 if (String.IsNullOrEmpty(attrib.Name.NamespaceName) || CompilerCore.WixNamespace == attrib.Name.Namespace)
4127 {
4128 switch (attrib.Name.LocalName)
4129 {
4130 case "Id":
4131 id = this.Core.GetAttributeIdentifier(sourceLineNumbers, attrib);
4132 break;
4133 case "Advertise":
4134 advertise = YesNoType.Yes == this.Core.GetAttributeYesNoValue(sourceLineNumbers, attrib);
4135 break;
4136 case "Arguments":
4137 arguments = this.Core.GetAttributeValue(sourceLineNumbers, attrib);
4138 break;
4139 case "Description":
4140 description = this.Core.GetAttributeValue(sourceLineNumbers, attrib);
4141 break;
4142 case "DescriptionResourceDll":
4143 descriptionResourceDll = this.Core.GetAttributeValue(sourceLineNumbers, attrib);
4144 break;
4145 case "DescriptionResourceId":
4146 descriptionResourceId = this.Core.GetAttributeIntegerValue(sourceLineNumbers, attrib, 0, Int16.MaxValue);
4147 break;
4148 case "Directory":
4149 directoryId = this.Core.GetAttributeIdentifierValue(sourceLineNumbers, attrib);
4150 this.Core.CreateSimpleReference(sourceLineNumbers, SymbolDefinitions.Directory, directoryId);
4151 break;
4152 case "Subdirectory":
4153 subdirectory = this.Core.GetAttributeLongFilename(sourceLineNumbers, attrib, allowRelative: true);
4154 break;
4155 case "DisplayResourceDll":
4156 displayResourceDll = this.Core.GetAttributeValue(sourceLineNumbers, attrib);
4157 break;
4158 case "DisplayResourceId":
4159 displayResourceId = this.Core.GetAttributeIntegerValue(sourceLineNumbers, attrib, 0, Int16.MaxValue);
4160 break;
4161 case "Hotkey":
4162 hotkey = this.Core.GetAttributeIntegerValue(sourceLineNumbers, attrib, 0, Int16.MaxValue);
4163 break;
4164 case "Icon":
4165 icon = this.Core.GetAttributeIdentifierValue(sourceLineNumbers, attrib);
4166 this.Core.CreateSimpleReference(sourceLineNumbers, SymbolDefinitions.Icon, icon);
4167 break;
4168 case "IconIndex":
4169 iconIndex = this.Core.GetAttributeIntegerValue(sourceLineNumbers, attrib, Int16.MinValue + 1, Int16.MaxValue);
4170 break;
4171 case "Name":
4172 name = this.Core.GetAttributeLongFilename(sourceLineNumbers, attrib, false);
4173 break;
4174 case "ShortName":
4175 shortName = this.Core.GetAttributeShortFilename(sourceLineNumbers, attrib, false);
4176 break;
4177 case "Show":
4178 var showValue = this.Core.GetAttributeValue(sourceLineNumbers, attrib);
4179 switch (showValue)
4180 {
4181 case "normal":
4182 show = ShortcutShowType.Normal;
4183 break;
4184 case "maximized":
4185 show = ShortcutShowType.Maximized;
4186 break;
4187 case "minimized":
4188 show = ShortcutShowType.Minimized;
4189 break;
4190 case "":
4191 break;
4192 default:
4193 this.Core.Write(ErrorMessages.IllegalAttributeValue(sourceLineNumbers, node.Name.LocalName, "Show", showValue, "normal", "maximized", "minimized"));
4194 break;
4195 }
4196 break;
4197 case "Target":
4198 target = this.Core.GetAttributeValue(sourceLineNumbers, attrib);
4199 break;
4200 case "WorkingDirectory":
4201 workingDirectoryId = this.Core.GetAttributeIdentifierValue(sourceLineNumbers, attrib);
4202 this.Core.CreateSimpleReference(sourceLineNumbers, SymbolDefinitions.Directory, workingDirectoryId);
4203 break;
4204 case "WorkingSubdirectory":
4205 workingSubdirectory = this.Core.GetAttributeLongFilename(sourceLineNumbers, attrib, allowRelative: true);
4206 break;
4207 default:
4208 this.Core.UnexpectedAttribute(node, attrib);
4209 break;
4210 }
4211 }
4212 else
4213 {
4214 this.Core.ParseExtensionAttribute(node, attrib);
4215 }
4216 }
4217
4218 if (advertise && null != target)
4219 {
4220 this.Core.Write(ErrorMessages.IllegalAttributeWithOtherAttribute(sourceLineNumbers, node.Name.LocalName, "Target", "Advertise", "yes"));
4221 }
4222
4223 if (null == directoryId)
4224 {
4225 if ("Component" == parentElementLocalName)
4226 {
4227 directoryId = defaultTarget;
4228 }
4229 else
4230 {
4231 this.Core.Write(ErrorMessages.ExpectedAttributeWhenElementNotUnderElement(sourceLineNumbers, node.Name.LocalName, "Directory", "Component"));
4232 }
4233 }
4234
4235 directoryId = this.HandleSubdirectory(sourceLineNumbers, node, directoryId, subdirectory, "Directory", "Subdirectory");
4236
4237 if (null != descriptionResourceDll)
4238 {
4239 if (!descriptionResourceId.HasValue)
4240 {
4241 this.Core.Write(ErrorMessages.ExpectedAttribute(sourceLineNumbers, node.Name.LocalName, "DescriptionResourceDll", "DescriptionResourceId"));
4242 }
4243 }
4244 else
4245 {
4246 if (descriptionResourceId.HasValue)
4247 {
4248 this.Core.Write(ErrorMessages.ExpectedAttribute(sourceLineNumbers, node.Name.LocalName, "DescriptionResourceId", "DescriptionResourceDll"));
4249 }
4250 }
4251
4252 if (null != displayResourceDll)
4253 {
4254 if (!displayResourceId.HasValue)
4255 {
4256 this.Core.Write(ErrorMessages.ExpectedAttribute(sourceLineNumbers, node.Name.LocalName, "DisplayResourceDll", "DisplayResourceId"));
4257 }
4258 }
4259 else
4260 {
4261 if (displayResourceId.HasValue)
4262 {
4263 this.Core.Write(ErrorMessages.ExpectedAttribute(sourceLineNumbers, node.Name.LocalName, "DisplayResourceId", "DisplayResourceDll"));
4264 }
4265 }
4266
4267 if (null == name)
4268 {
4269 this.Core.Write(ErrorMessages.ExpectedAttribute(sourceLineNumbers, node.Name.LocalName, "Name"));
4270 }
4271
4272 workingDirectoryId = this.HandleSubdirectory(sourceLineNumbers, node, workingDirectoryId, workingSubdirectory, "WorkingDirectory", "WorkingSubdirectory");
4273
4274 if ("Component" != parentElementLocalName && null != target)
4275 {
4276 this.Core.Write(ErrorMessages.IllegalAttributeWhenNested(sourceLineNumbers, node.Name.LocalName, "Target", parentElementLocalName));
4277 }
4278
4279 if (null == id)
4280 {
4281 id = this.Core.CreateIdentifier("sct", directoryId, LowercaseOrNull(name));
4282 }
4283
4284 foreach (var child in node.Elements())
4285 {
4286 if (CompilerCore.WixNamespace == child.Name.Namespace)
4287 {
4288 switch (child.Name.LocalName)
4289 {
4290 case "Icon":
4291 icon = this.ParseIconElement(child);
4292 break;
4293 case "ShortcutProperty":
4294 this.ParseShortcutPropertyElement(child, id.Id);
4295 break;
4296 default:
4297 this.Core.UnexpectedElement(node, child);
4298 break;
4299 }
4300 }
4301 else
4302 {
4303 this.Core.ParseExtensionElement(node, child);
4304 }
4305 }
4306
4307 if (!this.Core.EncounteredError)
4308 {
4309 if (advertise)
4310 {
4311 if (YesNoType.Yes != parentKeyPath && "Component" != parentElementLocalName)
4312 {
4313 this.Core.Write(WarningMessages.UnclearShortcut(sourceLineNumbers, id.Id, componentId, defaultTarget));
4314 }
4315
4316 target = Guid.Empty.ToString("B");
4317 }
4318 else if (null != target)
4319 {
4320 }
4321 else if ("Component" == parentElementLocalName || "CreateFolder" == parentElementLocalName)
4322 {
4323 target = "[" + defaultTarget + "]";
4324 }
4325 else if ("File" == parentElementLocalName)
4326 {
4327 target = "[#" + defaultTarget + "]";
4328 }
4329
4330 this.Core.AddSymbol(new ShortcutSymbol(sourceLineNumbers, id)
4331 {
4332 DirectoryRef = directoryId,
4333 Name = name,
4334 ShortName = shortName,
4335 ComponentRef = componentId,
4336 Target = target,
4337 Arguments = arguments,
4338 Description = description,
4339 Hotkey = hotkey,
4340 IconRef = icon,
4341 IconIndex = iconIndex,
4342 Show = show,
4343 WorkingDirectory = workingDirectoryId,
4344 DisplayResourceDll = displayResourceDll,
4345 DisplayResourceId = displayResourceId,
4346 DescriptionResourceDll = descriptionResourceDll,
4347 DescriptionResourceId = descriptionResourceId,
4348 });
4349 }
4350 }
4351
4352 /// <summary>
4353 /// Parses a shortcut property element.
4354 /// </summary>
4355 /// <param name="node">Element to parse.</param>
4356 /// <param name="shortcutId"></param>
4357 private void ParseShortcutPropertyElement(XElement node, string shortcutId)
4358 {
4359 var sourceLineNumbers = Preprocessor.GetSourceLineNumbers(node);
4360 Identifier id = null;
4361 string key = null;
4362 string value = null;
4363
4364 foreach (var attrib in node.Attributes())
4365 {
4366 if (String.IsNullOrEmpty(attrib.Name.NamespaceName) || CompilerCore.WixNamespace == attrib.Name.Namespace)
4367 {
4368 switch (attrib.Name.LocalName)
4369 {
4370 case "Id":
4371 id = this.Core.GetAttributeIdentifier(sourceLineNumbers, attrib);
4372 break;
4373 case "Key":
4374 key = this.Core.GetAttributeValue(sourceLineNumbers, attrib);
4375 break;
4376 case "Value":
4377 value = this.Core.GetAttributeValue(sourceLineNumbers, attrib);
4378 break;
4379 default:
4380 this.Core.UnexpectedAttribute(node, attrib);
4381 break;
4382 }
4383 }
4384 else
4385 {
4386 this.Core.ParseExtensionAttribute(node, attrib);
4387 }
4388 }
4389
4390 if (String.IsNullOrEmpty(key))
4391 {
4392 this.Core.Write(ErrorMessages.ExpectedAttribute(sourceLineNumbers, node.Name.LocalName, "Key"));
4393 }
4394 else if (null == id)
4395 {
4396 id = this.Core.CreateIdentifier("scp", shortcutId, key.ToUpperInvariant());
4397 }
4398
4399 if (String.IsNullOrEmpty(value))
4400 {
4401 this.Core.Write(ErrorMessages.ExpectedAttribute(sourceLineNumbers, node.Name.LocalName, "Value"));
4402 }
4403
4404 this.Core.ParseForExtensionElements(node);
4405
4406 if (!this.Core.EncounteredError)
4407 {
4408 this.Core.AddSymbol(new MsiShortcutPropertySymbol(sourceLineNumbers, id)
4409 {
4410 ShortcutRef = shortcutId,
4411 PropertyKey = key,
4412 PropVariantValue = value
4413 });
4414 }
4415 }
4416
4417 /// <summary>
4418 /// Parses a typelib element.
4419 /// </summary>
4420 /// <param name="node">Element to parse.</param>
4421 /// <param name="componentId">Identifier of parent component.</param>
4422 /// <param name="fileServer">Identifier of file that acts as typelib server.</param>
4423 /// <param name="win64Component">true if the component is 64-bit.</param>
4424 private void ParseTypeLibElement(XElement node, string componentId, string fileServer, bool win64Component)
4425 {
4426 var sourceLineNumbers = Preprocessor.GetSourceLineNumbers(node);
4427 string id = null;
4428 var advertise = YesNoType.NotSet;
4429 var cost = CompilerConstants.IntegerNotSet;
4430 string description = null;
4431 var flags = 0;
4432 string helpDirectoryId = null;
4433 string helpSubdirectory = null;
4434 var language = CompilerConstants.IntegerNotSet;
4435 XAttribute majorVersionAttrib = null;
4436 XAttribute minorVersionAttrib = null;
4437 var resourceId = CompilerConstants.LongNotSet;
4438
4439 foreach (var attrib in node.Attributes())
4440 {
4441 if (String.IsNullOrEmpty(attrib.Name.NamespaceName) || CompilerCore.WixNamespace == attrib.Name.Namespace)
4442 {
4443 switch (attrib.Name.LocalName)
4444 {
4445 case "Id":
4446 id = this.Core.GetAttributeGuidValue(sourceLineNumbers, attrib, false);
4447 break;
4448 case "Advertise":
4449 advertise = this.Core.GetAttributeYesNoValue(sourceLineNumbers, attrib);
4450 break;
4451 case "Control":
4452 if (YesNoType.Yes == this.Core.GetAttributeYesNoValue(sourceLineNumbers, attrib))
4453 {
4454 flags |= 2;
4455 }
4456 break;
4457 case "Cost":
4458 cost = this.Core.GetAttributeIntegerValue(sourceLineNumbers, attrib, 0, Int32.MaxValue);
4459 break;
4460 case "Description":
4461 description = this.Core.GetAttributeValue(sourceLineNumbers, attrib);
4462 break;
4463 case "HasDiskImage":
4464 if (YesNoType.Yes == this.Core.GetAttributeYesNoValue(sourceLineNumbers, attrib))
4465 {
4466 flags |= 8;
4467 }
4468 break;
4469 case "HelpDirectory":
4470 helpDirectoryId = this.Core.GetAttributeIdentifierValue(sourceLineNumbers, attrib);
4471 this.Core.CreateSimpleReference(sourceLineNumbers, SymbolDefinitions.Directory, helpDirectoryId);
4472 break;
4473 case "HelpSubdirectory":
4474 helpSubdirectory = this.Core.GetAttributeLongFilename(sourceLineNumbers, attrib, allowRelative: true);
4475 break;
4476 case "Hidden":
4477 if (YesNoType.Yes == this.Core.GetAttributeYesNoValue(sourceLineNumbers, attrib))
4478 {
4479 flags |= 4;
4480 }
4481 break;
4482 case "Language":
4483 language = this.Core.GetAttributeIntegerValue(sourceLineNumbers, attrib, 0, Int16.MaxValue);
4484 break;
4485 case "MajorVersion":
4486 majorVersionAttrib = attrib;
4487 break;
4488 case "MinorVersion":
4489 minorVersionAttrib = attrib;
4490 break;
4491 case "ResourceId":
4492 resourceId = this.Core.GetAttributeLongValue(sourceLineNumbers, attrib, Int32.MinValue, Int32.MaxValue);
4493 break;
4494 case "Restricted":
4495 if (YesNoType.Yes == this.Core.GetAttributeYesNoValue(sourceLineNumbers, attrib))
4496 {
4497 flags |= 1;
4498 }
4499 break;
4500 default:
4501 this.Core.UnexpectedAttribute(node, attrib);
4502 break;
4503 }
4504 }
4505 else
4506 {
4507 this.Core.ParseExtensionAttribute(node, attrib);
4508 }
4509 }
4510
4511 if (null == id)
4512 {
4513 this.Core.Write(ErrorMessages.ExpectedAttribute(sourceLineNumbers, node.Name.LocalName, "Id"));
4514 }
4515
4516 if (CompilerConstants.IntegerNotSet == language)
4517 {
4518 this.Core.Write(ErrorMessages.ExpectedAttribute(sourceLineNumbers, node.Name.LocalName, "Language"));
4519 language = CompilerConstants.IllegalInteger;
4520 }
4521
4522 helpDirectoryId = this.HandleSubdirectory(sourceLineNumbers, node, helpDirectoryId, helpSubdirectory, "HelpDirectory", "HelpSubdirectory");
4523
4524 // if the advertise state has not been set, default to non-advertised
4525 if (YesNoType.NotSet == advertise)
4526 {
4527 advertise = YesNoType.No;
4528 }
4529
4530 var majorVersion = (null == majorVersionAttrib) ? CompilerConstants.IntegerNotSet : this.Core.GetAttributeIntegerValue(sourceLineNumbers, majorVersionAttrib, 0, UInt16.MaxValue);
4531 var minorVersion = (null == minorVersionAttrib) ? CompilerConstants.IntegerNotSet : this.Core.GetAttributeIntegerValue(sourceLineNumbers, minorVersionAttrib, 0, (YesNoType.Yes == advertise) ? Byte.MaxValue : UInt16.MaxValue);
4532
4533 // build up the typelib version string for the registry if the major or minor version was specified
4534 string registryVersion = null;
4535 if (null != majorVersionAttrib || null != minorVersionAttrib)
4536 {
4537 if (null != majorVersionAttrib)
4538 {
4539 registryVersion = majorVersion.ToString("x", CultureInfo.InvariantCulture.NumberFormat);
4540 }
4541 else
4542 {
4543 registryVersion = "0";
4544 }
4545
4546 if (null != minorVersionAttrib)
4547 {
4548 registryVersion = String.Concat(registryVersion, ".", minorVersion.ToString("x", CultureInfo.InvariantCulture.NumberFormat));
4549 }
4550 else
4551 {
4552 registryVersion = String.Concat(registryVersion, ".0");
4553 }
4554 }
4555
4556 foreach (var child in node.Elements())
4557 {
4558 if (CompilerCore.WixNamespace == child.Name.Namespace)
4559 {
4560 switch (child.Name.LocalName)
4561 {
4562 case "AppId":
4563 this.ParseAppIdElement(child, componentId, YesNoType.NotSet, fileServer, id, registryVersion);
4564 break;
4565 case "Class":
4566 this.ParseClassElement(child, componentId, YesNoType.NotSet, fileServer, id, registryVersion, null);
4567 break;
4568 case "Interface":
4569 this.ParseInterfaceElement(child, componentId, null, null, id, registryVersion);
4570 break;
4571 default:
4572 this.Core.UnexpectedElement(node, child);
4573 break;
4574 }
4575 }
4576 else
4577 {
4578 this.Core.ParseExtensionElement(node, child);
4579 }
4580 }
4581
4582
4583 if (YesNoType.Yes == advertise)
4584 {
4585 if (CompilerConstants.LongNotSet != resourceId)
4586 {
4587 this.Core.Write(ErrorMessages.IllegalAttributeWhenAdvertised(sourceLineNumbers, node.Name.LocalName, "ResourceId"));
4588 }
4589
4590 if (0 != flags)
4591 {
4592 if (0x1 == (flags & 0x1))
4593 {
4594 this.Core.Write(ErrorMessages.IllegalAttributeWithOtherAttribute(sourceLineNumbers, node.Name.LocalName, "Restricted", "Advertise", "yes"));
4595 }
4596
4597 if (0x2 == (flags & 0x2))
4598 {
4599 this.Core.Write(ErrorMessages.IllegalAttributeWithOtherAttribute(sourceLineNumbers, node.Name.LocalName, "Control", "Advertise", "yes"));
4600 }
4601
4602 if (0x4 == (flags & 0x4))
4603 {
4604 this.Core.Write(ErrorMessages.IllegalAttributeWithOtherAttribute(sourceLineNumbers, node.Name.LocalName, "Hidden", "Advertise", "yes"));
4605 }
4606
4607 if (0x8 == (flags & 0x8))
4608 {
4609 this.Core.Write(ErrorMessages.IllegalAttributeWithOtherAttribute(sourceLineNumbers, node.Name.LocalName, "HasDiskImage", "Advertise", "yes"));
4610 }
4611 }
4612
4613 if (!this.Core.EncounteredError)
4614 {
4615 var symbol = this.Core.AddSymbol(new TypeLibSymbol(sourceLineNumbers)
4616 {
4617 LibId = id,
4618 Language = language,
4619 ComponentRef = componentId,
4620 Description = description,
4621 DirectoryRef = helpDirectoryId,
4622 FeatureRef = Guid.Empty.ToString("B")
4623 });
4624
4625 if (null != majorVersionAttrib || null != minorVersionAttrib)
4626 {
4627 symbol.Version = (null != majorVersionAttrib ? majorVersion * 256 : 0) + (null != minorVersionAttrib ? minorVersion : 0);
4628 }
4629
4630 if (CompilerConstants.IntegerNotSet != cost)
4631 {
4632 symbol.Cost = cost;
4633 }
4634 }
4635 }
4636 else if (YesNoType.No == advertise)
4637 {
4638 if (CompilerConstants.IntegerNotSet != cost && CompilerConstants.IllegalInteger != cost)
4639 {
4640 this.Core.Write(ErrorMessages.IllegalAttributeWithOtherAttribute(sourceLineNumbers, node.Name.LocalName, "Cost", "Advertise", "no"));
4641 }
4642
4643 if (null == fileServer)
4644 {
4645 this.Core.Write(ErrorMessages.MissingTypeLibFile(sourceLineNumbers, node.Name.LocalName, "File"));
4646 }
4647
4648 if (null == registryVersion)
4649 {
4650 this.Core.Write(ErrorMessages.ExpectedAttributesWithOtherAttribute(sourceLineNumbers, node.Name.LocalName, "MajorVersion", "MinorVersion", "Advertise", "no"));
4651 }
4652
4653 // HKCR\TypeLib\[ID]\[MajorVersion].[MinorVersion], (Default) = [Description]
4654 this.Core.CreateRegistryStringSymbol(sourceLineNumbers, RegistryRootType.ClassesRoot, String.Format(CultureInfo.InvariantCulture, @"TypeLib\{0}\{1}", id, registryVersion), null, description, componentId);
4655
4656 // HKCR\TypeLib\[ID]\[MajorVersion].[MinorVersion]\[Language]\[win16|win32|win64], (Default) = [TypeLibPath]\[ResourceId]
4657 var path = String.Concat("[#", fileServer, "]");
4658 if (CompilerConstants.LongNotSet != resourceId)
4659 {
4660 path = String.Concat(path, Path.DirectorySeparatorChar, resourceId.ToString(CultureInfo.InvariantCulture.NumberFormat));
4661 }
4662 this.Core.CreateRegistryStringSymbol(sourceLineNumbers, RegistryRootType.ClassesRoot, String.Format(CultureInfo.InvariantCulture, @"TypeLib\{0}\{1}\{2}\{3}", id, registryVersion, language, (win64Component ? "win64" : "win32")), null, path, componentId);
4663
4664 // HKCR\TypeLib\[ID]\[MajorVersion].[MinorVersion]\FLAGS, (Default) = [TypeLibFlags]
4665 this.Core.CreateRegistryStringSymbol(sourceLineNumbers, RegistryRootType.ClassesRoot, String.Format(CultureInfo.InvariantCulture, @"TypeLib\{0}\{1}\FLAGS", id, registryVersion), null, flags.ToString(CultureInfo.InvariantCulture.NumberFormat), componentId);
4666
4667 if (null != helpDirectoryId)
4668 {
4669 // HKCR\TypeLib\[ID]\[MajorVersion].[MinorVersion]\HELPDIR, (Default) = [HelpDirectory]
4670 this.Core.CreateRegistryStringSymbol(sourceLineNumbers, RegistryRootType.ClassesRoot, String.Format(CultureInfo.InvariantCulture, @"TypeLib\{0}\{1}\HELPDIR", id, registryVersion), null, String.Concat("[", helpDirectoryId, "]"), componentId);
4671 }
4672 }
4673 }
4674
4675 /// <summary>
4676 /// Parses an upgrade element.
4677 /// </summary>
4678 /// <param name="node">Element to parse.</param>
4679 private void ParseUpgradeElement(XElement node)
4680 {
4681 var sourceLineNumbers = Preprocessor.GetSourceLineNumbers(node);
4682 string id = null;
4683
4684 foreach (var attrib in node.Attributes())
4685 {
4686 if (String.IsNullOrEmpty(attrib.Name.NamespaceName) || CompilerCore.WixNamespace == attrib.Name.Namespace)
4687 {
4688 switch (attrib.Name.LocalName)
4689 {
4690 case "Id":
4691 id = this.Core.GetAttributeGuidValue(sourceLineNumbers, attrib, false);
4692 break;
4693 default:
4694 this.Core.UnexpectedAttribute(node, attrib);
4695 break;
4696 }
4697 }
4698 else
4699 {
4700 this.Core.ParseExtensionAttribute(node, attrib);
4701 }
4702 }
4703
4704 if (null == id)
4705 {
4706 this.Core.Write(ErrorMessages.ExpectedAttribute(sourceLineNumbers, node.Name.LocalName, "Id"));
4707 }
4708
4709 // process the UpgradeVersion children here
4710 foreach (var child in node.Elements())
4711 {
4712 if (CompilerCore.WixNamespace == child.Name.Namespace)
4713 {
4714 var childSourceLineNumbers = Preprocessor.GetSourceLineNumbers(child);
4715
4716 switch (child.Name.LocalName)
4717 {
4718 case "Property":
4719 this.ParsePropertyElement(child);
4720 this.Core.Write(WarningMessages.DeprecatedUpgradeProperty(childSourceLineNumbers));
4721 break;
4722 case "UpgradeVersion":
4723 this.ParseUpgradeVersionElement(child, id);
4724 break;
4725 default:
4726 this.Core.UnexpectedElement(node, child);
4727 break;
4728 }
4729 }
4730 else
4731 {
4732 this.Core.ParseExtensionElement(node, child);
4733 }
4734 }
4735
4736 // No rows created here. All row creation is done in ParseUpgradeVersionElement.
4737 }
4738
4739 /// <summary>
4740 /// Parse upgrade version element.
4741 /// </summary>
4742 /// <param name="node">Element to parse.</param>
4743 /// <param name="upgradeId">Upgrade code.</param>
4744 private void ParseUpgradeVersionElement(XElement node, string upgradeId)
4745 {
4746 var sourceLineNumbers = Preprocessor.GetSourceLineNumbers(node);
4747
4748 string actionProperty = null;
4749 string language = null;
4750 string maximum = null;
4751 string minimum = null;
4752 var excludeLanguages = false;
4753 var ignoreFailures = false;
4754 var includeMax = false;
4755 var includeMin = true;
4756 var migrateFeatures = false;
4757 var onlyDetect = false;
4758 string removeFeatures = null;
4759
4760 foreach (var attrib in node.Attributes())
4761 {
4762 if (String.IsNullOrEmpty(attrib.Name.NamespaceName) || CompilerCore.WixNamespace == attrib.Name.Namespace)
4763 {
4764 switch (attrib.Name.LocalName)
4765 {
4766 case "ExcludeLanguages":
4767 excludeLanguages = YesNoType.Yes == this.Core.GetAttributeYesNoValue(sourceLineNumbers, attrib);
4768 break;
4769 case "IgnoreRemoveFailure":
4770 ignoreFailures = YesNoType.Yes == this.Core.GetAttributeYesNoValue(sourceLineNumbers, attrib);
4771 break;
4772 case "IncludeMaximum":
4773 includeMax = YesNoType.Yes == this.Core.GetAttributeYesNoValue(sourceLineNumbers, attrib);
4774 break;
4775 case "IncludeMinimum": // this is "yes" by default
4776 includeMin = YesNoType.Yes == this.Core.GetAttributeYesNoValue(sourceLineNumbers, attrib);
4777 break;
4778 case "Language":
4779 language = this.Core.GetAttributeValue(sourceLineNumbers, attrib);
4780 break;
4781 case "Minimum":
4782 minimum = this.Core.GetAttributeVersionValue(sourceLineNumbers, attrib);
4783 break;
4784 case "Maximum":
4785 maximum = this.Core.GetAttributeVersionValue(sourceLineNumbers, attrib);
4786 break;
4787 case "MigrateFeatures":
4788 migrateFeatures = YesNoType.Yes == this.Core.GetAttributeYesNoValue(sourceLineNumbers, attrib);
4789 break;
4790 case "OnlyDetect":
4791 onlyDetect = YesNoType.Yes == this.Core.GetAttributeYesNoValue(sourceLineNumbers, attrib);
4792 break;
4793 case "Property":
4794 actionProperty = this.Core.GetAttributeIdentifierValue(sourceLineNumbers, attrib);
4795 break;
4796 case "RemoveFeatures":
4797 removeFeatures = this.Core.GetAttributeValue(sourceLineNumbers, attrib);
4798 break;
4799 default:
4800 this.Core.UnexpectedAttribute(node, attrib);
4801 break;
4802 }
4803 }
4804 else
4805 {
4806 this.Core.ParseExtensionAttribute(node, attrib);
4807 }
4808 }
4809
4810 if (null == actionProperty)
4811 {
4812 this.Core.Write(ErrorMessages.ExpectedAttribute(sourceLineNumbers, node.Name.LocalName, "Property"));
4813 }
4814 else if (actionProperty.ToUpper(CultureInfo.InvariantCulture) != actionProperty)
4815 {
4816 this.Core.Write(ErrorMessages.SecurePropertyNotUppercase(sourceLineNumbers, node.Name.LocalName, "Property", actionProperty));
4817 }
4818
4819 if (null == minimum && null == maximum)
4820 {
4821 this.Core.Write(ErrorMessages.ExpectedAttributes(sourceLineNumbers, node.Name.LocalName, "Minimum", "Maximum"));
4822 }
4823
4824 this.Core.ParseForExtensionElements(node);
4825
4826 if (!this.Core.EncounteredError)
4827 {
4828 this.Core.AddSymbol(new UpgradeSymbol(sourceLineNumbers)
4829 {
4830 UpgradeCode = upgradeId,
4831 VersionMin = minimum,
4832 VersionMax = maximum,
4833 Language = language,
4834 ExcludeLanguages = excludeLanguages,
4835 IgnoreRemoveFailures = ignoreFailures,
4836 VersionMaxInclusive = includeMax,
4837 VersionMinInclusive = includeMin,
4838 MigrateFeatures = migrateFeatures,
4839 OnlyDetect = onlyDetect,
4840 Remove = removeFeatures,
4841 ActionProperty = actionProperty
4842 });
4843
4844 // Ensure that RemoveExistingProducts is authored in InstallExecuteSequence
4845 // if at least one row in Upgrade table lacks the OnlyDetect attribute.
4846 if (!onlyDetect)
4847 {
4848 this.Core.CreateSimpleReference(sourceLineNumbers, SymbolDefinitions.WixAction, "InstallExecuteSequence", "RemoveExistingProducts");
4849 }
4850 }
4851 }
4852
4853 /// <summary>
4854 /// Parses a verb element.
4855 /// </summary>
4856 /// <param name="node">Element to parse.</param>
4857 /// <param name="extension">Extension verb is releated to.</param>
4858 /// <param name="progId">Optional progId for extension.</param>
4859 /// <param name="componentId">Identifier for parent component.</param>
4860 /// <param name="advertise">Flag if verb is advertised.</param>
4861 private void ParseVerbElement(XElement node, string extension, string progId, string componentId, YesNoType advertise)
4862 {
4863 var sourceLineNumbers = Preprocessor.GetSourceLineNumbers(node);
4864 string id = null;
4865 string argument = null;
4866 string command = null;
4867 var sequence = CompilerConstants.IntegerNotSet;
4868 string targetFile = null;
4869 string targetProperty = null;
4870
4871 foreach (var attrib in node.Attributes())
4872 {
4873 if (String.IsNullOrEmpty(attrib.Name.NamespaceName) || CompilerCore.WixNamespace == attrib.Name.Namespace)
4874 {
4875 switch (attrib.Name.LocalName)
4876 {
4877 case "Id":
4878 id = this.Core.GetAttributeValue(sourceLineNumbers, attrib);
4879 break;
4880 case "Argument":
4881 argument = this.Core.GetAttributeValue(sourceLineNumbers, attrib);
4882 break;
4883 case "Command":
4884 command = this.Core.GetAttributeValue(sourceLineNumbers, attrib);
4885 break;
4886 case "Sequence":
4887 sequence = this.Core.GetAttributeIntegerValue(sourceLineNumbers, attrib, 1, Int16.MaxValue);
4888 break;
4889 case "TargetFile":
4890 targetFile = this.Core.GetAttributeValue(sourceLineNumbers, attrib);
4891 this.Core.CreateSimpleReference(sourceLineNumbers, SymbolDefinitions.File, targetFile);
4892 break;
4893 case "TargetProperty":
4894 targetProperty = this.Core.GetAttributeValue(sourceLineNumbers, attrib);
4895 break;
4896 default:
4897 this.Core.UnexpectedAttribute(node, attrib);
4898 break;
4899 }
4900 }
4901 else
4902 {
4903 this.Core.ParseExtensionAttribute(node, attrib);
4904 }
4905 }
4906
4907 if (null == id)
4908 {
4909 this.Core.Write(ErrorMessages.ExpectedAttribute(sourceLineNumbers, node.Name.LocalName, "Id"));
4910 }
4911
4912 if (null != targetFile && null != targetProperty)
4913 {
4914 this.Core.Write(ErrorMessages.IllegalAttributeWithOtherAttribute(sourceLineNumbers, node.Name.LocalName, "TargetFile", "TargetProperty"));
4915 }
4916
4917 this.Core.ParseForExtensionElements(node);
4918
4919 if (YesNoType.Yes == advertise)
4920 {
4921 if (null != targetFile)
4922 {
4923 this.Core.Write(ErrorMessages.IllegalAttributeWhenAdvertised(sourceLineNumbers, node.Name.LocalName, "TargetFile"));
4924 }
4925
4926 if (null != targetProperty)
4927 {
4928 this.Core.Write(ErrorMessages.IllegalAttributeWhenAdvertised(sourceLineNumbers, node.Name.LocalName, "TargetProperty"));
4929 }
4930
4931 if (!this.Core.EncounteredError)
4932 {
4933 var symbol = this.Core.AddSymbol(new VerbSymbol(sourceLineNumbers)
4934 {
4935 ExtensionRef = extension,
4936 Verb = id,
4937 Command = command,
4938 Argument = argument,
4939 });
4940
4941 if (CompilerConstants.IntegerNotSet != sequence)
4942 {
4943 symbol.Sequence = sequence;
4944 }
4945 }
4946 }
4947 else if (YesNoType.No == advertise)
4948 {
4949 if (CompilerConstants.IntegerNotSet != sequence)
4950 {
4951 this.Core.Write(ErrorMessages.IllegalAttributeWithOtherAttribute(sourceLineNumbers, node.Name.LocalName, "Sequence", "Advertise", "no"));
4952 }
4953
4954 if (null == targetFile && null == targetProperty)
4955 {
4956 this.Core.Write(ErrorMessages.ExpectedAttributesWithOtherAttribute(sourceLineNumbers, node.Name.LocalName, "TargetFile", "TargetProperty", "Advertise", "no"));
4957 }
4958
4959 string target = null;
4960 if (null != targetFile)
4961 {
4962 target = String.Concat("\"[#", targetFile, "]\"");
4963 }
4964 else if (null != targetProperty)
4965 {
4966 target = String.Concat("\"[", targetProperty, "]\"");
4967 }
4968
4969 if (null != argument)
4970 {
4971 target = String.Concat(target, " ", argument);
4972 }
4973
4974 var prefix = progId ?? String.Concat(".", extension);
4975
4976 if (null != command)
4977 {
4978 this.Core.CreateRegistryStringSymbol(sourceLineNumbers, RegistryRootType.ClassesRoot, String.Concat(prefix, "\\shell\\", id), String.Empty, command, componentId);
4979 }
4980
4981 this.Core.CreateRegistryStringSymbol(sourceLineNumbers, RegistryRootType.ClassesRoot, String.Concat(prefix, "\\shell\\", id, "\\command"), String.Empty, target, componentId);
4982 }
4983 }
4984
4985 /// <summary>
4986 /// Parses a WixVariable element.
4987 /// </summary>
4988 /// <param name="node">Element to parse.</param>
4989 private void ParseWixVariableElement(XElement node)
4990 {
4991 var sourceLineNumbers = Preprocessor.GetSourceLineNumbers(node);
4992 Identifier id = null;
4993 var overridable = false;
4994 string value = null;
4995
4996 foreach (var attrib in node.Attributes())
4997 {
4998 if (String.IsNullOrEmpty(attrib.Name.NamespaceName) || CompilerCore.WixNamespace == attrib.Name.Namespace)
4999 {
5000 switch (attrib.Name.LocalName)
Showing first 5,000 of 5,069 lines. View raw