mirror of
https://github.com/chylex/TweetDuck.git
synced 2025-05-31 08:34:10 +02:00
Move LockManager to TweetLib.Core & remove WindowsUtils.CurrentProcessID
This commit is contained in:
parent
a6963a18d4
commit
bbb7907e54
@ -1,4 +1,5 @@
|
|||||||
using System;
|
using System;
|
||||||
|
using System.Diagnostics;
|
||||||
using System.Drawing;
|
using System.Drawing;
|
||||||
using System.Threading.Tasks;
|
using System.Threading.Tasks;
|
||||||
using System.Windows.Forms;
|
using System.Windows.Forms;
|
||||||
@ -333,7 +334,9 @@ private void updateBridge_UpdateDismissed(object sender, UpdateInfo update){
|
|||||||
|
|
||||||
protected override void WndProc(ref Message m){
|
protected override void WndProc(ref Message m){
|
||||||
if (isLoaded && m.Msg == Program.WindowRestoreMessage){
|
if (isLoaded && m.Msg == Program.WindowRestoreMessage){
|
||||||
if (WindowsUtils.CurrentProcessID == m.WParam.ToInt32()){
|
using Process me = Process.GetCurrentProcess();
|
||||||
|
|
||||||
|
if (me.Id == m.WParam.ToInt32()){
|
||||||
trayIcon_ClickRestore(trayIcon, EventArgs.Empty);
|
trayIcon_ClickRestore(trayIcon, EventArgs.Empty);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -16,15 +16,10 @@ static class WindowsUtils{
|
|||||||
|
|
||||||
private static readonly bool IsWindows8OrNewer;
|
private static readonly bool IsWindows8OrNewer;
|
||||||
|
|
||||||
public static int CurrentProcessID { get; }
|
|
||||||
public static bool ShouldAvoidToolWindow { get; }
|
public static bool ShouldAvoidToolWindow { get; }
|
||||||
public static bool IsAeroEnabled => IsWindows8OrNewer || (NativeMethods.DwmIsCompositionEnabled(out bool isCompositionEnabled) == 0 && isCompositionEnabled);
|
public static bool IsAeroEnabled => IsWindows8OrNewer || (NativeMethods.DwmIsCompositionEnabled(out bool isCompositionEnabled) == 0 && isCompositionEnabled);
|
||||||
|
|
||||||
static WindowsUtils(){
|
static WindowsUtils(){
|
||||||
using(Process me = Process.GetCurrentProcess()){
|
|
||||||
CurrentProcessID = me.Id;
|
|
||||||
}
|
|
||||||
|
|
||||||
Version ver = Environment.OSVersion.Version;
|
Version ver = Environment.OSVersion.Version;
|
||||||
IsWindows8OrNewer = ver.Major == 6 && ver.Minor == 2; // windows 8/10
|
IsWindows8OrNewer = ver.Major == 6 && ver.Minor == 2; // windows 8/10
|
||||||
|
|
||||||
|
58
Impl/LockHandler.cs
Normal file
58
Impl/LockHandler.cs
Normal file
@ -0,0 +1,58 @@
|
|||||||
|
using System;
|
||||||
|
using System.ComponentModel;
|
||||||
|
using System.Diagnostics;
|
||||||
|
using TweetDuck.Core.Utils;
|
||||||
|
using TweetLib.Core.Application;
|
||||||
|
|
||||||
|
namespace TweetDuck.Impl{
|
||||||
|
class LockHandler : IAppLockHandler{
|
||||||
|
private const int WaitRetryDelay = 250;
|
||||||
|
private const int RestoreFailTimeout = 2000;
|
||||||
|
private const int CloseNaturallyTimeout = 10000;
|
||||||
|
private const int CloseKillTimeout = 5000;
|
||||||
|
|
||||||
|
bool IAppLockHandler.RestoreProcess(Process process){
|
||||||
|
if (process.MainWindowHandle == IntPtr.Zero){ // restore if the original process is in tray
|
||||||
|
NativeMethods.BroadcastMessage(Program.WindowRestoreMessage, (uint)process.Id, 0);
|
||||||
|
|
||||||
|
if (WindowsUtils.TrySleepUntil(() => CheckProcessExited(process) || (process.MainWindowHandle != IntPtr.Zero && process.Responding), RestoreFailTimeout, WaitRetryDelay)){
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
bool IAppLockHandler.CloseProcess(Process process){
|
||||||
|
try{
|
||||||
|
if (process.CloseMainWindow()){
|
||||||
|
// ReSharper disable once AccessToDisposedClosure
|
||||||
|
WindowsUtils.TrySleepUntil(() => CheckProcessExited(process), CloseNaturallyTimeout, WaitRetryDelay);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!process.HasExited){
|
||||||
|
process.Kill();
|
||||||
|
// ReSharper disable once AccessToDisposedClosure
|
||||||
|
WindowsUtils.TrySleepUntil(() => CheckProcessExited(process), CloseKillTimeout, WaitRetryDelay);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (process.HasExited){
|
||||||
|
process.Dispose();
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
else{
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
}catch(Exception ex) when (ex is InvalidOperationException || ex is Win32Exception){
|
||||||
|
bool hasExited = CheckProcessExited(process);
|
||||||
|
process.Dispose();
|
||||||
|
return hasExited;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private static bool CheckProcessExited(Process process){
|
||||||
|
process.Refresh();
|
||||||
|
return process.HasExited;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
10
Program.cs
10
Program.cs
@ -15,6 +15,7 @@
|
|||||||
using TweetDuck.Impl;
|
using TweetDuck.Impl;
|
||||||
using TweetDuck.Resources;
|
using TweetDuck.Resources;
|
||||||
using TweetLib.Core;
|
using TweetLib.Core;
|
||||||
|
using TweetLib.Core.Application.Helpers;
|
||||||
using TweetLib.Core.Collections;
|
using TweetLib.Core.Collections;
|
||||||
using TweetLib.Core.Utils;
|
using TweetLib.Core.Utils;
|
||||||
|
|
||||||
@ -68,6 +69,7 @@ static Program(){
|
|||||||
|
|
||||||
Lib.Initialize(new App.Builder{
|
Lib.Initialize(new App.Builder{
|
||||||
ErrorHandler = Reporter,
|
ErrorHandler = Reporter,
|
||||||
|
LockHandler = new LockHandler(),
|
||||||
SystemHandler = new SystemHandler(),
|
SystemHandler = new SystemHandler(),
|
||||||
ResourceHandler = Resources
|
ResourceHandler = Resources
|
||||||
});
|
});
|
||||||
@ -105,15 +107,17 @@ private static void Main(){
|
|||||||
LockManager.Result lockResult = LockManager.Lock();
|
LockManager.Result lockResult = LockManager.Lock();
|
||||||
|
|
||||||
if (lockResult == LockManager.Result.HasProcess){
|
if (lockResult == LockManager.Result.HasProcess){
|
||||||
if (!LockManager.RestoreLockingProcess(2000) && FormMessage.Error("TweetDuck is Already Running", "Another instance of TweetDuck is already running.\nDo you want to close it?", FormMessage.Yes, FormMessage.No)){
|
if (!LockManager.RestoreLockingProcess() && FormMessage.Error("TweetDuck is Already Running", "Another instance of TweetDuck is already running.\nDo you want to close it?", FormMessage.Yes, FormMessage.No)){
|
||||||
if (!LockManager.CloseLockingProcess(10000, 5000)){
|
if (!LockManager.CloseLockingProcess()){
|
||||||
FormMessage.Error("TweetDuck Has Failed :(", "Could not close the other process.", FormMessage.OK);
|
FormMessage.Error("TweetDuck Has Failed :(", "Could not close the other process.", FormMessage.OK);
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
lockResult = LockManager.Lock();
|
lockResult = LockManager.Lock();
|
||||||
}
|
}
|
||||||
else return;
|
else{
|
||||||
|
return;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
if (lockResult != LockManager.Result.Success){
|
if (lockResult != LockManager.Result.Success){
|
||||||
|
@ -55,7 +55,6 @@
|
|||||||
<ItemGroup>
|
<ItemGroup>
|
||||||
<Compile Include="Configuration\Arguments.cs" />
|
<Compile Include="Configuration\Arguments.cs" />
|
||||||
<Compile Include="Configuration\ConfigManager.cs" />
|
<Compile Include="Configuration\ConfigManager.cs" />
|
||||||
<Compile Include="Configuration\LockManager.cs" />
|
|
||||||
<Compile Include="Configuration\SystemConfig.cs" />
|
<Compile Include="Configuration\SystemConfig.cs" />
|
||||||
<Compile Include="Configuration\UserConfig.cs" />
|
<Compile Include="Configuration\UserConfig.cs" />
|
||||||
<Compile Include="Core\Adapters\CefScriptExecutor.cs" />
|
<Compile Include="Core\Adapters\CefScriptExecutor.cs" />
|
||||||
@ -238,6 +237,7 @@
|
|||||||
<Compile Include="Core\Other\FormSettings.Designer.cs">
|
<Compile Include="Core\Other\FormSettings.Designer.cs">
|
||||||
<DependentUpon>FormSettings.cs</DependentUpon>
|
<DependentUpon>FormSettings.cs</DependentUpon>
|
||||||
</Compile>
|
</Compile>
|
||||||
|
<Compile Include="Impl\LockHandler.cs" />
|
||||||
<Compile Include="Impl\SystemHandler.cs" />
|
<Compile Include="Impl\SystemHandler.cs" />
|
||||||
<Compile Include="Plugins\PluginControl.cs">
|
<Compile Include="Plugins\PluginControl.cs">
|
||||||
<SubType>UserControl</SubType>
|
<SubType>UserControl</SubType>
|
||||||
|
@ -4,6 +4,7 @@
|
|||||||
namespace TweetLib.Core{
|
namespace TweetLib.Core{
|
||||||
public sealed class App{
|
public sealed class App{
|
||||||
public static IAppErrorHandler ErrorHandler { get; private set; }
|
public static IAppErrorHandler ErrorHandler { get; private set; }
|
||||||
|
public static IAppLockHandler LockHandler { get; private set; }
|
||||||
public static IAppSystemHandler SystemHandler { get; private set; }
|
public static IAppSystemHandler SystemHandler { get; private set; }
|
||||||
public static IAppResourceHandler ResourceHandler { get; private set; }
|
public static IAppResourceHandler ResourceHandler { get; private set; }
|
||||||
|
|
||||||
@ -11,6 +12,7 @@ public sealed class App{
|
|||||||
|
|
||||||
public sealed class Builder{
|
public sealed class Builder{
|
||||||
public IAppErrorHandler? ErrorHandler { get; set; }
|
public IAppErrorHandler? ErrorHandler { get; set; }
|
||||||
|
public IAppLockHandler? LockHandler { get; set; }
|
||||||
public IAppSystemHandler? SystemHandler { get; set; }
|
public IAppSystemHandler? SystemHandler { get; set; }
|
||||||
public IAppResourceHandler? ResourceHandler { get; set; }
|
public IAppResourceHandler? ResourceHandler { get; set; }
|
||||||
|
|
||||||
@ -18,6 +20,7 @@ public sealed class Builder{
|
|||||||
|
|
||||||
internal void Initialize(){
|
internal void Initialize(){
|
||||||
App.ErrorHandler = Validate(ErrorHandler, nameof(ErrorHandler))!;
|
App.ErrorHandler = Validate(ErrorHandler, nameof(ErrorHandler))!;
|
||||||
|
App.LockHandler = Validate(LockHandler, nameof(LockHandler))!;
|
||||||
App.SystemHandler = Validate(SystemHandler, nameof(SystemHandler))!;
|
App.SystemHandler = Validate(SystemHandler, nameof(SystemHandler))!;
|
||||||
App.ResourceHandler = Validate(ResourceHandler, nameof(ResourceHandler))!;
|
App.ResourceHandler = Validate(ResourceHandler, nameof(ResourceHandler))!;
|
||||||
}
|
}
|
||||||
|
@ -1,12 +1,11 @@
|
|||||||
using System;
|
using System;
|
||||||
using System.ComponentModel;
|
|
||||||
using System.Diagnostics;
|
using System.Diagnostics;
|
||||||
|
using System.Diagnostics.CodeAnalysis;
|
||||||
using System.IO;
|
using System.IO;
|
||||||
using System.Threading;
|
using System.Threading;
|
||||||
using TweetDuck.Core.Utils;
|
|
||||||
|
|
||||||
namespace TweetDuck.Configuration{
|
namespace TweetLib.Core.Application.Helpers{
|
||||||
sealed class LockManager{
|
public sealed class LockManager{
|
||||||
private const int RetryDelay = 250;
|
private const int RetryDelay = 250;
|
||||||
|
|
||||||
public enum Result{
|
public enum Result{
|
||||||
@ -14,8 +13,8 @@ public enum Result{
|
|||||||
}
|
}
|
||||||
|
|
||||||
private readonly string file;
|
private readonly string file;
|
||||||
private FileStream lockStream;
|
private FileStream? lockStream;
|
||||||
private Process lockingProcess;
|
private Process? lockingProcess;
|
||||||
|
|
||||||
public LockManager(string file){
|
public LockManager(string file){
|
||||||
this.file = file;
|
this.file = file;
|
||||||
@ -37,7 +36,7 @@ private bool ReleaseLockFileStream(){
|
|||||||
private Result TryCreateLockFile(){
|
private Result TryCreateLockFile(){
|
||||||
void CreateLockFileStream(){
|
void CreateLockFileStream(){
|
||||||
lockStream = new FileStream(file, FileMode.Create, FileAccess.Write, FileShare.Read);
|
lockStream = new FileStream(file, FileMode.Create, FileAccess.Write, FileShare.Read);
|
||||||
lockStream.Write(BitConverter.GetBytes(WindowsUtils.CurrentProcessID), 0, sizeof(int));
|
lockStream.Write(BitConverter.GetBytes(CurrentProcessID), 0, sizeof(int));
|
||||||
lockStream.Flush(true);
|
lockStream.Flush(true);
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -82,13 +81,11 @@ public Result Lock(){
|
|||||||
try{
|
try{
|
||||||
Process foundProcess = Process.GetProcessById(pid);
|
Process foundProcess = Process.GetProcessById(pid);
|
||||||
|
|
||||||
using(Process currentProcess = Process.GetCurrentProcess()){
|
if (MatchesCurrentProcess(foundProcess)){
|
||||||
if (foundProcess.MainModule.FileVersionInfo.InternalName == currentProcess.MainModule.FileVersionInfo.InternalName){
|
lockingProcess = foundProcess;
|
||||||
lockingProcess = foundProcess;
|
}
|
||||||
}
|
else{
|
||||||
else{
|
foundProcess.Close();
|
||||||
foundProcess.Close();
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}catch{
|
}catch{
|
||||||
// GetProcessById throws ArgumentException if the process is missing
|
// GetProcessById throws ArgumentException if the process is missing
|
||||||
@ -124,7 +121,7 @@ public bool Unlock(){
|
|||||||
try{
|
try{
|
||||||
File.Delete(file);
|
File.Delete(file);
|
||||||
}catch(Exception e){
|
}catch(Exception e){
|
||||||
Program.Reporter.LogImportant(e.ToString());
|
App.ErrorHandler.Log(e.ToString());
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -133,51 +130,33 @@ public bool Unlock(){
|
|||||||
}
|
}
|
||||||
|
|
||||||
// Locking process
|
// Locking process
|
||||||
|
|
||||||
|
public bool RestoreLockingProcess(){
|
||||||
|
return lockingProcess != null && App.LockHandler.RestoreProcess(lockingProcess);
|
||||||
|
}
|
||||||
|
|
||||||
public bool RestoreLockingProcess(int failTimeout){
|
public bool CloseLockingProcess(){
|
||||||
if (lockingProcess != null && lockingProcess.MainWindowHandle == IntPtr.Zero){ // restore if the original process is in tray
|
if (lockingProcess != null && App.LockHandler.CloseProcess(lockingProcess)){
|
||||||
NativeMethods.BroadcastMessage(Program.WindowRestoreMessage, (uint)lockingProcess.Id, 0);
|
lockingProcess = null;
|
||||||
|
return true;
|
||||||
if (WindowsUtils.TrySleepUntil(() => CheckLockingProcessExited() || (lockingProcess.MainWindowHandle != IntPtr.Zero && lockingProcess.Responding), failTimeout, RetryDelay)){
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
public bool CloseLockingProcess(int closeTimeout, int killTimeout){
|
// Utilities
|
||||||
if (lockingProcess != null){
|
|
||||||
try{
|
|
||||||
if (lockingProcess.CloseMainWindow()){
|
|
||||||
WindowsUtils.TrySleepUntil(CheckLockingProcessExited, closeTimeout, RetryDelay);
|
|
||||||
}
|
|
||||||
|
|
||||||
if (!lockingProcess.HasExited){
|
private static int CurrentProcessID{
|
||||||
lockingProcess.Kill();
|
get{
|
||||||
WindowsUtils.TrySleepUntil(CheckLockingProcessExited, killTimeout, RetryDelay);
|
using Process me = Process.GetCurrentProcess();
|
||||||
}
|
return me.Id;
|
||||||
|
|
||||||
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(){
|
[SuppressMessage("ReSharper", "PossibleNullReferenceException")]
|
||||||
lockingProcess.Refresh();
|
private static bool MatchesCurrentProcess(Process process){
|
||||||
return lockingProcess.HasExited;
|
using Process current = Process.GetCurrentProcess();
|
||||||
|
return current.MainModule.FileVersionInfo.InternalName == process.MainModule.FileVersionInfo.InternalName;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
8
lib/TweetLib.Core/Application/IAppLockHandler.cs
Normal file
8
lib/TweetLib.Core/Application/IAppLockHandler.cs
Normal file
@ -0,0 +1,8 @@
|
|||||||
|
using System.Diagnostics;
|
||||||
|
|
||||||
|
namespace TweetLib.Core.Application{
|
||||||
|
public interface IAppLockHandler{
|
||||||
|
bool RestoreProcess(Process process);
|
||||||
|
bool CloseProcess(Process process);
|
||||||
|
}
|
||||||
|
}
|
Loading…
Reference in New Issue
Block a user