Browse Source

Fix hotkey unregister issue.

Use singleton mode for hotkey callbacks.
tags/3.3.6
noisyfox 8 years ago
parent
commit
63816ccdd1
6 changed files with 292 additions and 261 deletions
  1. +103
    -0
      shadowsocks-csharp/Controller/System/Hotkeys/HotkeyCallbacks.cs
  2. +180
    -178
      shadowsocks-csharp/Controller/System/Hotkeys/Hotkeys.cs
  3. +1
    -0
      shadowsocks-csharp/Program.cs
  4. +5
    -81
      shadowsocks-csharp/View/HotkeySettingsForm.cs
  5. +2
    -1
      shadowsocks-csharp/shadowsocks-csharp.csproj
  6. +1
    -1
      test/UnitTest.cs

+ 103
- 0
shadowsocks-csharp/Controller/System/Hotkeys/HotkeyCallbacks.cs View File

@@ -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
}
}

shadowsocks-csharp/Util/Hotkeys.cs → shadowsocks-csharp/Controller/System/Hotkeys/Hotkeys.cs View File

@@ -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<HotKey, HotKeyCallBackHandler> _keymap = new Dictionary<HotKey, HotKeyCallBackHandler>();
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<TControl> GetChildControls<TControl>(this Control control) where TControl : Control
{
var children = control.Controls.Count > 0 ? control.Controls.OfType<TControl>() : Enumerable.Empty<TControl>();
return children.SelectMany(c => GetChildControls<TControl>(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<HotKey, HotKeyCallBackHandler> _keymap = new Dictionary<HotKey, HotKeyCallBackHandler>();

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<TControl> GetChildControls<TControl>(this Control control) where TControl : Control
{
var children = control.Controls.Count > 0 ? control.Controls.OfType<TControl>() : Enumerable.Empty<TControl>();
return children.SelectMany(c => GetChildControls<TControl>(c)).Concat(children);
}
}
}

+ 1
- 0
shadowsocks-csharp/Program.cs View File

@@ -6,6 +6,7 @@ using System.Windows.Forms;
using Microsoft.Win32;
using Shadowsocks.Controller;
using Shadowsocks.Controller.Hotkeys;
using Shadowsocks.Util;
using Shadowsocks.View;


+ 5
- 81
shadowsocks-csharp/View/HotkeySettingsForm.cs View File

@@ -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
/// <param name="type"></param>
/// <param name="methodname"></param>
/// <returns></returns>
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


+ 2
- 1
shadowsocks-csharp/shadowsocks-csharp.csproj View File

@@ -139,6 +139,7 @@
<Compile Include="3rd\zxing\ResultPoint.cs" />
<Compile Include="3rd\zxing\ResultPointCallback.cs" />
<Compile Include="3rd\zxing\WriterException.cs" />
<Compile Include="Controller\System\Hotkeys\HotkeyCallbacks.cs" />
<Compile Include="Model\HotKeyConfig.cs" />
<Compile Include="Model\ProxyConfig.cs" />
<Compile Include="Properties\Settings.Designer.cs">
@@ -186,7 +187,7 @@
<Compile Include="Proxy\Socks5Proxy.cs" />
<Compile Include="Settings.cs" />
<Compile Include="StringEx.cs" />
<Compile Include="Util\Hotkeys.cs" />
<Compile Include="Controller\System\Hotkeys\Hotkeys.cs" />
<Compile Include="Util\ProcessManagement\Job.cs" />
<Compile Include="Util\ProcessManagement\ThreadUtil.cs" />
<Compile Include="Encryption\RNG.cs" />


+ 1
- 1
test/UnitTest.cs View File

@@ -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
{


Loading…
Cancel
Save