diff --git a/QuickLook/App.xaml b/QuickLook/App.xaml index f6e5309..421cd69 100644 --- a/QuickLook/App.xaml +++ b/QuickLook/App.xaml @@ -3,6 +3,7 @@ xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml" xmlns:local="clr-namespace:QuickLook" Startup="Application_Startup" + Exit="App_OnExit" ShutdownMode="OnExplicitShutdown"> diff --git a/QuickLook/App.xaml.cs b/QuickLook/App.xaml.cs index e5e25e4..7466dc9 100644 --- a/QuickLook/App.xaml.cs +++ b/QuickLook/App.xaml.cs @@ -1,8 +1,8 @@ using System; +using System.Diagnostics; using System.IO; using System.Linq; using System.Reflection; -using System.Threading; using System.Windows; namespace QuickLook @@ -14,8 +14,7 @@ namespace QuickLook { public static readonly string AppFullPath = Assembly.GetExecutingAssembly().Location; public static readonly string AppPath = Path.GetDirectoryName(AppFullPath); - - private Mutex _isRunning; + public static bool RunningAsViewer; protected override void OnStartup(StartupEventArgs e) { @@ -28,7 +27,46 @@ namespace QuickLook private void Application_Startup(object sender, StartupEventArgs e) { - EnsureSingleInstance(); + if (e.Args.Any()) + if (Directory.Exists(e.Args.First()) || File.Exists(e.Args.First())) + RunAsViewer(e); + else + RunAsListener(e); + else + RunAsListener(e); + } + + private void RunAsViewer(StartupEventArgs e) + { + RunningAsViewer = true; + + var runningPid = PidHelper.GetRunningInstance(); + if (runningPid != -1) + { + Process.GetProcessById(runningPid).Kill(); + + Current.Shutdown(); + return; + } + + PidHelper.WritePid(); + + ViewWindowManager.GetInstance().InvokeViewer(e.Args.First()); + } + + private void RunAsListener(StartupEventArgs e) + { + RunningAsViewer = false; + + if (PidHelper.GetRunningInstance() != -1) + { + MessageBox.Show("QuickLook is already running in the background."); + + Current.Shutdown(); + return; + } + + PidHelper.WritePid(); TrayIcon.GetInstance(); if (!e.Args.Contains("/autorun")) @@ -39,15 +77,12 @@ namespace QuickLook BackgroundListener.GetInstance(); } - private void EnsureSingleInstance() + private void App_OnExit(object sender, ExitEventArgs e) { - var isNew = false; - _isRunning = new Mutex(true, "QuickLook.App", out isNew); - if (!isNew) - { - MessageBox.Show("QuickLook is already running in the background."); - Current.Shutdown(); - } + TrayIcon.GetInstance().Dispose(); + BackgroundListener.GetInstance().Dispose(); + + PidHelper.DeletePid(); } } } \ No newline at end of file diff --git a/QuickLook/BackgroundListener.cs b/QuickLook/BackgroundListener.cs index 5fd530f..a63ac0c 100644 --- a/QuickLook/BackgroundListener.cs +++ b/QuickLook/BackgroundListener.cs @@ -1,8 +1,9 @@ -using System.Windows.Forms; +using System; +using System.Windows.Forms; namespace QuickLook { - internal class BackgroundListener + internal class BackgroundListener : IDisposable { private static BackgroundListener _instance; @@ -13,8 +14,16 @@ namespace QuickLook InstallHook(HotkeyEventHandler); } + public void Dispose() + { + _hook?.Dispose(); + } + private void HotkeyEventHandler(object sender, KeyEventArgs e) { + if (e.Modifiers != Keys.None) + return; + ViewWindowManager.GetInstance().InvokeRoutine(); } diff --git a/QuickLook/Helpers/WindowHelper.cs b/QuickLook/Helpers/WindowHelper.cs index 84e8892..b6b88c4 100644 --- a/QuickLook/Helpers/WindowHelper.cs +++ b/QuickLook/Helpers/WindowHelper.cs @@ -1,5 +1,5 @@ using System; -using System.IO; +using System.Diagnostics; using System.Text; using System.Windows.Interop; using QuickLook.NativeMethods; @@ -44,9 +44,11 @@ namespace QuickLook.Helpers internal static bool IsFocusedWindowSelf() { - var focusedWindowClass = GetWindowClassName(GetFocusedWindow()); + var procId = Process.GetCurrentProcess().Id; + uint activeProcId; + User32.GetWindowThreadProcessId(GetFocusedWindow(), out activeProcId); - return focusedWindowClass.StartsWith($"HwndWrapper[{Path.GetFileName(App.AppFullPath)};;"); + return activeProcId == procId; } internal static bool IsFocusedControlExplorerItem() diff --git a/QuickLook/NativeMethods/User32.cs b/QuickLook/NativeMethods/User32.cs index 15de2ca..4128249 100644 --- a/QuickLook/NativeMethods/User32.cs +++ b/QuickLook/NativeMethods/User32.cs @@ -27,7 +27,10 @@ namespace QuickLook.NativeMethods internal static extern IntPtr GetForegroundWindow(); [DllImport("user32.dll")] - internal static extern IntPtr GetWindowThreadProcessId(IntPtr hWnd, IntPtr ProcessId); + internal static extern IntPtr GetWindowThreadProcessId(IntPtr hWnd, IntPtr processId); + + [DllImport("user32.dll")] + internal static extern IntPtr GetWindowThreadProcessId(IntPtr hWnd, out uint processId); [DllImport("user32.dll")] internal static extern IntPtr AttachThreadInput(IntPtr idAttach, IntPtr idAttachTo, bool fAttach); diff --git a/QuickLook/PidHelper.cs b/QuickLook/PidHelper.cs new file mode 100644 index 0000000..059d447 --- /dev/null +++ b/QuickLook/PidHelper.cs @@ -0,0 +1,46 @@ +using System.Diagnostics; +using System.IO; + +namespace QuickLook +{ + internal static class PidHelper + { + private static readonly string PidListener = + Path.Combine(Path.GetTempPath(), "QuickLook.App.Listener.D6EC3F8DDF6B.pid"); + + private static readonly string PidViewer = + Path.Combine(Path.GetTempPath(), "QuickLook.App.Viewer.A6FA53E93515.pid"); + + internal static int GetRunningInstance() + { + var pid = App.RunningAsViewer ? PidViewer : PidListener; + + if (!File.Exists(pid)) + return -1; + + var ppid = -1; + int.TryParse(File.ReadAllText(pid), out ppid); + + try + { + Process.GetProcessById(ppid); + } + catch + { + return -1; + } + + return ppid; + } + + internal static void WritePid() + { + File.WriteAllText(App.RunningAsViewer ? PidViewer : PidListener, Process.GetCurrentProcess().Id.ToString()); + } + + internal static void DeletePid() + { + File.Delete(App.RunningAsViewer ? PidViewer : PidListener); + } + } +} \ No newline at end of file diff --git a/QuickLook/QuickLook.csproj b/QuickLook/QuickLook.csproj index a6b517f..6858a10 100644 --- a/QuickLook/QuickLook.csproj +++ b/QuickLook/QuickLook.csproj @@ -87,6 +87,7 @@ + diff --git a/QuickLook/TrayIcon.cs b/QuickLook/TrayIcon.cs index a0963bd..dc63870 100644 --- a/QuickLook/TrayIcon.cs +++ b/QuickLook/TrayIcon.cs @@ -1,4 +1,5 @@ -using System.Diagnostics; +using System; +using System.Diagnostics; using System.Windows.Forms; using QuickLook.Helpers; using QuickLook.Properties; @@ -6,7 +7,7 @@ using Application = System.Windows.Application; namespace QuickLook { - public class TrayIcon + public class TrayIcon : IDisposable { private static TrayIcon _instance; @@ -39,6 +40,11 @@ namespace QuickLook _icon.ContextMenu.Popup += (sender, e) => { _itemAutorun.Checked = AutoStartupHelper.IsAutorun(); }; } + public void Dispose() + { + _icon.Visible = false; + } + public void ShowNotification(string title, string content, bool isError = false) { _icon.ShowBalloonTip(5000, title, content, isError ? ToolTipIcon.Error : ToolTipIcon.Info); diff --git a/QuickLook/ViewWindowManager.cs b/QuickLook/ViewWindowManager.cs index d6c1bd5..3865709 100644 --- a/QuickLook/ViewWindowManager.cs +++ b/QuickLook/ViewWindowManager.cs @@ -4,6 +4,7 @@ using System.IO; using System.Linq; using System.Text; using System.Threading.Tasks; +using System.Windows; using QuickLook.ExtensionMethods; using QuickLook.Helpers; using QuickLook.Plugin; @@ -16,16 +17,8 @@ namespace QuickLook private MainWindow _viewWindow; - internal void InvokeRoutine() + internal void InvokeViewer(string path) { - if (!WindowHelper.IsFocusedControlExplorerItem()) - if (!WindowHelper.IsFocusedWindowSelf()) - return; - - if (CloseCurrentWindow()) - return; - - var path = GetCurrentSelection(); if (string.IsNullOrEmpty(path)) return; if (!Directory.Exists(path) && !File.Exists(path)) @@ -36,11 +29,31 @@ namespace QuickLook BeginShowNewWindow(matchedPlugin, path); } + internal void InvokeRoutine() + { + if (!WindowHelper.IsFocusedControlExplorerItem()) + if (!WindowHelper.IsFocusedWindowSelf()) + return; + + if (CloseCurrentWindow()) + return; + + var path = GetCurrentSelection(); + + InvokeViewer(path); + } + private void BeginShowNewWindow(IViewer matchedPlugin, string path) { _viewWindow = new MainWindow(); _viewWindow.Closed += (sender2, e2) => { + if (App.RunningAsViewer) + { + Application.Current.Shutdown(); + return; + } + _viewWindow.Dispose(); _viewWindow = null; GC.Collect();