Archive

Author Archive

Extending FxCop Rules with fixing ability

June 28, 2011 14 comments

Whenever I see a tool telling a developer what to do to fix a problem and that problem is relatively easy to fix, I tend to label the creator of the tool as lazy. If you can find out a problem and know how to fix it, why don’t you offer the solution in place?

This is the case with static CodeAnalysis of Visual Studio. It issues FXCop rules to generate warnings/information in the form of "CAXXXX: Micorsoft.xxxx: the message”.

Wouldn’t it be nice to have the ability to right-click on the warning and tell it to fix it?

screenshot

Well, this is what this blog is about: extending FxCop rules with the ability to fix itself.

 

How it works

 

It’s actually very simple. We only have an ErrorItem object as input, retrieved from the ErrorList. An ErrorItem does not give you much more than textual information about the origin of the warning/error. No object linked to be used. Only the columns shown in the ErrorList are the properties of the ErrorItem. Luckely it shows the rule identity (CAXXXX). We can use this to look for the fix.

To gather the fixes we make use of an interface, IFixable.  The interface looks like this:

image

Members:

  • ErrorMessage: the VS Package will pass the description of the ErrorItem before calling the FixIt
  • IsBreaking: indicates when the fix is applied it will break the code
  • RuleIdentifier: here the implementor indicates the RuleId that will be fixed
  • FixIt(fileName, lineNr, Column): the actual fix. Notice nullable integers are used since the ErrorItem not always gives the needed information.

The VS Package has the responsibility of loading the assemblies and showing the command in the context menu. Some notifications (like when it is breaking) to the user are also added. It also provides a FileManager as a tool for loading and saving the file in the VS IDE.

The Package makes use of MEF to load the fixing assemblies. This means you’ll need to apply an export of the interface IFixable. The FileManager, mentioned above, can be imported using the IFileManager interface.

Example of an implementation:

using System.ComponentModel.Composition;
using Elbanique.FxCopRuleFixVSPackageLib;

namespace RecommendedRulesFixes.Design
{
    /// <summary>
    /// A delegate that handles a public or protected event does not have 
    /// the correct signature, return type, or parameter names.
    /// To fix a violation of this rule, correct the signature, 
    /// return type, or parameter names of the delegate. 
    /// </summary>
    [Export(typeof(IFixable))]
    public class CA1009 : IFixable
    {
        const string id = "CA1009";

        [Import(typeof(IFileManager))]
        private IFileManager _FileManager { get; set; }

        /// <summary>
        /// Gets the rule identifier.
        /// </summary>
        public string RuleIdentifier { get { return id; } }

        /// <summary>
        /// Gets a value indicating whether this instance is breaking.
        /// </summary>
        /// <value>
        /// <c>true</c> if this instance is breaking; otherwise, <c>false</c>.
        /// </value>
        public bool IsBreaking { get { return true; } }

        /// <summary>
        /// Gets or sets the error message.
        /// </summary>
        /// <value>
        /// The error message.
        /// </value>
        public string ErrorMessage { get; set; }

        /// <summary>
        /// Fixes the rule.
        /// </summary>
        /// <param name="originalFile">The original file.</param>
        /// <param name="lineNr">The line nr.</param>
        /// <param name="column">The column.</param>
        /// <returns>
        /// True if success, otherwise false.
        /// </returns>
        public bool FixIt(string originalFile, int? lineNr, int? column)
        {
            if (_FileManager == null)
                return false;

            _FileManager.Open(originalFile);
            string contextLine = null;
            if (lineNr.HasValue)
            {
                contextLine = _FileManager.GetLine(lineNr.Value -1);
                string fixedLine = FixLine(contextLine);
                _FileManager.ReplaceLine(lineNr.Value - 1, fixedLine);
                _FileManager.Save();
                return true;
            }

            return false;
        }

        private string FixLine(string contextLine)
        {
            //should be of type:  
            //public delegate void AlarmEventHandler(object sender, EventArgs e);
            MethodParser mp = MethodParser.Create(contextLine);
            if (mp.Return != "void") 
                mp.Return = "void";

            if (mp.Arguments[0].Type != "object")
                mp.Arguments[0].Type = "object";
           
            //should check if the argument is of type EventArgument, 
            //no compiled code, thus no can do.

            return mp.ToString();
        }
    }
}

Before you can use the FxCopRuleFixVSPackageLib you have to install the VSPackage. You can find it here.

The location after installation is %USER%\AppData\Local\Microsoft\VisualStudio\10.0\Extensions\Elbanique\FxCopRuleFixVSPackage\1.0\

Now there is still the issue of register the fixing assembly in the MEF container. This can be done by adding a MefComponent to the extension.vsixmanifest file (also located in the installation directory).

For example, presume you created a RecommendedRulesFixes.dll stored on a local drive c:\FxCopExtensions\ this should become:

<?xml version="1.0" encoding="utf-8"?>
<Vsix xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:xsd="http://www.w3.org/2001/XMLSchema" Version="1.0.0" xmlns="http://schemas.microsoft.com/developer/vsx-schema/2010">
  <Identifier Id="FxCopRuleFixVSPackage.Microsoft.87948990-c755-459d-b04d-4060ea76f5a4">
    <Name>FxCopRuleFixVSPackage</Name>
    <Author>Elbanique</Author>
    <Version>1.0</Version>
    <Description xml:space="preserve">This package is a tool that allows you to import (MEF) FxCop Rule repair assemblies. In the ErrorList, when selecting an CA-rule and an extension is found to fix it, the 'Fix It' item in the context menu will show up.</Description>
    <Locale>1033</Locale>
    <MoreInfoUrl>http://vsxexperience.net</MoreInfoUrl>
    <Icon>VS2010-FIXIT.png</Icon>
    <PreviewImage>screenshot.png</PreviewImage>
    <SupportedProducts>
      <VisualStudio Version="10.0">
        <Edition>Pro</Edition>
      </VisualStudio>
    </SupportedProducts>
    <SupportedFrameworkRuntimeEdition MinVersion="4.0" MaxVersion="4.0" />
  </Identifier>
  <References>
    <Reference Id="Microsoft.VisualStudio.MPF" MinVersion="10.0">
      <Name>Visual Studio MPF</Name>
    </Reference>
  </References>
  <Content>
    <VsPackage>FxCopRuleFixVSPackage.pkgdef</VsPackage>
    <MefComponent>FxCopRuleFixVSPackageLib.dll</MefComponent>
    <MefComponent>C:\FxCopExtensions\RecommendedRulesFixes.dll</MefComponent>
  </Content>
</Vsix>

 

— Any suggestions on improving the way of registering assemblies are welcome! —

NOTES:

  • You can imagine this is not the silver bullet because we have to work with a file as input. No CodeDOM or IL. Therefore, not all rules can be fixable. I’ve deliberately excluded the usage of EnvDTE or MPF when interfacing with fixing assemblies. If the future should prove the nenecessity, a supplementairy interface could be created passing the EnvDTE.
  • The package has an assembly (RecommendedRulesFixes.dll) included which – just for demo reasons – implements some CA rules (CA1009, CA1033, CA1305, CA2112, CA1801, CA2200). If there is enough response on this, I’ll consider to extend this assembly covering all CA rules.

Feature Builder Product Fiche

March 20, 2011 Leave a comment

I added a PDF document to the VSX Reference page. The pdf has 2 pages, one with a view for the user (or developer) and one for the creator (the Author).
I use it to give customers a quick view on feature builder to help them make a decission.
Free to use, ofcourse.

Feature Builder and Dynamic Visibility

March 10, 2011 2 comments

Recently I ran into the case where I had to make a context menu item dynamic visible. In this case the menu item must only be visible when the menu is activated on the context of a configuration file, thus when the file has the ‘.config’ extension.

With VSX this would be done by handling the BeforeQueryStatus event of the OleMenuCommand. Within Feature builder the separation between the behavior and execution is applied through the concept of LaunchPoint and Command. This allows you to write all execution logic within one library, while the Feature specific items are centralized around the package project. In my opinion, this is a very powerful concept making the package clean and specific, and the library reusable for other projects, especially when applying MEF.

So now the question: the LaunchPoint is located in the package project and holds the trigger for the BeforeQueryStatus (LaunchPoint inherits from OleMenuCommand), but I do not want to write this logic in the package project but in my library project.

If you take a closer look, you can find a QueryStatusStrategy property in the LaunchPoint class(*). It returns a IQueryStatusStrategy interface. This should ring a bell: interface..strategy… DI! Alright, it is setup to make use of MEF. So we can place our logic in the library and import it in the LaunchPoint.

(*) Also a OnQueryStatus method is available and could be used, but this would not follow the separation methodology.

Let’s apply this.

IQueryStatusStrategy

This interface only has one method to implement:

public interface IQueryStatusStrategy
{
    QueryStatus QueryStatus(IFeatureExtension feature);
}

where QueryStatus is a propertybag with 2 boolean properties, Enabled and Visible, and IFeatureExtension a reference to the extension calling this method

To get to the selected item we use the EnvDTE. To obtain it, import the SVsServiceProvider. Check on the extension and set the Visible property of the local created QueryStatus.

Wrapping it up results in this:

public class OnlyVisibleOnConfigQueryStrategy : IQueryStatusStrategy
{
    [Import]
    public SVsServiceProvider ServiceProvider { get; set; }

    public QueryStatus QueryStatus(IFeatureExtension feature)
    {
        QueryStatus queryStatus = new QueryStatus();
        queryStatus.Visible = false;
        queryStatus.Enabled = true;

        //
        //check to see if item is a config file (check on extension)
        //
        var dte = ServiceProvider.GetService<EnvDTE.DTE>();
        if (dte != null && dte.SelectedItems.Count > 0)
        {
            var firstItem = dte.SelectedItems.Item(1);
            if (firstItem != null &&
                firstItem.Name.EndsWith(".config"))
            {
                queryStatus.Visible = true;
            }
        }

        return queryStatus;
    }
}

 

Now we just have to export it to the MEF. The feature package already does the importing for the library project, so no extra effort needed here to make it visible in the container.

To export, place the export attribute on the class. I use a contractname to identify the strategy:

[Export("OnlyVisibleOnConfigQueryStrategy", typeof(IQueryStatusStrategy))]
public class OnlyVisibleOnConfigQueryStrategy : IQueryStatusStrategy

LaunchPoint

We cannot apply the import on the generated class, otherwise it would be overridden when regenerated. So create a partial class in the package project and implement the import of the QueryStatusStrategy on this partial class.

The full implementation of the LaunchPoint looks like this:

public partial class VsLaunchPointDynamic : VsLaunchPoint
{
    [Import("OnlyVisibleOnConfigQueryStrategy", typeof(IQueryStatusStrategy))]
    protected override IQueryStatusStrategy QueryStatusStrategy
    {
        get { return base.QueryStatusStrategy; }
        set { base.QueryStatusStrategy = value; }
    }
}

 
That’s all to it.

Working this way, you can build a strategy library with common scenario’s.

Maybe this can have a place in the Feature builder contrib.

Categories: Feature Builder Power Tool Tags: ,

Feature Builder Power Tool Review – Introduction

September 1, 2010 Leave a comment

Recently I installed the Feature Builder Power Tool from here. This VSIX file will install into the Visual Studio Extensions. You should be able to see it appear in your Installed Extensions of the Extension Manager (Tools > Extension Manager).

Important: currently it’s only available for the Visual Studio Ultimate edition to author the feature extension, though it can be deployed and used in the Professional edition.

Prerequisites: Visual Studio SDK

The Feature Builder Power Tool (FBPT) is intended as the successor of Guidance Automation, thus to make it easier to reuse code-based assets and practices by providing a predictable way of packaging and deploying them in Visual Studio. With the introduction of Visual Studio Visualization and Modeling (successor of DSL) and MEF there was a new set of tools within Visual Studio to achieve this goal. Besides the new tools Workflow is added, more later on this topic. As with the DSL tools, the package itself is build with it’s own package.

FBPT is build upon 3 core concepts: Map, Tools and Code.

  • Map: guidance for the developer and associated documentation
  • Tools: Visual Studio Automation
  • Code: artifacts

The package comes with a set of pre-configured project templates, which gives you a quick start. You can find them in the New Project dialog under the Extensibility node as shown in figure.

image

Let’s look which template contains what and what it is intended for.

  1. Info Pack Scenario: Guidance through documentation only; issues only the Map
  2. VSX Pack Scenario: Automation only; issues only the Tool
  3. Starter Pack Scenario: Documents and Source Code; issues the Map and Tools
  4. Starter Pack + Tools Scenario: Automation, Source Code and Documents; issues the Tools, Code and Map
  5. Blueprint Scenario: Automation, Source Code and Documents; issues the Tools, Code and an ”enhanced” Map (additional pre- and post-conditions on the guidance)
  6. Ultimate Blueprint Scenario: above + additional modeling features
  7. Modeling scenario: only the modeling features, issues an ”enhanced” Map

In my opinion, these project templates have no added value when developing a packages. On the other hand, I find them very useful for didactical usage, to explore the possibilities.

Beside the scenario focused project templates there are 2 “naked” project templates: Feature Extension for Premium And Ultimate and Feature Extension for Pro, Premium and Ultimate. Both have a simple one-step workflow and an empty project template. The later can contain tooling that leverages Architect Explorer and UML modeling.

When starting a new project one of the last 2 are recommended.

In future blogs I will first explain the 3 basic concepts and explain the interaction between them to establish a package. Automatically, we’ll hit one of the above project templates.

I should also mention, since the project templates are build with the FBPT itself, when opening a project from the list, it should already give you a good idea of the capabilities of FBPT.

Feature Builder Power Tool – release 1.0 coming very soon

Feature Builder is a Power Tool for Visual Studio 2010 promises that it wil help to easily create rich Visual Studio extensions. I should quickly package up sample code with custom menus, or create complete automated guidance experiences targeted toward a specific technology.
I’m very curious and wil shortly dive into it.

Keep track of http://visualstudiogallery.msdn.microsoft.com/en-us/396c5990-6356-41c0-aa20-af4c3e58c7ae to get the release.

The build process of an Integration Package (part I)

March 31, 2010 Leave a comment

This series of posts describe the inner workings of how a package is build with focus on the VSCT file. In this part we’ll ravel out the parts of the build process.

When building a package in Visual Studio, the project file is used as an input on which items to include in the build. When you open up a csproj file of a integration package project (unload the project and open for edit) you can find next items which are related to the package type project:

  • EmbeddedResource Include=”VSPackage.resx” with children MergeWithCTO and ManifestResourceName
  • None Include=”source.extension.vsixmanifest” with child SubType Designer
  • VSCTCompile Include”xxx.vsct” with children ResourceName and SubType Designer
  • A PropertyGroup with children TargetRegistryRoot, RegisterOutputPackage and RegisterWithCodebase
  • An Import tag with attribute Project=”…Microsoft.VsSDK.Targets”
    The VSPackage.resx is a resource for the package ;-) . You can find references to it in the InstalledProductRegistrationAttribute on the package class.

The source.extension.vsixmanifest is the designer for the VSIX manifest, it declares what type of extensions are provided by the content in the VSIX. For more info about VSIX files see Quan To’s post on VSIX.

The VSCT file is the source for the command table (see further). The <SubType>Designer</Subtype> on the vsct file tells VS to show a View Code/View Designer group in the context menu. In both cases it opens up a xml designer.

The PropertyGroup are options of the VSIX build process and can be found in the property page of the project in the VSIX tab.

All the previous items are used during the build process. This process is declared in the import tag pointing to a build targets file : “$(MSBuildExtensionsPath)\Microsoft\VisualStudio\v10.0\VSSDK\Microsoft.VsSDK.targets”. This file can be found typically in the “C:\Program Files\MSBuild\Microsoft\VisualStudio\v10.0\VSSDK” folder. When you open this folder you can find the Microsoft.VsSDK.targets, Microsoft.VsSDK.Common.targets files and an assembly Microsoft.VsSDK.Build.Tasks. These are the key players of the build process.

If we take a look at the tasks, we could categorize them as in beneath image:

VSSDK Build steps

  1. Check On VsSDK installation: checks if VsSDK is installed
  2. Compile VSCT: compiles the VSCT file into a CTO file (command table)
  3. Deploy VSIX: prepares for the deployment of the package
  4. Merge CTO resource: check on resources (like MenuResource) to merge into the cto file
  5. Generate Code from LEX/YACC files: the language token and lexical classes used to enable syntax coloring (see Babel)
  6. Generate PkgDef file: generates the package definition file which represents registry values that are created when an application is installed on a computer and that are referenced by the Visual Studio Shell when it starts the application.
  7. Generate Zip file: generates the Zip file to be deployed

In next post we’ll take a closer look at the tasks related to the VSCT file.

Categories: VSX Tags:

Writing to the VS ErrorList

March 23, 2010 4 comments

The Shell provides a class to gain access to the ErrorList pane : ErrorListProvider. It implements the Add/Remove of ErrorList items in the ErrorList pane. To gain access to this provider it expects a ServiceProvider in the constructor. This provider can be the GlobalService of the Package class.

An ErrorList item is a class (called ErrorTask) where you can set all needed properties to navigate to the location in the document, categorize it and show the message.

So, it’s a quite simple and natural way of showing items in the ErrorList. I’ll show you the details now.

The simpliest approach is to create a helper class that implements the IServiceProvider interface; the implementation body of this interface (GetService(Type)) is a call to the Package.GetGlobalService(serviceType). Now we enabled the construction of the ErrorListProvider with this helper class.

public class ErrorListHelper : IServiceProvider
{
   public object GetService(Type serviceType)
    {
        return Package.GetGlobalService(serviceType);
    }
}

Now we can instantiate the ErrorListProvider: it is important here to give the provider a name and a new guid. Try to avoid making a new (= new guid and name) ErrorListProvider for each call. Consider making a singleton to access the helper class, if so make it scope the package only (for example by making the class generic).

public class ErrorListHelper : IServiceProvider
{
    public ErrorListProvider GetErrorListProvider()
    {
        ErrorListProvider provider = new ErrorListProvider(this);//this implementing IServiceProvider
        provider.ProviderName = {name scoping package};
        provider.ProviderGuid = {guid scoping package};
        return provider;
    }
}

Now that we have the ErrorListProvider in place, we have to make the helper class to add and remove items from the list: this includes, creating a ErrorTask object and handle the event to navigate to the context document’s position.

Lets start taking a look on how to create a ErrorTask object:

public void Write(
            TaskCategory category,
            TaskErrorCategory errorCategory,
            string context, //used as an indicator when removing
            string text,
            string document,
            int line,
            int column)
{
    ErrorTask task = new ErrorTask();
    task.Text = text;
    task.ErrorCategory = errorCategory;
    //The task list does +1 before showing this numbers
    task.Line = line - 1;
    task.Column = column - 1;
    task.Document = document;
    task.Category = category;

    if(! string.IsNullOrEmpty(document))
    {
        //attach to the navigate event
        task.Navigate += NavigateDocument;
    }   
    GetErrorListProvider().Tasks.Add(task);//add it to the errorlistprovider

}

Now that we have the ErrorTask in place, we can take a look at the handling of the Navigate event. This is a bit of code that I have centralized in another helper class to be reusable.

void NavigateDocument(object sender, EventArgs e)
{
    Task task = sender as Task;
    if (task == null)
    {
        throw new ArgumentException("sender");
    }
    //use the helper class to handle the navigation
    ShellContext.OpenDocumentAndNavigateTo(task.Document, task.Line, task.Column);
}

What it must cover:

  • get the VS service to open the document (IVsShellOpenDocument)
  • open the document via the OpenDocumentViaProject; this gives you a pointer to the frame (IVsWindowFrame) hosting the document
  • use the frame to get the VsTextBuffer
  • get the VS service that manages the textbuffer (VsTextManager) and use it to navigate to the position in the document

Here is the peace of code that does this:

    public static void OpenDocumentAndNavigateTo(
                                    string path, int line, int column)
    {
        IVsUIShellOpenDocument openDoc =
            Package.GetGlobalService(typeof(IVsUIShellOpenDocument))
                    as IVsUIShellOpenDocument;
        if (openDoc == null)
        {
            return ;
        }
        IVsWindowFrame frame;
        Microsoft.VisualStudio.OLE.Interop.IServiceProvider sp;
        IVsUIHierarchy hier;
        uint itemid;
        Guid logicalView = VSConstants.LOGVIEWID_Code;
        if (ErrorHandler.Failed(
            openDoc.OpenDocumentViaProject(path, ref logicalView, out sp, out hier, out itemid, out frame))
            || frame == null)
        {
            return ;
        }  
        object docData;  
        frame.GetProperty((int)__VSFPROPID.VSFPROPID_DocData, out docData);  

        // Get the VsTextBuffer  
        VsTextBuffer buffer = docData as VsTextBuffer;  
        if (buffer == null )  
        {  
            IVsTextBufferProvider bufferProvider = docData as IVsTextBufferProvider;  
            if (bufferProvider != null )  
            {  
                IVsTextLines lines;  
                ErrorHandler.ThrowOnFailure(bufferProvider.GetTextBuffer(out lines));  
                buffer = lines as VsTextBuffer;  
                Debug.Assert(buffer != null, "IVsTextLines does not implement IVsTextBuffer");  
                if (buffer == null)  
                {  
                   return ;  
                }  
            }  
        } 
        // Finally, perform the navigation.  
        IVsTextManager mgr = Package.GetGlobalService(typeof(VsTextManagerClass))
             as IVsTextManager;  
        if (mgr == null)  
        {  
            return;  
        }  
        mgr.NavigateToLineAndColumn(buffer, ref logicalView, line, column, line, column);  
    }

To remove the ErrorTask from the list, make use of the same Guid/Name to re-instantiate the ErrorListProvider.  The context argument in the write method can be used to remove all ErrorTasks for this context. The handling with the context is not covered here, because this post is focussed on the element to make this work.

When removing you should use the SuspendRefresh() before removing and ResumeRefresh() after the items are removed to avoid flickering.

 With all this parts paste together it should do the job, for me it did ;-)

Categories: VSX Tags: ,

VS Menu CommandId’s listed

February 18, 2010 5 comments

Isn’t it hard, each time you want to attach your menuitem to a VS menu, to find the correct guid of that menu ? You can search in the vsshlids.h header file or set the EnableVSIPLogging registrykey to debug the VS (see Dr. eX’s Blog ). Second option is very helpfull but you have to write down the Id’s, no copy/past available here. You can imagine that this is very error proun. To avoid this repetitive handling, I decided to keep track of all the Id’s I found so far. This means this post will regulary be updated.

Following sections are covered:

Considerations

Attaching your menu to a VS menu

When you have found out the id of the VS Menu you want to attach to, you have to:

1. Define the id in the Symbols section of the vsct file.

The Guid is the GuidSymbol id, the Id the IDSymbol (listed in this post)

<!-- This is the guid used to group the Team Explorer’s My Favorits together -->
<GuidSymbol name="guidTE_MyFavoritesCmdSet" value="{7DD170F4-0855-4A2D-B3AA-7FB59A79D27D}">
      <IDSymbol name="MyFavorites" value="256" />
</GuidSymbol>

2. Use it as a parent in your Group

<!—Your command group attaching to the context menu of the MyFavorits tag in the Team Explorer window-->
<Group guid="guidMyPackageCmdSet" id="MyMenuGroup" priority="0x0600">
   <Parent guid="guidTE_MyFavoritesCmdSet" id="MyFavorites"/>
</Group>

Using dymamic visibility

When using dymamic visibility (= setting CommandFlag DynamicVisibility on your button in the vsct file and subscribing on the OnBeforeQueryStatus of the OleMenuCommand) you have to set the ProvideAutoLoadAttibute on your Package class to enable the querying.

[ProvideAutoLoad("{adfc4e64-0397-11d1-9f4e-00a0c911004f}")] //NoSolution
public sealed class MyPackage: Package
{
}

You can find the UIContext id’s in the vsshlid.h header file ({SDK directory} \VisualStudioIntegration\Common\Inc). For completeness I’ll state them here too.

UIContext Guid
SolutionBuilding {adfc4e60-0397-11d1-9f4e-00a0c911004f}
Debugging {adfc4e61-0397-11d1-9f4e-00a0c911004f}
FullScreenMode {adfc4e62-0397-11d1-9f4e-00a0c911004f}
DesignMode {adfc4e63-0397-11d1-9f4e-00a0c911004f}
NoSolution {adfc4e64-0397-11d1-9f4e-00a0c911004f}
SolutionExists {f1536ef8-92ec-443c-9ed7-fdadf150da82}
EmptySolution {adfc4e65-0397-11d1-9f4e-00a0c911004f}
SolutionHasSingleProject {adfc4e66-0397-11d1-9f4e-00a0c911004f}
SolutionHasMultipleProjects {93694fa0-0397-11d1-9f4e-00a0c911004f}
CodeWindow {8fe2df1d-e0da-4ebe-9d5c-415d40e487b5}
NotBuildingAndNotDebugging {48ea4a80-f14e-4107-88fa-8d0016f30b9c}
SolutionExistsAndNotBuildingAndNotDebugging {d0e4deec-1b53-4cda-8559-d454583ad23b}

Tip: you could place them in a separate class and use them as constants.

Lists

Team Explorer
Group: guidTE_TeamProjectServerCmdSet – GUIDSymbol: {23D49123-60AC-4D7E-939A-E01A4E176BEE}

Context IDSymbol
Team Foundation Server 256
Group: guidTE_MyFavoritesCmdSet – GUIDSymbol: {7DD170F4-0855-4A2D-B3AA-7FB59A79D27D}

Context IDSymbol
My Favorites 256
Group: guidTE_TeamProjectCmdSet - GUIDSymbol: {D309F791-903F-11D0-9EFC-00A0C911004F}

Context IDSymbol
Team Project 1799
Group: guidTE_WorkItemsCmdSet – GUIDSymbol: {2DC8D6BB-916C-4B80-9C52-FD8FC371ACC2}

Context IDSymbol
Work Items 513
Query Folder (*1) 514
Query (*2) 515

(*1): My Queries, Team Queries, Workbook Queries
(*2): MyBugs, MyTasks, OpenIssues, Product Backlog (Workbook Queries)…

Group: guidTE_BuildsCmdSet – GUIDSymbol: {34586048-8400-472E-BBBF-3AE30AF8046E}

Context IDSymbol
Builds 256
Build Definition 258
All Build Definitions 259
Categories: VSX Tags: , ,

VSXL 1 – Tackling commands and menus.

January 12, 2010 Leave a comment

This part will explain how we can reorganize the menus and commands to enable usage of an extension package.

To get around the workings of menus and commands I can recommend this blog by Inovak. Though it is written to build his VSXtra tool, it explains the inner workings of VS in context of menus and commands.

There is another blog by Inovak that explains the vsct file, which is a key part in the menus and commands workings (see below).

Next picture gives an overview of the parts that contribute in the menus and commands, based upon Inovak’s blog items.

Goal

We want the ability to add a (menu) command from a context menu when project is selected.

Let us take a look on what tasks should be automated when we use the current VSX approach:

  1. Update the vsct file and let the user give input on how and where the menu is displayed
  2. When dynamic menu, a BeforeQueryStatus eventhandler is needed
  3. Update the initialize method of the custom ‘Package.cs’ file to add the menucommand to the menucommand service

The first problem here is, there is no physical file for the command. All coding is done in the ‘package.cs’ file. So, let’s try to migrate the code from the package file to a separate command file.

The Command file.

These are the things that are directly related to the command and can be put in a separate class:

-          The Id of the command: a uint

-          The id of the commandset the command is belonging to : a guid

-          The implementation of execution of the command

-          [Optional] Visibility handling: via BeforeQueryStatus

Regarding the command id; in the MS solution they are gathered in the ‘PkgCmdId.cs’ file. This is not a bad idea since they are a range of id’s and no guids. For example, 0×100 is the first, 0×101 is the second, and so on. Otherwise, it would be nice to have them tightly coupled to the command file itself. So a reference to the id is the preferable solution.

Regarding the commandset id; in the MS solution they are gathered in the ‘GuidList.cs’ file. Since this is a guid and if we make use of a CommandGroup class as a holder of the id, I don’t see the benefit of using this.

Since a command is bound to its package, a reference to this package is also needed.

The CommandGroup itself is merely a representation of the Id. If we want to query which commands are attached, we can either use the VSCT file or query the Commands in the package that point to a specific CommandGroupClass.

The ‘CustomCommand’ is a set of 2 partial classes. One is generated by our integration package (storing the id’s and reference to the type of CommandGroup), another is presented to the developer to implement the base methods. This is called the double-derived pattern. In that way we can regenerate the custom command without removing the implementation.

A second task is to be done when generating a command class: managing the ‘PkgCmdId.cs’ file. Before loading our integration package command, we could load this class into memory and keep it there for the lifetime of the command execution. It should be presented as a package service so we could call something like ‘CreateNewCmdId(string commandName)’. The management and synchronization should be handled by this service. We’ll tackle this issue in a future blog, which will probably be: tackling the services.  

Notice, that to be able to register the command we need to call the IMenuCommandService.  Since the called method ‘GetService’ is declared on the Package class, we can only access it from there. For now, we’ll use a utility to retrieve all the Command classes that need to be registered. Likewise, it will be refactored when tackling the services.

A last task is to keep in sync with the VSCT file. We can make use of XDocument to readout and write to. It should not allow making any changes to the command ids. To make things a little bit easier we can make use of the VS internal RDT (running document table).  To keep in scope, we’ll dedicate this in a separate blog.

VSCT revised

There are a few things that one could change about the way the VSCT file is used. Since it is only a declarative way of showing the command table definitions, it is possible to handle the building of the resources ourselves and create a resource per command. But this would mean that a custom postbuild tool should be made to create this resource. Since the added value of this is minor, I don’t think it’s a good idea.

On the other hand, creating a custom editor for managing the VSCT file looks like a better idea. Of course, I’m not the only one who thought of that: VSCT PowerToy, available at the MSDN code gallery [http://code.msdn.microsoft.com/VSCTPowerToy]. With this tool you cannot only manage your own command table but also look at other packages’ command tables!  It’s a nice way to learn on how to build your own ones.

In the next blog we will show you the coding of this all.

Categories: VSX, VSXL Tags: , ,

Part 2 : Decide Approach

January 2, 2010 Leave a comment

In previous post I pointed out where I would be focussing on to achieve a better DX with VSX integration package:

  • Refactoring MPF in context of usability
  • Writing a new integration package project

Now, after some considerations made, I’ve decided to change my approach. What I realy want to achieve is to be able to present tools to fasten up the development and lower the threshold. Herefore I only need a integration package for the VSX integration package to automate common tasks.

Refactoring the MPF is a good plan, but actually, the only thing that should be refactored is the top-layer, the way MPF organizes itself. I’ve looked into refactoring it and notice that it tantamounts to change the way it registers components (commands, toolwindows, services). Also, the core of all is enclosed in one file, namely the Package class.

Refactoring would take a massive part of my time, so I looked around to see if someone else would have made the effort. I found a need project, http://vsxtra.codeplex.com/. This project does all the refactoring. I considered to use it as a base to create my own integration package. I will not use it because of an extra dependency into my package.

It is still my intention to write a new integration package project. I can start with an Integration package to later migrate to a seperate project template.

So to conclude: I will focus on wrap the most of the MPF with a thin layer to automate small pieces of functionality. This way of working allows me to gradually increase the scope and leverage very fast small pieces of automated task without impacting existing VSX packages.

I also named the project VSXL. So from now on I’ll will use this to address the Visual Studio Integration Package Integration Package.

Follow

Get every new post delivered to your Inbox.