private static void Inject(Process process, DalamudStartInfo info) { Console.WriteLine($"Injecting to {process.Id}"); // File check var libPath = Path.GetFullPath("Dalamud.dll"); if (!File.Exists(libPath)) { Console.WriteLine($"Can't find a dll on {libPath}"); return; } RemoteHooking.Inject(process.Id, InjectionOptions.DoNotRequireStrongName, libPath, libPath, info); Console.WriteLine("Injected"); }
/// <summary> /// Initializes a new instance of the <see cref="Dalamud"/> class. /// </summary> /// <param name="info">DalamudStartInfo instance.</param> /// <param name="loggingLevelSwitch">LoggingLevelSwitch to control Serilog level.</param> /// <param name="finishSignal">Signal signalling shutdown.</param> /// <param name="configuration">The Dalamud configuration.</param> public Dalamud(DalamudStartInfo info, LoggingLevelSwitch loggingLevelSwitch, ManualResetEvent finishSignal, DalamudConfiguration configuration) { this.ApplyProcessPatch(); Service <Dalamud> .Set(this); Service <DalamudStartInfo> .Set(info); Service <DalamudConfiguration> .Set(configuration); this.LogLevelSwitch = loggingLevelSwitch; this.unloadSignal = new ManualResetEvent(false); this.unloadSignal.Reset(); this.finishUnloadSignal = finishSignal; this.finishUnloadSignal.Reset(); }
private static void Inject(Process process, DalamudStartInfo startInfo) { var nethostName = "nethost.dll"; var bootName = "Dalamud.Boot.dll"; var nethostPath = Path.GetFullPath(nethostName); var bootPath = Path.GetFullPath(bootName); // ====================================================== using var injector = new Injector(process); injector.LoadLibrary(nethostPath, out _); injector.LoadLibrary(bootPath, out var bootModule); // ====================================================== var startInfoJson = JsonConvert.SerializeObject(startInfo); var startInfoBytes = Encoding.UTF8.GetBytes(startInfoJson); using var startInfoBuffer = new MemoryBufferHelper(process).CreatePrivateMemoryBuffer(startInfoBytes.Length + 0x8); var startInfoAddress = startInfoBuffer.Add(startInfoBytes); if (startInfoAddress == IntPtr.Zero) { throw new Exception("Unable to allocate start info JSON"); } injector.GetFunctionAddress(bootModule, "Initialize", out var initAddress); injector.CallRemoteFunction(initAddress, startInfoAddress, out var exitCode); // ====================================================== if (exitCode > 0) { Log.Error($"Dalamud.Boot::Initialize returned {exitCode}"); return; } Log.Information("Done"); }
private static DalamudStartInfo GetDefaultStartInfo() { var ffxivDir = Path.GetDirectoryName(process.MainModule.FileName); var startInfo = new DalamudStartInfo { WorkingDirectory = null, ConfigurationPath = Path.Combine(Environment.GetFolderPath(Environment.SpecialFolder.ApplicationData), "XIVLauncher", "dalamudConfig.json"), PluginDirectory = Path.Combine(Environment.GetFolderPath(Environment.SpecialFolder.ApplicationData), "XIVLauncher", "installedPlugins"), DefaultPluginDirectory = Path.Combine(Environment.GetFolderPath(Environment.SpecialFolder.ApplicationData), "XIVLauncher", "devPlugins"), GameVersion = File.ReadAllText(Path.Combine(ffxivDir, "ffxivgame.ver")), Language = ClientLanguage.English }; Console.WriteLine("Creating a StartInfo with:\n" + $"ConfigurationPath: {startInfo.ConfigurationPath}\n" + $"PluginDirectory: {startInfo.PluginDirectory}\n" + $"DefaultPluginDirectory: {startInfo.DefaultPluginDirectory}\n" + $"Language: {startInfo.Language}\n" + $"GameVersion: {startInfo.GameVersion}"); return(startInfo); }
/// <summary> /// Set up client state access. /// </summary> /// <param name="dalamud">Dalamud instance</param> /// /// <param name="startInfo">StartInfo of the current Dalamud launch</param> /// <param name="scanner">Sig scanner</param> public ClientState(Dalamud dalamud, DalamudStartInfo startInfo, SigScanner scanner) { Address = new ClientStateAddressResolver(); Address.Setup(scanner); Log.Verbose("===== C L I E N T S T A T E ====="); this.ClientLanguage = startInfo.Language; this.Actors = new ActorTable(dalamud, Address); this.JobGauges = new JobGauges(Address); this.KeyState = new KeyState(Address, scanner.Module.BaseAddress); Log.Verbose("SetupTerritoryType address {SetupTerritoryType}", Address.SetupTerritoryType); this.setupTerritoryTypeHook = new Hook <SetupTerritoryTypeDelegate>(Address.SetupTerritoryType, new SetupTerritoryTypeDelegate(SetupTerritoryTypeDetour), this); dalamud.Framework.OnUpdateEvent += FrameworkOnOnUpdateEvent; }
private static DalamudStartInfo GetDefaultStartInfo() { var startInfo = new DalamudStartInfo { WorkingDirectory = null, ConfigurationPath = Environment.GetFolderPath(Environment.SpecialFolder.ApplicationData) + @"\XIVLauncher\dalamudConfig.json", PluginDirectory = Environment.GetFolderPath(Environment.SpecialFolder.ApplicationData) + @"\XIVLauncher\installedPlugins", DefaultPluginDirectory = Environment.GetFolderPath(Environment.SpecialFolder.ApplicationData) + @"\XIVLauncher\devPlugins", GameVersion = "2020.02.11.0000.0000", Language = ClientLanguage.English }; Console.WriteLine("Creating a StartInfo with:\n" + $"ConfigurationPath: {startInfo.ConfigurationPath}\n" + $"PluginDirectory: {startInfo.PluginDirectory}\n" + $"DefaultPluginDirectory: {startInfo.DefaultPluginDirectory}\n" + $"Language: {startInfo.Language}\n" + $"GameVersion: {startInfo.GameVersion}"); return(startInfo); }
private void Run(DirectoryInfo gamePath, ClientLanguage language, Process gameProcess) { Log.Information("[HOOKS] DalamudLauncher::Run(gp:{0}, cl:{1}, d:{2}", gamePath.FullName, language); if (!CheckVcRedist()) { return; } var ingamePluginPath = Path.Combine(Paths.RoamingPath, "installedPlugins"); var defaultPluginPath = Path.Combine(Paths.RoamingPath, "devPlugins"); Directory.CreateDirectory(ingamePluginPath); Directory.CreateDirectory(defaultPluginPath); Thread.Sleep((int)App.Settings.DalamudInjectionDelayMs); if (DalamudUpdater.State != DalamudUpdater.DownloadState.Done) { DalamudUpdater.ShowOverlay(); } while (DalamudUpdater.State != DalamudUpdater.DownloadState.Done) { if (DalamudUpdater.State == DalamudUpdater.DownloadState.Failed) { DalamudUpdater.CloseOverlay(); return; } if (DalamudUpdater.State == DalamudUpdater.DownloadState.NoIntegrity) { DalamudUpdater.CloseOverlay(); MessageBox.Show(Loc.Localize("DalamudAntivirusHint", "The in-game addon ran into an error.\n\nThis is most likely caused by your antivirus. Please whitelist the quarantined files or disable the in-game addon.")); return; } Thread.Yield(); } if (!DalamudUpdater.Runner.Exists) { CustomMessageBox.Show( "Could not launch the in-game addon successfully. This might be caused by your antivirus.\nTo prevent this, please add an exception for the folder \"%AppData%\\XIVLauncher\\addons\".", "XIVLauncher Error", MessageBoxButton.OK, MessageBoxImage.Error); return; } if (!ReCheckVersion(gamePath)) { DalamudUpdater.SetOverlayProgress(DalamudLoadingOverlay.DalamudLoadingProgress.Unavailable); DalamudUpdater.ShowOverlay(); Log.Error("[HOOKS] ReCheckVersion fail."); return; } var startInfo = new DalamudStartInfo { Language = language, PluginDirectory = ingamePluginPath, DefaultPluginDirectory = defaultPluginPath, ConfigurationPath = DalamudSettings.configPath, AssetDirectory = DalamudUpdater.AssetDirectory.FullName, GameVersion = Repository.Ffxiv.GetVer(gamePath), OptOutMbCollection = _optOutMbCollection }; var parameters = Convert.ToBase64String(Encoding.UTF8.GetBytes(JsonConvert.SerializeObject(startInfo))); var process = new Process { StartInfo = { FileName = DalamudUpdater.Runner.FullName, WindowStyle = ProcessWindowStyle.Hidden, CreateNoWindow = true, Arguments = gameProcess.Id.ToString() + " " + parameters, WorkingDirectory = DalamudUpdater.Runner.DirectoryName } }; process.Start(); _overlay.Dispatcher.Invoke(() => { _overlay.Hide(); _overlay.Close(); }); DalamudUpdater.CloseOverlay(); Serilog.Log.Information("[HOOKS] Started dalamud!"); // Reset security protocol after updating ServicePointManager.SecurityProtocol = SecurityProtocolType.SystemDefault; }
static void Main(string[] args) { #if !DEBUG //check work path var filePath = AppDomain.CurrentDomain.BaseDirectory; var workPath = Directory.GetCurrentDirectory() + @"\"; if (filePath != workPath) { Directory.SetCurrentDirectory(filePath); } #endif //init Process gameProcess; var pid = -1; if (args.Length >= 1) { try { var argPid = args[0]; if (argPid.StartsWith("0x")) { pid = Convert.ToInt32(args[0], 16); } else { pid = Convert.ToInt32(args[0]); } } catch { throw new Exception("the first argument should be the pid of ffxiv game"); } } try { if (pid == -1) { gameProcess = Process.GetProcessesByName("ffxiv_dx11")[0]; } else { gameProcess = Process.GetProcessById(pid); if (gameProcess.ProcessName != "ffxiv_dx11") { throw new Exception("the pid is invalid"); } } } catch (Exception e) { throw e; } var lang = ClientLanguage.ChineseSimplified; if (args.Length >= 2) { try { lang = (ClientLanguage)Convert.ToInt32(args[1]); } catch { throw new Exception("the second argument should be the language enum"); } } #if !DEBUG //检查是否已经被注入 foreach (ProcessModule module in gameProcess.Modules) { if (module.ModuleName == "EasyHook64.dll") { Console.WriteLine($"gameProcess {gameProcess.Id} has been injected"); Environment.Exit(0); } } #endif // File check var libPath = Path.GetFullPath("Dalamud.dll"); var pluginPath = Path.GetDirectoryName(libPath); if (!File.Exists(libPath)) { Console.WriteLine("can not find dalamud.dll"); return; } var xivLauncherPath = Environment.ExpandEnvironmentVariables(@"%appdata%");//国际服only var loadPath = lang == ClientLanguage.Japanese ? xivLauncherPath : pluginPath; //构建command line var command = new DalamudStartInfo { WorkingDirectory = pluginPath, ConfigurationPath = loadPath + @"\XIVLauncher\dalamudConfig.json", PluginDirectory = loadPath + @"\XIVLauncher\installedPlugins", DefaultPluginDirectory = loadPath + @"\XIVLauncher\devPlugins", AssetDirectory = pluginPath + @"\XIVLauncher\dalamudAssets", GameVersion = GetGameVersion(gameProcess), Language = lang }; //clean dalamud log Task.Run(() => CleanDalamudLog()); // Inject try { Thread.Sleep(500); RemoteHooking.Inject(gameProcess.Id, InjectionOptions.DoNotRequireStrongName, libPath, libPath, command); } catch (Exception) { throw; } }
private void Run(DirectoryInfo gamePath, ClientLanguage language, Process gameProcess) { var addonDirectory = Path.Combine(Environment.GetFolderPath(Environment.SpecialFolder.ApplicationData), "XIVLauncher", "addon", "Hooks"); var addonExe = Path.Combine(addonDirectory, "Dalamud.Injector.exe"); var ingamePluginPath = Path.Combine(Environment.GetFolderPath(Environment.SpecialFolder.ApplicationData), "XIVLauncher", "installedPlugins"); var defaultPluginPath = Path.Combine(Environment.GetFolderPath(Environment.SpecialFolder.ApplicationData), "XIVLauncher", "devPlugins"); Directory.CreateDirectory(ingamePluginPath); Directory.CreateDirectory(defaultPluginPath); using (var client = new WebClient()) { // GitHub requires TLS 1.2, we need to hardcode this for Windows 7 ServicePointManager.SecurityProtocol = SecurityProtocolType.Tls12; var versionInfoJson = client.DownloadString(Remote + "version"); var remoteVersionInfo = JsonConvert.DeserializeObject <HooksVersionInfo>(versionInfoJson); if (!Mutex.TryOpenExisting(DALAMUD_MUTEX_NAME, out _)) { _openMutex = new Mutex(true, DALAMUD_MUTEX_NAME); if (!File.Exists(addonExe)) { Serilog.Log.Information("[HOOKS] Not found, redownloading"); Download(addonDirectory); } else { var versionInfo = FileVersionInfo.GetVersionInfo(addonExe); var version = versionInfo.ProductVersion; Serilog.Log.Information("Hooks update check: local {0} remote {1}", version, remoteVersionInfo.AssemblyVersion); if (!remoteVersionInfo.AssemblyVersion.StartsWith(version)) { Download(addonDirectory); } } } if (XivGame.GetLocalGameVer(gamePath) != remoteVersionInfo.SupportedGameVer) { return; } if (!File.Exists(Path.Combine(addonDirectory, "EasyHook.dll")) || !File.Exists(Path.Combine(addonDirectory, "Dalamud.dll"))) { MessageBox.Show( "Could not launch the in-game addon successfully. This might be caused by your antivirus.\n To prevent this, please add an exception for the folder \"%AppData%\\XIVLauncher\\addons\".", "XIVLauncher Error", MessageBoxButton.OK, MessageBoxImage.Error); Directory.Delete(addonDirectory, true); return; } var configPath = Path.Combine(Environment.GetFolderPath(Environment.SpecialFolder.ApplicationData), "XIVLauncher", "dalamudConfig.json"); var startInfo = new DalamudStartInfo { Language = language, PluginDirectory = ingamePluginPath, DefaultPluginDirectory = defaultPluginPath, ConfigurationPath = configPath, GameVersion = remoteVersionInfo.SupportedGameVer }; var parameters = Convert.ToBase64String(Encoding.UTF8.GetBytes(JsonConvert.SerializeObject(startInfo))); var process = new Process { StartInfo = { FileName = addonExe, WindowStyle = ProcessWindowStyle.Hidden, CreateNoWindow = true, Arguments = gameProcess.Id.ToString() + " " + parameters, WorkingDirectory = addonDirectory } }; process.Start(); Serilog.Log.Information("Started dalamud!"); // Reset security protocol after updating ServicePointManager.SecurityProtocol = SecurityProtocolType.SystemDefault; } }
public void Run(Process gameProcess) { // Launcher Hooks don't work on DX9 and probably never will if (!Settings.IsDX11()) { return; } var addonDirectory = Path.Combine(Environment.GetFolderPath(Environment.SpecialFolder.ApplicationData), "XIVLauncher", "addon", "Hooks"); var addonExe = Path.Combine(addonDirectory, "Dalamud.Injector.exe"); var ingamePluginPath = Path.Combine(Environment.GetFolderPath(Environment.SpecialFolder.ApplicationData), "XIVLauncher", "plugins"); var defaultPluginPath = Path.Combine(Environment.GetFolderPath(Environment.SpecialFolder.ApplicationData), "XIVLauncher", "defaultplugins"); Directory.CreateDirectory(ingamePluginPath); using (var client = new WebClient()) { var versionInfoJson = client.DownloadString(Remote + "version"); var remoteVersionInfo = JsonConvert.DeserializeObject <HooksVersionInfo>(versionInfoJson); if (!File.Exists(addonExe)) { Download(addonDirectory, defaultPluginPath); } else { var versionInfo = FileVersionInfo.GetVersionInfo(addonExe); var version = versionInfo.ProductVersion; Serilog.Log.Information("Hooks update check: local {0} remote {1}", version, remoteVersionInfo.AssemblyVersion); if (!remoteVersionInfo.AssemblyVersion.StartsWith(version)) { Download(addonDirectory, defaultPluginPath); } } if (XIVGame.GetLocalGamever() != remoteVersionInfo.SupportedGameVer) { return; } var dalamudConfig = new DalamudStartInfo { LanguageId = (int)Settings.GetLanguage(), DiscordFeatureConfig = Settings.DiscordFeatureConfig, PluginDirectory = ingamePluginPath, DefaultPluginDirectory = defaultPluginPath }; var parameters = Convert.ToBase64String(Encoding.UTF8.GetBytes(JsonConvert.SerializeObject(dalamudConfig))); var process = new Process { StartInfo = { FileName = addonExe, WindowStyle = ProcessWindowStyle.Hidden, CreateNoWindow = true, Arguments = gameProcess.Id.ToString() + " " + parameters, WorkingDirectory = addonDirectory } }; Serilog.Log.Information("Starting dalamud with parameters: {0} {1}", process.StartInfo.FileName, process.StartInfo.Arguments); process.Start(); } }
public void Run() { // Launcher Hooks don't work on DX9 and probably never will if (!Settings.IsDX11()) { return; } var addonDirectory = Path.Combine(Environment.GetFolderPath(Environment.SpecialFolder.ApplicationData), "XIVLauncher", "addon", "Hooks"); var addonExe = Path.Combine(addonDirectory, "Dalamud.Injector.exe"); var ingamePluginPath = Path.Combine(Environment.GetFolderPath(Environment.SpecialFolder.ApplicationData), "XIVLauncher", "plugins"); var defaultPluginPath = Path.Combine(Environment.GetFolderPath(Environment.SpecialFolder.ApplicationData), "XIVLauncher", "defaultplugins"); Directory.CreateDirectory(ingamePluginPath); using (var client = new WebClient()) { var versionInfoJson = client.DownloadString(REMOTE + "version"); var remoteVersionInfo = JsonConvert.DeserializeObject <HooksVersionInfo>(versionInfoJson); if (!File.Exists(addonExe)) { Download(addonDirectory, defaultPluginPath); } else { var versionInfo = FileVersionInfo.GetVersionInfo(addonExe); var version = versionInfo.ProductVersion; Serilog.Log.Information("Hooks update check: local {0} remote {1}", version, remoteVersionInfo.AssemblyVersion); if (!remoteVersionInfo.AssemblyVersion.StartsWith(version)) { Download(addonDirectory, defaultPluginPath); } } if (XivGame.GetLocalGameVer() != remoteVersionInfo.SupportedGameVer) { return; } if (!File.Exists(Path.Combine(addonDirectory, "EasyHook.dll"))) { MessageBox.Show( "Could not launch the in-game addon successfully. This might be caused by your antivirus.\n To prevent this, please add an exception for the folder \"%AppData%\\XIVLauncher\\addons\".", "XIVLauncher Error", MessageBoxButton.OK, MessageBoxImage.Error); return; } var dalamudConfig = new DalamudStartInfo { Language = Settings.GetLanguage(), DiscordFeatureConfig = Settings.DiscordFeatureConfig, PluginDirectory = ingamePluginPath, DefaultPluginDirectory = defaultPluginPath, OptOutMbCollection = Settings.OptOutMbUpload }; var parameters = Convert.ToBase64String(Encoding.UTF8.GetBytes(JsonConvert.SerializeObject(dalamudConfig))); var process = new Process { StartInfo = { FileName = addonExe, WindowStyle = ProcessWindowStyle.Hidden, CreateNoWindow = true, Arguments = _gameProcess.Id.ToString() + " " + parameters, WorkingDirectory = addonDirectory } }; process.Start(); Serilog.Log.Information("Started dalamud!"); } }
private void Run(DirectoryInfo gamePath, ClientLanguage language, Process gameProcess, bool doDownloads) { Log.Information("DalamudLauncher::Run(gp:{0}, cl:{1}, d:{2}", gamePath.FullName, language, doDownloads); if (!CheckVcRedist()) { return; } var addonDirectory = Path.Combine(Paths.RoamingPath, "addon", "Hooks"); var addonExe = Path.Combine(addonDirectory, "Dalamud.Injector.exe"); var ingamePluginPath = Path.Combine(Paths.RoamingPath, "installedPlugins"); var defaultPluginPath = Path.Combine(Paths.RoamingPath, "devPlugins"); Directory.CreateDirectory(ingamePluginPath); Directory.CreateDirectory(defaultPluginPath); var configPath = Path.Combine(Paths.RoamingPath, "dalamudConfig.json"); var config = DalamudSettings.DalamudConfig; Thread.Sleep((int)App.Settings.DalamudInjectionDelayMs); using var client = new WebClient(); // GitHub requires TLS 1.2, we need to hardcode this for Windows 7 ServicePointManager.SecurityProtocol = SecurityProtocolType.Tls12; var versionInfoJson = client.DownloadString(REMOTE_BASE + (config.DoDalamudTest ? "stg/" : string.Empty) + "version"); var remoteVersionInfo = JsonConvert.DeserializeObject <HooksVersionInfo>(versionInfoJson); if (doDownloads) { if (!File.Exists(addonExe)) { Serilog.Log.Information("[HOOKS] Not found, redownloading"); Download(addonDirectory, config.DoDalamudTest); } else { var versionInfo = FileVersionInfo.GetVersionInfo(addonExe); var version = versionInfo.ProductVersion; Serilog.Log.Information("[HOOKS] Hooks update check: local {0} remote {1}", version, remoteVersionInfo.AssemblyVersion); if (!remoteVersionInfo.AssemblyVersion.StartsWith(version)) { Download(addonDirectory, config.DoDalamudTest); } } } if (Repository.Ffxiv.GetVer(gamePath) != remoteVersionInfo.SupportedGameVer) { return; } if (!File.Exists(Path.Combine(addonDirectory, "EasyHook.dll")) || !File.Exists(Path.Combine(addonDirectory, "Dalamud.dll")) || !File.Exists(Path.Combine(addonDirectory, "Dalamud.Injector.exe"))) { MessageBox.Show( "Could not launch the in-game addon successfully. This might be caused by your antivirus.\n To prevent this, please add an exception for the folder \"%AppData%\\XIVLauncher\\addons\".", "XIVLauncher Error", MessageBoxButton.OK, MessageBoxImage.Error); Directory.Delete(addonDirectory, true); return; } var startInfo = new DalamudStartInfo { Language = language, PluginDirectory = ingamePluginPath, DefaultPluginDirectory = defaultPluginPath, ConfigurationPath = configPath, GameVersion = remoteVersionInfo.SupportedGameVer }; var parameters = Convert.ToBase64String(Encoding.UTF8.GetBytes(JsonConvert.SerializeObject(startInfo))); var process = new Process { StartInfo = { FileName = addonExe, WindowStyle = ProcessWindowStyle.Hidden, CreateNoWindow = true, Arguments = gameProcess.Id.ToString() + " " + parameters, WorkingDirectory = addonDirectory } }; process.Start(); Serilog.Log.Information("[HOOKS] Started dalamud! Staging: " + config.DoDalamudTest); // Reset security protocol after updating ServicePointManager.SecurityProtocol = SecurityProtocolType.SystemDefault; }
/// <summary> /// Initialize all Dalamud subsystems and start running on the main thread. /// </summary> /// <param name="info">The <see cref="DalamudStartInfo"/> containing information needed to initialize Dalamud.</param> private static void RunThread(DalamudStartInfo info) { if (EnvironmentConfiguration.DalamudWaitForDebugger) { while (!Debugger.IsAttached) { Thread.Sleep(100); } } // Setup logger var levelSwitch = InitLogging(info.WorkingDirectory); // Load configuration first to get some early persistent state, like log level var configuration = DalamudConfiguration.Load(info.ConfigurationPath); // Set the appropriate logging level from the configuration #if !DEBUG levelSwitch.MinimumLevel = configuration.LogLevel; #endif // Log any unhandled exception. AppDomain.CurrentDomain.UnhandledException += OnUnhandledException; TaskScheduler.UnobservedTaskException += OnUnobservedTaskException; var finishSignal = new ManualResetEvent(false); try { if (info.DelayInitializeMs > 0) { Log.Information(string.Format("Waiting for {0}ms before starting a session.", info.DelayInitializeMs)); Thread.Sleep(info.DelayInitializeMs); } Log.Information(new string('-', 80)); Log.Information("Initializing a session.."); // This is due to GitHub not supporting TLS 1.0, so we enable all TLS versions globally ServicePointManager.SecurityProtocol = SecurityProtocolType.Tls11 | SecurityProtocolType.Tls12 | SecurityProtocolType.Tls; if (!Util.IsLinux()) { InitSymbolHandler(info); } var dalamud = new Dalamud(info, levelSwitch, finishSignal, configuration); Log.Information("Starting a session.."); // Run session dalamud.LoadTier1(); dalamud.WaitForUnload(); dalamud.Dispose(); } catch (Exception ex) { Log.Fatal(ex, "Unhandled exception on main thread."); } finally { TaskScheduler.UnobservedTaskException -= OnUnobservedTaskException; AppDomain.CurrentDomain.UnhandledException -= OnUnhandledException; Log.Information("Session has ended."); Log.CloseAndFlush(); finishSignal.Set(); } }