Search code examples
c#visual-studiovisual-studio-2017vsix

Visual Studio Extension: How to get the "path" of a solution folder on selection?


Previous question: Visual Studio Extension: How to disable native command?

I build an extension that manages correctly Git Modules and, to do so, I have a folder in the solution called SubModules.

I managed to add my context menu to the solution file and to the project files, but I would like to disable some native commands when right-clicking on my SubModules folder (i.e. Remove, Add).

I now know how to disable the native commands I want, but to do so, I need to get the "path" of the selected Solution Folder.

I tried to implement IVsSelectionEvents, but no luck (o is not null before casting):

public int OnSelectionChanged(IVsHierarchy pHierOld, uint itemidOld, IVsMultiItemSelect pMISOld, ISelectionContainer pSCOld, IVsHierarchy pHierNew, uint itemidNew, IVsMultiItemSelect pMISNew, ISelectionContainer pSCNew)
{
    var o = GetProjectItem(pHierNew, itemidNew);
    return VSConstants.S_OK;
}

private static ProjectItem GetProjectItem(IVsHierarchy hierarchy, uint itemId)
{
    object o;
    if (hierarchy.GetProperty(itemId, (int)__VSHPROPID.VSHPROPID_ExtObject, out o) == VSConstants.S_OK)
    {
        var t = o as SolutionFolder; // ==> null

        return o as ProjectItem; // ==> null
    }
    return null;
}

Using the code I had (from the other question) (where I want to find the "path", but I could have managed with the above "solution"), I tried to cast to SolutionFolder or to FileProperties... still no luck; even though MSDN tells the returned objects are supposed to be of type FileProperties. Exploring with QuickWatch on (Marshal.GetObjectForIUnknown(selectionContainerPtr) as ISelectionContainer), I can go in the see the private property _nodes and there, I can see my SolutionFolder node and go up using its Parent property to ensure it is a folder I want to block commands. Here is the actual code:

private static void CommandEvents_BeforeExecute(string Guid, int ID, object CustomIn, object CustomOut, ref bool CancelDefault)
{
    string name = GetCommandName(Guid, ID);

    if (name == "Project.AddNewSolutionFolder")
    {
        CancelDefault = true;
    }
    if (name == "Edit.Delete")
    {
        CancelDefault = true;

        IVsMultiItemSelect multiItemSelect = null;
        IntPtr hierarchyPtr = IntPtr.Zero;
        IntPtr selectionContainerPtr = IntPtr.Zero;
        uint itemid = 0;

        int hr = monitorSelection.GetCurrentSelection(out hierarchyPtr, out itemid, out multiItemSelect, out selectionContainerPtr);
        object[] selected = new object[2];
        (Marshal.GetObjectForIUnknown(selectionContainerPtr) as ISelectionContainer).GetObjects(2, 1, selected);


        var t = selected[0] as VSLangProj.FileProperties; // ==> null
        var t2 = selected[0] as SolutionFolder; // ==> null
        var b = 1;
    }

    if (name == "View.Branch")
    {
        //TODO: Could disable this command if not able to find the branch changing command (that would be cancelled and launch ours)
        menuBranchOpened = true;
    }

    if (menuBranchOpened)
    {
        var a = 1;
    }

}

I was inspired by and tried (none worked except the above one seems to be really near) :


Solution

  • Finally, exploring (more) the website of the other person who answered me for my previous question, I finally found it. So I will share a method I made to know what type of items are selected in the Solution Explorer.

    applicationObject = await ServiceProvider.GetGlobalServiceAsync(typeof(SDTE)) as DTE2; // Was initiate in a method
    
    private enum SelectionTypes
    {
        Other = 0,
        InSubModules = 1,
        IsAFolder = 2,
        IsAProject = 4,
        InAProject = 8
    }
    
    private static List<SelectionTypes> GetSelectionTypes()
    {
        ThreadHelper.ThrowIfNotOnUIThread();
        var selectionTypes = new List<SelectionTypes>();
    
        EnvDTE.UIHierarchy solutionExplorer = applicationObject.ToolWindows.SolutionExplorer;
        object[] items = solutionExplorer.SelectedItems as object[];
        //{ Name = "WindowBase" FullName = "Microsoft.VisualStudio.Platform.WindowManagement.DTE.WindowBase"}
        if (items.Length > 0)
        {
            for (int i = 0; i < items.Length; i++)
            {
                var selectionType = SelectionTypes.Other;
                var selectedItem = items[0] as EnvDTE.UIHierarchyItem;
                var currentItem = selectedItem;
                var subModulesParentsCount = 0;
                var countingSubModulesParents = false;
                var nbParents = -1;
    
                if (currentItem.Object.GetType().FullName == "Microsoft.VisualStudio.ProjectSystem.VS.Implementation.Package.Automation.OAProject") selectionType |= SelectionTypes.IsAProject;
                if (currentItem.Object.GetType().FullName == "Microsoft.VisualStudio.ProjectSystem.VS.Implementation.Package.Automation.OAFolderItem") selectionType |= SelectionTypes.IsAFolder;
    
                while (currentItem != null)
                {
                    nbParents++;
                    if (countingSubModulesParents) subModulesParentsCount++;
    
                    if (currentItem.Name == "SubModules")
                    {
                        subModulesParentsCount = 0;
                        countingSubModulesParents = true;
                    }
    
                    if (currentItem.Object.GetType().FullName == "Microsoft.VisualStudio.ProjectSystem.VS.Implementation.Package.Automation.OAProject") selectionType |= SelectionTypes.InAProject;
    
                    currentItem = currentItem.Collection.Parent as EnvDTE.UIHierarchyItem;
                }
    
                if (selectionType == SelectionTypes.Other && nbParents != 0) selectionType |= SelectionTypes.IsAFolder;
    
                if (subModulesParentsCount == 1)
                {
                    selectionType |= SelectionTypes.InSubModules;
                }
    
                selectionTypes.Add(selectionType);
            }
        }
    
        return selectionTypes;
    }