noisyfox 8 years ago
parent
commit
2a12ac5578
25 changed files with 1707 additions and 340 deletions
  1. +6
    -6
      nuget.config
  2. +83
    -87
      shadowsocks-csharp/Controller/Service/TCPRelay.cs
  3. +40
    -4
      shadowsocks-csharp/Controller/ShadowsocksController.cs
  4. +14
    -0
      shadowsocks-csharp/Data/cn.txt
  5. +16
    -2
      shadowsocks-csharp/Data/zh_tw.txt
  6. +16
    -0
      shadowsocks-csharp/Encryption/EncryptorBase.cs
  7. +8
    -10
      shadowsocks-csharp/Encryption/IVEncryptor.cs
  8. +13
    -13
      shadowsocks-csharp/Encryption/MbedTLSEncryptor.cs
  9. +5
    -5
      shadowsocks-csharp/Encryption/SodiumEncryptor.cs
  10. +9
    -4
      shadowsocks-csharp/Model/Configuration.cs
  11. +33
    -0
      shadowsocks-csharp/Model/HotKeyConfig.cs
  12. +22
    -0
      shadowsocks-csharp/Model/ProxyConfig.cs
  13. +13
    -9
      shadowsocks-csharp/Program.cs
  14. +174
    -0
      shadowsocks-csharp/Util/Hotkeys.cs
  15. +29
    -15
      shadowsocks-csharp/Util/Util.cs
  16. +406
    -0
      shadowsocks-csharp/View/HotkeySettingsForm.cs
  17. +354
    -0
      shadowsocks-csharp/View/HotkeySettingsForm.designer.cs
  18. +183
    -0
      shadowsocks-csharp/View/HotkeySettingsForm.resx
  19. +41
    -32
      shadowsocks-csharp/View/LogForm.cs
  20. +66
    -37
      shadowsocks-csharp/View/MenuViewController.cs
  21. +125
    -116
      shadowsocks-csharp/View/ProxyForm.cs
  22. +1
    -0
      shadowsocks-csharp/packages.config
  23. +16
    -0
      shadowsocks-csharp/shadowsocks-csharp.csproj
  24. +29
    -0
      test/UnitTest.cs
  25. +5
    -0
      test/test.csproj

+ 6
- 6
nuget.config View File

@@ -1,6 +1,6 @@
<?xml version="1.0" encoding="utf-8"?>
<configuration>
<config>
<add key="repositoryPath" value="shadowsocks-csharp\3rd" />
</config>
</configuration>
<?xml version="1.0" encoding="utf-8"?>
<configuration>
<config>
<add key="repositoryPath" value="shadowsocks-csharp\3rd" />
</config>
</configuration>

+ 83
- 87
shadowsocks-csharp/Controller/Service/TCPRelay.cs View File

@@ -35,10 +35,7 @@ namespace Shadowsocks.Controller
|| (length < 2 || firstPacket[0] != 5)) || (length < 2 || firstPacket[0] != 5))
return false; return false;
socket.SetSocketOption(SocketOptionLevel.Tcp, SocketOptionName.NoDelay, true); socket.SetSocketOption(SocketOptionLevel.Tcp, SocketOptionName.NoDelay, true);
TCPHandler handler = new TCPHandler(this, _config);
handler.connection = socket;
handler.controller = _controller;
handler.tcprelay = this;
TCPHandler handler = new TCPHandler(_controller, _config, this, socket);
IList<TCPHandler> handlersToClose = new List<TCPHandler>(); IList<TCPHandler> handlersToClose = new List<TCPHandler>();
lock (Handlers) lock (Handlers)
@@ -130,68 +127,67 @@ namespace Shadowsocks.Controller
public static readonly int RecvReserveSize = IVEncryptor.ONETIMEAUTH_BYTES + IVEncryptor.AUTH_BYTES; // reserve for one-time auth public static readonly int RecvReserveSize = IVEncryptor.ONETIMEAUTH_BYTES + IVEncryptor.AUTH_BYTES; // reserve for one-time auth
public static readonly int BufferSize = RecvSize + RecvReserveSize + 32; public static readonly int BufferSize = RecvSize + RecvReserveSize + 32;
// public Encryptor encryptor;
public IEncryptor encryptor;
public Server server;
// Client socket.
private AsyncSession _currentRemoteSession;
public DateTime lastActivity;
public Socket connection;
public ShadowsocksController controller;
public TCPRelay tcprelay;
private ShadowsocksController _controller;
private Configuration _config;
private TCPRelay _tcprelay;
private Socket _connection;
public DateTime lastActivity;
private IEncryptor _encryptor;
private Server _server;
private const int MaxRetry = 4;
private int _retryCount = 0;
private bool _proxyConnected;
private bool _destConnected;
private AsyncSession _currentRemoteSession;
private const int MaxRetry = 4;
private int _retryCount = 0;
private bool _proxyConnected;
private bool _destConnected;
private byte _command;
private byte[] _firstPacket;
private int _firstPacketLength;
private byte _command;
private byte[] _firstPacket;
private int _firstPacketLength;
private int _totalRead = 0;
private int _totalWrite = 0;
private int _totalRead = 0;
private int _totalWrite = 0;
private byte[] _remoteRecvBuffer = new byte[BufferSize];
private byte[] _remoteSendBuffer = new byte[BufferSize];
private byte[] _connetionRecvBuffer = new byte[BufferSize];
private byte[] _connetionSendBuffer = new byte[BufferSize];
private byte[] _remoteRecvBuffer = new byte[BufferSize];
private byte[] _remoteSendBuffer = new byte[BufferSize];
private byte[] _connetionRecvBuffer = new byte[BufferSize];
private byte[] _connetionSendBuffer = new byte[BufferSize];
private bool _connectionShutdown = false;
private bool _remoteShutdown = false;
private bool _closed = false;
private bool _connectionShutdown = false;
private bool _remoteShutdown = false;
private bool _closed = false;
private object _encryptionLock = new object();
private object _decryptionLock = new object();
private object _encryptionLock = new object();
private object _decryptionLock = new object();
private DateTime _startConnectTime; private DateTime _startConnectTime;
private DateTime _startReceivingTime; private DateTime _startReceivingTime;
private DateTime _startSendingTime; private DateTime _startSendingTime;
private int _bytesToSend;
private TCPRelay _tcprelay; // TODO: is _tcprelay equals tcprelay declared above?
private Configuration _config;
public TCPHandler(TCPRelay tcprelay, Configuration config)
public TCPHandler(ShadowsocksController controller, Configuration config, TCPRelay tcprelay, Socket socket)
{ {
this._tcprelay = tcprelay;
this._controller = controller;
this._config = config; this._config = config;
this._tcprelay = tcprelay;
this._connection = socket;
} }
public void CreateRemote() public void CreateRemote()
{ {
Server server = controller.GetAServer(IStrategyCallerType.TCP, (IPEndPoint)connection.RemoteEndPoint);
Server server = _controller.GetAServer(IStrategyCallerType.TCP, (IPEndPoint)_connection.RemoteEndPoint);
if (server == null || server.server == "") if (server == null || server.server == "")
throw new ArgumentException("No server configured"); throw new ArgumentException("No server configured");
lock (_encryptionLock) lock (_encryptionLock)
{ {
lock (_decryptionLock) lock (_decryptionLock)
{ {
encryptor = EncryptorFactory.GetEncryptor(server.method, server.password, server.auth, false);
_encryptor = EncryptorFactory.GetEncryptor(server.method, server.password, server.auth, false);
} }
} }
this.server = server;
this._server = server;
} }
public void Start(byte[] firstPacket, int length) public void Start(byte[] firstPacket, int length)
@@ -210,18 +206,19 @@ namespace Shadowsocks.Controller
public void Close() public void Close()
{ {
lock (tcprelay.Handlers)
lock (this)
{ {
tcprelay.Handlers.Remove(this);
}
lock (this) {
if (_closed) return; if (_closed) return;
_closed = true; _closed = true;
} }
lock (_tcprelay.Handlers)
{
_tcprelay.Handlers.Remove(this);
}
try try
{ {
connection?.Shutdown(SocketShutdown.Both);
connection?.Close();
_connection?.Shutdown(SocketShutdown.Both);
_connection?.Close();
} }
catch (Exception e) catch (Exception e)
{ {
@@ -241,7 +238,7 @@ namespace Shadowsocks.Controller
{ {
lock (_decryptionLock) lock (_decryptionLock)
{ {
encryptor?.Dispose();
_encryptor?.Dispose();
} }
} }
} }
@@ -261,7 +258,7 @@ namespace Shadowsocks.Controller
response = new byte[] { 0, 91 }; response = new byte[] { 0, 91 };
Logging.Error("socks 5 protocol error"); Logging.Error("socks 5 protocol error");
} }
connection?.BeginSend(response, 0, response.Length, SocketFlags.None, new AsyncCallback(HandshakeSendCallback), null);
_connection?.BeginSend(response, 0, response.Length, SocketFlags.None, new AsyncCallback(HandshakeSendCallback), null);
} }
else else
Close(); Close();
@@ -278,7 +275,7 @@ namespace Shadowsocks.Controller
if (_closed) return; if (_closed) return;
try try
{ {
connection.EndSend(ar);
_connection.EndSend(ar);
// +-----+-----+-------+------+----------+----------+ // +-----+-----+-------+------+----------+----------+
// | VER | CMD | RSV | ATYP | DST.ADDR | DST.PORT | // | VER | CMD | RSV | ATYP | DST.ADDR | DST.PORT |
@@ -287,7 +284,7 @@ namespace Shadowsocks.Controller
// +-----+-----+-------+------+----------+----------+ // +-----+-----+-------+------+----------+----------+
// Skip first 3 bytes // Skip first 3 bytes
// TODO validate // TODO validate
connection.BeginReceive(_connetionRecvBuffer, 0, 3, SocketFlags.None, new AsyncCallback(handshakeReceive2Callback), null);
_connection.BeginReceive(_connetionRecvBuffer, 0, 3, SocketFlags.None, new AsyncCallback(handshakeReceive2Callback), null);
} }
catch (Exception e) catch (Exception e)
{ {
@@ -301,14 +298,14 @@ namespace Shadowsocks.Controller
if (_closed) return; if (_closed) return;
try try
{ {
int bytesRead = connection.EndReceive(ar);
int bytesRead = _connection.EndReceive(ar);
if (bytesRead >= 3) if (bytesRead >= 3)
{ {
_command = _connetionRecvBuffer[1]; _command = _connetionRecvBuffer[1];
if (_command == 1) if (_command == 1)
{ {
byte[] response = { 5, 0, 0, 1, 0, 0, 0, 0, 0, 0 }; byte[] response = { 5, 0, 0, 1, 0, 0, 0, 0, 0, 0 };
connection.BeginSend(response, 0, response.Length, SocketFlags.None, new AsyncCallback(ResponseCallback), null);
_connection.BeginSend(response, 0, response.Length, SocketFlags.None, new AsyncCallback(ResponseCallback), null);
} }
else if (_command == 3) else if (_command == 3)
HandleUDPAssociate(); HandleUDPAssociate();
@@ -328,7 +325,7 @@ namespace Shadowsocks.Controller
private void HandleUDPAssociate() private void HandleUDPAssociate()
{ {
IPEndPoint endPoint = (IPEndPoint)connection.LocalEndPoint;
IPEndPoint endPoint = (IPEndPoint)_connection.LocalEndPoint;
byte[] address = endPoint.Address.GetAddressBytes(); byte[] address = endPoint.Address.GetAddressBytes();
int port = endPoint.Port; int port = endPoint.Port;
byte[] response = new byte[4 + address.Length + 2]; byte[] response = new byte[4 + address.Length + 2];
@@ -345,7 +342,7 @@ namespace Shadowsocks.Controller
address.CopyTo(response, 4); address.CopyTo(response, 4);
response[response.Length - 1] = (byte)(port & 0xFF); response[response.Length - 1] = (byte)(port & 0xFF);
response[response.Length - 2] = (byte)((port >> 8) & 0xFF); response[response.Length - 2] = (byte)((port >> 8) & 0xFF);
connection.BeginSend(response, 0, response.Length, SocketFlags.None, new AsyncCallback(ReadAll), true);
_connection.BeginSend(response, 0, response.Length, SocketFlags.None, new AsyncCallback(ReadAll), true);
} }
private void ReadAll(IAsyncResult ar) private void ReadAll(IAsyncResult ar)
@@ -355,15 +352,15 @@ namespace Shadowsocks.Controller
{ {
if (ar.AsyncState != null) if (ar.AsyncState != null)
{ {
connection.EndSend(ar);
connection.BeginReceive(_connetionRecvBuffer, 0, RecvSize, SocketFlags.None, new AsyncCallback(ReadAll), null);
_connection.EndSend(ar);
_connection.BeginReceive(_connetionRecvBuffer, 0, RecvSize, SocketFlags.None, new AsyncCallback(ReadAll), null);
} }
else else
{ {
int bytesRead = connection.EndReceive(ar);
int bytesRead = _connection.EndReceive(ar);
if (bytesRead > 0) if (bytesRead > 0)
{ {
connection.BeginReceive(_connetionRecvBuffer, 0, RecvSize, SocketFlags.None, new AsyncCallback(ReadAll), null);
_connection.BeginReceive(_connetionRecvBuffer, 0, RecvSize, SocketFlags.None, new AsyncCallback(ReadAll), null);
} }
else else
Close(); Close();
@@ -380,7 +377,7 @@ namespace Shadowsocks.Controller
{ {
try try
{ {
connection?.EndSend(ar);
_connection?.EndSend(ar);
StartConnect(); StartConnect();
} }
catch (Exception e) catch (Exception e)
@@ -420,10 +417,10 @@ namespace Shadowsocks.Controller
// Setting up proxy // Setting up proxy
IProxy remote; IProxy remote;
EndPoint proxyEP; EndPoint proxyEP;
if (_config.useProxy)
if (_config.proxy.useProxy)
{ {
remote = new Socks5Proxy(); remote = new Socks5Proxy();
proxyEP = SocketUtil.GetEndPoint(_config.proxyServer, _config.proxyPort);
proxyEP = SocketUtil.GetEndPoint(_config.proxy.proxyServer, _config.proxy.proxyPort);
} }
else else
{ {
@@ -440,8 +437,8 @@ namespace Shadowsocks.Controller
proxyTimer.Enabled = true; proxyTimer.Enabled = true;
proxyTimer.Session = session; proxyTimer.Session = session;
proxyTimer.DestEndPoint = SocketUtil.GetEndPoint(server.server, server.server_port);
proxyTimer.Server = server;
proxyTimer.DestEndPoint = SocketUtil.GetEndPoint(_server.server, _server.server_port);
proxyTimer.Server = _server;
_proxyConnected = false; _proxyConnected = false;
@@ -542,7 +539,7 @@ namespace Shadowsocks.Controller
var session = timer.Session; var session = timer.Session;
Server server = timer.Server; Server server = timer.Server;
IStrategy strategy = controller.GetCurrentStrategy();
IStrategy strategy = _controller.GetCurrentStrategy();
strategy?.SetFailure(server); strategy?.SetFailure(server);
Logging.Info($"{server.FriendlyName()} timed out"); Logging.Info($"{server.FriendlyName()} timed out");
session.Remote.Close(); session.Remote.Close();
@@ -568,7 +565,7 @@ namespace Shadowsocks.Controller
{ {
var session = (AsyncSession<ServerTimer>) ar.AsyncState; var session = (AsyncSession<ServerTimer>) ar.AsyncState;
ServerTimer timer = session.State; ServerTimer timer = session.State;
server = timer.Server;
_server = timer.Server;
timer.Elapsed -= destConnectTimer_Elapsed; timer.Elapsed -= destConnectTimer_Elapsed;
timer.Enabled = false; timer.Enabled = false;
timer.Dispose(); timer.Dispose();
@@ -581,13 +578,13 @@ namespace Shadowsocks.Controller
if (_config.isVerboseLogging) if (_config.isVerboseLogging)
{ {
Logging.Info($"Socket connected to ss server: {server.FriendlyName()}");
Logging.Info($"Socket connected to ss server: {_server.FriendlyName()}");
} }
var latency = DateTime.Now - _startConnectTime; var latency = DateTime.Now - _startConnectTime;
IStrategy strategy = controller.GetCurrentStrategy();
strategy?.UpdateLatency(server, latency);
_tcprelay.UpdateLatency(server, latency);
IStrategy strategy = _controller.GetCurrentStrategy();
strategy?.UpdateLatency(_server, latency);
_tcprelay.UpdateLatency(_server, latency);
StartPipe(session); StartPipe(session);
} }
@@ -596,10 +593,10 @@ namespace Shadowsocks.Controller
} }
catch (Exception e) catch (Exception e)
{ {
if (server != null)
if (_server != null)
{ {
IStrategy strategy = controller.GetCurrentStrategy();
strategy?.SetFailure(server);
IStrategy strategy = _controller.GetCurrentStrategy();
strategy?.SetFailure(_server);
} }
Logging.LogUsefulException(e); Logging.LogUsefulException(e);
RetryConnect(); RetryConnect();
@@ -613,7 +610,7 @@ namespace Shadowsocks.Controller
{ {
_startReceivingTime = DateTime.Now; _startReceivingTime = DateTime.Now;
session.Remote.BeginReceive(_remoteRecvBuffer, 0, RecvSize, SocketFlags.None, new AsyncCallback(PipeRemoteReceiveCallback), session); session.Remote.BeginReceive(_remoteRecvBuffer, 0, RecvSize, SocketFlags.None, new AsyncCallback(PipeRemoteReceiveCallback), session);
connection?.BeginReceive(_connetionRecvBuffer, 0, RecvSize, SocketFlags.None, new AsyncCallback(PipeConnectionReceiveCallback),
_connection?.BeginReceive(_connetionRecvBuffer, 0, RecvSize, SocketFlags.None, new AsyncCallback(PipeConnectionReceiveCallback),
new AsyncSession<bool>(session, true) /* to tell the callback this is the first time reading packet, and we haven't found the header yet. */); new AsyncSession<bool>(session, true) /* to tell the callback this is the first time reading packet, and we haven't found the header yet. */);
} }
catch (Exception e) catch (Exception e)
@@ -631,7 +628,7 @@ namespace Shadowsocks.Controller
var session = (AsyncSession) ar.AsyncState; var session = (AsyncSession) ar.AsyncState;
int bytesRead = session.Remote.EndReceive(ar); int bytesRead = session.Remote.EndReceive(ar);
_totalRead += bytesRead; _totalRead += bytesRead;
_tcprelay.UpdateInboundCounter(server, bytesRead);
_tcprelay.UpdateInboundCounter(_server, bytesRead);
if (bytesRead > 0) if (bytesRead > 0)
{ {
lastActivity = DateTime.Now; lastActivity = DateTime.Now;
@@ -639,15 +636,15 @@ namespace Shadowsocks.Controller
lock (_decryptionLock) lock (_decryptionLock)
{ {
if (_closed) return; if (_closed) return;
encryptor.Decrypt(_remoteRecvBuffer, bytesRead, _remoteSendBuffer, out bytesToSend);
_encryptor.Decrypt(_remoteRecvBuffer, bytesRead, _remoteSendBuffer, out bytesToSend);
} }
connection.BeginSend(_remoteSendBuffer, 0, bytesToSend, SocketFlags.None, new AsyncCallback(PipeConnectionSendCallback), session);
IStrategy strategy = controller.GetCurrentStrategy();
strategy?.UpdateLastRead(server);
_connection.BeginSend(_remoteSendBuffer, 0, bytesToSend, SocketFlags.None, new AsyncCallback(PipeConnectionSendCallback), session);
IStrategy strategy = _controller.GetCurrentStrategy();
strategy?.UpdateLastRead(_server);
} }
else else
{ {
connection.Shutdown(SocketShutdown.Send);
_connection.Shutdown(SocketShutdown.Send);
_connectionShutdown = true; _connectionShutdown = true;
CheckClose(); CheckClose();
} }
@@ -664,8 +661,8 @@ namespace Shadowsocks.Controller
if (_closed) return; if (_closed) return;
try try
{ {
if(connection == null) return;
int bytesRead = connection.EndReceive(ar);
if(_connection == null) return;
int bytesRead = _connection.EndReceive(ar);
_totalWrite += bytesRead; _totalWrite += bytesRead;
var session = (AsyncSession<bool>) ar.AsyncState; var session = (AsyncSession<bool>) ar.AsyncState;
@@ -713,14 +710,13 @@ namespace Shadowsocks.Controller
lock (_encryptionLock) lock (_encryptionLock)
{ {
if (_closed) return; if (_closed) return;
encryptor.Encrypt(_connetionRecvBuffer, bytesRead, _connetionSendBuffer, out bytesToSend);
_encryptor.Encrypt(_connetionRecvBuffer, bytesRead, _connetionSendBuffer, out bytesToSend);
} }
_tcprelay.UpdateOutboundCounter(server, bytesToSend);
_tcprelay.UpdateOutboundCounter(_server, bytesToSend);
_startSendingTime = DateTime.Now; _startSendingTime = DateTime.Now;
_bytesToSend = bytesToSend;
remote.BeginSend(_connetionSendBuffer, 0, bytesToSend, SocketFlags.None, new AsyncCallback(PipeRemoteSendCallback), session); remote.BeginSend(_connetionSendBuffer, 0, bytesToSend, SocketFlags.None, new AsyncCallback(PipeRemoteSendCallback), session);
IStrategy strategy = controller.GetCurrentStrategy();
strategy?.UpdateLastWrite(server);
IStrategy strategy = _controller.GetCurrentStrategy();
strategy?.UpdateLastWrite(_server);
} }
else else
{ {
@@ -743,7 +739,7 @@ namespace Shadowsocks.Controller
{ {
var session = (AsyncSession)ar.AsyncState; var session = (AsyncSession)ar.AsyncState;
session.Remote.EndSend(ar); session.Remote.EndSend(ar);
connection?.BeginReceive(_connetionRecvBuffer, 0, RecvSize, SocketFlags.None, new AsyncCallback(PipeConnectionReceiveCallback), session);
_connection?.BeginReceive(_connetionRecvBuffer, 0, RecvSize, SocketFlags.None, new AsyncCallback(PipeConnectionReceiveCallback), session);
} }
catch (Exception e) catch (Exception e)
{ {
@@ -758,7 +754,7 @@ namespace Shadowsocks.Controller
try try
{ {
var session = (AsyncSession)ar.AsyncState; var session = (AsyncSession)ar.AsyncState;
connection?.EndSend(ar);
_connection?.EndSend(ar);
session.Remote.BeginReceive(_remoteRecvBuffer, 0, RecvSize, SocketFlags.None, new AsyncCallback(PipeRemoteReceiveCallback), session); session.Remote.BeginReceive(_remoteRecvBuffer, 0, RecvSize, SocketFlags.None, new AsyncCallback(PipeRemoteReceiveCallback), session);
} }
catch (Exception e) catch (Exception e)


+ 40
- 4
shadowsocks-csharp/Controller/ShadowsocksController.cs View File

@@ -217,15 +217,15 @@ namespace Shadowsocks.Controller
public void DisableProxy() public void DisableProxy()
{ {
_config.useProxy = false;
_config.proxy.useProxy = false;
SaveConfig(_config); SaveConfig(_config);
} }
public void EnableProxy(string proxy, int port) public void EnableProxy(string proxy, int port)
{ {
_config.useProxy = true;
_config.proxyServer = proxy;
_config.proxyPort = port;
_config.proxy.useProxy = true;
_config.proxy.proxyServer = proxy;
_config.proxy.proxyPort = port;
SaveConfig(_config); SaveConfig(_config);
} }
@@ -348,12 +348,40 @@ namespace Shadowsocks.Controller
{ {
_config.autoCheckUpdate = enabled; _config.autoCheckUpdate = enabled;
Configuration.Save(_config); Configuration.Save(_config);
if (ConfigChanged != null)
{
ConfigChanged(this, new EventArgs());
}
} }
public void SaveLogViewerConfig(LogViewerConfig newConfig) public void SaveLogViewerConfig(LogViewerConfig newConfig)
{ {
_config.logViewer = newConfig; _config.logViewer = newConfig;
Configuration.Save(_config); Configuration.Save(_config);
if (ConfigChanged != null)
{
ConfigChanged(this, new EventArgs());
}
}
public void SaveProxyConfig(ProxyConfig newConfig)
{
_config.proxy = newConfig;
Configuration.Save(_config);
if (ConfigChanged != null)
{
ConfigChanged(this, new EventArgs());
}
}
public void SaveHotkeyConfig(HotkeyConfig newConfig)
{
_config.hotkey = newConfig;
SaveConfig(_config);
if (ConfigChanged != null)
{
ConfigChanged(this, new EventArgs());
}
} }
public void UpdateLatency(Server server, TimeSpan latency) public void UpdateLatency(Server server, TimeSpan latency)
@@ -547,6 +575,8 @@ namespace Shadowsocks.Controller
File.WriteAllText(PACServer.PAC_FILE, abpContent, Encoding.UTF8); File.WriteAllText(PACServer.PAC_FILE, abpContent, Encoding.UTF8);
} }
#region Memory Management
private void StartReleasingMemory() private void StartReleasingMemory()
{ {
_ramThread = new Thread(new ThreadStart(ReleaseMemory)); _ramThread = new Thread(new ThreadStart(ReleaseMemory));
@@ -563,6 +593,10 @@ namespace Shadowsocks.Controller
} }
} }
#endregion
#region Traffic Statistics
private void StartTrafficStatistics(int queueMaxSize) private void StartTrafficStatistics(int queueMaxSize)
{ {
traffic = new QueueLast<TrafficPerSecond>(); traffic = new QueueLast<TrafficPerSecond>();
@@ -597,5 +631,7 @@ namespace Shadowsocks.Controller
} }
} }
#endregion
} }
} }

+ 14
- 0
shadowsocks-csharp/Data/cn.txt View File

@@ -27,6 +27,7 @@ Verbose Logging=详细记录日志
Updates...=更新... Updates...=更新...
Check for Updates...=检查更新 Check for Updates...=检查更新
Check for Updates at Startup=启动时检查更新 Check for Updates at Startup=启动时检查更新
Edit Hotkeys...=编辑快捷键...
About...=关于... About...=关于...
Quit=退出 Quit=退出
Edit Servers=编辑服务器 Edit Servers=编辑服务器
@@ -84,6 +85,17 @@ Edit Online PAC URL=编辑在线 PAC 网址
Edit Online PAC URL...=编辑在线 PAC 网址... Edit Online PAC URL...=编辑在线 PAC 网址...
Please input PAC Url=请输入 PAC 网址 Please input PAC Url=请输入 PAC 网址


# HotkeySettings Form

Switch system proxy=切换系统代理状态
Switch to PAC mode=切换到PAC模式
Switch to Global mode=切换到全局模式
Switch share over LAN=切换局域网共享
Show Logs=显示日志
Switch to prev server=切换上个服务器
Switch to next server=切换下个服务器
Reg All=注册全部热键

# Messages # Messages


Shadowsocks Error: {0}=Shadowsocks 错误: {0} Shadowsocks Error: {0}=Shadowsocks 错误: {0}
@@ -116,3 +128,5 @@ Unexpected error, shadowsocks will exit. Please report to=非预期错误,Shad
Unsupported operating system, use Windows Vista at least.=不支持的操作系统版本,最低需求为Windows Vista。 Unsupported operating system, use Windows Vista at least.=不支持的操作系统版本,最低需求为Windows Vista。
Proxy request failed=代理请求失败 Proxy request failed=代理请求失败
Proxy handshake failed=代理握手失败 Proxy handshake failed=代理握手失败
Register hotkey failed=注册热键失败
Cannot parse hotkey: {0}=解析热键失败: {0}

+ 16
- 2
shadowsocks-csharp/Data/zh_tw.txt View File

@@ -27,6 +27,7 @@ Verbose Logging=詳細記錄日誌
Updates...=更新... Updates...=更新...
Check for Updates...=檢查更新 Check for Updates...=檢查更新
Check for Updates at Startup=啟動時檢查更新 Check for Updates at Startup=啟動時檢查更新
Edit Hotkeys...=編輯熱鍵...
About...=關於... About...=關於...
Quit=退出 Quit=退出
Edit Servers=編輯伺服器 Edit Servers=編輯伺服器
@@ -84,12 +85,23 @@ Edit Online PAC URL=編輯在線 PAC 網址
Edit Online PAC URL...=編輯在線 PAC 網址... Edit Online PAC URL...=編輯在線 PAC 網址...
Please input PAC Url=請輸入 PAC 網址 Please input PAC Url=請輸入 PAC 網址


# HotkeySettings Form

Switch system proxy=切换系統代理狀態
Switch to PAC mode=切换為PAC模式
Switch to Global mode=切换為全局模式
Switch share over LAN=切换局域網共享
Show Logs=顯示日誌
Switch to prev server=切换上個伺服器
Switch to next server=切换下個伺服器
Reg All=註冊全部熱鍵

# Messages # Messages


Shadowsocks Error: {0}=Shadowsocks 錯誤: {0} Shadowsocks Error: {0}=Shadowsocks 錯誤: {0}
Port already in use=連接埠號碼已被占用 Port already in use=連接埠號碼已被占用
Illegal port number format=非法連接埠號碼格式 Illegal port number format=非法連接埠號碼格式
Please add at least one server=請新增至少壹個伺服器
Please add at least one server=請新增至少個伺服器
Server IP can not be blank=伺服器 IP 不能為空 Server IP can not be blank=伺服器 IP 不能為空
Password can not be blank=密碼不能為空 Password can not be blank=密碼不能為空
Port out of range=連接埠號碼超出範圍 Port out of range=連接埠號碼超出範圍
@@ -107,7 +119,7 @@ No updates found. Please report to GFWList if you have problems with it.=未發
No QRCode found. Try to zoom in or move it to the center of the screen.=未發現 QR 碼,嘗試把它放大或移動到靠近屏幕中間的位置 No QRCode found. Try to zoom in or move it to the center of the screen.=未發現 QR 碼,嘗試把它放大或移動到靠近屏幕中間的位置
Shadowsocks is already running.=Shadowsocks 已經在運行。 Shadowsocks is already running.=Shadowsocks 已經在運行。
Find Shadowsocks icon in your notify tray.=請在工作列裏尋找 Shadowsocks 圖標。 Find Shadowsocks icon in your notify tray.=請在工作列裏尋找 Shadowsocks 圖標。
If you want to start multiple Shadowsocks, make a copy in another directory.=如果想同時啟動多個,可以另外復制份到別的目錄。
If you want to start multiple Shadowsocks, make a copy in another directory.=如果想同時啟動多個,可以另外復制份到別的目錄。
Failed to decode QRCode=無法解析 QR 碼 Failed to decode QRCode=無法解析 QR 碼
Failed to update registry=無法修改登錄檔 Failed to update registry=無法修改登錄檔
System Proxy On: =系統代理已啟用: System Proxy On: =系統代理已啟用:
@@ -116,3 +128,5 @@ Unexpected error, shadowsocks will exit. Please report to=非預期錯誤,Shad
Unsupported operating system, use Windows Vista at least.=不支持的作業系統版本,最低需求為Windows Vista。 Unsupported operating system, use Windows Vista at least.=不支持的作業系統版本,最低需求為Windows Vista。
Proxy request failed=代理請求失敗 Proxy request failed=代理請求失敗
Proxy handshake failed=代理握手失敗 Proxy handshake failed=代理握手失敗
Register hotkey failed=註冊熱鍵失敗
Cannot parse hotkey: {0}=解析熱鍵失敗: {0}

+ 16
- 0
shadowsocks-csharp/Encryption/EncryptorBase.cs View File

@@ -2,6 +2,22 @@
namespace Shadowsocks.Encryption namespace Shadowsocks.Encryption
{ {
public struct EncryptorInfo
{
public string name;
public int key_size;
public int iv_size;
public int type;
public EncryptorInfo(string name, int key_size, int iv_size, int type)
{
this.name = name;
this.key_size = key_size;
this.iv_size = iv_size;
this.type = type;
}
}
public abstract class EncryptorBase public abstract class EncryptorBase
: IEncryptor : IEncryptor
{ {


+ 8
- 10
shadowsocks-csharp/Encryption/IVEncryptor.cs View File

@@ -16,8 +16,7 @@ namespace Shadowsocks.Encryption
protected static byte[] tempbuf = new byte[MAX_INPUT_SIZE]; protected static byte[] tempbuf = new byte[MAX_INPUT_SIZE];
protected Dictionary<string, Dictionary<string, int[]>> ciphers;
protected Dictionary<string, int[]> ciphersDetail;
protected Dictionary<string, EncryptorInfo> ciphers;
private static readonly ConcurrentDictionary<string, byte[]> CachedKeys = new ConcurrentDictionary<string, byte[]>(); private static readonly ConcurrentDictionary<string, byte[]> CachedKeys = new ConcurrentDictionary<string, byte[]>();
protected byte[] _encryptIV; protected byte[] _encryptIV;
@@ -28,7 +27,7 @@ namespace Shadowsocks.Encryption
protected int _cipher; protected int _cipher;
// cipher name in MbedTLS, useless when using LibSodium // cipher name in MbedTLS, useless when using LibSodium
protected string _cipherMbedName; protected string _cipherMbedName;
protected int[] _cipherInfo;
protected EncryptorInfo _cipherInfo;
protected byte[] _key; protected byte[] _key;
protected int keyLen; protected int keyLen;
protected int ivLen; protected int ivLen;
@@ -39,7 +38,7 @@ namespace Shadowsocks.Encryption
InitKey(method, password); InitKey(method, password);
} }
protected abstract Dictionary<string, Dictionary<string, int[]>> getCiphers();
protected abstract Dictionary<string, EncryptorInfo> getCiphers();
private void InitKey(string method, string password) private void InitKey(string method, string password)
{ {
@@ -47,16 +46,15 @@ namespace Shadowsocks.Encryption
_method = method; _method = method;
string k = method + ":" + password; string k = method + ":" + password;
ciphers = getCiphers(); ciphers = getCiphers();
ciphersDetail = ciphers[_method];
_cipherMbedName = ciphersDetail.Keys.FirstOrDefault();
_cipherInfo = ciphers[_method][_cipherMbedName];
_cipher = _cipherInfo[2];
_cipherInfo = ciphers[_method];
_cipherMbedName = _cipherInfo.name;
_cipher = _cipherInfo.type;
if (_cipher == 0) if (_cipher == 0)
{ {
throw new Exception("method not found"); throw new Exception("method not found");
} }
keyLen = _cipherInfo[0];
ivLen = _cipherInfo[1];
keyLen = _cipherInfo.key_size;
ivLen = _cipherInfo.iv_size;
_key = CachedKeys.GetOrAdd(k, (nk) => _key = CachedKeys.GetOrAdd(k, (nk) =>
{ {
byte[] passbuf = Encoding.UTF8.GetBytes(password); byte[] passbuf = Encoding.UTF8.GetBytes(password);


+ 13
- 13
shadowsocks-csharp/Encryption/MbedTLSEncryptor.cs View File

@@ -20,18 +20,18 @@ namespace Shadowsocks.Encryption
{ {
} }
private static Dictionary<string, Dictionary<string, int[]>> _ciphers = new Dictionary<string, Dictionary<string, int[]>> {
{ "aes-128-cfb", new Dictionary<string, int[]> { { "AES-128-CFB128", new int[] { 16, 16, CIPHER_AES } } } },
{ "aes-192-cfb", new Dictionary<string, int[]> { { "AES-192-CFB128", new int[] { 24, 16, CIPHER_AES } } } },
{ "aes-256-cfb", new Dictionary<string, int[]> { { "AES-256-CFB128", new int[] { 32, 16, CIPHER_AES } } } },
{ "aes-128-ctr", new Dictionary<string, int[]> { { "AES-128-CTR", new int[] { 16, 16, CIPHER_AES } } } },
{ "aes-192-ctr", new Dictionary<string, int[]> { { "AES-192-CTR", new int[] { 24, 16, CIPHER_AES } } } },
{ "aes-256-ctr", new Dictionary<string, int[]> { { "AES-256-CTR", new int[] { 32, 16, CIPHER_AES } } } },
{ "bf-cfb", new Dictionary<string, int[]> { { "BLOWFISH-CFB64", new int[] { 16, 8, CIPHER_BLOWFISH } } } },
{ "camellia-128-cfb", new Dictionary<string, int[]> { { "CAMELLIA-128-CFB128", new int[] { 16, 16, CIPHER_CAMELLIA } } } },
{ "camellia-192-cfb", new Dictionary<string, int[]> { { "CAMELLIA-192-CFB128", new int[] { 24, 16, CIPHER_CAMELLIA } } } },
{ "camellia-256-cfb", new Dictionary<string, int[]> { { "CAMELLIA-256-CFB128", new int[] { 32, 16, CIPHER_CAMELLIA } } } },
{ "rc4-md5", new Dictionary<string, int[]> { { "ARC4-128", new int[] { 16, 16, CIPHER_RC4 } } } }
private static Dictionary<string, EncryptorInfo> _ciphers = new Dictionary<string, EncryptorInfo> {
{ "aes-128-cfb", new EncryptorInfo("AES-128-CFB128", 16, 16, CIPHER_AES) },
{ "aes-192-cfb", new EncryptorInfo("AES-192-CFB128", 24, 16, CIPHER_AES) },
{ "aes-256-cfb", new EncryptorInfo("AES-256-CFB128", 32, 16, CIPHER_AES) },
{ "aes-128-ctr", new EncryptorInfo("AES-128-CTR", 16, 16, CIPHER_AES) },
{ "aes-192-ctr", new EncryptorInfo("AES-192-CTR", 24, 16, CIPHER_AES) },
{ "aes-256-ctr", new EncryptorInfo("AES-256-CTR", 32, 16, CIPHER_AES) },
{ "bf-cfb", new EncryptorInfo("BLOWFISH-CFB64", 16, 8, CIPHER_BLOWFISH) },
{ "camellia-128-cfb", new EncryptorInfo("CAMELLIA-128-CFB128", 16, 16, CIPHER_CAMELLIA) },
{ "camellia-192-cfb", new EncryptorInfo("CAMELLIA-192-CFB128", 24, 16, CIPHER_CAMELLIA) },
{ "camellia-256-cfb", new EncryptorInfo("CAMELLIA-256-CFB128", 32, 16, CIPHER_CAMELLIA) },
{ "rc4-md5", new EncryptorInfo("ARC4-128", 16, 16, CIPHER_RC4) }
}; };
public static List<string> SupportedCiphers() public static List<string> SupportedCiphers()
@@ -39,7 +39,7 @@ namespace Shadowsocks.Encryption
return new List<string>(_ciphers.Keys); return new List<string>(_ciphers.Keys);
} }
protected override Dictionary<string, Dictionary<string, int[]>> getCiphers()
protected override Dictionary<string, EncryptorInfo> getCiphers()
{ {
return _ciphers; return _ciphers;
} }


+ 5
- 5
shadowsocks-csharp/Encryption/SodiumEncryptor.cs View File

@@ -24,13 +24,13 @@ namespace Shadowsocks.Encryption
{ {
} }
private static Dictionary<string, Dictionary<string, int[]>> _ciphers = new Dictionary<string, Dictionary<string, int[]>> {
{ "salsa20", new Dictionary<string, int[]> { { "salsa20", new int[] { 32, 8, CIPHER_SALSA20 } } } },
{ "chacha20", new Dictionary<string, int[]> { { "chacha20", new int[] { 32, 8, CIPHER_CHACHA20 } } } },
{ "chacha20-ietf", new Dictionary<string, int[]> { { "chacha20-ietf", new int[] { 32, 12, CIPHER_CHACHA20_IETF } } } }
private static Dictionary<string, EncryptorInfo> _ciphers = new Dictionary<string, EncryptorInfo> {
{ "salsa20", new EncryptorInfo("salsa20", 32, 8, CIPHER_SALSA20) },
{ "chacha20", new EncryptorInfo("chacha20", 32, 8, CIPHER_CHACHA20) },
{ "chacha20-ietf", new EncryptorInfo("chacha20-ietf", 32, 12, CIPHER_CHACHA20_IETF) }
}; };
protected override Dictionary<string, Dictionary<string, int[]>> getCiphers()
protected override Dictionary<string, EncryptorInfo> getCiphers()
{ {
return _ciphers; return _ciphers;
} }


+ 9
- 4
shadowsocks-csharp/Model/Configuration.cs View File

@@ -26,9 +26,8 @@ namespace Shadowsocks.Model
public bool autoCheckUpdate; public bool autoCheckUpdate;
public bool isVerboseLogging; public bool isVerboseLogging;
public LogViewerConfig logViewer; public LogViewerConfig logViewer;
public bool useProxy;
public string proxyServer;
public int proxyPort;
public ProxyConfig proxy;
public HotkeyConfig hotkey;
private static string CONFIG_FILE = "gui-config.json"; private static string CONFIG_FILE = "gui-config.json";
@@ -58,6 +57,12 @@ namespace Shadowsocks.Model
config.localPort = 1080; config.localPort = 1080;
if (config.index == -1 && config.strategy == null) if (config.index == -1 && config.strategy == null)
config.index = 0; config.index = 0;
if (config.logViewer == null)
config.logViewer = new LogViewerConfig();
if (config.proxy == null)
config.proxy = new ProxyConfig();
if (config.hotkey == null)
config.hotkey = new HotkeyConfig();
return config; return config;
} }
catch (Exception e) catch (Exception e)
@@ -98,7 +103,7 @@ namespace Shadowsocks.Model
} }
catch (IOException e) catch (IOException e)
{ {
Console.Error.WriteLine(e);
Logging.LogUsefulException(e);
} }
} }


+ 33
- 0
shadowsocks-csharp/Model/HotKeyConfig.cs View File

@@ -0,0 +1,33 @@
using System;
namespace Shadowsocks.Model
{
/*
* Format:
* <modifiers-combination>+<key>
*
*/
[Serializable]
public class HotkeyConfig
{
public string SwitchSystemProxy;
public string ChangeToPac;
public string ChangeToGlobal;
public string SwitchAllowLan;
public string ShowLogs;
public string ServerMoveUp;
public string ServerMoveDown;
public HotkeyConfig()
{
SwitchSystemProxy = "";
ChangeToPac = "";
ChangeToGlobal = "";
SwitchAllowLan = "";
ShowLogs = "";
ServerMoveUp = "";
ServerMoveDown = "";
}
}
}

+ 22
- 0
shadowsocks-csharp/Model/ProxyConfig.cs View File

@@ -0,0 +1,22 @@
using Shadowsocks.View;
using System;
using System.Drawing;
using System.Windows.Forms;
namespace Shadowsocks.Model
{
[Serializable]
public class ProxyConfig
{
public bool useProxy;
public string proxyServer;
public int proxyPort;
public ProxyConfig()
{
useProxy = false;
proxyServer = "";
proxyPort = 0;
}
}
}

+ 13
- 9
shadowsocks-csharp/Program.cs View File

@@ -13,7 +13,9 @@ namespace Shadowsocks
{ {
static class Program static class Program
{ {
static ShadowsocksController controller;
private static ShadowsocksController _controller;
// XXX: Don't change this name
private static MenuViewController _viewController;
/// <summary> /// <summary>
/// 应用程序的主入口点。 /// 应用程序的主入口点。
@@ -37,6 +39,7 @@ namespace Shadowsocks
SystemEvents.PowerModeChanged += SystemEvents_PowerModeChanged; SystemEvents.PowerModeChanged += SystemEvents_PowerModeChanged;
Application.EnableVisualStyles(); Application.EnableVisualStyles();
Application.SetCompatibleTextRenderingDefault(false); Application.SetCompatibleTextRenderingDefault(false);
Application.ApplicationExit += (sender, args) => HotKeys.Destroy();
if (!mutex.WaitOne(0, false)) if (!mutex.WaitOne(0, false))
{ {
@@ -61,9 +64,10 @@ namespace Shadowsocks
#else #else
Logging.OpenLogFile(); Logging.OpenLogFile();
#endif #endif
controller = new ShadowsocksController();
MenuViewController viewController = new MenuViewController(controller);
controller.Start();
_controller = new ShadowsocksController();
_viewController = new MenuViewController(_controller);
HotKeys.Init();
_controller.Start();
Application.Run(); Application.Run();
} }
} }
@@ -88,10 +92,10 @@ namespace Shadowsocks
{ {
case PowerModes.Resume: case PowerModes.Resume:
Logging.Info("os wake up"); Logging.Info("os wake up");
controller?.Start();
_controller?.Start();
break; break;
case PowerModes.Suspend: case PowerModes.Suspend:
controller?.Stop();
_controller?.Stop();
Logging.Info("os suspend"); Logging.Info("os suspend");
break; break;
} }
@@ -99,10 +103,10 @@ namespace Shadowsocks
private static void Application_ApplicationExit(object sender, EventArgs e) private static void Application_ApplicationExit(object sender, EventArgs e)
{ {
if (controller != null)
if (_controller != null)
{ {
controller.Stop();
controller = null;
_controller.Stop();
_controller = null;
} }
} }
} }


+ 174
- 0
shadowsocks-csharp/Util/Hotkeys.cs View File

@@ -0,0 +1,174 @@
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.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);
}
}
}

+ 29
- 15
shadowsocks-csharp/Util/Util.cs View File

@@ -1,7 +1,9 @@
using System; using System;
using System.Collections.Generic;
using System.Diagnostics; using System.Diagnostics;
using System.IO; using System.IO;
using System.IO.Compression; using System.IO.Compression;
using System.Linq;
using System.Runtime.InteropServices; using System.Runtime.InteropServices;
using System.Windows.Forms; using System.Windows.Forms;
using Microsoft.Win32; using Microsoft.Win32;
@@ -9,6 +11,20 @@ using Shadowsocks.Controller;
namespace Shadowsocks.Util namespace Shadowsocks.Util
{ {
public struct BandwidthScaleInfo
{
public float value;
public string unit_name;
public long unit;
public BandwidthScaleInfo(float value, string unit_name, long unit)
{
this.value = value;
this.unit_name = unit_name;
this.unit = unit;
}
}
public class Utils public class Utils
{ {
private static bool? _portableMode; private static bool? _portableMode;
@@ -114,7 +130,7 @@ namespace Shadowsocks.Util
public static string FormatBandwidth(long n) public static string FormatBandwidth(long n)
{ {
var result = GetBandwidthScale(n); var result = GetBandwidthScale(n);
return $"{result.Item1:0.##}{result.Item2}";
return $"{result.value:0.##}{result.unit_name}";
} }
public static string FormatBytes(long bytes) public static string FormatBytes(long bytes)
@@ -127,32 +143,32 @@ namespace Shadowsocks.Util
const long E = P * 1024L; const long E = P * 1024L;
if (bytes >= P * 990) if (bytes >= P * 990)
return (bytes / (double)E).ToString("F5") + "EB";
return (bytes / (double)E).ToString("F5") + "EiB";
if (bytes >= T * 990) if (bytes >= T * 990)
return (bytes / (double)P).ToString("F5") + "PB";
return (bytes / (double)P).ToString("F5") + "PiB";
if (bytes >= G * 990) if (bytes >= G * 990)
return (bytes / (double)T).ToString("F5") + "TB";
return (bytes / (double)T).ToString("F5") + "TiB";
if (bytes >= M * 990) if (bytes >= M * 990)
{ {
return (bytes / (double)G).ToString("F4") + "GB";
return (bytes / (double)G).ToString("F4") + "GiB";
} }
if (bytes >= M * 100) if (bytes >= M * 100)
{ {
return (bytes / (double)M).ToString("F1") + "MB";
return (bytes / (double)M).ToString("F1") + "MiB";
} }
if (bytes >= M * 10) if (bytes >= M * 10)
{ {
return (bytes / (double)M).ToString("F2") + "MB";
return (bytes / (double)M).ToString("F2") + "MiB";
} }
if (bytes >= K * 990) if (bytes >= K * 990)
{ {
return (bytes / (double)M).ToString("F3") + "MB";
return (bytes / (double)M).ToString("F3") + "MiB";
} }
if (bytes > K * 2) if (bytes > K * 2)
{ {
return (bytes / (double)K).ToString("F1") + "KB";
return (bytes / (double)K).ToString("F1") + "KiB";
} }
return bytes.ToString();
return bytes.ToString() + "B";
} }
/// <summary> /// <summary>
@@ -160,11 +176,9 @@ namespace Shadowsocks.Util
/// </summary> /// </summary>
/// <param name="n">Raw bandwidth</param> /// <param name="n">Raw bandwidth</param>
/// <returns> /// <returns>
/// Item1: float, bandwidth with suitable scale (eg. 56)
/// Item2: string, scale unit name (eg. KiB)
/// Item3: long, scale unit (eg. 1024)
/// The BandwidthScaleInfo struct
/// </returns> /// </returns>
public static Tuple<float, string, long> GetBandwidthScale(long n)
public static BandwidthScaleInfo GetBandwidthScale(long n)
{ {
long scale = 1; long scale = 1;
float f = n; float f = n;
@@ -193,7 +207,7 @@ namespace Shadowsocks.Util
scale <<= 10; scale <<= 10;
unit = "TiB"; unit = "TiB";
} }
return new Tuple<float, string, long>(f, unit, scale);
return new BandwidthScaleInfo(f, unit, scale);
} }
public static RegistryKey OpenUserRegKey( string name, bool writable ) { public static RegistryKey OpenUserRegKey( string name, bool writable ) {


+ 406
- 0
shadowsocks-csharp/View/HotkeySettingsForm.cs View File

@@ -0,0 +1,406 @@
using System;
using System.Collections.Generic;
using System.Drawing;
using System.Linq;
using System.Reflection;
using System.Text;
using System.Windows.Forms;
using Shadowsocks.Controller;
using Shadowsocks.Model;
using Shadowsocks.Properties;
using Shadowsocks.Util;
namespace Shadowsocks.View
{
public partial class HotkeySettingsForm : Form
{
private ShadowsocksController _controller;
// this is a copy of configuration that we are working on
private HotkeyConfig _modifiedConfig;
private StringBuilder _sb = new StringBuilder();
private IEnumerable<TextBox> _allTextBoxes;
private static Label _lb = null;
private static HotKeys.HotKeyCallBackHandler _callBack = null;
public HotkeySettingsForm(ShadowsocksController controller)
{
InitializeComponent();
UpdateTexts();
this.Icon = Icon.FromHandle(Resources.ssw128.GetHicon());
_controller = controller;
_controller.ConfigChanged += controller_ConfigChanged;
LoadCurrentConfiguration();
// get all textboxes belong to this form
_allTextBoxes = HotKeys.GetChildControls<TextBox>(this.tableLayoutPanel1);
if (!_allTextBoxes.Any()) throw new Exception("Cannot get all textboxes");
}
private void controller_ConfigChanged(object sender, EventArgs e)
{
LoadCurrentConfiguration();
}
private void LoadCurrentConfiguration()
{
_modifiedConfig = _controller.GetConfigurationCopy().hotkey;
LoadConfiguration(_modifiedConfig);
}
private void LoadConfiguration(HotkeyConfig config)
{
SwitchSystemProxyTextBox.Text = config.SwitchSystemProxy;
ChangeToPacTextBox.Text = config.ChangeToPac;
ChangeToGlobalTextBox.Text = config.ChangeToGlobal;
SwitchAllowLanTextBox.Text = config.SwitchAllowLan;
ShowLogsTextBox.Text = config.ShowLogs;
ServerMoveUpTextBox.Text = config.ServerMoveUp;
ServerMoveDownTextBox.Text = config.ServerMoveDown;
}
private void UpdateTexts()
{
// I18N stuff
SwitchSystemProxyLabel.Text = I18N.GetString("Switch system proxy");
ChangeToPacLabel.Text = I18N.GetString("Switch to PAC mode");
ChangeToGlobalLabel.Text = I18N.GetString("Switch to Global mode");
SwitchAllowLanLabel.Text = I18N.GetString("Switch share over LAN");
ShowLogsLabel.Text = I18N.GetString("Show Logs");
ServerMoveUpLabel.Text = I18N.GetString("Switch to prev server");
ServerMoveDownLabel.Text = I18N.GetString("Switch to next server");
btnOK.Text = I18N.GetString("OK");
btnCancel.Text = I18N.GetString("Cancel");
btnRegisterAll.Text = I18N.GetString("Reg All");
this.Text = I18N.GetString("Edit Hotkeys...");
}
/// <summary>
/// Capture hotkey - Press key
/// </summary>
private void HotkeyDown(object sender, KeyEventArgs e)
{
_sb.Length = 0;
//Combination key only
if (e.Modifiers != 0)
{
// XXX: Hotkey parsing depends on the sequence, more specifically, ModifierKeysConverter.
// Windows key is reserved by operating system, we deny this key.
if (e.Control)
{
_sb.Append("Ctrl+");
}
if (e.Alt)
{
_sb.Append("Alt+");
}
if (e.Shift)
{
_sb.Append("Shift+");
}
Keys keyvalue = (Keys) e.KeyValue;
if ((keyvalue >= Keys.PageUp && keyvalue <= Keys.Down) ||
(keyvalue >= Keys.A && keyvalue <= Keys.Z) ||
(keyvalue >= Keys.F1 && keyvalue <= Keys.F12))
{
_sb.Append(e.KeyCode);
}
else if (keyvalue >= Keys.D0 && keyvalue <= Keys.D9)
{
_sb.Append('D').Append((char) e.KeyValue);
}
else if (keyvalue >= Keys.NumPad0 && keyvalue <= Keys.NumPad9)
{
_sb.Append("NumPad").Append((char) (e.KeyValue - 48));
}
}
((TextBox) sender).Text = _sb.ToString();
}
/// <summary>
/// Capture hotkey - Release key
/// </summary>
private void HotkeyUp(object sender, KeyEventArgs e)
{
TextBox tb = sender as TextBox;
string content = tb.Text.TrimEnd();
if (content.Length >= 1 && content[content.Length - 1] == '+')
{
tb.Text = "";
}
}
private void TextBox_TextChanged(object sender, EventArgs e)
{
TextBox tb = sender as TextBox;
if (tb.Text == "")
{
// unreg
UnregHotkey(tb);
}
}
private void UnregHotkey(TextBox tb)
{
PrepareForHotkey(tb, out _callBack, out _lb);
UnregPrevHotkey(_callBack);
}
private void CancelButton_Click(object sender, EventArgs e)
{
this.Close();
}
private void OKButton_Click(object sender, EventArgs e)
{
// try to register, notify to change settings if failed
foreach (var tb in _allTextBoxes)
{
if (tb.Text.IsNullOrEmpty())
{
continue;
}
if (!TryRegHotkey(tb))
{
MessageBox.Show(I18N.GetString("Register hotkey failed"));
return;
}
}
// All check passed, saving
SaveConfig();
this.Close();
}
private void RegisterAllButton_Click(object sender, EventArgs e)
{
foreach (var tb in _allTextBoxes)
{
if (tb.Text.IsNullOrEmpty())
{
continue;
}
TryRegHotkey(tb);
}
}
private bool TryRegHotkey(TextBox tb)
{
var hotkey = HotKeys.Str2HotKey(tb.Text);
if (hotkey == null)
{
MessageBox.Show(string.Format(I18N.GetString("Cannot parse hotkey: {0}"), tb.Text));
tb.Clear();
return false;
}
PrepareForHotkey(tb, out _callBack, out _lb);
UnregPrevHotkey(_callBack);
// try to register keys
// if already registered by other progs
// notify to change
// use the corresponding label color to indicate
// reg result.
// Green: not occupied by others and operation succeed
// Yellow: already registered by other program and need action: disable by clear the content
// or change to another one
// Transparent without color: first run or empty config
bool regResult = HotKeys.Regist(hotkey, _callBack);
_lb.BackColor = regResult ? Color.Green : Color.Yellow;
return regResult;
}
private static void UnregPrevHotkey(HotKeys.HotKeyCallBackHandler cb)
{
GlobalHotKey.HotKey prevHotKey;
if (HotKeys.IsCallbackExists(cb, out prevHotKey))
{
// unregister previous one
HotKeys.UnRegist(prevHotKey);
}
}
private void SaveConfig()
{
_modifiedConfig.SwitchSystemProxy = SwitchSystemProxyTextBox.Text;
_modifiedConfig.ChangeToPac = ChangeToPacTextBox.Text;
_modifiedConfig.ChangeToGlobal = ChangeToGlobalTextBox.Text;
_modifiedConfig.SwitchAllowLan = SwitchAllowLanTextBox.Text;
_modifiedConfig.ShowLogs = ShowLogsTextBox.Text;
_modifiedConfig.ServerMoveUp = ServerMoveUpTextBox.Text;
_modifiedConfig.ServerMoveDown = ServerMoveDownTextBox.Text;
_controller.SaveHotkeyConfig(_modifiedConfig);
}
#region Callbacks
private void SwitchSystemProxyCallback()
{
bool enabled = _controller.GetConfigurationCopy().enabled;
_controller.ToggleEnable(!enabled);
}
private void ChangeToPacCallback()
{
bool enabled = _controller.GetConfigurationCopy().enabled;
if (enabled == false) return;
_controller.ToggleGlobal(false);
}
private void ChangeToGlobalCallback()
{
bool enabled = _controller.GetConfigurationCopy().enabled;
if (enabled == false) return;
_controller.ToggleGlobal(true);
}
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
/// <summary>
/// Find correct callback and corresponding label
/// </summary>
/// <param name="tb"></param>
/// <param name="cb"></param>
/// <param name="lb"></param>
private void PrepareForHotkey(TextBox tb, out HotKeys.HotKeyCallBackHandler cb, out Label lb)
{
/*
* XXX: The labelName, TextBoxName and callbackName
* must follow this rule to make use of reflection
*
* <BaseName><Control-Type-Name>
*/
if (tb == null)
throw new ArgumentNullException(nameof(tb));
var pos = tb.Name.LastIndexOf("TextBox", StringComparison.OrdinalIgnoreCase);
var rawName = tb.Name.Substring(0, pos);
var labelName = rawName + "Label";
var callbackName = rawName + "Callback";
var callback = GetDelegateViaMethodName(this.GetType(), callbackName);
if (callback == null)
{
throw new Exception($"{callbackName} not found");
}
cb = callback as HotKeys.HotKeyCallBackHandler;
object label = GetFieldViaName(this.GetType(), labelName, this);
if (label == null)
{
throw new Exception($"{labelName} not found");
}
lb = label as Label;
}
/// <summary>
///
/// </summary>
/// <param name="type">from which type</param>
/// <param name="name">field name</param>
/// <param name="obj">pass null if static field</param>
/// <returns></returns>
private static object GetFieldViaName(Type type, string name, object obj)
{
if (type == null) throw new ArgumentNullException(nameof(type));
if (name.IsNullOrEmpty()) throw new ArgumentException(nameof(name));
// In general, TextBoxes and Labels are private
FieldInfo fi = type.GetField(name,
BindingFlags.NonPublic | BindingFlags.Instance | BindingFlags.IgnoreCase | BindingFlags.Static);
return fi == null ? null : fi.GetValue(obj);
}
/// <summary>
/// Create hotkey callback handler delegate based on callback name
/// </summary>
/// <param name="type"></param>
/// <param name="methodname"></param>
/// <returns></returns>
private Delegate GetDelegateViaMethodName(Type type, 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,
BindingFlags.NonPublic | BindingFlags.Instance | BindingFlags.IgnoreCase);
return dynMethod == null ? null : Delegate.CreateDelegate(delegateType, this, dynMethod);
}
#endregion
}
}

+ 354
- 0
shadowsocks-csharp/View/HotkeySettingsForm.designer.cs View File

@@ -0,0 +1,354 @@
namespace Shadowsocks.View
{
partial class HotkeySettingsForm
{
/// <summary>
/// Required designer variable.
/// </summary>
private System.ComponentModel.IContainer components = null;
/// <summary>
/// Clean up any resources being used.
/// </summary>
/// <param name="disposing">true if managed resources should be disposed; otherwise, false.</param>
protected override void Dispose(bool disposing)
{
if (disposing && (components != null))
{
components.Dispose();
}
base.Dispose(disposing);
}
#region Windows Form Designer generated code
/// <summary>
/// Required method for Designer support - do not modify
/// the contents of this method with the code editor.
/// </summary>
private void InitializeComponent()
{
System.Windows.Forms.FlowLayoutPanel flowLayoutPanel1;
this.btnOK = new System.Windows.Forms.Button();
this.btnCancel = new System.Windows.Forms.Button();
this.btnRegisterAll = new System.Windows.Forms.Button();
this.tableLayoutPanel1 = new System.Windows.Forms.TableLayoutPanel();
this.SwitchSystemProxyLabel = new System.Windows.Forms.Label();
this.ChangeToPacLabel = new System.Windows.Forms.Label();
this.ChangeToGlobalLabel = new System.Windows.Forms.Label();
this.SwitchAllowLanLabel = new System.Windows.Forms.Label();
this.ShowLogsLabel = new System.Windows.Forms.Label();
this.ServerMoveUpLabel = new System.Windows.Forms.Label();
this.ServerMoveDownLabel = new System.Windows.Forms.Label();
this.SwitchSystemProxyTextBox = new System.Windows.Forms.TextBox();
this.ChangeToPacTextBox = new System.Windows.Forms.TextBox();
this.ChangeToGlobalTextBox = new System.Windows.Forms.TextBox();
this.SwitchAllowLanTextBox = new System.Windows.Forms.TextBox();
this.ShowLogsTextBox = new System.Windows.Forms.TextBox();
this.ServerMoveUpTextBox = new System.Windows.Forms.TextBox();
this.ServerMoveDownTextBox = new System.Windows.Forms.TextBox();
flowLayoutPanel1 = new System.Windows.Forms.FlowLayoutPanel();
flowLayoutPanel1.SuspendLayout();
this.tableLayoutPanel1.SuspendLayout();
this.SuspendLayout();
//
// flowLayoutPanel1
//
this.tableLayoutPanel1.SetColumnSpan(flowLayoutPanel1, 2);
flowLayoutPanel1.Controls.Add(this.btnOK);
flowLayoutPanel1.Controls.Add(this.btnCancel);
flowLayoutPanel1.Controls.Add(this.btnRegisterAll);
flowLayoutPanel1.FlowDirection = System.Windows.Forms.FlowDirection.BottomUp;
flowLayoutPanel1.Location = new System.Drawing.Point(0, 227);
flowLayoutPanel1.Margin = new System.Windows.Forms.Padding(0);
flowLayoutPanel1.Name = "flowLayoutPanel1";
flowLayoutPanel1.Padding = new System.Windows.Forms.Padding(0, 0, 16, 0);
flowLayoutPanel1.RightToLeft = System.Windows.Forms.RightToLeft.Yes;
flowLayoutPanel1.Size = new System.Drawing.Size(475, 44);
flowLayoutPanel1.TabIndex = 6;
//
// btnOK
//
this.btnOK.DialogResult = System.Windows.Forms.DialogResult.OK;
this.btnOK.Location = new System.Drawing.Point(381, 10);
this.btnOK.Name = "btnOK";
this.btnOK.Size = new System.Drawing.Size(75, 31);
this.btnOK.TabIndex = 0;
this.btnOK.Text = "OK";
this.btnOK.UseVisualStyleBackColor = true;
this.btnOK.Click += new System.EventHandler(this.OKButton_Click);
//
// btnCancel
//
this.btnCancel.DialogResult = System.Windows.Forms.DialogResult.Cancel;
this.btnCancel.Location = new System.Drawing.Point(300, 10);
this.btnCancel.Name = "btnCancel";
this.btnCancel.Size = new System.Drawing.Size(75, 31);
this.btnCancel.TabIndex = 1;
this.btnCancel.Text = "Cancel";
this.btnCancel.UseVisualStyleBackColor = true;
this.btnCancel.Click += new System.EventHandler(this.CancelButton_Click);
//
// btnRegisterAll
//
this.btnRegisterAll.DialogResult = System.Windows.Forms.DialogResult.Cancel;
this.btnRegisterAll.Location = new System.Drawing.Point(219, 10);
this.btnRegisterAll.Name = "btnRegisterAll";
this.btnRegisterAll.Size = new System.Drawing.Size(75, 31);
this.btnRegisterAll.TabIndex = 2;
this.btnRegisterAll.Text = "Reg All";
this.btnRegisterAll.UseVisualStyleBackColor = true;
this.btnRegisterAll.Click += new System.EventHandler(this.RegisterAllButton_Click);
//
// tableLayoutPanel1
//
this.tableLayoutPanel1.ColumnCount = 2;
this.tableLayoutPanel1.ColumnStyles.Add(new System.Windows.Forms.ColumnStyle());
this.tableLayoutPanel1.ColumnStyles.Add(new System.Windows.Forms.ColumnStyle());
this.tableLayoutPanel1.Controls.Add(this.SwitchSystemProxyLabel, 0, 0);
this.tableLayoutPanel1.Controls.Add(this.ChangeToPacLabel, 0, 1);
this.tableLayoutPanel1.Controls.Add(this.ChangeToGlobalLabel, 0, 2);
this.tableLayoutPanel1.Controls.Add(this.SwitchAllowLanLabel, 0, 3);
this.tableLayoutPanel1.Controls.Add(this.ShowLogsLabel, 0, 4);
this.tableLayoutPanel1.Controls.Add(this.ServerMoveUpLabel, 0, 5);
this.tableLayoutPanel1.Controls.Add(this.ServerMoveDownLabel, 0, 6);
this.tableLayoutPanel1.Controls.Add(flowLayoutPanel1, 0, 7);
this.tableLayoutPanel1.Controls.Add(this.SwitchSystemProxyTextBox, 1, 0);
this.tableLayoutPanel1.Controls.Add(this.ChangeToPacTextBox, 1, 1);
this.tableLayoutPanel1.Controls.Add(this.ChangeToGlobalTextBox, 1, 2);
this.tableLayoutPanel1.Controls.Add(this.SwitchAllowLanTextBox, 1, 3);
this.tableLayoutPanel1.Controls.Add(this.ShowLogsTextBox, 1, 4);
this.tableLayoutPanel1.Controls.Add(this.ServerMoveUpTextBox, 1, 5);
this.tableLayoutPanel1.Controls.Add(this.ServerMoveDownTextBox, 1, 6);
this.tableLayoutPanel1.Dock = System.Windows.Forms.DockStyle.Fill;
this.tableLayoutPanel1.Location = new System.Drawing.Point(0, 0);
this.tableLayoutPanel1.Name = "tableLayoutPanel1";
this.tableLayoutPanel1.RowCount = 8;
this.tableLayoutPanel1.RowStyles.Add(new System.Windows.Forms.RowStyle(System.Windows.Forms.SizeType.Percent, 14.16667F));
this.tableLayoutPanel1.RowStyles.Add(new System.Windows.Forms.RowStyle(System.Windows.Forms.SizeType.Percent, 14.16667F));
this.tableLayoutPanel1.RowStyles.Add(new System.Windows.Forms.RowStyle(System.Windows.Forms.SizeType.Percent, 14.16667F));
this.tableLayoutPanel1.RowStyles.Add(new System.Windows.Forms.RowStyle(System.Windows.Forms.SizeType.Percent, 14.16667F));
this.tableLayoutPanel1.RowStyles.Add(new System.Windows.Forms.RowStyle(System.Windows.Forms.SizeType.Percent, 14.16667F));
this.tableLayoutPanel1.RowStyles.Add(new System.Windows.Forms.RowStyle(System.Windows.Forms.SizeType.Percent, 14.77778F));
this.tableLayoutPanel1.RowStyles.Add(new System.Windows.Forms.RowStyle(System.Windows.Forms.SizeType.Percent, 14.38889F));
this.tableLayoutPanel1.RowStyles.Add(new System.Windows.Forms.RowStyle(System.Windows.Forms.SizeType.Absolute, 40F));
this.tableLayoutPanel1.Size = new System.Drawing.Size(475, 271);
this.tableLayoutPanel1.TabIndex = 0;
//
// SwitchSystemProxyLabel
//
this.SwitchSystemProxyLabel.AutoSize = true;
this.SwitchSystemProxyLabel.Dock = System.Windows.Forms.DockStyle.Right;
this.SwitchSystemProxyLabel.Location = new System.Drawing.Point(25, 0);
this.SwitchSystemProxyLabel.Margin = new System.Windows.Forms.Padding(8, 0, 8, 0);
this.SwitchSystemProxyLabel.Name = "SwitchSystemProxyLabel";
this.SwitchSystemProxyLabel.Size = new System.Drawing.Size(147, 32);
this.SwitchSystemProxyLabel.TabIndex = 0;
this.SwitchSystemProxyLabel.Text = "Enable System Proxy";
this.SwitchSystemProxyLabel.TextAlign = System.Drawing.ContentAlignment.MiddleCenter;
//
// ChangeToPacLabel
//
this.ChangeToPacLabel.AutoSize = true;
this.ChangeToPacLabel.Dock = System.Windows.Forms.DockStyle.Right;
this.ChangeToPacLabel.Location = new System.Drawing.Point(135, 32);
this.ChangeToPacLabel.Margin = new System.Windows.Forms.Padding(8, 0, 8, 0);
this.ChangeToPacLabel.Name = "ChangeToPacLabel";
this.ChangeToPacLabel.Size = new System.Drawing.Size(37, 32);
this.ChangeToPacLabel.TabIndex = 1;
this.ChangeToPacLabel.Text = "PAC";
this.ChangeToPacLabel.TextAlign = System.Drawing.ContentAlignment.MiddleCenter;
//
// ChangeToGlobalLabel
//
this.ChangeToGlobalLabel.AutoSize = true;
this.ChangeToGlobalLabel.Dock = System.Windows.Forms.DockStyle.Right;
this.ChangeToGlobalLabel.Location = new System.Drawing.Point(119, 64);
this.ChangeToGlobalLabel.Margin = new System.Windows.Forms.Padding(8, 0, 8, 0);
this.ChangeToGlobalLabel.Name = "ChangeToGlobalLabel";
this.ChangeToGlobalLabel.Size = new System.Drawing.Size(53, 32);
this.ChangeToGlobalLabel.TabIndex = 2;
this.ChangeToGlobalLabel.Text = "Global";
this.ChangeToGlobalLabel.TextAlign = System.Drawing.ContentAlignment.MiddleCenter;
//
// SwitchAllowLanLabel
//
this.SwitchAllowLanLabel.AutoSize = true;
this.SwitchAllowLanLabel.Dock = System.Windows.Forms.DockStyle.Right;
this.SwitchAllowLanLabel.Location = new System.Drawing.Point(8, 96);
this.SwitchAllowLanLabel.Margin = new System.Windows.Forms.Padding(8, 0, 8, 0);
this.SwitchAllowLanLabel.Name = "SwitchAllowLanLabel";
this.SwitchAllowLanLabel.Size = new System.Drawing.Size(164, 32);
this.SwitchAllowLanLabel.TabIndex = 3;
this.SwitchAllowLanLabel.Text = "Allow Clients from LAN";
this.SwitchAllowLanLabel.TextAlign = System.Drawing.ContentAlignment.MiddleCenter;
//
// ShowLogsLabel
//
this.ShowLogsLabel.AutoSize = true;
this.ShowLogsLabel.Dock = System.Windows.Forms.DockStyle.Right;
this.ShowLogsLabel.Location = new System.Drawing.Point(82, 128);
this.ShowLogsLabel.Margin = new System.Windows.Forms.Padding(8, 0, 8, 0);
this.ShowLogsLabel.Name = "ShowLogsLabel";
this.ShowLogsLabel.Size = new System.Drawing.Size(90, 32);
this.ShowLogsLabel.TabIndex = 4;
this.ShowLogsLabel.Text = "Show Logs...";
this.ShowLogsLabel.TextAlign = System.Drawing.ContentAlignment.MiddleCenter;
//
// ServerMoveUpLabel
//
this.ServerMoveUpLabel.AutoSize = true;
this.ServerMoveUpLabel.Dock = System.Windows.Forms.DockStyle.Right;
this.ServerMoveUpLabel.Location = new System.Drawing.Point(103, 160);
this.ServerMoveUpLabel.Margin = new System.Windows.Forms.Padding(8, 0, 8, 0);
this.ServerMoveUpLabel.Name = "ServerMoveUpLabel";
this.ServerMoveUpLabel.Size = new System.Drawing.Size(69, 34);
this.ServerMoveUpLabel.TabIndex = 4;
this.ServerMoveUpLabel.Text = "Move up";
this.ServerMoveUpLabel.TextAlign = System.Drawing.ContentAlignment.MiddleCenter;
//
// ServerMoveDownLabel
//
this.ServerMoveDownLabel.AutoSize = true;
this.ServerMoveDownLabel.Dock = System.Windows.Forms.DockStyle.Right;
this.ServerMoveDownLabel.Location = new System.Drawing.Point(81, 194);
this.ServerMoveDownLabel.Margin = new System.Windows.Forms.Padding(8, 0, 8, 0);
this.ServerMoveDownLabel.Name = "ServerMoveDownLabel";
this.ServerMoveDownLabel.Size = new System.Drawing.Size(91, 33);
this.ServerMoveDownLabel.TabIndex = 4;
this.ServerMoveDownLabel.Text = "Move Down";
this.ServerMoveDownLabel.TextAlign = System.Drawing.ContentAlignment.MiddleCenter;
//
// SwitchSystemProxyTextBox
//
this.SwitchSystemProxyTextBox.Dock = System.Windows.Forms.DockStyle.Fill;
this.SwitchSystemProxyTextBox.Location = new System.Drawing.Point(183, 3);
this.SwitchSystemProxyTextBox.Margin = new System.Windows.Forms.Padding(3, 3, 16, 3);
this.SwitchSystemProxyTextBox.Name = "SwitchSystemProxyTextBox";
this.SwitchSystemProxyTextBox.ReadOnly = true;
this.SwitchSystemProxyTextBox.Size = new System.Drawing.Size(276, 25);
this.SwitchSystemProxyTextBox.TabIndex = 7;
this.SwitchSystemProxyTextBox.TextChanged += new System.EventHandler(this.TextBox_TextChanged);
this.SwitchSystemProxyTextBox.KeyDown += new System.Windows.Forms.KeyEventHandler(this.HotkeyDown);
this.SwitchSystemProxyTextBox.KeyUp += new System.Windows.Forms.KeyEventHandler(this.HotkeyUp);
//
// ChangeToPacTextBox
//
this.ChangeToPacTextBox.Dock = System.Windows.Forms.DockStyle.Fill;
this.ChangeToPacTextBox.Location = new System.Drawing.Point(183, 35);
this.ChangeToPacTextBox.Margin = new System.Windows.Forms.Padding(3, 3, 16, 3);
this.ChangeToPacTextBox.Name = "ChangeToPacTextBox";
this.ChangeToPacTextBox.ReadOnly = true;
this.ChangeToPacTextBox.Size = new System.Drawing.Size(276, 25);
this.ChangeToPacTextBox.TabIndex = 8;
this.ChangeToPacTextBox.TextChanged += new System.EventHandler(this.TextBox_TextChanged);
this.ChangeToPacTextBox.KeyDown += new System.Windows.Forms.KeyEventHandler(this.HotkeyDown);
this.ChangeToPacTextBox.KeyUp += new System.Windows.Forms.KeyEventHandler(this.HotkeyUp);
//
// ChangeToGlobalTextBox
//
this.ChangeToGlobalTextBox.Dock = System.Windows.Forms.DockStyle.Fill;
this.ChangeToGlobalTextBox.Location = new System.Drawing.Point(183, 67);
this.ChangeToGlobalTextBox.Margin = new System.Windows.Forms.Padding(3, 3, 16, 3);
this.ChangeToGlobalTextBox.Name = "ChangeToGlobalTextBox";
this.ChangeToGlobalTextBox.ReadOnly = true;
this.ChangeToGlobalTextBox.Size = new System.Drawing.Size(276, 25);
this.ChangeToGlobalTextBox.TabIndex = 9;
this.ChangeToGlobalTextBox.TextChanged += new System.EventHandler(this.TextBox_TextChanged);
this.ChangeToGlobalTextBox.KeyDown += new System.Windows.Forms.KeyEventHandler(this.HotkeyDown);
this.ChangeToGlobalTextBox.KeyUp += new System.Windows.Forms.KeyEventHandler(this.HotkeyUp);
//
// SwitchAllowLanTextBox
//
this.SwitchAllowLanTextBox.Dock = System.Windows.Forms.DockStyle.Fill;
this.SwitchAllowLanTextBox.Location = new System.Drawing.Point(183, 99);
this.SwitchAllowLanTextBox.Margin = new System.Windows.Forms.Padding(3, 3, 16, 3);
this.SwitchAllowLanTextBox.Name = "SwitchAllowLanTextBox";
this.SwitchAllowLanTextBox.ReadOnly = true;
this.SwitchAllowLanTextBox.Size = new System.Drawing.Size(276, 25);
this.SwitchAllowLanTextBox.TabIndex = 10;
this.SwitchAllowLanTextBox.TextChanged += new System.EventHandler(this.TextBox_TextChanged);
this.SwitchAllowLanTextBox.KeyDown += new System.Windows.Forms.KeyEventHandler(this.HotkeyDown);
this.SwitchAllowLanTextBox.KeyUp += new System.Windows.Forms.KeyEventHandler(this.HotkeyUp);
//
// ShowLogsTextBox
//
this.ShowLogsTextBox.Dock = System.Windows.Forms.DockStyle.Fill;
this.ShowLogsTextBox.Location = new System.Drawing.Point(183, 131);
this.ShowLogsTextBox.Margin = new System.Windows.Forms.Padding(3, 3, 16, 3);
this.ShowLogsTextBox.Name = "ShowLogsTextBox";
this.ShowLogsTextBox.ReadOnly = true;
this.ShowLogsTextBox.Size = new System.Drawing.Size(276, 25);
this.ShowLogsTextBox.TabIndex = 11;
this.ShowLogsTextBox.TextChanged += new System.EventHandler(this.TextBox_TextChanged);
this.ShowLogsTextBox.KeyDown += new System.Windows.Forms.KeyEventHandler(this.HotkeyDown);
this.ShowLogsTextBox.KeyUp += new System.Windows.Forms.KeyEventHandler(this.HotkeyUp);
//
// ServerMoveUpTextBox
//
this.ServerMoveUpTextBox.Dock = System.Windows.Forms.DockStyle.Fill;
this.ServerMoveUpTextBox.Location = new System.Drawing.Point(183, 163);
this.ServerMoveUpTextBox.Margin = new System.Windows.Forms.Padding(3, 3, 16, 3);
this.ServerMoveUpTextBox.Name = "ServerMoveUpTextBox";
this.ServerMoveUpTextBox.ReadOnly = true;
this.ServerMoveUpTextBox.Size = new System.Drawing.Size(276, 25);
this.ServerMoveUpTextBox.TabIndex = 12;
this.ServerMoveUpTextBox.TextChanged += new System.EventHandler(this.TextBox_TextChanged);
this.ServerMoveUpTextBox.KeyDown += new System.Windows.Forms.KeyEventHandler(this.HotkeyDown);
this.ServerMoveUpTextBox.KeyUp += new System.Windows.Forms.KeyEventHandler(this.HotkeyUp);
//
// ServerMoveDownTextBox
//
this.ServerMoveDownTextBox.Dock = System.Windows.Forms.DockStyle.Fill;
this.ServerMoveDownTextBox.Location = new System.Drawing.Point(183, 197);
this.ServerMoveDownTextBox.Margin = new System.Windows.Forms.Padding(3, 3, 16, 3);
this.ServerMoveDownTextBox.Name = "ServerMoveDownTextBox";
this.ServerMoveDownTextBox.ReadOnly = true;
this.ServerMoveDownTextBox.Size = new System.Drawing.Size(276, 25);
this.ServerMoveDownTextBox.TabIndex = 13;
this.ServerMoveDownTextBox.TextChanged += new System.EventHandler(this.TextBox_TextChanged);
this.ServerMoveDownTextBox.KeyDown += new System.Windows.Forms.KeyEventHandler(this.HotkeyDown);
this.ServerMoveDownTextBox.KeyUp += new System.Windows.Forms.KeyEventHandler(this.HotkeyUp);
//
// HotkeySettingsForm
//
this.AutoScaleDimensions = new System.Drawing.SizeF(8F, 19F);
this.AutoScaleMode = System.Windows.Forms.AutoScaleMode.Font;
this.ClientSize = new System.Drawing.Size(475, 271);
this.Controls.Add(this.tableLayoutPanel1);
this.Font = new System.Drawing.Font("微软雅黑", 10F, System.Drawing.FontStyle.Regular, System.Drawing.GraphicsUnit.Point, ((byte)(134)));
this.Margin = new System.Windows.Forms.Padding(4, 5, 4, 5);
this.MaximizeBox = false;
this.MinimizeBox = false;
this.Name = "HotkeySettingsForm";
this.SizeGripStyle = System.Windows.Forms.SizeGripStyle.Hide;
this.StartPosition = System.Windows.Forms.FormStartPosition.CenterScreen;
this.Text = "Edit Hotkeys...";
flowLayoutPanel1.ResumeLayout(false);
this.tableLayoutPanel1.ResumeLayout(false);
this.tableLayoutPanel1.PerformLayout();
this.ResumeLayout(false);
}
#endregion
private System.Windows.Forms.Label SwitchSystemProxyLabel;
private System.Windows.Forms.Label ChangeToPacLabel;
private System.Windows.Forms.Label ChangeToGlobalLabel;
private System.Windows.Forms.Label SwitchAllowLanLabel;
private System.Windows.Forms.Label ShowLogsLabel;
private System.Windows.Forms.Label ServerMoveUpLabel;
private System.Windows.Forms.Label ServerMoveDownLabel;
private System.Windows.Forms.Button btnOK;
private System.Windows.Forms.Button btnCancel;
private System.Windows.Forms.TextBox ShowLogsTextBox;
private System.Windows.Forms.TextBox SwitchAllowLanTextBox;
private System.Windows.Forms.TextBox ChangeToGlobalTextBox;
private System.Windows.Forms.TextBox ChangeToPacTextBox;
private System.Windows.Forms.TextBox SwitchSystemProxyTextBox;
private System.Windows.Forms.TextBox ServerMoveUpTextBox;
private System.Windows.Forms.TextBox ServerMoveDownTextBox;
private System.Windows.Forms.Button btnRegisterAll;
private System.Windows.Forms.TableLayoutPanel tableLayoutPanel1;
}
}

+ 183
- 0
shadowsocks-csharp/View/HotkeySettingsForm.resx View File

@@ -0,0 +1,183 @@
<?xml version="1.0" encoding="utf-8"?>
<root>
<!--
Microsoft ResX Schema
Version 2.0
The primary goals of this format is to allow a simple XML format
that is mostly human readable. The generation and parsing of the
various data types are done through the TypeConverter classes
associated with the data types.
Example:
... ado.net/XML headers & schema ...
<resheader name="resmimetype">text/microsoft-resx</resheader>
<resheader name="version">2.0</resheader>
<resheader name="reader">System.Resources.ResXResourceReader, System.Windows.Forms, ...</resheader>
<resheader name="writer">System.Resources.ResXResourceWriter, System.Windows.Forms, ...</resheader>
<data name="Name1"><value>this is my long string</value><comment>this is a comment</comment></data>
<data name="Color1" type="System.Drawing.Color, System.Drawing">Blue</data>
<data name="Bitmap1" mimetype="application/x-microsoft.net.object.binary.base64">
<value>[base64 mime encoded serialized .NET Framework object]</value>
</data>
<data name="Icon1" type="System.Drawing.Icon, System.Drawing" mimetype="application/x-microsoft.net.object.bytearray.base64">
<value>[base64 mime encoded string representing a byte array form of the .NET Framework object]</value>
<comment>This is a comment</comment>
</data>
There are any number of "resheader" rows that contain simple
name/value pairs.
Each data row contains a name, and value. The row also contains a
type or mimetype. Type corresponds to a .NET class that support
text/value conversion through the TypeConverter architecture.
Classes that don't support this are serialized and stored with the
mimetype set.
The mimetype is used for serialized objects, and tells the
ResXResourceReader how to depersist the object. This is currently not
extensible. For a given mimetype the value must be set accordingly:
Note - application/x-microsoft.net.object.binary.base64 is the format
that the ResXResourceWriter will generate, however the reader can
read any of the formats listed below.
mimetype: application/x-microsoft.net.object.binary.base64
value : The object must be serialized with
: System.Runtime.Serialization.Formatters.Binary.BinaryFormatter
: and then encoded with base64 encoding.
mimetype: application/x-microsoft.net.object.soap.base64
value : The object must be serialized with
: System.Runtime.Serialization.Formatters.Soap.SoapFormatter
: and then encoded with base64 encoding.
mimetype: application/x-microsoft.net.object.bytearray.base64
value : The object must be serialized into a byte array
: using a System.ComponentModel.TypeConverter
: and then encoded with base64 encoding.
-->
<xsd:schema id="root" xmlns="" xmlns:xsd="http://www.w3.org/2001/XMLSchema" xmlns:msdata="urn:schemas-microsoft-com:xml-msdata">
<xsd:import namespace="http://www.w3.org/XML/1998/namespace" />
<xsd:element name="root" msdata:IsDataSet="true">
<xsd:complexType>
<xsd:choice maxOccurs="unbounded">
<xsd:element name="metadata">
<xsd:complexType>
<xsd:sequence>
<xsd:element name="value" type="xsd:string" minOccurs="0" />
</xsd:sequence>
<xsd:attribute name="name" use="required" type="xsd:string" />
<xsd:attribute name="type" type="xsd:string" />
<xsd:attribute name="mimetype" type="xsd:string" />
<xsd:attribute ref="xml:space" />
</xsd:complexType>
</xsd:element>
<xsd:element name="assembly">
<xsd:complexType>
<xsd:attribute name="alias" type="xsd:string" />
<xsd:attribute name="name" type="xsd:string" />
</xsd:complexType>
</xsd:element>
<xsd:element name="data">
<xsd:complexType>
<xsd:sequence>
<xsd:element name="value" type="xsd:string" minOccurs="0" msdata:Ordinal="1" />
<xsd:element name="comment" type="xsd:string" minOccurs="0" msdata:Ordinal="2" />
</xsd:sequence>
<xsd:attribute name="name" type="xsd:string" use="required" msdata:Ordinal="1" />
<xsd:attribute name="type" type="xsd:string" msdata:Ordinal="3" />
<xsd:attribute name="mimetype" type="xsd:string" msdata:Ordinal="4" />
<xsd:attribute ref="xml:space" />
</xsd:complexType>
</xsd:element>
<xsd:element name="resheader">
<xsd:complexType>
<xsd:sequence>
<xsd:element name="value" type="xsd:string" minOccurs="0" msdata:Ordinal="1" />
</xsd:sequence>
<xsd:attribute name="name" type="xsd:string" use="required" />
</xsd:complexType>
</xsd:element>
</xsd:choice>
</xsd:complexType>
</xsd:element>
</xsd:schema>
<resheader name="resmimetype">
<value>text/microsoft-resx</value>
</resheader>
<resheader name="version">
<value>2.0</value>
</resheader>
<resheader name="reader">
<value>System.Resources.ResXResourceReader, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089</value>
</resheader>
<resheader name="writer">
<value>System.Resources.ResXResourceWriter, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089</value>
</resheader>
<metadata name="flowLayoutPanel1.GenerateMember" type="System.Boolean, mscorlib, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089">
<value>False</value>
</metadata>
<metadata name="flowLayoutPanel1.Locked" type="System.Boolean, mscorlib, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089">
<value>True</value>
</metadata>
<metadata name="tableLayoutPanel1.Locked" type="System.Boolean, mscorlib, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089">
<value>True</value>
</metadata>
<metadata name="SwitchSystemProxyLabel.Locked" type="System.Boolean, mscorlib, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089">
<value>True</value>
</metadata>
<metadata name="ChangeToPacLabel.Locked" type="System.Boolean, mscorlib, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089">
<value>True</value>
</metadata>
<metadata name="ChangeToGlobalLabel.Locked" type="System.Boolean, mscorlib, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089">
<value>True</value>
</metadata>
<metadata name="SwitchAllowLanLabel.Locked" type="System.Boolean, mscorlib, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089">
<value>True</value>
</metadata>
<metadata name="ShowLogsLabel.Locked" type="System.Boolean, mscorlib, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089">
<value>True</value>
</metadata>
<metadata name="ServerMoveUpLabel.Locked" type="System.Boolean, mscorlib, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089">
<value>True</value>
</metadata>
<metadata name="ServerMoveDownLabel.Locked" type="System.Boolean, mscorlib, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089">
<value>True</value>
</metadata>
<metadata name="SwitchSystemProxyTextBox.Locked" type="System.Boolean, mscorlib, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089">
<value>True</value>
</metadata>
<metadata name="ChangeToPacTextBox.Locked" type="System.Boolean, mscorlib, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089">
<value>True</value>
</metadata>
<metadata name="ChangeToGlobalTextBox.Locked" type="System.Boolean, mscorlib, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089">
<value>True</value>
</metadata>
<metadata name="SwitchAllowLanTextBox.Locked" type="System.Boolean, mscorlib, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089">
<value>True</value>
</metadata>
<metadata name="ShowLogsTextBox.Locked" type="System.Boolean, mscorlib, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089">
<value>True</value>
</metadata>
<metadata name="ServerMoveUpTextBox.Locked" type="System.Boolean, mscorlib, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089">
<value>True</value>
</metadata>
<metadata name="ServerMoveDownTextBox.Locked" type="System.Boolean, mscorlib, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089">
<value>True</value>
</metadata>
<metadata name="btnOK.Locked" type="System.Boolean, mscorlib, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089">
<value>True</value>
</metadata>
<metadata name="btnCancel.Locked" type="System.Boolean, mscorlib, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089">
<value>True</value>
</metadata>
<metadata name="btnRegisterAll.Locked" type="System.Boolean, mscorlib, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089">
<value>True</value>
</metadata>
<metadata name="$this.Locked" type="System.Boolean, mscorlib, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089">
<value>True</value>
</metadata>
</root>

+ 41
- 32
shadowsocks-csharp/View/LogForm.cs View File

@@ -10,9 +10,22 @@ using Shadowsocks.Controller;
using Shadowsocks.Properties; using Shadowsocks.Properties;
using Shadowsocks.Model; using Shadowsocks.Model;
using Shadowsocks.Util; using Shadowsocks.Util;
using System.Text;
namespace Shadowsocks.View namespace Shadowsocks.View
{ {
struct TrafficInfo
{
public long inbound;
public long outbound;
public TrafficInfo(long inbound, long outbound)
{
this.inbound = inbound;
this.outbound = outbound;
}
}
public partial class LogForm : Form public partial class LogForm : Form
{ {
long lastOffset; long lastOffset;
@@ -23,7 +36,7 @@ namespace Shadowsocks.View
#region chart #region chart
long lastMaxSpeed; long lastMaxSpeed;
public ShadowsocksController.QueueLast<Tuple<long, long>> traffic = new ShadowsocksController.QueueLast<Tuple<long, long>>();
ShadowsocksController.QueueLast<TrafficInfo> traffic = new ShadowsocksController.QueueLast<TrafficInfo>();
#endregion #endregion
public LogForm(ShadowsocksController controller, string filename) public LogForm(ShadowsocksController controller, string filename)
@@ -34,19 +47,13 @@ namespace Shadowsocks.View
Icon = Icon.FromHandle(Resources.ssw128.GetHicon()); Icon = Icon.FromHandle(Resources.ssw128.GetHicon());
LogViewerConfig config = controller.GetConfigurationCopy().logViewer; LogViewerConfig config = controller.GetConfigurationCopy().logViewer;
if (config == null)
{
config = new LogViewerConfig();
}
else
{
topMostTrigger = config.topMost;
wrapTextTrigger = config.wrapText;
toolbarTrigger = config.toolbarShown;
LogMessageTextBox.BackColor = config.GetBackgroundColor();
LogMessageTextBox.ForeColor = config.GetTextColor();
LogMessageTextBox.Font = config.GetFont();
}
topMostTrigger = config.topMost;
wrapTextTrigger = config.wrapText;
toolbarTrigger = config.toolbarShown;
LogMessageTextBox.BackColor = config.GetBackgroundColor();
LogMessageTextBox.ForeColor = config.GetTextColor();
LogMessageTextBox.Font = config.GetFont();
controller.TrafficChanged += controller_TrafficChanged; controller.TrafficChanged += controller_TrafficChanged;
@@ -59,7 +66,7 @@ namespace Shadowsocks.View
List<float> outboundPoints = new List<float>(); List<float> outboundPoints = new List<float>();
TextAnnotation inboundAnnotation = new TextAnnotation(); TextAnnotation inboundAnnotation = new TextAnnotation();
TextAnnotation outboundAnnotation = new TextAnnotation(); TextAnnotation outboundAnnotation = new TextAnnotation();
Tuple<float, string, long> bandwidthScale;
BandwidthScaleInfo bandwidthScale;
const long minScale = 50; const long minScale = 50;
long maxSpeed = 0; long maxSpeed = 0;
long lastInbound, lastOutbound; long lastInbound, lastOutbound;
@@ -70,12 +77,12 @@ namespace Shadowsocks.View
return; return;
foreach (var trafficPerSecond in traffic) foreach (var trafficPerSecond in traffic)
{ {
inboundPoints.Add(trafficPerSecond.Item1);
outboundPoints.Add(trafficPerSecond.Item2);
maxSpeed = Math.Max(maxSpeed, Math.Max(trafficPerSecond.Item1, trafficPerSecond.Item2));
inboundPoints.Add(trafficPerSecond.inbound);
outboundPoints.Add(trafficPerSecond.outbound);
maxSpeed = Math.Max(maxSpeed, Math.Max(trafficPerSecond.inbound, trafficPerSecond.outbound));
} }
lastInbound = traffic.Last().Item1;
lastOutbound = traffic.Last().Item2;
lastInbound = traffic.Last().inbound;
lastOutbound = traffic.Last().outbound;
} }
if (maxSpeed > 0) if (maxSpeed > 0)
@@ -92,15 +99,15 @@ namespace Shadowsocks.View
bandwidthScale = Utils.GetBandwidthScale(maxSpeed); bandwidthScale = Utils.GetBandwidthScale(maxSpeed);
//rescale the original data points, since it is List<float>, .ForEach does not work //rescale the original data points, since it is List<float>, .ForEach does not work
inboundPoints = inboundPoints.Select(p => p / bandwidthScale.Item3).ToList();
outboundPoints = outboundPoints.Select(p => p / bandwidthScale.Item3).ToList();
inboundPoints = inboundPoints.Select(p => p / bandwidthScale.unit).ToList();
outboundPoints = outboundPoints.Select(p => p / bandwidthScale.unit).ToList();
if (trafficChart.IsHandleCreated) if (trafficChart.IsHandleCreated)
{ {
trafficChart.Series["Inbound"].Points.DataBindY(inboundPoints); trafficChart.Series["Inbound"].Points.DataBindY(inboundPoints);
trafficChart.Series["Outbound"].Points.DataBindY(outboundPoints); trafficChart.Series["Outbound"].Points.DataBindY(outboundPoints);
trafficChart.ChartAreas[0].AxisY.LabelStyle.Format = "{0:0.##} " + bandwidthScale.Item2;
trafficChart.ChartAreas[0].AxisY.Maximum = bandwidthScale.Item1;
trafficChart.ChartAreas[0].AxisY.LabelStyle.Format = "{0:0.##} " + bandwidthScale.unit_name;
trafficChart.ChartAreas[0].AxisY.Maximum = bandwidthScale.value;
inboundAnnotation.AnchorDataPoint = trafficChart.Series["Inbound"].Points.Last(); inboundAnnotation.AnchorDataPoint = trafficChart.Series["Inbound"].Points.Last();
inboundAnnotation.Text = Utils.FormatBandwidth(lastInbound); inboundAnnotation.Text = Utils.FormatBandwidth(lastInbound);
outboundAnnotation.AnchorDataPoint = trafficChart.Series["Outbound"].Points.Last(); outboundAnnotation.AnchorDataPoint = trafficChart.Series["Outbound"].Points.Last();
@@ -115,10 +122,10 @@ namespace Shadowsocks.View
{ {
lock (this) lock (this)
{ {
traffic = new ShadowsocksController.QueueLast<Tuple<long, long>>();
traffic = new ShadowsocksController.QueueLast<TrafficInfo>();
foreach (var trafficPerSecond in controller.traffic) foreach (var trafficPerSecond in controller.traffic)
{ {
traffic.Enqueue(new Tuple<long, long>(trafficPerSecond.inboundIncreasement, trafficPerSecond.outboundIncreasement));
traffic.Enqueue(new TrafficInfo(trafficPerSecond.inboundIncreasement, trafficPerSecond.outboundIncreasement));
} }
} }
} }
@@ -162,9 +169,11 @@ namespace Shadowsocks.View
} }
string line = ""; string line = "";
StringBuilder appendText = new StringBuilder(1024);
while ((line = reader.ReadLine()) != null) while ((line = reader.ReadLine()) != null)
LogMessageTextBox.AppendText(line + Environment.NewLine);
appendText.Append(line + Environment.NewLine);
LogMessageTextBox.AppendText(appendText.ToString());
LogMessageTextBox.ScrollToCaret(); LogMessageTextBox.ScrollToCaret();
lastOffset = reader.BaseStream.Position; lastOffset = reader.BaseStream.Position;
@@ -182,14 +191,16 @@ namespace Shadowsocks.View
string line = ""; string line = "";
bool changed = false; bool changed = false;
StringBuilder appendText = new StringBuilder(128);
while ((line = reader.ReadLine()) != null) while ((line = reader.ReadLine()) != null)
{ {
changed = true; changed = true;
LogMessageTextBox.AppendText(line + Environment.NewLine);
appendText.Append(line + Environment.NewLine);
} }
if (changed) if (changed)
{ {
LogMessageTextBox.AppendText(appendText.ToString());
LogMessageTextBox.ScrollToCaret(); LogMessageTextBox.ScrollToCaret();
} }
@@ -214,8 +225,7 @@ namespace Shadowsocks.View
timer.Start(); timer.Start();
LogViewerConfig config = controller.GetConfigurationCopy().logViewer; LogViewerConfig config = controller.GetConfigurationCopy().logViewer;
if (config == null)
config = new LogViewerConfig();
Height = config.height; Height = config.height;
Width = config.width; Width = config.width;
Top = config.GetBestTop(); Top = config.GetBestTop();
@@ -241,8 +251,7 @@ namespace Shadowsocks.View
timer.Stop(); timer.Stop();
controller.TrafficChanged -= controller_TrafficChanged; controller.TrafficChanged -= controller_TrafficChanged;
LogViewerConfig config = controller.GetConfigurationCopy().logViewer; LogViewerConfig config = controller.GetConfigurationCopy().logViewer;
if (config == null)
config = new LogViewerConfig();
config.topMost = topMostTrigger; config.topMost = topMostTrigger;
config.wrapText = wrapTextTrigger; config.wrapText = wrapTextTrigger;
config.toolbarShown = toolbarTrigger; config.toolbarShown = toolbarTrigger;


+ 66
- 37
shadowsocks-csharp/View/MenuViewController.cs View File

@@ -49,11 +49,12 @@ namespace Shadowsocks.View
private MenuItem editOnlinePACItem; private MenuItem editOnlinePACItem;
private MenuItem autoCheckUpdatesToggleItem; private MenuItem autoCheckUpdatesToggleItem;
private MenuItem proxyItem; private MenuItem proxyItem;
private MenuItem hotKeyItem;
private MenuItem VerboseLoggingToggleItem; private MenuItem VerboseLoggingToggleItem;
private ConfigForm configForm; private ConfigForm configForm;
private ProxyForm proxyForm; private ProxyForm proxyForm;
private List<LogForm> logForms = new List<LogForm>();
private bool logFormsVisible = false;
private LogForm logForm;
private HotkeySettingsForm hotkeySettingsForm;
private string _urlToOpen; private string _urlToOpen;
public MenuViewController(ShadowsocksController controller) public MenuViewController(ShadowsocksController controller)
@@ -133,6 +134,8 @@ namespace Shadowsocks.View
MessageBox.Show(e.GetException().ToString(), String.Format(I18N.GetString("Shadowsocks Error: {0}"), e.GetException().Message)); MessageBox.Show(e.GetException().ToString(), String.Format(I18N.GetString("Shadowsocks Error: {0}"), e.GetException().Message));
} }
#region Tray Icon
private void UpdateTrayIcon() private void UpdateTrayIcon()
{ {
int dpi; int dpi;
@@ -177,10 +180,10 @@ namespace Shadowsocks.View
} }
// we want to show more details but notify icon title is limited to 63 characters // we want to show more details but notify icon title is limited to 63 characters
string text = I18N.GetString("Shadowsocks") + " " + UpdateChecker.Version + "\n" + string text = I18N.GetString("Shadowsocks") + " " + UpdateChecker.Version + "\n" +
(enabled ?
I18N.GetString("System Proxy On: ") + (global ? I18N.GetString("Global") : I18N.GetString("PAC")) :
String.Format(I18N.GetString("Running: Port {0}"), config.localPort)) // this feedback is very important because they need to know Shadowsocks is running
+ "\n" + serverInfo;
(enabled ?
I18N.GetString("System Proxy On: ") + (global ? I18N.GetString("Global") : I18N.GetString("PAC")) :
String.Format(I18N.GetString("Running: Port {0}"), config.localPort)) // this feedback is very important because they need to know Shadowsocks is running
+ "\n" + serverInfo;
_notifyIcon.Text = text.Substring(0, Math.Min(63, text.Length)); _notifyIcon.Text = text.Substring(0, Math.Min(63, text.Length));
} }
@@ -201,7 +204,7 @@ namespace Shadowsocks.View
else if (global) else if (global)
{ {
Color flyBlue = Color.FromArgb(25, 125, 191); Color flyBlue = Color.FromArgb(25, 125, 191);
// Muliply with flyBlue
// Multiply with flyBlue
int red = color.R * flyBlue.R / 255; int red = color.R * flyBlue.R / 255;
int green = color.G * flyBlue.G / 255; int green = color.G * flyBlue.G / 255;
int blue = color.B * flyBlue.B / 255; int blue = color.B * flyBlue.B / 255;
@@ -230,6 +233,10 @@ namespace Shadowsocks.View
return bitmap; return bitmap;
} }
#endregion
#region MenuItems and MenuGroups
private MenuItem CreateMenuItem(string text, EventHandler click) private MenuItem CreateMenuItem(string text, EventHandler click)
{ {
return new MenuItem(I18N.GetString(text), click); return new MenuItem(I18N.GetString(text), click);
@@ -271,6 +278,7 @@ namespace Shadowsocks.View
new MenuItem("-"), new MenuItem("-"),
CreateMenuItem("Show Logs...", new EventHandler(this.ShowLogItem_Click)), CreateMenuItem("Show Logs...", new EventHandler(this.ShowLogItem_Click)),
this.VerboseLoggingToggleItem = CreateMenuItem( "Verbose Logging", new EventHandler(this.VerboseLoggingToggleItem_Click) ), this.VerboseLoggingToggleItem = CreateMenuItem( "Verbose Logging", new EventHandler(this.VerboseLoggingToggleItem_Click) ),
this.hotKeyItem = CreateMenuItem("Edit Hotkeys...", new EventHandler(this.hotKeyItem_Click)),
CreateMenuGroup("Updates...", new MenuItem[] { CreateMenuGroup("Updates...", new MenuItem[] {
CreateMenuItem("Check for Updates...", new EventHandler(this.checkUpdatesItem_Click)), CreateMenuItem("Check for Updates...", new EventHandler(this.checkUpdatesItem_Click)),
new MenuItem("-"), new MenuItem("-"),
@@ -282,6 +290,8 @@ namespace Shadowsocks.View
}); });
} }
#endregion
private void controller_ConfigChanged(object sender, EventArgs e) private void controller_ConfigChanged(object sender, EventArgs e)
{ {
LoadCurrentConfiguration(); LoadCurrentConfiguration();
@@ -406,7 +416,7 @@ namespace Shadowsocks.View
i++; i++;
} }
// user want a seperator item between strategy and servers menugroup
// user wants a seperator item between strategy and servers menugroup
items.Add( i++, new MenuItem("-") ); items.Add( i++, new MenuItem("-") );
int strategyCount = i; int strategyCount = i;
@@ -459,31 +469,40 @@ namespace Shadowsocks.View
} }
} }
private void ShowLogForms()
private void ShowHotKeySettingsForm()
{ {
if (logForms.Count == 0)
if (hotkeySettingsForm != null)
{
hotkeySettingsForm.Activate();
}
else
{ {
LogForm f = new LogForm(controller, Logging.LogFilePath);
f.Show();
f.Activate();
f.FormClosed += logForm_FormClosed;
hotkeySettingsForm = new HotkeySettingsForm(controller);
hotkeySettingsForm.Show();
hotkeySettingsForm.Activate();
hotkeySettingsForm.FormClosed += hotkeySettingsForm_FormClosed;
}
}
logForms.Add(f);
logFormsVisible = true;
private void ShowLogForm()
{
if (logForm != null)
{
logForm.Activate();
} }
else else
{ {
logFormsVisible = !logFormsVisible;
foreach (LogForm f in logForms)
{
f.Visible = logFormsVisible;
}
logForm = new LogForm(controller, Logging.LogFilePath);
logForm.Show();
logForm.Activate();
logForm.FormClosed += logForm_FormClosed;
} }
} }
void logForm_FormClosed(object sender, FormClosedEventArgs e) void logForm_FormClosed(object sender, FormClosedEventArgs e)
{ {
logForms.Remove((LogForm)sender);
logForm = null;
Utils.ReleaseMemory(true);
} }
void configForm_FormClosed(object sender, FormClosedEventArgs e) void configForm_FormClosed(object sender, FormClosedEventArgs e)
@@ -504,6 +523,12 @@ namespace Shadowsocks.View
Utils.ReleaseMemory(true); Utils.ReleaseMemory(true);
} }
void hotkeySettingsForm_FormClosed(object sender, FormClosedEventArgs e)
{
hotkeySettingsForm = null;
Utils.ReleaseMemory(true);
}
private void Config_Click(object sender, EventArgs e) private void Config_Click(object sender, EventArgs e)
{ {
ShowConfigForm(); ShowConfigForm();
@@ -519,11 +544,9 @@ namespace Shadowsocks.View
private void CheckUpdateForFirstRun() private void CheckUpdateForFirstRun()
{ {
Configuration config = controller.GetConfigurationCopy(); Configuration config = controller.GetConfigurationCopy();
if (!config.isDefault)
{
_isStartupChecking = true;
updateChecker.CheckUpdate(config, 3000);
}
if (config.isDefault) return;
_isStartupChecking = true;
updateChecker.CheckUpdate(config, 3000);
} }
private void ShowFirstTimeBalloon() private void ShowFirstTimeBalloon()
@@ -547,7 +570,7 @@ namespace Shadowsocks.View
} }
else if (e.Button == MouseButtons.Middle) else if (e.Button == MouseButtons.Middle)
{ {
ShowLogForms();
ShowLogForm();
} }
} }
@@ -607,15 +630,6 @@ namespace Shadowsocks.View
controller.SelectStrategy((string)item.Tag); controller.SelectStrategy((string)item.Tag);
} }
private void ShowLogItem_Click(object sender, EventArgs e)
{
LogForm f = new LogForm(controller, Logging.LogFilePath);
f.Show();
f.FormClosed += logForm_FormClosed;
logForms.Add(f);
}
private void VerboseLoggingToggleItem_Click( object sender, EventArgs e ) { private void VerboseLoggingToggleItem_Click( object sender, EventArgs e ) {
VerboseLoggingToggleItem.Checked = ! VerboseLoggingToggleItem.Checked; VerboseLoggingToggleItem.Checked = ! VerboseLoggingToggleItem.Checked;
controller.ToggleVerboseLogging( VerboseLoggingToggleItem.Checked ); controller.ToggleVerboseLogging( VerboseLoggingToggleItem.Checked );
@@ -820,5 +834,20 @@ namespace Shadowsocks.View
{ {
ShowProxyForm(); ShowProxyForm();
} }
private void hotKeyItem_Click(object sender, EventArgs e)
{
ShowHotKeySettingsForm();
}
private void ShowLogItem_Click(object sender, EventArgs e)
{
ShowLogForm();
}
public void ShowLogForm_HotKey()
{
ShowLogForm();
}
} }
} }

+ 125
- 116
shadowsocks-csharp/View/ProxyForm.cs View File

@@ -1,116 +1,125 @@
using System;
using System.Drawing;
using System.Windows.Forms;
using Shadowsocks.Controller;
using Shadowsocks.Model;
using Shadowsocks.Properties;

namespace Shadowsocks.View
{
public partial class ProxyForm : Form
{
private ShadowsocksController controller;

// this is a copy of configuration that we are working on
private Configuration _modifiedConfiguration;

public ProxyForm(ShadowsocksController controller)
{
this.Font = System.Drawing.SystemFonts.MessageBoxFont;
InitializeComponent();

UpdateTexts();
this.Icon = Icon.FromHandle(Resources.ssw128.GetHicon());

this.controller = controller;
controller.ConfigChanged += controller_ConfigChanged;

UpdateEnabled();
LoadCurrentConfiguration();
}

private void UpdateTexts()
{
UseProxyCheckBox.Text = I18N.GetString("Use Proxy");
ProxyAddrLabel.Text = I18N.GetString("Proxy Addr");
ProxyPortLable.Text = I18N.GetString("Proxy Port");
OKButton.Text = I18N.GetString("OK");
MyCancelButton.Text = I18N.GetString("Cancel");
this.Text = I18N.GetString("Edit Proxy");
}

private void controller_ConfigChanged(object sender, EventArgs e)
{
LoadCurrentConfiguration();
}

private void LoadCurrentConfiguration()
{
_modifiedConfiguration = controller.GetConfigurationCopy();

UseProxyCheckBox.Checked = _modifiedConfiguration.useProxy;
ProxyServerTextBox.Text = _modifiedConfiguration.proxyServer;
ProxyPortTextBox.Text = _modifiedConfiguration.proxyPort.ToString();
}

private void OKButton_Click(object sender, EventArgs e)
{
if (UseProxyCheckBox.Checked)
{
try
{
var proxy = ProxyServerTextBox.Text;
var port = int.Parse(ProxyPortTextBox.Text);
Configuration.CheckServer(proxy);
Configuration.CheckPort(port);

controller.EnableProxy(proxy, port);
}
catch (FormatException)
{
MessageBox.Show(I18N.GetString("Illegal port number format"));
return;
}
catch (Exception ex)
{
MessageBox.Show(ex.Message);
return;
}
}
else
{
controller.DisableProxy();
}
this.Close();
}

private void CancelButton_Click(object sender, EventArgs e)
{
this.Close();
}

private void ProxyForm_FormClosed(object sender, FormClosedEventArgs e)
{
controller.ConfigChanged -= controller_ConfigChanged;
}

private void UseProxyCheckBox_CheckedChanged(object sender, EventArgs e)
{
UpdateEnabled();
}

private void UpdateEnabled()
{
if (UseProxyCheckBox.Checked)
{
ProxyServerTextBox.Enabled = true;
ProxyPortTextBox.Enabled = true;
}
else
{
ProxyServerTextBox.Enabled = false;
ProxyPortTextBox.Enabled = false;
}
}
}
}
using System;
using System.Drawing;
using System.Windows.Forms;
using Shadowsocks.Controller;
using Shadowsocks.Model;
using Shadowsocks.Properties;
namespace Shadowsocks.View
{
public partial class ProxyForm : Form
{
private ShadowsocksController controller;
// this is a copy of configuration that we are working on
private ProxyConfig _modifiedConfiguration;
public ProxyForm(ShadowsocksController controller)
{
this.Font = System.Drawing.SystemFonts.MessageBoxFont;
InitializeComponent();
UpdateTexts();
this.Icon = Icon.FromHandle(Resources.ssw128.GetHicon());
this.controller = controller;
controller.ConfigChanged += controller_ConfigChanged;
UpdateEnabled();
LoadCurrentConfiguration();
}
private void UpdateTexts()
{
UseProxyCheckBox.Text = I18N.GetString("Use Proxy");
ProxyAddrLabel.Text = I18N.GetString("Proxy Addr");
ProxyPortLable.Text = I18N.GetString("Proxy Port");
OKButton.Text = I18N.GetString("OK");
MyCancelButton.Text = I18N.GetString("Cancel");
this.Text = I18N.GetString("Edit Proxy");
}
private void controller_ConfigChanged(object sender, EventArgs e)
{
LoadCurrentConfiguration();
}
private void LoadCurrentConfiguration()
{
_modifiedConfiguration = controller.GetConfigurationCopy().proxy;
UseProxyCheckBox.Checked = _modifiedConfiguration.useProxy;
ProxyServerTextBox.Text = _modifiedConfiguration.proxyServer;
ProxyPortTextBox.Text = _modifiedConfiguration.proxyPort.ToString();
}
private void OKButton_Click(object sender, EventArgs e)
{
if (UseProxyCheckBox.Checked)
{
try
{
var proxy = ProxyServerTextBox.Text;
var port = int.Parse(ProxyPortTextBox.Text);
Configuration.CheckServer(proxy);
Configuration.CheckPort(port);
controller.EnableProxy(proxy, port);
}
catch (FormatException)
{
MessageBox.Show(I18N.GetString("Illegal port number format"));
return;
}
catch (Exception ex)
{
MessageBox.Show(ex.Message);
return;
}
}
else
{
controller.DisableProxy();
}
_modifiedConfiguration.useProxy = UseProxyCheckBox.Checked;
_modifiedConfiguration.proxyServer = ProxyServerTextBox.Text;
var tmpProxyPort = 0;
int.TryParse(ProxyPortTextBox.Text, out tmpProxyPort);
_modifiedConfiguration.proxyPort = tmpProxyPort;
controller.SaveProxyConfig(_modifiedConfiguration);
this.Close();
}
private void CancelButton_Click(object sender, EventArgs e)
{
this.Close();
}
private void ProxyForm_FormClosed(object sender, FormClosedEventArgs e)
{
controller.ConfigChanged -= controller_ConfigChanged;
}
private void UseProxyCheckBox_CheckedChanged(object sender, EventArgs e)
{
UpdateEnabled();
}
private void UpdateEnabled()
{
if (UseProxyCheckBox.Checked)
{
ProxyServerTextBox.Enabled = true;
ProxyPortTextBox.Enabled = true;
}
else
{
ProxyServerTextBox.Clear();
ProxyPortTextBox.Clear();
ProxyServerTextBox.Enabled = false;
ProxyPortTextBox.Enabled = false;
}
}
}
}

+ 1
- 0
shadowsocks-csharp/packages.config View File

@@ -3,6 +3,7 @@
<package id="Caseless.Fody" version="1.4.2" targetFramework="net40-client" developmentDependency="true" /> <package id="Caseless.Fody" version="1.4.2" targetFramework="net40-client" developmentDependency="true" />
<package id="Costura.Fody" version="1.3.3.0" targetFramework="net40-client" developmentDependency="true" /> <package id="Costura.Fody" version="1.3.3.0" targetFramework="net40-client" developmentDependency="true" />
<package id="Fody" version="1.29.4" targetFramework="net40-client" developmentDependency="true" /> <package id="Fody" version="1.29.4" targetFramework="net40-client" developmentDependency="true" />
<package id="GlobalHotKey" version="1.1.0" targetFramework="net40-client" />
<package id="Newtonsoft.Json" version="9.0.1" targetFramework="net40-client" /> <package id="Newtonsoft.Json" version="9.0.1" targetFramework="net40-client" />
<package id="StringEx.CS" version="0.3.1" targetFramework="net40-client" developmentDependency="true" /> <package id="StringEx.CS" version="0.3.1" targetFramework="net40-client" developmentDependency="true" />
</packages> </packages>

+ 16
- 0
shadowsocks-csharp/shadowsocks-csharp.csproj View File

@@ -65,6 +65,10 @@
<ApplicationManifest>app.manifest</ApplicationManifest> <ApplicationManifest>app.manifest</ApplicationManifest>
</PropertyGroup> </PropertyGroup>
<ItemGroup> <ItemGroup>
<Reference Include="GlobalHotKey, Version=1.1.0.0, Culture=neutral, processorArchitecture=MSIL">
<HintPath>3rd\GlobalHotKey.1.1.0\lib\GlobalHotKey.dll</HintPath>
<Private>True</Private>
</Reference>
<Reference Include="Microsoft.CSharp" /> <Reference Include="Microsoft.CSharp" />
<Reference Include="Microsoft.VisualBasic" /> <Reference Include="Microsoft.VisualBasic" />
<Reference Include="Newtonsoft.Json, Version=9.0.0.0, Culture=neutral, PublicKeyToken=30ad4fe6b2a6aeed, processorArchitecture=MSIL"> <Reference Include="Newtonsoft.Json, Version=9.0.0.0, Culture=neutral, PublicKeyToken=30ad4fe6b2a6aeed, processorArchitecture=MSIL">
@@ -139,6 +143,8 @@
<Compile Include="3rd\zxing\ResultPoint.cs" /> <Compile Include="3rd\zxing\ResultPoint.cs" />
<Compile Include="3rd\zxing\ResultPointCallback.cs" /> <Compile Include="3rd\zxing\ResultPointCallback.cs" />
<Compile Include="3rd\zxing\WriterException.cs" /> <Compile Include="3rd\zxing\WriterException.cs" />
<Compile Include="Model\HotKeyConfig.cs" />
<Compile Include="Model\ProxyConfig.cs" />
<Compile Include="Proxy\DirectConnect.cs" /> <Compile Include="Proxy\DirectConnect.cs" />
<Compile Include="Proxy\IProxy.cs" /> <Compile Include="Proxy\IProxy.cs" />
<Compile Include="Controller\Service\AvailabilityStatistics.cs" /> <Compile Include="Controller\Service\AvailabilityStatistics.cs" />
@@ -177,6 +183,7 @@
<Compile Include="Controller\Strategy\IStrategy.cs" /> <Compile Include="Controller\Strategy\IStrategy.cs" />
<Compile Include="Proxy\Socks5Proxy.cs" /> <Compile Include="Proxy\Socks5Proxy.cs" />
<Compile Include="StringEx.cs" /> <Compile Include="StringEx.cs" />
<Compile Include="Util\Hotkeys.cs" />
<Compile Include="Util\ProcessManagement\Job.cs" /> <Compile Include="Util\ProcessManagement\Job.cs" />
<Compile Include="Util\ProcessManagement\ThreadUtil.cs" /> <Compile Include="Util\ProcessManagement\ThreadUtil.cs" />
<Compile Include="Util\Sockets\SocketUtil.cs" /> <Compile Include="Util\Sockets\SocketUtil.cs" />
@@ -200,6 +207,12 @@
<Compile Include="View\CalculationControl.Designer.cs"> <Compile Include="View\CalculationControl.Designer.cs">
<DependentUpon>CalculationControl.cs</DependentUpon> <DependentUpon>CalculationControl.cs</DependentUpon>
</Compile> </Compile>
<Compile Include="View\HotkeySettingsForm.cs">
<SubType>Form</SubType>
</Compile>
<Compile Include="View\HotkeySettingsForm.designer.cs">
<DependentUpon>HotkeySettingsForm.cs</DependentUpon>
</Compile>
<Compile Include="View\LogForm.cs"> <Compile Include="View\LogForm.cs">
<SubType>Form</SubType> <SubType>Form</SubType>
</Compile> </Compile>
@@ -240,6 +253,9 @@
<EmbeddedResource Include="View\CalculationControl.resx"> <EmbeddedResource Include="View\CalculationControl.resx">
<DependentUpon>CalculationControl.cs</DependentUpon> <DependentUpon>CalculationControl.cs</DependentUpon>
</EmbeddedResource> </EmbeddedResource>
<EmbeddedResource Include="View\HotkeySettingsForm.resx">
<DependentUpon>HotkeySettingsForm.cs</DependentUpon>
</EmbeddedResource>
<EmbeddedResource Include="View\LogForm.resx"> <EmbeddedResource Include="View\LogForm.resx">
<DependentUpon>LogForm.cs</DependentUpon> <DependentUpon>LogForm.cs</DependentUpon>
</EmbeddedResource> </EmbeddedResource>


+ 29
- 0
test/UnitTest.cs View File

@@ -2,6 +2,9 @@
using Microsoft.VisualStudio.TestTools.UnitTesting; using Microsoft.VisualStudio.TestTools.UnitTesting;
using Shadowsocks.Controller; using Shadowsocks.Controller;
using Shadowsocks.Encryption; using Shadowsocks.Encryption;
using Shadowsocks.Util;
using GlobalHotKey;
using System.Windows.Input;
using System.Threading; using System.Threading;
using System.Collections.Generic; using System.Collections.Generic;
@@ -22,6 +25,32 @@ namespace test
Assert.IsTrue(UpdateChecker.Asset.CompareVersion("1.3.2", "1.3.1") > 0); Assert.IsTrue(UpdateChecker.Asset.CompareVersion("1.3.2", "1.3.1") > 0);
} }
[ TestMethod ]
public void TestHotKey2Str() {
Assert.AreEqual( "Ctrl+A", HotKeys.HotKey2Str( Key.A, ModifierKeys.Control ) );
Assert.AreEqual( "Ctrl+Alt+D2", HotKeys.HotKey2Str( Key.D2, (ModifierKeys.Alt | ModifierKeys.Control) ) );
Assert.AreEqual("Ctrl+Alt+Shift+NumPad7", HotKeys.HotKey2Str(Key.NumPad7, (ModifierKeys.Alt|ModifierKeys.Control|ModifierKeys.Shift)));
Assert.AreEqual( "Ctrl+Alt+Shift+F6", HotKeys.HotKey2Str( Key.F6, (ModifierKeys.Alt|ModifierKeys.Control|ModifierKeys.Shift)));
Assert.AreNotEqual("Ctrl+Shift+Alt+F6", HotKeys.HotKey2Str(Key.F6, (ModifierKeys.Alt | ModifierKeys.Control | ModifierKeys.Shift)));
}
[TestMethod]
public void TestStr2HotKey()
{
Assert.IsTrue(HotKeys.Str2HotKey("Ctrl+A").Equals(new HotKey(Key.A, ModifierKeys.Control)));
Assert.IsTrue(HotKeys.Str2HotKey("Ctrl+Alt+A").Equals(new HotKey(Key.A, (ModifierKeys.Control | ModifierKeys.Alt))));
Assert.IsTrue(HotKeys.Str2HotKey("Ctrl+Shift+A").Equals(new HotKey(Key.A, (ModifierKeys.Control | ModifierKeys.Shift))));
Assert.IsTrue(HotKeys.Str2HotKey("Ctrl+Alt+Shift+A").Equals(new HotKey(Key.A, (ModifierKeys.Control | ModifierKeys.Alt| ModifierKeys.Shift))));
HotKey testKey0 = HotKeys.Str2HotKey("Ctrl+Alt+Shift+A");
Assert.IsTrue(testKey0 != null && testKey0.Equals(new HotKey(Key.A, (ModifierKeys.Control | ModifierKeys.Alt | ModifierKeys.Shift))));
HotKey testKey1 = HotKeys.Str2HotKey("Ctrl+Alt+Shift+F2");
Assert.IsTrue(testKey1 != null && testKey1.Equals(new HotKey(Key.F2, (ModifierKeys.Control | ModifierKeys.Alt | ModifierKeys.Shift))));
HotKey testKey2 = HotKeys.Str2HotKey("Ctrl+Shift+Alt+D7");
Assert.IsTrue(testKey2 != null && testKey2.Equals(new HotKey(Key.D7, (ModifierKeys.Control | ModifierKeys.Alt | ModifierKeys.Shift))));
HotKey testKey3 = HotKeys.Str2HotKey("Ctrl+Shift+Alt+NumPad7");
Assert.IsTrue(testKey3 != null && testKey3.Equals(new HotKey(Key.NumPad7, (ModifierKeys.Control | ModifierKeys.Alt | ModifierKeys.Shift))));
}
[TestMethod] [TestMethod]
public void TestMD5() public void TestMD5()
{ {


+ 5
- 0
test/test.csproj View File

@@ -35,10 +35,15 @@
<StartupObject /> <StartupObject />
</PropertyGroup> </PropertyGroup>
<ItemGroup> <ItemGroup>
<Reference Include="GlobalHotKey, Version=1.1.0.0, Culture=neutral, processorArchitecture=MSIL">
<HintPath>..\shadowsocks-csharp\3rd\GlobalHotKey.1.1.0\lib\GlobalHotKey.dll</HintPath>
<Private>True</Private>
</Reference>
<Reference Include="System" /> <Reference Include="System" />
<Reference Include="System.Net" /> <Reference Include="System.Net" />
<Reference Include="System.Net.Http" /> <Reference Include="System.Net.Http" />
<Reference Include="System.Net.Http.WebRequest" /> <Reference Include="System.Net.Http.WebRequest" />
<Reference Include="WindowsBase" />
</ItemGroup> </ItemGroup>
<Choose> <Choose>
<When Condition="('$(VisualStudioVersion)' == '10.0' or '$(VisualStudioVersion)' == '') and '$(TargetFrameworkVersion)' == 'v3.5'"> <When Condition="('$(VisualStudioVersion)' == '10.0' or '$(VisualStudioVersion)' == '') and '$(TargetFrameworkVersion)' == 'v3.5'">


Loading…
Cancel
Save