Yes, but I suggest not doing so and therefore will not say.
"Catching" your entire program is ridiculously bad design and implementation. Fix your bugs, don't hide them.
I wholeheartedly disagree. Per Microsoft if you want your software to be logo certified (Certified for Windows 7, etc) you are not supposed to catch all unhandled exceptions but having your own internal bug reporting mechanism is far better than dealing with Microsoft's WinQual.
However you shouldn't just ignored WER (Windows Error Reporting) as some exceptions you'll never have an opportunity to collect but will wind up on a WinQual. If you have a code signing certificate and sign your assemblies then check out WinQual .
The user interface is somewhat cumbersome to deal with but they expose web APIs to gather WER data. Check out StackHash . Its free and syncs with WER to grab CAB files and other crash data.
As far as handling application crashes internally here is what I do. There is a lot of code below but the two main "catch all" event subscriptions you care about would be Application.ThreadException and AppDomain.CurrentDomain.UnhandledException
Here is a typical program startup:
sealed class Program : IApplicationExceptionProvider
{
Program() { }
/// <summary>
/// The main entry point for the application.
/// </summary>
[STAThread]
static void Main(string[] args)
{
DebugHelper.Configure();
Program prog = new Program();
if (!Vea.Extensions.Forms.Log4NetConfigure.ConfigureLog4Net(args))
return;
var reporter = ExceptionReporter.WireUpForUnhandledExceptions(prog);
//continues
}
#region IApplicationExceptionProvider Members
string IApplicationExceptionProvider.FormSkinName
{
get { return CFG.FormSkinName; }
}
bool IApplicationExceptionProvider.FormSkinsEnabled
{
get { return CFG.FormSkinsEnabled; }
}
Vea.Extensions.LK.SensourceLicense IApplicationExceptionProvider.LicenseKey
{
get { return CFG.Key; }
}
string IApplicationExceptionProvider.LastConnectionString
{
get { return Vea.Data.QueryHistory.LastConnStr; }
}
string IApplicationExceptionProvider.LastQuery
{
get { return Vea.Data.QueryHistory.LastQuery; }
}
#endregion
Then for reporting (there was a bit of code removed explaining why so many empty catches -- it has a few different paths):
using System;
using System.Diagnostics;
using System.IO;
using System.Threading;
using System.Windows.Forms;
using Vea.Extensions;
using Vea.Extensions.LK;
namespace Vea.Extensions.Forms
{
public sealed class ExceptionReporter : IDisposable
{
//
readonly IApplicationExceptionProvider provider;
readonly log4net.ILog log;
bool m_enabled;
public bool Enabled
{
get { return m_enabled; }
private set { m_enabled = value; }
}
/* -------------------------------------------------------------------- */
ExceptionReporter()
: base()
{
this.Enabled = false;
}
/* -------------------------------------------------------------------- */
[System.Diagnostics.CodeAnalysis.SuppressMessage("Microsoft.Security", "CA2122:DoNotIndirectlyExposeMethodsWithLinkDemands")]
ExceptionReporter(IApplicationExceptionProvider provider)
:this()
{
if (provider == null)
throw new ArgumentNullException("provider");
this.provider = provider;
log = log4net.LogManager.GetLogger(provider.GetType());
WireUp();
}
void WireUp()
{
#if !DEBUG
m_enabled = true;
if (m_enabled)
{
Application.ThreadException += new ThreadExceptionEventHandler(ThreadException);
AppDomain.CurrentDomain.UnhandledException += new UnhandledExceptionEventHandler(CurrentDomain_UnhandledException);
}
#endif
}
/* -------------------------------------------------------------------- */
void CurrentDomain_UnhandledException(object sender, UnhandledExceptionEventArgs e)
{
Exception ex = e.ExceptionObject as Exception;
if (e.IsTerminating)
{
if (log.IsFatalEnabled) log.Fatal("Unhandled application exception (terminating)", ex);
}
else
{
if (log.IsErrorEnabled) log.Error("Unhandled application exception (non-terminating)", ex);
}
if (ex != null)
{
WriteExceptionRunTime(ex, false);
}
}
/* -------------------------------------------------------------------- */
#region IDisposable Members
[System.Diagnostics.CodeAnalysis.SuppressMessage("Microsoft.Security", "CA2122:DoNotIndirectlyExposeMethodsWithLinkDemands")]
public void Dispose()
{
if (m_enabled)
{
Application.ThreadException -= new ThreadExceptionEventHandler(ThreadException);
AppDomain.CurrentDomain.UnhandledException -= new UnhandledExceptionEventHandler(CurrentDomain_UnhandledException);
}
}
#endregion
/* -------------------------------------------------------------------- */
void ThreadException(object sender, System.Threading.ThreadExceptionEventArgs e)
{
WriteExceptionRunTime(e.Exception, true);
}
[System.Diagnostics.CodeAnalysis.SuppressMessage("Microsoft.Design", "CA1031:DoNotCatchGeneralExceptionTypes")]
void WriteExceptionRunTime(Exception error, bool showMessage)
{
if (log.IsFatalEnabled) log.Fatal("Unhandled application exception", error);
try
{
string msg =
"date/time : " + DateTime.Now.ToString() + Environment.NewLine +
"computer name : " + Environment.MachineName + Environment.NewLine +
"user name : " + Environment.UserName + Environment.NewLine +
"operating system : " + Environment.OSVersion.VersionString + Environment.NewLine +
"domain user name : " + Environment.UserDomainName + Environment.NewLine +
"system up time : " + VeaUtils.GetSystemUptime() + Environment.NewLine +
"program up time : " + VeaUtils.GetProgramUptime() + Environment.NewLine +
"allocated memory : " + (System.Diagnostics.Process.GetCurrentProcess().WorkingSet64 / 1048576).ToString() + " MB" + Environment.NewLine +
"physical memory : " + VeaUtils.GetTotalMemory() + Environment.NewLine +
"processor : " + VeaUtils.GetCpuInformation() + Environment.NewLine +
"display mode : " + SystemInformation.PrimaryMonitorSize + Environment.NewLine +
"executable : " + System.IO.Path.GetFileName(Application.ExecutablePath) + Environment.NewLine +
"exec. date/time : " + System.Diagnostics.Process.GetCurrentProcess().StartTime.ToString() + Environment.NewLine +
"path : " + Application.ExecutablePath + Environment.NewLine +
"version : " + System.Reflection.Assembly.GetExecutingAssembly().GetName().Version.ToString() + Environment.NewLine +
"exception class : " + error.GetType() + Environment.NewLine +
"exception message : " + error.Message + Environment.NewLine +
"framework ver : " + Environment.Version + Environment.NewLine +
"skin name : " + provider.FormSkinName + Environment.NewLine +
"form skins enabled: " + (provider.FormSkinsEnabled ? "Yes" : "No") + Environment.NewLine +
"Company : " + (provider.LicenseKey != null ? provider.LicenseKey.Company : "Unknown?") + Environment.NewLine +
Environment.NewLine +
"Stack Trace: " + Environment.NewLine +
error.GetErrorText() + Environment.NewLine +
Environment.NewLine +
"Environment Stack Trace: " + Environment.NewLine +
Environment.StackTrace + Environment.NewLine +
Environment.NewLine +
"Processes: " + Environment.NewLine +
VeaUtils.GetProcessList() + Environment.NewLine +
Environment.NewLine +
"Referenced Assemblies: " + Environment.NewLine +
VeaUtils.GetReferencedAssemblies() + Environment.NewLine +
Environment.NewLine +
"Connection String: " + provider.LastConnectionString + Environment.NewLine +
"Last Query: " + Environment.NewLine +
provider.LastQuery;
try
{
Vea.Extensions.VeaUtils.PostRpt(msg);
}
catch { } //May consider rethrowing the exception so it is submitted to WinQual
try
{
string bugRptfile = Vea.Extensions.Forms.Functions.GetBugRptFileName();
using (StreamWriter writer = new StreamWriter(bugRptfile))
{
writer.WriteLine(msg);
writer.Close();
}
}
catch { }
}
catch { }
if (showMessage)
{
DevExpress.XtraEditors.XtraMessageBox.Show("An unhandled exception has occured and the application must be restarted." + Environment.NewLine + error.Message, "Error", MessageBoxButtons.OK, MessageBoxIcon.Stop);
Application.Exit();
}
}
/* -------------------------------------------------------------------- */
public static ExceptionReporter WireUpForUnhandledExceptions(IApplicationExceptionProvider exceptionProvider)
{
if (exceptionProvider == null)
throw new ArgumentNullException("ExceptionProvider");
return new ExceptionReporter(exceptionProvider);
}
/* -------------------------------------------------------------------- */
}
}
In another unit I handle submitting the message back to a bug server and I have a separate server side component for accepting inbound bug reports then logging them to an SQL database and one last client application to view bug report feedback.
Its optional and the user can elect whether to submit reports back or not.
This is a controversial topic and I agree its not the "best thing" to do but it works 98% of the time and for the %2 of failures (ex OutOfMemoryException) it will probably wind up on winqual anyway.