1
0
mirror of https://github.com/chylex/TweetDuck.git synced 2025-05-05 20:34:07 +02:00

Rewrite lock system to be more reliable and handle exceptions better

This commit is contained in:
chylex 2016-11-19 05:57:55 +01:00
parent 3f0028913d
commit 648d1b9aa9
2 changed files with 109 additions and 66 deletions

View File

@ -5,6 +5,10 @@
namespace TweetDck.Configuration{
sealed class LockManager{
public enum Result{
Success, HasProcess, Fail
}
public Process LockingProcess { get; private set; }
private readonly string file;
@ -14,87 +18,97 @@ public LockManager(string file){
this.file = file;
}
private bool CreateLockFile(){
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{
lockStream = new FileStream(file, FileMode.Create, FileAccess.Write, FileShare.Read);
byte[] id = BitConverter.GetBytes(Process.GetCurrentProcess().Id);
lockStream.Write(id, 0, id.Length);
lockStream.Flush();
if (LockingProcess != null){
LockingProcess.Close();
LockingProcess = null;
CreateLockFileStream();
return Result.Success;
}catch(DirectoryNotFoundException){
try{
CreateLockFileStream();
return Result.Success;
}catch{
ReleaseLockFileStream();
return Result.Fail;
}
return true;
}catch(Exception){
if (lockStream != null){
lockStream.Close();
lockStream.Dispose();
}
return false;
}catch(IOException){
return Result.HasProcess;
}catch{
ReleaseLockFileStream();
return Result.Fail;
}
}
public bool Lock(){
if (lockStream != null)return true;
try{
byte[] bytes = new byte[4];
using(FileStream fileStream = new FileStream(file, FileMode.Open, FileAccess.Read, FileShare.ReadWrite)){
fileStream.Read(bytes, 0, 4);
}
int pid = BitConverter.ToInt32(bytes, 0);
try{
Process foundProcess = Process.GetProcessById(pid);
using(Process currentProcess = Process.GetCurrentProcess()){
if (foundProcess.ProcessName == currentProcess.ProcessName || foundProcess.MainWindowTitle == Program.BrandName){
LockingProcess = foundProcess;
}
}
}catch(ArgumentException){}
return LockingProcess == null && CreateLockFile();
}catch(DirectoryNotFoundException){
string dir = Path.GetDirectoryName(file);
if (dir != null){
Directory.CreateDirectory(dir);
return CreateLockFile();
}
}catch(FileNotFoundException){
return CreateLockFile();
}catch(Exception){
return false;
public Result Lock(){
if (lockStream != null){
return Result.Success;
}
return false;
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 (lockStream != null){
lockStream.Dispose();
if (ReleaseLockFileStream()){
try{
File.Delete(file);
}catch(Exception e){
Program.Reporter.Log(e.ToString());
result = false;
}
lockStream = null;
}
return result;
@ -104,11 +118,9 @@ public bool CloseLockingProcess(int timeout){
if (LockingProcess != null){
LockingProcess.CloseMainWindow();
for(int waited = 0; waited < timeout && !LockingProcess.HasExited;){
for(int waited = 0; waited < timeout && !LockingProcess.HasExited; waited += 250){
LockingProcess.Refresh();
Thread.Sleep(100);
waited += 100;
Thread.Sleep(250);
}
if (LockingProcess.HasExited){
@ -120,5 +132,24 @@ public bool CloseLockingProcess(int timeout){
return false;
}
// 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;
}
}
}
}

View File

@ -64,9 +64,15 @@ private static void Main(){
if (Args.HasFlag("-restart")){
for(int attempt = 0; attempt < 21; attempt++){
if (LockManager.Lock()){
LockManager.Result lockResult = LockManager.Lock();
if (lockResult == LockManager.Result.Success){
break;
}
else if (lockResult == LockManager.Result.Fail){
MessageBox.Show("An unknown error occurred accessing the data folder. Please, make sure "+BrandName+" is not already running. If the problem persists, try restarting your system.", BrandName+" Has Failed :(", MessageBoxButtons.OK, MessageBoxIcon.Error);
return;
}
else if (attempt == 20){
using(FormMessage form = new FormMessage(BrandName+" Cannot Restart", BrandName+" is taking too long to close.", MessageBoxIcon.Warning)){
form.AddButton("Exit");
@ -88,13 +94,15 @@ private static void Main(){
}
}
else{
if (!LockManager.Lock()){
LockManager.Result lockResult = LockManager.Lock();
if (lockResult == LockManager.Result.HasProcess){
if (LockManager.LockingProcess.MainWindowHandle == IntPtr.Zero && LockManager.LockingProcess.Responding){ // restore if the original process is in tray
NativeMethods.SendMessage(NativeMethods.HWND_BROADCAST, WindowRestoreMessage, 0, IntPtr.Zero);
return;
}
else if (MessageBox.Show("Another instance of "+BrandName+" is already running.\r\nDo you want to close it?", BrandName+" is Already Running", MessageBoxButtons.YesNo, MessageBoxIcon.Error, MessageBoxDefaultButton.Button2) == DialogResult.Yes){
if (!LockManager.CloseLockingProcess(20000)){
if (!LockManager.CloseLockingProcess(30000)){
MessageBox.Show("Could not close the other process.", BrandName+" Has Failed :(", MessageBoxButtons.OK, MessageBoxIcon.Error);
return;
}
@ -103,6 +111,10 @@ private static void Main(){
}
else return;
}
else if (lockResult != LockManager.Result.Success){
MessageBox.Show("An unknown error occurred accessing the data folder. Please, make sure "+BrandName+" is not already running. If the problem persists, try restarting your system.", BrandName+" Has Failed :(", MessageBoxButtons.OK, MessageBoxIcon.Error);
return;
}
}
ReloadConfig();