mirror of
https://github.com/chylex/TweetDuck.git
synced 2024-11-25 05:42:47 +01:00
184 lines
5.9 KiB
C#
184 lines
5.9 KiB
C#
using System;
|
|
using System.ComponentModel;
|
|
using System.Diagnostics;
|
|
using System.IO;
|
|
using System.Threading;
|
|
using TweetDuck.Core.Utils;
|
|
|
|
namespace TweetDuck.Configuration{
|
|
sealed class LockManager{
|
|
private const int RetryDelay = 250;
|
|
|
|
public enum Result{
|
|
Success, HasProcess, Fail
|
|
}
|
|
|
|
private readonly string file;
|
|
private FileStream lockStream;
|
|
private Process lockingProcess;
|
|
|
|
public LockManager(string file){
|
|
this.file = file;
|
|
}
|
|
|
|
// Lock file
|
|
|
|
private bool ReleaseLockFileStream(){
|
|
if (lockStream != null){
|
|
lockStream.Dispose();
|
|
lockStream = null;
|
|
return true;
|
|
}
|
|
else{
|
|
return false;
|
|
}
|
|
}
|
|
|
|
private Result TryCreateLockFile(){
|
|
void CreateLockFileStream(){
|
|
lockStream = new FileStream(file, FileMode.Create, FileAccess.Write, FileShare.Read);
|
|
lockStream.Write(BitConverter.GetBytes(WindowsUtils.CurrentProcessID), 0, sizeof(int));
|
|
lockStream.Flush(true);
|
|
}
|
|
|
|
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;
|
|
}
|
|
}
|
|
|
|
// Lock management
|
|
|
|
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)){
|
|
byte[] bytes = new byte[sizeof(int)];
|
|
fileStream.Read(bytes, 0, bytes.Length);
|
|
pid = BitConverter.ToInt32(bytes, 0);
|
|
}
|
|
|
|
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 Result LockWait(int timeout){
|
|
for(int elapsed = 0; elapsed < timeout; elapsed += RetryDelay){
|
|
Result result = Lock();
|
|
|
|
if (result == Result.HasProcess){
|
|
Thread.Sleep(RetryDelay);
|
|
}
|
|
else{
|
|
return result;
|
|
}
|
|
}
|
|
|
|
return Lock();
|
|
}
|
|
|
|
public bool Unlock(){
|
|
if (ReleaseLockFileStream()){
|
|
try{
|
|
File.Delete(file);
|
|
}catch(Exception e){
|
|
Program.Reporter.Log(e.ToString());
|
|
return false;
|
|
}
|
|
}
|
|
|
|
return true;
|
|
}
|
|
|
|
// Locking process
|
|
|
|
public bool RestoreLockingProcess(int failTimeout){
|
|
if (lockingProcess != null && lockingProcess.MainWindowHandle == IntPtr.Zero){ // restore if the original process is in tray
|
|
NativeMethods.BroadcastMessage(Program.WindowRestoreMessage, (uint)lockingProcess.Id, 0);
|
|
|
|
if (WindowsUtils.TrySleepUntil(() => CheckLockingProcessExited() || (lockingProcess.MainWindowHandle != IntPtr.Zero && lockingProcess.Responding), failTimeout, RetryDelay)){
|
|
return true;
|
|
}
|
|
}
|
|
|
|
return false;
|
|
}
|
|
|
|
public bool CloseLockingProcess(int closeTimeout, int killTimeout){
|
|
if (lockingProcess != null){
|
|
try{
|
|
if (lockingProcess.CloseMainWindow()){
|
|
WindowsUtils.TrySleepUntil(CheckLockingProcessExited, closeTimeout, RetryDelay);
|
|
}
|
|
|
|
if (!lockingProcess.HasExited){
|
|
lockingProcess.Kill();
|
|
WindowsUtils.TrySleepUntil(CheckLockingProcessExited, killTimeout, RetryDelay);
|
|
}
|
|
|
|
if (lockingProcess.HasExited){
|
|
lockingProcess.Dispose();
|
|
lockingProcess = null;
|
|
return true;
|
|
}
|
|
}catch(Exception ex) when (ex is InvalidOperationException || ex is Win32Exception){
|
|
if (lockingProcess != null){
|
|
bool hasExited = CheckLockingProcessExited();
|
|
lockingProcess.Dispose();
|
|
return hasExited;
|
|
}
|
|
}
|
|
}
|
|
|
|
return false;
|
|
}
|
|
|
|
private bool CheckLockingProcessExited(){
|
|
lockingProcess.Refresh();
|
|
return lockingProcess.HasExited;
|
|
}
|
|
}
|
|
}
|