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