Tuesday, May 31, 2016

Handling the application crash in Managed and Unmanaged code

Even though, if we handle the exceptions in our program, there are few scenarios which our application fail to catch those errors using the try… catch. In this article, I will be explaining a way to handle those kinds of exceptions in our program.


When an exception occurs in our program, the operating system will try to find a handler for that exception in the program, if it didn’t find any exception handler for that exception, then it will call the top level exception handler program which is Dr.Watson. The path of the Dr.Watson is configured in the registry key and the operating system will use that information to call that program. We can also create our own exception handler program to replace the Dr.Watson.

Handling the “Unhandled Exception” in Unmanaged Code (C++/VC++)

If we override the SetUnhandledExceptionFilter() method in our C++ program, that method will get called when all the exception handlers failed in our program. We need to call/register that function while starting of our program. We need to pass our method name to the SetUnhandledExceptionFilter() method as a parameter.

#include "stdafx.h"
#include

LONG WINAPI UnhandledException(PEXCEPTION_POINTERS pExceptionPtrs)
{

MessageBox(NULL,TEXT("Caught the unhanlded exception"), TEXT("Test Application"), 0);
return EXCEPTION_EXECUTE_HANDLER;
}

int _tmain(int argc, _TCHAR* argv[])
{

::SetUnhandledExceptionFilter(UnhandledException);

int *p =0;
*p =0; //Generates the crash

return 0;

}
As dereferencing the NULL-Pointer and assigning a value to it in user mode is not allowed the operating system indicates a fault and throws a SEH-exception. Those exceptions cannot be handled even by try - catch statements. In this case, the UnhandledException() method will get called. Once that method gets called, as a programmer, we have the complete control of that exception. Here, we can create dump (mini dump) which will have the details information about the crash and this will be very much help ful for the developer to analyze the issue and provide the solutions.


Creating Mini dump

To create a mini dump we can use the dbghelp.dll which is from Microsoft. That dll comes along with the Microsoft Debug diagnostics tool. I have modified the UnhandledException() method to generate the dump file when we get the crash.

#include "stdafx.h"
#include 
#include  //Has method to generate the crash.

// based on dbghelp.h
typedef BOOL (WINAPI *MINIDUMPWRITEDUMP)(HANDLE hProcess, DWORD dwPid, HANDLE hFile, 
MINIDUMP_TYPE DumpType, CONST PMINIDUMP_EXCEPTION_INFORMATION ExceptionParam, 
CONST PMINIDUMP_USER_STREAM_INFORMATION UserStreamParam,
 CONST PMINIDUMP_CALLBACK_INFORMATION CallbackParam);

LONG WINAPI UnhandledException(PEXCEPTION_POINTERS pExceptionPtrs) 
{
MessageBox(NULL,TEXT("Caught the unhanlded exception"), TEXT("Test Application"), 0);
LONG retval = EXCEPTION_CONTINUE_SEARCH;
HWND hParent = NULL; // find a better value for your app
HMODULE hDll = NULL;

char szDbgHelpPath[_MAX_PATH];
if (GetModuleFileName( NULL, szDbgHelpPath, _MAX_PATH ))
{

char *pSlash = _tcsrchr( szDbgHelpPath, '\\' );
 if (pSlash)
 {

  _tcscpy( pSlash+1, "DBGHELP.DLL" );
  hDll = ::LoadLibrary( szDbgHelpPath );
 }

}

if (hDll==NULL)
{
// load any version we can
hDll = ::LoadLibrary( "DBGHELP.DLL" );
}

LPCTSTR szResult = NULL;
if (hDll)
{
MINIDUMPWRITEDUMP pDump = (MINIDUMPWRITEDUMP)::GetProcAddress( hDll, "MiniDumpWriteDump" );

if (pDump)
{
char szDumpPath[_MAX_PATH];
char szScratch [_MAX_PATH];

if (!GetTempPath( _MAX_PATH, szDumpPath ))
_tcscpy( szDumpPath, "c:\\temp\\" );


_tcscat( szDumpPath, "SampleApp" );
_tcscat( szDumpPath, ".dmp" );

if (::MessageBox( NULL, "Something bad happened in your program, would you 
                 like to save a diagnostic file?", "SampleApp", MB_YESNO )==IDYES)

{

// create the file
HANDLE hFile = ::CreateFile( szDumpPath, GENERIC_WRITE, FILE_SHARE_WRITE, NULL, 
                     CREATE_ALWAYS, FILE_ATTRIBUTE_NORMAL, NULL );

if (hFile!=INVALID_HANDLE_VALUE)

{
_MINIDUMP_EXCEPTION_INFORMATION ExInfo;
ExInfo.ThreadId = ::GetCurrentThreadId();
ExInfo.ExceptionPointers = pExceptionPtrs;
ExInfo.ClientPointers = NULL;

// write the dump
BOOL bOK = pDump( GetCurrentProcess(), GetCurrentProcessId(), hFile, MiniDumpNormal, 
               &ExInfo, NULL, NULL );

if (bOK)
{
sprintf( szScratch, "Saved dump file to '%s'", szDumpPath );
szResult = szScratch;
retval = EXCEPTION_EXECUTE_HANDLER;
}

else
{
sprintf( szScratch, "Failed to save dump file to '%s' (error %d)", 
                           szDumpPath, GetLastError());
szResult = szScratch;
}
::CloseHandle(hFile);

}

else
{
sprintf( szScratch, "Failed to create dump file '%s' (error %d)", szDumpPath, 
                     GetLastError());
szResult = szScratch;
}
}
}
else
{
szResult = "DBGHELP.DLL too old";
}
}
else
{
szResult = "DBGHELP.DLL not found";
}

if (szResult)
::MessageBox( NULL, szResult, "SampleApp", MB_OK );

return retval;
return EXCEPTION_EXECUTE_HANDLER; 

} 


int _tmain(int argc, _TCHAR* argv[])
{
::SetUnhandledExceptionFilter(UnhandledException); 
int *p =0;
*p =0;
return 0;

}
The created dump file can be analyzed/opened in Microsoft Visual studio or Microsoft Debug Diagnostics tool.

Crashrpt (https://code.google.com/p/crashrpt/) is a open source exception handling framework available to handle the unhandled exception in our application. This tool also generates the crash dump and sent that report to the software vendor over the internet.

Handling the “Unhandled Exception” in Managed code (.NET WinForm)

There are two event handlers are available to deal the unhandled exception in .NET

Application.ThreadException Event

This event will only be raised when the unhandled exception occurs in WinForM UI thread.

using System;
using System.Collections.Generic;
using System.Windows.Forms;

namespace HandleCrash_CSharp
{

static class Program
{
[STAThread]

static void Main()

{

Application.ThreadException +=

new System.Threading.ThreadExceptionEventHandler(MyApplication_ThreadException);

Application.EnableVisualStyles();
Application.SetCompatibleTextRenderingDefault(false);
Application.Run(new Form1());

}

public static void MyApplication_ThreadException (object sender, System.Threading.ThreadExceptionEventArgs e)
{

DialogResult result = DialogResult.Abort;

try
{

result = MessageBox.Show("Unhandled exception occured\n\n"
+ e.Exception.Message + e.Exception.StackTrace,
"Application Error", MessageBoxButtons.AbortRetryIgnore, MessageBoxIcon.Stop);

}
finally
{
         if (result == DialogResult.Abort)
        {

               Application.Exit();

        }
}
}
}

}

AppDomain.CurrentDomain.UnhandledException Event

The UnhandledException event on AppDomain, any exception in the current AppDomain will trigger that event. This includes not only the main application thread (the UI thread), but any other threads as well.

using System;
using System.Collections.Generic;
using System.Windows.Forms;
namespace HandleCrash_CSharp
{
static class Program
{

[STAThread]
static void Main()
{

AppDomain.CurrentDomain.UnhandledException +=
new UnhandledExceptionEventHandler(MyCurrentDomain_UnhandledException);
Application.EnableVisualStyles();
Application.SetCompatibleTextRenderingDefault(false);
Application.Run(new Form1());

}

static void MyCurrentDomain_UnhandledException(object sender, UnhandledExceptionEventArgs e)
{
try

{

Exception ex = (Exception)e.ExceptionObject;
MessageBox.Show("Unhandled exception occured\n\n" + ex.Message + ex.StackTrace,

"Fatal Error", MessageBoxButtons.OK, MessageBoxIcon.Stop);

}

finally

{

Application.Exit();

}

}
}

}