diff options
Diffstat (limited to 'src/wixext/DependencyDecompiler.cs')
-rw-r--r-- | src/wixext/DependencyDecompiler.cs | 345 |
1 files changed, 345 insertions, 0 deletions
diff --git a/src/wixext/DependencyDecompiler.cs b/src/wixext/DependencyDecompiler.cs new file mode 100644 index 00000000..3013cf7c --- /dev/null +++ b/src/wixext/DependencyDecompiler.cs | |||
@@ -0,0 +1,345 @@ | |||
1 | // Copyright (c) .NET Foundation and contributors. All rights reserved. Licensed under the Microsoft Reciprocal License. See LICENSE.TXT file in the project root for full license information. | ||
2 | |||
3 | namespace WixToolset.Extensions | ||
4 | { | ||
5 | using System; | ||
6 | using System.Collections.Generic; | ||
7 | using System.Collections.ObjectModel; | ||
8 | using WixToolset; | ||
9 | using WixToolset.Data; | ||
10 | using WixToolset.Extensibility; | ||
11 | using WixToolset.Extensions.Serialize.Dependency; | ||
12 | using Dependency = WixToolset.Extensions.Serialize.Dependency; | ||
13 | using Wix = WixToolset.Data.Serialize; | ||
14 | |||
15 | /// <summary> | ||
16 | /// The decompiler for the WiX toolset dependency extension. | ||
17 | /// </summary> | ||
18 | public sealed class DependencyDecompiler : DecompilerExtension | ||
19 | { | ||
20 | private RegistryKeyValueCollection registryValues; | ||
21 | private Dictionary<string, string> keyCache; | ||
22 | |||
23 | /// <summary> | ||
24 | /// Creates a new instance of the <see cref="DependencyDecompiler"/> class. | ||
25 | /// </summary> | ||
26 | public DependencyDecompiler() | ||
27 | { | ||
28 | this.registryValues = new RegistryKeyValueCollection(); | ||
29 | this.keyCache = new Dictionary<string, string>(); | ||
30 | |||
31 | this.TableDefinitions = DependencyExtensionData.GetExtensionTableDefinitions(); | ||
32 | } | ||
33 | |||
34 | /// <summary> | ||
35 | /// Get the extensions library to be removed. | ||
36 | /// </summary> | ||
37 | /// <param name="tableDefinitions">Table definitions for library.</param> | ||
38 | /// <returns>Library to remove from decompiled output.</returns> | ||
39 | public override Library GetLibraryToRemove(TableDefinitionCollection tableDefinitions) | ||
40 | { | ||
41 | return DependencyExtensionData.GetExtensionLibrary(tableDefinitions); | ||
42 | } | ||
43 | |||
44 | /// <summary> | ||
45 | /// Decompiles an extension table. | ||
46 | /// </summary> | ||
47 | /// <param name="table">The table to decompile.</param> | ||
48 | public override void DecompileTable(Table table) | ||
49 | { | ||
50 | switch (table.Name) | ||
51 | { | ||
52 | case "WixDependencyProvider": | ||
53 | this.DecompileWixDependencyProviderTable(table); | ||
54 | break; | ||
55 | |||
56 | case "WixDependency": | ||
57 | this.DecompileWixDependencyTable(table); | ||
58 | break; | ||
59 | |||
60 | case "WixDependencyRef": | ||
61 | this.DecompileWixDependencyRefTable(table); | ||
62 | break; | ||
63 | |||
64 | default: | ||
65 | base.DecompileTable(table); | ||
66 | break; | ||
67 | } | ||
68 | } | ||
69 | |||
70 | /// <summary> | ||
71 | /// Finalize decompilation by removing registry values that the compiler writes. | ||
72 | /// </summary> | ||
73 | /// <param name="tables">The collection of all tables.</param> | ||
74 | public override void Finish(TableIndexedCollection tables) | ||
75 | { | ||
76 | // Remove generated registry rows. | ||
77 | this.FinalizeRegistryTable(tables); | ||
78 | |||
79 | // Remove extension properties. | ||
80 | this.FinalizeProperties(); | ||
81 | } | ||
82 | |||
83 | /// <summary> | ||
84 | /// Decompiles the WixDependencyProvider table. | ||
85 | /// </summary> | ||
86 | /// <param name="table">The table to decompile.</param> | ||
87 | private void DecompileWixDependencyProviderTable(Table table) | ||
88 | { | ||
89 | foreach (Row row in table.Rows) | ||
90 | { | ||
91 | Provides provides = new Provides(); | ||
92 | |||
93 | provides.Id = (string)row[0]; | ||
94 | provides.Key = (string)row[2]; | ||
95 | |||
96 | if (null != row[3]) | ||
97 | { | ||
98 | provides.Version = (string)row[3]; | ||
99 | } | ||
100 | |||
101 | if (null != row[4]) | ||
102 | { | ||
103 | provides.DisplayName = (string)row[4]; | ||
104 | } | ||
105 | |||
106 | // Nothing to parse for attributes currently. | ||
107 | |||
108 | Wix.Component component = (Wix.Component)this.Core.GetIndexedElement("Component", (string)row[1]); | ||
109 | if (null != component) | ||
110 | { | ||
111 | component.AddChild(provides); | ||
112 | } | ||
113 | else | ||
114 | { | ||
115 | this.Core.OnMessage(WixWarnings.ExpectedForeignRow(row.SourceLineNumbers, table.Name, row.GetPrimaryKey(DecompilerConstants.PrimaryKeyDelimiter), "Component_", (string)row[1], "Component")); | ||
116 | } | ||
117 | |||
118 | // Index the provider to parent the RequiresRef elements. | ||
119 | this.Core.IndexElement(row, provides); | ||
120 | |||
121 | // Add the provider-specific registry keys to be removed during finalization. | ||
122 | // Only remove specific keys that the compiler writes. | ||
123 | string keyProvides = String.Concat(DependencyCommon.RegistryRoot, provides.Key); | ||
124 | |||
125 | this.registryValues.Add(keyProvides, null); | ||
126 | this.registryValues.Add(keyProvides, "Version"); | ||
127 | this.registryValues.Add(keyProvides, "DisplayName"); | ||
128 | this.registryValues.Add(keyProvides, "Attributes"); | ||
129 | |||
130 | // Cache the provider key. | ||
131 | this.keyCache[provides.Id] = provides.Key; | ||
132 | } | ||
133 | } | ||
134 | |||
135 | /// <summary> | ||
136 | /// Decompiles the WixDependency table. | ||
137 | /// </summary> | ||
138 | /// <param name="table">The table to decompile.</param> | ||
139 | private void DecompileWixDependencyTable(Table table) | ||
140 | { | ||
141 | foreach (Row row in table.Rows) | ||
142 | { | ||
143 | Requires requires = new Requires(); | ||
144 | |||
145 | requires.Id = (string)row[0]; | ||
146 | requires.ProviderKey = (string)row[1]; | ||
147 | |||
148 | if (null != row[2]) | ||
149 | { | ||
150 | requires.Minimum = (string)row[2]; | ||
151 | } | ||
152 | |||
153 | if (null != row[3]) | ||
154 | { | ||
155 | requires.Maximum = (string)row[3]; | ||
156 | } | ||
157 | |||
158 | if (null != row[4]) | ||
159 | { | ||
160 | int attributes = (int)row[4]; | ||
161 | |||
162 | if (0 != (attributes & DependencyCommon.RequiresAttributesMinVersionInclusive)) | ||
163 | { | ||
164 | requires.IncludeMinimum = Dependency.YesNoType.yes; | ||
165 | } | ||
166 | |||
167 | if (0 != (attributes & DependencyCommon.RequiresAttributesMaxVersionInclusive)) | ||
168 | { | ||
169 | requires.IncludeMaximum = Dependency.YesNoType.yes; | ||
170 | } | ||
171 | } | ||
172 | |||
173 | this.Core.RootElement.AddChild(requires); | ||
174 | |||
175 | // Cache the requires key. | ||
176 | this.keyCache[requires.Id] = requires.ProviderKey; | ||
177 | } | ||
178 | } | ||
179 | |||
180 | /// <summary> | ||
181 | /// Decompiles the WixDependencyRef table. | ||
182 | /// </summary> | ||
183 | /// <param name="table">The table to decompile.</param> | ||
184 | private void DecompileWixDependencyRefTable(Table table) | ||
185 | { | ||
186 | foreach (Row row in table.Rows) | ||
187 | { | ||
188 | RequiresRef requiresRef = new RequiresRef(); | ||
189 | |||
190 | requiresRef.Id = (string)row[1]; | ||
191 | |||
192 | Provides provides = (Provides)this.Core.GetIndexedElement("WixDependencyProvider", (string)row[0]); | ||
193 | if (null != provides) | ||
194 | { | ||
195 | provides.AddChild(requiresRef); | ||
196 | } | ||
197 | else | ||
198 | { | ||
199 | this.Core.OnMessage(WixWarnings.ExpectedForeignRow(row.SourceLineNumbers, table.Name, row.GetPrimaryKey(DecompilerConstants.PrimaryKeyDelimiter), "WixDependencyProvider_", (string)row[0], "WixDependencyProvider")); | ||
200 | } | ||
201 | |||
202 | // Get the cached keys for the provider and dependency IDs and generate registry rows. | ||
203 | string providesKey = null; | ||
204 | string requiresKey = null; | ||
205 | |||
206 | if (null != provides && this.keyCache.ContainsKey(provides.Id)) | ||
207 | { | ||
208 | providesKey = this.keyCache[provides.Id]; | ||
209 | } | ||
210 | else | ||
211 | { | ||
212 | this.Core.OnMessage(DependencyWarnings.ProvidesKeyNotFound(row.SourceLineNumbers, provides.Id)); | ||
213 | } | ||
214 | |||
215 | if (this.keyCache.ContainsKey(requiresRef.Id)) | ||
216 | { | ||
217 | requiresKey = this.keyCache[requiresRef.Id]; | ||
218 | } | ||
219 | else | ||
220 | { | ||
221 | this.Core.OnMessage(DependencyWarnings.RequiresKeyNotFound(row.SourceLineNumbers, requiresRef.Id)); | ||
222 | } | ||
223 | |||
224 | if (!this.Core.EncounteredError) | ||
225 | { | ||
226 | // Add the dependency-specific registry keys to be removed during finalization. | ||
227 | // Only remove specific keys that the compiler writes. | ||
228 | string keyRequires = String.Format(@"{0}{1}\{2}\{3}", DependencyCommon.RegistryRoot, requiresKey, DependencyCommon.RegistryDependents, providesKey); | ||
229 | |||
230 | this.registryValues.Add(keyRequires, "*"); | ||
231 | this.registryValues.Add(keyRequires, "MinVersion"); | ||
232 | this.registryValues.Add(keyRequires, "MaxVersion"); | ||
233 | this.registryValues.Add(keyRequires, "Attributes"); | ||
234 | } | ||
235 | } | ||
236 | } | ||
237 | |||
238 | /// <summary> | ||
239 | /// Removes rows from the Registry table that are generated by this extension. | ||
240 | /// </summary> | ||
241 | /// <param name="tables">The collection of tables.</param> | ||
242 | private void FinalizeRegistryTable(TableIndexedCollection tables) | ||
243 | { | ||
244 | Table registryTable = tables["Registry"]; | ||
245 | if (null != registryTable) | ||
246 | { | ||
247 | foreach (Row registryRow in registryTable.Rows) | ||
248 | { | ||
249 | // Check if the compiler writes this registry value; if so, it should be removed. | ||
250 | if (this.registryValues.Contains(registryRow)) | ||
251 | { | ||
252 | Wix.ISchemaElement elem = this.Core.GetIndexedElement(registryRow); | ||
253 | |||
254 | // If the registry row was found, remove it from its parent. | ||
255 | if (null != elem && null != elem.ParentElement) | ||
256 | { | ||
257 | Wix.IParentElement elemParent = elem.ParentElement as Wix.IParentElement; | ||
258 | if (null != elemParent) | ||
259 | { | ||
260 | elemParent.RemoveChild(elem); | ||
261 | } | ||
262 | } | ||
263 | } | ||
264 | } | ||
265 | } | ||
266 | } | ||
267 | |||
268 | /// <summary> | ||
269 | /// Removes properties defined by this extension. | ||
270 | /// </summary> | ||
271 | /// <param name="tables">The collection of tables.</param> | ||
272 | private void FinalizeProperties() | ||
273 | { | ||
274 | string[] properties = new string[] { "DISABLEDEPENDENCYCHECK", "IGNOREDEPENDENCIES" }; | ||
275 | foreach (string property in properties) | ||
276 | { | ||
277 | Wix.Property elem = this.Core.GetIndexedElement("Property", property) as Wix.Property; | ||
278 | if (null != elem) | ||
279 | { | ||
280 | // If a value is defined, log a warning we're removing it. | ||
281 | if (!String.IsNullOrEmpty(elem.Value)) | ||
282 | { | ||
283 | this.Core.OnMessage(DependencyWarnings.PropertyRemoved(elem.Id)); | ||
284 | } | ||
285 | |||
286 | // If the property row was found, remove it from its parent. | ||
287 | if (null != elem.ParentElement) | ||
288 | { | ||
289 | Wix.IParentElement elemParent = elem.ParentElement as Wix.IParentElement; | ||
290 | if (null != elemParent) | ||
291 | { | ||
292 | elemParent.RemoveChild(elem); | ||
293 | } | ||
294 | } | ||
295 | } | ||
296 | } | ||
297 | } | ||
298 | |||
299 | /// <summary> | ||
300 | /// Provides an O(1) lookup for registry key and value name pairs for use in the decompiler. | ||
301 | /// </summary> | ||
302 | private sealed class RegistryKeyValueCollection : KeyedCollection<int, KeyValuePair<string, string>> | ||
303 | { | ||
304 | /// <summary> | ||
305 | /// Adds the registry key and value name pair to the collection if it doesn't already exist. | ||
306 | /// </summary> | ||
307 | /// <param name="key">The registry key to add.</param> | ||
308 | /// <param name="name">The registry value name to add.</param> | ||
309 | internal void Add(string key, string name) | ||
310 | { | ||
311 | KeyValuePair<string, string> pair = new KeyValuePair<string, string>(key, name); | ||
312 | if (!this.Contains(pair)) | ||
313 | { | ||
314 | this.Add(pair); | ||
315 | } | ||
316 | } | ||
317 | |||
318 | /// <summary> | ||
319 | /// Returns whether the collection contains the registry key and value name pair from the <see cref="Row"/>. | ||
320 | /// </summary> | ||
321 | /// <param name="row">The registry <see cref="Row"/> to search for.</param> | ||
322 | /// <returns>True if the collection contains the registry key and value name pair from the <see cref="Row"/>; otherwise, false.</returns> | ||
323 | internal bool Contains(Row row) | ||
324 | { | ||
325 | if (null == row) | ||
326 | { | ||
327 | return false; | ||
328 | } | ||
329 | |||
330 | KeyValuePair<string, string> pair = new KeyValuePair<string, string>((string)row[2], (string)row[3]); | ||
331 | return this.Contains(pair); | ||
332 | } | ||
333 | |||
334 | /// <summary> | ||
335 | /// Return the hash code of the key and value pair concatenated with a colon as a delimiter. | ||
336 | /// </summary> | ||
337 | /// <param name="pair">The registry key and value name pair.</param> | ||
338 | /// <returns></returns> | ||
339 | protected override int GetKeyForItem(KeyValuePair<string, string> pair) | ||
340 | { | ||
341 | return String.Concat(pair.Key, ":", pair.Value).GetHashCode(); | ||
342 | } | ||
343 | } | ||
344 | } | ||
345 | } | ||