aboutsummaryrefslogtreecommitdiff
path: root/src/WixToolset.Core.WindowsInstaller/Bind/CalculateComponentGuids.cs
diff options
context:
space:
mode:
Diffstat (limited to 'src/WixToolset.Core.WindowsInstaller/Bind/CalculateComponentGuids.cs')
-rw-r--r--src/WixToolset.Core.WindowsInstaller/Bind/CalculateComponentGuids.cs174
1 files changed, 174 insertions, 0 deletions
diff --git a/src/WixToolset.Core.WindowsInstaller/Bind/CalculateComponentGuids.cs b/src/WixToolset.Core.WindowsInstaller/Bind/CalculateComponentGuids.cs
new file mode 100644
index 00000000..0c0aea1f
--- /dev/null
+++ b/src/WixToolset.Core.WindowsInstaller/Bind/CalculateComponentGuids.cs
@@ -0,0 +1,174 @@
1// Copyright (c) .NET Foundation and contributors. All rights reserved. Licensed under the Microsoft Reciprocal License. See LICENSE.TXT file in the project root for full license information.
2
3namespace WixToolset.Core.WindowsInstaller.Bind
4{
5 using System;
6 using System.Collections.Generic;
7 using System.IO;
8 using System.Linq;
9 using WixToolset.Bind;
10 using WixToolset.Core.Native;
11 using WixToolset.Data;
12 using WixToolset.Data.Tuples;
13
14 /// <summary>
15 /// Set the guids for components with generatable guids.
16 /// </summary>
17 internal class CalculateComponentGuids
18 {
19 public CalculateComponentGuids(IntermediateSection section)
20 {
21 this.Section = section;
22 }
23
24 private IntermediateSection Section { get; }
25
26 public void Execute()
27 {
28 Dictionary<string, RegistryTuple> registryKeyRows = null;
29 Dictionary<string, ResolvedDirectory> targetPathsByDirectoryId = null;
30 Dictionary<string, string> componentIdGenSeeds = null;
31 Dictionary<string, List<FileTuple>> filesByComponentId = null;
32
33 // Find components with generatable guids.
34 foreach (var componentRow in this.Section.Tuples.OfType<ComponentTuple>())
35 {
36 // Skip components that do not specify generate guid.
37 if (componentRow.ComponentId != "*")
38 {
39 continue;
40 }
41
42 var odbcDataSourceKeyPath = (componentRow.Attributes & MsiInterop.MsidbComponentAttributesODBCDataSource) != 0;
43
44 if (String.IsNullOrEmpty(componentRow.KeyPath) || odbcDataSourceKeyPath)
45 {
46 Messaging.Instance.OnMessage(WixErrors.IllegalComponentWithAutoGeneratedGuid(componentRow.SourceLineNumbers));
47 continue;
48 }
49
50 var registryKeyPath = (componentRow.Attributes & MsiInterop.MsidbComponentAttributesRegistryKeyPath) != 0;
51
52 if (registryKeyPath)
53 {
54 if (registryKeyRows is null)
55 {
56 registryKeyRows = this.Section.Tuples.OfType<RegistryTuple>().ToDictionary(t => t.Registry);
57 }
58
59 if (registryKeyRows.TryGetValue(componentRow.KeyPath, out var foundRow))
60 {
61 var is64Bit = (componentRow.Attributes & MsiInterop.MsidbComponentAttributes64bit) != 0;
62 var bitness = is64Bit ? "64" : String.Empty;
63 var regkey = String.Concat(bitness, foundRow[1], "\\", foundRow[2], "\\", foundRow[3]);
64 componentRow.ComponentId = Uuid.NewUuid(BindDatabaseCommand.WixComponentGuidNamespace, regkey.ToLowerInvariant()).ToString("B").ToUpperInvariant();
65 }
66 }
67 else // must be a File KeyPath.
68 {
69 // If the directory table hasn't been loaded into an indexed hash
70 // of directory ids to target names do that now.
71 if (targetPathsByDirectoryId is null)
72 {
73 var directories = this.Section.Tuples.OfType<DirectoryTuple>().ToList();
74
75 targetPathsByDirectoryId = new Dictionary<string, ResolvedDirectory>(directories.Count);
76
77 // Get the target paths for all directories.
78 foreach (var row in directories)
79 {
80 // If the directory Id already exists, we will skip it here since
81 // checking for duplicate primary keys is done later when importing tables
82 // into database
83 if (targetPathsByDirectoryId.ContainsKey(row.Directory))
84 {
85 continue;
86 }
87
88 var targetName = Common.GetName(row.DefaultDir, false, true);
89 targetPathsByDirectoryId.Add(row.Directory, new ResolvedDirectory(row.Directory_Parent, targetName));
90 }
91 }
92
93 // If the component id generation seeds have not been indexed
94 // from the WixDirectory table do that now.
95 if (componentIdGenSeeds is null)
96 {
97 // If there are any WixDirectory rows, build up the Component Guid
98 // generation seeds indexed by Directory/@Id.
99 componentIdGenSeeds = this.Section.Tuples.OfType<WixDirectoryTuple>()
100 .Where(t => !String.IsNullOrEmpty(t.ComponentGuidGenerationSeed))
101 .ToDictionary(t => t.Directory_, t => t.ComponentGuidGenerationSeed);
102 }
103
104 // if the file rows have not been indexed by File.Component yet
105 // then do that now
106 if (filesByComponentId is null)
107 {
108 var files = this.Section.Tuples.OfType<FileTuple>().ToList();
109
110 filesByComponentId = new Dictionary<string, List<FileTuple>>(files.Count);
111
112 foreach (var file in files)
113 {
114 if (!filesByComponentId.TryGetValue(file.Component_, out var componentFiles))
115 {
116 componentFiles = new List<FileTuple>();
117 filesByComponentId.Add(file.Component_, componentFiles);
118 }
119
120 componentFiles.Add(file);
121 }
122 }
123
124 // validate component meets all the conditions to have a generated guid
125 var currentComponentFiles = filesByComponentId[componentRow.Component];
126 var numFilesInComponent = currentComponentFiles.Count;
127 string path = null;
128
129 foreach (var fileRow in currentComponentFiles)
130 {
131 if (fileRow.File == componentRow.KeyPath)
132 {
133 // calculate the key file's canonical target path
134 string directoryPath = Binder.GetDirectoryPath(targetPathsByDirectoryId, componentIdGenSeeds, componentRow.Directory_, true);
135 string fileName = Common.GetName(fileRow.LongFileName, false, true).ToLowerInvariant();
136 path = Path.Combine(directoryPath, fileName);
137
138 // find paths that are not canonicalized
139 if (path.StartsWith(@"PersonalFolder\my pictures", StringComparison.Ordinal) ||
140 path.StartsWith(@"ProgramFilesFolder\common files", StringComparison.Ordinal) ||
141 path.StartsWith(@"ProgramMenuFolder\startup", StringComparison.Ordinal) ||
142 path.StartsWith("TARGETDIR", StringComparison.Ordinal) ||
143 path.StartsWith(@"StartMenuFolder\programs", StringComparison.Ordinal) ||
144 path.StartsWith(@"WindowsFolder\fonts", StringComparison.Ordinal))
145 {
146 Messaging.Instance.OnMessage(WixErrors.IllegalPathForGeneratedComponentGuid(componentRow.SourceLineNumbers, fileRow.Component_, path));
147 }
148
149 // if component has more than one file, the key path must be versioned
150 if (1 < numFilesInComponent && String.IsNullOrEmpty(fileRow.Version))
151 {
152 Messaging.Instance.OnMessage(WixErrors.IllegalGeneratedGuidComponentUnversionedKeypath(componentRow.SourceLineNumbers));
153 }
154 }
155 else
156 {
157 // not a key path, so it must be an unversioned file if component has more than one file
158 if (1 < numFilesInComponent && !String.IsNullOrEmpty(fileRow.Version))
159 {
160 Messaging.Instance.OnMessage(WixErrors.IllegalGeneratedGuidComponentVersionedNonkeypath(componentRow.SourceLineNumbers));
161 }
162 }
163 }
164
165 // if the rules were followed, reward with a generated guid
166 if (!Messaging.Instance.EncounteredError)
167 {
168 componentRow.ComponentId = Uuid.NewUuid(BindDatabaseCommand.WixComponentGuidNamespace, path).ToString("B").ToUpperInvariant();
169 }
170 }
171 }
172 }
173 }
174}