1
0
mirror of https://github.com/chylex/TweetDuck.git synced 2025-01-15 05:42:46 +01:00
TweetDuck/lib/TweetLib.Utils/Startup/LockFile.cs

129 lines
3.0 KiB
C#

using System;
using System.Diagnostics;
using System.IO;
using System.Threading;
using TweetLib.Utils.Static;
namespace TweetLib.Utils.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 UnlockResult Unlock() {
if (ReleaseLockFileStream()) {
try {
File.Delete(path);
} catch (Exception e) {
return new UnlockResult.Fail(e);
}
}
return UnlockResult.Success;
}
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)];
if (fileStream.Read(bytes, 0, bytes.Length) != bytes.Length) {
throw new IOException("Read fewer bytes than requested!");
}
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);
}
}
}
}