Logo
HOWTO: Add an error with navigation to the Error List from a Visual Studio add-in

Author: Carlos J. Quintero (Microsoft MVP) Applies to: Microsoft Visual Studio 2005
Date: August 2008   Microsoft Visual Studio 2008
Updated: March 2013   Microsoft Visual Studio 2010
      Microsoft Visual Studio 2012
Introduction

Visual Studio 2005 (and higher) provides an Error List window ("View", "Error List" menu) to show errors from compilers, etc. Errors can be truly Errors, Warnings or Messages.

The automation model (EnvDTE80) provides the EnvDTE80.ErrorList and the EnvDTE80.ErrorItems collection. The ErrorItems collection lacks the Add method, but you could use an alternate approach:

However, the OutputTaskItemString doesn't show the project of in Project column and doesn't provide navigation to the document when double-clicking an item in the list. This article shows an alternate approach to overcome these limitations.

More Information

The automation model (EnvDTE80) of Visual Studio provides the EnvDTE80.ErrorList class to represent the Error List. You can get an instance of this class as explained in the following article:

HOWTO: Get the programmable inner object of a toolwindow

To get more information about the EnvDTE80.dll assembly, see the following article:

INFO: Assemblies used in Visual Studio Extensibility

The ErrorItems property provides the collection of errors, but not the Add The ErrorItems property provides the collection of errors, but it lacks an Add method and the OutputWindowPane.OutputTaskItemString method has some limitations. Supposedly only Visual Studio packages can fully contribute to the error list implementing an ErrorListProvider. The Microsoft.VisualStudio.Shell.dll assembly provides an ErrorListProvider implementation that will be used from an add-in to add errors to the Error List.

The project sample needs references to the following assemblies:

  • Microsoft.VisualStudio.OLE.Interop.dll
  • Microsoft.VisualStudio.Shell.dll
  • Microsoft.VisualStudio.Shell.Interop.dll
  • Microsoft.VisualStudio.Shell.Interop.8.0.dll

To know how to add these assemblies as references to a project, see the following article:

HOWTO: Reference a Visual Studio assembly in the GAC from an add-in

The following add-in adds when loaded three errors (an error, warning and message) to the Error List associated to the first three lines of the first file of the first project of the solution (ensure that there is a solution loaded when loading the add-in). Notice that errors should be removed from the list when their project is removed from the solution, or when the solution is unloaded. It also shows how to navigate to the line of the file when the error is double-clicked.

Imports SystemvDTE
Imports EnvDTE80
Imports System.Windows.Forms
Imports Microsoft.VisualStudio.Shell
Imports System.Runtime.InteropServices
Imports Microsoft.VisualStudio
Imports Microsoft.VisualStudio.Shell.Interop
Imports System.Collections.Generic

Public Class Connect
   Implements IDTExtensibility2
   Implements System.IServiceProvider

   Private m_objDTE As DTE
   Private m_objErrorListProvider As ErrorListProvider
   Private m_colErrorTasks As List(Of ErrorTask)
   Private WithEvents m_objSolutionEvents As SolutionEvents

   Public Sub OnConnection(ByVal application As Object, ByVal connectMode As ext_ConnectMode, _
      ByVal addInInst As Object, ByRef custom As Array) Implements IDTExtensibility2.OnConnection

      m_objDTE = CType(application, DTE)

      Select Case connectMode

         Case ext_ConnectMode.ext_cm_AfterStartup
            OnStartupComplete(Nothing)

         Case ext_ConnectMode.ext_cm_Startup
            ' OnStartupComplete will be called automatically

      End Select

   End Sub

   Public Sub OnDisconnection(ByVal disconnectMode As ext_DisconnectMode, ByRef custom As Array) _
      Implements IDTExtensibility2.OnDisconnection

      RemoveTasks()

   End Sub

   Public Sub OnAddInsUpdate(ByRef custom As Array) Implements IDTExtensibility2.OnAddInsUpdate
   End Sub

   Public Sub OnBeginShutdown(ByRef custom As Array) Implements IDTExtensibility2.OnBeginShutdown
   End Sub

   Public Sub OnStartupComplete(ByRef custom As Array) Implements IDTExtensibility2.OnStartupComplete

      Try

         m_objSolutionEvents = m_objDTE.Events.SolutionEvents

         InitializeErrorListProvider()

         AddSampleErrors()

      Catch objException As Exception
         MessageBox.Show(objException.ToString, "Error", MessageBoxButtons.OK, MessageBoxIcon.Error)
      End Try

   End Sub

   Private Sub InitializeErrorListProvider()

      m_colErrorTasks = New List(Of ErrorTask)

      m_objErrorListProvider = New Microsoft.VisualStudio.Shell.ErrorListProvider(Me)

      m_objErrorListProvider.ProviderName = "My Error Provider"

      ' TODO: (IMPORTANT) Use your OWN Guid here: 
      m_objErrorListProvider.ProviderGuid = New Guid("5BA8BB0D-D517-45ae-966C-864C536454F1")

      m_objErrorListProvider.Show()

   End Sub

   Private Sub AddSampleErrors()

      Dim objProject As Project
      Dim objProjectItem As ProjectItem

      If Not m_objDTE.Solution.IsOpen Then
         MessageBox.Show("Open or create a solution.", "Error", MessageBoxButtons.OK, MessageBoxIcon.Error)
      Else

         If m_objDTE.Solution.Projects.Count = 0 Then
            MessageBox.Show("Open or create a solution with at least one project.", "Error", MessageBoxButtons.OK, MessageBoxIcon.Error)
         Else

            objProject = m_objDTE.Solution.Projects.Item(1)

            If objProject.ProjectItems.Count = 0 Then
               MessageBox.Show("Open or create a project with at least one file.", "Error", MessageBoxButtons.OK, MessageBoxIcon.Error)
            Else
               objProjectItem = objProject.ProjectItems.Item(1)

               AddErrorToErrorList(objProject, objProjectItem, "My custom error", TaskErrorCategory.Error, 1, 1)
               AddErrorToErrorList(objProject, objProjectItem, "My custom warning", TaskErrorCategory.Warning, 2, 1)
               AddErrorToErrorList(objProject, objProjectItem, "My custom message", TaskErrorCategory.Message, 3, 1)

            End If

         End If

      End If

   End Sub

   Private Sub AddErrorToErrorList(ByVal objProject As Project, ByVal objProjectItem As ProjectItem, _
      ByVal sErrorText As String, ByVal eTaskErrorCategory As TaskErrorCategory, ByVal iLine As Integer, _
      ByVal iColumn As Integer)

      Dim objErrorTask As ErrorTask
      Dim objIVsSolution As Microsoft.VisualStudio.Shell.Interop.IVsSolution
      Dim objVsHierarchy As IVsHierarchy = Nothing

      Try

         objIVsSolution = DirectCast(GetService(GetType(SVsSolution)), IVsSolution)

         ErrorHandler.ThrowOnFailure(objIVsSolution.GetProjectOfUniqueName(objProject.UniqueName, objVsHierarchy))

         objErrorTask = New Microsoft.VisualStudio.Shell.ErrorTask
         objErrorTask.ErrorCategory = eTaskErrorCategory
         objErrorTask.HierarchyItem = objVsHierarchy
         objErrorTask.Document = objProjectItem.FileNames(0)
         ' VS uses indexes starting at 0 while the automation model uses indexes starting at 1
         objErrorTask.Line = iLine - 1
         objErrorTask.Column = iColumn

         objErrorTask.Text = sErrorText
         AddHandler objErrorTask.Navigate, AddressOf ErrorTaskNavigate

         m_colErrorTasks.Add(objErrorTask)

         m_objErrorListProvider.Tasks.Add(objErrorTask)

      Catch objException As Exception
         MessageBox.Show(objException.ToString, "Error", MessageBoxButtons.OK, MessageBoxIcon.Error)
      End Try

   End Sub

   Private Sub ErrorTaskNavigate(ByVal sender As Object, ByVal e As EventArgs)

      Dim objErrorTask As ErrorTask
      Dim bResult As Boolean

      Try
         objErrorTask = CType(sender, ErrorTask)

         objErrorTask.Line += 1 ' Fix the index start
         bResult = m_objErrorListProvider.Navigate(objErrorTask, New Guid(EnvDTE.Constants.vsViewKindCode))
         objErrorTask.Line -= 1 ' Restore the index start

         If Not bResult Then
            MessageBox.Show("Error navigating to " & objErrorTask.Text, "Error", MessageBoxButtons.OK, MessageBoxIcon.Error)
         End If

      Catch objException As Exception
         MessageBox.Show(objException.ToString, "Error", MessageBoxButtons.OK, MessageBoxIcon.Error)
      End Try

   End Sub

   Private Function GetService(ByVal serviceType As System.Type) As Object _
      Implements System.IServiceProvider.GetService

      Dim objService As Object = Nothing

      Try
         objService = Microsoft.VisualStudio.Shell.Package.GetGlobalService(serviceType)
      Catch objException As Exception
         MessageBox.Show(objException.ToString, "Error", MessageBoxButtons.OK, MessageBoxIcon.Error)
      End Try

      Return objService

   End Function

   Private Sub m_objSolutionEvents_BeforeClosing() Handles m_objSolutionEvents.BeforeClosing

      RemoveTasks()

   End Sub

   Private Sub m_objSolutionEvents_ProjectRemoved(ByVal objProject As EnvDTE.Project) _
      Handles m_objSolutionEvents.ProjectRemoved

      Dim objErrorTask As ErrorTask
      Dim objErrorTaskProject As Project
      Dim objObject As Object = Nothing

      Try

         If Not (m_colErrorTasks Is Nothing) Then

            For iCounter As Integer = m_colErrorTasks.Count - 1 To 0 Step -1

               objErrorTask = m_colErrorTasks.Item(iCounter)

               ErrorHandler.ThrowOnFailure(objErrorTask.HierarchyItem.GetProperty( _
                  VSConstants.VSITEMID_ROOT, __VSHPROPID.VSHPROPID_ExtObject, objObject))

               If TypeOf objObject Is Project Then

                  objErrorTaskProject = DirectCast(objObject, Project)

                  If objErrorTaskProject.UniqueName = objProject.UniqueName Then

                     RemoveTask(objErrorTask)

                  End If

               End If

            Next

         End If

      Catch objException As Exception
         MessageBox.Show(objException.ToString, "Error", MessageBoxButtons.OK, MessageBoxIcon.Error)
      End Try

   End Sub

   Private Sub RemoveTasks()

      Dim objErrorTask As ErrorTask

      Try

         If Not (m_colErrorTasks Is Nothing) Then

            For iCounter As Integer = m_colErrorTasks.Count - 1 To 0 Step -1

               objErrorTask = m_colErrorTasks.Item(iCounter)

               RemoveTask(objErrorTask)

            Next

         End If

      Catch objException As Exception
         MessageBox.Show(objException.ToString, "Error", MessageBoxButtons.OK, MessageBoxIcon.Error)
      End Try

   End Sub

   Private Sub RemoveTask(ByVal objErrorTask As ErrorTask)

      Try

         m_objErrorListProvider.SuspendRefresh()

         RemoveHandler objErrorTask.Navigate, AddressOf ErrorTaskNavigate

         m_colErrorTasks.Remove(objErrorTask)

         m_objErrorListProvider.Tasks.Remove(objErrorTask)

      Catch objException As Exception
         
         MessageBox.Show(objException.ToString, "Error", MessageBoxButtons.OK, MessageBoxIcon.Error)
         
      Finally
      
         m_objErrorListProvider.ResumeRefresh()
         
      End Try

   End Sub

End Class

Related articles


Go back to the 'Resources for Visual Studio .NET extensibility' section for more articles like this


Top