using System; using System.Diagnostics; using System.IO; using CefSharp; using CefSharp.WinForms; using TweetDuck.Application; using TweetDuck.Browser; using TweetDuck.Browser.Base; using TweetDuck.Browser.Handling; using TweetDuck.Configuration; using TweetDuck.Dialogs; using TweetDuck.Management; using TweetDuck.Updates; using TweetDuck.Utils; using TweetLib.Browser.CEF.Utils; using TweetLib.Core; using TweetLib.Core.Application; using TweetLib.Core.Features.Plugins; using TweetLib.Core.Features.Plugins.Config; using TweetLib.Core.Features.TweetDeck; using TweetLib.Core.Resources; using TweetLib.Core.Systems.Configuration; using TweetLib.Utils.Collections; using Win = System.Windows.Forms; namespace TweetDuck { static class Program { public const string BrandName = Lib.BrandName; public const string VersionTag = Version.Tag; public const string Website = "https://tweetduck.chylex.com"; private const string InstallerFolder = "TD_Updates"; private const string CefDataFolder = "TD_Chromium"; private const string ConsoleLogFile = "TD_Console.txt"; public static string ExecutablePath => Win.Application.ExecutablePath; private static Reporter errorReporter; private static LockManager lockManager; private static bool hasCleanedUp; public static ConfigObjects<UserConfig, SystemConfig> Config { get; private set; } internal static void SetupWinForms() { Win.Application.EnableVisualStyles(); Win.Application.SetCompatibleTextRenderingDefault(false); } [STAThread] private static void Main() { AppDomain.CurrentDomain.UnhandledException += OnUnhandledException; SetupWinForms(); Cef.EnableHighDPISupport(); var reporter = new Reporter(); Config = new ConfigObjects<UserConfig, SystemConfig>( new UserConfig(), new SystemConfig(), new PluginConfig(new string[] { "official/clear-columns", "official/reply-account" }) ); try { Lib.AppLauncher launch = Lib.Initialize(new AppBuilder { Setup = new Setup(), ErrorHandler = reporter, SystemHandler = new SystemHandler(), MessageDialogs = new MessageDialogs(), FileDialogs = new FileDialogs(), }); errorReporter = reporter; launch(); } catch (AppException e) { FormMessage.Error(e.Title, e.Message, FormMessage.OK); } } private sealed class Setup : IAppSetup { public bool IsPortable => File.Exists(Path.Combine(AppDomain.CurrentDomain.BaseDirectory, "makeportable")); public bool IsDebugLogging => Arguments.HasFlag(Arguments.ArgLogging); public string CustomDataFolder => Arguments.GetValue(Arguments.ArgDataFolder); public string ResourceRewriteRules => Arguments.GetValue(Arguments.ArgFreeze); public ConfigManager CreateConfigManager(string storagePath) { return new ConfigManager<UserConfig, SystemConfig>(storagePath, Config); } public bool TryLockDataFolder(string lockFile) { lockManager = new LockManager(lockFile); return lockManager.Lock(Arguments.HasFlag(Arguments.ArgRestart)); } public void BeforeLaunch() { if (Arguments.HasFlag(Arguments.ArgUpdated)) { WindowsUtils.TryDeleteFolderWhenAble(Path.Combine(App.StoragePath, InstallerFolder), 8000); WindowsUtils.TryDeleteFolderWhenAble(Path.Combine(App.StoragePath, "Service Worker"), 4000); BrowserCache.TryClearNow(); } if (Config.System.Migrate()) { Config.System.Save(); } } public void Launch(ResourceCache resourceCache, PluginManager pluginManager) { string storagePath = App.StoragePath; BrowserCache.RefreshTimer(); CefSharpSettings.WcfEnabled = false; CefSharpSettings.SubprocessExitIfParentProcessClosed = false; CefSettings settings = new CefSettings { UserAgent = BrowserUtils.UserAgentChrome, BrowserSubprocessPath = Path.Combine(App.ProgramPath, BrandName + ".Browser.exe"), CachePath = storagePath, UserDataPath = Path.Combine(storagePath, CefDataFolder), LogFile = Path.Combine(storagePath, ConsoleLogFile), #if !DEBUG LogSeverity = Arguments.HasFlag(Arguments.ArgLogging) ? LogSeverity.Info : LogSeverity.Disable #endif }; CefSchemeHandlerFactory.Register(settings, new TweetDuckSchemeHandler(resourceCache)); CefSchemeHandlerFactory.Register(settings, new PluginSchemeHandler(resourceCache, pluginManager)); CefUtils.ParseCommandLineArguments(Config.User.CustomCefArgs).ToDictionary(settings.CefCommandLineArgs); BrowserUtils.SetupCefArgs(settings.CefCommandLineArgs); Cef.Initialize(settings, false, new BrowserProcessHandler()); Win.Application.ApplicationExit += (sender, args) => ExitCleanup(); var updateCheckClient = new UpdateCheckClient(Path.Combine(storagePath, InstallerFolder)); var mainForm = new FormBrowser(resourceCache, pluginManager, updateCheckClient, lockManager.WindowRestoreMessage); Win.Application.Run(mainForm); if (mainForm.UpdateInstaller != null) { ExitCleanup(); if (mainForm.UpdateInstaller.Launch()) { Win.Application.Exit(); } else { RestartWithArgsInternal(Arguments.GetCurrentClean()); } } } } private static void OnUnhandledException(object sender, UnhandledExceptionEventArgs e) { if (e.ExceptionObject is Exception ex) { const string title = "TweetDuck Has Failed :("; string message = "An unhandled exception has occurred: " + ex.Message; if (errorReporter == null) { Debug.WriteLine(ex); Reporter.HandleEarlyFailure(title, message); } else { errorReporter.HandleException(title, message, false, ex); } } } public static void Restart() { RestartWithArgs(Arguments.GetCurrentClean()); } public static void RestartWithArgs(CommandLineArgs args) { FormBrowser browserForm = FormManager.TryFind<FormBrowser>(); if (browserForm != null) { browserForm.ForceClose(); ExitCleanup(); RestartWithArgsInternal(args); } } private static void RestartWithArgsInternal(CommandLineArgs args) { args.AddFlag(Arguments.ArgRestart); Process.Start(ExecutablePath, args.ToString()); Win.Application.Exit(); } private static void ExitCleanup() { if (hasCleanedUp) { return; } App.Close(); Cef.Shutdown(); BrowserCache.Exit(); lockManager.Unlock(); hasCleanedUp = true; } } }