Files
2dust_v2rayN/v2rayN/v2rayN/Forms/MainForm.cs
T
2020-04-19 06:29:07 +08:00

1736 lines
59 KiB
C#
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
using System;
using System.Collections.Generic;
using System.IO;
using System.Text;
using System.Windows.Forms;
using v2rayN.Handler;
using v2rayN.HttpProxyHandler;
using v2rayN.Mode;
using v2rayN.Base;
using v2rayN.Tool;
using System.Diagnostics;
using System.Drawing;
using System.Net;
using System.Threading.Tasks;
using System.Linq;
namespace v2rayN.Forms
{
public partial class MainForm : BaseForm
{
private V2rayHandler v2rayHandler;
private List<int> lvSelecteds = new List<int>(); // 使用前需用 GetServerListSelectedConfigIndex 更新
private StatisticsHandler statistics = null;
#region Window 事件
public MainForm()
{
InitializeComponent();
this.ShowInTaskbar = false;
this.WindowState = FormWindowState.Minimized;
HideForm();
this.Text = Utils.GetVersion();
Global.processJob = new Job();
Application.ApplicationExit += (sender, args) =>
{
Closes();
};
}
private void MainForm_Load(object sender, EventArgs e)
{
ConfigHandler.LoadConfig(ref config);
v2rayHandler = new V2rayHandler();
v2rayHandler.ProcessEvent += v2rayHandler_ProcessEvent;
if (config.enableStatistics)
{
statistics = new StatisticsHandler(config, UpdateStatisticsHandler);
}
Microsoft.Win32.SystemEvents.DisplaySettingsChanged += new EventHandler(SystemEvents_DisplaySettingsChanged);
}
private void MainForm_VisibleChanged(object sender, EventArgs e)
{
if (statistics == null || !statistics.Enable) return;
if ((sender as Form).Visible)
{
statistics.UpdateUI = true;
}
else
{
statistics.UpdateUI = false;
}
}
private void MainForm_Shown(object sender, EventArgs e)
{
InitServersView();
RefreshServers();
RestoreUI();
LoadV2ray();
HideForm();
}
private void MainForm_FormClosing(object sender, FormClosingEventArgs e)
{
if (e.CloseReason == CloseReason.UserClosing)
{
e.Cancel = true;
HideForm();
return;
}
}
private void MainForm_Resize(object sender, EventArgs e)
{
//if (this.WindowState == FormWindowState.Minimized)
//{
// HideForm();
//}
//else
//{
//}
}
//private const int WM_QUERYENDSESSION = 0x0011;
//protected override void WndProc(ref Message m)
//{
// switch (m.Msg)
// {
// case WM_QUERYENDSESSION:
// Utils.SaveLog("Windows shutdown UnsetProxy");
// ConfigHandler.ToJsonFile(config);
// statistics?.SaveToFile();
// ProxySetting.UnsetProxy();
// m.Result = (IntPtr)1;
// break;
// default:
// base.WndProc(ref m);
// break;
// }
//}
#endregion
#region 窗口大小和列宽等取/存
private void RestoreUI()
{
scMain.Panel2Collapsed = true;
if (!config.uiItem.mainSize.IsEmpty)
{
this.Width = config.uiItem.mainSize.Width;
this.Height = config.uiItem.mainSize.Height;
}
foreach (ColumnHeader c in lvServers.Columns)
{
var width = ConfigHandler.GetformMainLvColWidth(ref config, c.Name, c.Width);
c.Width = width;
}
}
// Deprecated.
private void StorageUI()
{
config.uiItem.mainSize = new Size(this.Width, this.Height);
foreach (ColumnHeader c in lvServers.Columns)
{
ConfigHandler.AddformMainLvColWidth(ref config, c.Name, c.Width);
}
}
#endregion
#region 显示服务器 listview 和 menu
/// <summary>
/// 刷新服务器
/// </summary>
private void RefreshServers()
{
RefreshServersView();
//lvServers.AutoResizeColumns();
RefreshServersMenu();
}
/// <summary>
/// 初始化服务器列表
/// </summary>
private void InitServersView()
{
lvServers.ListViewItemSorter = new Sorter();
lvServers.BeginUpdate();
lvServers.Items.Clear();
lvServers.Columns.Add(EServerColName.def.ToString(), "", 30);
lvServers.Columns.Add(EServerColName.type.ToString(), UIRes.I18N("LvServiceType"), 80);
lvServers.Columns.Add(EServerColName.remarks.ToString(), UIRes.I18N("LvAlias"), 100);
lvServers.Columns.Add(EServerColName.address.ToString(), UIRes.I18N("LvAddress"), 120);
lvServers.Columns.Add(EServerColName.port.ToString(), UIRes.I18N("LvPort"), 50);
lvServers.Columns.Add(EServerColName.security.ToString(), UIRes.I18N("LvEncryptionMethod"), 90);
lvServers.Columns.Add(EServerColName.network.ToString(), UIRes.I18N("LvTransportProtocol"), 70);
lvServers.Columns.Add(EServerColName.subRemarks.ToString(), UIRes.I18N("LvSubscription"), 50);
lvServers.Columns.Add(EServerColName.testResult.ToString(), UIRes.I18N("LvTestResults"), 70);
lvServers.Columns[EServerColName.port.ToString()].Tag = Global.sortMode.Numeric.ToString();
if (statistics != null && statistics.Enable)
{
lvServers.Columns.Add(EServerColName.todayDown.ToString(), UIRes.I18N("LvTodayDownloadDataAmount"), 70);
lvServers.Columns.Add(EServerColName.todayUp.ToString(), UIRes.I18N("LvTodayUploadDataAmount"), 70);
lvServers.Columns.Add(EServerColName.totalDown.ToString(), UIRes.I18N("LvTotalDownloadDataAmount"), 70);
lvServers.Columns.Add(EServerColName.totalUp.ToString(), UIRes.I18N("LvTotalUploadDataAmount"), 70);
lvServers.Columns[EServerColName.todayDown.ToString()].Tag = Global.sortMode.Numeric.ToString();
lvServers.Columns[EServerColName.todayUp.ToString()].Tag = Global.sortMode.Numeric.ToString();
lvServers.Columns[EServerColName.totalDown.ToString()].Tag = Global.sortMode.Numeric.ToString();
lvServers.Columns[EServerColName.totalUp.ToString()].Tag = Global.sortMode.Numeric.ToString();
}
foreach (ColumnHeader c in lvServers.Columns)
{
if (config.uiItem.mainLvColLayout == null) break;
int i = config.uiItem.mainLvColLayout.IndexOf(c.Name);
if (i >= 0) c.DisplayIndex = i;
}
lvServers.EndUpdate();
}
/// <summary>
/// 刷新服务器列表
/// </summary>
private void RefreshServersView()
{
lvServers.Items.Clear();
List<ListViewItem> lst = new List<ListViewItem>();
for (int k = 0; k < config.vmess.Count; k++)
{
string def = string.Empty;
string totalUp = string.Empty,
totalDown = string.Empty,
todayUp = string.Empty,
todayDown = string.Empty;
if (config.index.Equals(k))
{
def = "√";
}
VmessItem item = config.vmess[k];
void _addSubItem(ListViewItem i, string name, string text, object tag = null)
{
var n = new ListViewItem.ListViewSubItem() { Text = text };
n.Name = name; // new don't accept it.
n.Tag = tag; // cell's data store.
i.SubItems.Add(n);
}
ListViewItem lvItem = new ListViewItem(def);
lvItem.Tag = k; // the Tag of line is config's index.
_addSubItem(lvItem, EServerColName.type.ToString(), ((EConfigType)item.configType).ToString());
_addSubItem(lvItem, EServerColName.remarks.ToString(), item.remarks);
_addSubItem(lvItem, EServerColName.address.ToString(), item.address);
_addSubItem(lvItem, EServerColName.port.ToString(), item.port.ToString(), item.port);
_addSubItem(lvItem, EServerColName.security.ToString(), item.security);
_addSubItem(lvItem, EServerColName.network.ToString(), item.network);
_addSubItem(lvItem, EServerColName.subRemarks.ToString(), item.getSubRemarks(config));
_addSubItem(lvItem, EServerColName.testResult.ToString(), item.testResult);
bool stats = statistics != null && statistics.Enable;
if (stats)
{
ServerStatItem sItem = statistics.Statistic.Find(item_ => item_.itemId == item.getItemId());
if (sItem != null)
{
totalUp = Utils.HumanFy(sItem.totalUp);
totalDown = Utils.HumanFy(sItem.totalDown);
todayUp = Utils.HumanFy(sItem.todayUp);
todayDown = Utils.HumanFy(sItem.todayDown);
}
_addSubItem(lvItem, EServerColName.todayDown.ToString(), todayDown, sItem?.todayDown);
_addSubItem(lvItem, EServerColName.todayUp.ToString(), todayUp, sItem?.todayUp);
_addSubItem(lvItem, EServerColName.totalDown.ToString(), totalDown, sItem?.totalDown);
_addSubItem(lvItem, EServerColName.totalUp.ToString(), totalUp, sItem?.totalUp);
}
if (config.interlaceColoring && k % 2 == 1) // 隔行着色
{
lvItem.BackColor = SystemColors.Control;
}
if (config.index.Equals(k))
{
//lvItem.Checked = true;
lvItem.ForeColor = SystemColors.MenuHighlight;
lvItem.Font = new Font(lvItem.Font, FontStyle.Bold);
}
if (lvItem != null) lst.Add(lvItem);
}
lvServers.Items.AddRange(lst.ToArray());
//if (lvServers.Items.Count > 0)
//{
// if (lvServers.Items.Count <= testConfigIndex)
// {
// testConfigIndex = lvServers.Items.Count - 1;
// }
// lvServers.Items[testConfigIndex].Selected = true;
// lvServers.Select();
//}
}
/// <summary>
/// 刷新托盘服务器菜单
/// </summary>
private void RefreshServersMenu()
{
menuServers.DropDownItems.Clear();
List<ToolStripMenuItem> lst = new List<ToolStripMenuItem>();
for (int k = 0; k < config.vmess.Count; k++)
{
VmessItem item = config.vmess[k];
string name = item.getSummary();
ToolStripMenuItem ts = new ToolStripMenuItem(name)
{
Tag = k
};
if (config.index.Equals(k))
{
ts.Checked = true;
}
ts.Click += new EventHandler(ts_Click);
lst.Add(ts);
}
menuServers.DropDownItems.AddRange(lst.ToArray());
}
private void ts_Click(object sender, EventArgs e)
{
try
{
ToolStripItem ts = (ToolStripItem)sender;
int index = Utils.ToInt(ts.Tag);
SetDefaultServer(index);
}
catch
{
}
}
private void lvServers_SelectedIndexChanged(object sender, EventArgs e)
{
RefreshQRCodePanel();
}
private void RefreshQRCodePanel()
{
if (scMain.Panel2Collapsed) return; // saving cpu.
int index = GetConfigIndexFromServerListSelected();
if (index < 0) return;
qrCodeControl.showQRCode(index, config);
}
private void RefreshTaryIcon()
{
notifyMain.Icon = MainFormHandler.Instance.GetNotifyIcon(config, this.Icon);
}
private void DisplayToolStatus()
{
ssMain.SuspendLayout();
toolSslSocksPort.Text =
toolSslHttpPort.Text =
toolSslPacPort.Text = "OFF";
toolSslSocksPort.Text = $"{Global.Loopback}:{config.inbound[0].localPort}";
if (config.listenerType != (int)ListenerType.noHttpProxy)
{
toolSslHttpPort.Text = $"{Global.Loopback}:{Global.httpPort}";
if (config.listenerType == ListenerType.GlobalPac ||
config.listenerType == ListenerType.PacOpenAndClear ||
config.listenerType == ListenerType.PacOpenOnly)
{
if (PACServerHandle.IsRunning)
{
toolSslPacPort.Text = $"{HttpProxyHandle.GetPacUrl()}";
}
else
{
toolSslPacPort.Text = UIRes.I18N("StartPacFailed");
}
}
}
string routingStatus = "";
switch (config.routingMode)
{
case 0:
routingStatus = UIRes.I18N("RoutingModeGlobal");
break;
case 1:
routingStatus = UIRes.I18N("RoutingModeBypassLAN");
break;
case 2:
routingStatus = UIRes.I18N("RoutingModeBypassCN");
break;
case 3:
routingStatus = UIRes.I18N("RoutingModeBypassLANCN");
break;
}
toolSslRouting.Text = routingStatus;
ssMain.ResumeLayout();
RefreshTaryIcon();
}
private void ssMain_ItemClicked(object sender, ToolStripItemClickedEventArgs e)
{
if (!Utils.IsNullOrEmpty(e.ClickedItem.Text))
{
Utils.SetClipboardData(e.ClickedItem.Text);
}
}
#endregion
public static Task autoLatencyRefreshTask;
private void autoLatencyRefresh()
{
if (config.listenerType != ListenerType.noHttpProxy)
{
if (autoLatencyRefreshTask == null || autoLatencyRefreshTask.IsCompleted)
{
autoLatencyRefreshTask = Task.Run(async delegate
{
if (!this.IsHandleCreated) return; // the GUI app is exiting.
await Task.Delay(2000);
this.Invoke((MethodInvoker)(delegate
{
toolSslServerLatencyRefresh();
}));
});
}
}
}
#region v2ray 操作
/// <summary>
/// 载入V2ray
/// </summary>
private async void LoadV2ray()
{
this.Invoke((MethodInvoker)(delegate
{
tsbReload.Enabled = false;
if (Global.reloadV2ray)
{
ClearMsg();
}
}));
await v2rayHandler.LoadV2ray(config);
Global.reloadV2ray = false;
ChangePACButtonStatus(config.listenerType);
//ConfigHandler.SaveConfigToFile(ref config, false); // ChangePACButtonStatus does it.
statistics?.SaveToFile();
this.Invoke((MethodInvoker)(delegate
{
tsbReload.Enabled = true;
autoLatencyRefresh();
}));
}
/// <summary>
/// 关闭相关组件
/// </summary>
private void Closes()
{
List<Task> tasks = new List<Task>
{
Task.Run(() => ConfigHandler.SaveConfigToFile(ref config)),
Task.Run(() => HttpProxyHandle.CloseHttpAgent(config)),
Task.Run(() => v2rayHandler.V2rayStop()),
Task.Run(() => PACServerHandle.Stop()),
Task.Run(() =>
{
statistics?.SaveToFile();
statistics?.Close();
})
};
Task.WaitAll(tasks.ToArray());
}
#endregion
#region 功能按钮
private void lvServers_DoubleClick(object sender, EventArgs e)
{
int index = GetConfigIndexFromServerListSelected();
if (index < 0) return;
if (config.vmess[index].configType == (int)EConfigType.Vmess)
{
AddServerForm fm = new AddServerForm
{
EditIndex = index
};
if (fm.ShowDialog() == DialogResult.OK)
{
//刷新
RefreshServers();
LoadV2ray();
}
}
else if (config.vmess[index].configType == (int)EConfigType.Shadowsocks)
{
AddServer3Form fm = new AddServer3Form
{
EditIndex = index
};
if (fm.ShowDialog() == DialogResult.OK)
{
RefreshServers();
LoadV2ray();
}
}
else if (config.vmess[index].configType == (int)EConfigType.Socks)
{
AddServer4Form fm = new AddServer4Form
{
EditIndex = index
};
if (fm.ShowDialog() == DialogResult.OK)
{
RefreshServers();
LoadV2ray();
}
}
else
{
AddServer2Form fm2 = new AddServer2Form
{
EditIndex = index
};
if (fm2.ShowDialog() == DialogResult.OK)
{
//刷新
RefreshServers();
LoadV2ray();
}
}
}
private void lvServers_KeyDown(object sender, KeyEventArgs e)
{
if (e.Control)
{
switch (e.KeyCode)
{
case Keys.A:
menuSelectAll_Click(null, null);
break;
case Keys.C:
menuExport2ShareUrl_Click(null, null);
break;
case Keys.V:
menuAddServers_Click(null, null);
break;
case Keys.P:
menuPingServer_Click(null, null);
break;
case Keys.O:
menuTcpingServer_Click(null, null);
break;
case Keys.R:
menuRealPingServer_Click(null, null);
break;
case Keys.S:
menuScanScreen_Click(null, null);
break;
case Keys.T:
menuSpeedServer_Click(null, null);
break;
}
}
else
{
switch (e.KeyCode)
{
case Keys.Enter:
menuSetDefaultServer_Click(null, null);
break;
case Keys.Delete:
menuRemoveServer_Click(null, null);
break;
case Keys.T:
menuMoveTop_Click(null, null);
break;
case Keys.B:
menuMoveBottom_Click(null, null);
break;
case Keys.U:
menuMoveUp_Click(null, null);
break;
case Keys.D:
menuMoveDown_Click(null, null);
break;
}
}
}
private void menuAddVmessServer_Click(object sender, EventArgs e)
{
AddServerForm fm = new AddServerForm
{
EditIndex = -1
};
if (fm.ShowDialog() == DialogResult.OK)
{
//刷新
RefreshServers();
LoadV2ray();
}
}
private void menuRemoveServer_Click(object sender, EventArgs e)
{
int index = GetConfigIndexFromServerListSelected();
if (index < 0) return;
if (UI.ShowYesNo(UIRes.I18N("RemoveServer")) == DialogResult.No)
{
return;
}
for (int k = lvSelecteds.Count - 1; k >= 0; k--)
{
ConfigHandler.RemoveServer(ref config, lvSelecteds[k]);
}
//刷新
RefreshServers();
LoadV2ray();
}
private void menuRemoveDuplicateServer_Click(object sender, EventArgs e)
{
Utils.DedupServerList(config.vmess, out List<VmessItem> servers, config.keepOlderDedupl);
int oldCount = config.vmess.Count;
int newCount = servers.Count;
if (servers != null)
{
config.vmess = servers;
}
//刷新
RefreshServers();
LoadV2ray();
UI.Show(string.Format(UIRes.I18N("RemoveDuplicateServerResult"), oldCount, newCount));
}
private void menuCopyServer_Click(object sender, EventArgs e)
{
int index = GetConfigIndexFromServerListSelected();
if (index < 0) return;
if (ConfigHandler.CopyServer(ref config, index) == 0)
{
//刷新
RefreshServers();
}
}
private void menuSetDefaultServer_Click(object sender, EventArgs e)
{
int index = GetConfigIndexFromServerListSelected();
if (index < 0) return;
SetDefaultServer(index);
}
private void menuPingServer_Click(object sender, EventArgs e)
{
Speedtest("ping");
}
private void menuTcpingServer_Click(object sender, EventArgs e)
{
Speedtest("tcping");
}
private void menuRealPingServer_Click(object sender, EventArgs e)
{
//if (!config.sysAgentEnabled)
//{
// UI.Show(UIRes.I18N("NeedHttpGlobalProxy"));
// return;
//}
//UI.Show(UIRes.I18N("SpeedServerTips"));
Speedtest("realping");
}
private void menuSpeedServer_Click(object sender, EventArgs e)
{
//if (!config.sysAgentEnabled)
//{
// UI.Show(UIRes.I18N("NeedHttpGlobalProxy"));
// return;
//}
//UI.Show(UIRes.I18N("SpeedServerTips"));
Speedtest("speedtest");
}
private void Speedtest(string actionType)
{
if (GetConfigIndexFromServerListSelected() < 0) return;
ClearTestResult();
SpeedtestHandler statistics = new SpeedtestHandler(ref config, ref v2rayHandler, lvSelecteds, actionType, UpdateSpeedtestHandler);
}
private async void menuTestMe_Click(object sender, EventArgs e)
{
string result = await httpProxyTest() + "ms";
AppendText(false, string.Format(UIRes.I18N("TestMeOutput"), result));
}
private async Task<int> httpProxyTest()
{
SpeedtestHandler statistics = new SpeedtestHandler(ref config, ref v2rayHandler, lvSelecteds, "", UpdateSpeedtestHandler);
return await Task.Run(() => statistics.RunAvailabilityCheck());
}
private void menuExport2ClientConfig_Click(object sender, EventArgs e)
{
int index = GetConfigIndexFromServerListSelected();
MainFormHandler.Instance.Export2ClientConfig(index, config);
}
private void menuExport2ServerConfig_Click(object sender, EventArgs e)
{
int index = GetConfigIndexFromServerListSelected();
MainFormHandler.Instance.Export2ServerConfig(index, config);
}
private void menuExport2ShareUrl_Click(object sender, EventArgs e)
{
GetConfigIndexFromServerListSelected();
StringBuilder sb = new StringBuilder();
foreach (int v in lvSelecteds)
{
string url = ConfigHandler.GetVmessQRCode(config, v);
if (Utils.IsNullOrEmpty(url))
{
continue;
}
sb.Append(url);
sb.AppendLine();
}
if (sb.Length > 0)
{
Utils.SetClipboardData(sb.ToString());
AppendText(false, UIRes.I18N("BatchExportURLSuccessfully"));
//UI.Show(UIRes.I18N("BatchExportURLSuccessfully"));
}
}
private void menuExport2SubContent_Click(object sender, EventArgs e)
{
GetConfigIndexFromServerListSelected();
StringBuilder sb = new StringBuilder();
foreach (int v in lvSelecteds)
{
string url = ConfigHandler.GetVmessQRCode(config, v);
if (Utils.IsNullOrEmpty(url))
{
continue;
}
sb.Append(url);
sb.AppendLine();
}
if (sb.Length > 0)
{
Utils.SetClipboardData(Utils.Base64Encode(sb.ToString()));
UI.Show(UIRes.I18N("BatchExportSubscriptionSuccessfully"));
}
}
private void tsbOptionSetting_Click(object sender, EventArgs e)
{
string tab = "";
if (sender == toolSslRouting) tab = "tabPreDefinedRules";
OptionSettingForm fm = new OptionSettingForm(tab);
if (fm.ShowDialog() == DialogResult.OK)
{
//刷新
RefreshServers();
//Application.DoEvents();
Task.Run(() =>
{
LoadV2ray();
HttpProxyHandle.RestartHttpAgent(config, true);
});
}
}
private void tsbReload_Click(object sender, EventArgs e)
{
Global.reloadV2ray = true;
LoadV2ray();
}
private void tsbClose_Click(object sender, EventArgs e)
{
HideForm();
//this.WindowState = FormWindowState.Minimized;
}
/// <summary>
/// 设置活动服务器
/// </summary>
/// <param name="index"></param>
/// <returns></returns>
private int SetDefaultServer(int index)
{
if (index < 0)
{
UI.Show(UIRes.I18N("PleaseSelectServer"));
return -1;
}
if (ConfigHandler.SetDefaultServer(ref config, index) == 0)
{
//刷新
RefreshServers();
LoadV2ray();
toolSslServerLatencySet();
}
return 0;
}
/// <summary>
/// 获取服务器列表选中行的配置项(config)索引(index)
///
/// 返回值对应首个选中项,无选中或出错时返回-1
/// 访问多选请在调用此函数后访问 lvSelecteds
/// </summary>
/// <returns></returns>
private int GetConfigIndexFromServerListSelected()
{
int index = -1;
lvSelecteds.Clear();
try
{
if (lvServers.SelectedIndices.Count <= 0)
{
UI.Show(UIRes.I18N("PleaseSelectServer"));
return index;
}
index = Convert.ToInt32(lvServers.SelectedItems[0].Tag);
foreach (int item in lvServers.SelectedIndices)
{
int i = Convert.ToInt32(lvServers.Items[item].Tag);
lvSelecteds.Add(i);
}
return index;
}
catch
{
return index;
}
}
/// <summary>
/// 获取服务器列表表示指定配置项的所有行的索引
/// 基于列表项的Tag属性
///
/// 出错时返回空。
/// </summary>
/// <returns></returns>
private List<int> GetServerListItemsByConfigIndex(int configIndex)
{
var l = new List<int>();
foreach (ListViewItem item in lvServers.Items)
{
string tagStr = item.Tag?.ToString();
if (int.TryParse(tagStr, out int tagInt)
&& tagInt == configIndex)
{
l.Add(item.Index);
}
}
return l;
}
private void menuAddCustomServer_Click(object sender, EventArgs e)
{
UI.Show(UIRes.I18N("CustomServerTips"));
OpenFileDialog fileDialog = new OpenFileDialog
{
Multiselect = false,
Filter = "Config|*.json|All|*.*"
};
if (fileDialog.ShowDialog() != DialogResult.OK)
{
return;
}
string fileName = fileDialog.FileName;
if (Utils.IsNullOrEmpty(fileName))
{
return;
}
if (ConfigHandler.AddCustomServer(ref config, fileName) == 0)
{
//刷新
RefreshServers();
LoadV2ray();
UI.Show(UIRes.I18N("SuccessfullyImportedCustomServer"));
}
else
{
UI.ShowWarning(UIRes.I18N("FailedImportedCustomServer"));
}
}
private void menuAddShadowsocksServer_Click(object sender, EventArgs e)
{
AddServer3Form fm = new AddServer3Form
{
EditIndex = -1
};
if (fm.ShowDialog() == DialogResult.OK)
{
//刷新
RefreshServers();
LoadV2ray();
}
ShowForm();
}
private void menuAddSocksServer_Click(object sender, EventArgs e)
{
AddServer4Form fm = new AddServer4Form
{
EditIndex = -1
};
if (fm.ShowDialog() == DialogResult.OK)
{
//刷新
RefreshServers();
LoadV2ray();
}
ShowForm();
}
private void menuAddServers_Click(object sender, EventArgs e)
{
string clipboardData = Utils.GetClipboardData();
int result = AddBatchServers(clipboardData);
if (result > 0)
{
UI.Show(string.Format(UIRes.I18N("SuccessfullyImportedServerViaClipboard"), result));
}
}
private void menuScanScreen_Click(object sender, EventArgs e)
{
HideForm();
bgwScan.RunWorkerAsync();
}
private int AddBatchServers(string clipboardData, string subid = "")
{
int counter;
int _Add()
{
return ConfigHandler.AddBatchServers(ref config, clipboardData, subid);
}
counter = _Add();
if (counter < 1)
{
clipboardData = Utils.Base64Decode(clipboardData);
counter = _Add();
}
RefreshServers();
return counter;
}
private void menuUpdateSubscriptions_Click(object sender, EventArgs e)
{
UpdateSubscriptionProcess();
}
#endregion
#region 提示信息
/// <summary>
/// 消息委托
/// </summary>
/// <param name="notify"></param>
/// <param name="msg"></param>
void v2rayHandler_ProcessEvent(bool notify, string msg)
{
AppendText(notify, msg);
}
delegate void AppendTextDelegate(string text);
void AppendText(bool notify, string msg)
{
try
{
AppendText(msg);
if (notify)
{
notifyMsg(msg);
}
}
catch
{
}
}
void AppendText(string text)
{
if (this.txtMsgBox.InvokeRequired)
{
Invoke(new AppendTextDelegate(AppendText), new object[] { text });
}
else
{
//this.txtMsgBox.AppendText(text);
ShowMsg(text);
}
}
/// <summary>
/// 提示信息
/// </summary>
/// <param name="msg"></param>
private void ShowMsg(string msg)
{
if (txtMsgBox.Lines.Length > 999)
{
ClearMsg();
}
this.txtMsgBox.AppendText(msg);
if (!msg.EndsWith(Environment.NewLine))
{
this.txtMsgBox.AppendText(Environment.NewLine);
}
}
/// <summary>
/// 清除信息
/// </summary>
private void ClearMsg()
{
this.txtMsgBox.Clear();
}
/// <summary>
/// 托盘信息
/// </summary>
/// <param name="msg"></param>
private void notifyMsg(string msg)
{
notifyMain.Text = msg;
}
#endregion
#region 托盘事件
private void notifyMain_MouseClick(object sender, MouseEventArgs e)
{
if (e.Button == MouseButtons.Left)
{
ShowForm();
}
}
private void menuExit_Click(object sender, EventArgs e)
{
this.Visible = false;
this.Close();
Application.Exit();
}
private void ShowForm()
{
this.Show();
this.WindowState = FormWindowState.Normal;
this.Activate();
this.ShowInTaskbar = true;
//this.notifyIcon1.Visible = false;
this.txtMsgBox.ScrollToCaret();
if (config.index >= 0 && config.index < lvServers.Items.Count)
{
lvServers.EnsureVisible(config.index); // workaround
}
SetVisibleCore(true);
}
private void HideForm()
{
//this.WindowState = FormWindowState.Minimized;
this.Hide();
//this.notifyMain.Icon = this.Icon;
this.notifyMain.Visible = true;
this.ShowInTaskbar = false;
SetVisibleCore(false);
}
#endregion
#region 后台测速
private void SetTestResult(int configIndex, string txt)
{
var l = GetServerListItemsByConfigIndex(configIndex);
if (l.Count > 0)
{
config.vmess[configIndex].testResult = txt;
l.ForEach((listIndex) => lvServers.Items[listIndex].SubItems["testResult"].Text = txt);
}
}
private void ClearTestResult()
{
foreach (int s in lvSelecteds)
{
SetTestResult(s, "");
}
}
private void UpdateSpeedtestHandler(int index, string msg)
{
lvServers.Invoke((MethodInvoker)delegate
{
SetTestResult(index, msg);
});
}
private void UpdateStatisticsHandler(ulong up, ulong down, List<ServerStatItem> statistics)
{
try
{
up /= (ulong)(config.statisticsFreshRate / 1000f);
down /= (ulong)(config.statisticsFreshRate / 1000f);
this.Invoke((MethodInvoker)(delegate
{
toolSslServerSpeed.Text = string.Format("{0}/s↑ | {1}/s↓", Utils.HumanFy(up), Utils.HumanFy(down));
}));
List<string[]> datas = new List<string[]>();
for (int i = 0; i < config.vmess.Count; i++)
{
int index = statistics.FindIndex(item_ => item_.itemId == config.vmess[i].getItemId());
if (index != -1)
{
if (lvServers == null) return; // The app is exiting.
lvServers.Invoke((MethodInvoker)delegate
{
lvServers.BeginUpdate();
lvServers.Items[i].SubItems["todayDown"].Text = Utils.HumanFy(statistics[index].todayDown);
lvServers.Items[i].SubItems["todayUp"].Text = Utils.HumanFy(statistics[index].todayUp);
lvServers.Items[i].SubItems["totalDown"].Text = Utils.HumanFy(statistics[index].totalDown);
lvServers.Items[i].SubItems["totalUp"].Text = Utils.HumanFy(statistics[index].totalUp);
lvServers.EndUpdate();
});
}
}
}
catch (Exception ex)
{
Utils.SaveLog(ex.Message, ex);
}
}
#endregion
#region 移动服务器
private void menuMoveTop_Click(object sender, EventArgs e)
{
MoveServer(EMove.Top);
}
private void menuMoveUp_Click(object sender, EventArgs e)
{
MoveServer(EMove.Up);
}
private void menuMoveDown_Click(object sender, EventArgs e)
{
MoveServer(EMove.Down);
}
private void menuMoveBottom_Click(object sender, EventArgs e)
{
MoveServer(EMove.Bottom);
}
private void MoveServer(EMove eMove)
{
int index = GetConfigIndexFromServerListSelected();
if (index < 0)
{
UI.Show(UIRes.I18N("PleaseSelectServer"));
return;
}
if (ConfigHandler.MoveServer(ref config, index, eMove) == 0)
{
//TODO: reload is not good.
RefreshServers();
LoadV2ray();
}
}
private void menuSelectAll_Click(object sender, EventArgs e)
{
foreach (ListViewItem item in lvServers.Items)
{
item.Selected = true;
}
}
#endregion
#region 系统代理相关
private void menuCopyPACUrl_Click(object sender, EventArgs e)
{
Utils.SetClipboardData(HttpProxyHandle.GetPacUrl());
}
private void menuNotEnabledHttp_Click(object sender, EventArgs e)
{
SetListenerType(ListenerType.noHttpProxy);
}
private void menuGlobal_Click(object sender, EventArgs e)
{
SetListenerType(ListenerType.GlobalHttp);
}
private void menuGlobalPAC_Click(object sender, EventArgs e)
{
SetListenerType(ListenerType.GlobalPac);
}
private void menuKeep_Click(object sender, EventArgs e)
{
SetListenerType(ListenerType.HttpOpenAndClear);
}
private void menuKeepPAC_Click(object sender, EventArgs e)
{
SetListenerType(ListenerType.PacOpenAndClear);
}
private void menuKeepNothing_Click(object sender, EventArgs e)
{
SetListenerType(ListenerType.HttpOpenOnly);
}
private void menuKeepPACNothing_Click(object sender, EventArgs e)
{
SetListenerType(ListenerType.PacOpenOnly);
}
private void SetListenerType(ListenerType type)
{
config.listenerType = type;
ChangePACButtonStatus(type);
}
private void ChangePACButtonStatus(ListenerType type)
{
if (type != ListenerType.noHttpProxy)
{
HttpProxyHandle.RestartHttpAgent(config, false);
}
else
{
HttpProxyHandle.CloseHttpAgent(config);
}
for (int k = 0; k < menuSysAgentMode.DropDownItems.Count; k++)
{
ToolStripMenuItem item = ((ToolStripMenuItem)menuSysAgentMode.DropDownItems[k]);
item.Checked = ((int)type == k);
}
Global.reloadV2ray = false;
ConfigHandler.SaveConfigToFile(ref config);
this.Invoke((MethodInvoker)(delegate
{
DisplayToolStatus();
}));
}
#endregion
#region CheckUpdate
private async void askToDownload(DownloadHandle downloadHandle, string url)
{
if (UI.ShowYesNo(string.Format(UIRes.I18N("DownloadYesNo"), url)) == DialogResult.Yes)
{
if (await httpProxyTest() > 0)
{
int httpPort = config.GetLocalPort(Global.InboundHttp);
WebProxy webProxy = new WebProxy(Global.Loopback, httpPort);
downloadHandle.DownloadFileAsync(url, webProxy, 60);
}
else
{
downloadHandle.DownloadFileAsync(url, null, 60);
}
}
}
private void tsbCheckUpdateN_Click(object sender, EventArgs e)
{
//System.Diagnostics.Process.Start(Global.UpdateUrl);
DownloadHandle downloadHandle = null;
if (downloadHandle == null)
{
downloadHandle = new DownloadHandle();
downloadHandle.AbsoluteCompleted += (sender2, args) =>
{
if (args.Success)
{
AppendText(false, string.Format(UIRes.I18N("MsgParsingSuccessfully"), "v2rayN"));
string url = args.Msg;
this.Invoke((MethodInvoker)(delegate
{
askToDownload(downloadHandle, url);
}));
}
else
{
AppendText(false, args.Msg);
}
};
downloadHandle.UpdateCompleted += (sender2, args) =>
{
if (args.Success)
{
AppendText(false, UIRes.I18N("MsgDownloadV2rayCoreSuccessfully"));
try
{
string fileName = Utils.GetPath(downloadHandle.DownloadFileName);
Process process = Process.Start("v2rayUpgrade.exe", "\"" + fileName + "\"");
if (process.Id > 0)
{
menuExit_Click(null, null);
}
}
catch (Exception ex)
{
AppendText(false, ex.Message);
}
}
else
{
AppendText(false, args.Msg);
}
};
downloadHandle.Error += (sender2, args) =>
{
AppendText(true, args.GetException().Message);
};
}
AppendText(false, string.Format(UIRes.I18N("MsgStartUpdating"), "v2rayN"));
downloadHandle.CheckUpdateAsync("v2rayN");
}
private void tsbCheckUpdateCore_Click(object sender, EventArgs e)
{
DownloadHandle downloadHandle = null;
if (downloadHandle == null)
{
downloadHandle = new DownloadHandle();
downloadHandle.AbsoluteCompleted += (sender2, args) =>
{
if (args.Success)
{
AppendText(false, string.Format(UIRes.I18N("MsgParsingSuccessfully"), "v2rayCore"));
string url = args.Msg;
this.Invoke((MethodInvoker)(delegate
{
askToDownload(downloadHandle, url);
}));
}
else
{
AppendText(false, args.Msg);
}
};
downloadHandle.UpdateCompleted += (sender2, args) =>
{
if (args.Success)
{
AppendText(false, UIRes.I18N("MsgDownloadV2rayCoreSuccessfully"));
AppendText(false, UIRes.I18N("MsgUnpacking"));
try
{
Closes();
string fileName = downloadHandle.DownloadFileName;
fileName = Utils.GetPath(fileName);
FileManager.ZipExtractToFile(fileName);
AppendText(false, UIRes.I18N("MsgUpdateV2rayCoreSuccessfullyMore"));
Global.reloadV2ray = true;
LoadV2ray();
AppendText(false, UIRes.I18N("MsgUpdateV2rayCoreSuccessfully"));
}
catch (Exception ex)
{
AppendText(false, ex.Message);
}
}
else
{
AppendText(false, args.Msg);
}
};
downloadHandle.Error += (sender2, args) =>
{
AppendText(true, args.GetException().Message);
};
}
AppendText(false, string.Format(UIRes.I18N("MsgStartUpdating"), "v2rayCore"));
downloadHandle.CheckUpdateAsync("Core");
}
private void tsbCheckUpdatePACList_Click(object sender, EventArgs e)
{
DownloadHandle pacListHandle = null;
if (pacListHandle == null)
{
pacListHandle = new DownloadHandle();
pacListHandle.UpdateCompleted += (sender2, args) =>
{
if (args.Success)
{
string result = args.Msg;
if (Utils.IsNullOrEmpty(result))
{
return;
}
pacListHandle.GenPacFile(result);
AppendText(false, UIRes.I18N("MsgPACUpdateSuccessfully"));
}
else
{
AppendText(false, UIRes.I18N("MsgPACUpdateFailed"));
}
};
pacListHandle.Error += (sender2, args) =>
{
AppendText(true, args.GetException().Message);
};
}
AppendText(false, UIRes.I18N("MsgStartUpdatingPAC"));
string url = Utils.IsNullOrEmpty(config.urlGFWList) ? Global.GFWLIST_URL : config.urlGFWList;
pacListHandle.WebDownloadString(url);
}
private void tsbCheckClearPACList_Click(object sender, EventArgs e)
{
try
{
File.WriteAllText(Utils.GetPath(Global.pacFILE), Utils.GetEmbedText(Global.BlankPacFileName), Encoding.UTF8);
AppendText(false, UIRes.I18N("MsgSimplifyPAC"));
}
catch (Exception ex)
{
Utils.SaveLog(ex.Message, ex);
}
}
#endregion
#region Help
private void tsbAbout_Click(object sender, EventArgs e)
{
Process.Start(Global.AboutUrl);
}
private void tsbV2rayWebsite_Click(object sender, EventArgs e)
{
Process.Start(Global.v2rayWebsiteUrl);
}
private void tsbPromotion_Click(object sender, EventArgs e)
{
Process.Start($"{Utils.Base64Decode(Global.PromotionUrl)}?t={DateTime.Now.Ticks}");
}
#endregion
#region ScanScreen
private void bgwScan_DoWork(object sender, System.ComponentModel.DoWorkEventArgs e)
{
string ret = Utils.ScanScreen();
bgwScan.ReportProgress(0, ret);
}
private void bgwScan_ProgressChanged(object sender, System.ComponentModel.ProgressChangedEventArgs e)
{
ShowForm();
string result = Convert.ToString(e.UserState);
if (Utils.IsNullOrEmpty(result))
{
UI.ShowWarning(UIRes.I18N("NoValidQRcodeFound"));
}
else
{
if (AddBatchServers(result) > 0)
{
UI.Show(UIRes.I18N("SuccessfullyImportedServerViaScan"));
}
}
}
#endregion
#region 订阅
private void tsbSubSetting_Click(object sender, EventArgs e)
{
SubSettingForm fm = new SubSettingForm();
if (fm.ShowDialog() == DialogResult.OK)
{
RefreshServers();
}
}
private void tsbSubUpdate_Click(object sender, EventArgs e)
{
UpdateSubscriptionProcess();
}
/// <summary>
/// the subscription update process
/// </summary>
private void UpdateSubscriptionProcess()
{
AppendText(false, UIRes.I18N("MsgUpdateSubscriptionStart"));
if (config.subItem == null || config.subItem.Count <= 0)
{
AppendText(false, UIRes.I18N("MsgNoValidSubscription"));
return;
}
for (int k = 1; k <= config.subItem.Count; k++)
{
string id = config.subItem[k - 1].id.TrimEx();
string url = config.subItem[k - 1].url.TrimEx();
string hashCode = $"{k}->";
if (config.subItem[k - 1].enabled == false)
{
continue;
}
if (Utils.IsNullOrEmpty(id) || Utils.IsNullOrEmpty(url))
{
AppendText(false, $"{hashCode}{UIRes.I18N("MsgNoValidSubscription")}");
continue;
}
DownloadHandle downloadHandle3 = new DownloadHandle();
downloadHandle3.UpdateCompleted += (sender2, args) =>
{
if (args.Success)
{
AppendText(false, $"{hashCode}{UIRes.I18N("MsgGetSubscriptionSuccessfully")}");
string result = Utils.Base64Decode(args.Msg);
if (Utils.IsNullOrEmpty(result))
{
AppendText(false, $"{hashCode}{UIRes.I18N("MsgSubscriptionDecodingFailed")}");
return;
}
ConfigHandler.RemoveServerViaSubid(ref config, id);
AppendText(false, $"{hashCode}{UIRes.I18N("MsgClearSubscription")}");
RefreshServers();
if (AddBatchServers(result, id) > 0)
{
}
else
{
AppendText(false, $"{hashCode}{UIRes.I18N("MsgFailedImportSubscription")}");
}
AppendText(false, $"{hashCode}{UIRes.I18N("MsgUpdateSubscriptionEnd")}");
}
else
{
AppendText(false, args.Msg);
}
};
downloadHandle3.Error += (sender2, args) =>
{
AppendText(true, args.GetException().Message);
};
downloadHandle3.WebDownloadString(url);
AppendText(false, $"{hashCode}{UIRes.I18N("MsgStartGettingSubscriptions")}");
}
}
private void tsbQRCodeSwitch_CheckedChanged(object sender, EventArgs e)
{
bool bShow = tsbQRCodeSwitch.Checked;
scMain.Panel2Collapsed = !bShow;
RefreshQRCodePanel();
}
#endregion
#region Language
private void tsbLanguageDef_Click(object sender, EventArgs e)
{
SetCurrentLanguage("en");
}
private void tsbLanguageZhHans_Click(object sender, EventArgs e)
{
SetCurrentLanguage("zh-Hans");
}
private void SetCurrentLanguage(string value)
{
Utils.RegWriteValue(Global.MyRegPath, Global.MyRegKeyLanguage, value);
//Application.Restart();
}
#endregion
private void SystemEvents_DisplaySettingsChanged(object sender, EventArgs e)
{
RefreshTaryIcon();
}
private async void toolSslServerLatencyRefresh()
{
toolSslServerLatencySet(UIRes.I18N("ServerLatencyChecking"));
string result = await httpProxyTest() + "ms";
toolSslServerLatencySet(result);
}
private void toolSslServerLatencySet(string text = "")
{
toolSslServerLatency.Text = string.Format(UIRes.I18N("toolSslServerLatency"), text);
}
private void toolSslServerLatency_Click(object sender, EventArgs e)
{
toolSslServerLatencyRefresh();
}
private void toolSslServerSpeed_Click(object sender, EventArgs e)
{
//toolSslServerLatencyRefresh();
}
private void toolSslRouting_Click(object sender, EventArgs e)
{
tsbOptionSetting_Click(toolSslRouting, null);
}
private int lastSortedColIndex = -1;
private bool lastSortedColDirection = false;
private void lvServers_ColumnClick(object sender, ColumnClickEventArgs e)
{
Sorter s = (Sorter)lvServers.ListViewItemSorter;
s.Column = e.Column;
int doIntSort;
bool isNum = lvServers.Columns[e.Column].Tag?.ToString() == Global.sortMode.Numeric.ToString();
if (lastSortedColIndex < 0 // 首次
|| (lastSortedColIndex >= 0 && lastSortedColIndex != e.Column) // 排序了其他列
|| (lastSortedColIndex == e.Column && !lastSortedColDirection) // 已排序
)
{
lastSortedColDirection = true;
doIntSort = isNum ? 1 : -1; // 正序
}
else
{
lastSortedColDirection = false;
doIntSort = isNum ? 2 : -2; // 倒序
}
lastSortedColIndex = e.Column;
s.Sorting = doIntSort;
lvServers.Sort();
reInterlaceColoring();
}
private void reInterlaceColoring()
{
if (!config.interlaceColoring) return;
for (int k = 0; k < lvServers.Items.Count; k++)
{
if (config.interlaceColoring && k % 2 == 1)
lvServers.Items[k].BackColor = SystemColors.Control;
else
lvServers.Items[k].BackColor = lvServers.BackColor;
}
}
private void lvServers_ColumnWidthChanged(object sender, ColumnWidthChangedEventArgs e)
{
ColumnHeader c = lvServers.Columns[e.ColumnIndex];
ConfigHandler.AddformMainLvColWidth(ref config, c.Name, c.Width);
Task.Run(() => ConfigHandler.SaveConfigToFile(ref config));
}
private void MainForm_ResizeEnd(object sender, EventArgs e)
{
config.uiItem.mainSize = new Size(this.Width, this.Height);
Task.Run(() => ConfigHandler.SaveConfigToFile(ref config));
}
private async void lvServers_ColumnReordered(object sender, ColumnReorderedEventArgs e)
{
await Task.Delay(500);
var names = (from col in lvServers.Columns.Cast<ColumnHeader>()
orderby col.DisplayIndex
select col.Name).ToList();
config.uiItem.mainLvColLayout = names;
_ = Task.Run(() => ConfigHandler.SaveConfigToFile(ref config));
}
}
}