Click here to Skip to main content
65,938 articles
CodeProject is changing. Read more.
Articles / desktop / WinForms

Allow Users to Report Meaningful Defect Information

4.68/5 (11 votes)
30 Jan 200711 min read 1   279  
This article describes an approach to providing users with the means to submit error information directly back to the software developers as errors are encountered during the use of a targeted application

Introduction

This article describes an approach to providing users with the means to submit error information directly back to the software developers as errors are encountered during the use of a targeted application. The approach described in this article uses a custom error dialog to display fault information to the user and further describes an approach by which the users can submit the error information directly back to the application developer's by means of a web service.

The approach is intended to be similar to the approach used within the Microsoft Windows operating system where the operating system provides the users with the means to send application error related information back to Microsoft for analysis.

Use of the approach as described in this article should yield information about the performance of a fielded or beta application. The approach demonstrated uses the exception message and stack trace as the source of information regarding specific exceptions encountered by the users; such information will identify the class, method, and the line of code where the exception occurred. Periodic review of the messages and stack traces encountered should provide sufficient information to the development staff to determine what if any problems are being encountered by users of the application and should clearly define specific failure points that may be targeted for repair. While user narrative type reports are necessary for conditions occurring within application that do not trigger an exception, in most instances, a stack trace would be far more useful than a user's description of any given problem.

Since the approach relies upon a web service as the vehicle for making the report it is obvious that the application will require an internet connection in order to enable this feature. Without a current internet connection, the application will not be able to report on an error. Further, the approach demonstrated herein relies upon the user providing consent as a precursor to sending the error information; if the user does not provide such consent, the error will not be reported. Of course it would be possible to send the message without the user's consent, however, when the application attempts to connect to the web service, the user will likely be warned of the connection attempt which may cause some suspicion about the intent of the application.

Image 1

Figure 1. Generating, Displaying, and Reporting Errors

Image 2

Figure 2. Viewing Defects Online

Getting Started

There are two solutions included with this download, one is win forms application that demonstrates the construction and use of the custom error dialog, the other is a web solution that demonstrates the construction of the bug reporting web service.

Figure 3 (below) shows the solution explorer for the project. The win forms application is shown at the top and contains a web reference to the bug reporting web service. It also contains two forms: Form1 is just a test container used to generate errors and evoke the display of the custom error dialog, the second form (ErrorReport) is used to display the error information to the user and to provide the user with option of viewing the stack trace and sending the error report to the web service.

Aside from these items, the win forms application ("BugReport") includes a resource file which contains three icons. These icons are used with the custom error dialog to show the appropriate icon on the form when it is displayed (e.g., an exclamation point icon, a denied icon, and an information icon).

The web solution contains a single web service called "ReportBug" as a well as a web page (Default) used to display all of the current reports contained in the defect database (which in this demo is an SQL server database called "BugTracker" and in a table called "BugReports". The database can of course be any type of database desired (e.g., Oracle, SQL Server, etc.). Even Microsoft Access could be used however Microsoft cautions against the use of OLEDB connections when working with a web services due mainly to security and permissions issues.

Image 3

Figure 3. Solution Explorer with Both Projects Visible

In this example, the web.config file was set up to run with Windows Authentication and the database users and permissions were configured to support this type of authentication. Depending upon your needs this may or may not be a satisfactory solution so feel free to set the users and permissions up according to your requirements should you want to implement the approach; the code in the demo is not dependent upon this part of the configuration so long as the set up allows the web service to connect to the database and perform an insert when the request is originated from the win forms application.

Database Setup.

For the purposes of this demonstration, an SQL service database was set up and called "BugTracker"; within this database, a single table was added ("BugReports"). That table included the following columns:

Image 4

Figure 4. The Bug Reports Table Definition

Naturally you may create your own table and add and remove columns from it as you see fit. Further, you may wish to add a stored procedure to handle the insert command; for the purposes of this demo, I did not add a stored procedure and I performed the inserts using dynamic SQL. Certainly using a stored procedure would make for a more robust and safer design.

The Web Project

Code: ReportBug Web Service.

The web service is fairly straight forward, there is a single web service contained in ReportBug.asmx and using the ReportBug.vb code behind file. The imports and declaration of this class are as follows:

VB.NET
Imports System.Web
Imports System.Web.Services
Imports System.Web.Services.Protocols
Imports System.Data
Imports System.Data.Sql
Imports System.Data.SqlClient

<WebService(Namespace:="http://localhost/BugTracker")> _
<WebServiceBinding(ConformsTo:=WsiProfiles.BasicProfile1_1)> _
<Global.Microsoft.VisualBasic.CompilerServices.DesignerGenerated()> _
Public Class ReportBug
Inherits System.Web.Services.WebService

In addition to the default imports, I've added System.Data, System.Data.SQL, and System.Data.SQLClient. These imports are necessary to support performing the insert to the SQL Server database. The class declaration is per the default created when the web service was added to the project.

The web service contains a single web method which is shown in the following; the section is annotated with comments and should be easy enough to understand.

VB.NET
''' <summary>
''' This web method (Report) loads the error and application
''' data into an SQL Server 2000 database table
''' </summary>
''' <param name="sDomain"></param>
''' <param name="sUser"></param>
''' <param name="sComputer"></param>
''' <param name="sApplication"></param>
''' <param name="sErrorMessage"></param>
''' <param name="sErrorStackTrace"></param>
''' <returns>Boolean, True if data loads successfully</returns>
''' <remarks>Demo uses access, could be any database</remarks>
<WebMethod()> _
Public Function Report(ByVal sDomain As String, _
                       ByVal sUser As String, _
                       ByVal sComputer As String, _
                       ByVal sApplication As String, _
                       ByVal sErrorMessage As String, _
                       ByVal sErrorStackTrace As String) As Boolean
    ' set up connection string
    Dim sConnectionString As String
    sConnectionString = "Data Source=(local);Initial Catalog=BugTracker;" & _
                        "Integrated Security=True"

    ' create connection
    Dim conn As SqlConnection = New SqlConnection(sConnectionString)
    Try
        ' insert the bug report data into the database
        ' format an insert statement
        Dim strBuilder As New System.Text.StringBuilder
        strBuilder.Append("INSERT INTO [BugReports] ")
        strBuilder.Append("([UserDomain], [UserName], [UserComputer], " & _
                          "[UserApplication], [ErrorMessage], " & _
                          "[ErrorStackTrace]) VALUES(")
        strBuilder.Append("'" & sDomain & "', ")
        strBuilder.Append("'" & sUser & "', ")
        strBuilder.Append("'" & sComputer & "', ")
        strBuilder.Append("'" & sApplication & "', ")
        strBuilder.Append("'" & sErrorMessage & "', ")
        strBuilder.Append("'" & sErrorStackTrace & "')")

        ' convert the stringbuilder into a string and create a command
        Dim sSQL As String = strBuilder.ToString()
        Dim cmd As SqlCommand = New SqlCommand(sSQL, conn)

        ' load the data
        Try
            conn.Open()
            cmd.ExecuteNonQuery()
            conn.Close()
        Catch ex As Exception
            conn.Close()
            Return False
        End Try

     Catch ex As Exception
        conn.Close()
        Return False
     End Try
     conn.Close()
     Return True
End Function

The web method accepts all of the required information to perform an insert as arguments, these values are passed to an insert statement formatted using a string builder. The code establishes a database connection and executes the insert statement through the connection. The rest of the method is used to capture and errors in execution and to close the connection following the insert.

Code: Default Page

The default page included in the web project is merely provided as a means for reviewing the fault information stored in the database. The default page was shown earlier in Figure 2 of this document. There is nothing particularly interesting about the default page; it contains a single data grid which is sourced back to the table containing the error report information. All of the information contained in the table is shown in the data grid with the exception of the time stamp.

Code: The web.config File

The web.config file is set up per the default; it calls for Windows Authentication. Depending upon your needs, you may opt to select an alternative form of authentication.

ASP.NET
<!--
The <authentication> section enables configuration 
of the security authentication mode used by 
ASP.NET to identify an incoming user. 
-->
<authentication mode="Windows" />
<!--

The Win Forms Project

Code: ErrorReport Form

The error report form is used as a custom error dialog; the form's design is simple, it contains a picture box used to display the error type icon, a text box with no border set to have a background color matching the form which is used to display the error message, a text box used to display the message details (in the form of the stack trace), a button which allows the user to show or hide the details, a button used to close the form, and a button used to send the error information collected to the database through the web service.

The form's code behind page is set up into regions: declarations, constructors, methods, and properties. Each of these sections is used to contain the relevant sections of the code.

The class imports and declaration are simple enough:

VB.NET
Imports System
Imports System.Windows.Forms.SystemInformation

''' <summary />
''' Class:  ErrorReport
''' The class is used to generate an error message dialog that shows
''' the user the error message, stack trace, and also provides the
''' user with the means to submit the error encountered back to
''' developer by means of a web service that stores the error information
''' in an sql server database.
''' </summary />
''' <remarks /></remarks />

Public Class ErrorReport

Aside from the System import, System.Windows.Forms.SystemInformation has been added to support capturing some of the information used to make the bug report.

The declarations section contains the variable declarations used to support this form:

VB.NET
#Region "Declarations"
     ' show this image on load
    Public Enum MessageImage
        Denied
        Exclaim
        Information
    End Enum

    Private mMessage As String
    Private mTitle As String
    Private mStackTrace As String
    Private mMessageImage As MessageImage
    Private mApplicationName As String

#End Region

The enumeration declared at the top of the region is used to define what type of image will be displayed as the form icon; in the demo, three alternatives are presented and included in this enumeration: Denied, Exclaim, and Information.

The next four variables are private local member variables used to contain the error message, the message title, the stack trace, and the message image; all of these values are supported through properties with public accessor methods used to set or retrieve the values of variables.

The constructors region contains two constructors, one default, empty constructor, and one overloaded constructor that accepts all of the necessary values used to generate an error report as a set of arguments:

VB.NET
#Region "Constructors"
     ' default constructor
    Public Sub New()

        ' This call is required by the Windows Form Designer.
        InitializeComponent()
        Me.Height = 160

    End Sub


    ' overloaded constructor
    Public Sub New(ByVal message As String, _
                   ByVal title As String, _
                   ByVal stack As String, _
                   ByVal app As String, _
                   ByVal msgImage As MessageImage)

        ' This call is required by the Windows Form Designer.
        InitializeComponent()
        Me.Height = 160

        mMessage = message
        mTitle = title
        mStackTrace = stack

        txtErrorMsg.Text = mMessage
        txtDetails.Text = mStackTrace
        Me.Text = mTitle

        Select Case (msgImage)
          Case MessageImage.Denied
            Me.picMsgPicture.Image = My.Resources.explorer_exe_Ico64_ico_Ico1
          Case MessageImage.Exclaim
            Me.picMsgPicture.Image = My.Resources.netplwiz_dll_Ico8_ico_Ico1
          Case MessageImage.Information
            Me.picMsgPicture.Image = My.Resources.explorer_exe_Ico52_ico_Ico1
          Case Else
            Me.picMsgPicture.Image = My.Resources.netplwiz_dll_Ico8_ico_Ico1
        End Select

    End Sub

#End Region

The details section of the error report is hidden by default, this is accomplished in both constructors merely by setting the height of the form to 160 on initialization. In the overloaded constructor, the arguments are processed and used to set each of the properties used in the dialog. A select case statement as the bottom of the overloaded constructor is used to set the image used on the form based upon the Message Image value passed into the constructor as a argument.

If the class is initialized using the default constructor, the user may set the properties for the form directly prior to showing the form.

The methods region of the class is used to handle the button clicks used to close the form, view the details, and send the error report. The click event handler for the close button is as follows:

VB.NET
#Region "Methods"
     Private Sub btnClose_Click(ByVal sender As System.Object, _
                       ByVal e As System.EventArgs) Handles btnClose.Click

        Me.Dispose()

    End Sub

The click event handler for the view details button is as follows:

VB.NET
Private Sub btnViewDetails_Click(ByVal sender As System.Object, _
                ByVal e As System.EventArgs) Handles btnViewDetails.Click

        If btnViewDetails.Text = "View Details" Then
            Me.Height = 300
            btnViewDetails.Text = "Hide Details"
        Else
            Me.Height = 160
            btnViewDetails.Text = "View Details"
        End If

    End Sub

This handler checks the label of the button to determine the appropriate action; the label is set to "View Details", it changes the height of the form from 160 to 300 which in turn exposes the details and the button used to send the details to the web service. It also changes the button label to read "Hide Details". If the button lable reads "Hide Details" when the button is clicked, the height of the form is set back 160 and the button label is reset to read "View Details".

The last button click event handler is used to make the error report. The code for that handler is:

VB.NET
Private Sub btnSendBugReport_Click(ByVal sender As System.Object, _
            ByVal e As System.EventArgs) Handles btnSendBugReport.Click

        ' Obtain the user name currently logged on to the computer
        Dim strUser As String
        strUser = System.Windows.Forms.SystemInformation.UserName.ToString()

        ' Obtain the current domain name
        Dim strDomain As String
        strDomain _
          = System.Windows.Forms.SystemInformation.UserDomainName.ToString()

        ' Get the name of the computer the application running the app
        Dim strComputerName As String
        strComputerName = 
        System.Windows.Forms.SystemInformation.ComputerName.ToString()

        ' Create a local instance of the web service
        Dim ws As New BugTracker.ReportBug()
        ws.Credentials = System.Net.CredentialCache.DefaultCredentials

        ' The web service returns a boolean to indicate 
        ' success or failure in execution
        Dim blnTest As Boolean = False

        ' Run the web service and load the installer's 
        ' information into the database
        Try

          blnTest = ws.Report(strDomain, strUser, strComputerName, _
                              ApplicationName, ErrorMessage, ErrorStack)

        Catch
            MessageBox.Show("The error message was not sent.", _
                           "Submittal Failed", _
                           MessageBoxButtons.OK, MessageBoxIcon.Exclamation)
        End Try

        If blnTest = True Then
            MessageBox.Show("The error information was successfully sent.", 
            "Submittal Complete", _
            MessageBoxButtons.OK, MessageBoxIcon.Information)
        End If

    End Sub

The code is annotated and should be easy to follow; at the beginning, the code obtains the domain name, user name, and computer name from the user's machine and uses the values obtained to populate some variables which will in turn be passed to the web service.

Next, an instance of the web service is created. Following that and since the web project is using Windows Authenication, I am passing the user's default credentials to the service. A boolean variable is then declared and used to capture the results returned by the web service when the error report information is submitted.

Within the try-catch block, the web service's Report web method is evoked and passed the system, user, and error related information. If the information is successfully loaded into the database, the web method returns a true, if the operation fails, a false is returned.

After the web method executes, the boolean is evaluated and the user is notified as to whether or not the data was transferred to the database.

The remaining properties section of the class contains the public accessor methods used to set each of the local member variables and to update the form:

VB.NET
#Region "Properties"
     Public Property ErrorMessage() As String
        Get
            Return mMessage
        End Get
        Set(ByVal value As String)
            mMessage = value
            txtErrorMsg.Text = mMessage
        End Set
    End Property


    Public Property ErrorStack() As String
        Get
            Return mStackTrace
        End Get
        Set(ByVal value As String)
            mStackTrace = value
            txtDetails.Text = "An error occurred at " & Trim(mStackTrace)
        End Set
    End Property


    Public Property ErrorTitle() As String
        Get
            Return mTitle
        End Get
        Set(ByVal value As String)
            mTitle = value
            Me.Text = mTitle
        End Set
    End Property


    Public Property ErrorImage() As MessageImage
        Get
            Return mMessageImage
        End Get
        Set(ByVal value As MessageImage)

          mMessageImage = value

          Select Case (mMessageImage)
            Case MessageImage.Denied
              Me.picMsgPicture.Image = My.Resources.explorer_exe_Ico64_ico_Ico1
            Case MessageImage.Exclaim
              Me.picMsgPicture.Image = My.Resources.netplwiz_dll_Ico8_ico_Ico1
            Case MessageImage.Information
              Me.picMsgPicture.Image = My.Resources.explorer_exe_Ico52_ico_Ico1
            Case Else
              Me.picMsgPicture.Image = My.Resources.netplwiz_dll_Ico8_ico_Ico1
            End Select

        End Set
    End Property


    Public Property ApplicationName() As String
        Get
            Return mApplicationName
        End Get
        Set(ByVal value As String)
            mApplicationName = value
        End Set
    End Property

#End Region

Code: Form1

This form is used as a test bed for the error report dialog. It is set up to force a divide by zero exception and an index out of bounds exception. When the exceptions are encountered, the catch block is used to create an instance of the error report dialog and to populate and display it. The form also contains a button used to open the default page of the web project and to display all of the reported errors contained in the database. The code for this form is annotated and easy to follow:

VB.NET
Public Class Form1

    Private Sub Button1_Click(ByVal sender As System.Object, ByVal e As 
    System.EventArgs) Handles Button1.Click

        ' create a divide by zero error
        Try
            Dim intA As Integer = 12
            Dim intB As Integer = 0
            Dim result As Integer = intA / intB

        Catch ex As Exception
            Dim f As New ErrorReport()
            f.ErrorMessage = ex.Message.ToString()
            f.ErrorStack = ex.StackTrace.ToString()
            f.ErrorTitle = "An Error Has Occurred"
            f.ErrorImage = ErrorReport.MessageImage.Denied
            f.ApplicationName = f.ProductName & " " & f.ProductVersion
            f.Show()
        End Try

    End Sub


    Private Sub btnIndexError_Click(ByVal sender As System.Object, _
                  ByVal e As System.EventArgs) Handles btnIndexError.Click

        ' create an indexing error
        Try
            Dim sJunk As String = "Test"
            Dim cArr() As Char = sJunk.ToCharArray()

            Dim i As Integer
            For i = 0 To 5
                sJunk += cArr(i)
            Next
        Catch ex As Exception
            Dim f As New ErrorReport()
            f.ErrorMessage = ex.Message.ToString()
            f.ErrorStack = ex.StackTrace.ToString()
            f.ErrorTitle = "An Error Has Occurred"
            f.ErrorImage = ErrorReport.MessageImage.Denied
            f.ApplicationName = f.ProductName & " " & f.ProductVersion
            f.Show()
        End Try

    End Sub


    Private Sub btnErrorView_Click(ByVal sender As System.Object, _
                   ByVal e As System.EventArgs) Handles btnErrorView.Click

        ' open the default.aspx page to display current bug history
        System.Diagnostics.Process.Start( _
                                "http://localhost/BugTracker/Default.aspx")

    End Sub


    Private Sub btnExit_Click(ByVal sender As System.Object, ByVal e As 
    System.EventArgs) Handles btnExit.Click

        Application.Exit()

    End Sub
End Class

Summary

This article demonstrates an approach that may be used to gather fault related information about an application directly from the users of the application. This approach could easily be modified to include other types of information in addition to that shown. In deploying this sort of feature you will need to attend to the authentication used by the web project as well as the users and permissions applicable to the database side of the project.

License

This article has no explicit license attached to it but may contain usage terms in the article text or the download files themselves. If in doubt please contact the author via the discussion board below.

A list of licenses authors might use can be found here