Category Archives: Moving from add-ins to packages

Announcing unified MZ-Tools 8.0 for Visual Studio, VBA (64-bit/32-bit), VB6 and VB5

It has been a long time since my last post here, and for a good reason. Since I released MZ-Tools 8.0 for Visual Studio 2015 last July 1 (converted from add-in to package), I have been very busy the last months to achieve a new huge milestone:

Announcing unified MZ-Tools 8.0 for Visual Studio, VBA (64-bit/32-bit), VB6 and VB5

From the extensibility point of view, MZ-Tools 8.0 is a unique piece in the world, AFAIK: it provides the same feature set for four different IDEs (Visual Studio, VBA editor, Visual Basic 6.0 and Visual Basic 5.0) with different extensibility APIs reusing the same code at binary level:

MZ-Tools 8.0 for Visual Studio (as package):
MZTools8VSPackage

MZ-Tools 8.0 for Visual Studio (as add-in):
MZTools8VSAddIn

MZ-Tools 8.0 for VBA:
MZTools8VBA

MZ-Tools 8.0 for VB6:
MZTools8VB6

MZ-Tools 8.0 for VB5:
MZTools8VB5

As you can see in the images, in all cases there is the same big MZTools8PlugIn.dll file, which contains the feature set, and smaller host adapter dlls that “plug” the features into the IDEs. If you are interested, I outlined the strategy to achieve this in this other post.

Visual Studio 2015 RTM just released

Finally Visual Studio 2015 RTM has been released today and I am installing now on my main dev machine, rather than on separate virtual machines as I have been doing until now. There is and there will be a lot of information about what’s new in this release, but as a developer of extensions for Visual Studio, this release introduces these changes that you must know:

  • Add-ins are gone and now packages are the primary way of extending Visual Studio for the typical need of creating some toolbar, menu, buttons, toolwindows, etc. or listening to events to do something. Migrating from an add-in to a package is not trivial due to the usage of services instead of the EnvDTE automation model (although you can still use the automation model) but also for the difficulties of the .vsct file. ElektroStudios StackOverflow user (and others): I do feel your pain. I will do my best to find time and provide decent tutorials and a GitHub sample about .vsct in the next couple of months.
  • The editions offering has been simplified a bit and now you have:
    • Community Edition, which supports extensibility and is free for some scenarios, at least “still free”, huh?
    • Professional Edition
    • Enterprise Edition
      All of them are already available. There is no Ultimate edition, but for some reason there are still Express editions for Windows Desktop, Web (both already available) and Windows 10 (coming soon).
  • The compilers for C# and VB.NET are now exposed to extensions through the .NET Compiler Platform (formerly “Roslyn”). This should replace the automation code models (EnvDTE.Project.CodeModel, EnvDTE.ProjectItem.FileCodeModel), although they can be still used and in fact their implementation has changed to use Roslyn under the hood, with quite a few bugs along the way that I have reported and most of them are fixed now.
  • There is new support for high resolution icons on buttons for high-dpi displays, although AFAIK it is not documented yet.
  • There is a new Common Project System (CPS) in the works.
  • The Visual Studio 2015 SDK is available as a separate download as until now, but it is also already included in the Visual Studio setup under the name Common Tools > Visual Studio Extensibility Tools:
    VS2015SDK
  • The way to create a package, to add commands and toolwindows has changed, as hinted in the setup:
    VS2015SDK_2
  • To create a package there is no longer a wizard as in Visual Studio 2013, but just a barebone VSIX project template, and later you add as many custom commands or custom toolwindows as you want using the Add > New Item dialog and selecting the corresponding item templates under the Extensibility node. This solves the old problem of how to add a second command or toolwindow to an existing package, at the cost of making it more difficult to know how to add the first ones.

I will elaborate on all these aspects in the next weeks (during my vacations, I’m afraid), and will provide new links on this site to VS 2015 specific stuff.

Did I mention that my extension MZ-Tools 8.0 is now a package for VS 2012, 2013 and 2015? It is also an add-in for VS 2005, 2008 and 2010, and they share 80% of code at binary level using the strategy of core plug-in +  host adapters that I outlined here. I have even released in the last weeks a restricted Beta 2 of MZ-Tools 8.0 for the VBA editor of Office and even for VB6 / VB5 sharing the same 80% of binary .NET code. This is an example of a complex add-in migrated to a package with a new good underlying architecture to accommodate not only future changes in the extensibility model of Visual Studio, but also to support other Microsoft IDEs.

MZ-Tools Articles Series: HOWTO: Guessing the IDE mode (design, debug or run-time) from a Visual Studio package

As I am moving from add-ins to packages, I always like to learn how to do things using native services of Visual Studio instead of using the automation model (EnvDTE). There is an area of Visual Studio extensibility, the debugger, that is very powerful but normally extensions don’t have to use, except maybe to know which mode (design-time, debug-time, run-time) the IDE is, and when the mode changes, because maybe some commands or features should be enabled/disabled based on the IDE mode.

Eight years ago I wrote how to do that using the automation model in this article:

HOWTO: Guessing the IDE mode (design, debug or run-time) from a Visual Studio add-in or macro

Today I have written a small equivalent article doing the same using the IVsDebugger native service:

HOWTO: Guessing the IDE mode (design, debug or run-time) from a Visual Studio package

MZ-Tools Articles Series: HOWTO Unload/Reload a project from a Visual Studio 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:

DTE.ExecuteCommand(“Project.UnloadProject”)
DTE.ExecuteCommand(“Project.ReloadProject”)

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.

MZ-Tools Articles Series: HOWTO: Create a toolwindow WITHOUT a ToolWindowPane class in a Visual Studio package

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
http://www.mztools.com/articles/2015/MZ2015005.aspx

MZ-Tools Articles Series: HOWTO: Create a toolwindow with 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:

PackageWizard

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
http://www.mztools.com/articles/2015/MZ2015004.aspx

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).

Issues with command names in Visual Studio packages

When creating a Visual Studio add-in, you had full control of the full name of its commands (command prefix + command short name, such as “MyAddIn.MyCommand”), as I explained in the article HOWTO: Create command names without ‘.Connect’ in Visual Studio add-ins. So, when I created my MZ-Tools add-in for Visual Studio, each version used a command prefix such as “MZTools6” or “MZTools7”, and I could provide that information to the end user to customize the keyboard shortcuts, for example:

keyboardshortcuts

Another nice consequence was that, given a Visual Studio version, two versions of the add-in (for example one purchased, next one evaluating) could be loaded at the same time without collisions of add-in command full names, because each one used a different command prefix.

With packages, you lack the control over the command prefix, as I explained in INFO: How a Visual Studio package command is named. So, the commands of a package cannot be named “MyPackage.MyCommand”. And it is perfectly possible that two packages (or two versions of the same package loaded side by side) create two commands with the same full name as in this example:

TwoCommands

This is possible because Visual Studio commands (as everything else in Visual Studio) are identified by Guids and Ids and not by names (the EnvDTE.Command interface exposes those two properties).

Therefore, given that a collision of command full names is not really a collision internally in Visual Studio (only in the user interface level), so far so good. But if your add-in assumes that its commands have unique full names (because the command prefix is unique for add-ins) and uses code like this:

EnvDTE.Command command = dte.Commands.Item("MyAddIn.MyCommand");

that will cause problems when migrating to a package:

EnvDTE.Command command = dte.Commands.Item("Tools.MyCommand");

You see, the command full name now uses “Tools” (for example) instead of “MyAddIn”. If you have two versions of your package loaded side by side, that call can return the command of the wrong package version. A similar problem to getting the correct CommandBar as explained in HOWTO: Get a CommandBar by Guid and Id rather than by name from a Visual Studio add-in.

The same can happen if you execute a command by name:

dte.ExecuteCommand("Tools.MyCommand");

The solution is to stop using command full names in the code to identify commands, and to use the command Guid / Id instead:

In my MZ-Tools extension I use a lot of command stuff, so I have to review/change several areas of the product as part of the migration to a package. Also, the user can create commands for each code template of the Code Library feature (notice the optional Command Name field):

codelibrary

In the code of the add-in, that is done using the EnvDTE80.Commands2.AddNamedCommand2 method. Although you can use automation (EnvDTE) from a package, you can’t use that method because its first parameter is of the type EnvDTE.AddIn. I guess that a package must use the AddNamedCommand methods of the IVsProfferCommands interface. I will post and provide a sample once I try it.

MZ-Tools Articles Series: HOWTO: Get solution events from a Visual Studio package

Continuing with the series of articles to show how to do things in a native way in a Visual Studio package instead of using the automation model (EnvDTE), in this new article I show how to get the solution events using the IVsSolutionEvents interface. As I expected, it is more difficult than using the automation model, with more code, and even requires you to use a cookie!:

HOWTO: Get solution events from a Visual Studio package
http://www.mztools.com/articles/2014/MZ2014024.aspx

Targeting multiple Visual Studio versions with the same binary package assembly

I am these days porting my MZ-Tools add-in to a package, and as every other developer of extensions I would like to target several different Visual Studio versions (say 2010, 2012 and 2013 and “14”) with:

  1. The same project file (MyAssembly.csproj)
  2. The same binary assembly (MyAssembly.dll)
  3. The same setup (MyAssembly.vsix or MyAssemblySetup.exe)

About #1, you need to learn something about MSBuild but is doable. In my case I just want to use VS 2013 always and changing something in the .csproj file I can deploy and debug selectively to VS 2010, 2012 or 2013 experimental instances. But if you want to change nothing, you can open the same .csproj file in different VS versions and deploy and debug for that VS version as Daniel Cazzulino explained in his post How to create a Visual Studio extensibility project that is compatible with VS 2010, 2012 and 2013. Jared Parsons has also explained about Round Tripping a VSIX project.

About #2, if you stick to the assemblies provided by VS 2010 that are also supplied by higher VS versions it is doable too. I have used that approach for MZ-Tools being an add-in for years, as I explained in the article HOWTO: Create an add-in that targets several Visual Studio versions with the same add-in DLL using C# or VB.NET.

About #3, .vsix files already have support for several Visual Studio versions specified in the manifest. And if you create your own setup (MSI or otherwise) you have full control of the deployment.

Forcing Command UI refresh

As you know, whether you are an add-in developer or a package developer, Visual Studio calls your package or add-in to query the status of your commands when it requires to know it, for example, before showing the menu items of a menu, when the selected object has changed, etc. However, in rare occasions you may need to tell Visual Studio that you want it to query the status of your commands. A user of the MSDN VSX forum has asked it today, for example.

I was somewhat familiar with the solution for add-ins, which is to use the UpdateCommandUI method of the EnvDTE80.Commands2 interface (notice that the EnvDTE.Commands interface lacks that method, so you need to cast DTE.Commands to EnvDTE80.Commands2). Reviewing the code of my MZ-Tools add-in, certainly I needed to use it for a integration test.

I was also somewhat familiar with the solution for packages because I found the UpdateCommandUI method of the IVsUIShell by chance some time ago just perusing the MSDN documentation.