Example #1
0
BOOL   STDCALL OSGetLoadedModuleList(HANDLE hProcess, StringList &ModuleList)
{
    HMODULE hMods[1024];
    DWORD count;

    if (EnumProcessModulesEx(hProcess, hMods, sizeof(hMods), &count, LIST_MODULES_ALL))
    {
        for (UINT i=0; i<(count / sizeof(HMODULE)); i++)
        {
            TCHAR szFileName[MAX_PATH];

            if (GetModuleFileNameEx(hProcess, hMods[i], szFileName, _countof(szFileName)-1))
            {
                TCHAR *p;
                p = srchr(szFileName, '\\');
                if (p)
                {
                    *p = 0;
                    p++;
                }

                slwr (p);
                ModuleList << p;
            }
        }
    }
    else
        return 0;

    return 1;
}
Example #2
0
BOOL CALLBACK GraphicsCaptureFindWindow(HWND hwnd, LPARAM lParam)
{
    TCHAR windowClass[256];
    TCHAR windowExecutable[MAX_PATH];

    windowClass[_countof(windowClass)-1] = windowExecutable[MAX_PATH-1] = 0;

    FindWindowData *fwd = (FindWindowData *)lParam;

    if (!IsWindowVisible(hwnd))
        return TRUE;

    if (GetClassName(hwnd, windowClass, _countof(windowClass) - 1) && !scmp(windowClass, fwd->classname))
    {
        //handle old sources which lack an exe name
        if (fwd->exename.IsEmpty())
            return TRUE;

        DWORD processID;
        GetWindowThreadProcessId(hwnd, &processID);

        HANDLE hProc = fwd->pOpenProcess(PROCESS_QUERY_INFORMATION | PROCESS_VM_READ, FALSE, processID);
        if (hProc)
        {
            DWORD wLen = _countof(windowExecutable) - 1;
            if (QueryFullProcessImageName(hProc, 0, windowExecutable, &wLen))
            {
                TCHAR *p;
                p = wcsrchr(windowExecutable, '\\');
                if (p)
                    p++;
                else
                    p = windowExecutable;

                slwr(p);

                if (!scmp(p, fwd->exename))
                {
                    CloseHandle(hProc);
                    fwd->hwnd = hwnd;
                    return FALSE;
                }
            }
            else
            {
                RUNONCE Log(TEXT("OpenProcess worked but QueryFullProcessImageName returned %d for pid %d?"), GetLastError(), processID);
            }

            CloseHandle(hProc);
        }
    }

    return TRUE;
}
Example #3
0
LONG CALLBACK OBSExceptionHandler (PEXCEPTION_POINTERS exceptionInfo)
{
    HANDLE  hProcess;

    HMODULE hDbgHelp;

    MINIDUMP_EXCEPTION_INFORMATION miniInfo;

    STACKFRAME64        frame = {0};
    CONTEXT             context = *exceptionInfo->ContextRecord;
    SYMBOL_INFO         *symInfo;
    DWORD64             fnOffset;
    TCHAR               logPath[MAX_PATH];

    OSVERSIONINFOEX     osInfo;
    SYSTEMTIME          timeInfo;

    ENUMERATELOADEDMODULES64    fnEnumerateLoadedModules64;
    SYMSETOPTIONS               fnSymSetOptions;
    SYMINITIALIZE               fnSymInitialize;
    STACKWALK64                 fnStackWalk64;
    SYMFUNCTIONTABLEACCESS64    fnSymFunctionTableAccess64;
    SYMGETMODULEBASE64          fnSymGetModuleBase64;
    SYMFROMADDR                 fnSymFromAddr;
    SYMCLEANUP                  fnSymCleanup;
    MINIDUMPWRITEDUMP           fnMiniDumpWriteDump;
    SYMGETMODULEINFO64          fnSymGetModuleInfo64;

    DWORD                       i;
    DWORD64                     InstructionPtr;
    DWORD                       imageType;

    TCHAR                       searchPath[MAX_PATH], *p;

    static BOOL                 inExceptionHandler = FALSE;

    moduleinfo_t                moduleInfo;

    //always break into a debugger if one is present
    if (IsDebuggerPresent ())
        return EXCEPTION_CONTINUE_SEARCH;

    //exception codes < 0x80000000 are typically informative only and not crash worthy
    //0xe06d7363 indicates a c++ exception was thrown, let's just hope it was caught.
    //this is no longer needed since we're an unhandled handler vs a vectored handler
    
    /*if (exceptionInfo->ExceptionRecord->ExceptionCode < 0x80000000 || exceptionInfo->ExceptionRecord->ExceptionCode == 0xe06d7363 ||
        exceptionInfo->ExceptionRecord->ExceptionCode == 0x800706b5)
        return EXCEPTION_CONTINUE_SEARCH;*/

    //uh oh, we're crashing inside ourselves... this is really bad!
    if (inExceptionHandler)
        return EXCEPTION_CONTINUE_SEARCH;

    inExceptionHandler = TRUE;

    //load dbghelp dynamically
    hDbgHelp = LoadLibrary (TEXT("DBGHELP"));

    if (!hDbgHelp)
        return EXCEPTION_CONTINUE_SEARCH;

    fnEnumerateLoadedModules64 = (ENUMERATELOADEDMODULES64)GetProcAddress (hDbgHelp, "EnumerateLoadedModulesW64");
    fnSymSetOptions = (SYMSETOPTIONS)GetProcAddress (hDbgHelp, "SymSetOptions");
    fnSymInitialize = (SYMINITIALIZE)GetProcAddress (hDbgHelp, "SymInitialize");
    fnSymFunctionTableAccess64 = (SYMFUNCTIONTABLEACCESS64)GetProcAddress (hDbgHelp, "SymFunctionTableAccess64");
    fnSymGetModuleBase64 = (SYMGETMODULEBASE64)GetProcAddress (hDbgHelp, "SymGetModuleBase64");
    fnStackWalk64 = (STACKWALK64)GetProcAddress (hDbgHelp, "StackWalk64");
    fnSymFromAddr = (SYMFROMADDR)GetProcAddress (hDbgHelp, "SymFromAddrW");
    fnSymCleanup = (SYMCLEANUP)GetProcAddress (hDbgHelp, "SymCleanup");
    fnSymGetModuleInfo64 = (SYMGETMODULEINFO64)GetProcAddress (hDbgHelp, "SymGetModuleInfo64");
    fnMiniDumpWriteDump = (MINIDUMPWRITEDUMP)GetProcAddress (hDbgHelp, "MiniDumpWriteDump");

    if (!fnEnumerateLoadedModules64 || !fnSymSetOptions || !fnSymInitialize || !fnSymFunctionTableAccess64 ||
        !fnSymGetModuleBase64 || !fnStackWalk64 || !fnSymFromAddr || !fnSymCleanup || !fnSymGetModuleInfo64)
    {
        FreeLibrary (hDbgHelp);
        return EXCEPTION_CONTINUE_SEARCH;
    }

    hProcess = GetCurrentProcess();

    fnSymSetOptions (SYMOPT_UNDNAME | SYMOPT_FAIL_CRITICAL_ERRORS | SYMOPT_LOAD_ANYTHING);

    GetModuleFileName (NULL, searchPath, _countof(searchPath)-1);
    p = srchr (searchPath, '\\');
    if (p)
        *p = 0;

    //create a log file
    GetSystemTime (&timeInfo);
    for (i = 1;;)
    {
        tsprintf_s (logPath, _countof(logPath)-1, TEXT("%s\\crashDumps\\OBSCrashLog%.4d-%.2d-%.2d_%d.txt"), lpAppDataPath, timeInfo.wYear, timeInfo.wMonth, timeInfo.wDay, i);
        if (GetFileAttributes(logPath) == INVALID_FILE_ATTRIBUTES)
            break;
        i++;
    }

    XFile   crashDumpLog;

    if (!crashDumpLog.Open(logPath, XFILE_WRITE, XFILE_CREATENEW))
    {
        FreeLibrary (hDbgHelp);
        return EXCEPTION_CONTINUE_SEARCH;
    }

    //initialize debug symbols
    fnSymInitialize (hProcess, NULL, TRUE);

#ifdef _WIN64
    InstructionPtr = context.Rip;
    frame.AddrPC.Offset = InstructionPtr;
    frame.AddrFrame.Offset = context.Rbp;
    frame.AddrStack.Offset = context.Rsp;
    imageType = IMAGE_FILE_MACHINE_AMD64;
#else
    InstructionPtr = context.Eip;
    frame.AddrPC.Offset = InstructionPtr;
    frame.AddrFrame.Offset = context.Ebp;
    frame.AddrStack.Offset = context.Esp;
    imageType = IMAGE_FILE_MACHINE_I386;
#endif

    frame.AddrFrame.Mode = AddrModeFlat;
    frame.AddrPC.Mode = AddrModeFlat;
    frame.AddrStack.Mode = AddrModeFlat;

    symInfo = (SYMBOL_INFO *)LocalAlloc (LPTR, sizeof(*symInfo) + 256);
    symInfo->SizeOfStruct = sizeof(SYMBOL_INFO);
    symInfo->MaxNameLen = 256;
    fnOffset = 0;

    //get os info
    memset (&osInfo, 0, sizeof(osInfo));
    osInfo.dwOSVersionInfoSize = sizeof(OSVERSIONINFOEX);

    if (!GetVersionEx ((OSVERSIONINFO *)&osInfo))
    {
        osInfo.dwOSVersionInfoSize = sizeof(OSVERSIONINFO);
        GetVersionEx ((OSVERSIONINFO *)&osInfo);
    }

    String cpuInfo;
    HKEY key;

    // get cpu info
    if(RegOpenKey(HKEY_LOCAL_MACHINE, TEXT("HARDWARE\\DESCRIPTION\\System\\CentralProcessor\\0"), &key) == ERROR_SUCCESS)
    {
        DWORD dwSize = 1024;
        cpuInfo.SetLength(dwSize);
        if (RegQueryValueEx(key, TEXT("ProcessorNameString"), NULL, NULL, (LPBYTE)cpuInfo.Array(), &dwSize) != ERROR_SUCCESS)
            cpuInfo = TEXT("<unable to query>");
        RegCloseKey(key);
    }
    else
        cpuInfo = TEXT("<unable to query>");

    //determine which module the crash occured in
    scpy (moduleInfo.moduleName, TEXT("<unknown>"));
    moduleInfo.faultAddress = InstructionPtr;
    fnEnumerateLoadedModules64 (hProcess, (PENUMLOADED_MODULES_CALLBACK64)EnumerateLoadedModulesProcInfo, (VOID *)&moduleInfo);
    slwr (moduleInfo.moduleName);

    BOOL isPlugin = FALSE;

    if (sstr (moduleInfo.moduleName, TEXT("plugins\\")))
        isPlugin = TRUE;

    String strModuleInfo;
    String crashMessage;

    fnEnumerateLoadedModules64(hProcess, (PENUMLOADED_MODULES_CALLBACK64)RecordAllLoadedModules, (VOID *)&strModuleInfo);

    crashMessage << 
        TEXT("OBS has encountered an unhandled exception and has terminated. If you are able to\r\n")
        TEXT("reproduce this crash, please submit this crash report on the forums at\r\n")
        TEXT("http://www.obsproject.com/ - include the contents of this crash log and the\r\n")
        TEXT("minidump .dmp file (if available) as well as your regular OBS log files and\r\n")
        TEXT("a description of what you were doing at the time of the crash.\r\n")
        TEXT("\r\n")
        TEXT("This crash appears to have occured in the '") << moduleInfo.moduleName << TEXT("' module.\r\n\r\n");

    crashDumpLog.WriteStr(crashMessage.Array());

    crashDumpLog.WriteStr(FormattedString(TEXT("**** UNHANDLED EXCEPTION: %x\r\nFault address: %I64p (%s)\r\n"), exceptionInfo->ExceptionRecord->ExceptionCode, InstructionPtr, moduleInfo.moduleName));

    crashDumpLog.WriteStr(TEXT("OBS version: ") OBS_VERSION_STRING TEXT("\r\n"));
    crashDumpLog.WriteStr(FormattedString(TEXT("Windows version: %d.%d (Build %d) %s\r\nCPU: %s\r\n\r\n"), osInfo.dwMajorVersion, osInfo.dwMinorVersion, osInfo.dwBuildNumber, osInfo.szCSDVersion, cpuInfo.Array()));

    crashDumpLog.WriteStr(TEXT("Crashing thread stack trace:\r\n"));
#ifdef _WIN64
    crashDumpLog.WriteStr(TEXT("Stack            EIP              Arg0             Arg1             Arg2             Arg3             Address\r\n"));
#else
    crashDumpLog.WriteStr(TEXT("Stack    EIP      Arg0     Arg1     Arg2     Arg3     Address\r\n"));
#endif
    crashDumpLog.FlushFileBuffers();

    while (fnStackWalk64 (imageType, hProcess, GetCurrentThread(), &frame, &context, NULL, (PFUNCTION_TABLE_ACCESS_ROUTINE64)fnSymFunctionTableAccess64, (PGET_MODULE_BASE_ROUTINE64)fnSymGetModuleBase64, NULL))
    {
        scpy (moduleInfo.moduleName, TEXT("<unknown>"));
        moduleInfo.faultAddress = frame.AddrPC.Offset;
        fnEnumerateLoadedModules64 (hProcess, (PENUMLOADED_MODULES_CALLBACK64)EnumerateLoadedModulesProcInfo, (VOID *)&moduleInfo);
        slwr (moduleInfo.moduleName);

        p = srchr (moduleInfo.moduleName, '\\');
        if (p)
            p++;
        else
            p = moduleInfo.moduleName;

#ifdef _WIN64
        if (fnSymFromAddr (hProcess, frame.AddrPC.Offset, &fnOffset, symInfo) && !(symInfo->Flags & SYMFLAG_EXPORT))
        {
            crashDumpLog.WriteStr(FormattedString(TEXT("%016I64X %016I64X %016I64X %016I64X %016I64X %016I64X %s!%s+0x%I64x\r\n"),
                frame.AddrStack.Offset,
                frame.AddrPC.Offset,
                frame.Params[0],
                frame.Params[1],
                frame.Params[2],
                frame.Params[3],
                p,
                symInfo->Name,
                fnOffset));
        }
        else
        {
            crashDumpLog.WriteStr(FormattedString(TEXT("%016I64X %016I64X %016I64X %016I64X %016I64X %016I64X %s!0x%I64x\r\n"),
                frame.AddrStack.Offset,
                frame.AddrPC.Offset,
                frame.Params[0],
                frame.Params[1],
                frame.Params[2],
                frame.Params[3],
                p,
                frame.AddrPC.Offset));
        }
#else
        if (fnSymFromAddr (hProcess, frame.AddrPC.Offset, &fnOffset, symInfo) && !(symInfo->Flags & SYMFLAG_EXPORT))
        {
            crashDumpLog.WriteStr(FormattedString(TEXT("%08.8I64X %08.8I64X %08.8X %08.8X %08.8X %08.8X %s!%s+0x%I64x\r\n"),
                frame.AddrStack.Offset,
                frame.AddrPC.Offset,
                (DWORD)frame.Params[0],
                (DWORD)frame.Params[1],
                (DWORD)frame.Params[2],
                (DWORD)frame.Params[3],
                p,
                symInfo->Name,
                fnOffset));
        }
        else
        {
            crashDumpLog.WriteStr(FormattedString(TEXT("%08.8I64X %08.8I64X %08.8X %08.8X %08.8X %08.8X %s!0x%I64x\r\n"),
                frame.AddrStack.Offset,
                frame.AddrPC.Offset,
                (DWORD)frame.Params[0],
                (DWORD)frame.Params[1],
                (DWORD)frame.Params[2],
                (DWORD)frame.Params[3],
                p,
                frame.AddrPC.Offset
                ));
        }
#endif

        crashDumpLog.FlushFileBuffers();
    }

    //if we manually crashed due to a deadlocked thread, record some extra info
    if (exceptionInfo->ExceptionRecord->ExceptionCode == EXCEPTION_BREAKPOINT)
    {
        HANDLE hVideoThread = NULL, hEncodeThread = NULL;
        App->GetThreadHandles (&hVideoThread, &hEncodeThread);

        if (hVideoThread)
        {
            crashDumpLog.WriteStr(TEXT("\r\nVideo thread stack trace:\r\n"));
#ifdef _WIN64
            crashDumpLog.WriteStr(TEXT("Stack            EIP              Arg0             Arg1             Arg2             Arg3             Address\r\n"));
#else
            crashDumpLog.WriteStr(TEXT("Stack    EIP      Arg0     Arg1     Arg2     Arg3     Address\r\n"));
#endif
            crashDumpLog.FlushFileBuffers();

            context.ContextFlags = CONTEXT_ALL;
            GetThreadContext (hVideoThread, &context);
            ZeroMemory (&frame, sizeof(frame));
#ifdef _WIN64
            InstructionPtr = context.Rip;
            frame.AddrPC.Offset = InstructionPtr;
            frame.AddrFrame.Offset = context.Rbp;
            frame.AddrStack.Offset = context.Rsp;
            imageType = IMAGE_FILE_MACHINE_AMD64;
#else
            InstructionPtr = context.Eip;
            frame.AddrPC.Offset = InstructionPtr;
            frame.AddrFrame.Offset = context.Ebp;
            frame.AddrStack.Offset = context.Esp;
            imageType = IMAGE_FILE_MACHINE_I386;
#endif

            frame.AddrFrame.Mode = AddrModeFlat;
            frame.AddrPC.Mode = AddrModeFlat;
            frame.AddrStack.Mode = AddrModeFlat;
            while (fnStackWalk64 (imageType, hProcess, hVideoThread, &frame, &context, NULL, (PFUNCTION_TABLE_ACCESS_ROUTINE64)fnSymFunctionTableAccess64, (PGET_MODULE_BASE_ROUTINE64)fnSymGetModuleBase64, NULL))
            {
                scpy (moduleInfo.moduleName, TEXT("<unknown>"));
                moduleInfo.faultAddress = frame.AddrPC.Offset;
                fnEnumerateLoadedModules64 (hProcess, (PENUMLOADED_MODULES_CALLBACK64)EnumerateLoadedModulesProcInfo, (VOID *)&moduleInfo);
                slwr (moduleInfo.moduleName);

                p = srchr (moduleInfo.moduleName, '\\');
                if (p)
                    p++;
                else
                    p = moduleInfo.moduleName;

#ifdef _WIN64
                if (fnSymFromAddr (hProcess, frame.AddrPC.Offset, &fnOffset, symInfo) && !(symInfo->Flags & SYMFLAG_EXPORT))
                {
                    crashDumpLog.WriteStr(FormattedString(TEXT("%016I64X %016I64X %016I64X %016I64X %016I64X %016I64X %s!%s+0x%I64x\r\n"),
                        frame.AddrStack.Offset,
                        frame.AddrPC.Offset,
                        frame.Params[0],
                        frame.Params[1],
                        frame.Params[2],
                        frame.Params[3],
                        p,
                        symInfo->Name,
                        fnOffset));
                }
                else
                {
                    crashDumpLog.WriteStr(FormattedString(TEXT("%016I64X %016I64X %016I64X %016I64X %016I64X %016I64X %s!0x%I64x\r\n"),
                        frame.AddrStack.Offset,
                        frame.AddrPC.Offset,
                        frame.Params[0],
                        frame.Params[1],
                        frame.Params[2],
                        frame.Params[3],
                        p,
                        frame.AddrPC.Offset));
                }
#else
                if (fnSymFromAddr (hProcess, frame.AddrPC.Offset, &fnOffset, symInfo) && !(symInfo->Flags & SYMFLAG_EXPORT))
                {
                    crashDumpLog.WriteStr(FormattedString(TEXT("%08.8I64X %08.8I64X %08.8X %08.8X %08.8X %08.8X %s!%s+0x%I64x\r\n"),
                        frame.AddrStack.Offset,
                        frame.AddrPC.Offset,
                        (DWORD)frame.Params[0],
                        (DWORD)frame.Params[1],
                        (DWORD)frame.Params[2],
                        (DWORD)frame.Params[3],
                        p,
                        symInfo->Name,
                        fnOffset));
                }
                else
                {
                    crashDumpLog.WriteStr(FormattedString(TEXT("%08.8I64X %08.8I64X %08.8X %08.8X %08.8X %08.8X %s!0x%I64x\r\n"),
                        frame.AddrStack.Offset,
                        frame.AddrPC.Offset,
                        (DWORD)frame.Params[0],
                        (DWORD)frame.Params[1],
                        (DWORD)frame.Params[2],
                        (DWORD)frame.Params[3],
                        p,
                        frame.AddrPC.Offset
                        ));
                }
#endif

                crashDumpLog.FlushFileBuffers();
            }
        }

        if (hEncodeThread)
        {
            crashDumpLog.WriteStr(TEXT("\r\nEncode thread stack trace:\r\n"));
#ifdef _WIN64
            crashDumpLog.WriteStr(TEXT("Stack            EIP              Arg0             Arg1             Arg2             Arg3             Address\r\n"));
#else
            crashDumpLog.WriteStr(TEXT("Stack    EIP      Arg0     Arg1     Arg2     Arg3     Address\r\n"));
#endif
            crashDumpLog.FlushFileBuffers();

            context.ContextFlags = CONTEXT_ALL;
            GetThreadContext (hEncodeThread, &context);
            ZeroMemory (&frame, sizeof(frame));
#ifdef _WIN64
            InstructionPtr = context.Rip;
            frame.AddrPC.Offset = InstructionPtr;
            frame.AddrFrame.Offset = context.Rbp;
            frame.AddrStack.Offset = context.Rsp;
            imageType = IMAGE_FILE_MACHINE_AMD64;
#else
            InstructionPtr = context.Eip;
            frame.AddrPC.Offset = InstructionPtr;
            frame.AddrFrame.Offset = context.Ebp;
            frame.AddrStack.Offset = context.Esp;
            imageType = IMAGE_FILE_MACHINE_I386;
#endif

            frame.AddrFrame.Mode = AddrModeFlat;
            frame.AddrPC.Mode = AddrModeFlat;
            frame.AddrStack.Mode = AddrModeFlat;
            while (fnStackWalk64 (imageType, hProcess, hEncodeThread, &frame, &context, NULL, (PFUNCTION_TABLE_ACCESS_ROUTINE64)fnSymFunctionTableAccess64, (PGET_MODULE_BASE_ROUTINE64)fnSymGetModuleBase64, NULL))
            {
                scpy (moduleInfo.moduleName, TEXT("<unknown>"));
                moduleInfo.faultAddress = frame.AddrPC.Offset;
                fnEnumerateLoadedModules64 (hProcess, (PENUMLOADED_MODULES_CALLBACK64)EnumerateLoadedModulesProcInfo, (VOID *)&moduleInfo);
                slwr (moduleInfo.moduleName);

                p = srchr (moduleInfo.moduleName, '\\');
                if (p)
                    p++;
                else
                    p = moduleInfo.moduleName;

#ifdef _WIN64
                if (fnSymFromAddr (hProcess, frame.AddrPC.Offset, &fnOffset, symInfo) && !(symInfo->Flags & SYMFLAG_EXPORT))
                {
                    crashDumpLog.WriteStr(FormattedString(TEXT("%016I64X %016I64X %016I64X %016I64X %016I64X %016I64X %s!%s+0x%I64x\r\n"),
                        frame.AddrStack.Offset,
                        frame.AddrPC.Offset,
                        frame.Params[0],
                        frame.Params[1],
                        frame.Params[2],
                        frame.Params[3],
                        p,
                        symInfo->Name,
                        fnOffset));
                }
                else
                {
                    crashDumpLog.WriteStr(FormattedString(TEXT("%016I64X %016I64X %016I64X %016I64X %016I64X %016I64X %s!0x%I64x\r\n"),
                        frame.AddrStack.Offset,
                        frame.AddrPC.Offset,
                        frame.Params[0],
                        frame.Params[1],
                        frame.Params[2],
                        frame.Params[3],
                        p,
                        frame.AddrPC.Offset));
                }
#else
                if (fnSymFromAddr (hProcess, frame.AddrPC.Offset, &fnOffset, symInfo) && !(symInfo->Flags & SYMFLAG_EXPORT))
                {
                    crashDumpLog.WriteStr(FormattedString(TEXT("%08.8I64X %08.8I64X %08.8X %08.8X %08.8X %08.8X %s!%s+0x%I64x\r\n"),
                        frame.AddrStack.Offset,
                        frame.AddrPC.Offset,
                        (DWORD)frame.Params[0],
                        (DWORD)frame.Params[1],
                        (DWORD)frame.Params[2],
                        (DWORD)frame.Params[3],
                        p,
                        symInfo->Name,
                        fnOffset));
                }
                else
                {
                    crashDumpLog.WriteStr(FormattedString(TEXT("%08.8I64X %08.8I64X %08.8X %08.8X %08.8X %08.8X %s!0x%I64x\r\n"),
                        frame.AddrStack.Offset,
                        frame.AddrPC.Offset,
                        (DWORD)frame.Params[0],
                        (DWORD)frame.Params[1],
                        (DWORD)frame.Params[2],
                        (DWORD)frame.Params[3],
                        p,
                        frame.AddrPC.Offset
                        ));
                }
#endif

                crashDumpLog.FlushFileBuffers();
            }
        }

    }

    //generate a minidump if possible
    if (fnMiniDumpWriteDump)
    {
        TCHAR     dumpPath[MAX_PATH];
        HANDLE    hFile;

        tsprintf_s (dumpPath, _countof(dumpPath)-1, TEXT("%s\\crashDumps\\OBSCrashDump%.4d-%.2d-%.2d_%d.dmp"), lpAppDataPath, timeInfo.wYear, timeInfo.wMonth, timeInfo.wDay, i);

        hFile = CreateFile (dumpPath, GENERIC_WRITE, 0, NULL, CREATE_ALWAYS, FILE_ATTRIBUTE_NORMAL, NULL);
        if (hFile != INVALID_HANDLE_VALUE)
        {
            MINIDUMP_TYPE dumpFlags = (MINIDUMP_TYPE)(MiniDumpWithIndirectlyReferencedMemory | MiniDumpWithUnloadedModules | MiniDumpWithProcessThreadData);

            miniInfo.ClientPointers = TRUE;
            miniInfo.ExceptionPointers = exceptionInfo;
            miniInfo.ThreadId = GetCurrentThreadId ();

            if (fnMiniDumpWriteDump (hProcess, GetCurrentProcessId(), hFile, dumpFlags, &miniInfo, NULL, NULL))
            {
                crashDumpLog.WriteStr(FormattedString(TEXT("\r\nA minidump was saved to %s.\r\nPlease include this file when posting a crash report.\r\n"), dumpPath));
            }
            else
            {
                CloseHandle (hFile);
                DeleteFile (dumpPath);
            }
        }
    }
    else
    {
        crashDumpLog.WriteStr(TEXT("\r\nA minidump could not be created. Please check dbghelp.dll is present.\r\n"));
    }

    crashDumpLog.WriteStr("\r\nList of loaded modules:\r\n");
#ifdef _WIN64
    crashDumpLog.WriteStr("Base Address                      Module\r\n");
#else
    crashDumpLog.WriteStr("Base Address      Module\r\n");
#endif
    crashDumpLog.WriteStr(strModuleInfo);

    crashDumpLog.Close();

    LocalFree (symInfo);

    fnSymCleanup (hProcess);

    if (OBSMessageBox(hwndMain, TEXT("Woops! OBS has crashed. Would you like to view a crash report?"), NULL, MB_ICONERROR | MB_YESNO) == IDYES)
        ShellExecute(NULL, NULL, logPath, NULL, searchPath, SW_SHOWDEFAULT);

    FreeLibrary (hDbgHelp);

    //we really shouldn't be returning here, if we're at the bottom of the VEH chain this is a pretty legitimate crash
    //and if we return we could end up invoking a second crash handler or other weird / annoying things
    //ExitProcess(exceptionInfo->ExceptionRecord->ExceptionCode);
    return EXCEPTION_CONTINUE_SEARCH;
}