Skip to content

Commit c3da2eb

Browse files
committed
Fixed Stopped bug in windows 24H2
There was a bug where when stopping the proxy it will be stuck forever and we have to end it through task manager. Well its fixed
1 parent 9d99eb1 commit c3da2eb

File tree

8 files changed

+388
-148
lines changed

8 files changed

+388
-148
lines changed

Netch/Forms/MainForm.cs

Lines changed: 108 additions & 65 deletions
Original file line numberDiff line numberDiff line change
@@ -24,6 +24,7 @@ public partial class MainForm : Form
2424
#region Start
2525

2626
private readonly Dictionary<string, object> _mainFormText = new();
27+
private CancellationTokenSource? _mainCancellationTokenSource;
2728

2829
private bool _textRecorded;
2930

@@ -517,73 +518,84 @@ private void fAQToolStripMenuItem_Click(object sender, EventArgs e)
517518
#region ControlButton
518519

519520
private async void ControlButton_Click(object? sender, EventArgs? e)
521+
{
522+
if (!IsWaiting())
520523
{
521-
if (!IsWaiting())
522-
{
523-
await StopCoreAsync();
524-
return;
525-
}
524+
await StopCoreAsync();
525+
return;
526+
}
526527

527-
Configuration.SaveAsync().Forget();
528+
Configuration.SaveAsync().Forget();
528529

529-
// 服务器、模式 需选择
530-
if (ServerComboBox.SelectedItem is not Server server)
531-
{
532-
MessageBoxX.Show(i18N.Translate("Please select a server first"));
533-
return;
534-
}
530+
// 服务器、模式 需选择
531+
if (ServerComboBox.SelectedItem is not Server server)
532+
{
533+
MessageBoxX.Show(i18N.Translate("Please select a server first"));
534+
return;
535+
}
535536

536-
if (ModeComboBox.SelectedItem is not Mode mode)
537-
{
538-
MessageBoxX.Show(i18N.Translate("Please select a mode first"));
539-
return;
540-
}
537+
if (ModeComboBox.SelectedItem is not Mode mode)
538+
{
539+
MessageBoxX.Show(i18N.Translate("Please select a mode first"));
540+
return;
541+
}
541542

542-
State = State.Starting;
543+
State = State.Starting;
544+
545+
// Create new cancellation token for this session
546+
_mainCancellationTokenSource = new CancellationTokenSource();
547+
var cancellationToken = _mainCancellationTokenSource.Token;
543548

544-
try
545-
{
546-
await MainController.StartAsync(server, mode);
547-
}
548-
catch (Exception exception)
549-
{
550-
State = State.Stopped;
551-
StatusText(i18N.Translate("Start failed"));
552-
MessageBoxX.Show(exception.Message, LogLevel.ERROR);
553-
return;
554-
}
549+
try
550+
{
551+
await MainController.StartAsync(server, mode);
552+
}
553+
catch (Exception exception)
554+
{
555+
State = State.Stopped;
556+
StatusText(i18N.Translate("Start failed"));
557+
MessageBoxX.Show(exception.Message, LogLevel.ERROR);
558+
return;
559+
}
555560

556-
State = State.Started;
561+
State = State.Started;
557562

558-
Task.Run(Bandwidth.NetTraffic).Forget();
559-
DiscoveryNatTypeAsync().Forget();
560-
HttpConnectAsync().Forget();
563+
Task.Run(() => Bandwidth.NetTraffic(cancellationToken), cancellationToken).Forget();
564+
DiscoveryNatTypeAsync().Forget();
565+
HttpConnectAsync().Forget();
561566

562-
if (Global.Settings.MinimizeWhenStarted)
563-
Minimize();
567+
if (Global.Settings.MinimizeWhenStarted)
568+
Minimize();
564569

565-
// 自动检测延迟
566-
async Task StartedPingAsync()
570+
// 自动检测延迟 - NOW WITH PROPER CANCELLATION
571+
async Task StartedPingAsync()
572+
{
573+
try
567574
{
568-
while (State == State.Started)
575+
while (State == State.Started && !cancellationToken.IsCancellationRequested)
569576
{
570577
if (Global.Settings.StartedPingInterval >= 0)
571578
{
572579
await server.PingAsync();
573580
ServerComboBox.Refresh();
574581

575-
await Task.Delay(Global.Settings.StartedPingInterval * 1000);
582+
await Task.Delay(Global.Settings.StartedPingInterval * 1000, cancellationToken);
576583
}
577584
else
578585
{
579-
await Task.Delay(5000);
586+
await Task.Delay(5000, cancellationToken);
580587
}
581588
}
582589
}
583-
584-
StartedPingAsync().Forget();
590+
catch (OperationCanceledException)
591+
{
592+
// Expected when shutting down
593+
}
585594
}
586595

596+
StartedPingAsync().Forget();
597+
}
598+
587599
#endregion
588600

589601
#region SettingsButton
@@ -1042,13 +1054,36 @@ public async Task StopAsync()
10421054
}
10431055

10441056
private async Task StopCoreAsync()
1057+
{
1058+
State = State.Stopping;
1059+
1060+
// Cancel all background operations
1061+
_mainCancellationTokenSource?.Cancel();
1062+
_discoveryNatCts?.Cancel();
1063+
_httpConnectCts?.Cancel();
1064+
1065+
try
10451066
{
1046-
State = State.Stopping;
1047-
_discoveryNatCts?.Cancel();
1048-
_httpConnectCts?.Cancel();
1049-
await MainController.StopAsync();
1067+
// Add timeout to prevent infinite hang
1068+
using var timeoutCts = new CancellationTokenSource(TimeSpan.FromSeconds(10));
1069+
await MainController.StopAsync().WaitAsync(timeoutCts.Token);
1070+
}
1071+
catch (OperationCanceledException)
1072+
{
1073+
// Force stop if timeout reached
1074+
Log.Warning("MainController.StopAsync() timed out, forcing termination");
1075+
}
1076+
catch (Exception ex)
1077+
{
1078+
Log.Error(ex, "Error during MainController.StopAsync()");
1079+
}
1080+
finally
1081+
{
1082+
_mainCancellationTokenSource?.Dispose();
1083+
_mainCancellationTokenSource = null;
10501084
State = State.Stopped;
10511085
}
1086+
}
10521087

10531088
private bool IsWaiting() => IsWaiting(_state);
10541089

@@ -1274,31 +1309,39 @@ private void Minimize()
12741309
}
12751310

12761311
public async void Exit(bool forceExit = false, bool saveConfiguration = true)
1312+
{
1313+
if (!IsWaiting() && !Global.Settings.StopWhenExited && !forceExit)
12771314
{
1278-
if (!IsWaiting() && !Global.Settings.StopWhenExited && !forceExit)
1279-
{
1280-
MessageBoxX.Show(i18N.Translate("Please press Stop button first"));
1281-
1282-
ShowMainFormToolStripButton.PerformClick();
1283-
return;
1284-
}
1315+
MessageBoxX.Show(i18N.Translate("Please press Stop button first"));
1316+
ShowMainFormToolStripButton.PerformClick();
1317+
return;
1318+
}
12851319

1286-
// State = State.Terminating;
1287-
NotifyIcon.Visible = false;
1288-
Hide();
1320+
// State = State.Terminating;
1321+
NotifyIcon.Visible = false;
1322+
Hide();
12891323

1290-
if (saveConfiguration)
1291-
await Configuration.SaveAsync();
1292-
1293-
foreach (var file in new[] { Constants.TempConfig, Constants.TempRouteFile })
1294-
if (File.Exists(file))
1295-
File.Delete(file);
1324+
if (saveConfiguration)
1325+
await Configuration.SaveAsync();
12961326

1297-
await StopAsync();
1327+
foreach (var file in new[] { Constants.TempConfig, Constants.TempRouteFile })
1328+
if (File.Exists(file))
1329+
File.Delete(file);
12981330

1299-
Dispose();
1300-
Environment.Exit(Environment.ExitCode);
1331+
// ADD TIMEOUT HERE TOO
1332+
try
1333+
{
1334+
using var exitTimeoutCts = new CancellationTokenSource(TimeSpan.FromSeconds(5));
1335+
await StopAsync().WaitAsync(exitTimeoutCts.Token);
13011336
}
1337+
catch (OperationCanceledException)
1338+
{
1339+
Log.Warning("StopAsync() timed out during exit, forcing termination");
1340+
}
1341+
1342+
Dispose();
1343+
Environment.Exit(Environment.ExitCode);
1344+
}
13021345

13031346
#region FormClosingButton
13041347

0 commit comments

Comments
 (0)