Click here to Skip to main content
65,938 articles
CodeProject is changing. Read more.
Articles
(untagged)

Logging exceptions of Windows Phone applications to a central server with the help of FogBugz

0.00/5 (No votes)
24 Jul 2012 1  
This article describes how to log exceptions of Windows Phone applications to a central server with the help of FogBugz.

Introduction

I would like to share the experiences that our company has gained about Windows Phone Marketplace with the community.

Background

All Windows Marketplace applications must meet the conditions imposed by Microsoft (Technical Certification Requirements). One of those items is the exclusion of 'Unhandled Exceptions'. To get our application out to the market, it must not close as a result of an unhandled exception. It is pointed out in the following regulation:

5.1.2. Application Closure

The application must handle exceptions raised by the .NET Framework and not close unexpectedly. During the certification process, the application is monitored for unexpected closure. An application that closes unexpectedly fails certification. The application must continue to run and remain responsive to user input after the exception is handled.

However, the fact that we show "friendly" face to the users when unhandled exceptions occur, in fact, it is only symptomatic treatment. In any case, if a similar situation occurs while using the application, our application will cause an unhandled exception. The real answer is if the developer is informed about this problem, and so with further development of the application they will avoid the exception. To do this information is needed about runtime unhandled exceptions. There are many possibilities for this:

As FogBugz users, for desktop applications we will get a framework easy to integrate:

The problem

The received code will not run with Windows Phone Silverlight. Besides several minor problems, the following major problems are to be solved:

  • The web-based communication should be organized in an asynchronous way; to do this we have to create
    • a delegate
    • an event
  • Due to the asynchronous task execution, a timeout monitoring and controlling code should be incorporated.
  • Due to the asynchrony, the response we receive from the web is not running in the UI executing thread.

The solution

I downloaded the sample code, and I developed the framework project for the presentation of the example, and then I made the necessary changes. The most important of these are:

We should transform the asynchronous response received from the web server into an event with the help of delegate, and that will indicate the interface.

  • Asynchronous communication
    • The definition of the delegate and the returned value:
    • #region delegate for async webresponse
      
      public delegate void fbBugScoutCompletedEventHandler(object sender, fbBugScoutCompletedEventArgs e);
      
      public class fbBugScoutCompletedEventArgs
      {
          public fbBugScoutCompletedEventArgs(fbBugScoutCompletionCode value, string msg) { code = value; message = msg; }
          public fbBugScoutCompletionCode code { get; private set; }
          public string message { get; private set; }
      }
      
      public enum fbBugScoutCompletionCode
      {
          CompletedOK,
          CompletedError
      }
      
      #endregion
    • The definition of the event handler
    • #region event for async webresponse
      
      public static event fbBugScoutCompletedEventHandler fbBugScoutCompletedEvent;
      void RaiseFbBugScoutCompleted(fbBugScoutCompletionCode code, string message)
      {
          if (null != fbBugScoutCompletedEvent)
              fbBugScoutCompletedEvent(this, new fbBugScoutCompletedEventArgs(code, message));
      }
      
      #endregion
  • Handling timeout
  • We handle timeout with a timer: when search starts, we set it and start it, and when it finishes, stopping the request we indicate timeout. Definitions needed for this:

    #region TimeOut methods
    
    private bool bIsInTime = false;
    private System.Windows.Threading.DispatcherTimer _toTimer;
    
    public void StartTimer(TimeSpan tTimeOut)
    {
        _toTimer = new System.Windows.Threading.DispatcherTimer();
        _toTimer.Interval = tTimeOut;
        _toTimer.Tick += toTimer_Tick;
        bIsInTime = true;
        _toTimer.Start();
    }
    
    private void StopToTimer()
    {
        bIsInTime = false;
        if (null != _toTimer)
        {
            _toTimer.Stop();
            _toTimer.Tick -= toTimer_Tick;
            _toTimer = null;
        }
    }
    
    private void toTimer_Tick(object o, EventArgs sender)
    {
        StopToTimer();
        RaiseFbBugScoutCompleted(fbBugScoutCompletionCode.CompletedError, "Time out occurred in network communication");
    }
    
    #endregion
  • Finally, the following code will use the previous above for the web-based communication:
  • #region async webrequest/response methods
    
    private void CallWebRequest(string sURL, Dictionary<string, string> rgArgs, TimeSpan tTimeOut)
    {
        string urlText = String.Empty;
    
        foreach (System.Collections.Generic.KeyValuePair<string, string> i in rgArgs)
        {
            urlText += (0 == urlText.Length ? "" : "&") + String.Format("{0}={1}", i.Key, i.Value);
        }
    
        string url = sURL + urlText;
        HttpWebRequest request = (HttpWebRequest)WebRequest.Create(new Uri(url));
        request.BeginGetResponse(HandleWebResponse, request);
        StartTimer(tTimeOut);
        return;
    }
    
    private void HandleWebResponse(IAsyncResult result)
    {
        if (bIsInTime)
        {
            // response only if request while timeout frame
            // stop the timer
            ((App)App.Current).RootFrame.Dispatcher.BeginInvoke(this.StopToTimer);
    
            // Put this in a try block in case the Web request was unsuccessful.
            try
            {
                // Get the request from the IAsyncResult
                HttpWebRequest request = (HttpWebRequest)(result.AsyncState);
    
                // Read the response stream from the response.
                StreamReader sr = new StreamReader(request.EndGetResponse(result).GetResponseStream());
                string data = sr.ReadToEnd();
                ParseResult(data);
            }
            catch (Exception ex)
            {
                RaiseFbBugScoutCompleted(fbBugScoutCompletionCode.CompletedError, 
                  string.Format("Unable to get data from Web: {0}", ex.Message));
            }
        }
        //else { /* The time is out: nothing todo */ }
    }
    
    #endregion
  • Multithreaded task execution
  • As we can see in the previous code, the response is not running in the same thread as the sent request. Therefore, in order to stop the running timeout monitoring timer (that we have started in the sent request thread) we have to get back to the executing thread of the interface (UI Thread), which is available through the following line:

    ((App)App.Current).RootFrame.Dispatcher.BeginInvoke(this.StopToTimer);

We will meet similar problems when we want to display the response received from the server on the interface:

First, we should create a delegate which will then display the response:

delegate void callerfbBugScoutCompleted(fbBugScoutCompletedEventArgs e);
void dofbBugScoutCompleted(fbBugScoutCompletedEventArgs e)
{
    if (fbBugScoutCompletionCode.CompletedOK == e.code)
    {
        lblStatus.Text = e.message;
    }
    else
    {
        lblStatus.Text = string.Format("Error on submit: {0}", e.message);
    }
    progressHide();
}

Then we should execute in the executing thread of the interface as usual:

private void btnSendInfo_Click(object sender, RoutedEventArgs e)
{
    //todo

    try
    {
        //************************************************************************************
        //TO DO: SET THESE VALUES BEFORE CALLING THIS METHOD!
        
        //example: http://localhost/fogbugz/scoutSubmit.asp
        string url = "https://smarterdb.fogbugz.com/scoutSubmit.asp?";
        string user = "Gábor Plesz"; //existing FogBugz User
        string project = "WPA.SmarterDB.CodeProject.FogBugz Errors"; //existing FogBugz project 
        string area = "CodeProject"; //existing FogBugz area
        string email = "gabor.plesz@gmail.com"; //email address of the customer who reports the bug
        //the message to return to the user if no Scout Message is found for an existing duplicate bug
        string defaultMessage = "Thank you for your message. It arrived safe " + 
           "and sound and we will resolve the issue as soon as possible."; 
        bool forceNewBug = false;
        //If set to true, this forces FogBugz to create a new case
        //for this bug, even if a bug with the same description already exists.
        //************************************************************************************

        //send the bug we created:
        progressShow();
        lblStatus.Text = "Sending debug info...";
        BugReport.fbBugScoutCompletedEvent += 
          new fbBugScoutCompletedEventHandler(BugReport_fbBugScoutCompletedEvent);
        BugReport.Submit(url, user, project, area, email, forceNewBug, 
           defaultMessage, Exception, true, "{0}.{1}.{2}.{3}", true);

        //lblStatus.ForeColor = Color.Green;
    }
    catch (Exception ex)
    {
        //lblStatus.ForeColor = Color.Red;
        lblStatus.Text = "Cannot send the debug info: " + ex.Message;
        progressHide();
    }

}

void BugReport_fbBugScoutCompletedEvent(object sender, fbBugScoutCompletedEventArgs e)
{
    BugReport.fbBugScoutCompletedEvent -= new fbBugScoutCompletedEventHandler(BugReport_fbBugScoutCompletedEvent);
    ((App)App.Current).RootFrame.Dispatcher.BeginInvoke(new callerfbBugScoutCompleted(dofbBugScoutCompleted), (e));
}

To overview the complete process, I have also drawn an illustration:

Sample Image

The elements marked with blue are running in the executing thread (UI Thread) of the interface, the red ones show the asynchronous response thread received from the web server, and the two green arrows indicate the points where the returning thread starts a process in the interface thread.

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