Beispiel #1
0
        internal static void HandleEmptyDirectories(XDocument doc)
        {
            XElement product = doc.Root.Select("Product");

            var dummyDirs = product.Descendants("Directory")
                            .SelectMany(x => x.Elements("Component"))
                            .Where(e => e.HasAttribute("Id", v => v.EndsWith(".EmptyDirectory")))
                            .Select(x => x.Parent("Directory"));

            if (SupportEmptyDirectories == CompilerSupportState.Automatic)
            {
                SupportEmptyDirectories = dummyDirs.Any() ? CompilerSupportState.Enabled : CompilerSupportState.Disabled; //it wasn't set by user so set it if any empty dir is detected
                Compiler.OutputWriteLine("Wix# support for EmptyDirectories is automatically " + SupportEmptyDirectories.ToString().ToLower());
            }

            if (SupportEmptyDirectories == CompilerSupportState.Enabled)
            {
                if (dummyDirs.Any())
                {
                    foreach (var item in dummyDirs)
                    {
                        XElement parent = item.Parent("Directory");
                        while (parent != null)
                        {
                            if (parent.Element("Component") == null)
                            {
                                var dirId = parent.Attribute("Id").Value;
                                if (Compiler.EnvironmentConstantsMapping.ContainsValue(dirId))
                                {
                                    break; //stop when reached start of user defined subdirs chain: TARGETDIR/ProgramFilesFolder!!!/ProgramFilesFolder.Company/INSTALLDIR
                                }
                                //just folder with nothing in it but not the last leaf
                                doc.CrteateComponentFor(parent);
                            }
                            parent = parent.Parent("Directory");
                        }
                    }
                }

                foreach (XElement xDir in product.Descendants("Directory").ToArray())
                {
                    var dirComponents = xDir.Elements("Component");

                    if (dirComponents.Any())
                    {
                        var componentsWithNoFiles = dirComponents.Where(x => !x.ContainsFiles()).ToArray();

                        //'EMPTY DIRECTORY' support processing section
                        foreach (XElement item in componentsWithNoFiles)
                        {
                            // Ridiculous MSI constrains:
                            //  * you cannot install install empty folders
                            //    - workaround is to insert empty component with CreateFolder element
                            //  * if Component+CreateFolder element is inserted the folder will not be removed on uninstall
                            //    - workaround is to insert RemoveFolder element in to empty component as well
                            //  * if Component+CreateFolder+RemoveFolder elements are placed in a dummy component to handle an empty folder
                            //    any parent folder with no files/components will not be removed on uninstall.
                            //    - workaround is to insert Component+Create+RemoveFolder elements in any parent folder with no files.
                            //
                            // OMG!!!! If it is not over-engineering I don't know what is.

                            bool oldAlgorithm = false;

                            if (!oldAlgorithm)
                            {
                                //current approach
                                InsertCreateFolder(item);
                                if (!xDir.ContainsAnyRemoveFolder())
                                {
                                    InsertRemoveFolder(xDir, item, "uninstall");
                                }
                            }
                            else
                            {
                                //old approach
                                if (!item.Attribute("Id").Value.EndsWith(".EmptyDirectory"))
                                {
                                    InsertCreateFolder(item);
                                }
                                else if (!xDir.ContainsAnyRemoveFolder())
                                {
                                    InsertRemoveFolder(xDir, item, "uninstall"); //to keep WiX/compiler happy and allow removal of the dummy directory
                                }
                            }
                        }
                    }
                }
            }
        }
Beispiel #2
0
        internal static void InjectAutoElementsHandler(XDocument doc, Project project)
        {
            ExpandCustomAttributes(doc, project);
            InjectShortcutIcons(doc);
            HandleEmptyDirectories(doc);
            InjectPlatformAttributes(doc);

            XElement product = doc.Root.Select("Product");

            int?absPathCount = null;

            foreach (XElement dir in product.Element("Directory").Elements("Directory"))
            {
                XElement installDir = dir;

                XAttribute installDirName = installDir.Attribute("Name");
                if (IO.Path.IsPathRooted(installDirName.Value))
                {
                    string absolutePath = installDirName.Value;

                    if (dir == product.Element("Directory").Elements("Directory").First()) //only for the first root dir
                    {
                        //ManagedUI will need some hint on the install dir as it cannot rely on the session action (e.g. Set_INSTALLDIR_AbsolutePath)
                        //because it is running outside of the sequence and analyses the tables directly for the INSTALLDIR
                        product.AddElement("Property", "Id=INSTALLDIR_ABSOLUTEPATH; Value=" + absolutePath);
                    }

                    installDirName.Value = $"ABSOLUTEPATH{absPathCount}";

                    //<SetProperty> for INSTALLDIR is an attractive approach but it doesn't allow conditional setting of 'ui' and 'execute' as required depending on UI level
                    // it is ether hard-coded 'both' or hard coded-both 'ui' or 'execute'
                    // <SetProperty Id="INSTALLDIR" Value="C:\My Company\MyProduct" Sequence="both" Before="AppSearch">

                    string actualDirName = installDir.Attribute("Id").Value;
                    string customAction  = $"Set_DirAbsolutePath{absPathCount}";

                    product.Add(new XElement("CustomAction",
                                             new XAttribute("Id", customAction),
                                             new XAttribute("Property", actualDirName),
                                             new XAttribute("Value", absolutePath)));

                    product.SelectOrCreate("InstallExecuteSequence").Add(
                        new XElement("Custom", $"(NOT Installed) AND (UILevel < 5) AND ({actualDirName} = ABSOLUTEPATH{absPathCount})",
                                     new XAttribute("Action", customAction),
                                     new XAttribute("Before", "AppSearch")));

                    product.SelectOrCreate("InstallUISequence").Add(
                        new XElement("Custom", $"(NOT Installed) AND (UILevel = 5) AND ({actualDirName} = ABSOLUTEPATH{absPathCount})",
                                     new XAttribute("Action", customAction),
                                     new XAttribute("Before", "AppSearch")));

                    if (absPathCount == null)
                    {
                        absPathCount = 0;
                    }
                    absPathCount++;
                }
            }

            foreach (XElement xDir in product.Descendants("Directory").ToArray())
            {
                var dirComponents = xDir.Elements("Component");

                if (dirComponents.Any())
                {
                    var componentsWithNoFilesOrRegestry = dirComponents.Where(x => !x.ContainsFilesOrRegistries()).ToArray();

                    foreach (XElement item in componentsWithNoFilesOrRegestry)
                    {
                        //if (!item.Attribute("Id").Value.EndsWith(".EmptyDirectory"))
                        EnsureKeyPath(item);

                        if (!xDir.ContainsAnyRemoveFolder())
                        {
                            InsertRemoveFolder(xDir, item, "uninstall"); //to keep WiX/compiler happy and allow removal of the dummy directory
                        }
                    }
                }

                foreach (XElement xComp in dirComponents)
                {
                    if (xDir.InUserProfile())
                    {
                        if (!xDir.ContainsAnyRemoveFolder())
                        {
                            InsertRemoveFolder(xDir, xComp);
                        }

                        if (!xComp.ContainsDummyUserProfileRegistry())
                        {
                            InsertDummyUserProfileRegistry(xComp);
                        }
                    }
                    else
                    {
                        if (xComp.ContainsNonAdvertisedShortcuts())
                        {
                            if (!xComp.ContainsDummyUserProfileRegistry())
                            {
                                InsertDummyUserProfileRegistry(xComp);
                            }
                        }
                    }

                    foreach (XElement xFile in xComp.Elements("File"))
                    {
                        if (xFile.ContainsAdvertisedShortcuts() && !xComp.ContainsDummyUserProfileRegistry())
                        {
                            SetFileKeyPath(xFile);
                        }
                    }
                }

                if (!xDir.ContainsComponents() && xDir.InUserProfile())
                {
                    if (!xDir.IsUserProfileRoot())
                    {
                        XElement xComp1 = doc.CrteateComponentFor(xDir);
                        if (!xDir.ContainsAnyRemoveFolder())
                        {
                            InsertRemoveFolder(xDir, xComp1);
                        }

                        if (!xComp1.ContainsDummyUserProfileRegistry())
                        {
                            InsertDummyUserProfileRegistry(xComp1);
                        }
                    }
                }
            }

            //Not a property Id as MSI requires
            Predicate <string> needsProperty =
                value => value.Contains("\\") ||
                value.Contains("//") ||
                value.Contains("%") ||
                value.Contains("[") ||
                value.Contains("]");

            foreach (XElement xShortcut in product.Descendants("Shortcut"))
            {
                if (xShortcut.HasAttribute("WorkingDirectory", x => needsProperty(x)))
                {
                    string workingDirectory = xShortcut.Attribute("WorkingDirectory").Value;

                    if (workingDirectory.StartsWith("%") && workingDirectory.EndsWith("%")) //%INSTALLDIR%
                    {
                        workingDirectory = workingDirectory.ExpandWixEnvConsts();
                        xShortcut.SetAttributeValue("WorkingDirectory", workingDirectory.Replace("%", ""));
                    }
                    else if (workingDirectory.StartsWith("[") && workingDirectory.EndsWith("]")) //[INSTALLDIR]
                    {
                        xShortcut.SetAttributeValue("WorkingDirectory", workingDirectory.Replace("[", "").Replace("]", ""));
                    }
                    else
                    {
                        string   workinDirPath    = workingDirectory.ReplaceWixSharpEnvConsts();
                        XElement existingProperty = product.Descendants("Property")
                                                    .Where(p => p.HasAttribute("Value", workingDirectory))
                                                    .FirstOrDefault();
                        if (existingProperty != null)
                        {
                            xShortcut.SetAttributeValue("WorkingDirectory", existingProperty.Attribute("Id").Value);
                        }
                        else
                        {
                            string propId = xShortcut.Attribute("Id").Value + ".WorkDir";
                            product.AddElement("Property", "Id=" + propId + "; Value=" + workinDirPath);
                            xShortcut.SetAttributeValue("WorkingDirectory", propId);
                        }
                    }
                }
            }
        }
Beispiel #3
0
        internal static void InjectAutoElementsHandler(XDocument doc)
        {
            InjectPlatformAttributes(doc);
            ExpandCustomAttributes(doc);
            InjectShortcutIcons(doc);

            XElement installDir = doc.Root.Select("Product").Element("Directory").Element("Directory");

            XAttribute installDirName = installDir.Attribute("Name");

            if (IO.Path.IsPathRooted(installDirName.Value))
            {
                var    product      = installDir.Parent("Product");
                string absolutePath = installDirName.Value;

                installDirName.Value = "ABSOLUTEPATH";

                //<SetProperty> is an attractive approach but it doesn't allow conditional setting of 'ui' and 'execute' as required depending on UI level
                // it is ether hard coded 'both' or hard coded both 'ui' or 'execute'
                // <SetProperty Id="INSTALLDIR" Value="C:\My Company\MyProduct" Sequence="both" Before="AppSearch">

                product.Add(new XElement("CustomAction",
                                         new XAttribute("Id", "Set_INSTALLDIR_AbsolutePath"),
                                         new XAttribute("Property", installDir.Attribute("Id").Value),
                                         new XAttribute("Value", absolutePath)));

                product.SelectOrCreate("InstallExecuteSequence").Add(
                    new XElement("Custom", "(NOT Installed) AND (UILevel < 5)",
                                 new XAttribute("Action", "Set_INSTALLDIR_AbsolutePath"),
                                 new XAttribute("Before", "CostFinalize")));

                product.SelectOrCreate("InstallUISequence").Add(
                    new XElement("Custom", "(NOT Installed) AND (UILevel = 5)",
                                 new XAttribute("Action", "Set_INSTALLDIR_AbsolutePath"),
                                 new XAttribute("Before", "CostFinalize")));
            }

            foreach (XElement xDir in doc.Root.Descendants("Directory").ToArray())
            {
                var dirComponents = xDir.Elements("Component");

                if (dirComponents.Any())
                {
                    var componentsWithNoFiles = dirComponents.Where(x => !x.ContainsFiles()).ToArray();

                    foreach (XElement item in componentsWithNoFiles)
                    {
                        if (!item.Attribute("Id").Value.EndsWith(".EmptyDirectory"))
                        {
                            InsertCreateFolder(item);
                        }
                        else if (!xDir.ContainsAnyRemoveFolder())
                        {
                            InsertRemoveFolder(xDir, item, "both"); //to keep WiX/compiler happy and allow removal of the dummy directory
                        }
                    }
                }

                foreach (XElement xComp in dirComponents)
                {
                    if (xDir.InUserProfile())
                    {
                        if (!xDir.ContainsAnyRemoveFolder())
                        {
                            InsertRemoveFolder(xDir, xComp);
                        }

                        if (!xComp.ContainsDummyUserProfileRegistry())
                        {
                            InsertDummyUserProfileRegistry(xComp);
                        }
                    }
                    else
                    {
                        if (xComp.ContainsNonAdvertisedShortcuts())
                        {
                            if (!xComp.ContainsDummyUserProfileRegistry())
                            {
                                InsertDummyUserProfileRegistry(xComp);
                            }
                        }
                    }

                    foreach (XElement xFile in xComp.Elements("File"))
                    {
                        if (xFile.ContainsAdvertisedShortcuts() && !xComp.ContainsDummyUserProfileRegistry())
                        {
                            SetFileKeyPath(xFile);
                        }
                    }
                }

                if (!xDir.ContainsComponents() && xDir.InUserProfile())
                {
                    if (!xDir.IsUserProfileRoot())
                    {
                        XElement xComp1 = doc.CrteateComponentFor(xDir);
                        if (!xDir.ContainsAnyRemoveFolder())
                        {
                            InsertRemoveFolder(xDir, xComp1);
                        }

                        if (!xComp1.ContainsDummyUserProfileRegistry())
                        {
                            InsertDummyUserProfileRegistry(xComp1);
                        }
                    }
                }
            }
        }