Подтвердить что ты не робот

Как получить список проектов в текущем решении Visual Studio?

Когда мы открываем консоль диспетчера пакетов в любом открытом решении, он показывает все проекты этого решения. Как он загружает все проекты одного и того же решения. Когда я попытался с приведенным ниже кодом, он извлекает мне проекты первого решения, которое я открыл.

    private List<Project> GetProjects()
    {
        var dte = (DTE)Marshal.GetActiveObject(string.Format(CultureInfo.InvariantCulture, "VisualStudio.DTE.{0}.0", targetVsVersion));
        var projects = dte.Solution.OfType<Project>().ToList();
        return projects;
    }
4b9b3361

Ответ 1

Вот несколько наборов функций, которые позволяют вам перечислять проекты в данном решении. Вот как вы могли бы использовать его с текущим решением:

// get current solution
IVsSolution solution = (IVsSolution)Microsoft.VisualStudio.Shell.Package.GetGlobalService(typeof(IVsSolution));
foreach(Project project in GetProjects(solution))
{
    ....
}

....

public static IEnumerable<EnvDTE.Project> GetProjects(IVsSolution solution)
{
    foreach (IVsHierarchy hier in GetProjectsInSolution(solution))
    {
        EnvDTE.Project project = GetDTEProject(hier);
        if (project != null)
            yield return project;
    }
}

public static IEnumerable<IVsHierarchy> GetProjectsInSolution(IVsSolution solution)
{
    return GetProjectsInSolution(solution, __VSENUMPROJFLAGS.EPF_LOADEDINSOLUTION);
}

public static IEnumerable<IVsHierarchy> GetProjectsInSolution(IVsSolution solution, __VSENUMPROJFLAGS flags)
{
    if (solution == null)
        yield break;

    IEnumHierarchies enumHierarchies;
    Guid guid = Guid.Empty;
    solution.GetProjectEnum((uint)flags, ref guid, out enumHierarchies);
    if (enumHierarchies == null)
        yield break;

    IVsHierarchy[] hierarchy = new IVsHierarchy[1];
    uint fetched;
    while (enumHierarchies.Next(1, hierarchy, out fetched) == VSConstants.S_OK && fetched == 1)
    {
        if (hierarchy.Length > 0 && hierarchy[0] != null)
            yield return hierarchy[0];
    }
}

public static EnvDTE.Project GetDTEProject(IVsHierarchy hierarchy)
{
    if (hierarchy == null)
        throw new ArgumentNullException("hierarchy");

    object obj;
    hierarchy.GetProperty(VSConstants.VSITEMID_ROOT, (int)__VSHPROPID.VSHPROPID_ExtObject, out obj);
    return obj as EnvDTE.Project;
}

Ответ 2

Может быть, лучше, но я быстро пошел на это и нашел, что это работает (предполагается, что у вас есть способ узнать имя решения). Согласно этот пост, GetActiveObject не гарантирует текущий экземпляр VS, поэтому вы получаете результаты из другого экземпляра. Вместо этого вы можете использовать метод GetDTE, показанный там:

[DllImport("ole32.dll")]
private static extern int CreateBindCtx(uint reserved, out IBindCtx ppbc);

public static DTE GetDTE(int processId)
{
    string progId = "!VisualStudio.DTE.10.0:" + processId.ToString();
    object runningObject = null;

    IBindCtx bindCtx = null;
    IRunningObjectTable rot = null;
    IEnumMoniker enumMonikers = null;

    try
    {
        Marshal.ThrowExceptionForHR(CreateBindCtx(reserved: 0, ppbc: out bindCtx));
        bindCtx.GetRunningObjectTable(out rot);
        rot.EnumRunning(out enumMonikers);

        IMoniker[] moniker = new IMoniker[1];
        IntPtr numberFetched = IntPtr.Zero;
        while (enumMonikers.Next(1, moniker, numberFetched) == 0)
        {
            IMoniker runningObjectMoniker = moniker[0];

            string name = null;

            try
            {
                if (runningObjectMoniker != null)
                {
                    runningObjectMoniker.GetDisplayName(bindCtx, null, out name);
                }
            }
            catch (UnauthorizedAccessException)
            {
                // Do nothing, there is something in the ROT that we do not have access to.
            }

            if (!string.IsNullOrEmpty(name) && string.Equals(name, progId, StringComparison.Ordinal))
            {
                Marshal.ThrowExceptionForHR(rot.GetObject(runningObjectMoniker, out runningObject));
                break;
            }
        }
    }
    finally
    {
        if (enumMonikers != null)
        {
            Marshal.ReleaseComObject(enumMonikers);
        }

        if (rot != null)
        {
            Marshal.ReleaseComObject(rot);
        }

        if (bindCtx != null)
        {
            Marshal.ReleaseComObject(bindCtx);
        }
    }

    return (DTE)runningObject;
} 

Если вы знаете имя решения заранее, вы можете найти его в свойстве MainWindowTitle Process и передать ProcessID методу выше.

var dte = GetDTE(System.Diagnostics.Process.GetProcesses().Where(x => x.MainWindowTitle.StartsWith("SolutionName") && x.ProcessName.Contains("devenv")).FirstOrDefault().Id);

В то время как приведенный выше код работал, я столкнулся с ошибкой COM, которую я исправил с помощью MessageFilter class показанного здесь.

Из этого сообщения это класс MessageFilter выглядит как

public class MessageFilter : IOleMessageFilter
{            
    // Class containing the IOleMessageFilter
    // thread error-handling functions.

    // Start the filter.
    public static void Register()
    {
        IOleMessageFilter newFilter = new MessageFilter();
        IOleMessageFilter oldFilter = null;
        CoRegisterMessageFilter(newFilter, out oldFilter);
    }


    // Done with the filter, close it.
    public static void Revoke()
    {
        IOleMessageFilter oldFilter = null;
        CoRegisterMessageFilter(null, out oldFilter);
    }


    //
    // IOleMessageFilter functions.
    // Handle incoming thread requests.
    int IOleMessageFilter.HandleInComingCall(int dwCallType,
    System.IntPtr hTaskCaller, int dwTickCount, System.IntPtr lpInterfaceInfo)
    {

        //Return the flag SERVERCALL_ISHANDLED.
        return 0;
    }


    // Thread call was rejected, so try again.
    int IOleMessageFilter.RetryRejectedCall(System.IntPtr
    hTaskCallee, int dwTickCount, int dwRejectType)
    {

        if (dwRejectType == 2)
        // flag = SERVERCALL_RETRYLATER.
        {
            // Retry the thread call immediately if return >=0 &
            // <100.
            return 99;
        }
        // Too busy; cancel call.
        return -1;
    }


    int IOleMessageFilter.MessagePending(System.IntPtr hTaskCallee,
    int dwTickCount, int dwPendingType)
    {
        //Return the flag PENDINGMSG_WAITDEFPROCESS.
        return 2;
    }


    // Implement the IOleMessageFilter interface.
    [DllImport("Ole32.dll")]
    private static extern int
      CoRegisterMessageFilter(IOleMessageFilter newFilter, out
      IOleMessageFilter oldFilter);
}



[ComImport(), Guid("00000016-0000-0000-C000-000000000046"),
InterfaceTypeAttribute(ComInterfaceType.InterfaceIsIUnknown)]
interface IOleMessageFilter
{
    [PreserveSig]
    int HandleInComingCall(
    int dwCallType,
    IntPtr hTaskCaller,
    int dwTickCount,
    IntPtr lpInterfaceInfo);

    [PreserveSig]
    int RetryRejectedCall(
    IntPtr hTaskCallee,
    int dwTickCount,
    int dwRejectType);


    [PreserveSig]
    int MessagePending(
        IntPtr hTaskCallee,
        int dwTickCount,
        int dwPendingType);
}

Затем вы можете получить доступ к таким проектам, как это.

var dte = GetDTE(System.Diagnostics.Process.GetProcesses().Where(x => x.MainWindowTitle.StartsWith("SolutionName") && x.ProcessName.Contains("devenv")).FirstOrDefault().Id);
MessageFilter.Register();
var projects = dte.Solution.OfType<Project>().ToList();
MessageFilter.Revoke();

foreach (var proj in projects)
{
   Debug.WriteLine(proj.Name);
}

Marshal.ReleaseComObject(dte);

Ответ 3

Я считаю, вы можете использовать что-то вроде этого:

var dte = (EnvDTE.DTE)GetService(typeof(EnvDTE.DTE));
if (dte != null)
{
    var solution = dte.Solution;
    if (solution != null)
    {
        // get your projects here
    }
}