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
- 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)
{
((App)App.Current).RootFrame.Dispatcher.BeginInvoke(this.StopToTimer);
try
{
HttpWebRequest request = (HttpWebRequest)(result.AsyncState);
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));
}
}
}
#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)
{
try
{
string url = "https://smarterdb.fogbugz.com/scoutSubmit.asp?";
string user = "Gábor Plesz"; string project = "WPA.SmarterDB.CodeProject.FogBugz Errors"; string area = "CodeProject"; string email = "gabor.plesz@gmail.com"; 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;
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);
}
catch (Exception ex)
{
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:
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.