static void SetupCrashHandler() { ScopedMem<WCHAR> symDir; ScopedMem<WCHAR> tmpDir(path::GetTempPath()); if (tmpDir) symDir.Set(path::Join(tmpDir, L"SumatraPDF-symbols")); else symDir.Set(AppGenDataFilename(L"SumatraPDF-symbols")); ScopedMem<WCHAR> crashDumpPath(AppGenDataFilename(CRASH_DUMP_FILE_NAME)); InstallCrashHandler(crashDumpPath, symDir); }
void SaveCallstackLogs() { char *s = dbghelp::GetCallstacks(); if (!s) return; ScopedMem<WCHAR> filePath(AppGenDataFilename(L"callstacks.txt")); file::WriteAll(filePath.Get(), s, str::Len(s)); free(s); }
// TODO: create in TEMP directory instead? static WCHAR *GetThumbnailPath(const WCHAR *filePath) { // create a fingerprint of a (normalized) path for the file name // I'd have liked to also include the file's last modification time // in the fingerprint (much quicker than hashing the entire file's // content), but that's too expensive for files on slow drives unsigned char digest[16]; ScopedMem<char> pathU(str::conv::ToUtf8(filePath)); if (path::HasVariableDriveLetter(filePath)) pathU[0] = '?'; // ignore the drive letter, if it might change CalcMD5Digest((unsigned char *)pathU.Get(), str::Len(pathU), digest); ScopedMem<char> fingerPrint(str::MemToHex(digest, 16)); ScopedMem<WCHAR> thumbsPath(AppGenDataFilename(THUMBNAILS_DIR_NAME)); if (!thumbsPath) return NULL; ScopedMem<WCHAR> fname(str::conv::FromAnsi(fingerPrint)); return str::Format(L"%s\\%s.png", thumbsPath, fname); }
// removes thumbnails that don't belong to any frequently used item in file history void CleanUpThumbnailCache(FileHistory& fileHistory) { ScopedMem<WCHAR> thumbsPath(AppGenDataFilename(THUMBNAILS_DIR_NAME)); if (!thumbsPath) return; ScopedMem<WCHAR> pattern(path::Join(thumbsPath, L"*.png")); WStrVec files; WIN32_FIND_DATA fdata; HANDLE hfind = FindFirstFile(pattern, &fdata); if (INVALID_HANDLE_VALUE == hfind) return; do { if (!(fdata.dwFileAttributes & FILE_ATTRIBUTE_DIRECTORY)) files.Append(str::Dup(fdata.cFileName)); } while (FindNextFile(hfind, &fdata)); FindClose(hfind); Vec<DisplayState *> list; fileHistory.GetFrequencyOrder(list); for (size_t i = 0; i < list.Count() && i < FILE_HISTORY_MAX_FREQUENT * 2; i++) { ScopedMem<WCHAR> bmpPath(GetThumbnailPath(list.At(i)->filePath)); if (!bmpPath) continue; int idx = files.Find(path::GetBaseName(bmpPath)); if (idx != -1) { CrashIf(idx < 0 || files.Count() <= (size_t)idx); WCHAR *fileName = files.At(idx); files.RemoveAt(idx); free(fileName); } } for (size_t i = 0; i < files.Count(); i++) { ScopedMem<WCHAR> bmpPath(path::Join(thumbsPath, files.At(i))); file::Delete(bmpPath); } }
// TODO: create in TEMP directory instead? static WCHAR* GetThumbnailPath(const WCHAR* filePath) { // create a fingerprint of a (normalized) path for the file name // I'd have liked to also include the file's last modification time // in the fingerprint (much quicker than hashing the entire file's // content), but that's too expensive for files on slow drives unsigned char digest[16]; // TODO: why is this happening? Seen in crash reports e.g. 35043 if (!filePath) return nullptr; OwnedData pathU(str::conv::ToUtf8(filePath)); if (!pathU.Get()) return nullptr; if (path::HasVariableDriveLetter(filePath)) pathU.Get()[0] = '?'; // ignore the drive letter, if it might change CalcMD5Digest((unsigned char*)pathU.Get(), str::Len(pathU.Get()), digest); AutoFree fingerPrint(_MemToHex(&digest)); AutoFreeW thumbsPath(AppGenDataFilename(THUMBNAILS_DIR_NAME)); if (!thumbsPath) return nullptr; AutoFreeW fname(str::conv::FromAnsi(fingerPrint)); return str::Format(L"%s\\%s.png", thumbsPath.Get(), fname.Get()); }
int APIENTRY WinMain(HINSTANCE hInstance, HINSTANCE hPrevInstance, LPSTR lpCmdLine, int nCmdShow) { int retCode = 1; // by default it's error #ifdef DEBUG // Memory leak detection (only enable _CRTDBG_LEAK_CHECK_DF for // regular termination so that leaks aren't checked on exceptions, // aborts, etc. where some clean-up might not take place) _CrtSetDbgFlag(_CRTDBG_ALLOC_MEM_DF); //_CrtSetBreakAlloc(421); TryLoadMemTrace(); #endif //BenchMD5(); DisableDataExecution(); // ensure that C functions behave consistently under all OS locales // (use Win32 functions where localized input or output is desired) setlocale(LC_ALL, "C"); RunUnitTests(); // don't show system-provided dialog boxes when accessing files on drives // that are not mounted (e.g. a: drive without floppy or cd rom drive // without a cd). SetErrorMode(SEM_NOOPENFILEERRORBOX | SEM_FAILCRITICALERRORS); srand((unsigned int)time(NULL)); // don't bother sending crash reports when running under Wine // as they're not helpful if (!RunningUnderWine()) { ScopedMem<TCHAR> symDir; ScopedMem<TCHAR> tmpDir(path::GetTempPath()); if (tmpDir) symDir.Set(path::Join(tmpDir, _T("SumatraPDF-symbols"))); else symDir.Set(AppGenDataFilename(_T("SumatraPDF-symbols"))); ScopedMem<TCHAR> crashDumpPath(AppGenDataFilename(CRASH_DUMP_FILE_NAME)); InstallCrashHandler(crashDumpPath, symDir); } ScopedOle ole; InitAllCommonControls(); ScopedGdiPlus gdiPlus(true); mui::Initialize(); uimsg::Initialize(); ScopedMem<TCHAR> prefsFilename(GetPrefsFileName()); if (!file::Exists(prefsFilename)) { // guess the ui language on first start CurrLangNameSet(Trans::GuessLanguage()); gFavorites = new Favorites(); } else { assert(gFavorites == NULL); Prefs::Load(prefsFilename, gGlobalPrefs, gFileHistory, &gFavorites); CurrLangNameSet(gGlobalPrefs.currentLanguage); } prefsFilename.Set(NULL); CommandLineInfo i; GetCommandLineInfo(i); if (i.showConsole) RedirectIOToConsole(); if (i.makeDefault) AssociateExeWithPdfExtension(); if (i.pathsToBenchmark.Count() > 0) { BenchFileOrDir(i.pathsToBenchmark); if (i.showConsole) system("pause"); } if (i.exitImmediately) goto Exit; gCrashOnOpen = i.crashOnOpen; gGlobalPrefs.bgColor = i.bgColor; gGlobalPrefs.fwdSearch.offset = i.fwdSearch.offset; gGlobalPrefs.fwdSearch.width = i.fwdSearch.width; gGlobalPrefs.fwdSearch.color = i.fwdSearch.color; gGlobalPrefs.fwdSearch.permanent = i.fwdSearch.permanent; gGlobalPrefs.escToExit = i.escToExit; gGlobalPrefs.cbxR2L = i.cbxR2L; gPolicyRestrictions = GetPolicies(i.restrictedUse); gRenderCache.colorRange[0] = i.colorRange[0]; gRenderCache.colorRange[1] = i.colorRange[1]; DebugGdiPlusDevice(gUseGdiRenderer); DebugAlternateChmEngine(!gUseEbookUI); if (i.inverseSearchCmdLine) { str::ReplacePtr(&gGlobalPrefs.inverseSearchCmdLine, i.inverseSearchCmdLine); gGlobalPrefs.enableTeXEnhancements = true; } CurrLangNameSet(i.lang); if (!RegisterWinClass(hInstance)) goto Exit; if (!InstanceInit(hInstance, nCmdShow)) goto Exit; if (i.hwndPluginParent) { if (!SetupPluginMode(i)) goto Exit; } if (i.printerName) { // note: this prints all PDF files. Another option would be to // print only the first one for (size_t n = 0; n < i.fileNames.Count(); n++) { bool ok = PrintFile(i.fileNames.At(n), i.printerName, !i.silent, i.printSettings); if (!ok) retCode++; } --retCode; // was 1 if no print failures, turn 1 into 0 goto Exit; } if (i.fileNames.Count() == 0 && gGlobalPrefs.rememberOpenedFiles && gGlobalPrefs.showStartPage) { // make the shell prepare the image list, so that it's ready when the first window's loaded SHFILEINFO sfi; SHGetFileInfo(_T(".pdf"), 0, &sfi, sizeof(sfi), SHGFI_SYSICONINDEX | SHGFI_SMALLICON | SHGFI_USEFILEATTRIBUTES); } WindowInfo *win = NULL; bool isFirstWin = true; for (size_t n = 0; n < i.fileNames.Count(); n++) { if (i.reuseInstance && !i.printDialog) { OpenUsingDde(i.fileNames.At(n), i, isFirstWin); } else { win = LoadOnStartup(i.fileNames.At(n), i, isFirstWin); if (!win) { retCode++; continue; } if (i.printDialog) OnMenuPrint(win, i.exitOnPrint); } isFirstWin = false; } if (i.fileNames.Count() > 0 && isFirstWin) { // failed to create any window, even though there // were files to load (or show a failure message for) goto Exit; } if (i.reuseInstance && !i.printDialog || i.printDialog && i.exitOnPrint) goto Exit; if (isFirstWin) { win = CreateAndShowWindowInfo(); if (!win) goto Exit; } UpdateUITextForLanguage(); // needed for RTL languages if (isFirstWin) UpdateToolbarAndScrollbarState(*win); // Make sure that we're still registered as default, // if the user has explicitly told us to be if (gGlobalPrefs.pdfAssociateShouldAssociate && win) RegisterForPdfExtentions(win->hwndFrame); if (gGlobalPrefs.enableAutoUpdate && gWindows.Count() > 0) AutoUpdateCheckAsync(gWindows.At(0)->hwndFrame, true); #ifndef THREAD_BASED_FILEWATCH const UINT_PTR timerID = SetTimer(NULL, -1, FILEWATCH_DELAY_IN_MS, NULL); #endif if (i.stressTestPath) { gIsStressTesting = true; StartStressTest(win, i.stressTestPath, i.stressTestFilter, i.stressTestRanges, i.stressTestCycles, &gRenderCache); } retCode = RunMessageLoop(); #ifndef THREAD_BASED_FILEWATCH KillTimer(NULL, timerID); #endif CleanUpThumbnailCache(gFileHistory); Exit: while (gWindows.Count() > 0) { DeleteWindowInfo(gWindows.At(0)); } while (gEbookWindows.Count() > 0) { DeleteEbookWindow(gEbookWindows.At(0), true); } #ifndef DEBUG // leave all the remaining clean-up to the OS // (as recommended for a quick exit) ExitProcess(retCode); #endif DeleteObject(gBrushNoDocBg); DeleteObject(gBrushLogoBg); DeleteObject(gBrushAboutBg); DeleteObject(gDefaultGuiFont); DeleteBitmap(gBitmapReloadingCue); delete gFavorites; mui::Destroy(); DrainUiMsgQueue(); uimsg::Destroy(); // it's still possible to crash after this (destructors of static classes, // atexit() code etc.) point, but it's very unlikely if (!RunningUnderWine()) UninstallCrashHandler(); #ifdef DEBUG // output leaks after all destructors of static objects have run _CrtSetDbgFlag(_CRTDBG_ALLOC_MEM_DF | _CRTDBG_LEAK_CHECK_DF); #endif return retCode; }
int APIENTRY WinMain(HINSTANCE hInstance, HINSTANCE hPrevInstance, LPSTR lpCmdLine, int nCmdShow) { int retCode = 1; // by default it's error #ifdef DEBUG // Memory leak detection (only enable _CRTDBG_LEAK_CHECK_DF for // regular termination so that leaks aren't checked on exceptions, // aborts, etc. where some clean-up might not take place) _CrtSetDbgFlag(_CRTDBG_ALLOC_MEM_DF); //_CrtSetBreakAlloc(421); TryLoadMemTrace(); #endif DisableDataExecution(); // ensure that C functions behave consistently under all OS locales // (use Win32 functions where localized input or output is desired) setlocale(LC_ALL, "C"); // don't show system-provided dialog boxes when accessing files on drives // that are not mounted (e.g. a: drive without floppy or cd rom drive // without a cd). SetErrorMode(SEM_NOOPENFILEERRORBOX | SEM_FAILCRITICALERRORS); #if defined(DEBUG) || defined(SVN_PRE_RELEASE_VER) if (str::StartsWith(lpCmdLine, "/tester")) { extern int TesterMain(); // in Tester.cpp return TesterMain(); } if (str::StartsWith(lpCmdLine, "/regress")) { extern int RegressMain(); // in Regress.cpp return RegressMain(); } #endif #ifdef SUPPORTS_AUTO_UPDATE if (str::StartsWith(lpCmdLine, "-autoupdate")) { bool quit = AutoUpdateMain(); if (quit) return 0; } #endif RunUnitTests(); srand((unsigned int)time(NULL)); { ScopedMem<WCHAR> symDir; ScopedMem<WCHAR> tmpDir(path::GetTempPath()); if (tmpDir) symDir.Set(path::Join(tmpDir, L"SumatraPDF-symbols")); else symDir.Set(AppGenDataFilename(L"SumatraPDF-symbols")); ScopedMem<WCHAR> crashDumpPath(AppGenDataFilename(CRASH_DUMP_FILE_NAME)); InstallCrashHandler(crashDumpPath, symDir); } ScopedOle ole; InitAllCommonControls(); ScopedGdiPlus gdiPlus(true); mui::Initialize(); uitask::Initialize(); LoadPrefs(); CommandLineInfo i; GetCommandLineInfo(i); SetCurrentLang(i.lang ? i.lang : gGlobalPrefs->currLangCode); if (i.showConsole) RedirectIOToConsole(); if (i.makeDefault) AssociateExeWithPdfExtension(); if (i.pathsToBenchmark.Count() > 0) { BenchFileOrDir(i.pathsToBenchmark); if (i.showConsole) system("pause"); } if (i.exitImmediately) goto Exit; gCrashOnOpen = i.crashOnOpen; gUserPrefs->mainWindowBackground = i.bgColor; if (gUserPrefs->forwardSearch.highlightColor != i.forwardSearch.highlightColor || gUserPrefs->forwardSearch.highlightOffset != i.forwardSearch.highlightOffset || gUserPrefs->forwardSearch.highlightPermanent != i.forwardSearch.highlightPermanent || gUserPrefs->forwardSearch.highlightWidth != i.forwardSearch.highlightWidth) { gGlobalPrefs->enableTeXEnhancements = true; } gUserPrefs->forwardSearch = i.forwardSearch; gUserPrefs->escToExit = i.escToExit; gGlobalPrefs->cbxR2L = i.cbxR2L; gPolicyRestrictions = GetPolicies(i.restrictedUse); gRenderCache.colorRange[0] = i.colorRange[0]; gRenderCache.colorRange[1] = i.colorRange[1]; DebugGdiPlusDevice(gUseGdiRenderer); DebugAlternateChmEngine(gUserPrefs->ebookUI.traditionalEbookUI); if (i.inverseSearchCmdLine) { str::ReplacePtr(&gGlobalPrefs->inverseSearchCmdLine, i.inverseSearchCmdLine); gGlobalPrefs->enableTeXEnhancements = true; } if (!RegisterWinClass(hInstance)) goto Exit; if (!InstanceInit(hInstance, nCmdShow)) goto Exit; if (i.hwndPluginParent) { if (!SetupPluginMode(i)) goto Exit; } if (i.printerName) { // note: this prints all PDF files. Another option would be to // print only the first one for (size_t n = 0; n < i.fileNames.Count(); n++) { bool ok = PrintFile(i.fileNames.At(n), i.printerName, !i.silent, i.printSettings); if (!ok) retCode++; } --retCode; // was 1 if no print failures, turn 1 into 0 goto Exit; } if (i.fileNames.Count() == 0 && gGlobalPrefs->rememberOpenedFiles && gGlobalPrefs->showStartPage) { // make the shell prepare the image list, so that it's ready when the first window's loaded SHFILEINFO sfi; SHGetFileInfo(L".pdf", 0, &sfi, sizeof(sfi), SHGFI_SYSICONINDEX | SHGFI_SMALLICON | SHGFI_USEFILEATTRIBUTES); } if (!i.reuseInstance && gUserPrefs->reuseInstance && FindWindow(FRAME_CLASS_NAME, 0)) i.reuseInstance = true; WindowInfo *win = NULL; bool isFirstWin = true; for (size_t n = 0; n < i.fileNames.Count(); n++) { if (i.reuseInstance && !i.printDialog) { OpenUsingDde(i.fileNames.At(n), i, isFirstWin); } else { win = LoadOnStartup(i.fileNames.At(n), i, isFirstWin); if (!win) { retCode++; continue; } if (i.printDialog) OnMenuPrint(win, i.exitWhenDone); } isFirstWin = false; } if (i.fileNames.Count() > 0 && isFirstWin) { // failed to create any window, even though there // were files to load (or show a failure message for) goto Exit; } if (i.reuseInstance && !i.printDialog || i.printDialog && i.exitWhenDone) goto Exit; if (isFirstWin) { win = CreateAndShowWindowInfo(); if (!win) goto Exit; } UpdateUITextForLanguage(); // needed for RTL languages if (isFirstWin) UpdateToolbarAndScrollbarState(*win); // Make sure that we're still registered as default, // if the user has explicitly told us to be if (gGlobalPrefs->pdfAssociateShouldAssociate && win) RegisterForPdfExtentions(win->hwndFrame); if (gGlobalPrefs->enableAutoUpdate && gWindows.Count() > 0) AutoUpdateCheckAsync(gWindows.At(0)->hwndFrame, true); if (i.stressTestPath) StartStressTest(&i, win, &gRenderCache); if (gFileHistory.Get(0)) { gFileExistenceChecker = new FileExistenceChecker(); gFileExistenceChecker->Start(); } retCode = RunMessageLoop(); CleanUpThumbnailCache(gFileHistory); Exit: while (gWindows.Count() > 0) { DeleteWindowInfo(gWindows.At(0)); } while (gEbookWindows.Count() > 0) { DeleteEbookWindow(gEbookWindows.At(0), true); } #ifndef DEBUG // leave all the remaining clean-up to the OS // (as recommended for a quick exit) ExitProcess(retCode); #else CrashIf(gFileExistenceChecker); DeleteObject(gBrushNoDocBg); DeleteObject(gBrushLogoBg); DeleteObject(gBrushAboutBg); DeleteObject(gDefaultGuiFont); DeleteBitmap(gBitmapReloadingCue); gFileHistory.UpdateStatesSource(NULL); DeleteGlobalPrefs(gGlobalPrefs); mui::Destroy(); uitask::Destroy(); trans::Destroy(); // it's still possible to crash after this (destructors of static classes, // atexit() code etc.) point, but it's very unlikely UninstallCrashHandler(); // output leaks after all destructors of static objects have run _CrtSetDbgFlag(_CRTDBG_ALLOC_MEM_DF | _CRTDBG_LEAK_CHECK_DF); return retCode; #endif }
WCHAR *GetSettingsPath() { return AppGenDataFilename(PREFS_FILE_NAME); }