using System; using System.ComponentModel; using System.Diagnostics; using System.IO; using TweetDuck.Core.Utils; namespace TweetDuck.Configuration{ sealed class LockManager{ public enum Result{ Success, HasProcess, Fail } public Process LockingProcess { get; private set; } private readonly string file; private FileStream lockStream; public LockManager(string file){ this.file = file; } private void CreateLockFileStream(){ lockStream = new FileStream(file, FileMode.Create, FileAccess.Write, FileShare.Read); WriteIntToStream(lockStream, GetCurrentProcessId()); lockStream.Flush(true); } private bool ReleaseLockFileStream(){ if (lockStream != null){ lockStream.Dispose(); lockStream = null; return true; } else{ return false; } } private Result TryCreateLockFile(){ if (lockStream != null){ throw new InvalidOperationException("Lock file already exists."); } try{ CreateLockFileStream(); return Result.Success; }catch(DirectoryNotFoundException){ try{ CreateLockFileStream(); return Result.Success; }catch{ ReleaseLockFileStream(); return Result.Fail; } }catch(IOException){ return Result.HasProcess; }catch{ ReleaseLockFileStream(); return Result.Fail; } } public Result Lock(){ if (lockStream != null){ return Result.Success; } Result initialResult = TryCreateLockFile(); if (initialResult == Result.HasProcess){ try{ int pid; using(FileStream fileStream = new FileStream(file, FileMode.Open, FileAccess.Read, FileShare.ReadWrite)){ pid = ReadIntFromStream(fileStream); } try{ Process foundProcess = Process.GetProcessById(pid); using(Process currentProcess = Process.GetCurrentProcess()){ if (foundProcess.MainModule.FileVersionInfo.InternalName == currentProcess.MainModule.FileVersionInfo.InternalName){ LockingProcess = foundProcess; } else{ foundProcess.Close(); } } }catch{ // GetProcessById throws ArgumentException if the process is missing // Process.MainModule can throw exceptions in some cases } return LockingProcess == null ? Result.Fail : Result.HasProcess; }catch{ return Result.Fail; } } return initialResult; } public bool Unlock(){ bool result = true; if (ReleaseLockFileStream()){ try{ File.Delete(file); }catch(Exception e){ Program.Reporter.Log(e.ToString()); result = false; } } return result; } public bool CloseLockingProcess(int closeTimeout, int killTimeout){ if (LockingProcess != null){ try{ if (LockingProcess.CloseMainWindow()){ WindowsUtils.TrySleepUntil(CheckLockingProcessExited, closeTimeout, 250); } if (!LockingProcess.HasExited){ LockingProcess.Kill(); WindowsUtils.TrySleepUntil(CheckLockingProcessExited, killTimeout, 250); } if (LockingProcess.HasExited){ LockingProcess.Dispose(); LockingProcess = null; return true; } }catch(Exception ex){ if (ex is InvalidOperationException || ex is Win32Exception){ if (LockingProcess != null){ LockingProcess.Refresh(); bool hasExited = LockingProcess.HasExited; LockingProcess.Dispose(); return hasExited; } } else throw; } } return false; } private bool CheckLockingProcessExited(){ LockingProcess.Refresh(); 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); } private static int GetCurrentProcessId(){ using(Process process = Process.GetCurrentProcess()){ return process.Id; } } } }