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
相关产品推荐
相关产品推荐

