This commit is contained in:
2025-11-23 15:11:21 +03:00
parent d23a5c74ce
commit 3fc0bc4ec2
7 changed files with 188 additions and 139 deletions

View File

@ -1,5 +1,6 @@
<Application xmlns="https://github.com/avaloniaui"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:dialogHostAvalonia="clr-namespace:DialogHostAvalonia;assembly=DialogHost.Avalonia"
x:Class="MicrosocksGUI.App"
xmlns:local="using:MicrosocksGUI"
RequestedThemeVariant="Default">
@ -11,5 +12,6 @@
<Application.Styles>
<FluentTheme />
<dialogHostAvalonia:DialogHostStyles />
</Application.Styles>
</Application>

View File

@ -22,6 +22,7 @@
<IncludeAssets Condition="'$(Configuration)' != 'Debug'">None</IncludeAssets>
<PrivateAssets Condition="'$(Configuration)' != 'Debug'">All</PrivateAssets>
</PackageReference>
<PackageReference Include="DialogHost.Avalonia" Version="0.10.0" />
<PackageReference Include="ReactiveUI.Avalonia" Version="11.3.8"/>
<PackageReference Include="ReactiveUI.SourceGenerators" Version="2.5.1">
<PrivateAssets>all</PrivateAssets>

View File

@ -0,0 +1,24 @@
namespace MicrosocksGUI.Services;
public interface IServiceManager
{
public record Arguments(string Ip, string Port);
public void CreateIfNotExists();
public void Start();
public void Stop();
public void Restart();
public bool IsRunning();
public bool GetAutostart();
public void SetAutostart(bool autostart);
public Arguments GetArguments();
public void SetArguments(Arguments arguments);
}

View File

@ -0,0 +1,130 @@
using System;
using System.Diagnostics;
using System.IO;
using System.Runtime.InteropServices;
using System.ServiceProcess;
namespace MicrosocksGUI.Services;
public class WindowsServiceManager : IServiceManager
{
private record NssmResult(string Output, string Errors);
private readonly ServiceController _serviceController = new("MicroSocks");
public void CreateIfNotExists()
{
if (!RunNssmProcess("status MicroSocks").Errors.Contains("Can't open service!"))
return;
var result = RunNssmProcess("install MicroSocks microsocks.exe -i 127.0.0.1 -p 1080");
if (!string.IsNullOrEmpty(result.Errors))
{
throw new ExternalException(result.Errors);
}
}
public void Start()
{
_serviceController.Refresh();
if (_serviceController.Status != ServiceControllerStatus.Stopped)
return;
_serviceController.Start();
_serviceController.WaitForStatus(ServiceControllerStatus.Running);
}
public void Stop()
{
_serviceController.Refresh();
if (_serviceController.Status != ServiceControllerStatus.Running)
return;
_serviceController.Stop();
_serviceController.WaitForStatus(ServiceControllerStatus.Stopped);
}
public void Restart()
{
Stop();
Start();
}
public bool IsRunning()
{
_serviceController.Refresh();
return _serviceController.Status == ServiceControllerStatus.Running;
}
public bool GetAutostart()
{
_serviceController.Refresh();
return _serviceController.StartType == ServiceStartMode.Automatic;
}
public void SetAutostart(bool autostart)
{
var param = autostart ? "SERVICE_AUTO_START" : "SERVICE_DEMAND_START";
var result = RunNssmProcess($"set MicroSocks Start {param}");
if (!string.IsNullOrEmpty(result.Errors))
{
throw new ExternalException(result.Errors);
}
}
public IServiceManager.Arguments GetArguments()
{
var result = RunNssmProcess("get MicroSocks AppParameters");
if (!string.IsNullOrEmpty(result.Errors))
{
throw new ExternalException(result.Errors);
}
return ParseArguments(result.Output);
}
public void SetArguments(IServiceManager.Arguments arguments)
{
var result = RunNssmProcess($"set MicroSocks AppParameters -i {arguments.Ip} -p {arguments.Port}");
if (!string.IsNullOrEmpty(result.Errors))
{
throw new ExternalException(result.Errors);
}
}
private IServiceManager.Arguments ParseArguments(string arguments)
{
var argumentsList = arguments.Split();
return new IServiceManager.Arguments(argumentsList[1], argumentsList[3]);
}
private NssmResult RunNssmProcess(string arguments)
{
using var process = new Process();
process.StartInfo = new ProcessStartInfo
{
FileName = ".\\nssm.exe",
Arguments = arguments,
UseShellExecute = false,
RedirectStandardOutput = true,
RedirectStandardError = true,
CreateNoWindow = true,
WorkingDirectory = Path.GetDirectoryName(".")
};
process.Start();
var output = process.StandardOutput.ReadToEnd().Replace("\0", "").Trim();
var errors = process.StandardError.ReadToEnd().Replace("\0", "").Trim();
process.WaitForExit();
return new NssmResult(output, errors);
}
}

View File

@ -1,155 +1,60 @@
using System;
using System.Diagnostics;
using System.IO;
using System.Management;
using System.Runtime.Versioning;
using System.Threading;
using System.Threading.Tasks;
using ReactiveUI;
using ReactiveUI.SourceGenerators;
using System.ServiceProcess;
using MicrosocksGUI.Services;
namespace MicrosocksGUI.ViewModels;
public partial class MainWindowViewModel : ViewModelBase
{
private record Arguments(string Ip, string Port);
[Reactive] private bool _isWorking;
[Reactive] private bool _isAutoloading;
[Reactive] private string? _ip;
[Reactive] private string? _port;
[Reactive] private bool _isRunning;
[Reactive] private bool _isAutostart;
[Reactive] private string _ip = string.Empty;
[Reactive] private string _port = string.Empty;
private bool _isUpdateLocked;
private Timer _timer;
//private readonly ManagementObject _serviceManager = new("Win32_Service.Name='Microsocks'");
private readonly ServiceController _sc = new("MicroSocks");
private readonly IServiceManager _serviceManager = new WindowsServiceManager();
public MainWindowViewModel()
{
// TODO Костыль, таймер запускается через секунду, а должен после инициализации ReactiveUI
_serviceManager.CreateIfNotExists();
_timer = new Timer(GetServiceStatus, null, TimeSpan.FromSeconds(1), TimeSpan.FromMilliseconds(100));
GetMicroSocksArguments();
}
private void GetServiceStatus(object? state)
{
if (_isUpdateLocked) return;
IsRunning = _serviceManager.IsRunning();
IsAutostart = _serviceManager.GetAutostart();
}
[ReactiveCommand]
private void SetServiceStatus()
{
/*var serviceProps = _serviceManager.GetMethodParameters("Change");
serviceProps["StartMode"] = IsAutoloading ? "Automatic" : "Manual";
_serviceManager.InvokeMethod("Change", serviceProps, null);*/
SetServiceAutoloading(IsAutoloading);
_isUpdateLocked = true;
if (IsWorking)
_serviceManager.SetAutostart(IsAutostart);
if (IsRunning)
{
StartService();
_serviceManager.Start();
}
else
{
StopService();
_serviceManager.Stop();
}
//_serviceManager.InvokeMethod(IsWorking ? "StartService" : "StopService", null, null);
}
private void StartService()
{
_sc.Refresh();
if (_sc.Status != ServiceControllerStatus.Stopped) return;
_sc.Start();
_sc.WaitForStatus(ServiceControllerStatus.Running);
//_serviceManager.InvokeMethod("StartService", null, null);
}
private void StopService()
{
_sc.Refresh();
if (_sc.Status != ServiceControllerStatus.Running) return;
_sc.Stop();
_sc.WaitForStatus(ServiceControllerStatus.Stopped);
//_serviceManager.InvokeMethod("StopService", null, null);
_isUpdateLocked = false;
}
private void RestartService()
{
StopService();
StartService();
}
private bool ServiceIsRunning()
{
_sc.Refresh();
return _sc.Status == ServiceControllerStatus.Running;
}
private bool ServiceIsAutoloading()
{
_sc.Refresh();
return _sc.StartType == ServiceStartMode.Automatic;
}
private void SetServiceAutoloading(bool isAutoloading)
{
using var process = new Process();
var param = isAutoloading ? "SERVICE_AUTO_START" : "SERVICE_DEMAND_START";
process.StartInfo = new ProcessStartInfo
{
FileName = ".\\nssm.exe",
Arguments = $"set MicroSocks Start {param}",
UseShellExecute = false,
RedirectStandardOutput = true,
RedirectStandardError = true,
CreateNoWindow = true,
WorkingDirectory = Path.GetDirectoryName(".")
};
process.Start();
process.WaitForExit();
}
private void GetServiceStatus(object? state)
{
//_sc.Refresh();
//_serviceManager.Get();
IsWorking = ServiceIsRunning();
IsAutoloading = ServiceIsAutoloading();
//IsWorking = _serviceManager["State"]?.ToString() == "Running";
//IsAutoloading = _serviceManager["StartMode"]?.ToString() == "Auto";
}
Arguments ParseArguments(string arguments)
{
var argumentsList = arguments.Replace("\0", "").Trim().Split();
return new Arguments(argumentsList[1], argumentsList[3]);
}
[ReactiveCommand]
private void GetMicroSocksArguments()
{
using var process = new Process();
process.StartInfo = new ProcessStartInfo
{
FileName = ".\\nssm.exe",
Arguments = "get MicroSocks AppParameters",
UseShellExecute = false,
RedirectStandardOutput = true,
RedirectStandardError = true,
CreateNoWindow = true,
WorkingDirectory = Path.GetDirectoryName(".")
};
process.Start();
var output = process.StandardOutput.ReadToEnd();
process.WaitForExit();
var arguments = ParseArguments(output);
var arguments = _serviceManager.GetArguments();
Ip = arguments.Ip;
Port = arguments.Port;
}
@ -157,20 +62,7 @@ public partial class MainWindowViewModel : ViewModelBase
[ReactiveCommand]
private void SetMicroSocksArguments()
{
using var process = new Process();
process.StartInfo = new ProcessStartInfo
{
FileName = ".\\nssm.exe",
Arguments = $"set MicroSocks AppParameters -i {_ip} -p {_port}",
UseShellExecute = false,
RedirectStandardOutput = true,
RedirectStandardError = true,
CreateNoWindow = true,
WorkingDirectory = Path.GetDirectoryName(".")
};
process.Start();
process.WaitForExit();
RestartService();
_serviceManager.SetArguments(new IServiceManager.Arguments(Ip, Port));
_serviceManager.Restart();
}
}

View File

@ -11,8 +11,7 @@
CanMaximize="False"
CanResize="False"
Width="250"
Height="330"
>
Height="330">
<Design.DataContext>
<!-- This only sets the DataContext for the previewer in an IDE,
@ -28,7 +27,7 @@
Grid.Column="0"
HorizontalAlignment="Stretch"
HorizontalContentAlignment="Center"
IsChecked="{Binding IsWorking}"
IsChecked="{Binding IsRunning}"
Command="{Binding SetServiceStatusCommand}">
Работа
</ToggleButton>
@ -37,7 +36,7 @@
Grid.Column="1"
HorizontalAlignment="Stretch"
HorizontalContentAlignment="Center"
IsChecked="{Binding IsAutoloading}"
IsChecked="{Binding IsAutostart}"
Command="{Binding SetServiceStatusCommand}">
Автозапуск
</ToggleButton>

View File

@ -1,4 +1,5 @@
using Avalonia.Controls;
using Avalonia.Input;
namespace MicrosocksGUI.Views;