Compare commits

...

3 commits

Author SHA1 Message Date
2dust
6de5a5215d Refactor code
Some checks failed
release Linux / build (Release) (push) Has been cancelled
release macOS / build (Release) (push) Has been cancelled
release Windows desktop (Avalonia UI) / build (Release) (push) Has been cancelled
release Windows / build (Release) (push) Has been cancelled
release Linux / rpm (push) Has been cancelled
Renamed FileManager to FileUtils and updated all references accordingly. Moved SemanticVersion to the Models namespace. Replaced WindowsJob with WindowsJobService, relocating and updating the implementation. Updated usages in CoreManager and related classes to use the new service and utility names. These changes improve code organization and naming consistency.
2025-11-03 20:01:36 +08:00
2dust
8d86aa2b72 Refactor ping and HTTP helpers, update usages
Moved GetRealPingTime from HttpClientHelper to ConnectionHandler and refactored related methods for clarity. Removed unused and redundant HTTP helper methods. Updated DownloadService and SpeedtestService to use the new method signatures. Simplified UpdateService constructor using primary constructor syntax.
2025-11-03 19:41:02 +08:00
DHR60
1aee3950f4
Fix (#8244) 2025-11-03 19:18:18 +08:00
17 changed files with 237 additions and 374 deletions

View file

@ -3,7 +3,7 @@ using System.IO.Compression;
namespace ServiceLib.Common;
public static class FileManager
public static class FileUtils
{
private static readonly string _tag = "FileManager";

View file

@ -1,177 +0,0 @@
namespace ServiceLib.Common;
/*
* See:
* http://stackoverflow.com/questions/6266820/working-example-of-createjobobject-setinformationjobobject-pinvoke-in-net
*/
public sealed class WindowsJob : IDisposable
{
private IntPtr handle = IntPtr.Zero;
public WindowsJob()
{
handle = CreateJobObject(IntPtr.Zero, null);
var extendedInfoPtr = IntPtr.Zero;
var info = new JOBOBJECT_BASIC_LIMIT_INFORMATION
{
LimitFlags = 0x2000
};
var extendedInfo = new JOBOBJECT_EXTENDED_LIMIT_INFORMATION
{
BasicLimitInformation = info
};
try
{
var length = Marshal.SizeOf(typeof(JOBOBJECT_EXTENDED_LIMIT_INFORMATION));
extendedInfoPtr = Marshal.AllocHGlobal(length);
Marshal.StructureToPtr(extendedInfo, extendedInfoPtr, false);
if (!SetInformationJobObject(handle, JobObjectInfoType.ExtendedLimitInformation, extendedInfoPtr,
(uint)length))
{
throw new Exception(string.Format("Unable to set information. Error: {0}",
Marshal.GetLastWin32Error()));
}
}
finally
{
if (extendedInfoPtr != IntPtr.Zero)
{
Marshal.FreeHGlobal(extendedInfoPtr);
}
}
}
public bool AddProcess(IntPtr processHandle)
{
var succ = AssignProcessToJobObject(handle, processHandle);
if (!succ)
{
Logging.SaveLog("Failed to call AssignProcessToJobObject! GetLastError=" + Marshal.GetLastWin32Error());
}
return succ;
}
public bool AddProcess(int processId)
{
return AddProcess(Process.GetProcessById(processId).Handle);
}
#region IDisposable
private bool disposed;
public void Dispose()
{
Dispose(true);
GC.SuppressFinalize(this);
}
private void Dispose(bool disposing)
{
if (disposed)
{
return;
}
disposed = true;
if (disposing)
{
// no managed objects to free
}
if (handle != IntPtr.Zero)
{
CloseHandle(handle);
handle = IntPtr.Zero;
}
}
~WindowsJob()
{
Dispose(false);
}
#endregion IDisposable
#region Interop
[DllImport("kernel32.dll", CharSet = CharSet.Unicode)]
private static extern IntPtr CreateJobObject(IntPtr a, string? lpName);
[DllImport("kernel32.dll", SetLastError = true)]
private static extern bool SetInformationJobObject(IntPtr hJob, JobObjectInfoType infoType, IntPtr lpJobObjectInfo, uint cbJobObjectInfoLength);
[DllImport("kernel32.dll", SetLastError = true)]
private static extern bool AssignProcessToJobObject(IntPtr job, IntPtr process);
[DllImport("kernel32.dll", SetLastError = true)]
[return: MarshalAs(UnmanagedType.Bool)]
private static extern bool CloseHandle(IntPtr hObject);
#endregion Interop
}
#region Helper classes
[StructLayout(LayoutKind.Sequential)]
internal struct IO_COUNTERS
{
public ulong ReadOperationCount;
public ulong WriteOperationCount;
public ulong OtherOperationCount;
public ulong ReadTransferCount;
public ulong WriteTransferCount;
public ulong OtherTransferCount;
}
[StructLayout(LayoutKind.Sequential)]
internal struct JOBOBJECT_BASIC_LIMIT_INFORMATION
{
public long PerProcessUserTimeLimit;
public long PerJobUserTimeLimit;
public uint LimitFlags;
public UIntPtr MinimumWorkingSetSize;
public UIntPtr MaximumWorkingSetSize;
public uint ActiveProcessLimit;
public UIntPtr Affinity;
public uint PriorityClass;
public uint SchedulingClass;
}
[StructLayout(LayoutKind.Sequential)]
public struct SECURITY_ATTRIBUTES
{
public uint nLength;
public IntPtr lpSecurityDescriptor;
public int bInheritHandle;
}
[StructLayout(LayoutKind.Sequential)]
internal struct JOBOBJECT_EXTENDED_LIMIT_INFORMATION
{
public JOBOBJECT_BASIC_LIMIT_INFORMATION BasicLimitInformation;
public IO_COUNTERS IoInfo;
public UIntPtr ProcessMemoryLimit;
public UIntPtr JobMemoryLimit;
public UIntPtr PeakProcessMemoryUsed;
public UIntPtr PeakJobMemoryUsed;
}
public enum JobObjectInfoType
{
AssociateCompletionPortInformation = 7,
BasicLimitInformation = 2,
BasicUIRestrictions = 4,
EndOfJobTimeInformation = 6,
ExtendedLimitInformation = 9,
SecurityLimitInformation = 5,
GroupInformation = 11
}
#endregion Helper classes

View file

@ -6,7 +6,7 @@ public static class ConnectionHandler
public static async Task<string> RunAvailabilityCheck()
{
var time = await GetRealPingTime();
var time = await GetRealPingTimeInfo();
var ip = time > 0 ? await GetIPInfo() ?? Global.None : Global.None;
return string.Format(ResUI.TestMeOutput, time, ip);
@ -39,7 +39,7 @@ public static class ConnectionHandler
return $"({country ?? "unknown"}) {ip}";
}
private static async Task<int> GetRealPingTime()
private static async Task<int> GetRealPingTimeInfo()
{
var responseTime = -1;
try
@ -50,7 +50,7 @@ public static class ConnectionHandler
for (var i = 0; i < 2; i++)
{
responseTime = await HttpClientHelper.Instance.GetRealPingTime(url, webProxy, 10);
responseTime = await GetRealPingTime(url, webProxy, 10);
if (responseTime > 0)
{
break;
@ -65,4 +65,34 @@ public static class ConnectionHandler
}
return responseTime;
}
public static async Task<int> GetRealPingTime(string url, IWebProxy? webProxy, int downloadTimeout)
{
var responseTime = -1;
try
{
using var cts = new CancellationTokenSource();
cts.CancelAfter(TimeSpan.FromSeconds(downloadTimeout));
using var client = new HttpClient(new SocketsHttpHandler()
{
Proxy = webProxy,
UseProxy = webProxy != null
});
List<int> oneTime = new();
for (var i = 0; i < 2; i++)
{
var timer = Stopwatch.StartNew();
await client.GetAsync(url, cts.Token).ConfigureAwait(false);
timer.Stop();
oneTime.Add((int)timer.Elapsed.TotalMilliseconds);
await Task.Delay(100);
}
responseTime = oneTime.Where(x => x > 0).OrderBy(x => x).FirstOrDefault();
}
catch
{
}
return responseTime;
}
}

View file

@ -18,7 +18,7 @@ public static class ProxySettingLinux
private static async Task ExecCmd(List<string> args)
{
var fileName = await FileManager.CreateLinuxShellFile(_proxySetFileName, EmbedUtils.GetEmbedText(Global.ProxySetLinuxShellFileName), false);
var fileName = await FileUtils.CreateLinuxShellFile(_proxySetFileName, EmbedUtils.GetEmbedText(Global.ProxySetLinuxShellFileName), false);
await Utils.GetCliWrapOutput(fileName, args);
}

View file

@ -23,7 +23,7 @@ public static class ProxySettingOSX
private static async Task ExecCmd(List<string> args)
{
var fileName = await FileManager.CreateLinuxShellFile(_proxySetFileName, EmbedUtils.GetEmbedText(Global.ProxySetOSXShellFileName), false);
var fileName = await FileUtils.CreateLinuxShellFile(_proxySetFileName, EmbedUtils.GetEmbedText(Global.ProxySetOSXShellFileName), false);
await Utils.GetCliWrapOutput(fileName, args);
}

View file

@ -49,14 +49,6 @@ public class HttpClientHelper
return await httpClient.GetStringAsync(url);
}
public async Task<string?> GetAsync(HttpClient client, string url, CancellationToken token = default)
{
if (url.IsNullOrEmpty())
{
return null;
}
return await client.GetStringAsync(url, token);
}
public async Task PutAsync(string url, Dictionary<string, string> headers)
{
@ -81,155 +73,5 @@ public class HttpClientHelper
await httpClient.DeleteAsync(url);
}
public static async Task DownloadFileAsync(HttpClient client, string url, string fileName, IProgress<double>? progress, CancellationToken token = default)
{
ArgumentNullException.ThrowIfNull(url);
ArgumentNullException.ThrowIfNull(fileName);
if (File.Exists(fileName))
{
File.Delete(fileName);
}
using var response = await client.GetAsync(url, HttpCompletionOption.ResponseHeadersRead, token);
if (!response.IsSuccessStatusCode)
{
throw new Exception(response.StatusCode.ToString());
}
var total = response.Content.Headers.ContentLength ?? -1L;
var canReportProgress = total != -1 && progress != null;
await using var stream = await response.Content.ReadAsStreamAsync(token);
await using var file = File.Create(fileName);
var totalRead = 0L;
var buffer = new byte[1024 * 1024];
var progressPercentage = 0;
while (true)
{
token.ThrowIfCancellationRequested();
var read = await stream.ReadAsync(buffer, token);
totalRead += read;
if (read == 0)
{
break;
}
await file.WriteAsync(buffer.AsMemory(0, read), token);
if (canReportProgress)
{
var percent = (int)(100.0 * totalRead / total);
//if (progressPercentage != percent && percent % 10 == 0)
{
progressPercentage = percent;
progress?.Report(percent);
}
}
}
if (canReportProgress)
{
progress?.Report(101);
}
}
public async Task DownloadDataAsync4Speed(HttpClient client, string url, IProgress<string> progress, CancellationToken token = default)
{
if (url.IsNullOrEmpty())
{
throw new ArgumentNullException(nameof(url));
}
var response = await client.GetAsync(url, HttpCompletionOption.ResponseHeadersRead, token);
if (!response.IsSuccessStatusCode)
{
throw new Exception(response.StatusCode.ToString());
}
//var total = response.Content.Headers.ContentLength.HasValue ? response.Content.Headers.ContentLength.Value : -1L;
//var canReportProgress = total != -1 && progress != null;
await using var stream = await response.Content.ReadAsStreamAsync(token);
var totalRead = 0L;
var buffer = new byte[1024 * 64];
var isMoreToRead = true;
var progressSpeed = string.Empty;
var totalDatetime = DateTime.Now;
var totalSecond = 0;
do
{
if (token.IsCancellationRequested)
{
if (totalRead > 0)
{
return;
}
else
{
token.ThrowIfCancellationRequested();
}
}
var read = await stream.ReadAsync(buffer, token);
if (read == 0)
{
isMoreToRead = false;
}
else
{
var data = new byte[read];
buffer.ToList().CopyTo(0, data, 0, read);
totalRead += read;
var ts = DateTime.Now - totalDatetime;
if (progress != null && ts.Seconds > totalSecond)
{
totalSecond = ts.Seconds;
var speed = (totalRead * 1d / ts.TotalMilliseconds / 1000).ToString("#0.0");
if (progressSpeed != speed)
{
progressSpeed = speed;
progress.Report(speed);
}
}
}
} while (isMoreToRead);
}
public async Task<int> GetRealPingTime(string url, IWebProxy? webProxy, int downloadTimeout)
{
var responseTime = -1;
try
{
using var cts = new CancellationTokenSource();
cts.CancelAfter(TimeSpan.FromSeconds(downloadTimeout));
using var client = new HttpClient(new SocketsHttpHandler()
{
Proxy = webProxy,
UseProxy = webProxy != null
});
List<int> oneTime = new();
for (var i = 0; i < 2; i++)
{
var timer = Stopwatch.StartNew();
await client.GetAsync(url, cts.Token).ConfigureAwait(false);
timer.Stop();
oneTime.Add((int)timer.Elapsed.TotalMilliseconds);
await Task.Delay(100);
}
responseTime = oneTime.Where(x => x > 0).OrderBy(x => x).FirstOrDefault();
}
catch //(Exception ex)
{
//Utile.SaveLog(ex.Message, ex);
}
return responseTime;
}
}

View file

@ -35,7 +35,7 @@ public class CoreAdminManager
sb.AppendLine("#!/bin/bash");
var cmdLine = $"{fileName.AppendQuotes()} {string.Format(coreInfo.Arguments, Utils.GetBinConfigPath(configPath).AppendQuotes())}";
sb.AppendLine($"exec sudo -S -- {cmdLine}");
var shFilePath = await FileManager.CreateLinuxShellFile("run_as_sudo.sh", sb.ToString(), true);
var shFilePath = await FileUtils.CreateLinuxShellFile("run_as_sudo.sh", sb.ToString(), true);
var procService = new ProcessService(
fileName: shFilePath,
@ -68,7 +68,7 @@ public class CoreAdminManager
try
{
var shellFileName = Utils.IsOSX() ? Global.KillAsSudoOSXShellFileName : Global.KillAsSudoLinuxShellFileName;
var shFilePath = await FileManager.CreateLinuxShellFile("kill_as_sudo.sh", EmbedUtils.GetEmbedText(shellFileName), true);
var shFilePath = await FileUtils.CreateLinuxShellFile("kill_as_sudo.sh", EmbedUtils.GetEmbedText(shellFileName), true);
if (shFilePath.Contains(' '))
{
shFilePath = shFilePath.AppendQuotes();

View file

@ -8,7 +8,7 @@ public class CoreManager
private static readonly Lazy<CoreManager> _instance = new(() => new());
public static CoreManager Instance => _instance.Value;
private Config _config;
private WindowsJob? _processJob;
private WindowsJobService? _processJob;
private ProcessService? _processService;
private ProcessService? _processPreService;
private bool _linuxSudo = false;
@ -27,7 +27,7 @@ public class CoreManager
var toPath = Utils.GetBinPath("");
if (fromPath != toPath)
{
FileManager.CopyDirectory(fromPath, toPath, true, false);
FileUtils.CopyDirectory(fromPath, toPath, true, false);
}
}

View file

@ -56,9 +56,9 @@ public class TaskManager
{
//Logging.SaveLog("Execute delete expired files");
FileManager.DeleteExpiredFiles(Utils.GetBinConfigPath(), DateTime.Now.AddHours(-1));
FileManager.DeleteExpiredFiles(Utils.GetLogPath(), DateTime.Now.AddMonths(-1));
FileManager.DeleteExpiredFiles(Utils.GetTempPath(), DateTime.Now.AddMonths(-1));
FileUtils.DeleteExpiredFiles(Utils.GetBinConfigPath(), DateTime.Now.AddHours(-1));
FileUtils.DeleteExpiredFiles(Utils.GetLogPath(), DateTime.Now.AddMonths(-1));
FileUtils.DeleteExpiredFiles(Utils.GetTempPath(), DateTime.Now.AddMonths(-1));
try
{

View file

@ -1,4 +1,4 @@
namespace ServiceLib.Common;
namespace ServiceLib.Models;
public class SemanticVersion
{

View file

@ -268,10 +268,13 @@ public partial class CoreConfigSingboxService
.Select(s => s + "\n-----END CERTIFICATE-----")
.Select(s => s.Replace("\r\n", "\n"))
.ToList() ?? new();
tls.certificate = certs.Count > 0 ? certs : null;
if (certs.Count > 0)
{
tls.certificate = certs;
tls.insecure = false;
}
else
}
else if (node.StreamSecurity == Global.StreamSecurityReality)
{
tls.reality = new Reality4Sbox()
{

View file

@ -71,7 +71,7 @@ public class DownloadService
AllowAutoRedirect = false,
Proxy = await GetWebProxy(blProxy)
};
HttpClient client = new(webRequestHandler);
var client = new HttpClient(webRequestHandler);
var response = await client.GetAsync(url);
if (response.StatusCode == HttpStatusCode.Redirect && response.Headers.Location is not null)
@ -156,7 +156,7 @@ public class DownloadService
}
using var cts = new CancellationTokenSource();
var result = await HttpClientHelper.Instance.GetAsync(client, url, cts.Token).WaitAsync(TimeSpan.FromSeconds(timeout), cts.Token);
var result = await client.GetStringAsync(url, cts.Token).WaitAsync(TimeSpan.FromSeconds(timeout), cts.Token);
return result;
}
catch (Exception ex)

View file

@ -272,7 +272,7 @@ public class SpeedtestService(Config config, Func<SpeedTestResult, Task> updateF
private async Task<int> DoRealPing(ServerTestItem it)
{
var webProxy = new WebProxy($"socks5://{Global.Loopback}:{it.Port}");
var responseTime = await HttpClientHelper.Instance.GetRealPingTime(_config.SpeedTestItem.SpeedPingTestUrl, webProxy, 10);
var responseTime = await ConnectionHandler.GetRealPingTime(_config.SpeedTestItem.SpeedPingTestUrl, webProxy, 10);
ProfileExManager.Instance.SetTestDelay(it.IndexId, responseTime);
await UpdateFunc(it.IndexId, responseTime.ToString());

View file

@ -1,18 +1,12 @@
namespace ServiceLib.Services;
public class UpdateService
public class UpdateService(Config config, Func<bool, string, Task> updateFunc)
{
private readonly Config? _config;
private readonly Func<bool, string, Task>? _updateFunc;
private readonly Config? _config = config;
private readonly Func<bool, string, Task>? _updateFunc = updateFunc;
private readonly int _timeout = 30;
private static readonly string _tag = "UpdateService";
public UpdateService(Config config, Func<bool, string, Task> updateFunc)
{
_config = config;
_updateFunc = updateFunc;
}
public async Task CheckUpdateGuiN(bool preRelease)
{
var url = string.Empty;

View file

@ -0,0 +1,171 @@
namespace ServiceLib.Services;
/// <summary>
/// http://stackoverflow.com/questions/6266820/working-example-of-createjobobject-setinformationjobobject-pinvoke-in-net
/// </summary>
public sealed class WindowsJobService : IDisposable
{
private nint handle = nint.Zero;
public WindowsJobService()
{
handle = CreateJobObject(nint.Zero, null);
var extendedInfoPtr = nint.Zero;
var info = new JOBOBJECT_BASIC_LIMIT_INFORMATION
{
LimitFlags = 0x2000
};
var extendedInfo = new JOBOBJECT_EXTENDED_LIMIT_INFORMATION
{
BasicLimitInformation = info
};
try
{
var length = Marshal.SizeOf(typeof(JOBOBJECT_EXTENDED_LIMIT_INFORMATION));
extendedInfoPtr = Marshal.AllocHGlobal(length);
Marshal.StructureToPtr(extendedInfo, extendedInfoPtr, false);
if (!SetInformationJobObject(handle, JobObjectInfoType.ExtendedLimitInformation, extendedInfoPtr,
(uint)length))
{
throw new Exception(string.Format("Unable to set information. Error: {0}",
Marshal.GetLastWin32Error()));
}
}
finally
{
if (extendedInfoPtr != nint.Zero)
{
Marshal.FreeHGlobal(extendedInfoPtr);
}
}
}
public bool AddProcess(nint processHandle)
{
var succ = AssignProcessToJobObject(handle, processHandle);
if (!succ)
{
Logging.SaveLog("Failed to call AssignProcessToJobObject! GetLastError=" + Marshal.GetLastWin32Error());
}
return succ;
}
public bool AddProcess(int processId)
{
return AddProcess(Process.GetProcessById(processId).Handle);
}
#region IDisposable
private bool disposed;
public void Dispose()
{
Dispose(true);
GC.SuppressFinalize(this);
}
private void Dispose(bool disposing)
{
if (disposed)
{
return;
}
disposed = true;
if (disposing)
{
// no managed objects to free
}
if (handle != nint.Zero)
{
CloseHandle(handle);
handle = nint.Zero;
}
}
~WindowsJobService()
{
Dispose(false);
}
#endregion IDisposable
#region Interop
[DllImport("kernel32.dll", CharSet = CharSet.Unicode)]
private static extern nint CreateJobObject(nint a, string? lpName);
[DllImport("kernel32.dll", SetLastError = true)]
private static extern bool SetInformationJobObject(nint hJob, JobObjectInfoType infoType, nint lpJobObjectInfo, uint cbJobObjectInfoLength);
[DllImport("kernel32.dll", SetLastError = true)]
private static extern bool AssignProcessToJobObject(nint job, nint process);
[DllImport("kernel32.dll", SetLastError = true)]
[return: MarshalAs(UnmanagedType.Bool)]
private static extern bool CloseHandle(nint hObject);
#endregion Interop
}
[StructLayout(LayoutKind.Sequential)]
internal struct IO_COUNTERS
{
public ulong ReadOperationCount;
public ulong WriteOperationCount;
public ulong OtherOperationCount;
public ulong ReadTransferCount;
public ulong WriteTransferCount;
public ulong OtherTransferCount;
}
[StructLayout(LayoutKind.Sequential)]
internal struct JOBOBJECT_BASIC_LIMIT_INFORMATION
{
public long PerProcessUserTimeLimit;
public long PerJobUserTimeLimit;
public uint LimitFlags;
public nuint MinimumWorkingSetSize;
public nuint MaximumWorkingSetSize;
public uint ActiveProcessLimit;
public nuint Affinity;
public uint PriorityClass;
public uint SchedulingClass;
}
[StructLayout(LayoutKind.Sequential)]
public struct SECURITY_ATTRIBUTES
{
public uint nLength;
public nint lpSecurityDescriptor;
public int bInheritHandle;
}
[StructLayout(LayoutKind.Sequential)]
internal struct JOBOBJECT_EXTENDED_LIMIT_INFORMATION
{
public JOBOBJECT_BASIC_LIMIT_INFORMATION BasicLimitInformation;
public IO_COUNTERS IoInfo;
public nuint ProcessMemoryLimit;
public nuint JobMemoryLimit;
public nuint PeakProcessMemoryUsed;
public nuint PeakJobMemoryUsed;
}
public enum JobObjectInfoType
{
AssociateCompletionPortInformation = 7,
BasicLimitInformation = 2,
BasicUIRestrictions = 4,
EndOfJobTimeInformation = 6,
ExtendedLimitInformation = 9,
SecurityLimitInformation = 5,
GroupInformation = 11
}

View file

@ -119,7 +119,7 @@ public class BackupAndRestoreViewModel : MyReactiveObject
return;
}
//check
var lstFiles = FileManager.GetFilesFromZip(fileName);
var lstFiles = FileUtils.GetFilesFromZip(fileName);
if (lstFiles is null || !lstFiles.Any(t => t.Contains(_guiConfigs)))
{
DisplayOperationMsg(ResUI.LocalRestoreInvalidZipTips);
@ -135,7 +135,7 @@ public class BackupAndRestoreViewModel : MyReactiveObject
await SQLiteHelper.Instance.DisposeDbConnectionAsync();
var toPath = Utils.GetConfigPath();
FileManager.ZipExtractToFile(fileName, toPath, "");
FileUtils.ZipExtractToFile(fileName, toPath, "");
if (Utils.IsWindows())
{
@ -167,8 +167,8 @@ public class BackupAndRestoreViewModel : MyReactiveObject
var configDirZipTemp = Utils.GetTempPath($"v2rayN_{DateTime.Now:yyyyMMddHHmmss}");
var configDirTemp = Path.Combine(configDirZipTemp, _guiConfigs);
FileManager.CopyDirectory(configDir, configDirTemp, false, true, "");
var ret = FileManager.CreateFromDirectory(configDirZipTemp, fileName);
FileUtils.CopyDirectory(configDir, configDirTemp, false, true, "");
var ret = FileUtils.CreateFromDirectory(configDirZipTemp, fileName);
Directory.Delete(configDirZipTemp, true);
return await Task.FromResult(ret);
}

View file

@ -271,24 +271,24 @@ public class CheckUpdateViewModel : MyReactiveObject
if (fileName.Contains(".tar.gz"))
{
FileManager.DecompressTarFile(fileName, toPath);
FileUtils.DecompressTarFile(fileName, toPath);
var dir = new DirectoryInfo(toPath);
if (dir.Exists)
{
foreach (var subDir in dir.GetDirectories())
{
FileManager.CopyDirectory(subDir.FullName, toPath, false, true);
FileUtils.CopyDirectory(subDir.FullName, toPath, false, true);
subDir.Delete(true);
}
}
}
else if (fileName.Contains(".gz"))
{
FileManager.DecompressFile(fileName, toPath, item.CoreType);
FileUtils.DecompressFile(fileName, toPath, item.CoreType);
}
else
{
FileManager.ZipExtractToFile(fileName, toPath, "geo");
FileUtils.ZipExtractToFile(fileName, toPath, "geo");
}
if (Utils.IsNonWindows())