/// <summary> /// Creates a new file media information from a file row. /// </summary> /// <param name="fileRow">File row.</param> public FileMediaInformation(FileRow fileRow) { if (null == fileRow) { throw new ArgumentNullException("fileRow"); } this.containedInModule = false; this.fileId = fileRow.File; this.directoryId = fileRow.Directory; this.mediaId = fileRow.DiskId; this.source = fileRow.Source; this.modulePath = null; this.rowNumber = fileRow.Number; this.patchGroup = fileRow.PatchGroup; if (0 != (fileRow.Attributes & 0x002000)) { this.fileCompression = FileCompressionValue.No; } else if (0 != (fileRow.Attributes & 0x004000)) { this.fileCompression = FileCompressionValue.Yes; } else { this.fileCompression = FileCompressionValue.NotSpecified; } this.sequence = -1; }
/// <summary> /// Appends previous data from another FileRow object. /// </summary> /// <param name="src">An row to get data from.</param> public void AppendPreviousDataFrom(FileRow src) { AppendStringToArray(ref this.previousSource, src.previousSource[0]); AppendStringToArray(ref this.previousSymbols, src.previousSymbols[0]); AppendStringToArray(ref this.previousRetainOffsets, src.previousRetainOffsets[0]); AppendStringToArray(ref this.previousRetainLengths, src.previousRetainLengths[0]); AppendStringToArray(ref this.previousIgnoreOffsets, src.previousIgnoreOffsets[0]); AppendStringToArray(ref this.previousIgnoreLengths, src.previousIgnoreLengths[0]); }
/// <summary> /// Copies data from another FileRow object. /// </summary> /// <param name="src">An row to get data from.</param> public void CopyFrom(FileRow src) { for (int i = 0; i < src.Fields.Length; i++) { this[i] = src[i]; } this.assemblyManifest = src.assemblyManifest; this.assemblyType = src.assemblyType; this.directory = src.directory; this.diskId = src.diskId; this.fromModule = src.fromModule; this.isGeneratedShortFileName = src.isGeneratedShortFileName; this.patchGroup = src.patchGroup; this.processorArchitecture = src.processorArchitecture; this.source = src.source; }
/// <summary> /// Creates a new row in the table. /// </summary> /// <param name="sourceLineNumbers">Original source lines for this row.</param> /// <returns>Row created in table.</returns> public Row CreateRow(SourceLineNumberCollection sourceLineNumbers) { Row row; switch (this.Name) { // TODO: create strongly types rows here case "BBControl": row = new BBControlRow(sourceLineNumbers, this); break; case "Control": row = new ControlRow(sourceLineNumbers, this); break; case "File": row = new FileRow(sourceLineNumbers, this); break; case "Media": row = new MediaRow(sourceLineNumbers, this); break; case "Merge": row = new MergeRow(sourceLineNumbers, this); break; case "Property": row = new PropertyRow(sourceLineNumbers, this); break; case "Upgrade": row = new UpgradeRow(sourceLineNumbers, this); break; default: row = new Row(sourceLineNumbers, this); break; } this.rows.Add(row); return(row); }
/// <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(SourceLineNumberCollection sourceLineNumbers, bool add) { Row row; switch (this.Name) { case "BBControl": row = new BBControlRow(sourceLineNumbers, this); break; case "ChainMsiPackage": row = new ChainMsiPackageRow(sourceLineNumbers, this); break; case "Component": row = new ComponentRow(sourceLineNumbers, this); break; case "Control": row = new ControlRow(sourceLineNumbers, this); break; case "File": row = new FileRow(sourceLineNumbers, this); break; case "Media": row = new MediaRow(sourceLineNumbers, this); break; case "PayloadInfo": row = new PayloadInfoRow(sourceLineNumbers, this); break; case "Upgrade": row = new UpgradeRow(sourceLineNumbers, this); break; case "Variable": row = new VariableRow(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 "WixBundlePatchTargetCode": row = new WixBundlePatchTargetCodeRow(sourceLineNumbers, this); break; case "WixBundleUpdate": row = new WixBundleUpdateRow(sourceLineNumbers, this); break; case "WixCatalog": row = new WixCatalogRow(sourceLineNumbers, this); break; case "WixCommandLine": row = new WixCommandLineRow(sourceLineNumbers, this); break; case "WixComplexReference": row = new WixComplexReferenceRow(sourceLineNumbers, this); break; case "WixFile": row = new WixFileRow(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 "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); }
/// <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(SourceLineNumberCollection sourceLineNumbers, bool add) { Row row; switch (this.Name) { case "BBControl": row = new BBControlRow(sourceLineNumbers, this); break; case "ChainMsiPackage": row = new ChainMsiPackageRow(sourceLineNumbers, this); break; case "Component": row = new ComponentRow(sourceLineNumbers, this); break; case "Control": row = new ControlRow(sourceLineNumbers, this); break; case "File": row = new FileRow(sourceLineNumbers, this); break; case "Media": row = new MediaRow(sourceLineNumbers, this); break; case "PayloadInfo": row = new PayloadInfoRow(sourceLineNumbers, this); break; case "Upgrade": row = new UpgradeRow(sourceLineNumbers, this); break; case "Variable": row = new VariableRow(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 "WixBundlePatchTargetCode": row = new WixBundlePatchTargetCodeRow(sourceLineNumbers, this); break; case "WixBundleUpdate": row = new WixBundleUpdateRow(sourceLineNumbers, this); break; case "WixCatalog": row = new WixCatalogRow(sourceLineNumbers, this); break; case "WixCommandLine": row = new WixCommandLineRow(sourceLineNumbers, this); break; case "WixComplexReference": row = new WixComplexReferenceRow(sourceLineNumbers, this); break; case "WixFile": row = new WixFileRow(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 "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; }
/// <summary> /// Creates a new row in the table. /// </summary> /// <param name="sourceLineNumbers">Original source lines for this row.</param> /// <returns>Row created in table.</returns> public Row CreateRow(SourceLineNumberCollection sourceLineNumbers) { Row row; switch (this.Name) { case "BBControl": row = new BBControlRow(sourceLineNumbers, this); break; case "Control": row = new ControlRow(sourceLineNumbers, this); break; case "File": row = new FileRow(sourceLineNumbers, this); break; case "Media": row = new MediaRow(sourceLineNumbers, this); break; case "Upgrade": row = new UpgradeRow(sourceLineNumbers, this); break; case "WixAction": row = new WixActionRow(sourceLineNumbers, this); break; case "WixComplexReference": row = new WixComplexReferenceRow(sourceLineNumbers, this); break; case "WixFile": row = new WixFileRow(sourceLineNumbers, this); break; case "WixMedia": row = new WixMediaRow(sourceLineNumbers, this); break; case "WixMerge": row = new WixMergeRow(sourceLineNumbers, this); break; case "WixProperty": row = new WixPropertyRow(sourceLineNumbers, this); break; case "WixSimpleReference": row = new WixSimpleReferenceRow(sourceLineNumbers, this); break; case "WixVariable": row = new WixVariableRow(sourceLineNumbers, this); break; default: row = new Row(sourceLineNumbers, this); break; } this.rows.Add(row); return(row); }
private void UpdateFileRow(Output output, IDictionary<string, string> infoCache, string modularizationGuid, Hashtable fileRowIndex, FileRow fileRow, bool overwriteHash) { FileInfo fileInfo = null; if (!this.suppressFileHashAndInfo || (!this.suppressAssemblies && FileAssemblyType.NotAnAssembly != fileRow.AssemblyType)) { try { fileInfo = new FileInfo(fileRow.Source); } catch (ArgumentException) { this.core.OnMessage(WixErrors.InvalidFileName(fileRow.SourceLineNumbers, fileRow.Source)); return; } catch (PathTooLongException) { this.core.OnMessage(WixErrors.InvalidFileName(fileRow.SourceLineNumbers, fileRow.Source)); return; } catch (NotSupportedException) { this.core.OnMessage(WixErrors.InvalidFileName(fileRow.SourceLineNumbers, fileRow.Source)); return; } } if (!this.suppressFileHashAndInfo) { if (fileInfo.Exists) { string version; string language; using (FileStream fileStream = new FileStream(fileInfo.FullName, FileMode.Open, FileAccess.Read, FileShare.Read)) { if (Int32.MaxValue < fileStream.Length) { throw new WixException(WixErrors.FileTooLarge(fileRow.SourceLineNumbers, fileRow.Source)); } fileRow.FileSize = Convert.ToInt32(fileStream.Length, CultureInfo.InvariantCulture); } 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(fileRow.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 (0 == version.Length) // unversioned files have their hashes added to the MsiFileHash table { if (null != fileRow.Version) { // Check if this is a companion file. If its not, it is a default version. if (!fileRowIndex.ContainsKey(fileRow.Version)) { this.core.OnMessage(WixWarnings.DefaultVersionUsedForUnversionedFile(fileRow.SourceLineNumbers, fileRow.Version, fileRow.File)); } } else { if (null != fileRow.Language) { this.core.OnMessage(WixWarnings.DefaultLanguageUsedForUnversionedFile(fileRow.SourceLineNumbers, fileRow.Language, fileRow.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(fileRow.SourceLineNumbers, fileInfo.FullName)); } else { throw new WixException(WixErrors.Win32Exception(e.NativeErrorCode, fileInfo.FullName, e.Message)); } } if (null == fileRow.HashRow) { Table msiFileHashTable = output.EnsureTable(this.core.TableDefinitions["MsiFileHash"]); fileRow.HashRow = msiFileHashTable.CreateRow(fileRow.SourceLineNumbers); } fileRow.HashRow[0] = fileRow.File; fileRow.HashRow[1] = 0; fileRow.HashRow[2] = hash[0]; fileRow.HashRow[3] = hash[1]; fileRow.HashRow[4] = hash[2]; fileRow.HashRow[5] = hash[3]; } } else // update the file row with the version and language information { // Check if the version field references a fileId because this would mean it has a companion file and the version should not be overwritten. if (null == fileRow.Version || !fileRowIndex.ContainsKey(fileRow.Version)) { fileRow.Version = version; } if (null != fileRow.Language && 0 == language.Length) { this.core.OnMessage(WixWarnings.DefaultLanguageUsedForVersionedFile(fileRow.SourceLineNumbers, fileRow.Language, fileRow.File)); } else { fileRow.Language = language; } // Populate the binder variables for this file information if requested. if (null != infoCache) { if (!String.IsNullOrEmpty(fileRow.Version)) { string key = String.Format(CultureInfo.InvariantCulture, "fileversion.{0}", Demodularize(output, modularizationGuid, fileRow.File)); infoCache[key] = fileRow.Version; } if (!String.IsNullOrEmpty(fileRow.Language)) { string key = String.Format(CultureInfo.InvariantCulture, "filelanguage.{0}", Demodularize(output, modularizationGuid, fileRow.File)); infoCache[key] = fileRow.Language; } } } } else { this.core.OnMessage(WixErrors.CannotFindFile(fileRow.SourceLineNumbers, fileRow.File, fileRow.FileName, fileRow.Source)); } } // if we're not suppressing automagically grabbing assembly information and this is a // CLR assembly, load the assembly and get the assembly name information if (!this.suppressAssemblies) { if (FileAssemblyType.DotNetAssembly == fileRow.AssemblyType) { 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 culture = referenceIdentity.GetAttribute(null, "Culture"); if (null != culture) { assemblyNameValues.Add("Culture", culture); } else { assemblyNameValues.Add("Culture", "neutral"); } 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 (fileRow.AssemblyApplication == null) { throw new WixException(WixErrors.GacAssemblyNoStrongName(fileRow.SourceLineNumbers, fileInfo.FullName, fileRow.Component)); } string version = referenceIdentity.GetAttribute(null, "Version"); if (null != version) { assemblyNameValues.Add("Version", version); } } else { this.core.OnMessage(WixErrors.InvalidAssemblyFile(fileRow.SourceLineNumbers, fileInfo.FullName, String.Format(CultureInfo.InvariantCulture, "HRESULT: 0x{0:x8}", result))); return; } Table assemblyNameTable = output.EnsureTable(this.core.TableDefinitions["MsiAssemblyName"]); if (assemblyNameValues.ContainsKey("name")) { SetMsiAssemblyName(output, assemblyNameTable, fileRow, "name", assemblyNameValues["name"], infoCache, modularizationGuid); } string fileVersion = null; if (this.setMsiAssemblyNameFileVersion) { string language; Installer.GetFileVersion(fileInfo.FullName, out fileVersion, out language); SetMsiAssemblyName(output, assemblyNameTable, fileRow, "fileVersion", fileVersion, infoCache, modularizationGuid); } if (assemblyNameValues.ContainsKey("version")) { string assemblyVersion = assemblyNameValues["version"]; if (!this.exactAssemblyVersions) { // there is a bug in 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 (this.setMsiAssemblyNameFileVersion && null != fileVersion && fileVersion.Length > assemblyVersion.Length) { string padding = new string('0', fileVersion.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); } } } SetMsiAssemblyName(output, assemblyNameTable, fileRow, "version", assemblyVersion, infoCache, modularizationGuid); } if (assemblyNameValues.ContainsKey("culture")) { SetMsiAssemblyName(output, assemblyNameTable, fileRow, "culture", assemblyNameValues["culture"], infoCache, modularizationGuid); } if (assemblyNameValues.ContainsKey("publicKeyToken")) { SetMsiAssemblyName(output, assemblyNameTable, fileRow, "publicKeyToken", assemblyNameValues["publicKeyToken"], infoCache, modularizationGuid); } if (null != fileRow.ProcessorArchitecture && 0 < fileRow.ProcessorArchitecture.Length) { SetMsiAssemblyName(output, assemblyNameTable, fileRow, "processorArchitecture", fileRow.ProcessorArchitecture, infoCache, modularizationGuid); } if (assemblyNameValues.ContainsKey("processorArchitecture")) { SetMsiAssemblyName(output, assemblyNameTable, fileRow, "processorArchitecture", assemblyNameValues["processorArchitecture"], infoCache, modularizationGuid); } // add the assembly name to the information cache if (null != infoCache) { string fileId = Demodularize(output, modularizationGuid, fileRow.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"]); } infoCache[key] = assemblyName; // Add entries with the preserved case publicKeyToken string pcAssemblyNameKey = String.Concat("assemblyfullnamepreservedcase.", fileId); infoCache[pcAssemblyNameKey] = (assemblyNameValues["publicKeyToken"] == assemblyNameValues["publicKeyTokenPreservedCase"]) ? assemblyName : assemblyName.Replace(assemblyNameValues["publicKeyToken"], assemblyNameValues["publicKeyTokenPreservedCase"]); string pcPublicKeyTokenKey = String.Concat("assemblypublickeytokenpreservedcase.", fileId); infoCache[pcPublicKeyTokenKey] = assemblyNameValues["publicKeyTokenPreservedCase"]; } } else if (FileAssemblyType.Win32Assembly == fileRow.AssemblyType) { // Able to use the index because only the Source field is used and it is used only for more complete error messages. FileRow fileManifestRow = (FileRow)fileRowIndex[fileRow.AssemblyManifest]; if (null == fileManifestRow) { this.core.OnMessage(WixErrors.MissingManifestForWin32Assembly(fileRow.SourceLineNumbers, fileRow.File, fileRow.AssemblyManifest)); } string type = null; string name = null; string version = null; string processorArchitecture = null; string publicKeyToken = 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(fileManifestRow.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) { this.core.OnMessage(WixErrors.InvalidManifestContent(fileRow.SourceLineNumbers, fileManifestRow.Source)); return; } if (nav.MoveToAttribute("type", String.Empty)) { type = nav.Value; nav.MoveToParent(); } if (nav.MoveToAttribute("name", String.Empty)) { name = nav.Value; nav.MoveToParent(); } if (nav.MoveToAttribute("version", String.Empty)) { version = nav.Value; nav.MoveToParent(); } if (nav.MoveToAttribute("processorArchitecture", String.Empty)) { processorArchitecture = nav.Value; nav.MoveToParent(); } if (nav.MoveToAttribute("publicKeyToken", String.Empty)) { publicKeyToken = nav.Value; nav.MoveToParent(); } } } } catch (FileNotFoundException fe) { this.core.OnMessage(WixErrors.FileNotFound(SourceLineNumberCollection.FromFileName(fileManifestRow.Source), fe.FileName, "AssemblyManifest")); } catch (XmlException xe) { this.core.OnMessage(WixErrors.InvalidXml(SourceLineNumberCollection.FromFileName(fileManifestRow.Source), "manifest", xe.Message)); } Table assemblyNameTable = output.EnsureTable(this.core.TableDefinitions["MsiAssemblyName"]); if (null != name && 0 < name.Length) { SetMsiAssemblyName(output, assemblyNameTable, fileRow, "name", name, infoCache, modularizationGuid); } if (null != version && 0 < version.Length) { SetMsiAssemblyName(output, assemblyNameTable, fileRow, "version", version, infoCache, modularizationGuid); } if (null != type && 0 < type.Length) { SetMsiAssemblyName(output, assemblyNameTable, fileRow, "type", type, infoCache, modularizationGuid); } if (null != processorArchitecture && 0 < processorArchitecture.Length) { SetMsiAssemblyName(output, assemblyNameTable, fileRow, "processorArchitecture", processorArchitecture, infoCache, modularizationGuid); } if (null != publicKeyToken && 0 < publicKeyToken.Length) { SetMsiAssemblyName(output, assemblyNameTable, fileRow, "publicKeyToken", publicKeyToken, infoCache, modularizationGuid); } } } }
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> /// Copies data from another FileRow object. /// </summary> /// <param name="src">An row to get data from.</param> public void CopyFrom(FileRow src) { for (int i = 0; i < src.Fields.Length; i++) { this[i] = src[i]; } this.assemblyManifest = src.assemblyManifest; this.assemblyType = src.assemblyType; this.directory = src.directory; this.diskId = src.diskId; this.fromModule = src.fromModule; this.isGeneratedShortFileName = src.isGeneratedShortFileName; this.patchGroup = src.patchGroup; this.processorArchitecture = src.processorArchitecture; this.source = src.source; this.PreviousSource = src.PreviousSource; this.Operation = src.Operation; this.symbols = src.symbols; this.PreviousSymbols = src.PreviousSymbols; this.patchAttributes = src.patchAttributes; this.retainOffsets = src.retainOffsets; this.retainLengths = src.retainLengths; this.ignoreOffsets = src.ignoreOffsets; this.ignoreLengths = src.ignoreLengths; this.PreviousRetainOffsets = src.PreviousRetainOffsets; this.PreviousRetainLengths = src.PreviousRetainLengths; this.PreviousIgnoreOffsets = src.PreviousIgnoreOffsets; this.PreviousIgnoreLengths = src.PreviousIgnoreLengths; }
/// <summary> /// Adds a FileRow to the end of the collection. /// </summary> /// <param name="fileRow">The FileRow to be added to the end of the collection.</param> public void Add(FileRow fileRow) { if (this.hashedCollection != null) { this.hashedCollection.Add(fileRow.File, fileRow); } this.sortedCollection.Add(fileRow, null); }
public virtual void ResolvePatch(FileRow fileRow, out bool retainRangeWarning) { if (fileRow == null) { throw new ArgumentNullException("fileRow"); } retainRangeWarning = false; if (this.deltaBinaryPatch && RowOperation.Modify == fileRow.Operation) { if (0 != (PatchAttributeType.IncludeWholeFile | fileRow.PatchAttributes)) { string deltaBase = Common.GenerateIdentifier("dlt", true, Common.GenerateGuid()); string deltaFile = Path.Combine(this.tempFilesLocation, String.Concat(deltaBase, ".dpf")); string headerFile = Path.Combine(this.tempFilesLocation, String.Concat(deltaBase, ".phd")); PatchAPI.PatchInterop.PatchSymbolFlagsType apiPatchingSymbolFlags = 0; bool optimizePatchSizeForLargeFiles = false; Table wixPatchIdTable = this.output.Tables["WixPatchId"]; if (null != wixPatchIdTable) { Row row = 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); } } } if (PatchAPI.PatchInterop.CreateDelta( deltaFile, fileRow.Source, fileRow.Symbols, fileRow.RetainOffsets, fileRow.PreviousSourceArray, fileRow.PreviousSymbolsArray, fileRow.PreviousIgnoreLengthsArray, fileRow.PreviousIgnoreOffsetsArray, fileRow.PreviousRetainLengthsArray, fileRow.PreviousRetainOffsetsArray, apiPatchingSymbolFlags, optimizePatchSizeForLargeFiles, out retainRangeWarning)) { PatchAPI.PatchInterop.ExtractDeltaHeader(deltaFile, headerFile); fileRow.Patch = headerFile; fileRow.Source = deltaFile; } } } }
/// <summary> /// Set an MsiAssemblyName row. If it was directly authored, override the value, otherwise /// create a new row. /// </summary> /// <param name="assemblyNameOutputTable">MsiAssemblyName output 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> private void SetMsiAssemblyName(OutputTable assemblyNameOutputTable, FileRow fileRow, string name, string value) { // check for null value (this can occur when grabbing the file version from an assembly without one) if (null == value || 0 == value.Length) { this.OnMessage(WixErrors.NullMsiAssemblyNameValue(fileRow.SourceLineNumbers, fileRow.Component, name)); } // override directly authored value foreach (OutputRow outputRow in assemblyNameOutputTable.OutputRows) { if ((string)outputRow.Row[0] == fileRow.Component && (string)outputRow.Row[1] == name) { outputRow.Row[2] = value; return; } } Row assemblyNameRow = new Row(assemblyNameOutputTable.TableDefinition); assemblyNameRow[0] = fileRow.Component; assemblyNameRow[1] = name; assemblyNameRow[2] = value; assemblyNameOutputTable.OutputRows.Add(new OutputRow(assemblyNameRow)); }
/// <summary> /// Create the #transform for the given main transform. /// </summary> /// <param name="patchId">patch GUID from patch authoring.</param> /// <param name="mainTransform">transform generated by torch.</param> /// <param name="mediaRow">media authored into patch.</param> /// <param name="productCode">output string to receive ProductCode.</param> public Output BuildPairedTransform(string patchId, Output mainTransform, MediaRow mediaRow, ref string productCode) { Output pairedTransform = new Output(null); pairedTransform.Type = OutputType.Transform; pairedTransform.Codepage = mainTransform.Codepage; // lookup productVersion property to correct summaryInformation string newProductVersion = null; Table mainPropertyTable = mainTransform.Tables["Property"]; if (mainPropertyTable != null) { foreach (Row row in mainPropertyTable.Rows) { if ("ProductVersion" == (string)row[0]) { newProductVersion = (string)row[1]; } } } // TODO: build class for manipulating SummaryInformation table Table mainSummaryTable = mainTransform.Tables["_SummaryInformation"]; // add required properties Hashtable mainSummaryRows = new Hashtable(); foreach (Row mainSummaryRow in mainSummaryTable.Rows) { mainSummaryRows[mainSummaryRow[0]] = mainSummaryRow; } if (!mainSummaryRows.Contains((int)SummaryInformation.Transform.ValidationFlags)) { Row mainSummaryRow = mainSummaryTable.CreateRow(null); mainSummaryRow[0] = (int)SummaryInformation.Transform.ValidationFlags; mainSummaryRow[1] = "0"; } // copy summary information from core transform Table pairedSummaryTable = pairedTransform.EnsureTable(this.tableDefinitions["_SummaryInformation"]); foreach (Row mainSummaryRow in mainSummaryTable.Rows) { string value = (string)mainSummaryRow[1]; switch ((SummaryInformation.Transform)mainSummaryRow[0]) { case SummaryInformation.Transform.ProductCodes: string[] propertyData = value.Split(';'); string oldProductVersion = propertyData[0].Substring(38); string upgradeCode = propertyData[2]; productCode = propertyData[0].Substring(0, 38); if (newProductVersion == null) { newProductVersion = oldProductVersion; } // force mainTranform to old;new;upgrade and pairedTransform to new;new;upgrade mainSummaryRow[1] = String.Concat(productCode, oldProductVersion, ';', productCode, newProductVersion, ';', upgradeCode); value = String.Concat(productCode, newProductVersion, ';', productCode, newProductVersion, ';', upgradeCode); break; case SummaryInformation.Transform.ValidationFlags: // TODO: ensure this row exists in mainSummaryTable!!!! // TODO: author these settings in patch XML or set in torch.exe int i = Convert.ToInt32(value); i |= (int)SummaryInformation.TransformFlags.ErrorAddExistingRow; i |= (int)SummaryInformation.TransformFlags.ErrorDeleteMissingRow; i |= (int)SummaryInformation.TransformFlags.ErrorAddExistingTable; i |= (int)SummaryInformation.TransformFlags.ErrorDeleteMissingTable; i |= (int)SummaryInformation.TransformFlags.ErrorUpdateMissingRow; i |= (int)SummaryInformation.TransformFlags.ValidateProduct; mainSummaryRow[1] = value = i.ToString(); break; } Row pairedSummaryRow = pairedSummaryTable.CreateRow(null); pairedSummaryRow[0] = mainSummaryRow[0]; pairedSummaryRow[1] = value; } if (productCode == null) { throw new InvalidOperationException("Could not determine ProductCode from transform summary information"); } // copy File table Table mainFileTable = mainTransform.Tables["File"]; Table mainWixFileTable = mainTransform.Tables["WixFile"]; if (mainFileTable != null) { FileRowCollection mainFileRows = new FileRowCollection(); mainFileRows.AddRange(mainFileTable.Rows); Table pairedFileTable = pairedTransform.EnsureTable(mainFileTable.Definition); foreach (Row mainWixFileRow in mainWixFileTable.Rows) { FileRow mainFileRow = mainFileRows[(string)mainWixFileRow[0]]; // set File.Sequence to non null to satisfy transform bind mainFileRow.Sequence = 1; // delete's don't need rows in the paired transform if (mainFileRow.Operation == RowOperation.Delete) { continue; } FileRow pairedFileRow = (FileRow)pairedFileTable.CreateRow(null); pairedFileRow.Operation = RowOperation.Modify; for (int i = 0; i < mainFileRow.Fields.Length; i++) { object value = mainFileRow[i]; pairedFileRow[i] = value; } // override authored media for patch bind // TODO: consider using File/@DiskId for patch media mainFileRow.DiskId = mediaRow.DiskId; mainWixFileRow[5] = mediaRow.DiskId; // suppress any change to File.Sequence to avoid bloat mainFileRow.Fields[7].Modified = false; // force File row to appear in the transform if (RowOperation.Modify == mainFileRow.Operation) { mainFileRow.Operation = RowOperation.Modify; pairedFileRow.Attributes |= MsiInterop.MsidbFileAttributesPatchAdded; pairedFileRow.Fields[6].Modified = true; pairedFileRow.Operation = RowOperation.Modify; } else if (RowOperation.Add == mainFileRow.Operation) { // set msidbFileAttributesPatchAdded pairedFileRow.Attributes |= MsiInterop.MsidbFileAttributesPatchAdded; pairedFileRow.Fields[6].Modified = true; pairedFileRow.Operation = RowOperation.Add; } else { pairedFileRow.Attributes = mainFileRow.Attributes; pairedFileRow.Fields[6].Modified = false; } } } // add Media row to pairedTransform Table pairedMediaTable = pairedTransform.EnsureTable(this.tableDefinitions["Media"]); Row pairedMediaRow = pairedMediaTable.CreateRow(null); pairedMediaRow.Operation = RowOperation.Add; for (int i = 0; i < mediaRow.Fields.Length; i++) { pairedMediaRow[i] = mediaRow[i]; } // add PatchPackage for this Media Table pairedPackageTable = pairedTransform.EnsureTable(this.tableDefinitions["PatchPackage"]); pairedPackageTable.Operation = TableOperation.Add; Row pairedPackageRow = pairedPackageTable.CreateRow(null); pairedPackageRow.Operation = RowOperation.Add; pairedPackageRow[0] = patchId; pairedPackageRow[1] = mediaRow.DiskId; // add property to both identify client patches and whether those patches are removable or not string patchPropertyId = new Guid(patchId).ToString("N", CultureInfo.InvariantCulture).ToUpper(); int allowRemoval = 0; Table msiPatchMetadataTable = this.patch.Tables["MsiPatchMetadata"]; if (null != msiPatchMetadataTable) { foreach (Row msiPatchMetadataRow in msiPatchMetadataTable.Rows) { if (string.Empty == (string)msiPatchMetadataRow[0] && "AllowRemoval" == (string)msiPatchMetadataRow[1]) { allowRemoval = Convert.ToInt32((string)msiPatchMetadataRow[2]); } } } Table pairedPropertyTable = pairedTransform.EnsureTable(this.tableDefinitions["Property"]); pairedPropertyTable.Operation = TableOperation.Add; Row pairedPropertyRow = pairedPropertyTable.CreateRow(null); pairedPropertyRow.Operation = RowOperation.Add; pairedPropertyRow[0] = string.Format(CultureInfo.InvariantCulture, "_{0}.AllowRemoval", patchPropertyId); pairedPropertyRow[1] = allowRemoval.ToString(); return(pairedTransform); }
/// <summary> /// Creates a new row in the table. /// </summary> /// <param name="sourceLineNumbers">Original source lines for this row.</param> /// <returns>Row created in table.</returns> public Row CreateRow(SourceLineNumberCollection sourceLineNumbers) { Row row; switch (this.Name) { case "BBControl": row = new BBControlRow(sourceLineNumbers, this); break; case "ChainMsiPackage": row = new ChainMsiPackageRow(sourceLineNumbers, this); break; case "Component": row = new ComponentRow(sourceLineNumbers, this); break; case "Control": row = new ControlRow(sourceLineNumbers, this); break; case "File": row = new FileRow(sourceLineNumbers, this); break; case "Media": row = new MediaRow(sourceLineNumbers, this); break; case "PayloadInfo": row = new PayloadInfoRow(sourceLineNumbers, this); break; case "Upgrade": row = new UpgradeRow(sourceLineNumbers, this); break; case "WixAction": row = new WixActionRow(sourceLineNumbers, this); break; case "WixComplexReference": row = new WixComplexReferenceRow(sourceLineNumbers, this); break; case "WixFile": row = new WixFileRow(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 "WixProperty": row = new WixPropertyRow(sourceLineNumbers, this); break; case "WixSimpleReference": row = new WixSimpleReferenceRow(sourceLineNumbers, this); break; case "WixVariable": row = new WixVariableRow(sourceLineNumbers, this); break; default: row = new Row(sourceLineNumbers, this); break; } this.rows.Add(row); return row; }
/// <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]); } Field field = row.Fields[2]; if (null != field.PreviousData) { if (null == fileRow.PreviousSymbols) { fileRow.PreviousSymbols = field.PreviousData; } else { fileRow.PreviousSymbols = String.Concat(fileRow.PreviousSymbols, ";", field.PreviousData); } } }
/// <summary> /// Retrieve files and their information from merge modules. /// </summary> /// <param name="output">Internal representation of the msi database to operate upon.</param> /// <param name="fileRows">The indexed file rows.</param> private void ProcessMergeModules(Output output, FileRowCollection fileRows) { Table wixMergeTable = output.Tables["WixMerge"]; if (null != wixMergeTable) { IMsmMerge2 merge = NativeMethods.GetMsmMerge(); // Get the output's minimum installer version int outputInstallerVersion = int.MinValue; Table summaryInformationTable = output.Tables["_SummaryInformation"]; if (null != summaryInformationTable) { foreach (Row row in summaryInformationTable.Rows) { if (14 == (int)row[0]) { outputInstallerVersion = Convert.ToInt32(row[1], CultureInfo.InvariantCulture); break; } } } foreach (Row row in wixMergeTable.Rows) { bool containsFiles = false; WixMergeRow wixMergeRow = (WixMergeRow)row; 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")) { Hashtable uniqueModuleFileIdentifiers = System.Collections.Specialized.CollectionsUtil.CreateCaseInsensitiveHashtable(); 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 = new FileRow(null, this.core.TableDefinitions["File"]); fileRow.File = record[1]; fileRow.Compressed = wixMergeRow.FileCompression; fileRow.Directory = record[2]; fileRow.DiskId = wixMergeRow.DiskId; fileRow.FromModule = true; fileRow.PatchGroup = -1; fileRow.Source = String.Concat(this.TempFilesLocation, Path.DirectorySeparatorChar, "MergeId.", wixMergeRow.Number.ToString(CultureInfo.InvariantCulture.NumberFormat), Path.DirectorySeparatorChar, record[1]); FileRow collidingFileRow = fileRows[fileRow.File]; FileRow collidingModuleFileRow = (FileRow)uniqueModuleFileIdentifiers[fileRow.File]; if (null == collidingFileRow && null == collidingModuleFileRow) { fileRows.Add(fileRow); // keep track of file identifiers in this merge module uniqueModuleFileIdentifiers.Add(fileRow.File, fileRow); } else // collision(s) detected { // case-sensitive collision with another merge module or a user-authored file identifier if (null != collidingFileRow) { this.core.OnMessage(WixErrors.DuplicateModuleFileIdentifier(wixMergeRow.SourceLineNumbers, wixMergeRow.Id, collidingFileRow.File)); } // case-insensitive collision with another file identifier in the same merge module if (null != collidingModuleFileRow) { this.core.OnMessage(WixErrors.DuplicateModuleCaseInsensitiveFileIdentifier(wixMergeRow.SourceLineNumbers, wixMergeRow.Id, fileRow.File, collidingModuleFileRow.File)); } } 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 > outputInstallerVersion) { this.core.OnMessage(WixWarnings.InvalidHigherInstallerVersionInModule(wixMergeRow.SourceLineNumbers, wixMergeRow.Id, moduleInstallerVersion, 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)); } // if the module has files and creating layout if (containsFiles && !this.suppressLayout) { bool moduleOpen = false; short mergeLanguage; try { mergeLanguage = Convert.ToInt16(wixMergeRow.Language, CultureInfo.InvariantCulture); } catch (System.FormatException) { this.core.OnMessage(WixErrors.InvalidMergeLanguage(wixMergeRow.SourceLineNumbers, wixMergeRow.Id, wixMergeRow.Language)); continue; } 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(); } } } } } }
/// <summary> /// Creates a new row in the table. /// </summary> /// <param name="sourceLineNumbers">Original source lines for this row.</param> /// <returns>Row created in table.</returns> public Row CreateRow(SourceLineNumberCollection sourceLineNumbers) { Row row; switch (this.Name) { // TODO: create strongly types rows here case "BBControl": row = new BBControlRow(sourceLineNumbers, this); break; case "Control": row = new ControlRow(sourceLineNumbers, this); break; case "File": row = new FileRow(sourceLineNumbers, this); break; case "Media": row = new MediaRow(sourceLineNumbers, this); break; case "Merge": row = new MergeRow(sourceLineNumbers, this); break; case "Property": row = new PropertyRow(sourceLineNumbers, this); break; case "Upgrade": row = new UpgradeRow(sourceLineNumbers, this); break; default: row = new Row(sourceLineNumbers, this); break; } this.rows.Add(row); return row; }