Exemple #1
0
 /// <summary>
 /// Creates a binder.
 /// </summary>
 public WixBinder()
 {
     this.extensions                    = new List <BinderExtension>();
     this.inspectorExtensions           = new List <InspectorExtension>();
     this.fileManager                   = new BinderFileManager();
     this.fileManager.TempFilesLocation = this.TempFilesLocation;
 }
Exemple #2
0
        /// <summary>
        /// Saves an output to a path on disk.
        /// </summary>
        /// <param name="path">Path to save output file to on disk.</param>
        /// <param name="binderFileManager">If provided, the binder file manager is used to bind files into the output.</param>
        /// <param name="wixVariableResolver">The Wix variable resolver.</param>
        /// <param name="tempFilesLocation">Location for temporary files.</param>
        public void Save(string path, BinderFileManager binderFileManager, WixVariableResolver wixVariableResolver, string tempFilesLocation)
        {
            FileMode fileMode = FileMode.Create;

            // Assure the location to output the xml exists
            Directory.CreateDirectory(Path.GetDirectoryName(Path.GetFullPath(path)));

            // Check if there was a cab on the output when it was created
            if (SaveCab(path, binderFileManager, wixVariableResolver, tempFilesLocation))
            {
                fileMode = FileMode.Append;
            }

            // save the xml
            using (FileStream fs = new FileStream(path, fileMode))
            {
                XmlWriter writer = null;

                try
                {
                    writer = new XmlTextWriter(fs, System.Text.Encoding.UTF8);

                    writer.WriteStartDocument();
                    this.Persist(writer);
                    writer.WriteEndDocument();
                }
                finally
                {
                    if (null != writer)
                    {
                        writer.Close();
                    }
                }
            }
        }
 /// <summary>
 /// Instantiate a new CabinetWorkItem.
 /// </summary>
 /// <param name="fileRows">The collection of files in this cabinet.</param>
 /// <param name="cabinetFile">The cabinet file.</param>
 /// <param name="maxThreshold">Maximum threshold for each cabinet.</param>
 /// <param name="compressionLevel">The compression level of the cabinet.</param>
 /// <param name="binderFileManager">The binder file manager.</param>
 public CabinetWorkItem(FileRowCollection fileRows, string cabinetFile, int maxThreshold, Cab.CompressionLevel compressionLevel, BinderFileManager binderFileManager)
 {
     this.cabinetFile       = cabinetFile;
     this.compressionLevel  = compressionLevel;
     this.fileRows          = fileRows;
     this.binderFileManager = binderFileManager;
     this.maxThreshold      = maxThreshold;
 }
Exemple #4
0
        /// <summary>
        /// Resolves the source path of a file.
        /// </summary>
        /// <param name="source">Original source value.</param>
        /// <param name="type">Optional type of source file being resolved.</param>
        /// <param name="sourceLineNumbers">Optional source line of source file being resolved.</param>
        /// <param name="bindStage">The binding stage used to determine what collection of bind paths will be used</param>
        /// <returns>Should return a valid path for the stream to be imported.</returns>
        public virtual string ResolveFile(string source, string type, SourceLineNumber sourceLineNumbers, BindStage bindStage)
        {
            if (String.IsNullOrEmpty(source))
            {
                throw new ArgumentNullException("source");
            }

            if (BinderFileManager.CheckFileExists(source)) // if the file exists, we're good to go.
            {
                return(source);
            }
            else if (Path.IsPathRooted(source)) // path is rooted so bindpaths won't help, bail since the file apparently doesn't exist.
            {
                return(null);
            }
            else // not a rooted path so let's try applying all the different source resolution options.
            {
                const string bindPathOpenString = "!(bindpath.";

                string bindName             = String.Empty;
                string path                 = source;
                string pathWithoutSourceDir = null;

                if (source.StartsWith(bindPathOpenString, StringComparison.Ordinal))
                {
                    int closeParen = source.IndexOf(')', bindPathOpenString.Length);
                    if (-1 != closeParen)
                    {
                        bindName = source.Substring(bindPathOpenString.Length, closeParen - bindPathOpenString.Length);
                        path     = source.Substring(bindPathOpenString.Length + bindName.Length + 1); // +1 for the closing brace.
                        path     = path.TrimStart('\\');                                              // remove starting '\\' char so the path doesn't look rooted.
                    }
                }
                else if (source.StartsWith("SourceDir\\", StringComparison.Ordinal) || source.StartsWith("SourceDir/", StringComparison.Ordinal))
                {
                    pathWithoutSourceDir = path.Substring(10);
                }

                var bindPaths = this.Core.GetBindPaths(bindStage, bindName);
                foreach (string bindPath in bindPaths)
                {
                    string filePath;
                    if (!String.IsNullOrEmpty(pathWithoutSourceDir))
                    {
                        filePath = Path.Combine(bindPath, pathWithoutSourceDir);
                        if (BinderFileManager.CheckFileExists(filePath))
                        {
                            return(filePath);
                        }
                    }

                    filePath = Path.Combine(bindPath, path);
                    if (BinderFileManager.CheckFileExists(filePath))
                    {
                        return(filePath);
                    }
                }
            }

            // Didn't find the file.
            return(null);
        }
Exemple #5
0
        public virtual ResolvedCabinet ResolveCabinet(string cabinetPath, IEnumerable <BindFileWithPath> filesWithPath)
        {
            if (null == filesWithPath)
            {
                throw new ArgumentNullException("fileRows");
            }

            // By default cabinet should be built and moved to the suggested location.
            ResolvedCabinet resolved = new ResolvedCabinet()
            {
                BuildOption = CabinetBuildOption.BuildAndMove, Path = cabinetPath
            };

            // If a cabinet cache path was provided, change the location for the cabinet
            // to be built to and check if there is a cabinet that can be reused.
            if (!String.IsNullOrEmpty(this.Core.CabCachePath))
            {
                string cabinetName = Path.GetFileName(cabinetPath);
                resolved.Path = Path.Combine(this.Core.CabCachePath, cabinetName);

                if (BinderFileManager.CheckFileExists(resolved.Path))
                {
                    // Assume that none of the following are true:
                    // 1. any files are added or removed
                    // 2. order of files changed or names changed
                    // 3. modified time changed
                    bool cabinetValid = true;

                    // Need to force garbage collection of WixEnumerateCab to ensure the handle
                    // associated with it is closed before it is reused.
                    using (Cab.WixEnumerateCab wixEnumerateCab = new Cab.WixEnumerateCab())
                    {
                        List <CabinetFileInfo> fileList = wixEnumerateCab.Enumerate(resolved.Path);

                        if (filesWithPath.Count() != fileList.Count)
                        {
                            cabinetValid = false;
                        }
                        else
                        {
                            int i = 0;
                            foreach (BindFileWithPath file in filesWithPath)
                            {
                                // First check that the file identifiers match because that is quick and easy.
                                CabinetFileInfo cabFileInfo = fileList[i];
                                cabinetValid = (cabFileInfo.FileId == file.Id);
                                if (cabinetValid)
                                {
                                    // Still valid so ensure the file sizes are the same.
                                    FileInfo fileInfo = new FileInfo(file.Path);
                                    cabinetValid = (cabFileInfo.Size == fileInfo.Length);
                                    if (cabinetValid)
                                    {
                                        // Still valid so ensure the source time stamp hasn't changed. Thus we need
                                        // to convert the source file time stamp into a cabinet compatible data/time.
                                        ushort sourceCabDate;
                                        ushort sourceCabTime;

                                        WixToolset.Core.Native.CabInterop.DateTimeToCabDateAndTime(fileInfo.LastWriteTime, out sourceCabDate, out sourceCabTime);
                                        cabinetValid = (cabFileInfo.Date == sourceCabDate && cabFileInfo.Time == sourceCabTime);
                                    }
                                }

                                if (!cabinetValid)
                                {
                                    break;
                                }

                                i++;
                            }
                        }
                    }

                    resolved.BuildOption = cabinetValid ? CabinetBuildOption.Copy : CabinetBuildOption.BuildAndCopy;
                }
            }

            return(resolved);
        }
        public virtual CabinetBuildOption ResolveCabinet(FileRowCollection fileRows, ref string cabinetPath)
        {
            if (fileRows == null)
            {
                throw new ArgumentNullException("fileRows");
            }

            // no special behavior specified, use the default
            if (null == this.cabCachePath && !this.reuseCabinets)
            {
                return(CabinetBuildOption.BuildAndMove);
            }

            // if a cabinet cache path was provided, change the location for the cabinet
            // to be built to
            if (null != this.cabCachePath)
            {
                string cabinetName = Path.GetFileName(cabinetPath);
                cabinetPath = Path.Combine(this.cabCachePath, cabinetName);
            }

            // if we still think we're going to reuse the cabinet check to see if the cabinet exists first
            if (this.reuseCabinets)
            {
                bool cabinetValid = false;

                if (BinderFileManager.CheckFileExists(cabinetPath))
                {
                    // check to see if
                    // 1. any files are added or removed
                    // 2. order of files changed or names changed
                    // 3. modified time changed
                    cabinetValid = true;

                    // Need to force garbage collection of WixEnumerateCab to ensure the handle
                    // associated with it is closed before it is reused.
                    using (Cab.WixEnumerateCab wixEnumerateCab = new Cab.WixEnumerateCab())
                    {
                        ArrayList fileList = wixEnumerateCab.Enumerate(cabinetPath);

                        if (fileRows.Count != fileList.Count)
                        {
                            cabinetValid = false;
                        }
                        else
                        {
                            int i = 0;
                            foreach (FileRow fileRow in fileRows)
                            {
                                // First check that the file identifiers match because that is quick and easy.
                                CabinetFileInfo cabFileInfo = fileList[i] as CabinetFileInfo;
                                cabinetValid = (cabFileInfo.FileId == fileRow.File);
                                if (cabinetValid)
                                {
                                    // Still valid so ensure the source time stamp hasn't changed. Thus we need
                                    // to convert the source file time stamp into a cabinet compatible data/time.
                                    DateTime sourceFileTime = File.GetLastWriteTime(fileRow.Source);
                                    ushort   sourceCabDate;
                                    ushort   sourceCabTime;

                                    Cab.Interop.CabInterop.DateTimeToCabDateAndTime(sourceFileTime, out sourceCabDate, out sourceCabTime);
                                    cabinetValid = (cabFileInfo.Date == sourceCabDate && cabFileInfo.Time == sourceCabTime);
                                }

                                if (!cabinetValid)
                                {
                                    break;
                                }

                                i++;
                            }
                        }
                    }
                }

                return(cabinetValid ? CabinetBuildOption.Copy : CabinetBuildOption.BuildAndCopy);
            }
            else // by default move the built cabinet
            {
                return(CabinetBuildOption.BuildAndMove);
            }
        }
        /// <summary>
        /// Resolves the source path of a file.
        /// </summary>
        /// <param name="source">Original source value.</param>
        /// <param name="type">Optional type of source file being resolved.</param>
        /// <param name="sourceLineNumbers">Optional source line of source file being resolved.</param>
        /// <param name="bindStage">The binding stage used to determine what collection of bind paths will be used</param>
        /// <returns>Should return a valid path for the stream to be imported.</returns>
        public virtual string ResolveFile(string source, string type, SourceLineNumber sourceLineNumbers, BindStage bindStage)
        {
            // the following new local variables are used for bind path and protect the changes to object field.
            StringCollection    currentBindPaths      = null;
            NameValueCollection currentNamedBindPaths = null;
            StringCollection    currentSourcePaths    = null;

            if (String.IsNullOrEmpty(source))
            {
                throw new ArgumentNullException("source");
            }

            // Call the original override function first. If it returns an answer then return that,
            // otherwise using the default resolving logic
            string filePath = this.ResolveFile(source, type, sourceLineNumbers);

            if (!String.IsNullOrEmpty(filePath))
            {
                return(filePath);
            }

            // Assign the correct bind path to file manager
            currentSourcePaths    = this.sourcePaths[bindStage];
            currentNamedBindPaths = this.namedBindPaths[bindStage];
            if (BindStage.Target != bindStage && BindStage.Updated != bindStage)
            {
                currentBindPaths = this.bindPaths[bindStage];
            }
            else
            {
                currentBindPaths = this.sourcePaths[bindStage];
            }

            // If the path is rooted, it better exist or we're not going to find it.
            if (Path.IsPathRooted(source))
            {
                if (BinderFileManager.CheckFileExists(source))
                {
                    return(source);
                }
            }
            else // not a rooted path so let's try applying all the different source resolution options.
            {
                const string bindPathOpenString = "!(bindpath.";

                if (source.StartsWith(bindPathOpenString, StringComparison.Ordinal) && source.IndexOf(')') != -1)
                {
                    int      bindpathSignatureLength = bindPathOpenString.Length;
                    string   name   = source.Substring(bindpathSignatureLength, source.IndexOf(')') - bindpathSignatureLength);
                    string[] values = currentNamedBindPaths.GetValues(name);

                    if (null != values)
                    {
                        foreach (string bindPath in values)
                        {
                            // Parse out '\\' chars that separate the "bindpath" variable and the next part of the path,
                            // because Path.Combine() thinks that rooted second paths don't need the first path.
                            string nameSection = string.Empty;
                            int    nameStart   = bindpathSignatureLength + 1 + name.Length; // +1 for the closing bracket.

                            nameSection = source.Substring(nameStart).TrimStart('\\');
                            filePath    = Path.Combine(bindPath, nameSection);

                            if (BinderFileManager.CheckFileExists(filePath))
                            {
                                return(filePath);
                            }
                        }
                    }
                }
                else if (source.StartsWith("SourceDir\\", StringComparison.Ordinal) || source.StartsWith("SourceDir/", StringComparison.Ordinal))
                {
                    foreach (string bindPath in currentBindPaths)
                    {
                        filePath = Path.Combine(bindPath, source.Substring(10));
                        if (BinderFileManager.CheckFileExists(filePath))
                        {
                            return(filePath);
                        }
                    }
                }
                else if (BinderFileManager.CheckFileExists(source))
                {
                    return(source);
                }

                foreach (string path in currentSourcePaths)
                {
                    filePath = Path.Combine(path, source);
                    if (BinderFileManager.CheckFileExists(filePath))
                    {
                        return(filePath);
                    }

                    if (source.StartsWith("SourceDir\\", StringComparison.Ordinal) || source.StartsWith("SourceDir/", StringComparison.Ordinal))
                    {
                        filePath = Path.Combine(path, source.Substring(10));
                        if (BinderFileManager.CheckFileExists(filePath))
                        {
                            return(filePath);
                        }
                    }
                }
            }

            // Didn't find the file.
            throw new WixFileNotFoundException(sourceLineNumbers, source, type);
        }
 public ContainerInfo(string id, string name, string type, string downloadUrl, BinderFileManager fileManager)
 {
     this.Id          = id;
     this.Name        = name;
     this.Type        = type;
     this.DownloadUrl = downloadUrl;
     this.FileManager = fileManager;
     this.TempPath    = Path.Combine(fileManager.TempFilesLocation, name);
     this.FileInfo    = new FileInfo(this.TempPath);
 }
 public ContainerInfo(Row row, BinderFileManager fileManager)
     : this((string)row[0], (string)row[1], (string)row[2], (string)row[3], fileManager)
 {
     this.SourceLineNumbers = row.SourceLineNumbers;
 }
Exemple #10
0
        /// <summary>
        /// Saves a library to a path on disk.
        /// </summary>
        /// <param name="path">Path to save library file to on disk.</param>
        /// <param name="binderFileManager">If provided, the binder file manager is used to bind files into the library.</param>
        /// <param name="wixVariableResolver">The Wix variable resolver.</param>
        public void Save(string path, BinderFileManager binderFileManager, WixVariableResolver wixVariableResolver)
        {
            FileMode         fileMode = FileMode.Create;
            StringCollection fileIds  = new StringCollection();
            StringCollection files    = new StringCollection();
            int index = 0;

            // resolve paths to files and create the library cabinet file
            foreach (Section section in this.sections)
            {
                foreach (Table table in section.Tables)
                {
                    foreach (Row row in table.Rows)
                    {
                        foreach (Field field in row.Fields)
                        {
                            ObjectField objectField = field as ObjectField;

                            if (null != objectField)
                            {
                                if (null != binderFileManager && null != objectField.Data)
                                {
                                    string cabinetFileId = (index++).ToString(CultureInfo.InvariantCulture);

                                    // resolve wix variables
                                    string resolvedValue = wixVariableResolver.ResolveVariables(row.SourceLineNumbers, (string)objectField.Data, false);
                                    files.Add(binderFileManager.ResolveFile(resolvedValue, table.Name, row.SourceLineNumbers, BindStage.Normal));

                                    // File was successfully resolved so track this cabient file id.
                                    objectField.CabinetFileId = cabinetFileId;
                                    fileIds.Add(cabinetFileId);
                                }
                                else // clear out a previous cabinet file id value
                                {
                                    objectField.CabinetFileId = null;
                                }
                            }
                        }
                    }
                }
            }

            // do not save the library if errors were found while resolving object paths
            if (wixVariableResolver.EncounteredError)
            {
                return;
            }

            // create the cabinet file
            if (0 < fileIds.Count)
            {
                using (WixCreateCab cab = new WixCreateCab(Path.GetFileName(path), Path.GetDirectoryName(path), fileIds.Count, 0, 0, CompressionLevel.Mszip))
                {
                    for (int i = 0; i < fileIds.Count; i++)
                    {
                        cab.AddFile(files[i], fileIds[i]);
                    }
                    cab.Complete();
                }

                // append the library xml to the end of the newly created cabinet file
                fileMode = FileMode.Append;
            }

            // Assure the location to output the lib exists
            Directory.CreateDirectory(Path.GetDirectoryName(Path.GetFullPath(path)));

            // save the xml
            using (FileStream fs = new FileStream(path, fileMode))
            {
                XmlWriter writer = null;

                try
                {
                    writer = new XmlTextWriter(fs, System.Text.Encoding.UTF8);

                    writer.WriteStartDocument();
                    this.Persist(writer);
                    writer.WriteEndDocument();
                }
                finally
                {
                    if (null != writer)
                    {
                        writer.Close();
                    }
                }
            }
        }
Exemple #11
0
        /// <summary>
        /// Resolves paths to files.
        /// </summary>
        /// <param name="sectionTables">TableCollection of tables to process</param>
        /// <param name="binderFileManager">If provided, the binder file manager is used to bind files into the output.</param>
        /// <param name="wixVariableResolver">The Wix variable resolver.</param>
        /// <param name="tempFilesLocation">Location for temporary files.</param>
        /// <param name="cabinets">Hash of source cabinets.</param>
        /// <param name="fileIds">Collection of CabinetFileIds.</param>
        /// <param name="files">Collection of file paths from compressed files.</param>
        /// <param name="index">CabinetFileId generator.</param>
        private static void ResolveSectionFiles(TableCollection sectionTables, BinderFileManager binderFileManager, WixVariableResolver wixVariableResolver, string tempFilesLocation, Hashtable cabinets, StringCollection fileIds, StringCollection files, ref int index)
        {
            foreach (Table table in sectionTables)
            {
                foreach (Row row in table.Rows)
                {
                    foreach (Field field in row.Fields)
                    {
                        ObjectField objectField = field as ObjectField;

                        if (null != objectField && null != objectField.Data)
                        {
                            string file              = null;
                            string previousFile      = null;
                            bool   isDefault         = true;
                            bool   isPreviousDefault = true;

                            // resolve localization and wix variables if there is a file manager that would use the value
                            // if it was different, otherwise we just don't care so skip the whole variable resolution thing.
                            if (null != wixVariableResolver && null != binderFileManager)
                            {
                                objectField.Data = wixVariableResolver.ResolveVariables(row.SourceLineNumbers, (string)objectField.Data, false, ref isDefault);

                                if (null != objectField.PreviousData)
                                {
                                    objectField.PreviousData = wixVariableResolver.ResolveVariables(row.SourceLineNumbers, objectField.PreviousData, false, ref isPreviousDefault);
                                }

                                // do not save the output if errors were found while resolving object paths
                                if (Messaging.Instance.EncounteredError)
                                {
                                    return;
                                }
                            }

                            // file is compressed in a cabinet (and not modified above)
                            if (null != objectField.CabinetFileId && isDefault)
                            {
                                // index cabinets that have not been previously encountered
                                if (!cabinets.ContainsKey(objectField.BaseUri))
                                {
                                    Uri    baseUri = new Uri(objectField.BaseUri);
                                    string localFileNameWithoutExtension = Path.GetFileNameWithoutExtension(baseUri.LocalPath);
                                    string extractedDirectoryName        = String.Format(CultureInfo.InvariantCulture, "cab_{0}_{1}", cabinets.Count, localFileNameWithoutExtension);

                                    // index the cabinet file's base URI (source location) and extracted directory
                                    cabinets.Add(objectField.BaseUri, Path.Combine(tempFilesLocation, extractedDirectoryName));
                                }

                                // set the path to the file once its extracted from the cabinet
                                file = Path.Combine((string)cabinets[objectField.BaseUri], objectField.CabinetFileId);
                            }
                            else if (null != binderFileManager)
                            {
                                file = binderFileManager.ResolveFile((string)objectField.Data, table.Name, row.SourceLineNumbers, BindStage.Normal);
                            }

                            // add the file to the list of files to go in the cabinet
                            if (null != file)
                            {
                                string cabinetFileId = (index++).ToString(CultureInfo.InvariantCulture);

                                objectField.CabinetFileId = cabinetFileId;
                                fileIds.Add(cabinetFileId);

                                files.Add(file);
                            }

                            // previous file is compressed in a cabinet (and not modified above)
                            if (null != objectField.PreviousCabinetFileId && isPreviousDefault)
                            {
                                // index cabinets that have not been previously encountered
                                if (!cabinets.ContainsKey(objectField.PreviousBaseUri))
                                {
                                    Uri    baseUri = new Uri(objectField.PreviousBaseUri);
                                    string localFileNameWithoutExtension = Path.GetFileNameWithoutExtension(baseUri.LocalPath);
                                    string extractedDirectoryName        = String.Format(CultureInfo.InvariantCulture, "cab_{0}_{1}", cabinets.Count, localFileNameWithoutExtension);

                                    // index the cabinet file's base URI (source location) and extracted directory
                                    cabinets.Add(objectField.PreviousBaseUri, Path.Combine(tempFilesLocation, extractedDirectoryName));
                                }

                                // set the path to the file once its extracted from the cabinet
                                previousFile = Path.Combine((string)cabinets[objectField.PreviousBaseUri], objectField.PreviousCabinetFileId);
                            }
                            else if (null != objectField.PreviousData && null != binderFileManager)
                            {
                                previousFile = binderFileManager.ResolveFile((string)objectField.PreviousData, table.Name, row.SourceLineNumbers, BindStage.Normal);
                            }

                            // add the file to the list of files to go in the cabinet
                            if (null != previousFile)
                            {
                                string cabinetFileId = (index++).ToString(CultureInfo.InvariantCulture);

                                objectField.PreviousCabinetFileId = cabinetFileId;
                                fileIds.Add(cabinetFileId);

                                files.Add(previousFile);
                            }
                        }
                    }
                }
            }
        }
Exemple #12
0
        /// <summary>
        /// Saves an outputs cab to a path on disk.
        /// </summary>
        /// <param name="path">Path to save outputs cab to on disk.</param>
        /// <param name="binderFileManager">If provided, the binder file manager is used to bind files into the outputs cab.</param>
        /// <param name="wixVariableResolver">The Wix variable resolver.</param>
        /// <param name="tempFilesLocation">Location for temporary files.</param>
        /// <returns>Returns true if a cabinet existed or was created, false otherwise.</returns>
        public bool SaveCab(string path, BinderFileManager binderFileManager, WixVariableResolver wixVariableResolver, string tempFilesLocation)
        {
            bool hasCab = false;

            // Check if there was a cab on the wixout when it was created
            if (null != this.cabPath)
            {
                // There was already a cab on the wixout when it was loaded. Reuse that one.
                File.Copy(this.cabPath, path, true);
                if (null != this.tempFileCollection)
                {
                    this.tempFileCollection.Delete();
                }
                hasCab = true;
            }
            else
            {
                int              index    = 0;
                Hashtable        cabinets = new Hashtable();
                StringCollection fileIds  = new StringCollection();
                StringCollection files    = new StringCollection();

                if (null != tempFilesLocation)
                {
                    // resolve paths to files and create the output cabinet file
                    if (0 == this.sections.Count)
                    {
                        Output.ResolveSectionFiles(this.tables, binderFileManager, wixVariableResolver, tempFilesLocation, cabinets, fileIds, files, ref index);
                    }
                    else
                    {
                        foreach (Section section in this.sections)
                        {
                            Output.ResolveSectionFiles(section.Tables, binderFileManager, wixVariableResolver, tempFilesLocation, cabinets, fileIds, files, ref index);
                        }
                    }
                }

                // extract files that come from cabinet files
                if (0 < cabinets.Count)
                {
                    // ensure the temporary directory exists
                    Directory.CreateDirectory(tempFilesLocation);

                    foreach (DictionaryEntry cabinet in cabinets)
                    {
                        Uri    baseUri = new Uri((string)cabinet.Key);
                        string localPath;

                        if ("embeddedresource" == baseUri.Scheme)
                        {
                            int    bytesRead;
                            byte[] buffer = new byte[512];

                            string   originalLocalPath = Path.GetFullPath(baseUri.LocalPath.Substring(1));
                            string   resourceName      = baseUri.Fragment.Substring(1);
                            Assembly assembly          = Assembly.LoadFile(originalLocalPath);

                            localPath = String.Concat(cabinet.Value, ".cab");

                            using (FileStream fs = File.OpenWrite(localPath))
                            {
                                using (Stream resourceStream = assembly.GetManifestResourceStream(resourceName))
                                {
                                    while (0 < (bytesRead = resourceStream.Read(buffer, 0, buffer.Length)))
                                    {
                                        fs.Write(buffer, 0, bytesRead);
                                    }
                                }
                            }
                        }
                        else // normal file
                        {
                            localPath = baseUri.LocalPath;
                        }

                        // extract the cabinet's files into a temporary directory
                        Directory.CreateDirectory((string)cabinet.Value);

                        using (WixExtractCab extractCab = new WixExtractCab())
                        {
                            extractCab.Extract(localPath, (string)cabinet.Value);
                        }
                    }
                }

                // create the cabinet file
                if (0 < fileIds.Count)
                {
                    using (WixCreateCab cab = new WixCreateCab(Path.GetFileName(path), Path.GetDirectoryName(path), fileIds.Count, 0, 0, CompressionLevel.Mszip))
                    {
                        for (int i = 0; i < fileIds.Count; i++)
                        {
                            cab.AddFile(files[i], fileIds[i]);
                        }
                        cab.Complete();
                    }

                    // append the output xml to the end of the newly created cabinet file
                    hasCab = true;
                }
            }

            return(hasCab);
        }