Integrating BugSplat crash reporting into your native Windows application is easy! The following example code is from the myConsoleCrasher application:
#pragma optimize( "", off) // prevent optimizer from interfering with our crash-producing code
#include "stdafx.h"
#include <vector>
#include <new.h>
#include <windows.h>
#include "BugSplat.h"
void MemoryException();
void StackOverflow(void *p);
void DivideByZero();
void ExhaustMemory();
void ThrowByUser();
void ThreadException();
void CallAbort();
void InvalidParameters();
void OutOfBoundsVectorCrash();
void VirtualFunctionCallCrash();
void CustomSEHException();
void CreateReport();
bool ExceptionCallback(UINT nCode, LPVOID lpVal1, LPVOID lpVal2);
int wmain(int argc, wchar_t **argv)
{
if (IsDebuggerPresent())
{
wprintf(L"Run this application without the debugger attached to enable BugSplat exception handling.\n");
DebugBreak();
exit(0);
}
mpSender =
new MiniDmpSender(L
"Fred", L
"myConsoleCrasher", L
"1.0", NULL, MDSF_USEGUARDMEMORY | MDSF_LOGFILE | MDSF_PREVENTHIJACKING);
SetGlobalCRTExceptionBehavior();
SetPerThreadCRTExceptionBehavior();
mpSender->
setNotes(_T(
"Additional 'notes' data supplied through API"));
for (int i = 1; i < argc; i++) {
if (!_wcsicmp(argv[i], L"/AttachFiles")) {
}
}
for (int i = 1; i < argc; i++) {
if (!_wcsicmp(argv[i], L"/Crash")) {
MemoryException();
}
if (!_wcsicmp(argv[i], L"/MemoryException")) {
MemoryException();
}
else if (!_wcsicmp(argv[i], L"/StackOverflow")) {
StackOverflow(NULL);
}
else if (!_wcsicmp(argv[i], L"/DivByZero")) {
DivideByZero();
}
else if (!_wcsicmp(argv[i], L"/OutOfMemory")) {
ExhaustMemory();
}
else if (!_wcsicmp(argv[i], L"/Throw")) {
ThrowByUser();
}
else if (!_wcsicmp(argv[i], L"/Thread")) {
ThreadException();
}
else if (!_wcsicmp(argv[i], L"/Abort")) {
CallAbort();
}
else if (!_wcsicmp(argv[i], L"/VectorOutOfBounds")) {
OutOfBoundsVectorCrash();
}
else if (!_wcsicmp(argv[i], L"/InvalidParameters")) {
InvalidParameters();
}
else if (!_wcsicmp(argv[i], L"/PureVirtual")) {
VirtualFunctionCallCrash();
}
else if (!_wcsicmp(argv[i], L"/SEH")) {
CustomSEHException();
wprintf(L"Application normal exit.\n");
exit(0);
}
else if (!_wcsicmp(argv[i], L"/CreateReport")) {
CreateReport();
wprintf(L"Application normal exit.\n");
exit(0);
}
}
MemoryException();
return 0;
}
void MemoryException()
{
wprintf(L"MemoryException!\n");
*(volatile int *)0 = 0;
}
void DivideByZero()
{
wprintf(L"DivideByZero!\n");
volatile int x, y, z;
x = 1;
y = 0;
z = x / y;
}
void StackOverflow(void *p)
{
wprintf(L"StackOverflow!\n");
volatile char q[10000];
while (true) {
StackOverflow((void *)q);
}
}
void ExhaustMemory()
{
wprintf(L"ExhaustMemory!\n");
while (true)
{
char* a = new char[1024 * 1024];
a[0] = 'X';
}
}
void ThrowByUser()
{
wprintf(L"Throw user generated exception!\n");
throw("User generated exception!");
}
DWORD WINAPI MyThreadCrasher( LPVOID )
{
wprintf(L"MyThreadCrasher!\n");
MemoryException();
return 0;
}
void ThreadException()
{
#define MAX_THREADS 1
DWORD dwThreadIdArray[MAX_THREADS];
HANDLE hThreadArray[MAX_THREADS];
for (int i = 0; i < MAX_THREADS; i++)
{
hThreadArray[i] = CreateThread(
NULL,
0,
MyThreadCrasher,
NULL,
0,
&dwThreadIdArray[i]);
if (hThreadArray[i] == NULL)
{
wprintf(L"CreateThread failed");
ExitProcess(3);
}
}
WaitForMultipleObjects(MAX_THREADS, hThreadArray, TRUE, INFINITE);
}
void CallAbort()
{
wprintf(L"abort()!\n");
abort();
}
void OutOfBoundsVectorCrash()
{
wprintf(L"std::vector out of bounds!\n");
std::vector<int> v;
v[0] = 5;
}
void InvalidParameters()
{
wprintf(L"Invalid parameters!\n");
char *fmt = NULL;
printf(fmt);
}
void VirtualFunctionCallCrash()
{
struct Base {
Base()
{
wprintf(L"Pure Virtual Function Call crash!");
BaseFunc();
}
virtual void DerivedFunc() = 0;
void BaseFunc()
{
DerivedFunc();
}
};
struct Derived : public Base
{
void DerivedFunc() {}
};
Base* instance = new Derived;
instance->DerivedFunc();
}
DWORD SEHFilterFunction(EXCEPTION_POINTERS *exp)
{
return EXCEPTION_EXECUTE_HANDLER;
}
void CustomSEHException()
{
__try
{
RaiseException(
0x123,
0,
0, NULL);
}
__except (SEHFilterFunction(GetExceptionInformation()))
{
return;
}
}
void CreateReport()
{
const __wchar_t *xml = L"<report><process>"
"<exception>"
"<code>FATAL ERROR</code>"
"<explanation>This is an error code explanation</explanation>"
"<func><![CDATA[myConsoleCrasher!MemoryException]]></func>"
"<file>/www/bugsplatAutomation/myConsoleCrasher/myConsoleCrasher.cpp</file>"
"<line>143</line>"
"<registers>"
"<cs>0023</cs>"
"<ds>002b</ds>"
"<eax>00000011</eax>"
"<ebp>00affb58</ebp>"
"<ebx>00858000</ebx>"
"<ecx>43bf1e0e</ecx>"
"<edi>00affb58</edi>"
"<edx>014480b4</edx>"
"<efl>00010202</efl>"
"</registers>"
"</exception>"
"<modules numloaded=\"2\">"
"<module>"
"<name>myConsoleCrasher</name>"
"<order>1</order>"
"<address>01320000-01457000</address>"
"<path>C:/www/BugsplatAutomation/BugsplatAutomation/bin/x64/Release/temp/BugSplat/bin/myConsoleCrasher.exe</path>"
"<symbolsloaded>deferred</symbolsloaded>"
"<fileversion/>"
"<productversion/>"
"<checksum>00000000</checksum>"
"<timedatestamp>SatJun1501:18:092019</timedatestamp>"
"</module>"
"<module>"
"<name>BugSplatRc</name>"
"<order>2</order>"
"<address>01320000-01457000</address>"
"<path>C:/www/BugsplatAutomation/BugsplatAutomation/bin/x64/Release/BugSplatRc.dll</path>"
"<symbolsloaded>deferred</symbolsloaded>"
"<fileversion/>"
"<productversion/>"
"<checksum>00000000</checksum>"
"<timedatestamp>SatJun1501:18:092019</timedatestamp>"
"</module>"
"</modules>"
"<threads count=\"2\">"
"<thread id=\"0\" current=\"yes\" event=\"yes\" framecount=\"3\">"
"<frame>"
"<symbol><![CDATA[myConsoleCrasher!MemoryException]]></symbol>"
"<file>/www/bugsplatAutomation/myConsoleCrasher/myConsoleCrasher.cpp</file>"
"<line>143</line>"
"<offset>0x35</offset>"
"</frame>"
"<frame>"
"<symbol><![CDATA[myConsoleCrasher!wmain]]></symbol>"
"<file>C:/www/BugsplatAutomation/BugsplatAutomation/BugSplat/samples/myConsoleCrasher/myConsoleCrasher.cpp</file>"
"<line>83</line>"
"<offset>0x239</offset>"
"</frame>"
"<frame>"
"<symbol><![CDATA[myConsoleCrasher!__scrt_wide_environment_policy::initialize_environment]]></symbol>"
"<file>d:/agent/_work/4/s/src/vctools/crt/vcstartup/src/startup/exe_common.inl</file>"
"<line>90</line>"
"<offset>0x43</offset>"
"</frame>"
"</thread>"
"<thread id=\"1\" current=\"no\" event=\"no\" framecount=\"3\">"
"<frame>"
"<symbol><![CDATA[my2ConsoleCrasher!MemoryException]]></symbol>"
"<file>/www/bugsplatAutomation/myConsoleCrasher/myConsoleCrasher.cpp</file>"
"<line>143</line>"
"<offset>0x35</offset>"
"</frame>"
"<frame>"
"<symbol><![CDATA[my2ConsoleCrasher!wmain]]></symbol>"
"<file>C:/www/BugsplatAutomation/BugsplatAutomation/BugSplat/samples/myConsoleCrasher/myConsoleCrasher.cpp</file>"
"<line>83</line>"
"<offset>0x239</offset>"
"</frame>"
"<frame>"
"<symbol><![CDATA[my2ConsoleCrasher!__scrt_wide_environment_policy::initialize_environment]]></symbol>"
"<file>d:/agent/_work/4/s/src/vctools/crt/vcstartup/src/startup/exe_common.inl</file>"
"<line>90</line>"
"<offset>0x43</offset>"
"</frame>"
"</thread>"
"</threads></process></report>";
}
bool ExceptionCallback(UINT nCode, LPVOID lpVal1, LPVOID lpVal2)
{
switch (nCode)
{
case MDSCB_EXCEPTIONCODE:
{
EXCEPTION_RECORD *p = (EXCEPTION_RECORD *)lpVal1;
DWORD code = p ? p->ExceptionCode : 0;
wchar_t cmdString[2 * MAX_PATH];
wchar_t filePath[MAX_PATH];
wchar_t tempPath[MAX_PATH];
GetTempPathW(MAX_PATH, tempPath);
wsprintf(filePath, L"%sfile1.txt", tempPath);
wsprintf(cmdString, L"echo Exception Code = 0x%08x > %s", code, filePath);
_wsystem(cmdString);
wsprintf(filePath, L"%sfile2.txt", tempPath);
wchar_t buf[_MAX_PATH];
wsprintf(cmdString, L"echo Crash reporting is so clutch! minidump path = %s > %s", buf, filePath);
_wsystem(cmdString);
}
break;
}
return false;
}