You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

Visual Studio扩展:如何获取选中解决方案文件夹的路径?

解决Visual Studio扩展中禁用Solution Folder原生命令并获取路径的问题

我之前开发VS扩展时也碰到过类似的困扰——要针对特定Solution Folder禁用原生右键命令,但死活拿不到正确的SolutionFolder对象。结合你的代码和问题,给你几个可行的解决方案:

一、修正IVsSelectionEvents中获取SolutionFolder的方式

你之前强转失败的核心原因是:SolutionFolder对应的ExtObject本质是Project类型,而非直接的SolutionFolder,需要先拿到Project对象,再通过它的Object属性转换。

修改你的获取方法为专门针对SolutionFolder的实现:

private static SolutionFolder GetSelectedSolutionFolder(IVsHierarchy hierarchy, uint itemId)
{
    if (hierarchy == null || itemId == VSConstants.VSITEMID_NIL)
        return null;

    // 情况1:选中的是Solution Folder本身
    object projectObj;
    if (hierarchy.GetProperty(VSConstants.VSITEMID_ROOT, (int)__VSHPROPID.VSHPROPID_ExtObject, out projectObj) == VSConstants.S_OK)
    {
        var project = projectObj as Project;
        // 验证是否为Solution Folder类型(固定GUID)
        if (project != null && project.Kind == "{66A26720-8FB5-11D2-AA7E-00C04F688DDE}")
        {
            return project.Object as SolutionFolder;
        }
    }

    // 情况2:选中的是Solution Folder下的子项,获取所在的父文件夹
    object itemObj;
    if (hierarchy.GetProperty(itemId, (int)__VSHPROPID.VSHPROPID_ExtObject, out itemObj) == VSConstants.S_OK)
    {
        var projectItem = itemObj as ProjectItem;
        if (projectItem != null && projectItem.ContainingProject.Kind == "{66A26720-8FB5-11D2-AA7E-00C04F688DDE}")
        {
            return projectItem.ContainingProject.Object as SolutionFolder;
        }
    }

    return null;
}

然后在OnSelectionChanged中调用这个方法,拿到目标文件夹后即可获取路径:

public int OnSelectionChanged(IVsHierarchy pHierOld, uint itemidOld, IVsMultiItemSelect pMISOld, ISelectionContainer pSCOld, IVsHierarchy pHierNew, uint itemidNew, IVsMultiItemSelect pMISNew, ISelectionContainer pSCNew) 
{
    var targetFolder = GetSelectedSolutionFolder(pHierNew, itemidNew);
    if (targetFolder != null && targetFolder.Name.Equals("SubModules", StringComparison.OrdinalIgnoreCase))
    {
        // 获取文件夹完整路径
        string folderPath = targetFolder.Properties.Item("FullPath").Value.ToString();
        // 这里可以记录状态,为后续禁用命令做准备
    }
    return VSConstants.S_OK;
}

二、在CommandEvents_BeforeExecute中直接拦截命令

相比监听选择事件,直接在命令执行前判断更直接,还能避免依赖私有API。通过DTE的SelectedItems获取选中对象,然后判断是否是目标文件夹:

private void CommandEvents_BeforeExecute(string Guid, int ID, object CustomIn, object CustomOut, ref bool CancelDefault)
{
    // 获取DTE服务实例
    var dte = (DTE)Package.GetGlobalService(typeof(DTE));
    if (dte == null || dte.SelectedItems.Count != 1)
        return;

    var selectedItem = dte.SelectedItems.Item(1);
    SolutionFolder targetFolder = null;

    // 判断选中的是Solution Folder本身还是其下的子项
    if (selectedItem.Project != null && selectedItem.Project.Kind == "{66A26720-8FB5-11D2-AA7E-00C04F688DDE}")
    {
        targetFolder = selectedItem.Project.Object as SolutionFolder;
    }
    else if (selectedItem.ProjectItem != null && selectedItem.ProjectItem.ContainingProject.Kind == "{66A26720-8FB5-11D2-AA7E-00C04F688DDE}")
    {
        targetFolder = selectedItem.ProjectItem.ContainingProject.Object as SolutionFolder;
    }

    // 匹配目标文件夹,禁用指定原生命令
    if (targetFolder != null && targetFolder.Name.Equals("SubModules", StringComparison.OrdinalIgnoreCase))
    {
        // 可通过GUID+ID精准匹配命令,比如Remove命令的GUID是{5EFC7975-14BC-11CF-9B2B-00AA00573819},ID是15
        var command = dte.Commands.Item(Guid, ID);
        if (command.Name.Equals("Edit.Remove", StringComparison.OrdinalIgnoreCase) || 
            command.Name.Equals("Project.AddNewItem", StringComparison.OrdinalIgnoreCase))
        {
            CancelDefault = true; // 阻止原生命令执行
        }
    }
}

三、关键注意事项

  • 注册IVsSelectionEvents时,要在扩展初始化时正确获取SVsShellMonitorSelection服务,销毁时取消注册,避免内存泄漏。
  • Solution Folder的GUID是固定的{66A26720-8FB5-11D2-AA7E-00C04F688DDE},建议定义成常量复用。
  • 命令名称可能因VS版本略有差异,优先用GUID+ID匹配命令,比名称更可靠。

内容的提问来源于stack exchange,提问作者Master DJon

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.05.11 08:51:31