1
0
mirror of https://github.com/chylex/TweetDuck.git synced 2025-05-24 05:34:06 +02:00

Refactor Program (tweak properties, move locking code)

This commit is contained in:
chylex 2017-07-31 18:04:04 +02:00
parent 5f44a1f4ad
commit c686349922
2 changed files with 78 additions and 77 deletions

View File

@ -2,28 +2,26 @@
using System.ComponentModel; using System.ComponentModel;
using System.Diagnostics; using System.Diagnostics;
using System.IO; using System.IO;
using System.Threading;
using TweetDuck.Core.Utils; using TweetDuck.Core.Utils;
namespace TweetDuck.Configuration{ namespace TweetDuck.Configuration{
sealed class LockManager{ sealed class LockManager{
private const int RetryDelay = 250;
public enum Result{ public enum Result{
Success, HasProcess, Fail Success, HasProcess, Fail
} }
public Process LockingProcess { get; private set; }
private readonly string file; private readonly string file;
private FileStream lockStream; private FileStream lockStream;
private Process lockingProcess;
public LockManager(string file){ public LockManager(string file){
this.file = file; this.file = file;
} }
private void CreateLockFileStream(){ // Lock file
lockStream = new FileStream(file, FileMode.Create, FileAccess.Write, FileShare.Read);
WriteIntToStream(lockStream, WindowsUtils.CurrentProcessID);
lockStream.Flush(true);
}
private bool ReleaseLockFileStream(){ private bool ReleaseLockFileStream(){
if (lockStream != null){ if (lockStream != null){
@ -37,8 +35,10 @@ private bool ReleaseLockFileStream(){
} }
private Result TryCreateLockFile(){ private Result TryCreateLockFile(){
if (lockStream != null){ void CreateLockFileStream(){
throw new InvalidOperationException("Lock file already exists."); lockStream = new FileStream(file, FileMode.Create, FileAccess.Write, FileShare.Read);
lockStream.Write(BitConverter.GetBytes(WindowsUtils.CurrentProcessID), 0, sizeof(int));
lockStream.Flush(true);
} }
try{ try{
@ -60,6 +60,8 @@ private Result TryCreateLockFile(){
} }
} }
// Lock management
public Result Lock(){ public Result Lock(){
if (lockStream != null){ if (lockStream != null){
return Result.Success; return Result.Success;
@ -72,7 +74,9 @@ public Result Lock(){
int pid; int pid;
using(FileStream fileStream = new FileStream(file, FileMode.Open, FileAccess.Read, FileShare.ReadWrite)){ using(FileStream fileStream = new FileStream(file, FileMode.Open, FileAccess.Read, FileShare.ReadWrite)){
pid = ReadIntFromStream(fileStream); byte[] bytes = new byte[sizeof(int)];
fileStream.Read(bytes, 0, bytes.Length);
pid = BitConverter.ToInt32(bytes, 0);
} }
try{ try{
@ -80,7 +84,7 @@ public Result Lock(){
using(Process currentProcess = Process.GetCurrentProcess()){ using(Process currentProcess = Process.GetCurrentProcess()){
if (foundProcess.MainModule.FileVersionInfo.InternalName == currentProcess.MainModule.FileVersionInfo.InternalName){ if (foundProcess.MainModule.FileVersionInfo.InternalName == currentProcess.MainModule.FileVersionInfo.InternalName){
LockingProcess = foundProcess; lockingProcess = foundProcess;
} }
else{ else{
foundProcess.Close(); foundProcess.Close();
@ -91,7 +95,7 @@ public Result Lock(){
// Process.MainModule can throw exceptions in some cases // Process.MainModule can throw exceptions in some cases
} }
return LockingProcess == null ? Result.Fail : Result.HasProcess; return lockingProcess == null ? Result.Fail : Result.HasProcess;
}catch{ }catch{
return Result.Fail; return Result.Fail;
} }
@ -100,45 +104,72 @@ public Result Lock(){
return initialResult; return initialResult;
} }
public bool Unlock(){ public Result LockWait(int timeout){
bool result = true; for(int elapsed = 0; elapsed < timeout; elapsed += RetryDelay){
Result result = Lock();
if (result == Result.HasProcess){
Thread.Sleep(RetryDelay);
}
else{
return result;
}
}
return Lock();
}
public bool Unlock(){
if (ReleaseLockFileStream()){ if (ReleaseLockFileStream()){
try{ try{
File.Delete(file); File.Delete(file);
}catch(Exception e){ }catch(Exception e){
Program.Reporter.Log(e.ToString()); Program.Reporter.Log(e.ToString());
result = false; return false;
} }
} }
return result; return true;
}
// Locking process
public bool RestoreLockingProcess(int failTimeout){
if (lockingProcess != null){
if (lockingProcess.MainWindowHandle == IntPtr.Zero){ // restore if the original process is in tray
NativeMethods.PostMessage(NativeMethods.HWND_BROADCAST, Program.WindowRestoreMessage, new UIntPtr((uint)lockingProcess.Id), IntPtr.Zero);
if (WindowsUtils.TrySleepUntil(() => CheckLockingProcessExited() || (lockingProcess.MainWindowHandle != IntPtr.Zero && lockingProcess.Responding), failTimeout, RetryDelay)){
return true;
}
}
}
return false;
} }
public bool CloseLockingProcess(int closeTimeout, int killTimeout){ public bool CloseLockingProcess(int closeTimeout, int killTimeout){
if (LockingProcess != null){ if (lockingProcess != null){
try{ try{
if (LockingProcess.CloseMainWindow()){ if (lockingProcess.CloseMainWindow()){
WindowsUtils.TrySleepUntil(CheckLockingProcessExited, closeTimeout, 250); WindowsUtils.TrySleepUntil(CheckLockingProcessExited, closeTimeout, RetryDelay);
} }
if (!LockingProcess.HasExited){ if (!lockingProcess.HasExited){
LockingProcess.Kill(); lockingProcess.Kill();
WindowsUtils.TrySleepUntil(CheckLockingProcessExited, killTimeout, 250); WindowsUtils.TrySleepUntil(CheckLockingProcessExited, killTimeout, RetryDelay);
} }
if (LockingProcess.HasExited){ if (lockingProcess.HasExited){
LockingProcess.Dispose(); lockingProcess.Dispose();
LockingProcess = null; lockingProcess = null;
return true; return true;
} }
}catch(Exception ex){ }catch(Exception ex){
if (ex is InvalidOperationException || ex is Win32Exception){ if (ex is InvalidOperationException || ex is Win32Exception){
if (LockingProcess != null){ if (lockingProcess != null){
LockingProcess.Refresh(); bool hasExited = CheckLockingProcessExited();
lockingProcess.Dispose();
bool hasExited = LockingProcess.HasExited;
LockingProcess.Dispose();
return hasExited; return hasExited;
} }
} }
@ -150,21 +181,8 @@ public bool CloseLockingProcess(int closeTimeout, int killTimeout){
} }
private bool CheckLockingProcessExited(){ private bool CheckLockingProcessExited(){
LockingProcess.Refresh(); lockingProcess.Refresh();
return LockingProcess.HasExited; return lockingProcess.HasExited;
}
// Utility functions
private static void WriteIntToStream(Stream stream, int value){
byte[] id = BitConverter.GetBytes(value);
stream.Write(id, 0, id.Length);
}
private static int ReadIntFromStream(Stream stream){
byte[] bytes = new byte[4];
stream.Read(bytes, 0, 4);
return BitConverter.ToInt32(bytes, 0);
} }
} }
} }

View File

@ -34,13 +34,13 @@ static class Program{
public static readonly string ScriptPath = Path.Combine(ProgramPath, "scripts"); public static readonly string ScriptPath = Path.Combine(ProgramPath, "scripts");
public static readonly string PluginPath = Path.Combine(ProgramPath, "plugins"); 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"); public static readonly string PluginDataPath = Path.Combine(StoragePath, "TD_Plugins");
private static readonly string InstallerPath = Path.Combine(StoragePath, "TD_Updates"); private static readonly string InstallerPath = Path.Combine(StoragePath, "TD_Updates");
public static string UserConfigFilePath => Path.Combine(StoragePath, "TD_UserConfig.cfg");
public static string SystemConfigFilePath => Path.Combine(StoragePath, "TD_SystemConfig.cfg");
public static string PluginConfigFilePath => Path.Combine(StoragePath, "TD_PluginConfig.cfg");
private static string ErrorLogFilePath => Path.Combine(StoragePath, "TD_Log.txt"); private static string ErrorLogFilePath => Path.Combine(StoragePath, "TD_Log.txt");
private static string ConsoleLogFilePath => Path.Combine(StoragePath, "TD_Console.txt"); private static string ConsoleLogFilePath => Path.Combine(StoragePath, "TD_Console.txt");
@ -80,53 +80,36 @@ private static void Main(){
} }
if (Arguments.HasFlag(Arguments.ArgRestart)){ if (Arguments.HasFlag(Arguments.ArgRestart)){
for(int attempt = 0; attempt < 21; attempt++){ LockManager.Result lockResult = LockManager.LockWait(10000);
LockManager.Result lockResult = LockManager.Lock();
if (lockResult == LockManager.Result.Success){ while(lockResult != LockManager.Result.Success){
break; if (lockResult == LockManager.Result.Fail){
}
else if (lockResult == LockManager.Result.Fail){
FormMessage.Error("TweetDuck Has Failed :(", "An unknown error occurred accessing the data folder. Please, make sure TweetDuck is not already running. If the problem persists, try restarting your system.", FormMessage.OK); FormMessage.Error("TweetDuck Has Failed :(", "An unknown error occurred accessing the data folder. Please, make sure TweetDuck is not already running. If the problem persists, try restarting your system.", FormMessage.OK);
return; return;
} }
else if (attempt == 20){ else if (!FormMessage.Warning("TweetDuck Cannot Restart", "TweetDuck is taking too long to close.", FormMessage.Retry, FormMessage.Exit)){
if (FormMessage.Warning("TweetDuck Cannot Restart", "TweetDuck is taking too long to close.", FormMessage.Retry, FormMessage.Exit)){
attempt /= 2;
continue;
}
return; return;
} }
else Thread.Sleep(500);
lockResult = LockManager.LockWait(5000);
} }
} }
else{ else{
LockManager.Result lockResult = LockManager.Lock(); LockManager.Result lockResult = LockManager.Lock();
if (lockResult == LockManager.Result.HasProcess){ if (lockResult == LockManager.Result.HasProcess){
if (LockManager.LockingProcess.MainWindowHandle == IntPtr.Zero){ // restore if the original process is in tray if (!LockManager.RestoreLockingProcess(2000) && FormMessage.Error("TweetDuck is Already Running", "Another instance of TweetDuck is already running.\nDo you want to close it?", FormMessage.Yes, FormMessage.No)){
NativeMethods.PostMessage(NativeMethods.HWND_BROADCAST, WindowRestoreMessage, new UIntPtr((uint)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;
}
}
if (FormMessage.Error("TweetDuck is Already Running", "Another instance of TweetDuck is already running.\nDo you want to close it?", FormMessage.Yes, FormMessage.No)){
if (!LockManager.CloseLockingProcess(10000, 5000)){ if (!LockManager.CloseLockingProcess(10000, 5000)){
FormMessage.Error("TweetDuck Has Failed :(", "Could not close the other process.", FormMessage.OK); FormMessage.Error("TweetDuck Has Failed :(", "Could not close the other process.", FormMessage.OK);
return; return;
} }
LockManager.Lock(); lockResult = LockManager.Lock();
} }
else return; else return;
} }
else if (lockResult != LockManager.Result.Success){
if (lockResult != LockManager.Result.Success){
FormMessage.Error("TweetDuck Has Failed :(", "An unknown error occurred accessing the data folder. Please, make sure TweetDuck is not already running. If the problem persists, try restarting your system.", FormMessage.OK); FormMessage.Error("TweetDuck Has Failed :(", "An unknown error occurred accessing the data folder. Please, make sure TweetDuck is not already running. If the problem persists, try restarting your system.", FormMessage.OK);
return; return;
} }