mirror of
https://github.com/chylex/TweetDuck.git
synced 2025-05-29 20:34:05 +02:00
Rewrite lock system to be more reliable and handle exceptions better
This commit is contained in:
parent
3f0028913d
commit
648d1b9aa9
@ -5,6 +5,10 @@
|
|||||||
|
|
||||||
namespace TweetDck.Configuration{
|
namespace TweetDck.Configuration{
|
||||||
sealed class LockManager{
|
sealed class LockManager{
|
||||||
|
public enum Result{
|
||||||
|
Success, HasProcess, Fail
|
||||||
|
}
|
||||||
|
|
||||||
public Process LockingProcess { get; private set; }
|
public Process LockingProcess { get; private set; }
|
||||||
|
|
||||||
private readonly string file;
|
private readonly string file;
|
||||||
@ -14,87 +18,97 @@ public LockManager(string file){
|
|||||||
this.file = 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){
|
if (lockStream != null){
|
||||||
throw new InvalidOperationException("Lock file already exists.");
|
throw new InvalidOperationException("Lock file already exists.");
|
||||||
}
|
}
|
||||||
|
|
||||||
try{
|
try{
|
||||||
lockStream = new FileStream(file, FileMode.Create, FileAccess.Write, FileShare.Read);
|
CreateLockFileStream();
|
||||||
|
return Result.Success;
|
||||||
byte[] id = BitConverter.GetBytes(Process.GetCurrentProcess().Id);
|
}catch(DirectoryNotFoundException){
|
||||||
lockStream.Write(id, 0, id.Length);
|
try{
|
||||||
lockStream.Flush();
|
CreateLockFileStream();
|
||||||
|
return Result.Success;
|
||||||
if (LockingProcess != null){
|
}catch{
|
||||||
LockingProcess.Close();
|
ReleaseLockFileStream();
|
||||||
LockingProcess = null;
|
return Result.Fail;
|
||||||
}
|
}
|
||||||
|
}catch(IOException){
|
||||||
return true;
|
return Result.HasProcess;
|
||||||
}catch(Exception){
|
}catch{
|
||||||
if (lockStream != null){
|
ReleaseLockFileStream();
|
||||||
lockStream.Close();
|
return Result.Fail;
|
||||||
lockStream.Dispose();
|
|
||||||
}
|
|
||||||
|
|
||||||
return false;
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
public bool Lock(){
|
public Result Lock(){
|
||||||
if (lockStream != null)return true;
|
if (lockStream != null){
|
||||||
|
return Result.Success;
|
||||||
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;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
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(){
|
public bool Unlock(){
|
||||||
bool result = true;
|
bool result = true;
|
||||||
|
|
||||||
if (lockStream != null){
|
if (ReleaseLockFileStream()){
|
||||||
lockStream.Dispose();
|
|
||||||
|
|
||||||
try{
|
try{
|
||||||
File.Delete(file);
|
File.Delete(file);
|
||||||
}catch(Exception e){
|
}catch(Exception e){
|
||||||
Program.Reporter.Log(e.ToString());
|
Program.Reporter.Log(e.ToString());
|
||||||
result = false;
|
result = false;
|
||||||
}
|
}
|
||||||
|
|
||||||
lockStream = null;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
return result;
|
return result;
|
||||||
@ -104,11 +118,9 @@ public bool CloseLockingProcess(int timeout){
|
|||||||
if (LockingProcess != null){
|
if (LockingProcess != null){
|
||||||
LockingProcess.CloseMainWindow();
|
LockingProcess.CloseMainWindow();
|
||||||
|
|
||||||
for(int waited = 0; waited < timeout && !LockingProcess.HasExited;){
|
for(int waited = 0; waited < timeout && !LockingProcess.HasExited; waited += 250){
|
||||||
LockingProcess.Refresh();
|
LockingProcess.Refresh();
|
||||||
|
Thread.Sleep(250);
|
||||||
Thread.Sleep(100);
|
|
||||||
waited += 100;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
if (LockingProcess.HasExited){
|
if (LockingProcess.HasExited){
|
||||||
@ -120,5 +132,24 @@ public bool CloseLockingProcess(int timeout){
|
|||||||
|
|
||||||
return false;
|
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;
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
18
Program.cs
18
Program.cs
@ -64,9 +64,15 @@ private static void Main(){
|
|||||||
|
|
||||||
if (Args.HasFlag("-restart")){
|
if (Args.HasFlag("-restart")){
|
||||||
for(int attempt = 0; attempt < 21; attempt++){
|
for(int attempt = 0; attempt < 21; attempt++){
|
||||||
if (LockManager.Lock()){
|
LockManager.Result lockResult = LockManager.Lock();
|
||||||
|
|
||||||
|
if (lockResult == LockManager.Result.Success){
|
||||||
break;
|
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){
|
else if (attempt == 20){
|
||||||
using(FormMessage form = new FormMessage(BrandName+" Cannot Restart", BrandName+" is taking too long to close.", MessageBoxIcon.Warning)){
|
using(FormMessage form = new FormMessage(BrandName+" Cannot Restart", BrandName+" is taking too long to close.", MessageBoxIcon.Warning)){
|
||||||
form.AddButton("Exit");
|
form.AddButton("Exit");
|
||||||
@ -88,13 +94,15 @@ private static void Main(){
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
else{
|
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
|
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);
|
NativeMethods.SendMessage(NativeMethods.HWND_BROADCAST, WindowRestoreMessage, 0, IntPtr.Zero);
|
||||||
return;
|
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){
|
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);
|
MessageBox.Show("Could not close the other process.", BrandName+" Has Failed :(", MessageBoxButtons.OK, MessageBoxIcon.Error);
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
@ -103,6 +111,10 @@ private static void Main(){
|
|||||||
}
|
}
|
||||||
else return;
|
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();
|
ReloadConfig();
|
||||||
|
Loading…
Reference in New Issue
Block a user