Feature Builder … some really cool nuggets on Codeplex and remembering a deprecated HOL

The Rangers Visual Studio 2010 Architecture Guidance is in the final revision phase, whereby the guidance will deliver answers to questions, practical guidance for scenarios such as reverse engineering, traceability and validation, as well as a platter of hands-on-labs. One of the hands on labs has just been re-written, using a new nugget that the architecture product group has published on Codeplex.

The nuggets

 

The “How to Create a Standalone feature Extension” is the one we used to re-work one of our hands-on-labs to show how to create guidance that lives within the Integrated Development Environment … yes, no more context switching between the IDE and guidance documentation :) We will be using this same concept to deliver the architecture guidance as an optional extension, giving you the option of using the old-style guidance or bringing the guidance into the IDE.

Parts of the deprecated HOL, which contains some other interesting information

The difference between the FBGX #2 – How to Create a Standalone Feature Extension and deprecated HOL, is that we included the logic to create a solution, if not exists, and then adding the guidance to the new or existing solution. While we decided to deprecate it from our guidance HOL, it is still interesting and useful magic  … hence we remember the HOL in this post.

The following extract describes the steps needed to complete the following objectives we set out to complete as part of the HOL, whereby we assume, for the sake of this blog, that we have just created a standard feature extension, using the “Feature Extension” project template and calling it GuidanceExtension.

Our objectives are to create a custom extension which delivers the following features:

  • Feature Extension that is available without having to use the New Project dialog.
  • Load in the so-called “No Solution” state (i.e. when VS loads)
  • Expose a menu item somewhere (e.g. Architecture menu).
  • Import (via MEF) the FeatureManager (the extensibility runtime’s master-control-center)
  • Use (when the menu item is selected) the FeatureManager API to locate your Feature Extension and then instantiate that feature into the solution.

Let’s go through the steps we send into the deprecated world and lose the learning's …

  1. Go to the GuidanceExtension project.
  2. Add the VSPackageGuide project as a reference. 
     
  3. Go to the VSPackageGuide project and edit the file VSPackageGuide.cs.
  4. Add the following imports to the beginning of the file:
    • using Microsoft.VisualStudio.TeamArchitect.PowerTools.Features;
      using Microsoft.VisualStudio.ComponentModelHost;
      using System.ComponentModel.Composition;
      using System.Linq;
  5. Add the following references to the project:
    • System.ComponentModel.Composition, which you will find under .NET assemblies.
    • Microsoft.VisualStudio.CommandBars, which you will find under .NET assemblies.
    • Microsoft.VisualStudio.ComponentModelHost, which you will find under .NET assemblies.
    • Microsoft.VisualStudio.ExtensionManager, which you will find under C:\Windows\Microsoft.Net\assembly\GAC_MSIL\Microsoft.VisualStudio.ExtensionManager\v4.0_10.0.0.0__b03f5f7f11d50a3a.
    • Microsoft.VisualStudio.TeamArchitect.PowerTools, which are part of the Visual Studio Architecture Feature Pack and can be found in your local Appdata. …AppData\Local\Microsoft\VisualStudio\10.0
      Extensions\Microsoft\Feature Extension Runtime\0.9
    • Microsoft.VisualStudio.TeamArchitect.PowerTools.Features, which are part of the Visual Studio Architecture Feature Pack and can be found in your local Appdata. …AppData\Local\Microsoft\VisualStudio\10.0\Extensions\Microsoft\Feature Extension Runtime\0.9 
       
  6. Add the MEF import manager which we need to instantiate features, by adding the following code in bold to the VSPackageGuidePackage class:
  7. Take note of the modified code as shown in bold text:
    • [ProvideMenuResource("Menus.ctmenu", 1)]
      [Guid(GuidList.guidVSPackageGuidePkgString)]
      public sealed class VSPackageGuidePackage : Package
      {
          [Import]
          public IFeatureManager theFeatureManager { get; set; }
         
  8. To satisfy the MEF import, add the following lines in bold:
    • protected override void Initialize()
      {
          var componentModel = this.GetService(typeof(SComponentModel)) as IComponentModel;
          componentModel.DefaultCompositionService.SatisfyImportsOnce(this);  
          …
  9. Implement the menu item call back method, by replacing the default implementation with the following code in bold to the MenuItemCallback method:… ~ line 81
    • private void MenuItemCallback(object sender, EventArgs e)
      {
      IFeatureRegistration featureRegistration = theFeatureManager.InstalledFeatures .FirstOrDefault(installedFeature => installedFeature.FeatureId.Equals("GuidanceExtension",  
      StringComparison.InvariantCultureIgnoreCase));
          if (featureRegistration == null)
          {
              // Show a Message Box to prove we were here
      IVsUIShell uiShell = (IVsUIShell)GetService(typeof(SVsUIShell));
              Guid clsid = Guid.Empty;
              int result;
      Microsoft.VisualStudio.ErrorHandler. ThrowOnFailure(uiShell.ShowMessageBox( 0, ref clsid, "Demo Package", string.Format(CultureInfo.CurrentCulture, "{0}",
      "Could not find GuidanceExtension"), string.Empty, 0, OLEMSGBUTTON.OLEMSGBUTTON_OK,
      OLEMSGDEFBUTTON.OLEMSGDEFBUTTON_FIRST, OLEMSGICON.OLEMSGICON_INFO, 0, out result));
         }

      //
      // get an instance of the VS Solution
      //
      IVsSolution vsSolution = (IVsSolution)GetService(typeof(SVsSolution));

      //
      // Get an instance to the DTE
      //
      EnvDTE.DTE dte = (EnvDTE.DTE)GetService(typeof(EnvDTE.DTE));
      // Check if we have a solution and create one if not
      if (!dte.Solution.IsOpen)
      vsSolution.CreateSolution(string.Empty, @"GuidanceExtension.sln", (int)(__VSCREATESOLUTIONFLAGS.CSF_TEMPORARY | __VSCREATESOLUTIONFLAGS.CSF_SILENT));

          //
          // Ok, we've got our feature, let's instantiate it
          //
          string newFeatureExtensionInstanceName =  
          GenerateFeatureInstanceName(theFeatureManager,
      featureRegistration);

          //
          // Then create the feature instance, saving the reference
          // for use in RunFinished
          //
      IFeatureExtension newFeatureExtension = theFeatureManager.Instantiate(featureRegistration.FeatureId, newFeatureExtensionInstanceName);

          #if !RTM_FEATURE_BUILDER
          FeatureManager fm = theFeatureManager as FeatureManager;
      fm.InitializeFeature(featureRegistration.FeatureId, newFeatureExtensionInstanceName, featureRegistration, featureRegistration.ExtensionManifest.Header.Version, false);
          #endif
      }

  10. Add the following method needed to create a unique feature instance name and allow multiple instantiations.
    • internal string GenerateFeatureInstanceName(IFeatureManager mgr,
      IFeatureRegistration reg)
      {
      int index = 1;
      var instanceName = reg.DefaultName + index;
      string baseProjectName = reg.DefaultName;
      while (mgr.InstantiatedFeatures.Any(f => f.InstanceName.Equals(instanceName,StringComparison.OrdinalIgnoreCase)))
      {
      instanceName = baseProjectName + ++index;
      }
      return instanceName;
      }
       
  11. Take note of the following:
    • We defined GuidanceExtension as the extension to launch at the beginning of the callback method. If we change the extension we must change the name we look for in the callback method as well.
    • The !RTM_FEATURE_BUILDER region include interim code to work around a current issue and will not be required in future.
  12. We also have to let our manifest know that we want to have another VS package and that it is MEF enabled.
  13. Expand project GuidanceExtension and then folder Visual Studio.
  14. Right-click on source.extension.vsixmanifest and select view code.
    • <Content>
      <ItemTemplate>Templates\Items</ItemTemplate>
      <ProjectTemplate>Templates\Projects</ProjectTemplate>
      <VsPackage>|GuidanceExtension;PkgdefProjectOutputGroup|</VsPackage>
      <MefComponent>|GuidanceExtension|</MefComponent>
      <MefComponent>|GuidanceExtensionLib|</MefComponent>
      < VsPackage > |VSPackageGuide;PkgdefProjectOutputGroup|</VsPackage>
      < MefComponent > |VSPackageGuide|</MefComponent>
      </Content> 
       
  15. Done … you could now build the solution and use the extension at this point. The next step, however, would be to go back to the activity workflow diagram and to define the guidance map.

‘till next time.