diff options
Diffstat (limited to 'src/tools/heat/RegFileHarvester.cs')
-rw-r--r-- | src/tools/heat/RegFileHarvester.cs | 436 |
1 files changed, 0 insertions, 436 deletions
diff --git a/src/tools/heat/RegFileHarvester.cs b/src/tools/heat/RegFileHarvester.cs deleted file mode 100644 index 49cf4c01..00000000 --- a/src/tools/heat/RegFileHarvester.cs +++ /dev/null | |||
@@ -1,436 +0,0 @@ | |||
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.Harvesters | ||
4 | { | ||
5 | using System; | ||
6 | using System.Collections; | ||
7 | using System.Globalization; | ||
8 | using System.IO; | ||
9 | using WixToolset.Data; | ||
10 | using WixToolset.Harvesters.Data; | ||
11 | using WixToolset.Harvesters.Extensibility; | ||
12 | using Wix = WixToolset.Harvesters.Serialize; | ||
13 | |||
14 | /// <summary> | ||
15 | /// Harvest WiX authoring for a reg file. | ||
16 | /// </summary> | ||
17 | public sealed class RegFileHarvester : BaseHarvesterExtension | ||
18 | { | ||
19 | private static readonly string ComponentPrefix = "cmp"; | ||
20 | |||
21 | /// <summary> | ||
22 | /// Current line in the reg file being processed. | ||
23 | /// </summary> | ||
24 | private int currentLineNumber = 0; | ||
25 | |||
26 | /// <summary> | ||
27 | /// Flag indicating whether this is a unicode registry file. | ||
28 | /// </summary> | ||
29 | private bool unicodeRegistry; | ||
30 | |||
31 | /// <summary> | ||
32 | /// Harvest a file. | ||
33 | /// </summary> | ||
34 | /// <param name="argument">The path of the file.</param> | ||
35 | /// <returns>A harvested file.</returns> | ||
36 | public override Wix.Fragment[] Harvest(string argument) | ||
37 | { | ||
38 | if (null == argument) | ||
39 | { | ||
40 | throw new ArgumentNullException("argument"); | ||
41 | } | ||
42 | |||
43 | // Harvest the keys from the registry file | ||
44 | Wix.Fragment fragment = this.HarvestRegFile(argument); | ||
45 | |||
46 | return new Wix.Fragment[] { fragment }; | ||
47 | } | ||
48 | |||
49 | /// <summary> | ||
50 | /// Harvest a reg file. | ||
51 | /// </summary> | ||
52 | /// <param name="path">The path of the file.</param> | ||
53 | /// <returns>A harvested registy file.</returns> | ||
54 | public Wix.Fragment HarvestRegFile(string path) | ||
55 | { | ||
56 | if (null == path) | ||
57 | { | ||
58 | throw new ArgumentNullException("path"); | ||
59 | } | ||
60 | |||
61 | if (!File.Exists(path)) | ||
62 | { | ||
63 | throw new WixException(HarvesterErrors.FileNotFound(path)); | ||
64 | } | ||
65 | |||
66 | var directory = DirectoryHelper.CreateDirectory("TARGETDIR"); | ||
67 | |||
68 | // Use absolute paths | ||
69 | path = Path.GetFullPath(path); | ||
70 | FileInfo file = new FileInfo(path); | ||
71 | |||
72 | using (StreamReader sr = file.OpenText()) | ||
73 | { | ||
74 | string line; | ||
75 | this.currentLineNumber = 0; | ||
76 | |||
77 | while (null != (line = this.GetNextLine(sr))) | ||
78 | { | ||
79 | if (line.StartsWith(@"Windows Registry Editor Version 5.00")) | ||
80 | { | ||
81 | this.unicodeRegistry = true; | ||
82 | } | ||
83 | else if (line.StartsWith(@"REGEDIT4")) | ||
84 | { | ||
85 | this.unicodeRegistry = false; | ||
86 | } | ||
87 | else if (line.StartsWith(@"[HKEY_CLASSES_ROOT\")) | ||
88 | { | ||
89 | this.ConvertKey(sr, ref directory, Wix.RegistryRootType.HKCR, line.Substring(19, line.Length - 20)); | ||
90 | } | ||
91 | else if (line.StartsWith(@"[HKEY_CURRENT_USER\")) | ||
92 | { | ||
93 | this.ConvertKey(sr, ref directory, Wix.RegistryRootType.HKCU, line.Substring(19, line.Length - 20)); | ||
94 | } | ||
95 | else if (line.StartsWith(@"[HKEY_LOCAL_MACHINE\")) | ||
96 | { | ||
97 | this.ConvertKey(sr, ref directory, Wix.RegistryRootType.HKLM, line.Substring(20, line.Length - 21)); | ||
98 | } | ||
99 | else if (line.StartsWith(@"[HKEY_USERS\")) | ||
100 | { | ||
101 | this.ConvertKey(sr, ref directory, Wix.RegistryRootType.HKU, line.Substring(12, line.Length - 13)); | ||
102 | } | ||
103 | } | ||
104 | } | ||
105 | |||
106 | Console.WriteLine("Processing complete"); | ||
107 | |||
108 | Wix.Fragment fragment = new Wix.Fragment(); | ||
109 | fragment.AddChild(directory); | ||
110 | |||
111 | return fragment; | ||
112 | } | ||
113 | |||
114 | /// <summary> | ||
115 | /// Converts the registry key to a WiX component element. | ||
116 | /// </summary> | ||
117 | /// <param name="sr">The registry file stream.</param> | ||
118 | /// <param name="directory">A WiX directory reference.</param> | ||
119 | /// <param name="root">The root key.</param> | ||
120 | /// <param name="line">The current line.</param> | ||
121 | private void ConvertKey(StreamReader sr, ref Wix.DirectoryBase directory, Wix.RegistryRootType root, string line) | ||
122 | { | ||
123 | Wix.Component component = new Wix.Component(); | ||
124 | |||
125 | component.Id = this.Core.GenerateIdentifier(ComponentPrefix, line); | ||
126 | component.KeyPath = Wix.YesNoType.yes; | ||
127 | |||
128 | this.ConvertValues(sr, ref component, root, line); | ||
129 | directory.AddChild(component); | ||
130 | } | ||
131 | |||
132 | /// <summary> | ||
133 | /// Converts the registry values to WiX regisry key element. | ||
134 | /// </summary> | ||
135 | /// <param name="sr">The registry file stream.</param> | ||
136 | /// <param name="component">A WiX component reference.</param> | ||
137 | /// <param name="root">The root key.</param> | ||
138 | /// <param name="line">The current line.</param> | ||
139 | private void ConvertValues(StreamReader sr, ref Wix.Component component, Wix.RegistryRootType root, string line) | ||
140 | { | ||
141 | string name = null; | ||
142 | string value = null; | ||
143 | var registryKey = new Wix.RegistryKey | ||
144 | { | ||
145 | Root = root, | ||
146 | Key = line | ||
147 | }; | ||
148 | |||
149 | while (this.GetValue(sr, ref name, ref value, out var type)) | ||
150 | { | ||
151 | Wix.RegistryValue registryValue = new Wix.RegistryValue(); | ||
152 | ArrayList charArray; | ||
153 | |||
154 | // Don't specifiy name for default attribute | ||
155 | if (!String.IsNullOrEmpty(name)) | ||
156 | { | ||
157 | registryValue.Name = name; | ||
158 | } | ||
159 | |||
160 | registryValue.Type = type; | ||
161 | |||
162 | switch (type) | ||
163 | { | ||
164 | case Wix.RegistryValue.TypeType.binary: | ||
165 | registryValue.Value = value.Replace(",", String.Empty).ToUpper(); | ||
166 | break; | ||
167 | |||
168 | case Wix.RegistryValue.TypeType.integer: | ||
169 | registryValue.Value = Int32.Parse(value, NumberStyles.HexNumber).ToString(); | ||
170 | break; | ||
171 | |||
172 | case Wix.RegistryValue.TypeType.expandable: | ||
173 | charArray = this.ConvertCharList(value); | ||
174 | value = String.Empty; | ||
175 | |||
176 | // create the string, remove the terminating null | ||
177 | for (int i = 0; i < charArray.Count; i++) | ||
178 | { | ||
179 | if ('\0' != (char)charArray[i]) | ||
180 | { | ||
181 | value += charArray[i]; | ||
182 | } | ||
183 | } | ||
184 | |||
185 | registryValue.Value = value; | ||
186 | break; | ||
187 | |||
188 | case Wix.RegistryValue.TypeType.multiString: | ||
189 | charArray = this.ConvertCharList(value); | ||
190 | value = String.Empty; | ||
191 | |||
192 | // Convert the character array to a string so we can simply split it at the nulls, ignore the final null null. | ||
193 | for (int i = 0; i < (charArray.Count - 2); i++) | ||
194 | { | ||
195 | value += charArray[i]; | ||
196 | } | ||
197 | |||
198 | // Although the value can use [~] the preffered way is to use MultiStringValue | ||
199 | string[] parts = value.Split("\0".ToCharArray()); | ||
200 | foreach (string part in parts) | ||
201 | { | ||
202 | Wix.MultiStringValue multiStringValue = new Wix.MultiStringValue(); | ||
203 | multiStringValue.Value = part; | ||
204 | registryValue.AddChild(multiStringValue); | ||
205 | } | ||
206 | |||
207 | break; | ||
208 | |||
209 | case Wix.RegistryValue.TypeType.@string: | ||
210 | // Remove \\ and \" | ||
211 | value = value.ToString().Replace("\\\"", "\""); | ||
212 | value = value.ToString().Replace(@"\\", @"\"); | ||
213 | // Escape [ and ] | ||
214 | value = value.ToString().Replace(@"[", @"[\[]"); | ||
215 | value = value.ToString().Replace(@"]", @"[\]]"); | ||
216 | // This undoes the duplicate escaping caused by the second replace | ||
217 | value = value.ToString().Replace(@"[\[[\]]", @"[\[]"); | ||
218 | // Escape $ | ||
219 | value = value.ToString().Replace(@"$", @"$$"); | ||
220 | |||
221 | registryValue.Value = value; | ||
222 | break; | ||
223 | |||
224 | default: | ||
225 | throw new ApplicationException(String.Format("Did not recognize the type of reg value on line {0}", this.currentLineNumber)); | ||
226 | } | ||
227 | |||
228 | registryKey.AddChild(registryValue); | ||
229 | } | ||
230 | |||
231 | // Make sure empty keys are created | ||
232 | if (null == value) | ||
233 | { | ||
234 | registryKey.ForceCreateOnInstall = Wix.YesNoType.yes; | ||
235 | } | ||
236 | |||
237 | component.AddChild(registryKey); | ||
238 | } | ||
239 | |||
240 | /// <summary> | ||
241 | /// Parse a value from a line. | ||
242 | /// </summary> | ||
243 | /// <param name="sr">Reader for the reg file.</param> | ||
244 | /// <param name="name">Name of the value.</param> | ||
245 | /// <param name="value">Value of the value.</param> | ||
246 | /// <param name="type">Type of the value.</param> | ||
247 | /// <returns>true if the value can be parsed, false otherwise.</returns> | ||
248 | private bool GetValue(StreamReader sr, ref string name, ref string value, out Wix.RegistryValue.TypeType type) | ||
249 | { | ||
250 | string line = this.GetNextLine(sr); | ||
251 | |||
252 | if (null == line || 0 == line.Length) | ||
253 | { | ||
254 | type = 0; | ||
255 | return false; | ||
256 | } | ||
257 | |||
258 | string[] parts; | ||
259 | |||
260 | if (line.StartsWith("@")) | ||
261 | { | ||
262 | // Special case for default value | ||
263 | parts = line.Trim().Split("=".ToCharArray(), 2); | ||
264 | |||
265 | name = null; | ||
266 | } | ||
267 | else | ||
268 | { | ||
269 | parts = line.Trim().Split("=".ToCharArray()); | ||
270 | |||
271 | // It is valid to have an '=' in the name or the data. This is probably a string so the separator will be '"="'. | ||
272 | if (2 != parts.Length) | ||
273 | { | ||
274 | string[] stringSeparator = new string[] { "\"=\"" }; | ||
275 | parts = line.Trim().Split(stringSeparator, StringSplitOptions.None); | ||
276 | |||
277 | if (2 != parts.Length) | ||
278 | { | ||
279 | // Line still no parsed correctly | ||
280 | throw new ApplicationException(String.Format("Cannot parse value: {0} at line {1}.", line, this.currentLineNumber)); | ||
281 | } | ||
282 | |||
283 | // Put back quotes stripped by Split() | ||
284 | parts[0] += "\""; | ||
285 | parts[1] = "\"" + parts[1]; | ||
286 | } | ||
287 | |||
288 | name = parts[0].Substring(1, parts[0].Length - 2); | ||
289 | } | ||
290 | |||
291 | if (parts[1].StartsWith("hex:")) | ||
292 | { | ||
293 | // binary | ||
294 | value = parts[1].Substring(4); | ||
295 | type = Wix.RegistryValue.TypeType.binary; | ||
296 | } | ||
297 | else if (parts[1].StartsWith("dword:")) | ||
298 | { | ||
299 | // dword | ||
300 | value = parts[1].Substring(6); | ||
301 | type = Wix.RegistryValue.TypeType.integer; | ||
302 | } | ||
303 | else if (parts[1].StartsWith("hex(2):")) | ||
304 | { | ||
305 | // expandable string | ||
306 | value = parts[1].Substring(7); | ||
307 | type = Wix.RegistryValue.TypeType.expandable; | ||
308 | } | ||
309 | else if (parts[1].StartsWith("hex(7):")) | ||
310 | { | ||
311 | // multi-string | ||
312 | value = parts[1].Substring(7); | ||
313 | type = Wix.RegistryValue.TypeType.multiString; | ||
314 | } | ||
315 | else if (parts[1].StartsWith("hex(")) | ||
316 | { | ||
317 | // Give a better error when we find something that isn't supported | ||
318 | // by specifying the type that isn't supported. | ||
319 | string unsupportedType = ""; | ||
320 | |||
321 | if (parts[1].StartsWith("hex(0")) { unsupportedType = "REG_NONE"; } | ||
322 | else if (parts[1].StartsWith("hex(6")) { unsupportedType = "REG_LINK"; } | ||
323 | else if (parts[1].StartsWith("hex(8")) { unsupportedType = "REG_RESOURCE_LIST"; } | ||
324 | else if (parts[1].StartsWith("hex(9")) { unsupportedType = "REG_FULL_RESOURCE_DESCRIPTOR"; } | ||
325 | else if (parts[1].StartsWith("hex(a")) { unsupportedType = "REG_RESOURCE_REQUIREMENTS_LIST"; } | ||
326 | else if (parts[1].StartsWith("hex(b")) { unsupportedType = "REG_QWORD"; } | ||
327 | |||
328 | // REG_NONE(0), REG_LINK(6), REG_RESOURCE_LIST(8), REG_FULL_RESOURCE_DESCRIPTOR(9), REG_RESOURCE_REQUIREMENTS_LIST(a), REG_QWORD(b) | ||
329 | this.Core.Messaging.Write(HarvesterWarnings.UnsupportedRegistryType(parts[0], this.currentLineNumber, unsupportedType)); | ||
330 | |||
331 | type = 0; | ||
332 | return false; | ||
333 | } | ||
334 | else if (parts[1].StartsWith("\"")) | ||
335 | { | ||
336 | // string | ||
337 | value = parts[1].Substring(1, parts[1].Length - 2); | ||
338 | type = Wix.RegistryValue.TypeType.@string; | ||
339 | } | ||
340 | else | ||
341 | { | ||
342 | // unsupported value | ||
343 | throw new ApplicationException(String.Format("Unsupported registry value {0} at line {1}.", line, this.currentLineNumber)); | ||
344 | } | ||
345 | |||
346 | return true; | ||
347 | } | ||
348 | |||
349 | /// <summary> | ||
350 | /// Get the next line from the reg file input stream. | ||
351 | /// </summary> | ||
352 | /// <param name="sr">Reader for the reg file.</param> | ||
353 | /// <returns>The next line.</returns> | ||
354 | private string GetNextLine(StreamReader sr) | ||
355 | { | ||
356 | string line; | ||
357 | string totalLine = null; | ||
358 | |||
359 | while (null != (line = sr.ReadLine())) | ||
360 | { | ||
361 | bool stop = true; | ||
362 | |||
363 | this.currentLineNumber++; | ||
364 | line = line.Trim(); | ||
365 | |||
366 | if (line.EndsWith("\\")) | ||
367 | { | ||
368 | stop = false; | ||
369 | line = line.Substring(0, line.Length - 1); | ||
370 | } | ||
371 | |||
372 | if (null == totalLine) | ||
373 | { | ||
374 | // first line | ||
375 | totalLine = line; | ||
376 | } | ||
377 | else | ||
378 | { | ||
379 | // other lines | ||
380 | totalLine += line; | ||
381 | } | ||
382 | |||
383 | // break if there is no more info for this line | ||
384 | if (stop) | ||
385 | { | ||
386 | break; | ||
387 | } | ||
388 | } | ||
389 | |||
390 | return totalLine; | ||
391 | } | ||
392 | |||
393 | /// <summary> | ||
394 | /// Convert a character list into the proper WiX format for either unicode or ansi lists. | ||
395 | /// </summary> | ||
396 | /// <param name="charList">List of characters.</param> | ||
397 | /// <returns>Array of characters.</returns> | ||
398 | private ArrayList ConvertCharList(string charList) | ||
399 | { | ||
400 | if (String.IsNullOrEmpty(charList)) | ||
401 | { | ||
402 | return new ArrayList(); | ||
403 | } | ||
404 | |||
405 | string[] strChars = charList.Split(",".ToCharArray()); | ||
406 | |||
407 | ArrayList charArray = new ArrayList(); | ||
408 | |||
409 | if (this.unicodeRegistry) | ||
410 | { | ||
411 | if (0 != strChars.Length % 2) | ||
412 | { | ||
413 | throw new ApplicationException(String.Format("Problem parsing Expandable string data at line {0}, its probably not Unicode.", this.currentLineNumber)); | ||
414 | } | ||
415 | |||
416 | for (int i = 0; i < strChars.Length; i += 2) | ||
417 | { | ||
418 | string chars = strChars[i + 1] + strChars[i]; | ||
419 | int unicodeInt = Int32.Parse(chars, NumberStyles.HexNumber); | ||
420 | char unicodeChar = (char)unicodeInt; | ||
421 | charArray.Add(unicodeChar); | ||
422 | } | ||
423 | } | ||
424 | else | ||
425 | { | ||
426 | for (int i = 0; i < strChars.Length; i++) | ||
427 | { | ||
428 | char charValue = (char)Int32.Parse(strChars[i], NumberStyles.HexNumber); | ||
429 | charArray.Add(charValue); | ||
430 | } | ||
431 | } | ||
432 | |||
433 | return charArray; | ||
434 | } | ||
435 | } | ||
436 | } | ||