Wednesday, 1 August 2012

Good progress on VisualStudio Extension and couple more issues

Following from Adding an item to VisualStudio's Error List Window I've made quite a lot of progress on my VisualStudio Extension efforts, and have now created some really nice APIs and PoCs (if you want a sneak preview you can get it from the VisualStudioGallery: O2 Platform for VisualStudio 2010)

Here is where I'm am at the moment: 

  • My implementation is based on the Package and MEF objects (and the services I can get from there)
  • I now have a full REPL environment inside VisualStudio, so I can code VisualStudio dynamically :) (i.e when I'm working on my Extension I don't need to recompile the Extension assembly everytime I make a code change or want to try some DTE , MEF or WPF action)
  • I'm able to get an DTE object by using: packageObject.GetService(typeof(EnvDTE.DTE))  (I use a similar technique to get and ErrorListProviderIVsUIShellOleMenuCommandService objects)
    • In this case the packageObject implements Package and is marked with the [PackageRegistration(UseManagedResourcesOnly = true)]   attribute
  • Once I had the ErrorListProvider object (created using ErrorListProvider(packageObject) ) , I was able to add items to it 
  • To create windows I used the packageObject.CreateToolWindow(typeof(WindowPane_WPF), index++) ; 
    • The WindowPane_WPF is a class I added which implements ToolWindowPane and sets its Content to a vanila WPF grid (I also have a WindowPane_WinForms which sets the content to a System.Windows.Forms.Integration.WindowsFormsHost control
    • In order for this to work, I had to preregister them on packageObject classes with:
      • [ProvideToolWindow(typeof(WindowPane_WPF)     ,  MultiInstances = true,  Style=VsDockStyle.Linked, Orientation = ToolWindowOrientation.Top, Window=EnvDTE.Constants.vsWindowKindOutput)]
      • [ProvideToolWindow(typeof(WindowPane_WinForms), MultiInstances = true, Style = VsDockStyle.Linked, Orientation = ToolWindowOrientation.Top, Window = EnvDTE.Constants.vsWindowKindSolutionExplorer)]
  • A number of problems I had initially where caused by threading issues. My scripts run a separate thread and there where a number of cases where the request needs to be executed from the main VisualStudio thread (although I usually got weird COM errors which didn't point that way). I dealt with this problem by creating an extension method that used the Dispatcher object from System.Windows.Application.Current
  • I also had issues getting the Events I subscribed via the DTE.Events.BuildEvents callbacks, until I pinned the object DTE.Events.BuildEvents to a static variable in my class
  • I'm adding menus dinamically using dte.CommandBars["MenuBar"] (since I didn't want to hard code the menus in my extension dll)
  • I'm using the [ProvideAutoLoad(UIContextGuids80.NoSolution)]  on my packageObject to get a callback on VisualStudio load
  • I implemented a couple wrappers on some MEF exports which give me easy access to the WPF TextEditor: 
    • IWpfTextViewCreationListener (allows the manipulation of Code content and add new Adornments)
    • IWpfTextViewMarginProvider (allows adding a new margin or section to the Code provider)
Here are the issues/questions I currently have:
  • How to get a live instance of the AddIn object. I currently have a DTE object, but don't know where to get the EnvDTE.AddIn
    • This is the object that is passed as the addInInst parameter of the OnConnection method from the VsConnect : IDTExtensibility2, IDTCommandTarget  (I needed that in order to be able to correctly set the variables used by consumed add-ins
  • How to control the position of a new ToolWindowPane when created programatically.
    • If you noticed from the ProvideToolWindow attribute above, I use it to define the place where new instances of this window are created. My issue in that at the moment I don't seem to be able to set them programatically (specially the target Window GUID).
  • Faster way to add menu items. The dte.CommandBars works ok, but it still takes about a couple secs to do it (and it hangs the GUI) so, I was wondering if there was another way. I have access to the main WPF VisualStudio window, so maybe that will be faster. Note that I don't need to preregister these items as MenuCommands, I just want to get Action callback when they are clicked
  • Is the  [ProvideAutoLoad(UIContextGuids80.NoSolution)] the best way to get some code to run on VisualStudio load?