static void CALLBACK ReadDirectoryChangesNotification(DWORD errCode, DWORD bytesTransfered, LPOVERLAPPED overlapped) { ScopedCritSec cs(&g_threadCritSec); OverlappedEx *over = (OverlappedEx*)overlapped; WatchedDir* wd = (WatchedDir*)over->data; lf(L"ReadDirectoryChangesNotification() dir: %s, numBytes: %d", wd->dirPath, (int)bytesTransfered); CrashIf(wd != wd->overlapped.data); if (errCode == ERROR_OPERATION_ABORTED) { lf(" ERROR_OPERATION_ABORTED"); DeleteWatchedDir(wd); InterlockedDecrement(&gRemovalsPending); return; } // This might mean overflow? Not sure. if (!bytesTransfered) return; FILE_NOTIFY_INFORMATION *notify = (FILE_NOTIFY_INFORMATION*)wd->buf; // collect files that changed, removing duplicates WStrVec changedFiles; for (;;) { ScopedMem<WCHAR> fileName(str::DupN(notify->FileName, notify->FileNameLength / sizeof(WCHAR))); // files can get updated either by writing to them directly or // by writing to a .tmp file first and then moving that file in place // (the latter only yields a RENAMED action with the expected file name) if (notify->Action == FILE_ACTION_MODIFIED || notify->Action == FILE_ACTION_RENAMED_NEW_NAME) { if (!changedFiles.Contains(fileName)) { lf(L"ReadDirectoryChangesNotification() FILE_ACTION_MODIFIED, for '%s'", fileName); changedFiles.Append(fileName.StealData()); } else { lf(L"ReadDirectoryChangesNotification() eliminating duplicate notification for '%s'", fileName); } } else { lf(L"ReadDirectoryChangesNotification() action=%d, for '%s'", (int)notify->Action, fileName); } // step to the next entry if there is one DWORD nextOff = notify->NextEntryOffset; if (!nextOff) break; notify = (FILE_NOTIFY_INFORMATION *)((char*)notify + nextOff); } StartMonitoringDirForChanges(wd); for (WCHAR **f = changedFiles.IterStart(); f; f = changedFiles.IterNext()) { NotifyAboutFile(wd, *f); } }
static void CALLBACK ReadDirectoryChangesNotification(DWORD errCode, DWORD bytesTransfered, LPOVERLAPPED overlapped) { ScopedCritSec cs(&g_threadCritSec); OverlappedEx *over = (OverlappedEx*)overlapped; WatchedDir* wd = (WatchedDir*)over->data; lf(L"ReadDirectoryChangesNotification() dir: %s, numBytes: %d", wd->dirPath, (int)bytesTransfered); CrashIf(wd != wd->overlapped.data); if (errCode == ERROR_OPERATION_ABORTED) { lf(" ERROR_OPERATION_ABORTED"); DeleteWatchedDir(wd); return; } // This might mean overflow? Not sure. if (!bytesTransfered) return; FILE_NOTIFY_INFORMATION *notify = (FILE_NOTIFY_INFORMATION*)wd->buf; // collect files that changed, removing duplicates WStrVec changedFiles; for (;;) { WCHAR *fileName = str::DupN(notify->FileName, notify->FileNameLength / sizeof(WCHAR)); if (notify->Action == FILE_ACTION_MODIFIED) { if (!changedFiles.Contains(fileName)) { lf(L"ReadDirectoryChangesNotification() FILE_ACTION_MODIFIED, for '%s'", fileName); changedFiles.Append(fileName); fileName = NULL; } else { lf(L"ReadDirectoryChangesNotification() eliminating duplicate notification for '%s'", fileName); } } else { lf(L"ReadDirectoryChangesNotification() action=%d, for '%s'", (int)notify->Action, fileName); } free(fileName); // step to the next entry if there is one DWORD nextOff = notify->NextEntryOffset; if (!nextOff) break; notify = (FILE_NOTIFY_INFORMATION *)((char*)notify + nextOff); } StartMonitoringDirForChanges(wd); for (WCHAR **f = changedFiles.IterStart(); f; f = changedFiles.IterNext()) { NotifyAboutFile(wd, *f); } }
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 srand((unsigned int)time(NULL)); // load uiautomationcore.dll before installing crash handler (i.e. initializing // dbghelp.dll), so that we get function names/offsets in GetCallstack() uia::Initialize(); #ifdef DEBUG dbghelp::RememberCallstackLogs(); #endif SetupCrashHandler(); ScopedOle ole; InitAllCommonControls(); ScopedGdiPlus gdiPlus(true); mui::Initialize(); uitask::Initialize(); prefs::Load(); CommandLineInfo i(GetCommandLine()); SetCurrentLang(i.lang ? i.lang : gGlobalPrefs->uiLanguage); // This allows ad-hoc comparison of gdi, gdi+ and gdi+ quick when used // in layout #if 0 RedirectIOToConsole(); BenchEbookLayout(L"C:\\kjk\\downloads\\pg12.mobi"); system("pause"); goto Exit; #endif if (i.showConsole) { RedirectIOToConsole(); RedirectDllIOToConsole(); } 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; gPolicyRestrictions = GetPolicies(i.restrictedUse); GetFixedPageUiColors(gRenderCache.textColor, gRenderCache.backgroundColor); DebugGdiPlusDevice(gUseGdiRenderer); if (!RegisterWinClass()) goto Exit; CrashIf(hInstance != GetModuleHandle(NULL)); if (!InstanceInit(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; } bool showStartPage = i.fileNames.Count() == 0 && gGlobalPrefs->rememberOpenedFiles && gGlobalPrefs->showStartPage; if (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 (gGlobalPrefs->reopenOnce) { WStrVec moreFileNames; ParseCmdLine(gGlobalPrefs->reopenOnce, moreFileNames); moreFileNames.Reverse(); for (WCHAR **fileName = moreFileNames.IterStart(); fileName; fileName = moreFileNames.IterNext()) { i.fileNames.Append(*fileName); } moreFileNames.RemoveAt(0, moreFileNames.Count()); str::ReplacePtr(&gGlobalPrefs->reopenOnce, NULL); } HANDLE hMutex = NULL; HWND hPrevWnd = NULL; if (i.printDialog || i.stressTestPath || gPluginMode) { // TODO: pass print request through to previous instance? } else if (i.reuseDdeInstance) { hPrevWnd = FindWindow(FRAME_CLASS_NAME, NULL); } else if (gGlobalPrefs->reuseInstance || gGlobalPrefs->useTabs) { hPrevWnd = FindPrevInstWindow(&hMutex); } if (hPrevWnd) { for (size_t n = 0; n < i.fileNames.Count(); n++) { OpenUsingDde(hPrevWnd, i.fileNames.At(n), i, 0 == n); } goto Exit; } WindowInfo *win = NULL; for (size_t n = 0; n < i.fileNames.Count(); n++) { win = LoadOnStartup(i.fileNames.At(n), i, !win); if (!win) { retCode++; continue; } if (i.printDialog) OnMenuPrint(win, i.exitWhenDone); } if (i.fileNames.Count() > 0 && !win) { // failed to create any window, even though there // were files to load (or show a failure message for) goto Exit; } if (i.printDialog && i.exitWhenDone) goto Exit; if (!win) { win = CreateAndShowWindowInfo(); if (!win) goto Exit; } UpdateUITextForLanguage(); // needed for RTL languages if (win->IsAboutWindow()) { // TODO: shouldn't CreateAndShowWindowInfo take care of this? UpdateToolbarAndScrollbarState(*win); } // Make sure that we're still registered as default, // if the user has explicitly told us to be if (gGlobalPrefs->associatedExtensions) RegisterForPdfExtentions(win->hwndFrame); if (i.stressTestPath) { // don't save file history and preference changes gPolicyRestrictions = (gPolicyRestrictions | Perm_RestrictedUse) & ~Perm_SavePreferences; RebuildMenuBarForWindow(win); StartStressTest(&i, win, &gRenderCache); } if (gGlobalPrefs->checkForUpdates) UpdateCheckAsync(win, true); // only hide newly missing files when showing the start page on startup if (showStartPage && gFileHistory.Get(0)) { gFileExistenceChecker = new FileExistenceChecker(); gFileExistenceChecker->Start(); } // call this once it's clear whether Perm_SavePreferences has been granted prefs::RegisterForFileChanges(); retCode = RunMessageLoop(); SafeCloseHandle(&hMutex); CleanUpThumbnailCache(gFileHistory); Exit: prefs::UnregisterForFileChanges(); while (gWindows.Count() > 0) { DeleteWindowInfo(gWindows.At(0)); } #ifndef DEBUG // leave all the remaining clean-up to the OS // (as recommended for a quick exit) ExitProcess(retCode); #else DeleteObject(GetDefaultGuiFont()); DeleteBitmap(gBitmapReloadingCue); DeleteSplitterBrush(); // wait for FileExistenceChecker to terminate // (which should be necessary only very rarely) while (gFileExistenceChecker) { Sleep(10); uitask::DrainQueue(); } mui::Destroy(); uitask::Destroy(); trans::Destroy(); SaveCallstackLogs(); dbghelp::FreeCallstackLogs(); // must be after uitask::Destroy() because we might have queued prefs::Reload() // which crashes if gGlobalPrefs is freed gFileHistory.UpdateStatesSource(NULL); DeleteGlobalPrefs(gGlobalPrefs); // 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 }