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