aboutsummaryrefslogtreecommitdiff
path: root/src/wixext/DependencyDecompiler.cs
diff options
context:
space:
mode:
Diffstat (limited to 'src/wixext/DependencyDecompiler.cs')
-rw-r--r--src/wixext/DependencyDecompiler.cs345
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
3namespace 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}