借助 FogBugz 将 Windows Phone 应用程序的异常记录到中央服务器
本文介绍了如何使用 FogBugz 将 Windows Phone 应用程序的异常记录到中央服务器。
引言
我想与社区分享我们公司在 Windows Phone Marketplace 上获得的经验。
背景
所有 Windows Marketplace 应用程序都必须满足 Microsoft 施加的条件(技术认证要求)。其中一项是排除“未处理的异常”。为了让我们的应用程序进入市场,它必须避免因未处理的异常而关闭。在以下规定中指出了这一点
5.1.2. 应用程序关闭
应用程序必须处理由 .NET Framework 引发的异常,并且不能意外关闭。在认证过程中,将监视应用程序是否意外关闭。意外关闭的应用程序将无法通过认证。应用程序必须继续运行并对用户输入保持响应,在异常处理之后。
然而,当发生未处理的异常时,我们向用户展示“友好”的界面,实际上这仅仅是症状治疗。无论如何,如果在应用程序使用过程中发生类似情况,我们的应用程序将导致未处理的异常。真正的答案是,如果开发人员被告知这个问题,那么在应用程序的进一步开发中,他们将避免异常。为此,需要有关运行时未处理异常的信息。这有很多可能性
- Windows Phone 7 上的错误报告
- 代码清理异常处理和为 Marketplace 准备
- BugSense
- Silverlight 日志记录
- Windows Phone 7 错误处理和报告
- 基本的 Windows Phone 7 诊断
- 出现异常:apphub windows phone 应用程序崩溃报告
作为 FogBugz 用户,对于桌面应用程序,我们将获得一个易于集成的框架
问题所在
收到的代码将无法在 Windows Phone Silverlight 上运行。除了几个小问题外,还应解决以下主要问题
- 基于 Web 的通信应以异步方式组织;为此,我们必须创建
- 一个委托
- 一个事件
- 由于异步任务执行,应包含超时监视和控制代码。
- 由于异步性,我们从 Web 接收的响应不是在 UI 执行线程中运行的。
解决方案
我下载了示例代码,我开发了框架项目用于演示示例,然后我做了必要的更改。其中最重要的是
我们应该借助委托将从 Web 服务器收到的异步响应转换为事件,这将指示界面。
- 异步通信
- 委托的定义和返回值
#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
#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
我们使用计时器处理超时:当搜索开始时,我们设置它并启动它,当它完成时,停止请求我们指示超时。这需要定义
#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
#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
正如我们在之前的代码中看到的,响应没有在与发送请求相同的线程中运行。因此,为了停止正在运行的超时监视计时器(我们已在发送请求线程中启动),我们必须返回到界面的执行线程 (UI 线程),这可以通过以下代码行获得
((App)App.Current).RootFrame.Dispatcher.BeginInvoke(this.StopToTimer);
当我们要显示从服务器收到的响应在界面上时,我们将遇到类似的问题
首先,我们应该创建一个委托,然后它将显示响应
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();
}
然后我们应该像往常一样在界面的执行线程中执行
private void btnSendInfo_Click(object sender, RoutedEventArgs e)
{
//todo
try
{
//************************************************************************************
//TO DO: SET THESE VALUES BEFORE CALLING THIS METHOD!
//example: https:///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));
}
为了概述整个过程,我还绘制了一个插图
标有蓝色的元素在界面的执行线程(UI 线程)中运行,红色的元素显示了从 Web 服务器接收的异步响应线程,两个绿色的箭头表示返回线程在界面线程中启动一个过程的点。