From 63816ccdd1f711a28a536e7c60a3c4fe2bdca260 Mon Sep 17 00:00:00 2001 From: noisyfox Date: Wed, 23 Nov 2016 02:34:07 +1100 Subject: [PATCH] Fix hotkey unregister issue. Use singleton mode for hotkey callbacks. --- .../System/Hotkeys/HotkeyCallbacks.cs | 103 +++++ .../System/Hotkeys}/Hotkeys.cs | 358 +++++++++--------- shadowsocks-csharp/Program.cs | 1 + shadowsocks-csharp/View/HotkeySettingsForm.cs | 86 +---- shadowsocks-csharp/shadowsocks-csharp.csproj | 3 +- test/UnitTest.cs | 2 +- 6 files changed, 292 insertions(+), 261 deletions(-) create mode 100644 shadowsocks-csharp/Controller/System/Hotkeys/HotkeyCallbacks.cs rename shadowsocks-csharp/{Util => Controller/System/Hotkeys}/Hotkeys.cs (96%) diff --git a/shadowsocks-csharp/Controller/System/Hotkeys/HotkeyCallbacks.cs b/shadowsocks-csharp/Controller/System/Hotkeys/HotkeyCallbacks.cs new file mode 100644 index 00000000..de811e57 --- /dev/null +++ b/shadowsocks-csharp/Controller/System/Hotkeys/HotkeyCallbacks.cs @@ -0,0 +1,103 @@ +using System.Reflection; +using Shadowsocks.View; + +namespace Shadowsocks.Controller.Hotkeys +{ + public class HotkeyCallbacks + { + public static HotkeyCallbacks Instance { get; private set; } + + public static void InitInstance(ShadowsocksController controller) + { + if (Instance != null) + { + return; + } + + Instance = new HotkeyCallbacks(controller); + } + + private readonly ShadowsocksController _controller; + + private HotkeyCallbacks(ShadowsocksController controller) + { + _controller = controller; + } + + #region Callbacks + + private void SwitchSystemProxyCallback() + { + bool enabled = _controller.GetConfigurationCopy().enabled; + _controller.ToggleEnable(!enabled); + } + + private void SwitchProxyModeCallback() + { + var config = _controller.GetConfigurationCopy(); + if (config.enabled == false) return; + var currStatus = config.global; + _controller.ToggleGlobal(!currStatus); + } + + private void SwitchAllowLanCallback() + { + var status = _controller.GetConfigurationCopy().shareOverLan; + _controller.ToggleShareOverLAN(!status); + } + + private void ShowLogsCallback() + { + // Get the current MenuViewController in this program via reflection + FieldInfo fi = Assembly.GetExecutingAssembly().GetType("Shadowsocks.Program") + .GetField("_viewController", + BindingFlags.NonPublic | BindingFlags.Static | BindingFlags.IgnoreCase); + // To retrieve the value of a static field, pass null here + var mvc = fi.GetValue(null) as MenuViewController; + mvc.ShowLogForm_HotKey(); + } + + private void ServerMoveUpCallback() + { + int currIndex; + int serverCount; + GetCurrServerInfo(out currIndex, out serverCount); + if (currIndex - 1 < 0) + { + // revert to last server + currIndex = serverCount - 1; + } + else + { + currIndex -= 1; + } + _controller.SelectServerIndex(currIndex); + } + + private void ServerMoveDownCallback() + { + int currIndex; + int serverCount; + GetCurrServerInfo(out currIndex, out serverCount); + if (currIndex + 1 == serverCount) + { + // revert to first server + currIndex = 0; + } + else + { + currIndex += 1; + } + _controller.SelectServerIndex(currIndex); + } + + private void GetCurrServerInfo(out int currIndex, out int serverCount) + { + var currConfig = _controller.GetCurrentConfiguration(); + currIndex = currConfig.index; + serverCount = currConfig.configs.Count; + } + + #endregion + } +} diff --git a/shadowsocks-csharp/Util/Hotkeys.cs b/shadowsocks-csharp/Controller/System/Hotkeys/Hotkeys.cs similarity index 96% rename from shadowsocks-csharp/Util/Hotkeys.cs rename to shadowsocks-csharp/Controller/System/Hotkeys/Hotkeys.cs index 5b148a08..450b7764 100644 --- a/shadowsocks-csharp/Util/Hotkeys.cs +++ b/shadowsocks-csharp/Controller/System/Hotkeys/Hotkeys.cs @@ -1,179 +1,181 @@ -using System; -using System.Collections.Generic; -using System.ComponentModel; -using System.Linq; -using System.Windows.Forms; -using System.Windows.Input; -using GlobalHotKey; - -namespace Shadowsocks.Util -{ - public static class HotKeys - { - private static HotKeyManager _hotKeyManager; - - public delegate void HotKeyCallBackHandler(); - // map key and corresponding handler function - private static Dictionary _keymap = new Dictionary(); - - public static void Init() - { - _hotKeyManager = new HotKeyManager(); - _hotKeyManager.KeyPressed += HotKeyManagerPressed; - } - - public static void Destroy() - { - _hotKeyManager.KeyPressed -= HotKeyManagerPressed; - _hotKeyManager.Dispose(); - } - - private static void HotKeyManagerPressed(object sender, KeyPressedEventArgs e) - { - var hotkey = e.HotKey; - HotKeyCallBackHandler callback; - if (_keymap.TryGetValue(hotkey, out callback)) - callback(); - } - - public static bool IsHotkeyExists( HotKey hotKey ) - { - if (hotKey == null) throw new ArgumentNullException(nameof(hotKey)); - return _keymap.Any( v => v.Key.Equals( hotKey ) ); - } - - public static bool IsCallbackExists( HotKeyCallBackHandler cb, out HotKey hotkey) - { - if (cb == null) throw new ArgumentNullException(nameof(cb)); - try - { - var key = _keymap.First(x => x.Value == cb).Key; - hotkey = key; - return true; - } - catch (InvalidOperationException) - { - // not found - hotkey = null; - return false; - } - } - public static string HotKey2Str( HotKey key ) - { - if (key == null) throw new ArgumentNullException(nameof(key)); - return HotKey2Str( key.Key, key.Modifiers ); - } - - public static string HotKey2Str( Key key, ModifierKeys modifier ) - { - if (!Enum.IsDefined(typeof(Key), key)) - throw new InvalidEnumArgumentException(nameof(key), (int) key, typeof(Key)); - try - { - ModifierKeysConverter mkc = new ModifierKeysConverter(); - var keyStr = Enum.GetName(typeof(Key), key); - var modifierStr = mkc.ConvertToInvariantString(modifier); - - return $"{modifierStr}+{keyStr}"; - } - catch (NotSupportedException) - { - // converter exception - return null; - } - } - - public static HotKey Str2HotKey(string s) - { - try - { - if (s.IsNullOrEmpty()) return null; - int offset = s.LastIndexOf("+", StringComparison.OrdinalIgnoreCase); - if (offset <= 0) return null; - string modifierStr = s.Substring(0, offset).Trim(); - string keyStr = s.Substring(offset + 1).Trim(); - - KeyConverter kc = new KeyConverter(); - ModifierKeysConverter mkc = new ModifierKeysConverter(); - Key key = (Key) kc.ConvertFrom(keyStr.ToUpper()); - ModifierKeys modifier = (ModifierKeys) mkc.ConvertFrom(modifierStr.ToUpper()); - - return new HotKey(key, modifier); - } - catch (NotSupportedException) - { - // converter exception - return null; - } - catch (NullReferenceException) - { - return null; - } - } - - public static bool Regist( HotKey key, HotKeyCallBackHandler callBack ) - { - if (key == null) - throw new ArgumentNullException(nameof(key)); - if (callBack == null) - throw new ArgumentNullException(nameof(callBack)); - try - { - _hotKeyManager.Register(key); - _keymap[key] = callBack; - return true; - } - catch (ArgumentException) - { - // already called this method with the specific hotkey - // return success silently - return true; - } - catch (Win32Exception) - { - // this hotkey already registered by other programs - // notify user to change key - return false; - } - } - - public static bool Regist(Key key, ModifierKeys modifiers, HotKeyCallBackHandler callBack) - { - if (!Enum.IsDefined(typeof(Key), key)) - throw new InvalidEnumArgumentException(nameof(key), (int) key, typeof(Key)); - try - { - var hotkey = _hotKeyManager.Register(key, modifiers); - _keymap[hotkey] = callBack; - return true; - } - catch (ArgumentException) - { - // already called this method with the specific hotkey - // return success silently - return true; - } - catch (Win32Exception) - { - // already registered by other programs - // notify user to change key - return false; - } - } - - public static void UnRegist(HotKey key) - { - if (key == null) - throw new ArgumentNullException(nameof(key)); - _hotKeyManager.Unregister(key); - if(_keymap.ContainsKey(key)) - _keymap.Remove(key); - } - - public static IEnumerable GetChildControls(this Control control) where TControl : Control - { - var children = control.Controls.Count > 0 ? control.Controls.OfType() : Enumerable.Empty(); - return children.SelectMany(c => GetChildControls(c)).Concat(children); - } - } +using System; +using System.Collections.Generic; +using System.ComponentModel; +using System.Linq; +using System.Windows.Forms; +using System.Windows.Input; +using GlobalHotKey; + +namespace Shadowsocks.Controller.Hotkeys +{ + public static class HotKeys + { + private static HotKeyManager _hotKeyManager; + + public delegate void HotKeyCallBackHandler(); + // map key and corresponding handler function + private static Dictionary _keymap = new Dictionary(); + + public static void Init(ShadowsocksController controller) + { + _hotKeyManager = new HotKeyManager(); + _hotKeyManager.KeyPressed += HotKeyManagerPressed; + + HotkeyCallbacks.InitInstance(controller); + } + + public static void Destroy() + { + _hotKeyManager.KeyPressed -= HotKeyManagerPressed; + _hotKeyManager.Dispose(); + } + + private static void HotKeyManagerPressed(object sender, KeyPressedEventArgs e) + { + var hotkey = e.HotKey; + HotKeyCallBackHandler callback; + if (_keymap.TryGetValue(hotkey, out callback)) + callback(); + } + + public static bool IsHotkeyExists( HotKey hotKey ) + { + if (hotKey == null) throw new ArgumentNullException(nameof(hotKey)); + return _keymap.Any( v => v.Key.Equals( hotKey ) ); + } + + public static bool IsCallbackExists( HotKeyCallBackHandler cb, out HotKey hotkey) + { + if (cb == null) throw new ArgumentNullException(nameof(cb)); + try + { + var key = _keymap.First(x => x.Value == cb).Key; + hotkey = key; + return true; + } + catch (InvalidOperationException) + { + // not found + hotkey = null; + return false; + } + } + public static string HotKey2Str( HotKey key ) + { + if (key == null) throw new ArgumentNullException(nameof(key)); + return HotKey2Str( key.Key, key.Modifiers ); + } + + public static string HotKey2Str( Key key, ModifierKeys modifier ) + { + if (!Enum.IsDefined(typeof(Key), key)) + throw new InvalidEnumArgumentException(nameof(key), (int) key, typeof(Key)); + try + { + ModifierKeysConverter mkc = new ModifierKeysConverter(); + var keyStr = Enum.GetName(typeof(Key), key); + var modifierStr = mkc.ConvertToInvariantString(modifier); + + return $"{modifierStr}+{keyStr}"; + } + catch (NotSupportedException) + { + // converter exception + return null; + } + } + + public static HotKey Str2HotKey(string s) + { + try + { + if (s.IsNullOrEmpty()) return null; + int offset = s.LastIndexOf("+", StringComparison.OrdinalIgnoreCase); + if (offset <= 0) return null; + string modifierStr = s.Substring(0, offset).Trim(); + string keyStr = s.Substring(offset + 1).Trim(); + + KeyConverter kc = new KeyConverter(); + ModifierKeysConverter mkc = new ModifierKeysConverter(); + Key key = (Key) kc.ConvertFrom(keyStr.ToUpper()); + ModifierKeys modifier = (ModifierKeys) mkc.ConvertFrom(modifierStr.ToUpper()); + + return new HotKey(key, modifier); + } + catch (NotSupportedException) + { + // converter exception + return null; + } + catch (NullReferenceException) + { + return null; + } + } + + public static bool Regist( HotKey key, HotKeyCallBackHandler callBack ) + { + if (key == null) + throw new ArgumentNullException(nameof(key)); + if (callBack == null) + throw new ArgumentNullException(nameof(callBack)); + try + { + _hotKeyManager.Register(key); + _keymap[key] = callBack; + return true; + } + catch (ArgumentException) + { + // already called this method with the specific hotkey + // return success silently + return true; + } + catch (Win32Exception) + { + // this hotkey already registered by other programs + // notify user to change key + return false; + } + } + + public static bool Regist(Key key, ModifierKeys modifiers, HotKeyCallBackHandler callBack) + { + if (!Enum.IsDefined(typeof(Key), key)) + throw new InvalidEnumArgumentException(nameof(key), (int) key, typeof(Key)); + try + { + var hotkey = _hotKeyManager.Register(key, modifiers); + _keymap[hotkey] = callBack; + return true; + } + catch (ArgumentException) + { + // already called this method with the specific hotkey + // return success silently + return true; + } + catch (Win32Exception) + { + // already registered by other programs + // notify user to change key + return false; + } + } + + public static void UnRegist(HotKey key) + { + if (key == null) + throw new ArgumentNullException(nameof(key)); + _hotKeyManager.Unregister(key); + if(_keymap.ContainsKey(key)) + _keymap.Remove(key); + } + + public static IEnumerable GetChildControls(this Control control) where TControl : Control + { + var children = control.Controls.Count > 0 ? control.Controls.OfType() : Enumerable.Empty(); + return children.SelectMany(c => GetChildControls(c)).Concat(children); + } + } } \ No newline at end of file diff --git a/shadowsocks-csharp/Program.cs b/shadowsocks-csharp/Program.cs index 0f4e95a9..c89fb689 100755 --- a/shadowsocks-csharp/Program.cs +++ b/shadowsocks-csharp/Program.cs @@ -6,6 +6,7 @@ using System.Windows.Forms; using Microsoft.Win32; using Shadowsocks.Controller; +using Shadowsocks.Controller.Hotkeys; using Shadowsocks.Util; using Shadowsocks.View; diff --git a/shadowsocks-csharp/View/HotkeySettingsForm.cs b/shadowsocks-csharp/View/HotkeySettingsForm.cs index 69225dc6..5b6abcdb 100644 --- a/shadowsocks-csharp/View/HotkeySettingsForm.cs +++ b/shadowsocks-csharp/View/HotkeySettingsForm.cs @@ -7,6 +7,7 @@ using System.Text; using System.Windows.Forms; using Shadowsocks.Controller; +using Shadowsocks.Controller.Hotkeys; using Shadowsocks.Model; using Shadowsocks.Properties; using Shadowsocks.Util; @@ -243,81 +244,7 @@ namespace Shadowsocks.View _controller.SaveHotkeyConfig(_modifiedConfig); } - #region Callbacks - private void SwitchSystemProxyCallback() - { - bool enabled = _controller.GetConfigurationCopy().enabled; - _controller.ToggleEnable(!enabled); - } - - private void SwitchProxyModeCallback() - { - var config = _controller.GetConfigurationCopy(); - if (config.enabled == false) return; - var currStatus = config.global; - _controller.ToggleGlobal(!currStatus); - } - - private void SwitchAllowLanCallback() - { - var status = _controller.GetConfigurationCopy().shareOverLan; - _controller.ToggleShareOverLAN(!status); - } - - private void ShowLogsCallback() - { - // Get the current MenuViewController in this program via reflection - FieldInfo fi = Assembly.GetExecutingAssembly().GetType("Shadowsocks.Program") - .GetField("_viewController", - BindingFlags.NonPublic | BindingFlags.Static | BindingFlags.IgnoreCase); - // To retrieve the value of a static field, pass null here - var mvc = fi.GetValue(null) as MenuViewController; - mvc.ShowLogForm_HotKey(); - } - - private void ServerMoveUpCallback() - { - int currIndex; - int serverCount; - GetCurrServerInfo(out currIndex, out serverCount); - if (currIndex - 1 < 0) - { - // revert to last server - currIndex = serverCount - 1; - } - else - { - currIndex -= 1; - } - _controller.SelectServerIndex(currIndex); - } - - private void ServerMoveDownCallback() - { - int currIndex; - int serverCount; - GetCurrServerInfo(out currIndex, out serverCount); - if (currIndex + 1 == serverCount) - { - // revert to first server - currIndex = 0; - } - else - { - currIndex += 1; - } - _controller.SelectServerIndex(currIndex); - } - - private void GetCurrServerInfo(out int currIndex, out int serverCount) - { - var currConfig = _controller.GetCurrentConfiguration(); - currIndex = currConfig.index; - serverCount = currConfig.configs.Count; - } - - #endregion #region Prepare hotkey @@ -343,7 +270,7 @@ namespace Shadowsocks.View var labelName = rawName + "Label"; var callbackName = rawName + "Callback"; - var callback = GetDelegateViaMethodName(this.GetType(), callbackName); + var callback = GetDelegateViaMethodName(callbackName); if (callback == null) { throw new Exception($"{callbackName} not found"); @@ -381,15 +308,12 @@ namespace Shadowsocks.View /// /// /// - private Delegate GetDelegateViaMethodName(Type type, string methodname) + private Delegate GetDelegateViaMethodName(string methodname) { - if (type == null) throw new ArgumentNullException(nameof(type)); if (methodname.IsNullOrEmpty()) throw new ArgumentException(nameof(methodname)); - //HotkeySettingsForm form = new HotkeySettingsForm(_controller); - Type delegateType = Type.GetType("Shadowsocks.Util.HotKeys").GetNestedType("HotKeyCallBackHandler"); - MethodInfo dynMethod = type.GetMethod(methodname, + MethodInfo dynMethod = typeof(HotkeyCallbacks).GetMethod(methodname, BindingFlags.NonPublic | BindingFlags.Instance | BindingFlags.IgnoreCase); - return dynMethod == null ? null : Delegate.CreateDelegate(delegateType, this, dynMethod); + return dynMethod == null ? null : Delegate.CreateDelegate(typeof(HotKeys.HotKeyCallBackHandler), HotkeyCallbacks.Instance, dynMethod); } #endregion diff --git a/shadowsocks-csharp/shadowsocks-csharp.csproj b/shadowsocks-csharp/shadowsocks-csharp.csproj index 522a65c8..f816dfc0 100644 --- a/shadowsocks-csharp/shadowsocks-csharp.csproj +++ b/shadowsocks-csharp/shadowsocks-csharp.csproj @@ -139,6 +139,7 @@ + @@ -186,7 +187,7 @@ - + diff --git a/test/UnitTest.cs b/test/UnitTest.cs index 539140d6..38f7dae2 100755 --- a/test/UnitTest.cs +++ b/test/UnitTest.cs @@ -2,11 +2,11 @@ using Microsoft.VisualStudio.TestTools.UnitTesting; using Shadowsocks.Controller; using Shadowsocks.Encryption; -using Shadowsocks.Util; using GlobalHotKey; using System.Windows.Input; using System.Threading; using System.Collections.Generic; +using Shadowsocks.Controller.Hotkeys; namespace test {