/// <summary> /// Initializes package state from the MSP contents. /// </summary> private void ResolveMspPackage(Output bundle) { string sourcePath = this.PackagePayload.FullFileName; try { // Read data out of the msp database... using (Microsoft.Deployment.WindowsInstaller.SummaryInfo sumInfo = new Microsoft.Deployment.WindowsInstaller.SummaryInfo(sourcePath, false)) { this.PatchCode = sumInfo.RevisionNumber.Substring(0, 38); } using (Microsoft.Deployment.WindowsInstaller.Database db = new Microsoft.Deployment.WindowsInstaller.Database(sourcePath)) { if (String.IsNullOrEmpty(this.DisplayName)) { this.DisplayName = ChainPackageInfo.GetPatchMetadataProperty(db, "DisplayName"); } if (String.IsNullOrEmpty(this.Description)) { this.Description = ChainPackageInfo.GetPatchMetadataProperty(db, "Description"); } this.Manufacturer = ChainPackageInfo.GetPatchMetadataProperty(db, "ManufacturerName"); } this.ProcessPatchXml(sourcePath, bundle); } catch (Microsoft.Deployment.WindowsInstaller.InstallerException e) { this.core.OnMessage(WixErrors.UnableToReadPackageInformation(this.PackagePayload.SourceLineNumbers, sourcePath, e.Message)); return; } if (String.IsNullOrEmpty(this.CacheId)) { this.CacheId = this.PatchCode; } }
/// <summary> /// Initializes package state from the MSI contents. /// </summary> private void ResolveMsiPackage(BinderFileManager fileManager, Dictionary<string, PayloadInfoRow> allPayloads, Dictionary<string, ContainerInfo> containers, YesNoType suppressLooseFilePayloadGeneration, YesNoType enableFeatureSelection, YesNoType forcePerMachine, Output bundle) { string sourcePath = this.PackagePayload.FullFileName; bool longNamesInImage = false; bool compressed = false; try { // Read data out of the msi database... using (Microsoft.Deployment.WindowsInstaller.SummaryInfo sumInfo = new Microsoft.Deployment.WindowsInstaller.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); // 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.PerMachine = (0 == (sumInfo.WordCount & 8)) ? YesNoDefaultType.Yes : YesNoDefaultType.No; } using (Microsoft.Deployment.WindowsInstaller.Database db = new Microsoft.Deployment.WindowsInstaller.Database(sourcePath)) { this.ProductCode = ChainPackageInfo.GetProperty(db, "ProductCode"); this.Language = ChainPackageInfo.GetProperty(db, "ProductLanguage"); this.Version = ChainPackageInfo.GetProperty(db, "ProductVersion"); if (!Common.IsValidModuleOrBundleVersion(this.Version)) { // not a proper .NET version (i.e., five fields); can we get a valid version number up to four fields? string version = null; string[] versionParts = this.Version.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)) { this.core.OnMessage(WixWarnings.VersionTruncated(this.PackagePayload.SourceLineNumbers, this.Version, sourcePath, version)); this.Version = version; } else { this.core.OnMessage(WixErrors.InvalidProductVersion(this.PackagePayload.SourceLineNumbers, this.Version, sourcePath)); } } if (String.IsNullOrEmpty(this.CacheId)) { this.CacheId = String.Format("{0}v{1}", this.ProductCode, this.Version); } if (String.IsNullOrEmpty(this.DisplayName)) { this.DisplayName = ChainPackageInfo.GetProperty(db, "ProductName"); } this.Manufacturer = ChainPackageInfo.GetProperty(db, "Manufacturer"); this.VerifyMsiProperties(); if (YesNoType.Yes == forcePerMachine) { if (YesNoDefaultType.No == this.PerMachine) { this.core.OnMessage(WixWarnings.PerUserButForcingPerMachine(this.PackagePayload.SourceLineNumbers, sourcePath)); this.PerMachine = YesNoDefaultType.Yes; // ensure that we think the MSI is per-machine. } this.MsiProperties.Add(new MsiPropertyInfo(this.Id, "ALLUSERS", "1")); // force ALLUSERS=1 via the MSI command-line. } else if (ChainPackageInfo.HasProperty(db, "ALLUSERS")) { string allusers = ChainPackageInfo.GetProperty(db, "ALLUSERS"); if (allusers.Equals("1", StringComparison.Ordinal)) { if (YesNoDefaultType.No == this.PerMachine) { this.core.OnMessage(WixErrors.PerUserButAllUsersEquals1(this.PackagePayload.SourceLineNumbers, sourcePath)); } } else if (allusers.Equals("2", StringComparison.Ordinal)) { this.core.OnMessage(WixWarnings.DiscouragedAllUsersValue(this.PackagePayload.SourceLineNumbers, sourcePath, (YesNoDefaultType.Yes == this.PerMachine) ? "machine" : "user")); } else { this.core.OnMessage(WixErrors.UnsupportedAllUsersValue(this.PackagePayload.SourceLineNumbers, sourcePath, allusers)); } } else if (YesNoDefaultType.Yes == this.PerMachine) // not forced per-machine and no ALLUSERS property, flip back to per-user { this.core.OnMessage(WixWarnings.ImplicitlyPerUser(this.PackagePayload.SourceLineNumbers, sourcePath)); this.PerMachine = YesNoDefaultType.No; } if (String.IsNullOrEmpty(this.Description) && ChainPackageInfo.HasProperty(db, "ARPCOMMENTS")) { this.Description = ChainPackageInfo.GetProperty(db, "ARPCOMMENTS"); } // Ensure the MSI package is appropriately marked visible or not. bool alreadyVisible = !ChainPackageInfo.HasProperty(db, "ARPSYSTEMCOMPONENT"); if (alreadyVisible != this.Visible) // if not already set to the correct visibility. { // If the authoring specifically added "ARPSYSTEMCOMPONENT", don't do it again. bool sysComponentSet = false; foreach (MsiPropertyInfo propertyInfo in this.MsiProperties) { if ("ARPSYSTEMCOMPONENT".Equals(propertyInfo.Name, StringComparison.Ordinal)) { sysComponentSet = true; break; } } if (!sysComponentSet) { this.MsiProperties.Add(new MsiPropertyInfo(this.Id, "ARPSYSTEMCOMPONENT", this.Visible ? "" : "1")); } } // Unless the MSI or setup code overrides the default, set MSIFASTINSTALL for best performance. if (!ChainPackageInfo.HasProperty(db, "MSIFASTINSTALL")) { bool fastInstallSet = false; foreach (MsiPropertyInfo propertyInfo in this.MsiProperties) { if ("MSIFASTINSTALL".Equals(propertyInfo.Name, StringComparison.Ordinal)) { fastInstallSet = true; break; } } if (!fastInstallSet) { this.MsiProperties.Add(new MsiPropertyInfo(this.Id, "MSIFASTINSTALL", "7")); } } this.UpgradeCode = ChainPackageInfo.GetProperty(db, "UpgradeCode"); // Represent the Upgrade table as related packages. if (db.Tables.Contains("Upgrade") && !String.IsNullOrEmpty(this.UpgradeCode)) { using (Microsoft.Deployment.WindowsInstaller.View view = db.OpenView("SELECT `UpgradeCode`, `VersionMin`, `VersionMax`, `Language`, `Attributes` FROM `Upgrade`")) { view.Execute(); while (true) { using (Microsoft.Deployment.WindowsInstaller.Record record = view.Fetch()) { if (null == record) { break; } RelatedPackage related = new RelatedPackage(); related.Id = record.GetString(1); related.MinVersion = record.GetString(2); related.MaxVersion = record.GetString(3); string languages = record.GetString(4); if (!String.IsNullOrEmpty(languages)) { string[] splitLanguages = languages.Split(new char[] { ',' }, StringSplitOptions.RemoveEmptyEntries); related.Languages.AddRange(splitLanguages); } int attributes = record.GetInteger(5); // when an Upgrade row has an upgrade code different than this package's upgrade code, don't count it as a possible downgrade to this package related.OnlyDetect = ((attributes & MsiInterop.MsidbUpgradeAttributesOnlyDetect) == MsiInterop.MsidbUpgradeAttributesOnlyDetect) && this.UpgradeCode.Equals(related.Id, StringComparison.OrdinalIgnoreCase); related.MinInclusive = (attributes & MsiInterop.MsidbUpgradeAttributesVersionMinInclusive) == MsiInterop.MsidbUpgradeAttributesVersionMinInclusive; related.MaxInclusive = (attributes & MsiInterop.MsidbUpgradeAttributesVersionMaxInclusive) == MsiInterop.MsidbUpgradeAttributesVersionMaxInclusive; related.LangInclusive = (attributes & MsiInterop.MsidbUpgradeAttributesLanguagesExclusive) == 0; this.RelatedPackages.Add(related); } } } } // If feature selection is enabled, represent the Feature table in the manifest. if (YesNoType.Yes == enableFeatureSelection && db.Tables.Contains("Feature")) { using (Microsoft.Deployment.WindowsInstaller.View featureView = db.OpenView("SELECT `Component_` FROM `FeatureComponents` WHERE `Feature_` = ?"), componentView = db.OpenView("SELECT `FileSize` FROM `File` WHERE `Component_` = ?")) { using (Microsoft.Deployment.WindowsInstaller.Record featureRecord = new Microsoft.Deployment.WindowsInstaller.Record(1), componentRecord = new Microsoft.Deployment.WindowsInstaller.Record(1)) { using (Microsoft.Deployment.WindowsInstaller.View allFeaturesView = db.OpenView("SELECT * FROM `Feature`")) { allFeaturesView.Execute(); while (true) { using (Microsoft.Deployment.WindowsInstaller.Record allFeaturesResultRecord = allFeaturesView.Fetch()) { if (null == allFeaturesResultRecord) { break; } MsiFeature feature = new MsiFeature(); string featureName = allFeaturesResultRecord.GetString(1); feature.Name = featureName; feature.Size = 0; feature.Parent = allFeaturesResultRecord.GetString(2); feature.Title = allFeaturesResultRecord.GetString(3); feature.Description = allFeaturesResultRecord.GetString(4); feature.Display = allFeaturesResultRecord.GetInteger(5); feature.Level = allFeaturesResultRecord.GetInteger(6); feature.Directory = allFeaturesResultRecord.GetString(7); feature.Attributes = allFeaturesResultRecord.GetInteger(8); this.MsiFeatures.Add(feature); // Determine Feature Size featureRecord.SetString(1, featureName); featureView.Execute(featureRecord); // Loop over all the components while (true) { using (Microsoft.Deployment.WindowsInstaller.Record componentResultRecord = featureView.Fetch()) { if (null == componentResultRecord) { break; } string component = componentResultRecord.GetString(1); componentRecord.SetString(1, component); componentView.Execute(componentRecord); // Loop over all the files while (true) { using (Microsoft.Deployment.WindowsInstaller.Record fileResultRecord = componentView.Fetch()) { if (null == fileResultRecord) { break; } string fileSize = fileResultRecord.GetString(1); feature.Size += Convert.ToInt32(fileSize, CultureInfo.InvariantCulture.NumberFormat); } } } } } } } } } } // Add all external cabinets as package payloads. if (db.Tables.Contains("Media")) { foreach (string cabinet in db.ExecuteStringQuery("SELECT `Cabinet` FROM `Media`")) { if (!String.IsNullOrEmpty(cabinet) && !cabinet.StartsWith("#", StringComparison.Ordinal)) { // If we didn't find the Payload as an existing child of the package, we need to // add it. We expect the file to exist on-disk in the same relative location as // the MSI expects to find it... string cabinetName = Path.Combine(Path.GetDirectoryName(this.PackagePayload.Name), cabinet); if (!this.IsExistingPayload(cabinetName)) { string generatedId = Common.GenerateIdentifier("cab", true, this.PackagePayload.Id, cabinet); string payloadSourceFile = fileManager.ResolveRelatedFile(this.PackagePayload.UnresolvedSourceFile, cabinet, "Cabinet", this.PackagePayload.SourceLineNumbers, BindStage.Normal); PayloadInfoRow payloadNew = PayloadInfoRow.Create(this.SourceLineNumbers, bundle, generatedId, cabinetName, payloadSourceFile, true, this.PackagePayload.SuppressSignatureValidation, null, this.PackagePayload.Container, this.PackagePayload.Packaging); payloadNew.ParentPackagePayload = this.PackagePayload.Id; if (!String.IsNullOrEmpty(payloadNew.Container)) { containers[payloadNew.Container].Payloads.Add(payloadNew); } this.Payloads.Add(payloadNew); allPayloads.Add(payloadNew.Id, payloadNew); this.Size += payloadNew.FileSize; // add the newly added payload to the package size. } } } } // Add all external files as package payloads and calculate the total install size as the rollup of // File table's sizes. this.InstallSize = 0; if (db.Tables.Contains("Component") && db.Tables.Contains("Directory") && db.Tables.Contains("File")) { Hashtable directories = new Hashtable(); // Load up the directory hash table so we will be able to resolve source paths // for files in the MSI database. using (Microsoft.Deployment.WindowsInstaller.View view = db.OpenView("SELECT `Directory`, `Directory_Parent`, `DefaultDir` FROM `Directory`")) { view.Execute(); while (true) { using (Microsoft.Deployment.WindowsInstaller.Record record = view.Fetch()) { if (null == record) { break; } string sourceName = Installer.GetName(record.GetString(3), true, longNamesInImage); directories.Add(record.GetString(1), new ResolvedDirectory(record.GetString(2), sourceName)); } } } // Resolve the source paths to external files and add each file size to the total // install size of the package. using (Microsoft.Deployment.WindowsInstaller.View view = db.OpenView("SELECT `Directory_`, `File`, `FileName`, `File`.`Attributes`, `FileSize` FROM `Component`, `File` WHERE `Component`.`Component`=`File`.`Component_`")) { view.Execute(); while (true) { using (Microsoft.Deployment.WindowsInstaller.Record record = view.Fetch()) { if (null == record) { break; } // Skip adding the loose files as payloads if it was suppressed. if (suppressLooseFilePayloadGeneration != YesNoType.Yes) { // If the file is explicitly uncompressed or the MSI is uncompressed and the file is not // explicitly marked compressed then this is an external file. if (MsiInterop.MsidbFileAttributesNoncompressed == (record.GetInteger(4) & MsiInterop.MsidbFileAttributesNoncompressed) || (!compressed && 0 == (record.GetInteger(4) & MsiInterop.MsidbFileAttributesCompressed))) { string generatedId = Common.GenerateIdentifier("f", true, this.PackagePayload.Id, record.GetString(2)); string fileSourcePath = Binder.GetFileSourcePath(directories, record.GetString(1), record.GetString(3), compressed, longNamesInImage); string payloadSourceFile = fileManager.ResolveRelatedFile(this.PackagePayload.UnresolvedSourceFile, fileSourcePath, "File", this.PackagePayload.SourceLineNumbers, BindStage.Normal); string name = Path.Combine(Path.GetDirectoryName(this.PackagePayload.Name), fileSourcePath); if (!this.IsExistingPayload(name)) { PayloadInfoRow payloadNew = PayloadInfoRow.Create(this.SourceLineNumbers, bundle, generatedId, name, payloadSourceFile, true, this.PackagePayload.SuppressSignatureValidation, null, this.PackagePayload.Container, this.PackagePayload.Packaging); payloadNew.ParentPackagePayload = this.PackagePayload.Id; if (!String.IsNullOrEmpty(payloadNew.Container)) { containers[payloadNew.Container].Payloads.Add(payloadNew); } this.Payloads.Add(payloadNew); allPayloads.Add(payloadNew.Id, payloadNew); this.Size += payloadNew.FileSize; // add the newly added payload to the package size. } } } this.InstallSize += record.GetInteger(5); } } } } // Import any dependency providers from the MSI. if (db.Tables.Contains("WixDependencyProvider")) { // Use the old schema (v1) if the Version column does not exist. bool hasVersion = db.Tables["WixDependencyProvider"].Columns.Contains("Version"); string query = "SELECT `ProviderKey`, `Version`, `DisplayName`, `Attributes` FROM `WixDependencyProvider`"; if (!hasVersion) { query = "SELECT `ProviderKey`, `Attributes` FROM `WixDependencyProvider`"; } using (Microsoft.Deployment.WindowsInstaller.View view = db.OpenView(query)) { view.Execute(); while (true) { using (Microsoft.Deployment.WindowsInstaller.Record record = view.Fetch()) { if (null == record) { break; } // Import the provider key and attributes. ProvidesDependency dependency = null; string providerKey = record.GetString(1); if (hasVersion) { string version = record.GetString(2) ?? this.Version; string displayName = record.GetString(3) ?? this.DisplayName; int attributes = record.GetInteger(4); dependency = new ProvidesDependency(providerKey, version, displayName, attributes); } else { int attributes = record.GetInteger(2); dependency = new ProvidesDependency(providerKey, this.Version, this.DisplayName, attributes); } dependency.Imported = true; this.Provides.Add(dependency); } } } } } } catch (Microsoft.Deployment.WindowsInstaller.InstallerException e) { this.core.OnMessage(WixErrors.UnableToReadPackageInformation(this.PackagePayload.SourceLineNumbers, sourcePath, e.Message)); } }
/// <summary> /// Initializes package state from the MSP contents. /// </summary> /// <param name="core">BinderCore for messages.</param> private void ResolveMspPackage(BinderCore core, Output bundle) { string sourcePath = this.PackagePayload.FullFileName; try { // Read data out of the msp database... using (Microsoft.Deployment.WindowsInstaller.SummaryInfo sumInfo = new Microsoft.Deployment.WindowsInstaller.SummaryInfo(sourcePath, false)) { this.PatchCode = sumInfo.RevisionNumber.Substring(0, 38); } using (Microsoft.Deployment.WindowsInstaller.Database db = new Microsoft.Deployment.WindowsInstaller.Database(sourcePath)) { if (String.IsNullOrEmpty(this.DisplayName)) { this.DisplayName = ChainPackageInfo.GetPatchMetadataProperty(db, "DisplayName"); } if (String.IsNullOrEmpty(this.Description)) { this.Description = ChainPackageInfo.GetPatchMetadataProperty(db, "Description"); } this.Manufacturer = ChainPackageInfo.GetPatchMetadataProperty(db, "ManufacturerName"); } this.PatchXml = Microsoft.Deployment.WindowsInstaller.Installer.ExtractPatchXmlData(sourcePath); XmlDocument doc = new XmlDocument(); doc.LoadXml(this.PatchXml); XmlNamespaceManager nsmgr = new XmlNamespaceManager(doc.NameTable); nsmgr.AddNamespace("p", "http://www.microsoft.com/msi/patch_applicability.xsd"); foreach (XmlNode node in doc.SelectNodes("/p:MsiPatch/p:TargetProduct", nsmgr)) { // If this patch targes a product code, this is the best case. XmlNode targetCode = node.SelectSingleNode("p:TargetProductCode", nsmgr); WixBundlePatchTargetCodeAttributes attributes = WixBundlePatchTargetCodeAttributes.None; if (null != targetCode) { attributes = WixBundlePatchTargetCodeAttributes.TargetsProductCode; } else // maybe targets and upgrade code? { targetCode = node.SelectSingleNode("p:UpgradeCode", nsmgr); if (null != targetCode) { attributes = WixBundlePatchTargetCodeAttributes.TargetsUpgradeCode; } else // this patch targets a unknown number of products { this.TargetUnspecified = true; } } Table table = bundle.EnsureTable(core.TableDefinitions["WixBundlePatchTargetCode"]); WixBundlePatchTargetCodeRow row = (WixBundlePatchTargetCodeRow)table.CreateRow(this.PackagePayload.SourceLineNumbers, false); row.MspPackageId = this.PackagePayload.Id; row.TargetCode = targetCode.InnerText; row.Attributes = attributes; if (this.TargetCodes.TryAdd(row)) { table.Rows.Add(row); } } } catch (Microsoft.Deployment.WindowsInstaller.InstallerException e) { core.OnMessage(WixErrors.UnableToReadPackageInformation(this.PackagePayload.SourceLineNumbers, sourcePath, e.Message)); return; } if (String.IsNullOrEmpty(this.CacheId)) { this.CacheId = this.PatchCode; } }