【问题标题】:How do I list all the projects in the current solution using EnvDTE?如何使用 EnvDTE 列出当前解决方案中的所有项目?
【发布时间】:2016-08-24 14:25:22
【问题描述】:

我一直在关注MSDN's Hello World guide to developing Visual Studio extensions(本文专门讨论如何将其创建为 Visual Studio 工具栏命令)。

我正在尝试列出当前/活动解决方案中包含的所有项目。

在命令模板的自动生成代码中。

我尝试过EnvDTE 的Solution 的Projects 属性,但它显示的项目为零。

还有一个ActiveSolutionProjects 属性,但它也显示一个空数组。

这是如何实现的?

P.S.:我尝试了 DTE 和 DTE2 接口,因为从文档中理解要使用哪个版本令人困惑。我得到了 DTE2 的空服务,所以我选择 DTE。

我的解决方案资源管理器如下所示:


更新:来自 gitter/extendvs 的 Bert Huijben 提出了以下建议,可在 VSSDK Extensibility Samples 找到 - 但这也不起作用(在构造函数和回调函数中返回 0 个元素功能):

private Hashtable GetLoadedControllableProjectsEnum()
{
    Hashtable mapHierarchies = new Hashtable();

    IVsSolution sol = (IVsSolution)this.ServiceProvider.GetService(typeof(SVsSolution));
    Guid rguidEnumOnlyThisType = new Guid();
    IEnumHierarchies ppenum = null;
    ErrorHandler.ThrowOnFailure(sol.GetProjectEnum((uint)__VSENUMPROJFLAGS.EPF_LOADEDINSOLUTION, ref rguidEnumOnlyThisType, out ppenum));

    IVsHierarchy[] rgelt = new IVsHierarchy[1];
    uint pceltFetched = 0;
    while (ppenum.Next(1, rgelt, out pceltFetched) == VSConstants.S_OK &&
           pceltFetched == 1)
    {
        IVsSccProject2 sccProject2 = rgelt[0] as IVsSccProject2;
        if (sccProject2 != null)
        {
            mapHierarchies[rgelt[0]] = true;
        }
    }

    return mapHierarchies;
}

【问题讨论】:

  • 快速浏览一下代码,应该可以工作。 this.ServiceProvider 得到了什么?如果您改为使用 GetService(typeof(SDTE)),但保持演员表不变,会有什么变化吗?
  • 在 Command1 构造函数中,可能尚未加载解决方案。尝试将您的代码移动到 MenuItemCallback。
  • @JasonMalinowski:它得到一个System.__ComObject,其中包含一个Solution 属性,而该属性又包含一个Projects 属性。我按照你的建议尝试了 SDTE,但是 SDTE 是一个空接口,所以没有多大帮助。
  • @SergeyVlasov:也尝试将代码移到那里,同样的事情。
  • @Veverke:我的意思是您在 GetService 参数中使用了 SDTE,但您仍然将其转换为常规接口。没有规定两者必须相同。

标签: c# visual-studio-extensions vsix envdte


【解决方案1】:

获取 EnvDTE.Project 对象:

    static private void FindProjectsIn(EnvDTE.ProjectItem item, List<EnvDTE.Project> results)
    {
        if (item.Object is EnvDTE.Project)
        {
            var proj = (EnvDTE.Project)item.Object;
            if (new Guid(proj.Kind) != Utilities.ProjectTypeGuids.Folder)
            {
                results.Add((EnvDTE.Project)item.Object);
            }
            else
            {
                foreach (EnvDTE.ProjectItem innerItem in proj.ProjectItems)
                {
                    FindProjectsIn(innerItem, results);
                }
            }
        }
        if (item.ProjectItems != null)
        {
            foreach (EnvDTE.ProjectItem innerItem in item.ProjectItems)
            {
                FindProjectsIn(innerItem, results);
            }
        }
    }

    static private void FindProjectsIn(EnvDTE.UIHierarchyItem item, List<EnvDTE.Project> results)
    {
        if (item.Object is EnvDTE.Project)
        {
            var proj = (EnvDTE.Project)item.Object;
            if (new Guid(proj.Kind) != Utilities.ProjectTypeGuids.Folder)
            {
                results.Add((EnvDTE.Project)item.Object);
            }
            else
            {
                foreach (EnvDTE.ProjectItem innerItem in proj.ProjectItems)
                {
                    FindProjectsIn(innerItem, results);
                }
            }
        }
        foreach (EnvDTE.UIHierarchyItem innerItem in item.UIHierarchyItems)
        {
            FindProjectsIn(innerItem, results);
        }
    }

    static internal IEnumerable<EnvDTE.Project> GetEnvDTEProjectsInSolution()
    {
        List<EnvDTE.Project> ret = new List<EnvDTE.Project>();
        EnvDTE80.DTE2 dte = (EnvDTE80.DTE2)ServiceProvider.GlobalProvider.GetService(typeof(EnvDTE.DTE));
        EnvDTE.UIHierarchy hierarchy = dte.ToolWindows.SolutionExplorer;
        foreach (EnvDTE.UIHierarchyItem innerItem in hierarchy.UIHierarchyItems)
        {
            FindProjectsIn(innerItem, ret);
        }
        return ret;
    }

特别是递归是挖掘解决方案文件夹所必需的。

如果您只需要文件路径,您可以不使用 DTE 执行此操作:

    static internal string[] GetProjectFilesInSolution()
    {
        IVsSolution sol = ServiceProvider.GlobalProvider.GetService(typeof(SVsSolution)) as IVsSolution;
        uint numProjects;
        ErrorHandler.ThrowOnFailure(sol.GetProjectFilesInSolution((uint)__VSGETPROJFILESFLAGS.GPFF_SKIPUNLOADEDPROJECTS, 0, null, out numProjects));
        string[] projects = new string[numProjects];
        ErrorHandler.ThrowOnFailure(sol.GetProjectFilesInSolution((uint)__VSGETPROJFILESFLAGS.GPFF_SKIPUNLOADEDPROJECTS, numProjects, projects, out numProjects));
        //GetProjectFilesInSolution also returns solution folders, so we want to do some filtering
        //things that don't exist on disk certainly can't be project files
        return projects.Where(p => !string.IsNullOrEmpty(p) && System.IO.File.Exists(p)).ToArray();
    }

【讨论】:

  • 这个递归解决方案非常好,有两个问题:1.它会在卸载的文件夹上崩溃(需要在每个var proj = (EnvDTE.Project)item.Object;之后检查null)和2.它会递归两次以获得解决方案文件夹,列表中包含重复项目,因此您需要在 foreach (EnvDTE.UIHierarchyItem innerItem in item.UIHierarchyItems) 和 if (item.ProjectItems != null) 之前添加一个 else
  • 'sol' 总是返回 null。
  • 不适用于复杂的解决方案层次结构。我将发布我自己的解决方案。
【解决方案2】:

很遗憾在这里找不到任何可行的解决方案,决定发布我自己的解决方案:

/// <summary>
/// Queries for all projects in solution, recursively (without recursion)
/// </summary>
/// <param name="sln">Solution</param>
/// <returns>List of projects</returns>
static List<Project> GetProjects(Solution sln)
{
    List<Project> list = new List<Project>();
    list.AddRange(sln.Projects.Cast<Project>());

    for (int i = 0; i < list.Count; i++)
        // OfType will ignore null's.
        list.AddRange(list[i].ProjectItems.Cast<ProjectItem>().Select(x => x.SubProject).OfType<Project>());

    return list;
}

如果你不知道要添加什么引用/命名空间,你可以从这里选择带有源代码的项目:

https://github.com/tapika/cppscriptcore/blob/2a73f45474c8b2179774fd4715b8d8e80080f3ae/Tools/vsStart/Program.cs#L478

并检查命名空间/引用。

【讨论】:

    【解决方案3】:

    为我工作:

    在您的包中为 dte 添加一个字段。 获取 DTE 服务。 参考解决方案。

    using EnvDTE;
    using EnvDTE80;
    

    在你的构造函数中:

    dte = this.ServiceProvider.GetService(typeof(EnvDTE.DTE)) as EnvDTE80.DTE2;
    

    在您的命令处理程序中:

    Integer count = ((EnvDTE.SolutionClass)dte.Solution).Projects.Count;
    

    我从中得到了正确的计数。

    截图(要求)

    代码

    //------------------------------------------------------------------------------
    // <copyright file="Command1.cs" company="Company">
    //     Copyright (c) Company.  All rights reserved.
    // </copyright>
    //------------------------------------------------------------------------------
    
    using System;
    using System.ComponentModel.Design;
    using System.Globalization;
    using Microsoft.VisualStudio.Shell;
    using Microsoft.VisualStudio.Shell.Interop;
    using EnvDTE;
    using EnvDTE80;
    
    namespace SolExpExt
    {
        internal sealed class Command1
        {
            public const int CommandId = 0x0100;
            public static readonly Guid CommandSet = new Guid("beff5a1a-dff5-4f6a-95c8-fd7ea7411a7b");
            private DTE2 dte;
            private readonly Package package;
            private IVsSolution sol;
            private Command1(Package package)
            {
                if (package == null)
                {
                    throw new ArgumentNullException("package");
                }
    
                this.package = package;
    
                OleMenuCommandService commandService = this.ServiceProvider.GetService(typeof(IMenuCommandService)) as OleMenuCommandService;
                if (commandService != null)
                {
                    var menuCommandID = new CommandID(CommandSet, CommandId);
                    var menuItem = new MenuCommand(this.MenuItemCallback, menuCommandID);
                    commandService.AddCommand(menuItem);
                }
    
                dte = this.ServiceProvider.GetService(typeof(EnvDTE.DTE)) as EnvDTE80.DTE2;
            }
    
            public static Command1 Instance
            {
                get;
                private set;
            }
    
            private IServiceProvider ServiceProvider
            {
                get
                {
                    return this.package;
                }
            }
    
            public static void Initialize(Package package)
            {
                Instance = new Command1(package);
            }
    
            private void MenuItemCallback(object sender, EventArgs e)
            {
                string message = $"There are {dte.Solution.Projects.Count} projects in this solution.";
                string title = "Command1";
    
                VsShellUtilities.ShowMessageBox(
                    this.ServiceProvider,
                    message,
                    title,
                    OLEMSGICON.OLEMSGICON_INFO,
                    OLEMSGBUTTON.OLEMSGBUTTON_OK,
                    OLEMSGDEFBUTTON.OLEMSGDEFBUTTON_FIRST);
            }
        }
    }
    

    【讨论】:

    • 您能否分享整个 IDE 窗口的屏幕截图,显示解决方案资源管理器和代码?你上面的建议仍然对我不起作用。我仍然得到 0 个项目。所以你得到 1 ?
    • @Veverke 有帮助吗?
    • 您好,感谢您的新反馈,我正在度假,会检查并回复您。
    【解决方案4】:

    以下内容,无耻地取自AutoFindReplace,使用VS2015社区工作:

    using EnvDTE;
    .
    .
    protected override void Initialize()
    {
        base.Initialize();
    
        IServiceContainer serviceContainer = this as IServiceContainer;
        dte = serviceContainer.GetService(typeof(SDTE)) as DTE;
        var solutionEvents = dte.Events.SolutionEvents;
        solutionEvents.Opened += OnSolutionOpened;
    
        var i = dte.Solution.Projects.Count; // Happy days !
    }
    

    上述所有代码行都预先存在于VSPackage.cs 内的solution 中,但“var i = dte.Solution.Projects.Count;”除外我在44 行之后将其本地添加到VSPackage.cs。然后我打开solution,按F5,在实验实例中我打开JoePublic.Sln,嘿,计数是正确的'2' - 宾果!快乐的日子!

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 2011-04-17
      • 1970-01-01
      • 1970-01-01
      • 2017-04-22
      • 1970-01-01
      • 2016-12-08
      • 1970-01-01
      • 1970-01-01
      相关资源
      最近更新 更多