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:
parent
5f44a1f4ad
commit
c686349922
@ -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);
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
47
Program.cs
47
Program.cs
@ -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;
|
||||||
}
|
}
|
||||||
|
Loading…
Reference in New Issue
Block a user