diff --git a/Visual_Studio_2017/ExcludeFromBuild/Util.cs b/Visual_Studio_2017/ExcludeFromBuild/Util.cs index e2ea406..ac465c9 100644 --- a/Visual_Studio_2017/ExcludeFromBuild/Util.cs +++ b/Visual_Studio_2017/ExcludeFromBuild/Util.cs @@ -6,6 +6,7 @@ using EnvDTE; using EnvDTE80; +using Microsoft.VisualStudio; using System; using System.Collections; using System.Collections.Generic; @@ -43,7 +44,8 @@ public static void SetExcludedFromBuild(DTE2 dte, bool value, Configuration conf var items = dte.ToolWindows.SolutionExplorer.SelectedItems as Array; if (items == null) return; - SetExcludedFromBuildRecursive(items, value, configuration); + HashSet visited = new HashSet(); + SetExcludedFromBuildRecursive(items, value, configuration, visited); } // Casting COM objects to VCFile and VCFilter works but the problem is that @@ -53,7 +55,8 @@ public static void SetExcludedFromBuild(DTE2 dte, bool value, Configuration conf private static void SetExcludedFromBuildRecursive(IEnumerable items, bool value, - Configuration configuration) + Configuration configuration, + HashSet visited) { foreach (var item in items) { @@ -61,6 +64,20 @@ private static void SetExcludedFromBuildRecursive(IEnumerable items, if (hitem == null) continue; + // Get unique name of current item + string name = null; + if (!IsVisitableItem(hitem, out name)) + continue; + + // Ignore already visited items if possible + if (name != null) + { + if (visited.Contains(name)) + continue; + else + visited.Add(name); + } + // Not expanded UIHierarchyItems report 0 Items if (!hitem.UIHierarchyItems.Expanded) { @@ -71,7 +88,10 @@ private static void SetExcludedFromBuildRecursive(IEnumerable items, // Any container, e.g. filter or C# XAML, etc. if (hitem.UIHierarchyItems.Count > 0) { - SetExcludedFromBuildRecursive(hitem.UIHierarchyItems, value, configuration); + SetExcludedFromBuildRecursive(hitem.UIHierarchyItems, + value, + configuration, + visited); } // For C++ this is Microsoft.VisualStudio.VCProjectEngine.VCProjectItem @@ -163,6 +183,46 @@ private static void SetExcludedFromBuildRecursive(IEnumerable items, } #pragma warning restore VSTHRD010 + private static bool IsVisitableItem(UIHierarchyItem hitem, out string name) + { + name = null; + + var pitem = hitem.Object as ProjectItem; + if (pitem != null) + { + name = GetPropertyValue(pitem, "FullPath") as string; + if (name == null) + { + if (pitem.Kind == VSConstants.ItemTypeGuid.VirtualFolder_string + && pitem.ContainingProject != null) + { + name = pitem.ContainingProject.FullName + "\\" + + GetPropertyValue(pitem, "CanonicalName") as string; + } + } + } + else + { + var proj = hitem.Object as Project; + if (proj != null) + { + name = proj.FullName; + } + else + { + var solution = hitem.Object as Solution; + if (solution != null) + name = solution.FullName; + else + // Could not be casted to neither ProjectItem, Project nor Solution. + // This is probably a reference, so ignore it. + return false; + } + } + + return true; + } + private static void SetPropertyValue(object o, string name, object value) { o.GetType().InvokeMember(name, @@ -196,12 +256,48 @@ private static Property GetProperty(ProjectItem pitem, string name) } } + private static Property GetProperty(Project proj, string name) + { + try + { + return proj.Properties.Item(name); + } + catch (Exception) + { + return null; + } + } + + private static Property GetProperty(Solution solution, string name) + { + try + { + return solution.Properties.Item(name); + } + catch (Exception) + { + return null; + } + } + private static object GetPropertyValue(ProjectItem pitem, string name) { Property prop = GetProperty(pitem, name); return prop != null ? prop.Value : null; } + private static object GetPropertyValue(Project proj, string name) + { + Property prop = GetProperty(proj, name); + return prop != null ? prop.Value : null; + } + + private static object GetPropertyValue(Solution solution, string name) + { + Property prop = GetProperty(solution, name); + return prop != null ? prop.Value : null; + } + private static void SetPropertyValue(ProjectItem pitem, string name, object value) { Property prop = GetProperty(pitem, name); @@ -209,6 +305,44 @@ private static void SetPropertyValue(ProjectItem pitem, string name, object valu prop.Value = value; } + private static void SetPropertyValue(Project proj, string name, object value) + { + Property prop = GetProperty(proj, name); + if (prop != null) + prop.Value = value; + } + + private static void SetPropertyValue(Solution solution, string name, object value) + { + Property prop = GetProperty(solution, name); + if (prop != null) + prop.Value = value; + } + + private static List GetPropertiesNames(ProjectItem pitem) + { + List result = new List(); + foreach (Property p in pitem.Properties) + result.Add(p.Name); + return result; + } + + private static List GetPropertiesNames(Project proj) + { + List result = new List(); + foreach (Property p in proj.Properties) + result.Add(p.Name); + return result; + } + + private static List GetPropertiesNames(Solution solution) + { + List result = new List(); + foreach (Property p in solution.Properties) + result.Add(p.Name); + return result; + } + public static Configuration GetConfigurationOption() { var package = ExcludeFromBuildPackage.Instance; @@ -233,13 +367,5 @@ private static string RootXMLElementName(string url) return null; } } - - private static List GetPropertiesNames(ProjectItem pitem) - { - List result = new List(); - foreach (Property p in pitem.Properties) - result.Add(p.Name); - return result; - } } }