mirror of
https://github.com/chylex/TweetDuck.git
synced 2025-01-15 05:42:46 +01:00
129 lines
3.0 KiB
C#
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);
|
|
}
|
|
}
|
|
}
|
|
}
|