diff options
Diffstat (limited to 'src/WixToolset.Core.WindowsInstaller/Bind/GenerateDatabaseCommand.cs')
| -rw-r--r-- | src/WixToolset.Core.WindowsInstaller/Bind/GenerateDatabaseCommand.cs | 535 |
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 | } |
