///
/// Copyright © 2003-2008 JetBrains s.r.o.
/// You may distribute under the terms of the GNU General Public License, as published by the Free Software Foundation, version 2 (see License.txt in the repository root folder).
///
using System;
using System.Collections;
using System.Diagnostics;
using System.Reflection;
using System.Threading;
using System.Windows.Forms;
using JetBrains.Omea.OpenAPI;
using JetBrains.Omea.AsyncProcessing;
using JetBrains.ExceptionReport;
namespace JetBrains.Omea
{
///
/// Handles uncaught exceptions and reports them to the tracker.
///
internal class CustomExceptionHandler
{
private Control _ownerControl;
private string _dbCreatorBuild;
private bool _newDatabase;
private bool _indexesRebuilt;
private string _extraData = "";
private bool _exceptionDialogVisible;
private Queue _exceptionQueue = new Queue();
private bool _ignoreAllExceptions;
internal class QueuedException
{
Exception _exception;
ExceptionReportFlags _flags;
public QueuedException( Exception exception, ExceptionReportFlags flags )
{
_exception = exception;
_flags = flags;
}
public Exception Exception
{
get { return _exception; }
}
public ExceptionReportFlags Flags
{
get { return _flags; }
}
}
internal string DBCreatorBuild
{
get { return _dbCreatorBuild; }
set { _dbCreatorBuild = value; }
}
internal bool NewDatabase
{
get { return _newDatabase; }
set { _newDatabase = value; }
}
internal bool IndexesRebuilt
{
get { return _indexesRebuilt; }
set { _indexesRebuilt = value; }
}
internal Control OwnerControl
{
get { return _ownerControl; }
set { _ownerControl = value; }
}
internal void OnThreadException( object sender, ThreadExceptionEventArgs e )
{
PluginEnvironment environment = (PluginEnvironment) ICore.Instance;
if ( !environment.NotifyBackgroundThreadException( e.Exception ) )
{
ReportException( e.Exception, 0 );
}
}
internal void AddExceptionReportData( string data )
{
if ( _extraData != "" )
_extraData += ",";
_extraData += data;
}
internal void ReportException( Exception ex, ExceptionReportFlags flags )
{
Trace.WriteLine( ex.ToString() );
if ( IsIgnoredException( ex ) )
{
return;
}
lock( _exceptionQueue )
{
_exceptionQueue.Enqueue( new QueuedException( ex, flags ) );
}
if ( _ownerControl != null && _ownerControl.IsHandleCreated && !_ownerControl.IsDisposed &&
_ownerControl.InvokeRequired )
{
Core.UIManager.QueueUIJob( new MethodInvoker( ShowExceptionDialog ) );
}
else
{
ShowExceptionDialog();
}
}
private bool IsIgnoredException( Exception ex )
{
if ( _ignoreAllExceptions )
{
return true;
}
string[] stackTrace = ex.ToString().Split( '\n' );
if ( stackTrace.Length < 2 )
{
return false;
}
string topMethod = stackTrace [1];
if ( topMethod.IndexOf( "System.Windows.Forms.UnsafeNativeMethods.CallWindowProc" ) > 0 )
{
return true;
}
return false;
}
private void ShowExceptionDialog()
{
if ( _exceptionDialogVisible )
return;
_exceptionDialogVisible = true;
bool exceptionFatal = false;
try
{
while( true )
{
QueuedException qex = null;
lock( _exceptionQueue )
{
if ( _exceptionQueue.Count > 0 )
{
qex = (QueuedException) _exceptionQueue.Dequeue();
}
}
if ( qex == null )
break;
if ( ( qex.Flags & ExceptionReportFlags.Fatal ) != 0 )
{
exceptionFatal = true;
}
Exception ex = qex.Exception;
if ( ex is AsyncProcessorException )
ex = ex.InnerException;
if ( ex is TargetInvocationException )
ex = ex.InnerException;
ISettingStore ini = Core.SettingStore;
using( ExceptionReportForm dlg = new ExceptionReportForm() )
{
ProxySettings proxySettings = new ProxySettings();
try
{
string address = ini.ReadString( "HttpProxy", "Address" );
proxySettings.CustomProxy = address.Length > 0;
if( proxySettings.CustomProxy )
{
proxySettings.Host = address;
proxySettings.Port = ini.ReadInt( "HttpProxy", "Port", 3128 );
proxySettings.Login = ini.ReadString( "HttpProxy", "User" );
proxySettings.Password = ini.ReadString( "HttpProxy", "Password" );
}
dlg.SetProxy( proxySettings );
}
catch( Exception pex )
{
Trace.WriteLine( "Failed to set exception reporter proxy: " + pex.ToString() );
}
// Setup our submitter
dlg.Submitter = new RPCExceptionSubmitter();
dlg.ProjectKey = "OM";
dlg.DisplaySubmissionResult = true;
string userName = ini.ReadString( "ErrorReport", "UserName" );
string password = ini.ReadString( "ErrorReport", "Password" );
if ( userName.Length > 0 && password.Length > 0 )
{
dlg.SetITNLogin( userName, password );
}
else
{
dlg.SetDefaultLogin( "om_anonymous", "guest" );
}
if ( ( qex.Flags & ExceptionReportFlags.AttachLog ) != 0 )
{
dlg.AttachLog = true;
}
dlg.SetBuildNumber( Assembly.GetExecutingAssembly().GetName().Version.Build );
IWin32Window ownerWindow = ( _ownerControl == null || _ownerControl.IsDisposed ) ? null : _ownerControl;
if ( dlg.ReportException( ownerWindow, ex, GetExceptionDescription() ) == DialogResult.OK )
{
if ( dlg.ITNUserName != "om_anonymous" )
{
ini.WriteString( "ErrorReport", "UserName", dlg.ITNUserName );
ini.WriteString( "ErrorReport", "Password", dlg.ITNPassword );
}
if ( dlg.AttachLog )
{
LogManager.SubmitErrorLog();
}
proxySettings = dlg.ProxySettings;
if( !proxySettings.CustomProxy )
{
ini.WriteString( "HttpProxy", "Address", string.Empty );
}
else
{
ini.WriteString( "HttpProxy", "Address", proxySettings.Host );
ini.WriteInt( "HttpProxy", "Port", proxySettings.Port );
ini.WriteString( "HttpProxy", "User", proxySettings.Login );
ini.WriteString( "HttpProxy", "Password", proxySettings.Password );
}
}
}
}
}
catch(Exception ex)
{
MessageBox.Show(null, "An exception has occured in the application, and the exception reporter failed to present it.\n\n" + ex.Message + "\n\nThe application will now be terminated.", "Fatal Error", MessageBoxButtons.OK, MessageBoxIcon.Stop);
exceptionFatal = true;
}
_exceptionDialogVisible = false;
if ( exceptionFatal )
{
(Core.MainWindow as MainFrame).ForceClose();
}
}
internal string GetExceptionDescription()
{
string description = Core.ProductFullName + " v" + Core.ProductVersion + " starting with a ";
if ( _newDatabase )
{
description += "new database";
}
else
{
description += "database from build " + _dbCreatorBuild;
if ( _indexesRebuilt )
description += " after a crash";
else
description += " after a clean shutdown";
}
description += ", " + Core.State;
description += ", " + MainFrame.PlatformVersions + ", active - " + Environment.Version;
if ( _extraData.Length > 0 )
{
description += ". ";
description += _extraData;
}
return description;
}
public void IgnoreAllExceptions()
{
_ignoreAllExceptions = true;
}
}
}