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()
         );
 }
Exemple #3
0
        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());
            }
        }
Exemple #5
0
        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();
 }
Exemple #7
0
        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();
        }
Exemple #8
0
 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();
        }
Exemple #12
0
        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);
        }