/// <summary> /// Extracts files from an MSI database and rewrites the paths embedded in the source .wixpdb to the output .wixpdb. /// </summary> private void MeltProduct() { // print friendly message saying what file is being decompiled Console.WriteLine("{0} / {1}", Path.GetFileName(this.inputFile), Path.GetFileName(this.inputPdbFile)); // extract files from the .msi (unless suppressed) and get the path map of File ids to target paths string outputDirectory = this.exportBasePath ?? Environment.GetEnvironmentVariable("WIX_TEMP"); IDictionary <string, string> paths = null; using (InstallPackage package = new InstallPackage(this.inputFile, DatabaseOpenMode.ReadOnly, null, outputDirectory)) { if (!this.suppressExtraction) { package.ExtractFiles(); } paths = package.Files.SourcePaths; } Pdb inputPdb = Pdb.Load(this.inputPdbFile, true, true); Table wixFileTable = inputPdb.Output.Tables["WixFile"]; if (null != wixFileTable) { foreach (Row row in wixFileTable.Rows) { WixFileRow fileRow = row as WixFileRow; if (null != fileRow) { string newPath; if (paths.TryGetValue(fileRow.File, out newPath)) { fileRow.Source = Path.Combine(outputDirectory, newPath); } } } } string tempPath = Path.Combine(Environment.GetEnvironmentVariable("WIX_TEMP") ?? Path.GetTempPath(), Path.GetRandomFileName()); try { inputPdb.Save(this.outputFile, null, null, tempPath); } finally { if (this.tidy) { if (!AppCommon.DeleteDirectory(tempPath, this.messageHandler)) { Console.WriteLine(MeltStrings.WAR_FailedToDeleteTempDir, tempPath); } } else { Console.WriteLine(MeltStrings.INF_TempDirLocatedAt, tempPath); } } }
private void SplitFiles(Output output) { // Split root file. Replace source path of root file. Table concatFilesTable = output.Tables["PSW_ConcatFiles"]; if ((concatFilesTable == null) || (concatFilesTable.Rows.Count <= 0)) { return; } concatFilesTable.Rows.Sort(new ConcatFilesComparer()); string tmpPath = Path.GetTempPath(); Table wixFileTable = output.Tables["WixFile"]; WixFileRow rootWixFile = null; int splitSize = Int32.MaxValue; FileStream rootFileStream = null; try { foreach (Row currConcatFileRow in concatFilesTable.Rows) { // New root file if (!currConcatFileRow[1].Equals(rootWixFile?.File)) { splitSize = (int)currConcatFileRow.Fields[4].Data; rootWixFile = Find(wixFileTable, currConcatFileRow.Fields[1].Data) as WixFileRow; if (rootWixFile == null) { Core.OnMessage(WixErrors.WixFileNotFound(currConcatFileRow.Fields[1].Data.ToString())); return; } rootFileStream?.Dispose(); rootFileStream = null; // Ensure no double-dispose in case next line throws rootFileStream = File.OpenRead(rootWixFile.Source); string splId = "spl" + Guid.NewGuid().ToString("N"); rootWixFile.Source = Path.Combine(tmpPath, splId); tempFiles_.Add(rootWixFile.Source); CopyFilePart(rootFileStream, rootWixFile.Source, splitSize); } WixFileRow currWixFile = Find(wixFileTable, currConcatFileRow.Fields[2].Data) as WixFileRow; if (currWixFile == null) { Core.OnMessage(WixErrors.WixFileNotFound(currConcatFileRow.Fields[2].Data.ToString())); return; } tempFiles_.Add(currWixFile.Source); CopyFilePart(rootFileStream, currWixFile.Source, splitSize); } } finally { rootFileStream?.Dispose(); } }
/// <summary> /// Generates the WixFile table based on a path to an admin image msi and an Output. /// </summary> /// <param name="databaseFile">The path to the msi database file in an admin image.</param> /// <param name="output">The Output that represents the msi database.</param> private void GenerateWixFileTable(string databaseFile, WindowsInstallerData output) { throw new NotImplementedException(); #if TODO_FIX_UNBINDING_FILES var adminRootPath = Path.GetDirectoryName(databaseFile); var componentDirectoryIndex = new Hashtable(); var componentTable = output.Tables["Component"]; foreach (var row in componentTable.Rows) { componentDirectoryIndex.Add(row[0], row[2]); } // Index full source paths for all directories var directoryDirectoryParentIndex = new Hashtable(); var directoryFullPathIndex = new Hashtable(); var directorySourceNameIndex = new Hashtable(); var directoryTable = output.Tables["Directory"]; foreach (var row in directoryTable.Rows) { directoryDirectoryParentIndex.Add(row[0], row[1]); if (null == row[1]) { directoryFullPathIndex.Add(row[0], adminRootPath); } else { directorySourceNameIndex.Add(row[0], GetAdminSourceName((string)row[2])); } } foreach (DictionaryEntry directoryEntry in directoryDirectoryParentIndex) { if (!directoryFullPathIndex.ContainsKey(directoryEntry.Key)) { this.GetAdminFullPath((string)directoryEntry.Key, directoryDirectoryParentIndex, directorySourceNameIndex, directoryFullPathIndex); } } var fileTable = output.Tables["File"]; var wixFileTable = output.EnsureTable(this.TableDefinitions["WixFile"]); foreach (var row in fileTable.Rows) { var wixFileRow = new WixFileRow(null, this.TableDefinitions["WixFile"]); wixFileRow.File = (string)row[0]; wixFileRow.Directory = (string)componentDirectoryIndex[(string)row[1]]; wixFileRow.Source = Path.Combine((string)directoryFullPathIndex[wixFileRow.Directory], GetAdminSourceName((string)row[2])); if (!File.Exists(wixFileRow.Source)) { throw new WixException(ErrorMessages.WixFileNotFound(wixFileRow.Source)); } wixFileTable.Rows.Add(wixFileRow); } #endif }
/// <summary> /// Generates the WixFile table based on a path to an admin image msi and an Output. /// </summary> /// <param name="databaseFile">The path to the msi database file in an admin image.</param> /// <param name="output">The Output that represents the msi database.</param> private void GenerateWixFileTable(string databaseFile, Output output) { string adminRootPath = Path.GetDirectoryName(databaseFile); Hashtable componentDirectoryIndex = new Hashtable(); Table componentTable = output.Tables["Component"]; foreach (Row row in componentTable.Rows) { componentDirectoryIndex.Add(row[0], row[2]); } // Index full source paths for all directories Hashtable directoryDirectoryParentIndex = new Hashtable(); Hashtable directoryFullPathIndex = new Hashtable(); Hashtable directorySourceNameIndex = new Hashtable(); Table directoryTable = output.Tables["Directory"]; foreach (Row row in directoryTable.Rows) { directoryDirectoryParentIndex.Add(row[0], row[1]); if (null == row[1]) { directoryFullPathIndex.Add(row[0], adminRootPath); } else { directorySourceNameIndex.Add(row[0], GetAdminSourceName((string)row[2])); } } foreach (DictionaryEntry directoryEntry in directoryDirectoryParentIndex) { if (!directoryFullPathIndex.ContainsKey(directoryEntry.Key)) { GetAdminFullPath((string)directoryEntry.Key, directoryDirectoryParentIndex, directorySourceNameIndex, directoryFullPathIndex); } } Table fileTable = output.Tables["File"]; Table wixFileTable = output.EnsureTable(this.TableDefinitions["WixFile"]); foreach (Row row in fileTable.Rows) { WixFileRow wixFileRow = new WixFileRow(null, this.TableDefinitions["WixFile"]); wixFileRow.File = (string)row[0]; wixFileRow.Directory = (string)componentDirectoryIndex[(string)row[1]]; wixFileRow.Source = Path.Combine((string)directoryFullPathIndex[wixFileRow.Directory], GetAdminSourceName((string)row[2])); if (!File.Exists(wixFileRow.Source)) { throw new WixException(ErrorMessages.WixFileNotFound(wixFileRow.Source)); } wixFileTable.Rows.Add(wixFileRow); } }
/// <summary> /// Extracts files from an MSI database and rewrites the paths embedded in the source .wixpdb to the output .wixpdb. /// </summary> private void MeltProduct() { // print friendly message saying what file is being decompiled Console.WriteLine("{0} / {1}", Path.GetFileName(this.inputFile), Path.GetFileName(this.inputPdbFile)); Pdb inputPdb = Pdb.Load(this.inputPdbFile, true); // extract files from the .msi (unless suppressed) and get the path map of File ids to target paths string outputDirectory = this.exportBasePath ?? Environment.GetEnvironmentVariable("WIX_TEMP"); IDictionary <string, string> paths = null; using (InstallPackage package = new InstallPackage(this.inputFile, DatabaseOpenMode.ReadOnly, null, outputDirectory)) { // ignore failures as this is a new validation in v3.x ValidateMsiMatchesPdb(package, inputPdb); if (!this.suppressExtraction) { package.ExtractFiles(); } paths = package.Files.SourcePaths; } Table wixFileTable = inputPdb.Output.Tables["WixFile"]; if (null != wixFileTable) { foreach (Row row in wixFileTable.Rows) { WixFileRow fileRow = row as WixFileRow; if (null != fileRow) { string newPath; if (paths.TryGetValue(fileRow.File, out newPath)) { fileRow.Source = Path.Combine(outputDirectory, newPath); } } } } inputPdb.Save(this.outputFile); }
/// <summary> /// Creates a new row in the table. /// </summary> /// <param name="sourceLineNumbers">Original source lines for this row.</param> /// <param name="add">Specifies whether to only create the row or add it to the table automatically.</param> /// <returns>Row created in table.</returns> public Row CreateRow(SourceLineNumber sourceLineNumbers, bool add = true) { Row row; switch (this.Name) { case "BBControl": row = new BBControlRow(sourceLineNumbers, this); break; case "WixBundlePackage": row = new WixBundlePackageRow(sourceLineNumbers, this); break; case "WixBundleExePackage": row = new WixBundleExePackageRow(sourceLineNumbers, this); break; case "WixBundleMsiPackage": row = new WixBundleMsiPackageRow(sourceLineNumbers, this); break; case "WixBundleMspPackage": row = new WixBundleMspPackageRow(sourceLineNumbers, this); break; case "WixBundleMsuPackage": row = new WixBundleMsuPackageRow(sourceLineNumbers, this); break; case "Component": row = new ComponentRow(sourceLineNumbers, this); break; case "WixBundleContainer": row = new WixBundleContainerRow(sourceLineNumbers, this); break; case "Control": row = new ControlRow(sourceLineNumbers, this); break; case "File": row = new FileRow(sourceLineNumbers, this); break; case "WixBundleMsiFeature": row = new WixBundleMsiFeatureRow(sourceLineNumbers, this); break; case "WixBundleMsiProperty": row = new WixBundleMsiPropertyRow(sourceLineNumbers, this); break; case "Media": row = new MediaRow(sourceLineNumbers, this); break; case "WixBundlePayload": row = new WixBundlePayloadRow(sourceLineNumbers, this); break; case "Property": row = new PropertyRow(sourceLineNumbers, this); break; case "WixRelatedBundle": row = new WixRelatedBundleRow(sourceLineNumbers, this); break; case "WixBundleRelatedPackage": row = new WixBundleRelatedPackageRow(sourceLineNumbers, this); break; case "WixBundleRollbackBoundary": row = new WixBundleRollbackBoundaryRow(sourceLineNumbers, this); break; case "Upgrade": row = new UpgradeRow(sourceLineNumbers, this); break; case "WixBundleVariable": row = new WixBundleVariableRow(sourceLineNumbers, this); break; case "WixAction": row = new WixActionRow(sourceLineNumbers, this); break; case "WixApprovedExeForElevation": row = new WixApprovedExeForElevationRow(sourceLineNumbers, this); break; case "WixBundle": row = new WixBundleRow(sourceLineNumbers, this); break; case "WixBundlePackageExitCode": row = new WixBundlePackageExitCodeRow(sourceLineNumbers, this); break; case "WixBundlePatchTargetCode": row = new WixBundlePatchTargetCodeRow(sourceLineNumbers, this); break; case "WixBundleSlipstreamMsp": row = new WixBundleSlipstreamMspRow(sourceLineNumbers, this); break; case "WixBundleUpdate": row = new WixBundleUpdateRow(sourceLineNumbers, this); break; case "WixBundleCatalog": row = new WixBundleCatalogRow(sourceLineNumbers, this); break; case "WixChain": row = new WixChainRow(sourceLineNumbers, this); break; case "WixChainItem": row = new WixChainItemRow(sourceLineNumbers, this); break; case "WixBundlePackageCommandLine": row = new WixBundlePackageCommandLineRow(sourceLineNumbers, this); break; case "WixComplexReference": row = new WixComplexReferenceRow(sourceLineNumbers, this); break; case "WixDeltaPatchFile": row = new WixDeltaPatchFileRow(sourceLineNumbers, this); break; case "WixDeltaPatchSymbolPaths": row = new WixDeltaPatchSymbolPathsRow(sourceLineNumbers, this); break; case "WixFile": row = new WixFileRow(sourceLineNumbers, this); break; case "WixGroup": row = new WixGroupRow(sourceLineNumbers, this); break; case "WixMedia": row = new WixMediaRow(sourceLineNumbers, this); break; case "WixMediaTemplate": row = new WixMediaTemplateRow(sourceLineNumbers, this); break; case "WixMerge": row = new WixMergeRow(sourceLineNumbers, this); break; case "WixPayloadProperties": row = new WixPayloadPropertiesRow(sourceLineNumbers, this); break; case "WixProperty": row = new WixPropertyRow(sourceLineNumbers, this); break; case "WixSimpleReference": row = new WixSimpleReferenceRow(sourceLineNumbers, this); break; case "WixUpdateRegistration": row = new WixUpdateRegistrationRow(sourceLineNumbers, this); break; case "WixVariable": row = new WixVariableRow(sourceLineNumbers, this); break; default: row = new Row(sourceLineNumbers, this); break; } if (add) { this.Rows.Add(row); } return(row); }
public FileFacade(bool fromModule, FileRow file, WixFileRow wixFile) { this.FromModule = fromModule; this.File = file; this.WixFile = wixFile; }
public FileFacade(FileRow file, WixFileRow wixFile, WixDeltaPatchFileRow deltaPatchFile) { this.File = file; this.WixFile = wixFile; this.DeltaPatchFile = deltaPatchFile; }
/// <summary> /// Parses a Tag element for Software Id Tag registration under a Product element. /// </summary> /// <param name="node">The element to parse.</param> private void ParseProductTagElement(XmlNode node) { SourceLineNumberCollection sourceLineNumbers = Preprocessor.GetSourceLineNumbers(node); string name = null; string regid = null; string feature = "WixSwidTag"; YesNoType licensed = YesNoType.NotSet; string type = null;; foreach (XmlAttribute attrib in node.Attributes) { if (0 == attrib.NamespaceURI.Length || attrib.NamespaceURI == this.schema.TargetNamespace) { switch (attrib.LocalName) { case "Name": name = this.Core.GetAttributeLongFilename(sourceLineNumbers, attrib, false); break; case "Regid": regid = this.Core.GetAttributeValue(sourceLineNumbers, attrib); break; case "Feature": feature = this.Core.GetAttributeIdentifierValue(sourceLineNumbers, attrib); break; case "Licensed": licensed = this.Core.GetAttributeYesNoValue(sourceLineNumbers, attrib); break; case "Type": type = this.ParseTagTypeAttribute(sourceLineNumbers, node, attrib); break; default: this.Core.UnexpectedAttribute(sourceLineNumbers, attrib); break; } } else { this.Core.UnsupportedExtensionAttribute(sourceLineNumbers, attrib); } } foreach (XmlNode child in node.ChildNodes) { if (XmlNodeType.Element == child.NodeType) { if (child.NamespaceURI == this.Schema.TargetNamespace) { this.Core.UnexpectedElement(node, child); } else { this.Core.UnsupportedExtensionElement(node, child); } } } if (String.IsNullOrEmpty(name)) { XmlAttribute productNameAttribute = node.ParentNode.Attributes["Name"]; if (null != productNameAttribute) { name = productNameAttribute.Value; } else { this.Core.OnMessage(WixErrors.ExpectedAttribute(sourceLineNumbers, node.Name, "Name")); } } if (!String.IsNullOrEmpty(name) && !CompilerCore.IsValidLongFilename(name, false)) { this.Core.OnMessage(TagErrors.IllegalName(sourceLineNumbers, node.ParentNode.LocalName, name)); } if (String.IsNullOrEmpty(regid)) { this.Core.OnMessage(WixErrors.ExpectedAttribute(sourceLineNumbers, node.Name, "Regid")); } if (!this.Core.EncounteredError) { string directoryId = "WixTagRegidFolder"; string fileId = this.Core.GenerateIdentifier("tag", regid, ".product.tag"); string fileName = String.Concat(regid, " ", name, ".swidtag"); string shortName = this.Core.GenerateShortName(fileName, false, false); this.Core.CreateWixSimpleReferenceRow(sourceLineNumbers, "Directory", directoryId); ComponentRow componentRow = (ComponentRow)this.Core.CreateRow(sourceLineNumbers, "Component"); componentRow.Component = fileId; componentRow.Guid = "*"; componentRow[3] = 0; componentRow.Directory = directoryId; componentRow.IsLocalOnly = true; componentRow.KeyPath = fileId; this.Core.CreateWixSimpleReferenceRow(sourceLineNumbers, "Feature", feature); this.Core.CreateComplexReference(sourceLineNumbers, ComplexReferenceParentType.Feature, feature, null, ComplexReferenceChildType.Component, fileId, true); FileRow fileRow = (FileRow)this.Core.CreateRow(sourceLineNumbers, "File"); fileRow.File = fileId; fileRow.Component = fileId; fileRow.FileName = String.Concat(shortName, "|", fileName); WixFileRow wixFileRow = (WixFileRow)this.Core.CreateRow(sourceLineNumbers, "WixFile"); wixFileRow.Directory = directoryId; wixFileRow.File = fileId; wixFileRow.DiskId = 1; wixFileRow.Attributes = 1; wixFileRow.Source = String.Concat("%TEMP%\\", fileName); this.Core.EnsureTable(sourceLineNumbers, "SoftwareIdentificationTag"); Row row = this.Core.CreateRow(sourceLineNumbers, "WixProductTag"); row[0] = fileId; row[1] = regid; row[2] = name; if (YesNoType.Yes == licensed) { row[3] = 1; } row[4] = type; this.Core.CreateWixSimpleReferenceRow(sourceLineNumbers, "File", fileId); } }
private bool CreateFacadesForMergeModuleFiles(WixMergeRow wixMergeRow, List <FileFacade> mergeModulesFileFacades, Dictionary <string, FileFacade> 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 <string, FileFacade> uniqueModuleFileIdentifiers = new Dictionary <string, FileFacade>(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); }
/// <summary> /// Parses a Tag element for Software Id Tag registration under a Product element. /// </summary> /// <param name="node">The element to parse.</param> private void ParseProductTagElement(XmlNode node) { SourceLineNumberCollection sourceLineNumbers = Preprocessor.GetSourceLineNumbers(node); string name = null; string regid = null; string feature = "WixSwidTag"; string installDirectory = null; bool win64 = (Platform.IA64 == this.Core.CurrentPlatform || Platform.X64 == this.Core.CurrentPlatform); foreach (XmlAttribute attrib in node.Attributes) { if (0 == attrib.NamespaceURI.Length || attrib.NamespaceURI == this.schema.TargetNamespace) { switch (attrib.LocalName) { case "Name": name = this.Core.GetAttributeLongFilename(sourceLineNumbers, attrib, false); break; case "Regid": regid = this.Core.GetAttributeValue(sourceLineNumbers, attrib); break; case "Feature": feature = this.Core.GetAttributeIdentifierValue(sourceLineNumbers, attrib); break; case "InstallDirectory": installDirectory = this.Core.GetAttributeIdentifierValue(sourceLineNumbers, attrib); break; case "InstallPath": this.Core.OnMessage(WixErrors.ExpectedParentWithAttribute(sourceLineNumbers, node.Name, attrib.Name, "Bundle")); break; case "Licensed": this.Core.OnMessage(WixWarnings.DeprecatedAttribute(sourceLineNumbers, node.Name, attrib.Name)); break; case "Type": this.Core.OnMessage(WixWarnings.DeprecatedAttribute(sourceLineNumbers, node.Name, attrib.Name)); break; case "Win64": win64 = (YesNoType.Yes == this.Core.GetAttributeYesNoValue(sourceLineNumbers, attrib)); break; default: this.Core.UnexpectedAttribute(sourceLineNumbers, attrib); break; } } else { this.Core.UnsupportedExtensionAttribute(sourceLineNumbers, attrib); } } foreach (XmlNode child in node.ChildNodes) { if (XmlNodeType.Element == child.NodeType) { if (child.NamespaceURI == this.Schema.TargetNamespace) { this.Core.UnexpectedElement(node, child); } else { this.Core.UnsupportedExtensionElement(node, child); } } } if (String.IsNullOrEmpty(name)) { XmlAttribute productNameAttribute = node.ParentNode.Attributes["Name"]; if (null != productNameAttribute) { name = productNameAttribute.Value; } else { this.Core.OnMessage(WixErrors.ExpectedAttribute(sourceLineNumbers, node.Name, "Name")); } } if (!String.IsNullOrEmpty(name) && !CompilerCore.IsValidLongFilename(name, false)) { this.Core.OnMessage(TagErrors.IllegalName(sourceLineNumbers, node.ParentNode.LocalName, name)); } if (String.IsNullOrEmpty(regid)) { this.Core.OnMessage(WixErrors.ExpectedAttribute(sourceLineNumbers, node.Name, "Regid")); } else if (regid.StartsWith("regid.")) { this.Core.OnMessage(TagWarnings.DeprecatedRegidFormat(sourceLineNumbers, regid)); return; } else if (regid.Equals("example.com", StringComparison.OrdinalIgnoreCase)) { this.Core.OnMessage(TagErrors.ExampleRegid(sourceLineNumbers, regid)); return; } if (String.IsNullOrEmpty(installDirectory)) { this.Core.OnMessage(WixErrors.ExpectedAttribute(sourceLineNumbers, node.Name, "InstallDirectory")); } if (!this.Core.EncounteredError) { string fileId = this.Core.GenerateIdentifier("tag", regid, ".product.tag"); string fileName = String.Concat(name, ".swidtag"); string shortName = this.Core.GenerateShortName(fileName, false, false); Row directoryRow = this.Core.CreateRow(sourceLineNumbers, "Directory"); directoryRow[0] = "WixTagInstallFolder"; directoryRow[1] = installDirectory; directoryRow[2] = "."; this.Core.CreateWixSimpleReferenceRow(sourceLineNumbers, "Directory", installDirectory); ComponentRow componentRow = (ComponentRow)this.Core.CreateRow(sourceLineNumbers, "Component"); componentRow.Component = fileId; componentRow.Guid = "*"; componentRow[3] = (win64 ? TagCompiler.MsidbComponentAttributes64bit : 0); componentRow.Directory = TagCompiler.TagFolderId; componentRow.IsLocalOnly = true; componentRow.KeyPath = fileId; this.Core.CreateWixSimpleReferenceRow(sourceLineNumbers, "Directory", TagCompiler.TagFolderId); this.Core.CreateWixSimpleReferenceRow(sourceLineNumbers, "Feature", feature); this.Core.CreateComplexReference(sourceLineNumbers, ComplexReferenceParentType.Feature, feature, null, ComplexReferenceChildType.Component, fileId, true); FileRow fileRow = (FileRow)this.Core.CreateRow(sourceLineNumbers, "File"); fileRow.File = fileId; fileRow.Component = fileId; fileRow.FileName = String.Concat(shortName, "|", fileName); WixFileRow wixFileRow = (WixFileRow)this.Core.CreateRow(sourceLineNumbers, "WixFile"); wixFileRow.Directory = TagCompiler.TagFolderId; wixFileRow.File = fileId; wixFileRow.DiskId = 1; wixFileRow.Attributes = 1; wixFileRow.Source = fileName; this.Core.EnsureTable(sourceLineNumbers, "SoftwareIdentificationTag"); Row row = this.Core.CreateRow(sourceLineNumbers, "WixProductTag"); row[0] = fileId; row[1] = regid; row[2] = name; this.Core.CreateWixSimpleReferenceRow(sourceLineNumbers, "File", fileId); } }
public void Execute() { Debug.Assert(OutputType.Patch != this.Output.Type); List <FileFacade> allFileRows = this.CopyOutFileRows ? new List <FileFacade>() : null; #if REVISIT_FOR_PATCHING // TODO: Fix this patching related code to work correctly with FileFacades. bool copyToPatch = (allFileRows != null); bool copyFromPatch = !copyToPatch; RowDictionary <MediaRow> patchMediaRows = new RowDictionary <MediaRow>(); Dictionary <int, RowDictionary <WixFileRow> > patchMediaFileRows = new Dictionary <int, RowDictionary <WixFileRow> >(); 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 <WixFileRow> mediaFileRows; if (!patchMediaFileRows.TryGetValue(diskId, out mediaFileRows)) { mediaFileRows = new RowDictionary <WixFileRow>(); patchMediaFileRows.Add(diskId, mediaFileRows); } mediaFileRows.Add(patchFileRow); } Table patchMediaTable = this.Output.EnsureTable(this.TableDefinitions["Media"]); patchMediaRows = new RowDictionary <MediaRow>(patchMediaTable); } // index paired transforms Dictionary <string, Output> pairedTransforms = new Dictionary <string, Output>(); 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 <WixFileRow> mainWixFiles = new RowDictionary <WixFileRow>(mainWixFileTable); RowDictionary <Row> mainMsiFileHashIndex = new RowDictionary <Row>(); 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 <Row>(mainMsiFileHashTable); } // Validate file row changes for keypath-related issues this.ValidateFileRowChanges(mainTransform); } // Index File table of pairedTransform Table pairedFileTable = pairedTransform.Tables["File"]; RowDictionary <FileRow> pairedFileRows = new RowDictionary <FileRow>(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 <WixFileRow> mediaFileRows; if (!patchMediaFileRows.TryGetValue(diskId, out mediaFileRows)) { mediaFileRows = new RowDictionary <WixFileRow>(); 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 <Row> 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; }