mirror of
https://github.com/chylex/TweetDuck.git
synced 2024-11-14 08:42:46 +01:00
279 lines
13 KiB
C#
279 lines
13 KiB
C#
using CefSharp;
|
|
using System;
|
|
using System.Diagnostics;
|
|
using System.IO;
|
|
using System.Linq;
|
|
using System.Threading;
|
|
using System.Windows.Forms;
|
|
using TweetDuck.Configuration;
|
|
using TweetDuck.Core;
|
|
using TweetDuck.Core.Handling;
|
|
using TweetDuck.Core.Other;
|
|
using TweetDuck.Core.Other.Settings.Export;
|
|
using TweetDuck.Core.Utils;
|
|
using TweetDuck.Plugins;
|
|
using TweetDuck.Plugins.Events;
|
|
using TweetDuck.Updates;
|
|
|
|
namespace TweetDuck{
|
|
static class Program{
|
|
public const string BrandName = "TweetDuck";
|
|
public const string Website = "https://tweetduck.chylex.com";
|
|
|
|
public const string VersionTag = "1.8";
|
|
public const string VersionFull = "1.8.0.0";
|
|
|
|
public static readonly Version Version = new Version(VersionTag);
|
|
public static readonly bool IsPortable = File.Exists("makeportable");
|
|
|
|
public static readonly string ProgramPath = AppDomain.CurrentDomain.BaseDirectory;
|
|
public static readonly string StoragePath = IsPortable ? Path.Combine(ProgramPath, "portable", "storage") : GetDataStoragePath();
|
|
|
|
public static readonly string ScriptPath = Path.Combine(ProgramPath, "scripts");
|
|
public static readonly string PluginPath = Path.Combine(ProgramPath, "plugins");
|
|
|
|
public static readonly string UserConfigFilePath = Path.Combine(StoragePath, "TD_UserConfig.cfg");
|
|
public static readonly string SystemConfigFilePath = Path.Combine(StoragePath, "TD_SystemConfig.cfg");
|
|
public static readonly string PluginConfigFilePath = Path.Combine(StoragePath, "TD_PluginConfig.cfg");
|
|
|
|
public static readonly string PluginDataPath = Path.Combine(StoragePath, "TD_Plugins");
|
|
private static readonly string InstallerPath = Path.Combine(StoragePath, "TD_Updates");
|
|
|
|
private static string ErrorLogFilePath => Path.Combine(StoragePath, "TD_Log.txt");
|
|
private static string ConsoleLogFilePath => Path.Combine(StoragePath, "TD_Console.txt");
|
|
|
|
public static uint WindowRestoreMessage;
|
|
|
|
private static readonly LockManager LockManager = new LockManager(Path.Combine(StoragePath, ".lock"));
|
|
private static bool HasCleanedUp;
|
|
|
|
public static UserConfig UserConfig { get; private set; }
|
|
public static SystemConfig SystemConfig { get; private set; }
|
|
public static Reporter Reporter { get; private set; }
|
|
|
|
public static event EventHandler UserConfigReplaced;
|
|
|
|
[STAThread]
|
|
private static void Main(){
|
|
Application.EnableVisualStyles();
|
|
Application.SetCompatibleTextRenderingDefault(false);
|
|
|
|
WindowRestoreMessage = NativeMethods.RegisterWindowMessage("TweetDuckRestore");
|
|
|
|
if (!WindowsUtils.CheckFolderWritePermission(StoragePath)){
|
|
MessageBox.Show(BrandName+" does not have write permissions to the storage folder: "+StoragePath, "Permission Error", MessageBoxButtons.OK, MessageBoxIcon.Warning);
|
|
return;
|
|
}
|
|
|
|
Reporter = new Reporter(ErrorLogFilePath);
|
|
Reporter.SetupUnhandledExceptionHandler(BrandName+" Has Failed :(");
|
|
|
|
if (Arguments.HasFlag(Arguments.ArgRestart)){
|
|
for(int attempt = 0; attempt < 21; attempt++){
|
|
LockManager.Result lockResult = LockManager.Lock();
|
|
|
|
if (lockResult == LockManager.Result.Success){
|
|
break;
|
|
}
|
|
else if (lockResult == LockManager.Result.Fail){
|
|
MessageBox.Show("An unknown error occurred accessing the data folder. Please, make sure "+BrandName+" is not already running. If the problem persists, try restarting your system.", BrandName+" Has Failed :(", MessageBoxButtons.OK, MessageBoxIcon.Error);
|
|
return;
|
|
}
|
|
else if (attempt == 20){
|
|
using(FormMessage form = new FormMessage(BrandName+" Cannot Restart", BrandName+" is taking too long to close.", MessageBoxIcon.Warning)){
|
|
form.CancelButton = form.AddButton("Exit");
|
|
form.ActiveControl = form.AddButton("Retry", DialogResult.Retry);
|
|
|
|
if (form.ShowDialog() == DialogResult.Retry){
|
|
attempt /= 2;
|
|
continue;
|
|
}
|
|
|
|
return;
|
|
}
|
|
}
|
|
else Thread.Sleep(500);
|
|
}
|
|
}
|
|
else{
|
|
LockManager.Result lockResult = LockManager.Lock();
|
|
|
|
if (lockResult == LockManager.Result.HasProcess){
|
|
if (LockManager.LockingProcess.MainWindowHandle == IntPtr.Zero){ // restore if the original process is in tray
|
|
NativeMethods.SendMessage(NativeMethods.HWND_BROADCAST, WindowRestoreMessage, LockManager.LockingProcess.Id, IntPtr.Zero);
|
|
|
|
if (WindowsUtils.TrySleepUntil(() => {
|
|
LockManager.LockingProcess.Refresh();
|
|
return LockManager.LockingProcess.HasExited || (LockManager.LockingProcess.MainWindowHandle != IntPtr.Zero && LockManager.LockingProcess.Responding);
|
|
}, 2000, 250)){
|
|
return; // should trigger on first attempt if succeeded, but wait just in case
|
|
}
|
|
}
|
|
|
|
if (MessageBox.Show("Another instance of "+BrandName+" is already running.\r\nDo you want to close it?", BrandName+" is Already Running", MessageBoxButtons.YesNo, MessageBoxIcon.Error, MessageBoxDefaultButton.Button2) == DialogResult.Yes){
|
|
if (!LockManager.CloseLockingProcess(10000, 5000)){
|
|
MessageBox.Show("Could not close the other process.", BrandName+" Has Failed :(", MessageBoxButtons.OK, MessageBoxIcon.Error);
|
|
return;
|
|
}
|
|
|
|
LockManager.Lock();
|
|
}
|
|
else return;
|
|
}
|
|
else if (lockResult != LockManager.Result.Success){
|
|
MessageBox.Show("An unknown error occurred accessing the data folder. Please, make sure "+BrandName+" is not already running. If the problem persists, try restarting your system.", BrandName+" Has Failed :(", MessageBoxButtons.OK, MessageBoxIcon.Error);
|
|
return;
|
|
}
|
|
}
|
|
|
|
ReloadConfig();
|
|
SystemConfig = SystemConfig.Load(SystemConfigFilePath);
|
|
|
|
if (Arguments.HasFlag(Arguments.ArgImportCookies)){
|
|
ExportManager.ImportCookies();
|
|
}
|
|
|
|
if (Arguments.HasFlag(Arguments.ArgUpdated)){
|
|
WindowsUtils.TryDeleteFolderWhenAble(InstallerPath, 8000);
|
|
}
|
|
|
|
CefSharpSettings.WcfEnabled = false;
|
|
|
|
CefSettings settings = new CefSettings{
|
|
AcceptLanguageList = BrowserUtils.HeaderAcceptLanguage,
|
|
UserAgent = BrowserUtils.HeaderUserAgent,
|
|
Locale = Arguments.GetValue(Arguments.ArgLocale, string.Empty),
|
|
BrowserSubprocessPath = BrandName+".Browser.exe",
|
|
CachePath = StoragePath,
|
|
LogFile = ConsoleLogFilePath,
|
|
#if !DEBUG
|
|
LogSeverity = Arguments.HasFlag(Arguments.ArgLogging) ? LogSeverity.Info : LogSeverity.Disable
|
|
#endif
|
|
};
|
|
|
|
CommandLineArgsParser.ReadCefArguments(UserConfig.CustomCefArgs).ToDictionary(settings.CefCommandLineArgs);
|
|
|
|
if (!SystemConfig.HardwareAcceleration){
|
|
settings.CefCommandLineArgs["disable-gpu"] = "1";
|
|
settings.CefCommandLineArgs["disable-gpu-vsync"] = "1";
|
|
}
|
|
|
|
settings.CefCommandLineArgs["disable-extensions"] = "1";
|
|
settings.CefCommandLineArgs["disable-plugins-discovery"] = "1";
|
|
settings.CefCommandLineArgs["enable-system-flash"] = "0";
|
|
|
|
Cef.Initialize(settings, false, new BrowserProcessHandler());
|
|
|
|
Application.ApplicationExit += (sender, args) => ExitCleanup();
|
|
|
|
PluginManager plugins = new PluginManager(PluginPath, PluginConfigFilePath);
|
|
plugins.Reloaded += plugins_Reloaded;
|
|
plugins.Executed += plugins_Executed;
|
|
plugins.Reload();
|
|
|
|
FormBrowser mainForm = new FormBrowser(plugins, new UpdaterSettings{
|
|
AllowPreReleases = Arguments.HasFlag(Arguments.ArgDebugUpdates),
|
|
DismissedUpdate = UserConfig.DismissedUpdate,
|
|
InstallerDownloadFolder = InstallerPath
|
|
});
|
|
|
|
Application.Run(mainForm);
|
|
|
|
if (mainForm.UpdateInstallerPath != null){
|
|
ExitCleanup();
|
|
|
|
// ProgramPath has a trailing backslash
|
|
string updaterArgs = "/SP- /SILENT /CLOSEAPPLICATIONS /UPDATEPATH=\""+ProgramPath+"\" /RUNARGS=\""+Arguments.GetCurrentForInstallerCmd()+"\""+(IsPortable ? " /PORTABLE=1" : "");
|
|
bool runElevated = !IsPortable || !WindowsUtils.CheckFolderWritePermission(ProgramPath);
|
|
|
|
WindowsUtils.StartProcess(mainForm.UpdateInstallerPath, updaterArgs, runElevated);
|
|
Application.Exit();
|
|
}
|
|
}
|
|
|
|
private static void plugins_Reloaded(object sender, PluginErrorEventArgs e){
|
|
if (e.HasErrors){
|
|
string doubleNL = Environment.NewLine+Environment.NewLine;
|
|
MessageBox.Show("The following plugins will not be available until the issues are resolved:"+doubleNL+string.Join(doubleNL, e.Errors), "Error Loading Plugins", MessageBoxButtons.OK, MessageBoxIcon.Error);
|
|
}
|
|
}
|
|
|
|
private static void plugins_Executed(object sender, PluginErrorEventArgs e){
|
|
if (e.HasErrors){
|
|
string doubleNL = Environment.NewLine+Environment.NewLine;
|
|
MessageBox.Show("Failed to execute the following plugins:"+doubleNL+string.Join(doubleNL, e.Errors), "Error Executing Plugins", MessageBoxButtons.OK, MessageBoxIcon.Error);
|
|
}
|
|
}
|
|
|
|
private static string GetDataStoragePath(){
|
|
string custom = Arguments.GetValue(Arguments.ArgDataFolder, null);
|
|
|
|
if (custom != null && (custom.Contains(Path.DirectorySeparatorChar) || custom.Contains(Path.AltDirectorySeparatorChar))){
|
|
if (Path.GetInvalidPathChars().Any(custom.Contains)){
|
|
Reporter.HandleEarlyFailure("Data Folder Invalid", "The data folder contains invalid characters:\n"+custom);
|
|
}
|
|
else if (!Path.IsPathRooted(custom)){
|
|
Reporter.HandleEarlyFailure("Data Folder Invalid", "The data folder has to be either a simple folder name, or a full path:\n"+custom);
|
|
}
|
|
|
|
return Environment.ExpandEnvironmentVariables(custom);
|
|
}
|
|
else{
|
|
return Path.Combine(Environment.GetFolderPath(Environment.SpecialFolder.LocalApplicationData), custom ?? BrandName);
|
|
}
|
|
}
|
|
|
|
public static void ReloadConfig(){
|
|
UserConfig = UserConfig.Load(UserConfigFilePath);
|
|
UserConfigReplaced?.Invoke(UserConfig, new EventArgs());
|
|
}
|
|
|
|
public static void ResetConfig(){
|
|
try{
|
|
File.Delete(UserConfigFilePath);
|
|
File.Delete(UserConfig.GetBackupFile(UserConfigFilePath));
|
|
}catch(Exception e){
|
|
Reporter.HandleException("Configuration Reset Error", "Could not delete configuration files to reset the options.", true, e);
|
|
return;
|
|
}
|
|
|
|
ReloadConfig();
|
|
}
|
|
|
|
public static void Restart(){
|
|
Restart(new string[0]);
|
|
}
|
|
|
|
public static void Restart(string[] extraArgs){
|
|
CommandLineArgs args = Arguments.GetCurrentClean();
|
|
CommandLineArgs.ReadStringArray('-', extraArgs, args);
|
|
RestartWithArgs(args);
|
|
}
|
|
|
|
public static void RestartWithArgs(CommandLineArgs args){
|
|
FormBrowser browserForm = Application.OpenForms.OfType<FormBrowser>().FirstOrDefault();
|
|
if (browserForm == null)return;
|
|
|
|
args.AddFlag(Arguments.ArgRestart);
|
|
|
|
browserForm.ForceClose();
|
|
ExitCleanup();
|
|
|
|
Process.Start(Application.ExecutablePath, args.ToString());
|
|
Application.Exit();
|
|
}
|
|
|
|
private static void ExitCleanup(){
|
|
if (HasCleanedUp)return;
|
|
|
|
UserConfig.Save();
|
|
|
|
Cef.Shutdown();
|
|
BrowserCache.Exit();
|
|
|
|
LockManager.Unlock();
|
|
HasCleanedUp = true;
|
|
}
|
|
}
|
|
}
|