I have been very stressed the last weeks in my spare time (nights and weekends) to ensure that the upcoming version 8.0 of my MZ-Tools extension works with Visual Studio 2015 CTPs and RC. This has been complicated because of these factors:
- MZ-Tools 7.0 was an add-in, and MZ-Tools 8.0 will be a package.
- MZ-Tools 8.0 for VS shares 80% of code with the also upcoming MZ-Tools 8.0 for VBA / VB, which is not yet finished.
- VS 2015 belongs to my definition of “painful” category for extensions, because of the Roslyn-based implementation of the file code model.
Today, VS 2015 RC has passed for the first time the 3,300+ automated tests of MZ-Tools. And they are integration tests (not unit tests), each one testing a whole user-simulated scenario: clicking the options window of MZ-Tools, setting some options, clicking the button of a feature, capturing the result (either contents of an output toolwindow or generated code) and comparing with the expected result. They are able to detect subtle changes in the behavior of VS 2015 such as different automatic formatting, automatic removal of line continuation character “_” in VB.NET, automatic addition of parenthesis “()” to parameter-less VB.NET methods if the test omitted them, namespaces omitted from types in the Error List after a build, etc. These are not really bugs but required to tweak the tests, in some cases maintaining backward compatibility with previous VS versions and in some cases detecting the VS version and using different code.
The tests also detected problems in the automation file code model (EnvDTE.ProjectItem.FileCodeModel), which I patiently isolated to root causes resulting in a total of 18 bugs that I have reported on GitHub, the last one, quite obscure, yesterday. At the time of this writing, at least 15 are fixed (or will be fixed for VS 2015 RTM) and the remaining three ones have workarounds that I have coded to pass the tests.
So, at this point, VS 2015 RC is good enough from my point of view.
Another question in the MSDN VSX Forum about the browser for folder dialog was intriguing enough for me to investigate. If you are using the .NET Framework since version 1.0 (like me), you may remember that that version didn’t provide a class, you had to use the native class of the Win32 API Shell, which was tricky. The FolderBrowserDialog was added in .NET Framework 1.1, and since then it has this appearance:
Despite being in Spanish (the computer that I am using to write this post), you can guess that it provides a title, a description, a treeview and an optional button to allow the creation of a new folder without leaving the dialog.
However, Visual Studio uses this other dialog to select a folder in several places:
Again, despite being in Spanish, you can guess that it provides a treeview to the left and a listview to the right, along with the button to create a new folder and, overall, somewhat better usability.
So, the question was how to leverage that Visual Studio dialog from a package. The answer lies in the IVsUIShell.GetDirectoryViaBrowseDlg method, and in this new article of mine you have a working example, because the API is awful, with arrays of structures, memory allocation, marshaling, etc:
HOWTO: Use the Browse for Folder dialog of Visual Studio from a package
I have created two new articles/samples from a question that I addressed in the StackOverflow forum, about unloading / reloading a project programmatically. Using the automation model (EnvDTE) I knew the answers since many years ago:
However, that approach requires that the project is selected previously in the Solution Explorer. Furthermore, having migrated my MZ-Tools add-in to a Visual Studio package I am no longer happy when I have to use the old automation model and I am eager to investigate how to do things using native services. Alas, the investigation is never easy. In this case the IVsSolution interface provides the CloseSolutionElement method, which is not intuitive because it serves to unload many things, from the solution to a document, and certainly a project too. And it doesn’t seem to provide a method to reload a project. So, I opened the MSDN documentation for IVsSolution2, IVsSolution3, IVsSolution4, IVsSolution5 and IVsSolution6 interfaces and lo and behold, the IVsSolution4 interface has the answers in the UnloadProject / ReloadProject methods. If you remember, Visual Studio 2012 introduced asynchronous solution loading (something that seemed to start in Visual Studio 2010), so the new IVsSolution4 interface introduced quite a few methods that helps with that area. So, here are the samples:
HOWTO: Unload a project from a Visual Studio Package
HOWTO: Reload a project from a Visual Studio Package
Both methods require the project guid that identifies the project in the solution (not to confuse with the project type guid). You can get the project guid from a IVsHierarchy that represents a project calling the GetProperty method with the __VSHPROPID.VSHPROPID_ProjectIDGuid property. For VS 2010, alas, when the project is unloaded the “hierarchy stub” that represents the unloaded project in the solution doesn’t implement that property (it does in VS 2012 and higher). So, the second article applies to VS 2012 and higher (because it starts with an unloaded project as the initial condition), while the first could apply also to VS 2010.
Finally, for completeness sake, given a IVsHierarchy that represents an unloaded project you can get the unload reason calling the GetProperty method with the __VSHPROPID5.VSHPROPID_ProjectUnloadStatus property, which should return a value of the _VSProjectUnloadStatus enumeration.
In this post I blogged about nine bugs introduced in the FileCodeModel of Visual Studio 2015 by the new Roslyn-based implementation. Those bugs were “critical” (that is, no easy workaround) and were fixed in the next days.
Then I reported two additional bugs that were less critical for me since I could implement workarounds:
EnvDTE.CodeElement GetStartPoint(EnvDTE.vsCMPart.vsCMPartBody).Line returns wrong result if declaration uses more than one line
EnvDTE80.CodeProperty2.ReadWrite returns always read-write value for VB.NET body-less properties
Those were fixed too. And two days ago I reported a couple of new critical bugs (no workaround) that I discovered:
EnvDTE.CodeFunction.FunctionKind causes COMException for Sub New() in VB.NET instead of returning vsCMFunction.vsCMFunctionConstructor
EnvDTE.CodeFunction.StartPoint, EndPoint, GetStartPoint, etc. cause exception for VB.NET “Declare Sub”/”Declare Function” methods
And those two have been already fixed on GitHub and will be in the some of the next Visual Studio 2015 CTPs.
It is also very incredible the level of transparency that Microsoft has now using open source for Roslyn on GitHub, where you can see the new code that fixes the bugs, the added unit tests, which MS developer is actually working on the fixes, who is reviewing them, etc.
So, thanks Microsoft for all!
The next five articles / samples that I have created about Team Explorer extensibility are about getting the information contained in the Git-related pages of the Team Explorer of Visual Studio 2013.
The Branches page contains the current branch and the Published and Unpublished sections:
To get that information from a package you need to use the Microsoft.TeamFoundation.Git.Controls.Extensibility.IBranchesExt interface. See the article:
HOWTO: Get the Git Branches in the Team Explorer of Visual Studio from a package
The Commits page contains the Incoming Commits and Outgoing Commits sections:
To get that information from a package you need to use the Microsoft.TeamFoundation.Git.Controls.Extensibility.ICommitsExt interface. See the article:
HOWTO: Get the Git Commits in the Team Explorer of Visual Studio from a package
The Changes page contains the Included Changes, Excluded Changes and Untracked Files sections:
To get that information from a package you need to use the Microsoft.TeamFoundation.Git.Controls.Extensibility.IChangesExt interface. See the article:
HOWTO: Get the Git Changes in the Team Explorer of Visual Studio from a package
The Resolve Conflicts page contains the Conflicts and Resolved Conflicts sections:
To get that information from a package you need to use the Microsoft.TeamFoundation.Git.Controls.Extensibility.IConflictsExt interface. See the article:
HOWTO: Get the Git Conflicts in the Team Explorer of Visual Studio from a package
The Commit Details page contains the commit properties and Changes section:
To get that information from a package you need to use the Microsoft.TeamFoundation.Git.Controls.Extensibility.ICommitDetailsExt interface. See the article:
HOWTO: Get the Git Commit Details in the Team Explorer of Visual Studio from a package
The two first samples that I have created about Team Explorer extensibility are about getting the information contained in the Builds and Pending Changes pages.
The Builds page contains three sections: My Builds, Favorite Builds Definitions and All Build Definitions:
To get that information from a package you need to use the Microsoft.TeamFoundation.Build.Controls.Extensibility.IBuildsPageExt interface. See the following article:
HOWTO: Get the Builds in the Team Explorer of Visual Studio from a package
The Pending Changes page contains the Comment, Related Work Items, Included Changes and Excluded Changes sections:
To get that information from a package you need to use the Microsoft.TeamFoundation.VersionControl.Controls.Extensibility.IPendingChangesEx interface. See the following article:
HOWTO: Get the Pending Changes in the Team Explorer of Visual Studio from a package
A question in the MSDN VSX forum prompted me to investigate another area, Team Explorer extensibility, which I am not very familiar with yet. After solving the question, I wanted to learn more about Team Explorer / TFS Extensibility and I found quite a few resources that I didn’t have listed on this VSX site. Some resources that I have updated are:
- In the Overview page, I have added an image from the MSDN documentation to illustrate the several client models, and clarified that you need the Team Explorer to get the client libraries even if you want a standalone application to connect to TFS.
- In the Downloads page, I have added the links to download the Team Explorer packages for Visual Studio 2005-2013, which, by the previous paragraph, are required to get the client libraries for standalone applications that don’t extend the Team Explorer. The client model object assemblies and namespaces are quite confusing because some are Microsoft.TeamFoundation.* and some others are Microsoft.VisualStudio.TeamFoundation.*, and are located in at least three folders (ReferenceAssemblies\v2.0, ReferenceAssemblies\v4.5, and PrivateAssemblies of the root folder Program Files (x86)\Microsoft Visual Studio <version>\Common7\IDE). The MSDN documentation for Visual Studio 2012 has a nice mapping between assemblies and namespaces.
- In the Articles section, I have added a couple of articles to extend the Team Explorer of Visual Studio 2012 / 2013, and an impressive collection of 56 articles about TFS API by MVP fellow Shai Raiten.
In the next posts, I will contribute myself explaining how to get information about the different pages / sections of the Team Explorer.
One of the things that I do from time to time is to take a question in the MSDN Visual Studio Integrate or StackOverflow forums about a subject of VSX that I am not very familiar with, and try to investigate and provide an answer. The other day I picked this one about extending the Solution Explorer filter with a custom filter, something that I didn’t know.
The question was that following the MSDN Walkthrough: Extending the Solution Explorer Filter the sample didn’t work for VS 2013 Update 4. I reproduced the issue and after some investigation I concluded that it was a bug in VS 2013, because the version of the Walkthrough for VS 2012 actually worked. I reported it to Microsoft Connect (the report seems to have been deleted now) and actually what was wrong was the MSDN sample, which should use the SVsServiceProvider parameter type (instead of IServiceProvider type) in this constructor:
public FileNameFilterProvider(SVsServiceProvider serviceProvider, ...)
There is now a MSDN feature suggestion to fix the code sample. But the sample has other two bugs:
- The Utilities.cs class is no longer required, you can use the provided HierarchyUtilities class.
- The command should not be bound with an event handler in the Initialize() method of the package. Its command guid/id are used as parameters in the SolutionTreeFilterProvider attribute to bind the command with the filter.
The usual way to create toolwindows in a package is described in my last post HOWTO: Create a toolwindow with a ToolWindowPane class in a Visual Studio package. While with add-ins only a usercontrol was required, with packages you need the usercontrol and a class that inherits from ToolWindowPane. That is, two files. When I first learned this, I found it somewhat overkill. But when I started to migrate my MZ-Tools add-in to a package, which has quite a few toolwindows, I found it totally overkill. Furthermore, requiring to decorate the package with a ProvideToolWindow attribute for each toolwindow defeats the 3rd strategy that I exposed in Strategies migrating from Visual Studio add-ins to packages, in which a core plug-in provides the features (and therefore the usercontrols for toolwindows) and a host adapter provides the communication between the host (Visual Studio) and the core plug-in. The host adapter is not aware of the features implemented by the core plug-in, and therefore it shouldn’t use specific toolwindows classes or attributes. I am aware that the ProvideToolWindow attribute and the class that inherits ToolWindowPane serve a purpose which was not available for toolwindows created by add-ins: to show automatically when VS is launched the toolwindows that were open in the last VS sessions. So, the initialization of the toolwindow must be contained in a class and not in the “click event” of the command that shows the toolwindow. But it happens that add-ins can provide that functionality too with minimal effort: when unloaded the add-in stores which toolwindows were open and when it is loaded again it shows them. Version 7.0 of MZ-Tools, being an add-in, offered this feature since it was released in 2012.
So, I wanted to create toolwindows in my MZ-Tools package like I was doing in my MZ-Tools add-in. While you can use the automation model (EnvDTE) from a package, alas, the EnvDTE80.Windows2. CreateToolWindow2 method is among the few ones that cannot be used from a package because it requires an EnvDTE.AddIn parameter, that a package cannot provide. Fortunately, the the IVsUIShell interface provides a CreateToolWindow method that can be used from packages. In this new article (equivalent to HOWTO: Create a dockable toolwindow from a Visual Studio .NET add-in) I show how to use it:
HOWTO: Create a toolwindow without a ToolWindowPane class in a Visual Studio package
Creating a package with a toolwindow using the package wizard is easy because the wizard provides you an option to do it:
However, the steps to create a second toolwindow are not evident. And even if you figure out the steps, knowing what they do may be difficult. And if you come from the add-ins space (where toolwindow creation uses an easier approach), toolwindows of packages is difficult stuff. This question has appeared twice in the StackOverflow forum in the last days.
I have written the following article to explain the stuff generated by the wizard when you request the toolwindow option, how the pieces are connected, and why each piece is required:
HOWTO: Create a toolwindow with a ToolWindowPane class in a Visual Studio package
In my next post/article I will explain a different approach to create toolwindows in a package, without a ToolWindowPane (hence the title of this article).