Is there a way to send a email message with the details when an unhandled exception occurs in a WP7 app?
I've seen the WCF logging/email sending approach, but I don't have a place to publicly host a service.
Take a look at Andy Pennell's "little watson." It works pretty well.
The only other option you have is to use the EmailComposeTask. This leaves you at the mercy of the user to send the message, because it will give you their email address, but its the only way currently to send a mail message without a WCF service.
Example 1:
private void emailAddressChooserTask_Completed(object sender, EmailResult e)
{
if (e.TaskResult == TaskResult.OK)
{
MessageBox.Show("Selected email :" + e.Email);
//in-real world application user expect to select it from his contacts and if not found enter manually.
//EmailComposeTask emailComposeTask = new EmailComposeTask();
//emailComposeTask.To = e.Email;
//emailComposeTask.To = saveEmailAddressTask.Email;
//emailComposeTask.Body = "WP7 Emails Demo";
//emailComposeTask.Cc = "testmail2#test.com";
//emailComposeTask.Subject = "Windows Phone 7";
//emailComposeTask.Show();
}
}
Example 2:
private void composeMail_Click(object sender, RoutedEventArgs e)
{
EmailComposeTask emailComposeTask = new EmailComposeTask();
emailComposeTask.To = "chris#example.com";
emailComposeTask.To = saveEmailAddressTask.Email;
emailComposeTask.Body = "WP7 Emails Demo";
emailComposeTask.Cc = "testmail2#test.com";
emailComposeTask.Subject = "Windows Phone 7";
emailComposeTask.Show();
}
Souce: http://www.windowsphonegeek.com/articles/1-how-to-perform-email-tasks-in-a-wp7-app
I use the following in my app, it is a little convoluted but works. The following code is in App.xaml.cs:
/// <summary>
/// Indicates whether the application needs to quit due to a previous exception
/// </summary>
private static bool _appMustQuit = false;
/// <summary>
/// Exception class that will be unhandled causing application to quit
/// </summary>
private class AppQuitException : Exception {}
// Code to execute on Unhandled Exceptions
private void Application_UnhandledException( object sender,
ApplicationUnhandledExceptionEventArgs e )
{
if( ( e.ExceptionObject is AppQuitException ) == false ) {
Debug.WriteLine( "App:Application_UnhandledException - " + e.ToString() );
if( Debugger.IsAttached ) {
// An unhandled exception has occurred; break into the debugger
Debugger.Break();
}
// Compose error report
StringBuilder report = new StringBuilder( 1024 );
report.AppendFormat( "{0}", LangResources.ErrorReportContent );
report.AppendFormat( "Message: {0}\n", e.ExceptionObject.Message );
if( e.ExceptionObject.InnerException != null ) {
report.AppendFormat( "Inner: {0}\n", e.ExceptionObject.InnerException.Message );
}
report.AppendFormat( "\nStackTrace: {0}\n", e.ExceptionObject.StackTrace );
if( MessageBox.Show( "Unexpected Error", "Error", MessageBoxButton.OKCancel )
== MessageBoxResult.OK ) {
e.Handled = true;
// Email the error report
Tasks.ComposeEmail( "\"Developer\" <your#emailaddress.com>", "MyApp Error Report",
report.ToString() );
_appMustQuit = true;
}
}
}
// Code to execute when the application is activated (brought to foreground)
// This code will not execute when the application is first launched
private void Application_Activated( object sender, ActivatedEventArgs e )
{
var state = PhoneApplicationService.Current.State;
if( state.ContainsKey( "AppMustQuit" ) ) {
throw new AppQuitException();
} else {
// Restore other tombstoned variables
}
}
// Code to execute when the application is deactivated (sent to background)
// This code will not execute when the application is closing
private void Application_Deactivated( object sender, DeactivatedEventArgs e )
{
if( _appMustQuit ) {
state["AppMustQuit"] = true;
} else {
// Save other variables for tombstoning
}
}
Tasks is a static class with a bunch of helper functions from the Microsoft.Phone.Tasks namespace.
using Microsoft.Phone.Tasks;
namespace MyApp
{
/// <summary>
/// Utility class for performing various phone tasks
/// </summary>
public static class Tasks
{
/// <summary>
/// Composes an email using the specified arguments
/// </summary>
/// <param name="to">The recepient(s) of the email</param>
/// <param name="subject">Email subject</param>
/// <param name="body">Email contents</param>
/// <param name="cc">The recipient(s) on the cc line of the email</param>
public static void ComposeEmail( string to, string subject, string body,
string cc = "" )
{
var task = new EmailComposeTask() {
To = to,
Subject = subject,
Body = body,
Cc = cc,
};
task.Show();
}
}
}
To explain the code a little, using the EmailComposeTask causes your app to be tombstoned. Since I do not want to continue executing the app after an unhandled exception I save a boolean to the PhoneApplicationService's State dictionary so that after the user sends the email, when the app re-awakens I can look for this boolean and throw another exception that is intentionally unhandled. This second exception causes the app to quit.
If somebody is looking for Email messages in background (without loading EmailComposeTask) then MailMessage might be the best option so for, it also allows attachments which EmailComposeTask does not supports at all. it would allow you to send emails without bothering the users in background using your own gmail or anyother account.
you can get it from NuGet Install-Package MailMessage to give it a try, However I haven't used it myself so may be you need to buy it from authors site its only in $29.99 its really worth paying for it. Free version also works but it shows a popup message in application before sending an email.
Related
So, as the subject says...what is the easiest way to hide FormRegion if email is in Reply mode whether its in a new window or with InlineResponse?
Just set the FormRegion.Visible property which returns a Boolean value that indicates whether the form region is visible or hidden.
Went back to test the approach in my answer after the question from #EugeneAstafiev and -- of course -- this was more complicated than I first thought... but I did get it working with some additional code.
The issue is that when the user clicks "Reply", it opens a new inspector window with a new instance of the FormRegion. So setting the Visible property to false in the event handler sets it only on the current "Read" mode inspector window -- rather than the new "Compose" mode inspector window that gets opened. So, instead the code samples below set up a bool flag property in ThisAddIn called LoadFormRegion that can be toggled to "false" when the Reply or ReplyAll event is fired.
Also, I noticed that setting Visible to false on the FormRegion still draws the area of the FormRegion on the inspector window, just with nothing in it. To completely prevent the FormRegion from loading, you can test for "Compose" mode and then the ThisAddin.LoadFormRegion flag in the FormRegionInitializing event handler located inside of the "Form Region Factory" at the top of the code page -- which is usually folded away from view. In that code block, setting "e.Cancel = true" will prevent the FormRegion from loading at all.
Here is the revised code for the FormRegion, which now subscribes to all three email button click events (Reply, ReplyAll, Forward), and sets the ThisAddIn.LoadFormRegion flag accordingly:
namespace TESTINGOutlookAddInVSTO
{
partial class FormRegion1
{
#region Form Region Factory
[Microsoft.Office.Tools.Outlook.FormRegionMessageClass(Microsoft.Office.Tools.Outlook.FormRegionMessageClassAttribute.Note)]
[Microsoft.Office.Tools.Outlook.FormRegionName("TESTINGOutlookAddInVSTO.FormRegion1")]
public partial class FormRegion1Factory
{
// Occurs before the form region is initialized.
// To prevent the form region from appearing, set e.Cancel to true.
// Use e.OutlookItem to get a reference to the current Outlook item.
private void FormRegion1Factory_FormRegionInitializing(object sender, Microsoft.Office.Tools.Outlook.FormRegionInitializingEventArgs e)
{
if (e.FormRegionMode == Outlook.OlFormRegionMode.olFormRegionCompose)
{
var myAddIn = Globals.ThisAddIn;
if (myAddIn.LoadFormRegion == false)
{
e.Cancel = true;
}
}
}
}
#endregion
// Occurs before the form region is displayed.
// Use this.OutlookItem to get a reference to the current Outlook item.
// Use this.OutlookFormRegion to get a reference to the form region.
private void FormRegion1_FormRegionShowing(object sender, System.EventArgs e)
{
// Reset ThisAddIn.LoadFormRegion flag to true (in case user starts
// composing email from scratch without clicking Reply, ReplyAll or Forward)
var myAddin = Globals.ThisAddIn;
myAddin.LoadFormRegion = true;
var myMailItem = this.OutlookItem as Outlook.MailItem;
// Track these events to set the ThisAddIn.FormRegionShowing flag
((Outlook.ItemEvents_10_Event)myMailItem).Reply += myMailItem_Reply;
((Outlook.ItemEvents_10_Event)myMailItem).ReplyAll += myMailItem_ReplyAll;
((Outlook.ItemEvents_10_Event)myMailItem).Forward += myMailItem_Forward;
}
// Sets FormRegionShowing flag on ThisAddin
private void SetFormRegionShowing(bool show)
{
var myAddIn = Globals.ThisAddIn;
myAddIn.LoadFormRegion = show;
}
private void myMailItem_Forward(object Forward, ref bool Cancel)
{
SetFormRegionShowing(true);
}
private void myMailItem_ReplyAll(object Response, ref bool Cancel)
{
SetFormRegionShowing(false);
}
private void myMailItem_Reply(object Response, ref bool Cancel)
{
SetFormRegionShowing(false);
}
// Occurs when the form region is closed.
// Use this.OutlookItem to get a reference to the current Outlook item.
// Use this.OutlookFormRegion to get a reference to the form region.
private void FormRegion1_FormRegionClosed(object sender, System.EventArgs e)
{
}
}
}
And here's the new code for ThisAddIn to setup the LoadFormRegion property flag:
namespace TESTINGOutlookAddInVSTO
{
public partial class ThisAddIn
{
private bool loadFormRegion;
public bool LoadFormRegion { get => loadFormRegion; set => loadFormRegion = value; }
private void ThisAddIn_Startup(object sender, System.EventArgs e)
{
LoadFormRegion = true;
}
private void ThisAddIn_Shutdown(object sender, System.EventArgs e)
{
// Note: Outlook no longer raises this event. If you have code that
// must run when Outlook shuts down, see https://go.microsoft.com/fwlink/?LinkId=506785
}
#region VSTO generated code
/// <summary>
/// Required method for Designer support - do not modify
/// the contents of this method with the code editor.
/// </summary>
private void InternalStartup()
{
this.Startup += new System.EventHandler(ThisAddIn_Startup);
this.Shutdown += new System.EventHandler(ThisAddIn_Shutdown);
}
#endregion
}
}
Tested the above and works nearly perfectly... I noticed that if there is a "Draft" reply in the user's Inbox created before the add-in was loaded, clicking on that email to continue editing in the Preview window or a new inspector window will cause some wonky display issues with the FormRegion. However, once I closed out that draft, then everything seemed to return to normal.
Can you please let me know how to handle the global exception(without App crash) in Xamarin Cross platform project.
There isn't a 'Xamarin.Forms' way of doing it that I know of. You need to hook into Android and iOS, what you could do is create one method that handles them both in the same way.
A nice post about this is by Peter Norman. He describes that to implement it in Android you can do this in your MainActivity.cs.
// In MainActivity
protected override void OnCreate(Bundle bundle)
{
base.OnCreate(bundle);
AppDomain.CurrentDomain.UnhandledException += CurrentDomainOnUnhandledException;
TaskScheduler.UnobservedTaskException += TaskSchedulerOnUnobservedTaskException;
Xamarin.Forms.Forms.Init(this, bundle);
DisplayCrashReport();
var app = new App();
LoadApplication(app);
}
#region Error handling
private static void TaskSchedulerOnUnobservedTaskException(object sender, UnobservedTaskExceptionEventArgs unobservedTaskExceptionEventArgs)
{
var newExc = new Exception("TaskSchedulerOnUnobservedTaskException", unobservedTaskExceptionEventArgs.Exception);
LogUnhandledException(newExc);
}
private static void CurrentDomainOnUnhandledException(object sender, UnhandledExceptionEventArgs unhandledExceptionEventArgs)
{
var newExc = new Exception("CurrentDomainOnUnhandledException", unhandledExceptionEventArgs.ExceptionObject as Exception);
LogUnhandledException(newExc);
}
internal static void LogUnhandledException(Exception exception)
{
try
{
const string errorFileName = "Fatal.log";
var libraryPath = Environment.GetFolderPath(Environment.SpecialFolder.Personal); // iOS: Environment.SpecialFolder.Resources
var errorFilePath = Path.Combine(libraryPath, errorFileName);
var errorMessage = String.Format("Time: {0}\r\nError: Unhandled Exception\r\n{1}",
DateTime.Now, exception.ToString());
File.WriteAllText(errorFilePath, errorMessage);
// Log to Android Device Logging.
Android.Util.Log.Error("Crash Report", errorMessage);
}
catch
{
// just suppress any error logging exceptions
}
}
/// <summary>
// If there is an unhandled exception, the exception information is diplayed
// on screen the next time the app is started (only in debug configuration)
/// </summary>
[Conditional("DEBUG")]
private void DisplayCrashReport()
{
const string errorFilename = "Fatal.log";
var libraryPath = Environment.GetFolderPath(Environment.SpecialFolder.Personal);
var errorFilePath = Path.Combine(libraryPath, errorFilename);
if (!File.Exists(errorFilePath))
{
return;
}
var errorText = File.ReadAllText(errorFilePath);
new AlertDialog.Builder(this)
.SetPositiveButton("Clear", (sender, args) =>
{
File.Delete(errorFilePath);
})
.SetNegativeButton("Close", (sender, args) =>
{
// User pressed Close.
})
.SetMessage(errorText)
.SetTitle("Crash Report")
.Show();
}
#endregion
And for iOS you can add code like this in your AppDelegate.cs.
//iOS: Different than Android. Must be in FinishedLaunching, not in Main.
// In AppDelegate
public override bool FinishedLaunching(UIApplication uiApplication, NSDictionary options)
{
AppDomain.CurrentDomain.UnhandledException += CurrentDomainOnUnhandledException;
TaskScheduler.UnobservedTaskException += TaskSchedulerOnUnobservedTaskException;
// Rest of your code...
}
/// <summary>
// If there is an unhandled exception, the exception information is diplayed
// on screen the next time the app is started (only in debug configuration)
/// </summary>
[Conditional("DEBUG")]
private static void DisplayCrashReport()
{
const string errorFilename = "Fatal.log";
var libraryPath = Environment.GetFolderPath(Environment.SpecialFolder.Resources);
var errorFilePath = Path.Combine(libraryPath, errorFilename);
if (!File.Exists(errorFilePath))
{
return;
}
var errorText = File.ReadAllText(errorFilePath);
var alertView = new UIAlertView("Crash Report", errorText, null, "Close", "Clear") { UserInteractionEnabled = true };
alertView.Clicked += (sender, args) =>
{
if (args.ButtonIndex != 0)
{
File.Delete(errorFilePath);
}
};
alertView.Show();
}
It also includes the ability to show the log when you're debugging the application. Of course you can implement your own logging or handling methods. One thing you could look at is HockeyApp. This handles unhandled exceptions by default and sends them back to you, amongst other things.
Update, since this is still found on Google: For crash reporting and analytics you now want to start looking at App Center. This is the evolvement of HockeyApp and Xamarin Insights (among others like building, distributing and push notifications) and now acts as the mission dashboard for everything that has to do with apps, and not just Xamarin.
For UWP and WinPhone 8.1 there should be a UnhandledException handler in the Application object. Check out this answer for more information. I quote:
For XAML-based apps, you can use UnhandledException; however, that
only captures exceptions that come up through the XAML (UI) framework
and you don't always get a lot of information about what the root
cause is, even in InnerException.
Update for Windows 8.1: UnhandledException also will capture
exceptions that are created by an async void method. In Windows 8,
such exceptions would just crash the app. LunarFrog has a good
discussion of this on their website.
Basically you should add a event handler in the constructor of your App() in App.xaml.cs: this.UnhandledException += (o, s) => {}.
This is what I did for xamarin forms Android global exception handling and app crashes.
This solution will handle both background and foreground exceptions.
AppDomain.CurrentDomain.UnhandledException += (s,e)=>
{
System.Diagnostics.Debug.WriteLine("AppDomain.CurrentDomain.UnhandledException: {0}. IsTerminating: {1}", e.ExceptionObject, e.IsTerminating);
};
AndroidEnvironment.UnhandledExceptionRaiser += (s, e) =>
{
System.Diagnostics.Debug.WriteLine("AndroidEnvironment.UnhandledExceptionRaiser: {0}. IsTerminating: {1}", e.Exception, e.Handled);
e.Handled = true;
};
As a result, I have different behavior for both types of exceptions:
foreground:
background:
I launch a page on UWP thanks to PageRenderer, this page allows take picture and I want go back when user take a pciture.
My problem is when I go back with hardware button I have no problem but with function goback doesn't work.
This is my code :
var frame = Window.Current.Content as Frame;
if (frame != null && frame.CanGoBack)
{
frame.GoBack();
}
CanGoBack return false.
So you have some idea
Thanks
You need to hook into the back button pressed and then force the navigation back
public PageContainer()
{
this.InitializeComponent();
SystemNavigationManager.GetForCurrentView().BackRequested += PageContainer_BackRequested;
}
/// <summary>
/// Handles when the user presses the back button
/// </summary>
/// <param name="sender"></param>
/// <param name="e"></param>
private void PageContainer_BackRequested(object sender, BackRequestedEventArgs e)
{
Frame rootFrame = Window.Current.Content as Frame;
if (rootFrame == null){
return;
}
// Navigate back if possible, and if the event has not
// already been handled .
if (rootFrame.CanGoBack && e.Handled == false)
{
e.Handled = true;
rootFrame.GoBack();
}
}
This example is an adaptation of the example provided on the MSDN website
You have to use the BackPressed Event.
public Test()
{
HardwareButtons.BackPressed += HardwareButtons_BackPressed;
}
private void Test_BackPressed(object sender, BackPressedEventArgs e)
{
if (this.Layer.CanGoBack)
{
e.Handled = true;
this.Layer.GoBack();
}
}
I have an windows forms application, with a form that holds 2 tabcontrols and a grid. I'd like to catch the pressing of esc key on any on this controls.
The question is : is it a simpler way to do that without subscribing to the keypress event on each control ?
Thanks!
You can Simply Do following.
Implement an IMessageFilter and Handle Key Down event.
Here is the complete Code to hook Escape Key Down.
public class MyKeboardHook:IMessageFilter
{
public const int WM_KEYDOWN = 0x0100;
public const int VK_ESCAPE = 0x1B;
public event EventHandler EscapeKeyDown;
public bool PreFilterMessage(ref Message m)
{
if (m.Msg == WM_KEYDOWN && m.WParam == new IntPtr(VK_ESCAPE))
{
OnEscapeKeyPressed();
}
return false; //Do not Process anything
}
protected virtual void OnEscapeKeyDown()
{
if(this.EscapeKeyDown!=null)
{
EscapeKeyDown(this, EventArgs.Empty);
}
}
}
Now you need to register this. The best place would be in Main
static class Program
{
/// <summary>
/// The main entry point for the application.
/// </summary>
[STAThread]
static void Main()
{
Application.EnableVisualStyles();
Application.SetCompatibleTextRenderingDefault(false);
MyKeboardHook myKeboardHook = new MyKeboardHook();
myKeboardHook.EscapeKeyDown += (e, x) =>
{
MessageBox.Show("Escape Key Pressed");
};
Application.AddMessageFilter(myKeboardHook);
Application.Run(new Form1());
}
}
Subscribe to the event on the form itself.
If the control doesn't handle the event it should bubble up to the form where it will be handled.
I'm trying to add ConfirmDialogHandler to dialog handler and i get NullReferenceException.
This happens on FF 3.6 with latest watin version, the same code works on IE7 and 8.
ConfirmDialogHandler confirmDialogHandler = new ConfirmDialogHandler();
browser.AddDialogHandler(confirmDialogHandler); // --> exception here
Any ideas?
Old Solution
Found the problem.
The DialogWatcher was not initialized when FF session was created added this line to watin code:
private void CreateFireFoxInstance(string url)
{
Logger.LogAction("Creating FireFox instance");
UtilityClass.MoveMousePoinerToTopLeft(Settings.AutoMoveMousePointerToTopLeft);
var clientPort = GetClientPort();
clientPort.Connect(url);
_ffBrowser = new FFBrowser(clientPort);
StartDialogWatcher(); // <-- Added line
WaitForComplete();
}
Please Note - This is the workaround
The provided solution didn't worked
This is my workaround:
I used AutoIt dll and handled the popups by my self, code sample:
using AutoItX3Lib;
public static void RunAutomaticEnterCommand(Session session)
{
var autoIt = GetPopupHandle();
autoIt.Send("{ENTER}");
Thread.Sleep(1000);
}
/// <summary>
/// Cancel the popup
/// For FF
/// </summary>
/// <param name="session"></param>
public static void RunAutomaticCancelCommand(Session session)
{
var autoIt = GetPopupHandle();
autoIt.Send("{TAB}");
autoIt.Send("{ENTER}");
Thread.Sleep(1000);
}
/// <summary>
/// Return the autoit popup handler
/// AutoIt is a script language, we using it to handle the firefox popups
/// </summary>
/// <returns></returns>
private static AutoItX3Class GetPopupHandle()
{
var autoIt = new AutoItX3Class();
autoIt.AutoItSetOption("WinTitleMatchMode", 2);
const string partialTitle = "The page at"; //the popup in Firefox starts with this text
autoIt.WinWait(partialTitle, "", 30);
string fullTitle = autoIt.WinGetTitle(partialTitle); //Get the whole popup title
autoIt.WinActivate(fullTitle); //Get focis to the popup
if (autoIt.WinWaitActive(fullTitle, "", 20) == 0)
{
reporter.Report("Failed to get the FireFox popup handle", false);
return autoIt;
}
return autoIt;
}