aboutsummaryrefslogtreecommitdiff
path: root/src/WixToolset.Core.WindowsInstaller/Bind/GenerateDatabaseCommand.cs
diff options
context:
space:
mode:
Diffstat (limited to 'src/WixToolset.Core.WindowsInstaller/Bind/GenerateDatabaseCommand.cs')
-rw-r--r--src/WixToolset.Core.WindowsInstaller/Bind/GenerateDatabaseCommand.cs535
1 files changed, 282 insertions, 253 deletions
diff --git a/src/WixToolset.Core.WindowsInstaller/Bind/GenerateDatabaseCommand.cs b/src/WixToolset.Core.WindowsInstaller/Bind/GenerateDatabaseCommand.cs
index 6b365ecd..ed3b6f01 100644
--- a/src/WixToolset.Core.WindowsInstaller/Bind/GenerateDatabaseCommand.cs
+++ b/src/WixToolset.Core.WindowsInstaller/Bind/GenerateDatabaseCommand.cs
@@ -5,367 +5,396 @@ namespace WixToolset.Core.WindowsInstaller.Bind
5 using System; 5 using System;
6 using System.Collections.Generic; 6 using System.Collections.Generic;
7 using System.ComponentModel; 7 using System.ComponentModel;
8 using System.Globalization;
9 using System.IO; 8 using System.IO;
10 using System.Text; 9 using System.Text;
10 using WixToolset.Core.WindowsInstaller.Msi;
11 using WixToolset.Data; 11 using WixToolset.Data;
12 using WixToolset.Extensibility;
13 using WixToolset.Data.WindowsInstaller; 12 using WixToolset.Data.WindowsInstaller;
14 using WixToolset.Extensibility.Services; 13 using WixToolset.Extensibility;
15 using WixToolset.Extensibility.Data; 14 using WixToolset.Extensibility.Data;
16 using WixToolset.Core.WindowsInstaller.Msi; 15 using WixToolset.Extensibility.Services;
17 16
18 internal class GenerateDatabaseCommand 17 internal class GenerateDatabaseCommand
19 { 18 {
20 public int Codepage { private get; set; } 19 public GenerateDatabaseCommand(IMessaging messaging, IBackendHelper backendHelper, IEnumerable<IFileSystemExtension> fileSystemExtensions, WindowsInstallerData data, string outputPath, TableDefinitionCollection tableDefinitions, string intermediateFolder, int codepage, bool keepAddedColumns, bool suppressAddingValidationRows, bool useSubdirectory)
20 {
21 this.Messaging = messaging;
22 this.BackendHelper = backendHelper;
23 this.Extensions = fileSystemExtensions;
24 this.Data = data;
25 this.OutputPath = outputPath;
26 this.TableDefinitions = tableDefinitions;
27 this.IntermediateFolder = intermediateFolder;
28 this.Codepage = codepage;
29 this.KeepAddedColumns = keepAddedColumns;
30 this.SuppressAddingValidationRows = suppressAddingValidationRows;
31 this.UseSubDirectory = useSubdirectory;
32 }
33
34 private int Codepage { get; }
21 35
22 public IBackendHelper BackendHelper { private get; set; } 36 private IBackendHelper BackendHelper { get; }
23 37
24 public IEnumerable<IFileSystemExtension> Extensions { private get; set; } 38 private IEnumerable<IFileSystemExtension> Extensions { get; }
25 39
26 /// <summary> 40 /// <summary>
27 /// Whether to keep columns added in a transform. 41 /// Whether to keep columns added in a transform.
28 /// </summary> 42 /// </summary>
29 public bool KeepAddedColumns { private get; set; } 43 private bool KeepAddedColumns { get; }
30 44
31 public IMessaging Messaging { private get; set; } 45 private IMessaging Messaging { get; }
32 46
33 public WindowsInstallerData Output { private get; set; } 47 private WindowsInstallerData Data { get; }
34 48
35 public string OutputPath { private get; set; } 49 private string OutputPath { get; }
36 50
37 public TableDefinitionCollection TableDefinitions { private get; set; } 51 private TableDefinitionCollection TableDefinitions { get; }
38 52
39 public string IntermediateFolder { private get; set; } 53 private string IntermediateFolder { get; }
40 54
41 public List<ITrackedFile> GeneratedTemporaryFiles { get; } = new List<ITrackedFile>(); 55 public List<ITrackedFile> GeneratedTemporaryFiles { get; } = new List<ITrackedFile>();
42 56
43 /// <summary> 57 /// <summary>
44 /// Whether to use a subdirectory based on the <paramref name="databaseFile"/> file name for intermediate files. 58 /// Whether to use a subdirectory based on the <paramref name="databaseFile"/> file name for intermediate files.
45 /// </summary> 59 /// </summary>
46 public bool SuppressAddingValidationRows { private get; set; } 60 private bool SuppressAddingValidationRows { get; }
47 61
48 public bool UseSubDirectory { private get; set; } 62 private bool UseSubDirectory { get; }
49 63
50 public void Execute() 64 public void Execute()
51 { 65 {
52 // Add the _Validation rows. 66 // Add the _Validation rows.
53 if (!this.SuppressAddingValidationRows) 67 if (!this.SuppressAddingValidationRows)
54 { 68 {
55 var validationTable = this.Output.EnsureTable(this.TableDefinitions["_Validation"]); 69 this.AddValidationRows();
56
57 foreach (var table in this.Output.Tables)
58 {
59 if (!table.Definition.Unreal)
60 {
61 // Add the validation rows for this table.
62 foreach (ColumnDefinition columnDef in table.Definition.Columns)
63 {
64 var row = validationTable.CreateRow(null);
65
66 row[0] = table.Name;
67
68 row[1] = columnDef.Name;
69
70 if (columnDef.Nullable)
71 {
72 row[2] = "Y";
73 }
74 else
75 {
76 row[2] = "N";
77 }
78
79 if (columnDef.MinValue.HasValue)
80 {
81 row[3] = columnDef.MinValue.Value;
82 }
83
84 if (columnDef.MaxValue.HasValue)
85 {
86 row[4] = columnDef.MaxValue.Value;
87 }
88
89 row[5] = columnDef.KeyTable;
90
91 if (columnDef.KeyColumn.HasValue)
92 {
93 row[6] = columnDef.KeyColumn.Value;
94 }
95
96 if (ColumnCategory.Unknown != columnDef.Category)
97 {
98 row[7] = columnDef.Category.ToString();
99 }
100
101 row[8] = columnDef.Possibilities;
102
103 row[9] = columnDef.Description;
104 }
105 }
106 }
107 } 70 }
108 71
109 // Set the base directory.
110 var baseDirectory = this.IntermediateFolder; 72 var baseDirectory = this.IntermediateFolder;
111 73
112 if (this.UseSubDirectory) 74 if (this.UseSubDirectory)
113 { 75 {
114 string filename = Path.GetFileNameWithoutExtension(this.OutputPath); 76 var filename = Path.GetFileNameWithoutExtension(this.OutputPath);
115 baseDirectory = Path.Combine(baseDirectory, filename); 77 baseDirectory = Path.Combine(baseDirectory, filename);
116
117 // make sure the directory exists
118 Directory.CreateDirectory(baseDirectory);
119 } 78 }
120 79
121 var idtDirectory = Path.Combine(baseDirectory, "_idts"); 80 var idtFolder = Path.Combine(baseDirectory, "_idts");
122 Directory.CreateDirectory(idtDirectory);
123 81
124 try 82 var type = OpenDatabase.CreateDirect;
83
84 if (OutputType.Patch == this.Data.Type)
125 { 85 {
126 OpenDatabase type = OpenDatabase.CreateDirect; 86 type |= OpenDatabase.OpenPatchFile;
87 }
127 88
128 // set special flag for patch files 89 // Localize the codepage if a value was specified directly.
129 if (OutputType.Patch == this.Output.Type) 90 if (-1 != this.Codepage)
130 { 91 {
131 type |= OpenDatabase.OpenPatchFile; 92 this.Data.Codepage = this.Codepage;
132 } 93 }
133 94
95 try
96 {
134#if DEBUG 97#if DEBUG
135 Console.WriteLine("Opening database at: {0}", this.OutputPath); 98 Console.WriteLine("Opening database at: {0}", this.OutputPath);
136#endif 99#endif
137 100
138 // Localize the codepage if a value was specified directly.
139 if (-1 != this.Codepage)
140 {
141 this.Output.Codepage = this.Codepage;
142 }
143
144 Directory.CreateDirectory(Path.GetDirectoryName(this.OutputPath)); 101 Directory.CreateDirectory(Path.GetDirectoryName(this.OutputPath));
145 102
146 using (Database db = new Database(this.OutputPath, type)) 103 Directory.CreateDirectory(idtFolder);
104
105 using (var db = new Database(this.OutputPath, type))
147 { 106 {
148 // if we're not using the default codepage, import a new one into our 107 // If we're not using the default codepage, import a new one into our
149 // database before we add any tables (or the tables would be added 108 // database before we add any tables (or the tables would be added
150 // with the wrong codepage). 109 // with the wrong codepage).
151 if (0 != this.Output.Codepage) 110 if (0 != this.Data.Codepage)
152 { 111 {
153 this.SetDatabaseCodepage(db, this.Output.Codepage, idtDirectory); 112 this.SetDatabaseCodepage(db, this.Data.Codepage, idtFolder);
154 } 113 }
155 114
156 foreach (Table table in this.Output.Tables) 115 this.ImportTables(db, idtFolder);
116
117 // Insert substorages (usually transforms inside a patch or instance transforms in a package).
118 this.ImportSubStorages(db);
119
120 // We're good, commit the changes to the new database.
121 db.Commit();
122 }
123 }
124 catch (IOException e)
125 {
126 // TODO: this error message doesn't seem specific enough
127 throw new WixException(ErrorMessages.FileNotFound(new SourceLineNumber(this.OutputPath), this.OutputPath), e);
128 }
129 }
130
131 private void AddValidationRows()
132 {
133 var validationTable = this.Data.EnsureTable(this.TableDefinitions["_Validation"]);
134
135 foreach (var table in this.Data.Tables)
136 {
137 if (!table.Definition.Unreal)
138 {
139 // Add the validation rows for this table.
140 foreach (var columnDef in table.Definition.Columns)
157 { 141 {
158 Table importTable = table; 142 var row = validationTable.CreateRow(null);
159 bool hasBinaryColumn = false; 143
144 row[0] = table.Name;
160 145
161 // Skip all unreal tables other than _Streams. 146 row[1] = columnDef.Name;
162 if (table.Definition.Unreal && "_Streams" != table.Name) 147
148 if (columnDef.Nullable)
163 { 149 {
164 continue; 150 row[2] = "Y";
151 }
152 else
153 {
154 row[2] = "N";
165 } 155 }
166 156
167 // Do not put the _Validation table in patches, it is not needed. 157 if (columnDef.MinValue.HasValue)
168 if (OutputType.Patch == this.Output.Type && "_Validation" == table.Name)
169 { 158 {
170 continue; 159 row[3] = columnDef.MinValue.Value;
171 } 160 }
172 161
173 // The only way to import binary data is to copy it to a local subdirectory first. 162 if (columnDef.MaxValue.HasValue)
174 // To avoid this extra copying and perf hit, import an empty table with the same
175 // definition and later import the binary data from source using records.
176 foreach (ColumnDefinition columnDefinition in table.Definition.Columns)
177 { 163 {
178 if (ColumnType.Object == columnDefinition.Type) 164 row[4] = columnDef.MaxValue.Value;
179 {
180 importTable = new Table(table.Definition);
181 hasBinaryColumn = true;
182 break;
183 }
184 } 165 }
185 166
186 // Create the table via IDT import. 167 row[5] = columnDef.KeyTable;
187 if ("_Streams" != importTable.Name) 168
169 if (columnDef.KeyColumn.HasValue)
188 { 170 {
189 try 171 row[6] = columnDef.KeyColumn.Value;
190 { 172 }
191 var command = new CreateIdtFileCommand(this.Messaging, importTable, this.Output.Codepage, idtDirectory, this.KeepAddedColumns);
192 command.Execute();
193 173
194 var buildOutput = this.BackendHelper.TrackFile(command.IdtPath, TrackedFileType.Temporary); 174 if (ColumnCategory.Unknown != columnDef.Category)
195 this.GeneratedTemporaryFiles.Add(buildOutput); 175 {
176 row[7] = columnDef.Category.ToString();
177 }
196 178
197 db.Import(command.IdtPath); 179 row[8] = columnDef.Possibilities;
198 }
199 catch (WixInvalidIdtException)
200 {
201 // If ValidateRows finds anything it doesn't like, it throws
202 importTable.ValidateRows();
203 180
204 // Otherwise we rethrow the InvalidIdt 181 row[9] = columnDef.Description;
205 throw; 182 }
206 } 183 }
184 }
185 }
186
187 private void ImportTables(Database db, string idtDirectory)
188 {
189 foreach (var table in this.Data.Tables)
190 {
191 var importTable = table;
192 var hasBinaryColumn = false;
193
194 // Skip all unreal tables other than _Streams.
195 if (table.Definition.Unreal && "_Streams" != table.Name)
196 {
197 continue;
198 }
199
200 // Do not put the _Validation table in patches, it is not needed.
201 if (OutputType.Patch == this.Data.Type && "_Validation" == table.Name)
202 {
203 continue;
204 }
205
206 // The only way to import binary data is to copy it to a local subdirectory first.
207 // To avoid this extra copying and perf hit, import an empty table with the same
208 // definition and later import the binary data from source using records.
209 foreach (var columnDefinition in table.Definition.Columns)
210 {
211 if (ColumnType.Object == columnDefinition.Type)
212 {
213 importTable = new Table(table.Definition);
214 hasBinaryColumn = true;
215 break;
216 }
217 }
218
219 // Create the table via IDT import.
220 if ("_Streams" != importTable.Name)
221 {
222 try
223 {
224 var command = new CreateIdtFileCommand(this.Messaging, importTable, this.Data.Codepage, idtDirectory, this.KeepAddedColumns);
225 command.Execute();
226
227 var buildOutput = this.BackendHelper.TrackFile(command.IdtPath, TrackedFileType.Temporary);
228 this.GeneratedTemporaryFiles.Add(buildOutput);
229
230 db.Import(command.IdtPath);
231 }
232 catch (WixInvalidIdtException)
233 {
234 // If ValidateRows finds anything it doesn't like, it throws
235 importTable.ValidateRows();
236
237 // Otherwise we rethrow the InvalidIdt
238 throw;
239 }
240 }
241
242 // insert the rows via SQL query if this table contains object fields
243 if (hasBinaryColumn)
244 {
245 var query = new StringBuilder("SELECT ");
246
247 // Build the query for the view.
248 var firstColumn = true;
249 foreach (var columnDefinition in table.Definition.Columns)
250 {
251 if (columnDefinition.Unreal)
252 {
253 continue;
207 } 254 }
208 255
209 // insert the rows via SQL query if this table contains object fields 256 if (!firstColumn)
210 if (hasBinaryColumn)
211 { 257 {
212 StringBuilder query = new StringBuilder("SELECT "); 258 query.Append(",");
259 }
213 260
214 // Build the query for the view. 261 query.AppendFormat(" `{0}`", columnDefinition.Name);
215 bool firstColumn = true; 262 firstColumn = false;
216 foreach (ColumnDefinition columnDefinition in table.Definition.Columns) 263 }
264 query.AppendFormat(" FROM `{0}`", table.Name);
265
266 using (var tableView = db.OpenExecuteView(query.ToString()))
267 {
268 // Import each row containing a stream
269 foreach (var row in table.Rows)
270 {
271 using (var record = new Record(table.Definition.Columns.Length))
217 { 272 {
218 if (!firstColumn) 273 // Stream names are created by concatenating the name of the table with the values
274 // of the primary key (delimited by periods).
275 var streamName = new StringBuilder();
276
277 // the _Streams table doesn't prepend the table name (or a period)
278 if ("_Streams" != table.Name)
219 { 279 {
220 query.Append(","); 280 streamName.Append(table.Name);
221 } 281 }
222 282
223 query.AppendFormat(" `{0}`", columnDefinition.Name); 283 var needStream = false;
224 firstColumn = false;
225 }
226 query.AppendFormat(" FROM `{0}`", table.Name);
227 284
228 using (View tableView = db.OpenExecuteView(query.ToString())) 285 for (var i = 0; i < table.Definition.Columns.Length; i++)
229 {
230 // Import each row containing a stream
231 foreach (Row row in table.Rows)
232 { 286 {
233 using (Record record = new Record(table.Definition.Columns.Length)) 287 var columnDefinition = table.Definition.Columns[i];
288
289 if (columnDefinition.Unreal)
234 { 290 {
235 StringBuilder streamName = new StringBuilder(); 291 continue;
236 bool needStream = false; 292 }
293
294 switch (columnDefinition.Type)
295 {
296 case ColumnType.Localized:
297 case ColumnType.Preserved:
298 case ColumnType.String:
299 var str = row.FieldAsString(i);
237 300
238 // the _Streams table doesn't prepend the table name (or a period) 301 if (columnDefinition.PrimaryKey)
239 if ("_Streams" != table.Name) 302 {
240 { 303 if (0 < streamName.Length)
241 streamName.Append(table.Name); 304 {
242 } 305 streamName.Append(".");
306 }
307
308 streamName.Append(str);
309 }
243 310
244 for (int i = 0; i < table.Definition.Columns.Length; i++) 311 record.SetString(i + 1, str);
245 { 312 break;
246 ColumnDefinition columnDefinition = table.Definition.Columns[i]; 313 case ColumnType.Number:
314 record.SetInteger(i + 1, row.FieldAsInteger(i));
315 break;
247 316
248 switch (columnDefinition.Type) 317 case ColumnType.Object:
318 if (null != row[i])
249 { 319 {
250 case ColumnType.Localized: 320 needStream = true;
251 case ColumnType.Preserved: 321 try
252 case ColumnType.String: 322 {
253 if (columnDefinition.PrimaryKey) 323 record.SetStream(i + 1, row.FieldAsString(i));
324 }
325 catch (Win32Exception e)
326 {
327 if (0xA1 == e.NativeErrorCode) // ERROR_BAD_PATHNAME
254 { 328 {
255 if (0 < streamName.Length) 329 throw new WixException(ErrorMessages.FileNotFound(row.SourceLineNumbers, row.FieldAsString(i)));
256 {
257 streamName.Append(".");
258 }
259 streamName.Append((string)row[i]);
260 } 330 }
261 331 else
262 record.SetString(i + 1, (string)row[i]);
263 break;
264 case ColumnType.Number:
265 record.SetInteger(i + 1, Convert.ToInt32(row[i], CultureInfo.InvariantCulture));
266 break;
267 case ColumnType.Object:
268 if (null != row[i])
269 { 332 {
270 needStream = true; 333 throw new WixException(ErrorMessages.Win32Exception(e.NativeErrorCode, e.Message));
271 try
272 {
273 record.SetStream(i + 1, (string)row[i]);
274 }
275 catch (Win32Exception e)
276 {
277 if (0xA1 == e.NativeErrorCode) // ERROR_BAD_PATHNAME
278 {
279 throw new WixException(ErrorMessages.FileNotFound(row.SourceLineNumbers, (string)row[i]));
280 }
281 else
282 {
283 throw new WixException(ErrorMessages.Win32Exception(e.NativeErrorCode, e.Message));
284 }
285 }
286 } 334 }
287 break; 335 }
288 } 336 }
289 } 337 break;
290
291 // stream names are created by concatenating the name of the table with the values
292 // of the primary key (delimited by periods)
293 // check for a stream name that is more than 62 characters long (the maximum allowed length)
294 if (needStream && MsiInterop.MsiMaxStreamNameLength < streamName.Length)
295 {
296 this.Messaging.Write(ErrorMessages.StreamNameTooLong(row.SourceLineNumbers, table.Name, streamName.ToString(), streamName.Length));
297 }
298 else // add the row to the database
299 {
300 tableView.Modify(ModifyView.Assign, record);
301 }
302 } 338 }
303 } 339 }
304 }
305
306 // Remove rows from the _Streams table for wixpdbs.
307 if ("_Streams" == table.Name)
308 {
309 table.Rows.Clear();
310 }
311 }
312 }
313 340
314 // Insert substorages (usually transforms inside a patch or instance transforms in a package). 341 // check for a stream name that is more than 62 characters long (the maximum allowed length)
315 if (0 < this.Output.SubStorages.Count) 342 if (needStream && MsiInterop.MsiMaxStreamNameLength < streamName.Length)
316 {
317 using (View storagesView = new View(db, "SELECT `Name`, `Data` FROM `_Storages`"))
318 {
319 foreach (SubStorage subStorage in this.Output.SubStorages)
320 {
321 string transformFile = Path.Combine(this.IntermediateFolder, String.Concat(subStorage.Name, ".mst"));
322
323 // Bind the transform.
324 this.BindTransform(subStorage.Data, transformFile);
325
326 if (this.Messaging.EncounteredError)
327 { 343 {
328 continue; 344 this.Messaging.Write(ErrorMessages.StreamNameTooLong(row.SourceLineNumbers, table.Name, streamName.ToString(), streamName.Length));
329 } 345 }
330 346 else // add the row to the database
331 // add the storage
332 using (Record record = new Record(2))
333 { 347 {
334 record.SetString(1, subStorage.Name); 348 tableView.Modify(ModifyView.Assign, record);
335 record.SetStream(2, transformFile);
336 storagesView.Modify(ModifyView.Assign, record);
337 } 349 }
338 } 350 }
339 } 351 }
340 } 352 }
341 353
342 // We're good, commit the changes to the new database. 354 // Remove rows from the _Streams table for wixpdbs.
343 db.Commit(); 355 if ("_Streams" == table.Name)
356 {
357 table.Rows.Clear();
358 }
344 } 359 }
345 } 360 }
346 catch (IOException e)
347 {
348 // TODO: this error message doesn't seem specific enough
349 throw new WixException(ErrorMessages.FileNotFound(new SourceLineNumber(this.OutputPath), this.OutputPath), e);
350 }
351 } 361 }
352 362
353 private void BindTransform(WindowsInstallerData transform, string outputPath) 363 private void ImportSubStorages(Database db)
354 { 364 {
355 var command = new BindTransformCommand(); 365 if (0 < this.Data.SubStorages.Count)
356 command.Messaging = this.Messaging; 366 {
357 command.Extensions = this.Extensions; 367 using (var storagesView = new View(db, "SELECT `Name`, `Data` FROM `_Storages`"))
358 command.TempFilesLocation = this.IntermediateFolder; 368 {
359 command.Transform = transform; 369 foreach (var subStorage in this.Data.SubStorages)
360 command.OutputPath = outputPath; 370 {
361 command.TableDefinitions = this.TableDefinitions; 371 var transformFile = Path.Combine(this.IntermediateFolder, String.Concat(subStorage.Name, ".mst"));
362 command.Execute(); 372
373 // Bind the transform.
374 var command = new BindTransformCommand(this.Messaging, this.BackendHelper, this.Extensions, this.IntermediateFolder, subStorage.Data, transformFile, this.TableDefinitions);
375 command.Execute();
376
377 if (this.Messaging.EncounteredError)
378 {
379 continue;
380 }
381
382 // Add the storage to the database.
383 using (var record = new Record(2))
384 {
385 record.SetString(1, subStorage.Name);
386 record.SetStream(2, transformFile);
387 storagesView.Modify(ModifyView.Assign, record);
388 }
389 }
390 }
391 }
363 } 392 }
364 393
365 private void SetDatabaseCodepage(Database db, int codepage, string idtDirectory) 394 private void SetDatabaseCodepage(Database db, int codepage, string idtFolder)
366 { 395 {
367 // write out the _ForceCodepage IDT file 396 // Write out the _ForceCodepage IDT file.
368 var idtPath = Path.Combine(idtDirectory, "_ForceCodepage.idt"); 397 var idtPath = Path.Combine(idtFolder, "_ForceCodepage.idt");
369 using (var idtFile = new StreamWriter(idtPath, false, Encoding.ASCII)) 398 using (var idtFile = new StreamWriter(idtPath, false, Encoding.ASCII))
370 { 399 {
371 idtFile.WriteLine(); // dummy column name record 400 idtFile.WriteLine(); // dummy column name record
@@ -377,14 +406,14 @@ namespace WixToolset.Core.WindowsInstaller.Bind
377 var trackId = this.BackendHelper.TrackFile(idtPath, TrackedFileType.Temporary); 406 var trackId = this.BackendHelper.TrackFile(idtPath, TrackedFileType.Temporary);
378 this.GeneratedTemporaryFiles.Add(trackId); 407 this.GeneratedTemporaryFiles.Add(trackId);
379 408
380 // try to import the table into the MSI 409 // Try to import the table into the MSI.
381 try 410 try
382 { 411 {
383 db.Import(idtPath); 412 db.Import(idtPath);
384 } 413 }
385 catch (WixInvalidIdtException) 414 catch (WixInvalidIdtException)
386 { 415 {
387 // the IDT should be valid, so an invalid code page was given 416 // The IDT should be valid, so an invalid code page was given.
388 throw new WixException(ErrorMessages.IllegalCodepage(codepage)); 417 throw new WixException(ErrorMessages.IllegalCodepage(codepage));
389 } 418 }
390 } 419 }