/// <summary> /// Checks whether a transform is valid for this Database, according to its validation data and flags. /// </summary> /// <param name="transformFile">Path to the transform file</param> /// <returns>true if the transform can be validly applied to this Database; false otherwise</returns> /// <exception cref="InstallerException">the transform could not be applied</exception> /// <exception cref="InvalidHandleException">the Database handle is invalid</exception> public bool IsTransformValid(string transformFile) { if (String.IsNullOrEmpty(transformFile)) { throw new ArgumentNullException("transformFile"); } using (SummaryInfo transformSummInfo = new SummaryInfo(transformFile, false)) { return(this.IsTransformValid(transformSummInfo)); } }
/// <summary> /// Closes the database handle. After closing a handle, further method calls may throw <see cref="InvalidHandleException"/>. /// </summary> /// <param name="disposing">If true, the method has been called directly or /// indirectly by a user's code, so managed and unmanaged resources will be /// disposed. If false, only unmanaged resources will be disposed.</param> protected override void Dispose(bool disposing) { if (!this.IsClosed && (this.OpenMode == DatabaseOpenMode.CreateDirect || this.OpenMode == DatabaseOpenMode.Direct)) { // Always commit a direct-opened database before closing. // This avoids unexpected corruption of the database. this.Commit(); } base.Dispose(disposing); if (disposing) { if (this.summaryInfo != null) { this.summaryInfo.Close(); this.summaryInfo = null; } if (this.deleteOnClose != null) { foreach (string path in this.deleteOnClose) { try { if (Directory.Exists(path)) { Directory.Delete(path, true); } else { if (File.Exists(path)) { File.Delete(path); } } } catch (IOException) { } catch (UnauthorizedAccessException) { } } this.deleteOnClose = null; } } }
/// <summary> /// Finalizes the persistent form of the database. All persistent data is written /// to the writeable database, and no temporary columns or rows are written. /// </summary> /// <exception cref="InvalidHandleException">the Database handle is invalid</exception> /// <remarks><p> /// For a database open in <see cref="DatabaseOpenMode.ReadOnly"/> mode, this method has no effect. /// </p><p> /// For a database open in <see cref="DatabaseOpenMode.CreateDirect" /> or <see cref="DatabaseOpenMode.Direct" /> /// mode, it is not necessary to call this method because the database will be automatically committed /// when it is closed. However this method may be called at any time to persist the current state of tables /// loaded into memory. /// </p><p> /// For a database open in <see cref="DatabaseOpenMode.Create" /> or <see cref="DatabaseOpenMode.Transact" /> /// mode, no changes will be persisted until this method is called. If the database object is closed without /// calling this method, the database file remains unmodified. /// </p><p> /// Win32 MSI API: /// <a href="http://msdn.microsoft.com/library/en-us/msi/setup/msidatabasecommit.asp">MsiDatabaseCommit</a> /// </p></remarks> public void Commit() { if (this.summaryInfo != null && !this.summaryInfo.IsClosed) { this.summaryInfo.Persist(); this.summaryInfo.Close(); this.summaryInfo = null; } uint ret = NativeMethods.MsiDatabaseCommit((int)this.Handle); if (ret != 0) { throw InstallerException.ExceptionFromReturnCode(ret); } }
/// <summary> /// Apply a transform to the database, suppressing any error conditions /// specified by the transform's summary information. /// </summary> /// <param name="transformFile">Path to the transform file</param> /// <exception cref="InstallerException">the transform could not be applied</exception> /// <exception cref="InvalidHandleException">the Database handle is invalid</exception> /// <remarks><p> /// Win32 MSI API: /// <a href="http://msdn.microsoft.com/library/en-us/msi/setup/msidatabaseapplytransform.asp">MsiDatabaseApplyTransform</a> /// </p></remarks> public void ApplyTransform(string transformFile) { if (String.IsNullOrEmpty(transformFile)) { throw new ArgumentNullException("transformFile"); } TransformErrors errorConditionsToSuppress; using (SummaryInfo transformSummInfo = new SummaryInfo(transformFile, false)) { int errorConditions = transformSummInfo.CharacterCount & 0xFFFF; errorConditionsToSuppress = (TransformErrors)errorConditions; } this.ApplyTransform(transformFile, errorConditionsToSuppress); }
/// <summary> /// Processes the Msp packages to add properties and payloads from the Msp packages. /// </summary> public void Execute() { var packagePayload = this.AuthoredPayloads[this.Facade.PackageSymbol.PayloadRef]; var mspPackage = (WixBundleMspPackageSymbol)this.Facade.SpecificPackageSymbol; var sourcePath = packagePayload.SourceFile.Path; try { // Read data out of the msp database... using (var sumInfo = new Dtf.SummaryInfo(sourcePath, false)) { mspPackage.PatchCode = sumInfo.RevisionNumber.Substring(0, 38); } using (var db = new Dtf.Database(sourcePath)) { if (String.IsNullOrEmpty(this.Facade.PackageSymbol.DisplayName)) { this.Facade.PackageSymbol.DisplayName = ProcessMspPackageCommand.GetPatchMetadataProperty(db, "DisplayName"); } if (String.IsNullOrEmpty(this.Facade.PackageSymbol.Description)) { this.Facade.PackageSymbol.Description = ProcessMspPackageCommand.GetPatchMetadataProperty(db, "Description"); } mspPackage.Manufacturer = ProcessMspPackageCommand.GetPatchMetadataProperty(db, "ManufacturerName"); } this.ProcessPatchXml(packagePayload, mspPackage, sourcePath); } catch (Dtf.InstallerException e) { this.Messaging.Write(ErrorMessages.UnableToReadPackageInformation(packagePayload.SourceLineNumbers, sourcePath, e.Message)); return; } if (String.IsNullOrEmpty(this.Facade.PackageSymbol.CacheId)) { this.Facade.PackageSymbol.CacheId = mspPackage.PatchCode; } }
/// <summary> /// Processes the Msp packages to add properties and payloads from the Msp packages. /// </summary> public void Execute() { WixBundlePayloadRow packagePayload = this.AuthoredPayloads.Get(this.Facade.Package.PackagePayload); string sourcePath = packagePayload.FullFileName; try { // Read data out of the msp database... using (Dtf.SummaryInfo sumInfo = new Dtf.SummaryInfo(sourcePath, false)) { this.Facade.MspPackage.PatchCode = sumInfo.RevisionNumber.Substring(0, 38); } using (Dtf.Database db = new Dtf.Database(sourcePath)) { if (String.IsNullOrEmpty(this.Facade.Package.DisplayName)) { this.Facade.Package.DisplayName = ProcessMspPackageCommand.GetPatchMetadataProperty(db, "DisplayName"); } if (String.IsNullOrEmpty(this.Facade.Package.Description)) { this.Facade.Package.Description = ProcessMspPackageCommand.GetPatchMetadataProperty(db, "Description"); } this.Facade.MspPackage.Manufacturer = ProcessMspPackageCommand.GetPatchMetadataProperty(db, "ManufacturerName"); } this.ProcessPatchXml(packagePayload, sourcePath); } catch (Dtf.InstallerException e) { Messaging.Instance.OnMessage(WixErrors.UnableToReadPackageInformation(packagePayload.SourceLineNumbers, sourcePath, e.Message)); return; } if (String.IsNullOrEmpty(this.Facade.Package.CacheId)) { this.Facade.Package.CacheId = this.Facade.MspPackage.PatchCode; } }
/// <summary> /// Processes the MSI packages to add properties and payloads from the MSI packages. /// </summary> public void Execute() { WixBundlePayloadRow packagePayload = this.AuthoredPayloads.Get(this.Facade.Package.PackagePayload); string sourcePath = packagePayload.FullFileName; bool longNamesInImage = false; bool compressed = false; bool x64 = false; try { // Read data out of the msi database... using (Dtf.SummaryInfo sumInfo = new Dtf.SummaryInfo(sourcePath, false)) { // 1 is the Word Count summary information stream bit that means // the MSI uses short file names when set. We care about long file // names so check when the bit is not set. longNamesInImage = 0 == (sumInfo.WordCount & 1); // 2 is the Word Count summary information stream bit that means // files are compressed in the MSI by default when the bit is set. compressed = 2 == (sumInfo.WordCount & 2); x64 = (sumInfo.Template.Contains("x64") || sumInfo.Template.Contains("Intel64")); // 8 is the Word Count summary information stream bit that means // "Elevated privileges are not required to install this package." // in MSI 4.5 and below, if this bit is 0, elevation is required. this.Facade.Package.PerMachine = (0 == (sumInfo.WordCount & 8)) ? YesNoDefaultType.Yes : YesNoDefaultType.No; this.Facade.Package.x64 = x64 ? YesNoType.Yes : YesNoType.No; } using (Dtf.Database db = new Dtf.Database(sourcePath)) { this.Facade.MsiPackage.ProductCode = ProcessMsiPackageCommand.GetProperty(db, "ProductCode"); this.Facade.MsiPackage.UpgradeCode = ProcessMsiPackageCommand.GetProperty(db, "UpgradeCode"); this.Facade.MsiPackage.Manufacturer = ProcessMsiPackageCommand.GetProperty(db, "Manufacturer"); this.Facade.MsiPackage.ProductLanguage = Convert.ToInt32(ProcessMsiPackageCommand.GetProperty(db, "ProductLanguage"), CultureInfo.InvariantCulture); this.Facade.MsiPackage.ProductVersion = ProcessMsiPackageCommand.GetProperty(db, "ProductVersion"); if (!Common.IsValidModuleOrBundleVersion(this.Facade.MsiPackage.ProductVersion)) { // not a proper .NET version (e.g., five fields); can we get a valid four-part version number? string version = null; string[] versionParts = this.Facade.MsiPackage.ProductVersion.Split('.'); int count = versionParts.Length; if (0 < count) { version = versionParts[0]; for (int i = 1; i < 4 && i < count; ++i) { version = String.Concat(version, ".", versionParts[i]); } } if (!String.IsNullOrEmpty(version) && Common.IsValidModuleOrBundleVersion(version)) { Messaging.Instance.OnMessage(WixWarnings.VersionTruncated(this.Facade.Package.SourceLineNumbers, this.Facade.MsiPackage.ProductVersion, sourcePath, version)); this.Facade.MsiPackage.ProductVersion = version; } else { Messaging.Instance.OnMessage(WixErrors.InvalidProductVersion(this.Facade.Package.SourceLineNumbers, this.Facade.MsiPackage.ProductVersion, sourcePath)); } } if (String.IsNullOrEmpty(this.Facade.Package.CacheId)) { this.Facade.Package.CacheId = String.Format("{0}v{1}", this.Facade.MsiPackage.ProductCode, this.Facade.MsiPackage.ProductVersion); } if (String.IsNullOrEmpty(this.Facade.Package.DisplayName)) { this.Facade.Package.DisplayName = ProcessMsiPackageCommand.GetProperty(db, "ProductName"); } if (String.IsNullOrEmpty(this.Facade.Package.Description)) { this.Facade.Package.Description = ProcessMsiPackageCommand.GetProperty(db, "ARPCOMMENTS"); } ISet <string> payloadNames = this.GetPayloadTargetNames(); ISet <string> msiPropertyNames = this.GetMsiPropertyNames(); this.SetPerMachineAppropriately(db, sourcePath); // Ensure the MSI package is appropriately marked visible or not. this.SetPackageVisibility(db, msiPropertyNames); // Unless the MSI or setup code overrides the default, set MSIFASTINSTALL for best performance. if (!msiPropertyNames.Contains("MSIFASTINSTALL") && !ProcessMsiPackageCommand.HasProperty(db, "MSIFASTINSTALL")) { this.AddMsiProperty("MSIFASTINSTALL", "7"); } this.CreateRelatedPackages(db); // If feature selection is enabled, represent the Feature table in the manifest. if (this.Facade.MsiPackage.EnableFeatureSelection) { this.CreateMsiFeatures(db); } // Add all external cabinets as package payloads. this.ImportExternalCabinetAsPayloads(db, packagePayload, payloadNames); // Add all external files as package payloads and calculate the total install size as the rollup of // File table's sizes. this.Facade.Package.InstallSize = this.ImportExternalFileAsPayloadsAndReturnInstallSize(db, packagePayload, longNamesInImage, compressed, payloadNames); // Add all dependency providers from the MSI. this.ImportDependencyProviders(db); } } catch (Dtf.InstallerException e) { Messaging.Instance.OnMessage(WixErrors.UnableToReadPackageInformation(this.Facade.Package.SourceLineNumbers, sourcePath, e.Message)); } }
/// <summary> /// Gets the value of a summary information stream property /// </summary> /// <param name="msi">The MSI to get the property from</param> /// <param name="propertyIndex">The summary information stream property Id of the property to get</param> /// <returns>Summary information stream property</returns> /// <remarks> /// This method reflects on wix.dll to use some of its internal methods for getting Summary Information data /// </remarks> public static string GetSummaryInformationProperty(string msi, int propertyIndex) { using (DTF.SummaryInfo summaryInfo = new DTF.SummaryInfo(msi, false)) { switch (propertyIndex) { case 1: return(summaryInfo.CodePage.ToString()); case 2: return(summaryInfo.Title); case 3: return(summaryInfo.Subject); case 4: return(summaryInfo.Author); case 5: return(summaryInfo.Keywords); case 6: return(summaryInfo.Comments); case 7: return(summaryInfo.Template); case 8: return(summaryInfo.LastSavedBy); case 9: return(summaryInfo.RevisionNumber); case 11: return(summaryInfo.LastPrintTime.ToString()); case 12: return(summaryInfo.CreateTime.ToString()); case 13: return(summaryInfo.LastSaveTime.ToString()); case 14: return(summaryInfo.PageCount.ToString()); case 15: return(summaryInfo.WordCount.ToString()); case 16: return(summaryInfo.CharacterCount.ToString()); case 18: return(summaryInfo.CreatingApp); case 19: return(summaryInfo.Security.ToString()); default: throw new NotImplementedException(String.Format("Unknown summary information property: {0}", propertyIndex)); } } }
/// <summary> /// Checks whether a transform is valid for this Database, according to its SummaryInfo data. /// </summary> /// <param name="transformSummaryInfo">SummaryInfo data of a transform file</param> /// <returns>true if the transform can be validly applied to this Database; false otherwise</returns> /// <exception cref="InstallerException">error processing summary info</exception> /// <exception cref="InvalidHandleException">the Database or SummaryInfo handle is invalid</exception> public bool IsTransformValid(SummaryInfo transformSummaryInfo) { if (transformSummaryInfo == null) { throw new ArgumentNullException("transformSummaryInfo"); } string[] rev = transformSummaryInfo.RevisionNumber.Split(new char[] { ';' }, 3); string targetProductCode = rev[0].Substring(0, 38); string targetProductVersion = rev[0].Substring(38); string upgradeCode = rev[2]; string[] templ = transformSummaryInfo.Template.Split(new char[] { ';' }, 2); int targetProductLanguage = 0; if (templ.Length >= 2 && templ[1].Length > 0) { targetProductLanguage = Int32.Parse(templ[1], CultureInfo.InvariantCulture.NumberFormat); } int flags = transformSummaryInfo.CharacterCount; int validateFlags = flags >> 16; string thisProductCode = this.ExecutePropertyQuery("ProductCode"); string thisProductVersion = this.ExecutePropertyQuery("ProductVersion"); string thisUpgradeCode = this.ExecutePropertyQuery("UpgradeCode"); string thisProductLang = this.ExecutePropertyQuery("ProductLanguage"); int thisProductLanguage = 0; if (!String.IsNullOrEmpty(thisProductLang)) { thisProductLanguage = Int32.Parse(thisProductLang, CultureInfo.InvariantCulture.NumberFormat); } if ((validateFlags & (int)TransformValidations.Product) != 0 && thisProductCode != targetProductCode) { return(false); } if ((validateFlags & (int)TransformValidations.UpgradeCode) != 0 && thisUpgradeCode != upgradeCode) { return(false); } if ((validateFlags & (int)TransformValidations.Language) != 0 && targetProductLanguage != 0 && thisProductLanguage != targetProductLanguage) { return(false); } Version thisProductVer = new Version(thisProductVersion); Version targetProductVer = new Version(targetProductVersion); if ((validateFlags & (int)TransformValidations.UpdateVersion) != 0) { if (thisProductVer.Major != targetProductVer.Major) { return(false); } if (thisProductVer.Minor != targetProductVer.Minor) { return(false); } if (thisProductVer.Build != targetProductVer.Build) { return(false); } } else if ((validateFlags & (int)TransformValidations.MinorVersion) != 0) { if (thisProductVer.Major != targetProductVer.Major) { return(false); } if (thisProductVer.Minor != targetProductVer.Minor) { return(false); } } else if ((validateFlags & (int)TransformValidations.MajorVersion) != 0) { if (thisProductVer.Major != targetProductVer.Major) { return(false); } } return(true); }
/// <summary> /// Gets a SummaryInfo object that can be used to examine, update, and add /// properties to the summary information stream of a package or transform. /// </summary> /// <param name="packagePath">Path to the package (database) or transform</param> /// <param name="enableWrite">True to reserve resources for writing summary information properties.</param> /// <exception cref="FileNotFoundException">the package does not exist or could not be read</exception> /// <exception cref="InstallerException">the package is an invalid format</exception> /// <remarks><p> /// The SummaryInfo object should be <see cref="InstallerHandle.Close"/>d after use. /// It is best that the handle be closed manually as soon as it is no longer /// needed, as leaving lots of unused handles open can degrade performance. /// </p><p> /// Win32 MSI API: /// <a href="http://msdn.microsoft.com/library/en-us/msi/setup/msigetsummaryinformation.asp">MsiGetSummaryInformation</a> /// </p></remarks> public SummaryInfo(string packagePath, bool enableWrite) : base((IntPtr)SummaryInfo.OpenSummaryInfo(packagePath, enableWrite), true) { }