/// <summary> /// Constructor for auto media assigner. /// </summary> /// <param name="output">Output</param> /// <param name="core">Binder core.</param> /// <param name="filesCompressed">True if files are compressed by default. </param> public AutoMediaAssigner(Output output, BinderCore core, bool filesCompressed) { this.output = output; this.core = core; this.filesCompressed = filesCompressed; this.cabinetNameTemplate = "Cab{0}.cab"; uncompressedFileRows = new FileRowCollection(); mediaRows = new MediaRowCollection(); cabinets = new Hashtable(); }
/// <summary> /// Creates a new empty output object. /// </summary> internal Output() { this.componentsToFeatures = new ConnectToFeatureCollection(); this.modulesToFeatures = new ConnectToFeatureCollection(); this.featuresToFeatures = new ConnectToFeatureCollection(); this.ignoreModularizations = new IgnoreModularizationCollection(); this.outputTables = new OutputTableCollection(); this.importStreams = new ImportStreamCollection(); this.modules = new RowCollection(); this.fileMediaInfoCollection = new FileMediaInformationCollection(); this.mediaRows = new MediaRowCollection(); this.suppressAdminSequence = false; this.suppressAdvertiseSequence = false; this.suppressUISequence = false; }
/// <summary> /// Finalize the File table. /// </summary> /// <param name="tables">The collection of all tables.</param> /// <remarks> /// Sets the source, diskId, and assembly information for each file. /// </remarks> private void FinalizeFileTable(TableCollection tables) { Table fileTable = tables["File"]; Table mediaTable = tables["Media"]; Table msiAssemblyTable = tables["MsiAssembly"]; Table typeLibTable = tables["TypeLib"]; MediaRowCollection mediaRows; // index the media table by media id if (null != mediaTable) { mediaRows = new MediaRowCollection(); mediaRows.AddRange(mediaTable.Rows); } // set the disk identifiers and sources for files if (null != fileTable) { foreach (FileRow fileRow in fileTable.Rows) { Wix.File file = (Wix.File)this.core.GetIndexedElement("File", fileRow.File); // Don't bother processing files that are orphaned (and won't show up in the output anyway) if (null != file.ParentElement) { // set the diskid if (null != mediaTable) { foreach (MediaRow mediaRow in mediaTable.Rows) { if (fileRow.Sequence <= mediaRow.LastSequence) { file.DiskId = Convert.ToString(mediaRow.DiskId); break; } } } // set the source (done here because it requires information from the Directory table) if (OutputType.Module == this.outputType) { file.Source = String.Concat(this.exportFilePath, Path.DirectorySeparatorChar, "File", Path.DirectorySeparatorChar, file.Id, '.', this.modularizationGuid.Substring(1, 36).Replace('-', '_')); } else if (Wix.YesNoDefaultType.yes == file.Compressed || (Wix.YesNoDefaultType.no != file.Compressed && this.compressed)) { file.Source = String.Concat(this.exportFilePath, Path.DirectorySeparatorChar, "File", Path.DirectorySeparatorChar, file.Id); } else // uncompressed { string fileName = (null != file.ShortName ? file.ShortName : file.Name); if (!this.shortNames && null != file.Name) { fileName = file.Name; } if (this.compressed) // uncompressed at the root of the source image { file.Source = String.Concat("SourceDir", Path.DirectorySeparatorChar, fileName); } else { string sourcePath = this.GetSourcePath(file); file.Source = Path.Combine(sourcePath, fileName); } } } } } // set the file assemblies and manifests if (null != msiAssemblyTable) { foreach (Row row in msiAssemblyTable.Rows) { Wix.Component component = (Wix.Component)this.core.GetIndexedElement("Component", Convert.ToString(row[0])); if (null == component) { this.core.OnMessage(WixWarnings.ExpectedForeignRow(row.SourceLineNumbers, "MsiAssembly", row.GetPrimaryKey(DecompilerCore.PrimaryKeyDelimiter), "Component_", Convert.ToString(row[0]), "Component")); } else { foreach (Wix.ISchemaElement element in component.Children) { Wix.File file = element as Wix.File; if (null != file && Wix.YesNoType.yes == file.KeyPath) { if (null != row[2]) { file.AssemblyManifest = Convert.ToString(row[2]); } if (null != row[3]) { file.AssemblyApplication = Convert.ToString(row[3]); } if (null == row[4] || 0 == Convert.ToInt32(row[4])) { file.Assembly = Wix.File.AssemblyType.net; } else { file.Assembly = Wix.File.AssemblyType.win32; } } } } } } // nest the TypeLib elements if (null != typeLibTable) { foreach (Row row in typeLibTable.Rows) { Wix.Component component = (Wix.Component)this.core.GetIndexedElement("Component", Convert.ToString(row[2])); Wix.TypeLib typeLib = (Wix.TypeLib)this.core.GetIndexedElement(row); foreach (Wix.ISchemaElement element in component.Children) { Wix.File file = element as Wix.File; if (null != file && Wix.YesNoType.yes == file.KeyPath) { file.AddChild(typeLib); } } } } }
/// <summary> /// Update several msi tables with data contained in files references in the File table. /// </summary> /// <remarks> /// For versioned files, update the file version and language in the File table. For /// unversioned files, add a row to the MsiFileHash table for the file. For assembly /// files, add a row to the MsiAssembly table and add AssemblyName information by adding /// MsiAssemblyName rows. /// </remarks> /// <param name="output">Internal representation of the msi database to operate upon.</param> /// <param name="fileRows">The indexed file rows.</param> /// <param name="mediaRows">The indexed media rows.</param> /// <param name="infoCache">A hashtable to populate with the file information (optional).</param> /// <param name="modularizationGuid">The modularization guid (used in case of a merge module).</param> private Hashtable UpdateFileInformation(Output output, FileRowCollection fileRows, MediaRowCollection mediaRows, IDictionary<string, string> infoCache, string modularizationGuid) { // Index for all the fileId's // NOTE: When dealing with patches, there is a file table for each transform. In most cases, the data in these rows will be the same, however users of this index need to be aware of this. Hashtable fileRowIndex = new Hashtable(fileRows.Count); Table mediaTable = output.Tables["Media"]; // calculate sequence numbers and media disk id layout for all file media information objects if (OutputType.Module == output.Type) { int lastSequence = 0; foreach (FileRow fileRow in fileRows) { fileRow.Sequence = ++lastSequence; fileRowIndex[fileRow.File] = fileRow; } } else if (null != mediaTable) { int lastSequence = 0; MediaRow mediaRow = null; SortedList patchGroups = new SortedList(); // sequence the non-patch-added files foreach (FileRow fileRow in fileRows) { fileRowIndex[fileRow.File] = fileRow; if (null == mediaRow) { mediaRow = mediaRows[fileRow.DiskId]; if (OutputType.Patch == output.Type) { // patch Media cannot start at zero lastSequence = mediaRow.LastSequence; } } else if (mediaRow.DiskId != fileRow.DiskId) { mediaRow.LastSequence = lastSequence; mediaRow = mediaRows[fileRow.DiskId]; } if (0 < fileRow.PatchGroup) { ArrayList patchGroup = (ArrayList)patchGroups[fileRow.PatchGroup]; if (null == patchGroup) { patchGroup = new ArrayList(); patchGroups.Add(fileRow.PatchGroup, patchGroup); } patchGroup.Add(fileRow); } else { fileRow.Sequence = ++lastSequence; } } if (null != mediaRow) { mediaRow.LastSequence = lastSequence; mediaRow = null; } // sequence the patch-added files foreach (ArrayList patchGroup in patchGroups.Values) { foreach (FileRow fileRow in patchGroup) { if (null == mediaRow) { mediaRow = mediaRows[fileRow.DiskId]; } else if (mediaRow.DiskId != fileRow.DiskId) { mediaRow.LastSequence = lastSequence; mediaRow = mediaRows[fileRow.DiskId]; } fileRow.Sequence = ++lastSequence; } } if (null != mediaRow) { mediaRow.LastSequence = lastSequence; } } else { foreach (FileRow fileRow in fileRows) { fileRowIndex[fileRow.File] = fileRow; } } // no more work to do here if there are no file rows in the file table // note that this does not mean there are no files - the files from // merge modules are never put in the output's file table Table fileTable = output.Tables["File"]; if (null == fileTable) { return fileRowIndex; } // gather information about files that did not come from merge modules foreach (FileRow row in fileTable.Rows) { UpdateFileRow(output, infoCache, modularizationGuid, fileRowIndex, row, false); } return fileRowIndex; }
/// <summary> /// Process uncompressed files. /// </summary> /// <param name="tempDatabaseFile">The temporary database file.</param> /// <param name="fileRows">The collection of files to copy into the image.</param> /// <param name="fileTransfers">Array of files to be transfered.</param> /// <param name="mediaRows">The indexed media rows.</param> /// <param name="layoutDirectory">The directory in which the image should be layed out.</param> /// <param name="compressed">Flag if source image should be compressed.</param> /// <param name="longNamesInImage">Flag if long names should be used.</param> private void ProcessUncompressedFiles(string tempDatabaseFile, FileRowCollection fileRows, ArrayList fileTransfers, MediaRowCollection mediaRows, string layoutDirectory, bool compressed, bool longNamesInImage) { if (0 == fileRows.Count || this.core.EncounteredError) { return; } Hashtable directories = new Hashtable(); using (Database db = new Database(tempDatabaseFile, OpenDatabase.ReadOnly)) { using (View directoryView = db.OpenExecuteView("SELECT `Directory`, `Directory_Parent`, `DefaultDir` FROM `Directory`")) { while (true) { using (Record directoryRecord = directoryView.Fetch()) { if (null == directoryRecord) { break; } string sourceName = Installer.GetName(directoryRecord.GetString(3), true, longNamesInImage); directories.Add(directoryRecord.GetString(1), new ResolvedDirectory(directoryRecord.GetString(2), sourceName)); } } } using (View fileView = db.OpenView("SELECT `Directory_`, `FileName` FROM `Component`, `File` WHERE `Component`.`Component`=`File`.`Component_` AND `File`.`File`=?")) { using (Record fileQueryRecord = new Record(1)) { // for each file in the array of uncompressed files foreach (FileRow fileRow in fileRows) { string relativeFileLayoutPath = null; string mediaLayoutDirectory = this.FileManager.ResolveMedia(mediaRows[fileRow.DiskId], layoutDirectory); // setup up the query record and find the appropriate file in the // previously executed file view fileQueryRecord[1] = fileRow.File; fileView.Execute(fileQueryRecord); using (Record fileRecord = fileView.Fetch()) { if (null == fileRecord) { throw new WixException(WixErrors.FileIdentifierNotFound(fileRow.SourceLineNumbers, fileRow.File)); } relativeFileLayoutPath = Binder.GetFileSourcePath(directories, fileRecord[1], fileRecord[2], compressed, longNamesInImage); } // finally put together the base media layout path and the relative file layout path string fileLayoutPath = Path.Combine(mediaLayoutDirectory, relativeFileLayoutPath); FileTransfer transfer; if (FileTransfer.TryCreate(fileRow.Source, fileLayoutPath, false, "File", fileRow.SourceLineNumbers, out transfer)) { fileTransfers.Add(transfer); } } } } } }
/// <summary> /// Creates cabinet files. /// </summary> /// <param name="output">Output to generate image for.</param> /// <param name="fileRows">The indexed file rows.</param> /// <param name="fileTransfers">Array of files to be transfered.</param> /// <param name="mediaRows">The indexed media rows.</param> /// <param name="layoutDirectory">The directory in which the image should be layed out.</param> /// <param name="compressed">Flag if source image should be compressed.</param> /// <returns>The uncompressed file rows.</returns> private FileRowCollection CreateCabinetFiles(Output output, FileRowCollection fileRows, ArrayList fileTransfers, MediaRowCollection mediaRows, string layoutDirectory, bool compressed, AutoMediaAssigner autoMediaAssigner) { this.SetCabbingThreadCount(); // Send Binder object to Facilitate NewCabNamesCallBack Callback CabinetBuilder cabinetBuilder = new CabinetBuilder(this.cabbingThreadCount, Marshal.GetFunctionPointerForDelegate(this.newCabNamesCallBack)); // Supply Compile MediaTemplate Attributes to Cabinet Builder int MaximumCabinetSizeForLargeFileSplitting; int MaximumUncompressedMediaSize; this.GetMediaTemplateAttributes(out MaximumCabinetSizeForLargeFileSplitting, out MaximumUncompressedMediaSize); cabinetBuilder.MaximumCabinetSizeForLargeFileSplitting = MaximumCabinetSizeForLargeFileSplitting; cabinetBuilder.MaximumUncompressedMediaSize = MaximumUncompressedMediaSize; if (null != this.MessageHandler) { cabinetBuilder.Message += new MessageEventHandler(this.MessageHandler); } foreach (DictionaryEntry entry in autoMediaAssigner.Cabinets) { MediaRow mediaRow = (MediaRow)entry.Key; FileRowCollection files = (FileRowCollection)entry.Value; string cabinetDir = this.FileManager.ResolveMedia(mediaRow, layoutDirectory); CabinetWorkItem cabinetWorkItem = this.CreateCabinetWorkItem(output, cabinetDir, mediaRow, files, fileTransfers); if (null != cabinetWorkItem) { cabinetBuilder.Enqueue(cabinetWorkItem); } } // stop processing if an error previously occurred if (this.core.EncounteredError) { return null; } // create queued cabinets with multiple threads int cabError = cabinetBuilder.CreateQueuedCabinets(); if (0 != cabError) { this.core.EncounteredError = true; return null; } return autoMediaAssigner.UncompressedFileRows; }
private bool BindDatabase(Output output, string databaseFile) { foreach (BinderExtension extension in this.extensions) { extension.DatabaseInitialize(output); } bool compressed = false; FileRowCollection fileRows = new FileRowCollection(OutputType.Patch == output.Type); bool longNames = false; MediaRowCollection mediaRows = new MediaRowCollection(); Hashtable suppressModularizationIdentifiers = null; StringCollection suppressedTableNames = new StringCollection(); Table propertyTable = output.Tables["Property"]; this.WriteBuildInfoTable(output, databaseFile); // gather all the wix variables Table wixVariableTable = output.Tables["WixVariable"]; if (null != wixVariableTable) { foreach (WixVariableRow wixVariableRow in wixVariableTable.Rows) { this.WixVariableResolver.AddVariable(wixVariableRow); } } // gather all the suppress modularization identifiers Table wixSuppressModularizationTable = output.Tables["WixSuppressModularization"]; if (null != wixSuppressModularizationTable) { suppressModularizationIdentifiers = new Hashtable(wixSuppressModularizationTable.Rows.Count); foreach (Row row in wixSuppressModularizationTable.Rows) { suppressModularizationIdentifiers[row[0]] = null; } } // localize fields, resolve wix variables, and resolve file paths Hashtable cabinets = new Hashtable(); ArrayList delayedFields = new ArrayList(); this.ResolveFields(output.Tables, cabinets, delayedFields); // if there are any fields to resolve later, create the cache to populate during bind IDictionary<string, string> variableCache = null; if (0 < delayedFields.Count) { variableCache = new Dictionary<string, string>(StringComparer.InvariantCultureIgnoreCase); } this.LocalizeUI(output.Tables); // process the summary information table before the other tables string modularizationGuid = this.BindDatabaseSummaryInfo(output, out longNames, out compressed); // stop processing if an error previously occurred if (this.core.EncounteredError) { return false; } // modularize identifiers and add tables with real streams to the import tables if (OutputType.Module == output.Type) { foreach (Table table in output.Tables) { table.Modularize(modularizationGuid, suppressModularizationIdentifiers); } // Reset the special property lists after modularization. The linker creates these properties before modularization // so we have to reconstruct them for merge modules after modularization in the binder. Table wixPropertyTable = output.Tables["WixProperty"]; if (null != wixPropertyTable) { // Create lists of the properties that contribute to the special lists of properties. SortedList adminProperties = new SortedList(); SortedList secureProperties = new SortedList(); SortedList hiddenProperties = new SortedList(); foreach (WixPropertyRow wixPropertyRow in wixPropertyTable.Rows) { if (wixPropertyRow.Admin) { adminProperties[wixPropertyRow.Id] = null; } if (wixPropertyRow.Hidden) { hiddenProperties[wixPropertyRow.Id] = null; } if (wixPropertyRow.Secure) { secureProperties[wixPropertyRow.Id] = null; } } if (0 < adminProperties.Count || 0 < hiddenProperties.Count || 0 < secureProperties.Count) { Table table = output.Tables["Property"]; foreach (Row propertyRow in table.Rows) { if ("AdminProperties" == (string)propertyRow[0]) { propertyRow[1] = GetPropertyListString(adminProperties); } if ("MsiHiddenProperties" == (string)propertyRow[0]) { propertyRow[1] = GetPropertyListString(hiddenProperties); } if ("SecureCustomProperties" == (string)propertyRow[0]) { propertyRow[1] = GetPropertyListString(secureProperties); } } } } } // merge unreal table data into the real tables // this must occur after all variables and source paths have been resolved this.MergeUnrealTables(output.Tables); if (this.core.EncounteredError) { return false; } if (OutputType.Patch == output.Type) { foreach (SubStorage substorage in output.SubStorages) { Output transform = (Output)substorage.Data; this.ResolveFields(transform.Tables, cabinets, null); this.MergeUnrealTables(transform.Tables); } } // stop processing if an error previously occurred if (this.core.EncounteredError) { return false; } // index the File table for quicker access later // this must occur after the unreal data has been merged in Table fileTable = output.Tables["File"]; if (null != fileTable) { fileRows.AddRange(fileTable.Rows); } // stop processing if an error previously occurred if (this.core.EncounteredError) { return false; } // add binder variables for all properties propertyTable = output.Tables["Property"]; if (null != propertyTable) { foreach (Row propertyRow in propertyTable.Rows) { string property = propertyRow[0].ToString(); // set the ProductCode if its generated if (OutputType.Product == output.Type && "ProductCode" == property && "*" == propertyRow[1].ToString()) { propertyRow[1] = Common.GenerateGuid(); // Update the target ProductCode in any instance transforms foreach (SubStorage subStorage in output.SubStorages) { Output subStorageOutput = (Output)subStorage.Data; if (OutputType.Transform != subStorageOutput.Type) { continue; } Table instanceSummaryInformationTable = subStorageOutput.Tables["_SummaryInformation"]; foreach (Row row in instanceSummaryInformationTable.Rows) { if ((int)SummaryInformation.Transform.ProductCodes == (int)row[0]) { row[1] = ((string)row[1]).Replace("*", (string)propertyRow[1]); break; } } } } // add the property name and value to the variableCache if (0 != delayedFields.Count) { string key = String.Concat("property.", Demodularize(output, modularizationGuid, property)); variableCache[key] = (string)propertyRow[1]; } } } // extract files that come from cabinet files (this does not extract files from merge modules) this.ExtractCabinets(cabinets); // retrieve files and their information from merge modules if (OutputType.Product == output.Type) { this.ProcessMergeModules(output, fileRows); } else if (OutputType.Patch == output.Type) { // merge transform data into the output object this.CopyTransformData(output, fileRows); } // stop processing if an error previously occurred if (this.core.EncounteredError) { return false; } // assign files to media AutoMediaAssigner autoMediaAssigner = new AutoMediaAssigner(output, this.core, compressed); autoMediaAssigner.AssignFiles(fileRows); // update file version, hash, assembly, etc.. information this.core.OnMessage(WixVerboses.UpdatingFileInformation()); Hashtable indexedFileRows = this.UpdateFileInformation(output, fileRows, autoMediaAssigner.MediaRows, variableCache, modularizationGuid); // set generated component guids this.SetComponentGuids(output); // With the Component Guids set now we can create instance transforms. this.CreateInstanceTransforms(output); this.ValidateComponentGuids(output); this.UpdateControlText(output); if (0 < delayedFields.Count) { this.ResolveDelayedFields(output, delayedFields, variableCache, modularizationGuid); } // stop processing if an error previously occurred if (this.core.EncounteredError) { return false; } // Extended binder extensions can be called now that fields are resolved. foreach (BinderExtension extension in this.extensions) { BinderExtensionEx extensionEx = extension as BinderExtensionEx; if (null != extensionEx) { output.EnsureTable(this.core.TableDefinitions["WixBindUpdatedFiles"]); extensionEx.DatabaseAfterResolvedFields(output); } } Table updatedFiles = output.Tables["WixBindUpdatedFiles"]; if (null != updatedFiles) { foreach (Row updatedFile in updatedFiles.Rows) { FileRow updatedFileRow = (FileRow)indexedFileRows[updatedFile[0]]; this.UpdateFileRow(output, null, modularizationGuid, indexedFileRows, updatedFileRow, true); } } // stop processing if an error previously occurred if (this.core.EncounteredError) { return false; } // create cabinet files and process uncompressed files string layoutDirectory = Path.GetDirectoryName(databaseFile); FileRowCollection uncompressedFileRows = null; if (!this.suppressLayout || OutputType.Module == output.Type) { this.core.OnMessage(WixVerboses.CreatingCabinetFiles()); uncompressedFileRows = this.CreateCabinetFiles(output, fileRows, this.fileTransfers, autoMediaAssigner.MediaRows, layoutDirectory, compressed, autoMediaAssigner); } if (OutputType.Patch == output.Type) { // copy output data back into the transforms this.CopyTransformData(output, null); } // stop processing if an error previously occurred if (this.core.EncounteredError) { return false; } // add back suppressed tables which must be present prior to merging in modules if (OutputType.Product == output.Type) { Table wixMergeTable = output.Tables["WixMerge"]; if (null != wixMergeTable && 0 < wixMergeTable.Rows.Count) { foreach (SequenceTable sequence in Enum.GetValues(typeof(SequenceTable))) { string sequenceTableName = sequence.ToString(); Table sequenceTable = output.Tables[sequenceTableName]; if (null == sequenceTable) { sequenceTable = output.EnsureTable(this.core.TableDefinitions[sequenceTableName]); } if (0 == sequenceTable.Rows.Count) { suppressedTableNames.Add(sequenceTableName); } } } } foreach (BinderExtension extension in this.extensions) { extension.DatabaseFinalize(output); } // generate database file this.core.OnMessage(WixVerboses.GeneratingDatabase()); string tempDatabaseFile = Path.Combine(this.TempFilesLocation, Path.GetFileName(databaseFile)); this.GenerateDatabase(output, tempDatabaseFile, false, false); FileTransfer transfer; if (FileTransfer.TryCreate(tempDatabaseFile, databaseFile, true, output.Type.ToString(), null, out transfer)) // note where this database needs to move in the future { transfer.Built = true; this.fileTransfers.Add(transfer); } // stop processing if an error previously occurred if (this.core.EncounteredError) { return false; } // Output the output to a file if (null != this.pdbFile) { Pdb pdb = new Pdb(null); pdb.Output = output; pdb.Save(this.pdbFile, null, this.WixVariableResolver, this.TempFilesLocation); } // merge modules if (OutputType.Product == output.Type) { this.core.OnMessage(WixVerboses.MergingModules()); this.MergeModules(tempDatabaseFile, output, fileRows, suppressedTableNames); // stop processing if an error previously occurred if (this.core.EncounteredError) { return false; } } // inspect the MSI prior to running ICEs InspectorCore inspectorCore = new InspectorCore(this.MessageHandler); foreach (InspectorExtension inspectorExtension in this.inspectorExtensions) { inspectorExtension.Core = inspectorCore; inspectorExtension.InspectDatabase(tempDatabaseFile, output); // reset inspectorExtension.Core = null; } if (inspectorCore.EncounteredError) { return false; } // validate the output if there is an MSI validator if (null != this.validator) { Stopwatch stopwatch = Stopwatch.StartNew(); // set the output file for source line information this.validator.Output = output; this.core.OnMessage(WixVerboses.ValidatingDatabase()); this.core.EncounteredError = !this.validator.Validate(tempDatabaseFile); stopwatch.Stop(); this.core.OnMessage(WixVerboses.ValidatedDatabase(stopwatch.ElapsedMilliseconds)); // stop processing if an error previously occurred if (this.core.EncounteredError) { return false; } } // process uncompressed files if (!this.suppressLayout) { this.ProcessUncompressedFiles(tempDatabaseFile, uncompressedFileRows, this.fileTransfers, autoMediaAssigner.MediaRows, layoutDirectory, compressed, longNames); } // layout media try { this.core.OnMessage(WixVerboses.LayingOutMedia()); this.LayoutMedia(this.fileTransfers, this.suppressAclReset); } finally { if (!String.IsNullOrEmpty(this.contentsFile)) { this.CreateContentsFile(this.contentsFile, fileRows); } if (!String.IsNullOrEmpty(this.outputsFile)) { this.CreateOutputsFile(this.outputsFile, this.fileTransfers, this.pdbFile); } if (!String.IsNullOrEmpty(this.builtOutputsFile)) { this.CreateBuiltOutputsFile(this.builtOutputsFile, this.fileTransfers, this.pdbFile); } } return !this.core.EncounteredError; }