diff --git a/QuickLook.Native.Shell32/QuickLook.Native.Shell32.cpp b/QuickLook.Native.Shell32/QuickLook.Native.Shell32.cpp index c5fffa8..f8b722b 100644 --- a/QuickLook.Native.Shell32/QuickLook.Native.Shell32.cpp +++ b/QuickLook.Native.Shell32/QuickLook.Native.Shell32.cpp @@ -5,6 +5,11 @@ #define EXPORT extern "C" __declspec(dllexport) +EXPORT int GetFocusedWindowType() +{ + return Shell32::GetFocusedWindowType(); +} + EXPORT void SaveCurrentSelection() { Shell32::SaveCurrentSelection(); diff --git a/QuickLook.Native.Shell32/Shell32.h b/QuickLook.Native.Shell32/Shell32.h index bfd5df2..45f4734 100644 --- a/QuickLook.Native.Shell32/Shell32.h +++ b/QuickLook.Native.Shell32/Shell32.h @@ -5,24 +5,23 @@ class Shell32 { public: + enum FocusedWindowType + { + INVALID = 0, + DESKTOP = 1, + EXPLORER = 2, + }; + static FocusedWindowType GetFocusedWindowType(); static void SaveCurrentSelection(); static UINT GetCurrentSelectionCount(); static void GetCurrentSelectionBuffer(PWCHAR buffer); private: - enum FocusedWindowType - { - INVALID, - DESKTOP, - EXPLORER, - }; - static std::vector vector_items; static void SaveSelectedFromDesktop(); static void SaveSelectedFromExplorer(); - static FocusedWindowType GetFocusedWindowType(); static CComQIPtr AttachDesktopShellWindow(); static void vectorFromDataObject(CComPtr dao); }; diff --git a/QuickLook.Plugin.LastResort/IconHelper.cs b/QuickLook.Plugin.LastResort/IconHelper.cs index 5642663..b97c55e 100644 --- a/QuickLook.Plugin.LastResort/IconHelper.cs +++ b/QuickLook.Plugin.LastResort/IconHelper.cs @@ -7,7 +7,7 @@ using System.Runtime.InteropServices; namespace QuickLook.Plugin.LastResort { - public class IconHelper + public static class IconHelper { public enum IconSizeEnum { diff --git a/QuickLook/BackgroundListener.cs b/QuickLook/BackgroundListener.cs index 0110b59..d67429b 100644 --- a/QuickLook/BackgroundListener.cs +++ b/QuickLook/BackgroundListener.cs @@ -1,8 +1,4 @@ -using System; -using System.Linq; -using System.Text; -using System.Threading.Tasks; -using System.Windows.Forms; +using System.Windows.Forms; using QuickLook.Utilities; namespace QuickLook @@ -13,8 +9,6 @@ namespace QuickLook private GlobalKeyboardHook _hook; - private MainWindow _showingWindow; - protected BackgroundListener() { InstallHook(HotkeyEventHandler); @@ -22,49 +16,7 @@ namespace QuickLook private void HotkeyEventHandler(object sender, KeyEventArgs e) { - if (_showingWindow != null) - { - _showingWindow.Close(); - _showingWindow = null; - - GC.Collect(); - - return; - } - - var path = string.Empty; - - // communicate with COM in a separate thread - Task.Run(() => - { - var paths = GetCurrentSelection(); - - if (paths.Any()) - path = paths.First(); - }) - .Wait(); - - if (string.IsNullOrEmpty(path)) - return; - - var matched = PluginManager.FindMatch(path); - - if (matched == null) - return; - - _showingWindow = new MainWindow(); - _showingWindow.Closed += (sender2, e2) => { _showingWindow = null; }; - - _showingWindow.viewContentContainer.ViewerPlugin = matched; - - // get window size before showing it - matched.Prepare(path, _showingWindow.viewContentContainer); - - _showingWindow.Show(); - - matched.View(path, _showingWindow.viewContentContainer); - - _showingWindow.ShowFinishLoadingAnimation(); + ViewWindowManager.GetInstance().InvokeRoutine(); } private void InstallHook(KeyEventHandler handler) @@ -76,19 +28,6 @@ namespace QuickLook _hook.KeyUp += handler; } - private string[] GetCurrentSelection() - { - NativeMethods.QuickLook.SaveCurrentSelection(); - - var n = NativeMethods.QuickLook.GetCurrentSelectionCount(); - - var sb = new StringBuilder(n * 261); // MAX_PATH + NULL = 261 - - NativeMethods.QuickLook.GetCurrentSelectionBuffer(sb); - - return sb.Length == 0 ? new string[0] : sb.ToString().Split('|'); - } - internal static BackgroundListener GetInstance() { return _instance ?? (_instance = new BackgroundListener()); diff --git a/QuickLook/MainWindow.xaml.cs b/QuickLook/MainWindow.xaml.cs index c9541b1..f77d2dd 100644 --- a/QuickLook/MainWindow.xaml.cs +++ b/QuickLook/MainWindow.xaml.cs @@ -8,6 +8,7 @@ using System.Windows.Media.Animation; using System.Windows.Threading; using QuickLook.Annotations; using QuickLook.ExtensionMethods; +using QuickLook.Plugin; using QuickLook.Utilities; namespace QuickLook @@ -53,10 +54,24 @@ namespace QuickLook base.Show(); - NoactivateWindowHelper.SetNoactivate(new WindowInteropHelper(this)); + WindowHelper.SetNoactivate(new WindowInteropHelper(this)); } - internal void ShowFinishLoadingAnimation() + internal void BeginShow(IViewer matchedPlugin, string path) + { + viewContentContainer.ViewerPlugin = matchedPlugin; + + // get window size before showing it + matchedPlugin.Prepare(path, viewContentContainer); + + Show(); + + matchedPlugin.View(path, viewContentContainer); + + ShowFinishLoadingAnimation(); + } + + private void ShowFinishLoadingAnimation() { var speed = 100; diff --git a/QuickLook/NativeMethods/Kernel32.cs b/QuickLook/NativeMethods/Kernel32.cs index 75e4f81..5793603 100644 --- a/QuickLook/NativeMethods/Kernel32.cs +++ b/QuickLook/NativeMethods/Kernel32.cs @@ -3,9 +3,12 @@ using System.Runtime.InteropServices; namespace QuickLook.NativeMethods { - internal class Kernel32 + internal static class Kernel32 { [DllImport("kernel32.dll")] internal static extern IntPtr LoadLibrary(string lpFileName); + + [DllImport("kernel32.dll")] + internal static extern IntPtr GetCurrentThreadId(); } } \ No newline at end of file diff --git a/QuickLook/NativeMethods/QuickLook.cs b/QuickLook/NativeMethods/QuickLook.cs index 9007dd2..5c0dee3 100644 --- a/QuickLook/NativeMethods/QuickLook.cs +++ b/QuickLook/NativeMethods/QuickLook.cs @@ -3,8 +3,11 @@ using System.Text; namespace QuickLook.NativeMethods { - internal class QuickLook + internal static class QuickLook { + [DllImport("QuickLook.Native.Shell32.dll", CallingConvention = CallingConvention.Cdecl)] + internal static extern int GetFocusedWindowType(); + [DllImport("QuickLook.Native.Shell32.dll", CallingConvention = CallingConvention.Cdecl)] internal static extern void SaveCurrentSelection(); diff --git a/QuickLook/NativeMethods/User32.cs b/QuickLook/NativeMethods/User32.cs index d82d605..15de2ca 100644 --- a/QuickLook/NativeMethods/User32.cs +++ b/QuickLook/NativeMethods/User32.cs @@ -1,10 +1,11 @@ using System; using System.Diagnostics.CodeAnalysis; using System.Runtime.InteropServices; +using System.Text; namespace QuickLook.NativeMethods { - internal class User32 + internal static class User32 { [DllImport("user32.dll")] internal static extern IntPtr SetWindowsHookEx(int idHook, KeyboardHookProc callback, IntPtr hInstance, @@ -17,10 +18,28 @@ namespace QuickLook.NativeMethods internal static extern int CallNextHookEx(IntPtr idHook, int nCode, int wParam, ref KeyboardHookStruct lParam); [DllImport("user32.dll")] - public static extern IntPtr SetWindowLong(IntPtr hWnd, int nIndex, int dwNewLong); + internal static extern IntPtr SetWindowLong(IntPtr hWnd, int nIndex, int dwNewLong); [DllImport("user32.dll")] - public static extern int GetWindowLong(IntPtr hWnd, int nIndex); + internal static extern int GetWindowLong(IntPtr hWnd, int nIndex); + + [DllImport("user32.dll")] + internal static extern IntPtr GetForegroundWindow(); + + [DllImport("user32.dll")] + internal static extern IntPtr GetWindowThreadProcessId(IntPtr hWnd, IntPtr ProcessId); + + [DllImport("user32.dll")] + internal static extern IntPtr AttachThreadInput(IntPtr idAttach, IntPtr idAttachTo, bool fAttach); + + [DllImport("user32.dll")] + internal static extern IntPtr GetFocus(); + + [DllImport("user32.dll", CharSet = CharSet.Auto)] + internal static extern int GetClassName(IntPtr hWnd, StringBuilder lpClassName, int nMaxCount); + + [DllImport("user32.dll")] + internal static extern IntPtr GetParent(IntPtr hWnd); internal delegate int KeyboardHookProc(int code, int wParam, ref KeyboardHookStruct lParam); diff --git a/QuickLook/QuickLook.csproj b/QuickLook/QuickLook.csproj index 6efc481..5a2d401 100644 --- a/QuickLook/QuickLook.csproj +++ b/QuickLook/QuickLook.csproj @@ -79,7 +79,8 @@ ViewContentContainer.xaml - + + Designer MSBuild:Compile diff --git a/QuickLook/Utilities/AeroGlass.cs b/QuickLook/Utilities/AeroGlass.cs index 7e55c6e..d055894 100644 --- a/QuickLook/Utilities/AeroGlass.cs +++ b/QuickLook/Utilities/AeroGlass.cs @@ -5,7 +5,7 @@ using System.Windows.Interop; namespace QuickLook.Utilities { - internal class AeroGlass + internal static class AeroGlass { internal static void EnableBlur(Window window) { diff --git a/QuickLook/Utilities/NoactivateWindowHelper.cs b/QuickLook/Utilities/NoactivateWindowHelper.cs deleted file mode 100644 index cc7364c..0000000 --- a/QuickLook/Utilities/NoactivateWindowHelper.cs +++ /dev/null @@ -1,15 +0,0 @@ -using System.Windows.Interop; -using QuickLook.NativeMethods; - -namespace QuickLook.Utilities -{ - internal class NoactivateWindowHelper - { - internal static void SetNoactivate(WindowInteropHelper window) - { - User32.SetWindowLong(window.Handle, User32.GWL_EXSTYLE, - User32.GetWindowLong(window.Handle, User32.GWL_EXSTYLE) | - User32.WS_EX_NOACTIVATE); - } - } -} \ No newline at end of file diff --git a/QuickLook/Utilities/WindowHelper.cs b/QuickLook/Utilities/WindowHelper.cs new file mode 100644 index 0000000..9d901bd --- /dev/null +++ b/QuickLook/Utilities/WindowHelper.cs @@ -0,0 +1,62 @@ +using System; +using System.Text; +using System.Windows.Interop; +using QuickLook.NativeMethods; + +namespace QuickLook.Utilities +{ + internal static class WindowHelper + { + internal static void SetNoactivate(WindowInteropHelper window) + { + User32.SetWindowLong(window.Handle, User32.GWL_EXSTYLE, + User32.GetWindowLong(window.Handle, User32.GWL_EXSTYLE) | + User32.WS_EX_NOACTIVATE); + } + + internal static string GetWindowClassName(IntPtr window) + { + var pClassName = new StringBuilder(256); + User32.GetClassName(window, pClassName, pClassName.Capacity); + + return pClassName.ToString(); + } + + internal static IntPtr GetParentWindow(IntPtr child) + { + return User32.GetParent(child); + } + + internal static IntPtr GetFocusedWindow() + { + var activeWindowHandle = User32.GetForegroundWindow(); + + var activeWindowThread = User32.GetWindowThreadProcessId(activeWindowHandle, IntPtr.Zero); + var currentThread = Kernel32.GetCurrentThreadId(); + + User32.AttachThreadInput(activeWindowThread, currentThread, true); + var focusedControlHandle = User32.GetFocus(); + User32.AttachThreadInput(activeWindowThread, currentThread, false); + + return focusedControlHandle; + } + + internal static bool IsFocusedControlExplorerItem() + { + if (NativeMethods.QuickLook.GetFocusedWindowType() == 0) + return false; + + var focusedWindowClass = GetWindowClassName(GetFocusedWindow()); + var focusedWindowParentClass = + GetWindowClassName(GetParentWindow(GetFocusedWindow())); + + if (focusedWindowParentClass != "SHELLDLL_DefView") + return false; + + if (focusedWindowClass != "SysListView32" && focusedWindowClass != "DirectUIHWND") + return false; + + return true; + } + } +} \ No newline at end of file diff --git a/QuickLook/ViewWindowManager.cs b/QuickLook/ViewWindowManager.cs new file mode 100644 index 0000000..13e94ad --- /dev/null +++ b/QuickLook/ViewWindowManager.cs @@ -0,0 +1,90 @@ +using System; +using System.Linq; +using System.Text; +using System.Threading.Tasks; +using QuickLook.Plugin; +using QuickLook.Utilities; + +namespace QuickLook +{ + internal class ViewWindowManager + { + private static ViewWindowManager _instance; + + private MainWindow _viewWindow; + + internal void InvokeRoutine() + { + if (CloseCurrentWindow()) + return; + + if (!WindowHelper.IsFocusedControlExplorerItem()) + return; + + var path = GetCurrentSelection(); + if (string.IsNullOrEmpty(path)) + return; + + var matchedPlugin = PluginManager.FindMatch(path); + if (matchedPlugin == null) + return; + + BeginShowNewWindow(matchedPlugin, path); + } + + private void BeginShowNewWindow(IViewer matchedPlugin, string path) + { + _viewWindow = new MainWindow(); + _viewWindow.Closed += (sender2, e2) => { _viewWindow = null; }; + + _viewWindow.BeginShow(matchedPlugin, path); + } + + private bool CloseCurrentWindow() + { + if (_viewWindow != null) + { + _viewWindow.Close(); + _viewWindow = null; + + GC.Collect(); + + return true; + } + return false; + } + + private string GetCurrentSelection() + { + var path = string.Empty; + + // communicate with COM in a separate thread + Task.Run(() => + { + var paths = GetCurrentSelectionNative(); + + if (paths.Any()) + path = paths.First(); + }) + .Wait(); + + return string.IsNullOrEmpty(path) ? string.Empty : path; + } + + private string[] GetCurrentSelectionNative() + { + NativeMethods.QuickLook.SaveCurrentSelection(); + + var n = NativeMethods.QuickLook.GetCurrentSelectionCount(); + var sb = new StringBuilder(n * 261); // MAX_PATH + NULL = 261 + NativeMethods.QuickLook.GetCurrentSelectionBuffer(sb); + + return sb.Length == 0 ? new string[0] : sb.ToString().Split('|'); + } + + internal static ViewWindowManager GetInstance() + { + return _instance ?? (_instance = new ViewWindowManager()); + } + } +} \ No newline at end of file