From 3d32e17afd6580253bbd3b68611fbde190005484 Mon Sep 17 00:00:00 2001 From: NaBian <836904362@qq.com> Date: Wed, 21 Aug 2019 21:48:05 +0800 Subject: [PATCH] add SystemMenuHook --- .../Themes/Styles/Window.xaml | 2 +- .../Themes/Styles/Window.xaml | 2 +- .../Themes/Styles/Window.xaml | 2 +- .../ViewModel/Basic/ChatBoxViewModel.cs | 2 +- .../Controls/Other/Dialog.cs | 4 +- .../Controls/Other/NotifyIcon.cs | 17 +- .../Controls/Window/BlurWindow.cs | 2 +- .../Controls/Window/MessageBox.cs | 2 +- .../HandyControl_Shared.projitems | 3 +- .../Tools/Helper/CreateParams.cs | 54 ------- .../Tools/Helper/VisualHelper.cs | 142 +--------------- .../Tools/Helper/WindowHelper.cs | 152 ++++++++++++++++++ .../Tools/Hook/ClipboardHook.cs | 9 +- .../Tools/Hook/SystemMenuHook.cs | 53 ++++++ .../Tools/Interop/NativeMethods.cs | 9 ++ 15 files changed, 236 insertions(+), 219 deletions(-) delete mode 100644 src/Shared/HandyControl_Shared/Tools/Helper/CreateParams.cs create mode 100644 src/Shared/HandyControl_Shared/Tools/Helper/WindowHelper.cs create mode 100644 src/Shared/HandyControl_Shared/Tools/Hook/SystemMenuHook.cs diff --git a/src/Core_30/HandyControl_Core_30/Themes/Styles/Window.xaml b/src/Core_30/HandyControl_Core_30/Themes/Styles/Window.xaml index 67b72f45..3c0a882e 100644 --- a/src/Core_30/HandyControl_Core_30/Themes/Styles/Window.xaml +++ b/src/Core_30/HandyControl_Core_30/Themes/Styles/Window.xaml @@ -132,7 +132,7 @@ - + diff --git a/src/Net_40/HandyControl_Net_40/Themes/Styles/Window.xaml b/src/Net_40/HandyControl_Net_40/Themes/Styles/Window.xaml index 526c868b..40a5d912 100644 --- a/src/Net_40/HandyControl_Net_40/Themes/Styles/Window.xaml +++ b/src/Net_40/HandyControl_Net_40/Themes/Styles/Window.xaml @@ -132,7 +132,7 @@ - + diff --git a/src/Net_GE45/HandyControl_Net_GE45/Themes/Styles/Window.xaml b/src/Net_GE45/HandyControl_Net_GE45/Themes/Styles/Window.xaml index f071fd22..c9750cf5 100644 --- a/src/Net_GE45/HandyControl_Net_GE45/Themes/Styles/Window.xaml +++ b/src/Net_GE45/HandyControl_Net_GE45/Themes/Styles/Window.xaml @@ -132,7 +132,7 @@ - + diff --git a/src/Shared/HandyControlDemo_Shared/ViewModel/Basic/ChatBoxViewModel.cs b/src/Shared/HandyControlDemo_Shared/ViewModel/Basic/ChatBoxViewModel.cs index 4bc00623..65c31f5f 100644 --- a/src/Shared/HandyControlDemo_Shared/ViewModel/Basic/ChatBoxViewModel.cs +++ b/src/Shared/HandyControlDemo_Shared/ViewModel/Basic/ChatBoxViewModel.cs @@ -88,7 +88,7 @@ namespace HandyControlDemo.ViewModel { new ImageBrowser(new Uri(info.Enclosure.ToString())) { - Owner = VisualHelper.GetActiveWindow() + Owner = WindowHelper.GetActiveWindow() }.Show(); } else if (info.Type == ChatMessageType.Audio) diff --git a/src/Shared/HandyControl_Shared/Controls/Other/Dialog.cs b/src/Shared/HandyControl_Shared/Controls/Other/Dialog.cs index d117cb7d..7f2fc50a 100644 --- a/src/Shared/HandyControl_Shared/Controls/Other/Dialog.cs +++ b/src/Shared/HandyControl_Shared/Controls/Other/Dialog.cs @@ -109,7 +109,7 @@ namespace HandyControl.Controls if (string.IsNullOrEmpty(token)) { - window = VisualHelper.GetActiveWindow(); + window = WindowHelper.GetActiveWindow(); } else { @@ -146,7 +146,7 @@ namespace HandyControl.Controls { if (string.IsNullOrEmpty(_token)) { - Close(VisualHelper.GetActiveWindow()); + Close(WindowHelper.GetActiveWindow()); } else if (WindowDic.TryGetValue(_token, out var window)) { diff --git a/src/Shared/HandyControl_Shared/Controls/Other/NotifyIcon.cs b/src/Shared/HandyControl_Shared/Controls/Other/NotifyIcon.cs index 25098b63..f9b072f9 100644 --- a/src/Shared/HandyControl_Shared/Controls/Other/NotifyIcon.cs +++ b/src/Shared/HandyControl_Shared/Controls/Other/NotifyIcon.cs @@ -5,7 +5,6 @@ using System.Windows; using System.Windows.Controls; using System.Windows.Controls.Primitives; using System.Windows.Input; -using System.Windows.Interop; using System.Windows.Media; using System.Windows.Threading; using HandyControl.Data; @@ -508,26 +507,14 @@ namespace HandyControl.Controls Content = ContextContent }; _contextContent.IsOpen = true; - var handle = IntPtr.Zero; - var hwndSource = (HwndSource)PresentationSource.FromVisual(_contextContent.Child); - if (hwndSource != null) - { - handle = hwndSource.Handle; - } - UnsafeNativeMethods.SetForegroundWindow(handle); + UnsafeNativeMethods.SetForegroundWindow(_contextContent.Child.GetHandle()); } else if (ContextMenu != null) { ContextMenu.Placement = PlacementMode.Mouse; ContextMenu.IsOpen = true; - var handle = IntPtr.Zero; - var hwndSource = (HwndSource)PresentationSource.FromVisual(ContextMenu); - if (hwndSource != null) - { - handle = hwndSource.Handle; - } - UnsafeNativeMethods.SetForegroundWindow(handle); + UnsafeNativeMethods.SetForegroundWindow(ContextMenu.GetHandle()); } } diff --git a/src/Shared/HandyControl_Shared/Controls/Window/BlurWindow.cs b/src/Shared/HandyControl_Shared/Controls/Window/BlurWindow.cs index accf561d..066ca9ce 100644 --- a/src/Shared/HandyControl_Shared/Controls/Window/BlurWindow.cs +++ b/src/Shared/HandyControl_Shared/Controls/Window/BlurWindow.cs @@ -51,7 +51,7 @@ namespace HandyControl.Controls Data = accentPtr }; - ExternDllHelper.SetWindowCompositionAttribute(new WindowInteropHelper(window).Handle, ref data); + ExternDllHelper.SetWindowCompositionAttribute(window.GetHandle(), ref data); Marshal.FreeHGlobal(accentPtr); } diff --git a/src/Shared/HandyControl_Shared/Controls/Window/MessageBox.cs b/src/Shared/HandyControl_Shared/Controls/Window/MessageBox.cs index f5d6a43e..62f0c676 100644 --- a/src/Shared/HandyControl_Shared/Controls/Window/MessageBox.cs +++ b/src/Shared/HandyControl_Shared/Controls/Window/MessageBox.cs @@ -348,7 +348,7 @@ namespace HandyControl.Controls throw new InvalidEnumArgumentException(nameof(defaultResult), (int)defaultResult, typeof(MessageBoxResult)); } - var ownerWindow = owner ?? VisualHelper.GetActiveWindow(); + var ownerWindow = owner ?? WindowHelper.GetActiveWindow(); var ownerIsNull = ownerWindow is null; return new MessageBox diff --git a/src/Shared/HandyControl_Shared/HandyControl_Shared.projitems b/src/Shared/HandyControl_Shared/HandyControl_Shared.projitems index b6160619..be4d466b 100644 --- a/src/Shared/HandyControl_Shared/HandyControl_Shared.projitems +++ b/src/Shared/HandyControl_Shared/HandyControl_Shared.projitems @@ -261,7 +261,6 @@ - @@ -271,9 +270,11 @@ + + diff --git a/src/Shared/HandyControl_Shared/Tools/Helper/CreateParams.cs b/src/Shared/HandyControl_Shared/Tools/Helper/CreateParams.cs deleted file mode 100644 index 03f4d550..00000000 --- a/src/Shared/HandyControl_Shared/Tools/Helper/CreateParams.cs +++ /dev/null @@ -1,54 +0,0 @@ -using System; -using System.Text; - -namespace HandyControl.Tools -{ - internal class CreateParams - { - public string ClassName { get; set; } - - public string Caption { get; set; } - - public int Style { get; set; } - - public int ExStyle{ get; set; } - - public int ClassStyle { get; set; } - - public int X { get; set; } - - public int Y { get; set; } - - public int Width { get; set; } - - public int Height { get; set; } - - public IntPtr Parent { get; set; } - - public object Param { get; set; } - - public override string ToString() - { - var sb = new StringBuilder(64); - sb.Append("CreateParams {'"); - sb.Append(ClassName); - sb.Append("', '"); - sb.Append(Caption); - sb.Append("', 0x"); - sb.Append(Convert.ToString(Style, 16)); - sb.Append(", 0x"); - sb.Append(Convert.ToString(ExStyle, 16)); - sb.Append(", {"); - sb.Append(X); - sb.Append(", "); - sb.Append(Y); - sb.Append(", "); - sb.Append(Width); - sb.Append(", "); - sb.Append(Height); - sb.Append("}"); - sb.Append("}"); - return sb.ToString(); - } - } -} \ No newline at end of file diff --git a/src/Shared/HandyControl_Shared/Tools/Helper/VisualHelper.cs b/src/Shared/HandyControl_Shared/Tools/Helper/VisualHelper.cs index 715e02ca..7afa19a2 100644 --- a/src/Shared/HandyControl_Shared/Tools/Helper/VisualHelper.cs +++ b/src/Shared/HandyControl_Shared/Tools/Helper/VisualHelper.cs @@ -1,17 +1,12 @@ using System; -using System.Collections; -using System.ComponentModel; using System.Linq; -using System.Runtime.InteropServices; -using System.Security; using System.Windows; +using System.Windows.Interop; using System.Windows.Media; -using HandyControl.Tools.Interop; -using HandyControl.Tools.Extension; namespace HandyControl.Tools { - public class VisualHelper + public static class VisualHelper { internal static VisualStateGroup TryGetVisualStateGroup(DependencyObject d, string groupName) { @@ -52,136 +47,7 @@ namespace HandyControl.Tools return default; } - /// - /// 获取当前应用中处于激活的一个窗口 - /// - /// - public static Window GetActiveWindow() => Application.Current.Windows.OfType().SingleOrDefault(x => x.IsActive); - - private static readonly BitArray _cacheValid = new BitArray((int)CacheSlot.NumSlots); - - private static bool _setDpiX = true; - - private static bool _dpiInitialized; - - private static readonly object _dpiLock = new object(); - - private static int _dpi; - - internal static int Dpi - { - [SecurityCritical, SecuritySafeCritical] - get - { - if (!_dpiInitialized) - { - lock (_dpiLock) - { - if (!_dpiInitialized) - { - var desktopWnd = new HandleRef(null, IntPtr.Zero); - - // Win32Exception will get the Win32 error code so we don't have to - var dc = UnsafeNativeMethods.GetDC(desktopWnd); - - // Detecting error case from unmanaged call, required by PREsharp to throw a Win32Exception - if (dc == IntPtr.Zero) - { - throw new Win32Exception(); - } - - try - { - _dpi = UnsafeNativeMethods.GetDeviceCaps(new HandleRef(null, dc), NativeMethods.LOGPIXELSY); - _dpiInitialized = true; - } - finally - { - UnsafeNativeMethods.ReleaseDC(desktopWnd, new HandleRef(null, dc)); - } - } - } - } - return _dpi; - } - } - - private static int _dpiX; - - internal static int DpiX - { - [SecurityCritical, SecuritySafeCritical] - get - { - if (_setDpiX) - { - lock (_cacheValid) - { - if (_setDpiX) - { - _setDpiX = false; - var desktopWnd = new HandleRef(null, IntPtr.Zero); - var dc = UnsafeNativeMethods.GetDC(desktopWnd); - if (dc == IntPtr.Zero) - { - throw new Win32Exception(); - } - try - { - _dpiX = UnsafeNativeMethods.GetDeviceCaps(new HandleRef(null, dc), NativeMethods.LOGPIXELSX); - _cacheValid[(int)CacheSlot.DpiX] = true; - } - finally - { - UnsafeNativeMethods.ReleaseDC(desktopWnd, new HandleRef(null, dc)); - } - } - } - } - - return _dpiX; - } - } - - private static Thickness _windowResizeBorderThickness; - - internal static Thickness WindowResizeBorderThickness - { - [SecurityCritical] - get - { - lock (_cacheValid) - { - while (!_cacheValid[(int)CacheSlot.WindowResizeBorderThickness]) - { - _cacheValid[(int)CacheSlot.WindowResizeBorderThickness] = true; - - var frameSize = new Size(NativeMethods.GetSystemMetrics(SM.CXSIZEFRAME), NativeMethods.GetSystemMetrics(SM.CYSIZEFRAME)); - var frameSizeInDips = DpiHelper.DeviceSizeToLogical(frameSize, DpiX / 96.0, Dpi / 96.0); - - _windowResizeBorderThickness = new Thickness(frameSizeInDips.Width, frameSizeInDips.Height, frameSizeInDips.Width, frameSizeInDips.Height); - } - } - - return _windowResizeBorderThickness; - } - } - - public static Thickness WindowMaximizedPadding - { - get - { -#if netle40 - return WindowResizeBorderThickness; -#elif Core - var hdc = UnsafeNativeMethods.GetDC(IntPtr.Zero); - var scale = UnsafeNativeMethods.GetDeviceCaps(hdc, NativeMethods.DESKTOPVERTRES) / (float)UnsafeNativeMethods.GetDeviceCaps(hdc, NativeMethods.VERTRES); - UnsafeNativeMethods.ReleaseDC(IntPtr.Zero, hdc); - return WindowResizeBorderThickness.Add(new Thickness(4 * scale)); -#else - return WindowResizeBorderThickness.Add(new Thickness(4)); -#endif - } - } + public static IntPtr GetHandle(this Visual visual) => + (PresentationSource.FromVisual(visual) as HwndSource)?.Handle ?? IntPtr.Zero; } } \ No newline at end of file diff --git a/src/Shared/HandyControl_Shared/Tools/Helper/WindowHelper.cs b/src/Shared/HandyControl_Shared/Tools/Helper/WindowHelper.cs new file mode 100644 index 00000000..c606d165 --- /dev/null +++ b/src/Shared/HandyControl_Shared/Tools/Helper/WindowHelper.cs @@ -0,0 +1,152 @@ +using System; +using System.Collections; +using System.ComponentModel; +using System.Linq; +using System.Runtime.InteropServices; +using System.Security; +using System.Windows; +using System.Windows.Interop; +using HandyControl.Tools.Extension; +using HandyControl.Tools.Interop; + +namespace HandyControl.Tools +{ + public static class WindowHelper + { + /// + /// 获取当前应用中处于激活的一个窗口 + /// + /// + public static Window GetActiveWindow() => Application.Current.Windows.OfType().SingleOrDefault(x => x.IsActive); + + private static readonly BitArray _cacheValid = new BitArray((int)CacheSlot.NumSlots); + + private static bool _setDpiX = true; + + private static bool _dpiInitialized; + + private static readonly object _dpiLock = new object(); + + private static int _dpi; + + internal static int Dpi + { + [SecurityCritical, SecuritySafeCritical] + get + { + if (!_dpiInitialized) + { + lock (_dpiLock) + { + if (!_dpiInitialized) + { + var desktopWnd = new HandleRef(null, IntPtr.Zero); + + // Win32Exception will get the Win32 error code so we don't have to + var dc = UnsafeNativeMethods.GetDC(desktopWnd); + + // Detecting error case from unmanaged call, required by PREsharp to throw a Win32Exception + if (dc == IntPtr.Zero) + { + throw new Win32Exception(); + } + + try + { + _dpi = UnsafeNativeMethods.GetDeviceCaps(new HandleRef(null, dc), NativeMethods.LOGPIXELSY); + _dpiInitialized = true; + } + finally + { + UnsafeNativeMethods.ReleaseDC(desktopWnd, new HandleRef(null, dc)); + } + } + } + } + return _dpi; + } + } + + private static int _dpiX; + + internal static int DpiX + { + [SecurityCritical, SecuritySafeCritical] + get + { + if (_setDpiX) + { + lock (_cacheValid) + { + if (_setDpiX) + { + _setDpiX = false; + var desktopWnd = new HandleRef(null, IntPtr.Zero); + var dc = UnsafeNativeMethods.GetDC(desktopWnd); + if (dc == IntPtr.Zero) + { + throw new Win32Exception(); + } + try + { + _dpiX = UnsafeNativeMethods.GetDeviceCaps(new HandleRef(null, dc), NativeMethods.LOGPIXELSX); + _cacheValid[(int)CacheSlot.DpiX] = true; + } + finally + { + UnsafeNativeMethods.ReleaseDC(desktopWnd, new HandleRef(null, dc)); + } + } + } + } + + return _dpiX; + } + } + + private static Thickness _windowResizeBorderThickness; + + internal static Thickness WindowResizeBorderThickness + { + [SecurityCritical] + get + { + lock (_cacheValid) + { + while (!_cacheValid[(int)CacheSlot.WindowResizeBorderThickness]) + { + _cacheValid[(int)CacheSlot.WindowResizeBorderThickness] = true; + + var frameSize = new Size(NativeMethods.GetSystemMetrics(SM.CXSIZEFRAME), NativeMethods.GetSystemMetrics(SM.CYSIZEFRAME)); + var frameSizeInDips = DpiHelper.DeviceSizeToLogical(frameSize, DpiX / 96.0, Dpi / 96.0); + + _windowResizeBorderThickness = new Thickness(frameSizeInDips.Width, frameSizeInDips.Height, frameSizeInDips.Width, frameSizeInDips.Height); + } + } + + return _windowResizeBorderThickness; + } + } + + public static Thickness WindowMaximizedPadding + { + get + { +#if netle40 + return WindowResizeBorderThickness; +#elif Core + var hdc = UnsafeNativeMethods.GetDC(IntPtr.Zero); + var scale = UnsafeNativeMethods.GetDeviceCaps(hdc, NativeMethods.DESKTOPVERTRES) / (float)UnsafeNativeMethods.GetDeviceCaps(hdc, NativeMethods.VERTRES); + UnsafeNativeMethods.ReleaseDC(IntPtr.Zero, hdc); + return WindowResizeBorderThickness.Add(new Thickness(4 * scale)); +#else + return WindowResizeBorderThickness.Add(new Thickness(4)); +#endif + } + } + + public static IntPtr CreateHandle() => new WindowInteropHelper(new Window()).EnsureHandle(); + + public static IntPtr GetHandle(this Window window) => new WindowInteropHelper(window).EnsureHandle(); + } +} diff --git a/src/Shared/HandyControl_Shared/Tools/Hook/ClipboardHook.cs b/src/Shared/HandyControl_Shared/Tools/Hook/ClipboardHook.cs index 2c4de7fe..b98bb50a 100644 --- a/src/Shared/HandyControl_Shared/Tools/Hook/ClipboardHook.cs +++ b/src/Shared/HandyControl_Shared/Tools/Hook/ClipboardHook.cs @@ -1,5 +1,4 @@ using System; -using System.Windows; using System.Windows.Interop; using HandyControl.Tools.Interop; @@ -20,7 +19,7 @@ namespace HandyControl.Tools Stop(); } - HookId = new WindowInteropHelper(new Window()).EnsureHandle(); + HookId = WindowHelper.CreateHandle(); HWndSource = HwndSource.FromHwnd(HookId); if (HWndSource != null) { @@ -29,7 +28,11 @@ namespace HandyControl.Tools } } - public static void Stop() => NativeMethods.RemoveClipboardFormatListener(HookId); + public static void Stop() + { + HWndSource.RemoveHook(WinProc); + NativeMethods.RemoveClipboardFormatListener(HookId); + } private static IntPtr WinProc(IntPtr hwnd, int msg, IntPtr wparam, IntPtr lparam, ref bool handled) { diff --git a/src/Shared/HandyControl_Shared/Tools/Hook/SystemMenuHook.cs b/src/Shared/HandyControl_Shared/Tools/Hook/SystemMenuHook.cs new file mode 100644 index 00000000..8dd87340 --- /dev/null +++ b/src/Shared/HandyControl_Shared/Tools/Hook/SystemMenuHook.cs @@ -0,0 +1,53 @@ +using System; +using System.Collections.Generic; +using System.Windows; +using System.Windows.Interop; +using HandyControl.Tools.Interop; + +namespace HandyControl.Tools +{ + public class SystemMenuHook + { + private static readonly Dictionary DataDic = new Dictionary(); + + public static event EventHandler Click; + + public static void Insert(int index, int id, string text, Window window) + { + var hookId = window.GetHandle(); + var source = HwndSource.FromHwnd(hookId); + if (source != null) + { + DataDic[id] = source; + NativeMethods.InsertMenu(NativeMethods.GetSystemMenu(hookId, false), index, NativeMethods.MF_BYPOSITION, id, text); + source.AddHook(WinProc); + } + } + + public static void InsertSeperator(int index, Window window) => NativeMethods.InsertMenu( + NativeMethods.GetSystemMenu(window.GetHandle(), false), index, + NativeMethods.MF_BYPOSITION | NativeMethods.MF_SEPARATOR, 0, ""); + + public static void Remove(int id) + { + if (DataDic.TryGetValue(id, out var data)) + { + data.RemoveHook(WinProc); + DataDic.Remove(id); + } + } + + private static IntPtr WinProc(IntPtr hwnd, int msg, IntPtr wparam, IntPtr lparam, ref bool handled) + { + if (msg == NativeMethods.WM_SYSCOMMAND) + { + var id = wparam.ToInt32(); + if (DataDic.ContainsKey(id)) + { + Click?.Invoke(null, id); + } + } + return IntPtr.Zero; + } + } +} diff --git a/src/Shared/HandyControl_Shared/Tools/Interop/NativeMethods.cs b/src/Shared/HandyControl_Shared/Tools/Interop/NativeMethods.cs index b010ff9d..dd453058 100644 --- a/src/Shared/HandyControl_Shared/Tools/Interop/NativeMethods.cs +++ b/src/Shared/HandyControl_Shared/Tools/Interop/NativeMethods.cs @@ -36,6 +36,7 @@ namespace HandyControl.Tools.Interop WM_KEYUP = 0x0101, WM_SYSKEYDOWN = 0x0104, WM_SYSKEYUP = 0x0105, + WM_SYSCOMMAND = 0x112, WM_MOUSEMOVE = 0x0200, WM_LBUTTONDOWN = 0x0201, WM_LBUTTONUP = 0x0202, @@ -48,6 +49,8 @@ namespace HandyControl.Tools.Interop WM_MBUTTONDBLCLK = 0x0209, WM_CLIPBOARDUPDATE = 0x031D, WM_USER = 0x0400, + MF_BYPOSITION = 0x400, + MF_SEPARATOR = 0x800, TB_GETBUTTON = WM_USER + 23, TB_BUTTONCOUNT = WM_USER + 24, TB_GETITEMRECT = WM_USER + 29, @@ -232,5 +235,11 @@ namespace HandyControl.Tools.Interop [DllImport(ExternDll.User32, SetLastError = true)] [return: MarshalAs(UnmanagedType.Bool)] public static extern bool RemoveClipboardFormatListener(IntPtr hwnd); + + [DllImport(ExternDll.User32)] + public static extern IntPtr GetSystemMenu(IntPtr hWnd, bool bRevert); + + [DllImport(ExternDll.User32)] + public static extern bool InsertMenu(IntPtr hMenu, int wPosition, int wFlags, int wIDNewItem, string lpNewItem); } } \ No newline at end of file