private void FireStartUseFeature() { FeatureTrackingManager.Instance.UseFeature( Features.App_Open, "CurrentVersion", ClientVersion.CurrentVersion, ".NETInfo", ComputerStatistics.GetCLRInfo() ); }
private void FireStartUseFeature() { FeatureTrackingManager.Instance.UseFeature( Features.App_Open, "CurrentVersion", ClientVersion.CurrentVersion, ".NET4Client", ComputerStatistics.IsNET4ClientInstalled(), ".NET4Full", ComputerStatistics.IsNET4FullInstalled() ); }
private static int log_close_down_counter = 2; // 2: once at end of main, plus once at (hopefully) end of threadpool queue. public static void CloseLogFile(object o) { ComputerStatistics.ReportMemoryStatus($"Status at termination end stage {3 - log_close_down_counter}"); // only close the log when this function has been called TWICE: log_close_down_counter--; if (log_close_down_counter == 0) { // This must be the last line the application executes, EVAR! Logging.ShutDown(); } }
// NOTE: // We could've used the DispatchTimer for this one (https://social.msdn.microsoft.com/Forums/vstudio/en-US/e7b66990-89c3-4958-b4e7-1d2ad1d4dd74/wpf-multitasking-along-with-a-timer-and-background-worker) // as the results as largely important only for the UI, but I've decided to // run this bugger in public static void BackgroundTask() { // We FIRST exec the UI update routine, because the UI timing parts of the CURRENT history record // will only now have been updated by the additional measurement background tasks. // If we would to this OnTick UI update work LAST, we would always lag one 'tick' (~ 2 seconds) // behind on timings, which would then show a flat leading part in any chart. OnTick?.Invoke(null, null); // history record based graphs, etc. *too*! // GC memory pressure? MemoryStatus mstat = ComputerStatistics.GetMemoryStatus(); bool are_timing_tests_running = false; // SHIFT the cursor one record forward and fill the new record as best as possible. // This means we have a 'fixed sample frequency' in the history, as set up in the // BackgroundWorkerDaemon. collected_data.ProcessCurrentRecord(shift: true, (rec, old) => { rec.memory = mstat; rec.UIResponsiveness = old.UIResponsiveness; are_timing_tests_running = (resp_housekeeping.running > 0); // the easiest way to ensure the timing values stay until updated, is to copy // the now-old values: }); // measure UI responsiveness? Only when we won't collide with still running tests! if (!are_timing_tests_running) { lock (collected_data.data_lock) { resp_housekeeping.running = 2; resp_housekeeping.clk2Test = Stopwatch.StartNew(); } SafeThreadPool.QueueUserWorkItem(o => MeasureUIResponsivenessNormal()); SafeThreadPool.QueueUserWorkItem(o => MeasureUIResponsivenessBackground()); } }
internal static void DoBundle() { try { // Try get this info for us which could be useful... // If things are really broken, it may fail, that's ok. string environment_details_filename = null; try { string environmentDetails = "Generated at:" + DateTime.UtcNow.ToString("yyyyMMdd HH:mm:ss") + Environment.NewLine; environmentDetails += ComputerStatistics.GetCommonStatistics(); environment_details_filename = TempFile.GenerateTempFilename("txt"); File.WriteAllText(environment_details_filename, environmentDetails); } catch (Exception ex) { Logging.Warn(ex, "Could not get environment details"); } // Get the destination location SaveFileDialog save_file_dialog = new SaveFileDialog(); save_file_dialog.AddExtension = true; save_file_dialog.CheckPathExists = true; save_file_dialog.DereferenceLinks = true; save_file_dialog.OverwritePrompt = true; save_file_dialog.ValidateNames = true; save_file_dialog.DefaultExt = "7z"; save_file_dialog.Filter = "7Z files (*.7z)|*.7z|All files (*.*)|*.*"; save_file_dialog.FileName = "QiqqaLogs.7z"; // Generate and save if (true == save_file_dialog.ShowDialog()) { string target_filename = save_file_dialog.FileName; string file_list = Path.GetFullPath(Path.Combine(Environment.GetFolderPath(Environment.SpecialFolder.LocalApplicationData, Environment.SpecialFolderOption.Create), @"Quantisle/Qiqqa/Logs", @"Qiqqa*.log*")); if (environment_details_filename != null) { file_list += " \"" + environment_details_filename + "\""; } // Delete the target filename if it exists... FileTools.Delete(target_filename); // STDOUT/STDERR string process_parameters = String.Format("a -t7z -mmt=on -mx9 -ssw \"{0}\" \"{1}\"", target_filename, file_list); using (Process process = ProcessSpawning.SpawnChildProcess(ConfigurationManager.Instance.Program7ZIP, process_parameters, ProcessPriorityClass.Normal)) { using (ProcessOutputReader process_output_reader = new ProcessOutputReader(process)) { process.WaitForExit(); Logging.Info("7ZIP Log Bundling progress:\n{0}", process_output_reader.GetOutputsDumpString()); } MessageBoxes.Info("The Qiqqa logs with some diagnostic info have been zipped to the location you specified. Please upload it as issue attachment in your issue filed at https://github.com/jimmejardine/qiqqa-open-source/issues if the support team has requested it. Many thanks!"); FileTools.BrowseToFileInExplorer(target_filename); } } FileTools.Delete(environment_details_filename); } catch (Exception ex) { Logging.Warn(ex, "Problem zipping logs"); MessageBoxes.Error("Unfortunately there was a problem creating the log bundle. Please zip them manually, they are found at C:\\Temp\\Qiqqa.log*. There may be more than one. Thanks!"); } }
private void PopulateMachineStats() { TextMachineStats.Text = ComputerStatistics.GetCommonStatistics(); }
private static void DoPreamble() { // Make sure the temp directory exists if (!TempDirectoryCreator.CheckTempExists()) { MessageBoxes.Error(@"Qiqqa needs the directory {0} to exist for it to function properly. Please create it or set the TEMP environment variable and restart Qiqqa.", TempFile.TempDirectory); } // Make sure that windir is set (goddamned font sybustem bug: http://stackoverflow.com/questions/10094197/wpf-window-throws-typeinitializationexception-at-start-up) { if (String.IsNullOrEmpty(Environment.GetEnvironmentVariable("windir"))) { Logging.Warn("Environment variable windir is empty so setting it to {0}", Environment.GetEnvironmentVariable("SystemRoot")); Environment.SetEnvironmentVariable("windir", Environment.GetEnvironmentVariable("SystemRoot")); } } Thread.CurrentThread.CurrentUICulture = Thread.CurrentThread.CurrentCulture; string on_your_conscience = "Qiqqa is Copyright © Quantisle 2010-2016. All rights reserved." + "If you are reading this in a disassembler, you know you are doing evil and will probably always have to look over your shoulder..." ; on_your_conscience = "Main"; Thread.CurrentThread.Name = on_your_conscience; if (RegistrySettings.Instance.IsSet(RegistrySettings.DebugConsole)) { Console.Instance.Init(); Logging.Info("Console initialised"); } // Support windows-level error reporting - helps suppressing the errors in pdfdraw.exe and QiqqaOCR.exe // https://msdn.microsoft.com/en-us/library/windows/desktop/ms680621%28v=vs.85%29.aspx if (true) { try { SetErrorMode(0x0001 | 0x0002 | 0x0004 | 0x8000); } catch (Exception ex) { Logging.Error(ex, "Error trying to suppress global process failure."); } } if (true) { AppDomain.CurrentDomain.AssemblyLoad += delegate(object sender, AssemblyLoadEventArgs args) { Logging.Info("Loaded assembly: {0}", args.LoadedAssembly.FullName); }; } try { FirstInstallWebLauncher.Check(); } catch (Exception ex) { Logging.Error(ex, "Unknown exception during FirstInstallWebLauncher.Check()."); } try { FileAssociationRegistration.DoRegistration(); } catch (Exception ex) { Logging.Error(ex, "Unknown exception during FileAssociationRegistration.DoRegistration()."); } //// Start tracing WPF events //WPFTrace wpf_trace = new WPFTrace(); //PresentationTraceSources.Refresh(); //PresentationTraceSources.DataBindingSource.Listeners.Add(wpf_trace); //PresentationTraceSources.DataBindingSource.Switch.Level = SourceLevels.Error; //System.Diagnostics.Trace.AutoFlush = true; // If we have a command line parameter and another instance is running, send it to them and exit if (true) { string[] command_line_args = Environment.GetCommandLineArgs(); if (1 < command_line_args.Length && !ProcessSingleton.IsProcessUnique(false)) { IPCClient.SendMessage(command_line_args[1]); Environment.Exit(-2); } } // Check that we are the only instance running if (true) { try { if (!RegistrySettings.Instance.IsSet(RegistrySettings.AllowMultipleQiqqaInstances) && !ProcessSingleton.IsProcessUnique(true)) { MessageBoxes.Info("There seems to be an instance of Qiqqa already running so Qiqqa will not start again.\n\nSometimes it takes a few moments for Qiqqa to exit as it finishes up a final OCR or download. If this problem persists, you can kill the Qiqqa.exe process in Task Manager."); Logging.Info("There is another instance of Qiqqa running, so exiting."); Environment.Exit(-1); } } catch (Exception ex) { Logging.Error(ex, "Unknown exception while checking for single app instance. Continuing as normal so there could be several Qiqqas running."); } } ComputerStatistics.LogCommonStatistics(); }
private void PopulateMachineStats() { TextMachineStats.Text = ComputerStatistics.GetCommonStatistics(ConfigurationManager.GetCurrentConfigInfos()); }
private static void DoPreamble() { // Make sure the temp directory exists if (!TempDirectoryCreator.CheckTempExists()) { MessageBoxes.Error(@"Qiqqa needs the directory {0} to exist for it to function properly. Please create it or set the TEMP environment variable and restart Qiqqa.", TempFile.TempDirectoryForQiqqa); } // Make sure that windir is set (goddamned font subsystem bug: http://stackoverflow.com/questions/10094197/wpf-window-throws-typeinitializationexception-at-start-up) { if (String.IsNullOrEmpty(Environment.GetEnvironmentVariable("windir"))) { Logging.Warn("Environment variable windir is empty so setting it to {0}", Environment.GetEnvironmentVariable("SystemRoot")); Environment.SetEnvironmentVariable("windir", Environment.GetEnvironmentVariable("SystemRoot")); } } Thread.CurrentThread.CurrentUICulture = Thread.CurrentThread.CurrentCulture; Thread.CurrentThread.Name = "Main"; if (RegistrySettings.Instance.IsSet(RegistrySettings.DebugConsole)) { Console.Instance.Init(); Logging.Info("Console initialised"); } // Support windows-level error reporting - helps suppressing the errors in pdfdraw.exe and QiqqaOCR.exe // https://msdn.microsoft.com/en-us/library/windows/desktop/ms680621%28v=vs.85%29.aspx try { SetErrorMode(0x0001 | 0x0002 | 0x0004 | 0x8000); } catch (Exception ex) { Logging.Error(ex, "Error trying to suppress global process failure."); } // kick the number of threads in the threadpool down to a reasonable number SafeThreadPool.SetMaxActiveThreadCount(); AppDomain.CurrentDomain.AssemblyLoad += delegate(object sender, AssemblyLoadEventArgs args) { Logging.Info("Loaded assembly: {0}", args.LoadedAssembly.FullName); Logging.TriggerInit(); }; #if CEFSHARP #region CEFsharp setup // CEFsharp setup for AnyPC as per https://github.com/cefsharp/CefSharp/issues/1714: AppDomain.CurrentDomain.AssemblyResolve += CefResolver; InitCef(); #endregion CEFsharp setup #endif try { FirstInstallWebLauncher.Check(); } catch (Exception ex) { Logging.Error(ex, "Unknown exception during FirstInstallWebLauncher.Check()."); } try { FileAssociationRegistration.DoRegistration(); } catch (Exception ex) { Logging.Error(ex, "Unknown exception during FileAssociationRegistration.DoRegistration()."); } // Start tracing WPF events #if DEBUG WPFTrace wpf_trace = new WPFTrace(); PresentationTraceSources.Refresh(); PresentationTraceSources.DataBindingSource.Listeners.Add(wpf_trace); PresentationTraceSources.DataBindingSource.Switch.Level = SourceLevels.Error; System.Diagnostics.Trace.AutoFlush = true; #endif // If we have a command line parameter and another instance is running, send it to them and exit string[] command_line_args = Environment.GetCommandLineArgs(); if (1 < command_line_args.Length && !ProcessSingleton.IsProcessUnique(false)) { IPCClient.SendMessage(command_line_args[1]); Environment.Exit(-2); } // Check that we are the only instance running try { if (!RegistrySettings.Instance.IsSet(RegistrySettings.AllowMultipleQiqqaInstances) && !ProcessSingleton.IsProcessUnique(bring_other_process_to_front_if_it_exists: true)) { MessageBoxes.Info("There seems to be an instance of Qiqqa already running so Qiqqa will not start again.\n\nSometimes it takes a few moments for Qiqqa to exit as it finishes up a final OCR or download. If this problem persists, you can kill the Qiqqa.exe process in Task Manager."); Logging.Info("There is another instance of Qiqqa running, so exiting."); Environment.Exit(-1); } } catch (Exception ex) { Logging.Error(ex, "Unknown exception while checking for single app instance. Continuing as normal so there could be several Qiqqas running."); } ComputerStatistics.LogCommonStatistics(); }
internal static void DoBundle() { string target_filename = null; try { // Get the destination location SaveFileDialog save_file_dialog = new SaveFileDialog(); save_file_dialog.AddExtension = true; save_file_dialog.CheckPathExists = true; save_file_dialog.DereferenceLinks = true; save_file_dialog.OverwritePrompt = true; save_file_dialog.ValidateNames = true; save_file_dialog.DefaultExt = "7z"; save_file_dialog.Filter = "7Z files (*.7z)|*.7z|All files (*.*)|*.*"; save_file_dialog.FileName = "QiqqaLogs.7z"; // Generate and save if (true == save_file_dialog.ShowDialog()) { target_filename = save_file_dialog.FileName; } } catch (Exception ex) { Logging.Warn(ex, "Problem zipping logs"); MessageBoxes.Error("Unfortunately there was a problem creating the log bundle. Please zip them manually, they are found at C:\\Temp\\Qiqqa.log*. There may be more than one. Thanks!"); target_filename = null; } if (target_filename != null) { int progress = 1; int wait_period = 300; const int MAX_PROGRESS = 100; StatusManager.Instance.UpdateStatus("LogBundler", "Bundling the logfile. Please wait...", progress, MAX_PROGRESS); SafeThreadPool.QueueUserWorkItem(o => { string environment_details_filename = null; try { // Delete the target filename if it exists... FileTools.Delete(target_filename); // Note: Path.GetFullPath() throws an exception when you feed it wildcards, e.g. '*' // hence we construct the search path in two steps: const string MAGIC_FILENAME = @"QQQ"; string file_list = Path.GetFullPath(Path.Combine(Environment.GetFolderPath(Environment.SpecialFolder.LocalApplicationData, Environment.SpecialFolderOption.Create), @"Quantisle/Qiqqa/Logs", MAGIC_FILENAME)); // and then we replace the 'magic file name' with the real thing: file_list = $"\"{Path.GetSuffixedDirectoryName(file_list)}Qiqqa*.log*\""; // Try get this info for us which could be useful... // If things are really broken, it may fail, that's ok. try { string environmentDetails = "Generated at:" + DateTime.UtcNow.ToString("yyyyMMdd HH:mm:ss") + Environment.NewLine; environmentDetails += ComputerStatistics.GetCommonStatistics(ConfigurationManager.GetCurrentConfigInfos()); environmentDetails += "\r\nConfiguration Bits:\r\n"; environmentDetails += $"Background Tasks: {(ConfigurationManager.Instance.ConfigurationRecord.DisableAllBackgroundTasks ? "Disabled ALL" : "Normal (Enabled)")}\r\n"; environmentDetails += $"Library OCR Task: {(ConfigurationManager.Instance.ConfigurationRecord.Library_OCRDisabled ? "Disabled" : "Normal (Enabled)")}\r\n"; environment_details_filename = TempFile.GenerateTempFilename("txt"); File.WriteAllText(environment_details_filename, environmentDetails); } catch (Exception ex) { Logging.Warn(ex, "Could not get environment details"); } if (environment_details_filename != null) { file_list += " \"" + environment_details_filename + "\""; } // STDOUT/STDERR string process_parameters = String.Format("a -t7z -mmt=on -mx9 -ssw \"{0}\" {1}", target_filename, file_list); Logging.Info($"Bundling the logfiles via command:\n {ConfigurationManager.Instance.Program7ZIP} {process_parameters}"); using (Process process = ProcessSpawning.SpawnChildProcess(ConfigurationManager.Instance.Program7ZIP, process_parameters, ProcessPriorityClass.Normal)) { using (ProcessOutputReader process_output_reader = new ProcessOutputReader(process)) { while (!process.WaitForExit(wait_period)) { progress = Math.Min(MAX_PROGRESS - 5, progress + 1); if (progress >= 25) { wait_period = 1000; } StatusManager.Instance.UpdateStatus("LogBundler", "Bundling the logfile. Please wait...", progress, MAX_PROGRESS); } Logging.Info("7ZIP Log Bundling progress:\n{0}", process_output_reader.GetOutputsDumpStrings()); } MessageBoxes.Info($"The Qiqqa logs with some diagnostic info have been zipped to the location you specified:\n{target_filename}\n\nPlease upload it as issue attachment in your issue filed at https://github.com/jimmejardine/qiqqa-open-source/issues if the support team has requested it. Many thanks!"); FileTools.BrowseToFileInExplorer(target_filename); } } catch (Exception ex) { Logging.Warn(ex, "Problem zipping logs"); MessageBoxes.Error("Unfortunately there was a problem creating the log bundle. Please zip them manually, they are found at C:\\Temp\\Qiqqa.log*. There may be more than one. Thanks!"); target_filename = null; } if (environment_details_filename != null) { FileTools.Delete(environment_details_filename); } }); } }
private void StartMainApplication() { WPFDoEvents.AssertThisCodeIsRunningInTheUIThread(); // prevent invocation loop via close() call at the end of this function body: if (StandardWindowFactory.Has(nameof(MainWindow))) { return; } WPFDoEvents.SetHourglassCursor(); ConfigurationManager.Instance.BaseDirectoryForQiqqaIsFixedFromNowOn = true; // Initialise the web browser try { StatusManager.Instance.UpdateStatus("AppStart", "Installing browser components"); GeckoInstaller.CheckForInstall(); StatusManager.Instance.UpdateStatus("AppStart", "Initialising browser components"); GeckoManager.Initialise(); GeckoManager.RegisterPDFInterceptor(); } catch (Exception ex) { Logging.Error(ex, "Problem initialising GeckoFX."); } Logging.Info("Log the config+stats again now that we are sure to have loaded the working configuration:"); ComputerStatistics.LogCommonStatistics(ConfigurationManager.GetCurrentConfigInfos()); // Fire up Qiqqa! SafeThreadPool.QueueUserWorkItem(o => { try { // Perform any upgrade paths that we must StatusManager.Instance.UpdateStatus("AppStart", "Upgrading old libraries"); UpgradeManager.RunUpgrades(); #if false Thread.Sleep(15000); #endif StatusManager.Instance.UpdateStatus("AppStart", "Starting background processes"); WebLibraryManager.Instance.Kick(); } catch (Exception ex) { Logging.Error(ex, "Problem while starting up the Qiqqa core."); } }); StatusManager.Instance.UpdateStatus("AppStart", "Launching Qiqqa!"); FireStartUseFeature(); StandardWindowFactory.Create(nameof(MainWindow), () => { MainWindow window = new MainWindow(); window.Show(); return(window); }); Hide(); Close(); }
private static void Main() { Logging.Info("+static Main()"); try { StatusManager.Instance.UpdateStatus("AppStart", "Logging in"); // NOTE: the initial Login Dialog will be shown by code at the end // of the (background) DoPostUpgrade() process which is already running // by the time we arrive at this location. // // This ensures all process parts, which are expected to be done by // the time to login Dialog is visible (and usable by the user), are // indeed ready. Exception has_ex = null; try { application.Run(); } catch (Exception ex) { has_ex = ex; Logging.Error(ex, "Exception caught at Main() Application::Run(). Disaster."); } SignalShutdown(has_ex != null ? $"Exception caught in Main Application::Run() function: {has_ex}" : "Main Application::Run() has terminated."); } catch (Exception ex) { Logging.Error(ex, "Exception caught at Main(). Disaster."); SignalShutdown($"Exception caught in Main Application function: {ex}"); } Logging.Info("-static Main()"); // When we get here and wait for the other threads to close off any business they're attending, // we SHOULD have a nicely terminated application run and all the heap allocations left should // be MEMORY LEAKS. // BUT that's only going to be anywhere near TRUE when we make sure we discard the loaded // libraries all properly like, etc. // So that's what we're going to do next. And forced GC to kick the sluggish off the lot // before we report. Logging.Info("Unloading all user libraries..."); // kick off all the libraries WebLibraryManager.Instance.UnloadAllLibraries(); SafeThreadPool.QueueUserWorkItem(CloseLogFile, skip_task_at_app_shutdown: false); Logging.Info("Making sure all threads have completed or terminated..."); // give this a sane upper limit so the application cannot ever be 'stuck in the background' due to this: int wait_time = 5000; for (int min_rounds = 3; wait_time > 0 && ( min_rounds > 0 || SafeThreadPool.RunningThreadCount > 0 || GC.GetTotalMemory(false) > 10000000L || ComputerStatistics.GetTotalRunningThreadCount() > 0 ); min_rounds--) { GC.WaitForPendingFinalizers(); GC.Collect(GC.MaxGeneration, GCCollectionMode.Forced, true, true); Logging.Info($"-static Heap after forced GC compacting at the end (in the wait-for-all-threads-to-terminate loop): {GC.GetTotalMemory(false)} Bytes, {SafeThreadPool.RunningThreadCount} tasks active, {ComputerStatistics.GetTotalRunningThreadCount()} threads running"); Thread.Sleep(1000); wait_time -= 1000; } Logging.Info($"Last machine state observation before shutting down the log at the very end of the application run: Heap after forced GC compacting: {GC.GetTotalMemory(false)} Bytes, {SafeThreadPool.RunningThreadCount} tasks active, {ComputerStatistics.GetTotalRunningThreadCount()} threads running, {wait_time / 1000} seconds overtime unused (more than zero for this one is good!)"); CloseLogFile(null); }