mirror of
https://github.com/chylex/TweetDuck.git
synced 2025-08-25 23:35:16 +02:00
.github
.idea
bld
lib
TweetLib.Browser
TweetLib.Browser.CEF
TweetLib.Communication
TweetLib.Core
TweetLib.Utils
Collections
Data
Dialogs
Globalization
IO
Serialization
Startup
LockFile.cs
LockResult.cs
UnlockResult.cs
Static
Lib.cs
TweetLib.Utils.csproj
TweetTest.Browser.CEF
TweetTest.Core
TweetTest.Utils
linux
resources
windows
.gitattributes
.gitignore
LICENSE.md
README.md
TweetDuck.sln
TweetDuck.sln.DotSettings
Version.cs
global.json
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);
|
|
}
|
|
}
|
|
}
|
|
}
|