如何在当前的Visual studio解决方案中获得项目列表

本文关键字:解决方案 列表 项目 studio Visual | 更新日期: 2023-09-27 17:54:50

当我们在任何打开的解决方案中打开Package Manager Console时,它会显示该解决方案的所有项目。如何加载同一解决方案的所有项目。当我尝试使用下面显示的代码时,它正在获取我打开的第一个解决方案的项目。

    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;
    }

如何在当前的Visual studio解决方案中获得项目列表

这里有一组不同的函数,它们允许您枚举给定解决方案中的项目。下面是当前解决方案的用法:

// 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;
}

可能有更好的方法,但我很快就发现这是可行的(它假设您知道解决方案名称)。根据这篇文章,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;
} 

如果事先知道溶液名称,可以在ProcessMainWindowTitle属性中找到,并将ProcessID传递给上述方法。

var dte = GetDTE(System.Diagnostics.Process.GetProcesses().Where(x => x.MainWindowTitle.StartsWith("SolutionName") && x.ProcessName.Contains("devenv")).FirstOrDefault().Id);
当上面的代码工作时,我遇到了一个COM错误,我通过使用这里显示的MessageFilter类修复了这个错误。

从那篇文章,这是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);

我相信你可以这样写:

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