main
cs 328 lines 13.3 KB
Raw
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.Data.WindowsInstaller
4 {
5 using System;
6 using System.Collections.Generic;
7 using System.Globalization;
8 using System.Linq;
9 using System.Xml;
10
11 /// <summary>
12 /// Output is generated by the linker.
13 /// </summary>
14 public sealed class WindowsInstallerData
15 {
16 internal const string XmlNamespaceUri = "http://wixtoolset.org/schemas/v4/windowsinstallerdata";
17 internal const string XmlElementName = "windowsInstallerData";
18
19 private static readonly Version CurrentVersion = new Version("4.0.0.0");
20 private const string WixOutputStreamName = "wix-wid.xml";
21 private static readonly XmlReaderSettings ReaderSettings = new XmlReaderSettings
22 {
23 CheckCharacters = false
24 };
25 private static readonly XmlWriterSettings WriterSettings = new XmlWriterSettings
26 {
27 CheckCharacters = false,
28 CloseOutput = false,
29 OmitXmlDeclaration = true,
30 };
31
32 /// <summary>
33 /// Creates a new empty output object.
34 /// </summary>
35 /// <param name="sourceLineNumbers">The source line information for the output.</param>
36 public WindowsInstallerData(SourceLineNumber sourceLineNumbers)
37 {
38 this.SourceLineNumbers = sourceLineNumbers;
39 this.SubStorages = new List<SubStorage>();
40 this.Tables = new TableIndexedCollection();
41 }
42
43 /// <summary>
44 /// Gets the type of the output.
45 /// </summary>
46 /// <value>Type of the output.</value>
47 public OutputType Type { get; set; }
48
49 /// <summary>
50 /// Gets or sets the codepage for this output.
51 /// </summary>
52 /// <value>Codepage of the output.</value>
53 public int Codepage { get; set; }
54
55 /// <summary>
56 /// Gets the source line information for this output.
57 /// </summary>
58 /// <value>The source line information for this output.</value>
59 public SourceLineNumber SourceLineNumbers { get; private set; }
60
61 /// <summary>
62 /// Gets the substorages in this output.
63 /// </summary>
64 /// <value>The substorages in this output.</value>
65 public ICollection<SubStorage> SubStorages { get; private set; }
66
67 /// <summary>
68 /// Gets the tables contained in this output.
69 /// </summary>
70 /// <value>Collection of tables.</value>
71 public TableIndexedCollection Tables { get; private set; }
72
73 /// <summary>
74 /// Ensure this output contains a particular table.
75 /// </summary>
76 /// <param name="tableDefinition">Definition of the table that should exist.</param>
77 /// <returns>The table in this output.</returns>
78 public Table EnsureTable(TableDefinition tableDefinition)
79 {
80 if (!this.Tables.TryGetTable(tableDefinition.Name, out var table))
81 {
82 table = new Table(tableDefinition);
83 this.Tables.Add(table);
84 }
85
86 return table;
87 }
88
89 /// <summary>
90 /// Saves an output to a <c>WixOutput</c> container.
91 /// </summary>
92 /// <param name="wixout">Container to save to.</param>
93 public void Save(WixOutput wixout)
94 {
95 using (var writer = XmlWriter.Create(wixout.CreateDataStream(WixOutputStreamName), WriterSettings))
96 {
97 this.Save(writer);
98 }
99 }
100
101 /// <summary>
102 /// Saves an output to an <c>XmlWriter</c>.
103 /// </summary>
104 /// <param name="writer">XmlWriter to save to.</param>
105 public void Save(XmlWriter writer)
106 {
107 writer.WriteStartDocument();
108 this.Write(writer);
109 writer.WriteEndDocument();
110 }
111
112 /// <summary>
113 /// Gets table by name.
114 /// </summary>
115 public bool TryGetTable(string tableName, out Table table) => this.Tables.TryGetTable(tableName, out table);
116
117 /// <summary>
118 /// Loads an output from a path on disk.
119 /// </summary>
120 /// <param name="path">Path to output file saved on disk.</param>
121 /// <param name="suppressVersionCheck">Suppresses wix.dll version mismatch check.</param>
122 /// <returns>Output object.</returns>
123 public static WindowsInstallerData Load(string path, bool suppressVersionCheck = false)
124 {
125 var tableDefinitions = new TableDefinitionCollection(WindowsInstallerTableDefinitions.All);
126 return WindowsInstallerData.Load(path, tableDefinitions, suppressVersionCheck);
127 }
128
129 /// <summary>
130 /// Loads an output from a path on disk.
131 /// </summary>
132 /// <param name="path">Path to output file saved on disk.</param>
133 /// <param name="tableDefinitions">Table definitions to use for creating strongly-typed rows.</param>
134 /// <param name="suppressVersionCheck">Suppresses wix.dll version mismatch check.</param>
135 /// <returns>Output object.</returns>
136 public static WindowsInstallerData Load(string path, TableDefinitionCollection tableDefinitions, bool suppressVersionCheck = false)
137 {
138 using (var wixOutput = WixOutput.Read(path))
139 {
140 return WindowsInstallerData.Load(wixOutput, tableDefinitions, suppressVersionCheck);
141 }
142 }
143
144 /// <summary>
145 /// Loads an output from a WixOutput object.
146 /// </summary>
147 /// <param name="wixOutput">WixOutput object.</param>
148 /// <param name="suppressVersionCheck">Suppresses wix.dll version mismatch check.</param>
149 /// <returns>Output object.</returns>
150 public static WindowsInstallerData Load(WixOutput wixOutput, bool suppressVersionCheck = false)
151 {
152 var tableDefinitions = new TableDefinitionCollection(WindowsInstallerTableDefinitions.All);
153 return WindowsInstallerData.Load(wixOutput, tableDefinitions, suppressVersionCheck);
154 }
155
156 /// <summary>
157 /// Loads an output from a WixOutput object.
158 /// </summary>
159 /// <param name="wixOutput">WixOutput object.</param>
160 /// <param name="tableDefinitions">Table definitions to use for creating strongly-typed rows.</param>
161 /// <param name="suppressVersionCheck">Suppresses wix.dll version mismatch check.</param>
162 /// <returns>Output object.</returns>
163 public static WindowsInstallerData Load(WixOutput wixOutput, TableDefinitionCollection tableDefinitions, bool suppressVersionCheck = false)
164 {
165 using (var stream = wixOutput.GetDataStream(WixOutputStreamName))
166 using (var reader = XmlReader.Create(stream, ReaderSettings, wixOutput.Uri.AbsoluteUri))
167 {
168 try
169 {
170 reader.MoveToContent();
171 return WindowsInstallerData.Read(reader, tableDefinitions, suppressVersionCheck);
172 }
173 catch (XmlException xe)
174 {
175 throw new WixCorruptFileException(wixOutput.Uri.AbsoluteUri, "wixout", xe);
176 }
177 }
178 }
179
180 /// <summary>
181 /// Processes an XmlReader and builds up the output object.
182 /// </summary>
183 /// <param name="reader">Reader to get data from.</param>
184 /// <param name="tableDefinitions">Table definitions to use for creating strongly-typed rows.</param>
185 /// <param name="suppressVersionCheck">Suppresses wix.dll version mismatch check.</param>
186 /// <returns>The Output represented by the Xml.</returns>
187 internal static WindowsInstallerData Read(XmlReader reader, TableDefinitionCollection tableDefinitions, bool suppressVersionCheck)
188 {
189 if (!reader.LocalName.Equals(WindowsInstallerData.XmlElementName))
190 {
191 throw new XmlException();
192 }
193
194 var empty = reader.IsEmptyElement;
195 var output = new WindowsInstallerData(SourceLineNumber.CreateFromUri(reader.BaseURI));
196 Version version = null;
197
198 while (reader.MoveToNextAttribute())
199 {
200 switch (reader.LocalName)
201 {
202 case "codepage":
203 output.Codepage = Convert.ToInt32(reader.Value, CultureInfo.InvariantCulture.NumberFormat);
204 break;
205 case "type":
206 switch (reader.Value)
207 {
208 case "Bundle":
209 output.Type = OutputType.Bundle;
210 break;
211 case "Module":
212 output.Type = OutputType.Module;
213 break;
214 case "Patch":
215 output.Type = OutputType.Patch;
216 break;
217 case "PatchCreation":
218 output.Type = OutputType.PatchCreation;
219 break;
220 case "Package":
221 case "Product":
222 output.Type = OutputType.Package;
223 break;
224 case "Transform":
225 output.Type = OutputType.Transform;
226 break;
227 default:
228 throw new XmlException();
229 }
230 break;
231 case "version":
232 version = new Version(reader.Value);
233 break;
234 }
235 }
236
237 if (!suppressVersionCheck && null != version && !WindowsInstallerData.CurrentVersion.Equals(version))
238 {
239 throw new WixException(ErrorMessages.VersionMismatch(SourceLineNumber.CreateFromUri(reader.BaseURI), WindowsInstallerData.XmlElementName, version.ToString(), WindowsInstallerData.CurrentVersion.ToString()));
240 }
241
242 // loop through the rest of the xml building up the Output object
243 TableDefinitionCollection xmlTableDefinitions = null;
244 var tables = new List<Table>();
245 if (!empty)
246 {
247 var done = false;
248
249 // loop through all the fields in a row
250 while (!done && reader.Read())
251 {
252 switch (reader.NodeType)
253 {
254 case XmlNodeType.Element:
255 switch (reader.LocalName)
256 {
257 case "subStorage":
258 output.SubStorages.Add(SubStorage.Read(reader, tableDefinitions));
259 break;
260 case "table":
261 if (null == xmlTableDefinitions)
262 {
263 throw new XmlException();
264 }
265 tables.Add(Table.Read(reader, xmlTableDefinitions));
266 break;
267 case "tableDefinitions":
268 xmlTableDefinitions = TableDefinitionCollection.Read(reader, tableDefinitions);
269 break;
270 default:
271 throw new XmlException();
272 }
273 break;
274 case XmlNodeType.EndElement:
275 done = true;
276 break;
277 }
278 }
279
280 if (!done)
281 {
282 throw new XmlException();
283 }
284 }
285
286 output.Tables = new TableIndexedCollection(tables);
287 return output;
288 }
289
290 /// <summary>
291 /// Persists an output in an XML format.
292 /// </summary>
293 /// <param name="writer">XmlWriter where the Output should persist itself as XML.</param>
294 internal void Write(XmlWriter writer)
295 {
296 writer.WriteStartElement(WindowsInstallerData.XmlElementName, XmlNamespaceUri);
297
298 writer.WriteAttributeString("type", this.Type.ToString());
299
300 if (0 != this.Codepage)
301 {
302 writer.WriteAttributeString("codepage", this.Codepage.ToString(CultureInfo.InvariantCulture));
303 }
304
305 writer.WriteAttributeString("version", WindowsInstallerData.CurrentVersion.ToString());
306
307 // Collect all the table definitions and write them.
308 var tableDefinitions = new TableDefinitionCollection();
309 foreach (var table in this.Tables)
310 {
311 tableDefinitions.Add(table.Definition);
312 }
313 tableDefinitions.Write(writer);
314
315 foreach (var table in this.Tables.OrderBy(t => t.Name))
316 {
317 table.Write(writer);
318 }
319
320 foreach (var subStorage in this.SubStorages)
321 {
322 subStorage.Write(writer);
323 }
324
325 writer.WriteEndElement();
326 }
327 }
328 }