/src/wix/Binder.cs
C# | 7382 lines | 6326 code | 502 blank | 554 comment | 760 complexity | 94b8d58ea811c85ed07eeab508bb81a8 MD5 | raw file
Possible License(s): CPL-1.0
Large files files are truncated, but you can click here to view the full file
- //-------------------------------------------------------------------------------------------------
- // <copyright file="Binder.cs" company="Outercurve Foundation">
- // Copyright (c) 2004, Outercurve Foundation.
- // This software is released under Microsoft Reciprocal License (MS-RL).
- // The license and further copyright text can be found in the file
- // LICENSE.TXT at the root directory of the distribution.
- // </copyright>
- //
- // <summary>
- // Binder core of the WiX toolset.
- // </summary>
- //-------------------------------------------------------------------------------------------------
-
- namespace WixToolset
- {
- using System;
- using System.Collections;
- using System.Collections.Generic;
- using System.Collections.ObjectModel;
- using System.Collections.Specialized;
- using System.ComponentModel;
- using System.Diagnostics;
- using System.Diagnostics.CodeAnalysis;
- using System.Globalization;
- using System.IO;
- using System.Reflection;
- using System.Runtime.InteropServices;
- using System.Security.Cryptography;
- using System.Security.Cryptography.X509Certificates;
- using System.Text;
- using System.Xml;
- using System.Xml.XPath;
- using WixToolset.Cab;
- using WixToolset.CLR.Interop;
- using WixToolset.MergeMod;
- using WixToolset.Msi;
- using WixToolset.Msi.Interop;
-
- using Wix = WixToolset.Serialize;
-
- // TODO: (4.0) Refactor so that these don't need to be copied.
- // Copied verbatim from ext\UtilExtension\wixext\UtilCompiler.cs
- [Flags]
- internal enum WixFileSearchAttributes
- {
- Default = 0x001,
- MinVersionInclusive = 0x002,
- MaxVersionInclusive = 0x004,
- MinSizeInclusive = 0x008,
- MaxSizeInclusive = 0x010,
- MinDateInclusive = 0x020,
- MaxDateInclusive = 0x040,
- WantVersion = 0x080,
- WantExists = 0x100,
- IsDirectory = 0x200,
- }
-
- [Flags]
- internal enum WixRegistrySearchAttributes
- {
- Raw = 0x01,
- Compatible = 0x02,
- ExpandEnvironmentVariables = 0x04,
- WantValue = 0x08,
- WantExists = 0x10,
- Win64 = 0x20,
- }
-
- internal enum WixComponentSearchAttributes
- {
- KeyPath = 0x1,
- State = 0x2,
- WantDirectory = 0x4,
- }
-
- [Flags]
- internal enum WixProductSearchAttributes
- {
- Version = 0x1,
- Language = 0x2,
- State = 0x4,
- Assignment = 0x8,
- }
-
-
- /// <summary>
- /// Binder core of the WiX toolset.
- /// </summary>
- public sealed class Binder : WixBinder, IDisposable
- {
- // 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}");
-
- // The following constants must stay in sync with src\burn\engine\core.h
- private const string BURN_BUNDLE_NAME = "WixBundleName";
- private const string BURN_BUNDLE_ORIGINAL_SOURCE = "WixBundleOriginalSource";
- private const string BURN_BUNDLE_LAST_USED_SOURCE = "WixBundleLastUsedSource";
-
- private string emptyFile;
-
- private bool backwardsCompatibleGuidGen;
- private int cabbingThreadCount;
- private string cabCachePath;
- private Cab.CompressionLevel defaultCompressionLevel;
- private bool exactAssemblyVersions;
- private bool setMsiAssemblyNameFileVersion;
- private string pdbFile;
- private bool reuseCabinets;
- private bool suppressAssemblies;
- private bool suppressAclReset;
- private bool suppressBuildInfo;
- private bool suppressFileHashAndInfo;
- private StringCollection suppressICEs;
- private bool suppressLayout;
- private bool suppressWixPdb;
- private bool suppressValidation;
-
- private StringCollection ices;
- private StringCollection invalidArgs;
- private bool suppressAddingValidationRows;
- private Validator validator;
-
- private string contentsFile;
- private string outputsFile;
- private string builtOutputsFile;
- private string wixprojectFile;
-
- /// <summary>
- /// Creates an MSI binder.
- /// </summary>
- public Binder()
- {
- this.defaultCompressionLevel = Cab.CompressionLevel.Mszip;
- this.suppressICEs = new StringCollection();
-
- this.ices = new StringCollection();
- this.invalidArgs = new StringCollection();
- this.validator = new Validator();
- }
-
- /// <summary>
- /// Gets or sets whether the GUID generation should use a backwards
- /// compatible version (i.e. MD5).
- /// </summary>
- public bool BackwardsCompatibleGuidGen
- {
- get { return this.backwardsCompatibleGuidGen; }
- set { this.backwardsCompatibleGuidGen = value; }
- }
-
- /// <summary>
- /// Gets or sets the number of threads to use for cabinet creation.
- /// </summary>
- /// <value>The number of threads to use for cabinet creation.</value>
- public int CabbingThreadCount
- {
- get { return this.cabbingThreadCount; }
- set { this.cabbingThreadCount = value; }
- }
-
- /// <summary>
- /// Gets or sets the default compression level to use for cabinets
- /// that don't have their compression level explicitly set.
- /// </summary>
- public Cab.CompressionLevel DefaultCompressionLevel
- {
- get { return this.defaultCompressionLevel; }
- set { this.defaultCompressionLevel = value; }
- }
-
- /// <summary>
- /// Gets or sets the exact assembly versions flag (see docs).
- /// </summary>
- public bool ExactAssemblyVersions
- {
- get { return this.exactAssemblyVersions; }
- set { this.exactAssemblyVersions = value; }
- }
-
- /// <summary>
- /// Gets and sets the location to save the WixPdb.
- /// </summary>
- /// <value>The location in which to save the WixPdb. Null if the the WixPdb should not be output.</value>
- public string PdbFile
- {
- get { return this.pdbFile; }
- set { this.pdbFile = value; }
- }
-
- /// <summary>
- /// Gets and sets the option to set the file version in the MsiAssemblyName table.
- /// </summary>
- /// <value>The option to set the file version in the MsiAssemblyName table.</value>
- public bool SetMsiAssemblyNameFileVersion
- {
- get { return this.setMsiAssemblyNameFileVersion; }
- set { this.setMsiAssemblyNameFileVersion = value; }
- }
-
- /// <summary>
- /// Gets and sets the option to suppress resetting ACLs by the binder.
- /// </summary>
- /// <value>The option to suppress resetting ACLs by the binder.</value>
- public bool SuppressAclReset
- {
- get { return this.suppressAclReset; }
- set { this.suppressAclReset = value; }
- }
-
- /// <summary>
- /// Gets and sets the option to suppress adding _Validation rows.
- /// </summary>
- /// <value>The option to suppress adding _Validation rows.</value>
- public bool SuppressAddingValidationRows
- {
- get { return this.suppressAddingValidationRows; }
- set { this.suppressAddingValidationRows = value; }
- }
-
- /// <summary>
- /// Gets and sets the option to suppress grabbing assembly name information from assemblies.
- /// </summary>
- /// <value>The option to suppress grabbing assembly name information from assemblies.</value>
- public bool SuppressAssemblies
- {
- get { return this.suppressAssemblies; }
- set { this.suppressAssemblies = value; }
- }
-
- /// <summary>
- /// Gets and sets the option to suppress writing build information in the output.
- /// </summary>
- /// <value>The option to suppress writing build information in the output.</value>
- public bool SuppressBuildInfo
- {
- get { return this.suppressBuildInfo; }
- set { this.suppressBuildInfo = value; }
- }
-
- /// <summary>
- /// Gets and sets the option to suppress grabbing the file hash, version and language at link time.
- /// </summary>
- /// <value>The option to suppress grabbing the file hash, version and language.</value>
- public bool SuppressFileHashAndInfo
- {
- get { return this.suppressFileHashAndInfo; }
- set { this.suppressFileHashAndInfo = value; }
- }
-
- /// <summary>
- /// Gets and sets the option to suppress creating an image for MSI/MSM.
- /// </summary>
- /// <value>The option to suppress creating an image for MSI/MSM.</value>
- public bool SuppressLayout
- {
- get { return this.suppressLayout; }
- set { this.suppressLayout = value; }
- }
-
- /// <summary>
- /// Gets and sets the option to suppress MSI/MSM validation.
- /// </summary>
- /// <value>The option to suppress MSI/MSM validation.</value>
- /// <remarks>This must be set before calling Bind.</remarks>
- public bool SuppressValidation
- {
- get
- {
- return this.suppressValidation;
- }
-
- set
- {
- // make a new validator if validation has been turned off and is now being turned back on
- if (!value && null == this.validator)
- {
- this.validator = new Validator();
- }
-
- this.suppressValidation = value;
- }
- }
-
- /// <summary>
- /// Gets help for all the command line arguments for this binder.
- /// </summary>
- /// <returns>A string to be added to light's help string.</returns>
- public override string GetCommandLineArgumentsHelpString()
- {
- return WixStrings.BinderArguments;
- }
-
- /// <summary>
- /// Parse the commandline arguments.
- /// </summary>
- /// <param name="args">Commandline arguments.</param>
- /// <param name="consoleMessageHandler">The console message handler.</param>
- [SuppressMessage("Microsoft.Globalization", "CA1308:NormalizeStringsToUppercase", Justification = "These strings are not round tripped, and have no security impact")]
- public override StringCollection ParseCommandLine(string[] args, ConsoleMessageHandler consoleMessageHandler)
- {
- for (int i = 0; i < args.Length; ++i)
- {
- string arg = args[i];
- if (null == arg || 0 == arg.Length) // skip blank arguments
- {
- continue;
- }
-
- if ('-' == arg[0] || '/' == arg[0])
- {
- string parameter = arg.Substring(1);
- if (parameter.Equals("bcgg", StringComparison.Ordinal))
- {
- this.backwardsCompatibleGuidGen = true;
- }
- else if (parameter.Equals("cc", StringComparison.Ordinal))
- {
- this.cabCachePath = CommandLine.GetDirectory(parameter, consoleMessageHandler, args, ++i);
-
- if (String.IsNullOrEmpty(this.cabCachePath))
- {
- return this.invalidArgs;
- }
- }
- else if (parameter.Equals("ct", StringComparison.Ordinal))
- {
- if (!CommandLine.IsValidArg(args, ++i))
- {
- consoleMessageHandler.Display(this, WixErrors.IllegalCabbingThreadCount(String.Empty));
- return this.invalidArgs;
- }
-
- try
- {
- this.cabbingThreadCount = Convert.ToInt32(args[i], CultureInfo.InvariantCulture.NumberFormat);
-
- if (0 >= this.cabbingThreadCount)
- {
- consoleMessageHandler.Display(this, WixErrors.IllegalCabbingThreadCount(args[i]));
- }
-
- consoleMessageHandler.Display(this, WixVerboses.SetCabbingThreadCount(this.cabbingThreadCount.ToString()));
- }
- catch (FormatException)
- {
- consoleMessageHandler.Display(this, WixErrors.IllegalCabbingThreadCount(args[i]));
- }
- catch (OverflowException)
- {
- consoleMessageHandler.Display(this, WixErrors.IllegalCabbingThreadCount(args[i]));
- }
- }
- else if (parameter.Equals("cub", StringComparison.Ordinal))
- {
- string cubeFile = CommandLine.GetFile(parameter, consoleMessageHandler, args, ++i);
-
- if (String.IsNullOrEmpty(cubeFile))
- {
- return this.invalidArgs;
- }
-
- this.validator.AddCubeFile(cubeFile);
- }
- else if (parameter.StartsWith("dcl:", StringComparison.Ordinal))
- {
- string defaultCompressionLevel = arg.Substring(5);
-
- if (String.IsNullOrEmpty(defaultCompressionLevel))
- {
- return this.invalidArgs;
- }
-
- this.defaultCompressionLevel = WixCreateCab.CompressionLevelFromString(defaultCompressionLevel);
- }
- else if (parameter.Equals("eav", StringComparison.Ordinal))
- {
- this.exactAssemblyVersions = true;
- }
- else if (parameter.Equals("fv", StringComparison.Ordinal))
- {
- this.setMsiAssemblyNameFileVersion = true;
- }
- else if (parameter.StartsWith("ice:", StringComparison.Ordinal))
- {
- this.ices.Add(parameter.Substring(4));
- }
- else if (parameter.Equals("contentsfile", StringComparison.Ordinal))
- {
- this.contentsFile = CommandLine.GetFile(parameter, consoleMessageHandler, args, ++i);
-
- if (String.IsNullOrEmpty(this.contentsFile))
- {
- return this.invalidArgs;
- }
- }
- else if (parameter.Equals("outputsfile", StringComparison.Ordinal))
- {
- this.outputsFile = CommandLine.GetFile(parameter, consoleMessageHandler, args, ++i);
-
- if (String.IsNullOrEmpty(this.outputsFile))
- {
- return this.invalidArgs;
- }
- }
- else if (parameter.Equals("builtoutputsfile", StringComparison.Ordinal))
- {
- this.builtOutputsFile = CommandLine.GetFile(parameter, consoleMessageHandler, args, ++i);
-
- if (String.IsNullOrEmpty(this.builtOutputsFile))
- {
- return this.invalidArgs;
- }
- }
- else if (parameter.Equals("wixprojectfile", StringComparison.Ordinal))
- {
- this.wixprojectFile = CommandLine.GetFile(parameter, consoleMessageHandler, args, ++i);
-
- if (String.IsNullOrEmpty(this.wixprojectFile))
- {
- return this.invalidArgs;
- }
- }
- else if (parameter.Equals("O1", StringComparison.Ordinal))
- {
- consoleMessageHandler.Display(this, WixWarnings.DeprecatedCommandLineSwitch("O1"));
- }
- else if (parameter.Equals("O2", StringComparison.Ordinal))
- {
- consoleMessageHandler.Display(this, WixWarnings.DeprecatedCommandLineSwitch("O2"));
- }
- else if (parameter.Equals("pdbout", StringComparison.Ordinal))
- {
- this.pdbFile = CommandLine.GetFile(parameter, consoleMessageHandler, args, ++i);
-
- if (String.IsNullOrEmpty(this.pdbFile))
- {
- return this.invalidArgs;
- }
- }
- else if (parameter.Equals("reusecab", StringComparison.Ordinal))
- {
- this.reuseCabinets = true;
- }
- else if (parameter.Equals("sa", StringComparison.Ordinal))
- {
- this.suppressAssemblies = true;
- }
- else if (parameter.Equals("sacl", StringComparison.Ordinal))
- {
- this.suppressAclReset = true;
- }
- else if (parameter.Equals("sbuildinfo", StringComparison.Ordinal))
- {
- this.suppressBuildInfo = true;
- }
- else if (parameter.Equals("sf", StringComparison.Ordinal))
- {
- this.suppressAssemblies = true;
- this.suppressFileHashAndInfo = true;
- }
- else if (parameter.Equals("sh", StringComparison.Ordinal))
- {
- this.suppressFileHashAndInfo = true;
- }
- else if (parameter.StartsWith("sice:", StringComparison.Ordinal))
- {
- this.suppressICEs.Add(parameter.Substring(5));
- }
- else if (parameter.Equals("sl", StringComparison.Ordinal))
- {
- this.suppressLayout = true;
- }
- else if (parameter.Equals("spdb", StringComparison.Ordinal))
- {
- this.suppressWixPdb = true;
- }
- else if (parameter.Equals("sval", StringComparison.Ordinal))
- {
- this.suppressValidation = true;
- }
- else
- {
- this.invalidArgs.Add(arg);
- }
- }
- else
- {
- this.invalidArgs.Add(arg);
- }
- }
-
- this.pdbFile = this.suppressWixPdb ? null : this.pdbFile;
-
- return this.invalidArgs;
- }
-
- /// <summary>
- /// Do any setting up needed after all command line parsing.
- /// </summary>
- public override void PostParseCommandLine()
- {
- if (!this.suppressWixPdb && null == this.pdbFile && null != this.OutputFile)
- {
- this.pdbFile = Path.ChangeExtension(this.OutputFile, ".wixpdb");
- }
- }
-
- /// <summary>
- /// Adds an event handler.
- /// </summary>
- /// <param name="newHandler">The event handler to add.</param>
- public override void AddMessageEventHandler(MessageEventHandler newHandler)
- {
- base.AddMessageEventHandler(newHandler);
- validator.Extension.Message += newHandler;
- }
-
- /// <summary>
- /// Binds an output.
- /// </summary>
- /// <param name="output">The output to bind.</param>
- /// <param name="file">The Windows Installer file to create.</param>
- /// <remarks>The Binder.DeleteTempFiles method should be called after calling this method.</remarks>
- /// <returns>true if binding completed successfully; false otherwise</returns>
- public override bool Bind(Output output, string file)
- {
- // ensure the cabinet cache path exists if we are going to use it
- if (null != this.cabCachePath && !Directory.Exists(this.cabCachePath))
- {
- Directory.CreateDirectory(this.cabCachePath);
- }
-
- // tell the binder about the validator if validation isn't suppressed
- if (!this.suppressValidation && (OutputType.Module == output.Type || OutputType.Product == output.Type))
- {
- if (String.IsNullOrEmpty(this.validator.TempFilesLocation))
- {
- this.validator.TempFilesLocation = Environment.GetEnvironmentVariable("WIX_TEMP");
- }
-
- // set the default cube file
- Assembly lightAssembly = Assembly.GetExecutingAssembly();
- string lightDirectory = Path.GetDirectoryName(lightAssembly.Location);
- if (OutputType.Module == output.Type)
- {
- this.validator.AddCubeFile(Path.Combine(lightDirectory, "mergemod.cub"));
- }
- else // product
- {
- this.validator.AddCubeFile(Path.Combine(lightDirectory, "darice.cub"));
- }
-
- // by default, disable ICEs that have equivalent-or-better checks in WiX
- this.suppressICEs.Add("ICE08");
- this.suppressICEs.Add("ICE33");
- this.suppressICEs.Add("ICE47");
- this.suppressICEs.Add("ICE66");
-
- // set the ICEs
- string[] iceArray = new string[this.ices.Count];
- this.ices.CopyTo(iceArray, 0);
- this.validator.ICEs = iceArray;
-
- // set the suppressed ICEs
- string[] suppressICEArray = new string[this.suppressICEs.Count];
- this.suppressICEs.CopyTo(suppressICEArray, 0);
- this.validator.SuppressedICEs = suppressICEArray;
- }
- else
- {
- this.validator = null;
- }
-
- this.core = new BinderCore(this.MessageHandler);
- this.FileManager.MessageHandler = this.core;
-
- foreach (BinderExtension extension in this.extensions)
- {
- extension.Core = this.core;
- }
-
- if (null == output)
- {
- throw new ArgumentNullException("output");
- }
-
- this.core.EncounteredError = false;
-
- switch (output.Type)
- {
- case OutputType.Bundle:
- return this.BindBundle(output, file);
- case OutputType.Transform:
- return this.BindTransform(output, file);
- default:
- return this.BindDatabase(output, file);
- }
- }
-
- /// <summary>
- /// Does any housekeeping after Bind.
- /// </summary>
- /// <param name="tidy">Whether or not any actual tidying should be done.</param>
- public override void Cleanup(bool tidy)
- {
- // If Bind hasn't been called yet, core will be null. There will be
- // nothing to cleanup.
- if (this.core == null)
- {
- return;
- }
-
- if (tidy)
- {
- if (!this.DeleteTempFiles())
- {
- this.core.OnMessage(WixWarnings.FailedToDeleteTempDir(this.TempFilesLocation));
- }
- }
- else
- {
- this.core.OnMessage(WixVerboses.BinderTempDirLocatedAt(this.TempFilesLocation));
- }
-
- if (null != this.validator && !String.IsNullOrEmpty(this.validator.TempFilesLocation))
- {
- if (tidy)
- {
- if (!this.validator.DeleteTempFiles())
- {
- this.core.OnMessage(WixWarnings.FailedToDeleteTempDir(this.validator.TempFilesLocation));
- }
- }
- else
- {
- this.core.OnMessage(WixVerboses.ValidatorTempDirLocatedAt(this.validator.TempFilesLocation));
- }
- }
- }
-
- /// <summary>
- /// Cleans up the temp files used by the Binder.
- /// </summary>
- /// <returns>True if all files were deleted, false otherwise.</returns>
- public override bool DeleteTempFiles()
- {
- bool deleted = base.DeleteTempFiles();
- if (deleted)
- {
- this.emptyFile = null;
- }
-
- return deleted;
- }
-
- /// <summary>
- /// Process a list of loaded extensions.
- /// </summary>
- /// <param name="loadedExtensionList">The list of loaded extensions.</param>
- public override void ProcessExtensions(WixExtension[] loadedExtensionList)
- {
- bool binderFileManagerLoaded = false;
- bool validatorExtensionLoaded = false;
-
- foreach (WixExtension wixExtension in loadedExtensionList)
- {
- if (null != wixExtension.BinderFileManager)
- {
- if (binderFileManagerLoaded)
- {
- core.OnMessage(WixErrors.CannotLoadBinderFileManager(wixExtension.BinderFileManager.GetType().ToString(), this.FileManager.ToString()));
- }
-
- this.FileManager = wixExtension.BinderFileManager;
- binderFileManagerLoaded = true;
- }
-
- ValidatorExtension validatorExtension = wixExtension.ValidatorExtension;
- if (null != validatorExtension)
- {
- if (validatorExtensionLoaded)
- {
- core.OnMessage(WixErrors.CannotLoadLinkerExtension(validatorExtension.GetType().ToString(), this.validator.Extension.ToString()));
- }
-
- this.validator.Extension = validatorExtension;
- validatorExtensionLoaded = true;
- }
- }
-
- this.FileManager.CabCachePath = this.cabCachePath;
- this.FileManager.ReuseCabinets = this.reuseCabinets;
- }
-
- /// <summary>
- /// Creates the MSI/MSM/PCP database.
- /// </summary>
- /// <param name="output">Output to create database for.</param>
- /// <param name="databaseFile">The database file to create.</param>
- /// <param name="keepAddedColumns">Whether to keep columns added in a transform.</param>
- /// <param name="useSubdirectory">Whether to use a subdirectory based on the <paramref name="databaseFile"/> file name for intermediate files.</param>
- internal void GenerateDatabase(Output output, string databaseFile, bool keepAddedColumns, bool useSubdirectory)
- {
- // add the _Validation rows
- if (!this.suppressAddingValidationRows)
- {
- Table validationTable = output.EnsureTable(this.core.TableDefinitions["_Validation"]);
-
- foreach (Table table in output.Tables)
- {
- if (!table.Definition.IsUnreal)
- {
- // add the validation rows for this table
- table.Definition.AddValidationRows(validationTable);
- }
- }
- }
-
- // set the base directory
- string baseDirectory = this.TempFilesLocation;
- if (useSubdirectory)
- {
- string filename = Path.GetFileNameWithoutExtension(databaseFile);
- 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 == output.Type)
- {
- type |= OpenDatabase.OpenPatchFile;
- }
-
- // try to create the database
- using (Database db = new Database(databaseFile, type))
- {
- // localize the codepage if a value was specified by the localizer
- if (null != this.Localizer && -1 != this.Localizer.Codepage)
- {
- output.Codepage = this.Localizer.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 != output.Codepage)
- {
- this.SetDatabaseCodepage(db, output);
- }
-
- foreach (Table table in output.Tables)
- {
- Table importTable = table;
- bool hasBinaryColumn = false;
-
- // skip all unreal tables other than _Streams
- if (table.Definition.IsUnreal && "_Streams" != table.Name)
- {
- continue;
- }
-
- // Do not put the _Validation table in patches, it is not needed
- if (OutputType.Patch == 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(output.Codepage, this.core, importTable, baseDirectory, 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();
-
- // 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.IsPrimaryKey)
- {
- 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])
- {
- 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 (MsiInterop.MsiMaxStreamNameLength < streamName.Length)
- {
- this.core.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 (like transforms inside a patch)
- if (0 < output.SubStorages.Count)
- {
- using (View storagesView = new View(db, "SELECT `Name`, `Data` FROM `_Storages`"))
- {
- foreach (SubStorage subStorage in output.SubStorages)
- {
- string transformFile = Path.Combine(this.TempFilesLocation, String.Concat(subStorage.Name, ".mst"));
-
- // bind the transform
- if (this.BindTransform(subStorage.Data, transformFile))
- {
- // 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 MSI
- db.Commit();
- }
- }
- catch (IOException)
- {
- // TODO: this error message doesn't seem specific enough
- throw new WixFileNotFoundException(SourceLineNumberCollection.FromFileName(databaseFile), databaseFile);
- }
- }
-
- /// <summary>
- /// Get the source path of a directory.
- /// </summary>
- /// <param name="directories">All cached directories.</param>
- /// <param name="componentIdGenSeeds">Hash table of Component GUID generation seeds indexed by directory id.</param>
- /// <param name="directory">Directory identifier.</param>
- /// <param name="canonicalize">Canonicalize the path for standard directories.</param>
- /// <returns>Source path of a directory.</returns>
- [SuppressMessage("Microsoft.Globalization", "CA1308:NormalizeStringsToUppercase", Justification = "Changing the way this string normalizes would result " +
- "in a change to the way autogenerated GUIDs are generated. Furthermore, there is no security hole here, as the strings won't need to " +
- "make a round trip")]
- private static string GetDirectoryPath(Hashtable directories, Hashtable componentIdGenSeeds, string directory, bool canonicalize)
- {
- if (!directories.Contains(directory))
- {
- throw new WixException(WixErrors.ExpectedDirectory(directory));
- }
- ResolvedDirectory resolvedDirectory = (ResolvedDirectory)directories[directory];
-
- if (null == resolvedDirectory.Path)
- {
- if (null != componentIdGenSeeds && componentIdGenSeeds.Contains(directory))
- {
- resolvedDirectory.Path = (string)componentIdGenSeeds[directory];
- }
- else if (canonicalize && Util.IsStandardDirectory(directory))
- {
- // when canonicalization is on, standard directories are treated equally
- resolvedDirectory.Path = directory;
- }
- else
- {
- string name = resolvedDirectory.Name;
-
- if (canonicalize && null != name)
- {
- name = name.ToLower(CultureInfo.InvariantCulture);
- }
-
- if (String.IsNullOrEmpty(resolvedDirectory.DirectoryParent))
- {
- resolvedDirectory.Path = name;
- }
- else
- {
- string parentPath = GetDirectoryPath(directories, componentIdGenSeeds, resolvedDirectory.DirectoryParent, canonicalize);
-
- if (null != resolvedDirectory.Name)
- {
- resolvedDirectory.Path = Path.Combine(parentPath, name);
- }
- else
- {
- resolvedDirectory.Path = parentPath;
- }
- }
- }
- }
-
- return resolvedDirectory.Path;
- }
-
- /// <summary>
- /// Gets the source path of a file.
- /// </summary>
- /// <param name="directories">All cached directories in <see cref="ResolvedDirectory"/>.</param>
- /// <param name="directoryId">Parent directory identifier.</param>
- /// <param name="fileName">File name (in long|source format).</param>
- /// <param name="compressed">Specifies the package is compressed.</param>
- /// <param name="useLongName">Specifies the package uses long file names.</param>
- /// <returns>Source path of file relative to package directory.</returns>
- internal static string GetFileSourcePath(Hashtable directories, string directoryId, string fileName, bool compressed, bool useLongName)
- {
- string fileSourcePath = Installer.GetName(fileName, true, useLongName);
-
- if (compressed)
- {
- // Use just the file name of the file since all uncompressed files must appear
- // in the root of the image in a compressed package.
- }
- else
- {
- // Get the relative path of where we want the file to be layed out as specified
- // in the Directory table.
- string directoryPath = Binder.GetDirectoryPath(directories, null, directoryId, false);
- fileSourcePath = Path.Combine(directoryPath, fileSourcePath);
- }
-
- // Strip off "SourceDir" if it's still on there.
- if (fileSourcePath.StartsWith("SourceDir\\", StringComparison.Ordinal))
- {
- fileSourcePath = fileSourcePath.Substring(10);
- }
-
- return fileSourcePath;
- }
-
- /// <summary>
- /// Set an MsiAssemblyName row. If it was directly authored, override the value, otherwise
- /// create a new row.
- /// </summary>
- /// <param name="output">The output to bind.</param>
- /// <param name="assemblyNameTable">MsiAssemblyName table.</param>
- /// <param name="fileRow">FileRow containing the assembly read for the MsiAssemblyName row.</param>
- /// <param name="name">MsiAssemblyName name.</param>
- /// <param name="value">MsiAssemblyName value.</param>
- /// <param name="infoCache">Cache to populate with file information (optional).</param>
- /// <param name="modularizationGuid">The modularization GUID (in the case of merge modules).</param>
- [SuppressMessage("Microsoft.Globalization", "CA1308:NormalizeStringsToUppercase", Justification = "This string is not round tripped, and not used for any security decisions")]
- private void SetMsiAssemblyName(Output output, Table assemblyNameTable, FileRow fileRow, string name, string value, IDictionary<string, string> infoCache, string modularizationGuid)
- {
- // check for null value (this can occur when grabbing the file version from an assembly without one)
- if (null == value || 0 == value.Length)
- {
- this.core.OnMessage(WixWarnings.NullMsiAssemblyNameValue(fileRow.SourceLineNumbers, fileRow.Component, name));
- }
- else
- {
- Row assemblyNameRow = null;
-
- // override directly authored value
- foreach (Row row in assemblyNameTable.Rows)
- {
- if ((string)row[0] == fileRow.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 == fileRow.AssemblyType && String.IsNullOrEmpty(fileRow.AssemblyApplication) && !String.Equals(Path.GetFileNameWithoutExtension(fileRow.LongFileName), value, StringComparison.OrdinalIgnoreCase))
- {
- this.core.OnMessage(WixErrors.GACAssemblyIdentityWarning(fileRow.SourceLineNumbers, Path.GetFileNameWithoutExtension(fileRow.LongFileName), value));
- }
-
- if (null == assemblyNameRow)
- {
- assemblyNameRow = assemblyNameTable.CreateRow(fileRow.SourceLineNumbers);
- assemblyNameRow[0] = fileRow.Component;
- assemblyNameRow[1] = name;
- assemblyNameRow[2] = value;
-
- // put the MsiAssemblyName row in the same section as the related File row
- assemblyNameRow.SectionId = fileRow.SectionId;
-
- if (null == fileRow.AssemblyNameRows)
- {
- fileRow.AssemblyNameRows = new RowCollection();
- }
- fileRow.AssemblyNameRows.Add(assemblyNameRow);
- }
- else
- {
- assemblyNameRow[2] = value;
- }
-
- if (infoCache != null)
- {
- string key = String.Format(CultureInfo.InvariantCulture, "assembly{0}.{1}", name, Demodularize(output, modularizationGuid, fileRow.File)).ToLower(CultureInfo.InvariantCulture);
- infoCache[key] = (string)assemblyNameRow[2];
- }
- }
- }
-
- /// <summary>
- /// Merge data from a row in the WixPatchSymbolsPaths table into an associated FileRow.
- /// </summary>
- /// <param name="row">Row from the WixPatchSymbolsPaths table.</param>
- /// <param name="fileRow">FileRow into which to set symbol information.</param>
- /// <comment>This includes PreviousData as well.</comment>
- private static void MergeSymbolPaths(Row row, FileRow fileRow)
- {
- if (null == fileRow.Symbols)
- {
- fileRow.Symbols = (string)row[2];
- }
- else
- {
- fileRow.Symbols = String.Concat(fileRow.Symbols, ";", (string)row[2]);
- …
Large files files are truncated, but you can click here to view the full file