From dbde9e7104b907bbbaea17e21247d8cafc8b3a4c Mon Sep 17 00:00:00 2001 From: Rob Mensching Date: Sat, 14 Oct 2017 16:12:07 -0700 Subject: Massive refactoring to introduce the concept of IBackend --- src/WixToolset.Core/Bind/BindBundleCommand.cs | 905 -------------- src/WixToolset.Core/Bind/BindDatabaseCommand.cs | 1311 -------------------- src/WixToolset.Core/Bind/BindTransformCommand.cs | 473 ------- .../AutomaticallySlipstreamPatchesCommand.cs | 112 -- src/WixToolset.Core/Bind/Bundles/BurnCommon.cs | 378 ------ src/WixToolset.Core/Bind/Bundles/BurnReader.cs | 210 ---- src/WixToolset.Core/Bind/Bundles/BurnWriter.cs | 239 ---- ...CreateBootstrapperApplicationManifestCommand.cs | 241 ---- .../Bind/Bundles/CreateBurnManifestCommand.cs | 686 ---------- .../Bind/Bundles/CreateContainerCommand.cs | 68 - .../Bind/Bundles/GetPackageFacadesCommand.cs | 62 - .../OrderPackagesAndRollbackBoundariesCommand.cs | 145 --- src/WixToolset.Core/Bind/Bundles/PackageFacade.cs | 58 - .../Bind/Bundles/ProcessExePackageCommand.cs | 33 - .../Bind/Bundles/ProcessMsiPackageCommand.cs | 560 --------- .../Bind/Bundles/ProcessMspPackageCommand.cs | 189 --- .../Bind/Bundles/ProcessMsuPackageCommand.cs | 30 - .../Bind/Bundles/ProcessPayloadsCommand.cs | 159 --- .../Bundles/VerifyPayloadsWithCatalogCommand.cs | 148 --- .../Bind/Databases/AssignMediaCommand.cs | 314 ----- .../Bind/Databases/BindSummaryInfoCommand.cs | 135 -- .../Bind/Databases/CabinetBuilder.cs | 176 --- .../Bind/Databases/CabinetWorkItem.cs | 78 -- .../Bind/Databases/ConfigurationCallback.cs | 91 -- .../Bind/Databases/CopyTransformDataCommand.cs | 606 --------- .../Bind/Databases/CreateCabinetsCommand.cs | 489 -------- .../Bind/Databases/CreateDeltaPatchesCommand.cs | 86 -- .../Databases/CreateSpecialPropertiesCommand.cs | 68 - .../Databases/ExtractMergeModuleFilesCommand.cs | 225 ---- src/WixToolset.Core/Bind/Databases/FileFacade.cs | 44 - .../Bind/Databases/GetFileFacadesCommand.cs | 148 --- .../Bind/Databases/MergeModulesCommand.cs | 350 ------ .../Databases/ProcessUncompressedFilesCommand.cs | 115 -- .../Bind/Databases/UpdateControlTextCommand.cs | 80 -- .../Bind/Databases/UpdateFileFacadesCommand.cs | 532 -------- src/WixToolset.Core/Bind/DelayedField.cs | 13 +- src/WixToolset.Core/Bind/ExpectedExtractFile.cs | 16 + src/WixToolset.Core/Bind/ExtractEmbeddedFiles.cs | 36 +- .../Bind/ExtractEmbeddedFilesCommand.cs | 29 +- src/WixToolset.Core/Bind/FileFacade.cs | 44 + src/WixToolset.Core/Bind/FileResolver.cs | 231 ++++ src/WixToolset.Core/Bind/FileTransfer.cs | 113 -- .../Bind/GenerateDatabaseCommand.cs | 335 ----- .../Bind/ResolveDelayedFieldsCommand.cs | 15 +- src/WixToolset.Core/Bind/ResolveFieldsCommand.cs | 71 +- src/WixToolset.Core/Bind/ResolveResult.cs | 14 + src/WixToolset.Core/Bind/ResolvedDirectory.cs | 2 +- src/WixToolset.Core/Bind/TransferFilesCommand.cs | 61 +- 48 files changed, 420 insertions(+), 10104 deletions(-) delete mode 100644 src/WixToolset.Core/Bind/BindBundleCommand.cs delete mode 100644 src/WixToolset.Core/Bind/BindDatabaseCommand.cs delete mode 100644 src/WixToolset.Core/Bind/BindTransformCommand.cs delete mode 100644 src/WixToolset.Core/Bind/Bundles/AutomaticallySlipstreamPatchesCommand.cs delete mode 100644 src/WixToolset.Core/Bind/Bundles/BurnCommon.cs delete mode 100644 src/WixToolset.Core/Bind/Bundles/BurnReader.cs delete mode 100644 src/WixToolset.Core/Bind/Bundles/BurnWriter.cs delete mode 100644 src/WixToolset.Core/Bind/Bundles/CreateBootstrapperApplicationManifestCommand.cs delete mode 100644 src/WixToolset.Core/Bind/Bundles/CreateBurnManifestCommand.cs delete mode 100644 src/WixToolset.Core/Bind/Bundles/CreateContainerCommand.cs delete mode 100644 src/WixToolset.Core/Bind/Bundles/GetPackageFacadesCommand.cs delete mode 100644 src/WixToolset.Core/Bind/Bundles/OrderPackagesAndRollbackBoundariesCommand.cs delete mode 100644 src/WixToolset.Core/Bind/Bundles/PackageFacade.cs delete mode 100644 src/WixToolset.Core/Bind/Bundles/ProcessExePackageCommand.cs delete mode 100644 src/WixToolset.Core/Bind/Bundles/ProcessMsiPackageCommand.cs delete mode 100644 src/WixToolset.Core/Bind/Bundles/ProcessMspPackageCommand.cs delete mode 100644 src/WixToolset.Core/Bind/Bundles/ProcessMsuPackageCommand.cs delete mode 100644 src/WixToolset.Core/Bind/Bundles/ProcessPayloadsCommand.cs delete mode 100644 src/WixToolset.Core/Bind/Bundles/VerifyPayloadsWithCatalogCommand.cs delete mode 100644 src/WixToolset.Core/Bind/Databases/AssignMediaCommand.cs delete mode 100644 src/WixToolset.Core/Bind/Databases/BindSummaryInfoCommand.cs delete mode 100644 src/WixToolset.Core/Bind/Databases/CabinetBuilder.cs delete mode 100644 src/WixToolset.Core/Bind/Databases/CabinetWorkItem.cs delete mode 100644 src/WixToolset.Core/Bind/Databases/ConfigurationCallback.cs delete mode 100644 src/WixToolset.Core/Bind/Databases/CopyTransformDataCommand.cs delete mode 100644 src/WixToolset.Core/Bind/Databases/CreateCabinetsCommand.cs delete mode 100644 src/WixToolset.Core/Bind/Databases/CreateDeltaPatchesCommand.cs delete mode 100644 src/WixToolset.Core/Bind/Databases/CreateSpecialPropertiesCommand.cs delete mode 100644 src/WixToolset.Core/Bind/Databases/ExtractMergeModuleFilesCommand.cs delete mode 100644 src/WixToolset.Core/Bind/Databases/FileFacade.cs delete mode 100644 src/WixToolset.Core/Bind/Databases/GetFileFacadesCommand.cs delete mode 100644 src/WixToolset.Core/Bind/Databases/MergeModulesCommand.cs delete mode 100644 src/WixToolset.Core/Bind/Databases/ProcessUncompressedFilesCommand.cs delete mode 100644 src/WixToolset.Core/Bind/Databases/UpdateControlTextCommand.cs delete mode 100644 src/WixToolset.Core/Bind/Databases/UpdateFileFacadesCommand.cs create mode 100644 src/WixToolset.Core/Bind/ExpectedExtractFile.cs create mode 100644 src/WixToolset.Core/Bind/FileFacade.cs create mode 100644 src/WixToolset.Core/Bind/FileResolver.cs delete mode 100644 src/WixToolset.Core/Bind/FileTransfer.cs delete mode 100644 src/WixToolset.Core/Bind/GenerateDatabaseCommand.cs create mode 100644 src/WixToolset.Core/Bind/ResolveResult.cs (limited to 'src/WixToolset.Core/Bind') diff --git a/src/WixToolset.Core/Bind/BindBundleCommand.cs b/src/WixToolset.Core/Bind/BindBundleCommand.cs deleted file mode 100644 index 7ea0c830..00000000 --- a/src/WixToolset.Core/Bind/BindBundleCommand.cs +++ /dev/null @@ -1,905 +0,0 @@ -// Copyright (c) .NET Foundation and contributors. All rights reserved. Licensed under the Microsoft Reciprocal License. See LICENSE.TXT file in the project root for full license information. - -namespace WixToolset.Bind -{ - using System; - using System.Collections.Generic; - using System.Diagnostics; - using System.Globalization; - using System.IO; - using System.Linq; - using System.Reflection; - using WixToolset.Bind.Bundles; - using WixToolset.Data; - using WixToolset.Data.Rows; - using WixToolset.Extensibility; - - /// - /// Binds a this.bundle. - /// - internal class BindBundleCommand : ICommand - { - public CompressionLevel DefaultCompressionLevel { private get; set; } - - public IEnumerable Extensions { private get; set; } - - public BinderFileManagerCore FileManagerCore { private get; set; } - - public IEnumerable FileManagers { private get; set; } - - public Output Output { private get; set; } - - public string OutputPath { private get; set; } - - public string PdbFile { private get; set; } - - public TableDefinitionCollection TableDefinitions { private get; set; } - - public string TempFilesLocation { private get; set; } - - public WixVariableResolver WixVariableResolver { private get; set; } - - public IEnumerable FileTransfers { get; private set; } - - public IEnumerable ContentFilePaths { get; private set; } - - public void Execute() - { - this.FileTransfers = Enumerable.Empty(); - this.ContentFilePaths = Enumerable.Empty(); - - // First look for data we expect to find... Chain, WixGroups, etc. - - // We shouldn't really get past the linker phase if there are - // no group items... that means that there's no UX, no Chain, - // *and* no Containers! - Table chainPackageTable = this.GetRequiredTable("WixBundlePackage"); - - Table wixGroupTable = this.GetRequiredTable("WixGroup"); - - // Ensure there is one and only one row in the WixBundle table. - // The compiler and linker behavior should have colluded to get - // this behavior. - WixBundleRow bundleRow = (WixBundleRow)this.GetSingleRowTable("WixBundle"); - - bundleRow.PerMachine = true; // default to per-machine but the first-per user package wil flip the bundle per-user. - - // Ensure there is one and only one row in the WixBootstrapperApplication table. - // The compiler and linker behavior should have colluded to get - // this behavior. - Row baRow = this.GetSingleRowTable("WixBootstrapperApplication"); - - // Ensure there is one and only one row in the WixChain table. - // The compiler and linker behavior should have colluded to get - // this behavior. - WixChainRow chainRow = (WixChainRow)this.GetSingleRowTable("WixChain"); - - if (Messaging.Instance.EncounteredError) - { - return; - } - - // Localize fields, resolve wix variables, and resolve file paths. - ExtractEmbeddedFiles filesWithEmbeddedFiles = new ExtractEmbeddedFiles(); - - IEnumerable delayedFields; - { - ResolveFieldsCommand command = new ResolveFieldsCommand(); - command.Tables = this.Output.Tables; - command.FilesWithEmbeddedFiles = filesWithEmbeddedFiles; - command.FileManagerCore = this.FileManagerCore; - command.FileManagers = this.FileManagers; - command.SupportDelayedResolution = true; - command.TempFilesLocation = this.TempFilesLocation; - command.WixVariableResolver = this.WixVariableResolver; - command.Execute(); - - delayedFields = command.DelayedFields; - } - - if (Messaging.Instance.EncounteredError) - { - return; - } - - // If there are any fields to resolve later, create the cache to populate during bind. - IDictionary variableCache = null; - if (delayedFields.Any()) - { - variableCache = new Dictionary(StringComparer.InvariantCultureIgnoreCase); - } - - // TODO: Although the WixSearch tables are defined in the Util extension, - // the Bundle Binder has to know all about them. We hope to revisit all - // of this in the 4.0 timeframe. - IEnumerable orderedSearches = this.OrderSearches(); - - // Extract files that come from cabinet files (this does not extract files from merge modules). - { - ExtractEmbeddedFilesCommand extractEmbeddedFilesCommand = new ExtractEmbeddedFilesCommand(); - extractEmbeddedFilesCommand.FilesWithEmbeddedFiles = filesWithEmbeddedFiles; - extractEmbeddedFilesCommand.Execute(); - } - - // Get the explicit payloads. - RowDictionary payloads = new RowDictionary(this.Output.Tables["WixBundlePayload"]); - - // Update explicitly authored payloads with their parent package and container (as appropriate) - // to make it easier to gather the payloads later. - foreach (WixGroupRow row in wixGroupTable.RowsAs()) - { - if (ComplexReferenceChildType.Payload == row.ChildType) - { - WixBundlePayloadRow payload = payloads.Get(row.ChildId); - - if (ComplexReferenceParentType.Package == row.ParentType) - { - Debug.Assert(String.IsNullOrEmpty(payload.Package)); - payload.Package = row.ParentId; - } - else if (ComplexReferenceParentType.Container == row.ParentType) - { - Debug.Assert(String.IsNullOrEmpty(payload.Container)); - payload.Container = row.ParentId; - } - else if (ComplexReferenceParentType.Layout == row.ParentType) - { - payload.LayoutOnly = true; - } - } - } - - List fileTransfers = new List(); - string layoutDirectory = Path.GetDirectoryName(this.OutputPath); - - // Process the explicitly authored payloads. - ISet processedPayloads; - { - ProcessPayloadsCommand command = new ProcessPayloadsCommand(); - command.Payloads = payloads.Values; - command.DefaultPackaging = bundleRow.DefaultPackagingType; - command.LayoutDirectory = layoutDirectory; - command.Execute(); - - fileTransfers.AddRange(command.FileTransfers); - - processedPayloads = new HashSet(payloads.Keys); - } - - IDictionary facades; - { - GetPackageFacadesCommand command = new GetPackageFacadesCommand(); - command.PackageTable = chainPackageTable; - command.ExePackageTable = this.Output.Tables["WixBundleExePackage"]; - command.MsiPackageTable = this.Output.Tables["WixBundleMsiPackage"]; - command.MspPackageTable = this.Output.Tables["WixBundleMspPackage"]; - command.MsuPackageTable = this.Output.Tables["WixBundleMsuPackage"]; - command.Execute(); - - facades = command.PackageFacades; - } - - // Process each package facade. Note this is likely to add payloads and other rows to tables so - // note that any indexes created above may be out of date now. - foreach (PackageFacade facade in facades.Values) - { - switch (facade.Package.Type) - { - case WixBundlePackageType.Exe: - { - ProcessExePackageCommand command = new ProcessExePackageCommand(); - command.AuthoredPayloads = payloads; - command.Facade = facade; - command.Execute(); - - // ? variableCache.Add(String.Concat("packageManufacturer.", facade.Package.WixChainItemId), facade.ExePackage.Manufacturer); - } - break; - - case WixBundlePackageType.Msi: - { - ProcessMsiPackageCommand command = new ProcessMsiPackageCommand(); - command.AuthoredPayloads = payloads; - command.Facade = facade; - command.FileManager = this.FileManagers.First(); - command.MsiFeatureTable = this.Output.EnsureTable(this.TableDefinitions["WixBundleMsiFeature"]); - command.MsiPropertyTable = this.Output.EnsureTable(this.TableDefinitions["WixBundleMsiProperty"]); - command.PayloadTable = this.Output.Tables["WixBundlePayload"]; - command.RelatedPackageTable = this.Output.EnsureTable(this.TableDefinitions["WixBundleRelatedPackage"]); - command.Execute(); - - if (null != variableCache) - { - variableCache.Add(String.Concat("packageLanguage.", facade.Package.WixChainItemId), facade.MsiPackage.ProductLanguage.ToString()); - - if (null != facade.MsiPackage.Manufacturer) - { - variableCache.Add(String.Concat("packageManufacturer.", facade.Package.WixChainItemId), facade.MsiPackage.Manufacturer); - } - } - - } - break; - - case WixBundlePackageType.Msp: - { - ProcessMspPackageCommand command = new ProcessMspPackageCommand(); - command.AuthoredPayloads = payloads; - command.Facade = facade; - command.WixBundlePatchTargetCodeTable = this.Output.EnsureTable(this.TableDefinitions["WixBundlePatchTargetCode"]); - command.Execute(); - } - break; - - case WixBundlePackageType.Msu: - { - ProcessMsuPackageCommand command = new ProcessMsuPackageCommand(); - command.Facade = facade; - command.Execute(); - } - break; - } - - if (null != variableCache) - { - BindBundleCommand.PopulatePackageVariableCache(facade.Package, variableCache); - } - } - - // Reindex the payloads now that all the payloads (minus the manifest payloads that will be created later) - // are present. - payloads = new RowDictionary(this.Output.Tables["WixBundlePayload"]); - - // Process the payloads that were added by processing the packages. - { - ProcessPayloadsCommand command = new ProcessPayloadsCommand(); - command.Payloads = payloads.Values.Where(r => !processedPayloads.Contains(r.Id)).ToList(); - command.DefaultPackaging = bundleRow.DefaultPackagingType; - command.LayoutDirectory = layoutDirectory; - command.Execute(); - - fileTransfers.AddRange(command.FileTransfers); - - processedPayloads = null; - } - - // Set the package metadata from the payloads now that we have the complete payload information. - ILookup payloadsByPackage = payloads.Values.ToLookup(p => p.Package); - - { - foreach (PackageFacade facade in facades.Values) - { - facade.Package.Size = 0; - - IEnumerable packagePayloads = payloadsByPackage[facade.Package.WixChainItemId]; - - foreach (WixBundlePayloadRow payload in packagePayloads) - { - facade.Package.Size += payload.FileSize; - } - - if (!facade.Package.InstallSize.HasValue) - { - facade.Package.InstallSize = facade.Package.Size; - - } - - WixBundlePayloadRow packagePayload = payloads[facade.Package.PackagePayload]; - - if (String.IsNullOrEmpty(facade.Package.Description)) - { - facade.Package.Description = packagePayload.Description; - } - - if (String.IsNullOrEmpty(facade.Package.DisplayName)) - { - facade.Package.DisplayName = packagePayload.DisplayName; - } - } - } - - - // Give the UX payloads their embedded IDs... - int uxPayloadIndex = 0; - { - foreach (WixBundlePayloadRow payload in payloads.Values.Where(p => Compiler.BurnUXContainerId == p.Container)) - { - // In theory, UX payloads could be embedded in the UX CAB, external to the bundle EXE, or even - // downloaded. The current engine requires the UX to be fully present before any downloading starts, - // so that rules out downloading. Also, the burn engine does not currently copy external UX payloads - // into the temporary UX directory correctly, so we don't allow external either. - if (PackagingType.Embedded != payload.Packaging) - { - Messaging.Instance.OnMessage(WixWarnings.UxPayloadsOnlySupportEmbedding(payload.SourceLineNumbers, payload.FullFileName)); - payload.Packaging = PackagingType.Embedded; - } - - payload.EmbeddedId = String.Format(CultureInfo.InvariantCulture, BurnCommon.BurnUXContainerEmbeddedIdFormat, uxPayloadIndex); - ++uxPayloadIndex; - } - - if (0 == uxPayloadIndex) - { - // If we didn't get any UX payloads, it's an error! - throw new WixException(WixErrors.MissingBundleInformation("BootstrapperApplication")); - } - - // Give the embedded payloads without an embedded id yet an embedded id. - int payloadIndex = 0; - foreach (WixBundlePayloadRow payload in payloads.Values) - { - Debug.Assert(PackagingType.Unknown != payload.Packaging); - - if (PackagingType.Embedded == payload.Packaging && String.IsNullOrEmpty(payload.EmbeddedId)) - { - payload.EmbeddedId = String.Format(CultureInfo.InvariantCulture, BurnCommon.BurnAttachedContainerEmbeddedIdFormat, payloadIndex); - ++payloadIndex; - } - } - } - - // Determine patches to automatically slipstream. - { - AutomaticallySlipstreamPatchesCommand command = new AutomaticallySlipstreamPatchesCommand(); - command.PackageFacades = facades.Values; - command.SlipstreamMspTable = this.Output.EnsureTable(this.TableDefinitions["WixBundleSlipstreamMsp"]); - command.WixBundlePatchTargetCodeTable = this.Output.EnsureTable(this.TableDefinitions["WixBundlePatchTargetCode"]); - command.Execute(); - } - - // If catalog files exist, non-embedded payloads should validate with the catalogs. - IEnumerable catalogs = this.Output.Tables["WixBundleCatalog"].RowsAs(); - - if (catalogs.Any()) - { - VerifyPayloadsWithCatalogCommand command = new VerifyPayloadsWithCatalogCommand(); - command.Catalogs = catalogs; - command.Payloads = payloads.Values; - command.Execute(); - } - - if (Messaging.Instance.EncounteredError) - { - return; - } - - IEnumerable orderedFacades; - IEnumerable boundaries; - { - OrderPackagesAndRollbackBoundariesCommand command = new OrderPackagesAndRollbackBoundariesCommand(); - command.Boundaries = new RowDictionary(this.Output.Tables["WixBundleRollbackBoundary"]); - command.PackageFacades = facades; - command.WixGroupTable = wixGroupTable; - command.Execute(); - - orderedFacades = command.OrderedPackageFacades; - boundaries = command.UsedRollbackBoundaries; - } - - // Resolve any delayed fields before generating the manifest. - if (delayedFields.Any()) - { - ResolveDelayedFieldsCommand resolveDelayedFieldsCommand = new ResolveDelayedFieldsCommand(); - resolveDelayedFieldsCommand.OutputType = this.Output.Type; - resolveDelayedFieldsCommand.DelayedFields = delayedFields; - resolveDelayedFieldsCommand.ModularizationGuid = null; - resolveDelayedFieldsCommand.VariableCache = variableCache; - resolveDelayedFieldsCommand.Execute(); - } - - // Set the overridable bundle provider key. - this.SetBundleProviderKey(this.Output, bundleRow); - - // Import or generate dependency providers for packages in the manifest. - this.ProcessDependencyProviders(this.Output, facades); - - // Update the bundle per-machine/per-user scope based on the chained packages. - this.ResolveBundleInstallScope(bundleRow, orderedFacades); - - // Generate the core-defined BA manifest tables... - { - CreateBootstrapperApplicationManifestCommand command = new CreateBootstrapperApplicationManifestCommand(); - command.BundleRow = bundleRow; - command.ChainPackages = orderedFacades; - command.LastUXPayloadIndex = uxPayloadIndex; - command.MsiFeatures = this.Output.Tables["WixBundleMsiFeature"].RowsAs(); - command.Output = this.Output; - command.Payloads = payloads; - command.TableDefinitions = this.TableDefinitions; - command.TempFilesLocation = this.TempFilesLocation; - command.Execute(); - - WixBundlePayloadRow baManifestPayload = command.BootstrapperApplicationManifestPayloadRow; - payloads.Add(baManifestPayload); - } - - foreach (BinderExtension extension in this.Extensions) - { - extension.Finish(Output); - } - - // Create all the containers except the UX container first so the manifest (that goes in the UX container) - // can contain all size and hash information about the non-UX containers. - RowDictionary containers = new RowDictionary(this.Output.Tables["WixBundleContainer"]); - - ILookup payloadsByContainer = payloads.Values.ToLookup(p => p.Container); - - int attachedContainerIndex = 1; // count starts at one because UX container is "0". - - IEnumerable uxContainerPayloads = Enumerable.Empty(); - - foreach (WixBundleContainerRow container in containers.Values) - { - IEnumerable containerPayloads = payloadsByContainer[container.Id]; - - if (!containerPayloads.Any()) - { - if (container.Id != Compiler.BurnDefaultAttachedContainerId) - { - // TODO: display warning that we're ignoring container that ended up with no paylods in it. - } - } - else if (Compiler.BurnUXContainerId == container.Id) - { - container.WorkingPath = Path.Combine(this.TempFilesLocation, container.Name); - container.AttachedContainerIndex = 0; - - // Gather the list of UX payloads but ensure the BootstrapperApplication Payload is the first - // in the list since that is the Payload that Burn attempts to load. - List uxPayloads = new List(); - - string baPayloadId = baRow.FieldAsString(0); - - foreach (WixBundlePayloadRow uxPayload in containerPayloads) - { - if (uxPayload.Id == baPayloadId) - { - uxPayloads.Insert(0, uxPayload); - } - else - { - uxPayloads.Add(uxPayload); - } - } - - uxContainerPayloads = uxPayloads; - } - else - { - container.WorkingPath = Path.Combine(this.TempFilesLocation, container.Name); - - // Add detached containers to the list of file transfers. - if (ContainerType.Detached == container.Type) - { - FileTransfer transfer; - if (FileTransfer.TryCreate(container.WorkingPath, Path.Combine(layoutDirectory, container.Name), true, "Container", container.SourceLineNumbers, out transfer)) - { - transfer.Built = true; - fileTransfers.Add(transfer); - } - } - else // update the attached container index. - { - Debug.Assert(ContainerType.Attached == container.Type); - - container.AttachedContainerIndex = attachedContainerIndex; - ++attachedContainerIndex; - } - - this.CreateContainer(container, containerPayloads, null); - } - } - - // Create the bundle manifest then UX container. - string manifestPath = Path.Combine(this.TempFilesLocation, "bundle-manifest.xml"); - { - CreateBurnManifestCommand command = new CreateBurnManifestCommand(); - command.FileManagers = this.FileManagers; - command.Output = this.Output; - - command.BundleInfo = bundleRow; - command.Chain = chainRow; - command.Containers = containers; - command.Catalogs = catalogs; - command.ExecutableName = Path.GetFileName(this.OutputPath); - command.OrderedPackages = orderedFacades; - command.OutputPath = manifestPath; - command.RollbackBoundaries = boundaries; - command.OrderedSearches = orderedSearches; - command.Payloads = payloads; - command.UXContainerPayloads = uxContainerPayloads; - command.Execute(); - } - - WixBundleContainerRow uxContainer = containers[Compiler.BurnUXContainerId]; - this.CreateContainer(uxContainer, uxContainerPayloads, manifestPath); - - // Copy the burn.exe to a writable location then mark it to be moved to its final build location. Note - // that today, the x64 Burn uses the x86 stub. - string stubPlatform = (Platform.X64 == bundleRow.Platform) ? "x86" : bundleRow.Platform.ToString(); - - string stubFile = Path.Combine(Path.GetDirectoryName(Assembly.GetExecutingAssembly().Location), stubPlatform, "burn.exe"); - string bundleTempPath = Path.Combine(this.TempFilesLocation, Path.GetFileName(this.OutputPath)); - - Messaging.Instance.OnMessage(WixVerboses.GeneratingBundle(bundleTempPath, stubFile)); - - string bundleFilename = Path.GetFileName(this.OutputPath); - if ("setup.exe".Equals(bundleFilename, StringComparison.OrdinalIgnoreCase)) - { - Messaging.Instance.OnMessage(WixErrors.InsecureBundleFilename(bundleFilename)); - } - - FileTransfer bundleTransfer; - if (FileTransfer.TryCreate(bundleTempPath, this.OutputPath, true, "Bundle", bundleRow.SourceLineNumbers, out bundleTransfer)) - { - bundleTransfer.Built = true; - fileTransfers.Add(bundleTransfer); - } - - File.Copy(stubFile, bundleTempPath, true); - File.SetAttributes(bundleTempPath, FileAttributes.Normal); - - this.UpdateBurnResources(bundleTempPath, this.OutputPath, bundleRow); - - // Update the .wixburn section to point to at the UX and attached container(s) then attach the containers - // if they should be attached. - using (BurnWriter writer = BurnWriter.Open(bundleTempPath)) - { - FileInfo burnStubFile = new FileInfo(bundleTempPath); - writer.InitializeBundleSectionData(burnStubFile.Length, bundleRow.BundleId); - - // Always attach the UX container first - writer.AppendContainer(uxContainer.WorkingPath, BurnWriter.Container.UX); - - // Now append all other attached containers - foreach (WixBundleContainerRow container in containers.Values) - { - if (ContainerType.Attached == container.Type) - { - // The container was only created if it had payloads. - if (!String.IsNullOrEmpty(container.WorkingPath) && Compiler.BurnUXContainerId != container.Id) - { - writer.AppendContainer(container.WorkingPath, BurnWriter.Container.Attached); - } - } - } - } - - if (null != this.PdbFile) - { - Pdb pdb = new Pdb(); - pdb.Output = Output; - pdb.Save(this.PdbFile); - } - - this.FileTransfers = fileTransfers; - this.ContentFilePaths = payloads.Values.Where(p => p.ContentFile).Select(p => p.FullFileName).ToList(); - } - - private Table GetRequiredTable(string tableName) - { - Table table = this.Output.Tables[tableName]; - if (null == table || 0 == table.Rows.Count) - { - throw new WixException(WixErrors.MissingBundleInformation(tableName)); - } - - return table; - } - - private Row GetSingleRowTable(string tableName) - { - Table table = this.Output.Tables[tableName]; - if (null == table || 1 != table.Rows.Count) - { - throw new WixException(WixErrors.MissingBundleInformation(tableName)); - } - - return table.Rows[0]; - } - - private List OrderSearches() - { - Dictionary allSearches = new Dictionary(); - Table wixFileSearchTable = this.Output.Tables["WixFileSearch"]; - if (null != wixFileSearchTable && 0 < wixFileSearchTable.Rows.Count) - { - foreach (Row row in wixFileSearchTable.Rows) - { - WixFileSearchInfo fileSearchInfo = new WixFileSearchInfo(row); - allSearches.Add(fileSearchInfo.Id, fileSearchInfo); - } - } - - Table wixRegistrySearchTable = this.Output.Tables["WixRegistrySearch"]; - if (null != wixRegistrySearchTable && 0 < wixRegistrySearchTable.Rows.Count) - { - foreach (Row row in wixRegistrySearchTable.Rows) - { - WixRegistrySearchInfo registrySearchInfo = new WixRegistrySearchInfo(row); - allSearches.Add(registrySearchInfo.Id, registrySearchInfo); - } - } - - Table wixComponentSearchTable = this.Output.Tables["WixComponentSearch"]; - if (null != wixComponentSearchTable && 0 < wixComponentSearchTable.Rows.Count) - { - foreach (Row row in wixComponentSearchTable.Rows) - { - WixComponentSearchInfo componentSearchInfo = new WixComponentSearchInfo(row); - allSearches.Add(componentSearchInfo.Id, componentSearchInfo); - } - } - - Table wixProductSearchTable = this.Output.Tables["WixProductSearch"]; - if (null != wixProductSearchTable && 0 < wixProductSearchTable.Rows.Count) - { - foreach (Row row in wixProductSearchTable.Rows) - { - WixProductSearchInfo productSearchInfo = new WixProductSearchInfo(row); - allSearches.Add(productSearchInfo.Id, productSearchInfo); - } - } - - // Merge in the variable/condition info and get the canonical ordering for - // the searches. - List orderedSearches = new List(); - Table wixSearchTable = this.Output.Tables["WixSearch"]; - if (null != wixSearchTable && 0 < wixSearchTable.Rows.Count) - { - orderedSearches.Capacity = wixSearchTable.Rows.Count; - foreach (Row row in wixSearchTable.Rows) - { - WixSearchInfo searchInfo = allSearches[(string)row[0]]; - searchInfo.AddWixSearchRowInfo(row); - orderedSearches.Add(searchInfo); - } - } - - return orderedSearches; - } - - /// - /// Populates the variable cache with specific package properties. - /// - /// The package with properties to cache. - /// The property cache. - private static void PopulatePackageVariableCache(WixBundlePackageRow package, IDictionary variableCache) - { - string id = package.WixChainItemId; - - variableCache.Add(String.Concat("packageDescription.", id), package.Description); - //variableCache.Add(String.Concat("packageLanguage.", id), package.Language); - //variableCache.Add(String.Concat("packageManufacturer.", id), package.Manufacturer); - variableCache.Add(String.Concat("packageName.", id), package.DisplayName); - variableCache.Add(String.Concat("packageVersion.", id), package.Version); - } - - private void CreateContainer(WixBundleContainerRow container, IEnumerable containerPayloads, string manifestFile) - { - CreateContainerCommand command = new CreateContainerCommand(); - command.DefaultCompressionLevel = this.DefaultCompressionLevel; - command.Payloads = containerPayloads; - command.ManifestFile = manifestFile; - command.OutputPath = container.WorkingPath; - command.Execute(); - - container.Hash = command.Hash; - container.Size = command.Size; - } - - private void ResolveBundleInstallScope(WixBundleRow bundleInfo, IEnumerable facades) - { - foreach (PackageFacade facade in facades) - { - if (bundleInfo.PerMachine && YesNoDefaultType.No == facade.Package.PerMachine) - { - Messaging.Instance.OnMessage(WixVerboses.SwitchingToPerUserPackage(facade.Package.SourceLineNumbers, facade.Package.WixChainItemId)); - - bundleInfo.PerMachine = false; - break; - } - } - - foreach (PackageFacade facade in facades) - { - // Update package scope from bundle scope if default. - if (YesNoDefaultType.Default == facade.Package.PerMachine) - { - facade.Package.PerMachine = bundleInfo.PerMachine ? YesNoDefaultType.Yes : YesNoDefaultType.No; - } - - // We will only register packages in the same scope as the bundle. Warn if any packages with providers - // are in a different scope and not permanent (permanents typically don't need a ref-count). - if (!bundleInfo.PerMachine && YesNoDefaultType.Yes == facade.Package.PerMachine && !facade.Package.Permanent && 0 < facade.Provides.Count) - { - Messaging.Instance.OnMessage(WixWarnings.NoPerMachineDependencies(facade.Package.SourceLineNumbers, facade.Package.WixChainItemId)); - } - } - } - - private void UpdateBurnResources(string bundleTempPath, string outputPath, WixBundleRow bundleInfo) - { - WixToolset.Dtf.Resources.ResourceCollection resources = new WixToolset.Dtf.Resources.ResourceCollection(); - WixToolset.Dtf.Resources.VersionResource version = new WixToolset.Dtf.Resources.VersionResource("#1", 1033); - - version.Load(bundleTempPath); - resources.Add(version); - - // Ensure the bundle info provides a full four part version. - Version fourPartVersion = new Version(bundleInfo.Version); - int major = (fourPartVersion.Major < 0) ? 0 : fourPartVersion.Major; - int minor = (fourPartVersion.Minor < 0) ? 0 : fourPartVersion.Minor; - int build = (fourPartVersion.Build < 0) ? 0 : fourPartVersion.Build; - int revision = (fourPartVersion.Revision < 0) ? 0 : fourPartVersion.Revision; - - if (UInt16.MaxValue < major || UInt16.MaxValue < minor || UInt16.MaxValue < build || UInt16.MaxValue < revision) - { - throw new WixException(WixErrors.InvalidModuleOrBundleVersion(bundleInfo.SourceLineNumbers, "Bundle", bundleInfo.Version)); - } - - fourPartVersion = new Version(major, minor, build, revision); - version.FileVersion = fourPartVersion; - version.ProductVersion = fourPartVersion; - - WixToolset.Dtf.Resources.VersionStringTable strings = version[1033]; - strings["LegalCopyright"] = bundleInfo.Copyright; - strings["OriginalFilename"] = Path.GetFileName(outputPath); - strings["FileVersion"] = bundleInfo.Version; // string versions do not have to be four parts. - strings["ProductVersion"] = bundleInfo.Version; // string versions do not have to be four parts. - - if (!String.IsNullOrEmpty(bundleInfo.Name)) - { - strings["ProductName"] = bundleInfo.Name; - strings["FileDescription"] = bundleInfo.Name; - } - - if (!String.IsNullOrEmpty(bundleInfo.Publisher)) - { - strings["CompanyName"] = bundleInfo.Publisher; - } - else - { - strings["CompanyName"] = String.Empty; - } - - if (!String.IsNullOrEmpty(bundleInfo.IconPath)) - { - Dtf.Resources.GroupIconResource iconGroup = new Dtf.Resources.GroupIconResource("#1", 1033); - iconGroup.ReadFromFile(bundleInfo.IconPath); - resources.Add(iconGroup); - - foreach (Dtf.Resources.Resource icon in iconGroup.Icons) - { - resources.Add(icon); - } - } - - if (!String.IsNullOrEmpty(bundleInfo.SplashScreenBitmapPath)) - { - Dtf.Resources.BitmapResource bitmap = new Dtf.Resources.BitmapResource("#1", 1033); - bitmap.ReadFromFile(bundleInfo.SplashScreenBitmapPath); - resources.Add(bitmap); - } - - resources.Save(bundleTempPath); - } - - #region DependencyExtension - /// - /// Imports authored dependency providers for each package in the manifest, - /// and generates dependency providers for certain package types that do not - /// have a provider defined. - /// - /// The object for the bundle. - /// An indexed collection of chained packages. - private void ProcessDependencyProviders(Output bundle, IDictionary facades) - { - // First import any authored dependencies. These may merge with imported provides from MSI packages. - Table wixDependencyProviderTable = bundle.Tables["WixDependencyProvider"]; - if (null != wixDependencyProviderTable && 0 < wixDependencyProviderTable.Rows.Count) - { - // Add package information for each dependency provider authored into the manifest. - foreach (Row wixDependencyProviderRow in wixDependencyProviderTable.Rows) - { - string packageId = (string)wixDependencyProviderRow[1]; - - PackageFacade facade = null; - if (facades.TryGetValue(packageId, out facade)) - { - ProvidesDependency dependency = new ProvidesDependency(wixDependencyProviderRow); - - if (String.IsNullOrEmpty(dependency.Key)) - { - switch (facade.Package.Type) - { - // The WixDependencyExtension allows an empty Key for MSIs and MSPs. - case WixBundlePackageType.Msi: - dependency.Key = facade.MsiPackage.ProductCode; - break; - case WixBundlePackageType.Msp: - dependency.Key = facade.MspPackage.PatchCode; - break; - } - } - - if (String.IsNullOrEmpty(dependency.Version)) - { - dependency.Version = facade.Package.Version; - } - - // If the version is still missing, a version could not be harvested from the package and was not authored. - if (String.IsNullOrEmpty(dependency.Version)) - { - Messaging.Instance.OnMessage(WixErrors.MissingDependencyVersion(facade.Package.WixChainItemId)); - } - - if (String.IsNullOrEmpty(dependency.DisplayName)) - { - dependency.DisplayName = facade.Package.DisplayName; - } - - if (!facade.Provides.Merge(dependency)) - { - Messaging.Instance.OnMessage(WixErrors.DuplicateProviderDependencyKey(dependency.Key, facade.Package.WixChainItemId)); - } - } - } - } - - // Generate providers for MSI packages that still do not have providers. - foreach (PackageFacade facade in facades.Values) - { - string key = null; - - if (WixBundlePackageType.Msi == facade.Package.Type && 0 == facade.Provides.Count) - { - key = facade.MsiPackage.ProductCode; - } - else if (WixBundlePackageType.Msp == facade.Package.Type && 0 == facade.Provides.Count) - { - key = facade.MspPackage.PatchCode; - } - - if (!String.IsNullOrEmpty(key)) - { - ProvidesDependency dependency = new ProvidesDependency(key, facade.Package.Version, facade.Package.DisplayName, 0); - - if (!facade.Provides.Merge(dependency)) - { - Messaging.Instance.OnMessage(WixErrors.DuplicateProviderDependencyKey(dependency.Key, facade.Package.WixChainItemId)); - } - } - } - } - - /// - /// Sets the provider key for the bundle. - /// - /// The object for the bundle. - /// The containing the provider key and other information for the bundle. - private void SetBundleProviderKey(Output bundle, WixBundleRow bundleInfo) - { - // From DependencyCommon.cs in the WixDependencyExtension. - const int ProvidesAttributesBundle = 0x10000; - - Table wixDependencyProviderTable = bundle.Tables["WixDependencyProvider"]; - if (null != wixDependencyProviderTable && 0 < wixDependencyProviderTable.Rows.Count) - { - // Search the WixDependencyProvider table for the single bundle provider key. - foreach (Row wixDependencyProviderRow in wixDependencyProviderTable.Rows) - { - object attributes = wixDependencyProviderRow[5]; - if (null != attributes && 0 != (ProvidesAttributesBundle & (int)attributes)) - { - bundleInfo.ProviderKey = (string)wixDependencyProviderRow[2]; - break; - } - } - } - - // Defaults to the bundle ID as the provider key. - } - #endregion - } -} diff --git a/src/WixToolset.Core/Bind/BindDatabaseCommand.cs b/src/WixToolset.Core/Bind/BindDatabaseCommand.cs deleted file mode 100644 index 93af2e9a..00000000 --- a/src/WixToolset.Core/Bind/BindDatabaseCommand.cs +++ /dev/null @@ -1,1311 +0,0 @@ -// Copyright (c) .NET Foundation and contributors. All rights reserved. Licensed under the Microsoft Reciprocal License. See LICENSE.TXT file in the project root for full license information. - -namespace WixToolset.Bind -{ - using System; - using System.Collections; - using System.Collections.Generic; - using System.Diagnostics; - using System.Globalization; - using System.IO; - using System.Linq; - using WixToolset.Bind.Databases; - using WixToolset.Data; - using WixToolset.Data.Rows; - using WixToolset.Extensibility; - using WixToolset.Msi; - - /// - /// Binds a databse. - /// - internal class BindDatabaseCommand : ICommand - { - // As outlined in RFC 4122, this is our namespace for generating name-based (version 3) UUIDs. - private static readonly Guid WixComponentGuidNamespace = new Guid("{3064E5C6-FB63-4FE9-AC49-E446A792EFA5}"); - - public int Codepage { private get; set; } - - public int CabbingThreadCount { private get; set; } - - public CompressionLevel DefaultCompressionLevel { private get; set; } - - public bool DeltaBinaryPatch { get; set; } - - public IEnumerable Extensions { private get; set; } - - public BinderFileManagerCore FileManagerCore { private get; set; } - - public IEnumerable FileManagers { private get; set; } - - public IEnumerable InspectorExtensions { private get; set; } - - public Localizer Localizer { private get; set; } - - public string PdbFile { private get; set; } - - public Output Output { private get; set; } - - public string OutputPath { private get; set; } - - public bool SuppressAddingValidationRows { private get; set; } - - public bool SuppressLayout { private get; set; } - - public TableDefinitionCollection TableDefinitions { private get; set; } - - public string TempFilesLocation { private get; set; } - - public Validator Validator { private get; set; } - - public WixVariableResolver WixVariableResolver { private get; set; } - - public IEnumerable FileTransfers { get; private set; } - - public IEnumerable ContentFilePaths { get; private set; } - - public void Execute() - { - List fileTransfers = new List(); - - HashSet suppressedTableNames = new HashSet(); - - // Localize fields, resolve wix variables, and resolve file paths. - ExtractEmbeddedFiles filesWithEmbeddedFiles = new ExtractEmbeddedFiles(); - - IEnumerable delayedFields; - { - ResolveFieldsCommand command = new ResolveFieldsCommand(); - command.Tables = this.Output.Tables; - command.FilesWithEmbeddedFiles = filesWithEmbeddedFiles; - command.FileManagerCore = this.FileManagerCore; - command.FileManagers = this.FileManagers; - command.SupportDelayedResolution = true; - command.TempFilesLocation = this.TempFilesLocation; - command.WixVariableResolver = this.WixVariableResolver; - command.Execute(); - - delayedFields = command.DelayedFields; - } - - if (OutputType.Patch == this.Output.Type) - { - foreach (SubStorage transform in this.Output.SubStorages) - { - ResolveFieldsCommand command = new ResolveFieldsCommand(); - command.Tables = transform.Data.Tables; - command.FilesWithEmbeddedFiles = filesWithEmbeddedFiles; - command.FileManagerCore = this.FileManagerCore; - command.FileManagers = this.FileManagers; - command.SupportDelayedResolution = false; - command.TempFilesLocation = this.TempFilesLocation; - command.WixVariableResolver = this.WixVariableResolver; - command.Execute(); - } - } - - // If there are any fields to resolve later, create the cache to populate during bind. - IDictionary variableCache = null; - if (delayedFields.Any()) - { - variableCache = new Dictionary(StringComparer.InvariantCultureIgnoreCase); - } - - this.LocalizeUI(this.Output.Tables); - - // Process the summary information table before the other tables. - bool compressed; - bool longNames; - int installerVersion; - string modularizationGuid; - { - BindSummaryInfoCommand command = new BindSummaryInfoCommand(); - command.Output = this.Output; - command.Execute(); - - compressed = command.Compressed; - longNames = command.LongNames; - installerVersion = command.InstallerVersion; - modularizationGuid = command.ModularizationGuid; - } - - // Stop processing if an error previously occurred. - if (Messaging.Instance.EncounteredError) - { - return; - } - - // Modularize identifiers and add tables with real streams to the import tables. - if (OutputType.Module == this.Output.Type) - { - // Gather all the suppress modularization identifiers - HashSet suppressModularizationIdentifiers = null; - Table wixSuppressModularizationTable = this.Output.Tables["WixSuppressModularization"]; - if (null != wixSuppressModularizationTable) - { - suppressModularizationIdentifiers = new HashSet(wixSuppressModularizationTable.Rows.Select(row => (string)row[0])); - } - - foreach (Table table in this.Output.Tables) - { - table.Modularize(modularizationGuid, suppressModularizationIdentifiers); - } - } - - // This must occur after all variables and source paths have been resolved and after modularization. - List fileFacades; - { - GetFileFacadesCommand command = new GetFileFacadesCommand(); - command.FileTable = this.Output.Tables["File"]; - command.WixFileTable = this.Output.Tables["WixFile"]; - command.WixDeltaPatchFileTable = this.Output.Tables["WixDeltaPatchFile"]; - command.WixDeltaPatchSymbolPathsTable = this.Output.Tables["WixDeltaPatchSymbolPaths"]; - command.Execute(); - - fileFacades = command.FileFacades; - } - - ////if (OutputType.Patch == this.Output.Type) - ////{ - //// foreach (SubStorage substorage in this.Output.SubStorages) - //// { - //// Output transform = substorage.Data; - - //// ResolveFieldsCommand command = new ResolveFieldsCommand(); - //// command.Tables = transform.Tables; - //// command.FilesWithEmbeddedFiles = filesWithEmbeddedFiles; - //// command.FileManagerCore = this.FileManagerCore; - //// command.FileManagers = this.FileManagers; - //// command.SupportDelayedResolution = false; - //// command.TempFilesLocation = this.TempFilesLocation; - //// command.WixVariableResolver = this.WixVariableResolver; - //// command.Execute(); - - //// this.MergeUnrealTables(transform.Tables); - //// } - ////} - - { - CreateSpecialPropertiesCommand command = new CreateSpecialPropertiesCommand(); - command.PropertyTable = this.Output.Tables["Property"]; - command.WixPropertyTable = this.Output.Tables["WixProperty"]; - command.Execute(); - } - - if (Messaging.Instance.EncounteredError) - { - return; - } - - // Add binder variables for all properties. - Table propertyTable = this.Output.Tables["Property"]; - if (null != propertyTable) - { - foreach (PropertyRow propertyRow in propertyTable.Rows) - { - // Set the ProductCode if it is to be generated. - if (OutputType.Product == this.Output.Type && "ProductCode".Equals(propertyRow.Property, StringComparison.Ordinal) && "*".Equals(propertyRow.Value, StringComparison.Ordinal)) - { - propertyRow.Value = Common.GenerateGuid(); - - // Update the target ProductCode in any instance transforms. - foreach (SubStorage subStorage in this.Output.SubStorages) - { - Output subStorageOutput = subStorage.Data; - if (OutputType.Transform != subStorageOutput.Type) - { - continue; - } - - Table instanceSummaryInformationTable = subStorageOutput.Tables["_SummaryInformation"]; - foreach (Row row in instanceSummaryInformationTable.Rows) - { - if ((int)SummaryInformation.Transform.ProductCodes == row.FieldAsInteger(0)) - { - row[1] = row.FieldAsString(1).Replace("*", propertyRow.Value); - break; - } - } - } - } - - // Add the property name and value to the variableCache. - if (null != variableCache) - { - string key = String.Concat("property.", Demodularize(this.Output.Type, modularizationGuid, propertyRow.Property)); - variableCache[key] = propertyRow.Value; - } - } - } - - // Extract files that come from cabinet files (this does not extract files from merge modules). - { - ExtractEmbeddedFilesCommand command = new ExtractEmbeddedFilesCommand(); - command.FilesWithEmbeddedFiles = filesWithEmbeddedFiles; - command.Execute(); - } - - if (OutputType.Product == this.Output.Type) - { - // Retrieve files and their information from merge modules. - Table wixMergeTable = this.Output.Tables["WixMerge"]; - - if (null != wixMergeTable) - { - ExtractMergeModuleFilesCommand command = new ExtractMergeModuleFilesCommand(); - command.FileFacades = fileFacades; - command.FileTable = this.Output.Tables["File"]; - command.WixFileTable = this.Output.Tables["WixFile"]; - command.WixMergeTable = wixMergeTable; - command.OutputInstallerVersion = installerVersion; - command.SuppressLayout = this.SuppressLayout; - command.TempFilesLocation = this.TempFilesLocation; - command.Execute(); - - fileFacades.AddRange(command.MergeModulesFileFacades); - } - } - else if (OutputType.Patch == this.Output.Type) - { - // Merge transform data into the output object. - IEnumerable filesFromTransform = this.CopyFromTransformData(this.Output); - - fileFacades.AddRange(filesFromTransform); - } - - // stop processing if an error previously occurred - if (Messaging.Instance.EncounteredError) - { - return; - } - - Messaging.Instance.OnMessage(WixVerboses.UpdatingFileInformation()); - - // Gather information about files that did not come from merge modules (i.e. rows with a reference to the File table). - { - UpdateFileFacadesCommand command = new UpdateFileFacadesCommand(); - command.FileFacades = fileFacades; - command.UpdateFileFacades = fileFacades.Where(f => !f.FromModule); - command.ModularizationGuid = modularizationGuid; - command.Output = this.Output; - command.OverwriteHash = true; - command.TableDefinitions = this.TableDefinitions; - command.VariableCache = variableCache; - command.Execute(); - } - - // Set generated component guids. - this.SetComponentGuids(this.Output); - - // With the Component Guids set now we can create instance transforms. - this.CreateInstanceTransforms(this.Output); - - this.ValidateComponentGuids(this.Output); - - this.UpdateControlText(this.Output); - - if (delayedFields.Any()) - { - ResolveDelayedFieldsCommand command = new ResolveDelayedFieldsCommand(); - command.OutputType = this.Output.Type; - command.DelayedFields = delayedFields; - command.ModularizationGuid = null; - command.VariableCache = variableCache; - command.Execute(); - } - - // Assign files to media. - RowDictionary assignedMediaRows; - Dictionary> filesByCabinetMedia; - IEnumerable uncompressedFiles; - { - AssignMediaCommand command = new AssignMediaCommand(); - command.FilesCompressed = compressed; - command.FileFacades = fileFacades; - command.Output = this.Output; - command.TableDefinitions = this.TableDefinitions; - command.Execute(); - - assignedMediaRows = command.MediaRows; - filesByCabinetMedia = command.FileFacadesByCabinetMedia; - uncompressedFiles = command.UncompressedFileFacades; - } - - // Update file sequence. - this.UpdateMediaSequences(this.Output.Type, fileFacades, assignedMediaRows); - - // stop processing if an error previously occurred - if (Messaging.Instance.EncounteredError) - { - return; - } - - // Extended binder extensions can be called now that fields are resolved. - { - Table updatedFiles = this.Output.EnsureTable(this.TableDefinitions["WixBindUpdatedFiles"]); - - foreach (BinderExtension extension in this.Extensions) - { - extension.AfterResolvedFields(this.Output); - } - - List updatedFileFacades = new List(); - - foreach (Row updatedFile in updatedFiles.Rows) - { - string updatedId = updatedFile.FieldAsString(0); - - FileFacade updatedFacade = fileFacades.First(f => f.File.File.Equals(updatedId)); - - updatedFileFacades.Add(updatedFacade); - } - - if (updatedFileFacades.Any()) - { - UpdateFileFacadesCommand command = new UpdateFileFacadesCommand(); - command.FileFacades = fileFacades; - command.UpdateFileFacades = updatedFileFacades; - command.ModularizationGuid = modularizationGuid; - command.Output = this.Output; - command.OverwriteHash = true; - command.TableDefinitions = this.TableDefinitions; - command.VariableCache = variableCache; - command.Execute(); - } - } - - // stop processing if an error previously occurred - if (Messaging.Instance.EncounteredError) - { - return; - } - - Directory.CreateDirectory(this.TempFilesLocation); - - if (OutputType.Patch == this.Output.Type && this.DeltaBinaryPatch) - { - CreateDeltaPatchesCommand command = new CreateDeltaPatchesCommand(); - command.FileFacades = fileFacades; - command.WixPatchIdTable = this.Output.Tables["WixPatchId"]; - command.TempFilesLocation = this.TempFilesLocation; - command.Execute(); - } - - // create cabinet files and process uncompressed files - string layoutDirectory = Path.GetDirectoryName(this.OutputPath); - if (!this.SuppressLayout || OutputType.Module == this.Output.Type) - { - Messaging.Instance.OnMessage(WixVerboses.CreatingCabinetFiles()); - - CreateCabinetsCommand command = new CreateCabinetsCommand(); - command.CabbingThreadCount = this.CabbingThreadCount; - command.DefaultCompressionLevel = this.DefaultCompressionLevel; - command.Output = this.Output; - command.FileManagers = this.FileManagers; - command.LayoutDirectory = layoutDirectory; - command.Compressed = compressed; - command.FileRowsByCabinet = filesByCabinetMedia; - command.ResolveMedia = this.ResolveMedia; - command.TableDefinitions = this.TableDefinitions; - command.TempFilesLocation = this.TempFilesLocation; - command.WixMediaTable = this.Output.Tables["WixMedia"]; - command.Execute(); - - fileTransfers.AddRange(command.FileTransfers); - } - - if (OutputType.Patch == this.Output.Type) - { - // copy output data back into the transforms - this.CopyToTransformData(this.Output); - } - - // stop processing if an error previously occurred - if (Messaging.Instance.EncounteredError) - { - return; - } - - // add back suppressed tables which must be present prior to merging in modules - if (OutputType.Product == this.Output.Type) - { - Table wixMergeTable = this.Output.Tables["WixMerge"]; - - if (null != wixMergeTable && 0 < wixMergeTable.Rows.Count) - { - foreach (SequenceTable sequence in Enum.GetValues(typeof(SequenceTable))) - { - string sequenceTableName = sequence.ToString(); - Table sequenceTable = this.Output.Tables[sequenceTableName]; - - if (null == sequenceTable) - { - sequenceTable = this.Output.EnsureTable(this.TableDefinitions[sequenceTableName]); - } - - if (0 == sequenceTable.Rows.Count) - { - suppressedTableNames.Add(sequenceTableName); - } - } - } - } - - foreach (BinderExtension extension in this.Extensions) - { - extension.Finish(this.Output); - } - - // generate database file - Messaging.Instance.OnMessage(WixVerboses.GeneratingDatabase()); - string tempDatabaseFile = Path.Combine(this.TempFilesLocation, Path.GetFileName(this.OutputPath)); - this.GenerateDatabase(this.Output, tempDatabaseFile, false, false); - - FileTransfer transfer; - if (FileTransfer.TryCreate(tempDatabaseFile, this.OutputPath, true, this.Output.Type.ToString(), null, out transfer)) // note where this database needs to move in the future - { - transfer.Built = true; - fileTransfers.Add(transfer); - } - - // stop processing if an error previously occurred - if (Messaging.Instance.EncounteredError) - { - return; - } - - // Output the output to a file - Pdb pdb = new Pdb(); - pdb.Output = this.Output; - if (!String.IsNullOrEmpty(this.PdbFile)) - { - pdb.Save(this.PdbFile); - } - - // Merge modules. - if (OutputType.Product == this.Output.Type) - { - Messaging.Instance.OnMessage(WixVerboses.MergingModules()); - - MergeModulesCommand command = new MergeModulesCommand(); - command.FileFacades = fileFacades; - command.Output = this.Output; - command.OutputPath = tempDatabaseFile; - command.SuppressedTableNames = suppressedTableNames; - command.Execute(); - - // stop processing if an error previously occurred - if (Messaging.Instance.EncounteredError) - { - return; - } - } - - // inspect the MSI prior to running ICEs - InspectorCore inspectorCore = new InspectorCore(); - foreach (InspectorExtension inspectorExtension in this.InspectorExtensions) - { - inspectorExtension.Core = inspectorCore; - inspectorExtension.InspectDatabase(tempDatabaseFile, pdb); - - inspectorExtension.Core = null; // reset. - } - - if (Messaging.Instance.EncounteredError) - { - return; - } - - // validate the output if there is an MSI validator - if (null != this.Validator) - { - Stopwatch stopwatch = Stopwatch.StartNew(); - - // set the output file for source line information - this.Validator.Output = this.Output; - - Messaging.Instance.OnMessage(WixVerboses.ValidatingDatabase()); - - this.Validator.Validate(tempDatabaseFile); - - stopwatch.Stop(); - Messaging.Instance.OnMessage(WixVerboses.ValidatedDatabase(stopwatch.ElapsedMilliseconds)); - - // Stop processing if an error occurred. - if (Messaging.Instance.EncounteredError) - { - return; - } - } - - // Process uncompressed files. - if (!Messaging.Instance.EncounteredError && !this.SuppressLayout && uncompressedFiles.Any()) - { - ProcessUncompressedFilesCommand command = new ProcessUncompressedFilesCommand(); - command.Compressed = compressed; - command.FileFacades = uncompressedFiles; - command.LayoutDirectory = layoutDirectory; - command.LongNamesInImage = longNames; - command.MediaRows = assignedMediaRows; - command.ResolveMedia = this.ResolveMedia; - command.DatabasePath = tempDatabaseFile; - command.WixMediaTable = this.Output.Tables["WixMedia"]; - command.Execute(); - - fileTransfers.AddRange(command.FileTransfers); - } - - this.FileTransfers = fileTransfers; - this.ContentFilePaths = fileFacades.Select(r => r.WixFile.Source).ToList(); - } - - /// - /// Localize dialogs and controls. - /// - /// The tables to localize. - private void LocalizeUI(TableIndexedCollection tables) - { - Table dialogTable = tables["Dialog"]; - if (null != dialogTable) - { - foreach (Row row in dialogTable.Rows) - { - string dialog = (string)row[0]; - LocalizedControl localizedControl = this.Localizer.GetLocalizedControl(dialog, null); - if (null != localizedControl) - { - if (CompilerConstants.IntegerNotSet != localizedControl.X) - { - row[1] = localizedControl.X; - } - - if (CompilerConstants.IntegerNotSet != localizedControl.Y) - { - row[2] = localizedControl.Y; - } - - if (CompilerConstants.IntegerNotSet != localizedControl.Width) - { - row[3] = localizedControl.Width; - } - - if (CompilerConstants.IntegerNotSet != localizedControl.Height) - { - row[4] = localizedControl.Height; - } - - row[5] = (int)row[5] | localizedControl.Attributes; - - if (!String.IsNullOrEmpty(localizedControl.Text)) - { - row[6] = localizedControl.Text; - } - } - } - } - - Table controlTable = tables["Control"]; - if (null != controlTable) - { - foreach (Row row in controlTable.Rows) - { - string dialog = (string)row[0]; - string control = (string)row[1]; - LocalizedControl localizedControl = this.Localizer.GetLocalizedControl(dialog, control); - if (null != localizedControl) - { - if (CompilerConstants.IntegerNotSet != localizedControl.X) - { - row[3] = localizedControl.X.ToString(); - } - - if (CompilerConstants.IntegerNotSet != localizedControl.Y) - { - row[4] = localizedControl.Y.ToString(); - } - - if (CompilerConstants.IntegerNotSet != localizedControl.Width) - { - row[5] = localizedControl.Width.ToString(); - } - - if (CompilerConstants.IntegerNotSet != localizedControl.Height) - { - row[6] = localizedControl.Height.ToString(); - } - - row[7] = (int)row[7] | localizedControl.Attributes; - - if (!String.IsNullOrEmpty(localizedControl.Text)) - { - row[9] = localizedControl.Text; - } - } - } - } - } - - /// - /// Copy file data between transform substorages and the patch output object - /// - /// The output to bind. - /// True if copying from transform to patch, false the other way. - private IEnumerable CopyFromTransformData(Output output) - { - CopyTransformDataCommand command = new CopyTransformDataCommand(); - command.CopyOutFileRows = true; - command.FileManagerCore = this.FileManagerCore; - command.FileManagers = this.FileManagers; - command.Output = output; - command.TableDefinitions = this.TableDefinitions; - command.Execute(); - - return command.FileFacades; - } - - /// - /// Copy file data between transform substorages and the patch output object - /// - /// The output to bind. - /// True if copying from transform to patch, false the other way. - private void CopyToTransformData(Output output) - { - CopyTransformDataCommand command = new CopyTransformDataCommand(); - command.CopyOutFileRows = false; - command.FileManagerCore = this.FileManagerCore; - command.FileManagers = this.FileManagers; - command.Output = output; - command.TableDefinitions = this.TableDefinitions; - command.Execute(); - } - - /// - /// Takes an id, and demodularizes it (if possible). - /// - /// - /// If the output type is a module, returns a demodularized version of an id. Otherwise, returns the id. - /// - /// The type of the output to bind. - /// The modularization GUID. - /// The id to demodularize. - /// The demodularized id. - internal static string Demodularize(OutputType outputType, string modularizationGuid, string id) - { - if (OutputType.Module == outputType && id.EndsWith(String.Concat(".", modularizationGuid), StringComparison.Ordinal)) - { - id = id.Substring(0, id.Length - 37); - } - - return id; - } - - private void UpdateMediaSequences(OutputType outputType, IEnumerable fileFacades, RowDictionary mediaRows) - { - // Calculate sequence numbers and media disk id layout for all file media information objects. - if (OutputType.Module == outputType) - { - int lastSequence = 0; - foreach (FileFacade facade in fileFacades) // TODO: Sort these rows directory path and component id and maybe file size or file extension and other creative ideas to get optimal install speed out of MSI. - { - facade.File.Sequence = ++lastSequence; - } - } - else - { - int lastSequence = 0; - MediaRow mediaRow = null; - Dictionary> patchGroups = new Dictionary>(); - - // sequence the non-patch-added files - foreach (FileFacade facade in fileFacades) // TODO: Sort these rows directory path and component id and maybe file size or file extension and other creative ideas to get optimal install speed out of MSI. - { - if (null == mediaRow) - { - mediaRow = mediaRows.Get(facade.WixFile.DiskId); - if (OutputType.Patch == outputType) - { - // patch Media cannot start at zero - lastSequence = mediaRow.LastSequence; - } - } - else if (mediaRow.DiskId != facade.WixFile.DiskId) - { - mediaRow.LastSequence = lastSequence; - mediaRow = mediaRows.Get(facade.WixFile.DiskId); - } - - if (0 < facade.WixFile.PatchGroup) - { - List patchGroup = patchGroups[facade.WixFile.PatchGroup]; - - if (null == patchGroup) - { - patchGroup = new List(); - patchGroups.Add(facade.WixFile.PatchGroup, patchGroup); - } - - patchGroup.Add(facade); - } - else - { - facade.File.Sequence = ++lastSequence; - } - } - - if (null != mediaRow) - { - mediaRow.LastSequence = lastSequence; - mediaRow = null; - } - - // sequence the patch-added files - foreach (List patchGroup in patchGroups.Values) - { - foreach (FileFacade facade in patchGroup) - { - if (null == mediaRow) - { - mediaRow = mediaRows.Get(facade.WixFile.DiskId); - } - else if (mediaRow.DiskId != facade.WixFile.DiskId) - { - mediaRow.LastSequence = lastSequence; - mediaRow = mediaRows.Get(facade.WixFile.DiskId); - } - - facade.File.Sequence = ++lastSequence; - } - } - - if (null != mediaRow) - { - mediaRow.LastSequence = lastSequence; - } - } - } - - /// - /// Set the guids for components with generatable guids. - /// - /// Internal representation of the database to operate on. - private void SetComponentGuids(Output output) - { - Table componentTable = output.Tables["Component"]; - if (null != componentTable) - { - Hashtable registryKeyRows = null; - Hashtable directories = null; - Hashtable componentIdGenSeeds = null; - Dictionary> fileRows = null; - - // find components with generatable guids - foreach (ComponentRow componentRow in componentTable.Rows) - { - // component guid will be generated - if ("*" == componentRow.Guid) - { - if (null == componentRow.KeyPath || componentRow.IsOdbcDataSourceKeyPath) - { - Messaging.Instance.OnMessage(WixErrors.IllegalComponentWithAutoGeneratedGuid(componentRow.SourceLineNumbers)); - } - else if (componentRow.IsRegistryKeyPath) - { - if (null == registryKeyRows) - { - Table registryTable = output.Tables["Registry"]; - - registryKeyRows = new Hashtable(registryTable.Rows.Count); - - foreach (Row registryRow in registryTable.Rows) - { - registryKeyRows.Add((string)registryRow[0], registryRow); - } - } - - Row foundRow = registryKeyRows[componentRow.KeyPath] as Row; - - string bitness = componentRow.Is64Bit ? "64" : String.Empty; - if (null != foundRow) - { - string regkey = String.Concat(bitness, foundRow[1], "\\", foundRow[2], "\\", foundRow[3]); - componentRow.Guid = Uuid.NewUuid(BindDatabaseCommand.WixComponentGuidNamespace, regkey.ToLowerInvariant()).ToString("B").ToUpperInvariant(); - } - } - else // must be a File KeyPath - { - // if the directory table hasn't been loaded into an indexed hash - // of directory ids to target names do that now. - if (null == directories) - { - Table directoryTable = output.Tables["Directory"]; - - int numDirectoryTableRows = (null != directoryTable) ? directoryTable.Rows.Count : 0; - - directories = new Hashtable(numDirectoryTableRows); - - // get the target paths for all directories - if (null != directoryTable) - { - foreach (Row row in directoryTable.Rows) - { - // if the directory Id already exists, we will skip it here since - // checking for duplicate primary keys is done later when importing tables - // into database - if (directories.ContainsKey(row[0])) - { - continue; - } - - string targetName = Installer.GetName((string)row[2], false, true); - directories.Add(row[0], new ResolvedDirectory((string)row[1], targetName)); - } - } - } - - // if the component id generation seeds have not been indexed - // from the WixDirectory table do that now. - if (null == componentIdGenSeeds) - { - Table wixDirectoryTable = output.Tables["WixDirectory"]; - - int numWixDirectoryRows = (null != wixDirectoryTable) ? wixDirectoryTable.Rows.Count : 0; - - componentIdGenSeeds = new Hashtable(numWixDirectoryRows); - - // if there are any WixDirectory rows, build up the Component Guid - // generation seeds indexed by Directory/@Id. - if (null != wixDirectoryTable) - { - foreach (Row row in wixDirectoryTable.Rows) - { - componentIdGenSeeds.Add(row[0], (string)row[1]); - } - } - } - - // if the file rows have not been indexed by File.Component yet - // then do that now - if (null == fileRows) - { - Table fileTable = output.Tables["File"]; - - int numFileRows = (null != fileTable) ? fileTable.Rows.Count : 0; - - fileRows = new Dictionary>(numFileRows); - - if (null != fileTable) - { - foreach (FileRow file in fileTable.Rows) - { - List files; - if (!fileRows.TryGetValue(file.Component, out files)) - { - files = new List(); - fileRows.Add(file.Component, files); - } - - files.Add(file); - } - } - } - - // validate component meets all the conditions to have a generated guid - List currentComponentFiles = fileRows[componentRow.Component]; - int numFilesInComponent = currentComponentFiles.Count; - string path = null; - - foreach (FileRow fileRow in currentComponentFiles) - { - if (fileRow.File == componentRow.KeyPath) - { - // calculate the key file's canonical target path - string directoryPath = Binder.GetDirectoryPath(directories, componentIdGenSeeds, componentRow.Directory, true); - string fileName = Installer.GetName(fileRow.FileName, false, true).ToLower(CultureInfo.InvariantCulture); - path = Path.Combine(directoryPath, fileName); - - // find paths that are not canonicalized - if (path.StartsWith(@"PersonalFolder\my pictures", StringComparison.Ordinal) || - path.StartsWith(@"ProgramFilesFolder\common files", StringComparison.Ordinal) || - path.StartsWith(@"ProgramMenuFolder\startup", StringComparison.Ordinal) || - path.StartsWith("TARGETDIR", StringComparison.Ordinal) || - path.StartsWith(@"StartMenuFolder\programs", StringComparison.Ordinal) || - path.StartsWith(@"WindowsFolder\fonts", StringComparison.Ordinal)) - { - Messaging.Instance.OnMessage(WixErrors.IllegalPathForGeneratedComponentGuid(componentRow.SourceLineNumbers, fileRow.Component, path)); - } - - // if component has more than one file, the key path must be versioned - if (1 < numFilesInComponent && String.IsNullOrEmpty(fileRow.Version)) - { - Messaging.Instance.OnMessage(WixErrors.IllegalGeneratedGuidComponentUnversionedKeypath(componentRow.SourceLineNumbers)); - } - } - else - { - // not a key path, so it must be an unversioned file if component has more than one file - if (1 < numFilesInComponent && !String.IsNullOrEmpty(fileRow.Version)) - { - Messaging.Instance.OnMessage(WixErrors.IllegalGeneratedGuidComponentVersionedNonkeypath(componentRow.SourceLineNumbers)); - } - } - } - - // if the rules were followed, reward with a generated guid - if (!Messaging.Instance.EncounteredError) - { - componentRow.Guid = Uuid.NewUuid(BindDatabaseCommand.WixComponentGuidNamespace, path).ToString("B").ToUpperInvariant(); - } - } - } - } - } - } - - /// - /// Creates instance transform substorages in the output. - /// - /// Output containing instance transform definitions. - private void CreateInstanceTransforms(Output output) - { - // Create and add substorages for instance transforms. - Table wixInstanceTransformsTable = output.Tables["WixInstanceTransforms"]; - if (null != wixInstanceTransformsTable && 0 <= wixInstanceTransformsTable.Rows.Count) - { - string targetProductCode = null; - string targetUpgradeCode = null; - string targetProductVersion = null; - - Table targetSummaryInformationTable = output.Tables["_SummaryInformation"]; - Table targetPropertyTable = output.Tables["Property"]; - - // Get the data from target database - foreach (Row propertyRow in targetPropertyTable.Rows) - { - if ("ProductCode" == (string)propertyRow[0]) - { - targetProductCode = (string)propertyRow[1]; - } - else if ("ProductVersion" == (string)propertyRow[0]) - { - targetProductVersion = (string)propertyRow[1]; - } - else if ("UpgradeCode" == (string)propertyRow[0]) - { - targetUpgradeCode = (string)propertyRow[1]; - } - } - - // Index the Instance Component Rows. - Dictionary instanceComponentGuids = new Dictionary(); - Table targetInstanceComponentTable = output.Tables["WixInstanceComponent"]; - if (null != targetInstanceComponentTable && 0 < targetInstanceComponentTable.Rows.Count) - { - foreach (Row row in targetInstanceComponentTable.Rows) - { - // Build up all the instances, we'll get the Components rows from the real Component table. - instanceComponentGuids.Add((string)row[0], null); - } - - Table targetComponentTable = output.Tables["Component"]; - foreach (ComponentRow componentRow in targetComponentTable.Rows) - { - string component = (string)componentRow[0]; - if (instanceComponentGuids.ContainsKey(component)) - { - instanceComponentGuids[component] = componentRow; - } - } - } - - // Generate the instance transforms - foreach (Row instanceRow in wixInstanceTransformsTable.Rows) - { - string instanceId = (string)instanceRow[0]; - - Output instanceTransform = new Output(instanceRow.SourceLineNumbers); - instanceTransform.Type = OutputType.Transform; - instanceTransform.Codepage = output.Codepage; - - Table instanceSummaryInformationTable = instanceTransform.EnsureTable(this.TableDefinitions["_SummaryInformation"]); - string targetPlatformAndLanguage = null; - - foreach (Row summaryInformationRow in targetSummaryInformationTable.Rows) - { - if (7 == (int)summaryInformationRow[0]) // PID_TEMPLATE - { - targetPlatformAndLanguage = (string)summaryInformationRow[1]; - } - - // Copy the row's data to the transform. - Row copyOfSummaryRow = instanceSummaryInformationTable.CreateRow(null); - copyOfSummaryRow[0] = summaryInformationRow[0]; - copyOfSummaryRow[1] = summaryInformationRow[1]; - } - - // Modify the appropriate properties. - Table propertyTable = instanceTransform.EnsureTable(this.TableDefinitions["Property"]); - - // Change the ProductCode property - string productCode = (string)instanceRow[2]; - if ("*" == productCode) - { - productCode = Common.GenerateGuid(); - } - - Row productCodeRow = propertyTable.CreateRow(instanceRow.SourceLineNumbers); - productCodeRow.Operation = RowOperation.Modify; - productCodeRow.Fields[1].Modified = true; - productCodeRow[0] = "ProductCode"; - productCodeRow[1] = productCode; - - // Change the instance property - Row instanceIdRow = propertyTable.CreateRow(instanceRow.SourceLineNumbers); - instanceIdRow.Operation = RowOperation.Modify; - instanceIdRow.Fields[1].Modified = true; - instanceIdRow[0] = (string)instanceRow[1]; - instanceIdRow[1] = instanceId; - - if (null != instanceRow[3]) - { - // Change the ProductName property - Row productNameRow = propertyTable.CreateRow(instanceRow.SourceLineNumbers); - productNameRow.Operation = RowOperation.Modify; - productNameRow.Fields[1].Modified = true; - productNameRow[0] = "ProductName"; - productNameRow[1] = (string)instanceRow[3]; - } - - if (null != instanceRow[4]) - { - // Change the UpgradeCode property - Row upgradeCodeRow = propertyTable.CreateRow(instanceRow.SourceLineNumbers); - upgradeCodeRow.Operation = RowOperation.Modify; - upgradeCodeRow.Fields[1].Modified = true; - upgradeCodeRow[0] = "UpgradeCode"; - upgradeCodeRow[1] = instanceRow[4]; - - // Change the Upgrade table - Table targetUpgradeTable = output.Tables["Upgrade"]; - if (null != targetUpgradeTable && 0 <= targetUpgradeTable.Rows.Count) - { - string upgradeId = (string)instanceRow[4]; - Table upgradeTable = instanceTransform.EnsureTable(this.TableDefinitions["Upgrade"]); - foreach (Row row in targetUpgradeTable.Rows) - { - // In case they are upgrading other codes to this new product, leave the ones that don't match the - // Product.UpgradeCode intact. - if (targetUpgradeCode == (string)row[0]) - { - Row upgradeRow = upgradeTable.CreateRow(null); - upgradeRow.Operation = RowOperation.Add; - upgradeRow.Fields[0].Modified = true; - // I was hoping to be able to RowOperation.Modify, but that didn't appear to function. - // upgradeRow.Fields[0].PreviousData = (string)row[0]; - - // Inserting a new Upgrade record with the updated UpgradeCode - upgradeRow[0] = upgradeId; - upgradeRow[1] = row[1]; - upgradeRow[2] = row[2]; - upgradeRow[3] = row[3]; - upgradeRow[4] = row[4]; - upgradeRow[5] = row[5]; - upgradeRow[6] = row[6]; - - // Delete the old row - Row upgradeRemoveRow = upgradeTable.CreateRow(null); - upgradeRemoveRow.Operation = RowOperation.Delete; - upgradeRemoveRow[0] = row[0]; - upgradeRemoveRow[1] = row[1]; - upgradeRemoveRow[2] = row[2]; - upgradeRemoveRow[3] = row[3]; - upgradeRemoveRow[4] = row[4]; - upgradeRemoveRow[5] = row[5]; - upgradeRemoveRow[6] = row[6]; - } - } - } - } - - // If there are instance Components generate new GUIDs for them. - if (0 < instanceComponentGuids.Count) - { - Table componentTable = instanceTransform.EnsureTable(this.TableDefinitions["Component"]); - foreach (ComponentRow targetComponentRow in instanceComponentGuids.Values) - { - string guid = targetComponentRow.Guid; - if (!String.IsNullOrEmpty(guid)) - { - Row instanceComponentRow = componentTable.CreateRow(targetComponentRow.SourceLineNumbers); - instanceComponentRow.Operation = RowOperation.Modify; - instanceComponentRow.Fields[1].Modified = true; - instanceComponentRow[0] = targetComponentRow[0]; - instanceComponentRow[1] = Uuid.NewUuid(BindDatabaseCommand.WixComponentGuidNamespace, String.Concat(guid, instanceId)).ToString("B").ToUpper(CultureInfo.InvariantCulture); - instanceComponentRow[2] = targetComponentRow[2]; - instanceComponentRow[3] = targetComponentRow[3]; - instanceComponentRow[4] = targetComponentRow[4]; - instanceComponentRow[5] = targetComponentRow[5]; - } - } - } - - // Update the summary information - Hashtable summaryRows = new Hashtable(instanceSummaryInformationTable.Rows.Count); - foreach (Row row in instanceSummaryInformationTable.Rows) - { - summaryRows[row[0]] = row; - - if ((int)SummaryInformation.Transform.UpdatedPlatformAndLanguage == (int)row[0]) - { - row[1] = targetPlatformAndLanguage; - } - else if ((int)SummaryInformation.Transform.ProductCodes == (int)row[0]) - { - row[1] = String.Concat(targetProductCode, targetProductVersion, ';', productCode, targetProductVersion, ';', targetUpgradeCode); - } - else if ((int)SummaryInformation.Transform.ValidationFlags == (int)row[0]) - { - row[1] = 0; - } - else if ((int)SummaryInformation.Transform.Security == (int)row[0]) - { - row[1] = "4"; - } - } - - if (!summaryRows.Contains((int)SummaryInformation.Transform.UpdatedPlatformAndLanguage)) - { - Row summaryRow = instanceSummaryInformationTable.CreateRow(null); - summaryRow[0] = (int)SummaryInformation.Transform.UpdatedPlatformAndLanguage; - summaryRow[1] = targetPlatformAndLanguage; - } - else if (!summaryRows.Contains((int)SummaryInformation.Transform.ValidationFlags)) - { - Row summaryRow = instanceSummaryInformationTable.CreateRow(null); - summaryRow[0] = (int)SummaryInformation.Transform.ValidationFlags; - summaryRow[1] = "0"; - } - else if (!summaryRows.Contains((int)SummaryInformation.Transform.Security)) - { - Row summaryRow = instanceSummaryInformationTable.CreateRow(null); - summaryRow[0] = (int)SummaryInformation.Transform.Security; - summaryRow[1] = "4"; - } - - output.SubStorages.Add(new SubStorage(instanceId, instanceTransform)); - } - } - } - - /// - /// Validate that there are no duplicate GUIDs in the output. - /// - /// - /// Duplicate GUIDs without conditions are an error condition; with conditions, it's a - /// warning, as the conditions might be mutually exclusive. - /// - private void ValidateComponentGuids(Output output) - { - Table componentTable = output.Tables["Component"]; - if (null != componentTable) - { - Dictionary componentGuidConditions = new Dictionary(componentTable.Rows.Count); - - foreach (ComponentRow row in componentTable.Rows) - { - // we don't care about unmanaged components and if there's a * GUID remaining, - // there's already an error that prevented it from being replaced with a real GUID. - if (!String.IsNullOrEmpty(row.Guid) && "*" != row.Guid) - { - bool thisComponentHasCondition = !String.IsNullOrEmpty(row.Condition); - bool allComponentsHaveConditions = thisComponentHasCondition; - - if (componentGuidConditions.ContainsKey(row.Guid)) - { - allComponentsHaveConditions = componentGuidConditions[row.Guid] && thisComponentHasCondition; - - if (allComponentsHaveConditions) - { - Messaging.Instance.OnMessage(WixWarnings.DuplicateComponentGuidsMustHaveMutuallyExclusiveConditions(row.SourceLineNumbers, row.Component, row.Guid)); - } - else - { - Messaging.Instance.OnMessage(WixErrors.DuplicateComponentGuids(row.SourceLineNumbers, row.Component, row.Guid)); - } - } - - componentGuidConditions[row.Guid] = allComponentsHaveConditions; - } - } - } - } - - /// - /// Update Control and BBControl text by reading from files when necessary. - /// - /// Internal representation of the msi database to operate upon. - private void UpdateControlText(Output output) - { - UpdateControlTextCommand command = new UpdateControlTextCommand(); - command.BBControlTable = output.Tables["BBControl"]; - command.WixBBControlTable = output.Tables["WixBBControl"]; - command.ControlTable = output.Tables["Control"]; - command.WixControlTable = output.Tables["WixControl"]; - command.Execute(); - } - - private string ResolveMedia(MediaRow mediaRow, string mediaLayoutDirectory, string layoutDirectory) - { - string layout = null; - - foreach (IBinderFileManager fileManager in this.FileManagers) - { - layout = fileManager.ResolveMedia(mediaRow, mediaLayoutDirectory, layoutDirectory); - if (!String.IsNullOrEmpty(layout)) - { - break; - } - } - - // If no binder file manager resolved the layout, do the default behavior. - if (String.IsNullOrEmpty(layout)) - { - if (String.IsNullOrEmpty(mediaLayoutDirectory)) - { - layout = layoutDirectory; - } - else if (Path.IsPathRooted(mediaLayoutDirectory)) - { - layout = mediaLayoutDirectory; - } - else - { - layout = Path.Combine(layoutDirectory, mediaLayoutDirectory); - } - } - - return layout; - } - - /// - /// Creates the MSI/MSM/PCP database. - /// - /// Output to create database for. - /// The database file to create. - /// Whether to keep columns added in a transform. - /// Whether to use a subdirectory based on the file name for intermediate files. - private void GenerateDatabase(Output output, string databaseFile, bool keepAddedColumns, bool useSubdirectory) - { - GenerateDatabaseCommand command = new GenerateDatabaseCommand(); - command.Extensions = this.Extensions; - command.FileManagers = this.FileManagers; - command.Output = output; - command.OutputPath = databaseFile; - command.KeepAddedColumns = keepAddedColumns; - command.UseSubDirectory = useSubdirectory; - command.SuppressAddingValidationRows = this.SuppressAddingValidationRows; - command.TableDefinitions = this.TableDefinitions; - command.TempFilesLocation = this.TempFilesLocation; - command.Codepage = this.Codepage; - command.Execute(); - } - } -} diff --git a/src/WixToolset.Core/Bind/BindTransformCommand.cs b/src/WixToolset.Core/Bind/BindTransformCommand.cs deleted file mode 100644 index e909f191..00000000 --- a/src/WixToolset.Core/Bind/BindTransformCommand.cs +++ /dev/null @@ -1,473 +0,0 @@ -// Copyright (c) .NET Foundation and contributors. All rights reserved. Licensed under the Microsoft Reciprocal License. See LICENSE.TXT file in the project root for full license information. - -namespace WixToolset.Bind -{ - using System; - using System.Collections.Generic; - using System.Globalization; - using System.IO; - using WixToolset.Data; - using WixToolset.Extensibility; - using WixToolset.Msi; - using WixToolset.Core.Native; - - internal class BindTransformCommand : ICommand - { - public IEnumerable Extensions { private get; set; } - - public IEnumerable FileManagers { private get; set; } - - public TableDefinitionCollection TableDefinitions { private get; set; } - - public string TempFilesLocation { private get; set; } - - public Output Transform { private get; set; } - - public string OutputPath { private get; set; } - - public void Execute() - { - int transformFlags = 0; - - Output targetOutput = new Output(null); - Output updatedOutput = new Output(null); - - // TODO: handle added columns - - // to generate a localized transform, both the target and updated - // databases need to have the same code page. the only reason to - // set different code pages is to support localized primary key - // columns, but that would only support deleting rows. if this - // becomes necessary, define a PreviousCodepage property on the - // Output class and persist this throughout transform generation. - targetOutput.Codepage = this.Transform.Codepage; - updatedOutput.Codepage = this.Transform.Codepage; - - // remove certain Property rows which will be populated from summary information values - string targetUpgradeCode = null; - string updatedUpgradeCode = null; - - Table propertyTable = this.Transform.Tables["Property"]; - if (null != propertyTable) - { - for (int i = propertyTable.Rows.Count - 1; i >= 0; i--) - { - Row row = propertyTable.Rows[i]; - - if ("ProductCode" == (string)row[0] || "ProductLanguage" == (string)row[0] || "ProductVersion" == (string)row[0] || "UpgradeCode" == (string)row[0]) - { - propertyTable.Rows.RemoveAt(i); - - if ("UpgradeCode" == (string)row[0]) - { - updatedUpgradeCode = (string)row[1]; - } - } - } - } - - Table targetSummaryInfo = targetOutput.EnsureTable(this.TableDefinitions["_SummaryInformation"]); - Table updatedSummaryInfo = updatedOutput.EnsureTable(this.TableDefinitions["_SummaryInformation"]); - Table targetPropertyTable = targetOutput.EnsureTable(this.TableDefinitions["Property"]); - Table updatedPropertyTable = updatedOutput.EnsureTable(this.TableDefinitions["Property"]); - - // process special summary information values - foreach (Row row in this.Transform.Tables["_SummaryInformation"].Rows) - { - if ((int)SummaryInformation.Transform.CodePage == (int)row[0]) - { - // convert from a web name if provided - string codePage = (string)row.Fields[1].Data; - if (null == codePage) - { - codePage = "0"; - } - else - { - codePage = Common.GetValidCodePage(codePage).ToString(CultureInfo.InvariantCulture); - } - - string previousCodePage = (string)row.Fields[1].PreviousData; - if (null == previousCodePage) - { - previousCodePage = "0"; - } - else - { - previousCodePage = Common.GetValidCodePage(previousCodePage).ToString(CultureInfo.InvariantCulture); - } - - Row targetCodePageRow = targetSummaryInfo.CreateRow(null); - targetCodePageRow[0] = 1; // PID_CODEPAGE - targetCodePageRow[1] = previousCodePage; - - Row updatedCodePageRow = updatedSummaryInfo.CreateRow(null); - updatedCodePageRow[0] = 1; // PID_CODEPAGE - updatedCodePageRow[1] = codePage; - } - else if ((int)SummaryInformation.Transform.TargetPlatformAndLanguage == (int)row[0] || - (int)SummaryInformation.Transform.UpdatedPlatformAndLanguage == (int)row[0]) - { - // the target language - string[] propertyData = ((string)row[1]).Split(';'); - string lang = 2 == propertyData.Length ? propertyData[1] : "0"; - - Table tempSummaryInfo = (int)SummaryInformation.Transform.TargetPlatformAndLanguage == (int)row[0] ? targetSummaryInfo : updatedSummaryInfo; - Table tempPropertyTable = (int)SummaryInformation.Transform.TargetPlatformAndLanguage == (int)row[0] ? targetPropertyTable : updatedPropertyTable; - - Row productLanguageRow = tempPropertyTable.CreateRow(null); - productLanguageRow[0] = "ProductLanguage"; - productLanguageRow[1] = lang; - - // set the platform;language on the MSI to be generated - Row templateRow = tempSummaryInfo.CreateRow(null); - templateRow[0] = 7; // PID_TEMPLATE - templateRow[1] = (string)row[1]; - } - else if ((int)SummaryInformation.Transform.ProductCodes == (int)row[0]) - { - string[] propertyData = ((string)row[1]).Split(';'); - - Row targetProductCodeRow = targetPropertyTable.CreateRow(null); - targetProductCodeRow[0] = "ProductCode"; - targetProductCodeRow[1] = propertyData[0].Substring(0, 38); - - Row targetProductVersionRow = targetPropertyTable.CreateRow(null); - targetProductVersionRow[0] = "ProductVersion"; - targetProductVersionRow[1] = propertyData[0].Substring(38); - - Row updatedProductCodeRow = updatedPropertyTable.CreateRow(null); - updatedProductCodeRow[0] = "ProductCode"; - updatedProductCodeRow[1] = propertyData[1].Substring(0, 38); - - Row updatedProductVersionRow = updatedPropertyTable.CreateRow(null); - updatedProductVersionRow[0] = "ProductVersion"; - updatedProductVersionRow[1] = propertyData[1].Substring(38); - - // UpgradeCode is optional and may not exists in the target - // or upgraded databases, so do not include a null-valued - // UpgradeCode property. - - targetUpgradeCode = propertyData[2]; - if (!String.IsNullOrEmpty(targetUpgradeCode)) - { - Row targetUpgradeCodeRow = targetPropertyTable.CreateRow(null); - targetUpgradeCodeRow[0] = "UpgradeCode"; - targetUpgradeCodeRow[1] = targetUpgradeCode; - - // If the target UpgradeCode is specified, an updated - // UpgradeCode is required. - if (String.IsNullOrEmpty(updatedUpgradeCode)) - { - updatedUpgradeCode = targetUpgradeCode; - } - } - - if (!String.IsNullOrEmpty(updatedUpgradeCode)) - { - Row updatedUpgradeCodeRow = updatedPropertyTable.CreateRow(null); - updatedUpgradeCodeRow[0] = "UpgradeCode"; - updatedUpgradeCodeRow[1] = updatedUpgradeCode; - } - } - else if ((int)SummaryInformation.Transform.ValidationFlags == (int)row[0]) - { - transformFlags = Convert.ToInt32(row[1], CultureInfo.InvariantCulture); - } - else if ((int)SummaryInformation.Transform.Reserved11 == (int)row[0]) - { - // PID_LASTPRINTED should be null for transforms - row.Operation = RowOperation.None; - } - else - { - // add everything else as is - Row targetRow = targetSummaryInfo.CreateRow(null); - targetRow[0] = row[0]; - targetRow[1] = row[1]; - - Row updatedRow = updatedSummaryInfo.CreateRow(null); - updatedRow[0] = row[0]; - updatedRow[1] = row[1]; - } - } - - // Validate that both databases have an UpgradeCode if the - // authoring transform will validate the UpgradeCode; otherwise, - // MsiCreateTransformSummaryinfo() will fail with 1620. - if (((int)TransformFlags.ValidateUpgradeCode & transformFlags) != 0 && - (String.IsNullOrEmpty(targetUpgradeCode) || String.IsNullOrEmpty(updatedUpgradeCode))) - { - Messaging.Instance.OnMessage(WixErrors.BothUpgradeCodesRequired()); - } - - string emptyFile = null; - - foreach (Table table in this.Transform.Tables) - { - // Ignore unreal tables when building transforms except the _Stream table. - // These tables are ignored when generating the database so there is no reason - // to process them here. - if (table.Definition.Unreal && "_Streams" != table.Name) - { - continue; - } - - // process table operations - switch (table.Operation) - { - case TableOperation.Add: - updatedOutput.EnsureTable(table.Definition); - break; - case TableOperation.Drop: - targetOutput.EnsureTable(table.Definition); - continue; - default: - targetOutput.EnsureTable(table.Definition); - updatedOutput.EnsureTable(table.Definition); - break; - } - - // process row operations - foreach (Row row in table.Rows) - { - switch (row.Operation) - { - case RowOperation.Add: - Table updatedTable = updatedOutput.EnsureTable(table.Definition); - updatedTable.Rows.Add(row); - continue; - case RowOperation.Delete: - Table targetTable = targetOutput.EnsureTable(table.Definition); - targetTable.Rows.Add(row); - - // fill-in non-primary key values - foreach (Field field in row.Fields) - { - if (!field.Column.PrimaryKey) - { - if (ColumnType.Number == field.Column.Type && !field.Column.IsLocalizable) - { - field.Data = field.Column.MinValue; - } - else if (ColumnType.Object == field.Column.Type) - { - if (null == emptyFile) - { - emptyFile = Path.Combine(this.TempFilesLocation, "empty"); - } - - field.Data = emptyFile; - } - else - { - field.Data = "0"; - } - } - } - continue; - } - - // Assure that the file table's sequence is populated - if ("File" == table.Name) - { - foreach (Row fileRow in table.Rows) - { - if (null == fileRow[7]) - { - if (RowOperation.Add == fileRow.Operation) - { - Messaging.Instance.OnMessage(WixErrors.InvalidAddedFileRowWithoutSequence(fileRow.SourceLineNumbers, (string)fileRow[0])); - break; - } - - // Set to 1 to prevent invalid IDT file from being generated - fileRow[7] = 1; - } - } - } - - // process modified and unmodified rows - bool modifiedRow = false; - Row targetRow = new Row(null, table.Definition); - Row updatedRow = row; - for (int i = 0; i < row.Fields.Length; i++) - { - Field updatedField = row.Fields[i]; - - if (updatedField.Modified) - { - // set a different value in the target row to ensure this value will be modified during transform generation - if (ColumnType.Number == updatedField.Column.Type && !updatedField.Column.IsLocalizable) - { - if (null == updatedField.Data || 1 != (int)updatedField.Data) - { - targetRow[i] = 1; - } - else - { - targetRow[i] = 2; - } - } - else if (ColumnType.Object == updatedField.Column.Type) - { - if (null == emptyFile) - { - emptyFile = Path.Combine(this.TempFilesLocation, "empty"); - } - - targetRow[i] = emptyFile; - } - else - { - if ("0" != (string)updatedField.Data) - { - targetRow[i] = "0"; - } - else - { - targetRow[i] = "1"; - } - } - - modifiedRow = true; - } - else if (ColumnType.Object == updatedField.Column.Type) - { - ObjectField objectField = (ObjectField)updatedField; - - // create an empty file for comparing against - if (null == objectField.PreviousData) - { - if (null == emptyFile) - { - emptyFile = Path.Combine(this.TempFilesLocation, "empty"); - } - - targetRow[i] = emptyFile; - modifiedRow = true; - } - else if (!this.CompareFiles(objectField.PreviousData, (string)objectField.Data)) - { - targetRow[i] = objectField.PreviousData; - modifiedRow = true; - } - } - else // unmodified - { - if (null != updatedField.Data) - { - targetRow[i] = updatedField.Data; - } - } - } - - // modified rows and certain special rows go in the target and updated msi databases - if (modifiedRow || - ("Property" == table.Name && - ("ProductCode" == (string)row[0] || - "ProductLanguage" == (string)row[0] || - "ProductVersion" == (string)row[0] || - "UpgradeCode" == (string)row[0]))) - { - Table targetTable = targetOutput.EnsureTable(table.Definition); - targetTable.Rows.Add(targetRow); - - Table updatedTable = updatedOutput.EnsureTable(table.Definition); - updatedTable.Rows.Add(updatedRow); - } - } - } - - foreach (BinderExtension extension in this.Extensions) - { - extension.Finish(this.Transform); - } - - // Any errors encountered up to this point can cause errors during generation. - if (Messaging.Instance.EncounteredError) - { - return; - } - - string transformFileName = Path.GetFileNameWithoutExtension(this.OutputPath); - string targetDatabaseFile = Path.Combine(this.TempFilesLocation, String.Concat(transformFileName, "_target.msi")); - string updatedDatabaseFile = Path.Combine(this.TempFilesLocation, String.Concat(transformFileName, "_updated.msi")); - - try - { - if (!String.IsNullOrEmpty(emptyFile)) - { - using (FileStream fileStream = File.Create(emptyFile)) - { - } - } - - this.GenerateDatabase(targetOutput, targetDatabaseFile, false); - this.GenerateDatabase(updatedOutput, updatedDatabaseFile, true); - - // make sure the directory exists - Directory.CreateDirectory(Path.GetDirectoryName(this.OutputPath)); - - // create the transform file - using (Database targetDatabase = new Database(targetDatabaseFile, OpenDatabase.ReadOnly)) - { - using (Database updatedDatabase = new Database(updatedDatabaseFile, OpenDatabase.ReadOnly)) - { - if (updatedDatabase.GenerateTransform(targetDatabase, this.OutputPath)) - { - updatedDatabase.CreateTransformSummaryInfo(targetDatabase, this.OutputPath, (TransformErrorConditions)(transformFlags & 0xFFFF), (TransformValidations)((transformFlags >> 16) & 0xFFFF)); - } - else - { - Messaging.Instance.OnMessage(WixErrors.NoDifferencesInTransform(this.Transform.SourceLineNumbers)); - } - } - } - } - finally - { - if (!String.IsNullOrEmpty(emptyFile)) - { - File.Delete(emptyFile); - } - } - } - - private bool CompareFiles(string targetFile, string updatedFile) - { - bool? compared = null; - foreach (IBinderFileManager fileManager in this.FileManagers) - { - compared = fileManager.CompareFiles(targetFile, updatedFile); - if (compared.HasValue) - { - break; - } - } - - if (!compared.HasValue) - { - throw new InvalidOperationException(); // TODO: something needs to be said here that none of the binder file managers returned a result. - } - - return compared.Value; - } - - private void GenerateDatabase(Output output, string outputPath, bool keepAddedColumns) - { - GenerateDatabaseCommand command = new GenerateDatabaseCommand(); - command.Codepage = output.Codepage; - command.Extensions = this.Extensions; - command.FileManagers = this.FileManagers; - command.KeepAddedColumns = keepAddedColumns; - command.Output = output; - command.OutputPath = outputPath; - command.TableDefinitions = this.TableDefinitions; - command.TempFilesLocation = this.TempFilesLocation; - command.SuppressAddingValidationRows = true; - command.UseSubDirectory = true; - command.Execute(); - } - } -} diff --git a/src/WixToolset.Core/Bind/Bundles/AutomaticallySlipstreamPatchesCommand.cs b/src/WixToolset.Core/Bind/Bundles/AutomaticallySlipstreamPatchesCommand.cs deleted file mode 100644 index eb02a983..00000000 --- a/src/WixToolset.Core/Bind/Bundles/AutomaticallySlipstreamPatchesCommand.cs +++ /dev/null @@ -1,112 +0,0 @@ -// Copyright (c) .NET Foundation and contributors. All rights reserved. Licensed under the Microsoft Reciprocal License. See LICENSE.TXT file in the project root for full license information. - -namespace WixToolset.Bind.Bundles -{ - using System; - using System.Collections.Generic; - using System.Diagnostics; - using System.Linq; - using WixToolset.Data; - using WixToolset.Data.Rows; - - internal class AutomaticallySlipstreamPatchesCommand : ICommand - { - public IEnumerable PackageFacades { private get; set; } - - public Table WixBundlePatchTargetCodeTable { private get; set; } - - public Table SlipstreamMspTable { private get; set; } - - public void Execute() - { - List msiPackages = new List(); - Dictionary> targetsProductCode = new Dictionary>(); - Dictionary> targetsUpgradeCode = new Dictionary>(); - - foreach (PackageFacade facade in this.PackageFacades) - { - if (WixBundlePackageType.Msi == facade.Package.Type) - { - // Keep track of all MSI packages. - msiPackages.Add(facade.MsiPackage); - } - else if (WixBundlePackageType.Msp == facade.Package.Type && facade.MspPackage.Slipstream) - { - IEnumerable patchTargetCodeRows = this.WixBundlePatchTargetCodeTable.RowsAs().Where(r => r.MspPackageId == facade.Package.WixChainItemId); - - // Index target ProductCodes and UpgradeCodes for slipstreamed MSPs. - foreach (WixBundlePatchTargetCodeRow row in patchTargetCodeRows) - { - if (row.TargetsProductCode) - { - List rows; - if (!targetsProductCode.TryGetValue(row.TargetCode, out rows)) - { - rows = new List(); - targetsProductCode.Add(row.TargetCode, rows); - } - - rows.Add(row); - } - else if (row.TargetsUpgradeCode) - { - List rows; - if (!targetsUpgradeCode.TryGetValue(row.TargetCode, out rows)) - { - rows = new List(); - targetsUpgradeCode.Add(row.TargetCode, rows); - } - } - } - } - } - - RowIndexedList slipstreamMspRows = new RowIndexedList(SlipstreamMspTable); - - // Loop through the MSI and slipstream patches targeting it. - foreach (WixBundleMsiPackageRow msi in msiPackages) - { - List rows; - if (targetsProductCode.TryGetValue(msi.ProductCode, out rows)) - { - foreach (WixBundlePatchTargetCodeRow row in rows) - { - Debug.Assert(row.TargetsProductCode); - Debug.Assert(!row.TargetsUpgradeCode); - - Row slipstreamMspRow = SlipstreamMspTable.CreateRow(row.SourceLineNumbers, false); - slipstreamMspRow[0] = msi.ChainPackageId; - slipstreamMspRow[1] = row.MspPackageId; - - if (slipstreamMspRows.TryAdd(slipstreamMspRow)) - { - SlipstreamMspTable.Rows.Add(slipstreamMspRow); - } - } - - rows = null; - } - - if (!String.IsNullOrEmpty(msi.UpgradeCode) && targetsUpgradeCode.TryGetValue(msi.UpgradeCode, out rows)) - { - foreach (WixBundlePatchTargetCodeRow row in rows) - { - Debug.Assert(!row.TargetsProductCode); - Debug.Assert(row.TargetsUpgradeCode); - - Row slipstreamMspRow = SlipstreamMspTable.CreateRow(row.SourceLineNumbers, false); - slipstreamMspRow[0] = msi.ChainPackageId; - slipstreamMspRow[1] = row.MspPackageId; - - if (slipstreamMspRows.TryAdd(slipstreamMspRow)) - { - SlipstreamMspTable.Rows.Add(slipstreamMspRow); - } - } - - rows = null; - } - } - } - } -} diff --git a/src/WixToolset.Core/Bind/Bundles/BurnCommon.cs b/src/WixToolset.Core/Bind/Bundles/BurnCommon.cs deleted file mode 100644 index 8cb07791..00000000 --- a/src/WixToolset.Core/Bind/Bundles/BurnCommon.cs +++ /dev/null @@ -1,378 +0,0 @@ -// Copyright (c) .NET Foundation and contributors. All rights reserved. Licensed under the Microsoft Reciprocal License. See LICENSE.TXT file in the project root for full license information. - -namespace WixToolset.Bind.Bundles -{ - using System; - using System.Diagnostics; - using System.IO; - using WixToolset.Data; - - /// - /// Common functionality for Burn PE Writer & Reader for the WiX toolset. - /// - /// This class encapsulates common functionality related to - /// bundled/chained setup packages. - /// - /// - internal abstract class BurnCommon : IDisposable - { - public const string BurnNamespace = "http://wixtoolset.org/schemas/v4/2008/Burn"; - public const string BurnUXContainerEmbeddedIdFormat = "u{0}"; - public const string BurnUXContainerPayloadIdFormat = "p{0}"; - public const string BurnAttachedContainerEmbeddedIdFormat = "a{0}"; - - // See WinNT.h for details about the PE format, including the - // structure and offsets for IMAGE_DOS_HEADER, IMAGE_NT_HEADERS32, - // IMAGE_FILE_HEADER, etc. - protected const UInt32 IMAGE_DOS_HEADER_SIZE = 64; - protected const UInt32 IMAGE_DOS_HEADER_OFFSET_MAGIC = 0; - protected const UInt32 IMAGE_DOS_HEADER_OFFSET_NTHEADER = 60; - - protected const UInt32 IMAGE_NT_HEADER_SIZE = 24; // signature DWORD (4) + IMAGE_FILE_HEADER (20) - protected const UInt32 IMAGE_NT_HEADER_OFFSET_SIGNATURE = 0; - protected const UInt32 IMAGE_NT_HEADER_OFFSET_NUMBEROFSECTIONS = 6; - protected const UInt32 IMAGE_NT_HEADER_OFFSET_SIZEOFOPTIONALHEADER = 20; - - protected const UInt32 IMAGE_OPTIONAL_OFFSET_CHECKSUM = 4 * 16; // checksum is 16 DWORDs into IMAGE_OPTIONAL_HEADER which is right after the IMAGE_NT_HEADER. - protected const UInt32 IMAGE_OPTIONAL_NEGATIVE_OFFSET_CERTIFICATETABLE = (IMAGE_DATA_DIRECTORY_SIZE * (IMAGE_NUMBEROF_DIRECTORY_ENTRIES - IMAGE_DIRECTORY_ENTRY_SECURITY)); - - protected const UInt32 IMAGE_SECTION_HEADER_SIZE = 40; - protected const UInt32 IMAGE_SECTION_HEADER_OFFSET_NAME = 0; - protected const UInt32 IMAGE_SECTION_HEADER_OFFSET_VIRTUALSIZE = 8; - protected const UInt32 IMAGE_SECTION_HEADER_OFFSET_SIZEOFRAWDATA = 16; - protected const UInt32 IMAGE_SECTION_HEADER_OFFSET_POINTERTORAWDATA = 20; - - protected const UInt32 IMAGE_DATA_DIRECTORY_SIZE = 8; // struct of two DWORDs. - protected const UInt32 IMAGE_DIRECTORY_ENTRY_SECURITY = 4; - protected const UInt32 IMAGE_NUMBEROF_DIRECTORY_ENTRIES = 16; - - protected const UInt16 IMAGE_DOS_SIGNATURE = 0x5A4D; - protected const UInt32 IMAGE_NT_SIGNATURE = 0x00004550; - protected const UInt64 IMAGE_SECTION_WIXBURN_NAME = 0x6E7275627869772E; // ".wixburn", as a qword. - - // The ".wixburn" section contains: - // 0- 3: magic number - // 4- 7: version - // 8-23: bundle GUID - // 24-27: engine (stub) size - // 28-31: original checksum - // 32-35: original signature offset - // 36-39: original signature size - // 40-43: container type (1 = CAB) - // 44-47: container count - // 48-51: byte count of manifest + UX container - // 52-55: byte count of attached container - protected const UInt32 BURN_SECTION_OFFSET_MAGIC = 0; - protected const UInt32 BURN_SECTION_OFFSET_VERSION = 4; - protected const UInt32 BURN_SECTION_OFFSET_BUNDLEGUID = 8; - protected const UInt32 BURN_SECTION_OFFSET_STUBSIZE = 24; - protected const UInt32 BURN_SECTION_OFFSET_ORIGINALCHECKSUM = 28; - protected const UInt32 BURN_SECTION_OFFSET_ORIGINALSIGNATUREOFFSET = 32; - protected const UInt32 BURN_SECTION_OFFSET_ORIGINALSIGNATURESIZE = 36; - protected const UInt32 BURN_SECTION_OFFSET_FORMAT = 40; - protected const UInt32 BURN_SECTION_OFFSET_COUNT = 44; - protected const UInt32 BURN_SECTION_OFFSET_UXSIZE = 48; - protected const UInt32 BURN_SECTION_OFFSET_ATTACHEDCONTAINERSIZE = 52; - protected const UInt32 BURN_SECTION_SIZE = BURN_SECTION_OFFSET_ATTACHEDCONTAINERSIZE + 4; // last field + sizeof(DWORD) - - protected const UInt32 BURN_SECTION_MAGIC = 0x00f14300; - protected const UInt32 BURN_SECTION_VERSION = 0x00000002; - - protected string fileExe; - protected UInt32 peOffset = UInt32.MaxValue; - protected UInt16 sections = UInt16.MaxValue; - protected UInt32 firstSectionOffset = UInt32.MaxValue; - protected UInt32 checksumOffset; - protected UInt32 certificateTableSignatureOffset; - protected UInt32 certificateTableSignatureSize; - protected UInt32 wixburnDataOffset = UInt32.MaxValue; - - // TODO: does this enum exist in another form somewhere? - /// - /// The types of attached containers that BurnWriter supports. - /// - public enum Container - { - Nothing = 0, - UX, - Attached - } - - /// - /// Creates a BurnCommon for re-writing a PE file. - /// - /// File to modify in-place. - /// GUID for the bundle. - public BurnCommon(string fileExe) - { - this.fileExe = fileExe; - } - - public UInt32 Checksum { get; protected set; } - public UInt32 SignatureOffset { get; protected set; } - public UInt32 SignatureSize { get; protected set; } - public UInt32 Version { get; protected set; } - public UInt32 StubSize { get; protected set; } - public UInt32 OriginalChecksum { get; protected set; } - public UInt32 OriginalSignatureOffset { get; protected set; } - public UInt32 OriginalSignatureSize { get; protected set; } - public UInt32 EngineSize { get; protected set; } - public UInt32 ContainerCount { get; protected set; } - public UInt32 UXAddress { get; protected set; } - public UInt32 UXSize { get; protected set; } - public UInt32 AttachedContainerAddress { get; protected set; } - public UInt32 AttachedContainerSize { get; protected set; } - - public void Dispose() - { - Dispose(true); - - GC.SuppressFinalize(this); - } - - /// - /// Copies one stream to another. - /// - /// Input stream. - /// Output stream. - /// Optional count of bytes to copy. 0 indicates whole input stream from current should be copied. - protected static int CopyStream(Stream input, Stream output, int size) - { - byte[] bytes = new byte[4096]; - int total = 0; - int read = 0; - do - { - read = Math.Min(bytes.Length, size - total); - read = input.Read(bytes, 0, read); - if (0 == read) - { - break; - } - - output.Write(bytes, 0, read); - total += read; - } while (0 == size || total < size); - - return total; - } - - /// - /// Initialize the common information about a Burn engine. - /// - /// Binary reader open against a Burn engine. - /// True if initialized. - protected bool Initialize(BinaryReader reader) - { - if (!GetWixburnSectionInfo(reader)) - { - return false; - } - - reader.BaseStream.Seek(this.wixburnDataOffset, SeekOrigin.Begin); - byte[] bytes = reader.ReadBytes((int)BURN_SECTION_SIZE); - UInt32 uint32 = 0; - - uint32 = BurnCommon.ReadUInt32(bytes, BURN_SECTION_OFFSET_MAGIC); - if (BURN_SECTION_MAGIC != uint32) - { - Messaging.Instance.OnMessage(WixErrors.InvalidBundle(this.fileExe)); - return false; - } - - this.Version = BurnCommon.ReadUInt32(bytes, BURN_SECTION_OFFSET_VERSION); - if (BURN_SECTION_VERSION != this.Version) - { - Messaging.Instance.OnMessage(WixErrors.BundleTooNew(this.fileExe, this.Version)); - return false; - } - - uint32 = BurnCommon.ReadUInt32(bytes, BURN_SECTION_OFFSET_FORMAT); // We only know how to deal with CABs right now - if (1 != uint32) - { - Messaging.Instance.OnMessage(WixErrors.InvalidBundle(this.fileExe)); - return false; - } - - this.StubSize = BurnCommon.ReadUInt32(bytes, BURN_SECTION_OFFSET_STUBSIZE); - this.OriginalChecksum = BurnCommon.ReadUInt32(bytes, BURN_SECTION_OFFSET_ORIGINALCHECKSUM); - this.OriginalSignatureOffset = BurnCommon.ReadUInt32(bytes, BURN_SECTION_OFFSET_ORIGINALSIGNATUREOFFSET); - this.OriginalSignatureSize = BurnCommon.ReadUInt32(bytes, BURN_SECTION_OFFSET_ORIGINALSIGNATURESIZE); - - this.ContainerCount = BurnCommon.ReadUInt32(bytes, BURN_SECTION_OFFSET_COUNT); - this.UXAddress = this.StubSize; - this.UXSize = BurnCommon.ReadUInt32(bytes, BURN_SECTION_OFFSET_UXSIZE); - - // If there is an original signature use that to determine the engine size. - if (0 < this.OriginalSignatureOffset) - { - this.EngineSize = this.OriginalSignatureOffset + this.OriginalSignatureSize; - } - else if (0 < this.SignatureOffset && 2 > this.ContainerCount) // if there is a signature and no attached containers, use the current signature. - { - this.EngineSize = this.SignatureOffset + this.SignatureSize; - } - else // just use the stub and UX container as the size of the engine. - { - this.EngineSize = this.StubSize + this.UXSize; - } - - this.AttachedContainerAddress = this.ContainerCount > 1 ? this.EngineSize : 0; - this.AttachedContainerSize = this.ContainerCount > 1 ? BurnCommon.ReadUInt32(bytes, BURN_SECTION_OFFSET_ATTACHEDCONTAINERSIZE) : 0; - - return true; - } - - protected virtual void Dispose(bool disposing) - { - } - - /// - /// Finds the ".wixburn" section in the current exe. - /// - /// true if the ".wixburn" section is successfully found; false otherwise - private bool GetWixburnSectionInfo(BinaryReader reader) - { - if (UInt32.MaxValue == this.wixburnDataOffset) - { - if (!EnsureNTHeader(reader)) - { - return false; - } - - UInt32 wixburnSectionOffset = UInt32.MaxValue; - byte[] bytes = new byte[IMAGE_SECTION_HEADER_SIZE]; - - reader.BaseStream.Seek(this.firstSectionOffset, SeekOrigin.Begin); - for (UInt16 sectionIndex = 0; sectionIndex < this.sections; ++sectionIndex) - { - reader.Read(bytes, 0, bytes.Length); - - if (IMAGE_SECTION_WIXBURN_NAME == BurnCommon.ReadUInt64(bytes, IMAGE_SECTION_HEADER_OFFSET_NAME)) - { - wixburnSectionOffset = this.firstSectionOffset + (IMAGE_SECTION_HEADER_SIZE * sectionIndex); - break; - } - } - - if (UInt32.MaxValue == wixburnSectionOffset) - { - Messaging.Instance.OnMessage(WixErrors.StubMissingWixburnSection(this.fileExe)); - return false; - } - - // we need 56 bytes for the manifest header, which is always going to fit in - // the smallest alignment (512 bytes), but just to be paranoid... - if (BURN_SECTION_SIZE > BurnCommon.ReadUInt32(bytes, IMAGE_SECTION_HEADER_OFFSET_SIZEOFRAWDATA)) - { - Messaging.Instance.OnMessage(WixErrors.StubWixburnSectionTooSmall(this.fileExe)); - return false; - } - - this.wixburnDataOffset = BurnCommon.ReadUInt32(bytes, IMAGE_SECTION_HEADER_OFFSET_POINTERTORAWDATA); - } - - return true; - } - - /// - /// Checks for a valid Windows PE signature (IMAGE_NT_SIGNATURE) in the current exe. - /// - /// true if the exe is a Windows executable; false otherwise - private bool EnsureNTHeader(BinaryReader reader) - { - if (UInt32.MaxValue == this.firstSectionOffset) - { - if (!EnsureDosHeader(reader)) - { - return false; - } - - reader.BaseStream.Seek(this.peOffset, SeekOrigin.Begin); - byte[] bytes = reader.ReadBytes((int)IMAGE_NT_HEADER_SIZE); - - // Verify the NT signature... - if (IMAGE_NT_SIGNATURE != BurnCommon.ReadUInt32(bytes, IMAGE_NT_HEADER_OFFSET_SIGNATURE)) - { - Messaging.Instance.OnMessage(WixErrors.InvalidStubExe(this.fileExe)); - return false; - } - - ushort sizeOptionalHeader = BurnCommon.ReadUInt16(bytes, IMAGE_NT_HEADER_OFFSET_SIZEOFOPTIONALHEADER); - - this.sections = BurnCommon.ReadUInt16(bytes, IMAGE_NT_HEADER_OFFSET_NUMBEROFSECTIONS); - this.firstSectionOffset = this.peOffset + IMAGE_NT_HEADER_SIZE + sizeOptionalHeader; - - this.checksumOffset = this.peOffset + IMAGE_NT_HEADER_SIZE + IMAGE_OPTIONAL_OFFSET_CHECKSUM; - this.certificateTableSignatureOffset = this.peOffset + IMAGE_NT_HEADER_SIZE + sizeOptionalHeader - IMAGE_OPTIONAL_NEGATIVE_OFFSET_CERTIFICATETABLE; - this.certificateTableSignatureSize = this.certificateTableSignatureOffset + 4; // size is in the DWORD after the offset. - - bytes = reader.ReadBytes(sizeOptionalHeader); - this.Checksum = BurnCommon.ReadUInt32(bytes, IMAGE_OPTIONAL_OFFSET_CHECKSUM); - this.SignatureOffset = BurnCommon.ReadUInt32(bytes, sizeOptionalHeader - IMAGE_OPTIONAL_NEGATIVE_OFFSET_CERTIFICATETABLE); - this.SignatureSize = BurnCommon.ReadUInt32(bytes, sizeOptionalHeader - IMAGE_OPTIONAL_NEGATIVE_OFFSET_CERTIFICATETABLE + 4); - } - - return true; - } - - /// - /// Checks for a valid DOS header in the current exe. - /// - /// true if the exe starts with a DOS stub; false otherwise - private bool EnsureDosHeader(BinaryReader reader) - { - if (UInt32.MaxValue == this.peOffset) - { - byte[] bytes = reader.ReadBytes((int)IMAGE_DOS_HEADER_SIZE); - - // Verify the DOS 'MZ' signature. - if (IMAGE_DOS_SIGNATURE != BurnCommon.ReadUInt16(bytes, IMAGE_DOS_HEADER_OFFSET_MAGIC)) - { - Messaging.Instance.OnMessage(WixErrors.InvalidStubExe(this.fileExe)); - return false; - } - - this.peOffset = BurnCommon.ReadUInt32(bytes, IMAGE_DOS_HEADER_OFFSET_NTHEADER); - } - - return true; - } - - /// - /// Reads a UInt16 value in little-endian format from an offset in an array of bytes. - /// - /// Array from which to read. - /// Beginning offset from which to read. - /// value at offset - private static UInt16 ReadUInt16(byte[] bytes, UInt32 offset) - { - Debug.Assert(offset + 2 <= bytes.Length); - return (UInt16)(bytes[offset] + (bytes[offset + 1] << 8)); - } - - /// - /// Reads a UInt32 value in little-endian format from an offset in an array of bytes. - /// - /// Array from which to read. - /// Beginning offset from which to read. - /// value at offset - private static UInt32 ReadUInt32(byte[] bytes, UInt32 offset) - { - Debug.Assert(offset + 4 <= bytes.Length); - return (UInt32)(bytes[offset] + (bytes[offset + 1] << 8) + (bytes[offset + 2] << 16) + (bytes[offset + 3] << 24)); - } - - /// - /// Reads a UInt64 value in little-endian format from an offset in an array of bytes. - /// - /// Array from which to read. - /// Beginning offset from which to read. - /// value at offset - private static UInt64 ReadUInt64(byte[] bytes, UInt32 offset) - { - Debug.Assert(offset + 8 <= bytes.Length); - return BurnCommon.ReadUInt32(bytes, offset) + ((UInt64)(BurnCommon.ReadUInt32(bytes, offset + 4)) << 32); - } - } -} diff --git a/src/WixToolset.Core/Bind/Bundles/BurnReader.cs b/src/WixToolset.Core/Bind/Bundles/BurnReader.cs deleted file mode 100644 index f6d7a197..00000000 --- a/src/WixToolset.Core/Bind/Bundles/BurnReader.cs +++ /dev/null @@ -1,210 +0,0 @@ -// Copyright (c) .NET Foundation and contributors. All rights reserved. Licensed under the Microsoft Reciprocal License. See LICENSE.TXT file in the project root for full license information. - -namespace WixToolset.Bind.Bundles -{ - using System; - using System.Collections; - using System.Collections.Generic; - using System.IO; - using System.Xml; - using WixToolset.Cab; - - /// - /// Burn PE reader for the WiX toolset. - /// - /// This class encapsulates reading from a stub EXE with containers attached - /// for dissecting bundled/chained setup packages. - /// - /// using (BurnReader reader = BurnReader.Open(fileExe, this.core, guid)) - /// { - /// reader.ExtractUXContainer(file1, tempFolder); - /// } - /// - internal class BurnReader : BurnCommon - { - private bool disposed; - - private bool invalidBundle; - private BinaryReader binaryReader; - private List attachedContainerPayloadNames; - - /// - /// Creates a BurnReader for reading a PE file. - /// - /// File to read. - private BurnReader(string fileExe) - : base(fileExe) - { - this.attachedContainerPayloadNames = new List(); - } - - /// - /// Gets the underlying stream. - /// - public Stream Stream - { - get - { - return (null != this.binaryReader) ? this.binaryReader.BaseStream : null; - } - } - - /// - /// Opens a Burn reader. - /// - /// Path to file. - /// Burn reader. - public static BurnReader Open(string fileExe) - { - BurnReader reader = new BurnReader(fileExe); - - reader.binaryReader = new BinaryReader(File.Open(fileExe, FileMode.Open, FileAccess.Read, FileShare.Read | FileShare.Delete)); - if (!reader.Initialize(reader.binaryReader)) - { - reader.invalidBundle = true; - } - - return reader; - } - - /// - /// Gets the UX container from the exe and extracts its contents to the output directory. - /// - /// Directory to write extracted files to. - /// True if successful, false otherwise - public bool ExtractUXContainer(string outputDirectory, string tempDirectory) - { - // No UX container to extract - if (this.UXAddress == 0 || this.UXSize == 0) - { - return false; - } - - if (this.invalidBundle) - { - return false; - } - - Directory.CreateDirectory(outputDirectory); - string tempCabPath = Path.Combine(tempDirectory, "ux.cab"); - string manifestOriginalPath = Path.Combine(outputDirectory, "0"); - string manifestPath = Path.Combine(outputDirectory, "manifest.xml"); - - this.binaryReader.BaseStream.Seek(this.UXAddress, SeekOrigin.Begin); - using (Stream tempCab = File.Open(tempCabPath, FileMode.Create, FileAccess.Write)) - { - BurnCommon.CopyStream(this.binaryReader.BaseStream, tempCab, (int)this.UXSize); - } - - using (WixExtractCab extract = new WixExtractCab()) - { - extract.Extract(tempCabPath, outputDirectory); - } - - Directory.CreateDirectory(Path.GetDirectoryName(manifestPath)); - File.Delete(manifestPath); - File.Move(manifestOriginalPath, manifestPath); - - XmlDocument document = new XmlDocument(); - document.Load(manifestPath); - XmlNamespaceManager namespaceManager = new XmlNamespaceManager(document.NameTable); - namespaceManager.AddNamespace("burn", BurnCommon.BurnNamespace); - XmlNodeList uxPayloads = document.SelectNodes("/burn:BurnManifest/burn:UX/burn:Payload", namespaceManager); - XmlNodeList payloads = document.SelectNodes("/burn:BurnManifest/burn:Payload", namespaceManager); - - foreach (XmlNode uxPayload in uxPayloads) - { - XmlNode sourcePathNode = uxPayload.Attributes.GetNamedItem("SourcePath"); - XmlNode filePathNode = uxPayload.Attributes.GetNamedItem("FilePath"); - - string sourcePath = Path.Combine(outputDirectory, sourcePathNode.Value); - string destinationPath = Path.Combine(outputDirectory, filePathNode.Value); - - Directory.CreateDirectory(Path.GetDirectoryName(destinationPath)); - File.Delete(destinationPath); - File.Move(sourcePath, destinationPath); - } - - foreach (XmlNode payload in payloads) - { - XmlNode sourcePathNode = payload.Attributes.GetNamedItem("SourcePath"); - XmlNode filePathNode = payload.Attributes.GetNamedItem("FilePath"); - XmlNode packagingNode = payload.Attributes.GetNamedItem("Packaging"); - - string sourcePath = sourcePathNode.Value; - string destinationPath = filePathNode.Value; - string packaging = packagingNode.Value; - - if (packaging.Equals("embedded", StringComparison.OrdinalIgnoreCase)) - { - this.attachedContainerPayloadNames.Add(new DictionaryEntry(sourcePath, destinationPath)); - } - } - - return true; - } - - /// - /// Gets the attached container from the exe and extracts its contents to the output directory. - /// - /// Directory to write extracted files to. - /// True if successful, false otherwise - public bool ExtractAttachedContainer(string outputDirectory, string tempDirectory) - { - // No attached container to extract - if (this.AttachedContainerAddress == 0 || this.AttachedContainerSize == 0) - { - return false; - } - - if (this.invalidBundle) - { - return false; - } - - Directory.CreateDirectory(outputDirectory); - string tempCabPath = Path.Combine(tempDirectory, "attached.cab"); - - this.binaryReader.BaseStream.Seek(this.AttachedContainerAddress, SeekOrigin.Begin); - using (Stream tempCab = File.Open(tempCabPath, FileMode.Create, FileAccess.Write)) - { - BurnCommon.CopyStream(this.binaryReader.BaseStream, tempCab, (int)this.AttachedContainerSize); - } - - using (WixExtractCab extract = new WixExtractCab()) - { - extract.Extract(tempCabPath, outputDirectory); - } - - foreach (DictionaryEntry entry in this.attachedContainerPayloadNames) - { - string sourcePath = Path.Combine(outputDirectory, (string)entry.Key); - string destinationPath = Path.Combine(outputDirectory, (string)entry.Value); - - Directory.CreateDirectory(Path.GetDirectoryName(destinationPath)); - File.Delete(destinationPath); - File.Move(sourcePath, destinationPath); - } - - return true; - } - - /// - /// Dispose object. - /// - /// True when releasing managed objects. - protected override void Dispose(bool disposing) - { - if (!this.disposed) - { - if (disposing && this.binaryReader != null) - { - this.binaryReader.Close(); - this.binaryReader = null; - } - - this.disposed = true; - } - } - } -} diff --git a/src/WixToolset.Core/Bind/Bundles/BurnWriter.cs b/src/WixToolset.Core/Bind/Bundles/BurnWriter.cs deleted file mode 100644 index bc0baf46..00000000 --- a/src/WixToolset.Core/Bind/Bundles/BurnWriter.cs +++ /dev/null @@ -1,239 +0,0 @@ -// Copyright (c) .NET Foundation and contributors. All rights reserved. Licensed under the Microsoft Reciprocal License. See LICENSE.TXT file in the project root for full license information. - -namespace WixToolset.Bind.Bundles -{ - using System; - using System.Diagnostics; - using System.IO; - using WixToolset.Data; - - /// - /// Burn PE writer for the WiX toolset. - /// - /// This class encapsulates reading/writing to a stub EXE for - /// creating bundled/chained setup packages. - /// - /// using (BurnWriter writer = new BurnWriter(fileExe, this.core, guid)) - /// { - /// writer.AppendContainer(file1, BurnWriter.Container.UX); - /// writer.AppendContainer(file2, BurnWriter.Container.Attached); - /// } - /// - internal class BurnWriter : BurnCommon - { - private bool disposed; - private bool invalidBundle; - private BinaryWriter binaryWriter; - - /// - /// Creates a BurnWriter for re-writing a PE file. - /// - /// File to modify in-place. - /// GUID for the bundle. - private BurnWriter(string fileExe) - : base(fileExe) - { - } - - /// - /// Opens a Burn writer. - /// - /// Path to file. - /// Burn writer. - public static BurnWriter Open(string fileExe) - { - BurnWriter writer = new BurnWriter(fileExe); - - using (BinaryReader binaryReader = new BinaryReader(File.Open(fileExe, FileMode.Open, FileAccess.Read, FileShare.Read | FileShare.Delete))) - { - if (!writer.Initialize(binaryReader)) - { - writer.invalidBundle = true; - } - } - - if (!writer.invalidBundle) - { - writer.binaryWriter = new BinaryWriter(File.Open(fileExe, FileMode.Open, FileAccess.ReadWrite, FileShare.Read | FileShare.Delete)); - } - - return writer; - } - - /// - /// Update the ".wixburn" section data. - /// - /// Size of the stub engine "burn.exe". - /// Unique identifier for this bundle. - /// - public bool InitializeBundleSectionData(long stubSize, Guid bundleId) - { - if (this.invalidBundle) - { - return false; - } - - this.WriteToBurnSectionOffset(BURN_SECTION_OFFSET_MAGIC, BURN_SECTION_MAGIC); - this.WriteToBurnSectionOffset(BURN_SECTION_OFFSET_VERSION, BURN_SECTION_VERSION); - - Messaging.Instance.OnMessage(WixVerboses.BundleGuid(bundleId.ToString("B"))); - this.binaryWriter.BaseStream.Seek(this.wixburnDataOffset + BURN_SECTION_OFFSET_BUNDLEGUID, SeekOrigin.Begin); - this.binaryWriter.Write(bundleId.ToByteArray()); - - this.StubSize = (uint)stubSize; - - this.WriteToBurnSectionOffset(BURN_SECTION_OFFSET_STUBSIZE, this.StubSize); - this.WriteToBurnSectionOffset(BURN_SECTION_OFFSET_ORIGINALCHECKSUM, 0); - this.WriteToBurnSectionOffset(BURN_SECTION_OFFSET_ORIGINALSIGNATUREOFFSET, 0); - this.WriteToBurnSectionOffset(BURN_SECTION_OFFSET_ORIGINALSIGNATURESIZE, 0); - this.WriteToBurnSectionOffset(BURN_SECTION_OFFSET_FORMAT, 1); // Hard-coded to CAB for now. - this.WriteToBurnSectionOffset(BURN_SECTION_OFFSET_COUNT, 0); - this.WriteToBurnSectionOffset(BURN_SECTION_OFFSET_UXSIZE, 0); - this.WriteToBurnSectionOffset(BURN_SECTION_OFFSET_ATTACHEDCONTAINERSIZE, 0); - this.binaryWriter.BaseStream.Flush(); - - this.EngineSize = this.StubSize; - - return true; - } - - /// - /// Appends a UX or Attached container to the exe and updates the ".wixburn" section data to point to it. - /// - /// File path to append to the current exe. - /// Container section represented by the fileContainer. - /// true if the container data is successfully appended; false otherwise - public bool AppendContainer(string fileContainer, BurnCommon.Container container) - { - using (FileStream reader = File.OpenRead(fileContainer)) - { - return this.AppendContainer(reader, reader.Length, container); - } - } - - /// - /// Appends a UX or Attached container to the exe and updates the ".wixburn" section data to point to it. - /// - /// File stream to append to the current exe. - /// Size of container to append. - /// Container section represented by the fileContainer. - /// true if the container data is successfully appended; false otherwise - public bool AppendContainer(Stream containerStream, long containerSize, BurnCommon.Container container) - { - UInt32 burnSectionCount = 0; - UInt32 burnSectionOffsetSize = 0; - - switch (container) - { - case Container.UX: - burnSectionCount = 1; - burnSectionOffsetSize = BURN_SECTION_OFFSET_UXSIZE; - // TODO: verify that the size in the section data is 0 or the same size. - this.EngineSize += (uint)containerSize; - this.UXSize = (uint)containerSize; - break; - - case Container.Attached: - burnSectionCount = 2; - burnSectionOffsetSize = BURN_SECTION_OFFSET_ATTACHEDCONTAINERSIZE; - // TODO: verify that the size in the section data is 0 or the same size. - this.AttachedContainerSize = (uint)containerSize; - break; - - default: - Debug.Assert(false); - return false; - } - - return AppendContainer(containerStream, (UInt32)containerSize, burnSectionOffsetSize, burnSectionCount); - } - - public void RememberThenResetSignature() - { - if (this.invalidBundle) - { - return; - } - - this.OriginalChecksum = this.Checksum; - this.OriginalSignatureOffset = this.SignatureOffset; - this.OriginalSignatureSize = this.SignatureSize; - - this.WriteToBurnSectionOffset(BURN_SECTION_OFFSET_ORIGINALCHECKSUM, this.OriginalChecksum); - this.WriteToBurnSectionOffset(BURN_SECTION_OFFSET_ORIGINALSIGNATUREOFFSET, this.OriginalSignatureOffset); - this.WriteToBurnSectionOffset(BURN_SECTION_OFFSET_ORIGINALSIGNATURESIZE, this.OriginalSignatureSize); - - this.Checksum = 0; - this.SignatureOffset = 0; - this.SignatureSize = 0; - - this.WriteToOffset(this.checksumOffset, this.Checksum); - this.WriteToOffset(this.certificateTableSignatureOffset, this.SignatureOffset); - this.WriteToOffset(this.certificateTableSignatureSize, this.SignatureSize); - } - - /// - /// Dispose object. - /// - /// True when releasing managed objects. - protected override void Dispose(bool disposing) - { - if (!this.disposed) - { - if (disposing && this.binaryWriter != null) - { - this.binaryWriter.Close(); - this.binaryWriter = null; - } - - this.disposed = true; - } - } - - /// - /// Appends a container to the exe and updates the ".wixburn" section data to point to it. - /// - /// File stream to append to the current exe. - /// Offset of size field for this container in ".wixburn" section data. - /// true if the container data is successfully appended; false otherwise - private bool AppendContainer(Stream containerStream, UInt32 containerSize, UInt32 burnSectionOffsetSize, UInt32 burnSectionCount) - { - if (this.invalidBundle) - { - return false; - } - - // Update the ".wixburn" section data - this.WriteToBurnSectionOffset(BURN_SECTION_OFFSET_COUNT, burnSectionCount); - this.WriteToBurnSectionOffset(burnSectionOffsetSize, containerSize); - - // Append the container to the end of the existing bits. - this.binaryWriter.BaseStream.Seek(0, SeekOrigin.End); - BurnCommon.CopyStream(containerStream, this.binaryWriter.BaseStream, (int)containerSize); - this.binaryWriter.BaseStream.Flush(); - - return true; - } - - /// - /// Writes the value to an offset in the Burn section data. - /// - /// Offset in to the Burn section data. - /// Value to write. - private void WriteToBurnSectionOffset(uint offset, uint value) - { - this.WriteToOffset(this.wixburnDataOffset + offset, value); - } - - /// - /// Writes the value to an offset in the Burn stub. - /// - /// Offset in to the Burn stub. - /// Value to write. - private void WriteToOffset(uint offset, uint value) - { - this.binaryWriter.BaseStream.Seek((int)offset, SeekOrigin.Begin); - this.binaryWriter.Write(value); - } - } -} diff --git a/src/WixToolset.Core/Bind/Bundles/CreateBootstrapperApplicationManifestCommand.cs b/src/WixToolset.Core/Bind/Bundles/CreateBootstrapperApplicationManifestCommand.cs deleted file mode 100644 index 1040b394..00000000 --- a/src/WixToolset.Core/Bind/Bundles/CreateBootstrapperApplicationManifestCommand.cs +++ /dev/null @@ -1,241 +0,0 @@ -// Copyright (c) .NET Foundation and contributors. All rights reserved. Licensed under the Microsoft Reciprocal License. See LICENSE.TXT file in the project root for full license information. - -namespace WixToolset.Bind.Bundles -{ - using System; - using System.Collections.Generic; - using System.Diagnostics; - using System.Globalization; - using System.IO; - using System.Text; - using System.Xml; - using WixToolset.Data; - using WixToolset.Data.Rows; - - internal class CreateBootstrapperApplicationManifestCommand : ICommand - { - public WixBundleRow BundleRow { private get; set; } - - public IEnumerable ChainPackages { private get; set; } - - public int LastUXPayloadIndex { private get; set; } - - public IEnumerable MsiFeatures { private get; set; } - - public Output Output { private get; set; } - - public RowDictionary Payloads { private get; set; } - - public TableDefinitionCollection TableDefinitions { private get; set; } - - public string TempFilesLocation { private get; set; } - - public WixBundlePayloadRow BootstrapperApplicationManifestPayloadRow { get; private set; } - - public void Execute() - { - this.GenerateBAManifestBundleTables(); - - this.GenerateBAManifestMsiFeatureTables(); - - this.GenerateBAManifestPackageTables(); - - this.GenerateBAManifestPayloadTables(); - - string baManifestPath = Path.Combine(this.TempFilesLocation, "wix-badata.xml"); - - this.CreateBootstrapperApplicationManifest(baManifestPath); - - this.BootstrapperApplicationManifestPayloadRow = this.CreateBootstrapperApplicationManifestPayloadRow(baManifestPath); - } - - private void GenerateBAManifestBundleTables() - { - Table wixBundlePropertiesTable = this.Output.EnsureTable(this.TableDefinitions["WixBundleProperties"]); - - Row row = wixBundlePropertiesTable.CreateRow(this.BundleRow.SourceLineNumbers); - row[0] = this.BundleRow.Name; - row[1] = this.BundleRow.LogPathVariable; - row[2] = (YesNoDefaultType.Yes == this.BundleRow.Compressed) ? "yes" : "no"; - row[3] = this.BundleRow.BundleId.ToString("B"); - row[4] = this.BundleRow.UpgradeCode; - row[5] = this.BundleRow.PerMachine ? "yes" : "no"; - } - - private void GenerateBAManifestPackageTables() - { - Table wixPackagePropertiesTable = this.Output.EnsureTable(this.TableDefinitions["WixPackageProperties"]); - - foreach (PackageFacade package in this.ChainPackages) - { - WixBundlePayloadRow packagePayload = this.Payloads[package.Package.PackagePayload]; - - Row row = wixPackagePropertiesTable.CreateRow(package.Package.SourceLineNumbers); - row[0] = package.Package.WixChainItemId; - row[1] = (YesNoType.Yes == package.Package.Vital) ? "yes" : "no"; - row[2] = package.Package.DisplayName; - row[3] = package.Package.Description; - row[4] = package.Package.Size.ToString(CultureInfo.InvariantCulture); // TODO: DownloadSize (compressed) (what does this mean when it's embedded?) - row[5] = package.Package.Size.ToString(CultureInfo.InvariantCulture); // Package.Size (uncompressed) - row[6] = package.Package.InstallSize.Value.ToString(CultureInfo.InvariantCulture); // InstallSize (required disk space) - row[7] = package.Package.Type.ToString(); - row[8] = package.Package.Permanent ? "yes" : "no"; - row[9] = package.Package.LogPathVariable; - row[10] = package.Package.RollbackLogPathVariable; - row[11] = (PackagingType.Embedded == packagePayload.Packaging) ? "yes" : "no"; - - if (WixBundlePackageType.Msi == package.Package.Type) - { - row[12] = package.MsiPackage.DisplayInternalUI ? "yes" : "no"; - - if (!String.IsNullOrEmpty(package.MsiPackage.ProductCode)) - { - row[13] = package.MsiPackage.ProductCode; - } - - if (!String.IsNullOrEmpty(package.MsiPackage.UpgradeCode)) - { - row[14] = package.MsiPackage.UpgradeCode; - } - } - else if (WixBundlePackageType.Msp == package.Package.Type) - { - row[12] = package.MspPackage.DisplayInternalUI ? "yes" : "no"; - - if (!String.IsNullOrEmpty(package.MspPackage.PatchCode)) - { - row[13] = package.MspPackage.PatchCode; - } - } - - if (!String.IsNullOrEmpty(package.Package.Version)) - { - row[15] = package.Package.Version; - } - - if (!String.IsNullOrEmpty(package.Package.InstallCondition)) - { - row[16] = package.Package.InstallCondition; - } - - switch (package.Package.Cache) - { - case YesNoAlwaysType.No: - row[17] = "no"; - break; - case YesNoAlwaysType.Yes: - row[17] = "yes"; - break; - case YesNoAlwaysType.Always: - row[17] = "always"; - break; - } - } - } - - private void GenerateBAManifestMsiFeatureTables() - { - Table wixPackageFeatureInfoTable = this.Output.EnsureTable(this.TableDefinitions["WixPackageFeatureInfo"]); - - foreach (WixBundleMsiFeatureRow feature in this.MsiFeatures) - { - Row row = wixPackageFeatureInfoTable.CreateRow(feature.SourceLineNumbers); - row[0] = feature.ChainPackageId; - row[1] = feature.Name; - row[2] = Convert.ToString(feature.Size, CultureInfo.InvariantCulture); - row[3] = feature.Parent; - row[4] = feature.Title; - row[5] = feature.Description; - row[6] = Convert.ToString(feature.Display, CultureInfo.InvariantCulture); - row[7] = Convert.ToString(feature.Level, CultureInfo.InvariantCulture); - row[8] = feature.Directory; - row[9] = Convert.ToString(feature.Attributes, CultureInfo.InvariantCulture); - } - - } - - private void GenerateBAManifestPayloadTables() - { - Table wixPayloadPropertiesTable = this.Output.EnsureTable(this.TableDefinitions["WixPayloadProperties"]); - - foreach (WixBundlePayloadRow payload in this.Payloads.Values) - { - WixPayloadPropertiesRow row = (WixPayloadPropertiesRow)wixPayloadPropertiesTable.CreateRow(payload.SourceLineNumbers); - row.Id = payload.Id; - row.Package = payload.Package; - row.Container = payload.Container; - row.Name = payload.Name; - row.Size = payload.FileSize.ToString(); - row.DownloadUrl = payload.DownloadUrl; - row.LayoutOnly = payload.LayoutOnly ? "yes" : "no"; - } - } - - private void CreateBootstrapperApplicationManifest(string path) - { - using (XmlTextWriter writer = new XmlTextWriter(path, Encoding.Unicode)) - { - writer.Formatting = Formatting.Indented; - writer.WriteStartDocument(); - writer.WriteStartElement("BootstrapperApplicationData", "http://wixtoolset.org/schemas/v4/2010/BootstrapperApplicationData"); - - foreach (Table table in this.Output.Tables) - { - if (table.Definition.BootstrapperApplicationData) - { - // We simply assert that the table (and field) name is valid, because - // this is up to the extension developer to get right. An author will - // only affect the attribute value, and that will get properly escaped. -#if DEBUG - Debug.Assert(Common.IsIdentifier(table.Name)); - foreach (ColumnDefinition column in table.Definition.Columns) - { - Debug.Assert(Common.IsIdentifier(column.Name)); - } -#endif // DEBUG - - foreach (Row row in table.Rows) - { - writer.WriteStartElement(table.Name); - - foreach (Field field in row.Fields) - { - if (null != field.Data) - { - writer.WriteAttributeString(field.Column.Name, field.Data.ToString()); - } - } - - writer.WriteEndElement(); - } - } - } - - writer.WriteEndElement(); - writer.WriteEndDocument(); - } - } - - private WixBundlePayloadRow CreateBootstrapperApplicationManifestPayloadRow(string baManifestPath) - { - Table payloadTable = this.Output.EnsureTable(this.TableDefinitions["WixBundlePayload"]); - WixBundlePayloadRow row = (WixBundlePayloadRow)payloadTable.CreateRow(this.BundleRow.SourceLineNumbers); - row.Id = Common.GenerateIdentifier("ux", "BootstrapperApplicationData.xml"); - row.Name = "BootstrapperApplicationData.xml"; - row.SourceFile = baManifestPath; - row.Compressed = YesNoDefaultType.Yes; - row.UnresolvedSourceFile = baManifestPath; - row.Container = Compiler.BurnUXContainerId; - row.EmbeddedId = String.Format(CultureInfo.InvariantCulture, BurnCommon.BurnUXContainerEmbeddedIdFormat, this.LastUXPayloadIndex); - row.Packaging = PackagingType.Embedded; - - FileInfo fileInfo = new FileInfo(row.SourceFile); - - row.FileSize = (int)fileInfo.Length; - - row.Hash = Common.GetFileHash(fileInfo.FullName); - - return row; - } - } -} diff --git a/src/WixToolset.Core/Bind/Bundles/CreateBurnManifestCommand.cs b/src/WixToolset.Core/Bind/Bundles/CreateBurnManifestCommand.cs deleted file mode 100644 index 7bc708a3..00000000 --- a/src/WixToolset.Core/Bind/Bundles/CreateBurnManifestCommand.cs +++ /dev/null @@ -1,686 +0,0 @@ -// Copyright (c) .NET Foundation and contributors. All rights reserved. Licensed under the Microsoft Reciprocal License. See LICENSE.TXT file in the project root for full license information. - -namespace WixToolset.Bind.Bundles -{ - using System; - using System.Collections.Generic; - using System.Diagnostics; - using System.Globalization; - using System.Linq; - using System.Text; - using System.Xml; - using WixToolset.Data; - using WixToolset.Data.Rows; - using WixToolset.Extensibility; - - internal class CreateBurnManifestCommand : ICommand - { - public IEnumerable FileManagers { private get; set; } - - public Output Output { private get; set; } - - public string ExecutableName { private get; set; } - - public WixBundleRow BundleInfo { private get; set; } - - public WixChainRow Chain { private get; set; } - - public string OutputPath { private get; set; } - - public IEnumerable RollbackBoundaries { private get; set; } - - public IEnumerable OrderedPackages { private get; set; } - - public IEnumerable OrderedSearches { private get; set; } - - public Dictionary Payloads { private get; set; } - - public Dictionary Containers { private get; set; } - - public IEnumerable UXContainerPayloads { private get; set; } - - public IEnumerable Catalogs { private get; set; } - - public void Execute() - { - using (XmlTextWriter writer = new XmlTextWriter(this.OutputPath, Encoding.UTF8)) - { - writer.WriteStartDocument(); - - writer.WriteStartElement("BurnManifest", BurnCommon.BurnNamespace); - - // Write the condition, if there is one - if (null != this.BundleInfo.Condition) - { - writer.WriteElementString("Condition", this.BundleInfo.Condition); - } - - // Write the log element if default logging wasn't disabled. - if (!String.IsNullOrEmpty(this.BundleInfo.LogPrefix)) - { - writer.WriteStartElement("Log"); - if (!String.IsNullOrEmpty(this.BundleInfo.LogPathVariable)) - { - writer.WriteAttributeString("PathVariable", this.BundleInfo.LogPathVariable); - } - writer.WriteAttributeString("Prefix", this.BundleInfo.LogPrefix); - writer.WriteAttributeString("Extension", this.BundleInfo.LogExtension); - writer.WriteEndElement(); - } - - - // Get update if specified. - WixBundleUpdateRow updateRow = this.Output.Tables["WixBundleUpdate"].RowsAs().FirstOrDefault(); - - if (null != updateRow) - { - writer.WriteStartElement("Update"); - writer.WriteAttributeString("Location", updateRow.Location); - writer.WriteEndElement(); // - } - - // Write the RelatedBundle elements - - // For the related bundles with duplicated identifiers the second instance is ignored (i.e. the Duplicates - // enumeration in the index row list is not used). - RowIndexedList relatedBundles = new RowIndexedList(this.Output.Tables["WixRelatedBundle"]); - - foreach (WixRelatedBundleRow relatedBundle in relatedBundles) - { - writer.WriteStartElement("RelatedBundle"); - writer.WriteAttributeString("Id", relatedBundle.Id); - writer.WriteAttributeString("Action", Convert.ToString(relatedBundle.Action, CultureInfo.InvariantCulture)); - writer.WriteEndElement(); - } - - // Write the variables - IEnumerable variables = this.Output.Tables["WixBundleVariable"].RowsAs(); - - foreach (WixBundleVariableRow variable in variables) - { - writer.WriteStartElement("Variable"); - writer.WriteAttributeString("Id", variable.Id); - if (null != variable.Type) - { - writer.WriteAttributeString("Value", variable.Value); - writer.WriteAttributeString("Type", variable.Type); - } - writer.WriteAttributeString("Hidden", variable.Hidden ? "yes" : "no"); - writer.WriteAttributeString("Persisted", variable.Persisted ? "yes" : "no"); - writer.WriteEndElement(); - } - - // Write the searches - foreach (WixSearchInfo searchinfo in this.OrderedSearches) - { - searchinfo.WriteXml(writer); - } - - // write the UX element - writer.WriteStartElement("UX"); - if (!String.IsNullOrEmpty(this.BundleInfo.SplashScreenBitmapPath)) - { - writer.WriteAttributeString("SplashScreen", "yes"); - } - - // write the UX allPayloads... - foreach (WixBundlePayloadRow payload in this.UXContainerPayloads) - { - writer.WriteStartElement("Payload"); - this.WriteBurnManifestPayloadAttributes(writer, payload, true, this.Payloads); - writer.WriteEndElement(); - } - - writer.WriteEndElement(); // - - // write the catalog elements - if (this.Catalogs.Any()) - { - foreach (WixBundleCatalogRow catalog in this.Catalogs) - { - writer.WriteStartElement("Catalog"); - writer.WriteAttributeString("Id", catalog.Id); - writer.WriteAttributeString("Payload", catalog.Payload); - writer.WriteEndElement(); - } - } - - foreach (WixBundleContainerRow container in this.Containers.Values) - { - if (!String.IsNullOrEmpty(container.WorkingPath) && Compiler.BurnUXContainerId != container.Id) - { - writer.WriteStartElement("Container"); - this.WriteBurnManifestContainerAttributes(writer, this.ExecutableName, container); - writer.WriteEndElement(); - } - } - - foreach (WixBundlePayloadRow payload in this.Payloads.Values) - { - if (PackagingType.Embedded == payload.Packaging && Compiler.BurnUXContainerId != payload.Container) - { - writer.WriteStartElement("Payload"); - this.WriteBurnManifestPayloadAttributes(writer, payload, true, this.Payloads); - writer.WriteEndElement(); - } - else if (PackagingType.External == payload.Packaging) - { - writer.WriteStartElement("Payload"); - this.WriteBurnManifestPayloadAttributes(writer, payload, false, this.Payloads); - writer.WriteEndElement(); - } - } - - foreach (WixBundleRollbackBoundaryRow rollbackBoundary in this.RollbackBoundaries) - { - writer.WriteStartElement("RollbackBoundary"); - writer.WriteAttributeString("Id", rollbackBoundary.ChainPackageId); - writer.WriteAttributeString("Vital", YesNoType.Yes == rollbackBoundary.Vital ? "yes" : "no"); - writer.WriteAttributeString("Transaction", YesNoType.Yes == rollbackBoundary.Transaction ? "yes" : "no"); - writer.WriteEndElement(); - } - - // Write the registration information... - writer.WriteStartElement("Registration"); - - writer.WriteAttributeString("Id", this.BundleInfo.BundleId.ToString("B")); - writer.WriteAttributeString("ExecutableName", this.ExecutableName); - writer.WriteAttributeString("PerMachine", this.BundleInfo.PerMachine ? "yes" : "no"); - writer.WriteAttributeString("Tag", this.BundleInfo.Tag); - writer.WriteAttributeString("Version", this.BundleInfo.Version); - writer.WriteAttributeString("ProviderKey", this.BundleInfo.ProviderKey); - - writer.WriteStartElement("Arp"); - writer.WriteAttributeString("Register", (0 < this.BundleInfo.DisableModify && this.BundleInfo.DisableRemove) ? "no" : "yes"); // do not register if disabled modify and remove. - writer.WriteAttributeString("DisplayName", this.BundleInfo.Name); - writer.WriteAttributeString("DisplayVersion", this.BundleInfo.Version); - - if (!String.IsNullOrEmpty(this.BundleInfo.Publisher)) - { - writer.WriteAttributeString("Publisher", this.BundleInfo.Publisher); - } - - if (!String.IsNullOrEmpty(this.BundleInfo.HelpLink)) - { - writer.WriteAttributeString("HelpLink", this.BundleInfo.HelpLink); - } - - if (!String.IsNullOrEmpty(this.BundleInfo.HelpTelephone)) - { - writer.WriteAttributeString("HelpTelephone", this.BundleInfo.HelpTelephone); - } - - if (!String.IsNullOrEmpty(this.BundleInfo.AboutUrl)) - { - writer.WriteAttributeString("AboutUrl", this.BundleInfo.AboutUrl); - } - - if (!String.IsNullOrEmpty(this.BundleInfo.UpdateUrl)) - { - writer.WriteAttributeString("UpdateUrl", this.BundleInfo.UpdateUrl); - } - - if (!String.IsNullOrEmpty(this.BundleInfo.ParentName)) - { - writer.WriteAttributeString("ParentDisplayName", this.BundleInfo.ParentName); - } - - if (1 == this.BundleInfo.DisableModify) - { - writer.WriteAttributeString("DisableModify", "yes"); - } - else if (2 == this.BundleInfo.DisableModify) - { - writer.WriteAttributeString("DisableModify", "button"); - } - - if (this.BundleInfo.DisableRemove) - { - writer.WriteAttributeString("DisableRemove", "yes"); - } - writer.WriteEndElement(); // - - // Get update registration if specified. - WixUpdateRegistrationRow updateRegistrationInfo = this.Output.Tables["WixUpdateRegistration"].RowsAs().FirstOrDefault(); - - if (null != updateRegistrationInfo) - { - writer.WriteStartElement("Update"); // - writer.WriteAttributeString("Manufacturer", updateRegistrationInfo.Manufacturer); - - if (!String.IsNullOrEmpty(updateRegistrationInfo.Department)) - { - writer.WriteAttributeString("Department", updateRegistrationInfo.Department); - } - - if (!String.IsNullOrEmpty(updateRegistrationInfo.ProductFamily)) - { - writer.WriteAttributeString("ProductFamily", updateRegistrationInfo.ProductFamily); - } - - writer.WriteAttributeString("Name", updateRegistrationInfo.Name); - writer.WriteAttributeString("Classification", updateRegistrationInfo.Classification); - writer.WriteEndElement(); // - } - - IEnumerable bundleTags = this.Output.Tables["WixBundleTag"].RowsAs(); - - foreach (Row row in bundleTags) - { - writer.WriteStartElement("SoftwareTag"); - writer.WriteAttributeString("Filename", (string)row[0]); - writer.WriteAttributeString("Regid", (string)row[1]); - writer.WriteCData((string)row[4]); - writer.WriteEndElement(); - } - - writer.WriteEndElement(); // - - // write the Chain... - writer.WriteStartElement("Chain"); - if (this.Chain.DisableRollback) - { - writer.WriteAttributeString("DisableRollback", "yes"); - } - - if (this.Chain.DisableSystemRestore) - { - writer.WriteAttributeString("DisableSystemRestore", "yes"); - } - - if (this.Chain.ParallelCache) - { - writer.WriteAttributeString("ParallelCache", "yes"); - } - - // Index a few tables by package. - ILookup targetCodesByPatch = this.Output.Tables["WixBundlePatchTargetCode"].RowsAs().ToLookup(r => r.MspPackageId); - ILookup msiFeaturesByPackage = this.Output.Tables["WixBundleMsiFeature"].RowsAs().ToLookup(r => r.ChainPackageId); - ILookup msiPropertiesByPackage = this.Output.Tables["WixBundleMsiProperty"].RowsAs().ToLookup(r => r.ChainPackageId); - ILookup payloadsByPackage = this.Payloads.Values.ToLookup(p => p.Package); - ILookup relatedPackagesByPackage = this.Output.Tables["WixBundleRelatedPackage"].RowsAs().ToLookup(r => r.ChainPackageId); - ILookup slipstreamMspsByPackage = this.Output.Tables["WixBundleSlipstreamMsp"].RowsAs().ToLookup(r => r.ChainPackageId); - ILookup exitCodesByPackage = this.Output.Tables["WixBundlePackageExitCode"].RowsAs().ToLookup(r => r.ChainPackageId); - ILookup commandLinesByPackage = this.Output.Tables["WixBundlePackageCommandLine"].RowsAs().ToLookup(r => r.ChainPackageId); - - // Build up the list of target codes from all the MSPs in the chain. - List targetCodes = new List(); - - foreach (PackageFacade package in this.OrderedPackages) - { - writer.WriteStartElement(String.Format(CultureInfo.InvariantCulture, "{0}Package", package.Package.Type)); - - writer.WriteAttributeString("Id", package.Package.WixChainItemId); - - switch (package.Package.Cache) - { - case YesNoAlwaysType.No: - writer.WriteAttributeString("Cache", "no"); - break; - case YesNoAlwaysType.Yes: - writer.WriteAttributeString("Cache", "yes"); - break; - case YesNoAlwaysType.Always: - writer.WriteAttributeString("Cache", "always"); - break; - } - - writer.WriteAttributeString("CacheId", package.Package.CacheId); - writer.WriteAttributeString("InstallSize", Convert.ToString(package.Package.InstallSize)); - writer.WriteAttributeString("Size", Convert.ToString(package.Package.Size)); - writer.WriteAttributeString("PerMachine", YesNoDefaultType.Yes == package.Package.PerMachine ? "yes" : "no"); - writer.WriteAttributeString("Permanent", package.Package.Permanent ? "yes" : "no"); - writer.WriteAttributeString("Vital", (YesNoType.Yes == package.Package.Vital) ? "yes" : "no"); - - if (null != package.Package.RollbackBoundary) - { - writer.WriteAttributeString("RollbackBoundaryForward", package.Package.RollbackBoundary); - } - - if (!String.IsNullOrEmpty(package.Package.RollbackBoundaryBackward)) - { - writer.WriteAttributeString("RollbackBoundaryBackward", package.Package.RollbackBoundaryBackward); - } - - if (!String.IsNullOrEmpty(package.Package.LogPathVariable)) - { - writer.WriteAttributeString("LogPathVariable", package.Package.LogPathVariable); - } - - if (!String.IsNullOrEmpty(package.Package.RollbackLogPathVariable)) - { - writer.WriteAttributeString("RollbackLogPathVariable", package.Package.RollbackLogPathVariable); - } - - if (!String.IsNullOrEmpty(package.Package.InstallCondition)) - { - writer.WriteAttributeString("InstallCondition", package.Package.InstallCondition); - } - - if (WixBundlePackageType.Exe == package.Package.Type) - { - writer.WriteAttributeString("DetectCondition", package.ExePackage.DetectCondition); - writer.WriteAttributeString("InstallArguments", package.ExePackage.InstallCommand); - writer.WriteAttributeString("UninstallArguments", package.ExePackage.UninstallCommand); - writer.WriteAttributeString("RepairArguments", package.ExePackage.RepairCommand); - writer.WriteAttributeString("Repairable", package.ExePackage.Repairable ? "yes" : "no"); - if (!String.IsNullOrEmpty(package.ExePackage.ExeProtocol)) - { - writer.WriteAttributeString("Protocol", package.ExePackage.ExeProtocol); - } - } - else if (WixBundlePackageType.Msi == package.Package.Type) - { - writer.WriteAttributeString("ProductCode", package.MsiPackage.ProductCode); - writer.WriteAttributeString("Language", package.MsiPackage.ProductLanguage.ToString(CultureInfo.InvariantCulture)); - writer.WriteAttributeString("Version", package.MsiPackage.ProductVersion); - writer.WriteAttributeString("DisplayInternalUI", package.MsiPackage.DisplayInternalUI ? "yes" : "no"); - if (!String.IsNullOrEmpty(package.MsiPackage.UpgradeCode)) - { - writer.WriteAttributeString("UpgradeCode", package.MsiPackage.UpgradeCode); - } - } - else if (WixBundlePackageType.Msp == package.Package.Type) - { - writer.WriteAttributeString("PatchCode", package.MspPackage.PatchCode); - writer.WriteAttributeString("PatchXml", package.MspPackage.PatchXml); - writer.WriteAttributeString("DisplayInternalUI", package.MspPackage.DisplayInternalUI ? "yes" : "no"); - - // If there is still a chance that all of our patches will target a narrow set of - // product codes, add the patch list to the overall list. - if (null != targetCodes) - { - if (!package.MspPackage.TargetUnspecified) - { - IEnumerable patchTargetCodes = targetCodesByPatch[package.MspPackage.ChainPackageId]; - - targetCodes.AddRange(patchTargetCodes); - } - else // we have a patch that targets the world, so throw the whole list away. - { - targetCodes = null; - } - } - } - else if (WixBundlePackageType.Msu == package.Package.Type) - { - writer.WriteAttributeString("DetectCondition", package.MsuPackage.DetectCondition); - writer.WriteAttributeString("KB", package.MsuPackage.MsuKB); - } - - IEnumerable packageMsiFeatures = msiFeaturesByPackage[package.Package.WixChainItemId]; - - foreach (WixBundleMsiFeatureRow feature in packageMsiFeatures) - { - writer.WriteStartElement("MsiFeature"); - writer.WriteAttributeString("Id", feature.Name); - writer.WriteEndElement(); - } - - IEnumerable packageMsiProperties = msiPropertiesByPackage[package.Package.WixChainItemId]; - - foreach (WixBundleMsiPropertyRow msiProperty in packageMsiProperties) - { - writer.WriteStartElement("MsiProperty"); - writer.WriteAttributeString("Id", msiProperty.Name); - writer.WriteAttributeString("Value", msiProperty.Value); - if (!String.IsNullOrEmpty(msiProperty.Condition)) - { - writer.WriteAttributeString("Condition", msiProperty.Condition); - } - writer.WriteEndElement(); - } - - IEnumerable packageSlipstreamMsps = slipstreamMspsByPackage[package.Package.WixChainItemId]; - - foreach (WixBundleSlipstreamMspRow slipstreamMsp in packageSlipstreamMsps) - { - writer.WriteStartElement("SlipstreamMsp"); - writer.WriteAttributeString("Id", slipstreamMsp.MspPackageId); - writer.WriteEndElement(); - } - - IEnumerable packageExitCodes = exitCodesByPackage[package.Package.WixChainItemId]; - - foreach (WixBundlePackageExitCodeRow exitCode in packageExitCodes) - { - writer.WriteStartElement("ExitCode"); - - if (exitCode.Code.HasValue) - { - writer.WriteAttributeString("Code", unchecked((uint)exitCode.Code).ToString(CultureInfo.InvariantCulture)); - } - else - { - writer.WriteAttributeString("Code", "*"); - } - - writer.WriteAttributeString("Type", ((int)exitCode.Behavior).ToString(CultureInfo.InvariantCulture)); - writer.WriteEndElement(); - } - - IEnumerable packageCommandLines = commandLinesByPackage[package.Package.WixChainItemId]; - - foreach (WixBundlePackageCommandLineRow commandLine in packageCommandLines) - { - writer.WriteStartElement("CommandLine"); - writer.WriteAttributeString("InstallArgument", commandLine.InstallArgument); - writer.WriteAttributeString("UninstallArgument", commandLine.UninstallArgument); - writer.WriteAttributeString("RepairArgument", commandLine.RepairArgument); - writer.WriteAttributeString("Condition", commandLine.Condition); - writer.WriteEndElement(); - } - - // Output the dependency information. - foreach (ProvidesDependency dependency in package.Provides) - { - // TODO: Add to wixpdb as an imported table, or link package wixpdbs to bundle wixpdbs. - dependency.WriteXml(writer); - } - - IEnumerable packageRelatedPackages = relatedPackagesByPackage[package.Package.WixChainItemId]; - - foreach (WixBundleRelatedPackageRow related in packageRelatedPackages) - { - writer.WriteStartElement("RelatedPackage"); - writer.WriteAttributeString("Id", related.Id); - if (!String.IsNullOrEmpty(related.MinVersion)) - { - writer.WriteAttributeString("MinVersion", related.MinVersion); - writer.WriteAttributeString("MinInclusive", related.MinInclusive ? "yes" : "no"); - } - if (!String.IsNullOrEmpty(related.MaxVersion)) - { - writer.WriteAttributeString("MaxVersion", related.MaxVersion); - writer.WriteAttributeString("MaxInclusive", related.MaxInclusive ? "yes" : "no"); - } - writer.WriteAttributeString("OnlyDetect", related.OnlyDetect ? "yes" : "no"); - - string[] relatedLanguages = related.Languages.Split(new[] { ',' }, StringSplitOptions.RemoveEmptyEntries); - - if (0 < relatedLanguages.Length) - { - writer.WriteAttributeString("LangInclusive", related.LangInclusive ? "yes" : "no"); - foreach (string language in relatedLanguages) - { - writer.WriteStartElement("Language"); - writer.WriteAttributeString("Id", language); - writer.WriteEndElement(); - } - } - writer.WriteEndElement(); - } - - // Write any contained Payloads with the PackagePayload being first - writer.WriteStartElement("PayloadRef"); - writer.WriteAttributeString("Id", package.Package.PackagePayload); - writer.WriteEndElement(); - - IEnumerable packagePayloads = payloadsByPackage[package.Package.WixChainItemId]; - - foreach (WixBundlePayloadRow payload in packagePayloads) - { - if (payload.Id != package.Package.PackagePayload) - { - writer.WriteStartElement("PayloadRef"); - writer.WriteAttributeString("Id", payload.Id); - writer.WriteEndElement(); - } - } - - writer.WriteEndElement(); // - } - writer.WriteEndElement(); // - - if (null != targetCodes) - { - foreach (WixBundlePatchTargetCodeRow targetCode in targetCodes) - { - writer.WriteStartElement("PatchTargetCode"); - writer.WriteAttributeString("TargetCode", targetCode.TargetCode); - writer.WriteAttributeString("Product", targetCode.TargetsProductCode ? "yes" : "no"); - writer.WriteEndElement(); - } - } - - // Write the ApprovedExeForElevation elements. - IEnumerable approvedExesForElevation = this.Output.Tables["WixApprovedExeForElevation"].RowsAs(); - - foreach (WixApprovedExeForElevationRow approvedExeForElevation in approvedExesForElevation) - { - writer.WriteStartElement("ApprovedExeForElevation"); - writer.WriteAttributeString("Id", approvedExeForElevation.Id); - writer.WriteAttributeString("Key", approvedExeForElevation.Key); - - if (!String.IsNullOrEmpty(approvedExeForElevation.ValueName)) - { - writer.WriteAttributeString("ValueName", approvedExeForElevation.ValueName); - } - - if (approvedExeForElevation.Win64) - { - writer.WriteAttributeString("Win64", "yes"); - } - - writer.WriteEndElement(); - } - - writer.WriteEndDocument(); // - } - } - - private void WriteBurnManifestContainerAttributes(XmlTextWriter writer, string executableName, WixBundleContainerRow container) - { - writer.WriteAttributeString("Id", container.Id); - writer.WriteAttributeString("FileSize", container.Size.ToString(CultureInfo.InvariantCulture)); - writer.WriteAttributeString("Hash", container.Hash); - - if (ContainerType.Detached == container.Type) - { - string resolvedUrl = this.ResolveUrl(container.DownloadUrl, null, null, container.Id, container.Name); - if (!String.IsNullOrEmpty(resolvedUrl)) - { - writer.WriteAttributeString("DownloadUrl", resolvedUrl); - } - else if (!String.IsNullOrEmpty(container.DownloadUrl)) - { - writer.WriteAttributeString("DownloadUrl", container.DownloadUrl); - } - - writer.WriteAttributeString("FilePath", container.Name); - } - else if (ContainerType.Attached == container.Type) - { - if (!String.IsNullOrEmpty(container.DownloadUrl)) - { - Messaging.Instance.OnMessage(WixWarnings.DownloadUrlNotSupportedForAttachedContainers(container.SourceLineNumbers, container.Id)); - } - - writer.WriteAttributeString("FilePath", executableName); // attached containers use the name of the bundle since they are attached to the executable. - writer.WriteAttributeString("AttachedIndex", container.AttachedContainerIndex.ToString(CultureInfo.InvariantCulture)); - writer.WriteAttributeString("Attached", "yes"); - writer.WriteAttributeString("Primary", "yes"); - } - } - - private void WriteBurnManifestPayloadAttributes(XmlTextWriter writer, WixBundlePayloadRow payload, bool embeddedOnly, Dictionary allPayloads) - { - Debug.Assert(!embeddedOnly || PackagingType.Embedded == payload.Packaging); - - writer.WriteAttributeString("Id", payload.Id); - writer.WriteAttributeString("FilePath", payload.Name); - writer.WriteAttributeString("FileSize", payload.FileSize.ToString(CultureInfo.InvariantCulture)); - writer.WriteAttributeString("Hash", payload.Hash); - - if (payload.LayoutOnly) - { - writer.WriteAttributeString("LayoutOnly", "yes"); - } - - if (!String.IsNullOrEmpty(payload.PublicKey)) - { - writer.WriteAttributeString("CertificateRootPublicKeyIdentifier", payload.PublicKey); - } - - if (!String.IsNullOrEmpty(payload.Thumbprint)) - { - writer.WriteAttributeString("CertificateRootThumbprint", payload.Thumbprint); - } - - switch (payload.Packaging) - { - case PackagingType.Embedded: // this means it's in a container. - if (!String.IsNullOrEmpty(payload.DownloadUrl)) - { - Messaging.Instance.OnMessage(WixWarnings.DownloadUrlNotSupportedForEmbeddedPayloads(payload.SourceLineNumbers, payload.Id)); - } - - writer.WriteAttributeString("Packaging", "embedded"); - writer.WriteAttributeString("SourcePath", payload.EmbeddedId); - - if (Compiler.BurnUXContainerId != payload.Container) - { - writer.WriteAttributeString("Container", payload.Container); - } - break; - - case PackagingType.External: - string packageId = payload.ParentPackagePayload; - string parentUrl = payload.ParentPackagePayload == null ? null : allPayloads[payload.ParentPackagePayload].DownloadUrl; - string resolvedUrl = this.ResolveUrl(payload.DownloadUrl, parentUrl, packageId, payload.Id, payload.Name); - if (!String.IsNullOrEmpty(resolvedUrl)) - { - writer.WriteAttributeString("DownloadUrl", resolvedUrl); - } - else if (!String.IsNullOrEmpty(payload.DownloadUrl)) - { - writer.WriteAttributeString("DownloadUrl", payload.DownloadUrl); - } - - writer.WriteAttributeString("Packaging", "external"); - writer.WriteAttributeString("SourcePath", payload.Name); - break; - } - - if (!String.IsNullOrEmpty(payload.Catalog)) - { - writer.WriteAttributeString("Catalog", payload.Catalog); - } - } - - private string ResolveUrl(string url, string fallbackUrl, string packageId, string payloadId, string fileName) - { - string resolved = null; - foreach (IBinderFileManager fileManager in this.FileManagers) - { - resolved = fileManager.ResolveUrl(url, fallbackUrl, packageId, payloadId, fileName); - if (!String.IsNullOrEmpty(resolved)) - { - break; - } - } - - return resolved; - } - } -} diff --git a/src/WixToolset.Core/Bind/Bundles/CreateContainerCommand.cs b/src/WixToolset.Core/Bind/Bundles/CreateContainerCommand.cs deleted file mode 100644 index 1bf987e3..00000000 --- a/src/WixToolset.Core/Bind/Bundles/CreateContainerCommand.cs +++ /dev/null @@ -1,68 +0,0 @@ -// Copyright (c) .NET Foundation and contributors. All rights reserved. Licensed under the Microsoft Reciprocal License. See LICENSE.TXT file in the project root for full license information. - -namespace WixToolset.Bind.Bundles -{ - using System; - using System.Collections.Generic; - using System.Diagnostics; - using System.IO; - using System.Linq; - using WixToolset.Cab; - using WixToolset.Data; - using WixToolset.Data.Rows; - - /// - /// Creates cabinet files. - /// - internal class CreateContainerCommand : ICommand - { - public CompressionLevel DefaultCompressionLevel { private get; set; } - - public IEnumerable Payloads { private get; set; } - - public string ManifestFile { private get; set; } - - public string OutputPath { private get; set; } - - public string Hash { get; private set; } - - public long Size { get; private set; } - - public void Execute() - { - int payloadCount = this.Payloads.Count(); // The number of embedded payloads - - if (!String.IsNullOrEmpty(this.ManifestFile)) - { - ++payloadCount; - } - - using (WixCreateCab cab = new WixCreateCab(Path.GetFileName(this.OutputPath), Path.GetDirectoryName(this.OutputPath), payloadCount, 0, 0, this.DefaultCompressionLevel)) - { - // If a manifest was provided always add it as "payload 0" to the container. - if (!String.IsNullOrEmpty(this.ManifestFile)) - { - cab.AddFile(this.ManifestFile, "0"); - } - - foreach (WixBundlePayloadRow payload in this.Payloads) - { - Debug.Assert(PackagingType.Embedded == payload.Packaging); - - Messaging.Instance.OnMessage(WixVerboses.LoadingPayload(payload.FullFileName)); - - cab.AddFile(payload.FullFileName, payload.EmbeddedId); - } - - cab.Complete(); - } - - // Now that the container is created, set the outputs of the command. - FileInfo fileInfo = new FileInfo(this.OutputPath); - - this.Hash = Common.GetFileHash(fileInfo.FullName); - - this.Size = fileInfo.Length; - } - } -} diff --git a/src/WixToolset.Core/Bind/Bundles/GetPackageFacadesCommand.cs b/src/WixToolset.Core/Bind/Bundles/GetPackageFacadesCommand.cs deleted file mode 100644 index dc19e380..00000000 --- a/src/WixToolset.Core/Bind/Bundles/GetPackageFacadesCommand.cs +++ /dev/null @@ -1,62 +0,0 @@ -// Copyright (c) .NET Foundation and contributors. All rights reserved. Licensed under the Microsoft Reciprocal License. See LICENSE.TXT file in the project root for full license information. - -namespace WixToolset.Bind.Bundles -{ - using System.Collections.Generic; - using WixToolset.Data; - using WixToolset.Data.Rows; - - internal class GetPackageFacadesCommand : ICommand - { - public Table PackageTable { private get; set; } - - public Table ExePackageTable { private get; set; } - - public Table MsiPackageTable { private get; set; } - - public Table MspPackageTable { private get; set; } - - public Table MsuPackageTable { private get; set; } - - public IDictionary PackageFacades { get; private set; } - - public void Execute() - { - RowDictionary exePackages = new RowDictionary(this.ExePackageTable); - RowDictionary msiPackages = new RowDictionary(this.MsiPackageTable); - RowDictionary mspPackages = new RowDictionary(this.MspPackageTable); - RowDictionary msuPackages = new RowDictionary(this.MsuPackageTable); - - Dictionary facades = new Dictionary(this.PackageTable.Rows.Count); - - foreach (WixBundlePackageRow package in this.PackageTable.Rows) - { - string id = package.WixChainItemId; - PackageFacade facade = null; - - switch (package.Type) - { - case WixBundlePackageType.Exe: - facade = new PackageFacade(package, exePackages.Get(id)); - break; - - case WixBundlePackageType.Msi: - facade = new PackageFacade(package, msiPackages.Get(id)); - break; - - case WixBundlePackageType.Msp: - facade = new PackageFacade(package, mspPackages.Get(id)); - break; - - case WixBundlePackageType.Msu: - facade = new PackageFacade(package, msuPackages.Get(id)); - break; - } - - facades.Add(id, facade); - } - - this.PackageFacades = facades; - } - } -} diff --git a/src/WixToolset.Core/Bind/Bundles/OrderPackagesAndRollbackBoundariesCommand.cs b/src/WixToolset.Core/Bind/Bundles/OrderPackagesAndRollbackBoundariesCommand.cs deleted file mode 100644 index ac3a301d..00000000 --- a/src/WixToolset.Core/Bind/Bundles/OrderPackagesAndRollbackBoundariesCommand.cs +++ /dev/null @@ -1,145 +0,0 @@ -// Copyright (c) .NET Foundation and contributors. All rights reserved. Licensed under the Microsoft Reciprocal License. See LICENSE.TXT file in the project root for full license information. - -namespace WixToolset.Bind.Bundles -{ - using System; - using System.Collections.Generic; - using WixToolset.Data; - using WixToolset.Data.Rows; - - internal class OrderPackagesAndRollbackBoundariesCommand : ICommand - { - public Table WixGroupTable { private get; set; } - - public RowDictionary Boundaries { private get; set; } - - public IDictionary PackageFacades { private get; set; } - - public IEnumerable OrderedPackageFacades { get; private set; } - - public IEnumerable UsedRollbackBoundaries { get; private set; } - - public void Execute() - { - List orderedFacades = new List(); - List usedBoundaries = new List(); - - // Process the chain of packages to add them in the correct order - // and assign the forward rollback boundaries as appropriate. Remember - // rollback boundaries are authored as elements in the chain which - // we re-interpret here to add them as attributes on the next available - // package in the chain. Essentially we mark some packages as being - // the start of a rollback boundary when installing and repairing. - // We handle uninstall (aka: backwards) rollback boundaries after - // we get these install/repair (aka: forward) rollback boundaries - // defined. - WixBundleRollbackBoundaryRow previousRollbackBoundary = null; - WixBundleRollbackBoundaryRow lastRollbackBoundary = null; - bool boundaryHadX86Package = false; - - foreach (WixGroupRow row in this.WixGroupTable.Rows) - { - if (ComplexReferenceChildType.Package == row.ChildType && ComplexReferenceParentType.PackageGroup == row.ParentType && "WixChain" == row.ParentId) - { - PackageFacade facade = null; - if (PackageFacades.TryGetValue(row.ChildId, out facade)) - { - if (null != previousRollbackBoundary) - { - usedBoundaries.Add(previousRollbackBoundary); - facade.Package.RollbackBoundary = previousRollbackBoundary.ChainPackageId; - previousRollbackBoundary = null; - - boundaryHadX86Package = (facade.Package.x64 == YesNoType.Yes); - } - - // Error if MSI transaction has x86 package preceding x64 packages - if ((lastRollbackBoundary != null) && (lastRollbackBoundary.Transaction == YesNoType.Yes) - && boundaryHadX86Package - && (facade.Package.x64 == YesNoType.Yes)) - { - Messaging.Instance.OnMessage(WixErrors.MsiTransactionX86BeforeX64(lastRollbackBoundary.SourceLineNumbers)); - } - boundaryHadX86Package = boundaryHadX86Package || (facade.Package.x64 == YesNoType.No); - - orderedFacades.Add(facade); - } - else // must be a rollback boundary. - { - // Discard the next rollback boundary if we have a previously defined boundary. - WixBundleRollbackBoundaryRow nextRollbackBoundary = Boundaries.Get(row.ChildId); - if (null != previousRollbackBoundary) - { - Messaging.Instance.OnMessage(WixWarnings.DiscardedRollbackBoundary(nextRollbackBoundary.SourceLineNumbers, nextRollbackBoundary.ChainPackageId)); - } - else - { - previousRollbackBoundary = nextRollbackBoundary; - lastRollbackBoundary = nextRollbackBoundary; - } - } - } - } - - if (null != previousRollbackBoundary) - { - Messaging.Instance.OnMessage(WixWarnings.DiscardedRollbackBoundary(previousRollbackBoundary.SourceLineNumbers, previousRollbackBoundary.ChainPackageId)); - } - - // With the forward rollback boundaries assigned, we can now go - // through the packages with rollback boundaries and assign backward - // rollback boundaries. Backward rollback boundaries are used when - // the chain is going "backwards" which (AFAIK) only happens during - // uninstall. - // - // Consider the scenario with three packages: A, B and C. Packages A - // and C are marked as rollback boundary packages and package B is - // not. The naive implementation would execute the chain like this - // (numbers indicate where rollback boundaries would end up): - // install: 1 A B 2 C - // uninstall: 2 C B 1 A - // - // The uninstall chain is wrong, A and B should be grouped together - // not C and B. The fix is to label packages with a "backwards" - // rollback boundary used during uninstall. The backwards rollback - // boundaries are assigned to the package *before* the next rollback - // boundary. Using our example from above again, I'll mark the - // backwards rollback boundaries prime (aka: with '). - // install: 1 A B 1' 2 C 2' - // uninstall: 2' C 2 1' B A 1 - // - // If the marked boundaries are ignored during install you get the - // same thing as above (good) and if the non-marked boundaries are - // ignored during uninstall then A and B are correctly grouped. - // Here's what it looks like without all the markers: - // install: 1 A B 2 C - // uninstall: 2 C 1 B A - // Woot! - string previousRollbackBoundaryId = null; - PackageFacade previousFacade = null; - - foreach (PackageFacade package in orderedFacades) - { - if (null != package.Package.RollbackBoundary) - { - if (null != previousFacade) - { - previousFacade.Package.RollbackBoundaryBackward = previousRollbackBoundaryId; - } - - previousRollbackBoundaryId = package.Package.RollbackBoundary; - } - - previousFacade = package; - } - - if (!String.IsNullOrEmpty(previousRollbackBoundaryId) && null != previousFacade) - { - previousFacade.Package.RollbackBoundaryBackward = previousRollbackBoundaryId; - } - - this.OrderedPackageFacades = orderedFacades; - this.UsedRollbackBoundaries = usedBoundaries; - } - } -} diff --git a/src/WixToolset.Core/Bind/Bundles/PackageFacade.cs b/src/WixToolset.Core/Bind/Bundles/PackageFacade.cs deleted file mode 100644 index f7e6410f..00000000 --- a/src/WixToolset.Core/Bind/Bundles/PackageFacade.cs +++ /dev/null @@ -1,58 +0,0 @@ -// Copyright (c) .NET Foundation and contributors. All rights reserved. Licensed under the Microsoft Reciprocal License. See LICENSE.TXT file in the project root for full license information. - -namespace WixToolset.Bind.Bundles -{ - using WixToolset.Data.Rows; - - internal class PackageFacade - { - private PackageFacade(WixBundlePackageRow package) - { - this.Package = package; - this.Provides = new ProvidesDependencyCollection(); - } - - public PackageFacade(WixBundlePackageRow package, WixBundleExePackageRow exePackage) - : this(package) - { - this.ExePackage = exePackage; - } - - public PackageFacade(WixBundlePackageRow package, WixBundleMsiPackageRow msiPackage) - : this(package) - { - this.MsiPackage = msiPackage; - } - - public PackageFacade(WixBundlePackageRow package, WixBundleMspPackageRow mspPackage) - : this(package) - { - this.MspPackage = mspPackage; - } - - public PackageFacade(WixBundlePackageRow package, WixBundleMsuPackageRow msuPackage) - : this(package) - { - this.MsuPackage = msuPackage; - } - - public WixBundlePackageRow Package { get; private set; } - - public WixBundleExePackageRow ExePackage { get; private set; } - - public WixBundleMsiPackageRow MsiPackage { get; private set; } - - public WixBundleMspPackageRow MspPackage { get; private set; } - - public WixBundleMsuPackageRow MsuPackage { get; private set; } - - /// - /// The provides dependencies authored and imported for this package. - /// - /// - /// TODO: Eventually this collection should turn into Rows so they are tracked in the PDB but - /// the relationship with the extension makes it much trickier to pull off. - /// - public ProvidesDependencyCollection Provides { get; private set; } - } -} diff --git a/src/WixToolset.Core/Bind/Bundles/ProcessExePackageCommand.cs b/src/WixToolset.Core/Bind/Bundles/ProcessExePackageCommand.cs deleted file mode 100644 index a1e7c271..00000000 --- a/src/WixToolset.Core/Bind/Bundles/ProcessExePackageCommand.cs +++ /dev/null @@ -1,33 +0,0 @@ -// Copyright (c) .NET Foundation and contributors. All rights reserved. Licensed under the Microsoft Reciprocal License. See LICENSE.TXT file in the project root for full license information. - -namespace WixToolset.Bind.Bundles -{ - using System; - using WixToolset.Data; - using WixToolset.Data.Rows; - - /// - /// Initializes package state from the Exe contents. - /// - internal class ProcessExePackageCommand : ICommand - { - public RowDictionary AuthoredPayloads { private get; set; } - - public PackageFacade Facade { private get; set; } - - /// - /// Processes the Exe packages to add properties and payloads from the Exe packages. - /// - public void Execute() - { - WixBundlePayloadRow packagePayload = this.AuthoredPayloads.Get(this.Facade.Package.PackagePayload); - - if (String.IsNullOrEmpty(this.Facade.Package.CacheId)) - { - this.Facade.Package.CacheId = packagePayload.Hash; - } - - this.Facade.Package.Version = packagePayload.Version; - } - } -} diff --git a/src/WixToolset.Core/Bind/Bundles/ProcessMsiPackageCommand.cs b/src/WixToolset.Core/Bind/Bundles/ProcessMsiPackageCommand.cs deleted file mode 100644 index f73776c0..00000000 --- a/src/WixToolset.Core/Bind/Bundles/ProcessMsiPackageCommand.cs +++ /dev/null @@ -1,560 +0,0 @@ -// Copyright (c) .NET Foundation and contributors. All rights reserved. Licensed under the Microsoft Reciprocal License. See LICENSE.TXT file in the project root for full license information. - -namespace WixToolset.Bind.Bundles -{ - using System; - using System.Collections; - using System.Collections.Generic; - using System.Diagnostics; - using System.Globalization; - using System.IO; - using System.Linq; - using WixToolset.Data; - using WixToolset.Data.Rows; - using WixToolset.Extensibility; - using WixToolset.Msi; - using WixToolset.Core.Native; - using Dtf = WixToolset.Dtf.WindowsInstaller; - - /// - /// Initializes package state from the MSI contents. - /// - internal class ProcessMsiPackageCommand : ICommand - { - private const string PropertySqlFormat = "SELECT `Value` FROM `Property` WHERE `Property` = '{0}'"; - - public RowDictionary AuthoredPayloads { private get; set; } - - public PackageFacade Facade { private get; set; } - - public IBinderFileManager FileManager { private get; set; } - - public Table MsiFeatureTable { private get; set; } - - public Table MsiPropertyTable { private get; set; } - - public Table PayloadTable { private get; set; } - - public Table RelatedPackageTable { private get; set; } - - /// - /// Processes the MSI packages to add properties and payloads from the MSI packages. - /// - 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 payloadNames = this.GetPayloadTargetNames(); - - ISet 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)); - } - } - - private ISet GetPayloadTargetNames() - { - IEnumerable payloadNames = this.PayloadTable.RowsAs() - .Where(r => r.Package == this.Facade.Package.WixChainItemId) - .Select(r => r.Name); - - return new HashSet(payloadNames, StringComparer.OrdinalIgnoreCase); - } - - private ISet GetMsiPropertyNames() - { - IEnumerable properties = this.MsiPropertyTable.RowsAs() - .Where(r => r.ChainPackageId == this.Facade.Package.WixChainItemId) - .Select(r => r.Name); - - return new HashSet(properties, StringComparer.Ordinal); - } - - private void SetPerMachineAppropriately(Dtf.Database db, string sourcePath) - { - if (this.Facade.MsiPackage.ForcePerMachine) - { - if (YesNoDefaultType.No == this.Facade.Package.PerMachine) - { - Messaging.Instance.OnMessage(WixWarnings.PerUserButForcingPerMachine(this.Facade.Package.SourceLineNumbers, sourcePath)); - this.Facade.Package.PerMachine = YesNoDefaultType.Yes; // ensure that we think the package is per-machine. - } - - // Force ALLUSERS=1 via the MSI command-line. - this.AddMsiProperty("ALLUSERS", "1"); - } - else - { - string allusers = ProcessMsiPackageCommand.GetProperty(db, "ALLUSERS"); - - if (String.IsNullOrEmpty(allusers)) - { - // Not forced per-machine and no ALLUSERS property, flip back to per-user. - if (YesNoDefaultType.Yes == this.Facade.Package.PerMachine) - { - Messaging.Instance.OnMessage(WixWarnings.ImplicitlyPerUser(this.Facade.Package.SourceLineNumbers, sourcePath)); - this.Facade.Package.PerMachine = YesNoDefaultType.No; - } - } - else if (allusers.Equals("1", StringComparison.Ordinal)) - { - if (YesNoDefaultType.No == this.Facade.Package.PerMachine) - { - Messaging.Instance.OnMessage(WixErrors.PerUserButAllUsersEquals1(this.Facade.Package.SourceLineNumbers, sourcePath)); - } - } - else if (allusers.Equals("2", StringComparison.Ordinal)) - { - Messaging.Instance.OnMessage(WixWarnings.DiscouragedAllUsersValue(this.Facade.Package.SourceLineNumbers, sourcePath, (YesNoDefaultType.Yes == this.Facade.Package.PerMachine) ? "machine" : "user")); - } - else - { - Messaging.Instance.OnMessage(WixErrors.UnsupportedAllUsersValue(this.Facade.Package.SourceLineNumbers, sourcePath, allusers)); - } - } - } - - private void SetPackageVisibility(Dtf.Database db, ISet msiPropertyNames) - { - bool alreadyVisible = !ProcessMsiPackageCommand.HasProperty(db, "ARPSYSTEMCOMPONENT"); - - if (alreadyVisible != this.Facade.Package.Visible) // if not already set to the correct visibility. - { - // If the authoring specifically added "ARPSYSTEMCOMPONENT", don't do it again. - if (!msiPropertyNames.Contains("ARPSYSTEMCOMPONENT")) - { - this.AddMsiProperty("ARPSYSTEMCOMPONENT", this.Facade.Package.Visible ? String.Empty : "1"); - } - } - } - - private void CreateRelatedPackages(Dtf.Database db) - { - // Represent the Upgrade table as related packages. - if (db.Tables.Contains("Upgrade")) - { - using (Dtf.View view = db.OpenView("SELECT `UpgradeCode`, `VersionMin`, `VersionMax`, `Language`, `Attributes` FROM `Upgrade`")) - { - view.Execute(); - while (true) - { - using (Dtf.Record record = view.Fetch()) - { - if (null == record) - { - break; - } - - WixBundleRelatedPackageRow related = (WixBundleRelatedPackageRow)this.RelatedPackageTable.CreateRow(this.Facade.Package.SourceLineNumbers); - related.ChainPackageId = this.Facade.Package.WixChainItemId; - related.Id = record.GetString(1); - related.MinVersion = record.GetString(2); - related.MaxVersion = record.GetString(3); - related.Languages = record.GetString(4); - - int attributes = record.GetInteger(5); - related.OnlyDetect = (attributes & MsiInterop.MsidbUpgradeAttributesOnlyDetect) == MsiInterop.MsidbUpgradeAttributesOnlyDetect; - related.MinInclusive = (attributes & MsiInterop.MsidbUpgradeAttributesVersionMinInclusive) == MsiInterop.MsidbUpgradeAttributesVersionMinInclusive; - related.MaxInclusive = (attributes & MsiInterop.MsidbUpgradeAttributesVersionMaxInclusive) == MsiInterop.MsidbUpgradeAttributesVersionMaxInclusive; - related.LangInclusive = (attributes & MsiInterop.MsidbUpgradeAttributesLanguagesExclusive) == 0; - } - } - } - } - } - - private void CreateMsiFeatures(Dtf.Database db) - { - if (db.Tables.Contains("Feature")) - { - using (Dtf.View featureView = db.OpenView("SELECT `Component_` FROM `FeatureComponents` WHERE `Feature_` = ?")) - using (Dtf.View componentView = db.OpenView("SELECT `FileSize` FROM `File` WHERE `Component_` = ?")) - { - using (Dtf.Record featureRecord = new Dtf.Record(1)) - using (Dtf.Record componentRecord = new Dtf.Record(1)) - { - using (Dtf.View allFeaturesView = db.OpenView("SELECT * FROM `Feature`")) - { - allFeaturesView.Execute(); - - while (true) - { - using (Dtf.Record allFeaturesResultRecord = allFeaturesView.Fetch()) - { - if (null == allFeaturesResultRecord) - { - break; - } - - string featureName = allFeaturesResultRecord.GetString(1); - - // Calculate the Feature size. - featureRecord.SetString(1, featureName); - featureView.Execute(featureRecord); - - // Loop over all the components for the feature to calculate the size of the feature. - long size = 0; - while (true) - { - using (Dtf.Record componentResultRecord = featureView.Fetch()) - { - if (null == componentResultRecord) - { - break; - } - string component = componentResultRecord.GetString(1); - componentRecord.SetString(1, component); - componentView.Execute(componentRecord); - - while (true) - { - using (Dtf.Record fileResultRecord = componentView.Fetch()) - { - if (null == fileResultRecord) - { - break; - } - - string fileSize = fileResultRecord.GetString(1); - size += Convert.ToInt32(fileSize, CultureInfo.InvariantCulture.NumberFormat); - } - } - } - } - - WixBundleMsiFeatureRow feature = (WixBundleMsiFeatureRow)this.MsiFeatureTable.CreateRow(this.Facade.Package.SourceLineNumbers); - feature.ChainPackageId = this.Facade.Package.WixChainItemId; - feature.Name = featureName; - 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); - feature.Size = size; - } - } - } - } - } - } - } - - private void ImportExternalCabinetAsPayloads(Dtf.Database db, WixBundlePayloadRow packagePayload, ISet payloadNames) - { - 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(packagePayload.Name), cabinet); - - if (!payloadNames.Contains(cabinetName)) - { - string generatedId = Common.GenerateIdentifier("cab", packagePayload.Id, cabinet); - string payloadSourceFile = FileManager.ResolveRelatedFile(packagePayload.UnresolvedSourceFile, cabinet, "Cabinet", this.Facade.Package.SourceLineNumbers, BindStage.Normal); - - WixBundlePayloadRow payload = (WixBundlePayloadRow)this.PayloadTable.CreateRow(this.Facade.Package.SourceLineNumbers); - payload.Id = generatedId; - payload.Name = cabinetName; - payload.SourceFile = payloadSourceFile; - payload.Compressed = packagePayload.Compressed; - payload.UnresolvedSourceFile = cabinetName; - payload.Package = packagePayload.Package; - payload.Container = packagePayload.Container; - payload.ContentFile = true; - payload.EnableSignatureValidation = packagePayload.EnableSignatureValidation; - payload.Packaging = packagePayload.Packaging; - payload.ParentPackagePayload = packagePayload.Id; - } - } - } - } - } - - private long ImportExternalFileAsPayloadsAndReturnInstallSize(Dtf.Database db, WixBundlePayloadRow packagePayload, bool longNamesInImage, bool compressed, ISet payloadNames) - { - long size = 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 (Dtf.View view = db.OpenView("SELECT `Directory`, `Directory_Parent`, `DefaultDir` FROM `Directory`")) - { - view.Execute(); - while (true) - { - using (Dtf.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 (Dtf.View view = db.OpenView("SELECT `Directory_`, `File`, `FileName`, `File`.`Attributes`, `FileSize` FROM `Component`, `File` WHERE `Component`.`Component`=`File`.`Component_`")) - { - view.Execute(); - while (true) - { - using (Dtf.Record record = view.Fetch()) - { - if (null == record) - { - break; - } - - // Skip adding the loose files as payloads if it was suppressed. - if (!this.Facade.MsiPackage.SuppressLooseFilePayloadGeneration) - { - // 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 fileSourcePath = Binder.GetFileSourcePath(directories, record.GetString(1), record.GetString(3), compressed, longNamesInImage); - string name = Path.Combine(Path.GetDirectoryName(packagePayload.Name), fileSourcePath); - - if (!payloadNames.Contains(name)) - { - string generatedId = Common.GenerateIdentifier("f", packagePayload.Id, record.GetString(2)); - string payloadSourceFile = FileManager.ResolveRelatedFile(packagePayload.UnresolvedSourceFile, fileSourcePath, "File", this.Facade.Package.SourceLineNumbers, BindStage.Normal); - - WixBundlePayloadRow payload = (WixBundlePayloadRow)this.PayloadTable.CreateRow(this.Facade.Package.SourceLineNumbers); - payload.Id = generatedId; - payload.Name = name; - payload.SourceFile = payloadSourceFile; - payload.Compressed = packagePayload.Compressed; - payload.UnresolvedSourceFile = name; - payload.Package = packagePayload.Package; - payload.Container = packagePayload.Container; - payload.ContentFile = true; - payload.EnableSignatureValidation = packagePayload.EnableSignatureValidation; - payload.Packaging = packagePayload.Packaging; - payload.ParentPackagePayload = packagePayload.Id; - } - } - } - - size += record.GetInteger(5); - } - } - } - } - - return size; - } - - private void AddMsiProperty(string name, string value) - { - WixBundleMsiPropertyRow row = (WixBundleMsiPropertyRow)this.MsiPropertyTable.CreateRow(this.Facade.MsiPackage.SourceLineNumbers); - row.ChainPackageId = this.Facade.Package.WixChainItemId; - row.Name = name; - row.Value = value; - } - - private void ImportDependencyProviders(Dtf.Database db) - { - if (db.Tables.Contains("WixDependencyProvider")) - { - string query = "SELECT `ProviderKey`, `Version`, `DisplayName`, `Attributes` FROM `WixDependencyProvider`"; - - using (Dtf.View view = db.OpenView(query)) - { - view.Execute(); - while (true) - { - using (Dtf.Record record = view.Fetch()) - { - if (null == record) - { - break; - } - - // Import the provider key and attributes. - string providerKey = record.GetString(1); - string version = record.GetString(2) ?? this.Facade.MsiPackage.ProductVersion; - string displayName = record.GetString(3) ?? this.Facade.Package.DisplayName; - int attributes = record.GetInteger(4); - - ProvidesDependency dependency = new ProvidesDependency(providerKey, version, displayName, attributes); - dependency.Imported = true; - - this.Facade.Provides.Add(dependency); - } - } - } - } - } - - /// - /// Queries a Windows Installer database for a Property value. - /// - /// Database to query. - /// Property to examine. - /// String value for result or null if query doesn't match a single result. - private static string GetProperty(Dtf.Database db, string property) - { - try - { - return db.ExecuteScalar(PropertyQuery(property)).ToString(); - } - catch (Dtf.InstallerException) - { - } - - return null; - } - - /// - /// Queries a Windows Installer database to determine if one or more rows exist in the Property table. - /// - /// Database to query. - /// Property to examine. - /// True if query matches at least one result. - private static bool HasProperty(Dtf.Database db, string property) - { - try - { - return 0 < db.ExecuteQuery(PropertyQuery(property)).Count; - } - catch (Dtf.InstallerException) - { - } - - return false; - } - - private static string PropertyQuery(string property) - { - // quick sanity check that we'll be creating a valid query... - // TODO: Are there any other special characters we should be looking for? - Debug.Assert(!property.Contains("'")); - - return String.Format(CultureInfo.InvariantCulture, ProcessMsiPackageCommand.PropertySqlFormat, property); - } - } -} diff --git a/src/WixToolset.Core/Bind/Bundles/ProcessMspPackageCommand.cs b/src/WixToolset.Core/Bind/Bundles/ProcessMspPackageCommand.cs deleted file mode 100644 index 24063221..00000000 --- a/src/WixToolset.Core/Bind/Bundles/ProcessMspPackageCommand.cs +++ /dev/null @@ -1,189 +0,0 @@ -// Copyright (c) .NET Foundation and contributors. All rights reserved. Licensed under the Microsoft Reciprocal License. See LICENSE.TXT file in the project root for full license information. - -namespace WixToolset.Bind.Bundles -{ - using System; - using System.Collections.Generic; - using System.Diagnostics; - using System.Globalization; - using System.IO; - using System.Text; - using System.Xml; - using WixToolset.Data; - using WixToolset.Data.Rows; - using Dtf = WixToolset.Dtf.WindowsInstaller; - - /// - /// Initializes package state from the Msp contents. - /// - internal class ProcessMspPackageCommand : ICommand - { - private const string PatchMetadataFormat = "SELECT `Value` FROM `MsiPatchMetadata` WHERE `Property` = '{0}'"; - private static readonly Encoding XmlOutputEncoding = new UTF8Encoding(false); - - public RowDictionary AuthoredPayloads { private get; set; } - - public PackageFacade Facade { private get; set; } - - public Table WixBundlePatchTargetCodeTable { private get; set; } - - /// - /// Processes the Msp packages to add properties and payloads from the Msp packages. - /// - 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; - } - } - - private void ProcessPatchXml(WixBundlePayloadRow packagePayload, string sourcePath) - { - HashSet uniqueTargetCodes = new HashSet(); - - string patchXml = Dtf.Installer.ExtractPatchXmlData(sourcePath); - - XmlDocument doc = new XmlDocument(); - doc.LoadXml(patchXml); - - XmlNamespaceManager nsmgr = new XmlNamespaceManager(doc.NameTable); - nsmgr.AddNamespace("p", "http://www.microsoft.com/msi/patch_applicability.xsd"); - - // Determine target ProductCodes and/or UpgradeCodes. - foreach (XmlNode node in doc.SelectNodes("/p:MsiPatch/p:TargetProduct", nsmgr)) - { - // If this patch targets a product code, this is the best case. - XmlNode targetCodeElement = node.SelectSingleNode("p:TargetProductCode", nsmgr); - WixBundlePatchTargetCodeAttributes attributes = WixBundlePatchTargetCodeAttributes.None; - - if (ProcessMspPackageCommand.TargetsCode(targetCodeElement)) - { - attributes = WixBundlePatchTargetCodeAttributes.TargetsProductCode; - } - else // maybe targets an upgrade code? - { - targetCodeElement = node.SelectSingleNode("p:UpgradeCode", nsmgr); - if (ProcessMspPackageCommand.TargetsCode(targetCodeElement)) - { - attributes = WixBundlePatchTargetCodeAttributes.TargetsUpgradeCode; - } - else // this patch targets an unknown number of products - { - this.Facade.MspPackage.Attributes |= WixBundleMspPackageAttributes.TargetUnspecified; - } - } - - string targetCode = targetCodeElement.InnerText; - - if (uniqueTargetCodes.Add(targetCode)) - { - WixBundlePatchTargetCodeRow row = (WixBundlePatchTargetCodeRow)this.WixBundlePatchTargetCodeTable.CreateRow(packagePayload.SourceLineNumbers); - row.MspPackageId = packagePayload.Id; - row.TargetCode = targetCode; - row.Attributes = attributes; - } - } - - // Suppress patch sequence data for improved performance. - XmlNode root = doc.DocumentElement; - foreach (XmlNode node in root.SelectNodes("p:SequenceData", nsmgr)) - { - root.RemoveChild(node); - } - - // Save the XML as compact as possible. - using (StringWriter writer = new StringWriter()) - { - XmlWriterSettings settings = new XmlWriterSettings() - { - Encoding = ProcessMspPackageCommand.XmlOutputEncoding, - Indent = false, - NewLineChars = string.Empty, - NewLineHandling = NewLineHandling.Replace, - }; - - using (XmlWriter xmlWriter = XmlWriter.Create(writer, settings)) - { - doc.WriteTo(xmlWriter); - } - - this.Facade.MspPackage.PatchXml = writer.ToString(); - } - } - - /// - /// Queries a Windows Installer patch database for a Property value from the MsiPatchMetadata table. - /// - /// Database to query. - /// Property to examine. - /// String value for result or null if query doesn't match a single result. - private static string GetPatchMetadataProperty(Dtf.Database db, string property) - { - try - { - return db.ExecuteScalar(PatchMetadataPropertyQuery(property)).ToString(); - } - catch (Dtf.InstallerException) - { - } - - return null; - } - - private static string PatchMetadataPropertyQuery(string property) - { - // quick sanity check that we'll be creating a valid query... - // TODO: Are there any other special characters we should be looking for? - Debug.Assert(!property.Contains("'")); - - return String.Format(CultureInfo.InvariantCulture, ProcessMspPackageCommand.PatchMetadataFormat, property); - } - - private static bool TargetsCode(XmlNode node) - { - if (null != node) - { - XmlAttribute attr = node.Attributes["Validate"]; - return null != attr && "true".Equals(attr.Value); - } - - return false; - } - } -} diff --git a/src/WixToolset.Core/Bind/Bundles/ProcessMsuPackageCommand.cs b/src/WixToolset.Core/Bind/Bundles/ProcessMsuPackageCommand.cs deleted file mode 100644 index ba59f5f5..00000000 --- a/src/WixToolset.Core/Bind/Bundles/ProcessMsuPackageCommand.cs +++ /dev/null @@ -1,30 +0,0 @@ -// Copyright (c) .NET Foundation and contributors. All rights reserved. Licensed under the Microsoft Reciprocal License. See LICENSE.TXT file in the project root for full license information. - -namespace WixToolset.Bind.Bundles -{ - using System; - using WixToolset.Data; - using WixToolset.Data.Rows; - - /// - /// Processes the Msu packages to add properties and payloads from the Msu packages. - /// - internal class ProcessMsuPackageCommand : ICommand - { - public RowDictionary AuthoredPayloads { private get; set; } - - public PackageFacade Facade { private get; set; } - - public void Execute() - { - WixBundlePayloadRow packagePayload = this.AuthoredPayloads.Get(this.Facade.Package.PackagePayload); - - if (String.IsNullOrEmpty(this.Facade.Package.CacheId)) - { - this.Facade.Package.CacheId = packagePayload.Hash; - } - - this.Facade.Package.PerMachine = YesNoDefaultType.Yes; // MSUs are always per-machine. - } - } -} diff --git a/src/WixToolset.Core/Bind/Bundles/ProcessPayloadsCommand.cs b/src/WixToolset.Core/Bind/Bundles/ProcessPayloadsCommand.cs deleted file mode 100644 index a83a7a4a..00000000 --- a/src/WixToolset.Core/Bind/Bundles/ProcessPayloadsCommand.cs +++ /dev/null @@ -1,159 +0,0 @@ -// Copyright (c) .NET Foundation and contributors. All rights reserved. Licensed under the Microsoft Reciprocal License. See LICENSE.TXT file in the project root for full license information. - -namespace WixToolset.Bind.Bundles -{ - using System; - using System.Collections.Generic; - using System.Diagnostics; - using System.IO; - using System.Security.Cryptography; - using System.Security.Cryptography.X509Certificates; - using System.Text; - using WixToolset.Data; - using WixToolset.Data.Rows; - - internal class ProcessPayloadsCommand : ICommand - { - private static readonly Version EmptyVersion = new Version(0, 0, 0, 0); - - public IEnumerable Payloads { private get; set; } - - public PackagingType DefaultPackaging { private get; set; } - - public string LayoutDirectory { private get; set; } - - public IEnumerable FileTransfers { get; private set; } - - public void Execute() - { - List fileTransfers = new List(); - - foreach (WixBundlePayloadRow payload in this.Payloads) - { - string normalizedPath = payload.Name.Replace('\\', '/'); - if (normalizedPath.StartsWith("../", StringComparison.Ordinal) || normalizedPath.Contains("/../")) - { - Messaging.Instance.OnMessage(WixErrors.PayloadMustBeRelativeToCache(payload.SourceLineNumbers, "Payload", "Name", payload.Name)); - } - - // Embedded files (aka: files from binary .wixlibs) are not content files (because they are hidden - // in the .wixlib). - ObjectField field = (ObjectField)payload.Fields[2]; - payload.ContentFile = !field.EmbeddedFileIndex.HasValue; - - this.UpdatePayloadPackagingType(payload); - - if (String.IsNullOrEmpty(payload.SourceFile)) - { - // Remote payloads obviously cannot be embedded. - Debug.Assert(PackagingType.Embedded != payload.Packaging); - } - else // not a remote payload so we have a lot more to update. - { - this.UpdatePayloadFileInformation(payload); - - this.UpdatePayloadVersionInformation(payload); - - // External payloads need to be transfered. - if (PackagingType.External == payload.Packaging) - { - FileTransfer transfer; - if (FileTransfer.TryCreate(payload.FullFileName, Path.Combine(this.LayoutDirectory, payload.Name), false, "Payload", payload.SourceLineNumbers, out transfer)) - { - fileTransfers.Add(transfer); - } - } - } - } - - this.FileTransfers = fileTransfers; - } - - private void UpdatePayloadPackagingType(WixBundlePayloadRow payload) - { - if (PackagingType.Unknown == payload.Packaging) - { - if (YesNoDefaultType.Yes == payload.Compressed) - { - payload.Packaging = PackagingType.Embedded; - } - else if (YesNoDefaultType.No == payload.Compressed) - { - payload.Packaging = PackagingType.External; - } - else - { - payload.Packaging = this.DefaultPackaging; - } - } - - // Embedded payloads that are not assigned a container already are placed in the default attached - // container. - if (PackagingType.Embedded == payload.Packaging && String.IsNullOrEmpty(payload.Container)) - { - payload.Container = Compiler.BurnDefaultAttachedContainerId; - } - } - - private void UpdatePayloadFileInformation(WixBundlePayloadRow payload) - { - FileInfo fileInfo = new FileInfo(payload.SourceFile); - - if (null != fileInfo) - { - payload.FileSize = (int)fileInfo.Length; - - payload.Hash = Common.GetFileHash(fileInfo.FullName); - - // Try to get the certificate if the payload is a signed file and we're not suppressing signature validation. - if (payload.EnableSignatureValidation) - { - X509Certificate2 certificate = null; - try - { - certificate = new X509Certificate2(fileInfo.FullName); - } - catch (CryptographicException) // we don't care about non-signed files. - { - } - - // If there is a certificate, remember its hashed public key identifier and thumbprint. - if (null != certificate) - { - byte[] publicKeyIdentifierHash = new byte[128]; - uint publicKeyIdentifierHashSize = (uint)publicKeyIdentifierHash.Length; - - WixToolset.Core.Native.NativeMethods.HashPublicKeyInfo(certificate.Handle, publicKeyIdentifierHash, ref publicKeyIdentifierHashSize); - StringBuilder sb = new StringBuilder(((int)publicKeyIdentifierHashSize + 1) * 2); - for (int i = 0; i < publicKeyIdentifierHashSize; ++i) - { - sb.AppendFormat("{0:X2}", publicKeyIdentifierHash[i]); - } - - payload.PublicKey = sb.ToString(); - payload.Thumbprint = certificate.Thumbprint; - } - } - } - } - - private void UpdatePayloadVersionInformation(WixBundlePayloadRow payload) - { - FileVersionInfo versionInfo = FileVersionInfo.GetVersionInfo(payload.SourceFile); - - if (null != versionInfo) - { - // Use the fixed version info block for the file since the resource text may not be a dotted quad. - Version version = new Version(versionInfo.ProductMajorPart, versionInfo.ProductMinorPart, versionInfo.ProductBuildPart, versionInfo.ProductPrivatePart); - - if (ProcessPayloadsCommand.EmptyVersion != version) - { - payload.Version = version.ToString(); - } - - payload.Description = versionInfo.FileDescription; - payload.DisplayName = versionInfo.ProductName; - } - } - } -} diff --git a/src/WixToolset.Core/Bind/Bundles/VerifyPayloadsWithCatalogCommand.cs b/src/WixToolset.Core/Bind/Bundles/VerifyPayloadsWithCatalogCommand.cs deleted file mode 100644 index 9c614c26..00000000 --- a/src/WixToolset.Core/Bind/Bundles/VerifyPayloadsWithCatalogCommand.cs +++ /dev/null @@ -1,148 +0,0 @@ -// Copyright (c) .NET Foundation and contributors. All rights reserved. Licensed under the Microsoft Reciprocal License. See LICENSE.TXT file in the project root for full license information. - -namespace WixToolset.Bind.Bundles -{ - using System; - using System.Collections.Generic; - using System.IO; - using System.Linq; - using System.Runtime.InteropServices; - using System.Text; - using WixToolset.Data; - using WixToolset.Data.Rows; - - internal class VerifyPayloadsWithCatalogCommand : ICommand - { - public IEnumerable Catalogs { private get; set; } - - public IEnumerable Payloads { private get; set; } - - public void Execute() - { - List catalogIdsWithPaths = this.Catalogs - .Join(this.Payloads, - catalog => catalog.Payload, - payload => payload.Id, - (catalog, payload) => new CatalogIdWithPath() { Id = catalog.Id, FullPath = Path.GetFullPath(payload.SourceFile) }) - .ToList(); - - foreach (WixBundlePayloadRow payloadInfo in this.Payloads) - { - // Payloads that are not embedded should be verfied. - if (String.IsNullOrEmpty(payloadInfo.EmbeddedId)) - { - bool validated = false; - - foreach (CatalogIdWithPath catalog in catalogIdsWithPaths) - { - if (!validated) - { - // Get the file hash - uint cryptHashSize = 20; - byte[] cryptHashBytes = new byte[cryptHashSize]; - int error; - IntPtr fileHandle = IntPtr.Zero; - using (FileStream payloadStream = File.OpenRead(payloadInfo.FullFileName)) - { - // Get the file handle - fileHandle = payloadStream.SafeFileHandle.DangerousGetHandle(); - - // 20 bytes is usually the hash size. Future hashes may be bigger - if (!VerifyInterop.CryptCATAdminCalcHashFromFileHandle(fileHandle, ref cryptHashSize, cryptHashBytes, 0)) - { - error = Marshal.GetLastWin32Error(); - - if (VerifyInterop.ErrorInsufficientBuffer == error) - { - error = 0; - cryptHashBytes = new byte[cryptHashSize]; - if (!VerifyInterop.CryptCATAdminCalcHashFromFileHandle(fileHandle, ref cryptHashSize, cryptHashBytes, 0)) - { - error = Marshal.GetLastWin32Error(); - } - } - - if (0 != error) - { - Messaging.Instance.OnMessage(WixErrors.CatalogFileHashFailed(payloadInfo.FullFileName, error)); - } - } - } - - VerifyInterop.WinTrustCatalogInfo catalogData = new VerifyInterop.WinTrustCatalogInfo(); - VerifyInterop.WinTrustData trustData = new VerifyInterop.WinTrustData(); - try - { - // Create WINTRUST_CATALOG_INFO structure - catalogData.cbStruct = (uint)Marshal.SizeOf(catalogData); - catalogData.cbCalculatedFileHash = cryptHashSize; - catalogData.pbCalculatedFileHash = Marshal.AllocCoTaskMem((int)cryptHashSize); - Marshal.Copy(cryptHashBytes, 0, catalogData.pbCalculatedFileHash, (int)cryptHashSize); - - StringBuilder hashString = new StringBuilder(); - foreach (byte hashByte in cryptHashBytes) - { - hashString.Append(hashByte.ToString("X2")); - } - catalogData.pcwszMemberTag = hashString.ToString(); - - // The file names need to be lower case for older OSes - catalogData.pcwszMemberFilePath = payloadInfo.FullFileName.ToLowerInvariant(); - catalogData.pcwszCatalogFilePath = catalog.FullPath.ToLowerInvariant(); - - // Create WINTRUST_DATA structure - trustData.cbStruct = (uint)Marshal.SizeOf(trustData); - trustData.dwUIChoice = VerifyInterop.WTD_UI_NONE; - trustData.fdwRevocationChecks = VerifyInterop.WTD_REVOKE_NONE; - trustData.dwUnionChoice = VerifyInterop.WTD_CHOICE_CATALOG; - trustData.dwStateAction = VerifyInterop.WTD_STATEACTION_VERIFY; - trustData.dwProvFlags = VerifyInterop.WTD_REVOCATION_CHECK_NONE; - - // Create the structure pointers for unmanaged - trustData.pCatalog = Marshal.AllocCoTaskMem(Marshal.SizeOf(catalogData)); - Marshal.StructureToPtr(catalogData, trustData.pCatalog, false); - - // Call WinTrustVerify to validate the file with the catalog - IntPtr noWindow = new IntPtr(-1); - Guid verifyGuid = new Guid(VerifyInterop.GenericVerify2); - long verifyResult = VerifyInterop.WinVerifyTrust(noWindow, ref verifyGuid, ref trustData); - if (0 == verifyResult) - { - payloadInfo.Catalog = catalog.Id; - validated = true; - break; - } - } - finally - { - // Free the structure memory - if (IntPtr.Zero != trustData.pCatalog) - { - Marshal.FreeCoTaskMem(trustData.pCatalog); - } - - if (IntPtr.Zero != catalogData.pbCalculatedFileHash) - { - Marshal.FreeCoTaskMem(catalogData.pbCalculatedFileHash); - } - } - } - } - - // Error message if the file was not validated by one of the catalogs - if (!validated) - { - Messaging.Instance.OnMessage(WixErrors.CatalogVerificationFailed(payloadInfo.FullFileName)); - } - } - } - } - - private class CatalogIdWithPath - { - public string Id { get; set; } - - public string FullPath { get; set; } - } - } -} diff --git a/src/WixToolset.Core/Bind/Databases/AssignMediaCommand.cs b/src/WixToolset.Core/Bind/Databases/AssignMediaCommand.cs deleted file mode 100644 index 5e2650e9..00000000 --- a/src/WixToolset.Core/Bind/Databases/AssignMediaCommand.cs +++ /dev/null @@ -1,314 +0,0 @@ -// Copyright (c) .NET Foundation and contributors. All rights reserved. Licensed under the Microsoft Reciprocal License. See LICENSE.TXT file in the project root for full license information. - -namespace WixToolset.Bind.Databases -{ - using System; - using System.Collections.Generic; - using System.Globalization; - using System.IO; - using WixToolset.Data; - using WixToolset.Data.Rows; - - /// - /// AssignMediaCommand assigns files to cabs based on Media or MediaTemplate rows. - /// - public class AssignMediaCommand : ICommand - { - public AssignMediaCommand() - { - this.CabinetNameTemplate = "Cab{0}.cab"; - } - - public Output Output { private get; set; } - - public bool FilesCompressed { private get; set; } - - public string CabinetNameTemplate { private get; set; } - - public IEnumerable FileFacades { private get; set; } - - public TableDefinitionCollection TableDefinitions { private get; set; } - - /// - /// Gets cabinets with their file rows. - /// - public Dictionary> FileFacadesByCabinetMedia { get; private set; } - - /// - /// Get media rows. - /// - public RowDictionary MediaRows { get; private set; } - - /// - /// Get uncompressed file rows. This will contain file rows of File elements that are marked with compression=no. - /// This contains all the files when Package element is marked with compression=no - /// - public IEnumerable UncompressedFileFacades { get; private set; } - - public void Execute() - { - Dictionary> filesByCabinetMedia = new Dictionary>(); - - RowDictionary mediaRows = new RowDictionary(); - - List uncompressedFiles = new List(); - - MediaRow mergeModuleMediaRow = null; - Table mediaTable = this.Output.Tables["Media"]; - Table mediaTemplateTable = this.Output.Tables["WixMediaTemplate"]; - - // If both tables are authored, it is an error. - if ((mediaTemplateTable != null && mediaTemplateTable.Rows.Count > 0) && (mediaTable != null && mediaTable.Rows.Count > 1)) - { - throw new WixException(WixErrors.MediaTableCollision(null)); - } - - // When building merge module, all the files go to "#MergeModule.CABinet". - if (OutputType.Module == this.Output.Type) - { - Table mergeModuleMediaTable = new Table(null, this.TableDefinitions["Media"]); - mergeModuleMediaRow = (MediaRow)mergeModuleMediaTable.CreateRow(null); - mergeModuleMediaRow.Cabinet = "#MergeModule.CABinet"; - - filesByCabinetMedia.Add(mergeModuleMediaRow, new List()); - } - - if (OutputType.Module == this.Output.Type || null == mediaTemplateTable) - { - this.ManuallyAssignFiles(mediaTable, mergeModuleMediaRow, this.FileFacades, filesByCabinetMedia, mediaRows, uncompressedFiles); - } - else - { - this.AutoAssignFiles(mediaTable, this.FileFacades, filesByCabinetMedia, mediaRows, uncompressedFiles); - } - - this.FileFacadesByCabinetMedia = new Dictionary>(); - - foreach (var mediaRowWithFiles in filesByCabinetMedia) - { - this.FileFacadesByCabinetMedia.Add(mediaRowWithFiles.Key, mediaRowWithFiles.Value); - } - - this.MediaRows = mediaRows; - - this.UncompressedFileFacades = uncompressedFiles; - } - - /// - /// Assign files to cabinets based on MediaTemplate authoring. - /// - /// FileRowCollection - private void AutoAssignFiles(Table mediaTable, IEnumerable fileFacades, Dictionary> filesByCabinetMedia, RowDictionary mediaRows, List uncompressedFiles) - { - const int MaxCabIndex = 999; - - ulong currentPreCabSize = 0; - ulong maxPreCabSizeInBytes; - int maxPreCabSizeInMB = 0; - int currentCabIndex = 0; - - MediaRow currentMediaRow = null; - - Table mediaTemplateTable = this.Output.Tables["WixMediaTemplate"]; - - // Auto assign files to cabinets based on maximum uncompressed media size - mediaTable.Rows.Clear(); - WixMediaTemplateRow mediaTemplateRow = (WixMediaTemplateRow)mediaTemplateTable.Rows[0]; - - if (!String.IsNullOrEmpty(mediaTemplateRow.CabinetTemplate)) - { - this.CabinetNameTemplate = mediaTemplateRow.CabinetTemplate; - } - - string mumsString = Environment.GetEnvironmentVariable("WIX_MUMS"); - - try - { - // Override authored mums value if environment variable is authored. - if (!String.IsNullOrEmpty(mumsString)) - { - maxPreCabSizeInMB = Int32.Parse(mumsString); - } - else - { - maxPreCabSizeInMB = mediaTemplateRow.MaximumUncompressedMediaSize; - } - - maxPreCabSizeInBytes = (ulong)maxPreCabSizeInMB * 1024 * 1024; - } - catch (FormatException) - { - throw new WixException(WixErrors.IllegalEnvironmentVariable("WIX_MUMS", mumsString)); - } - catch (OverflowException) - { - throw new WixException(WixErrors.MaximumUncompressedMediaSizeTooLarge(null, maxPreCabSizeInMB)); - } - - foreach (FileFacade facade in this.FileFacades) - { - // When building a product, if the current file is not to be compressed or if - // the package set not to be compressed, don't cab it. - if (OutputType.Product == this.Output.Type && - (YesNoType.No == facade.File.Compressed || - (YesNoType.NotSet == facade.File.Compressed && !this.FilesCompressed))) - { - uncompressedFiles.Add(facade); - continue; - } - - if (currentCabIndex == MaxCabIndex) - { - // Associate current file with last cab (irrespective of the size) and cab index is not incremented anymore. - List cabinetFiles = filesByCabinetMedia[currentMediaRow]; - facade.WixFile.DiskId = currentCabIndex; - cabinetFiles.Add(facade); - continue; - } - - // Update current cab size. - currentPreCabSize += (ulong)facade.File.FileSize; - - if (currentPreCabSize > maxPreCabSizeInBytes) - { - // Overflow due to current file - currentMediaRow = this.AddMediaRow(mediaTemplateRow, mediaTable, ++currentCabIndex); - mediaRows.Add(currentMediaRow); - filesByCabinetMedia.Add(currentMediaRow, new List()); - - List cabinetFileRows = filesByCabinetMedia[currentMediaRow]; - facade.WixFile.DiskId = currentCabIndex; - cabinetFileRows.Add(facade); - // Now files larger than MaxUncompressedMediaSize will be the only file in its cabinet so as to respect MaxUncompressedMediaSize - currentPreCabSize = (ulong)facade.File.FileSize; - } - else - { - // File fits in the current cab. - if (currentMediaRow == null) - { - // Create new cab and MediaRow - currentMediaRow = this.AddMediaRow(mediaTemplateRow, mediaTable, ++currentCabIndex); - mediaRows.Add(currentMediaRow); - filesByCabinetMedia.Add(currentMediaRow, new List()); - } - - // Associate current file with current cab. - List cabinetFiles = filesByCabinetMedia[currentMediaRow]; - facade.WixFile.DiskId = currentCabIndex; - cabinetFiles.Add(facade); - } - } - - // If there are uncompressed files and no MediaRow, create a default one. - if (uncompressedFiles.Count > 0 && mediaTable.Rows.Count == 0) - { - MediaRow defaultMediaRow = (MediaRow)mediaTable.CreateRow(null); - defaultMediaRow.DiskId = 1; - mediaRows.Add(defaultMediaRow); - } - } - - /// - /// Assign files to cabinets based on Media authoring. - /// - /// - /// - /// - private void ManuallyAssignFiles(Table mediaTable, MediaRow mergeModuleMediaRow, IEnumerable fileFacades, Dictionary> filesByCabinetMedia, RowDictionary mediaRows, List uncompressedFiles) - { - if (OutputType.Module != this.Output.Type) - { - if (null != mediaTable) - { - Dictionary cabinetMediaRows = new Dictionary(StringComparer.InvariantCultureIgnoreCase); - foreach (MediaRow mediaRow in mediaTable.Rows) - { - // If the Media row has a cabinet, make sure it is unique across all Media rows. - if (!String.IsNullOrEmpty(mediaRow.Cabinet)) - { - MediaRow existingRow; - if (cabinetMediaRows.TryGetValue(mediaRow.Cabinet, out existingRow)) - { - Messaging.Instance.OnMessage(WixErrors.DuplicateCabinetName(mediaRow.SourceLineNumbers, mediaRow.Cabinet)); - Messaging.Instance.OnMessage(WixErrors.DuplicateCabinetName2(existingRow.SourceLineNumbers, existingRow.Cabinet)); - } - else - { - cabinetMediaRows.Add(mediaRow.Cabinet, mediaRow); - } - } - - mediaRows.Add(mediaRow); - } - } - - foreach (MediaRow mediaRow in mediaRows.Values) - { - if (null != mediaRow.Cabinet) - { - filesByCabinetMedia.Add(mediaRow, new List()); - } - } - } - - foreach (FileFacade facade in fileFacades) - { - if (OutputType.Module == this.Output.Type) - { - filesByCabinetMedia[mergeModuleMediaRow].Add(facade); - } - else - { - MediaRow mediaRow; - if (!mediaRows.TryGetValue(facade.WixFile.DiskId.ToString(CultureInfo.InvariantCulture), out mediaRow)) - { - Messaging.Instance.OnMessage(WixErrors.MissingMedia(facade.File.SourceLineNumbers, facade.WixFile.DiskId)); - continue; - } - - // When building a product, if the current file is not to be compressed or if - // the package set not to be compressed, don't cab it. - if (OutputType.Product == this.Output.Type && - (YesNoType.No == facade.File.Compressed || - (YesNoType.NotSet == facade.File.Compressed && !this.FilesCompressed))) - { - uncompressedFiles.Add(facade); - } - else // file is marked compressed. - { - List cabinetFiles; - if (filesByCabinetMedia.TryGetValue(mediaRow, out cabinetFiles)) - { - cabinetFiles.Add(facade); - } - else - { - Messaging.Instance.OnMessage(WixErrors.ExpectedMediaCabinet(facade.File.SourceLineNumbers, facade.File.File, facade.WixFile.DiskId)); - } - } - } - } - } - - /// - /// Adds a row to the media table with cab name template filled in. - /// - /// - /// - /// - private MediaRow AddMediaRow(WixMediaTemplateRow mediaTemplateRow, Table mediaTable, int cabIndex) - { - MediaRow currentMediaRow = (MediaRow)mediaTable.CreateRow(mediaTemplateRow.SourceLineNumbers); - currentMediaRow.DiskId = cabIndex; - currentMediaRow.Cabinet = String.Format(CultureInfo.InvariantCulture, this.CabinetNameTemplate, cabIndex); - - Table wixMediaTable = this.Output.EnsureTable(this.TableDefinitions["WixMedia"]); - WixMediaRow row = (WixMediaRow)wixMediaTable.CreateRow(mediaTemplateRow.SourceLineNumbers); - row.DiskId = cabIndex; - row.CompressionLevel = mediaTemplateRow.CompressionLevel; - - return currentMediaRow; - } - } -} diff --git a/src/WixToolset.Core/Bind/Databases/BindSummaryInfoCommand.cs b/src/WixToolset.Core/Bind/Databases/BindSummaryInfoCommand.cs deleted file mode 100644 index 95bd4cf0..00000000 --- a/src/WixToolset.Core/Bind/Databases/BindSummaryInfoCommand.cs +++ /dev/null @@ -1,135 +0,0 @@ -// Copyright (c) .NET Foundation and contributors. All rights reserved. Licensed under the Microsoft Reciprocal License. See LICENSE.TXT file in the project root for full license information. - -namespace WixToolset.Bind.Databases -{ - using System; - using System.Globalization; - using WixToolset.Data; - - /// - /// Binds the summary information table of a database. - /// - internal class BindSummaryInfoCommand : ICommand - { - /// - /// The output to bind. - /// - public Output Output { private get; set; } - - /// - /// Returns a flag indicating if files are compressed by default. - /// - public bool Compressed { get; private set; } - - /// - /// Returns a flag indicating if uncompressed files use long filenames. - /// - public bool LongNames { get; private set; } - - public int InstallerVersion { get; private set; } - - /// - /// Modularization guid, or null if the output is not a module. - /// - public string ModularizationGuid { get; private set; } - - public void Execute() - { - this.Compressed = false; - this.LongNames = false; - this.InstallerVersion = 0; - this.ModularizationGuid = null; - - Table summaryInformationTable = this.Output.Tables["_SummaryInformation"]; - - if (null != summaryInformationTable) - { - bool foundCreateDataTime = false; - bool foundLastSaveDataTime = false; - bool foundCreatingApplication = false; - string now = DateTime.Now.ToString("yyyy/MM/dd HH:mm:ss", CultureInfo.InvariantCulture); - - foreach (Row summaryInformationRow in summaryInformationTable.Rows) - { - switch (summaryInformationRow.FieldAsInteger(0)) - { - case 1: // PID_CODEPAGE - // make sure the code page is an int and not a web name or null - string codepage = summaryInformationRow.FieldAsString(1); - - if (null == codepage) - { - codepage = "0"; - } - else - { - summaryInformationRow[1] = Common.GetValidCodePage(codepage, false, false, summaryInformationRow.SourceLineNumbers).ToString(CultureInfo.InvariantCulture); - } - break; - case 9: // PID_REVNUMBER - string packageCode = (string)summaryInformationRow[1]; - - if (OutputType.Module == this.Output.Type) - { - this.ModularizationGuid = packageCode.Substring(1, 36).Replace('-', '_'); - } - else if ("*" == packageCode) - { - // set the revision number (package/patch code) if it should be automatically generated - summaryInformationRow[1] = Common.GenerateGuid(); - } - break; - case 12: // PID_CREATE_DTM - foundCreateDataTime = true; - break; - case 13: // PID_LASTSAVE_DTM - foundLastSaveDataTime = true; - break; - case 14: - this.InstallerVersion = summaryInformationRow.FieldAsInteger(1); - break; - case 15: // PID_WORDCOUNT - if (OutputType.Patch == this.Output.Type) - { - this.LongNames = true; - this.Compressed = true; - } - else - { - this.LongNames = (0 == (summaryInformationRow.FieldAsInteger(1) & 1)); - this.Compressed = (2 == (summaryInformationRow.FieldAsInteger(1) & 2)); - } - break; - case 18: // PID_APPNAME - foundCreatingApplication = true; - break; - } - } - - // add a summary information row for the create time/date property if its not already set - if (!foundCreateDataTime) - { - Row createTimeDateRow = summaryInformationTable.CreateRow(null); - createTimeDateRow[0] = 12; - createTimeDateRow[1] = now; - } - - // add a summary information row for the last save time/date property if its not already set - if (!foundLastSaveDataTime) - { - Row lastSaveTimeDateRow = summaryInformationTable.CreateRow(null); - lastSaveTimeDateRow[0] = 13; - lastSaveTimeDateRow[1] = now; - } - - // add a summary information row for the creating application property if its not already set - if (!foundCreatingApplication) - { - Row creatingApplicationRow = summaryInformationTable.CreateRow(null); - creatingApplicationRow[0] = 18; - creatingApplicationRow[1] = String.Format(CultureInfo.InvariantCulture, AppCommon.GetCreatingApplicationString()); - } - } - } - } -} diff --git a/src/WixToolset.Core/Bind/Databases/CabinetBuilder.cs b/src/WixToolset.Core/Bind/Databases/CabinetBuilder.cs deleted file mode 100644 index 2de6ec25..00000000 --- a/src/WixToolset.Core/Bind/Databases/CabinetBuilder.cs +++ /dev/null @@ -1,176 +0,0 @@ -// Copyright (c) .NET Foundation and contributors. All rights reserved. Licensed under the Microsoft Reciprocal License. See LICENSE.TXT file in the project root for full license information. - -namespace WixToolset.Bind.Databases -{ - using System; - using System.Collections; - using System.IO; - using System.Linq; - using System.Threading; - using WixToolset.Cab; - using WixToolset.Data; - using WixToolset.Data.Rows; - - /// - /// Builds cabinets using multiple threads. This implements a thread pool that generates cabinets with multiple - /// threads. Unlike System.Threading.ThreadPool, it waits until all threads are finished. - /// - internal sealed class CabinetBuilder - { - private Queue cabinetWorkItems; - private object lockObject; - private int threadCount; - - // Address of Binder's callback function for Cabinet Splitting - private IntPtr newCabNamesCallBackAddress; - - public int MaximumCabinetSizeForLargeFileSplitting { get; set; } - public int MaximumUncompressedMediaSize { get; set; } - - /// - /// Instantiate a new CabinetBuilder. - /// - /// number of threads to use - /// Address of Binder's callback function for Cabinet Splitting - public CabinetBuilder(int threadCount, IntPtr newCabNamesCallBackAddress) - { - if (0 >= threadCount) - { - throw new ArgumentOutOfRangeException("threadCount"); - } - - this.cabinetWorkItems = new Queue(); - this.lockObject = new object(); - - this.threadCount = threadCount; - - // Set Address of Binder's callback function for Cabinet Splitting - this.newCabNamesCallBackAddress = newCabNamesCallBackAddress; - } - - /// - /// Enqueues a CabinetWorkItem to the queue. - /// - /// cabinet work item - public void Enqueue(CabinetWorkItem cabinetWorkItem) - { - this.cabinetWorkItems.Enqueue(cabinetWorkItem); - } - - /// - /// Create the queued cabinets. - /// - /// error message number (zero if no error) - public void CreateQueuedCabinets() - { - // don't create more threads than the number of cabinets to build - if (this.cabinetWorkItems.Count < this.threadCount) - { - this.threadCount = this.cabinetWorkItems.Count; - } - - if (0 < this.threadCount) - { - Thread[] threads = new Thread[this.threadCount]; - - for (int i = 0; i < threads.Length; i++) - { - threads[i] = new Thread(new ThreadStart(this.ProcessWorkItems)); - threads[i].Start(); - } - - // wait for all threads to finish - foreach (Thread thread in threads) - { - thread.Join(); - } - } - } - - /// - /// This function gets called by multiple threads to do actual work. - /// It takes one work item at a time and calls this.CreateCabinet(). - /// It does not return until cabinetWorkItems queue is empty - /// - private void ProcessWorkItems() - { - try - { - while (true) - { - CabinetWorkItem cabinetWorkItem; - - lock (this.cabinetWorkItems) - { - // check if there are any more cabinets to create - if (0 == this.cabinetWorkItems.Count) - { - break; - } - - cabinetWorkItem = (CabinetWorkItem)this.cabinetWorkItems.Dequeue(); - } - - // create a cabinet - this.CreateCabinet(cabinetWorkItem); - } - } - catch (WixException we) - { - Messaging.Instance.OnMessage(we.Error); - } - catch (Exception e) - { - Messaging.Instance.OnMessage(WixErrors.UnexpectedException(e.Message, e.GetType().ToString(), e.StackTrace)); - } - } - - /// - /// Creates a cabinet using the wixcab.dll interop layer. - /// - /// CabinetWorkItem containing information about the cabinet to create. - private void CreateCabinet(CabinetWorkItem cabinetWorkItem) - { - Messaging.Instance.OnMessage(WixVerboses.CreateCabinet(cabinetWorkItem.CabinetFile)); - - int maxCabinetSize = 0; // The value of 0 corresponds to default of 2GB which means no cabinet splitting - ulong maxPreCompressedSizeInBytes = 0; - - if (MaximumCabinetSizeForLargeFileSplitting != 0) - { - // User Specified Max Cab Size for File Splitting, So Check if this cabinet has a single file larger than MaximumUncompressedFileSize - // If a file is larger than MaximumUncompressedFileSize, then the cabinet containing it will have only this file - if (1 == cabinetWorkItem.FileFacades.Count()) - { - // Cabinet has Single File, Check if this is Large File than needs Splitting into Multiple cabs - // Get the Value for Max Uncompressed Media Size - maxPreCompressedSizeInBytes = (ulong)MaximumUncompressedMediaSize * 1024 * 1024; - - foreach (FileFacade facade in cabinetWorkItem.FileFacades) // No other easy way than looping to get the only row - { - if ((ulong)facade.File.FileSize >= maxPreCompressedSizeInBytes) - { - // If file is larger than MaximumUncompressedFileSize set Maximum Cabinet Size for Cabinet Splitting - maxCabinetSize = MaximumCabinetSizeForLargeFileSplitting; - } - } - } - } - - // create the cabinet file - string cabinetFileName = Path.GetFileName(cabinetWorkItem.CabinetFile); - string cabinetDirectory = Path.GetDirectoryName(cabinetWorkItem.CabinetFile); - - using (WixCreateCab cab = new WixCreateCab(cabinetFileName, cabinetDirectory, cabinetWorkItem.FileFacades.Count(), maxCabinetSize, cabinetWorkItem.MaxThreshold, cabinetWorkItem.CompressionLevel)) - { - foreach (FileFacade facade in cabinetWorkItem.FileFacades) - { - cab.AddFile(facade); - } - - cab.Complete(newCabNamesCallBackAddress); - } - } - } -} - diff --git a/src/WixToolset.Core/Bind/Databases/CabinetWorkItem.cs b/src/WixToolset.Core/Bind/Databases/CabinetWorkItem.cs deleted file mode 100644 index 20241bc9..00000000 --- a/src/WixToolset.Core/Bind/Databases/CabinetWorkItem.cs +++ /dev/null @@ -1,78 +0,0 @@ -// Copyright (c) .NET Foundation and contributors. All rights reserved. Licensed under the Microsoft Reciprocal License. See LICENSE.TXT file in the project root for full license information. - -namespace WixToolset.Bind.Databases -{ - using System.Collections.Generic; - using WixToolset.Data; - using WixToolset.Data.Rows; - - /// - /// A cabinet builder work item. - /// - internal sealed class CabinetWorkItem - { - private string cabinetFile; - private CompressionLevel compressionLevel; - //private BinderFileManager binderFileManager; - private int maxThreshold; - - /// - /// Instantiate a new CabinetWorkItem. - /// - /// The collection of files in this cabinet. - /// The cabinet file. - /// Maximum threshold for each cabinet. - /// The compression level of the cabinet. - /// The binder file manager. - public CabinetWorkItem(IEnumerable fileFacades, string cabinetFile, int maxThreshold, CompressionLevel compressionLevel /*, BinderFileManager binderFileManager*/) - { - this.cabinetFile = cabinetFile; - this.compressionLevel = compressionLevel; - this.FileFacades = fileFacades; - //this.binderFileManager = binderFileManager; - this.maxThreshold = maxThreshold; - } - - /// - /// Gets the cabinet file. - /// - /// The cabinet file. - public string CabinetFile - { - get { return this.cabinetFile; } - } - - /// - /// Gets the compression level of the cabinet. - /// - /// The compression level of the cabinet. - public CompressionLevel CompressionLevel - { - get { return this.compressionLevel; } - } - - /// - /// Gets the collection of files in this cabinet. - /// - /// The collection of files in this cabinet. - public IEnumerable FileFacades { get; private set; } - - /// - /// Gets the binder file manager. - /// - /// The binder file manager. - //public BinderFileManager BinderFileManager - //{ - // get { return this.binderFileManager; } - //} - - /// - /// Gets the max threshold. - /// - /// The maximum threshold for a folder in a cabinet. - public int MaxThreshold - { - get { return this.maxThreshold; } - } - } -} diff --git a/src/WixToolset.Core/Bind/Databases/ConfigurationCallback.cs b/src/WixToolset.Core/Bind/Databases/ConfigurationCallback.cs deleted file mode 100644 index 7cb18e0f..00000000 --- a/src/WixToolset.Core/Bind/Databases/ConfigurationCallback.cs +++ /dev/null @@ -1,91 +0,0 @@ -// Copyright (c) .NET Foundation and contributors. All rights reserved. Licensed under the Microsoft Reciprocal License. See LICENSE.TXT file in the project root for full license information. - -namespace WixToolset.Bind.Databases -{ - using System; - using System.Collections; - using System.Globalization; - using WixToolset.MergeMod; - - /// - /// Callback object for configurable merge modules. - /// - internal sealed class ConfigurationCallback : IMsmConfigureModule - { - private const int SOk = 0x0; - private const int SFalse = 0x1; - private Hashtable configurationData; - - /// - /// Creates a ConfigurationCallback object. - /// - /// String to break up into name/value pairs. - public ConfigurationCallback(string configData) - { - if (String.IsNullOrEmpty(configData)) - { - throw new ArgumentNullException("configData"); - } - - string[] pairs = configData.Split(','); - this.configurationData = new Hashtable(pairs.Length); - for (int i = 0; i < pairs.Length; ++i) - { - string[] nameVal = pairs[i].Split('='); - string name = nameVal[0]; - string value = nameVal[1]; - - name = name.Replace("%2C", ","); - name = name.Replace("%3D", "="); - name = name.Replace("%25", "%"); - - value = value.Replace("%2C", ","); - value = value.Replace("%3D", "="); - value = value.Replace("%25", "%"); - - this.configurationData[name] = value; - } - } - - /// - /// Returns text data based on name. - /// - /// Name of value to return. - /// Out param to put configuration data into. - /// S_OK if value provided, S_FALSE if not. - public int ProvideTextData(string name, out string configData) - { - if (this.configurationData.Contains(name)) - { - configData = (string)this.configurationData[name]; - return SOk; - } - else - { - configData = null; - return SFalse; - } - } - - /// - /// Returns integer data based on name. - /// - /// Name of value to return. - /// Out param to put configuration data into. - /// S_OK if value provided, S_FALSE if not. - public int ProvideIntegerData(string name, out int configData) - { - if (this.configurationData.Contains(name)) - { - string val = (string)this.configurationData[name]; - configData = Convert.ToInt32(val, CultureInfo.InvariantCulture); - return SOk; - } - else - { - configData = 0; - return SFalse; - } - } - } -} diff --git a/src/WixToolset.Core/Bind/Databases/CopyTransformDataCommand.cs b/src/WixToolset.Core/Bind/Databases/CopyTransformDataCommand.cs deleted file mode 100644 index af1ab3b0..00000000 --- a/src/WixToolset.Core/Bind/Databases/CopyTransformDataCommand.cs +++ /dev/null @@ -1,606 +0,0 @@ -// Copyright (c) .NET Foundation and contributors. All rights reserved. Licensed under the Microsoft Reciprocal License. See LICENSE.TXT file in the project root for full license information. - -namespace WixToolset.Bind.Databases -{ - using System; - using System.Collections.Generic; - using System.Diagnostics; - using WixToolset.Data; - using WixToolset.Data.Rows; - using WixToolset.Extensibility; - using WixToolset.Core.Native; - - internal class CopyTransformDataCommand : ICommand - { - public bool CopyOutFileRows { private get; set; } - - public BinderFileManagerCore FileManagerCore { private get; set; } - - public IEnumerable FileManagers { private get; set; } - - public Output Output { private get; set; } - - public TableDefinitionCollection TableDefinitions { private get; set; } - - public IEnumerable FileFacades { get; private set; } - - public void Execute() - { - Debug.Assert(OutputType.Patch != this.Output.Type); - - List allFileRows = this.CopyOutFileRows ? new List() : null; - -#if false // TODO: Fix this patching related code to work correctly with FileFacades. - bool copyToPatch = (allFileRows != null); - bool copyFromPatch = !copyToPatch; - - RowDictionary patchMediaRows = new RowDictionary(); - - Dictionary> patchMediaFileRows = new Dictionary>(); - - Table patchActualFileTable = this.Output.EnsureTable(this.TableDefinitions["File"]); - Table patchFileTable = this.Output.EnsureTable(this.TableDefinitions["WixFile"]); - - if (copyFromPatch) - { - // index patch files by diskId+fileId - foreach (WixFileRow patchFileRow in patchFileTable.Rows) - { - int diskId = patchFileRow.DiskId; - RowDictionary mediaFileRows; - if (!patchMediaFileRows.TryGetValue(diskId, out mediaFileRows)) - { - mediaFileRows = new RowDictionary(); - patchMediaFileRows.Add(diskId, mediaFileRows); - } - - mediaFileRows.Add(patchFileRow); - } - - Table patchMediaTable = this.Output.EnsureTable(this.TableDefinitions["Media"]); - patchMediaRows = new RowDictionary(patchMediaTable); - } - - // index paired transforms - Dictionary pairedTransforms = new Dictionary(); - foreach (SubStorage substorage in this.Output.SubStorages) - { - if (substorage.Name.StartsWith("#")) - { - pairedTransforms.Add(substorage.Name.Substring(1), substorage.Data); - } - } - - try - { - // copy File bind data into substorages - foreach (SubStorage substorage in this.Output.SubStorages) - { - if (substorage.Name.StartsWith("#")) - { - // no changes necessary for paired transforms - continue; - } - - Output mainTransform = substorage.Data; - Table mainWixFileTable = mainTransform.Tables["WixFile"]; - Table mainMsiFileHashTable = mainTransform.Tables["MsiFileHash"]; - - this.FileManagerCore.ActiveSubStorage = substorage; - - RowDictionary mainWixFiles = new RowDictionary(mainWixFileTable); - RowDictionary mainMsiFileHashIndex = new RowDictionary(); - - Table mainFileTable = mainTransform.Tables["File"]; - Output pairedTransform = (Output)pairedTransforms[substorage.Name]; - - // copy Media.LastSequence and index the MsiFileHash table if it exists. - if (copyFromPatch) - { - Table pairedMediaTable = pairedTransform.Tables["Media"]; - foreach (MediaRow pairedMediaRow in pairedMediaTable.Rows) - { - MediaRow patchMediaRow = patchMediaRows.Get(pairedMediaRow.DiskId); - pairedMediaRow.Fields[1] = patchMediaRow.Fields[1]; - } - - if (null != mainMsiFileHashTable) - { - mainMsiFileHashIndex = new RowDictionary(mainMsiFileHashTable); - } - - // Validate file row changes for keypath-related issues - this.ValidateFileRowChanges(mainTransform); - } - - // Index File table of pairedTransform - Table pairedFileTable = pairedTransform.Tables["File"]; - RowDictionary pairedFileRows = new RowDictionary(pairedFileTable); - - if (null != mainFileTable) - { - if (copyFromPatch) - { - // Remove the MsiFileHash table because it will be updated later with the final file hash for each file - mainTransform.Tables.Remove("MsiFileHash"); - } - - foreach (FileRow mainFileRow in mainFileTable.Rows) - { - if (RowOperation.Delete == mainFileRow.Operation) - { - continue; - } - else if (RowOperation.None == mainFileRow.Operation && !copyToPatch) - { - continue; - } - - WixFileRow mainWixFileRow = mainWixFiles.Get(mainFileRow.File); - - if (copyToPatch) // when copying to the patch, we need compare the underlying files and include all file changes. - { - ObjectField objectField = (ObjectField)mainWixFileRow.Fields[6]; - FileRow pairedFileRow = pairedFileRows.Get(mainFileRow.File); - - // If the file is new, we always need to add it to the patch. - if (mainFileRow.Operation != RowOperation.Add) - { - // If PreviousData doesn't exist, target and upgrade layout point to the same location. No need to compare. - if (null == objectField.PreviousData) - { - if (mainFileRow.Operation == RowOperation.None) - { - continue; - } - } - else - { - // TODO: should this entire condition be placed in the binder file manager? - if ((0 == (PatchAttributeType.Ignore & mainWixFileRow.PatchAttributes)) && - !this.CompareFiles(objectField.PreviousData.ToString(), objectField.Data.ToString())) - { - // If the file is different, we need to mark the mainFileRow and pairedFileRow as modified. - mainFileRow.Operation = RowOperation.Modify; - if (null != pairedFileRow) - { - // Always patch-added, but never non-compressed. - pairedFileRow.Attributes |= MsiInterop.MsidbFileAttributesPatchAdded; - pairedFileRow.Attributes &= ~MsiInterop.MsidbFileAttributesNoncompressed; - pairedFileRow.Fields[6].Modified = true; - pairedFileRow.Operation = RowOperation.Modify; - } - } - else - { - // The File is same. We need mark all the attributes as unchanged. - mainFileRow.Operation = RowOperation.None; - foreach (Field field in mainFileRow.Fields) - { - field.Modified = false; - } - - if (null != pairedFileRow) - { - pairedFileRow.Attributes &= ~MsiInterop.MsidbFileAttributesPatchAdded; - pairedFileRow.Fields[6].Modified = false; - pairedFileRow.Operation = RowOperation.None; - } - continue; - } - } - } - else if (null != pairedFileRow) // RowOperation.Add - { - // Always patch-added, but never non-compressed. - pairedFileRow.Attributes |= MsiInterop.MsidbFileAttributesPatchAdded; - pairedFileRow.Attributes &= ~MsiInterop.MsidbFileAttributesNoncompressed; - pairedFileRow.Fields[6].Modified = true; - pairedFileRow.Operation = RowOperation.Add; - } - } - - // index patch files by diskId+fileId - int diskId = mainWixFileRow.DiskId; - - RowDictionary mediaFileRows; - if (!patchMediaFileRows.TryGetValue(diskId, out mediaFileRows)) - { - mediaFileRows = new RowDictionary(); - patchMediaFileRows.Add(diskId, mediaFileRows); - } - - string fileId = mainFileRow.File; - WixFileRow patchFileRow = mediaFileRows.Get(fileId); - if (copyToPatch) - { - if (null == patchFileRow) - { - FileRow patchActualFileRow = (FileRow)patchFileTable.CreateRow(mainFileRow.SourceLineNumbers); - patchActualFileRow.CopyFrom(mainFileRow); - - patchFileRow = (WixFileRow)patchFileTable.CreateRow(mainFileRow.SourceLineNumbers); - patchFileRow.CopyFrom(mainWixFileRow); - - mediaFileRows.Add(patchFileRow); - - allFileRows.Add(new FileFacade(patchActualFileRow, patchFileRow, null)); // TODO: should we be passing along delta information? Probably, right? - } - else - { - // TODO: confirm the rest of data is identical? - - // make sure Source is same. Otherwise we are silently ignoring a file. - if (0 != String.Compare(patchFileRow.Source, mainWixFileRow.Source, StringComparison.OrdinalIgnoreCase)) - { - Messaging.Instance.OnMessage(WixErrors.SameFileIdDifferentSource(mainFileRow.SourceLineNumbers, fileId, patchFileRow.Source, mainWixFileRow.Source)); - } - - // capture the previous file versions (and associated data) from this targeted instance of the baseline into the current filerow. - patchFileRow.AppendPreviousDataFrom(mainWixFileRow); - } - } - else - { - // copy data from the patch back to the transform - if (null != patchFileRow) - { - FileRow pairedFileRow = (FileRow)pairedFileRows.Get(fileId); - for (int i = 0; i < patchFileRow.Fields.Length; i++) - { - string patchValue = patchFileRow[i] == null ? "" : patchFileRow[i].ToString(); - string mainValue = mainFileRow[i] == null ? "" : mainFileRow[i].ToString(); - - if (1 == i) - { - // File.Component_ changes should not come from the shared file rows - // that contain the file information as each individual transform might - // have different changes (or no changes at all). - } - // File.Attributes should not changed for binary deltas - else if (6 == i) - { - if (null != patchFileRow.Patch) - { - // File.Attribute should not change for binary deltas - pairedFileRow.Attributes = mainFileRow.Attributes; - mainFileRow.Fields[i].Modified = false; - } - } - // File.Sequence is updated in pairedTransform, not mainTransform - else if (7 == i) - { - // file sequence is updated in Patch table instead of File table for delta patches - if (null != patchFileRow.Patch) - { - pairedFileRow.Fields[i].Modified = false; - } - else - { - pairedFileRow[i] = patchFileRow[i]; - pairedFileRow.Fields[i].Modified = true; - } - mainFileRow.Fields[i].Modified = false; - } - else if (patchValue != mainValue) - { - mainFileRow[i] = patchFileRow[i]; - mainFileRow.Fields[i].Modified = true; - if (mainFileRow.Operation == RowOperation.None) - { - mainFileRow.Operation = RowOperation.Modify; - } - } - } - - // copy MsiFileHash row for this File - Row patchHashRow; - if (!mainMsiFileHashIndex.TryGetValue(patchFileRow.File, out patchHashRow)) - { - patchHashRow = patchFileRow.Hash; - } - - if (null != patchHashRow) - { - Table mainHashTable = mainTransform.EnsureTable(this.TableDefinitions["MsiFileHash"]); - Row mainHashRow = mainHashTable.CreateRow(mainFileRow.SourceLineNumbers); - for (int i = 0; i < patchHashRow.Fields.Length; i++) - { - mainHashRow[i] = patchHashRow[i]; - if (i > 1) - { - // assume all hash fields have been modified - mainHashRow.Fields[i].Modified = true; - } - } - - // assume the MsiFileHash operation follows the File one - mainHashRow.Operation = mainFileRow.Operation; - } - - // copy MsiAssemblyName rows for this File - List patchAssemblyNameRows = patchFileRow.AssemblyNames; - if (null != patchAssemblyNameRows) - { - Table mainAssemblyNameTable = mainTransform.EnsureTable(this.TableDefinitions["MsiAssemblyName"]); - foreach (Row patchAssemblyNameRow in patchAssemblyNameRows) - { - // Copy if there isn't an identical modified/added row already in the transform. - bool foundMatchingModifiedRow = false; - foreach (Row mainAssemblyNameRow in mainAssemblyNameTable.Rows) - { - if (RowOperation.None != mainAssemblyNameRow.Operation && mainAssemblyNameRow.GetPrimaryKey('/').Equals(patchAssemblyNameRow.GetPrimaryKey('/'))) - { - foundMatchingModifiedRow = true; - break; - } - } - - if (!foundMatchingModifiedRow) - { - Row mainAssemblyNameRow = mainAssemblyNameTable.CreateRow(mainFileRow.SourceLineNumbers); - for (int i = 0; i < patchAssemblyNameRow.Fields.Length; i++) - { - mainAssemblyNameRow[i] = patchAssemblyNameRow[i]; - } - - // assume value field has been modified - mainAssemblyNameRow.Fields[2].Modified = true; - mainAssemblyNameRow.Operation = mainFileRow.Operation; - } - } - } - - // Add patch header for this file - if (null != patchFileRow.Patch) - { - // Add the PatchFiles action automatically to the AdminExecuteSequence and InstallExecuteSequence tables. - AddPatchFilesActionToSequenceTable(SequenceTable.AdminExecuteSequence, mainTransform, pairedTransform, mainFileRow); - AddPatchFilesActionToSequenceTable(SequenceTable.InstallExecuteSequence, mainTransform, pairedTransform, mainFileRow); - - // Add to Patch table - Table patchTable = pairedTransform.EnsureTable(this.TableDefinitions["Patch"]); - if (0 == patchTable.Rows.Count) - { - patchTable.Operation = TableOperation.Add; - } - - Row patchRow = patchTable.CreateRow(mainFileRow.SourceLineNumbers); - patchRow[0] = patchFileRow.File; - patchRow[1] = patchFileRow.Sequence; - - FileInfo patchFile = new FileInfo(patchFileRow.Source); - patchRow[2] = (int)patchFile.Length; - patchRow[3] = 0 == (PatchAttributeType.AllowIgnoreOnError & patchFileRow.PatchAttributes) ? 0 : 1; - - string streamName = patchTable.Name + "." + patchRow[0] + "." + patchRow[1]; - if (MsiInterop.MsiMaxStreamNameLength < streamName.Length) - { - streamName = "_" + Guid.NewGuid().ToString("D").ToUpperInvariant().Replace('-', '_'); - Table patchHeadersTable = pairedTransform.EnsureTable(this.TableDefinitions["MsiPatchHeaders"]); - if (0 == patchHeadersTable.Rows.Count) - { - patchHeadersTable.Operation = TableOperation.Add; - } - Row patchHeadersRow = patchHeadersTable.CreateRow(mainFileRow.SourceLineNumbers); - patchHeadersRow[0] = streamName; - patchHeadersRow[1] = patchFileRow.Patch; - patchRow[5] = streamName; - patchHeadersRow.Operation = RowOperation.Add; - } - else - { - patchRow[4] = patchFileRow.Patch; - } - patchRow.Operation = RowOperation.Add; - } - } - else - { - // TODO: throw because all transform rows should have made it into the patch - } - } - } - } - - if (copyFromPatch) - { - this.Output.Tables.Remove("Media"); - this.Output.Tables.Remove("File"); - this.Output.Tables.Remove("MsiFileHash"); - this.Output.Tables.Remove("MsiAssemblyName"); - } - } - } - finally - { - this.FileManagerCore.ActiveSubStorage = null; - } -#endif - this.FileFacades = allFileRows; - } - - /// - /// Adds the PatchFiles action to the sequence table if it does not already exist. - /// - /// The sequence table to check or modify. - /// The primary authoring transform. - /// The secondary patch transform. - /// The file row that contains information about the patched file. - private void AddPatchFilesActionToSequenceTable(SequenceTable table, Output mainTransform, Output pairedTransform, Row mainFileRow) - { - // Find/add PatchFiles action (also determine sequence for it). - // Search mainTransform first, then pairedTransform (pairedTransform overrides). - bool hasPatchFilesAction = false; - int seqInstallFiles = 0; - int seqDuplicateFiles = 0; - string tableName = table.ToString(); - - TestSequenceTableForPatchFilesAction( - mainTransform.Tables[tableName], - ref hasPatchFilesAction, - ref seqInstallFiles, - ref seqDuplicateFiles); - TestSequenceTableForPatchFilesAction( - pairedTransform.Tables[tableName], - ref hasPatchFilesAction, - ref seqInstallFiles, - ref seqDuplicateFiles); - if (!hasPatchFilesAction) - { - Table iesTable = pairedTransform.EnsureTable(this.TableDefinitions[tableName]); - if (0 == iesTable.Rows.Count) - { - iesTable.Operation = TableOperation.Add; - } - - Row patchAction = iesTable.CreateRow(null); - WixActionRow wixPatchAction = WindowsInstallerStandard.GetStandardActions()[table, "PatchFiles"]; - int sequence = wixPatchAction.Sequence; - // Test for default sequence value's appropriateness - if (seqInstallFiles >= sequence || (0 != seqDuplicateFiles && seqDuplicateFiles <= sequence)) - { - if (0 != seqDuplicateFiles) - { - if (seqDuplicateFiles < seqInstallFiles) - { - throw new WixException(WixErrors.InsertInvalidSequenceActionOrder(mainFileRow.SourceLineNumbers, iesTable.Name, "InstallFiles", "DuplicateFiles", wixPatchAction.Action)); - } - else - { - sequence = (seqDuplicateFiles + seqInstallFiles) / 2; - if (seqInstallFiles == sequence || seqDuplicateFiles == sequence) - { - throw new WixException(WixErrors.InsertSequenceNoSpace(mainFileRow.SourceLineNumbers, iesTable.Name, "InstallFiles", "DuplicateFiles", wixPatchAction.Action)); - } - } - } - else - { - sequence = seqInstallFiles + 1; - } - } - patchAction[0] = wixPatchAction.Action; - patchAction[1] = wixPatchAction.Condition; - patchAction[2] = sequence; - patchAction.Operation = RowOperation.Add; - } - } - - /// - /// Tests sequence table for PatchFiles and associated actions - /// - /// The table to test. - /// Set to true if PatchFiles action is found. Left unchanged otherwise. - /// Set to sequence value of InstallFiles action if found. Left unchanged otherwise. - /// Set to sequence value of DuplicateFiles action if found. Left unchanged otherwise. - private static void TestSequenceTableForPatchFilesAction(Table iesTable, ref bool hasPatchFilesAction, ref int seqInstallFiles, ref int seqDuplicateFiles) - { - if (null != iesTable) - { - foreach (Row iesRow in iesTable.Rows) - { - if (String.Equals("PatchFiles", (string)iesRow[0], StringComparison.Ordinal)) - { - hasPatchFilesAction = true; - } - if (String.Equals("InstallFiles", (string)iesRow[0], StringComparison.Ordinal)) - { - seqInstallFiles = (int)iesRow.Fields[2].Data; - } - if (String.Equals("DuplicateFiles", (string)iesRow[0], StringComparison.Ordinal)) - { - seqDuplicateFiles = (int)iesRow.Fields[2].Data; - } - } - } - } - - /// - /// Signal a warning if a non-keypath file was changed in a patch without also changing the keypath file of the component. - /// - /// The output to validate. - private void ValidateFileRowChanges(Output transform) - { - Table componentTable = transform.Tables["Component"]; - Table fileTable = transform.Tables["File"]; - - // There's no sense validating keypaths if the transform has no component or file table - if (componentTable == null || fileTable == null) - { - return; - } - - Dictionary componentKeyPath = new Dictionary(componentTable.Rows.Count); - - // Index the Component table for non-directory & non-registry key paths. - foreach (Row row in componentTable.Rows) - { - if (null != row.Fields[5].Data && - 0 != ((int)row.Fields[3].Data & MsiInterop.MsidbComponentAttributesRegistryKeyPath)) - { - componentKeyPath.Add(row.Fields[0].Data.ToString(), row.Fields[5].Data.ToString()); - } - } - - Dictionary componentWithChangedKeyPath = new Dictionary(); - Dictionary componentWithNonKeyPathChanged = new Dictionary(); - // Verify changes in the file table, now that file diffing has occurred - foreach (FileRow row in fileTable.Rows) - { - string fileId = row.Fields[0].Data.ToString(); - string componentId = row.Fields[1].Data.ToString(); - - if (RowOperation.Modify != row.Operation) - { - continue; - } - - // If this file is the keypath of a component - if (componentKeyPath.ContainsValue(fileId)) - { - if (!componentWithChangedKeyPath.ContainsKey(componentId)) - { - componentWithChangedKeyPath.Add(componentId, fileId); - } - } - else - { - if (!componentWithNonKeyPathChanged.ContainsKey(componentId)) - { - componentWithNonKeyPathChanged.Add(componentId, fileId); - } - } - } - - foreach (KeyValuePair componentFile in componentWithNonKeyPathChanged) - { - // Make sure all changes to non keypath files also had a change in the keypath. - if (!componentWithChangedKeyPath.ContainsKey(componentFile.Key) && componentKeyPath.ContainsKey(componentFile.Key)) - { - Messaging.Instance.OnMessage(WixWarnings.UpdateOfNonKeyPathFile((string)componentFile.Value, (string)componentFile.Key, (string)componentKeyPath[componentFile.Key])); - } - } - } - - private bool CompareFiles(string targetFile, string updatedFile) - { - bool? compared = null; - foreach (IBinderFileManager fileManager in this.FileManagers) - { - compared = fileManager.CompareFiles(targetFile, updatedFile); - if (compared.HasValue) - { - break; - } - } - - if (!compared.HasValue) - { - throw new InvalidOperationException(); // TODO: something needs to be said here that none of the binder file managers returned a result. - } - - return compared.Value; - } - } -} diff --git a/src/WixToolset.Core/Bind/Databases/CreateCabinetsCommand.cs b/src/WixToolset.Core/Bind/Databases/CreateCabinetsCommand.cs deleted file mode 100644 index 35c8abb4..00000000 --- a/src/WixToolset.Core/Bind/Databases/CreateCabinetsCommand.cs +++ /dev/null @@ -1,489 +0,0 @@ -// Copyright (c) .NET Foundation and contributors. All rights reserved. Licensed under the Microsoft Reciprocal License. See LICENSE.TXT file in the project root for full license information. - -namespace WixToolset.Bind.Databases -{ - using System; - using System.Collections.Generic; - using System.Globalization; - using System.IO; - using System.Linq; - using System.Runtime.InteropServices; - using System.Threading; - using WixToolset.Data; - using WixToolset.Data.Rows; - using WixToolset.Extensibility; - - /// - /// Creates cabinet files. - /// - internal class CreateCabinetsCommand : ICommand - { - private List fileTransfers; - - private FileSplitCabNamesCallback newCabNamesCallBack; - - private Dictionary lastCabinetAddedToMediaTable; // Key is First Cabinet Name, Value is Last Cabinet Added in the Split Sequence - - public CreateCabinetsCommand() - { - this.fileTransfers = new List(); - - this.newCabNamesCallBack = NewCabNamesCallBack; - } - - /// - /// Sets the number of threads to use for cabinet creation. - /// - public int CabbingThreadCount { private get; set; } - - public string TempFilesLocation { private get; set; } - - /// - /// Sets the default compression level to use for cabinets - /// that don't have their compression level explicitly set. - /// - public CompressionLevel DefaultCompressionLevel { private get; set; } - - public Output Output { private get; set; } - - public IEnumerable FileManagers { private get; set; } - - public string LayoutDirectory { private get; set; } - - public bool Compressed { private get; set; } - - public Dictionary> FileRowsByCabinet { private get; set; } - - public Func ResolveMedia { private get; set; } - - public TableDefinitionCollection TableDefinitions { private get; set; } - - public Table WixMediaTable { private get; set; } - - public IEnumerable FileTransfers { get { return this.fileTransfers; } } - - /// Output to generate image for. - /// Array of files to be transfered. - /// The directory in which the image should be layed out. - /// Flag if source image should be compressed. - /// The uncompressed file rows. - public void Execute() - { - RowDictionary wixMediaRows = new RowDictionary(this.WixMediaTable); - - this.lastCabinetAddedToMediaTable = new Dictionary(); - - this.SetCabbingThreadCount(); - - // Send Binder object to Facilitate NewCabNamesCallBack Callback - CabinetBuilder cabinetBuilder = new CabinetBuilder(this.CabbingThreadCount, Marshal.GetFunctionPointerForDelegate(this.newCabNamesCallBack)); - - // Supply Compile MediaTemplate Attributes to Cabinet Builder - int MaximumCabinetSizeForLargeFileSplitting; - int MaximumUncompressedMediaSize; - this.GetMediaTemplateAttributes(out MaximumCabinetSizeForLargeFileSplitting, out MaximumUncompressedMediaSize); - cabinetBuilder.MaximumCabinetSizeForLargeFileSplitting = MaximumCabinetSizeForLargeFileSplitting; - cabinetBuilder.MaximumUncompressedMediaSize = MaximumUncompressedMediaSize; - - foreach (var entry in this.FileRowsByCabinet) - { - MediaRow mediaRow = entry.Key; - IEnumerable files = entry.Value; - CompressionLevel compressionLevel = this.DefaultCompressionLevel; - - WixMediaRow wixMediaRow = null; - string mediaLayoutFolder = null; - - if (wixMediaRows.TryGetValue(mediaRow.GetKey(), out wixMediaRow)) - { - mediaLayoutFolder = wixMediaRow.Layout; - - if (wixMediaRow.CompressionLevel.HasValue) - { - compressionLevel = wixMediaRow.CompressionLevel.Value; - } - } - - string cabinetDir = this.ResolveMedia(mediaRow, mediaLayoutFolder, this.LayoutDirectory); - - CabinetWorkItem cabinetWorkItem = this.CreateCabinetWorkItem(this.Output, cabinetDir, mediaRow, compressionLevel, files, this.fileTransfers); - if (null != cabinetWorkItem) - { - cabinetBuilder.Enqueue(cabinetWorkItem); - } - } - - // stop processing if an error previously occurred - if (Messaging.Instance.EncounteredError) - { - return; - } - - // create queued cabinets with multiple threads - cabinetBuilder.CreateQueuedCabinets(); - if (Messaging.Instance.EncounteredError) - { - return; - } - } - - /// - /// Sets the thead count to the number of processors if the current thread count is set to 0. - /// - /// The thread count value must be greater than 0 otherwise and exception will be thrown. - private void SetCabbingThreadCount() - { - // default the number of cabbing threads to the number of processors if it wasn't specified - if (0 == this.CabbingThreadCount) - { - string numberOfProcessors = System.Environment.GetEnvironmentVariable("NUMBER_OF_PROCESSORS"); - - try - { - if (null != numberOfProcessors) - { - this.CabbingThreadCount = Convert.ToInt32(numberOfProcessors, CultureInfo.InvariantCulture.NumberFormat); - - if (0 >= this.CabbingThreadCount) - { - throw new WixException(WixErrors.IllegalEnvironmentVariable("NUMBER_OF_PROCESSORS", numberOfProcessors)); - } - } - else // default to 1 if the environment variable is not set - { - this.CabbingThreadCount = 1; - } - - Messaging.Instance.OnMessage(WixVerboses.SetCabbingThreadCount(this.CabbingThreadCount.ToString())); - } - catch (ArgumentException) - { - throw new WixException(WixErrors.IllegalEnvironmentVariable("NUMBER_OF_PROCESSORS", numberOfProcessors)); - } - catch (FormatException) - { - throw new WixException(WixErrors.IllegalEnvironmentVariable("NUMBER_OF_PROCESSORS", numberOfProcessors)); - } - } - } - - - /// - /// Creates a work item to create a cabinet. - /// - /// Output for the current database. - /// Directory to create cabinet in. - /// MediaRow containing information about the cabinet. - /// Collection of files in this cabinet. - /// Array of files to be transfered. - /// created CabinetWorkItem object - private CabinetWorkItem CreateCabinetWorkItem(Output output, string cabinetDir, MediaRow mediaRow, CompressionLevel compressionLevel, IEnumerable fileFacades, List fileTransfers) - { - CabinetWorkItem cabinetWorkItem = null; - string tempCabinetFileX = Path.Combine(this.TempFilesLocation, mediaRow.Cabinet); - - // check for an empty cabinet - if (!fileFacades.Any()) - { - string cabinetName = mediaRow.Cabinet; - - // remove the leading '#' from the embedded cabinet name to make the warning easier to understand - if (cabinetName.StartsWith("#", StringComparison.Ordinal)) - { - cabinetName = cabinetName.Substring(1); - } - - // If building a patch, remind them to run -p for torch. - if (OutputType.Patch == output.Type) - { - Messaging.Instance.OnMessage(WixWarnings.EmptyCabinet(mediaRow.SourceLineNumbers, cabinetName, true)); - } - else - { - Messaging.Instance.OnMessage(WixWarnings.EmptyCabinet(mediaRow.SourceLineNumbers, cabinetName)); - } - } - - ResolvedCabinet resolvedCabinet = this.ResolveCabinet(tempCabinetFileX, fileFacades); - - // create a cabinet work item if it's not being skipped - if (CabinetBuildOption.BuildAndCopy == resolvedCabinet.BuildOption || CabinetBuildOption.BuildAndMove == resolvedCabinet.BuildOption) - { - int maxThreshold = 0; // default to the threshold for best smartcabbing (makes smallest cabinet). - - cabinetWorkItem = new CabinetWorkItem(fileFacades, resolvedCabinet.Path, maxThreshold, compressionLevel/*, this.FileManager*/); - } - else // reuse the cabinet from the cabinet cache. - { - Messaging.Instance.OnMessage(WixVerboses.ReusingCabCache(mediaRow.SourceLineNumbers, mediaRow.Cabinet, resolvedCabinet.Path)); - - try - { - // Ensure the cached cabinet timestamp is current to prevent perpetual incremental builds. The - // problematic scenario goes like this. Imagine two cabinets in the cache. Update a file that - // goes into one of the cabinets. One cabinet will get rebuilt, the other will be copied from - // the cache. Now the file (an input) has a newer timestamp than the reused cabient (an output) - // causing the project to look like it perpetually needs a rebuild until all of the reused - // cabinets get newer timestamps. - File.SetLastWriteTime(resolvedCabinet.Path, DateTime.Now); - } - catch (Exception e) - { - Messaging.Instance.OnMessage(WixWarnings.CannotUpdateCabCache(mediaRow.SourceLineNumbers, resolvedCabinet.Path, e.Message)); - } - } - - if (mediaRow.Cabinet.StartsWith("#", StringComparison.Ordinal)) - { - Table streamsTable = output.EnsureTable(this.TableDefinitions["_Streams"]); - - Row streamRow = streamsTable.CreateRow(mediaRow.SourceLineNumbers); - streamRow[0] = mediaRow.Cabinet.Substring(1); - streamRow[1] = resolvedCabinet.Path; - } - else - { - string destinationPath = Path.Combine(cabinetDir, mediaRow.Cabinet); - FileTransfer transfer; - if (FileTransfer.TryCreate(resolvedCabinet.Path, destinationPath, CabinetBuildOption.BuildAndMove == resolvedCabinet.BuildOption, "Cabinet", mediaRow.SourceLineNumbers, out transfer)) - { - transfer.Built = true; - fileTransfers.Add(transfer); - } - } - - return cabinetWorkItem; - } - - private ResolvedCabinet ResolveCabinet(string cabinetPath, IEnumerable fileFacades) - { - ResolvedCabinet resolved = null; - - List filesWithPath = fileFacades.Select(f => new BindFileWithPath() { Id = f.File.File, Path = f.WixFile.Source }).ToList(); - - foreach (IBinderFileManager fileManager in this.FileManagers) - { - resolved = fileManager.ResolveCabinet(cabinetPath, filesWithPath); - if (null != resolved) - { - break; - } - } - - return resolved; - } - - /// - /// Delegate for Cabinet Split Callback - /// - [UnmanagedFunctionPointer(CallingConvention.StdCall)] - internal delegate void FileSplitCabNamesCallback([MarshalAs(UnmanagedType.LPWStr)]string firstCabName, [MarshalAs(UnmanagedType.LPWStr)]string newCabName, [MarshalAs(UnmanagedType.LPWStr)]string fileToken); - - /// - /// Call back to Add File Transfer for new Cab and add new Cab to Media table - /// This callback can come from Multiple Cabinet Builder Threads and so should be thread safe - /// This callback will not be called in case there is no File splitting. i.e. MaximumCabinetSizeForLargeFileSplitting was not authored - /// - /// The name of splitting cabinet without extention e.g. "cab1". - /// The name of the new cabinet that would be formed by splitting e.g. "cab1b.cab" - /// The file token of the first file present in the splitting cabinet - internal void NewCabNamesCallBack([MarshalAs(UnmanagedType.LPWStr)]string firstCabName, [MarshalAs(UnmanagedType.LPWStr)]string newCabName, [MarshalAs(UnmanagedType.LPWStr)]string fileToken) - { - // Locking Mutex here as this callback can come from Multiple Cabinet Builder Threads - Mutex mutex = new Mutex(false, "WixCabinetSplitBinderCallback"); - try - { - if (!mutex.WaitOne(0, false)) // Check if you can get the lock - { - // Cound not get the Lock - Messaging.Instance.OnMessage(WixVerboses.CabinetsSplitInParallel()); - mutex.WaitOne(); // Wait on other thread - } - - string firstCabinetName = firstCabName + ".cab"; - string newCabinetName = newCabName; - bool transferAdded = false; // Used for Error Handling - - // Create File Transfer for new Cabinet using transfer of Base Cabinet - foreach (FileTransfer transfer in this.FileTransfers) - { - if (firstCabinetName.Equals(Path.GetFileName(transfer.Source), StringComparison.InvariantCultureIgnoreCase)) - { - string newCabSourcePath = Path.Combine(Path.GetDirectoryName(transfer.Source), newCabinetName); - string newCabTargetPath = Path.Combine(Path.GetDirectoryName(transfer.Destination), newCabinetName); - - FileTransfer newTransfer; - if (FileTransfer.TryCreate(newCabSourcePath, newCabTargetPath, transfer.Move, "Cabinet", transfer.SourceLineNumbers, out newTransfer)) - { - newTransfer.Built = true; - this.fileTransfers.Add(newTransfer); - transferAdded = true; - break; - } - } - } - - // Check if File Transfer was added - if (!transferAdded) - { - throw new WixException(WixErrors.SplitCabinetCopyRegistrationFailed(newCabinetName, firstCabinetName)); - } - - // Add the new Cabinets to media table using LastSequence of Base Cabinet - Table mediaTable = this.Output.Tables["Media"]; - Table wixFileTable = this.Output.Tables["WixFile"]; - int diskIDForLastSplitCabAdded = 0; // The DiskID value for the first cab in this cabinet split chain - int lastSequenceForLastSplitCabAdded = 0; // The LastSequence value for the first cab in this cabinet split chain - bool lastSplitCabinetFound = false; // Used for Error Handling - - string lastCabinetOfThisSequence = String.Empty; - // Get the Value of Last Cabinet Added in this split Sequence from Dictionary - if (!this.lastCabinetAddedToMediaTable.TryGetValue(firstCabinetName, out lastCabinetOfThisSequence)) - { - // If there is no value for this sequence, then use first Cabinet is the last one of this split sequence - lastCabinetOfThisSequence = firstCabinetName; - } - - foreach (MediaRow mediaRow in mediaTable.Rows) - { - // Get details for the Last Cabinet Added in this Split Sequence - if ((lastSequenceForLastSplitCabAdded == 0) && lastCabinetOfThisSequence.Equals(mediaRow.Cabinet, StringComparison.InvariantCultureIgnoreCase)) - { - lastSequenceForLastSplitCabAdded = mediaRow.LastSequence; - diskIDForLastSplitCabAdded = mediaRow.DiskId; - lastSplitCabinetFound = true; - } - - // Check for Name Collision for the new Cabinet added - if (newCabinetName.Equals(mediaRow.Cabinet, StringComparison.InvariantCultureIgnoreCase)) - { - // Name Collision of generated Split Cabinet Name and user Specified Cab name for current row - throw new WixException(WixErrors.SplitCabinetNameCollision(newCabinetName, firstCabinetName)); - } - } - - // Check if the last Split Cabinet was found in the Media Table - if (!lastSplitCabinetFound) - { - throw new WixException(WixErrors.SplitCabinetInsertionFailed(newCabinetName, firstCabinetName, lastCabinetOfThisSequence)); - } - - // The new Row has to be inserted just after the last cab in this cabinet split chain according to DiskID Sort - // This is because the FDI Extract requires DiskID of Split Cabinets to be continuous. It Fails otherwise with - // Error 2350 (FDI Server Error) as next DiskID did not have the right split cabinet during extraction - MediaRow newMediaRow = (MediaRow)mediaTable.CreateRow(null); - newMediaRow.Cabinet = newCabinetName; - newMediaRow.DiskId = diskIDForLastSplitCabAdded + 1; // When Sorted with DiskID, this new Cabinet Row is an Insertion - newMediaRow.LastSequence = lastSequenceForLastSplitCabAdded; - - // Now increment the DiskID for all rows that come after the newly inserted row to Ensure that DiskId is unique - foreach (MediaRow mediaRow in mediaTable.Rows) - { - // Check if this row comes after inserted row and it is not the new cabinet inserted row - if (mediaRow.DiskId >= newMediaRow.DiskId && !newCabinetName.Equals(mediaRow.Cabinet, StringComparison.InvariantCultureIgnoreCase)) - { - mediaRow.DiskId++; // Increment DiskID - } - } - - // Now Increment DiskID for All files Rows so that they refer to the right Media Row - foreach (WixFileRow wixFileRow in wixFileTable.Rows) - { - // Check if this row comes after inserted row and if this row is not the file that has to go into the current cabinet - // This check will work as we have only one large file in every splitting cabinet - // If we want to support splitting cabinet with more large files we need to update this code - if (wixFileRow.DiskId >= newMediaRow.DiskId && !wixFileRow.File.Equals(fileToken, StringComparison.InvariantCultureIgnoreCase)) - { - wixFileRow.DiskId++; // Increment DiskID - } - } - - // Update the Last Cabinet Added in the Split Sequence in Dictionary for future callback - this.lastCabinetAddedToMediaTable[firstCabinetName] = newCabinetName; - - mediaTable.ValidateRows(); // Valdiates DiskDIs, throws Exception as Wix Error if validation fails - } - finally - { - // Releasing the Mutex here - mutex.ReleaseMutex(); - } - } - - - /// - /// Gets Compiler Values of MediaTemplate Attributes governing Maximum Cabinet Size after applying Environment Variable Overrides - /// - /// Output to generate image for. - /// The indexed file rows. - private void GetMediaTemplateAttributes(out int maxCabSizeForLargeFileSplitting, out int maxUncompressedMediaSize) - { - // Get Environment Variable Overrides for MediaTemplate Attributes governing Maximum Cabinet Size - string mcslfsString = Environment.GetEnvironmentVariable("WIX_MCSLFS"); - string mumsString = Environment.GetEnvironmentVariable("WIX_MUMS"); - int maxCabSizeForLargeFileInMB = 0; - int maxPreCompressedSizeInMB = 0; - ulong testOverFlow = 0; - - // Supply Compile MediaTemplate Attributes to Cabinet Builder - Table mediaTemplateTable = this.Output.Tables["WixMediaTemplate"]; - if (mediaTemplateTable != null) - { - WixMediaTemplateRow mediaTemplateRow = (WixMediaTemplateRow)mediaTemplateTable.Rows[0]; - - // Get the Value for Max Cab Size for File Splitting - try - { - // Override authored mcslfs value if environment variable is authored. - if (!String.IsNullOrEmpty(mcslfsString)) - { - maxCabSizeForLargeFileInMB = Int32.Parse(mcslfsString); - } - else - { - maxCabSizeForLargeFileInMB = mediaTemplateRow.MaximumCabinetSizeForLargeFileSplitting; - } - testOverFlow = (ulong)maxCabSizeForLargeFileInMB * 1024 * 1024; - } - catch (FormatException) - { - throw new WixException(WixErrors.IllegalEnvironmentVariable("WIX_MCSLFS", mcslfsString)); - } - catch (OverflowException) - { - throw new WixException(WixErrors.MaximumCabinetSizeForLargeFileSplittingTooLarge(null, maxCabSizeForLargeFileInMB, CompilerCore.MaxValueOfMaxCabSizeForLargeFileSplitting)); - } - - try - { - // Override authored mums value if environment variable is authored. - if (!String.IsNullOrEmpty(mumsString)) - { - maxPreCompressedSizeInMB = Int32.Parse(mumsString); - } - else - { - maxPreCompressedSizeInMB = mediaTemplateRow.MaximumUncompressedMediaSize; - } - testOverFlow = (ulong)maxPreCompressedSizeInMB * 1024 * 1024; - } - catch (FormatException) - { - throw new WixException(WixErrors.IllegalEnvironmentVariable("WIX_MUMS", mumsString)); - } - catch (OverflowException) - { - throw new WixException(WixErrors.MaximumUncompressedMediaSizeTooLarge(null, maxPreCompressedSizeInMB)); - } - - maxCabSizeForLargeFileSplitting = maxCabSizeForLargeFileInMB; - maxUncompressedMediaSize = maxPreCompressedSizeInMB; - } - else - { - maxCabSizeForLargeFileSplitting = 0; - maxUncompressedMediaSize = CompilerCore.DefaultMaximumUncompressedMediaSize; - } - } - } -} diff --git a/src/WixToolset.Core/Bind/Databases/CreateDeltaPatchesCommand.cs b/src/WixToolset.Core/Bind/Databases/CreateDeltaPatchesCommand.cs deleted file mode 100644 index 933a1ea8..00000000 --- a/src/WixToolset.Core/Bind/Databases/CreateDeltaPatchesCommand.cs +++ /dev/null @@ -1,86 +0,0 @@ -// Copyright (c) .NET Foundation and contributors. All rights reserved. Licensed under the Microsoft Reciprocal License. See LICENSE.TXT file in the project root for full license information. - -namespace WixToolset.Bind.Databases -{ - using System; - using System.Collections.Generic; - using System.Globalization; - using System.IO; - using WixToolset.Data; - using WixToolset.Data.Rows; - - /// - /// Creates delta patches and updates the appropriate rows to point to the newly generated patches. - /// - internal class CreateDeltaPatchesCommand : ICommand - { - public IEnumerable FileFacades { private get; set; } - - public Table WixPatchIdTable { private get; set; } - - public string TempFilesLocation { private get; set; } - - public void Execute() - { - bool optimizePatchSizeForLargeFiles = false; - PatchAPI.PatchInterop.PatchSymbolFlagsType apiPatchingSymbolFlags = 0; - - if (null != this.WixPatchIdTable) - { - Row row = this.WixPatchIdTable.Rows[0]; - if (null != row) - { - if (null != row[2]) - { - optimizePatchSizeForLargeFiles = (1 == Convert.ToUInt32(row[2], CultureInfo.InvariantCulture)); - } - - if (null != row[3]) - { - apiPatchingSymbolFlags = (PatchAPI.PatchInterop.PatchSymbolFlagsType)Convert.ToUInt32(row[3], CultureInfo.InvariantCulture); - } - } - } - - foreach (FileFacade facade in this.FileFacades) - { - if (RowOperation.Modify == facade.File.Operation && - 0 != (facade.WixFile.PatchAttributes & PatchAttributeType.IncludeWholeFile)) - { - string deltaBase = String.Concat("delta_", facade.File.File); - string deltaFile = Path.Combine(this.TempFilesLocation, String.Concat(deltaBase, ".dpf")); - string headerFile = Path.Combine(this.TempFilesLocation, String.Concat(deltaBase, ".phd")); - - bool retainRangeWarning = false; - - if (PatchAPI.PatchInterop.CreateDelta( - deltaFile, - facade.WixFile.Source, - facade.DeltaPatchFile.Symbols, - facade.DeltaPatchFile.RetainOffsets, - new[] { facade.WixFile.PreviousSource }, - facade.DeltaPatchFile.PreviousSymbols.Split(new[] { ';' }), - facade.DeltaPatchFile.PreviousIgnoreLengths.Split(new[] { ';' }), - facade.DeltaPatchFile.PreviousIgnoreOffsets.Split(new[] { ';' }), - facade.DeltaPatchFile.PreviousRetainLengths.Split(new[] { ';' }), - facade.DeltaPatchFile.PreviousRetainOffsets.Split(new[] { ';' }), - apiPatchingSymbolFlags, - optimizePatchSizeForLargeFiles, - out retainRangeWarning)) - { - PatchAPI.PatchInterop.ExtractDeltaHeader(deltaFile, headerFile); - - facade.WixFile.Source = deltaFile; - facade.WixFile.DeltaPatchHeaderSource = headerFile; - } - - if (retainRangeWarning) - { - // TODO: get patch family to add to warning message for PatchWiz parity. - Messaging.Instance.OnMessage(WixWarnings.RetainRangeMismatch(facade.File.SourceLineNumbers, facade.File.File)); - } - } - } - } - } -} diff --git a/src/WixToolset.Core/Bind/Databases/CreateSpecialPropertiesCommand.cs b/src/WixToolset.Core/Bind/Databases/CreateSpecialPropertiesCommand.cs deleted file mode 100644 index 5db2768b..00000000 --- a/src/WixToolset.Core/Bind/Databases/CreateSpecialPropertiesCommand.cs +++ /dev/null @@ -1,68 +0,0 @@ -// Copyright (c) .NET Foundation and contributors. All rights reserved. Licensed under the Microsoft Reciprocal License. See LICENSE.TXT file in the project root for full license information. - -namespace WixToolset.Bind.Databases -{ - using System; - using System.Collections.Generic; - using WixToolset.Data; - using WixToolset.Data.Rows; - - internal class CreateSpecialPropertiesCommand : ICommand - { - public Table PropertyTable { private get; set; } - - public Table WixPropertyTable { private get; set; } - - public void Execute() - { - // Create the special properties. - if (null != this.WixPropertyTable) - { - // Create lists of the properties that contribute to the special lists of properties. - SortedSet adminProperties = new SortedSet(); - SortedSet secureProperties = new SortedSet(); - SortedSet hiddenProperties = new SortedSet(); - - foreach (WixPropertyRow wixPropertyRow in this.WixPropertyTable.Rows) - { - if (wixPropertyRow.Admin) - { - adminProperties.Add(wixPropertyRow.Id); - } - - if (wixPropertyRow.Hidden) - { - hiddenProperties.Add(wixPropertyRow.Id); - } - - if (wixPropertyRow.Secure) - { - secureProperties.Add(wixPropertyRow.Id); - } - } - - Table propertyTable = this.PropertyTable; - if (0 < adminProperties.Count) - { - PropertyRow row = (PropertyRow)propertyTable.CreateRow(null); - row.Property = "AdminProperties"; - row.Value = String.Join(";", adminProperties); - } - - if (0 < secureProperties.Count) - { - PropertyRow row = (PropertyRow)propertyTable.CreateRow(null); - row.Property = "SecureCustomProperties"; - row.Value = String.Join(";", secureProperties); - } - - if (0 < hiddenProperties.Count) - { - PropertyRow row = (PropertyRow)propertyTable.CreateRow(null); - row.Property = "MsiHiddenProperties"; - row.Value = String.Join(";", hiddenProperties); - } - } - } - } -} diff --git a/src/WixToolset.Core/Bind/Databases/ExtractMergeModuleFilesCommand.cs b/src/WixToolset.Core/Bind/Databases/ExtractMergeModuleFilesCommand.cs deleted file mode 100644 index bee1488b..00000000 --- a/src/WixToolset.Core/Bind/Databases/ExtractMergeModuleFilesCommand.cs +++ /dev/null @@ -1,225 +0,0 @@ -// Copyright (c) .NET Foundation and contributors. All rights reserved. Licensed under the Microsoft Reciprocal License. See LICENSE.TXT file in the project root for full license information. - -namespace WixToolset.Bind.Databases -{ - using System; - using System.Collections.Generic; - using System.ComponentModel; - using System.Globalization; - using System.IO; - using System.Linq; - using System.Runtime.InteropServices; - using WixToolset.Cab; - using WixToolset.Data; - using WixToolset.Data.Rows; - using WixToolset.MergeMod; - using WixToolset.Msi; - using WixToolset.Core.Native; - - /// - /// Retrieve files information and extract them from merge modules. - /// - internal class ExtractMergeModuleFilesCommand : ICommand - { - public IEnumerable FileFacades { private get; set; } - - public Table FileTable { private get; set; } - - public Table WixFileTable { private get; set; } - - public Table WixMergeTable { private get; set; } - - public int OutputInstallerVersion { private get; set; } - - public bool SuppressLayout { private get; set; } - - public string TempFilesLocation { private get; set; } - - public IEnumerable MergeModulesFileFacades { get; private set; } - - public void Execute() - { - List mergeModulesFileFacades = new List(); - - IMsmMerge2 merge = MsmInterop.GetMsmMerge(); - - // Index all of the file rows to be able to detect collisions with files in the Merge Modules. - // It may seem a bit expensive to build up this index solely for the purpose of checking collisions - // and you may be thinking, "Surely, we must need the file rows indexed elsewhere." It turns out - // there are other cases where we need all the file rows indexed, however they are not common cases. - // Now since Merge Modules are already slow and generally less desirable than .wixlibs we'll let - // this case be slightly more expensive because the cost of maintaining an indexed file row collection - // is a lot more costly for the common cases. - Dictionary indexedFileFacades = this.FileFacades.ToDictionary(f => f.File.File, StringComparer.Ordinal); - - foreach (WixMergeRow wixMergeRow in this.WixMergeTable.Rows) - { - bool containsFiles = this.CreateFacadesForMergeModuleFiles(wixMergeRow, mergeModulesFileFacades, indexedFileFacades); - - // If the module has files and creating layout - if (containsFiles && !this.SuppressLayout) - { - this.ExtractFilesFromMergeModule(merge, wixMergeRow); - } - } - - this.MergeModulesFileFacades = mergeModulesFileFacades; - } - - private bool CreateFacadesForMergeModuleFiles(WixMergeRow wixMergeRow, List mergeModulesFileFacades, Dictionary indexedFileFacades) - { - bool containsFiles = false; - - try - { - // read the module's File table to get its FileMediaInformation entries and gather any other information needed from the module. - using (Database db = new Database(wixMergeRow.SourceFile, OpenDatabase.ReadOnly)) - { - if (db.TableExists("File") && db.TableExists("Component")) - { - Dictionary uniqueModuleFileIdentifiers = new Dictionary(StringComparer.OrdinalIgnoreCase); - - using (View view = db.OpenExecuteView("SELECT `File`, `Directory_` FROM `File`, `Component` WHERE `Component_`=`Component`")) - { - // add each file row from the merge module into the file row collection (check for errors along the way) - while (true) - { - using (Record record = view.Fetch()) - { - if (null == record) - { - break; - } - - // NOTE: this is very tricky - the merge module file rows are not added to the - // file table because they should not be created via idt import. Instead, these - // rows are created by merging in the actual modules. - FileRow fileRow = (FileRow)this.FileTable.CreateRow(wixMergeRow.SourceLineNumbers, false); - fileRow.File = record[1]; - fileRow.Compressed = wixMergeRow.FileCompression; - - WixFileRow wixFileRow = (WixFileRow)this.WixFileTable.CreateRow(wixMergeRow.SourceLineNumbers, false); - wixFileRow.Directory = record[2]; - wixFileRow.DiskId = wixMergeRow.DiskId; - wixFileRow.PatchGroup = -1; - wixFileRow.Source = String.Concat(this.TempFilesLocation, Path.DirectorySeparatorChar, "MergeId.", wixMergeRow.Number.ToString(CultureInfo.InvariantCulture), Path.DirectorySeparatorChar, record[1]); - - FileFacade mergeModuleFileFacade = new FileFacade(true, fileRow, wixFileRow); - - FileFacade collidingFacade; - - // If case-sensitive collision with another merge module or a user-authored file identifier. - if (indexedFileFacades.TryGetValue(mergeModuleFileFacade.File.File, out collidingFacade)) - { - Messaging.Instance.OnMessage(WixErrors.DuplicateModuleFileIdentifier(wixMergeRow.SourceLineNumbers, wixMergeRow.Id, collidingFacade.File.File)); - } - else if (uniqueModuleFileIdentifiers.TryGetValue(mergeModuleFileFacade.File.File, out collidingFacade)) // case-insensitive collision with another file identifier in the same merge module - { - Messaging.Instance.OnMessage(WixErrors.DuplicateModuleCaseInsensitiveFileIdentifier(wixMergeRow.SourceLineNumbers, wixMergeRow.Id, mergeModuleFileFacade.File.File, collidingFacade.File.File)); - } - else // no collision - { - mergeModulesFileFacades.Add(mergeModuleFileFacade); - - // Keep updating the indexes as new rows are added. - indexedFileFacades.Add(mergeModuleFileFacade.File.File, mergeModuleFileFacade); - uniqueModuleFileIdentifiers.Add(mergeModuleFileFacade.File.File, mergeModuleFileFacade); - } - - containsFiles = true; - } - } - } - } - - // Get the summary information to detect the Schema - using (SummaryInformation summaryInformation = new SummaryInformation(db)) - { - string moduleInstallerVersionString = summaryInformation.GetProperty(14); - - try - { - int moduleInstallerVersion = Convert.ToInt32(moduleInstallerVersionString, CultureInfo.InvariantCulture); - if (moduleInstallerVersion > this.OutputInstallerVersion) - { - Messaging.Instance.OnMessage(WixWarnings.InvalidHigherInstallerVersionInModule(wixMergeRow.SourceLineNumbers, wixMergeRow.Id, moduleInstallerVersion, this.OutputInstallerVersion)); - } - } - catch (FormatException) - { - throw new WixException(WixErrors.MissingOrInvalidModuleInstallerVersion(wixMergeRow.SourceLineNumbers, wixMergeRow.Id, wixMergeRow.SourceFile, moduleInstallerVersionString)); - } - } - } - } - catch (FileNotFoundException) - { - throw new WixException(WixErrors.FileNotFound(wixMergeRow.SourceLineNumbers, wixMergeRow.SourceFile)); - } - catch (Win32Exception) - { - throw new WixException(WixErrors.CannotOpenMergeModule(wixMergeRow.SourceLineNumbers, wixMergeRow.Id, wixMergeRow.SourceFile)); - } - - return containsFiles; - } - - private void ExtractFilesFromMergeModule(IMsmMerge2 merge, WixMergeRow wixMergeRow) - { - bool moduleOpen = false; - short mergeLanguage; - - try - { - mergeLanguage = Convert.ToInt16(wixMergeRow.Language, CultureInfo.InvariantCulture); - } - catch (System.FormatException) - { - Messaging.Instance.OnMessage(WixErrors.InvalidMergeLanguage(wixMergeRow.SourceLineNumbers, wixMergeRow.Id, wixMergeRow.Language)); - return; - } - - try - { - merge.OpenModule(wixMergeRow.SourceFile, mergeLanguage); - moduleOpen = true; - - string safeMergeId = wixMergeRow.Number.ToString(CultureInfo.InvariantCulture.NumberFormat); - - // extract the module cabinet, then explode all of the files to a temp directory - string moduleCabPath = String.Concat(this.TempFilesLocation, Path.DirectorySeparatorChar, safeMergeId, ".module.cab"); - merge.ExtractCAB(moduleCabPath); - - string mergeIdPath = String.Concat(this.TempFilesLocation, Path.DirectorySeparatorChar, "MergeId.", safeMergeId); - Directory.CreateDirectory(mergeIdPath); - - using (WixExtractCab extractCab = new WixExtractCab()) - { - try - { - extractCab.Extract(moduleCabPath, mergeIdPath); - } - catch (FileNotFoundException) - { - throw new WixException(WixErrors.CabFileDoesNotExist(moduleCabPath, wixMergeRow.SourceFile, mergeIdPath)); - } - catch - { - throw new WixException(WixErrors.CabExtractionFailed(moduleCabPath, wixMergeRow.SourceFile, mergeIdPath)); - } - } - } - catch (COMException ce) - { - throw new WixException(WixErrors.UnableToOpenModule(wixMergeRow.SourceLineNumbers, wixMergeRow.SourceFile, ce.Message)); - } - finally - { - if (moduleOpen) - { - merge.CloseModule(); - } - } - } - } -} diff --git a/src/WixToolset.Core/Bind/Databases/FileFacade.cs b/src/WixToolset.Core/Bind/Databases/FileFacade.cs deleted file mode 100644 index 37115c97..00000000 --- a/src/WixToolset.Core/Bind/Databases/FileFacade.cs +++ /dev/null @@ -1,44 +0,0 @@ -// Copyright (c) .NET Foundation and contributors. All rights reserved. Licensed under the Microsoft Reciprocal License. See LICENSE.TXT file in the project root for full license information. - -namespace WixToolset.Bind.Databases -{ - using System.Collections.Generic; - using WixToolset.Data; - using WixToolset.Data.Rows; - - public class FileFacade - { - public FileFacade(FileRow file, WixFileRow wixFile, WixDeltaPatchFileRow deltaPatchFile) - { - this.File = file; - this.WixFile = wixFile; - this.DeltaPatchFile = deltaPatchFile; - } - - public FileFacade(bool fromModule, FileRow file, WixFileRow wixFile) - { - this.FromModule = fromModule; - this.File = file; - this.WixFile = wixFile; - } - - public bool FromModule { get; private set; } - - public FileRow File { get; private set; } - - public WixFileRow WixFile { get; private set; } - - public WixDeltaPatchFileRow DeltaPatchFile { get; private set; } - - /// - /// Gets the set of MsiAssemblyName rows created for this file. - /// - /// RowCollection of MsiAssemblyName table. - public List AssemblyNames { get; set; } - - /// - /// Gets or sets the MsiFileHash row for this file. - /// - public Row Hash { get; set; } - } -} diff --git a/src/WixToolset.Core/Bind/Databases/GetFileFacadesCommand.cs b/src/WixToolset.Core/Bind/Databases/GetFileFacadesCommand.cs deleted file mode 100644 index b6bcd3af..00000000 --- a/src/WixToolset.Core/Bind/Databases/GetFileFacadesCommand.cs +++ /dev/null @@ -1,148 +0,0 @@ -// Copyright (c) .NET Foundation and contributors. All rights reserved. Licensed under the Microsoft Reciprocal License. See LICENSE.TXT file in the project root for full license information. - -namespace WixToolset.Bind.Databases -{ - using System; - using System.Collections.Generic; - using System.Globalization; - using System.Linq; - using WixToolset.Data; - using WixToolset.Data.Rows; - - internal class GetFileFacadesCommand : ICommand - { - public Table FileTable { private get; set; } - - public Table WixFileTable { private get; set; } - - public Table WixDeltaPatchFileTable { private get; set; } - - public Table WixDeltaPatchSymbolPathsTable { private get; set; } - - public List FileFacades { get; private set; } - - public void Execute() - { - List facades = new List(this.FileTable.Rows.Count); - - RowDictionary wixFiles = new RowDictionary(this.WixFileTable); - RowDictionary deltaPatchFiles = new RowDictionary(this.WixDeltaPatchFileTable); - - foreach (FileRow file in this.FileTable.Rows) - { - WixDeltaPatchFileRow deltaPatchFile = null; - - deltaPatchFiles.TryGetValue(file.File, out deltaPatchFile); - - facades.Add(new FileFacade(file, wixFiles[file.File], deltaPatchFile)); - } - - if (null != this.WixDeltaPatchSymbolPathsTable) - { - this.ResolveDeltaPatchSymbolPaths(deltaPatchFiles, facades); - } - - this.FileFacades = facades; - } - - /// - /// Merge data from the WixPatchSymbolPaths rows into the WixDeltaPatchFile rows. - /// - public RowDictionary ResolveDeltaPatchSymbolPaths(RowDictionary deltaPatchFiles, IEnumerable facades) - { - ILookup filesByComponent = null; - ILookup filesByDirectory = null; - ILookup filesByDiskId = null; - - foreach (WixDeltaPatchSymbolPathsRow row in this.WixDeltaPatchSymbolPathsTable.RowsAs().OrderBy(r => r.Type)) - { - switch (row.Type) - { - case SymbolPathType.File: - this.MergeSymbolPaths(row, deltaPatchFiles[row.Id]); - break; - - case SymbolPathType.Component: - if (null == filesByComponent) - { - filesByComponent = facades.ToLookup(f => f.File.Component); - } - - foreach (FileFacade facade in filesByComponent[row.Id]) - { - this.MergeSymbolPaths(row, deltaPatchFiles[facade.File.File]); - } - break; - - case SymbolPathType.Directory: - if (null == filesByDirectory) - { - filesByDirectory = facades.ToLookup(f => f.WixFile.Directory); - } - - foreach (FileFacade facade in filesByDirectory[row.Id]) - { - this.MergeSymbolPaths(row, deltaPatchFiles[facade.File.File]); - } - break; - - case SymbolPathType.Media: - if (null == filesByDiskId) - { - filesByDiskId = facades.ToLookup(f => f.WixFile.DiskId.ToString(CultureInfo.InvariantCulture)); - } - - foreach (FileFacade facade in filesByDiskId[row.Id]) - { - this.MergeSymbolPaths(row, deltaPatchFiles[facade.File.File]); - } - break; - - case SymbolPathType.Product: - foreach (WixDeltaPatchFileRow fileRow in deltaPatchFiles.Values) - { - this.MergeSymbolPaths(row, fileRow); - } - break; - - default: - // error - break; - } - } - - return deltaPatchFiles; - } - - /// - /// Merge data from a row in the WixPatchSymbolsPaths table into an associated WixDeltaPatchFile row. - /// - /// Row from the WixPatchSymbolsPaths table. - /// FileRow into which to set symbol information. - /// This includes PreviousData as well. - private void MergeSymbolPaths(WixDeltaPatchSymbolPathsRow row, WixDeltaPatchFileRow file) - { - if (null == file.Symbols) - { - file.Symbols = row.SymbolPaths; - } - else - { - file.Symbols = String.Concat(file.Symbols, ";", row.SymbolPaths); - } - - Field field = row.Fields[2]; - if (null != field.PreviousData) - { - if (null == file.PreviousSymbols) - { - file.PreviousSymbols = field.PreviousData; - } - else - { - file.PreviousSymbols = String.Concat(file.PreviousSymbols, ";", field.PreviousData); - } - } - } - } -} diff --git a/src/WixToolset.Core/Bind/Databases/MergeModulesCommand.cs b/src/WixToolset.Core/Bind/Databases/MergeModulesCommand.cs deleted file mode 100644 index 035ef059..00000000 --- a/src/WixToolset.Core/Bind/Databases/MergeModulesCommand.cs +++ /dev/null @@ -1,350 +0,0 @@ -// Copyright (c) .NET Foundation and contributors. All rights reserved. Licensed under the Microsoft Reciprocal License. See LICENSE.TXT file in the project root for full license information. - -namespace WixToolset.Bind.Databases -{ - using System; - using System.Collections.Generic; - using System.Collections.Specialized; - using System.ComponentModel; - using System.Diagnostics; - using System.Globalization; - using System.IO; - using System.Linq; - using System.Runtime.InteropServices; - using System.Text; - using System.Xml; - using System.Xml.XPath; - using WixToolset.Clr.Interop; - using WixToolset.Data; - using WixToolset.Data.Rows; - using WixToolset.MergeMod; - using WixToolset.Msi; - using WixToolset.Core.Native; - - /// - /// Update file information. - /// - internal class MergeModulesCommand : ICommand - { - public IEnumerable FileFacades { private get; set; } - - public Output Output { private get; set; } - - public string OutputPath { private get; set; } - - public IEnumerable SuppressedTableNames { private get; set; } - - public string TempFilesLocation { private get; set; } - - public void Execute() - { - Debug.Assert(OutputType.Product == this.Output.Type); - - Table wixMergeTable = this.Output.Tables["WixMerge"]; - Table wixFeatureModulesTable = this.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 = MsmInterop.GetMsmMerge(); - - logPath = Path.Combine(this.TempFilesLocation, "merge.log"); - merge.OpenLog(logPath); - logOpen = true; - - merge.OpenDatabase(this.OutputPath); - 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) - { - Messaging.Instance.OnMessage(WixErrors.InvalidMergeLanguage(wixMergeRow.SourceLineNumbers, wixMergeRow.Id, wixMergeRow.Language)); - continue; - } - - Messaging.Instance.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 - Messaging.Instance.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]) - { - Messaging.Instance.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: - Messaging.Instance.OnMessage(WixErrors.MergeExcludedModule(wixMergeRow.SourceLineNumbers, wixMergeRow.Id, moduleKeys.ToString())); - break; - case MsmErrorType.msmErrorFeatureRequired: - Messaging.Instance.OnMessage(WixErrors.MergeFeatureRequired(wixMergeRow.SourceLineNumbers, mergeError.ModuleTable, moduleKeys.ToString(), wixMergeRow.SourceFile, wixMergeRow.Id)); - break; - case MsmErrorType.msmErrorLanguageFailed: - Messaging.Instance.OnMessage(WixErrors.MergeLanguageFailed(wixMergeRow.SourceLineNumbers, mergeError.Language, wixMergeRow.SourceFile)); - break; - case MsmErrorType.msmErrorLanguageUnsupported: - Messaging.Instance.OnMessage(WixErrors.MergeLanguageUnsupported(wixMergeRow.SourceLineNumbers, mergeError.Language, wixMergeRow.SourceFile)); - break; - case MsmErrorType.msmErrorResequenceMerge: - Messaging.Instance.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 - { - Messaging.Instance.OnMessage(WixWarnings.MergeTableFailed(wixMergeRow.SourceLineNumbers, mergeError.DatabaseTable, databaseKeys.ToString(), wixMergeRow.SourceFile)); - } - break; - case MsmErrorType.msmErrorPlatformMismatch: - Messaging.Instance.OnMessage(WixErrors.MergePlatformMismatch(wixMergeRow.SourceLineNumbers, wixMergeRow.SourceFile)); - break; - default: - Messaging.Instance.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) - { - Messaging.Instance.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 (Messaging.Instance.EncounteredError) - { - return; - } - - using (Database db = new Database(this.OutputPath, OpenDatabase.Direct)) - { - Table suppressActionTable = this.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) - { - Messaging.Instance.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 this.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; - } - - Messaging.Instance.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 - Messaging.Instance.OnMessage(WixVerboses.ResequencingMergeModuleFiles()); - using (View view = db.OpenView("SELECT `Sequence`, `Attributes` FROM `File` WHERE `File`=?")) - { - foreach (FileFacade file in this.FileFacades) - { - if (!file.FromModule) - { - continue; - } - - using (Record record = new Record(1)) - { - record.SetString(1, file.File.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, file.File.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 == file.File.Compressed) - { - // these are mutually exclusive - attributes |= MsiInterop.MsidbFileAttributesCompressed; - attributes &= ~MsiInterop.MsidbFileAttributesNoncompressed; - } - else if (YesNoType.No == file.File.Compressed) - { - // these are mutually exclusive - attributes |= MsiInterop.MsidbFileAttributesNoncompressed; - attributes &= ~MsiInterop.MsidbFileAttributesCompressed; - } - else // not specified - { - Debug.Assert(YesNoType.NotSet == file.File.Compressed); - - // clear any compression bits - attributes &= ~MsiInterop.MsidbFileAttributesCompressed; - attributes &= ~MsiInterop.MsidbFileAttributesNoncompressed; - } - - recordUpdate.SetInteger(2, attributes); - - view.Modify(ModifyView.Update, recordUpdate); - } - } - } - - db.Commit(); - } - } - } -} diff --git a/src/WixToolset.Core/Bind/Databases/ProcessUncompressedFilesCommand.cs b/src/WixToolset.Core/Bind/Databases/ProcessUncompressedFilesCommand.cs deleted file mode 100644 index dd7b85b7..00000000 --- a/src/WixToolset.Core/Bind/Databases/ProcessUncompressedFilesCommand.cs +++ /dev/null @@ -1,115 +0,0 @@ -// Copyright (c) .NET Foundation and contributors. All rights reserved. Licensed under the Microsoft Reciprocal License. See LICENSE.TXT file in the project root for full license information. - -namespace WixToolset.Bind.Databases -{ - using System; - using System.Collections; - using System.Collections.Generic; - using System.IO; - using WixToolset.Data; - using WixToolset.Data.Rows; - using WixToolset.Msi; - using WixToolset.Core.Native; - - /// - /// Defines the file transfers necessary to layout the uncompressed files. - /// - internal class ProcessUncompressedFilesCommand : ICommand - { - public string DatabasePath { private get; set; } - - public IEnumerable FileFacades { private get; set; } - - public RowDictionary MediaRows { private get; set; } - - public string LayoutDirectory { private get; set; } - - public bool Compressed { private get; set; } - - public bool LongNamesInImage { private get; set; } - - public Func ResolveMedia { private get; set; } - - public Table WixMediaTable { private get; set; } - - public IEnumerable FileTransfers { get; private set; } - - public void Execute() - { - List fileTransfers = new List(); - - Hashtable directories = new Hashtable(); - - RowDictionary wixMediaRows = new RowDictionary(this.WixMediaTable); - - using (Database db = new Database(this.DatabasePath, OpenDatabase.ReadOnly)) - { - using (View directoryView = db.OpenExecuteView("SELECT `Directory`, `Directory_Parent`, `DefaultDir` FROM `Directory`")) - { - while (true) - { - using (Record directoryRecord = directoryView.Fetch()) - { - if (null == directoryRecord) - { - break; - } - - string sourceName = Installer.GetName(directoryRecord.GetString(3), true, this.LongNamesInImage); - - directories.Add(directoryRecord.GetString(1), new ResolvedDirectory(directoryRecord.GetString(2), sourceName)); - } - } - } - - using (View fileView = db.OpenView("SELECT `Directory_`, `FileName` FROM `Component`, `File` WHERE `Component`.`Component`=`File`.`Component_` AND `File`.`File`=?")) - { - using (Record fileQueryRecord = new Record(1)) - { - // for each file in the array of uncompressed files - foreach (FileFacade facade in this.FileFacades) - { - MediaRow mediaRow = this.MediaRows.Get(facade.WixFile.DiskId); - string relativeFileLayoutPath = null; - - WixMediaRow wixMediaRow = null; - string mediaLayoutFolder = null; - - if (wixMediaRows.TryGetValue(mediaRow.GetKey(), out wixMediaRow)) - { - mediaLayoutFolder = wixMediaRow.Layout; - } - - string mediaLayoutDirectory = this.ResolveMedia(mediaRow, mediaLayoutFolder, this.LayoutDirectory); - - // setup up the query record and find the appropriate file in the - // previously executed file view - fileQueryRecord[1] = facade.File.File; - fileView.Execute(fileQueryRecord); - - using (Record fileRecord = fileView.Fetch()) - { - if (null == fileRecord) - { - throw new WixException(WixErrors.FileIdentifierNotFound(facade.File.SourceLineNumbers, facade.File.File)); - } - - relativeFileLayoutPath = Binder.GetFileSourcePath(directories, fileRecord[1], fileRecord[2], this.Compressed, this.LongNamesInImage); - } - - // finally put together the base media layout path and the relative file layout path - string fileLayoutPath = Path.Combine(mediaLayoutDirectory, relativeFileLayoutPath); - FileTransfer transfer; - if (FileTransfer.TryCreate(facade.WixFile.Source, fileLayoutPath, false, "File", facade.File.SourceLineNumbers, out transfer)) - { - fileTransfers.Add(transfer); - } - } - } - } - } - - this.FileTransfers = fileTransfers; - } - } -} diff --git a/src/WixToolset.Core/Bind/Databases/UpdateControlTextCommand.cs b/src/WixToolset.Core/Bind/Databases/UpdateControlTextCommand.cs deleted file mode 100644 index 9e17ee02..00000000 --- a/src/WixToolset.Core/Bind/Databases/UpdateControlTextCommand.cs +++ /dev/null @@ -1,80 +0,0 @@ -// Copyright (c) .NET Foundation and contributors. All rights reserved. Licensed under the Microsoft Reciprocal License. See LICENSE.TXT file in the project root for full license information. - -namespace WixToolset.Bind.Databases -{ - using System; - using System.IO; - using WixToolset.Data; - using WixToolset.Data.Rows; - - internal class UpdateControlTextCommand : ICommand - { - public Table BBControlTable { private get; set; } - - public Table WixBBControlTable { private get; set; } - - public Table ControlTable { private get; set; } - - public Table WixControlTable { private get; set; } - - public void Execute() - { - if (null != this.WixBBControlTable) - { - RowDictionary bbControlRows = new RowDictionary(this.BBControlTable); - foreach (Row wixRow in this.WixBBControlTable.Rows) - { - BBControlRow bbControlRow = bbControlRows.Get(wixRow.GetPrimaryKey()); - bbControlRow.Text = this.ReadTextFile(bbControlRow.SourceLineNumbers, wixRow.FieldAsString(2)); - } - } - - if (null != this.WixControlTable) - { - RowDictionary controlRows = new RowDictionary(this.ControlTable); - foreach (Row wixRow in this.WixControlTable.Rows) - { - ControlRow controlRow = controlRows.Get(wixRow.GetPrimaryKey()); - controlRow.Text = this.ReadTextFile(controlRow.SourceLineNumbers, wixRow.FieldAsString(2)); - } - } - } - - /// - /// Reads a text file and returns the contents. - /// - /// Source line numbers for row from source. - /// Source path to file to read. - /// Text string read from file. - private string ReadTextFile(SourceLineNumber sourceLineNumbers, string source) - { - string text = null; - - try - { - using (StreamReader reader = new StreamReader(source)) - { - text = reader.ReadToEnd(); - } - } - catch (DirectoryNotFoundException e) - { - Messaging.Instance.OnMessage(WixErrors.BinderFileManagerMissingFile(sourceLineNumbers, e.Message)); - } - catch (FileNotFoundException e) - { - Messaging.Instance.OnMessage(WixErrors.BinderFileManagerMissingFile(sourceLineNumbers, e.Message)); - } - catch (IOException e) - { - Messaging.Instance.OnMessage(WixErrors.BinderFileManagerMissingFile(sourceLineNumbers, e.Message)); - } - catch (NotSupportedException) - { - Messaging.Instance.OnMessage(WixErrors.FileNotFound(sourceLineNumbers, source)); - } - - return text; - } - } -} diff --git a/src/WixToolset.Core/Bind/Databases/UpdateFileFacadesCommand.cs b/src/WixToolset.Core/Bind/Databases/UpdateFileFacadesCommand.cs deleted file mode 100644 index 36818afa..00000000 --- a/src/WixToolset.Core/Bind/Databases/UpdateFileFacadesCommand.cs +++ /dev/null @@ -1,532 +0,0 @@ -// Copyright (c) .NET Foundation and contributors. All rights reserved. Licensed under the Microsoft Reciprocal License. See LICENSE.TXT file in the project root for full license information. - -namespace WixToolset.Bind.Databases -{ - using System; - using System.Collections.Generic; - using System.Collections.Specialized; - using System.ComponentModel; - using System.Globalization; - using System.IO; - using System.Linq; - using System.Xml; - using System.Xml.XPath; - using WixToolset.Clr.Interop; - using WixToolset.Data; - using WixToolset.Data.Rows; - using WixToolset.Msi; - - /// - /// Update file information. - /// - internal class UpdateFileFacadesCommand : ICommand - { - public IEnumerable FileFacades { private get; set; } - - public IEnumerable UpdateFileFacades { private get; set; } - - public string ModularizationGuid { private get; set; } - - public Output Output { private get; set; } - - public bool OverwriteHash { private get; set; } - - public TableDefinitionCollection TableDefinitions { private get; set; } - - public IDictionary VariableCache { private get; set; } - - public void Execute() - { - foreach (FileFacade file in this.UpdateFileFacades) - { - this.UpdateFileFacade(file); - } - } - - private void UpdateFileFacade(FileFacade file) - { - FileInfo fileInfo = null; - try - { - fileInfo = new FileInfo(file.WixFile.Source); - } - catch (ArgumentException) - { - Messaging.Instance.OnMessage(WixErrors.InvalidFileName(file.File.SourceLineNumbers, file.WixFile.Source)); - return; - } - catch (PathTooLongException) - { - Messaging.Instance.OnMessage(WixErrors.InvalidFileName(file.File.SourceLineNumbers, file.WixFile.Source)); - return; - } - catch (NotSupportedException) - { - Messaging.Instance.OnMessage(WixErrors.InvalidFileName(file.File.SourceLineNumbers, file.WixFile.Source)); - return; - } - - if (!fileInfo.Exists) - { - Messaging.Instance.OnMessage(WixErrors.CannotFindFile(file.File.SourceLineNumbers, file.File.File, file.File.FileName, file.WixFile.Source)); - return; - } - - using (FileStream fileStream = new FileStream(fileInfo.FullName, FileMode.Open, FileAccess.Read, FileShare.Read)) - { - if (Int32.MaxValue < fileStream.Length) - { - throw new WixException(WixErrors.FileTooLarge(file.File.SourceLineNumbers, file.WixFile.Source)); - } - - file.File.FileSize = Convert.ToInt32(fileStream.Length, CultureInfo.InvariantCulture); - } - - string version = null; - string language = null; - try - { - Installer.GetFileVersion(fileInfo.FullName, out version, out language); - } - catch (Win32Exception e) - { - if (0x2 == e.NativeErrorCode) // ERROR_FILE_NOT_FOUND - { - throw new WixException(WixErrors.FileNotFound(file.File.SourceLineNumbers, fileInfo.FullName)); - } - else - { - throw new WixException(WixErrors.Win32Exception(e.NativeErrorCode, e.Message)); - } - } - - // If there is no version, it is assumed there is no language because it won't matter in the versioning of the install. - if (String.IsNullOrEmpty(version)) // unversioned files have their hashes added to the MsiFileHash table - { - if (!this.OverwriteHash) - { - // not overwriting hash, so don't do the rest of these options. - } - else if (null != file.File.Version) - { - // Search all of the file rows available to see if the specified version is actually a companion file. Yes, this looks - // very expensive and you're probably thinking it would be better to create an index of some sort to do an O(1) look up. - // That's a reasonable thought but companion file usage is usually pretty rare so we'd be doing something expensive (indexing - // all the file rows) for a relatively uncommon situation. Let's not do that. - // - // Also, if we do not find a matching file identifier then the user provided a default version and is providing a version - // for unversioned file. That's allowed but generally a dangerous thing to do so let's point that out to the user. - if (!this.FileFacades.Any(r => file.File.Version.Equals(r.File.File, StringComparison.Ordinal))) - { - Messaging.Instance.OnMessage(WixWarnings.DefaultVersionUsedForUnversionedFile(file.File.SourceLineNumbers, file.File.Version, file.File.File)); - } - } - else - { - if (null != file.File.Language) - { - Messaging.Instance.OnMessage(WixWarnings.DefaultLanguageUsedForUnversionedFile(file.File.SourceLineNumbers, file.File.Language, file.File.File)); - } - - int[] hash; - try - { - Installer.GetFileHash(fileInfo.FullName, 0, out hash); - } - catch (Win32Exception e) - { - if (0x2 == e.NativeErrorCode) // ERROR_FILE_NOT_FOUND - { - throw new WixException(WixErrors.FileNotFound(file.File.SourceLineNumbers, fileInfo.FullName)); - } - else - { - throw new WixException(WixErrors.Win32Exception(e.NativeErrorCode, fileInfo.FullName, e.Message)); - } - } - - if (null == file.Hash) - { - Table msiFileHashTable = this.Output.EnsureTable(this.TableDefinitions["MsiFileHash"]); - file.Hash = msiFileHashTable.CreateRow(file.File.SourceLineNumbers); - } - - file.Hash[0] = file.File.File; - file.Hash[1] = 0; - file.Hash[2] = hash[0]; - file.Hash[3] = hash[1]; - file.Hash[4] = hash[2]; - file.Hash[5] = hash[3]; - } - } - else // update the file row with the version and language information. - { - // If no version was provided by the user, use the version from the file itself. - // This is the most common case. - if (String.IsNullOrEmpty(file.File.Version)) - { - file.File.Version = version; - } - else if (!this.FileFacades.Any(r => file.File.Version.Equals(r.File.File, StringComparison.Ordinal))) // this looks expensive, but see explanation below. - { - // The user provided a default version for the file row so we looked for a companion file (a file row with Id matching - // the version value). We didn't find it so, we will override the default version they provided with the actual - // version from the file itself. Now, I know it looks expensive to search through all the file rows trying to match - // on the Id. However, the alternative is to build a big index of all file rows to do look ups. Since this case - // where the file version is already present is rare (companion files are pretty uncommon), we'll do the more - // CPU intensive search to save on the memory intensive index that wouldn't be used much. - // - // Also note this case can occur when the file is being updated using the WixBindUpdatedFiles extension mechanism. - // That's typically even more rare than companion files so again, no index, just search. - file.File.Version = version; - } - - if (!String.IsNullOrEmpty(file.File.Language) && String.IsNullOrEmpty(language)) - { - Messaging.Instance.OnMessage(WixWarnings.DefaultLanguageUsedForVersionedFile(file.File.SourceLineNumbers, file.File.Language, file.File.File)); - } - else // override the default provided by the user (usually nothing) with the actual language from the file itself. - { - file.File.Language = language; - } - - // Populate the binder variables for this file information if requested. - if (null != this.VariableCache) - { - if (!String.IsNullOrEmpty(file.File.Version)) - { - string key = String.Format(CultureInfo.InvariantCulture, "fileversion.{0}", BindDatabaseCommand.Demodularize(this.Output.Type, this.ModularizationGuid, file.File.File)); - this.VariableCache[key] = file.File.Version; - } - - if (!String.IsNullOrEmpty(file.File.Language)) - { - string key = String.Format(CultureInfo.InvariantCulture, "filelanguage.{0}", BindDatabaseCommand.Demodularize(this.Output.Type, ModularizationGuid, file.File.File)); - this.VariableCache[key] = file.File.Language; - } - } - } - - // If this is a CLR assembly, load the assembly and get the assembly name information - if (FileAssemblyType.DotNetAssembly == file.WixFile.AssemblyType) - { - bool targetNetfx1 = false; - StringDictionary assemblyNameValues = new StringDictionary(); - - ClrInterop.IReferenceIdentity referenceIdentity = null; - Guid referenceIdentityGuid = ClrInterop.ReferenceIdentityGuid; - uint result = ClrInterop.GetAssemblyIdentityFromFile(fileInfo.FullName, ref referenceIdentityGuid, out referenceIdentity); - if (0 == result && null != referenceIdentity) - { - string imageRuntimeVersion = referenceIdentity.GetAttribute(null, "ImageRuntimeVersion"); - if (null != imageRuntimeVersion) - { - targetNetfx1 = imageRuntimeVersion.StartsWith("v1", StringComparison.OrdinalIgnoreCase); - } - - string culture = referenceIdentity.GetAttribute(null, "Culture") ?? "neutral"; - assemblyNameValues.Add("Culture", culture); - - string name = referenceIdentity.GetAttribute(null, "Name"); - if (null != name) - { - assemblyNameValues.Add("Name", name); - } - - string processorArchitecture = referenceIdentity.GetAttribute(null, "ProcessorArchitecture"); - if (null != processorArchitecture) - { - assemblyNameValues.Add("ProcessorArchitecture", processorArchitecture); - } - - string publicKeyToken = referenceIdentity.GetAttribute(null, "PublicKeyToken"); - if (null != publicKeyToken) - { - bool publicKeyIsNeutral = (String.Equals(publicKeyToken, "neutral", StringComparison.OrdinalIgnoreCase)); - - // Managed code expects "null" instead of "neutral", and - // this won't be installed to the GAC since it's not signed anyway. - assemblyNameValues.Add("publicKeyToken", publicKeyIsNeutral ? "null" : publicKeyToken.ToUpperInvariant()); - assemblyNameValues.Add("publicKeyTokenPreservedCase", publicKeyIsNeutral ? "null" : publicKeyToken); - } - else if (file.WixFile.AssemblyApplication == null) - { - throw new WixException(WixErrors.GacAssemblyNoStrongName(file.File.SourceLineNumbers, fileInfo.FullName, file.File.Component)); - } - - string assemblyVersion = referenceIdentity.GetAttribute(null, "Version"); - if (null != version) - { - assemblyNameValues.Add("Version", assemblyVersion); - } - } - else - { - Messaging.Instance.OnMessage(WixErrors.InvalidAssemblyFile(file.File.SourceLineNumbers, fileInfo.FullName, String.Format(CultureInfo.InvariantCulture, "HRESULT: 0x{0:x8}", result))); - return; - } - - Table assemblyNameTable = this.Output.EnsureTable(this.TableDefinitions["MsiAssemblyName"]); - if (assemblyNameValues.ContainsKey("name")) - { - this.SetMsiAssemblyName(assemblyNameTable, file, "name", assemblyNameValues["name"]); - } - - if (!String.IsNullOrEmpty(version)) - { - this.SetMsiAssemblyName(assemblyNameTable, file, "fileVersion", version); - } - - if (assemblyNameValues.ContainsKey("version")) - { - string assemblyVersion = assemblyNameValues["version"]; - - if (!targetNetfx1) - { - // There is a bug in v1 fusion that requires the assembly's "version" attribute - // to be equal to or longer than the "fileVersion" in length when its present; - // the workaround is to prepend zeroes to the last version number in the assembly - // version. - if (null != version && version.Length > assemblyVersion.Length) - { - string padding = new string('0', version.Length - assemblyVersion.Length); - string[] assemblyVersionNumbers = assemblyVersion.Split('.'); - - if (assemblyVersionNumbers.Length > 0) - { - assemblyVersionNumbers[assemblyVersionNumbers.Length - 1] = String.Concat(padding, assemblyVersionNumbers[assemblyVersionNumbers.Length - 1]); - assemblyVersion = String.Join(".", assemblyVersionNumbers); - } - } - } - - this.SetMsiAssemblyName(assemblyNameTable, file, "version", assemblyVersion); - } - - if (assemblyNameValues.ContainsKey("culture")) - { - this.SetMsiAssemblyName(assemblyNameTable, file, "culture", assemblyNameValues["culture"]); - } - - if (assemblyNameValues.ContainsKey("publicKeyToken")) - { - this.SetMsiAssemblyName(assemblyNameTable, file, "publicKeyToken", assemblyNameValues["publicKeyToken"]); - } - - if (!String.IsNullOrEmpty(file.WixFile.ProcessorArchitecture)) - { - this.SetMsiAssemblyName(assemblyNameTable, file, "processorArchitecture", file.WixFile.ProcessorArchitecture); - } - - if (assemblyNameValues.ContainsKey("processorArchitecture")) - { - this.SetMsiAssemblyName(assemblyNameTable, file, "processorArchitecture", assemblyNameValues["processorArchitecture"]); - } - - // add the assembly name to the information cache - if (null != this.VariableCache) - { - string fileId = BindDatabaseCommand.Demodularize(this.Output.Type, this.ModularizationGuid, file.File.File); - string key = String.Concat("assemblyfullname.", fileId); - string assemblyName = String.Concat(assemblyNameValues["name"], ", version=", assemblyNameValues["version"], ", culture=", assemblyNameValues["culture"], ", publicKeyToken=", String.IsNullOrEmpty(assemblyNameValues["publicKeyToken"]) ? "null" : assemblyNameValues["publicKeyToken"]); - if (assemblyNameValues.ContainsKey("processorArchitecture")) - { - assemblyName = String.Concat(assemblyName, ", processorArchitecture=", assemblyNameValues["processorArchitecture"]); - } - - this.VariableCache[key] = assemblyName; - - // Add entries with the preserved case publicKeyToken - string pcAssemblyNameKey = String.Concat("assemblyfullnamepreservedcase.", fileId); - this.VariableCache[pcAssemblyNameKey] = (assemblyNameValues["publicKeyToken"] == assemblyNameValues["publicKeyTokenPreservedCase"]) ? assemblyName : assemblyName.Replace(assemblyNameValues["publicKeyToken"], assemblyNameValues["publicKeyTokenPreservedCase"]); - - string pcPublicKeyTokenKey = String.Concat("assemblypublickeytokenpreservedcase.", fileId); - this.VariableCache[pcPublicKeyTokenKey] = assemblyNameValues["publicKeyTokenPreservedCase"]; - } - } - else if (FileAssemblyType.Win32Assembly == file.WixFile.AssemblyType) - { - // TODO: Consider passing in the this.FileFacades as an indexed collection instead of searching through - // all files like this. Even though this is a rare case it looks like we might be able to index the - // file earlier. - FileFacade fileManifest = this.FileFacades.SingleOrDefault(r => r.File.File.Equals(file.WixFile.AssemblyManifest, StringComparison.Ordinal)); - if (null == fileManifest) - { - Messaging.Instance.OnMessage(WixErrors.MissingManifestForWin32Assembly(file.File.SourceLineNumbers, file.File.File, file.WixFile.AssemblyManifest)); - } - - string win32Type = null; - string win32Name = null; - string win32Version = null; - string win32ProcessorArchitecture = null; - string win32PublicKeyToken = null; - - // loading the dom is expensive we want more performant APIs than the DOM - // Navigator is cheaper than dom. Perhaps there is a cheaper API still. - try - { - XPathDocument doc = new XPathDocument(fileManifest.WixFile.Source); - XPathNavigator nav = doc.CreateNavigator(); - nav.MoveToRoot(); - - // this assumes a particular schema for a win32 manifest and does not - // provide error checking if the file does not conform to schema. - // The fallback case here is that nothing is added to the MsiAssemblyName - // table for an out of tolerance Win32 manifest. Perhaps warnings needed. - if (nav.MoveToFirstChild()) - { - while (nav.NodeType != XPathNodeType.Element || nav.Name != "assembly") - { - nav.MoveToNext(); - } - - if (nav.MoveToFirstChild()) - { - bool hasNextSibling = true; - while (nav.NodeType != XPathNodeType.Element || nav.Name != "assemblyIdentity" && hasNextSibling) - { - hasNextSibling = nav.MoveToNext(); - } - if (!hasNextSibling) - { - Messaging.Instance.OnMessage(WixErrors.InvalidManifestContent(file.File.SourceLineNumbers, fileManifest.WixFile.Source)); - return; - } - - if (nav.MoveToAttribute("type", String.Empty)) - { - win32Type = nav.Value; - nav.MoveToParent(); - } - - if (nav.MoveToAttribute("name", String.Empty)) - { - win32Name = nav.Value; - nav.MoveToParent(); - } - - if (nav.MoveToAttribute("version", String.Empty)) - { - win32Version = nav.Value; - nav.MoveToParent(); - } - - if (nav.MoveToAttribute("processorArchitecture", String.Empty)) - { - win32ProcessorArchitecture = nav.Value; - nav.MoveToParent(); - } - - if (nav.MoveToAttribute("publicKeyToken", String.Empty)) - { - win32PublicKeyToken = nav.Value; - nav.MoveToParent(); - } - } - } - } - catch (FileNotFoundException fe) - { - Messaging.Instance.OnMessage(WixErrors.FileNotFound(new SourceLineNumber(fileManifest.WixFile.Source), fe.FileName, "AssemblyManifest")); - } - catch (XmlException xe) - { - Messaging.Instance.OnMessage(WixErrors.InvalidXml(new SourceLineNumber(fileManifest.WixFile.Source), "manifest", xe.Message)); - } - - Table assemblyNameTable = this.Output.EnsureTable(this.TableDefinitions["MsiAssemblyName"]); - if (!String.IsNullOrEmpty(win32Name)) - { - this.SetMsiAssemblyName(assemblyNameTable, file, "name", win32Name); - } - - if (!String.IsNullOrEmpty(win32Version)) - { - this.SetMsiAssemblyName(assemblyNameTable, file, "version", win32Version); - } - - if (!String.IsNullOrEmpty(win32Type)) - { - this.SetMsiAssemblyName(assemblyNameTable, file, "type", win32Type); - } - - if (!String.IsNullOrEmpty(win32ProcessorArchitecture)) - { - this.SetMsiAssemblyName(assemblyNameTable, file, "processorArchitecture", win32ProcessorArchitecture); - } - - if (!String.IsNullOrEmpty(win32PublicKeyToken)) - { - this.SetMsiAssemblyName(assemblyNameTable, file, "publicKeyToken", win32PublicKeyToken); - } - } - } - - /// - /// Set an MsiAssemblyName row. If it was directly authored, override the value, otherwise - /// create a new row. - /// - /// MsiAssemblyName table. - /// FileFacade containing the assembly read for the MsiAssemblyName row. - /// MsiAssemblyName name. - /// MsiAssemblyName value. - private void SetMsiAssemblyName(Table assemblyNameTable, FileFacade file, string name, string value) - { - // check for null value (this can occur when grabbing the file version from an assembly without one) - if (String.IsNullOrEmpty(value)) - { - Messaging.Instance.OnMessage(WixWarnings.NullMsiAssemblyNameValue(file.File.SourceLineNumbers, file.File.Component, name)); - } - else - { - Row assemblyNameRow = null; - - // override directly authored value - foreach (Row row in assemblyNameTable.Rows) - { - if ((string)row[0] == file.File.Component && (string)row[1] == name) - { - assemblyNameRow = row; - break; - } - } - - // if the assembly will be GAC'd and the name in the file table doesn't match the name in the MsiAssemblyName table, error because the install will fail. - if ("name" == name && FileAssemblyType.DotNetAssembly == file.WixFile.AssemblyType && - String.IsNullOrEmpty(file.WixFile.AssemblyApplication) && - !String.Equals(Path.GetFileNameWithoutExtension(file.File.LongFileName), value, StringComparison.OrdinalIgnoreCase)) - { - Messaging.Instance.OnMessage(WixErrors.GACAssemblyIdentityWarning(file.File.SourceLineNumbers, Path.GetFileNameWithoutExtension(file.File.LongFileName), value)); - } - - if (null == assemblyNameRow) - { - assemblyNameRow = assemblyNameTable.CreateRow(file.File.SourceLineNumbers); - assemblyNameRow[0] = file.File.Component; - assemblyNameRow[1] = name; - assemblyNameRow[2] = value; - - // put the MsiAssemblyName row in the same section as the related File row - assemblyNameRow.SectionId = file.File.SectionId; - - if (null == file.AssemblyNames) - { - file.AssemblyNames = new List(); - } - - file.AssemblyNames.Add(assemblyNameRow); - } - else - { - assemblyNameRow[2] = value; - } - - if (this.VariableCache != null) - { - string key = String.Format(CultureInfo.InvariantCulture, "assembly{0}.{1}", name, BindDatabaseCommand.Demodularize(this.Output.Type, this.ModularizationGuid, file.File.File)).ToLowerInvariant(); - this.VariableCache[key] = (string)assemblyNameRow[2]; - } - } - } - } -} diff --git a/src/WixToolset.Core/Bind/DelayedField.cs b/src/WixToolset.Core/Bind/DelayedField.cs index 181ac3e3..6c56f27c 100644 --- a/src/WixToolset.Core/Bind/DelayedField.cs +++ b/src/WixToolset.Core/Bind/DelayedField.cs @@ -1,18 +1,15 @@ // Copyright (c) .NET Foundation and contributors. All rights reserved. Licensed under the Microsoft Reciprocal License. See LICENSE.TXT file in the project root for full license information. -namespace WixToolset.Bind +namespace WixToolset.Core.Bind { - using System; - using System.Collections.Generic; - using System.Linq; - using System.Text; using WixToolset.Data; + using WixToolset.Extensibility; /// /// Structure used to hold a row and field that contain binder variables, which need to be resolved /// later, once the files have been resolved. /// - internal class DelayedField + internal class DelayedField : IDelayedField { /// /// Basic constructor for struct @@ -28,11 +25,11 @@ namespace WixToolset.Bind /// /// The row containing the field. /// - public Row Row { get; private set; } + public Row Row { get; } /// /// The field needing further resolving. /// - public Field Field { get; private set; } + public Field Field { get; } } } diff --git a/src/WixToolset.Core/Bind/ExpectedExtractFile.cs b/src/WixToolset.Core/Bind/ExpectedExtractFile.cs new file mode 100644 index 00000000..fc2b43c7 --- /dev/null +++ b/src/WixToolset.Core/Bind/ExpectedExtractFile.cs @@ -0,0 +1,16 @@ +// Copyright (c) .NET Foundation and contributors. All rights reserved. Licensed under the Microsoft Reciprocal License. See LICENSE.TXT file in the project root for full license information. + +namespace WixToolset.Core.Bind +{ + using System; + using WixToolset.Extensibility; + + internal class ExpectedExtractFile : IExpectedExtractFile + { + public Uri Uri { get; set; } + + public int EmbeddedFileIndex { get; set; } + + public string OutputPath { get; set; } + } +} diff --git a/src/WixToolset.Core/Bind/ExtractEmbeddedFiles.cs b/src/WixToolset.Core/Bind/ExtractEmbeddedFiles.cs index 0ecd0096..28fc4817 100644 --- a/src/WixToolset.Core/Bind/ExtractEmbeddedFiles.cs +++ b/src/WixToolset.Core/Bind/ExtractEmbeddedFiles.cs @@ -1,6 +1,6 @@ // Copyright (c) .NET Foundation and contributors. All rights reserved. Licensed under the Microsoft Reciprocal License. See LICENSE.TXT file in the project root for full license information. -namespace WixToolset.Bind +namespace WixToolset.Core.Bind { using System; using System.Collections.Generic; @@ -13,11 +13,11 @@ namespace WixToolset.Bind /// /// Internal helper class used to extract embedded files. /// - internal sealed class ExtractEmbeddedFiles + internal class ExtractEmbeddedFiles { private Dictionary> filesWithEmbeddedFiles = new Dictionary>(); - public IEnumerable Uris { get { return this.filesWithEmbeddedFiles.Keys; } } + public IEnumerable Uris => this.filesWithEmbeddedFiles.Keys; /// /// Adds an embedded file index to track and returns the path where the embedded file will be extracted. Duplicates will return the same extract path. @@ -53,15 +53,30 @@ namespace WixToolset.Bind return extractPath; } - public IEnumerable GetExtractFilesForUri(Uri uri) + public IEnumerable GetExpectedEmbeddedFiles() { - SortedList extracts; - if (!filesWithEmbeddedFiles.TryGetValue(uri, out extracts)) + foreach (var uriWithExtracts in filesWithEmbeddedFiles) + { + foreach (var extracts in uriWithExtracts.Value) + { + yield return new ExpectedExtractFile + { + Uri = uriWithExtracts.Key, + EmbeddedFileIndex = extracts.Key, + OutputPath = extracts.Value, + }; + } + } + } + + public IEnumerable GetExtractFilesForUri(Uri uri) + { + if (!filesWithEmbeddedFiles.TryGetValue(uri, out var extracts)) { extracts = new SortedList(); } - return extracts.Select(e => new ExtractFile() { EmbeddedFileIndex = e.Key, OutputPath = e.Value }); + return extracts.Select(e => new ExpectedExtractFile() { Uri = uri, EmbeddedFileIndex = e.Key, OutputPath = e.Value }); } private string HashUri(string uri) @@ -72,12 +87,5 @@ namespace WixToolset.Bind return Convert.ToBase64String(hash).TrimEnd('=').Replace('+', '-').Replace('/', '_'); } } - - internal struct ExtractFile - { - public int EmbeddedFileIndex { get; set; } - - public string OutputPath { get; set; } - } } } diff --git a/src/WixToolset.Core/Bind/ExtractEmbeddedFilesCommand.cs b/src/WixToolset.Core/Bind/ExtractEmbeddedFilesCommand.cs index 68bfd8d7..7de40fb8 100644 --- a/src/WixToolset.Core/Bind/ExtractEmbeddedFilesCommand.cs +++ b/src/WixToolset.Core/Bind/ExtractEmbeddedFilesCommand.cs @@ -1,19 +1,26 @@ // Copyright (c) .NET Foundation and contributors. All rights reserved. Licensed under the Microsoft Reciprocal License. See LICENSE.TXT file in the project root for full license information. -namespace WixToolset.Bind +namespace WixToolset.Core.Bind { + using System.Collections.Generic; using System.IO; + using System.Linq; using System.Reflection; using WixToolset.Data; + using WixToolset.Extensibility; - internal class ExtractEmbeddedFilesCommand : ICommand + public class ExtractEmbeddedFilesCommand { - public ExtractEmbeddedFiles FilesWithEmbeddedFiles { private get; set; } + public IEnumerable FilesWithEmbeddedFiles { private get; set; } public void Execute() { - foreach (var baseUri in this.FilesWithEmbeddedFiles.Uris) + var group = this.FilesWithEmbeddedFiles.GroupBy(e => e.Uri); + + foreach (var expectedEmbeddedFileByUri in group) { + var baseUri = expectedEmbeddedFileByUri.Key; + Stream stream = null; try { @@ -34,18 +41,20 @@ namespace WixToolset.Bind using (FileStructure fs = FileStructure.Read(stream)) { - foreach (var embeddedFile in this.FilesWithEmbeddedFiles.GetExtractFilesForUri(baseUri)) + var uniqueIndicies = new SortedSet(); + + foreach (var embeddedFile in expectedEmbeddedFileByUri) { - fs.ExtractEmbeddedFile(embeddedFile.EmbeddedFileIndex, embeddedFile.OutputPath); + if (uniqueIndicies.Add(embeddedFile.EmbeddedFileIndex)) + { + fs.ExtractEmbeddedFile(embeddedFile.EmbeddedFileIndex, embeddedFile.OutputPath); + } } } } finally { - if (null != stream) - { - stream.Close(); - } + stream?.Close(); } } } diff --git a/src/WixToolset.Core/Bind/FileFacade.cs b/src/WixToolset.Core/Bind/FileFacade.cs new file mode 100644 index 00000000..aaa6b7d3 --- /dev/null +++ b/src/WixToolset.Core/Bind/FileFacade.cs @@ -0,0 +1,44 @@ +// Copyright (c) .NET Foundation and contributors. All rights reserved. Licensed under the Microsoft Reciprocal License. See LICENSE.TXT file in the project root for full license information. + +namespace WixToolset.Core.Bind +{ + using System.Collections.Generic; + using WixToolset.Data; + using WixToolset.Data.Rows; + + public class FileFacade + { + public FileFacade(FileRow file, WixFileRow wixFile, WixDeltaPatchFileRow deltaPatchFile) + { + this.File = file; + this.WixFile = wixFile; + this.DeltaPatchFile = deltaPatchFile; + } + + public FileFacade(bool fromModule, FileRow file, WixFileRow wixFile) + { + this.FromModule = fromModule; + this.File = file; + this.WixFile = wixFile; + } + + public bool FromModule { get; private set; } + + public FileRow File { get; private set; } + + public WixFileRow WixFile { get; private set; } + + public WixDeltaPatchFileRow DeltaPatchFile { get; private set; } + + /// + /// Gets the set of MsiAssemblyName rows created for this file. + /// + /// RowCollection of MsiAssemblyName table. + public List AssemblyNames { get; set; } + + /// + /// Gets or sets the MsiFileHash row for this file. + /// + public Row Hash { get; set; } + } +} diff --git a/src/WixToolset.Core/Bind/FileResolver.cs b/src/WixToolset.Core/Bind/FileResolver.cs new file mode 100644 index 00000000..8d624e6f --- /dev/null +++ b/src/WixToolset.Core/Bind/FileResolver.cs @@ -0,0 +1,231 @@ +// Copyright (c) .NET Foundation and contributors. All rights reserved. Licensed under the Microsoft Reciprocal License. See LICENSE.TXT file in the project root for full license information. + +namespace WixToolset.Core.Bind +{ + using System; + using System.Collections.Generic; + using System.IO; + using System.Linq; + using System.Runtime.InteropServices; + using WixToolset.Data; + using WixToolset.Data.Bind; + using WixToolset.Extensibility; + + internal class FileResolver + { + private const string BindPathOpenString = "!(bindpath."; + + private FileResolver(IEnumerable bindPaths) + { + this.BindPaths = (bindPaths ?? Array.Empty()).ToLookup(b => b.Stage); + this.RebaseTarget = this.BindPaths[BindStage.Target].Any(); + this.RebaseUpdated = this.BindPaths[BindStage.Updated].Any(); + } + + public FileResolver(IEnumerable bindPaths, IEnumerable extensions) : this(bindPaths) + { + this.BinderExtensions = extensions ?? Array.Empty(); + } + + public FileResolver(IEnumerable bindPaths, IEnumerable extensions) : this(bindPaths) + { + this.LibrarianExtensions = extensions ?? Array.Empty(); + } + + private ILookup BindPaths { get; } + + public bool RebaseTarget { get; } + + public bool RebaseUpdated { get; } + + private IEnumerable BinderExtensions { get; } + + private IEnumerable LibrarianExtensions { get; } + + /// + /// Copies a file. + /// + /// The file to copy. + /// The destination file. + /// true if the destination file can be overwritten; otherwise, false. + public bool CopyFile(string source, string destination, bool overwrite) + { + foreach (var extension in this.BinderExtensions) + { + if (extension.CopyFile(source, destination, overwrite)) + { + return true; + } + } + + if (overwrite && File.Exists(destination)) + { + File.Delete(destination); + } + + if (!CreateHardLink(destination, source, IntPtr.Zero)) + { +#if DEBUG + int er = Marshal.GetLastWin32Error(); +#endif + + File.Copy(source, destination, overwrite); + } + + return true; + } + + /// + /// Moves a file. + /// + /// The file to move. + /// The destination file. + public bool MoveFile(string source, string destination, bool overwrite) + { + foreach (var extension in this.BinderExtensions) + { + if (extension.MoveFile(source, destination, overwrite)) + { + return true; + } + } + + if (overwrite && File.Exists(destination)) + { + File.Delete(destination); + } + + var directory = Path.GetDirectoryName(destination); + if (!String.IsNullOrEmpty(directory)) + { + Directory.CreateDirectory(directory); + } + + File.Move(source, destination); + + return true; + } + + public string Resolve(SourceLineNumber sourceLineNumbers, string table, string path) + { + foreach (var extension in this.LibrarianExtensions) + { + var resolved = extension.Resolve(sourceLineNumbers, table, path); + + if (null != resolved) + { + return resolved; + } + } + + return this.ResolveUsingBindPaths(path, table, sourceLineNumbers, BindStage.Normal); + } + + /// + /// Resolves the source path of a file using binder extensions. + /// + /// Original source value. + /// Optional type of source file being resolved. + /// Optional source line of source file being resolved. + /// The binding stage used to determine what collection of bind paths will be used + /// Should return a valid path for the stream to be imported. + public string ResolveFile(string source, string type, SourceLineNumber sourceLineNumbers, BindStage bindStage) + { + foreach (var extension in this.BinderExtensions) + { + var resolved = extension.ResolveFile(source, type, sourceLineNumbers, bindStage); + + if (null != resolved) + { + return resolved; + } + } + + return this.ResolveUsingBindPaths(source, type, sourceLineNumbers, bindStage); + } + + private string ResolveUsingBindPaths(string source, string type, SourceLineNumber sourceLineNumbers, BindStage bindStage) + { + string resolved = null; + + // If the file exists, we're good to go. + if (CheckFileExists(source)) + { + resolved = source; + } + else if (Path.IsPathRooted(source)) // path is rooted so bindpaths won't help, bail since the file apparently doesn't exist. + { + resolved = null; + } + else // not a rooted path so let's try applying all the different source resolution options. + { + string bindName = String.Empty; + var path = source; + string pathWithoutSourceDir = null; + + if (source.StartsWith(BindPathOpenString, StringComparison.Ordinal)) + { + int closeParen = source.IndexOf(')', BindPathOpenString.Length); + if (-1 != closeParen) + { + bindName = source.Substring(BindPathOpenString.Length, closeParen - BindPathOpenString.Length); + path = source.Substring(BindPathOpenString.Length + bindName.Length + 1); // +1 for the closing brace. + path = path.TrimStart('\\'); // remove starting '\\' char so the path doesn't look rooted. + } + } + else if (source.StartsWith("SourceDir\\", StringComparison.Ordinal) || source.StartsWith("SourceDir/", StringComparison.Ordinal)) + { + pathWithoutSourceDir = path.Substring(10); + } + + var bindPaths = this.BindPaths[bindStage]; + + foreach (var bindPath in bindPaths) + { + if (!String.IsNullOrEmpty(pathWithoutSourceDir)) + { + var filePath = Path.Combine(bindPath.Path, pathWithoutSourceDir); + + if (CheckFileExists(filePath)) + { + resolved = filePath; + } + } + + if (String.IsNullOrEmpty(resolved)) + { + var filePath = Path.Combine(bindPath.Path, path); + + if (CheckFileExists(filePath)) + { + resolved = filePath; + } + } + } + } + + if (null == resolved) + { + throw new WixFileNotFoundException(sourceLineNumbers, source, type); + } + + // Didn't find the file. + return resolved; + } + + private static bool CheckFileExists(string path) + { + try + { + return File.Exists(path); + } + catch (ArgumentException) + { + throw new WixException(WixErrors.IllegalCharactersInPath(path)); + } + } + + [DllImport("Kernel32.dll", CharSet = CharSet.Unicode, SetLastError = true)] + private static extern bool CreateHardLink(string lpFileName, string lpExistingFileName, IntPtr lpSecurityAttributes); + } +} diff --git a/src/WixToolset.Core/Bind/FileTransfer.cs b/src/WixToolset.Core/Bind/FileTransfer.cs deleted file mode 100644 index 64bbc5f1..00000000 --- a/src/WixToolset.Core/Bind/FileTransfer.cs +++ /dev/null @@ -1,113 +0,0 @@ -// Copyright (c) .NET Foundation and contributors. All rights reserved. Licensed under the Microsoft Reciprocal License. See LICENSE.TXT file in the project root for full license information. - -namespace WixToolset.Bind -{ - using System; - using System.IO; - using WixToolset; - using WixToolset.Data; - - /// - /// Structure used for all file transfer information. - /// - internal class FileTransfer - { - /// Source path to file. - public string Source { get; set; } - - /// Destination path for file. - public string Destination { get; set; } - - /// Flag if file should be moved (optimal). - public bool Move { get; set; } - - /// Optional source line numbers where this file transfer orginated. - public SourceLineNumber SourceLineNumbers { get; set; } - - /// Optional type of file this transfer is moving or copying. - public string Type { get; set; } - - /// Indicates whether the file transer was a built by this build or copied from other some build. - internal bool Built { get; set; } - - /// Set during layout of media when the file transfer when the source and target resolve to the same path. - internal bool Redundant { get; set; } - - /// - /// Prefer the TryCreate() method to create FileTransfer objects. - /// - /// Source path to file. - /// Destination path for file. - /// File if file should be moved (optimal). - /// Optional type of file this transfer is transferring. - /// Optional source line numbers wher this transfer originated. - public FileTransfer(string source, string destination, bool move, string type = null, SourceLineNumber sourceLineNumbers = null) - { - this.Source = source; - this.Destination = destination; - this.Move = move; - - this.Type = type; - this.SourceLineNumbers = sourceLineNumbers; - } - - /// - /// Creates a file transfer if the source and destination are different. - /// - /// Source path to file. - /// Destination path for file. - /// File if file should be moved (optimal). - /// Optional type of file this transfer is transferring. - /// Optional source line numbers wher this transfer originated. - /// true if the source and destination are the different, false if no file transfer is created. - public static bool TryCreate(string source, string destination, bool move, string type, SourceLineNumber sourceLineNumbers, out FileTransfer transfer) - { - string sourceFullPath = GetValidatedFullPath(sourceLineNumbers, source); - - string fileLayoutFullPath = GetValidatedFullPath(sourceLineNumbers, destination); - - // if the current source path (where we know that the file already exists) and the resolved - // path as dictated by the Directory table are not the same, then propagate the file. The - // image that we create may have already been done by some other process other than the linker, so - // there is no reason to copy the files to the resolved source if they are already there. - if (String.Equals(sourceFullPath, fileLayoutFullPath, StringComparison.OrdinalIgnoreCase)) - { - transfer = null; - return false; - } - - transfer = new FileTransfer(source, destination, move, type, sourceLineNumbers); - return true; - } - - private static string GetValidatedFullPath(SourceLineNumber sourceLineNumbers, string path) - { - string result; - - try - { - result = Path.GetFullPath(path); - - string filename = Path.GetFileName(result); - - foreach (string reservedName in Common.ReservedFileNames) - { - if (reservedName.Equals(filename, StringComparison.OrdinalIgnoreCase)) - { - throw new WixException(WixErrors.InvalidFileName(sourceLineNumbers, path)); - } - } - } - catch (System.ArgumentException) - { - throw new WixException(WixErrors.InvalidFileName(sourceLineNumbers, path)); - } - catch (System.IO.PathTooLongException) - { - throw new WixException(WixErrors.PathTooLong(sourceLineNumbers, path)); - } - - return result; - } - } -} diff --git a/src/WixToolset.Core/Bind/GenerateDatabaseCommand.cs b/src/WixToolset.Core/Bind/GenerateDatabaseCommand.cs deleted file mode 100644 index fdf1ab32..00000000 --- a/src/WixToolset.Core/Bind/GenerateDatabaseCommand.cs +++ /dev/null @@ -1,335 +0,0 @@ -// Copyright (c) .NET Foundation and contributors. All rights reserved. Licensed under the Microsoft Reciprocal License. See LICENSE.TXT file in the project root for full license information. - -namespace WixToolset.Bind -{ - using System; - using System.Collections.Generic; - using System.ComponentModel; - using System.Globalization; - using System.IO; - using System.Text; - using WixToolset.Data; - using WixToolset.Extensibility; - using WixToolset.Msi; - using WixToolset.Core.Native; - - internal class GenerateDatabaseCommand : ICommand - { - public int Codepage { private get; set; } - - public IEnumerable Extensions { private get; set; } - - public IEnumerable FileManagers { private get; set; } - - /// - /// Whether to keep columns added in a transform. - /// - public bool KeepAddedColumns { private get; set; } - - public Output Output { private get; set; } - - public string OutputPath { private get; set; } - - public TableDefinitionCollection TableDefinitions { private get; set; } - - public string TempFilesLocation { private get; set; } - - /// - /// Whether to use a subdirectory based on the file name for intermediate files. - /// - public bool SuppressAddingValidationRows { private get; set; } - - public bool UseSubDirectory { private get; set; } - - public void Execute() - { - // Add the _Validation rows. - if (!this.SuppressAddingValidationRows) - { - Table validationTable = this.Output.EnsureTable(this.TableDefinitions["_Validation"]); - - foreach (Table table in this.Output.Tables) - { - if (!table.Definition.Unreal) - { - // Add the validation rows for this table. - table.Definition.AddValidationRows(validationTable); - } - } - } - - // Set the base directory. - string baseDirectory = this.TempFilesLocation; - - if (this.UseSubDirectory) - { - string filename = Path.GetFileNameWithoutExtension(this.OutputPath); - baseDirectory = Path.Combine(baseDirectory, filename); - - // make sure the directory exists - Directory.CreateDirectory(baseDirectory); - } - - 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 - - using (Database db = new Database(this.OutputPath, type)) - { - // Localize the codepage if a value was specified directly. - if (-1 != this.Codepage) - { - this.Output.Codepage = this.Codepage; - } - - // 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); - } - - 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.Section, table.Definition); - hasBinaryColumn = true; - break; - } - } - - // Create the table via IDT import. - if ("_Streams" != importTable.Name) - { - try - { - db.ImportTable(this.Output.Codepage, importTable, baseDirectory, this.KeepAddedColumns); - } - 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.Count)) - { - 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.Count; 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(WixErrors.FileNotFound(row.SourceLineNumbers, (string)row[i])); - } - else - { - throw new WixException(WixErrors.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) - { - Messaging.Instance.OnMessage(WixErrors.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.TempFilesLocation, String.Concat(subStorage.Name, ".mst")); - - // Bind the transform. - this.BindTransform(subStorage.Data, transformFile); - - if (Messaging.Instance.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) - { - // TODO: this error message doesn't seem specific enough - throw new WixFileNotFoundException(new SourceLineNumber(this.OutputPath), this.OutputPath); - } - } - - private void BindTransform(Output transform, string outputPath) - { - BindTransformCommand command = new BindTransformCommand(); - command.Extensions = this.Extensions; - command.FileManagers = this.FileManagers; - command.TempFilesLocation = this.TempFilesLocation; - command.Transform = transform; - command.OutputPath = outputPath; - command.TableDefinitions = this.TableDefinitions; - command.Execute(); - } - - /// - /// Sets the codepage of a database. - /// - /// Database to set codepage into. - /// Output with the codepage for the database. - private void SetDatabaseCodepage(Database db, int codepage) - { - // write out the _ForceCodepage IDT file - string idtPath = Path.Combine(this.TempFilesLocation, "_ForceCodepage.idt"); - using (StreamWriter idtFile = new StreamWriter(idtPath, false, Encoding.ASCII)) - { - idtFile.WriteLine(); // dummy column name record - idtFile.WriteLine(); // dummy column definition record - idtFile.Write(codepage); - idtFile.WriteLine("\t_ForceCodepage"); - } - - // try to import the table into the MSI - try - { - db.Import(idtPath); - } - catch (WixInvalidIdtException) - { - // the IDT should be valid, so an invalid code page was given - throw new WixException(WixErrors.IllegalCodepage(codepage)); - } - } - } -} diff --git a/src/WixToolset.Core/Bind/ResolveDelayedFieldsCommand.cs b/src/WixToolset.Core/Bind/ResolveDelayedFieldsCommand.cs index 4ffe9e82..15365c2a 100644 --- a/src/WixToolset.Core/Bind/ResolveDelayedFieldsCommand.cs +++ b/src/WixToolset.Core/Bind/ResolveDelayedFieldsCommand.cs @@ -1,23 +1,22 @@ // Copyright (c) .NET Foundation and contributors. All rights reserved. Licensed under the Microsoft Reciprocal License. See LICENSE.TXT file in the project root for full license information. -namespace WixToolset.Bind +namespace WixToolset.Core.Bind { using System; using System.Collections.Generic; using System.Globalization; - using System.Linq; - using System.Text; using WixToolset.Data; + using WixToolset.Extensibility; /// /// Resolves the fields which had variables that needed to be resolved after the file information /// was loaded. /// - internal class ResolveDelayedFieldsCommand : ICommand + public class ResolveDelayedFieldsCommand : ICommand { public OutputType OutputType { private get; set;} - public IEnumerable DelayedFields { private get; set;} + public IEnumerable DelayedFields { private get; set;} public IDictionary VariableCache { private get; set; } @@ -29,9 +28,9 @@ namespace WixToolset.Bind /// The modularization guid (used in case of a merge module). public void Execute() { - List deferredFields = new List(); + var deferredFields = new List(); - foreach (DelayedField delayedField in this.DelayedFields) + foreach (IDelayedField delayedField in this.DelayedFields) { try { @@ -43,7 +42,7 @@ namespace WixToolset.Bind string value = WixVariableResolver.ResolveDelayedVariables(propertyRow.SourceLineNumbers, (string)delayedField.Field.Data, this.VariableCache); // update the variable cache with the new value - string key = String.Concat("property.", BindDatabaseCommand.Demodularize(this.OutputType, this.ModularizationGuid, (string)propertyRow[0])); + string key = String.Concat("property.", Common.Demodularize(this.OutputType, this.ModularizationGuid, (string)propertyRow[0])); this.VariableCache[key] = value; // update the field data diff --git a/src/WixToolset.Core/Bind/ResolveFieldsCommand.cs b/src/WixToolset.Core/Bind/ResolveFieldsCommand.cs index 4caec9b4..f4f4f9e8 100644 --- a/src/WixToolset.Core/Bind/ResolveFieldsCommand.cs +++ b/src/WixToolset.Core/Bind/ResolveFieldsCommand.cs @@ -1,29 +1,32 @@ // Copyright (c) .NET Foundation and contributors. All rights reserved. Licensed under the Microsoft Reciprocal License. See LICENSE.TXT file in the project root for full license information. -namespace WixToolset.Bind +namespace WixToolset.Core.Bind { using System.Collections.Generic; using WixToolset.Data; + using WixToolset.Data.Bind; using WixToolset.Extensibility; /// /// Resolve source fields in the tables included in the output /// - internal class ResolveFieldsCommand : ICommand + internal class ResolveFieldsCommand { - public TableIndexedCollection Tables { private get; set; } + public bool BuildingPatch { private get; set; } - public ExtractEmbeddedFiles FilesWithEmbeddedFiles { private get; set; } + public IBindVariableResolver BindVariableResolver { private get; set; } - public BinderFileManagerCore FileManagerCore { private get; set; } + public IEnumerable BindPaths { private get; set; } - public IEnumerable FileManagers { private get; set; } + public IEnumerable Extensions { private get; set; } - public bool SupportDelayedResolution { private get; set; } + public ExtractEmbeddedFiles FilesWithEmbeddedFiles { private get; set; } + + public string IntermediateFolder { private get; set; } - public string TempFilesLocation { private get; set; } + public TableIndexedCollection Tables { private get; set; } - public WixVariableResolver WixVariableResolver { private get; set; } + public bool SupportDelayedResolution { private get; set; } public IEnumerable DelayedFields { get; private set; } @@ -31,6 +34,8 @@ namespace WixToolset.Bind { List delayedFields = this.SupportDelayedResolution ? new List() : null; + var fileResolver = new FileResolver(this.BindPaths, this.Extensions); + foreach (Table table in this.Tables) { foreach (Row row in table.Rows) @@ -46,7 +51,7 @@ namespace WixToolset.Bind // resolve localization and wix variables if (field.Data is string) { - field.Data = this.WixVariableResolver.ResolveVariables(row.SourceLineNumbers, field.AsString(), false, ref isDefault, ref delayedResolve); + field.Data = this.BindVariableResolver.ResolveVariables(row.SourceLineNumbers, field.AsString(), false, out isDefault, out delayedResolve); if (delayedResolve) { delayedFields.Add(new DelayedField(row, field)); @@ -74,7 +79,7 @@ namespace WixToolset.Bind // File is embedded and path to it was not modified above. if (objectField.EmbeddedFileIndex.HasValue && isDefault) { - string extractPath = this.FilesWithEmbeddedFiles.AddEmbeddedFileIndex(objectField.BaseUri, objectField.EmbeddedFileIndex.Value, this.TempFilesLocation); + string extractPath = this.FilesWithEmbeddedFiles.AddEmbeddedFileIndex(objectField.BaseUri, objectField.EmbeddedFileIndex.Value, this.IntermediateFolder); // Set the path to the embedded file once where it will be extracted. objectField.Data = extractPath; @@ -83,7 +88,7 @@ namespace WixToolset.Bind { try { - if (OutputType.Patch != this.FileManagerCore.Output.Type) // Normal binding for non-Patch scenario such as link (light.exe) + if (!this.BuildingPatch) // Normal binding for non-Patch scenario such as link (light.exe) { // keep a copy of the un-resolved data for future replay. This will be saved into wixpdb file if (null == objectField.UnresolvedData) @@ -92,12 +97,12 @@ namespace WixToolset.Bind } // resolve the path to the file - objectField.Data = this.ResolveFile((string)objectField.Data, table.Name, row.SourceLineNumbers, BindStage.Normal); + objectField.Data = fileResolver.ResolveFile((string)objectField.Data, table.Name, row.SourceLineNumbers, BindStage.Normal); } - else if (!(this.FileManagerCore.RebaseTarget || this.FileManagerCore.RebaseUpdated)) // Normal binding for Patch Scenario (normal patch, no re-basing logic) + else if (!fileResolver.RebaseTarget && !fileResolver.RebaseUpdated) // Normal binding for Patch Scenario (normal patch, no re-basing logic) { // resolve the path to the file - objectField.Data = this.ResolveFile((string)objectField.Data, table.Name, row.SourceLineNumbers, BindStage.Normal); + objectField.Data = fileResolver.ResolveFile((string)objectField.Data, table.Name, row.SourceLineNumbers, BindStage.Normal); } else // Re-base binding path scenario caused by pyro.exe -bt -bu { @@ -106,7 +111,7 @@ namespace WixToolset.Bind // if -bu is used in pyro command, this condition holds true and the tool // will use pre-resolved source for new wixpdb file - if (this.FileManagerCore.RebaseUpdated) + if (fileResolver.RebaseUpdated) { // try to use the unResolved Source if it exists. // New version of wixpdb file keeps a copy of pre-resolved Source. i.e. !(bindpath.test)\foo.dll @@ -117,7 +122,7 @@ namespace WixToolset.Bind } } - objectField.Data = this.ResolveFile(filePathToResolve, table.Name, row.SourceLineNumbers, BindStage.Updated); + objectField.Data = fileResolver.ResolveFile(filePathToResolve, table.Name, row.SourceLineNumbers, BindStage.Updated); } } catch (WixFileNotFoundException) @@ -127,10 +132,10 @@ namespace WixToolset.Bind } } - isDefault = true; if (null != objectField.PreviousData) { - objectField.PreviousData = this.WixVariableResolver.ResolveVariables(row.SourceLineNumbers, objectField.PreviousData, false, ref isDefault); + objectField.PreviousData = this.BindVariableResolver.ResolveVariables(row.SourceLineNumbers, objectField.PreviousData, false, out isDefault); + if (!Messaging.Instance.EncounteredError) // TODO: make this error handling more specific to just the failure to resolve variables in this field. { // file is compressed in a cabinet (and not modified above) @@ -142,7 +147,7 @@ namespace WixToolset.Bind objectField.PreviousBaseUri = objectField.BaseUri; } - string extractPath = this.FilesWithEmbeddedFiles.AddEmbeddedFileIndex(objectField.PreviousBaseUri, objectField.PreviousEmbeddedFileIndex.Value, this.TempFilesLocation); + string extractPath = this.FilesWithEmbeddedFiles.AddEmbeddedFileIndex(objectField.PreviousBaseUri, objectField.PreviousEmbeddedFileIndex.Value, this.IntermediateFolder); // set the path to the file once its extracted from the cabinet objectField.PreviousData = extractPath; @@ -151,14 +156,14 @@ namespace WixToolset.Bind { try { - if (!this.FileManagerCore.RebaseTarget && !this.FileManagerCore.RebaseUpdated) + if (!fileResolver.RebaseTarget && !fileResolver.RebaseUpdated) { // resolve the path to the file - objectField.PreviousData = this.ResolveFile((string)objectField.PreviousData, table.Name, row.SourceLineNumbers, BindStage.Normal); + objectField.PreviousData = fileResolver.ResolveFile((string)objectField.PreviousData, table.Name, row.SourceLineNumbers, BindStage.Normal); } else { - if (this.FileManagerCore.RebaseTarget) + if (fileResolver.RebaseTarget) { // if -bt is used, it come here // Try to use the original unresolved source from either target build or update build @@ -172,7 +177,7 @@ namespace WixToolset.Bind } // resolve the path to the file - objectField.PreviousData = this.ResolveFile((string)objectField.PreviousData, table.Name, row.SourceLineNumbers, BindStage.Target); + objectField.PreviousData = fileResolver.ResolveFile((string)objectField.PreviousData, table.Name, row.SourceLineNumbers, BindStage.Target); } } @@ -192,24 +197,28 @@ namespace WixToolset.Bind this.DelayedFields = delayedFields; } +#if false private string ResolveFile(string source, string type, SourceLineNumber sourceLineNumbers, BindStage bindStage = BindStage.Normal) { string path = null; - foreach (IBinderFileManager fileManager in this.FileManagers) + foreach (var extension in this.Extensions) { - path = fileManager.ResolveFile(source, type, sourceLineNumbers, bindStage); + path = extension.ResolveFile(source, type, sourceLineNumbers, bindStage); if (null != path) { break; } } - if (null == path) - { - throw new WixFileNotFoundException(sourceLineNumbers, source, type); - } + throw new NotImplementedException(); // need to do default binder stuff + + //if (null == path) + //{ + // throw new WixFileNotFoundException(sourceLineNumbers, source, type); + //} - return path; + //return path; } +#endif } } diff --git a/src/WixToolset.Core/Bind/ResolveResult.cs b/src/WixToolset.Core/Bind/ResolveResult.cs new file mode 100644 index 00000000..13f25054 --- /dev/null +++ b/src/WixToolset.Core/Bind/ResolveResult.cs @@ -0,0 +1,14 @@ +// Copyright (c) .NET Foundation and contributors. All rights reserved. Licensed under the Microsoft Reciprocal License. See LICENSE.TXT file in the project root for full license information. + +namespace WixToolset.Core.Bind +{ + using System.Collections.Generic; + using WixToolset.Extensibility; + + public class ResolveResult + { + public IEnumerable ExpectedEmbeddedFiles { get; set; } + + public IEnumerable DelayedFields { get; set; } + } +} \ No newline at end of file diff --git a/src/WixToolset.Core/Bind/ResolvedDirectory.cs b/src/WixToolset.Core/Bind/ResolvedDirectory.cs index 6985f95d..fca706d8 100644 --- a/src/WixToolset.Core/Bind/ResolvedDirectory.cs +++ b/src/WixToolset.Core/Bind/ResolvedDirectory.cs @@ -5,7 +5,7 @@ namespace WixToolset.Bind /// /// Structure used for resolved directory information. /// - internal struct ResolvedDirectory + public struct ResolvedDirectory { /// The directory parent. public string DirectoryParent; diff --git a/src/WixToolset.Core/Bind/TransferFilesCommand.cs b/src/WixToolset.Core/Bind/TransferFilesCommand.cs index 719b8b20..f116569c 100644 --- a/src/WixToolset.Core/Bind/TransferFilesCommand.cs +++ b/src/WixToolset.Core/Bind/TransferFilesCommand.cs @@ -1,29 +1,37 @@ // Copyright (c) .NET Foundation and contributors. All rights reserved. Licensed under the Microsoft Reciprocal License. See LICENSE.TXT file in the project root for full license information. -namespace WixToolset.Bind +namespace WixToolset.Core.Bind { using System; using System.Collections.Generic; using System.IO; using System.Security.AccessControl; using WixToolset.Data; + using WixToolset.Data.Bind; using WixToolset.Extensibility; - internal class TransferFilesCommand : ICommand + internal class TransferFilesCommand { - public IEnumerable FileManagers { private get; set; } + public TransferFilesCommand(IEnumerable bindPaths, IEnumerable extensions, IEnumerable fileTransfers, bool suppressAclReset) + { + this.FileResolver = new FileResolver(bindPaths, extensions); + this.FileTransfers = fileTransfers; + this.SuppressAclReset = suppressAclReset; + } + + private FileResolver FileResolver { get; } - public IEnumerable FileTransfers { private get; set; } + private IEnumerable FileTransfers { get; } - public bool SuppressAclReset { private get; set; } + private bool SuppressAclReset { get; } public void Execute() { List destinationFiles = new List(); - foreach (FileTransfer fileTransfer in this.FileTransfers) + foreach (var fileTransfer in this.FileTransfers) { - string fileSource = this.ResolveFile(fileTransfer.Source, fileTransfer.Type, fileTransfer.SourceLineNumbers, BindStage.Normal); + string fileSource = this.FileResolver.ResolveFile(fileTransfer.Source, fileTransfer.Type, fileTransfer.SourceLineNumbers, BindStage.Normal); // If the source and destination are identical, then there's nothing to do here if (0 == String.Compare(fileSource, fileTransfer.Destination, StringComparison.OrdinalIgnoreCase)) @@ -165,44 +173,17 @@ namespace WixToolset.Bind } } - private string ResolveFile(string source, string type, SourceLineNumber sourceLineNumbers, BindStage bindStage) + private void TransferFile(bool move, string source, string destination) { - string path = null; - foreach (IBinderFileManager fileManager in this.FileManagers) - { - path = fileManager.ResolveFile(source, type, sourceLineNumbers, bindStage); - if (null != path) - { - break; - } - } + bool complete = false; - if (null == path) + if (move) { - throw new WixFileNotFoundException(sourceLineNumbers, source, type); + complete = this.FileResolver.MoveFile(source, destination, true); } - - return path; - } - - private void TransferFile(bool move, string source, string destination) - { - bool complete = false; - foreach (IBinderFileManager fileManager in this.FileManagers) + else { - if (move) - { - complete = fileManager.MoveFile(source, destination, true); - } - else - { - complete = fileManager.CopyFile(source, destination, true); - } - - if (complete) - { - break; - } + complete = this.FileResolver.CopyFile(source, destination, true); } if (!complete) -- cgit v1.2.3-55-g6feb