using System; using System.Diagnostics; using System.IO; using System.Threading; using TweetLib.Core.Utils; namespace TweetLib.Core.Systems.Startup{ public sealed class LockFile{ private static int CurrentProcessID{ get{ using Process me = Process.GetCurrentProcess(); return me.Id; } } private readonly string path; private FileStream? stream; public LockFile(string path){ this.path = path; } private void CreateLockFileStream(){ stream = new FileStream(path, FileMode.Create, FileAccess.Write, FileShare.Read); stream.Write(BitConverter.GetBytes(CurrentProcessID), 0, sizeof(int)); stream.Flush(true); } private bool ReleaseLockFileStream(){ if (stream != null){ stream.Dispose(); stream = null; return true; } else{ return false; } } public LockResult Lock(){ if (stream != null){ return LockResult.Success; } try{ CreateLockFileStream(); return LockResult.Success; }catch(DirectoryNotFoundException){ try{ FileUtils.CreateDirectoryForFile(path); CreateLockFileStream(); return LockResult.Success; }catch(Exception e){ ReleaseLockFileStream(); return new LockResult.Fail(e); } }catch(IOException e){ return DetermineLockingProcessOrFail(e); }catch(Exception e){ ReleaseLockFileStream(); return new LockResult.Fail(e); } } public LockResult LockWait(int timeout, int retryDelay){ for(int elapsed = 0; elapsed < timeout; elapsed += retryDelay){ var result = Lock(); if (result is LockResult.HasProcess info){ info.Dispose(); Thread.Sleep(retryDelay); } else{ return result; } } return Lock(); } public bool Unlock(){ if (ReleaseLockFileStream()){ try{ File.Delete(path); }catch(Exception e){ App.ErrorHandler.Log(e.ToString()); return false; } } return true; } private LockResult DetermineLockingProcessOrFail(Exception originalException){ try{ int pid; using(var fileStream = new FileStream(path, FileMode.Open, FileAccess.Read, FileShare.ReadWrite)){ byte[] bytes = new byte[sizeof(int)]; fileStream.Read(bytes, 0, bytes.Length); pid = BitConverter.ToInt32(bytes, 0); } try{ var foundProcess = Process.GetProcessById(pid); using var currentProcess = Process.GetCurrentProcess(); if (currentProcess.MainModule.FileVersionInfo.InternalName == foundProcess.MainModule.FileVersionInfo.InternalName){ return new LockResult.HasProcess(foundProcess); } else{ foundProcess.Close(); } }catch{ // GetProcessById throws ArgumentException if the process is missing // Process.MainModule can throw exceptions in some cases } return new LockResult.Fail(originalException); }catch(Exception e){ return new LockResult.Fail(e); } } } }