mirror of
https://github.com/chylex/TweetDuck.git
synced 2024-11-14 17:42:47 +01:00
177 lines
5.5 KiB
C#
177 lines
5.5 KiB
C#
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;
|
|
}
|
|
}
|
|
}
|
|
}
|