5

I have successfully created the Visual Studio Extension project. It works nice and neat. I made some event for solutions.

The manuals in MSDN and the Internet are brief. And I cannot find an answer to my question: How can I retrieve all metadata related to the class and interfaces (namespaces, class names, base types, etc.) in the solution where this Extension Package is installed?

Roman Melnyk
  • 1,097
  • 1
  • 8
  • 21
alerya
  • 3,125
  • 3
  • 28
  • 50

2 Answers2

4

You can use ITypeDiscoveryService to list all available types in project.

To do this, you should add Microsoft.VisualStudio.Shell.Design reference to project. This way you can use DynamicTypeService to get an instance of ITypeDiscoveryService.

Add this methods to your Package class:

public List<Type> GetAllTypes()
{
    var trs = GetTypeDiscoveryService();
    var types = trs.GetTypes(typeof(object), true /*excludeGlobalTypes*/);
    var result = new List<Type>();
    foreach (Type type in types)
    {
        if (type.IsPublic)
        {
            if (!result.Contains(type))
                result.Add(type);
        }
    }
    return result;
}

private ITypeDiscoveryService GetTypeDiscoveryService()
{
    var dte = GetService<EnvDTE.DTE>();
    var typeService = GetService<DynamicTypeService>();
    var solution = GetService<IVsSolution>();
    IVsHierarchy hier;
    var projects = dte.ActiveSolutionProjects as Array;
    var currentProject = projects.GetValue(0) as Project;
    solution.GetProjectOfUniqueName(currentProject.UniqueName, out hier);
    return typeService.GetTypeDiscoveryService(hier);
}

private T GetService<T>()
{
    return (T)GetService(typeof(T));
}

Then you can use GetAllTypes to get all types of active project:

List<Type> types= GetAllTypes();
Reza Aghaei
  • 120,393
  • 18
  • 203
  • 398
  • very interesting. But last your private T GetService() { return (T)GetService(typeof(T)); } has a error for recursive call – alerya Sep 26 '15 at 07:29
  • Alas. Doesn't work for me. I found another solution I made as a part of ToolWindow. I need these all features not as a part of Package. Your solution will work as a part of the package. – alerya Sep 27 '15 at 07:15
  • (T)GetService(typeof(T)); Works only inside of package class because of protected level. Sure I can write a kind of even aggregator to tie my xaml. But I don't think this is an elegant way of coding. So I am in the searching now. – alerya Sep 27 '15 at 07:43
  • @alerya Unless you are creating an scaffolding extension for `ASP.Net MVC`, Wherever you want to find types, `ITypeDiscoveryService` is the key point and you should use `GetService` method of an `IServiceProvider` to get an instance of `ITypeDiscoveryService`. – Reza Aghaei Sep 27 '15 at 15:38
  • @alerya This answer guides you to do such job. You should notice that you can pass an `IServiceProvider` to your other classes and methods where you need. For example, here the `Package` class is that `IServiceProvider` which could be passed to other methods and classes and there you can use `GetService` method of that `IServiceProvider`. – Reza Aghaei Sep 27 '15 at 15:43
  • Although it may need to adapt to your case, but I think this post, answers the quetion **"Visual Studio Extension get all classes and interfaces metadata"** :) – Reza Aghaei Sep 28 '15 at 10:37
  • I found. But my aim was namespaces only. I will write it right now – alerya Oct 04 '15 at 04:46
  • @RezaAghaei Great solution. I did some tests. Everything worked fine with the .NET Framework. Unfortunately it does not work against .NET Core classes (typeService doesn't find the classes or runs in exception). Do you have an idea or a workaround? – Snowcrack Mar 18 '20 at 15:41
  • @Snowcrack I guess it will not work for .NET Core, because the code is running in .NET run-time and cannot work with .NET Core assemblies. I don't have any idea about the possible solution. If I find time to give it a try I'll notify you :) – Reza Aghaei Mar 19 '20 at 04:04
  • @RezaAghaei Thanks for your answer. I updated my question with the new informations. Feel free to take a look if you have time. [see here: Visual Studio SDK get type modifier information - is type abstract or internal?](https://stackoverflow.com/questions/60737596/visual-studio-sdk-get-type-modifier-information-is-type-abstract-or-internal) – Snowcrack Mar 19 '20 at 07:48
4

This code is about namespace, but you can easily transform it to take everything from CodeModel:

public class Metadata
{

    public List<Namespace> ExtractNamespaces()
    {
        var namespaces = new List<Namespace>();

        var service = (DTE)Package.GetGlobalService(typeof(SDTE));
        var projects = service.Solution.Projects;

        foreach (Project project in projects)
        {
            var projectItems = GetProjectItemsRecursively(project.ProjectItems);
            foreach (ProjectItem item in projectItems.Where(i => i.FileCodeModel != null))
            {
                foreach (CodeElement elem in item.FileCodeModel.CodeElements)
                {
                    namespaces.AddRange(GetNamespacesRecursive(elem).Select(n=>new Namespace(n)));
                }
            }
        }
        return namespaces.Distinct().OrderBy(n=>n.ToString()).ToList();
    }

    private static List<string> GetNamespacesRecursive(CodeElement elem)
    {
        var namespaces = new List<string>();

        if (IsNamespaceable(elem.Kind) && IsEmptyNamespace(elem))
        {
            namespaces.Add(string.Empty);
        }

        if (elem.Kind == vsCMElement.vsCMElementNamespace && !namespaces.Contains(elem.FullName))
        {
            namespaces.Add(elem.FullName);
            if (elem.Children != null)
            {
                foreach (CodeElement codeElement in elem.Children)
                {
                    var ns = GetNamespacesRecursive(codeElement);
                    if (ns.Count > 0)
                        namespaces.AddRange(ns);
                }
            }
        }

        return namespaces;
    }

    private static bool IsEmptyNamespace(CodeElement elem)
    {
        return elem.FullName.IndexOf('.') < 0;
    }

    private static bool IsNamespaceable(vsCMElement kind)
    {
        return (kind == vsCMElement.vsCMElementClass
                || kind == vsCMElement.vsCMElementEnum
                || kind == vsCMElement.vsCMElementInterface
                || kind == vsCMElement.vsCMElementStruct);
    }

    private static List<ProjectItem> GetProjectItemsRecursively(ProjectItems items)
    {
        var ret = new List<EnvDTE.ProjectItem>();
        if (items == null) return ret;
        foreach (ProjectItem item in items)
        {
            ret.Add(item);
            ret.AddRange(GetProjectItemsRecursively(item.ProjectItems));
        }
        return ret;
    }
}
alerya
  • 3,125
  • 3
  • 28
  • 50