Subversion Repositories AndroidProjects

Rev

Blame | Last modification | View Log | RSS feed

#include "SEHCatcher.h"
#include "SexyAppBase.h"
#include <fstream>
#include <process.h>

#ifdef ZYLOM
#include "zylomso.h"
using namespace zylom::zylomso;
#endif

using namespace Sexy;

LPTOP_LEVEL_EXCEPTION_FILTER SEHCatcher::mPreviousFilter;
SexyAppBase*                            SEHCatcher::mApp = NULL;
HFONT                                           SEHCatcher::mDialogFont = NULL;
HFONT                                           SEHCatcher::mBoldFont = NULL;
bool                                            SEHCatcher::mHasDemoFile = false;
bool                                            SEHCatcher::mDebugError = false;
std::string                                     SEHCatcher::mErrorTitle;
std::string                                     SEHCatcher::mErrorText;
std::string                                     SEHCatcher::mUserText;
std::string                                     SEHCatcher::mUploadFileName;
HWND                                            SEHCatcher::mYesButtonWindow = NULL;
HWND                                            SEHCatcher::mNoButtonWindow = NULL;
HWND                                            SEHCatcher::mDebugButtonWindow = NULL;
HWND                                            SEHCatcher::mEditWindow = NULL;
HMODULE                                         SEHCatcher::mImageHelpLib = NULL;
SYMINITIALIZEPROC                       SEHCatcher::mSymInitialize = NULL;
SYMSETOPTIONSPROC                       SEHCatcher::mSymSetOptions = NULL;
UNDECORATESYMBOLNAMEPROC        SEHCatcher::mUnDecorateSymbolName = NULL;
SYMCLEANUPPROC                          SEHCatcher::mSymCleanup = NULL;
STACKWALKPROC                           SEHCatcher::mStackWalk = NULL;
SYMFUNCTIONTABLEACCESSPROC      SEHCatcher::mSymFunctionTableAccess = NULL;
SYMGETMODULEBASEPROC            SEHCatcher::mSymGetModuleBase = NULL;
SYMGETSYMFROMADDRPROC           SEHCatcher::mSymGetSymFromAddr = NULL;
HTTPTransfer                            SEHCatcher::mSubmitReportTransfer;
bool                                            SEHCatcher::mExiting = false;
bool                                            SEHCatcher::mShowUI = true;
bool                                            SEHCatcher::mAllowSubmit = true;

std::wstring                                    SEHCatcher::mCrashMessage = L"An unexpected error has occured!";
std::string                                             SEHCatcher::mSubmitHost;
std::wstring                                    SEHCatcher::mSubmitMessage = L"Please help out by providing as much information as you can about this crash.";
std::wstring                                    SEHCatcher::mSubmitErrorMessage = L"Failed to connect to server.";

static bool gUseDefaultFonts = true;

struct
{
    DWORD   dwExceptionCode;
    char    *szMessage;
} gMsgTable[] = {
   { STATUS_SEGMENT_NOTIFICATION,     "Segment Notification" },
   { STATUS_BREAKPOINT,               "Breakpoint" },
   { STATUS_SINGLE_STEP,              "Single step" },
   { STATUS_WAIT_0,                   "Wait 0" },
   { STATUS_ABANDONED_WAIT_0,         "Abandoned Wait 0" },
   { STATUS_USER_APC,                 "User APC" },
   { STATUS_TIMEOUT,                  "Timeout" },
   { STATUS_PENDING,                  "Pending" },
   { STATUS_GUARD_PAGE_VIOLATION,     "Guard Page Violation" },
   { STATUS_DATATYPE_MISALIGNMENT,    "Data Type Misalignment" },
   { STATUS_ACCESS_VIOLATION,         "Access Violation" },
   { STATUS_IN_PAGE_ERROR,            "In Page Error" },
   { STATUS_NO_MEMORY,                "No Memory" },
   { STATUS_ILLEGAL_INSTRUCTION,      "Illegal Instruction" },
   { STATUS_NONCONTINUABLE_EXCEPTION, "Noncontinuable Exception" },
   { STATUS_INVALID_DISPOSITION,      "Invalid Disposition" },
   { STATUS_ARRAY_BOUNDS_EXCEEDED,    "Array Bounds Exceeded" },
   { STATUS_FLOAT_DENORMAL_OPERAND,   "Float Denormal Operand" },
   { STATUS_FLOAT_DIVIDE_BY_ZERO,     "Divide by Zero" },
   { STATUS_FLOAT_INEXACT_RESULT,     "Float Inexact Result" },
   { STATUS_FLOAT_INVALID_OPERATION,  "Float Invalid Operation" },
   { STATUS_FLOAT_OVERFLOW,           "Float Overflow" },
   { STATUS_FLOAT_STACK_CHECK,        "Float Stack Check" },
   { STATUS_FLOAT_UNDERFLOW,          "Float Underflow" },
   { STATUS_INTEGER_DIVIDE_BY_ZERO,   "Integer Divide by Zero" },
   { STATUS_INTEGER_OVERFLOW,         "Integer Overflow" },
   { STATUS_PRIVILEGED_INSTRUCTION,   "Privileged Instruction" },
   { STATUS_STACK_OVERFLOW,           "Stack Overflow" },
   { STATUS_CONTROL_C_EXIT,           "Ctrl+C Exit" },
   { 0xFFFFFFFF,                      "" }
};


SEHCatcher::SEHCatcher()
{      
        mPreviousFilter = SetUnhandledExceptionFilter(UnhandledExceptionFilter);        
}

SEHCatcher::~SEHCatcher()
{
        SetUnhandledExceptionFilter(mPreviousFilter);
}

long __stdcall SEHCatcher::UnhandledExceptionFilter(LPEXCEPTION_POINTERS lpExceptPtr)
{
        if (mApp != NULL)
                mApp->SEHOccured();    

        DoHandleDebugEvent(lpExceptPtr);
       
        if (!mDebugError)
                SetErrorMode(SEM_NOGPFAULTERRORBOX);
       
        return EXCEPTION_CONTINUE_SEARCH;      
}

bool SEHCatcher::LoadImageHelp()
{
        mImageHelpLib = LoadLibraryA("IMAGEHLP.DLL");
    if (!mImageHelpLib)
        return false;

    mSymInitialize = (SYMINITIALIZEPROC) GetProcAddress(mImageHelpLib, "SymInitialize");
    if (!mSymInitialize)
        return false;

        mSymSetOptions = (SYMSETOPTIONSPROC) GetProcAddress(mImageHelpLib, "SymSetOptions");
    if (!mSymSetOptions)
        return false;

    mSymCleanup = (SYMCLEANUPPROC) GetProcAddress(mImageHelpLib, "SymCleanup");
    if (!mSymCleanup)
        return false;

        mUnDecorateSymbolName = (UNDECORATESYMBOLNAMEPROC) GetProcAddress(mImageHelpLib, "UnDecorateSymbolName");
    if (!mUnDecorateSymbolName)
        return false;

    mStackWalk = (STACKWALKPROC) GetProcAddress(mImageHelpLib, "StackWalk");
    if (!mStackWalk)
        return false;

    mSymFunctionTableAccess = (SYMFUNCTIONTABLEACCESSPROC) GetProcAddress(mImageHelpLib, "SymFunctionTableAccess");
    if (!mSymFunctionTableAccess)
        return false;

    mSymGetModuleBase = (SYMGETMODULEBASEPROC) GetProcAddress(mImageHelpLib, "SymGetModuleBase");
    if (!mSymGetModuleBase)
        return false;

    mSymGetSymFromAddr = (SYMGETSYMFROMADDRPROC) GetProcAddress(mImageHelpLib, "SymGetSymFromAddr" );
    if (!mSymGetSymFromAddr)
        return false;    

        mSymSetOptions(SYMOPT_DEFERRED_LOADS);

        // Get image filename of the main executable
        char filepath[MAX_PATH], *lastdir, *pPath;
        DWORD filepathlen = GetModuleFileNameA ( NULL, filepath, sizeof(filepath));
       
    lastdir = strrchr (filepath, '/');
    if (lastdir == NULL) lastdir = strrchr (filepath, '\\');
    if (lastdir != NULL) lastdir[0] = '\0';

    // Initialize the symbol table routines, supplying a pointer to the path
    pPath = filepath;
    if (strlen (filepath) == 0) pPath = NULL;

     if (!mSymInitialize (GetCurrentProcess(), pPath, TRUE))
                return false;

    return true;
}

void SEHCatcher::UnloadImageHelp()
{
        if (mImageHelpLib != NULL)
                FreeLibrary(mImageHelpLib);
}

static bool StrToLongHex(const std::string& aString, DWORD* theValue)
{      
        *theValue = 0;

        for (int i = 0; i < (int)aString.length(); i++)
        {
                char aChar = aString[i];

                int aValue;
                if ((aChar >= '0') && (aChar <= '9'))
                        aValue = aChar - '0';
                else if ((aChar >= 'A') && (aChar <= 'F'))
                        aValue = (aChar - 'A') + 10;
                else if ((aChar >= 'a') && (aChar <= 'f'))
                        aValue = (aChar - 'a') + 10;
                else
                        return false;

                *theValue += aValue << ((aString.length()-i-1)*4);
        }

        return true;
}

void SEHCatcher::GetSymbolsFromMapFile(std::string &theDebugDump)
{
        DWORD aTick = GetTickCount();
        WIN32_FIND_DATAA aFindData;
        HANDLE aFindHandle = FindFirstFileA("*.map",&aFindData);
        if (aFindHandle==INVALID_HANDLE_VALUE)
                return;

        FindClose(aFindHandle);

        typedef std::pair<DWORD,DWORD> SymbolPair;
        typedef std::map<SymbolPair,std::string> SymbolMap;
        typedef std::pair<std::string,int> LineNumPair;
        typedef std::map<SymbolPair,LineNumPair> LineNumMap;

/**/
        SymbolMap aSymbolMap;
        LineNumMap aLineNumMap;
        std::fstream aFile(aFindData.cFileName, std::ios::in);

        if (!aFile.is_open())
                return;

        // Parse map file
        std::string aCurLinesFile;
        while (!aFile.eof())
        {
                char aStr[4096];
                aFile.getline(aStr, 4096);

                std::string aString = aStr;

                if ((aString.length() > 22) && (aString[5] == ':'))
                {
                        std::string aFoundPreStr = aString.substr(1, 4);
                        std::string aFoundPostStr = aString.substr(6, 8);

                        DWORD aFoundPreVal;
                        DWORD aFoundPostVal;

                        if (StrToLongHex(aFoundPreStr, &aFoundPreVal) && StrToLongHex(aFoundPostStr, &aFoundPostVal))
                        {
                                int aSpacePos = aString.find(' ', 21);

                                if ((aString[20] == ' ') && (aSpacePos >= 0))
                                        aSymbolMap[SymbolPair(aFoundPreVal,aFoundPostVal)] =  aString.substr(21, aSpacePos-21);
                        }
                }
                else if (strcmp(aString.substr(0, strlen("Line numbers")).c_str(), "Line numbers") == 0)
                {
                        int aSegmentPos = aString.rfind(')');
                        if (aSegmentPos == -1)
                                aSegmentPos = aString.length();

                        int aPreLen = strlen("Line numbers for ");

                        int aStartPos = aString.find('(');

                        aCurLinesFile = aString.substr(aStartPos + 1, aSegmentPos - aStartPos - 1);
                }
                else if ((aCurLinesFile.length() > 0) && (aString.length() == 80))
                {      
                        // Line number stuff

                        for (int i = 0; i < 4; i++)
                        {
                                int aStartLoc = 20 * i;

                                int aLine = atoi(aString.substr(aStartLoc, 6).c_str());

                                std::string aFoundPreStr = aString.substr(aStartLoc + 7, 4);
                                std::string aFoundPostStr = aString.substr(aStartLoc + 12, 8);

                                DWORD aFoundPreVal;
                                DWORD aFoundPostVal;

                                if (StrToLongHex(aFoundPreStr, &aFoundPreVal) && StrToLongHex(aFoundPostStr, &aFoundPostVal))
                                {
                                        aLineNumMap[SymbolPair(aFoundPreVal,aFoundPostVal)] = LineNumPair(aCurLinesFile,aLine);
                                }
                        }
                }
        }

        // Parse stack trace
        for (int i = 0; i < (int)theDebugDump.length(); i++)
        {
                if (theDebugDump.at(i) == ':')
                {
                        std::string aFindPreStr = theDebugDump.substr(i-4, 4);
                        std::string aFindPostStr = theDebugDump.substr(i+1, 8);

                        DWORD aFindPreVal;
                        DWORD aFindPostVal;

                        if (StrToLongHex(aFindPreStr, &aFindPreVal) && StrToLongHex(aFindPostStr, &aFindPostVal))
                        {

                                int aBestDist = -1;
                                SymbolMap::iterator aSymbolItr  = aSymbolMap.lower_bound(SymbolPair(aFindPreVal,aFindPostVal));
                                if (aSymbolItr!=aSymbolMap.end() && aSymbolItr!=aSymbolMap.begin() && aSymbolItr->first.second!=aFindPostVal)
                                        --aSymbolItr;

                                if (aSymbolItr!=aSymbolMap.end() && aSymbolItr->first.first==aFindPreVal)
                                        aBestDist = aFindPostVal - aSymbolItr->first.second;

                                if (aBestDist != -1)
                                {
                                        std::string &aBestName = aSymbolItr->second;

                                        char aSymbolName[4096];

                                        if (mUnDecorateSymbolName(aBestName.c_str(), aSymbolName, 4096,
                                                UNDNAME_NO_ALLOCATION_MODEL | UNDNAME_NO_ACCESS_SPECIFIERS |
                                                UNDNAME_NO_THROW_SIGNATURES | UNDNAME_NO_MEMBER_TYPE) == 0)
                                                strcpy(aSymbolName, aBestName.c_str());

                                        if (aBestDist != 0)
                                        {
                                                sprintf(aSymbolName+strlen(aSymbolName), "+0x%X", aBestDist);
                                        }

                                        std::string aNewText = aSymbolName;

                                        LineNumMap::iterator aLineNumItr = aLineNumMap.lower_bound(SymbolPair(aFindPreVal,aFindPostVal));
                                        if (aLineNumItr!=aLineNumMap.end() && aLineNumItr!=aLineNumMap.begin() && aLineNumItr->first.second!=aFindPostVal)
                                                --aLineNumItr;

                                        if (aLineNumItr!=aLineNumMap.end() && aLineNumItr->first.first==aFindPreVal)
                                        {                                                      
                                                std::string &aBestFile = aLineNumItr->second.first;
                                                int aBestLine = aLineNumItr->second.second;
                                                int aBestLineDist =  aFindPostVal - aLineNumItr->first.second;

                                                char aDistStr[4096];
                                                sprintf(aDistStr, "\r\n    %s line %d +0x%X", aBestFile.c_str(), aBestLine, aBestLineDist);
                                                aNewText += aDistStr;
                                        }

                                        theDebugDump.erase(i-4, 13);
                                        theDebugDump.insert(i-4, aNewText.c_str());
                                }
                        }
                }
        }

//      MessageBox(NULL,StrFormat("%d",GetTickCount()-aTick).c_str(),"Time",MB_OK);
}

void SEHCatcher::DoHandleDebugEvent(LPEXCEPTION_POINTERS lpEP)
{
        bool hasImageHelp = LoadImageHelp();

        std::string anErrorTitle;
        std::string aDebugDump;

        char aBuffer[2048];
   
                ///////////////////////////
        // first name the exception    
        char  *szName = NULL;
    for (int i=0; gMsgTable[i].dwExceptionCode != 0xFFFFFFFF; i++)
        {
        if (gMsgTable[i].dwExceptionCode == lpEP->ExceptionRecord->ExceptionCode)
                {
            szName = gMsgTable[i].szMessage;
            break;
        }
    }  

    if (szName != NULL)
        {              
                sprintf(aBuffer,"Exception: %s (code 0x%x) at address %08X in thread %X\r\n",
                                szName, lpEP->ExceptionRecord->ExceptionCode,
                                lpEP->ExceptionRecord->ExceptionAddress, GetCurrentThreadId());
        }
        else
        {
                sprintf(aBuffer,"Unknown exception: (code 0x%x) at address %08X in thread %X\r\n",
                                lpEP->ExceptionRecord->ExceptionCode,
                                lpEP->ExceptionRecord->ExceptionAddress, GetCurrentThreadId());
        }

        aDebugDump += aBuffer; 

        ///////////////////////////////////////////////////////////
        // Get logical address of the module where exception occurs
        DWORD section, offset;
        GetLogicalAddress(lpEP->ExceptionRecord->ExceptionAddress, aBuffer, sizeof(aBuffer), section, offset);
        aDebugDump += "Module: " + GetFilename(aBuffer) + "\r\n";              
        sprintf(aBuffer, "Logical Address: %04X:%08X\r\n", section, offset);
        aDebugDump += aBuffer; 
       
        aDebugDump += "\r\n";  

        anErrorTitle = StrFormat("Exception at %04X:%08X", section, offset);

        std::string aWalkString;       

        if (hasImageHelp)
                aWalkString = ImageHelpWalk(lpEP->ContextRecord, 0);

        if (aWalkString.length() == 0)
                aWalkString = IntelWalk(lpEP->ContextRecord, 0);

        aDebugDump += aWalkString;

        aDebugDump += "\r\n";
        sprintf(aBuffer, ("EAX:%08X EBX:%08X ECX:%08X EDX:%08X ESI:%08X EDI:%08X\r\n"),
            lpEP->ContextRecord->Eax, lpEP->ContextRecord->Ebx, lpEP->ContextRecord->Ecx, lpEP->ContextRecord->Edx, lpEP->ContextRecord->Esi, lpEP->ContextRecord->Edi);
        aDebugDump += aBuffer;
    sprintf(aBuffer, "EIP:%08X ESP:%08X  EBP:%08X\r\n", lpEP->ContextRecord->Eip, lpEP->ContextRecord->Esp, lpEP->ContextRecord->Ebp); 
        aDebugDump += aBuffer;
    sprintf(aBuffer, "CS:%04X SS:%04X DS:%04X ES:%04X FS:%04X GS:%04X\r\n", lpEP->ContextRecord->SegCs, lpEP->ContextRecord->SegSs, lpEP->ContextRecord->SegDs, lpEP->ContextRecord->SegEs, lpEP->ContextRecord->SegFs, lpEP->ContextRecord->SegGs );
        aDebugDump += aBuffer;
    sprintf(aBuffer, "Flags:%08X\r\n", lpEP->ContextRecord->EFlags );
        aDebugDump += aBuffer;

        aDebugDump += "\r\n";
        aDebugDump += GetSysInfo();    

        if (mApp != NULL)
        {
                std::string aGameSEHInfo = mApp->GetGameSEHInfo();
                if (aGameSEHInfo.length() > 0)
                {
                        aDebugDump += "\r\n";
                        aDebugDump += aGameSEHInfo;
                }

                mApp->CopyToClipboard(aDebugDump);
        }              

        if (hasImageHelp)
                GetSymbolsFromMapFile(aDebugDump);

        WriteToFile(aDebugDump);

#ifdef ZYLOM
        ZylomGS_StandAlone_SendBugReport((char*) aDebugDump.c_str());
#else
        if (mApp != NULL)
        {
                if (mApp->mRecordingDemoBuffer)
                {
                        // Make sure we have enough update block things in there to
                        //  get to the final crashing update
                        mApp->WriteDemoTimingBlock();
                        mApp->mDemoBuffer.WriteNumBits(0, 1);
                        mApp->mDemoBuffer.WriteNumBits(DEMO_IDLE, 5);
                        mApp->WriteDemoBuffer();               
                        mUploadFileName = mApp->mDemoFileName;
                }      

                mHasDemoFile = mApp->mRecordingDemoBuffer;
                std::string anUploadFile = mApp->NotifyCrashHook();
                if (!anUploadFile.empty())
                {
                        mUploadFileName = anUploadFile;
                        mHasDemoFile = true;
                }

                mApp->mRecordingDemoBuffer = false;
                mApp->mPlayingDemoBuffer = false;
        }

        if (mShowUI)
                ShowErrorDialog(anErrorTitle, aDebugDump);
#endif

        //::MessageBox(NULL, aDebugDump.c_str(), "ERROR", MB_ICONERROR);       

        UnloadImageHelp();     
}

std::string SEHCatcher::IntelWalk(PCONTEXT theContext, int theSkipCount)
{
        std::string aDebugDump;
        char aBuffer[2048];

        DWORD pc = theContext->Eip;
    PDWORD pFrame, pPrevFrame;
   
    pFrame = (PDWORD)theContext->Ebp;

    for (;;)
    {
        char szModule[MAX_PATH] = "";
        DWORD section = 0, offset = 0;

        GetLogicalAddress((PVOID)pc, szModule, sizeof(szModule), section, offset);

        sprintf(aBuffer, "%08X  %08X  %04X:%08X %s\r\n",
                  pc, pFrame, section, offset, GetFilename(szModule).c_str());
                aDebugDump += aBuffer;

        pc = pFrame[1];

        pPrevFrame = pFrame;

        pFrame = (PDWORD)pFrame[0]; // proceed to next higher frame on stack

        if ((DWORD)pFrame & 3)    // Frame pointer must be aligned on a
            break;                  // DWORD boundary.  Bail if not so.

        if (pFrame <= pPrevFrame)
            break;

        // Can two DWORDs be read from the supposed frame address?          
        if (IsBadWritePtr(pFrame, sizeof(PVOID)*2))
            break;
    };

        return aDebugDump;
}

std::string SEHCatcher::ImageHelpWalk(PCONTEXT theContext, int theSkipCount)
{
        char aBuffer[2048];
        std::string aDebugDump;

        STACKFRAME sf;
        memset( &sf, 0, sizeof(sf) );  

        // Initialize the STACKFRAME structure for the first call.  This is only
        // necessary for Intel CPUs, and isn't mentioned in the documentation.
        sf.AddrPC.Offset       = theContext->Eip;
        sf.AddrPC.Mode         = AddrModeFlat;
        sf.AddrStack.Offset    = theContext->Esp;
        sf.AddrStack.Mode      = AddrModeFlat;
        sf.AddrFrame.Offset    = theContext->Ebp;
        sf.AddrFrame.Mode      = AddrModeFlat;
       
        int aLevelCount = 0;

        for (;;)
        {
                if (!mStackWalk(IMAGE_FILE_MACHINE_I386, GetCurrentProcess(), GetCurrentThread(),
                                                &sf, NULL  /*theContext*/, NULL, mSymFunctionTableAccess, mSymGetModuleBase, 0))
                {
                        DWORD lastErr = GetLastError();
                        sprintf(aBuffer, "StackWalk failed (error %d)\r\n", lastErr);
                        aDebugDump += aBuffer;
                        break;
                }

                if ((sf.AddrFrame.Offset == 0) || (sf.AddrPC.Offset == 0))
                        break;

                if (theSkipCount > 0)
                {
                        theSkipCount--;
                        continue;
                }

                BYTE symbolBuffer[sizeof(IMAGEHLP_SYMBOL) + 512];
                PIMAGEHLP_SYMBOL pSymbol = (PIMAGEHLP_SYMBOL)symbolBuffer;
                pSymbol->SizeOfStruct = sizeof(symbolBuffer);
                pSymbol->MaxNameLength = 512;
                       
                DWORD symDisplacement = 0;  // Displacement of the input address,
                                                                        // relative to the start of the symbol
                       
                if (mSymGetSymFromAddr(GetCurrentProcess(), sf.AddrPC.Offset, &symDisplacement, pSymbol))
                {
                        char aUDName[256];
                        mUnDecorateSymbolName(pSymbol->Name, aUDName, 256,
                                                                 UNDNAME_NO_ALLOCATION_MODEL | UNDNAME_NO_ALLOCATION_LANGUAGE |
                                                                 UNDNAME_NO_MS_THISTYPE | UNDNAME_NO_ACCESS_SPECIFIERS |
                                                                 UNDNAME_NO_THISTYPE | UNDNAME_NO_MEMBER_TYPE |
                                                                 UNDNAME_NO_RETURN_UDT_MODEL | UNDNAME_NO_THROW_SIGNATURES |
                                                                 UNDNAME_NO_SPECIAL_SYMS);
                               
                        sprintf(aBuffer, "%08X %08X %hs+%X\r\n",
                                        sf.AddrFrame.Offset, sf.AddrPC.Offset, aUDName, symDisplacement);
                }
                else // No symbol found.  Print out the logical address instead.
                {
                        char szModule[MAX_PATH];            
                        DWORD section = 0, offset = 0;

                        GetLogicalAddress((PVOID)sf.AddrPC.Offset, szModule, sizeof(szModule), section, offset);                               
                        sprintf(aBuffer, "%08X %08X %04X:%08X %s\r\n", sf.AddrFrame.Offset, sf.AddrPC.Offset, section, offset, GetFilename(szModule).c_str());
                }
                aDebugDump += aBuffer;
               
                sprintf(aBuffer, "Params: %08X %08X %08X %08X\r\n", sf.Params[0], sf.Params[1], sf.Params[2], sf.Params[3]);
                aDebugDump += aBuffer;         
                aDebugDump += "\r\n";

                aLevelCount++;
        }

        return aDebugDump;
}

bool SEHCatcher::GetLogicalAddress(void* addr, char* szModule, DWORD len, DWORD& section, DWORD& offset)
{
    MEMORY_BASIC_INFORMATION mbi;

    if (!VirtualQuery(addr, &mbi, sizeof(mbi)))
        return false;

    DWORD hMod = (DWORD)mbi.AllocationBase;

    if (!GetModuleFileNameA((HMODULE) hMod, szModule, len))
        return false;

    // Point to the DOS header in memory
    PIMAGE_DOS_HEADER pDosHdr = (PIMAGE_DOS_HEADER) hMod;

    // From the DOS header, find the NT (PE) header
    PIMAGE_NT_HEADERS pNtHdr = (PIMAGE_NT_HEADERS) (hMod + pDosHdr->e_lfanew);

    PIMAGE_SECTION_HEADER pSection = IMAGE_FIRST_SECTION(pNtHdr);

    DWORD rva = (DWORD) addr - hMod; // RVA is offset from module load address

    // Iterate through the section table, looking for the one that encompasses
    // the linear address.
    for (unsigned i = 0; i < pNtHdr->FileHeader.NumberOfSections; i++, pSection++)
    {
        DWORD sectionStart = pSection->VirtualAddress;
        DWORD sectionEnd = sectionStart + max(pSection->SizeOfRawData, pSection->Misc.VirtualSize);

        // Is the address in this section???
        if ((rva >= sectionStart) && (rva <= sectionEnd))
        {
            // Yes, address is in the section.  Calculate section and offset,
            // and store in the "section" & "offset" params, which were
            // passed by reference.
            section = i+1;
            offset = rva - sectionStart;
            return true;
        }
    }

    return false; // Should never get here!
}

std::string SEHCatcher::GetFilename(const std::string& thePath)
{
        int aLastSlash = max((int) thePath.rfind('\\'), (int) thePath.rfind('/'));     

        if (aLastSlash >= 0)
        {
                return thePath.substr(aLastSlash+1);
        }
        else
                return thePath;
}

HWND gEditWindow = NULL;
int aCount = 0;

static LRESULT CALLBACK SEHProgressWindowProc(HWND hWnd, UINT uMsg, WPARAM wParam, LPARAM lParam)
{
        switch (uMsg)
        {              
        case WM_COMMAND:
                {
                        HWND hwndCtl = (HWND) lParam;
                        if (hwndCtl == SEHCatcher::mNoButtonWindow)
                        {
                                // Cancel it, man!
                                SEHCatcher::mSubmitReportTransfer.Abort();
                        }
                }
                break;
        case WM_CLOSE:
                SEHCatcher::mSubmitReportTransfer.Abort();
                return 0;
        case WM_TIMER:
                {
                        if (wParam == 0)
                        {
                                int aResult = SEHCatcher::mSubmitReportTransfer.GetResultCode();

                                if ((aResult != HTTPTransfer::RESULT_NOT_COMPLETED) &&
                                        (aResult != HTTPTransfer::RESULT_NOT_STARTED))
                                {
                                        if ((aResult != HTTPTransfer::RESULT_DONE) &&
                                                (aResult != HTTPTransfer::RESULT_ABORTED))
                                        {
                                                SEHCatcher::mSubmitReportTransfer.Reset();

                                                if (::MessageBoxW(hWnd, SEHCatcher::mSubmitErrorMessage.c_str(),
                                                        L"Connection Failed", MB_RETRYCANCEL | MB_APPLMODAL) == IDRETRY)
                                                {
                                                        _beginthread(SEHCatcher::SubmitReportThread, 0, 0);
                                                        return 0;
                                                }
                                        }

                                        SEHCatcher::mExiting = true;
                                }
                        }
                        else if (wParam == 1)
                        {
                                ++aCount;

                                std::string aNewString = "Please Wait";

                                for (int i = 0; i < (aCount % 10); i++)
                                        aNewString += " .";
                               
                                SetWindowTextA(gEditWindow, aNewString.c_str());
                        }
                }
                break;
        }

        return DefWindowProc(hWnd, uMsg, wParam, lParam);
}

static void CreateProgressWindow()
{
        WNDCLASSA wc;
        wc.style = 0;
        wc.cbClsExtra = 0;
        wc.cbWndExtra = 0;
        wc.hbrBackground = ::GetSysColorBrush(COLOR_BTNFACE);
        wc.hCursor = ::LoadCursor(NULL, IDC_ARROW);
        wc.hIcon = ::LoadIcon(NULL, IDI_ERROR);
        wc.hInstance = gHInstance;
        wc.lpfnWndProc = SEHProgressWindowProc;
        wc.lpszClassName = "SEHProgressWindow";
        wc.lpszMenuName = NULL;
        RegisterClassA(&wc);

        RECT aRect;
                aRect.left = 0;
                aRect.top = 0;
                aRect.right = 240;
                aRect.bottom = 64;

        DWORD aWindowStyle = WS_CLIPCHILDREN | WS_POPUP | WS_BORDER | WS_CAPTION | WS_SYSMENU | WS_MINIMIZEBOX;

        BOOL worked = AdjustWindowRect(&aRect, aWindowStyle, FALSE);

        HWND aHWnd = CreateWindowA("SEHProgressWindow", "Submitting Report",
                aWindowStyle,
                64, 64,
                aRect.right - aRect.left,
                aRect.bottom - aRect.top,
                NULL,
                NULL,
                gHInstance,
                0);

        // Check every 20ms to see if the transfer has completed
        SetTimer(aHWnd, 0, 20, NULL);

        // Every second we should change the edit text
        SetTimer(aHWnd, 1, 1000, NULL);

        gEditWindow = CreateWindowA("EDIT",
                "Please Wait",
                               
                WS_VISIBLE | WS_CHILD | ES_READONLY,
                24, 10,
                240-8-8,
                24,
                aHWnd,
                NULL,
                gHInstance,
                0);    
        if (!gUseDefaultFonts)
                SendMessage(gEditWindow, WM_SETFONT, (WPARAM) SEHCatcher::mBoldFont, 0);

        SEHCatcher::mNoButtonWindow = CreateWindowA("BUTTON", "Abort",
                WS_VISIBLE | WS_CHILD | BS_DEFPUSHBUTTON | BS_PUSHBUTTON,
                (240 - 96)/2, 64 - 22 - 6,
                96,
                22,
                aHWnd,
                NULL,
                gHInstance,
                0);
        if (!gUseDefaultFonts)
                SendMessage(SEHCatcher::mNoButtonWindow, WM_SETFONT, (WPARAM) SEHCatcher::mDialogFont, 0);

        ShowWindow(aHWnd, SW_NORMAL);
}

LRESULT CALLBACK SEHCatcher::SEHWindowProc(HWND hWnd, UINT uMsg, WPARAM wParam, LPARAM lParam)
{
        switch (uMsg)
        {
        case WM_COMMAND:
                {
                        HWND hwndCtl = (HWND) lParam;
                        if (hwndCtl == mYesButtonWindow)
                        {
                                // Hide current window and bring up score submitting stuff
                                ShowWindow(hWnd, SW_HIDE);

                                ShowSubmitInfoDialog();
                        }                      
                        else if (hwndCtl == mNoButtonWindow)
                        {
                                mExiting = true;
                        }
                        else if (hwndCtl == mDebugButtonWindow)
                        {
                                mDebugError = true;
                                mExiting = true;
                        }
                }
                break;
        case WM_CLOSE:
                mExiting = true;
                return 0;
        }

        return DefWindowProc(hWnd, uMsg, wParam, lParam);
}

LRESULT CALLBACK SEHCatcher::SubmitInfoWindowProc(HWND hWnd, UINT uMsg, WPARAM wParam, LPARAM lParam)
{
        switch (uMsg)
        {              
        case WM_COMMAND:
                {
                        HWND hwndCtl = (HWND) lParam;
                        if (hwndCtl == mYesButtonWindow)
                        {
                                // Hide current window and bring up score submitting stuff
                                ShowWindow(hWnd, SW_HIDE);

                                char aStr[8192];
                                GetWindowTextA(mEditWindow, aStr, 8192);
                                mUserText = aStr;

                                CreateProgressWindow();
                                _beginthread(SubmitReportThread, 0, 0);
                        }
                        else if (hwndCtl == mNoButtonWindow)
                        {
                                mExiting = true;
                        }
                }
                break;
        case WM_CLOSE:
                mExiting = true;
                return 0;
        }

        return DefWindowProc(hWnd, uMsg, wParam, lParam);
}

void SEHCatcher::WriteToFile(const std::string& theErrorText)
{
        std::fstream aStream("crash.txt", std::ios::out);
        aStream << theErrorText.c_str() << std::endl;
}

void SEHCatcher::SubmitReportThread(void *theArg)
{
        std::string aSeperator = "---------------------------7d3e1f30eec";
       
        DefinesMap aSEHWebParams;

        mApp->GetSEHWebParams(&aSEHWebParams);

        std::string aContent;

        DefinesMap::iterator anItr = aSEHWebParams.begin();
        while (anItr != aSEHWebParams.end())
        {
                aContent += "--" + aSeperator + "\r\n"
                "Content-Disposition: form-data; name=\"" + anItr->first + "\"\r\n" +          
                "\r\n" +
                anItr->second + "\r\n";
               
                ++anItr;
        }

        aContent +=
               
                /*"--" + aSeperator + "\r\n"
                "Content-Disposition: form-data; name=\"username\"\r\n" +              
                "\r\n" +
                mApp->mUserName + "\r\n" +      */


                "--" + aSeperator + "\r\n"
                "Content-Disposition: form-data; name=\"prod\"\r\n" +          
                "\r\n" +
                mApp->mProdName + "\r\n" +     

                "--" + aSeperator + "\r\n"
                "Content-Disposition: form-data; name=\"version\"\r\n" +               
                "\r\n" +
                mApp->mProductVersion + "\r\n" +

                /*"--" + aSeperator + "\r\n"
                "Content-Disposition: form-data; name=\"buildnum\"\r\n" +              
                "\r\n" +
                StrFormat("%d", mApp->mBuildNum) + "\r\n" +

                "--" + aSeperator + "\r\n"
                "Content-Disposition: form-data; name=\"builddate\"\r\n" +             
                "\r\n" +
                mApp->mBuildDate + "\r\n" +

                "--" + aSeperator + "\r\n"
                "Content-Disposition: form-data; name=\"referid\"\r\n" +               
                "\r\n" +
                mApp->mReferId + "\r\n" +*/


                "--" + aSeperator + "\r\n"
                "Content-Disposition: form-data; name=\"usertext\"\r\n" +
                "\r\n" +
                mUserText + "\r\n" +

                "--" + aSeperator + "\r\n"
                "Content-Disposition: form-data; name=\"errortitle\"\r\n" +            
                "\r\n" +
                mErrorTitle + "\r\n" +

                "--" + aSeperator + "\r\n"
                "Content-Disposition: form-data; name=\"errortext\"\r\n" +             
                "\r\n" +
                mErrorText + "\r\n";

        if (mHasDemoFile)
        {
                Buffer aBuffer;
                mApp->ReadBufferFromFile(mUploadFileName, &aBuffer, true);

                aContent +=
                        "--" + aSeperator + "\r\n"
                        "Content-Disposition: form-data; name=\"demofile\"; filename=\"popcap.dmo\"\r\n" +
                        "Content-Type: application/octet-stream\r\n" +
                        "\r\n";        

                aContent.insert(aContent.end(), (char*) aBuffer.GetDataPtr(), (char*) aBuffer.GetDataPtr() + aBuffer.GetDataLen());                    
                               
                aContent += "\r\n";
        }

        aContent +=
                "--" + aSeperator + "--\r\n";

        std::string aSendString =
                "POST /deluxe_error.php HTTP/1.1\r\n"
                "Content-Type: multipart/form-data; boundary=" + aSeperator + "\r\n"
                "User-Agent: Mozilla/4.0 (compatible; popcap)\r\n" +
                "Host: " + mSubmitHost + "\r\n" +
                "Content-Length: " + StrFormat("%d", aContent.length()) + "\r\n" +
                "Connection: close\r\n" +
                "\r\n" + aContent;
       
        mSubmitReportTransfer.SendRequestString(mSubmitHost, aSendString);
}

void SEHCatcher::ShowSubmitInfoDialog()
{
        WNDCLASSA wc;
        wc.style = 0;
        wc.cbClsExtra = 0;
        wc.cbWndExtra = 0;
        wc.hbrBackground = ::GetSysColorBrush(COLOR_BTNFACE);
        wc.hCursor = ::LoadCursor(NULL, IDC_ARROW);
        wc.hIcon = ::LoadIcon(NULL, IDI_ERROR);
        wc.hInstance = gHInstance;
        wc.lpfnWndProc = SubmitInfoWindowProc;
        wc.lpszClassName = "SubmitInfoWindow";
        wc.lpszMenuName = NULL;
        RegisterClassA(&wc);

        RECT aRect;
                aRect.left = 0;
                aRect.top = 0;
                aRect.right = 400;
                aRect.bottom = 300;

        DWORD aWindowStyle = WS_CLIPCHILDREN | WS_POPUP | WS_BORDER | WS_CAPTION | WS_SYSMENU | WS_MINIMIZEBOX;

        BOOL worked = AdjustWindowRect(&aRect, aWindowStyle, FALSE);

        HWND aHWnd = CreateWindowA("SubmitInfoWindow", "Error Details",
                aWindowStyle,
                64+16, 64+16,
                aRect.right - aRect.left,
                aRect.bottom - aRect.top,
                NULL,
                NULL,
                gHInstance,
                0);

        HWND aLabelWindow = CreateWindowW(L"EDIT", mSubmitMessage.c_str(),
                               
                WS_VISIBLE | WS_CHILD | ES_MULTILINE | ES_READONLY,
                8, 8,
                400-8-8,
                84,
                aHWnd,
                NULL,
                gHInstance,
                0);

        HDC aDC = ::GetDC(aLabelWindow);
        int aFontHeight = -MulDiv(9, 96, 72);
        ::ReleaseDC(aLabelWindow, aDC);
        HFONT aBoldArialFont = CreateFontA(aFontHeight, 0, 0, 0, FW_BOLD, 0, 0,
                        false, ANSI_CHARSET, OUT_DEFAULT_PRECIS, CLIP_DEFAULT_PRECIS, DEFAULT_QUALITY,
                        DEFAULT_PITCH | FF_DONTCARE, "Arial");

        if (!gUseDefaultFonts)
                SendMessage(aLabelWindow, WM_SETFONT, (WPARAM) aBoldArialFont, 0);

        mEditWindow = CreateWindowA("EDIT", "",
                WS_VISIBLE | WS_CHILD | ES_MULTILINE | WS_BORDER | WS_VSCROLL,
                8, 300-168-24-8-8,
                400-8-8,
                168,
                aHWnd,
                NULL,
                gHInstance,
                0);    

        aDC = ::GetDC(mEditWindow);
        aFontHeight = -MulDiv(8, 96, 72);
        ::ReleaseDC(mEditWindow, aDC);
        HFONT aCourierNewFont = CreateFontA(aFontHeight, 0, 0, 0, FW_NORMAL, 0, 0,
                        false, ANSI_CHARSET, OUT_DEFAULT_PRECIS, CLIP_DEFAULT_PRECIS, DEFAULT_QUALITY,
                        DEFAULT_PITCH | FF_DONTCARE, "Courier New");
        if (!gUseDefaultFonts)
                SendMessage(mEditWindow, WM_SETFONT, (WPARAM) mDialogFont, 0);
        SetFocus(mEditWindow);

        int aButtonWidth = (400 - 8 - 8 - 8) / 2;
        int aCurX = 8;

        aWindowStyle = WS_VISIBLE | WS_CHILD | BS_DEFPUSHBUTTON | BS_PUSHBUTTON;

        if (mApp == NULL)
                aWindowStyle |= WS_DISABLED;

        mYesButtonWindow = CreateWindowA("BUTTON", "Continue",
                aWindowStyle,
                aCurX, 300-24-8,
                aButtonWidth,
                24,
                aHWnd,
                NULL,
                gHInstance,
                0);
        if (!gUseDefaultFonts)
                SendMessage(mYesButtonWindow, WM_SETFONT, (WPARAM) aBoldArialFont, 0);

        aCurX += aButtonWidth+8;

        mNoButtonWindow = CreateWindowA("BUTTON", "Abort",
                WS_VISIBLE | WS_CHILD | BS_DEFPUSHBUTTON | BS_PUSHBUTTON,
                aCurX, 300-24-8,
                aButtonWidth,
                24,
                aHWnd,
                NULL,
                gHInstance,
                0);
        if (!gUseDefaultFonts)
                SendMessage(mNoButtonWindow, WM_SETFONT, (WPARAM) aBoldArialFont, 0);

        ShowWindow(aHWnd, SW_NORMAL);
}

void SEHCatcher::ShowErrorDialog(const std::string& theErrorTitle, const std::string& theErrorText)
{
        OSVERSIONINFO aVersionInfo;
        aVersionInfo.dwOSVersionInfoSize = sizeof(aVersionInfo);
        GetVersionEx(&aVersionInfo);

        // Setting fonts on 98 causes weirdo crash things in GDI upon the second crash.
        //  That's no good.    
        gUseDefaultFonts = aVersionInfo.dwPlatformId != VER_PLATFORM_WIN32_NT;

        int aHeight = -MulDiv(8, 96, 72);
        mDialogFont = ::CreateFontA(aHeight, 0, 0, 0, FW_NORMAL, FALSE, FALSE,
                        false, ANSI_CHARSET, OUT_DEFAULT_PRECIS, CLIP_DEFAULT_PRECIS, DEFAULT_QUALITY,
                        DEFAULT_PITCH | FF_DONTCARE, "Tahoma");

        aHeight = -MulDiv(10, 96, 72);
        mBoldFont = ::CreateFontA(aHeight, 0, 0, 0, FW_BOLD, FALSE, FALSE,
                        false, ANSI_CHARSET, OUT_DEFAULT_PRECIS, CLIP_DEFAULT_PRECIS, DEFAULT_QUALITY,
                        DEFAULT_PITCH | FF_DONTCARE, "Tahoma");

        ::SetCursor(::LoadCursor(NULL, IDC_ARROW));

        mErrorTitle = theErrorTitle;
        mErrorText = theErrorText;     


        WNDCLASSA wc;
        wc.style = 0;
        wc.cbClsExtra = 0;
        wc.cbWndExtra = 0;
        wc.hbrBackground = ::GetSysColorBrush(COLOR_BTNFACE);
        wc.hCursor = ::LoadCursor(NULL, IDC_ARROW);
        wc.hIcon = ::LoadIcon(NULL, IDI_ERROR);
        wc.hInstance = gHInstance;
        wc.lpfnWndProc = SEHWindowProc;
        wc.lpszClassName = "SEHWindow";
        wc.lpszMenuName = NULL;
        RegisterClassA(&wc);

        RECT aRect;
                aRect.left = 0;
                aRect.top = 0;
                aRect.right = 400;
                aRect.bottom = 300;

        DWORD aWindowStyle = WS_CLIPCHILDREN | WS_POPUP | WS_BORDER | WS_CAPTION | WS_SYSMENU | WS_MINIMIZEBOX;

        BOOL worked = AdjustWindowRect(&aRect, aWindowStyle, FALSE);

        HWND aHWnd = CreateWindowW(L"SEHWindow", L"Fatal Error!",
                aWindowStyle,
                64, 64,
                aRect.right - aRect.left,
                aRect.bottom - aRect.top,
                NULL,
                NULL,
                gHInstance,
                0);

        HWND aLabelWindow = CreateWindowW(L"EDIT",
                mCrashMessage.c_str(),
                WS_VISIBLE | WS_CHILD | ES_MULTILINE | ES_READONLY,
                8, 8,
                400-8-8,
                84,
                aHWnd,
                NULL,
                gHInstance,
                0);
       
        int aFontHeight = -MulDiv(9, 96, 72);  
        HFONT aBoldArialFont = CreateFontA(aFontHeight, 0, 0, 0, FW_BOLD, 0, 0,
                        false, ANSI_CHARSET, OUT_DEFAULT_PRECIS, CLIP_DEFAULT_PRECIS, DEFAULT_QUALITY,
                        DEFAULT_PITCH | FF_DONTCARE, "Arial");

        if (!gUseDefaultFonts)
                SendMessage(aLabelWindow, WM_SETFONT, (WPARAM) aBoldArialFont, 0);             

        HWND anEditWindow = CreateWindowA("EDIT", theErrorText.c_str(),
                WS_VISIBLE | WS_CHILD | ES_MULTILINE | WS_BORDER | WS_VSCROLL | ES_READONLY,
                8, 300-168-24-8-8,
                400-8-8,
                168,
                aHWnd,
                NULL,
                gHInstance,
                0);    
       
        aFontHeight = -MulDiv(8, 96, 72);      
        HFONT aCourierNewFont = CreateFontA(aFontHeight, 0, 0, 0, FW_NORMAL, 0, 0,
                        false, ANSI_CHARSET, OUT_DEFAULT_PRECIS, CLIP_DEFAULT_PRECIS, DEFAULT_QUALITY,
                        DEFAULT_PITCH | FF_DONTCARE, "Courier New");
        if (!gUseDefaultFonts)
                SendMessage(anEditWindow, WM_SETFONT, (WPARAM) aCourierNewFont, 0);

        aWindowStyle = WS_VISIBLE | WS_CHILD | BS_DEFPUSHBUTTON | BS_PUSHBUTTON;

        if (mApp == NULL)
                aWindowStyle |= WS_DISABLED;

#ifdef _DEBUG
        bool doDebugButton = true;
#else
        bool doDebugButton = false;
#endif

        bool canSubmit = mAllowSubmit && !mSubmitHost.empty(); 
        int aNumButtons = 1 + (doDebugButton ? 1 : 0) + (canSubmit ? 1 : 0);
       
        int aButtonWidth = (400 - 8 - 8 - (aNumButtons - 1)*8) / aNumButtons;          
               
        int aCurX = 8;

        if (canSubmit)
        {
                mYesButtonWindow = CreateWindowA("BUTTON", "Send Report",
                        aWindowStyle,
                        aCurX, 300-24-8,
                        aButtonWidth,
                        24,
                        aHWnd,
                        NULL,
                        gHInstance,
                        0);
                if (!gUseDefaultFonts)
                        SendMessage(mYesButtonWindow, WM_SETFONT, (WPARAM) aBoldArialFont, 0);

                aCurX += aButtonWidth + 8;     
        }

        if (doDebugButton)
        {
                mDebugButtonWindow = CreateWindowA("BUTTON", "Debug",
                        aWindowStyle,
                        aCurX, 300-24-8,
                        aButtonWidth,
                        24,
                        aHWnd,
                        NULL,
                        gHInstance,
                        0);            
                if (!gUseDefaultFonts)
                        SendMessage(mDebugButtonWindow, WM_SETFONT, (WPARAM) aBoldArialFont, 0);               

                aCurX += aButtonWidth + 8;
        }

        mNoButtonWindow = CreateWindowA("BUTTON", "Close Now",
                WS_VISIBLE | WS_CHILD | BS_DEFPUSHBUTTON | BS_PUSHBUTTON,
                aCurX, 300-24-8,
                aButtonWidth,
                24,
                aHWnd,
                NULL,
                gHInstance,
                0);

        if (!gUseDefaultFonts)
                SendMessage(mNoButtonWindow, WM_SETFONT, (WPARAM) aBoldArialFont, 0);

        ShowWindow(aHWnd, SW_NORMAL);

        MSG msg;
        while ((GetMessage(&msg, NULL, 0, 0) > 0) && (!mExiting))
        {
                TranslateMessage(&msg);
                DispatchMessage(&msg);
        }      

        DestroyWindow(aHWnd);

        DeleteObject(mDialogFont);
        DeleteObject(mBoldFont);
        DeleteObject(aBoldArialFont);
        DeleteObject(aCourierNewFont);
}

std::string SEHCatcher::GetSysInfo()
{
        std::string aDebugDump;

        OSVERSIONINFOA aVersionInfo;
        aVersionInfo.dwOSVersionInfoSize = sizeof(OSVERSIONINFO);
        GetVersionExA(&aVersionInfo);

        aDebugDump += "Windows Ver: ";
        if (aVersionInfo.dwPlatformId == VER_PLATFORM_WIN32_NT)
                aDebugDump += "NT ";
        else
                aDebugDump += "9x ";

        char aVersionStr[20];
        sprintf(aVersionStr, "%d.%d", aVersionInfo.dwMajorVersion, aVersionInfo.dwMinorVersion);
        aDebugDump += aVersionStr;
        aDebugDump += " ";
        aDebugDump += aVersionInfo.szCSDVersion;
        aDebugDump += " ";
        sprintf(aVersionStr, "%d", aVersionInfo.dwBuildNumber);
        aDebugDump += "Build ";
        aDebugDump += aVersionStr;
        aDebugDump += "\r\n";

        HMODULE aMod;
        char aPath[256];

        if (mApp != NULL)
        {
                aMod = LoadLibraryA("ddraw.dll");
                if (aMod != NULL)
                {
                        GetModuleFileNameA(aMod, aPath, 256);                  
                        aDebugDump += "DDraw Ver: " + mApp->GetProductVersion(aPath) + "\r\n";
                        FreeLibrary(aMod);
                }

                aMod = LoadLibraryA("dsound.dll");
                if (aMod != NULL)
                {
                        GetModuleFileNameA(aMod, aPath, 256);                  
                        aDebugDump += "DSound Ver: " + mApp->GetProductVersion(aPath) + "\r\n";
                        FreeLibrary(aMod);
                }
        }      

        return aDebugDump;
}