/// <summary> /// Merges in any modules to the output database. /// </summary> /// <param name="tempDatabaseFile">The temporary database file.</param> /// <param name="output">Output that specifies database and modules to merge.</param> /// <param name="fileRows">The indexed file rows.</param> /// <param name="suppressedTableNames">The names of tables that are suppressed.</param> /// <remarks>Expects that output's database has already been generated.</remarks> private void MergeModules(string tempDatabaseFile, Output output, FileRowCollection fileRows, StringCollection suppressedTableNames) { Debug.Assert(OutputType.Product == output.Type); Table wixMergeTable = output.Tables["WixMerge"]; Table wixFeatureModulesTable = output.Tables["WixFeatureModules"]; // check for merge rows to see if there is any work to do if (null == wixMergeTable || 0 == wixMergeTable.Rows.Count) { return; } IMsmMerge2 merge = null; bool commit = true; bool logOpen = false; bool databaseOpen = false; string logPath = null; try { merge = NativeMethods.GetMsmMerge(); logPath = Path.Combine(this.TempFilesLocation, "merge.log"); merge.OpenLog(logPath); logOpen = true; merge.OpenDatabase(tempDatabaseFile); databaseOpen = true; // process all the merge rows foreach (WixMergeRow wixMergeRow in wixMergeTable.Rows) { bool moduleOpen = false; try { short mergeLanguage; try { mergeLanguage = Convert.ToInt16(wixMergeRow.Language, CultureInfo.InvariantCulture); } catch (System.FormatException) { this.core.OnMessage(WixErrors.InvalidMergeLanguage(wixMergeRow.SourceLineNumbers, wixMergeRow.Id, wixMergeRow.Language)); continue; } this.core.OnMessage(WixVerboses.OpeningMergeModule(wixMergeRow.SourceFile, mergeLanguage)); merge.OpenModule(wixMergeRow.SourceFile, mergeLanguage); moduleOpen = true; // If there is merge configuration data, create a callback object to contain it all. ConfigurationCallback callback = null; if (!String.IsNullOrEmpty(wixMergeRow.ConfigurationData)) { callback = new ConfigurationCallback(wixMergeRow.ConfigurationData); } // merge the module into the database that's being built this.core.OnMessage(WixVerboses.MergingMergeModule(wixMergeRow.SourceFile)); merge.MergeEx(wixMergeRow.Feature, wixMergeRow.Directory, callback); // connect any non-primary features if (null != wixFeatureModulesTable) { foreach (Row row in wixFeatureModulesTable.Rows) { if (wixMergeRow.Id == (string)row[1]) { this.core.OnMessage(WixVerboses.ConnectingMergeModule(wixMergeRow.SourceFile, (string)row[0])); merge.Connect((string)row[0]); } } } } catch (COMException) { commit = false; } finally { IMsmErrors mergeErrors = merge.Errors; // display all the errors encountered during the merge operations for this module for (int i = 1; i <= mergeErrors.Count; i++) { IMsmError mergeError = mergeErrors[i]; StringBuilder databaseKeys = new StringBuilder(); StringBuilder moduleKeys = new StringBuilder(); // build a string of the database keys for (int j = 1; j <= mergeError.DatabaseKeys.Count; j++) { if (1 != j) { databaseKeys.Append(';'); } databaseKeys.Append(mergeError.DatabaseKeys[j]); } // build a string of the module keys for (int j = 1; j <= mergeError.ModuleKeys.Count; j++) { if (1 != j) { moduleKeys.Append(';'); } moduleKeys.Append(mergeError.ModuleKeys[j]); } // display the merge error based on the msm error type switch (mergeError.Type) { case MsmErrorType.msmErrorExclusion: this.core.OnMessage(WixErrors.MergeExcludedModule(wixMergeRow.SourceLineNumbers, wixMergeRow.Id, moduleKeys.ToString())); break; case MsmErrorType.msmErrorFeatureRequired: this.core.OnMessage(WixErrors.MergeFeatureRequired(wixMergeRow.SourceLineNumbers, mergeError.ModuleTable, moduleKeys.ToString(), wixMergeRow.SourceFile, wixMergeRow.Id)); break; case MsmErrorType.msmErrorLanguageFailed: this.core.OnMessage(WixErrors.MergeLanguageFailed(wixMergeRow.SourceLineNumbers, mergeError.Language, wixMergeRow.SourceFile)); break; case MsmErrorType.msmErrorLanguageUnsupported: this.core.OnMessage(WixErrors.MergeLanguageUnsupported(wixMergeRow.SourceLineNumbers, mergeError.Language, wixMergeRow.SourceFile)); break; case MsmErrorType.msmErrorResequenceMerge: this.core.OnMessage(WixWarnings.MergeRescheduledAction(wixMergeRow.SourceLineNumbers, mergeError.DatabaseTable, databaseKeys.ToString(), wixMergeRow.SourceFile)); break; case MsmErrorType.msmErrorTableMerge: if ("_Validation" != mergeError.DatabaseTable) // ignore merge errors in the _Validation table { this.core.OnMessage(WixWarnings.MergeTableFailed(wixMergeRow.SourceLineNumbers, mergeError.DatabaseTable, databaseKeys.ToString(), wixMergeRow.SourceFile)); } break; case MsmErrorType.msmErrorPlatformMismatch: this.core.OnMessage(WixErrors.MergePlatformMismatch(wixMergeRow.SourceLineNumbers, wixMergeRow.SourceFile)); break; default: this.core.OnMessage(WixErrors.UnexpectedException(String.Format(CultureInfo.CurrentUICulture, WixStrings.EXP_UnexpectedMergerErrorWithType, Enum.GetName(typeof(MsmErrorType), mergeError.Type), logPath), "InvalidOperationException", Environment.StackTrace)); break; } } if (0 >= mergeErrors.Count && !commit) { this.core.OnMessage(WixErrors.UnexpectedException(String.Format(CultureInfo.CurrentUICulture, WixStrings.EXP_UnexpectedMergerErrorInSourceFile, wixMergeRow.SourceFile, logPath), "InvalidOperationException", Environment.StackTrace)); } if (moduleOpen) { merge.CloseModule(); } } } } finally { if (databaseOpen) { merge.CloseDatabase(commit); } if (logOpen) { merge.CloseLog(); } } // stop processing if an error previously occurred if (this.core.EncounteredError) { return; } using (Database db = new Database(tempDatabaseFile, OpenDatabase.Direct)) { Table suppressActionTable = output.Tables["WixSuppressAction"]; // suppress individual actions if (null != suppressActionTable) { foreach (Row row in suppressActionTable.Rows) { if (db.TableExists((string)row[0])) { string query = String.Format(CultureInfo.InvariantCulture, "SELECT * FROM {0} WHERE `Action` = '{1}'", row[0].ToString(), (string)row[1]); using (View view = db.OpenExecuteView(query)) { using (Record record = view.Fetch()) { if (null != record) { this.core.OnMessage(WixWarnings.SuppressMergedAction((string)row[1], row[0].ToString())); view.Modify(ModifyView.Delete, record); } } } } } } // query for merge module actions in suppressed sequences and drop them foreach (string tableName in suppressedTableNames) { if (!db.TableExists(tableName)) { continue; } using (View view = db.OpenExecuteView(String.Concat("SELECT `Action` FROM ", tableName))) { while (true) { using (Record resultRecord = view.Fetch()) { if (null == resultRecord) { break; } this.core.OnMessage(WixWarnings.SuppressMergedAction(resultRecord.GetString(1), tableName)); } } } // drop suppressed sequences using (View view = db.OpenExecuteView(String.Concat("DROP TABLE ", tableName))) { } // delete the validation rows using (View view = db.OpenView(String.Concat("DELETE FROM _Validation WHERE `Table` = ?"))) { using (Record record = new Record(1)) { record.SetString(1, tableName); view.Execute(record); } } } // now update the Attributes column for the files from the Merge Modules this.core.OnMessage(WixVerboses.ResequencingMergeModuleFiles()); using (View view = db.OpenView("SELECT `Sequence`, `Attributes` FROM `File` WHERE `File`=?")) { foreach (FileRow fileRow in fileRows) { if (!fileRow.FromModule) { continue; } using (Record record = new Record(1)) { record.SetString(1, fileRow.File); view.Execute(record); } using (Record recordUpdate = view.Fetch()) { if (null == recordUpdate) { throw new InvalidOperationException("Failed to fetch a File row from the database that was merged in from a module."); } recordUpdate.SetInteger(1, fileRow.Sequence); // update the file attributes to match the compression specified // on the Merge element or on the Package element int attributes = 0; // get the current value if its not null if (!recordUpdate.IsNull(2)) { attributes = recordUpdate.GetInteger(2); } if (YesNoType.Yes == fileRow.Compressed) { // these are mutually exclusive attributes |= MsiInterop.MsidbFileAttributesCompressed; attributes &= ~MsiInterop.MsidbFileAttributesNoncompressed; } else if (YesNoType.No == fileRow.Compressed) { // these are mutually exclusive attributes |= MsiInterop.MsidbFileAttributesNoncompressed; attributes &= ~MsiInterop.MsidbFileAttributesCompressed; } else // not specified { Debug.Assert(YesNoType.NotSet == fileRow.Compressed); // clear any compression bits attributes &= ~MsiInterop.MsidbFileAttributesCompressed; attributes &= ~MsiInterop.MsidbFileAttributesNoncompressed; } recordUpdate.SetInteger(2, attributes); view.Modify(ModifyView.Update, recordUpdate); } } } db.Commit(); } }
// This code configures Web API. The Startup class is specified as a type // parameter in the WebApp.Start method. public void Configuration(IAppBuilder appBuilder) { // Configure Web API for self-host. HttpConfiguration config = new HttpConfiguration(); config.Routes.MapHttpRoute( name: "DefaultApi", routeTemplate: "api/v{version}/{controller}/{id}", defaults: new { id = RouteParameter.Optional } ); config.Services.Replace(typeof(IHttpControllerSelector), new VersionedApiControllerSelector(config)); config.DependencyResolver = new DependencyResolver(); if (ConfigurationCallback != null) { ConfigurationCallback.Invoke(config); } DependencyContainer.Register((c, np) => new DefaultControllerIdentificationDetector(config)); DependencyContainer.Register((c, np) => new DefaultRequestControllerIdentificationDetector(config)); appBuilder.UseWebApi(config); }
/// <summary> /// Merges in any modules to the output database. /// </summary> /// <param name="databasePath">Path to database.</param> /// <param name="output">Output that specifies database and modules to merge.</param> /// <remarks>Expects that output's database has already been generated.</remarks> private void MergeModules(string databasePath, Output output) { Debug.Assert(OutputType.Product == output.Type); if (0 == output.Modules.Count) // no work to do { return; } IMsmMerge2 merge = null; bool commit = false; bool logOpen = false; bool databaseOpen = false; bool moduleOpen = false; try { bool foundError = false; MsmMerge msm = new MsmMerge(); merge = (IMsmMerge2)msm; merge.OpenLog(String.Concat(this.tempFiles.BasePath, Path.DirectorySeparatorChar, "merge.log")); logOpen = true; merge.OpenDatabase(databasePath); databaseOpen = true; // process all the merge rows foreach (MergeRow mergeRow in output.Modules) { string mergeModulePath = null; try { mergeModulePath = this.extension.FileResolutionHandler(mergeRow.SourceFile, FileResolutionType.Module); } catch (WixFileNotFoundException wfnfe) { this.OnMessage(WixErrors.BinderExtensionMissingFile(mergeRow.SourceLineNumbers, ErrorLevel.Normal, wfnfe.Message)); foundError = true; continue; } try { merge.OpenModule(mergeModulePath, mergeRow.Language); } catch (COMException ce) { if (-2147023273 == ce.ErrorCode) // 0x80070657 - ERROR_INSTALL_LANGUAGE_UNSUPPORTED { throw new WixUnknownMergeLanguageException(mergeRow.SourceLineNumbers, mergeRow.Id, mergeModulePath, mergeRow.Language, ce); } else { throw; } } moduleOpen = true; ConnectToFeature connection = output.ModulesToFeatures[mergeRow.Id]; if (null == connection) { throw new WixMergeModuleMissingFeatureException(mergeRow.SourceLineNumbers, mergeRow.Id); } string configData = mergeRow.ConfigurationData; if (null != configData) { ConfigurationCallback callback = new ConfigurationCallback(configData); merge.MergeEx(connection.PrimaryFeature, mergeRow.Directory, callback); } else { merge.Merge(connection.PrimaryFeature, mergeRow.Directory); } /* IMsmErrors errorCollection = null; merge.get_Errors(out errorCollection); long count = errorCollection.get_Count(); if (0 < count) { throw new WixMergeFailureException(null, this.tempFiles.BasePath, count, null); } */ foreach (string connectTo in connection.ConnectFeatures) { merge.Connect(connectTo); } // if the module has files and creating layout if (mergeRow.HasFiles && !this.suppressLayout) { string hashedMergeId = mergeRow.Id.GetHashCode().ToString("X4", CultureInfo.InvariantCulture.NumberFormat); // extract the module cabinet, then explode all of the files to a temp directory string moduleCabPath = String.Concat(this.tempFiles.BasePath, Path.DirectorySeparatorChar, hashedMergeId, ".module.cab"); merge.ExtractCAB(moduleCabPath); string mergeIdPath = String.Concat(this.tempFiles.BasePath, Path.DirectorySeparatorChar, "MergeId.", hashedMergeId); Directory.CreateDirectory(mergeIdPath); WixExtractCab extCab = null; try { extCab = new WixExtractCab(); extCab.Extract(moduleCabPath, mergeIdPath); } catch (WixCabExtractionException wce) { COMException comException = wce.InnerException as COMException; foundError = true; if (null != comException && 0x80070002 == unchecked((uint)comException.ErrorCode)) { extCab = null; // Cab doesn't exist, so drop the object. this.OnMessage(WixErrors.CabFileDoesNotExist(moduleCabPath, mergeModulePath, mergeIdPath)); } else { this.OnMessage(WixErrors.CabExtractionFailed(moduleCabPath, mergeModulePath, mergeIdPath)); } } finally { if (null != extCab) { try { extCab.Close(); } catch (WixCabExtractionException) { this.OnMessage(WixErrors.CabClosureFailed(moduleCabPath)); } } } } moduleOpen = false; merge.CloseModule(); } commit = !foundError; // if all seems to have progressed cleanly, feel free to commit the changes to the database } finally { if (moduleOpen) { merge.CloseModule(); } if (databaseOpen) { merge.CloseDatabase(commit); } if (logOpen) { merge.CloseLog(); } } // create a Hashtable of all the suppressed sequence types Hashtable suppressedTableNames = new Hashtable(); if (output.SuppressAdminSequence) { suppressedTableNames[Action.SequenceTypeToString(SequenceType.adminExecute)] = null; suppressedTableNames[Action.SequenceTypeToString(SequenceType.adminUI)] = null; } if (output.SuppressAdvertiseSequence) { suppressedTableNames[Action.SequenceTypeToString(SequenceType.advertiseExecute)] = null; } if (output.SuppressUISequence) { suppressedTableNames[Action.SequenceTypeToString(SequenceType.adminUI)] = null; suppressedTableNames[Action.SequenceTypeToString(SequenceType.installUI)] = null; } using (Database db = new Database(databasePath, OpenDatabase.Direct)) { OutputTable suppressActionOutputTable = output.OutputTables["SuppressAction"]; // suppress individual actions if (null != suppressActionOutputTable) { foreach (OutputRow outputRow in suppressActionOutputTable.OutputRows) { if (db.TableExists((string)outputRow.Row[0])) { Row row = outputRow.Row; string query = String.Format("SELECT * FROM {0} WHERE `Action` = '{1}'", row[0].ToString(), (string)row[1]); using (View view = db.OpenExecuteView(query)) { Record record; if (view.Fetch(out record)) { this.OnMessage(WixWarnings.SuppressMergedAction((string)row[1], row[0].ToString())); view.Modify(ModifyView.Delete, record); record.Close(); } } } } } // query for merge module actions in suppressed sequences and drop them foreach (string tableName in suppressedTableNames.Keys) { if (!db.TableExists(tableName)) { continue; } using (View view = db.OpenExecuteView(String.Concat("SELECT `Action` FROM ", tableName))) { Record resultRecord; while (view.Fetch(out resultRecord)) { this.OnMessage(WixWarnings.SuppressMergedAction(resultRecord.GetString(1), tableName)); resultRecord.Close(); } } // drop suppressed sequences using (View view = db.OpenExecuteView(String.Concat("DROP TABLE ", tableName))) { } // delete the validation rows using (View view = db.OpenView(String.Concat("DELETE FROM _Validation WHERE `Table` = ?"))) { Record record = new Record(1); record.SetString(1, tableName); view.Execute(record); } } // now update the Attributes column for the files from the Merge Modules using (View view = db.OpenView("SELECT `Sequence`, `Attributes` FROM `File` WHERE `File`=?")) { foreach (FileMediaInformation fmi in output.FileMediaInformationCollection) { if (!fmi.IsInModule) { continue; } Record record = new Record(1); record.SetString(1, fmi.File); view.Execute(record); Record recordUpdate; view.Fetch(out recordUpdate); if (null == recordUpdate) { throw new WixMergeFailureException(null, this.tempFiles.BasePath, 1, null); } recordUpdate.SetInteger(1, fmi.Sequence); // update the file attributes to match the compression specified // on the Merge element or on the Package element int attributes = 0; // get the current value if its not null if (!recordUpdate.IsNull(2)) { attributes = recordUpdate.GetInteger(2); } if (FileCompressionValue.Yes == fmi.FileCompression) { attributes |= MsiInterop.MsidbFileAttributesCompressed; } else if (FileCompressionValue.No == fmi.FileCompression) { attributes |= MsiInterop.MsidbFileAttributesNoncompressed; } else // not specified { Debug.Assert(FileCompressionValue.NotSpecified == fmi.FileCompression); // clear any compression bits attributes &= ~MsiInterop.MsidbFileAttributesCompressed; attributes &= ~MsiInterop.MsidbFileAttributesNoncompressed; } recordUpdate.SetInteger(2, attributes); view.Modify(ModifyView.Update, recordUpdate); } } db.Commit(); } }