From d7bee814c7425196c717417ef8a0f954747d0816 Mon Sep 17 00:00:00 2001 From: justerzhu Date: Wed, 4 Sep 2024 00:35:26 +0800 Subject: [PATCH 01/33] =?UTF-8?q?feature:=20=E6=B7=BB=E5=8A=A0=E5=8D=95?= =?UTF-8?q?=E5=85=83=E6=B5=8B=E8=AF=95?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../GeneralUpdate.Bowl.Test.csproj | 23 ++++++++++++ src/c#/GeneralUpdate.Bowl.Test/UnitTest1.cs | 9 +++++ .../GeneralUpdate.Client.Test.csproj | 23 ++++++++++++ src/c#/GeneralUpdate.Client.Test/UnitTest1.cs | 9 +++++ .../GeneralUpdate.Differential.Test.csproj | 23 ++++++++++++ .../UnitTest1.cs | 9 +++++ .../GeneralUpdate.Upgrad.Test.csproj | 23 ++++++++++++ src/c#/GeneralUpdate.Upgrad.Test/UnitTest1.cs | 9 +++++ .../GeneralUpdate.Zip.Test.csproj | 23 ++++++++++++ src/c#/GeneralUpdate.Zip.Test/UnitTest1.cs | 9 +++++ src/c#/GeneralUpdate.sln | 37 +++++++++++++++++++ 11 files changed, 197 insertions(+) create mode 100644 src/c#/GeneralUpdate.Bowl.Test/GeneralUpdate.Bowl.Test.csproj create mode 100644 src/c#/GeneralUpdate.Bowl.Test/UnitTest1.cs create mode 100644 src/c#/GeneralUpdate.Client.Test/GeneralUpdate.Client.Test.csproj create mode 100644 src/c#/GeneralUpdate.Client.Test/UnitTest1.cs create mode 100644 src/c#/GeneralUpdate.Differential.Test/GeneralUpdate.Differential.Test.csproj create mode 100644 src/c#/GeneralUpdate.Differential.Test/UnitTest1.cs create mode 100644 src/c#/GeneralUpdate.Upgrad.Test/GeneralUpdate.Upgrad.Test.csproj create mode 100644 src/c#/GeneralUpdate.Upgrad.Test/UnitTest1.cs create mode 100644 src/c#/GeneralUpdate.Zip.Test/GeneralUpdate.Zip.Test.csproj create mode 100644 src/c#/GeneralUpdate.Zip.Test/UnitTest1.cs diff --git a/src/c#/GeneralUpdate.Bowl.Test/GeneralUpdate.Bowl.Test.csproj b/src/c#/GeneralUpdate.Bowl.Test/GeneralUpdate.Bowl.Test.csproj new file mode 100644 index 00000000..085bdddf --- /dev/null +++ b/src/c#/GeneralUpdate.Bowl.Test/GeneralUpdate.Bowl.Test.csproj @@ -0,0 +1,23 @@ + + + + net8.0 + enable + enable + + false + true + + + + + + + + + + + + + + diff --git a/src/c#/GeneralUpdate.Bowl.Test/UnitTest1.cs b/src/c#/GeneralUpdate.Bowl.Test/UnitTest1.cs new file mode 100644 index 00000000..c76c89cc --- /dev/null +++ b/src/c#/GeneralUpdate.Bowl.Test/UnitTest1.cs @@ -0,0 +1,9 @@ +namespace GeneralUpdate.Bowl.Test; + +public class UnitTest1 +{ + [Fact] + public void Test1() + { + } +} \ No newline at end of file diff --git a/src/c#/GeneralUpdate.Client.Test/GeneralUpdate.Client.Test.csproj b/src/c#/GeneralUpdate.Client.Test/GeneralUpdate.Client.Test.csproj new file mode 100644 index 00000000..085bdddf --- /dev/null +++ b/src/c#/GeneralUpdate.Client.Test/GeneralUpdate.Client.Test.csproj @@ -0,0 +1,23 @@ + + + + net8.0 + enable + enable + + false + true + + + + + + + + + + + + + + diff --git a/src/c#/GeneralUpdate.Client.Test/UnitTest1.cs b/src/c#/GeneralUpdate.Client.Test/UnitTest1.cs new file mode 100644 index 00000000..a34ba46c --- /dev/null +++ b/src/c#/GeneralUpdate.Client.Test/UnitTest1.cs @@ -0,0 +1,9 @@ +namespace GeneralUpdate.Client.Test; + +public class UnitTest1 +{ + [Fact] + public void Test1() + { + } +} \ No newline at end of file diff --git a/src/c#/GeneralUpdate.Differential.Test/GeneralUpdate.Differential.Test.csproj b/src/c#/GeneralUpdate.Differential.Test/GeneralUpdate.Differential.Test.csproj new file mode 100644 index 00000000..085bdddf --- /dev/null +++ b/src/c#/GeneralUpdate.Differential.Test/GeneralUpdate.Differential.Test.csproj @@ -0,0 +1,23 @@ + + + + net8.0 + enable + enable + + false + true + + + + + + + + + + + + + + diff --git a/src/c#/GeneralUpdate.Differential.Test/UnitTest1.cs b/src/c#/GeneralUpdate.Differential.Test/UnitTest1.cs new file mode 100644 index 00000000..8744088a --- /dev/null +++ b/src/c#/GeneralUpdate.Differential.Test/UnitTest1.cs @@ -0,0 +1,9 @@ +namespace GeneralUpdate.Differential.Test; + +public class UnitTest1 +{ + [Fact] + public void Test1() + { + } +} \ No newline at end of file diff --git a/src/c#/GeneralUpdate.Upgrad.Test/GeneralUpdate.Upgrad.Test.csproj b/src/c#/GeneralUpdate.Upgrad.Test/GeneralUpdate.Upgrad.Test.csproj new file mode 100644 index 00000000..085bdddf --- /dev/null +++ b/src/c#/GeneralUpdate.Upgrad.Test/GeneralUpdate.Upgrad.Test.csproj @@ -0,0 +1,23 @@ + + + + net8.0 + enable + enable + + false + true + + + + + + + + + + + + + + diff --git a/src/c#/GeneralUpdate.Upgrad.Test/UnitTest1.cs b/src/c#/GeneralUpdate.Upgrad.Test/UnitTest1.cs new file mode 100644 index 00000000..89a044d4 --- /dev/null +++ b/src/c#/GeneralUpdate.Upgrad.Test/UnitTest1.cs @@ -0,0 +1,9 @@ +namespace GeneralUpdate.Upgrad.Test; + +public class UnitTest1 +{ + [Fact] + public void Test1() + { + } +} \ No newline at end of file diff --git a/src/c#/GeneralUpdate.Zip.Test/GeneralUpdate.Zip.Test.csproj b/src/c#/GeneralUpdate.Zip.Test/GeneralUpdate.Zip.Test.csproj new file mode 100644 index 00000000..085bdddf --- /dev/null +++ b/src/c#/GeneralUpdate.Zip.Test/GeneralUpdate.Zip.Test.csproj @@ -0,0 +1,23 @@ + + + + net8.0 + enable + enable + + false + true + + + + + + + + + + + + + + diff --git a/src/c#/GeneralUpdate.Zip.Test/UnitTest1.cs b/src/c#/GeneralUpdate.Zip.Test/UnitTest1.cs new file mode 100644 index 00000000..0a715df0 --- /dev/null +++ b/src/c#/GeneralUpdate.Zip.Test/UnitTest1.cs @@ -0,0 +1,9 @@ +namespace GeneralUpdate.Zip.Test; + +public class UnitTest1 +{ + [Fact] + public void Test1() + { + } +} \ No newline at end of file diff --git a/src/c#/GeneralUpdate.sln b/src/c#/GeneralUpdate.sln index cf1cae86..6ccfb902 100644 --- a/src/c#/GeneralUpdate.sln +++ b/src/c#/GeneralUpdate.sln @@ -27,6 +27,18 @@ Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "GeneralUpdate.Common", "Gen EndProject Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "GeneralUpdate.Bowl", "GeneralUpdate.Bowl\GeneralUpdate.Bowl.csproj", "{49D0687D-1321-48E9-84C3-936B10532367}" EndProject +Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "test", "test", "{305810CB-3BBB-4BDF-A718-F68DA1CFC5F5}" +EndProject +Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "GeneralUpdate.Client.Test", "GeneralUpdate.Client.Test\GeneralUpdate.Client.Test.csproj", "{D02F729E-2A54-4667-88CA-7EB1C94E0A68}" +EndProject +Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "GeneralUpdate.Upgrad.Test", "GeneralUpdate.Upgrad.Test\GeneralUpdate.Upgrad.Test.csproj", "{665F4A98-C6D3-42E7-BFAE-B4E8FC938FFA}" +EndProject +Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "GeneralUpdate.Zip.Test", "GeneralUpdate.Zip.Test\GeneralUpdate.Zip.Test.csproj", "{A7B03A99-3C82-4B1A-B34D-9D43E25EF598}" +EndProject +Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "GeneralUpdate.Differential.Test", "GeneralUpdate.Differential.Test\GeneralUpdate.Differential.Test.csproj", "{B4462DE1-1978-4871-AE51-3A6A2BAF22DC}" +EndProject +Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "GeneralUpdate.Bowl.Test", "GeneralUpdate.Bowl.Test\GeneralUpdate.Bowl.Test.csproj", "{B1FE5A6A-F8B4-473D-8492-E43DDC6D73E0}" +EndProject Global GlobalSection(SolutionConfigurationPlatforms) = preSolution Debug|Any CPU = Debug|Any CPU @@ -75,6 +87,26 @@ Global {49D0687D-1321-48E9-84C3-936B10532367}.Debug|Any CPU.Build.0 = Debug|Any CPU {49D0687D-1321-48E9-84C3-936B10532367}.Release|Any CPU.ActiveCfg = Release|Any CPU {49D0687D-1321-48E9-84C3-936B10532367}.Release|Any CPU.Build.0 = Release|Any CPU + {D02F729E-2A54-4667-88CA-7EB1C94E0A68}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {D02F729E-2A54-4667-88CA-7EB1C94E0A68}.Debug|Any CPU.Build.0 = Debug|Any CPU + {D02F729E-2A54-4667-88CA-7EB1C94E0A68}.Release|Any CPU.ActiveCfg = Release|Any CPU + {D02F729E-2A54-4667-88CA-7EB1C94E0A68}.Release|Any CPU.Build.0 = Release|Any CPU + {665F4A98-C6D3-42E7-BFAE-B4E8FC938FFA}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {665F4A98-C6D3-42E7-BFAE-B4E8FC938FFA}.Debug|Any CPU.Build.0 = Debug|Any CPU + {665F4A98-C6D3-42E7-BFAE-B4E8FC938FFA}.Release|Any CPU.ActiveCfg = Release|Any CPU + {665F4A98-C6D3-42E7-BFAE-B4E8FC938FFA}.Release|Any CPU.Build.0 = Release|Any CPU + {A7B03A99-3C82-4B1A-B34D-9D43E25EF598}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {A7B03A99-3C82-4B1A-B34D-9D43E25EF598}.Debug|Any CPU.Build.0 = Debug|Any CPU + {A7B03A99-3C82-4B1A-B34D-9D43E25EF598}.Release|Any CPU.ActiveCfg = Release|Any CPU + {A7B03A99-3C82-4B1A-B34D-9D43E25EF598}.Release|Any CPU.Build.0 = Release|Any CPU + {B4462DE1-1978-4871-AE51-3A6A2BAF22DC}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {B4462DE1-1978-4871-AE51-3A6A2BAF22DC}.Debug|Any CPU.Build.0 = Debug|Any CPU + {B4462DE1-1978-4871-AE51-3A6A2BAF22DC}.Release|Any CPU.ActiveCfg = Release|Any CPU + {B4462DE1-1978-4871-AE51-3A6A2BAF22DC}.Release|Any CPU.Build.0 = Release|Any CPU + {B1FE5A6A-F8B4-473D-8492-E43DDC6D73E0}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {B1FE5A6A-F8B4-473D-8492-E43DDC6D73E0}.Debug|Any CPU.Build.0 = Debug|Any CPU + {B1FE5A6A-F8B4-473D-8492-E43DDC6D73E0}.Release|Any CPU.ActiveCfg = Release|Any CPU + {B1FE5A6A-F8B4-473D-8492-E43DDC6D73E0}.Release|Any CPU.Build.0 = Release|Any CPU EndGlobalSection GlobalSection(SolutionProperties) = preSolution HideSolutionNode = FALSE @@ -90,6 +122,11 @@ Global {7779FB4A-D121-48CC-B033-C3D36BD5D4FF} = {74BE0282-A10D-4A81-A0F0-FAA79A6152B7} {D14E59CD-404B-467B-9C6D-91EFC5994D37} = {91F059E6-7AD3-4FB7-9604-30A7849C6EFF} {49D0687D-1321-48E9-84C3-936B10532367} = {91F059E6-7AD3-4FB7-9604-30A7849C6EFF} + {D02F729E-2A54-4667-88CA-7EB1C94E0A68} = {305810CB-3BBB-4BDF-A718-F68DA1CFC5F5} + {665F4A98-C6D3-42E7-BFAE-B4E8FC938FFA} = {305810CB-3BBB-4BDF-A718-F68DA1CFC5F5} + {A7B03A99-3C82-4B1A-B34D-9D43E25EF598} = {305810CB-3BBB-4BDF-A718-F68DA1CFC5F5} + {B4462DE1-1978-4871-AE51-3A6A2BAF22DC} = {305810CB-3BBB-4BDF-A718-F68DA1CFC5F5} + {B1FE5A6A-F8B4-473D-8492-E43DDC6D73E0} = {305810CB-3BBB-4BDF-A718-F68DA1CFC5F5} EndGlobalSection GlobalSection(ExtensibilityGlobals) = postSolution SolutionGuid = {A7B2D0AD-E000-4749-BAC0-FF21B9872805} From e62dcffd34ef47e7afa9d60548e3478fad90d29d Mon Sep 17 00:00:00 2001 From: justerzhu Date: Fri, 6 Sep 2024 23:08:38 +0800 Subject: [PATCH 02/33] =?UTF-8?q?refactor:=20=E7=A7=BB=E9=99=A4=EF=BC=8C?= =?UTF-8?q?=E9=99=A4core=E5=BA=93=E4=BB=A5=E5=A4=96=E7=9A=84=E6=89=80?= =?UTF-8?q?=E6=9C=89=E9=A9=B1=E5=8A=A8=E7=9B=B8=E5=85=B3=E4=BB=A3=E7=A0=81?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/c#/GeneralUpdate.Client/MySample.cs | 21 ------ .../GeneralUpdate.ClientCore.csproj | 9 --- .../Download/DownloadManager.cs | 2 +- .../Download/DownloadTask.cs | 3 +- .../Driver/BackupDriverCommand.cs | 48 ------------- .../Driver/CommandExecutor.cs | 44 ------------ .../Driver/DeleteDriverCommand.cs | 23 ------- .../Driver/DriverInformation.cs | 69 ------------------- .../Driver/DriverProcessor.cs | 36 ---------- .../Driver/IDriverCommand.cs | 7 -- .../Driver/InstallDriverCommand.cs | 44 ------------ .../Driver/RestoreDriverCommand.cs | 25 ------- .../Internal/Pipeline/PipelineBuilder.cs | 4 +- .../PlatformWindows/WindowsStrategy.cs | 4 +- 14 files changed, 6 insertions(+), 333 deletions(-) delete mode 100644 src/c#/GeneralUpdate.Common/Driver/BackupDriverCommand.cs delete mode 100644 src/c#/GeneralUpdate.Common/Driver/CommandExecutor.cs delete mode 100644 src/c#/GeneralUpdate.Common/Driver/DeleteDriverCommand.cs delete mode 100644 src/c#/GeneralUpdate.Common/Driver/DriverInformation.cs delete mode 100644 src/c#/GeneralUpdate.Common/Driver/DriverProcessor.cs delete mode 100644 src/c#/GeneralUpdate.Common/Driver/IDriverCommand.cs delete mode 100644 src/c#/GeneralUpdate.Common/Driver/InstallDriverCommand.cs delete mode 100644 src/c#/GeneralUpdate.Common/Driver/RestoreDriverCommand.cs diff --git a/src/c#/GeneralUpdate.Client/MySample.cs b/src/c#/GeneralUpdate.Client/MySample.cs index d602edbc..618aba36 100644 --- a/src/c#/GeneralUpdate.Client/MySample.cs +++ b/src/c#/GeneralUpdate.Client/MySample.cs @@ -2,7 +2,6 @@ using GeneralUpdate.Core.Bootstrap; using GeneralUpdate.Core.Domain.Entity; using GeneralUpdate.Core.Domain.Enum; -using GeneralUpdate.Core.Driver; using GeneralUpdate.Core.Events.CommonArgs; using GeneralUpdate.Core.Events.MultiEventArgs; using GeneralUpdate.Core.Strategys.PlatformWindows; @@ -230,26 +229,6 @@ public async Task TestDifferentialDirty() #region 测试驱动功能 - public void TestDrive() - { - var path1 = "D:\\packet\\source"; - var path2 = "D:\\packet\\target"; - - var drivers = GetAllDriverDirectories(path1); - - var information = new DriverInformation.Builder() - .SetInstallDirectory(path1) - .SetOutPutDirectory(path2) - .SetDriverNames(drivers) - .Build(); - - var processor = new DriverProcessor(); - processor.AddCommand(new BackupDriverCommand(information)); - processor.AddCommand(new DeleteDriverCommand(information)); - processor.AddCommand(new InstallDriverCommand(information)); - processor.ProcessCommands(); - } - /// /// Identifies all folders containing driver files in the specified directory and returns the directory collection. /// diff --git a/src/c#/GeneralUpdate.ClientCore/GeneralUpdate.ClientCore.csproj b/src/c#/GeneralUpdate.ClientCore/GeneralUpdate.ClientCore.csproj index 1c03115f..30e37e57 100644 --- a/src/c#/GeneralUpdate.ClientCore/GeneralUpdate.ClientCore.csproj +++ b/src/c#/GeneralUpdate.ClientCore/GeneralUpdate.ClientCore.csproj @@ -107,14 +107,6 @@ - - - - - - - - @@ -138,7 +130,6 @@ - diff --git a/src/c#/GeneralUpdate.Common/Download/DownloadManager.cs b/src/c#/GeneralUpdate.Common/Download/DownloadManager.cs index cf8f60ba..8ddaaf10 100644 --- a/src/c#/GeneralUpdate.Common/Download/DownloadManager.cs +++ b/src/c#/GeneralUpdate.Common/Download/DownloadManager.cs @@ -4,7 +4,7 @@ using System.Collections.Immutable; using GeneralUpdate.Common.Download; -namespace GeneralUpdate.Core.Download +namespace GeneralUpdate.Common.Download { public class DownloadManager { diff --git a/src/c#/GeneralUpdate.Common/Download/DownloadTask.cs b/src/c#/GeneralUpdate.Common/Download/DownloadTask.cs index 05bd92fb..073cbbc0 100644 --- a/src/c#/GeneralUpdate.Common/Download/DownloadTask.cs +++ b/src/c#/GeneralUpdate.Common/Download/DownloadTask.cs @@ -4,9 +4,8 @@ using System.Net.Http; using System.Threading; using System.Threading.Tasks; -using GeneralUpdate.Common.Download; -namespace GeneralUpdate.Core.Download +namespace GeneralUpdate.Common.Download { public class DownloadTask { diff --git a/src/c#/GeneralUpdate.Common/Driver/BackupDriverCommand.cs b/src/c#/GeneralUpdate.Common/Driver/BackupDriverCommand.cs deleted file mode 100644 index cf339c49..00000000 --- a/src/c#/GeneralUpdate.Common/Driver/BackupDriverCommand.cs +++ /dev/null @@ -1,48 +0,0 @@ -using System.IO; -using System.Text; - -namespace GeneralUpdate.Common.Driver -{ - /// - /// When the /export-driver command backs up a driver, it backs up the driver package along with all its dependencies, such as associated library files and other related files. - /// - public class BackupDriverCommand : IDriverCommand - { - private DriverInformation _information; - - public BackupDriverCommand(DriverInformation information) => _information = information; - - public void Execute() - { - /* - * Back up the specified list of drives. - */ - foreach (var driver in _information.Drivers) - { - //Export the backup according to the driver name. - var outPutDirectory = Path.Combine(_information.OutPutDirectory, Path.GetFileNameWithoutExtension(driver)); - - if (Directory.Exists(outPutDirectory)) - Directory.Delete(outPutDirectory, true); - - Directory.CreateDirectory(outPutDirectory); - - /* - * If no test driver files are available, you can run the following command to export all installed driver files. - * (1) dism /online /export-driver /destination:"D:\packet\cache\" - * (2) pnputil /export-driver * D:\packet\cache - * - * The following code example exports the specified driver to the specified directory. - * pnputil /export-driver oem14.inf D:\packet\cache - */ - var command = new StringBuilder("/c pnputil /export-driver ") - .Append(driver) - .Append(' ') - .Append(outPutDirectory) - .ToString(); - - CommandExecutor.ExecuteCommand(command); - } - } - } -} \ No newline at end of file diff --git a/src/c#/GeneralUpdate.Common/Driver/CommandExecutor.cs b/src/c#/GeneralUpdate.Common/Driver/CommandExecutor.cs deleted file mode 100644 index a6c55651..00000000 --- a/src/c#/GeneralUpdate.Common/Driver/CommandExecutor.cs +++ /dev/null @@ -1,44 +0,0 @@ -using System.Diagnostics; - -namespace GeneralUpdate.Common.Driver -{ - /// - /// When the process starts, PnPUtil is used to execute driver processing commands. - /// - public class CommandExecutor - { - public static void ExecuteCommand(string command) - { - /* - * - *Problems may occur, including: -Permission issues: PnPUtil requires administrator rights to run. If you try to run it without the proper permissions, the backup or restore may fail. -Driver compatibility: Although the backed up drivers work properly at backup time, if the operating system is upgraded, the backed up drivers may no longer be compatible with the new operating system version. -Hardware problems: If the hardware device fails or the hardware configuration changes, the backup driver may not work properly. - - To minimize these risks, the following measures are recommended: -Before doing anything, create a system restore point so that it can be restored to its previous state if something goes wrong. -Update the driver regularly to ensure that the driver is compatible with the current operating system version. -If possible, use pre-tested drivers that are proven to work. - * - */ - var processStartInfo = new ProcessStartInfo - { - WindowStyle = ProcessWindowStyle.Hidden, - FileName = "cmd.exe", - Arguments = command, - UseShellExecute = true, - Verb = "runas" - }; - - using (var process = new Process { StartInfo = processStartInfo }) - { - process.Start(); - process.WaitForExit(); - - if (process.ExitCode != 0) - throw new System.Exception($"Operation failed code: {process.ExitCode}"); - } - } - } -} \ No newline at end of file diff --git a/src/c#/GeneralUpdate.Common/Driver/DeleteDriverCommand.cs b/src/c#/GeneralUpdate.Common/Driver/DeleteDriverCommand.cs deleted file mode 100644 index 64c43198..00000000 --- a/src/c#/GeneralUpdate.Common/Driver/DeleteDriverCommand.cs +++ /dev/null @@ -1,23 +0,0 @@ -using System.Text; - -namespace GeneralUpdate.Common.Driver -{ - public class DeleteDriverCommand : IDriverCommand - { - private DriverInformation _information; - - public DeleteDriverCommand(DriverInformation information) => _information = information; - - public void Execute() - { - //Before installing the driver, delete the driver that has been installed on the local system. Otherwise, an exception may occur. - foreach (var driver in _information.Drivers) - { - var command = new StringBuilder("/c pnputil /delete-driver ") - .Append(driver) - .ToString(); - CommandExecutor.ExecuteCommand(command); - } - } - } -} \ No newline at end of file diff --git a/src/c#/GeneralUpdate.Common/Driver/DriverInformation.cs b/src/c#/GeneralUpdate.Common/Driver/DriverInformation.cs deleted file mode 100644 index 8a6a85ee..00000000 --- a/src/c#/GeneralUpdate.Common/Driver/DriverInformation.cs +++ /dev/null @@ -1,69 +0,0 @@ -using System.Collections.Generic; -using System.Linq; - -namespace GeneralUpdate.Common.Driver -{ - /// - /// Driver information object. - /// - public class DriverInformation - { - /// - /// Directory for storing the driver to be installed (Update the driver file in the package). - /// - public string InstallDirectory { get; private set; } - - /// - /// All driver backup directories. - /// - public string OutPutDirectory { get; private set; } - - /// - /// A collection of driver files to be backed up. - /// - public List Drivers { get; private set; } - - private DriverInformation() - { } - - public class Builder - { - private DriverInformation _information = new DriverInformation(); - - public Builder SetInstallDirectory(string installDirectory) - { - _information.InstallDirectory = installDirectory; - return this; - } - - public Builder SetOutPutDirectory(string outPutDirectory) - { - _information.OutPutDirectory = outPutDirectory; - return this; - } - - /// - /// Find the collection of driver names that need to be updated from the update package. - /// - /// - /// - public Builder SetDriverNames(List driverNames) - { - _information.Drivers = driverNames; - return this; - } - - public DriverInformation Build() - { - if (string.IsNullOrWhiteSpace(_information.InstallDirectory) || - string.IsNullOrWhiteSpace(_information.OutPutDirectory) || - !_information.Drivers.Any()) - { - throw new System.ArgumentException("Cannot create DriverInformation, not all fields are set."); - } - - return _information; - } - } - } -} \ No newline at end of file diff --git a/src/c#/GeneralUpdate.Common/Driver/DriverProcessor.cs b/src/c#/GeneralUpdate.Common/Driver/DriverProcessor.cs deleted file mode 100644 index a8d5f970..00000000 --- a/src/c#/GeneralUpdate.Common/Driver/DriverProcessor.cs +++ /dev/null @@ -1,36 +0,0 @@ -using System.Collections.Generic; -using System.Linq; - -namespace GeneralUpdate.Common.Driver -{ - /// - /// Handle all drive-related. - /// - public class DriverProcessor - { - private readonly List _commands = new List(); - - public void AddCommand(IDriverCommand command) - { - _commands.Add(command); - } - - /// - /// Execute all driver-related commands. - /// - public void ProcessCommands() - { - if (!_commands.Any()) return; - - /* - * This section describes the PnPUtil command. - * https://learn.microsoft.com/en-us/windows-hardware/drivers/devtest/pnputil-command-syntax - */ - foreach (var command in _commands) - { - command.Execute(); - } - _commands.Clear(); - } - } -} \ No newline at end of file diff --git a/src/c#/GeneralUpdate.Common/Driver/IDriverCommand.cs b/src/c#/GeneralUpdate.Common/Driver/IDriverCommand.cs deleted file mode 100644 index 4e10a51a..00000000 --- a/src/c#/GeneralUpdate.Common/Driver/IDriverCommand.cs +++ /dev/null @@ -1,7 +0,0 @@ -namespace GeneralUpdate.Common.Driver -{ - public interface IDriverCommand - { - void Execute(); - } -} \ No newline at end of file diff --git a/src/c#/GeneralUpdate.Common/Driver/InstallDriverCommand.cs b/src/c#/GeneralUpdate.Common/Driver/InstallDriverCommand.cs deleted file mode 100644 index dc0b8654..00000000 --- a/src/c#/GeneralUpdate.Common/Driver/InstallDriverCommand.cs +++ /dev/null @@ -1,44 +0,0 @@ -using System.IO; -using System.Text; - -namespace GeneralUpdate.Common.Driver -{ - /// - /// Install the new driver, and if the installation fails, the backup is automatically restored. - /// - public class InstallDriverCommand : IDriverCommand - { - private DriverInformation _information; - - public InstallDriverCommand(DriverInformation information) => _information = information; - - public void Execute() - { - try - { - foreach (var driver in _information.Drivers) - { - /* - * 1.It is best to ensure that the installed file is OEM INF, otherwise PnPUtil may indicate that non-OEM INF cannot perform the current operation. - * - * 2.Before installation, you need to delete the previously installed driver, otherwise PnPUtil will prompt 259 to exit the code. - * (On Windows, an ExitCode value of 259 (STILL_ACTIVE) means that the process is still running) - * If you do not remove the previous installation 259 prompt will give you a misleading impression of what is running. - */ - var path = Path.Combine(_information.InstallDirectory, Path.GetFileNameWithoutExtension(driver), driver); - var command = new StringBuilder("/c pnputil /add-driver ") - .Append(path) - .Append(" /install") - .ToString(); - CommandExecutor.ExecuteCommand(command); - } - } - catch (System.Exception ex) - { - //restore all the drivers in the backup directory. - new RestoreDriverCommand(_information).Execute(); - throw new System.Exception($"Failed to execute install command for {_information.InstallDirectory}", ex); - } - } - } -} \ No newline at end of file diff --git a/src/c#/GeneralUpdate.Common/Driver/RestoreDriverCommand.cs b/src/c#/GeneralUpdate.Common/Driver/RestoreDriverCommand.cs deleted file mode 100644 index 504b241f..00000000 --- a/src/c#/GeneralUpdate.Common/Driver/RestoreDriverCommand.cs +++ /dev/null @@ -1,25 +0,0 @@ -using System.IO; -using System.Text; - -namespace GeneralUpdate.Common.Driver -{ - public class RestoreDriverCommand : IDriverCommand - { - private DriverInformation _information; - - public RestoreDriverCommand(DriverInformation information) => _information = information; - - public void Execute() - { - foreach (var driver in _information.Drivers) - { - //Install all drivers in the specified directory, and if the installation fails, restore all the drivers in the backup directory. - var command = new StringBuilder("/c pnputil /add-driver ") - .Append(Path.Combine(_information.OutPutDirectory, Path.GetFileNameWithoutExtension(driver), driver)) - .Append(" /install") - .ToString(); - CommandExecutor.ExecuteCommand(command); - } - } - } -} \ No newline at end of file diff --git a/src/c#/GeneralUpdate.Common/Internal/Pipeline/PipelineBuilder.cs b/src/c#/GeneralUpdate.Common/Internal/Pipeline/PipelineBuilder.cs index 5d0fb84b..dac85eaf 100644 --- a/src/c#/GeneralUpdate.Common/Internal/Pipeline/PipelineBuilder.cs +++ b/src/c#/GeneralUpdate.Common/Internal/Pipeline/PipelineBuilder.cs @@ -11,13 +11,13 @@ public sealed class PipelineBuilder(IContext context = null) { private ImmutableStack _middlewareStack = ImmutableStack.Empty; - public PipelineBuilder Use(TMiddleware middleware) where TMiddleware : IMiddleware, new() + public PipelineBuilder UseMiddleware(TMiddleware middleware) where TMiddleware : IMiddleware, new() { _middlewareStack = _middlewareStack.Push(middleware); return this; } - public PipelineBuilder UseIf(TMiddleware middleware, Func condition) + public PipelineBuilder UseMiddlewareIf(TMiddleware middleware, Func condition) where TMiddleware : IMiddleware, new() { if (!condition()) return this; diff --git a/src/c#/GeneralUpdate.Core/Strategys/PlatformWindows/WindowsStrategy.cs b/src/c#/GeneralUpdate.Core/Strategys/PlatformWindows/WindowsStrategy.cs index 34bfcbfe..352619c0 100644 --- a/src/c#/GeneralUpdate.Core/Strategys/PlatformWindows/WindowsStrategy.cs +++ b/src/c#/GeneralUpdate.Core/Strategys/PlatformWindows/WindowsStrategy.cs @@ -57,8 +57,8 @@ public override void Execute() .Build(); var pipelineBuilder = new PipelineBuilder(context) - .UseMiddleware().UseMiddleware() - .UseMiddlewareIf(Packet.DriveEnabled) + .UseMiddleware() + .UseMiddleware() .UseMiddleware(); await pipelineBuilder.Build(); } From e830f113f55bb915194d13e7f81d4fe23fcf3abf Mon Sep 17 00:00:00 2001 From: justerzhu Date: Sat, 7 Sep 2024 12:34:25 +0800 Subject: [PATCH 03/33] feature: add bowl.jpeg --- imgs/bowl.jpeg | Bin 0 -> 102561 bytes 1 file changed, 0 insertions(+), 0 deletions(-) create mode 100644 imgs/bowl.jpeg diff --git a/imgs/bowl.jpeg b/imgs/bowl.jpeg new file mode 100644 index 0000000000000000000000000000000000000000..b099e2163483804fb316c5e3175e91529696f139 GIT binary patch literal 102561 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

{4_8fyCvcxW3#L6D4x5C0$<+C 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%0AM)@ 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�dJFgX%?Vuw$r>S4p=A6%;rP}mFfpM!f> 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 literal 0 HcmV?d00001 From c438a89aa7e7c00fd5ec6e4a18a1263a0119dea1 Mon Sep 17 00:00:00 2001 From: justerzhu Date: Tue, 10 Sep 2024 22:07:33 +0800 Subject: [PATCH 04/33] =?UTF-8?q?refactor:=20=E9=87=8D=E6=9E=84=E7=AE=A1?= =?UTF-8?q?=E9=81=93=E6=9C=BA=E5=88=B6?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../File/ComparisonResult.cs | 14 +++ .../File/GeneralFileManager.cs | 17 ++-- .../Internal/Bootstrap/AbstractBootstrap.cs | 98 +++++-------------- .../Internal/Bootstrap/UpdateOption.cs | 26 +++++ .../Internal/Pipeline/IMiddleware.cs | 2 +- .../Internal/Pipeline/PipelineBuilder.cs | 8 +- .../Internal/Pipeline/PipelineContext.cs | 32 ++++++ .../Internal/Strategy/AbstractStrategy.cs | 4 +- .../Internal/Strategy/IStrategy.cs | 3 +- .../Shared/Object/Packet.cs | 11 +-- 10 files changed, 116 insertions(+), 99 deletions(-) create mode 100644 src/c#/GeneralUpdate.Common/Internal/Pipeline/PipelineContext.cs diff --git a/src/c#/GeneralUpdate.Common/File/ComparisonResult.cs b/src/c#/GeneralUpdate.Common/File/ComparisonResult.cs index 6a8d165a..37ab0f8c 100644 --- a/src/c#/GeneralUpdate.Common/File/ComparisonResult.cs +++ b/src/c#/GeneralUpdate.Common/File/ComparisonResult.cs @@ -2,14 +2,28 @@ namespace GeneralUpdate.Common; +///

+/// Result of a comparison between two directories. +/// public class ComparisonResult { private readonly List _uniqueToA = new List(); private readonly List _uniqueToB = new List(); private readonly List _differentFiles = new List(); + /// + /// List of files that are unique to A. + /// public IReadOnlyList UniqueToA => _uniqueToA.AsReadOnly(); + + /// + /// List of files that are unique to B. + /// public IReadOnlyList UniqueToB => _uniqueToB.AsReadOnly(); + + /// + /// List of files that are different between A and B. + /// public IReadOnlyList DifferentFiles => _differentFiles.AsReadOnly(); public void AddUniqueToA(IEnumerable files) => _uniqueToA.AddRange(files); diff --git a/src/c#/GeneralUpdate.Common/File/GeneralFileManager.cs b/src/c#/GeneralUpdate.Common/File/GeneralFileManager.cs index cf41e9cd..e57f140d 100644 --- a/src/c#/GeneralUpdate.Common/File/GeneralFileManager.cs +++ b/src/c#/GeneralUpdate.Common/File/GeneralFileManager.cs @@ -63,6 +63,11 @@ public static void RemoveBlackFile(string fileName) _blackFiles.Remove(fileName); } + /// + /// Compare two directories. + /// + /// + /// public void CompareDirectories(string dirA, string dirB) { ComparisonResult = new ComparisonResult(); @@ -100,7 +105,7 @@ public static void CreateJson(string targetPath, T obj) File.WriteAllText(targetPath, jsonString); } - public static T GetJson(string path) + public static T? GetJson(string path) { if (File.Exists(path)) { @@ -115,13 +120,13 @@ public static T GetJson(string path) /// /// /// - public static string Serialize(object obj) + public static string Serialize(object? obj) { if (obj == null) return string.Empty; var json = JsonConvert.SerializeObject(obj); var bytes = Encoding.Default.GetBytes(json); - var base64str = Convert.ToBase64String(bytes); - return base64str; + var base64Str = Convert.ToBase64String(bytes); + return base64Str; } /// @@ -130,7 +135,7 @@ public static string Serialize(object obj) /// /// /// - public static T Deserialize(string str) + public static T? Deserialize(string str) { var obj = default(T); if (string.IsNullOrEmpty(str)) return obj; @@ -142,7 +147,7 @@ public static T Deserialize(string str) public static string GetTempDirectory(string name) { - var path = $"generalupdate_{DateTime.Now.ToString("yyyy-MM-dd")}_{name}"; + var path = $"generalupdate_{DateTime.Now:yyyy-MM-dd}_{name}"; var tempDir = Path.Combine(Path.GetTempPath(), path); if (!Directory.Exists(tempDir)) { diff --git a/src/c#/GeneralUpdate.Common/Internal/Bootstrap/AbstractBootstrap.cs b/src/c#/GeneralUpdate.Common/Internal/Bootstrap/AbstractBootstrap.cs index c388b248..f9a635f9 100644 --- a/src/c#/GeneralUpdate.Common/Internal/Bootstrap/AbstractBootstrap.cs +++ b/src/c#/GeneralUpdate.Common/Internal/Bootstrap/AbstractBootstrap.cs @@ -1,7 +1,7 @@ using System; using System.Collections.Concurrent; -using System.Collections.Generic; -using System.Diagnostics.Contracts; +using System.Diagnostics; +using System.Threading.Tasks; using GeneralUpdate.Common.Internal.Strategy; namespace GeneralUpdate.Common.Internal.Bootstrap @@ -10,74 +10,31 @@ public abstract class AbstractBootstrap where TBootstrap : AbstractBootstrap where TStrategy : IStrategy { - #region Private Members - private readonly ConcurrentDictionary _options; - private volatile Func _strategyFactory; - private IStrategy _strategy; - - #endregion Private Members - - #region Constructors - - protected internal AbstractBootstrap() => this._options = new ConcurrentDictionary(); - - #endregion Constructors - #region Methods + protected internal AbstractBootstrap() => _options = new ConcurrentDictionary(); /// /// Launch udpate. /// /// - public virtual TBootstrap LaunchAsync() - { - return (TBootstrap)this; - } - - #region Strategy - - protected IStrategy InitStrategy() - { - return _strategy; - } - - protected string GetPlatform() => _strategy.GetPlatform(); - - protected IStrategy ExecuteStrategy() - { - if (_strategy != null) _strategy.Execute(); - return _strategy; - } - - public virtual TBootstrap Validate() - { - if (this._strategyFactory == null) throw new InvalidOperationException("Strategy or strategy factory not set."); - return (TBootstrap)this; - } - - public virtual TBootstrap Strategy() where T : TStrategy, new() => this.StrategyFactory(() => new T()); + protected abstract TBootstrap Launch(); + + /// + /// Launch async udpate. + /// + /// + protected abstract Task LaunchAsync(); - public TBootstrap StrategyFactory(Func strategyFactory) - { - this._strategyFactory = strategyFactory; - return (TBootstrap)this; - } + protected abstract IStrategy InitStrategy(); - #endregion Strategy + protected abstract IStrategy ExecuteStrategy(); - #region Config option. + protected virtual TBootstrap Strategy() where T : TStrategy, new() => this.StrategyFactory(() => new T()); - /// - /// Files in the blacklist will skip the update. - /// - /// blacklist file name - /// - public virtual TBootstrap SetBlacklist(List files = null, List fileFormats = null) - { - return (TBootstrap)this; - } + protected abstract TBootstrap StrategyFactory(Func strategyFactory); + /// /// Setting update configuration. /// @@ -87,7 +44,7 @@ public virtual TBootstrap SetBlacklist(List files = null, List f /// public virtual TBootstrap Option(UpdateOption option, T value) { - Contract.Requires(option != null); + Debug.Assert(option != null); if (value == null) { this._options.TryRemove(option, out _); @@ -99,24 +56,13 @@ public virtual TBootstrap Option(UpdateOption option, T value) return (TBootstrap)this; } - public virtual T GetOption(UpdateOption option) + public virtual T? GetOption(UpdateOption option) { - try - { - if (_options == null || _options.Count == 0) return default(T); - var val = _options[option]; - if (val != null) return (T)val.GetValue(); - return default(T); - } - catch - { - return default(T); - } + Debug.Assert(option != null); + if (_options.Count == 0) return default(T); + var val = _options[option]; + if (val != null) return (T)val.GetValue(); + return default(T); } - - #endregion Config option. - - - #endregion Methods } } \ No newline at end of file diff --git a/src/c#/GeneralUpdate.Common/Internal/Bootstrap/UpdateOption.cs b/src/c#/GeneralUpdate.Common/Internal/Bootstrap/UpdateOption.cs index e0455d17..131b0b59 100644 --- a/src/c#/GeneralUpdate.Common/Internal/Bootstrap/UpdateOption.cs +++ b/src/c#/GeneralUpdate.Common/Internal/Bootstrap/UpdateOption.cs @@ -1,12 +1,38 @@ using System; using System.Collections.Generic; using System.Diagnostics.Contracts; +using System.Text; using System.Threading; namespace GeneralUpdate.Common.Internal.Bootstrap { public abstract class UpdateOption : AbstractConstant { + /// + /// Update the file format of the package. + /// + public static readonly UpdateOption Format = ValueOf("COMPRESSFORMAT"); + + /// + /// Compress encoding. + /// + public static readonly UpdateOption Encoding = ValueOf("COMPRESSENCODING"); + + /// + /// Main program name. + /// + public static readonly UpdateOption MainApp = ValueOf("MAINAPP"); + + /// + /// Timeout period (unit: second). If this parameter is not specified, the default timeout period is 30 seconds. + /// + public static readonly UpdateOption DownloadTimeOut = ValueOf("DOWNLOADTIMEOUT"); + + /// + /// Whether to enable the driver upgrade function. + /// + public static readonly UpdateOption Drive = ValueOf("DRIVE"); + private class UpdateOptionPool : ConstantPool { protected override IConstant NewConstant(int id, string name) => new UpdateOption(id, name); diff --git a/src/c#/GeneralUpdate.Common/Internal/Pipeline/IMiddleware.cs b/src/c#/GeneralUpdate.Common/Internal/Pipeline/IMiddleware.cs index 910fa130..4e26809e 100644 --- a/src/c#/GeneralUpdate.Common/Internal/Pipeline/IMiddleware.cs +++ b/src/c#/GeneralUpdate.Common/Internal/Pipeline/IMiddleware.cs @@ -7,6 +7,6 @@ namespace GeneralUpdate.Common.Internal.Pipeline /// public interface IMiddleware { - Task InvokeAsync(IContext context, IMiddleware middleware); + Task InvokeAsync(PipelineContext context, IMiddleware middleware); } } \ No newline at end of file diff --git a/src/c#/GeneralUpdate.Common/Internal/Pipeline/PipelineBuilder.cs b/src/c#/GeneralUpdate.Common/Internal/Pipeline/PipelineBuilder.cs index dac85eaf..fae0c90d 100644 --- a/src/c#/GeneralUpdate.Common/Internal/Pipeline/PipelineBuilder.cs +++ b/src/c#/GeneralUpdate.Common/Internal/Pipeline/PipelineBuilder.cs @@ -7,20 +7,22 @@ namespace GeneralUpdate.Common.Internal.Pipeline /// /// Pipeline builder. /// - public sealed class PipelineBuilder(IContext context = null) + public sealed class PipelineBuilder(PipelineContext context = null) { private ImmutableStack _middlewareStack = ImmutableStack.Empty; - public PipelineBuilder UseMiddleware(TMiddleware middleware) where TMiddleware : IMiddleware, new() + public PipelineBuilder UseMiddleware() where TMiddleware : IMiddleware, new() { + var middleware = new TMiddleware(); _middlewareStack = _middlewareStack.Push(middleware); return this; } - public PipelineBuilder UseMiddlewareIf(TMiddleware middleware, Func condition) + public PipelineBuilder UseMiddlewareIf(Func condition) where TMiddleware : IMiddleware, new() { if (!condition()) return this; + var middleware = new TMiddleware(); _middlewareStack = _middlewareStack.Push(middleware); return this; } diff --git a/src/c#/GeneralUpdate.Common/Internal/Pipeline/PipelineContext.cs b/src/c#/GeneralUpdate.Common/Internal/Pipeline/PipelineContext.cs new file mode 100644 index 00000000..8a85686f --- /dev/null +++ b/src/c#/GeneralUpdate.Common/Internal/Pipeline/PipelineContext.cs @@ -0,0 +1,32 @@ +using System; +using System.Collections.Concurrent; + +namespace GeneralUpdate.Common.Internal.Pipeline; + +public class PipelineContext +{ + private ConcurrentDictionary _context = new(); + + public TValue? Get(string key) + { + if (_context.TryGetValue(key, out var value)) + { + return value is TValue typedValue ? typedValue : default; + } + return default; + } + + public void Add(string key, TValue? value) + { + if (string.IsNullOrWhiteSpace(key)) + { + throw new ArgumentException("Key cannot be null or whitespace.", nameof(key)); + } + + _context[key] = value; + } + + public bool Remove(string key) => _context.TryRemove(key, out _); + + public bool ContainsKey(string key) => _context.ContainsKey(key); +} \ No newline at end of file diff --git a/src/c#/GeneralUpdate.Common/Internal/Strategy/AbstractStrategy.cs b/src/c#/GeneralUpdate.Common/Internal/Strategy/AbstractStrategy.cs index 527325d6..344a7fde 100644 --- a/src/c#/GeneralUpdate.Common/Internal/Strategy/AbstractStrategy.cs +++ b/src/c#/GeneralUpdate.Common/Internal/Strategy/AbstractStrategy.cs @@ -9,8 +9,10 @@ public abstract class AbstractStrategy : IStrategy protected const string PATCHS = "patchs"; public virtual void Execute() => throw new NotImplementedException(); + + public virtual Task ExecuteAsync() => throw new NotImplementedException(); - public virtual bool StartApp(string appName, int appType) => throw new NotImplementedException(); + public virtual void StartApp(string appName, int appType) => throw new NotImplementedException(); public virtual string GetPlatform() => throw new NotImplementedException(); diff --git a/src/c#/GeneralUpdate.Common/Internal/Strategy/IStrategy.cs b/src/c#/GeneralUpdate.Common/Internal/Strategy/IStrategy.cs index 5295a586..9dbd5754 100644 --- a/src/c#/GeneralUpdate.Common/Internal/Strategy/IStrategy.cs +++ b/src/c#/GeneralUpdate.Common/Internal/Strategy/IStrategy.cs @@ -18,7 +18,7 @@ public interface IStrategy /// /// /// - bool StartApp(string appName, int appType); + void StartApp(string appName, int appType); /// /// Get the platform for the current strategy. @@ -34,7 +34,6 @@ public interface IStrategy /// /// Create a strategy. /// - /// Abstraction for updating package information. void Create(T parameter) where T : class; } } \ No newline at end of file diff --git a/src/c#/GeneralUpdate.Common/Shared/Object/Packet.cs b/src/c#/GeneralUpdate.Common/Shared/Object/Packet.cs index a2edd846..60937814 100644 --- a/src/c#/GeneralUpdate.Common/Shared/Object/Packet.cs +++ b/src/c#/GeneralUpdate.Common/Shared/Object/Packet.cs @@ -116,12 +116,7 @@ public Packet(string mainUpdateUrl, int appType, string updateUrl, string appNam /// Configuration parameters for upgrading the terminal program. /// public string ProcessBase64 { get; set; } - - /// - /// The platform to which the current strategy belongs. - /// - public string Platform { get; set; } - + /// /// Files in the blacklist will skip the update. /// @@ -137,9 +132,5 @@ public Packet(string mainUpdateUrl, int appType, string updateUrl, string appNam /// public bool DriveEnabled { get; set; } - /// - /// Whether open note function, if you want to start needs to be synchronized to deploy 'GeneralUpdate. SystemService' service. - /// - public bool WillMessageEnabled { get; set; } } } \ No newline at end of file From 2b5d8cd8fe7e6932f98a7dcb67cbbc84084433c7 Mon Sep 17 00:00:00 2001 From: justerzhu Date: Thu, 12 Sep 2024 23:59:02 +0800 Subject: [PATCH 05/33] =?UTF-8?q?remove:=20=E7=A7=BB=E9=99=A4=E5=86=97?= =?UTF-8?q?=E4=BD=99=E4=BB=A3=E7=A0=81?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../Shared/Object/Configinfo.cs | 2 +- .../Shared/Object/Entity.cs | 31 ------------------- .../Shared/Object/Packet.cs | 4 +-- .../Shared/Object/ParamsOSS.cs | 2 +- .../Shared/Object/ProcessInfo.cs | 2 +- .../Shared/Object/VersionInfo.cs | 3 +- 6 files changed, 5 insertions(+), 39 deletions(-) delete mode 100644 src/c#/GeneralUpdate.Common/Shared/Object/Entity.cs diff --git a/src/c#/GeneralUpdate.Common/Shared/Object/Configinfo.cs b/src/c#/GeneralUpdate.Common/Shared/Object/Configinfo.cs index 1b7f541d..116145e3 100644 --- a/src/c#/GeneralUpdate.Common/Shared/Object/Configinfo.cs +++ b/src/c#/GeneralUpdate.Common/Shared/Object/Configinfo.cs @@ -3,7 +3,7 @@ namespace GeneralUpdate.Common.Shared.Object { - public class Configinfo : Entity + public class Configinfo { public Configinfo() { } diff --git a/src/c#/GeneralUpdate.Common/Shared/Object/Entity.cs b/src/c#/GeneralUpdate.Common/Shared/Object/Entity.cs deleted file mode 100644 index c40237f6..00000000 --- a/src/c#/GeneralUpdate.Common/Shared/Object/Entity.cs +++ /dev/null @@ -1,31 +0,0 @@ -using System; -using System.Text.RegularExpressions; - -namespace GeneralUpdate.Common.Shared.Object -{ - public class Entity - { - /// - /// 委派标识 - /// - protected string Identity { get; set; } - - public string ID - { - get { return this.Identity; } - protected set { this.Identity = value; } - } - - protected bool IsURL(string url) - { - string check = @"((http|ftp|https)://)(([a-zA-Z0-9\._-]+\.[a-zA-Z]{2,6})|([0-9]{1,3}\.[0-9]{1,3}\.[0-9]{1,3}\.[0-9]{1,3}))(:[0-9]{1,4})*(/[a-zA-Z0-9\&%_\./-~-]*)?"; - var regex = new Regex(check); - return regex.IsMatch(url); - } - - protected bool IsVersion(string version) - { - return Version.TryParse(version, out var ver); - } - } -} \ No newline at end of file diff --git a/src/c#/GeneralUpdate.Common/Shared/Object/Packet.cs b/src/c#/GeneralUpdate.Common/Shared/Object/Packet.cs index 60937814..5addbe86 100644 --- a/src/c#/GeneralUpdate.Common/Shared/Object/Packet.cs +++ b/src/c#/GeneralUpdate.Common/Shared/Object/Packet.cs @@ -4,16 +4,14 @@ namespace GeneralUpdate.Common.Shared.Object { - public class Packet : Entity + public class Packet { public Packet() { } public Packet(string mainUpdateUrl, int appType, string updateUrl, string appName, string mainAppName, string format, bool isUpdate, string updateLogUrl, Encoding encoding, int downloadTimeOut, string appSecretKey, string tempPath) { - if (!IsURL(mainUpdateUrl)) throw new Exception($"Illegal url {nameof(mainUpdateUrl)}"); MainUpdateUrl = mainUpdateUrl ?? throw new ArgumentNullException(nameof(MainUpdateUrl)); - if (!IsURL(updateUrl)) throw new Exception($"Illegal url {nameof(UpdateUrl)}"); UpdateUrl = updateUrl ?? throw new ArgumentNullException(nameof(updateUrl)); UpdateLogUrl = updateLogUrl ?? throw new ArgumentNullException(nameof(updateLogUrl)); AppType = appType; diff --git a/src/c#/GeneralUpdate.Common/Shared/Object/ParamsOSS.cs b/src/c#/GeneralUpdate.Common/Shared/Object/ParamsOSS.cs index 3f861905..e8675c17 100644 --- a/src/c#/GeneralUpdate.Common/Shared/Object/ParamsOSS.cs +++ b/src/c#/GeneralUpdate.Common/Shared/Object/ParamsOSS.cs @@ -2,7 +2,7 @@ namespace GeneralUpdate.Common.Shared.Object { - public class ParamsOSS : Entity + public class ParamsOSS { public string Url { get; set; } diff --git a/src/c#/GeneralUpdate.Common/Shared/Object/ProcessInfo.cs b/src/c#/GeneralUpdate.Common/Shared/Object/ProcessInfo.cs index 00062bdd..8e20661a 100644 --- a/src/c#/GeneralUpdate.Common/Shared/Object/ProcessInfo.cs +++ b/src/c#/GeneralUpdate.Common/Shared/Object/ProcessInfo.cs @@ -5,7 +5,7 @@ namespace GeneralUpdate.Common.Shared.Object { - public class ProcessInfo : Entity + public class ProcessInfo { public ProcessInfo() { } diff --git a/src/c#/GeneralUpdate.Common/Shared/Object/VersionInfo.cs b/src/c#/GeneralUpdate.Common/Shared/Object/VersionInfo.cs index e118cbbf..ebe6bb5d 100644 --- a/src/c#/GeneralUpdate.Common/Shared/Object/VersionInfo.cs +++ b/src/c#/GeneralUpdate.Common/Shared/Object/VersionInfo.cs @@ -2,7 +2,7 @@ namespace GeneralUpdate.Common.Shared.Object { - public class VersionInfo : Entity + public class VersionInfo { public VersionInfo() { } @@ -14,7 +14,6 @@ public VersionInfo(long pubTime, string name, string hash, string version, strin Hash = hash ?? throw new ArgumentNullException(nameof(hash)); Version = version ?? throw new ArgumentNullException(nameof(version)); Url = url ?? throw new ArgumentNullException(nameof(Url)); - if (!IsURL(Url)) throw new Exception($"Illegal url {nameof(Url)}"); } /// From 10d6099014a74c845efc5a292c07eecdce1057d7 Mon Sep 17 00:00:00 2001 From: justerzhu Date: Fri, 20 Sep 2024 20:26:43 +0800 Subject: [PATCH 06/33] refactor: refactor client core --- src/c#/GeneralUpdate.Bowl/Bowl.cs | 3 + .../Strategys/LinuxStrategy.cs | 18 +- src/c#/GeneralUpdate.Client/MySample.cs | 67 ++-- .../CustomAwaiter/.gitkeep | 0 .../Differential/.gitkeep | 0 .../Differential/Binary/.gitkeep | 0 .../Differential/ContentProvider/.gitkeep | 0 .../Differential/GStream/.gitkeep | 0 .../GeneralUpdate.ClientCore/Domain/.gitkeep | 0 .../Domain/DTO/Assembler/.gitkeep | 0 .../Domain/Entity/Assembler/.gitkeep | 0 .../Domain/Enum/.gitkeep | 0 .../Domain/PO/.gitkeep | 0 .../Domain/PO/Assembler/.gitkeep | 0 .../Domain/Service/.gitkeep | 0 .../Domain/VO/.gitkeep | 0 .../Download/.gitkeep | 0 .../Events/CommonArgs/.gitkeep | 0 .../Events/MultiEventArgs/.gitkeep | 0 .../Exceptions/.gitkeep | 0 .../Exceptions/CustomArgs/.gitkeep | 0 .../Exceptions/CustomException/.gitkeep | 0 .../GeneralClientBootstrap.cs | 320 ++++++++++-------- .../GeneralClientOSS.cs | 17 +- .../GeneralUpdate.ClientCore.csproj | 149 +------- .../HashAlgorithms/.gitkeep | 0 .../Internal/ExceptionEventArgs.cs | 15 + .../Pipeline/HashMiddleware.cs | 32 ++ .../Pipeline/PatchMiddleware.cs | 20 ++ .../Pipeline/ZipMiddleware.cs | 39 +++ .../Pipelines/Attributes/.gitkeep | 0 .../Pipelines/Context/.gitkeep | 0 .../Pipelines/Middleware/.gitkeep | 0 .../Pipelines/MiddlewareResolver/.gitkeep | 0 .../Pipelines/Pipeline/.gitkeep | 0 .../Strategys/LinuxStrategy.cs | 8 + .../Strategys/PlatformLinux/.gitkeep | 0 .../Strategys/PlatformWindows/.gitkeep | 0 .../Strategys/WindowsStrategy.cs | 103 ++++++ .../WillMessage/.gitkeep | 0 .../ZipFactory/Events/.gitkeep | 0 .../ZipFactory/Factory/.gitkeep | 0 .../Download/DownloadManager.cs | 31 +- .../Download/DownloadTask.cs | 39 +-- .../File/GeneralFileManager.cs | 49 +-- .../GeneralUpdate.Common.csproj | 6 +- .../Internal/Bootstrap/AbstractBootstrap.cs | 29 +- .../Internal/Event/EventManager.cs | 139 ++------ .../Internal/Pipeline/IContext.cs | 10 - .../Internal/Pipeline/IMiddleware.cs | 2 +- .../Internal/Pipeline/PipelineBuilder.cs | 10 +- .../Internal/Strategy/AbstractStrategy.cs | 26 +- .../Internal/Strategy/IStrategy.cs | 11 +- .../Object/Assembler/ProcessAssembler.cs | 60 ---- .../Shared/Object/Packet.cs | 5 +- .../Shared/Service/VersionService.cs | 4 +- .../Bootstrap/AbstractBootstrap.cs | 28 +- src/c#/GeneralUpdate.Upgrad/Program.cs | 6 +- 58 files changed, 580 insertions(+), 666 deletions(-) delete mode 100644 src/c#/GeneralUpdate.ClientCore/CustomAwaiter/.gitkeep delete mode 100644 src/c#/GeneralUpdate.ClientCore/Differential/.gitkeep delete mode 100644 src/c#/GeneralUpdate.ClientCore/Differential/Binary/.gitkeep delete mode 100644 src/c#/GeneralUpdate.ClientCore/Differential/ContentProvider/.gitkeep delete mode 100644 src/c#/GeneralUpdate.ClientCore/Differential/GStream/.gitkeep delete mode 100644 src/c#/GeneralUpdate.ClientCore/Domain/.gitkeep delete mode 100644 src/c#/GeneralUpdate.ClientCore/Domain/DTO/Assembler/.gitkeep delete mode 100644 src/c#/GeneralUpdate.ClientCore/Domain/Entity/Assembler/.gitkeep delete mode 100644 src/c#/GeneralUpdate.ClientCore/Domain/Enum/.gitkeep delete mode 100644 src/c#/GeneralUpdate.ClientCore/Domain/PO/.gitkeep delete mode 100644 src/c#/GeneralUpdate.ClientCore/Domain/PO/Assembler/.gitkeep delete mode 100644 src/c#/GeneralUpdate.ClientCore/Domain/Service/.gitkeep delete mode 100644 src/c#/GeneralUpdate.ClientCore/Domain/VO/.gitkeep delete mode 100644 src/c#/GeneralUpdate.ClientCore/Download/.gitkeep delete mode 100644 src/c#/GeneralUpdate.ClientCore/Events/CommonArgs/.gitkeep delete mode 100644 src/c#/GeneralUpdate.ClientCore/Events/MultiEventArgs/.gitkeep delete mode 100644 src/c#/GeneralUpdate.ClientCore/Exceptions/.gitkeep delete mode 100644 src/c#/GeneralUpdate.ClientCore/Exceptions/CustomArgs/.gitkeep delete mode 100644 src/c#/GeneralUpdate.ClientCore/Exceptions/CustomException/.gitkeep delete mode 100644 src/c#/GeneralUpdate.ClientCore/HashAlgorithms/.gitkeep create mode 100644 src/c#/GeneralUpdate.ClientCore/Internal/ExceptionEventArgs.cs create mode 100644 src/c#/GeneralUpdate.ClientCore/Pipeline/HashMiddleware.cs create mode 100644 src/c#/GeneralUpdate.ClientCore/Pipeline/PatchMiddleware.cs create mode 100644 src/c#/GeneralUpdate.ClientCore/Pipeline/ZipMiddleware.cs delete mode 100644 src/c#/GeneralUpdate.ClientCore/Pipelines/Attributes/.gitkeep delete mode 100644 src/c#/GeneralUpdate.ClientCore/Pipelines/Context/.gitkeep delete mode 100644 src/c#/GeneralUpdate.ClientCore/Pipelines/Middleware/.gitkeep delete mode 100644 src/c#/GeneralUpdate.ClientCore/Pipelines/MiddlewareResolver/.gitkeep delete mode 100644 src/c#/GeneralUpdate.ClientCore/Pipelines/Pipeline/.gitkeep create mode 100644 src/c#/GeneralUpdate.ClientCore/Strategys/LinuxStrategy.cs delete mode 100644 src/c#/GeneralUpdate.ClientCore/Strategys/PlatformLinux/.gitkeep delete mode 100644 src/c#/GeneralUpdate.ClientCore/Strategys/PlatformWindows/.gitkeep create mode 100644 src/c#/GeneralUpdate.ClientCore/Strategys/WindowsStrategy.cs delete mode 100644 src/c#/GeneralUpdate.ClientCore/WillMessage/.gitkeep delete mode 100644 src/c#/GeneralUpdate.ClientCore/ZipFactory/Events/.gitkeep delete mode 100644 src/c#/GeneralUpdate.ClientCore/ZipFactory/Factory/.gitkeep delete mode 100644 src/c#/GeneralUpdate.Common/Internal/Pipeline/IContext.cs delete mode 100644 src/c#/GeneralUpdate.Common/Shared/Object/Assembler/ProcessAssembler.cs diff --git a/src/c#/GeneralUpdate.Bowl/Bowl.cs b/src/c#/GeneralUpdate.Bowl/Bowl.cs index fc828ebb..fd0fa2a3 100644 --- a/src/c#/GeneralUpdate.Bowl/Bowl.cs +++ b/src/c#/GeneralUpdate.Bowl/Bowl.cs @@ -4,6 +4,9 @@ namespace GeneralUpdate.Bowl; +/// +/// Surveillance Main Program. +/// public class Bowl { private IStrategy _strategy; diff --git a/src/c#/GeneralUpdate.Bowl/Strategys/LinuxStrategy.cs b/src/c#/GeneralUpdate.Bowl/Strategys/LinuxStrategy.cs index 64f300c5..2829493e 100644 --- a/src/c#/GeneralUpdate.Bowl/Strategys/LinuxStrategy.cs +++ b/src/c#/GeneralUpdate.Bowl/Strategys/LinuxStrategy.cs @@ -8,14 +8,16 @@ namespace GeneralUpdate.Bowl.Strategys; public class LinuxStrategy : AbstractStrategy { - /*procdump-3.3.0-0.cm2.x86_64.rpm: - 适合系统:此RPM包可能适用于基于CentOS或RHEL的某些派生版本,具体来说是CM2版本。CM2通常指的是ClearOS 7.x或类似的社区维护版本。 - procdump-3.3.0-0.el8.x86_64.rpm: - 适合系统:此RPM包适用于Red Hat Enterprise Linux 8 (RHEL 8)、CentOS 8及其他基于RHEL 8的发行版。 - procdump_3.3.0_amd64.deb: - 适合系统:此DEB包适用于Debian及其衍生发行版,如Ubuntu,适用于64位系统(amd64架构)。*/ + /*procdump-3.3.0-0.cm2.x86_64.rpm: + Compatible Systems: This RPM package may be suitable for certain CentOS or RHEL-based derivatives, specifically the CM2 version. CM2 typically refers to ClearOS 7.x or similar community-maintained versions. + + procdump-3.3.0-0.el8.x86_64.rpm: + Compatible Systems: This RPM package is suitable for Red Hat Enterprise Linux 8 (RHEL 8), CentOS 8, and other RHEL 8-based distributions. + + procdump_3.3.0_amd64.deb: + Compatible Systems: This DEB package is suitable for Debian and its derivatives, such as Ubuntu, for 64-bit systems (amd64 architecture).*/ - private IReadOnlyList procdump_amd64 = new List { "Ubuntu", "Debian" }; + private IReadOnlyList _rocdumpAmd64 = new List { "Ubuntu", "Debian" }; private IReadOnlyList procdump_el8_x86_64 = new List { "Red Hat", "CentOS", "Fedora" }; private IReadOnlyList procdump_cm2_x86_64 = new List { "ClearOS" }; @@ -66,7 +68,7 @@ private string GetPacketName() { string packageFileName = string.Empty; LinuxSystem system = GetSystem(); - if (procdump_amd64.Contains(system.Name)) + if (_rocdumpAmd64.Contains(system.Name)) { packageFileName = $"procdump_3.3.0_amd64.deb"; } diff --git a/src/c#/GeneralUpdate.Client/MySample.cs b/src/c#/GeneralUpdate.Client/MySample.cs index 618aba36..ef63fcc8 100644 --- a/src/c#/GeneralUpdate.Client/MySample.cs +++ b/src/c#/GeneralUpdate.Client/MySample.cs @@ -1,13 +1,13 @@ using GeneralUpdate.ClientCore; -using GeneralUpdate.Core.Bootstrap; -using GeneralUpdate.Core.Domain.Entity; -using GeneralUpdate.Core.Domain.Enum; -using GeneralUpdate.Core.Events.CommonArgs; -using GeneralUpdate.Core.Events.MultiEventArgs; -using GeneralUpdate.Core.Strategys.PlatformWindows; using GeneralUpdate.Differential; using System.Diagnostics; using System.Text; +using GeneralUpdate.ClientCore.Internal; +using GeneralUpdate.ClientCore.Strategys; +using GeneralUpdate.Common.Download; +using GeneralUpdate.Common.Internal.Bootstrap; +using GeneralUpdate.Common.Shared.Object; +using VersionInfo = GeneralUpdate.Core.Domain.Entity.VersionInfo; namespace GeneralUpdate.Client { @@ -51,35 +51,32 @@ public async Task Upgrade() //generalClientBootstrap.Config(baseUrl, "B8A7FADD-386C-46B0-B283-C9F963420C7C"). var configinfo = GetWindowsConfigInfo(); var generalClientBootstrap = await new GeneralClientBootstrap() - //单个或多个更新包下载通知事件 - .AddListenerMultiDownloadProgress(OnMultiDownloadProgressChanged) - //单个或多个更新包下载速度、剩余下载事件、当前下载版本信息通知事件 - .AddListenerMultiDownloadStatistics(OnMultiDownloadStatistics) - //单个或多个更新包下载完成 - .AddListenerMultiDownloadCompleted(OnMultiDownloadCompleted) - //完成所有的下载任务通知 - .AddListenerMultiAllDownloadCompleted(OnMultiAllDownloadCompleted) - //下载过程出现的异常通知 - .AddListenerMultiDownloadError(OnMultiDownloadError) - //整个更新过程出现的任何问题都会通过这个事件通知 - .AddListenerException(OnException) - .Config(configinfo) - .Option(UpdateOption.DownloadTimeOut, 60) - .Option(UpdateOption.Encoding, Encoding.Default) - .Option(UpdateOption.Format, Format.ZIP) - //开启驱动更新 - //.Option(UpdateOption.Drive, true) - //开启遗言功能,需要部署GeneralUpdate.SystemService Windows服务。 - .Option(UpdateOption.WillMessage, true) - .Strategy() - //注入一个func让用户决定是否跳过本次更新,如果是强制更新则不生效 - //.SetCustomSkipOption(ShowCustomOption) - //注入一个自定义方法集合,该集合会在更新启动前执行。执行自定义方法列表如果出现任何异常,将通过异常订阅通知。(推荐在更新之前检查当前软件环境) - //.AddCustomOption(new List>() { () => Check1(), () => Check2() }) - //默认黑名单文件: { "Newtonsoft.Json.dll" } 默认黑名单文件扩展名: { ".patch", ".7z", ".zip", ".rar", ".tar" , ".json" } - //如果不需要扩展,需要重新传入黑名单集合来覆盖。 - //.SetBlacklist(GetBlackFiles(), GetBlackFormats()) - .LaunchTaskAsync(); + //单个或多个更新包下载通知事件 + .AddListenerMultiDownloadProgress(OnMultiDownloadProgressChanged) + //单个或多个更新包下载速度、剩余下载事件、当前下载版本信息通知事件 + .AddListenerMultiDownloadStatistics(OnMultiDownloadStatistics) + //单个或多个更新包下载完成 + .AddListenerMultiDownloadCompleted(OnMultiDownloadCompleted) + //完成所有的下载任务通知 + .AddListenerMultiAllDownloadCompleted(OnMultiAllDownloadCompleted) + //下载过程出现的异常通知 + .AddListenerMultiDownloadError(OnMultiDownloadError) + //整个更新过程出现的任何问题都会通过这个事件通知 + .AddListenerException(OnException) + .SetConfig(configinfo) + .Option(UpdateOption.DownloadTimeOut, 60) + .Option(UpdateOption.Encoding, Encoding.Default) + .Option(UpdateOption.Format, Format.ZIP) + //开启驱动更新 + //.Option(UpdateOption.Drive, true) + //注入一个func让用户决定是否跳过本次更新,如果是强制更新则不生效 + //.SetCustomSkipOption(ShowCustomOption) + //注入一个自定义方法集合,该集合会在更新启动前执行。执行自定义方法列表如果出现任何异常,将通过异常订阅通知。(推荐在更新之前检查当前软件环境) + //.AddCustomOption(new List>() { () => Check1(), () => Check2() }) + //默认黑名单文件: { "Newtonsoft.Json.dll" } 默认黑名单文件扩展名: { ".patch", ".7z", ".zip", ".rar", ".tar" , ".json" } + //如果不需要扩展,需要重新传入黑名单集合来覆盖。 + //.SetBlacklist(GetBlackFiles(), GetBlackFormats()) + .LaunchAsync(); } private bool Check1() => true; diff --git a/src/c#/GeneralUpdate.ClientCore/CustomAwaiter/.gitkeep b/src/c#/GeneralUpdate.ClientCore/CustomAwaiter/.gitkeep deleted file mode 100644 index e69de29b..00000000 diff --git a/src/c#/GeneralUpdate.ClientCore/Differential/.gitkeep b/src/c#/GeneralUpdate.ClientCore/Differential/.gitkeep deleted file mode 100644 index e69de29b..00000000 diff --git a/src/c#/GeneralUpdate.ClientCore/Differential/Binary/.gitkeep b/src/c#/GeneralUpdate.ClientCore/Differential/Binary/.gitkeep deleted file mode 100644 index e69de29b..00000000 diff --git a/src/c#/GeneralUpdate.ClientCore/Differential/ContentProvider/.gitkeep b/src/c#/GeneralUpdate.ClientCore/Differential/ContentProvider/.gitkeep deleted file mode 100644 index e69de29b..00000000 diff --git a/src/c#/GeneralUpdate.ClientCore/Differential/GStream/.gitkeep b/src/c#/GeneralUpdate.ClientCore/Differential/GStream/.gitkeep deleted file mode 100644 index e69de29b..00000000 diff --git a/src/c#/GeneralUpdate.ClientCore/Domain/.gitkeep b/src/c#/GeneralUpdate.ClientCore/Domain/.gitkeep deleted file mode 100644 index e69de29b..00000000 diff --git a/src/c#/GeneralUpdate.ClientCore/Domain/DTO/Assembler/.gitkeep b/src/c#/GeneralUpdate.ClientCore/Domain/DTO/Assembler/.gitkeep deleted file mode 100644 index e69de29b..00000000 diff --git a/src/c#/GeneralUpdate.ClientCore/Domain/Entity/Assembler/.gitkeep b/src/c#/GeneralUpdate.ClientCore/Domain/Entity/Assembler/.gitkeep deleted file mode 100644 index e69de29b..00000000 diff --git a/src/c#/GeneralUpdate.ClientCore/Domain/Enum/.gitkeep b/src/c#/GeneralUpdate.ClientCore/Domain/Enum/.gitkeep deleted file mode 100644 index e69de29b..00000000 diff --git a/src/c#/GeneralUpdate.ClientCore/Domain/PO/.gitkeep b/src/c#/GeneralUpdate.ClientCore/Domain/PO/.gitkeep deleted file mode 100644 index e69de29b..00000000 diff --git a/src/c#/GeneralUpdate.ClientCore/Domain/PO/Assembler/.gitkeep b/src/c#/GeneralUpdate.ClientCore/Domain/PO/Assembler/.gitkeep deleted file mode 100644 index e69de29b..00000000 diff --git a/src/c#/GeneralUpdate.ClientCore/Domain/Service/.gitkeep b/src/c#/GeneralUpdate.ClientCore/Domain/Service/.gitkeep deleted file mode 100644 index e69de29b..00000000 diff --git a/src/c#/GeneralUpdate.ClientCore/Domain/VO/.gitkeep b/src/c#/GeneralUpdate.ClientCore/Domain/VO/.gitkeep deleted file mode 100644 index e69de29b..00000000 diff --git a/src/c#/GeneralUpdate.ClientCore/Download/.gitkeep b/src/c#/GeneralUpdate.ClientCore/Download/.gitkeep deleted file mode 100644 index e69de29b..00000000 diff --git a/src/c#/GeneralUpdate.ClientCore/Events/CommonArgs/.gitkeep b/src/c#/GeneralUpdate.ClientCore/Events/CommonArgs/.gitkeep deleted file mode 100644 index e69de29b..00000000 diff --git a/src/c#/GeneralUpdate.ClientCore/Events/MultiEventArgs/.gitkeep b/src/c#/GeneralUpdate.ClientCore/Events/MultiEventArgs/.gitkeep deleted file mode 100644 index e69de29b..00000000 diff --git a/src/c#/GeneralUpdate.ClientCore/Exceptions/.gitkeep b/src/c#/GeneralUpdate.ClientCore/Exceptions/.gitkeep deleted file mode 100644 index e69de29b..00000000 diff --git a/src/c#/GeneralUpdate.ClientCore/Exceptions/CustomArgs/.gitkeep b/src/c#/GeneralUpdate.ClientCore/Exceptions/CustomArgs/.gitkeep deleted file mode 100644 index e69de29b..00000000 diff --git a/src/c#/GeneralUpdate.ClientCore/Exceptions/CustomException/.gitkeep b/src/c#/GeneralUpdate.ClientCore/Exceptions/CustomException/.gitkeep deleted file mode 100644 index e69de29b..00000000 diff --git a/src/c#/GeneralUpdate.ClientCore/GeneralClientBootstrap.cs b/src/c#/GeneralUpdate.ClientCore/GeneralClientBootstrap.cs index 11bded52..fb75e554 100644 --- a/src/c#/GeneralUpdate.ClientCore/GeneralClientBootstrap.cs +++ b/src/c#/GeneralUpdate.ClientCore/GeneralClientBootstrap.cs @@ -1,20 +1,23 @@ -using GeneralUpdate.Core.Bootstrap; -using GeneralUpdate.Core.Domain.DTO.Assembler; -using GeneralUpdate.Core.Domain.Entity; -using GeneralUpdate.Core.Domain.Entity.Assembler; -using GeneralUpdate.Core.Domain.Enum; -using GeneralUpdate.Core.Domain.Service; -using GeneralUpdate.Core.Exceptions.CustomArgs; using GeneralUpdate.Core.Exceptions.CustomException; -using GeneralUpdate.Core.Strategys; using System; using System.Collections.Generic; using System.Diagnostics; +using System.Diagnostics.Contracts; using System.IO; using System.Linq; using System.Reflection; +using System.Runtime.InteropServices; using System.Security; +using System.Text.Json; using System.Threading.Tasks; +using GeneralUpdate.ClientCore.Internal; +using GeneralUpdate.ClientCore.Strategys; +using GeneralUpdate.Common.Download; +using GeneralUpdate.Common.Internal.Bootstrap; +using GeneralUpdate.Common.Internal.Event; +using GeneralUpdate.Common.Internal.Strategy; +using GeneralUpdate.Common.Shared.Object; +using GeneralUpdate.Common.Shared.Service; namespace GeneralUpdate.ClientCore { @@ -23,59 +26,42 @@ namespace GeneralUpdate.ClientCore /// public class GeneralClientBootstrap : AbstractBootstrap { + #region Private Members + + private IStrategy _strategy; + private Func _customSkipOption; - private Func> _customSkipTaskOption; - - private List> _customOptions; - private List>> _customTaskOptions; - - public GeneralClientBootstrap() : base() - { - _customOptions = new List>(); - _customTaskOptions = new List>>(); - } - - #region Public Methods + + private List> _customOptions = new List>(); + + #endregion + #region Public Properties + /// - /// Start the update. + /// All update actions of the core object for automatic upgrades will be related to the Packet object. /// - /// - public override GeneralClientBootstrap LaunchAsync() - { - Task.Run(() => BaseLaunch()); - return this; - } + private Packet Packet { get; set; } + + #endregion + #region Public Methods + /// - /// Start the update. + /// Main function for booting the update startup. /// /// - public Task LaunchTaskAsync() => BaseLaunch(); - - private async Task BaseLaunch() + public override async Task LaunchAsync() { - ClearEnvironmentVariable(); - await ExecuteCustomOptions(); - var versionService = new VersionService(); - var mainResp = await versionService.ValidationVersion(Packet.MainUpdateUrl); - var upgradeResp = await versionService.ValidationVersion(Packet.UpdateUrl); - //if (!CheckWillMessage()) return this; - Packet.IsUpgradeUpdate = upgradeResp.Body.IsUpdate; - Packet.IsMainUpdate = mainResp.Body.IsUpdate; - //No need to update, return directly. - if ((!Packet.IsMainUpdate) && (!Packet.IsUpgradeUpdate)) return this; - //If the main program needs to be forced to update, the skip will not take effect. - var isForcibly = mainResp.Body.IsForcibly || upgradeResp.Body.IsForcibly; - if (await IsSkip(isForcibly)) return this; - Packet.UpdateVersions = VersionAssembler.ToEntitys(upgradeResp.Body.Versions); - Packet.LastVersion = Packet.UpdateVersions.Last().Version; - var processInfo = new ProcessInfo(Packet.MainAppName, Packet.InstallPath, - Packet.ClientVersion, Packet.LastVersion, Packet.UpdateLogUrl, - Packet.Encoding, Packet.Format, Packet.DownloadTimeOut, - Packet.AppSecretKey, mainResp.Body.Versions); - Packet.ProcessBase64 = ProcessAssembler.ToBase64(processInfo); - return base.LaunchAsync(); + ExecuteCustomOptions(); + await InitializeData(); + var manager = new DownloadManager(Packet.InstallPath, Packet.Format, 30); + foreach (var versionInfo in Packet.UpdateVersions) + { + manager.Add(new DownloadTask(manager, versionInfo)); + } + await manager.LaunchTasksAsync(); + return this; } /// @@ -85,31 +71,24 @@ private async Task BaseLaunch() /// The updater name does not need to contain an extension. /// /// Parameter initialization is abnormal. - public GeneralClientBootstrap Config(string url, string appSecretKey, string appName = "GeneralUpdate.Upgrade") + public GeneralClientBootstrap SetConfig(string url, string appSecretKey, string appName) { if (string.IsNullOrEmpty(url)) throw new Exception("Url cannot be empty !"); - try - { - string basePath = System.Threading.Thread.GetDomain().BaseDirectory; - Packet.InstallPath = basePath; - Packet.AppSecretKey = appSecretKey; - //update app. - Packet.AppName = appName; - string clientVersion = GetFileVersion(Path.Combine(basePath, $"{Packet.AppName}.exe")); - Packet.ClientVersion = clientVersion; - Packet.AppType = AppType.ClientApp; - Packet.UpdateUrl = $"{url}/versions/{AppType.ClientApp}/{clientVersion}/{Packet.AppSecretKey}"; - //main app. - string mainAppName = Path.GetFileNameWithoutExtension(Process.GetCurrentProcess().MainModule.FileName); - string mainVersion = Assembly.GetExecutingAssembly().GetName().Version.ToString(); - Packet.MainUpdateUrl = $"{url}/versions/{AppType.ClientApp}/{mainVersion}/{Packet.AppSecretKey}"; - Packet.MainAppName = mainAppName; - return this; - } - catch (Exception ex) - { - throw new GeneralUpdateException(ex.Message, ex.InnerException); - } + string basePath = System.Threading.Thread.GetDomain().BaseDirectory; + Packet.InstallPath = basePath; + Packet.AppSecretKey = appSecretKey; + //update app. + Packet.AppName = appName; + string clientVersion = GetFileVersion(Path.Combine(basePath, $"{Packet.AppName}.exe")); + Packet.ClientVersion = clientVersion; + Packet.AppType = AppType.ClientApp; + Packet.UpdateUrl = $"{url}/versions/{AppType.ClientApp}/{clientVersion}/{Packet.AppSecretKey}"; + //main app. + string mainAppName = Path.GetFileNameWithoutExtension(Process.GetCurrentProcess().MainModule.FileName); + string mainVersion = Assembly.GetExecutingAssembly().GetName().Version.ToString(); + Packet.MainUpdateUrl = $"{url}/versions/{AppType.ClientApp}/{mainVersion}/{Packet.AppSecretKey}"; + Packet.MainAppName = mainAppName; + return this; } /// @@ -117,7 +96,7 @@ public GeneralClientBootstrap Config(string url, string appSecretKey, string app /// /// /// - public GeneralClientBootstrap Config(Configinfo info) + public GeneralClientBootstrap SetConfig(Configinfo info) { Packet.AppType = info.AppType; Packet.AppName = info.AppName; @@ -138,24 +117,11 @@ public GeneralClientBootstrap Config(Configinfo info) /// public GeneralClientBootstrap SetCustomSkipOption(Func func) { - if (func == null) throw new ArgumentNullException(nameof(func)); + Contract.Requires(func != null); _customSkipOption = func; return this; } - /// - /// Let the user decide whether to update in the state of non-mandatory update. - /// - /// Custom function ,Custom actions to let users decide whether to update. true update false do not update . - /// - /// - public GeneralClientBootstrap SetCustomSkipOption(Func> func) - { - if (func == null) throw new ArgumentNullException(nameof(func)); - _customSkipTaskOption = func; - return this; - } - /// /// Add an asynchronous custom operation. /// In theory, any custom operation can be done. It is recommended to register the environment check method to ensure that there are normal dependencies and environments after the update is completed. @@ -165,28 +131,61 @@ public GeneralClientBootstrap SetCustomSkipOption(Func> func) /// public GeneralClientBootstrap AddCustomOption(List> funcs) { - if (funcs == null || !funcs.Any()) throw new ArgumentNullException(nameof(funcs)); + Contract.Requires(funcs != null && funcs.Any()); _customOptions.AddRange(funcs); return this; } - /// - /// Add a synchronization custom operation. - /// In theory, any custom operation can be done. It is recommended to register the environment check method to ensure that there are normal dependencies and environments after the update is completed. - /// - /// - /// - /// - public GeneralClientBootstrap AddCustomOption(List>> funcs) - { - if (funcs == null || !funcs.Any()) throw new ArgumentNullException(nameof(funcs)); - _customTaskOptions.AddRange(funcs); - return this; - } + public GeneralClientBootstrap AddListenerMultiAllDownloadCompleted(Action callbackAction) => AddListener(callbackAction); + + public GeneralClientBootstrap AddListenerMultiDownloadProgress(Action callbackAction) => AddListener(callbackAction); + + public GeneralClientBootstrap AddListenerMultiDownloadCompleted(Action callbackAction) => AddListener(callbackAction); + public GeneralClientBootstrap AddListenerMultiDownloadError(Action callbackAction) => AddListener(callbackAction); + + public GeneralClientBootstrap AddListenerMultiDownloadStatistics(Action callbackAction) => AddListener(callbackAction); + + public GeneralClientBootstrap AddListenerException(Action callbackAction) => AddListener(callbackAction); + #endregion Public Methods #region Private Methods + + private async Task InitializeData() + { + ClearEnvironmentVariable(); + + //Request the upgrade information needed by the client and upgrade end, and determine if an upgrade is necessary. + var versionService = new VersionService(); + var mainResp = await versionService.ValidationVersion(Packet.MainUpdateUrl); + var upgradeResp = await versionService.ValidationVersion(Packet.UpdateUrl); + + Packet.IsUpgradeUpdate = upgradeResp.Body.IsUpdate; + Packet.IsMainUpdate = mainResp.Body.IsUpdate; + //No need to update, return directly. + if ((!Packet.IsMainUpdate) && (!Packet.IsUpgradeUpdate)) return; + + //If the main program needs to be forced to update, the skip will not take effect. + var isForcibly = mainResp.Body.IsForcibly || upgradeResp.Body.IsForcibly; + if (CanSkip(isForcibly)) return; + + Packet.UpdateVersions = VersionAssembler.ToEntitys(upgradeResp.Body.Versions); + Packet.LastVersion = Packet.UpdateVersions.Last().Version; + + //Initialize the process transfer parameter object. + var processInfo = new ProcessInfo(Packet.MainAppName + , Packet.InstallPath + , Packet.ClientVersion + , Packet.LastVersion + , Packet.UpdateLogUrl + , Packet.Encoding + , Packet.Format + , Packet.DownloadTimeOut + , Packet.AppSecretKey + , mainResp.Body.Versions); + Packet.ProcessInfo = JsonSerializer.Serialize(processInfo); + } /// ///Gets the application version number @@ -197,73 +196,118 @@ public GeneralClientBootstrap AddCustomOption(List>> funcs) private string GetFileVersion(string filePath) { var fileInfo = new FileInfo(filePath); - if (fileInfo != null && fileInfo.Exists) return FileVersionInfo.GetVersionInfo(filePath).FileVersion; - throw new GeneralUpdateException($"Failed to obtain file '{filePath}' version. Procedure."); + if (fileInfo.Exists) return FileVersionInfo.GetVersionInfo(filePath).FileVersion; + throw new FileNotFoundException($"Failed to obtain file '{filePath}' version. Procedure."); } /// /// User decides if update is required. /// /// is false to continue execution. - private async Task IsSkip(bool isForcibly) + private bool CanSkip(bool isForcibly) { - try - { - bool isSkip = false; - if (isForcibly) return false; - if (_customSkipTaskOption != null) isSkip = await _customSkipTaskOption.Invoke(); - if (_customSkipOption != null) isSkip = _customSkipOption.Invoke(); - return isSkip; - } - catch (Exception ex) - { - throw new GeneralUpdateException($"The injected user skips this update with an exception ! {ex.Message}", ex.InnerException); - } + if (isForcibly) return false; + Contract.Requires(_customSkipOption != null); + return _customSkipOption.Invoke(); } /// /// Performs all injected custom operations. /// /// - private async Task ExecuteCustomOptions() + private void ExecuteCustomOptions() { - if (_customTaskOptions.Any()) - { - foreach (var option in _customTaskOptions) - { - var isSuccess = await option.Invoke(); - if (!isSuccess) - Core.Events.EventManager.Instance.Dispatch>(this, new Core.Events.CommonArgs.ExceptionEventArgs($"{nameof(option)}Execution failure!")); - } - } - else if (_customOptions.Any()) + Contract.Requires(_customOptions != null && _customOptions.Any()); + foreach (var option in _customOptions) { - foreach (var option in _customOptions) + if (!option.Invoke()) { - var isSuccess = option.Invoke(); - if (!isSuccess) - Core.Events.EventManager.Instance.Dispatch>(this, new Core.Events.CommonArgs.ExceptionEventArgs($"{nameof(option)}Execution failure!")); + EventManager.Instance.Dispatch(this, + new ExceptionEventArgs(null ,$"{nameof(option)}Execution failure!")); } } } /// - /// The values passed between processes during previous updates are cleared when the client starts. + /// Clear the environment variable information needed to start the upgrade assistant process. /// private void ClearEnvironmentVariable() { try { - Environment.SetEnvironmentVariable("ProcessBase64", null, EnvironmentVariableTarget.User); + Environment.SetEnvironmentVariable("ProcessInfo", null, EnvironmentVariableTarget.User); } catch (SecurityException ex) { - Trace.WriteLine($"Error: You do not have sufficient permissions to delete this environment variable.{ex}"); + EventManager.Instance.Dispatch(this, + new ExceptionEventArgs(ex, + $"Error: You do not have sufficient permissions to delete this environment variable.")); } catch (ArgumentException ex) { - Trace.WriteLine($"Error: The environment variable name is invalid. {ex}"); + EventManager.Instance.Dispatch(this, + new ExceptionEventArgs(ex, $"Error: The environment variable name is invalid.")); + } + catch (IOException ex) + { + EventManager.Instance.Dispatch(this, + new ExceptionEventArgs(ex, + $"Error: An I/O error occurred while deleting the environment variable.")); + } + catch (Exception ex) + { + EventManager.Instance.Dispatch(this, + new ExceptionEventArgs(ex, $"Error: An unknown error occurred while deleting the environment variable.")); + } + } + + protected override void ExecuteStrategy() + { + _strategy.Create(Packet); + _strategy.Execute(); + } + + protected override GeneralClientBootstrap StrategyFactory() + { + if (RuntimeInformation.IsOSPlatform(OSPlatform.Windows)) + { + _strategy = new WindowsStrategy(); + } + else if (RuntimeInformation.IsOSPlatform(OSPlatform.Linux)) + { + _strategy = new LinuxStrategy(); } + else + { + throw new PlatformNotSupportedException("The current operating system is not supported!"); + } + + return this; + } + + private GeneralClientBootstrap AddListener(Action callbackAction) where TArgs : EventArgs + { + Contract.Requires(callbackAction != null); + EventManager.Instance.AddListener(callbackAction); + return this; + } + + private void OnMultiDownloadStatistics(object sender, MultiDownloadStatisticsEventArgs e) + => EventManager.Instance.Dispatch(sender, e); + + private void OnMultiDownloadProgressChanged(object sender, MultiDownloadProgressChangedEventArgs e) + => EventManager.Instance.Dispatch(sender, e); + + private void OnMultiDownloadCompleted(object sender, MultiDownloadCompletedEventArgs e) + => EventManager.Instance.Dispatch(sender, e); + + private void OnMultiDownloadError(object sender, MultiDownloadErrorEventArgs e) + => EventManager.Instance.Dispatch(sender, e); + + private void OnMultiAllDownloadCompleted(object sender, MultiAllDownloadCompletedEventArgs e) + { + EventManager.Instance.Dispatch(sender, e); + ExecuteStrategy(); } #endregion Private Methods diff --git a/src/c#/GeneralUpdate.ClientCore/GeneralClientOSS.cs b/src/c#/GeneralUpdate.ClientCore/GeneralClientOSS.cs index c08f1e57..345a1b94 100644 --- a/src/c#/GeneralUpdate.ClientCore/GeneralClientOSS.cs +++ b/src/c#/GeneralUpdate.ClientCore/GeneralClientOSS.cs @@ -1,14 +1,15 @@ using GeneralUpdate.Core.ContentProvider; -using GeneralUpdate.Core.Domain.Entity; -using GeneralUpdate.Core.Domain.Entity.Assembler; -using GeneralUpdate.Core.Domain.PO; using System; using System.Collections.Generic; using System.Diagnostics; using System.IO; using System.Linq; using System.Net; +using System.Text.Json; using System.Threading.Tasks; +using GeneralUpdate.ClientCore.Internal; +using GeneralUpdate.Common.Internal.Event; +using GeneralUpdate.Common.Shared.Object; namespace GeneralUpdate.ClientCore { @@ -41,12 +42,12 @@ await Task.Run(() => var appPath = Path.Combine(basePath, $"{upgradeAppName}.exe"); if (!File.Exists(appPath)) throw new Exception($"The application does not exist {upgradeAppName} !"); //If you confirm that an update is required, start the upgrade application. - var processBase64 = ProcessAssembler.ToBase64(configParams); - Process.Start(appPath, processBase64); + var json = JsonSerializer.Serialize(configParams); + //TODO: set environment variable + Process.Start(appPath, json); } catch (Exception ex) { - GeneralUpdate.Core.Events.EventManager.Instance.Dispatch>(typeof(GeneralClientOSS), new GeneralUpdate.Core.Events.CommonArgs.ExceptionEventArgs(ex)); throw new Exception($"GeneralClientOSS update exception ! {ex.Message}", ex.InnerException); } finally @@ -82,14 +83,14 @@ private static void DownloadFile(string url, string path) } } - public static void AddListenerException(Action callbackAction) + public static void AddListenerException(Action callbackAction) { AddListener(callbackAction); } private static void AddListener(Action callbackAction) where TArgs : EventArgs { - if (callbackAction != null) GeneralUpdate.Core.Events.EventManager.Instance.AddListener(callbackAction); + if (callbackAction != null) EventManager.Instance.AddListener(callbackAction); } } } \ No newline at end of file diff --git a/src/c#/GeneralUpdate.ClientCore/GeneralUpdate.ClientCore.csproj b/src/c#/GeneralUpdate.ClientCore/GeneralUpdate.ClientCore.csproj index 30e37e57..9871d3b1 100644 --- a/src/c#/GeneralUpdate.ClientCore/GeneralUpdate.ClientCore.csproj +++ b/src/c#/GeneralUpdate.ClientCore/GeneralUpdate.ClientCore.csproj @@ -67,9 +67,6 @@ - - - @@ -77,72 +74,6 @@ - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - @@ -164,86 +95,16 @@ - - + - + - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - + + + diff --git a/src/c#/GeneralUpdate.ClientCore/HashAlgorithms/.gitkeep b/src/c#/GeneralUpdate.ClientCore/HashAlgorithms/.gitkeep deleted file mode 100644 index e69de29b..00000000 diff --git a/src/c#/GeneralUpdate.ClientCore/Internal/ExceptionEventArgs.cs b/src/c#/GeneralUpdate.ClientCore/Internal/ExceptionEventArgs.cs new file mode 100644 index 00000000..1a2c913b --- /dev/null +++ b/src/c#/GeneralUpdate.ClientCore/Internal/ExceptionEventArgs.cs @@ -0,0 +1,15 @@ +using System; + +namespace GeneralUpdate.ClientCore.Internal; + +public class ExceptionEventArgs : EventArgs +{ + public Exception Exception { get; private set; } + public string Message { get; private set; } + + public ExceptionEventArgs(Exception exception = null, string message = null) + { + Exception = exception ?? throw new Exception(nameof(exception)); + Message = message ?? exception.Message; + } +} \ No newline at end of file diff --git a/src/c#/GeneralUpdate.ClientCore/Pipeline/HashMiddleware.cs b/src/c#/GeneralUpdate.ClientCore/Pipeline/HashMiddleware.cs new file mode 100644 index 00000000..9d78eec4 --- /dev/null +++ b/src/c#/GeneralUpdate.ClientCore/Pipeline/HashMiddleware.cs @@ -0,0 +1,32 @@ +using System; +using System.Security.Cryptography; +using System.Threading.Tasks; +using GeneralUpdate.Common.HashAlgorithms; +using GeneralUpdate.Common.Internal.Pipeline; + +namespace GeneralUpdate.ClientCore.Pipeline; + +public class HashMiddleware : IMiddleware +{ + public async Task InvokeAsync(PipelineContext context) + { + var fileName = context.Get("FileName"); + var hash = context.Get("Hash"); + + bool isVerify = await VerifyFileHash(fileName, hash); + if (!isVerify) + { + throw new CryptographicException("Hash verification failed ."); + } + } + + private Task VerifyFileHash(string fileName, string hash) + { + return Task.Run(() => + { + var hashAlgorithm = new Sha256HashAlgorithm(); + var hashSha256 = hashAlgorithm.ComputeHash(fileName); + return string.Equals(hash, hashSha256, StringComparison.OrdinalIgnoreCase); + }); + } +} \ No newline at end of file diff --git a/src/c#/GeneralUpdate.ClientCore/Pipeline/PatchMiddleware.cs b/src/c#/GeneralUpdate.ClientCore/Pipeline/PatchMiddleware.cs new file mode 100644 index 00000000..968a14e0 --- /dev/null +++ b/src/c#/GeneralUpdate.ClientCore/Pipeline/PatchMiddleware.cs @@ -0,0 +1,20 @@ +using System.Collections.Generic; +using System.Threading.Tasks; +using GeneralUpdate.Common.Internal.Pipeline; +using GeneralUpdate.Differential; + +namespace GeneralUpdate.ClientCore.Pipeline; + +public class PatchMiddleware : IMiddleware +{ + public async Task InvokeAsync(PipelineContext context) + { + var sourcePath = context.Get("SourcePath"); + var targetPath = context.Get("TargetPath"); + var blackFiles = context.Get>("BlackFiles"); + var blackFileFormats = context.Get>("BlackFileFormats"); + + DifferentialCore.Instance.SetBlocklist(blackFiles, blackFileFormats); + await DifferentialCore.Instance.Dirty(sourcePath, targetPath); + } +} \ No newline at end of file diff --git a/src/c#/GeneralUpdate.ClientCore/Pipeline/ZipMiddleware.cs b/src/c#/GeneralUpdate.ClientCore/Pipeline/ZipMiddleware.cs new file mode 100644 index 00000000..502f4ffa --- /dev/null +++ b/src/c#/GeneralUpdate.ClientCore/Pipeline/ZipMiddleware.cs @@ -0,0 +1,39 @@ +using System.Text; +using System.Threading.Tasks; +using GeneralUpdate.Common.Internal.Pipeline; +using GeneralUpdate.Zip; +using GeneralUpdate.Zip.Factory; + +namespace GeneralUpdate.ClientCore.Pipeline; + +public class ZipMiddleware : IMiddleware +{ + public Task InvokeAsync(PipelineContext context) + { + return Task.Run(() => + { + var type = MatchType(context.Get("Format")); + var name = context.Get("Name"); + var sourcePath = context.Get("SourcePath"); + var destinationPath = context.Get("DestinationPath"); + var encoding = context.Get("Encoding"); + + var generalZipfactory = new GeneralZipFactory(); + generalZipfactory.CompressProgress += (sender, args) => { }; + generalZipfactory.Completed += (sender, args) => { }; + generalZipfactory.CreateOperate(type, name, sourcePath, destinationPath, true, encoding); + generalZipfactory.UnZip(); + }); + } + + private static OperationType MatchType(string extensionName) + { + var type = extensionName switch + { + ".zip" => OperationType.GZip, + ".7z" => OperationType.G7z, + _ => OperationType.None + }; + return type; + } +} \ No newline at end of file diff --git a/src/c#/GeneralUpdate.ClientCore/Pipelines/Attributes/.gitkeep b/src/c#/GeneralUpdate.ClientCore/Pipelines/Attributes/.gitkeep deleted file mode 100644 index e69de29b..00000000 diff --git a/src/c#/GeneralUpdate.ClientCore/Pipelines/Context/.gitkeep b/src/c#/GeneralUpdate.ClientCore/Pipelines/Context/.gitkeep deleted file mode 100644 index e69de29b..00000000 diff --git a/src/c#/GeneralUpdate.ClientCore/Pipelines/Middleware/.gitkeep b/src/c#/GeneralUpdate.ClientCore/Pipelines/Middleware/.gitkeep deleted file mode 100644 index e69de29b..00000000 diff --git a/src/c#/GeneralUpdate.ClientCore/Pipelines/MiddlewareResolver/.gitkeep b/src/c#/GeneralUpdate.ClientCore/Pipelines/MiddlewareResolver/.gitkeep deleted file mode 100644 index e69de29b..00000000 diff --git a/src/c#/GeneralUpdate.ClientCore/Pipelines/Pipeline/.gitkeep b/src/c#/GeneralUpdate.ClientCore/Pipelines/Pipeline/.gitkeep deleted file mode 100644 index e69de29b..00000000 diff --git a/src/c#/GeneralUpdate.ClientCore/Strategys/LinuxStrategy.cs b/src/c#/GeneralUpdate.ClientCore/Strategys/LinuxStrategy.cs new file mode 100644 index 00000000..372154eb --- /dev/null +++ b/src/c#/GeneralUpdate.ClientCore/Strategys/LinuxStrategy.cs @@ -0,0 +1,8 @@ +using GeneralUpdate.Common.Internal.Strategy; + +namespace GeneralUpdate.ClientCore.Strategys +{ + public class LinuxStrategy : AbstractStrategy + { + } +} \ No newline at end of file diff --git a/src/c#/GeneralUpdate.ClientCore/Strategys/PlatformLinux/.gitkeep b/src/c#/GeneralUpdate.ClientCore/Strategys/PlatformLinux/.gitkeep deleted file mode 100644 index e69de29b..00000000 diff --git a/src/c#/GeneralUpdate.ClientCore/Strategys/PlatformWindows/.gitkeep b/src/c#/GeneralUpdate.ClientCore/Strategys/PlatformWindows/.gitkeep deleted file mode 100644 index e69de29b..00000000 diff --git a/src/c#/GeneralUpdate.ClientCore/Strategys/WindowsStrategy.cs b/src/c#/GeneralUpdate.ClientCore/Strategys/WindowsStrategy.cs new file mode 100644 index 00000000..2eb81ec4 --- /dev/null +++ b/src/c#/GeneralUpdate.ClientCore/Strategys/WindowsStrategy.cs @@ -0,0 +1,103 @@ +using GeneralUpdate.Core.ContentProvider; +using System; +using System.Diagnostics; +using System.IO; +using System.Linq; +using System.Threading.Tasks; +using GeneralUpdate.ClientCore.Pipeline; +using GeneralUpdate.Common; +using GeneralUpdate.Common.Internal.Pipeline; +using GeneralUpdate.Common.Internal.Strategy; +using GeneralUpdate.Common.Shared.Object; + +namespace GeneralUpdate.ClientCore.Strategys +{ + /// + /// Update policy based on the Windows platform. + /// + public class WindowsStrategy : AbstractStrategy + { + private Packet Packet { get; set; } + + #region Public Methods + + public override void Create(Packet parameter) => Packet = parameter; + + public override async Task ExecuteAsync() + { + var updateVersions = Packet.UpdateVersions.OrderBy(x => x.PubTime).ToList(); + if (updateVersions.Count > 0) + { + foreach (var version in updateVersions) + { + var patchPath = FileProvider.GetTempDirectory(PATCHS); + var zipFilePath = Path.Combine(Packet.TempPath, $"{version.Name}{Packet.Format}"); + + var context = new PipelineContext(); + //hash middleware + context.Add("Hash", version.Hash); + context.Add("FileName", zipFilePath); + //zip middleware + context.Add("Format", Packet.Format); + context.Add("Name", zipFilePath); + context.Add("SourcePath", Packet.TempPath); + context.Add("DestinationPath", Packet.InstallPath); + context.Add("Encoding", Packet.Encoding); + //patch middleware + context.Add("SourcePath", patchPath); + context.Add("TargetPath", Packet.InstallPath); + context.Add("BlackFiles", GeneralFileManager.BlackFiles); + context.Add("BlackFileFormats", GeneralFileManager.BlackFileFormats); + + var pipelineBuilder = new PipelineBuilder(context) + .UseMiddleware() + .UseMiddleware() + .UseMiddleware(); + await pipelineBuilder.Build(); + } + + if (!string.IsNullOrEmpty(Packet.UpdateLogUrl)) + OpenBrowser(Packet.UpdateLogUrl); + } + + Clear(); + StartApp(Packet.AppName, Packet.AppType); + } + + public override void StartApp(string appName, int appType) + { + var path = Path.Combine(Packet.InstallPath, appName); + switch (appType) + { + case AppType.ClientApp: + Environment.SetEnvironmentVariable("ProcessInfo", Packet.ProcessInfo, EnvironmentVariableTarget.User); + Process.Start(path); + break; + + case AppType.UpgradeApp: + Process.Start(path); + break; + + default: + throw new ArgumentException("Invalid app type"); + } + } + + #endregion Public Methods + + #region Private Methods + + /// + /// Remove update redundant files. + /// + /// + private void Clear() + { + if (File.Exists(Packet.TempPath)) File.Delete(Packet.TempPath); + var dirPath = Path.GetDirectoryName(Packet.TempPath); + if (Directory.Exists(dirPath)) Directory.Delete(dirPath, true); + } + + #endregion Private Methods + } +} \ No newline at end of file diff --git a/src/c#/GeneralUpdate.ClientCore/WillMessage/.gitkeep b/src/c#/GeneralUpdate.ClientCore/WillMessage/.gitkeep deleted file mode 100644 index e69de29b..00000000 diff --git a/src/c#/GeneralUpdate.ClientCore/ZipFactory/Events/.gitkeep b/src/c#/GeneralUpdate.ClientCore/ZipFactory/Events/.gitkeep deleted file mode 100644 index e69de29b..00000000 diff --git a/src/c#/GeneralUpdate.ClientCore/ZipFactory/Factory/.gitkeep b/src/c#/GeneralUpdate.ClientCore/ZipFactory/Factory/.gitkeep deleted file mode 100644 index e69de29b..00000000 diff --git a/src/c#/GeneralUpdate.Common/Download/DownloadManager.cs b/src/c#/GeneralUpdate.Common/Download/DownloadManager.cs index 8ddaaf10..247a6c2a 100644 --- a/src/c#/GeneralUpdate.Common/Download/DownloadManager.cs +++ b/src/c#/GeneralUpdate.Common/Download/DownloadManager.cs @@ -2,11 +2,11 @@ using System.Collections.Generic; using System.Threading.Tasks; using System.Collections.Immutable; -using GeneralUpdate.Common.Download; +using System.Diagnostics.Contracts; namespace GeneralUpdate.Common.Download { - public class DownloadManager + public class DownloadManager { #region Private Members @@ -14,8 +14,8 @@ public class DownloadManager private readonly string _format; private readonly int _timeOut; private readonly IList<(object, string)> _failedVersions; - private ImmutableList>.Builder _downloadTasksBuilder; - private ImmutableList> _downloadTasks; + private ImmutableList.Builder _downloadTasksBuilder; + private ImmutableList _downloadTasks; #endregion Private Members @@ -27,7 +27,7 @@ public DownloadManager(string path, string format, int timeOut) _format = format; _timeOut = timeOut; _failedVersions = new List<(object, string)>(); - _downloadTasksBuilder = ImmutableList.Create>().ToBuilder(); + _downloadTasksBuilder = ImmutableList.Create().ToBuilder(); } #endregion Constructors @@ -42,7 +42,7 @@ public DownloadManager(string path, string format, int timeOut) public int TimeOut => _timeOut; - public ImmutableList> DownloadTasks => _downloadTasks ?? (_downloadTasksBuilder.ToImmutable()); + private ImmutableList DownloadTasks => _downloadTasks ?? (_downloadTasksBuilder.ToImmutable()); public event EventHandler MultiAllDownloadCompleted; public event EventHandler MultiDownloadProgressChanged; @@ -75,19 +75,13 @@ public async Task LaunchTasksAsync() } public void OnMultiDownloadStatistics(object sender, MultiDownloadStatisticsEventArgs e) - { - MultiDownloadStatistics?.Invoke(this, e); - } + => MultiDownloadStatistics?.Invoke(this, e); public void OnMultiDownloadProgressChanged(object sender, MultiDownloadProgressChangedEventArgs e) - { - MultiDownloadProgressChanged?.Invoke(this, e); - } + => MultiDownloadProgressChanged?.Invoke(this, e); public void OnMultiAsyncCompleted(object sender, MultiDownloadCompletedEventArgs e) - { - MultiDownloadCompleted?.Invoke(this, e); - } + => MultiDownloadCompleted?.Invoke(this, e); public void OnMultiDownloadError(object sender, MultiDownloadErrorEventArgs e) { @@ -95,15 +89,16 @@ public void OnMultiDownloadError(object sender, MultiDownloadErrorEventArgs e) _failedVersions.Add((e.Version, e.Exception.Message)); } - public void Add(DownloadTask task) + public void Add(DownloadTask task) { - if (task != null && !_downloadTasksBuilder.Contains(task)) + Contract.Requires(task != null); + if (!_downloadTasksBuilder.Contains(task)) { _downloadTasksBuilder.Add(task); } } - public void Remove(DownloadTask task) => _downloadTasksBuilder.Remove(task); + public void Remove(DownloadTask task) => _downloadTasksBuilder.Remove(task); #endregion Public Methods } diff --git a/src/c#/GeneralUpdate.Common/Download/DownloadTask.cs b/src/c#/GeneralUpdate.Common/Download/DownloadTask.cs index 073cbbc0..2546faf7 100644 --- a/src/c#/GeneralUpdate.Common/Download/DownloadTask.cs +++ b/src/c#/GeneralUpdate.Common/Download/DownloadTask.cs @@ -4,16 +4,17 @@ using System.Net.Http; using System.Threading; using System.Threading.Tasks; +using GeneralUpdate.Common.Shared.Object; namespace GeneralUpdate.Common.Download { - public class DownloadTask + public class DownloadTask { #region Private Members private readonly HttpClient _httpClient; - private readonly DownloadManager _manager; - private readonly TVersion _version; + private readonly DownloadManager _manager; + private readonly VersionInfo _version; private const int DEFAULT_DELTA = 1048576; // 1024*1024 private long _beforBytes; private long _receivedBytes; @@ -25,7 +26,7 @@ public class DownloadTask #region Constructors - public DownloadTask(DownloadManager manager, TVersion version) + public DownloadTask(DownloadManager manager, VersionInfo version) { _manager = manager; _version = version; @@ -49,8 +50,8 @@ public async Task LaunchAsync() { try { - var url = GetPropertyValue(_version, "Url"); - var name = GetPropertyValue(_version, "Name"); + var url = _version.Url; + var name = _version.Name; var installPath = $"{_manager.Path}{name}{_manager.Format}"; InitStatisticsEvent(); @@ -62,7 +63,6 @@ public async Task LaunchAsync() catch (Exception ex) { _manager.OnMultiDownloadError(this, new MultiDownloadErrorEventArgs(ex, _version)); - //throw new GeneralUpdateException("'download task' The executes abnormally !", ex); } } @@ -206,30 +206,11 @@ private void InitCompletedEvent() } private void OnProgressChanged(long bytesReceived, long totalBytes) - { - _manager.OnMultiDownloadProgressChanged(this, new MultiDownloadProgressChangedEventArgs( + => _manager.OnMultiDownloadProgressChanged(this, new MultiDownloadProgressChangedEventArgs( bytesReceived, totalBytes, (float)bytesReceived / totalBytes, _version)); - } - + private void OnCompleted() - { - _manager.OnMultiAsyncCompleted(this, new MultiDownloadCompletedEventArgs(_version, null, false, _version)); - } - - private TResult GetPropertyValue(TVersion entity, string propertyName) - { - TResult result = default(TResult); - try - { - var propertyInfo = typeof(TVersion).GetProperty(propertyName); - result = (TResult)propertyInfo?.GetValue(entity); - } - catch (Exception ex) - { - //throw new GeneralUpdateException($"Error getting property value: {ex.Message}", ex); - } - return result; - } + => _manager.OnMultiAsyncCompleted(this, new MultiDownloadCompletedEventArgs(_version, null, false, _version)); private string ToUnit(long byteSize) { diff --git a/src/c#/GeneralUpdate.Common/File/GeneralFileManager.cs b/src/c#/GeneralUpdate.Common/File/GeneralFileManager.cs index e57f140d..287ba037 100644 --- a/src/c#/GeneralUpdate.Common/File/GeneralFileManager.cs +++ b/src/c#/GeneralUpdate.Common/File/GeneralFileManager.cs @@ -2,9 +2,8 @@ using System.Collections.Generic; using System.IO; using System.Linq; -using System.Text; +using System.Text.Json; using GeneralUpdate.Common.HashAlgorithms; -using Newtonsoft.Json; namespace GeneralUpdate.Common { @@ -28,8 +27,8 @@ public sealed class GeneralFileManager #region Public Properties - public static IReadOnlyList BlackFileFormats => _blackFileFormats.AsReadOnly(); - public static IReadOnlyList BlackFiles => _blackFiles.AsReadOnly(); + public static IList BlackFileFormats => _blackFileFormats.AsReadOnly(); + public static IList BlackFiles => _blackFiles.AsReadOnly(); public ComparisonResult ComparisonResult { get; private set; } @@ -92,7 +91,7 @@ public void CompareDirectories(string dirA, string dirB) } } - public static void CreateJson(string targetPath, T obj) + public static void CreateJson(string targetPath, T obj) where T : class { var folderPath = Path.GetDirectoryName(targetPath) ?? throw new ArgumentException("invalid path", nameof(targetPath)); @@ -101,50 +100,20 @@ public static void CreateJson(string targetPath, T obj) Directory.CreateDirectory(folderPath); } - var jsonString = JsonConvert.SerializeObject(obj); + var jsonString = JsonSerializer.Serialize(obj); File.WriteAllText(targetPath, jsonString); } - public static T? GetJson(string path) + public static T? GetJson(string path) where T : class { if (File.Exists(path)) { var json = File.ReadAllText(path); - return JsonConvert.DeserializeObject(json); + return JsonSerializer.Deserialize(json); } - return default(T); + return default; } - /// - /// Convert object to base64 string. - /// - /// - /// - public static string Serialize(object? obj) - { - if (obj == null) return string.Empty; - var json = JsonConvert.SerializeObject(obj); - var bytes = Encoding.Default.GetBytes(json); - var base64Str = Convert.ToBase64String(bytes); - return base64Str; - } - - /// - /// Convert base64 object to string. - /// - /// - /// - /// - public static T? Deserialize(string str) - { - var obj = default(T); - if (string.IsNullOrEmpty(str)) return obj; - byte[] bytes = Convert.FromBase64String(str); - var json = Encoding.Default.GetString(bytes); - var result = JsonConvert.DeserializeObject(json); - return result; - } - public static string GetTempDirectory(string name) { var path = $"generalupdate_{DateTime.Now:yyyy-MM-dd}_{name}"; @@ -159,7 +128,7 @@ public static string GetTempDirectory(string name) #endregion #region Private Methods - + private IEnumerable GetRelativeFilePaths(string rootDir, string currentDir) { foreach (var file in Directory.GetFiles(currentDir)) diff --git a/src/c#/GeneralUpdate.Common/GeneralUpdate.Common.csproj b/src/c#/GeneralUpdate.Common/GeneralUpdate.Common.csproj index 05f942be..f842ca31 100644 --- a/src/c#/GeneralUpdate.Common/GeneralUpdate.Common.csproj +++ b/src/c#/GeneralUpdate.Common/GeneralUpdate.Common.csproj @@ -7,9 +7,9 @@ - - - + + + diff --git a/src/c#/GeneralUpdate.Common/Internal/Bootstrap/AbstractBootstrap.cs b/src/c#/GeneralUpdate.Common/Internal/Bootstrap/AbstractBootstrap.cs index f9a635f9..dbd7e0e8 100644 --- a/src/c#/GeneralUpdate.Common/Internal/Bootstrap/AbstractBootstrap.cs +++ b/src/c#/GeneralUpdate.Common/Internal/Bootstrap/AbstractBootstrap.cs @@ -1,6 +1,5 @@ -using System; -using System.Collections.Concurrent; -using System.Diagnostics; +using System.Collections.Concurrent; +using System.Diagnostics.Contracts; using System.Threading.Tasks; using GeneralUpdate.Common.Internal.Strategy; @@ -14,27 +13,16 @@ public abstract class AbstractBootstrap protected internal AbstractBootstrap() => _options = new ConcurrentDictionary(); - /// - /// Launch udpate. - /// - /// - protected abstract TBootstrap Launch(); - /// /// Launch async udpate. /// /// - protected abstract Task LaunchAsync(); - - protected abstract IStrategy InitStrategy(); - - protected abstract IStrategy ExecuteStrategy(); + public abstract Task LaunchAsync(); - protected virtual TBootstrap Strategy() where T : TStrategy, new() => this.StrategyFactory(() => new T()); + protected abstract void ExecuteStrategy(); - protected abstract TBootstrap StrategyFactory(Func strategyFactory); + protected abstract TBootstrap StrategyFactory(); - /// /// Setting update configuration. /// @@ -44,21 +32,20 @@ public abstract class AbstractBootstrap /// public virtual TBootstrap Option(UpdateOption option, T value) { - Debug.Assert(option != null); if (value == null) { - this._options.TryRemove(option, out _); + _options.TryRemove(option, out _); } else { - this._options[option] = new UpdateOptionValue(option, value); + _options[option] = new UpdateOptionValue(option, value); } return (TBootstrap)this; } public virtual T? GetOption(UpdateOption option) { - Debug.Assert(option != null); + Contract.Requires(option != null); if (_options.Count == 0) return default(T); var val = _options[option]; if (val != null) return (T)val.GetValue(); diff --git a/src/c#/GeneralUpdate.Common/Internal/Event/EventManager.cs b/src/c#/GeneralUpdate.Common/Internal/Event/EventManager.cs index f31f430b..4308aab0 100644 --- a/src/c#/GeneralUpdate.Common/Internal/Event/EventManager.cs +++ b/src/c#/GeneralUpdate.Common/Internal/Event/EventManager.cs @@ -1,46 +1,16 @@ using System; using System.Collections.Generic; -using System.ComponentModel; namespace GeneralUpdate.Common.Internal.Event { - /// - /// Manage all events in the component. - /// - public class EventManager : IEventManager, IDisposable + public class EventManager : IDisposable { - // Use interop to call the method necessary - // to clean up the unmanaged resource. - [System.Runtime.InteropServices.DllImport("Kernel32")] - private static extern Boolean CloseHandle(IntPtr handle); - private static readonly object _lockObj = new object(); private static EventManager _instance; private Dictionary _dicDelegates = new Dictionary(); - - // Track whether Dispose has been called. private bool disposed = false; - // Pointer to an external unmanaged resource. - private IntPtr handle; - - // Other managed resource this class uses. - private Component component = null; - - private EventManager() => component = new Component(); - - // Use C# finalizer syntax for finalization code. - // This finalizer will run only if the Dispose method - // does not get called. - // It gives your base class the opportunity to finalize. - // Do not provide finalizer in types derived from this class. - ~EventManager() - { - // Do not re-create Dispose clean-up code here. - // Calling Dispose(disposing: false) is optimal in terms of - // readability and maintainability. - Dispose(disposing: false); - } + private EventManager() { } public static EventManager Instance { @@ -58,107 +28,48 @@ public static EventManager Instance } } - /// - /// Add listener - /// - /// Specify the delegate type. - /// Delegate to be added. - /// parameter null exception. - public void AddListener(TDelegate newDelegate) where TDelegate : Delegate + public void AddListener(Action listener) where TEventArgs : EventArgs { - if (newDelegate == null) throw new ArgumentNullException(nameof(newDelegate)); - if (_dicDelegates.ContainsKey(typeof(TDelegate))) return; - handle = new IntPtr(1); - _dicDelegates.Add(typeof(TDelegate), newDelegate); + if (listener == null) throw new ArgumentNullException(nameof(listener)); + var delegateType = typeof(Action); + if (_dicDelegates.ContainsKey(delegateType)) + { + _dicDelegates[delegateType] = Delegate.Combine(_dicDelegates[delegateType], listener); + } + else + { + _dicDelegates.Add(delegateType, listener); + } } - /// - /// Remove listener - /// - /// Specify the delegate type. - /// Remove old delegates. - /// parameter null exception. - public void RemoveListener(TDelegate oldDelegate) where TDelegate : Delegate + public void RemoveListener(Action listener) where TEventArgs : EventArgs { - if (oldDelegate == null) throw new ArgumentNullException(nameof(oldDelegate)); - var delegateType = oldDelegate.GetType(); - if (!delegateType.IsInstanceOfType(typeof(TDelegate))) return; - Delegate tempDelegate = null; - if (_dicDelegates.TryGetValue(delegateType, out tempDelegate)) + if (listener == null) throw new ArgumentNullException(nameof(listener)); + var delegateType = typeof(Action); + if (_dicDelegates.TryGetValue(delegateType, out var existingDelegate)) { - if (tempDelegate == null) - { - _dicDelegates.Remove(delegateType); - } - else - { - _dicDelegates[delegateType] = tempDelegate; - } + _dicDelegates[delegateType] = Delegate.Remove(existingDelegate, listener); } } - /// - /// Triggers a delegate of the same type. - /// - /// - /// trigger source object. - /// event args. - /// parameter null exception. - public void Dispatch(object sender, EventArgs eventArgs) where TDelegate : Delegate + public void Dispatch(object sender, TEventArgs eventArgs) where TEventArgs : EventArgs { if (sender == null) throw new ArgumentNullException(nameof(sender)); if (eventArgs == null) throw new ArgumentNullException(nameof(eventArgs)); - if (!_dicDelegates.ContainsKey(typeof(TDelegate))) return; - _dicDelegates[typeof(TDelegate)].DynamicInvoke(sender, eventArgs); + var delegateType = typeof(Action); + if (_dicDelegates.TryGetValue(delegateType, out var existingDelegate)) + { + ((Action)existingDelegate)?.Invoke(sender, eventArgs); + } } - /// - /// Clear all listeners. - /// public void Clear() => _dicDelegates.Clear(); - // Implement IDisposable. - // Do not make this method virtual. - // A derived class should not be able to override this method. public void Dispose() { - Dispose(disposing: true); - // This object will be cleaned up by the Dispose method. - // Therefore, you should call GC.SuppressFinalize to - // take this object off the finalization queue - // and prevent finalization code for this object - // from executing a second time. - GC.SuppressFinalize(this); - } - - // Dispose(bool disposing) executes in two distinct scenarios. - // If disposing equals true, the method has been called directly - // or indirectly by a user's code. Managed and unmanaged resources - // can be disposed. - // If disposing equals false, the method has been called by the - // runtime from inside the finalizer and you should not reference - // other objects. Only unmanaged resources can be disposed. - protected virtual void Dispose(bool disposing) - { - // Check to see if Dispose has already been called. if (!this.disposed) { - // If disposing equals true, dispose all managed - // and unmanaged resources. - if (disposing) - { - // Dispose managed resources. - component.Dispose(); - } - - // Call the appropriate methods to clean up - // unmanaged resources here. - // If disposing is false, - // only the following code is executed. - CloseHandle(handle); - handle = IntPtr.Zero; - - // Note disposing has been done. + _dicDelegates.Clear(); disposed = true; } } diff --git a/src/c#/GeneralUpdate.Common/Internal/Pipeline/IContext.cs b/src/c#/GeneralUpdate.Common/Internal/Pipeline/IContext.cs deleted file mode 100644 index 2b8400b1..00000000 --- a/src/c#/GeneralUpdate.Common/Internal/Pipeline/IContext.cs +++ /dev/null @@ -1,10 +0,0 @@ -namespace GeneralUpdate.Common.Internal.Pipeline -{ - /// - /// Pipeline context. - /// - public interface IContext - { - - } -} \ No newline at end of file diff --git a/src/c#/GeneralUpdate.Common/Internal/Pipeline/IMiddleware.cs b/src/c#/GeneralUpdate.Common/Internal/Pipeline/IMiddleware.cs index 4e26809e..606b1c7c 100644 --- a/src/c#/GeneralUpdate.Common/Internal/Pipeline/IMiddleware.cs +++ b/src/c#/GeneralUpdate.Common/Internal/Pipeline/IMiddleware.cs @@ -7,6 +7,6 @@ namespace GeneralUpdate.Common.Internal.Pipeline /// public interface IMiddleware { - Task InvokeAsync(PipelineContext context, IMiddleware middleware); + Task InvokeAsync(PipelineContext context); } } \ No newline at end of file diff --git a/src/c#/GeneralUpdate.Common/Internal/Pipeline/PipelineBuilder.cs b/src/c#/GeneralUpdate.Common/Internal/Pipeline/PipelineBuilder.cs index fae0c90d..7ff667ba 100644 --- a/src/c#/GeneralUpdate.Common/Internal/Pipeline/PipelineBuilder.cs +++ b/src/c#/GeneralUpdate.Common/Internal/Pipeline/PipelineBuilder.cs @@ -9,6 +9,9 @@ namespace GeneralUpdate.Common.Internal.Pipeline /// public sealed class PipelineBuilder(PipelineContext context = null) { + /// + /// LIFO,Last In First Out. + /// private ImmutableStack _middlewareStack = ImmutableStack.Empty; public PipelineBuilder UseMiddleware() where TMiddleware : IMiddleware, new() @@ -29,8 +32,11 @@ public PipelineBuilder UseMiddlewareIf(Func condition) public async Task Build() { - var middleware = _middlewareStack.Peek(); - await middleware.InvokeAsync(context, _middlewareStack.Peek()); + while (!_middlewareStack.IsEmpty) + { + _middlewareStack.Pop(out var middleware); + await middleware.InvokeAsync(context); + } } } } \ No newline at end of file diff --git a/src/c#/GeneralUpdate.Common/Internal/Strategy/AbstractStrategy.cs b/src/c#/GeneralUpdate.Common/Internal/Strategy/AbstractStrategy.cs index 344a7fde..a88d164d 100644 --- a/src/c#/GeneralUpdate.Common/Internal/Strategy/AbstractStrategy.cs +++ b/src/c#/GeneralUpdate.Common/Internal/Strategy/AbstractStrategy.cs @@ -1,6 +1,8 @@ using System; -using System.Text; +using System.Diagnostics; +using System.Runtime.InteropServices; using System.Threading.Tasks; +using GeneralUpdate.Common.Shared.Object; namespace GeneralUpdate.Common.Internal.Strategy { @@ -13,13 +15,25 @@ public abstract class AbstractStrategy : IStrategy public virtual Task ExecuteAsync() => throw new NotImplementedException(); public virtual void StartApp(string appName, int appType) => throw new NotImplementedException(); - - public virtual string GetPlatform() => throw new NotImplementedException(); - + public virtual Task ExecuteTaskAsync() => throw new NotImplementedException(); - public virtual void Create(T parameter) where T : class => throw new NotImplementedException(); + public virtual void Create(Packet parameter) => throw new NotImplementedException(); - public virtual void Create(T parameter, Encoding encoding) where T : class => throw new NotImplementedException(); + protected void OpenBrowser(string url) + { + if (RuntimeInformation.IsOSPlatform(OSPlatform.Windows)) + { + Process.Start(new ProcessStartInfo("cmd", $"/c start {url}") { CreateNoWindow = true }); + } + else if (RuntimeInformation.IsOSPlatform(OSPlatform.Linux)) + { + Process.Start("xdg-open", url); + } + else + { + throw new PlatformNotSupportedException("Unsupported OS platform"); + } + } } } \ No newline at end of file diff --git a/src/c#/GeneralUpdate.Common/Internal/Strategy/IStrategy.cs b/src/c#/GeneralUpdate.Common/Internal/Strategy/IStrategy.cs index 9dbd5754..e3d8a3ee 100644 --- a/src/c#/GeneralUpdate.Common/Internal/Strategy/IStrategy.cs +++ b/src/c#/GeneralUpdate.Common/Internal/Strategy/IStrategy.cs @@ -1,4 +1,5 @@ using System.Threading.Tasks; +using GeneralUpdate.Common.Shared.Object; namespace GeneralUpdate.Common.Internal.Strategy { @@ -19,13 +20,7 @@ public interface IStrategy /// /// void StartApp(string appName, int appType); - - /// - /// Get the platform for the current strategy. - /// - /// - string GetPlatform(); - + /// /// Execution strategy. /// @@ -34,6 +29,6 @@ public interface IStrategy /// /// Create a strategy. /// - void Create(T parameter) where T : class; + void Create(Packet parameter); } } \ No newline at end of file diff --git a/src/c#/GeneralUpdate.Common/Shared/Object/Assembler/ProcessAssembler.cs b/src/c#/GeneralUpdate.Common/Shared/Object/Assembler/ProcessAssembler.cs deleted file mode 100644 index 4c30240a..00000000 --- a/src/c#/GeneralUpdate.Common/Shared/Object/Assembler/ProcessAssembler.cs +++ /dev/null @@ -1,60 +0,0 @@ -using System.Text; - -namespace GeneralUpdate.Common.Shared.Object -{ - public class ProcessAssembler - { - public static Packet ToPacket(ProcessInfo info) - { - var packet = new Packet(); - packet.AppName = info.AppName; - packet.AppSecretKey = info.AppSecretKey; - packet.AppType = info.AppType; - packet.InstallPath = info.InstallPath; - packet.ClientVersion = info.CurrentVersion; - packet.LastVersion = info.LastVersion; - packet.UpdateLogUrl = info.LogUrl; - packet.Encoding = ToEncoding(info.CompressEncoding); - packet.Format = info.CompressFormat; - packet.DownloadTimeOut = info.DownloadTimeOut; - packet.UpdateVersions = info.UpdateVersions; - return packet; - } - - private static Encoding ToEncoding(int type) - { - Encoding encoding = Encoding.Default; - switch (type) - { - case 1: - encoding = Encoding.UTF8; - break; - - case 2: - encoding = Encoding.UTF7; - break; - - case 3: - encoding = Encoding.UTF32; - break; - - case 4: - encoding = Encoding.Unicode; - break; - - case 5: - encoding = Encoding.BigEndianUnicode; - break; - - case 6: - encoding = Encoding.ASCII; - break; - - case 7: - encoding = Encoding.Default; - break; - } - return encoding; - } - } -} \ No newline at end of file diff --git a/src/c#/GeneralUpdate.Common/Shared/Object/Packet.cs b/src/c#/GeneralUpdate.Common/Shared/Object/Packet.cs index 5addbe86..2fa473d8 100644 --- a/src/c#/GeneralUpdate.Common/Shared/Object/Packet.cs +++ b/src/c#/GeneralUpdate.Common/Shared/Object/Packet.cs @@ -6,9 +6,6 @@ namespace GeneralUpdate.Common.Shared.Object { public class Packet { - public Packet() - { } - public Packet(string mainUpdateUrl, int appType, string updateUrl, string appName, string mainAppName, string format, bool isUpdate, string updateLogUrl, Encoding encoding, int downloadTimeOut, string appSecretKey, string tempPath) { MainUpdateUrl = mainUpdateUrl ?? throw new ArgumentNullException(nameof(MainUpdateUrl)); @@ -113,7 +110,7 @@ public Packet(string mainUpdateUrl, int appType, string updateUrl, string appNam /// /// Configuration parameters for upgrading the terminal program. /// - public string ProcessBase64 { get; set; } + public string ProcessInfo { get; set; } /// /// Files in the blacklist will skip the update. diff --git a/src/c#/GeneralUpdate.Common/Shared/Service/VersionService.cs b/src/c#/GeneralUpdate.Common/Shared/Service/VersionService.cs index 52e74ada..934394be 100644 --- a/src/c#/GeneralUpdate.Common/Shared/Service/VersionService.cs +++ b/src/c#/GeneralUpdate.Common/Shared/Service/VersionService.cs @@ -2,9 +2,9 @@ using System.Net.Http; using System.Security.Cryptography.X509Certificates; using System.Net.Security; +using System.Text.Json; using System.Threading.Tasks; using GeneralUpdate.Common.Shared.Object; -using Newtonsoft.Json; namespace GeneralUpdate.Common.Shared.Service { @@ -58,7 +58,7 @@ private async Task GetTaskAsync(string url, string headerKey = null, strin var response = await _httpClient.SendAsync(request); response.EnsureSuccessStatusCode(); // Throw if not a success code. var responseContent = await response.Content.ReadAsStringAsync(); - var result = JsonConvert.DeserializeObject(responseContent); + var result = JsonSerializer.Deserialize(responseContent); return result; } diff --git a/src/c#/GeneralUpdate.Core/Bootstrap/AbstractBootstrap.cs b/src/c#/GeneralUpdate.Core/Bootstrap/AbstractBootstrap.cs index 637f4677..535889a6 100644 --- a/src/c#/GeneralUpdate.Core/Bootstrap/AbstractBootstrap.cs +++ b/src/c#/GeneralUpdate.Core/Bootstrap/AbstractBootstrap.cs @@ -95,8 +95,6 @@ protected IStrategy InitStrategy() return _strategy; } - protected string GetPlatform() => _strategy.GetPlatform(); - protected IStrategy ExecuteStrategy() { if (_strategy != null) _strategy.Execute(); @@ -126,7 +124,7 @@ public TBootstrap StrategyFactory(Func strategyFactory) /// /// blacklist file name /// - public virtual TBootstrap SetBlacklist(List files = null, List fileFormats = null) + protected virtual TBootstrap SetBlacklist(List files = null, List fileFormats = null) { Packet.BlackFiles = files; Packet.BlackFormats = fileFormats; @@ -140,7 +138,7 @@ public virtual TBootstrap SetBlacklist(List files = null, List f /// Configuration Action Enumeration. /// Value /// - public virtual TBootstrap Option(UpdateOption option, T value) + protected virtual TBootstrap Option(UpdateOption option, T value) { Contract.Requires(option != null); if (value == null) @@ -154,7 +152,7 @@ public virtual TBootstrap Option(UpdateOption option, T value) return (TBootstrap)this; } - public virtual T GetOption(UpdateOption option) + protected virtual T GetOption(UpdateOption option) { try { @@ -173,19 +171,25 @@ public virtual T GetOption(UpdateOption option) #region Callback event. - public TBootstrap AddListenerMultiAllDownloadCompleted(Action callbackAction) => AddListener(callbackAction); + public TBootstrap AddListenerMultiAllDownloadCompleted(Action callbackAction) + => AddListener(callbackAction); - public TBootstrap AddListenerMultiDownloadProgress(Action callbackAction) => AddListener(callbackAction); + public TBootstrap AddListenerMultiDownloadProgress(Action callbackAction) + => AddListener(callbackAction); - public TBootstrap AddListenerMultiDownloadCompleted(Action callbackAction) => AddListener(callbackAction); + public TBootstrap AddListenerMultiDownloadCompleted(Action callbackAction) + => AddListener(callbackAction); - public TBootstrap AddListenerMultiDownloadError(Action callbackAction) => AddListener(callbackAction); + public TBootstrap AddListenerMultiDownloadError(Action callbackAction) + => AddListener(callbackAction); - public TBootstrap AddListenerMultiDownloadStatistics(Action callbackAction) => AddListener(callbackAction); + public TBootstrap AddListenerMultiDownloadStatistics(Action callbackAction) + => AddListener(callbackAction); - public TBootstrap AddListenerException(Action callbackAction) => AddListener(callbackAction); + public TBootstrap AddListenerException(Action callbackAction) + => AddListener(callbackAction); - protected TBootstrap AddListener(Action callbackAction) where TArgs : EventArgs + private TBootstrap AddListener(Action callbackAction) where TArgs : EventArgs { if (callbackAction != null) EventManager.Instance.AddListener(callbackAction); return (TBootstrap)this; diff --git a/src/c#/GeneralUpdate.Upgrad/Program.cs b/src/c#/GeneralUpdate.Upgrad/Program.cs index ac3952d2..e4d9e542 100644 --- a/src/c#/GeneralUpdate.Upgrad/Program.cs +++ b/src/c#/GeneralUpdate.Upgrad/Program.cs @@ -49,9 +49,9 @@ private static void Main(string[] args) //整个更新过程出现的任何问题都会通过这个事件通知 .AddListenerException(OnException) .Strategy() - .Option(UpdateOption.Encoding, Encoding.Default) - .Option(UpdateOption.DownloadTimeOut, 60) - .Option(UpdateOption.Format, Format.ZIP) + //.Option(UpdateOption.Encoding, Encoding.Default) + //.Option(UpdateOption.DownloadTimeOut, 60) + //.Option(UpdateOption.Format, Format.ZIP) .LaunchTaskAsync(); }); Console.Read(); From aa6eb9bc9cc3216e79d3ea4f15f10fcd651a7391 Mon Sep 17 00:00:00 2001 From: justerzhu Date: Fri, 20 Sep 2024 22:55:40 +0800 Subject: [PATCH 07/33] refactor: refactor Generalupdate.zip --- src/c#/GeneralUpdate.Client/MySample.cs | 298 -------- .../Bootstrap/.gitkeep | 0 .../GeneralUpdate.ClientCore/Driver/.gitkeep | 0 .../Bootstrap/AbstractBootstrap.cs | 227 ------- .../Bootstrap/UpdateEventArgs.cs | 112 --- .../Bootstrap/UpdateOption.cs | 233 ------- .../Bootstrap/UpdateOptionValue.cs | 29 - .../ContentProvider/FileNode.cs | 148 ---- .../ContentProvider/FileProvider-Comparer.cs | 67 -- .../ContentProvider/FileProvider-Filter.cs | 45 -- .../ContentProvider/FileProvider-Manage.cs | 49 -- .../FileProvider-Serialization.cs | 63 -- .../ContentProvider/FileProvider.cs | 115 ---- .../ContentProvider/FileTree.cs | 173 ----- .../CustomAwaiter/IAwaitable.cs | 12 - .../CustomAwaiter/IAwaiter.cs | 18 - .../CustomAwaiter/ICriticalAwaiter.cs | 12 - .../Domain/DO/Assembler/VersionAssembler.cs | 23 - .../Domain/DO/VersionConfigDO.cs | 64 -- .../Domain/DTO/Assembler/VersionAssembler.cs | 23 - .../Domain/DTO/BaseResponseDTO.cs | 11 - .../Domain/DTO/UploadReapDTO.cs | 8 - .../Domain/DTO/VersionDTO.cs | 24 - .../Domain/DTO/VersionRespDTO.cs | 27 - .../Entity/Assembler/ProcessAssembler.cs | 74 -- .../Domain/Entity/Configinfo.cs | 66 -- .../Domain/Entity/Entity.cs | 31 - .../Domain/Entity/Packet.cs | 145 ---- .../Domain/Entity/ParamsOSS.cs | 23 - .../Domain/Entity/ProcessInfo.cs | 107 --- .../Domain/Entity/VersionInfo.cs | 50 -- .../GeneralUpdate.Core/Domain/Enum/AppType.cs | 15 - .../GeneralUpdate.Core/Domain/Enum/Format.cs | 8 - .../Domain/Enum/HttpStatus.cs | 12 - .../Domain/Enum/PlatformType.cs | 48 -- .../Domain/Enum/ProgressType.cs | 45 -- .../Domain/PO/Assembler/VersionAssembler.cs | 23 - .../GeneralUpdate.Core/Domain/PO/VersionPO.cs | 33 - .../Domain/PO/WillMessagePO.cs | 81 --- .../Domain/Service/VersionService.cs | 103 --- src/c#/GeneralUpdate.Core/Domain/VO/FileVO.cs | 28 - .../Download/AbstractTask.cs | 339 ---------- .../Download/AbstractTaskManager.cs | 13 - .../Download/DownloadManager.cs | 164 ----- .../Download/DownloadTask.cs | 216 ------ src/c#/GeneralUpdate.Core/Download/ITask.cs | 5 - .../Download/ITaskManger.cs | 21 - .../Events/CommonArgs/ExceptionEventArgs.cs | 18 - .../GeneralUpdate.Core/Events/EventManager.cs | 166 ----- .../Events/IEventManager.cs | 37 - .../MutiAllDownloadCompletedEventArgs.cs | 21 - .../MutiDownloadCompletedEventArgs.cs | 19 - .../MutiDownloadErrorEventArgs.cs | 17 - .../MutiDownloadProgressChangedEventArgs.cs | 55 -- .../MutiDownloadStatisticsEventArgs.cs | 20 - .../Events/OSSArgs/OSSDownloadArgs.cs | 23 - .../Exceptions/CustomArgs/ExceptionArgs.cs | 11 - .../CustomArgs/HttpExceptionArgs.cs | 35 - .../CustomArgs/PatchDirtyExceptionArgs.cs | 24 - .../CustomArgs/UnZipExceptionArgs.cs | 24 - .../CustomArgs/UpdateExceptionArgs.cs | 32 - .../CustomException/GeneralUpdateException.cs | 54 -- .../Exceptions/ThrowExceptionUtility.cs | 57 -- .../HashAlgorithms/HashAlgorithmBase.cs | 32 - .../HashAlgorithms/Md5HashAlgorithm.cs | 9 - .../HashAlgorithms/Sha1HashAlgorithm.cs | 9 - .../HashAlgorithms/Sha256HashAlgorithm.cs | 9 - .../Internal/ExceptionEventArgs.cs | 15 + .../Internal/OSSDownloadArgs.cs | 6 + .../Pipeline/DriverMiddleware.cs | 6 + .../Pipeline/HashMiddleware.cs | 29 + .../Pipeline/PatchMiddleware.cs | 20 + .../Pipeline/ZipMiddleware.cs | 39 ++ .../DynamicallyAccessedMemberTypes.cs | 38 -- .../DynamicallyAccessedMembersAttribute.cs | 15 - .../Pipelines/Context/BaseContext.cs | 88 --- .../Pipelines/Middleware/DriveMiddleware.cs | 76 --- .../Pipelines/Middleware/HashMiddleware.cs | 30 - .../Pipelines/Middleware/IMiddleware.cs | 10 - .../Middleware/MiddlewareExtensions.cs | 64 -- .../Pipelines/Middleware/MiddlewareStack.cs | 62 -- .../Pipelines/Middleware/PatchMiddleware.cs | 22 - .../Pipelines/Middleware/ZipMiddleware.cs | 68 -- .../ActivatorMiddlewareResolver.cs | 9 - .../Pipelines/Pipeline/IPipelineBuilder.cs | 21 - .../Pipelines/PipelineBuilder.cs | 48 -- .../Strategys/AbstractStrategy.cs | 23 - .../GeneralUpdate.Core/Strategys/IStrategy.cs | 40 -- .../{PlatformLinux => }/LinuxStrategy.cs | 0 .../{PlatformWindows => }/WindowsStrategy.cs | 0 .../{GStream => Binary}/BZip2Constants.cs | 0 .../{GStream => Binary}/BZip2InputStream.cs | 0 .../{GStream => Binary}/BZip2OutputStream.cs | 0 .../Binary/BinaryHandle.cs | 640 ------------------ .../Binary/IBinary.cs | 25 - .../{GStream => Binary}/IChecksum.cs | 0 .../{GStream => Binary}/StrangeCRC.cs | 0 .../ContentProvider/.gitkeep | 0 .../CustomAwaiter/.gitkeep | 0 .../Domain/Entity/.gitkeep | 0 .../Domain/PO/.gitkeep | 0 .../Exceptions/.gitkeep | 0 .../Exceptions/CustomArgs/.gitkeep | 0 .../Exceptions/CustomException/.gitkeep | 0 .../GeneralUpdate.Zip.csproj | 7 - 105 files changed, 115 insertions(+), 5389 deletions(-) delete mode 100644 src/c#/GeneralUpdate.Client/MySample.cs delete mode 100644 src/c#/GeneralUpdate.ClientCore/Bootstrap/.gitkeep delete mode 100644 src/c#/GeneralUpdate.ClientCore/Driver/.gitkeep delete mode 100644 src/c#/GeneralUpdate.Core/Bootstrap/AbstractBootstrap.cs delete mode 100644 src/c#/GeneralUpdate.Core/Bootstrap/UpdateEventArgs.cs delete mode 100644 src/c#/GeneralUpdate.Core/Bootstrap/UpdateOption.cs delete mode 100644 src/c#/GeneralUpdate.Core/Bootstrap/UpdateOptionValue.cs delete mode 100644 src/c#/GeneralUpdate.Core/ContentProvider/FileNode.cs delete mode 100644 src/c#/GeneralUpdate.Core/ContentProvider/FileProvider-Comparer.cs delete mode 100644 src/c#/GeneralUpdate.Core/ContentProvider/FileProvider-Filter.cs delete mode 100644 src/c#/GeneralUpdate.Core/ContentProvider/FileProvider-Manage.cs delete mode 100644 src/c#/GeneralUpdate.Core/ContentProvider/FileProvider-Serialization.cs delete mode 100644 src/c#/GeneralUpdate.Core/ContentProvider/FileProvider.cs delete mode 100644 src/c#/GeneralUpdate.Core/ContentProvider/FileTree.cs delete mode 100644 src/c#/GeneralUpdate.Core/CustomAwaiter/IAwaitable.cs delete mode 100644 src/c#/GeneralUpdate.Core/CustomAwaiter/IAwaiter.cs delete mode 100644 src/c#/GeneralUpdate.Core/CustomAwaiter/ICriticalAwaiter.cs delete mode 100644 src/c#/GeneralUpdate.Core/Domain/DO/Assembler/VersionAssembler.cs delete mode 100644 src/c#/GeneralUpdate.Core/Domain/DO/VersionConfigDO.cs delete mode 100644 src/c#/GeneralUpdate.Core/Domain/DTO/Assembler/VersionAssembler.cs delete mode 100644 src/c#/GeneralUpdate.Core/Domain/DTO/BaseResponseDTO.cs delete mode 100644 src/c#/GeneralUpdate.Core/Domain/DTO/UploadReapDTO.cs delete mode 100644 src/c#/GeneralUpdate.Core/Domain/DTO/VersionDTO.cs delete mode 100644 src/c#/GeneralUpdate.Core/Domain/DTO/VersionRespDTO.cs delete mode 100644 src/c#/GeneralUpdate.Core/Domain/Entity/Assembler/ProcessAssembler.cs delete mode 100644 src/c#/GeneralUpdate.Core/Domain/Entity/Configinfo.cs delete mode 100644 src/c#/GeneralUpdate.Core/Domain/Entity/Entity.cs delete mode 100644 src/c#/GeneralUpdate.Core/Domain/Entity/Packet.cs delete mode 100644 src/c#/GeneralUpdate.Core/Domain/Entity/ParamsOSS.cs delete mode 100644 src/c#/GeneralUpdate.Core/Domain/Entity/ProcessInfo.cs delete mode 100644 src/c#/GeneralUpdate.Core/Domain/Entity/VersionInfo.cs delete mode 100644 src/c#/GeneralUpdate.Core/Domain/Enum/AppType.cs delete mode 100644 src/c#/GeneralUpdate.Core/Domain/Enum/Format.cs delete mode 100644 src/c#/GeneralUpdate.Core/Domain/Enum/HttpStatus.cs delete mode 100644 src/c#/GeneralUpdate.Core/Domain/Enum/PlatformType.cs delete mode 100644 src/c#/GeneralUpdate.Core/Domain/Enum/ProgressType.cs delete mode 100644 src/c#/GeneralUpdate.Core/Domain/PO/Assembler/VersionAssembler.cs delete mode 100644 src/c#/GeneralUpdate.Core/Domain/PO/VersionPO.cs delete mode 100644 src/c#/GeneralUpdate.Core/Domain/PO/WillMessagePO.cs delete mode 100644 src/c#/GeneralUpdate.Core/Domain/Service/VersionService.cs delete mode 100644 src/c#/GeneralUpdate.Core/Domain/VO/FileVO.cs delete mode 100644 src/c#/GeneralUpdate.Core/Download/AbstractTask.cs delete mode 100644 src/c#/GeneralUpdate.Core/Download/AbstractTaskManager.cs delete mode 100644 src/c#/GeneralUpdate.Core/Download/DownloadManager.cs delete mode 100644 src/c#/GeneralUpdate.Core/Download/DownloadTask.cs delete mode 100644 src/c#/GeneralUpdate.Core/Download/ITask.cs delete mode 100644 src/c#/GeneralUpdate.Core/Download/ITaskManger.cs delete mode 100644 src/c#/GeneralUpdate.Core/Events/CommonArgs/ExceptionEventArgs.cs delete mode 100644 src/c#/GeneralUpdate.Core/Events/EventManager.cs delete mode 100644 src/c#/GeneralUpdate.Core/Events/IEventManager.cs delete mode 100644 src/c#/GeneralUpdate.Core/Events/MultiEventArgs/MutiAllDownloadCompletedEventArgs.cs delete mode 100644 src/c#/GeneralUpdate.Core/Events/MultiEventArgs/MutiDownloadCompletedEventArgs.cs delete mode 100644 src/c#/GeneralUpdate.Core/Events/MultiEventArgs/MutiDownloadErrorEventArgs.cs delete mode 100644 src/c#/GeneralUpdate.Core/Events/MultiEventArgs/MutiDownloadProgressChangedEventArgs.cs delete mode 100644 src/c#/GeneralUpdate.Core/Events/MultiEventArgs/MutiDownloadStatisticsEventArgs.cs delete mode 100644 src/c#/GeneralUpdate.Core/Events/OSSArgs/OSSDownloadArgs.cs delete mode 100644 src/c#/GeneralUpdate.Core/Exceptions/CustomArgs/ExceptionArgs.cs delete mode 100644 src/c#/GeneralUpdate.Core/Exceptions/CustomArgs/HttpExceptionArgs.cs delete mode 100644 src/c#/GeneralUpdate.Core/Exceptions/CustomArgs/PatchDirtyExceptionArgs.cs delete mode 100644 src/c#/GeneralUpdate.Core/Exceptions/CustomArgs/UnZipExceptionArgs.cs delete mode 100644 src/c#/GeneralUpdate.Core/Exceptions/CustomArgs/UpdateExceptionArgs.cs delete mode 100644 src/c#/GeneralUpdate.Core/Exceptions/CustomException/GeneralUpdateException.cs delete mode 100644 src/c#/GeneralUpdate.Core/Exceptions/ThrowExceptionUtility.cs delete mode 100644 src/c#/GeneralUpdate.Core/HashAlgorithms/HashAlgorithmBase.cs delete mode 100644 src/c#/GeneralUpdate.Core/HashAlgorithms/Md5HashAlgorithm.cs delete mode 100644 src/c#/GeneralUpdate.Core/HashAlgorithms/Sha1HashAlgorithm.cs delete mode 100644 src/c#/GeneralUpdate.Core/HashAlgorithms/Sha256HashAlgorithm.cs create mode 100644 src/c#/GeneralUpdate.Core/Internal/ExceptionEventArgs.cs create mode 100644 src/c#/GeneralUpdate.Core/Internal/OSSDownloadArgs.cs create mode 100644 src/c#/GeneralUpdate.Core/Pipeline/DriverMiddleware.cs create mode 100644 src/c#/GeneralUpdate.Core/Pipeline/HashMiddleware.cs create mode 100644 src/c#/GeneralUpdate.Core/Pipeline/PatchMiddleware.cs create mode 100644 src/c#/GeneralUpdate.Core/Pipeline/ZipMiddleware.cs delete mode 100644 src/c#/GeneralUpdate.Core/Pipelines/Attributes/DynamicallyAccessedMemberTypes.cs delete mode 100644 src/c#/GeneralUpdate.Core/Pipelines/Attributes/DynamicallyAccessedMembersAttribute.cs delete mode 100644 src/c#/GeneralUpdate.Core/Pipelines/Context/BaseContext.cs delete mode 100644 src/c#/GeneralUpdate.Core/Pipelines/Middleware/DriveMiddleware.cs delete mode 100644 src/c#/GeneralUpdate.Core/Pipelines/Middleware/HashMiddleware.cs delete mode 100644 src/c#/GeneralUpdate.Core/Pipelines/Middleware/IMiddleware.cs delete mode 100644 src/c#/GeneralUpdate.Core/Pipelines/Middleware/MiddlewareExtensions.cs delete mode 100644 src/c#/GeneralUpdate.Core/Pipelines/Middleware/MiddlewareStack.cs delete mode 100644 src/c#/GeneralUpdate.Core/Pipelines/Middleware/PatchMiddleware.cs delete mode 100644 src/c#/GeneralUpdate.Core/Pipelines/Middleware/ZipMiddleware.cs delete mode 100644 src/c#/GeneralUpdate.Core/Pipelines/MiddlewareResolver/ActivatorMiddlewareResolver.cs delete mode 100644 src/c#/GeneralUpdate.Core/Pipelines/Pipeline/IPipelineBuilder.cs delete mode 100644 src/c#/GeneralUpdate.Core/Pipelines/PipelineBuilder.cs delete mode 100644 src/c#/GeneralUpdate.Core/Strategys/AbstractStrategy.cs delete mode 100644 src/c#/GeneralUpdate.Core/Strategys/IStrategy.cs rename src/c#/GeneralUpdate.Core/Strategys/{PlatformLinux => }/LinuxStrategy.cs (100%) rename src/c#/GeneralUpdate.Core/Strategys/{PlatformWindows => }/WindowsStrategy.cs (100%) rename src/c#/GeneralUpdate.Differential/{GStream => Binary}/BZip2Constants.cs (100%) rename src/c#/GeneralUpdate.Differential/{GStream => Binary}/BZip2InputStream.cs (100%) rename src/c#/GeneralUpdate.Differential/{GStream => Binary}/BZip2OutputStream.cs (100%) delete mode 100644 src/c#/GeneralUpdate.Differential/Binary/BinaryHandle.cs delete mode 100644 src/c#/GeneralUpdate.Differential/Binary/IBinary.cs rename src/c#/GeneralUpdate.Differential/{GStream => Binary}/IChecksum.cs (100%) rename src/c#/GeneralUpdate.Differential/{GStream => Binary}/StrangeCRC.cs (100%) delete mode 100644 src/c#/GeneralUpdate.Differential/ContentProvider/.gitkeep delete mode 100644 src/c#/GeneralUpdate.Differential/CustomAwaiter/.gitkeep delete mode 100644 src/c#/GeneralUpdate.Differential/Domain/Entity/.gitkeep delete mode 100644 src/c#/GeneralUpdate.Differential/Domain/PO/.gitkeep delete mode 100644 src/c#/GeneralUpdate.Differential/Exceptions/.gitkeep delete mode 100644 src/c#/GeneralUpdate.Differential/Exceptions/CustomArgs/.gitkeep delete mode 100644 src/c#/GeneralUpdate.Differential/Exceptions/CustomException/.gitkeep diff --git a/src/c#/GeneralUpdate.Client/MySample.cs b/src/c#/GeneralUpdate.Client/MySample.cs deleted file mode 100644 index ef63fcc8..00000000 --- a/src/c#/GeneralUpdate.Client/MySample.cs +++ /dev/null @@ -1,298 +0,0 @@ -using GeneralUpdate.ClientCore; -using GeneralUpdate.Differential; -using System.Diagnostics; -using System.Text; -using GeneralUpdate.ClientCore.Internal; -using GeneralUpdate.ClientCore.Strategys; -using GeneralUpdate.Common.Download; -using GeneralUpdate.Common.Internal.Bootstrap; -using GeneralUpdate.Common.Shared.Object; -using VersionInfo = GeneralUpdate.Core.Domain.Entity.VersionInfo; - -namespace GeneralUpdate.Client -{ - internal class MySample - { - #region 推送功能 - - private const string baseUrl = @"http://127.0.0.1:5000"; - private const string hubName = "versionhub"; - - internal MySample() - { - //Receive sample code pushed by the server - //VersionHub.Instance.Subscribe($"{baseUrl}/{hubName}", "TESTNAME", new Action(GetMessage)); - } - - //Receive sample code pushed by the server - private async void GetMessage(string msg) - { - var isUpdate = true; - if (isUpdate) Upgrade(); - } - - #endregion 推送功能 - - #region 常规更新 - - public async Task Upgrade() - { - //Task.Run(async () => - //{ - // var url = "http://192.168.50.203"; - // var appName = "GeneralUpdate.Client"; - // var version = "1.0.0.0"; - // var versionFileName = "version.json"; - // ParamsOSS @params = new ParamsOSS(url, appName, version, versionFileName); - // await GeneralClientOSS.Start(@params); - //}); - - //ClientStrategy该更新策略将完成1.自动升级组件自更新 2.启动更新组件 3.配置好ClientParameter无需再像之前的版本写args数组进程通讯了。 - //generalClientBootstrap.Config(baseUrl, "B8A7FADD-386C-46B0-B283-C9F963420C7C"). - var configinfo = GetWindowsConfigInfo(); - var generalClientBootstrap = await new GeneralClientBootstrap() - //单个或多个更新包下载通知事件 - .AddListenerMultiDownloadProgress(OnMultiDownloadProgressChanged) - //单个或多个更新包下载速度、剩余下载事件、当前下载版本信息通知事件 - .AddListenerMultiDownloadStatistics(OnMultiDownloadStatistics) - //单个或多个更新包下载完成 - .AddListenerMultiDownloadCompleted(OnMultiDownloadCompleted) - //完成所有的下载任务通知 - .AddListenerMultiAllDownloadCompleted(OnMultiAllDownloadCompleted) - //下载过程出现的异常通知 - .AddListenerMultiDownloadError(OnMultiDownloadError) - //整个更新过程出现的任何问题都会通过这个事件通知 - .AddListenerException(OnException) - .SetConfig(configinfo) - .Option(UpdateOption.DownloadTimeOut, 60) - .Option(UpdateOption.Encoding, Encoding.Default) - .Option(UpdateOption.Format, Format.ZIP) - //开启驱动更新 - //.Option(UpdateOption.Drive, true) - //注入一个func让用户决定是否跳过本次更新,如果是强制更新则不生效 - //.SetCustomSkipOption(ShowCustomOption) - //注入一个自定义方法集合,该集合会在更新启动前执行。执行自定义方法列表如果出现任何异常,将通过异常订阅通知。(推荐在更新之前检查当前软件环境) - //.AddCustomOption(new List>() { () => Check1(), () => Check2() }) - //默认黑名单文件: { "Newtonsoft.Json.dll" } 默认黑名单文件扩展名: { ".patch", ".7z", ".zip", ".rar", ".tar" , ".json" } - //如果不需要扩展,需要重新传入黑名单集合来覆盖。 - //.SetBlacklist(GetBlackFiles(), GetBlackFormats()) - .LaunchAsync(); - } - - private bool Check1() => true; - - private bool Check2() => true; - - private List GetBlackFiles() - { - var blackFiles = new List(); - blackFiles.Add("MainApp"); - return blackFiles; - } - - private List GetBlackFormats() - { - var blackFormats = new List(); - blackFormats.Add(".zip"); - return blackFormats; - } - - /// - /// 获取Windows平台所需的配置参数 - /// - /// - private Configinfo GetWindowsConfigInfo() - { - //该对象用于主程序客户端与更新组件进程之间交互用的对象 - var config = new Configinfo(); - //本机的客户端程序应用地址 - config.InstallPath = @"D:\packet\source"; - //更新公告网页 - config.UpdateLogUrl = "https://www.baidu.com/"; - //客户端当前版本号 - config.ClientVersion = "1.1.1.1"; - //客户端类型:1.主程序客户端 2.更新组件 - config.AppType = AppType.UpgradeApp; - //指定应用密钥,用于区分客户端应用 - config.AppSecretKey = "B8A7FADD-386C-46B0-B283-C9F963420C7C"; - //更新组件更新包下载地址 - config.UpdateUrl = $"{baseUrl}/versions/{config.AppType}/{config.ClientVersion}/{config.AppSecretKey}"; - //更新程序exe名称 - config.AppName = "GeneralUpdate.Core"; - //主程序客户端exe名称 - config.MainAppName = "GeneralUpdate.ClientCore"; - //主程序信息 - var mainVersion = "1.1.1.1"; - //主程序客户端更新包下载地址 - config.MainUpdateUrl = $"{baseUrl}/versions/{AppType.ClientApp}/{mainVersion}/{config.AppSecretKey}"; - return config; - } - - /// - /// 获取Android平台所需要的参数 - /// - /// - private Configinfo GetAndroidConfigInfo() - { - var config = new Configinfo(); - config.InstallPath = System.Threading.Thread.GetDomain().BaseDirectory; - //主程序客户端当前版本号 - config.ClientVersion = "1.0.0.0"; //VersionTracking.Default.CurrentVersion.ToString(); - config.AppType = AppType.ClientApp; - config.AppSecretKey = "41A54379-C7D6-4920-8768-21A3468572E5"; - //主程序客户端exe名称 - config.MainAppName = "GeneralUpdate.ClientCore"; - //主程序信息 - var mainVersion = "1.1.1.1"; - config.MainUpdateUrl = $"{baseUrl}/versions/{AppType.ClientApp}/{mainVersion}/{config.AppSecretKey}"; - return config; - } - - /// - /// 让用户决定是否跳过本次更新 - /// - /// - private async Task ShowCustomOption() - { - return await Task.FromResult(true); - } - - private void OnMultiDownloadStatistics(object sender, MultiDownloadStatisticsEventArgs e) - { - //e.Remaining 剩余下载时间 - //e.Speed 下载速度 - //e.Version 当前下载的版本信息 - } - - private void OnMultiDownloadProgressChanged(object sender, MultiDownloadProgressChangedEventArgs e) - { - //e.TotalBytesToReceive 当前更新包需要下载的总大小 - //e.ProgressValue 当前进度值 - //e.ProgressPercentage 当前进度的百分比 - //e.Version 当前下载的版本信息 - //e.Type 当前正在执行的操作 1.ProgressType.Check 检查版本信息中 2.ProgressType.Donwload 正在下载当前版本 3. ProgressType.Updatefile 更新当前版本 4. ProgressType.Done更新完成 5.ProgressType.Fail 更新失败 - //e.BytesReceived 已下载大小 - DispatchMessage($"{e.ProgressPercentage}%"); - //MyProgressBar.ProgressTo(e.ProgressValue, 100, Easing.Default); - } - - private void OnException(object sender, ExceptionEventArgs e) - { - //DispatchMessage(e.Exception.Message); - } - - private void OnMultiAllDownloadCompleted(object sender, MultiAllDownloadCompletedEventArgs e) - { - //e.FailedVersions; 如果出现下载失败则会把下载错误的版本、错误原因统计到该集合当中。 - DispatchMessage($"Is all download completed {e.IsAllDownloadCompleted}."); - } - - private void OnMultiDownloadCompleted(object sender, MultiDownloadCompletedEventArgs e) - { - var info = e.Version as VersionInfo; - DispatchMessage($"{info.Name} download completed."); - } - - private void OnMultiDownloadError(object sender, MultiDownloadErrorEventArgs e) - { - var info = e.Version as VersionInfo; - DispatchMessage($"{info.Name} error!"); - } - - private void DispatchMessage(string message) - { - } - - #endregion 常规更新 - - #region 测试二进制更新包整理 - - public async Task TestDifferentialClean() - { - var path1 = "D:\\packet\\source"; - var path2 = "D:\\packet\\target"; - var path3 = "D:\\packet\\patchs"; - await DifferentialCore.Instance.Clean(path1, path2, path3); - } - - public async Task TestDifferentialDirty() - { - var path1 = "D:\\packet\\source"; - var path2 = "D:\\packet\\patchs"; - await DifferentialCore.Instance.Dirty(path1, path2); - } - - #endregion 测试二进制更新包整理 - - #region 测试驱动功能 - - /// - /// Identifies all folders containing driver files in the specified directory and returns the directory collection. - /// - /// - /// - private List GetAllDriverDirectories(string path) - { - var driverDirectories = new HashSet(); - try - { - foreach (string filePath in Directory.GetFiles(path)) - { - if (IsDriverFile(filePath)) - driverDirectories.Add(filePath); - } - - foreach (string directory in Directory.GetDirectories(path)) - { - driverDirectories.UnionWith(GetAllDriverDirectories(directory)); - } - } - catch (UnauthorizedAccessException) - { - Trace.WriteLine("No access directory:" + path); - } - catch (PathTooLongException) - { - Trace.WriteLine("Path overlength:" + path); - } - - return new List(driverDirectories); - } - - /// - /// Match the driver installation boot file. - /// - /// - /// - private bool IsDriverFile(string filePath) => - string.Equals(Path.GetExtension(filePath), ".inf", StringComparison.OrdinalIgnoreCase); - - #endregion 测试驱动功能 - - #region 文件管理测试 - - public void TestFileProvider() - { - var sourcePath = "D:\\packet\\source"; - var targetPath = "D:\\packet\\target"; - var resultPath = "D:\\packet\\patchs"; - - //FileProvider fileProvider = new FileProvider(); - //var list = fileProvider.ExecuteOperation(sourcePath, targetPath,new List(), new List()); - //foreach (var item in list) { - // Console.WriteLine(item); - //} - //Console.WriteLine("total" + list.Count()); - //Console.WriteLine("--------------------------------------"); - //FileProvider fileProvider1 = new FileProvider(); - //var list1 = fileProvider1.ExecuteOperation(targetPath, sourcePath, new List(), new List()); - //foreach (var item in list1) - //{ - // Console.WriteLine(item); - //} - //Console.WriteLine("total" + list1.Count()); - } - - #endregion 文件管理测试 - } -} \ No newline at end of file diff --git a/src/c#/GeneralUpdate.ClientCore/Bootstrap/.gitkeep b/src/c#/GeneralUpdate.ClientCore/Bootstrap/.gitkeep deleted file mode 100644 index e69de29b..00000000 diff --git a/src/c#/GeneralUpdate.ClientCore/Driver/.gitkeep b/src/c#/GeneralUpdate.ClientCore/Driver/.gitkeep deleted file mode 100644 index e69de29b..00000000 diff --git a/src/c#/GeneralUpdate.Core/Bootstrap/AbstractBootstrap.cs b/src/c#/GeneralUpdate.Core/Bootstrap/AbstractBootstrap.cs deleted file mode 100644 index 535889a6..00000000 --- a/src/c#/GeneralUpdate.Core/Bootstrap/AbstractBootstrap.cs +++ /dev/null @@ -1,227 +0,0 @@ -using GeneralUpdate.Core.ContentProvider; -using GeneralUpdate.Core.Domain.Entity; -using GeneralUpdate.Core.Domain.Enum; -using GeneralUpdate.Core.Download; -using GeneralUpdate.Core.Events; -using GeneralUpdate.Core.Events.CommonArgs; -using GeneralUpdate.Core.Events.MultiEventArgs; -using GeneralUpdate.Core.Strategys; -using System; -using System.Collections.Concurrent; -using System.Collections.Generic; -using System.Diagnostics.Contracts; -using System.IO; -using System.Text; - -namespace GeneralUpdate.Core.Bootstrap -{ - public abstract class AbstractBootstrap - where TBootstrap : AbstractBootstrap - where TStrategy : IStrategy - { - #region Private Members - - private readonly ConcurrentDictionary _options; - private volatile Func _strategyFactory; - private Packet _packet; - private IStrategy _strategy; - private const string EXECUTABLE_FILE = ".exe"; - - #endregion Private Members - - #region Constructors - - protected internal AbstractBootstrap() => this._options = new ConcurrentDictionary(); - - #endregion Constructors - - #region Public Properties - - public Packet Packet - { - get { return _packet ?? (_packet = new Packet()); } - set { _packet = value; } - } - - #endregion Public Properties - - #region Methods - - /// - /// Launch udpate. - /// - /// - public virtual TBootstrap LaunchAsync() - { - try - { - InitStrategy(); - //When the upgrade stops and does not need to be updated, the client needs to be updated. Start the upgrade assistant directly. - if (!Packet.IsUpgradeUpdate && Packet.IsMainUpdate) _strategy.StartApp(Packet.AppName, Packet.AppType); - Packet.Format = $".{GetOption(UpdateOption.Format) ?? Format.ZIP}"; - Packet.Encoding = GetOption(UpdateOption.Encoding) ?? Encoding.Default; - Packet.DownloadTimeOut = GetOption(UpdateOption.DownloadTimeOut); - Packet.AppName = $"{Packet.AppName ?? GetOption(UpdateOption.MainApp)}{EXECUTABLE_FILE}"; - Packet.TempPath = $"{FileProvider.GetTempDirectory(Packet.LastVersion)}{Path.DirectorySeparatorChar}"; - Packet.DriveEnabled = GetOption(UpdateOption.Drive) ?? false; - Packet.WillMessageEnabled = GetOption(UpdateOption.WillMessage) ?? false; - var manager = new DownloadManager(Packet.TempPath, Packet.Format, Packet.DownloadTimeOut); - manager.MultiAllDownloadCompleted += OnMultiAllDownloadCompleted; - manager.MultiDownloadCompleted += OnMultiDownloadCompleted; - manager.MultiDownloadError += OnMultiDownloadError; - manager.MultiDownloadProgressChanged += OnMultiDownloadProgressChanged; - manager.MultiDownloadStatistics += OnMultiDownloadStatistics; - Packet.UpdateVersions.ForEach((v) => manager.Add(new DownloadTask(manager, v))); - manager.LaunchTaskAsync(); - } - catch (Exception ex) - { - EventManager.Instance.Dispatch>(this, new ExceptionEventArgs(ex)); - } - return (TBootstrap)this; - } - - #region Strategy - - protected IStrategy InitStrategy() - { - if (_strategy == null) - { - Validate(); - _strategy = _strategyFactory(); - Packet.Platform = _strategy.GetPlatform(); - _strategy.Create(Packet); - } - return _strategy; - } - - protected IStrategy ExecuteStrategy() - { - if (_strategy != null) _strategy.Execute(); - return _strategy; - } - - public virtual TBootstrap Validate() - { - if (this._strategyFactory == null) throw new InvalidOperationException("Strategy or strategy factory not set."); - return (TBootstrap)this; - } - - public virtual TBootstrap Strategy() where T : TStrategy, new() => this.StrategyFactory(() => new T()); - - public TBootstrap StrategyFactory(Func strategyFactory) - { - this._strategyFactory = strategyFactory; - return (TBootstrap)this; - } - - #endregion Strategy - - #region Config option. - - /// - /// Files in the blacklist will skip the update. - /// - /// blacklist file name - /// - protected virtual TBootstrap SetBlacklist(List files = null, List fileFormats = null) - { - Packet.BlackFiles = files; - Packet.BlackFormats = fileFormats; - return (TBootstrap)this; - } - - /// - /// Setting update configuration. - /// - /// - /// Configuration Action Enumeration. - /// Value - /// - protected virtual TBootstrap Option(UpdateOption option, T value) - { - Contract.Requires(option != null); - if (value == null) - { - this._options.TryRemove(option, out _); - } - else - { - this._options[option] = new UpdateOptionValue(option, value); - } - return (TBootstrap)this; - } - - protected virtual T GetOption(UpdateOption option) - { - try - { - if (_options == null || _options.Count == 0) return default(T); - var val = _options[option]; - if (val != null) return (T)val.GetValue(); - return default(T); - } - catch - { - return default(T); - } - } - - #endregion Config option. - - #region Callback event. - - public TBootstrap AddListenerMultiAllDownloadCompleted(Action callbackAction) - => AddListener(callbackAction); - - public TBootstrap AddListenerMultiDownloadProgress(Action callbackAction) - => AddListener(callbackAction); - - public TBootstrap AddListenerMultiDownloadCompleted(Action callbackAction) - => AddListener(callbackAction); - - public TBootstrap AddListenerMultiDownloadError(Action callbackAction) - => AddListener(callbackAction); - - public TBootstrap AddListenerMultiDownloadStatistics(Action callbackAction) - => AddListener(callbackAction); - - public TBootstrap AddListenerException(Action callbackAction) - => AddListener(callbackAction); - - private TBootstrap AddListener(Action callbackAction) where TArgs : EventArgs - { - if (callbackAction != null) EventManager.Instance.AddListener(callbackAction); - return (TBootstrap)this; - } - - private void OnMultiDownloadStatistics(object sender, MultiDownloadStatisticsEventArgs e) - => EventManager.Instance.Dispatch>(sender, e); - - private void OnMultiDownloadProgressChanged(object sender, MultiDownloadProgressChangedEventArgs e) - => EventManager.Instance.Dispatch>(sender, e); - - private void OnMultiDownloadCompleted(object sender, MultiDownloadCompletedEventArgs e) - => EventManager.Instance.Dispatch>(sender, e); - - private void OnMultiDownloadError(object sender, MultiDownloadErrorEventArgs e) - => EventManager.Instance.Dispatch>(sender, e); - - private void OnMultiAllDownloadCompleted(object sender, MultiAllDownloadCompletedEventArgs e) - { - try - { - EventManager.Instance.Dispatch>(sender, e); - ExecuteStrategy(); - } - catch (Exception ex) - { - EventManager.Instance.Dispatch>(this, new ExceptionEventArgs(ex)); - } - } - - #endregion Callback event. - - #endregion Methods - } -} \ No newline at end of file diff --git a/src/c#/GeneralUpdate.Core/Bootstrap/UpdateEventArgs.cs b/src/c#/GeneralUpdate.Core/Bootstrap/UpdateEventArgs.cs deleted file mode 100644 index 13c09a3b..00000000 --- a/src/c#/GeneralUpdate.Core/Bootstrap/UpdateEventArgs.cs +++ /dev/null @@ -1,112 +0,0 @@ -using GeneralUpdate.Core.Domain.Enum; -using System; -using System.Collections.Generic; -using System.ComponentModel; - -namespace GeneralUpdate.Core.Bootstrap -{ - public class DownloadProgressChangedEventArgsEx : EventArgs - { - public long BytesReceived { get; private set; } - - public long TotalBytesToReceive { get; private set; } - - public float ProgressPercentage { get; private set; } - - public object UserState { get; set; } - - public DownloadProgressChangedEventArgsEx(long received, long toReceive, float progressPercentage, object userState) - { - BytesReceived = received; - TotalBytesToReceive = toReceive; - ProgressPercentage = progressPercentage; - UserState = userState; - } - } - - public class ExceptionEventArgs : EventArgs - { - public Exception Exception { get; set; } - - public ExceptionEventArgs(Exception exception) - { - Exception = exception; - } - } - - #region Muti - - public class MutiDownloadStatisticsEventArgs : EventArgs - { - public MutiDownloadStatisticsEventArgs(object version,DateTime remaining,string speed) - { - Version = version; - Remaining = remaining; - Speed = speed; - } - - public object Version { get; set; } - - public DateTime Remaining { get; set; } - - public string Speed { get; set; } - } - - public class MutiDownloadProgressChangedEventArgs : DownloadProgressChangedEventArgsEx - { - public ProgressType Type { get; set; } - - public object Version { get; set; } - - public string Message { get; set; } - - public double ProgressValue { get; set; } - - public MutiDownloadProgressChangedEventArgs(object version, ProgressType type, string message, long received = 0, long toReceive = 0, float progressPercentage = 0, object userState = null) - : base(received, toReceive, progressPercentage, userState) - { - ProgressValue = progressPercentage; - Version = version; - Message = message; - Type = type; - } - } - - public class MutiDownloadCompletedEventArgs : AsyncCompletedEventArgs - { - public object Version { get; set; } - - public MutiDownloadCompletedEventArgs(object version, Exception error, bool cancelled, object userState) : base(error, cancelled, userState) - { - Version = version; - } - } - - public class MutiAllDownloadCompletedEventArgs : EventArgs - { - public bool IsAllDownloadCompleted { get; set; } - - public IList> FailedVersions { get; set; } - - public MutiAllDownloadCompletedEventArgs(bool isAllDownloadCompleted, IList> failedVersions) - { - IsAllDownloadCompleted = isAllDownloadCompleted; - FailedVersions = failedVersions; - } - } - - public class MutiDownloadErrorEventArgs : EventArgs - { - public Exception Exception { get; set; } - - public object Version { get; set; } - - public MutiDownloadErrorEventArgs(Exception exception, object updateVersion) - { - Exception = exception; - Version = updateVersion; - } - } - - #endregion Muti -} \ No newline at end of file diff --git a/src/c#/GeneralUpdate.Core/Bootstrap/UpdateOption.cs b/src/c#/GeneralUpdate.Core/Bootstrap/UpdateOption.cs deleted file mode 100644 index 46e3c09a..00000000 --- a/src/c#/GeneralUpdate.Core/Bootstrap/UpdateOption.cs +++ /dev/null @@ -1,233 +0,0 @@ -using System; -using System.Collections.Generic; -using System.Diagnostics.Contracts; -using System.Text; -using System.Threading; - -namespace GeneralUpdate.Core.Bootstrap -{ - public abstract class UpdateOption : AbstractConstant - { - private class UpdateOptionPool : ConstantPool - { - protected override IConstant NewConstant(int id, string name) => new UpdateOption(id, name); - } - - private static readonly UpdateOptionPool Pool = new UpdateOptionPool(); - - public static UpdateOption ValueOf(string name) => (UpdateOption)Pool.ValueOf(name); - - #region parameter configuration - - /// - /// Update the file format of the package. - /// - public static readonly UpdateOption Format = ValueOf("COMPRESSFORMAT"); - - /// - /// Compress encoding. - /// - public static readonly UpdateOption Encoding = ValueOf("COMPRESSENCODING"); - - /// - /// Main program name. - /// - public static readonly UpdateOption MainApp = ValueOf("MAINAPP"); - - /// - /// Timeout period (unit: second). If this parameter is not specified, the default timeout period is 30 seconds. - /// - public static readonly UpdateOption DownloadTimeOut = ValueOf("DOWNLOADTIMEOUT"); - - /// - /// Whether to enable the driver upgrade function. - /// - public static readonly UpdateOption Drive = ValueOf("DRIVE"); - - /// - /// Whether open note function, if you want to start needs to be synchronized to deploy 'GeneralUpdate. SystemService' service. - /// - public static readonly UpdateOption WillMessage = ValueOf("WILLMESSAGE"); - - #endregion parameter configuration - - internal UpdateOption(int id, string name) - : base(id, name) { } - - public abstract bool Set(IUpdateConfiguration configuration, object value); - } - - public sealed class UpdateOption : UpdateOption - { - internal UpdateOption(int id, string name) - : base(id, name) - { - } - - public void Validate(T value) => Contract.Requires(value != null); - - public override bool Set(IUpdateConfiguration configuration, object value) => configuration.SetOption(this, (T)value); - } - - public abstract class ConstantPool - { - private readonly Dictionary constants = new Dictionary(); - private int nextId = 1; - - /// Shortcut of this.ValueOf(firstNameComponent.Name + "#" + secondNameComponent). - public IConstant ValueOf(Type firstNameComponent, string secondNameComponent) - { - Contract.Requires(firstNameComponent != null); - Contract.Requires(secondNameComponent != null); - return this.ValueOf(firstNameComponent.Name + '#' + secondNameComponent); - } - - /// - /// Returns the which is assigned to the specified name. - /// If there's no such , a new one will be created and returned. - /// Once created, the subsequent calls with the same name will always return the previously created one - /// (i.e. singleton.) - /// - /// the name of the - public IConstant ValueOf(string name) - { - IConstant constant; - lock (this.constants) - { - if (this.constants.TryGetValue(name, out constant)) - { - return constant; - } - else - { - constant = this.NewInstance0(name); - } - } - return constant; - } - - /// Returns true if a exists for the given name. - public bool Exists(string name) - { - CheckNotNullAndNotEmpty(name); - lock (this.constants) - return this.constants.ContainsKey(name); - } - - /// - /// Creates a new for the given name or fail with an - /// if a for the given name exists. - /// - public IConstant NewInstance(string name) - { - if (this.Exists(name)) throw new ArgumentException($"'{name}' is already in use"); - IConstant constant = this.NewInstance0(name); - return constant; - } - - // Be careful that this dose not check whether the argument is null or empty. - private IConstant NewInstance0(string name) - { - lock (this.constants) - { - IConstant constant = this.NewConstant(this.nextId, name); - this.constants[name] = constant; - this.nextId++; - return constant; - } - } - - private static void CheckNotNullAndNotEmpty(string name) => Contract.Requires(!string.IsNullOrEmpty(name)); - - protected abstract IConstant NewConstant(int id, string name); - - [Obsolete] - public int NextId() - { - lock (this.constants) - { - int id = this.nextId; - this.nextId++; - return id; - } - } - } - - public interface IConstant - { - /// Returns the unique number assigned to this . - int Id { get; } - - /// Returns the name of this . - string Name { get; } - } - - public interface IUpdateConfiguration - { - T GetOption(UpdateOption option); - - bool SetOption(UpdateOption option, object value); - - bool SetOption(UpdateOption option, T value); - } - - public abstract class AbstractConstant : IConstant - { - private static long nextUniquifier; - private long volatileUniquifier; - - protected AbstractConstant(int id, string name) - { - this.Id = id; - this.Name = name; - } - - public int Id { get; } - - public string Name { get; } - - public override sealed string ToString() => this.Name; - - protected long Uniquifier - { - get - { - long result; - if ((result = Volatile.Read(ref this.volatileUniquifier)) == 0) - { - result = Interlocked.Increment(ref nextUniquifier); - long previousUniquifier = Interlocked.CompareExchange(ref this.volatileUniquifier, result, 0); - if (previousUniquifier != 0) result = previousUniquifier; - } - return result; - } - } - } - - public abstract class AbstractConstant : AbstractConstant, IComparable, IEquatable - where T : AbstractConstant - { - /// Creates a new instance. - protected AbstractConstant(int id, string name) - : base(id, name) { } - - public override sealed int GetHashCode() => base.GetHashCode(); - - public override sealed bool Equals(object obj) => base.Equals(obj); - - public bool Equals(T other) => ReferenceEquals(this, other); - - public int CompareTo(T o) - { - if (ReferenceEquals(this, o)) return 0; - AbstractConstant other = o; - int returnCode = this.GetHashCode() - other.GetHashCode(); - if (returnCode != 0) return returnCode; - long thisUV = this.Uniquifier; - long otherUV = other.Uniquifier; - if (thisUV < otherUV) return -1; - if (thisUV > otherUV) return 1; - throw new Exception("failed to compare two different constants"); - } - } -} \ No newline at end of file diff --git a/src/c#/GeneralUpdate.Core/Bootstrap/UpdateOptionValue.cs b/src/c#/GeneralUpdate.Core/Bootstrap/UpdateOptionValue.cs deleted file mode 100644 index e5d50689..00000000 --- a/src/c#/GeneralUpdate.Core/Bootstrap/UpdateOptionValue.cs +++ /dev/null @@ -1,29 +0,0 @@ -namespace GeneralUpdate.Core.Bootstrap -{ - public abstract class UpdateOptionValue - { - public abstract UpdateOption Option { get; } - - public abstract bool Set(IUpdateConfiguration config); - - public abstract object GetValue(); - } - - public sealed class UpdateOptionValue : UpdateOptionValue - { - public override UpdateOption Option { get; } - private readonly T value; - - public UpdateOptionValue(UpdateOption option, T value) - { - this.Option = option; - this.value = value; - } - - public override object GetValue() => this.value; - - public override bool Set(IUpdateConfiguration config) => config.SetOption(this.Option, this.value); - - public override string ToString() => this.value.ToString(); - } -} \ No newline at end of file diff --git a/src/c#/GeneralUpdate.Core/ContentProvider/FileNode.cs b/src/c#/GeneralUpdate.Core/ContentProvider/FileNode.cs deleted file mode 100644 index b74a0594..00000000 --- a/src/c#/GeneralUpdate.Core/ContentProvider/FileNode.cs +++ /dev/null @@ -1,148 +0,0 @@ -using System; - -namespace GeneralUpdate.Core.ContentProvider -{ - public class FileNode - { - #region Public Properties - - public long Id { get; set; } - - public string Name { get; set; } - - public string FullName { get; set; } - - public string Path { get; set; } - - public string Hash { get; set; } - - public FileNode Left { get; set; } - - public FileNode Right { get; set; } - - public int LeftType { get; set; } - - public int RightType { get; set; } - - public string RelativePath { get; set; } - - #endregion Public Properties - - #region Constructors - - public FileNode() - { } - - public FileNode(int id) - { - Id = id; - } - - #endregion Constructors - - #region Public Methods - - public void Add(FileNode node) - { - if (node == null) return; - - if (node.Id < Id) - { - if (Left == null) - { - Left = node; - } - else - { - Left.Add(node); - } - } - else - { - if (Right == null) - { - Right = node; - } - else - { - Right.Add(node); - } - } - } - - public void InfixOrder() - { - if (Left != null) - { - Left.InfixOrder(); - } - if (Right != null) - { - Right.InfixOrder(); - } - } - - public FileNode Search(long id) - { - if (id == Id) - { - return this; - } - else if (id < Id) - { - if (Left == null) return null; - return Left.Search(id); - } - else - { - if (Right == null) return null; - return Right.Search(id); - } - } - - /// - /// Find the parent node of the node that you want to delete. - /// - /// - /// - public FileNode SearchParent(long id) - { - if (Left != null && Left.Id == id || Right != null && Right.Id == id) - { - return this; - } - else - { - if (id < Id && Left != null) - { - return Left.SearchParent(id); - } - else if (id >= Id && Right != null) - { - return Right.SearchParent(id); - } - else - { - return null; - } - } - } - - /// - /// Compare tree nodes equally by Hash and file names. - /// - /// - /// - public override bool Equals(object obj) - { - if (obj == null) return false; - var tempNode = obj as FileNode; - if (tempNode == null) throw new ArgumentException(nameof(tempNode)); - return string.Equals(Hash, tempNode.Hash, StringComparison.OrdinalIgnoreCase) && string.Equals(Name, tempNode.Name, StringComparison.OrdinalIgnoreCase); - } - - public override int GetHashCode() => base.GetHashCode(); - - #endregion Public Methods - } -} \ No newline at end of file diff --git a/src/c#/GeneralUpdate.Core/ContentProvider/FileProvider-Comparer.cs b/src/c#/GeneralUpdate.Core/ContentProvider/FileProvider-Comparer.cs deleted file mode 100644 index be5f91e7..00000000 --- a/src/c#/GeneralUpdate.Core/ContentProvider/FileProvider-Comparer.cs +++ /dev/null @@ -1,67 +0,0 @@ -using System; -using System.Collections.Generic; -using System.IO; -using System.Linq; - -namespace GeneralUpdate.Core.ContentProvider -{ - public partial class FileProvider - { - private string _directoryA; - private string _directoryB; - - public List Comparer(string directoryA, string directoryB) - { - _directoryA = directoryA.TrimEnd(Path.DirectorySeparatorChar) + Path.DirectorySeparatorChar; - _directoryB = directoryB.TrimEnd(Path.DirectorySeparatorChar) + Path.DirectorySeparatorChar; - var filesInDirectoryA = new HashSet(GetAllFiles(_directoryA).Select(file => file.Substring(_directoryA.Length)), StringComparer.InvariantCultureIgnoreCase); - var missingFilesPath = GetAllFiles(_directoryB).Where(fileB => !filesInDirectoryA.Contains(fileB.Substring(_directoryB.Length))).ToList(); - var missingFiles = missingFilesPath.Select(path => new FileInfo(path)).ToList(); - return missingFiles; - } - - private IEnumerable GetAllFiles(string directoryPath) - { - var directories = new Stack(); - directories.Push(directoryPath); - - while (directories.Count > 0) - { - var currentDirectory = directories.Pop(); - - if (Directory.EnumerateFiles(currentDirectory, "*.inf").Any()) - continue; - - IEnumerable currentFiles; - try - { - currentFiles = Directory.EnumerateFiles(currentDirectory); - } - catch - { - continue; - } - - foreach (var file in currentFiles) - { - yield return file; - } - - IEnumerable subDirectories; - try - { - subDirectories = Directory.EnumerateDirectories(currentDirectory); - } - catch - { - continue; - } - - foreach (var subDirectory in subDirectories) - { - directories.Push(subDirectory); - } - } - } - } -} \ No newline at end of file diff --git a/src/c#/GeneralUpdate.Core/ContentProvider/FileProvider-Filter.cs b/src/c#/GeneralUpdate.Core/ContentProvider/FileProvider-Filter.cs deleted file mode 100644 index 6fe01323..00000000 --- a/src/c#/GeneralUpdate.Core/ContentProvider/FileProvider-Filter.cs +++ /dev/null @@ -1,45 +0,0 @@ -using System.Collections.Generic; - -namespace GeneralUpdate.Core.ContentProvider -{ - public partial class FileProvider - { - private static List _blackFiles, - _blackFileFormats; - private static readonly List DefaultBlackFileFormats = new List(6) - { - ".patch", - ".7z", - ".zip", - ".rar", - ".tar", - ".json" - }; - private static readonly List DefaultBlackFiles = new List(1) { "Newtonsoft.Json.dll" }; - - /// - /// Set a blacklist. - /// - /// A collection of blacklist files that are skipped when updated. - /// A collection of blacklist file name extensions that are skipped on update. - public static void SetBlacklist(List blackFiles, List blackFileFormats) - { - _blackFiles = blackFiles; - _blackFileFormats = blackFileFormats; - } - - /// - /// These files will be skipped when updating. - /// - /// - public static List GetBlackFiles() => - _blackFiles ?? DefaultBlackFiles; - - /// - /// These files that contain the file suffix will be skipped when updating. - /// - /// - public static List GetBlackFileFormats() => - _blackFileFormats ?? DefaultBlackFileFormats; - } -} diff --git a/src/c#/GeneralUpdate.Core/ContentProvider/FileProvider-Manage.cs b/src/c#/GeneralUpdate.Core/ContentProvider/FileProvider-Manage.cs deleted file mode 100644 index 97580db8..00000000 --- a/src/c#/GeneralUpdate.Core/ContentProvider/FileProvider-Manage.cs +++ /dev/null @@ -1,49 +0,0 @@ -using System; -using System.Collections.Generic; -using System.IO; - -namespace GeneralUpdate.Core.ContentProvider -{ - public partial class FileProvider - { - public static string GetTempDirectory(string name) - { - var path = $"generalupdate_{DateTime.Now.ToString("yyyy-MM-dd")}_{name}"; - var tempDir = Path.Combine(Path.GetTempPath(), path); - if (!Directory.Exists(tempDir)) - { - Directory.CreateDirectory(tempDir); - } - return tempDir; - } - - public static List GetAllfiles(string path) - { - try - { - var files = new List(); - files.AddRange(new DirectoryInfo(path).GetFiles()); - var tmpDir = new DirectoryInfo(path).GetDirectories(); - foreach (var dic in tmpDir) - { - files.AddRange(GetAllfiles(dic.FullName)); - } - return files; - } - catch (Exception) - { - return null; - } - } - - /// - /// Delete the backup file directory and recursively delete all backup content. - /// - public static void DeleteDir(string path) - { - if (string.IsNullOrWhiteSpace(path)) return; - if (Directory.Exists(path)) - Directory.Delete(path, true); - } - } -} \ No newline at end of file diff --git a/src/c#/GeneralUpdate.Core/ContentProvider/FileProvider-Serialization.cs b/src/c#/GeneralUpdate.Core/ContentProvider/FileProvider-Serialization.cs deleted file mode 100644 index 9dd15143..00000000 --- a/src/c#/GeneralUpdate.Core/ContentProvider/FileProvider-Serialization.cs +++ /dev/null @@ -1,63 +0,0 @@ -using Newtonsoft.Json; -using System; -using System.IO; -using System.Text; - -namespace GeneralUpdate.Core.ContentProvider -{ - public partial class FileProvider - { - public static void CreateJson(string targetPath, T obj) - { - var folderPath = Path.GetDirectoryName(targetPath) ?? - throw new ArgumentException("invalid path", nameof(targetPath)); - if (!Directory.Exists(folderPath)) - { - Directory.CreateDirectory(folderPath); - } - - var jsonString = JsonConvert.SerializeObject(obj); - File.WriteAllText(targetPath, jsonString); - } - - public static T GetJson(string path) - { - if (File.Exists(path)) - { - var json = File.ReadAllText(path); - return JsonConvert.DeserializeObject(json); - } - return default(T); - } - - /// - /// Convert object to base64 string. - /// - /// - /// - public static string Serialize(object obj) - { - if (obj == null) return string.Empty; - var json = JsonConvert.SerializeObject(obj); - var bytes = Encoding.Default.GetBytes(json); - var base64str = Convert.ToBase64String(bytes); - return base64str; - } - - /// - /// Convert base64 object to string. - /// - /// - /// - /// - public static T Deserialize(string str) - { - var obj = default(T); - if (string.IsNullOrEmpty(str)) return obj; - byte[] bytes = Convert.FromBase64String(str); - var json = Encoding.Default.GetString(bytes); - var result = JsonConvert.DeserializeObject(json); - return result; - } - } -} \ No newline at end of file diff --git a/src/c#/GeneralUpdate.Core/ContentProvider/FileProvider.cs b/src/c#/GeneralUpdate.Core/ContentProvider/FileProvider.cs deleted file mode 100644 index 5a89c1d1..00000000 --- a/src/c#/GeneralUpdate.Core/ContentProvider/FileProvider.cs +++ /dev/null @@ -1,115 +0,0 @@ -using GeneralUpdate.Core.HashAlgorithms; -using System; -using System.Collections.Generic; -using System.IO; -using System.Linq; -using System.Threading; -using System.Threading.Tasks; - -namespace GeneralUpdate.Core.ContentProvider -{ - public partial class FileProvider - { - private long _fileCount = 0; - - #region Public Methods - - /// - /// Compare two binary trees with different children. - /// - /// Left tree folder path. - /// Right tree folder path. - /// ValueTuple(leftFileNodes,rightFileNodes, differentTreeNode) - public async Task, IEnumerable, IEnumerable>> Compare(string leftPath, string rightPath) - { - return await Task.Run(() => - { - ResetId(); - var leftFileNodes = Read(leftPath); - var rightFileNodes = Read(rightPath); - var leftTree = new FileTree(leftFileNodes); - var rightTree = new FileTree(rightFileNodes); - var differentTreeNode = new List(); - leftTree.Compare(leftTree.GetRoot(), rightTree.GetRoot(), ref differentTreeNode); - return ValueTuple.Create(leftFileNodes, rightFileNodes, differentTreeNode); - }); - } - - /// - /// Using the list on the left as a baseline, find the set of differences between the two file lists. - /// - /// Previous version file list path - /// The current version file list path - /// Except collection - public async Task> Except(string leftPath, string rightPath) - { - return await Task.Run(() => - { - var leftFileNodes = Read(leftPath); - var rightFileNodes = Read(rightPath); - var rightNodeDic = rightFileNodes.ToDictionary(x => x.RelativePath, x => x); - var filesOnlyInLeft = leftFileNodes.Where(f => !rightNodeDic.ContainsKey(f.RelativePath)).ToList(); - return filesOnlyInLeft; - }); - } - - #endregion Public Methods - - #region Private Methods - - /// - /// Recursively read all files in the folder path. - /// - /// folder path. - /// folder root path. - /// different chalders. - private IEnumerable Read(string path, string rootPath = null) - { - var resultFiles = new List(); - if (string.IsNullOrEmpty(rootPath)) rootPath = path; - if (!rootPath.EndsWith("/")) rootPath += "/"; - Uri rootUri = new Uri(rootPath); - foreach (var subPath in Directory.GetFiles(path)) - { - if (IsMatchBlacklist(subPath)) continue; - - var hashAlgorithm = new Sha256HashAlgorithm(); - var hash = hashAlgorithm.ComputeHash(subPath); - var subFileInfo = new FileInfo(subPath); - Uri subUri = new Uri(subFileInfo.FullName); - resultFiles.Add(new FileNode() { Id = GetId(), Path = path, Name = subFileInfo.Name, Hash = hash, FullName = subFileInfo.FullName, RelativePath = rootUri.MakeRelativeUri(subUri).ToString() }); - } - foreach (var subPath in Directory.GetDirectories(path)) - { - resultFiles.AddRange(Read(subPath, rootPath)); - } - return resultFiles; - } - - /// - /// Self-growing file tree node ID. - /// - /// - private long GetId() => Interlocked.Increment(ref _fileCount); - - private void ResetId() => Interlocked.Exchange(ref _fileCount, 0); - - /// - /// Whether the file name in the file path can match the blacklisted file. - /// - /// - /// - private bool IsMatchBlacklist(string subPath) - { - var blackList = GetBlackFiles(); - if (blackList == null) return false; - foreach (var file in blackList) - { - if (subPath.Contains(file)) return true; - } - return false; - } - - #endregion Private Methods - } -} \ No newline at end of file diff --git a/src/c#/GeneralUpdate.Core/ContentProvider/FileTree.cs b/src/c#/GeneralUpdate.Core/ContentProvider/FileTree.cs deleted file mode 100644 index 31fd7f00..00000000 --- a/src/c#/GeneralUpdate.Core/ContentProvider/FileTree.cs +++ /dev/null @@ -1,173 +0,0 @@ -using System.Collections.Generic; -using System.Diagnostics; - -namespace GeneralUpdate.Core.ContentProvider -{ - /// - /// Simple file binary tree. - /// - public class FileTree - { - #region Private Members - - private FileNode _root; - - #endregion Private Members - - #region Constructors - - public FileTree() - { } - - public FileTree(IEnumerable nodes) - { - foreach (var node in nodes) Add(node); - } - - #endregion Constructors - - #region Public Methods - - public void Add(FileNode node) - { - if (_root == null) - { - _root = node; - } - else - { - _root.Add(node); - } - } - - public void InfixOrder() - { - if (_root != null) - { - _root.InfixOrder(); - } - else - { - Debug.WriteLine("The binary sort tree is empty and cannot be traversed!"); - } - } - - public FileNode Search(long id) => _root == null ? null : _root.Search(id); - - public FileNode SearchParent(long id) => _root == null ? null : _root.SearchParent(id); - - public long DelRightTreeMin(FileNode node) - { - FileNode target = node; - while (target.Left != null) - { - target = target.Left; - } - DelNode(target.Id); - return target.Id; - } - - public void DelNode(long id) - { - if (_root == null) - { - return; - } - else - { - FileNode targetNode = Search(id); - if (targetNode == null) - { - return; - } - if (_root.Left == null && _root.Right == null) - { - _root = null; - return; - } - - FileNode parent = SearchParent(id); - if (targetNode.Left == null && targetNode.Right == null) - { - if (parent.Left != null && parent.Left.Id == id) - { - parent.Left = null; - } - else if (parent.Right != null && parent.Right.Id == id) - { - parent.Right = null; - } - } - else if (targetNode.Left != null && targetNode.Right != null) - { - long minVal = DelRightTreeMin(targetNode.Right); - targetNode.Id = minVal; - } - else - { - if (targetNode.Left != null) - { - if (parent.Left.Id == id) - { - parent.Left = targetNode.Left; - } - else - { - parent.Right = targetNode.Left; - } - } - else - { - if (parent.Left.Id == id) - { - parent.Left = targetNode.Right; - } - else - { - parent.Right = targetNode.Right; - } - } - } - } - } - - /// - /// Starting from the root node, recursively compares two different child nodes of the binary tree and nodes that are not included. - /// - /// - /// - /// - public void Compare(FileNode node, FileNode node0, ref List nodes) - { - if (node != null && node.Left != null) - { - if (!node.Equals(node0) && node0 != null) nodes.Add(node0); - Compare(node.Left, node0.Left, ref nodes); - } - else if (node0 != null && node0.Left != null) - { - nodes.Add(node0); - Compare(node.Left, node0.Left, ref nodes); - } - - if (node != null && node.Right != null) - { - if (!node.Equals(node0) && node0 != null) nodes.Add(node0); - Compare(node.Right, node0 == null ? null : node0.Right, ref nodes); - } - else if (node0 != null && node0.Right != null) - { - nodes.Add(node0); - Compare(node == null ? null : node.Right, node0.Right, ref nodes); - } - else if (node0 != null) - { - nodes.Add(node0); - } - } - - public FileNode GetRoot() => _root; - - #endregion Public Methods - } -} \ No newline at end of file diff --git a/src/c#/GeneralUpdate.Core/CustomAwaiter/IAwaitable.cs b/src/c#/GeneralUpdate.Core/CustomAwaiter/IAwaitable.cs deleted file mode 100644 index 0c854044..00000000 --- a/src/c#/GeneralUpdate.Core/CustomAwaiter/IAwaitable.cs +++ /dev/null @@ -1,12 +0,0 @@ -namespace GeneralUpdate.Core.CustomAwaiter -{ - public interface IAwaitable where TAwaiter : IAwaiter - { - TAwaiter GetAwaiter(); - } - - public interface IAwaitable where TAwaiter : IAwaiter - { - TAwaiter GetAwaiter(); - } -} \ No newline at end of file diff --git a/src/c#/GeneralUpdate.Core/CustomAwaiter/IAwaiter.cs b/src/c#/GeneralUpdate.Core/CustomAwaiter/IAwaiter.cs deleted file mode 100644 index 42255ef0..00000000 --- a/src/c#/GeneralUpdate.Core/CustomAwaiter/IAwaiter.cs +++ /dev/null @@ -1,18 +0,0 @@ -using System.Runtime.CompilerServices; - -namespace GeneralUpdate.Core.CustomAwaiter -{ - public interface IAwaiter : INotifyCompletion - { - bool IsCompleted { get; } - - void GetResult(); - } - - public interface IAwaiter : INotifyCompletion - { - bool IsCompleted { get; } - - TResult GetResult(); - } -} \ No newline at end of file diff --git a/src/c#/GeneralUpdate.Core/CustomAwaiter/ICriticalAwaiter.cs b/src/c#/GeneralUpdate.Core/CustomAwaiter/ICriticalAwaiter.cs deleted file mode 100644 index f4876ca4..00000000 --- a/src/c#/GeneralUpdate.Core/CustomAwaiter/ICriticalAwaiter.cs +++ /dev/null @@ -1,12 +0,0 @@ -using System.Runtime.CompilerServices; - -namespace GeneralUpdate.Core.CustomAwaiter -{ - public interface ICriticalAwaiter : IAwaiter, ICriticalNotifyCompletion - { - } - - public interface ICriticalAwaiter : IAwaiter, ICriticalNotifyCompletion - { - } -} \ No newline at end of file diff --git a/src/c#/GeneralUpdate.Core/Domain/DO/Assembler/VersionAssembler.cs b/src/c#/GeneralUpdate.Core/Domain/DO/Assembler/VersionAssembler.cs deleted file mode 100644 index 867c325f..00000000 --- a/src/c#/GeneralUpdate.Core/Domain/DO/Assembler/VersionAssembler.cs +++ /dev/null @@ -1,23 +0,0 @@ -using GeneralUpdate.Core.Domain.Entity; -using System.Collections.Generic; - -namespace GeneralUpdate.Core.Domain.DO.Assembler -{ - public class VersionAssembler - { - public static List ToDataObjects(List versionDTO) - { - List entitys = new List(); - versionDTO.ForEach((v) => - { - entitys.Add(ToDataObject(v)); - }); - return entitys; - } - - public static VersionInfo ToDataObject(VersionConfigDO versionDO) - { - return new VersionInfo(versionDO.PubTime, versionDO.Name, versionDO.Hash, versionDO.Version, versionDO.Url); - } - } -} \ No newline at end of file diff --git a/src/c#/GeneralUpdate.Core/Domain/DO/VersionConfigDO.cs b/src/c#/GeneralUpdate.Core/Domain/DO/VersionConfigDO.cs deleted file mode 100644 index a9829bd6..00000000 --- a/src/c#/GeneralUpdate.Core/Domain/DO/VersionConfigDO.cs +++ /dev/null @@ -1,64 +0,0 @@ -using System; - -namespace GeneralUpdate.Core.Domain.DO -{ - public class VersionConfigDO - { - /// - /// Product branch ID (Used to distinguish multiple branches under the same product). - /// - public string Guid { get; set; } - - /// - /// Update package download location. - /// - public string Url { get; set; } - - /// - /// Hash verification code - /// - public string Hash { get; set; } - - /// - /// Update the package name. - /// - public string Name { get; set; } - - /// - /// Update the package file format. - /// - public string Format { get; set; } - - /// - /// The version number that will be updated. - /// - public string Version { get; set; } - - /// - /// Update package release time. - /// - public long PubTime { get; set; } - - /// - /// Init version config infomation. - /// - /// - /// - /// - /// - /// - /// - /// - /// - public VersionConfigDO(string guid, string url, string hash, string name, string format, string version, long pubTime) - { - Guid = guid ?? throw new ArgumentNullException(nameof(guid)); - Url = url ?? throw new ArgumentNullException(nameof(url)); - Hash = hash ?? throw new ArgumentNullException(nameof(hash)); - Name = name ?? throw new ArgumentNullException(nameof(name)); - Format = format ?? throw new ArgumentNullException(nameof(format)); - Version = version ?? throw new ArgumentNullException(nameof(version)); - PubTime = pubTime; - } - } -} \ No newline at end of file diff --git a/src/c#/GeneralUpdate.Core/Domain/DTO/Assembler/VersionAssembler.cs b/src/c#/GeneralUpdate.Core/Domain/DTO/Assembler/VersionAssembler.cs deleted file mode 100644 index ac850e2b..00000000 --- a/src/c#/GeneralUpdate.Core/Domain/DTO/Assembler/VersionAssembler.cs +++ /dev/null @@ -1,23 +0,0 @@ -using GeneralUpdate.Core.Domain.Entity; -using System.Collections.Generic; - -namespace GeneralUpdate.Core.Domain.DTO.Assembler -{ - public class VersionAssembler - { - public static List ToEntitys(List versionDTO) - { - List entitys = new List(); - versionDTO.ForEach((v) => - { - entitys.Add(ToEntity(v)); - }); - return entitys; - } - - public static VersionInfo ToEntity(VersionDTO versionDTO) - { - return new VersionInfo(versionDTO.PubTime, versionDTO.Name, versionDTO.Hash, versionDTO.Version, versionDTO.Url); - } - } -} \ No newline at end of file diff --git a/src/c#/GeneralUpdate.Core/Domain/DTO/BaseResponseDTO.cs b/src/c#/GeneralUpdate.Core/Domain/DTO/BaseResponseDTO.cs deleted file mode 100644 index 3ebb1f93..00000000 --- a/src/c#/GeneralUpdate.Core/Domain/DTO/BaseResponseDTO.cs +++ /dev/null @@ -1,11 +0,0 @@ -namespace GeneralUpdate.Core.Domain.DTO -{ - public class BaseResponseDTO - { - public int Code { get; set; } - - public TBody Body { get; set; } - - public string Message { get; set; } - } -} \ No newline at end of file diff --git a/src/c#/GeneralUpdate.Core/Domain/DTO/UploadReapDTO.cs b/src/c#/GeneralUpdate.Core/Domain/DTO/UploadReapDTO.cs deleted file mode 100644 index c3c59264..00000000 --- a/src/c#/GeneralUpdate.Core/Domain/DTO/UploadReapDTO.cs +++ /dev/null @@ -1,8 +0,0 @@ -using GeneralUpdate.Core.Domain.DTO; - -namespace GeneralUpdate.AspNetCore.DTO -{ - public class UploadReapDTO : BaseResponseDTO - { - } -} \ No newline at end of file diff --git a/src/c#/GeneralUpdate.Core/Domain/DTO/VersionDTO.cs b/src/c#/GeneralUpdate.Core/Domain/DTO/VersionDTO.cs deleted file mode 100644 index 6f070e7b..00000000 --- a/src/c#/GeneralUpdate.Core/Domain/DTO/VersionDTO.cs +++ /dev/null @@ -1,24 +0,0 @@ -namespace GeneralUpdate.Core.Domain.DTO -{ - public class VersionDTO - { - public VersionDTO(string hash, long pubTime, string version, string url, string name) - { - Hash = hash; - PubTime = pubTime; - Version = version; - Url = url; - Name = name; - } - - public string Hash { get; set; } - - public long PubTime { get; set; } - - public string Version { get; set; } - - public string Url { get; set; } - - public string Name { get; set; } - } -} \ No newline at end of file diff --git a/src/c#/GeneralUpdate.Core/Domain/DTO/VersionRespDTO.cs b/src/c#/GeneralUpdate.Core/Domain/DTO/VersionRespDTO.cs deleted file mode 100644 index eda2a878..00000000 --- a/src/c#/GeneralUpdate.Core/Domain/DTO/VersionRespDTO.cs +++ /dev/null @@ -1,27 +0,0 @@ -using System.Collections.Generic; - -namespace GeneralUpdate.Core.Domain.DTO -{ - public class VersionRespDTO : BaseResponseDTO - { } - - public class VersionBodyDTO - { - public bool IsUpdate { get; set; } - - /// - /// Is forcibly update. - /// - public bool IsForcibly { get; set; } - - /// - /// 1:ClientApp 2:UpdateApp - /// - public int ClientType { get; set; } - - /// - /// Returns information about all versions that are different from the latest version based on the current version of the client. - /// - public List Versions { get; set; } - } -} \ No newline at end of file diff --git a/src/c#/GeneralUpdate.Core/Domain/Entity/Assembler/ProcessAssembler.cs b/src/c#/GeneralUpdate.Core/Domain/Entity/Assembler/ProcessAssembler.cs deleted file mode 100644 index 88eb236c..00000000 --- a/src/c#/GeneralUpdate.Core/Domain/Entity/Assembler/ProcessAssembler.cs +++ /dev/null @@ -1,74 +0,0 @@ -using GeneralUpdate.Core.ContentProvider; -using System; -using System.Text; - -namespace GeneralUpdate.Core.Domain.Entity.Assembler -{ - public class ProcessAssembler - { - public static string ToBase64(ProcessInfo info) - { - if (info == null) throw new ArgumentNullException(nameof(info)); - return FileProvider.Serialize(info); - } - - public static string ToBase64(ParamsOSS info) - { - if (info == null) throw new ArgumentNullException(nameof(info)); - return FileProvider.Serialize(info); - } - - public static Packet ToPacket(ProcessInfo info) - { - var packet = new Packet(); - packet.AppName = info.AppName; - packet.AppSecretKey = info.AppSecretKey; - packet.AppType = info.AppType; - packet.InstallPath = info.InstallPath; - packet.ClientVersion = info.CurrentVersion; - packet.LastVersion = info.LastVersion; - packet.UpdateLogUrl = info.LogUrl; - packet.Encoding = ToEncoding(info.CompressEncoding); - packet.Format = info.CompressFormat; - packet.DownloadTimeOut = info.DownloadTimeOut; - packet.UpdateVersions = info.UpdateVersions; - return packet; - } - - private static Encoding ToEncoding(int type) - { - Encoding encoding = Encoding.Default; - switch (type) - { - case 1: - encoding = Encoding.UTF8; - break; - - case 2: - encoding = Encoding.UTF7; - break; - - case 3: - encoding = Encoding.UTF32; - break; - - case 4: - encoding = Encoding.Unicode; - break; - - case 5: - encoding = Encoding.BigEndianUnicode; - break; - - case 6: - encoding = Encoding.ASCII; - break; - - case 7: - encoding = Encoding.Default; - break; - } - return encoding; - } - } -} \ No newline at end of file diff --git a/src/c#/GeneralUpdate.Core/Domain/Entity/Configinfo.cs b/src/c#/GeneralUpdate.Core/Domain/Entity/Configinfo.cs deleted file mode 100644 index 4e4560e7..00000000 --- a/src/c#/GeneralUpdate.Core/Domain/Entity/Configinfo.cs +++ /dev/null @@ -1,66 +0,0 @@ -using System; -using System.IO; - -namespace GeneralUpdate.Core.Domain.Entity -{ - public class Configinfo : Entity - { - public Configinfo() - { } - - public Configinfo(int appType, string appName, string appSecretKey, string clientVersion, string updateUrl, string updateLogUrl, string installPath, string mainUpdateUrl, string mainAppName) - { - AppType = appType; - AppName = appName ?? throw new ArgumentNullException(nameof(appName)); - AppSecretKey = appSecretKey ?? throw new ArgumentNullException(nameof(appSecretKey)); - ClientVersion = clientVersion ?? throw new ArgumentNullException(nameof(clientVersion)); - UpdateUrl = updateUrl ?? throw new ArgumentNullException(nameof(updateUrl)); - UpdateLogUrl = updateLogUrl ?? throw new ArgumentNullException(nameof(updateLogUrl)); - InstallPath = installPath ?? Directory.GetCurrentDirectory(); - MainUpdateUrl = mainUpdateUrl ?? throw new ArgumentNullException(nameof(mainUpdateUrl)); - MainAppName = mainAppName ?? throw new ArgumentNullException(nameof(mainAppName)); - } - - /// - /// 1:ClientApp 2:UpdateApp - /// - public int AppType { get; set; } - - /// - /// Need to start the name of the app. - /// - public string AppName { get; set; } - - /// - /// application key - /// - public string AppSecretKey { get; set; } - - /// - /// Client current version. - /// - public string ClientVersion { get; set; } - - /// - /// Update check api address. - /// - public string UpdateUrl { get; set; } - - /// - /// Update log web address. - /// - public string UpdateLogUrl { get; set; } - - /// - /// installation path (for update file logic). - /// - public string InstallPath { get; set; } - - /// - /// Update check api address. - /// - public string MainUpdateUrl { get; set; } - - public string MainAppName { get; set; } - } -} \ No newline at end of file diff --git a/src/c#/GeneralUpdate.Core/Domain/Entity/Entity.cs b/src/c#/GeneralUpdate.Core/Domain/Entity/Entity.cs deleted file mode 100644 index a33975ed..00000000 --- a/src/c#/GeneralUpdate.Core/Domain/Entity/Entity.cs +++ /dev/null @@ -1,31 +0,0 @@ -using System; -using System.Text.RegularExpressions; - -namespace GeneralUpdate.Core.Domain.Entity -{ - public class Entity - { - /// - /// 委派标识 - /// - protected string Identity { get; set; } - - public string ID - { - get { return this.Identity; } - protected set { this.Identity = value; } - } - - protected bool IsURL(string url) - { - string check = @"((http|ftp|https)://)(([a-zA-Z0-9\._-]+\.[a-zA-Z]{2,6})|([0-9]{1,3}\.[0-9]{1,3}\.[0-9]{1,3}\.[0-9]{1,3}))(:[0-9]{1,4})*(/[a-zA-Z0-9\&%_\./-~-]*)?"; - var regex = new Regex(check); - return regex.IsMatch(url); - } - - protected bool IsVersion(string version) - { - return Version.TryParse(version, out var ver); - } - } -} \ No newline at end of file diff --git a/src/c#/GeneralUpdate.Core/Domain/Entity/Packet.cs b/src/c#/GeneralUpdate.Core/Domain/Entity/Packet.cs deleted file mode 100644 index 8b5e76a1..00000000 --- a/src/c#/GeneralUpdate.Core/Domain/Entity/Packet.cs +++ /dev/null @@ -1,145 +0,0 @@ -using System; -using System.Collections.Generic; -using System.Text; - -namespace GeneralUpdate.Core.Domain.Entity -{ - public class Packet : Entity - { - public Packet() - { } - - public Packet(string mainUpdateUrl, int appType, string updateUrl, string appName, string mainAppName, string format, bool isUpdate, string updateLogUrl, Encoding encoding, int downloadTimeOut, string appSecretKey, string tempPath) - { - if (!IsURL(mainUpdateUrl)) throw new Exception($"Illegal url {nameof(mainUpdateUrl)}"); - MainUpdateUrl = mainUpdateUrl ?? throw new ArgumentNullException(nameof(MainUpdateUrl)); - if (!IsURL(updateUrl)) throw new Exception($"Illegal url {nameof(UpdateUrl)}"); - UpdateUrl = updateUrl ?? throw new ArgumentNullException(nameof(updateUrl)); - UpdateLogUrl = updateLogUrl ?? throw new ArgumentNullException(nameof(updateLogUrl)); - AppType = appType; - AppName = appName ?? throw new ArgumentNullException(nameof(appName)); - MainAppName = mainAppName ?? throw new ArgumentNullException(nameof(mainAppName)); - Format = format ?? throw new ArgumentNullException(nameof(format)); - IsUpgradeUpdate = isUpdate; - Encoding = encoding ?? throw new ArgumentNullException(nameof(encoding)); - DownloadTimeOut = downloadTimeOut; - AppSecretKey = appSecretKey ?? throw new ArgumentNullException(nameof(appSecretKey)); - TempPath = tempPath ?? throw new ArgumentNullException(nameof(tempPath)); - } - - /// - /// Update check api address. - /// - public string MainUpdateUrl { get; set; } - - /// - /// 1:ClientApp 2:UpdateApp - /// - public int AppType { get; set; } - - /// - /// Update check api address. - /// - public string UpdateUrl { get; set; } - - /// - /// Need to start the name of the app. - /// - public string AppName { get; set; } - - /// - /// The name of the main application, without .exe. - /// - public string MainAppName { get; set; } - - /// - /// Update package file format(Defult format is Zip). - /// - public string Format { get; set; } - - /// - /// Whether an update is required to upgrade the application. - /// - public bool IsUpgradeUpdate { get; set; } - - /// - /// Whether the main application needs to be updated. - /// - public bool IsMainUpdate { get; set; } - - /// - /// Update log web address. - /// - public string UpdateLogUrl { get; set; } - - /// - /// Version information that needs to be updated. - /// - public List UpdateVersions { get; set; } - - /// - /// The encoding format for file operations. - /// - public Encoding Encoding { get; set; } - - /// - /// Time-out event for file download. - /// - public int DownloadTimeOut { get; set; } - - /// - /// application key - /// - public string AppSecretKey { get; set; } - - /// - /// Client current version. - /// - public string ClientVersion { get; set; } - - /// - /// The latest version. - /// - public string LastVersion { get; set; } - - /// - /// installation path (for update file logic). - /// - public string InstallPath { get; set; } - - /// - /// Download file temporary storage path (for update file logic). - /// - public string TempPath { get; set; } - - /// - /// Configuration parameters for upgrading the terminal program. - /// - public string ProcessBase64 { get; set; } - - /// - /// The platform to which the current strategy belongs. - /// - public string Platform { get; set; } - - /// - /// Files in the blacklist will skip the update. - /// - public List BlackFiles { get; set; } - - /// - /// File formats in the blacklist will skip the update. - /// - public List BlackFormats { get; set; } - - /// - /// Whether to enable the driver upgrade function. - /// - public bool DriveEnabled { get; set; } - - /// - /// Whether open note function, if you want to start needs to be synchronized to deploy 'GeneralUpdate. SystemService' service. - /// - public bool WillMessageEnabled { get; set; } - } -} \ No newline at end of file diff --git a/src/c#/GeneralUpdate.Core/Domain/Entity/ParamsOSS.cs b/src/c#/GeneralUpdate.Core/Domain/Entity/ParamsOSS.cs deleted file mode 100644 index 8283cd8a..00000000 --- a/src/c#/GeneralUpdate.Core/Domain/Entity/ParamsOSS.cs +++ /dev/null @@ -1,23 +0,0 @@ -using System; - -namespace GeneralUpdate.Core.Domain.Entity -{ - public class ParamsOSS : Entity - { - public string Url { get; set; } - - public string AppName { get; set; } - - public string CurrentVersion { get; set; } - - public string VersionFileName { get; set; } - - public ParamsOSS(string url, string appName, string currentVersion, string versionFileName) - { - Url = url ?? throw new ArgumentNullException(nameof(url)); - AppName = appName ?? throw new ArgumentNullException(nameof(appName)); - CurrentVersion = currentVersion ?? throw new ArgumentNullException(nameof(currentVersion)); - VersionFileName = versionFileName ?? "versions.json"; - } - } -} \ No newline at end of file diff --git a/src/c#/GeneralUpdate.Core/Domain/Entity/ProcessInfo.cs b/src/c#/GeneralUpdate.Core/Domain/Entity/ProcessInfo.cs deleted file mode 100644 index 4082dd9f..00000000 --- a/src/c#/GeneralUpdate.Core/Domain/Entity/ProcessInfo.cs +++ /dev/null @@ -1,107 +0,0 @@ -using GeneralUpdate.Core.Domain.DTO; -using GeneralUpdate.Core.Domain.DTO.Assembler; -using System; -using System.Collections.Generic; -using System.IO; -using System.Text; - -namespace GeneralUpdate.Core.Domain.Entity -{ - public class ProcessInfo : Entity - { - public ProcessInfo() - { } - - public ProcessInfo(string appName, string installPath, string currentVersion, string lastVersion, string logUrl, Encoding compressEncoding, string compressFormat, int downloadTimeOut, string appSecretKey, List updateVersions) - { - AppName = appName ?? throw new ArgumentNullException(nameof(appName)); - if (!Directory.Exists(installPath)) throw new ArgumentException($"{nameof(installPath)} path does not exist ! {installPath}."); - InstallPath = installPath ?? throw new ArgumentNullException(nameof(installPath)); - CurrentVersion = currentVersion ?? throw new ArgumentNullException(nameof(currentVersion)); - LastVersion = lastVersion ?? throw new ArgumentNullException(nameof(lastVersion)); - LogUrl = logUrl; - compressEncoding = compressEncoding ?? Encoding.Default; - CompressEncoding = ToEncodingType(compressEncoding); - CompressFormat = compressFormat; - if (downloadTimeOut < 0) throw new ArgumentException("Timeout must be greater than 0 !"); - DownloadTimeOut = downloadTimeOut; - AppSecretKey = appSecretKey ?? throw new ArgumentNullException(nameof(appSecretKey)); - if (updateVersions == null || updateVersions.Count == 0) throw new ArgumentException("Collection cannot be null or has 0 elements !"); - UpdateVersions = VersionAssembler.ToEntitys(updateVersions); - } - - private int ToEncodingType(Encoding encoding) - { - int type = -1; - if (encoding == Encoding.UTF8) - { - type = 1; - } - else if (encoding == Encoding.UTF7) - { - type = 2; - } - else if (encoding == Encoding.UTF32) - { - type = 3; - } - else if (encoding == Encoding.Unicode) - { - type = 4; - } - else if (encoding == Encoding.BigEndianUnicode) - { - type = 5; - } - else if (encoding == Encoding.ASCII) - { - type = 6; - } - else if (encoding == Encoding.Default) - { - type = 7; - } - return type; - } - - /// - /// 1:MainApp 2:UpdateApp - /// - public int AppType { get; set; } - - /// - /// Need to start the name of the app. - /// - public string AppName { get; set; } - - /// - /// Installation directory (the path where the update package is decompressed). - /// - public string InstallPath { get; set; } - - public string CurrentVersion { get; set; } - - public string LastVersion { get; set; } - - /// - /// Update log web address. - /// - public string LogUrl { get; set; } - - public int CompressEncoding { get; set; } - - public string CompressFormat { get; set; } - - public int DownloadTimeOut { get; set; } - - /// - /// application key - /// - public string AppSecretKey { get; set; } - - /// - /// One or more version update information. - /// - public List UpdateVersions { get; set; } - } -} \ No newline at end of file diff --git a/src/c#/GeneralUpdate.Core/Domain/Entity/VersionInfo.cs b/src/c#/GeneralUpdate.Core/Domain/Entity/VersionInfo.cs deleted file mode 100644 index e1d611b7..00000000 --- a/src/c#/GeneralUpdate.Core/Domain/Entity/VersionInfo.cs +++ /dev/null @@ -1,50 +0,0 @@ -using System; - -namespace GeneralUpdate.Core.Domain.Entity -{ - public class VersionInfo : Entity - { - public VersionInfo() - { } - - public VersionInfo(long pubTime, string name, string hash, string version, string url) - { - PubTime = pubTime; - Name = name ?? throw new ArgumentNullException(nameof(name)); - Hash = hash ?? throw new ArgumentNullException(nameof(hash)); - Version = version ?? throw new ArgumentNullException(nameof(version)); - Url = url ?? throw new ArgumentNullException(nameof(Url)); - if (!IsURL(Url)) throw new Exception($"Illegal url {nameof(Url)}"); - } - - /// - /// Update package release time. - /// - public long PubTime { get; set; } - - /// - /// Update package name. - /// - public string Name { get; set; } - - /// - /// Compare and verify with the downloaded update package. - /// - public string Hash { get; set; } - - /// - /// The version number. - /// - public string Version { get; set; } - - /// - /// Remote service url address. - /// - public string Url { get; set; } - - public override string ToString() - { - return Version; - } - } -} \ No newline at end of file diff --git a/src/c#/GeneralUpdate.Core/Domain/Enum/AppType.cs b/src/c#/GeneralUpdate.Core/Domain/Enum/AppType.cs deleted file mode 100644 index b983ff24..00000000 --- a/src/c#/GeneralUpdate.Core/Domain/Enum/AppType.cs +++ /dev/null @@ -1,15 +0,0 @@ -namespace GeneralUpdate.Core.Domain.Enum -{ - public class AppType - { - /// - /// main program - /// - public const int ClientApp = 1; - - /// - /// upgrade program. - /// - public const int UpgradeApp = 2; - } -} \ No newline at end of file diff --git a/src/c#/GeneralUpdate.Core/Domain/Enum/Format.cs b/src/c#/GeneralUpdate.Core/Domain/Enum/Format.cs deleted file mode 100644 index 081e69fc..00000000 --- a/src/c#/GeneralUpdate.Core/Domain/Enum/Format.cs +++ /dev/null @@ -1,8 +0,0 @@ -namespace GeneralUpdate.Core.Domain.Enum -{ - public class Format - { - public const string ZIP = "zip"; - public const string SEVENZIP = "7z"; - } -} \ No newline at end of file diff --git a/src/c#/GeneralUpdate.Core/Domain/Enum/HttpStatus.cs b/src/c#/GeneralUpdate.Core/Domain/Enum/HttpStatus.cs deleted file mode 100644 index 3ee8cc30..00000000 --- a/src/c#/GeneralUpdate.Core/Domain/Enum/HttpStatus.cs +++ /dev/null @@ -1,12 +0,0 @@ -namespace GeneralUpdate.Core.Domain.Enum -{ - public class HttpStatus - { - public const int OK = 200; - public const int BAD_REQUEST = 400; - public const int FORBIDDEN = 403; - public const int NOT_FOUND = 404; - public const int REQUEST_TIMEOUT = 408; - public const int SERVICE_UNAVAILABLE = 500; - } -} \ No newline at end of file diff --git a/src/c#/GeneralUpdate.Core/Domain/Enum/PlatformType.cs b/src/c#/GeneralUpdate.Core/Domain/Enum/PlatformType.cs deleted file mode 100644 index fb1e8aee..00000000 --- a/src/c#/GeneralUpdate.Core/Domain/Enum/PlatformType.cs +++ /dev/null @@ -1,48 +0,0 @@ -namespace GeneralUpdate.Core.Domain.Enum -{ - /// - /// Adapt to the update process on different platforms. - /// - public class PlatformType - { - /// - /// Update on mac platform. - /// - public const string Mac = "MAC_PLATFORM"; - - /// - /// Update on windows platform. - /// - public const string Windows = "WIN_PLATFORM"; - - /// - /// Update on iOS platform. - /// - public const string iOS = "IOS_PLATFORM"; - - /// - /// Update on android platform. - /// - public const string Android = "ANDROID_PLATFORM"; - - /// - /// Update on linux platform. - /// - public const string Linux = "LINUX_PLATFORM"; - - /// - /// Update on IoT platform. - /// - //public const string IoT = "IOT_PLATFORM"; - - /// - /// Update on Tizen platform. - /// - //public const string Tizen = "TIZEN_PLATFORM"; - - /// - /// Update on Blazor platform. - /// - //public const string Blazor = "BLAZOR_PLATFORM"; - } -} \ No newline at end of file diff --git a/src/c#/GeneralUpdate.Core/Domain/Enum/ProgressType.cs b/src/c#/GeneralUpdate.Core/Domain/Enum/ProgressType.cs deleted file mode 100644 index 027268be..00000000 --- a/src/c#/GeneralUpdate.Core/Domain/Enum/ProgressType.cs +++ /dev/null @@ -1,45 +0,0 @@ -namespace GeneralUpdate.Core.Domain.Enum -{ - public enum ProgressType - { - /// - /// Check for updates - /// - Check, - - /// - /// Download the update package - /// - Download, - - /// - /// update file - /// - Updatefile, - - /// - /// update completed - /// - Done, - - /// - /// Update failed - /// - Fail, - - /// - /// Update config - /// - Config, - - /// - /// Update patch - /// - Patch, - - /// - /// Hash code - /// - Hash - } -} \ No newline at end of file diff --git a/src/c#/GeneralUpdate.Core/Domain/PO/Assembler/VersionAssembler.cs b/src/c#/GeneralUpdate.Core/Domain/PO/Assembler/VersionAssembler.cs deleted file mode 100644 index 57cea11a..00000000 --- a/src/c#/GeneralUpdate.Core/Domain/PO/Assembler/VersionAssembler.cs +++ /dev/null @@ -1,23 +0,0 @@ -using GeneralUpdate.Core.Domain.Entity; -using System.Collections.Generic; - -namespace GeneralUpdate.Core.Domain.PO.Assembler -{ - public class VersionAssembler - { - public static List ToDataObjects(List versionDTO) - { - List entitys = new List(); - versionDTO.ForEach((v) => - { - entitys.Add(ToDataObject(v)); - }); - return entitys; - } - - public static VersionInfo ToDataObject(VersionPO versionDO) - { - return new VersionInfo(versionDO.PubTime, versionDO.Name, versionDO.Hash, versionDO.Version, versionDO.Url); - } - } -} \ No newline at end of file diff --git a/src/c#/GeneralUpdate.Core/Domain/PO/VersionPO.cs b/src/c#/GeneralUpdate.Core/Domain/PO/VersionPO.cs deleted file mode 100644 index bca517a3..00000000 --- a/src/c#/GeneralUpdate.Core/Domain/PO/VersionPO.cs +++ /dev/null @@ -1,33 +0,0 @@ -namespace GeneralUpdate.Core.Domain.PO -{ - /// - /// Version data persistence. - /// - public class VersionPO - { - /// - /// Update package release time. - /// - public long PubTime { get; set; } - - /// - /// Update package name. - /// - public string Name { get; set; } - - /// - /// Compare and verify with the downloaded update package. - /// - public string Hash { get; set; } - - /// - /// The version number. - /// - public string Version { get; set; } - - /// - /// Remote service url address. - /// - public string Url { get; set; } - } -} \ No newline at end of file diff --git a/src/c#/GeneralUpdate.Core/Domain/PO/WillMessagePO.cs b/src/c#/GeneralUpdate.Core/Domain/PO/WillMessagePO.cs deleted file mode 100644 index 30dca502..00000000 --- a/src/c#/GeneralUpdate.Core/Domain/PO/WillMessagePO.cs +++ /dev/null @@ -1,81 +0,0 @@ -using System; -using System.Collections.Generic; - -namespace GeneralUpdate.Core.Domain.PO -{ - public enum WillMessageStatus - { - /// - /// Processing has not yet begun. - /// - NotStarted, - - /// - /// Processing completed. - /// - Completed, - - /// - /// Processing failure. - /// - Failed - } - - public class BackupPO - { - public string AppPath { get; set; } - - public string BackupPath { get; set; } - - public string Version { get; set; } - - public string Hash { get; set; } - - public int AppType { get; set; } - } - - public class WillMessagePO - { - public Stack Message { get; private set; } - public WillMessageStatus Status { get; private set; } - public DateTime CreateTime { get; private set; } - public DateTime ChangeTime { get; private set; } - - private WillMessagePO() - { } - - public class Builder - { - private readonly WillMessagePO _messagePO = new WillMessagePO(); - - public Builder SetMessage(Stack message) - { - _messagePO.Message = message ?? throw new ArgumentNullException($"{nameof(message)} cannot be null"); - return this; - } - - public Builder SetStatus(WillMessageStatus status) - { - _messagePO.Status = status; - return this; - } - - public Builder SetCreateTime(DateTime createTime) - { - _messagePO.CreateTime = createTime; - return this; - } - - public Builder SetChangeTime(DateTime changeTime) - { - _messagePO.ChangeTime = changeTime; - return this; - } - - public WillMessagePO Build() - { - return _messagePO; - } - } - } -} \ No newline at end of file diff --git a/src/c#/GeneralUpdate.Core/Domain/Service/VersionService.cs b/src/c#/GeneralUpdate.Core/Domain/Service/VersionService.cs deleted file mode 100644 index 4c3af355..00000000 --- a/src/c#/GeneralUpdate.Core/Domain/Service/VersionService.cs +++ /dev/null @@ -1,103 +0,0 @@ -using GeneralUpdate.Core.Domain.DTO; -using GeneralUpdate.Core.Domain.Enum; -using Newtonsoft.Json; -using System; -using System.IO; -using System.Net; -using System.Net.Http; -using System.Net.Security; -using System.Security.Cryptography.X509Certificates; -using System.Text; -using System.Threading.Tasks; -using GeneralUpdate.Core.Events; -using GeneralUpdate.Core.Events.CommonArgs; - -namespace GeneralUpdate.Core.Domain.Service -{ - public class VersionService - { - public async Task ValidationVersion(string url) - { - var updateResp = await GetTaskAsync(url); - if (updateResp == null || updateResp.Body == null) - { - throw new ArgumentNullException( - nameof(updateResp), - "The verification request is abnormal, please check the network or parameter configuration!" - ); - } - - if (updateResp.Code == HttpStatus.OK) - { - return updateResp; - } - else - { - throw new WebException( - $"Request failed , Code :{updateResp.Code}, Message:{updateResp.Message} !" - ); - } - } - - private async Task GetTaskAsync( - string httpUrl, - string headerKey = null, - string headerValue = null - ) - { - HttpWebResponse response = null; - try - { - ServicePointManager.ServerCertificateValidationCallback = CheckValidationResult; - var request = (HttpWebRequest)WebRequest.Create(httpUrl); - request.Method = "GET"; - request.Accept = "text/html, application/xhtml+xml, */*"; - request.ContentType = "application/x-www-form-urlencoded"; - request.Timeout = 15000; - if (!string.IsNullOrEmpty(headerKey) && !string.IsNullOrEmpty(headerValue)) - { - request.Headers[headerKey] = headerValue; - } - response = (HttpWebResponse)await request.GetResponseAsync(); - if (response.StatusCode != HttpStatusCode.OK) - { - throw new WebException( - $"Response status code does not indicate success: {response.StatusCode}!" - ); - } - var responseStream = response.GetResponseStream(); - if (responseStream == null) - { - throw new WebException( - "Response stream is null, please check the network or parameter configuration!" - ); - } - using (var reader = new StreamReader(responseStream, Encoding.UTF8)) - { - var tempStr = await reader.ReadToEndAsync(); - var respContent = JsonConvert.DeserializeObject(tempStr); - return respContent; - } - } - catch (Exception ex) - { - EventManager.Instance.Dispatch>( - this, - new ExceptionEventArgs(ex) - ); - return default; - } - finally - { - response?.Close(); - } - } - - private static bool CheckValidationResult( - object sender, - X509Certificate certificate, - X509Chain chain, - SslPolicyErrors sslPolicyErrors - ) => true; - } -} \ No newline at end of file diff --git a/src/c#/GeneralUpdate.Core/Domain/VO/FileVO.cs b/src/c#/GeneralUpdate.Core/Domain/VO/FileVO.cs deleted file mode 100644 index a8156c49..00000000 --- a/src/c#/GeneralUpdate.Core/Domain/VO/FileVO.cs +++ /dev/null @@ -1,28 +0,0 @@ -namespace GeneralUpdate.Core.Domain.VO -{ - /// - /// file object value. - /// - public class FileVO - { - /// - /// Client current version. - /// - public string ClientVersion { get; set; } - - /// - /// The latest version. - /// - public string LastVersion { get; set; } - - /// - /// installation path (for update file logic). - /// - public string InstallPath { get; set; } - - /// - /// Download file temporary storage path (for update file logic). - /// - public string TempPath { get; set; } - } -} \ No newline at end of file diff --git a/src/c#/GeneralUpdate.Core/Download/AbstractTask.cs b/src/c#/GeneralUpdate.Core/Download/AbstractTask.cs deleted file mode 100644 index 746c40fa..00000000 --- a/src/c#/GeneralUpdate.Core/Download/AbstractTask.cs +++ /dev/null @@ -1,339 +0,0 @@ -using GeneralUpdate.Core.Events.MultiEventArgs; -using GeneralUpdate.Core.Exceptions.CustomArgs; -using GeneralUpdate.Core.Exceptions.CustomException; -using System; -using System.Collections.Specialized; -using System.ComponentModel; -using System.IO; -using System.Net; -using System.Net.Http; -using System.Threading; - -namespace GeneralUpdate.Core.Download -{ - public abstract class AbstractTask : WebClient, ITask - { - #region Private Members - - private DownloadFileRangeState _fileRange; - private long _beforBytes; - private long _receivedBytes; - private long _totalBytes; - private int _timeOut; - - #endregion Private Members - - #region Public Properties - - public delegate void MultiDownloadProgressChangedEventHandler(object sender, MultiDownloadProgressChangedEventArgs e); - - public event MultiDownloadProgressChangedEventHandler MultiDownloadProgressChanged; - - public delegate void MultiAsyncCompletedEventHandler(object sender, AsyncCompletedEventArgs e); - - public event MultiAsyncCompletedEventHandler MultiDownloadFileCompleted; - - protected Timer SpeedTimer { get; set; } - protected DateTime StartTime { get; set; } - - public long BeforBytes - { - get - { - return Interlocked.Read(ref _beforBytes); - } - - set - { - Interlocked.Exchange(ref _beforBytes, value); - } - } - - public long ReceivedBytes - { - get - { - return Interlocked.Read(ref _receivedBytes); - } - - set - { - Interlocked.Exchange(ref _receivedBytes, value); - } - } - - public long TotalBytes - { - get - { - return Interlocked.Read(ref _totalBytes); - } - set - { - Interlocked.Exchange(ref _totalBytes, value); - } - } - - #endregion Public Properties - - #region Public Methods - - public void InitTimeOut(int timeout) - { - if (timeout <= 0) timeout = 30; - _timeOut = 1000 * timeout; - } - - protected override WebRequest GetWebRequest(Uri address) - { - HttpWebRequest request; - if (address.Scheme == "https") - { - ServicePointManager.ServerCertificateValidationCallback = (a, b, c, d) => { return true; }; - request = (HttpWebRequest)base.GetWebRequest(address); - request.ProtocolVersion = HttpVersion.Version10; - } - else - { - request = (HttpWebRequest)base.GetWebRequest(address); - } - - request.Timeout = _timeOut; - request.ReadWriteTimeout = _timeOut; - request.AllowAutoRedirect = false; - request.AllowWriteStreamBuffering = true; - - var cookieContainer = new CookieContainer(); - var collection = new NameValueCollection - { - { "Accept-Language", "zh-cn,zh;q=0.5" }, - { "Accept-Encoding", "gzip,deflate" }, - { "Accept-Charset", "GB2312,utf-8;q=0.7,*;q=0.7" }, - { "Keep-Alive", "115" } - }; - request.Accept = "text/html,application/xhtml+xml,application/xml;q=0.9,*/*;q=0.8"; - request.UserAgent = "Mozilla/5.0 (Windows; U; Windows NT 5.1; zh-CN; rv:1.9.2.13) Gecko/20101203 Firefox/3.6.13"; - request.Headers.Add(collection); - request.CookieContainer = cookieContainer; - request.ServicePoint.BindIPEndPointDelegate = (servicePoint, remoteEndPoint, retryCount) => - { - if (remoteEndPoint.AddressFamily == System.Net.Sockets.AddressFamily.InterNetworkV6) - return new IPEndPoint(IPAddress.IPv6Any, 0); - else - return new IPEndPoint(IPAddress.Any, 0); - }; - return request; - } - - public new void CancelAsync() - { - base.CancelAsync(); - if (_fileRange != null && _fileRange.IsRangeDownload) _fileRange.IsRangeDownload = false; - } - - public void DownloadFileRange(string url, string path, object userState) - { - if (_fileRange != null && _fileRange.IsRangeDownload) return; - _fileRange = new DownloadFileRangeState(path, userState, this); - _fileRange.OnCompleted = () => MultiDownloadFileCompleted; - _fileRange.IsRangeDownload = true; - long startPos = CheckFile(_fileRange); - if (startPos == -1) return; - try - { - _fileRange.Request = (HttpWebRequest)GetWebRequest(new Uri(url)); - _fileRange.Request.ReadWriteTimeout = _timeOut; - _fileRange.Request.Timeout = _timeOut; - _fileRange.Respone = _fileRange.Request.GetResponse(); - _fileRange.Stream = _fileRange.Respone.GetResponseStream(); - if (_fileRange.Respone.ContentLength == startPos) - { - _fileRange.Close(); - File.Move(_fileRange.TempPath, _fileRange.Path); - _fileRange.Done(true); - return; - } - if (startPos > 0) _fileRange.Request.AddRange((int)startPos); - long totalBytesReceived = _fileRange.Respone.ContentLength + startPos; - long bytesReceived = startPos; - if (totalBytesReceived != 0 && bytesReceived >= totalBytesReceived) - { - _fileRange.Close(); - try - { - if (File.Exists(_fileRange.Path)) File.Delete(_fileRange.Path); - File.Move(_fileRange.TempPath, _fileRange.Path); - } - catch (Exception e) - { - _fileRange.Exception = e; - _fileRange.Close(); - } - } - else - { - WriteFile(_fileRange, startPos); - } - } - catch (HttpRequestException ex) - { - throw new GeneralUpdateException(new HttpExceptionArgs(url, 400, "Download file failed."), ex.Message, ex.InnerException); - } - catch (Exception e) - { - _fileRange.Exception = e; - throw new Exception($"'DownloadFileRange' This function has an internal exception : {e.Message} .", e.InnerException); - } - finally - { - if (_fileRange != null) _fileRange.Close(); - } - } - - #endregion Public Methods - - #region Private Methods - - private long CheckFile(DownloadFileRangeState state) - { - long startPos = 0; - if (File.Exists(state.TempPath)) - { - state.FileStream = File.OpenWrite(state.TempPath); - startPos = state.FileStream.Length; - state.FileStream.Seek(startPos, SeekOrigin.Current); - } - else - { - try - { - string direName = Path.GetDirectoryName(state.TempPath); - if (!Directory.Exists(direName)) Directory.CreateDirectory(direName); - state.FileStream = new FileStream(state.TempPath, FileMode.Create); - } - catch (Exception e) - { - state.Exception = e; - startPos = -1; - state.Close(); - } - } - return startPos; - } - - private void WriteFile(DownloadFileRangeState state, long startPos) - { - var bytesReceived = startPos; - byte[] bytes = new byte[1024]; - bool isDownloadCompleted = false; - var totalBytesReceived = state.Respone.ContentLength + startPos; - int readSize = state.Stream.Read(bytes, 0, 1024); - while (readSize > 0 && state.IsRangeDownload) - { - if (state == null || state.FileStream == null) break; - lock (state.FileStream) - { - if (MultiDownloadProgressChanged != null) - MultiDownloadProgressChanged(this, new MultiDownloadProgressChangedEventArgs(bytesReceived, totalBytesReceived, ((float)bytesReceived / totalBytesReceived), state.UserState)); - state.FileStream.Write(bytes, 0, readSize); - bytesReceived += readSize; - if (totalBytesReceived != 0 && bytesReceived >= totalBytesReceived) - { - try - { - state.Close(); - if (File.Exists(state.Path)) File.Delete(state.Path); - File.Move(state.TempPath, state.Path); - isDownloadCompleted = true; - state.Done(isDownloadCompleted); - } - catch (Exception e) - { - state.Exception = e; - state.Done(false); - } - } - else - { - readSize = state.Stream.Read(bytes, 0, 1024); - } - } - } - if (!isDownloadCompleted) state.Exception = new Exception("Request for early closure"); - } - - #endregion Private Methods - - private class DownloadFileRangeState - { - #region Private Members - - private const string tmpSuffix = ".temp"; - private Func _onDownloadCompleted = null; - private HttpWebRequest _request = null; - private WebResponse _respone = null; - private Stream _stream = null; - private FileStream _fileStream = null; - private Exception _exception = null; - private bool _isRangeDownload; - private string _tempPath; - private string _path; - private object _userState; - private object _sender; - - #endregion Private Members - - #region Constructors - - public DownloadFileRangeState(string path, object userState, object sender) - { - _path = path; - _userState = userState; - _tempPath = _path + tmpSuffix; - _sender = sender; - } - - #endregion Constructors - - #region Public Properties - - public Func OnCompleted { get => _onDownloadCompleted; set => _onDownloadCompleted = value; } - public HttpWebRequest Request { get => _request; set => _request = value; } - public WebResponse Respone { get => _respone; set => _respone = value; } - public Stream Stream { get => _stream; set => _stream = value; } - public FileStream FileStream { get => _fileStream; set => _fileStream = value; } - public Exception Exception { get => _exception; set => _exception = value; } - public bool IsRangeDownload { get => _isRangeDownload; set => _isRangeDownload = value; } - public string TempPath { get => _tempPath; } - public string Path { get => _path; } - public object UserState { get => _userState; } - public object Sender { get => _sender; } - - #endregion Public Properties - - #region Public Methods - - public void Close() - { - if (_fileStream != null) - { - _fileStream.Flush(); - _fileStream.Close(); - _fileStream = null; - } - if (_stream != null) _stream.Close(); - if (_respone != null) _respone.Close(); - if (_request != null) _request.Abort(); - if (_exception != null) throw new Exception(_exception.Message); - } - - public void Done(bool isCompleted) - { - if (_exception != null) throw new Exception(_exception.Message); - _onDownloadCompleted()(Sender, new AsyncCompletedEventArgs(_exception, isCompleted, _userState)); - } - - #endregion Public Methods - } - } -} \ No newline at end of file diff --git a/src/c#/GeneralUpdate.Core/Download/AbstractTaskManager.cs b/src/c#/GeneralUpdate.Core/Download/AbstractTaskManager.cs deleted file mode 100644 index aa4d2921..00000000 --- a/src/c#/GeneralUpdate.Core/Download/AbstractTaskManager.cs +++ /dev/null @@ -1,13 +0,0 @@ -namespace GeneralUpdate.Core.Download -{ - /// - /// Abstract task manager class. - /// - /// 'T' is the download task. - public abstract class AbstractTaskManager : ITaskManger> - { - public abstract void Remove(ITask task); - - public abstract void Add(ITask task); - } -} \ No newline at end of file diff --git a/src/c#/GeneralUpdate.Core/Download/DownloadManager.cs b/src/c#/GeneralUpdate.Core/Download/DownloadManager.cs deleted file mode 100644 index f22c7cbb..00000000 --- a/src/c#/GeneralUpdate.Core/Download/DownloadManager.cs +++ /dev/null @@ -1,164 +0,0 @@ -using GeneralUpdate.Core.Events.MultiEventArgs; -using System; -using System.Collections.Generic; -using System.Collections.Immutable; -using System.Reflection; -using System.Threading.Tasks; - -namespace GeneralUpdate.Core.Download -{ - /// - /// download task manager. - /// - /// update version information. - public sealed class DownloadManager : AbstractTaskManager - { - #region Private Members - - private string _path; - private string _format; - private int _timeOut; - private IList<(object, string)> _failedVersions; - private ImmutableList>.Builder _downloadTasksBuilder; - private ImmutableList> _downloadTasks; - - #endregion Private Members - - #region Constructors - - /// - /// download manager constructors. - /// - /// - /// - /// - public DownloadManager(string path, string format, int timeOut) - { - _path = path; - _format = format; - _timeOut = timeOut; - _failedVersions = new List>(); - _downloadTasksBuilder = ImmutableList.Create>().ToBuilder(); - } - - #endregion Constructors - - #region Public Properties - - /// - /// Record download exception information for all versions. - /// object: is 'UpdateVersion' , string: is error information. - /// - public IList<(object, string)> FailedVersions { get => _failedVersions; } - - public string Path { get => _path; } - - public string Format { get => _format; } - - public int TimeOut { get => _timeOut; } - - public ImmutableList> DownloadTasks { get => _downloadTasks ?? (_downloadTasksBuilder.ToImmutable()); private set => _downloadTasks = value; } - - public delegate void MultiAllDownloadCompletedEventHandler(object sender, MultiAllDownloadCompletedEventArgs e); - - public event MultiAllDownloadCompletedEventHandler MultiAllDownloadCompleted; - - public delegate void MultiDownloadProgressChangedEventHandler(object sender, MultiDownloadProgressChangedEventArgs e); - - public event MultiDownloadProgressChangedEventHandler MultiDownloadProgressChanged; - - public delegate void MultiAsyncCompletedEventHandler(object sender, MultiDownloadCompletedEventArgs e); - - public event MultiAsyncCompletedEventHandler MultiDownloadCompleted; - - public delegate void MultiDownloadErrorEventHandler(object sender, MultiDownloadErrorEventArgs e); - - public event MultiDownloadErrorEventHandler MultiDownloadError; - - public delegate void MultiDownloadStatisticsEventHandler(object sender, MultiDownloadStatisticsEventArgs e); - - public event MultiDownloadStatisticsEventHandler MultiDownloadStatistics; - - #endregion Public Properties - - #region Public Methods - - /// - /// launch update. - /// - /// - /// - /// - public void LaunchTaskAsync() - { - try - { - var downloadTasks = new List(); - foreach (var task in DownloadTasks) - { - var downloadTask = (task as DownloadTask); - downloadTasks.Add(downloadTask.Launch()); - } - Task.WaitAll(downloadTasks.ToArray()); - MultiAllDownloadCompleted(this, new MultiAllDownloadCompletedEventArgs(true, _failedVersions)); - } - catch (ObjectDisposedException ex) - { - throw new ArgumentNullException("Download manager launch abnormally ! exception is 'ObjectDisposedException'.", ex); - } - catch (AggregateException ex) - { - throw new ArgumentNullException("Download manager launch abnormally ! exception is 'AggregateException'.", ex); - } - catch (ArgumentNullException ex) - { - throw new ArgumentNullException("Download manager launch abnormally ! exception is 'ArgumentNullException'.", ex); - } - catch (AmbiguousMatchException ex) - { - throw new AmbiguousMatchException("Download manager launch abnormally ! exception is 'AmbiguousMatchException'.", ex); - } - catch (Exception ex) - { - throw new Exception($"Download manager error : {ex.Message} !", ex.InnerException); - } - finally - { - if (_failedVersions.Count > 0) MultiAllDownloadCompleted(this, new MultiAllDownloadCompletedEventArgs(true, _failedVersions)); - } - } - - public void OnMultiDownloadStatistics(object sender, MultiDownloadStatisticsEventArgs e) - { - if (MultiDownloadStatistics != null) this.MultiDownloadStatistics(sender, e); - } - - public void OnMultiDownloadProgressChanged(object sender, MultiDownloadProgressChangedEventArgs e) - { - if (MultiDownloadProgressChanged != null) this.MultiDownloadProgressChanged(sender, e); - } - - public void OnMultiAsyncCompleted(object sender, MultiDownloadCompletedEventArgs e) - { - if (MultiDownloadCompleted != null) this.MultiDownloadCompleted(sender, e); - } - - public void OnMultiDownloadError(object sender, MultiDownloadErrorEventArgs e) - { - if (MultiDownloadError != null) this.MultiDownloadError(sender, e); - _failedVersions.Add((e.Version, e.Exception.Message)); - } - - public override void Remove(ITask task) - { - if (task != null && _downloadTasksBuilder.Contains(task)) _downloadTasksBuilder.Remove(task); - } - - public override void Add(ITask task) - { - if (task != null && !_downloadTasksBuilder.Contains(task)) _downloadTasksBuilder.Add(task); - } - - #endregion Public Methods - } -} \ No newline at end of file diff --git a/src/c#/GeneralUpdate.Core/Download/DownloadTask.cs b/src/c#/GeneralUpdate.Core/Download/DownloadTask.cs deleted file mode 100644 index 146ec545..00000000 --- a/src/c#/GeneralUpdate.Core/Download/DownloadTask.cs +++ /dev/null @@ -1,216 +0,0 @@ -using GeneralUpdate.Core.CustomAwaiter; -using GeneralUpdate.Core.Events.MultiEventArgs; -using GeneralUpdate.Core.Exceptions.CustomArgs; -using GeneralUpdate.Core.Exceptions.CustomException; -using System; -using System.Globalization; -using System.Reflection; -using System.Runtime.ExceptionServices; -using System.Threading; -using System.Threading.Tasks; - -namespace GeneralUpdate.Core.Download -{ - /// - /// Download task class. - /// - /// 'T' is the version information that needs to be downloaded. - public sealed class DownloadTask : AbstractTask, IAwaiter> - { - #region Private Members - - private Exception _exception; - private DownloadManager _manager; - private const int DEFAULT_DELTA = 1048576;//1024*1024 - private TVersion _version; - - #endregion Private Members - - #region Constructors - - public DownloadTask(DownloadManager manger, TVersion version) - { - _manager = manger; - _version = version; - } - - #endregion Constructors - - #region Public Properties - - public bool IsCompleted { get; private set; } - - #endregion Public Properties - - #region Public Methods - - /// - /// Launch the current download task. - /// - /// - public async Task Launch() - { - try - { - var url = GetPropertyValue(_version, "Url"); - var name = GetPropertyValue(_version, "Name"); - InitTimeOut(_manager.TimeOut); - InitStatisticsEvent(); - InitProgressEvent(); - InitCompletedEvent(); - var installPath = $"{_manager.Path}{name}{_manager.Format}"; - DownloadFileRange(url, installPath, null); - await this; - } - catch (Exception ex) - { - _manager.OnMultiDownloadError(this, new MultiDownloadErrorEventArgs(ex, _version)); - _exception = new GeneralUpdateException("'download task' The executes abnormally !", ex); - } - } - - public DownloadTask GetResult() - { - if (_exception != null) ExceptionDispatchInfo.Capture(_exception).Throw(); - return this; - } - - public void OnCompleted(Action continuation) - { - if (IsCompleted) continuation?.Invoke(); - } - - public DownloadTask GetAwaiter() => this; - - public async Task AsTask(DownloadTask awaiter) => await awaiter; - - #endregion Public Methods - - #region Private Methods - - private void InitStatisticsEvent() - { - if (SpeedTimer != null) return; - - SpeedTimer = new Timer(_ => - { - try - { - var interval = DateTime.Now - StartTime; - var downLoadSpeed = interval.Seconds < 1 - ? ToUnit(ReceivedBytes - BeforBytes) - : ToUnit(ReceivedBytes - BeforBytes / interval.Seconds); - var size = (TotalBytes - ReceivedBytes) / DEFAULT_DELTA; - var remainingTime = new DateTime().AddSeconds(Convert.ToDouble(size)); - _manager.OnMultiDownloadStatistics(this, new MultiDownloadStatisticsEventArgs(_version, remainingTime, downLoadSpeed)); - StartTime = DateTime.Now; - BeforBytes = ReceivedBytes; - } - catch (Exception exception) - { - _manager.OnMultiDownloadError(this, new MultiDownloadErrorEventArgs(exception, _version)); - } - }, null, 0, 1000); - } - - private void InitProgressEvent() - { - MultiDownloadProgressChanged += ((sender, e) => - { - try - { - ReceivedBytes = e.BytesReceived; - TotalBytes = e.TotalBytesToReceive; - - var eventArgs = new MultiDownloadProgressChangedEventArgs(_version, - e.BytesReceived / DEFAULT_DELTA, - e.TotalBytesToReceive / DEFAULT_DELTA, - e.ProgressPercentage, - e.UserState); - - _manager.OnMultiDownloadProgressChanged(this, eventArgs); - } - catch (Exception exception) - { - _manager.OnMultiDownloadError(this, new MultiDownloadErrorEventArgs(exception, _version)); - } - }); - } - - private void InitCompletedEvent() - { - MultiDownloadFileCompleted += ((sender, e) => - { - try - { - if (SpeedTimer != null) - { - SpeedTimer.Dispose(); - SpeedTimer = null; - } - var eventArgs = new MultiDownloadCompletedEventArgs(_version, e.Error, e.Cancelled, e.UserState); - _manager.OnMultiAsyncCompleted(this, eventArgs); - Dispose(); - } - catch (Exception exception) - { - _manager.FailedVersions.Add(new ValueTuple { }); - _manager.OnMultiDownloadError(this, new MultiDownloadErrorEventArgs(exception, _version)); - } - finally - { - IsCompleted = true; - } - }); - } - - private TResult GetPropertyValue(TVersion entity, string propertyName) - { - TResult result = default(TResult); - Type entityType = typeof(TVersion); - try - { - PropertyInfo info = entityType.GetProperty(propertyName); - result = (TResult)info.GetValue(entity); - } - catch (ArgumentNullException ex) - { - throw _exception = new ArgumentNullException("'GetPropertyValue' The method executes abnormally !", ex); - } - catch (AmbiguousMatchException ex) - { - throw _exception = new AmbiguousMatchException("'GetPropertyValue' The method executes abnormally !", ex); - } - catch (Exception ex) - { - throw new GeneralUpdateException($"Download task GetPropertyValue error : {ex.Message}", ex.InnerException); - } - return result; - } - - private string ToUnit(long byteSize) - { - string result; - var tempSize = Convert.ToSingle(byteSize) / 1024; - if (tempSize > 1) - { - var tempMbyte = tempSize / 1024; - if (tempMbyte > 1) - { - result = $"{tempMbyte.ToString("##0.00", CultureInfo.InvariantCulture)}MB/S"; - } - else - { - result = $"{tempSize.ToString("##0.00", CultureInfo.InvariantCulture)}KB/S"; - } - } - else - { - result = $"{byteSize.ToString(CultureInfo.InvariantCulture)}B/S"; - } - return result; - } - - #endregion Private Methods - } -} \ No newline at end of file diff --git a/src/c#/GeneralUpdate.Core/Download/ITask.cs b/src/c#/GeneralUpdate.Core/Download/ITask.cs deleted file mode 100644 index f1d2856f..00000000 --- a/src/c#/GeneralUpdate.Core/Download/ITask.cs +++ /dev/null @@ -1,5 +0,0 @@ -namespace GeneralUpdate.Core.Download -{ - public interface ITask - { } -} \ No newline at end of file diff --git a/src/c#/GeneralUpdate.Core/Download/ITaskManger.cs b/src/c#/GeneralUpdate.Core/Download/ITaskManger.cs deleted file mode 100644 index c6502641..00000000 --- a/src/c#/GeneralUpdate.Core/Download/ITaskManger.cs +++ /dev/null @@ -1,21 +0,0 @@ -namespace GeneralUpdate.Core.Download -{ - /// - /// Download task interface. - /// - /// 'T' is the version information that needs to be downloaded. - internal interface ITaskManger - { - /// - /// Add download task . - /// - /// - void Add(T task); - - /// - /// Delete download task . - /// - /// - void Remove(T task); - } -} \ No newline at end of file diff --git a/src/c#/GeneralUpdate.Core/Events/CommonArgs/ExceptionEventArgs.cs b/src/c#/GeneralUpdate.Core/Events/CommonArgs/ExceptionEventArgs.cs deleted file mode 100644 index 2b54d23d..00000000 --- a/src/c#/GeneralUpdate.Core/Events/CommonArgs/ExceptionEventArgs.cs +++ /dev/null @@ -1,18 +0,0 @@ -using System; - -namespace GeneralUpdate.Core.Events.CommonArgs -{ - public class ExceptionEventArgs : EventArgs - { - private readonly Exception _exception; - - public ExceptionEventArgs(Exception exception) - { - _exception = exception; - } - - public ExceptionEventArgs(string mesage) => _exception = new Exception(mesage); - - public Exception Exception => _exception; - } -} \ No newline at end of file diff --git a/src/c#/GeneralUpdate.Core/Events/EventManager.cs b/src/c#/GeneralUpdate.Core/Events/EventManager.cs deleted file mode 100644 index 66ee85e2..00000000 --- a/src/c#/GeneralUpdate.Core/Events/EventManager.cs +++ /dev/null @@ -1,166 +0,0 @@ -using System; -using System.Collections.Generic; -using System.ComponentModel; - -namespace GeneralUpdate.Core.Events -{ - /// - /// Manage all events in the component. - /// - public class EventManager : IEventManager, IDisposable - { - // Use interop to call the method necessary - // to clean up the unmanaged resource. - [System.Runtime.InteropServices.DllImport("Kernel32")] - private static extern Boolean CloseHandle(IntPtr handle); - - private static readonly object _lockObj = new object(); - private static EventManager _instance; - private Dictionary _dicDelegates = new Dictionary(); - - // Track whether Dispose has been called. - private bool disposed = false; - - // Pointer to an external unmanaged resource. - private IntPtr handle; - - // Other managed resource this class uses. - private Component component = null; - - private EventManager() => component = new Component(); - - // Use C# finalizer syntax for finalization code. - // This finalizer will run only if the Dispose method - // does not get called. - // It gives your base class the opportunity to finalize. - // Do not provide finalizer in types derived from this class. - ~EventManager() - { - // Do not re-create Dispose clean-up code here. - // Calling Dispose(disposing: false) is optimal in terms of - // readability and maintainability. - Dispose(disposing: false); - } - - public static EventManager Instance - { - get - { - if (_instance == null) - { - lock (_lockObj) - { - if (_instance == null) - _instance = new EventManager(); - } - } - return _instance; - } - } - - /// - /// Add listener - /// - /// Specify the delegate type. - /// Delegate to be added. - /// parameter null exception. - public void AddListener(TDelegate newDelegate) where TDelegate : Delegate - { - if (newDelegate == null) throw new ArgumentNullException(nameof(newDelegate)); - if (_dicDelegates.ContainsKey(typeof(TDelegate))) return; - handle = new IntPtr(1); - _dicDelegates.Add(typeof(TDelegate), newDelegate); - } - - /// - /// Remove listener - /// - /// Specify the delegate type. - /// Remove old delegates. - /// parameter null exception. - public void RemoveListener(TDelegate oldDelegate) where TDelegate : Delegate - { - if (oldDelegate == null) throw new ArgumentNullException(nameof(oldDelegate)); - var delegateType = oldDelegate.GetType(); - if (!delegateType.IsInstanceOfType(typeof(TDelegate))) return; - Delegate tempDelegate = null; - if (_dicDelegates.TryGetValue(delegateType, out tempDelegate)) - { - if (tempDelegate == null) - { - _dicDelegates.Remove(delegateType); - } - else - { - _dicDelegates[delegateType] = tempDelegate; - } - } - } - - /// - /// Triggers a delegate of the same type. - /// - /// - /// trigger source object. - /// event args. - /// parameter null exception. - public void Dispatch(object sender, EventArgs eventArgs) where TDelegate : Delegate - { - if (sender == null) throw new ArgumentNullException(nameof(sender)); - if (eventArgs == null) throw new ArgumentNullException(nameof(eventArgs)); - if (!_dicDelegates.ContainsKey(typeof(TDelegate))) return; - _dicDelegates[typeof(TDelegate)].DynamicInvoke(sender, eventArgs); - } - - /// - /// Clear all listeners. - /// - public void Clear() => _dicDelegates.Clear(); - - // Implement IDisposable. - // Do not make this method virtual. - // A derived class should not be able to override this method. - public void Dispose() - { - Dispose(disposing: true); - // This object will be cleaned up by the Dispose method. - // Therefore, you should call GC.SuppressFinalize to - // take this object off the finalization queue - // and prevent finalization code for this object - // from executing a second time. - GC.SuppressFinalize(this); - } - - // Dispose(bool disposing) executes in two distinct scenarios. - // If disposing equals true, the method has been called directly - // or indirectly by a user's code. Managed and unmanaged resources - // can be disposed. - // If disposing equals false, the method has been called by the - // runtime from inside the finalizer and you should not reference - // other objects. Only unmanaged resources can be disposed. - protected virtual void Dispose(bool disposing) - { - // Check to see if Dispose has already been called. - if (!this.disposed) - { - // If disposing equals true, dispose all managed - // and unmanaged resources. - if (disposing) - { - // Dispose managed resources. - component.Dispose(); - } - - // Call the appropriate methods to clean up - // unmanaged resources here. - // If disposing is false, - // only the following code is executed. - CloseHandle(handle); - handle = IntPtr.Zero; - - // Note disposing has been done. - disposed = true; - } - } - } -} \ No newline at end of file diff --git a/src/c#/GeneralUpdate.Core/Events/IEventManager.cs b/src/c#/GeneralUpdate.Core/Events/IEventManager.cs deleted file mode 100644 index 6bf07c60..00000000 --- a/src/c#/GeneralUpdate.Core/Events/IEventManager.cs +++ /dev/null @@ -1,37 +0,0 @@ -using System; - -namespace GeneralUpdate.Core.Events -{ - /// - /// Event manager interface. - /// - public interface IEventManager - { - /// - /// Adding Event Listeners. - /// - /// Generic delegate. - /// New delegate that needs to be injected. - void AddListener(TDelegate newDelegate) where TDelegate : Delegate; - - /// - /// Removing Event Listening. - /// - /// Generic delegate. - /// Need to remove an existing delegate. - void RemoveListener(TDelegate oldDelegate) where TDelegate : Delegate; - - /// - /// Triggers notifications of the same event type based on the listening event type. - /// - /// generic delegate. - /// Event handler. - /// Event args. - void Dispatch(object sender, EventArgs eventArgs) where TDelegate : Delegate; - - /// - /// Remove all injected delegates. - /// - void Clear(); - } -} \ No newline at end of file diff --git a/src/c#/GeneralUpdate.Core/Events/MultiEventArgs/MutiAllDownloadCompletedEventArgs.cs b/src/c#/GeneralUpdate.Core/Events/MultiEventArgs/MutiAllDownloadCompletedEventArgs.cs deleted file mode 100644 index e1951169..00000000 --- a/src/c#/GeneralUpdate.Core/Events/MultiEventArgs/MutiAllDownloadCompletedEventArgs.cs +++ /dev/null @@ -1,21 +0,0 @@ -using System; -using System.Collections.Generic; - -namespace GeneralUpdate.Core.Events.MultiEventArgs -{ - public class MultiAllDownloadCompletedEventArgs : EventArgs - { - public MultiAllDownloadCompletedEventArgs() - { } - - public MultiAllDownloadCompletedEventArgs(bool isAllDownloadCompleted, IList<(object, string)> failedVersions) - { - IsAllDownloadCompleted = isAllDownloadCompleted; - FailedVersions = failedVersions; - } - - public bool IsAllDownloadCompleted { get; set; } - - public IList> FailedVersions { get; set; } - } -} \ No newline at end of file diff --git a/src/c#/GeneralUpdate.Core/Events/MultiEventArgs/MutiDownloadCompletedEventArgs.cs b/src/c#/GeneralUpdate.Core/Events/MultiEventArgs/MutiDownloadCompletedEventArgs.cs deleted file mode 100644 index cdf8bd3e..00000000 --- a/src/c#/GeneralUpdate.Core/Events/MultiEventArgs/MutiDownloadCompletedEventArgs.cs +++ /dev/null @@ -1,19 +0,0 @@ -using System; -using System.ComponentModel; - -namespace GeneralUpdate.Core.Events.MultiEventArgs -{ - public class MultiDownloadCompletedEventArgs : AsyncCompletedEventArgs - { - public MultiDownloadCompletedEventArgs(Exception error, bool cancelled, object userState) : base(error, cancelled, userState) - { - } - - public MultiDownloadCompletedEventArgs(object version, Exception error, bool cancelled, object userState) : base(error, cancelled, userState) - { - Version = version; - } - - public object Version { get; set; } - } -} \ No newline at end of file diff --git a/src/c#/GeneralUpdate.Core/Events/MultiEventArgs/MutiDownloadErrorEventArgs.cs b/src/c#/GeneralUpdate.Core/Events/MultiEventArgs/MutiDownloadErrorEventArgs.cs deleted file mode 100644 index dbecc3ea..00000000 --- a/src/c#/GeneralUpdate.Core/Events/MultiEventArgs/MutiDownloadErrorEventArgs.cs +++ /dev/null @@ -1,17 +0,0 @@ -using System; - -namespace GeneralUpdate.Core.Events.MultiEventArgs -{ - public class MultiDownloadErrorEventArgs : EventArgs - { - public MultiDownloadErrorEventArgs(Exception exception, object version) - { - Exception = exception; - Version = version; - } - - public Exception Exception { get; set; } - - public object Version { get; set; } - } -} \ No newline at end of file diff --git a/src/c#/GeneralUpdate.Core/Events/MultiEventArgs/MutiDownloadProgressChangedEventArgs.cs b/src/c#/GeneralUpdate.Core/Events/MultiEventArgs/MutiDownloadProgressChangedEventArgs.cs deleted file mode 100644 index 08dc8e35..00000000 --- a/src/c#/GeneralUpdate.Core/Events/MultiEventArgs/MutiDownloadProgressChangedEventArgs.cs +++ /dev/null @@ -1,55 +0,0 @@ -using GeneralUpdate.Core.Domain.Enum; -using System; - -namespace GeneralUpdate.Core.Events.MultiEventArgs -{ - public class MultiDownloadProgressChangedEventArgs : EventArgs - { - public MultiDownloadProgressChangedEventArgs(long bytesReceived, long totalBytesToReceive, float progressPercentage, object userState) - { - BytesReceived = bytesReceived; - TotalBytesToReceive = totalBytesToReceive; - ProgressPercentage = progressPercentage; - UserState = userState; - } - - public MultiDownloadProgressChangedEventArgs(object version, long bytesReceived, long totalBytesToReceive, float progressPercentage, object userState, string message = null) - { - BytesReceived = bytesReceived; - TotalBytesToReceive = totalBytesToReceive; - ProgressPercentage = progressPercentage; - UserState = userState; - Version = version; - Message = message; - } - - public MultiDownloadProgressChangedEventArgs(object version, string message) - { - Version = version; - Message = message; - } - - public MultiDownloadProgressChangedEventArgs(object version, ProgressType type, string message) - { - Version = version; - Type = type; - Message = message; - } - - public ProgressType Type { get; set; } - - public long BytesReceived { get; private set; } - - public long TotalBytesToReceive { get; private set; } - - public float ProgressPercentage { get; private set; } - - public object UserState { get; set; } - - public object Version { get; private set; } - - public string Message { get; private set; } - - public double ProgressValue { get; set; } - } -} \ No newline at end of file diff --git a/src/c#/GeneralUpdate.Core/Events/MultiEventArgs/MutiDownloadStatisticsEventArgs.cs b/src/c#/GeneralUpdate.Core/Events/MultiEventArgs/MutiDownloadStatisticsEventArgs.cs deleted file mode 100644 index 183c6b72..00000000 --- a/src/c#/GeneralUpdate.Core/Events/MultiEventArgs/MutiDownloadStatisticsEventArgs.cs +++ /dev/null @@ -1,20 +0,0 @@ -using System; - -namespace GeneralUpdate.Core.Events.MultiEventArgs -{ - public class MultiDownloadStatisticsEventArgs : EventArgs - { - public object Version { get; set; } - - public DateTime Remaining { get; set; } - - public string Speed { get; set; } - - public MultiDownloadStatisticsEventArgs(object version, DateTime remaining, string speed) - { - Version = version ?? throw new ArgumentNullException(nameof(version)); - Remaining = remaining; - Speed = speed ?? throw new ArgumentNullException(nameof(speed)); - } - } -} \ No newline at end of file diff --git a/src/c#/GeneralUpdate.Core/Events/OSSArgs/OSSDownloadArgs.cs b/src/c#/GeneralUpdate.Core/Events/OSSArgs/OSSDownloadArgs.cs deleted file mode 100644 index 44cb3264..00000000 --- a/src/c#/GeneralUpdate.Core/Events/OSSArgs/OSSDownloadArgs.cs +++ /dev/null @@ -1,23 +0,0 @@ -using System; - -namespace GeneralUpdate.Core.Events.OSSArgs -{ - public class OSSDownloadArgs : EventArgs - { - /// - /// The number of file bytes read when the file was downloaded. - /// - public long ReadLength { get; set; } - - /// - /// The total number of bytes of the file that needs to be downloaded. - /// - public long TotalLength { get; set; } - - public OSSDownloadArgs(long readLength, long totalLength) - { - ReadLength = readLength; - TotalLength = totalLength; - } - } -} \ No newline at end of file diff --git a/src/c#/GeneralUpdate.Core/Exceptions/CustomArgs/ExceptionArgs.cs b/src/c#/GeneralUpdate.Core/Exceptions/CustomArgs/ExceptionArgs.cs deleted file mode 100644 index fac82b79..00000000 --- a/src/c#/GeneralUpdate.Core/Exceptions/CustomArgs/ExceptionArgs.cs +++ /dev/null @@ -1,11 +0,0 @@ -using System; - -namespace GeneralUpdate.Core.Exceptions.CustomArgs -{ - [Serializable] - public abstract class ExceptionArgs - { - public virtual string Message - { get { return String.Empty; } } - } -} \ No newline at end of file diff --git a/src/c#/GeneralUpdate.Core/Exceptions/CustomArgs/HttpExceptionArgs.cs b/src/c#/GeneralUpdate.Core/Exceptions/CustomArgs/HttpExceptionArgs.cs deleted file mode 100644 index 46d976b1..00000000 --- a/src/c#/GeneralUpdate.Core/Exceptions/CustomArgs/HttpExceptionArgs.cs +++ /dev/null @@ -1,35 +0,0 @@ -using System; - -namespace GeneralUpdate.Core.Exceptions.CustomArgs -{ - [Serializable] - public sealed class HttpExceptionArgs : ExceptionArgs - { - private readonly String _url, _errorMessage; - private readonly int _code; - - public HttpExceptionArgs(String url, int code, string errorMessage) - { - _url = url; - _code = code; - _errorMessage = errorMessage; - } - - public String Url - { get { return _url; } } - - public String ErrorMessage - { get { return _errorMessage; } } - - public int Code - { get { return _code; } } - - public override string Message - { - get - { - return (_url == null) ? base.Message : $"Failed to request this address {_url} , status code {_code} , mssage : {_errorMessage}"; - } - } - } -} \ No newline at end of file diff --git a/src/c#/GeneralUpdate.Core/Exceptions/CustomArgs/PatchDirtyExceptionArgs.cs b/src/c#/GeneralUpdate.Core/Exceptions/CustomArgs/PatchDirtyExceptionArgs.cs deleted file mode 100644 index f9bb6e16..00000000 --- a/src/c#/GeneralUpdate.Core/Exceptions/CustomArgs/PatchDirtyExceptionArgs.cs +++ /dev/null @@ -1,24 +0,0 @@ -using System; - -namespace GeneralUpdate.Core.Exceptions.CustomArgs -{ - [Serializable] - public sealed class PatchDirtyExceptionArgs : ExceptionArgs - { - private readonly String _patchPath; - - public PatchDirtyExceptionArgs(String patchPath) - { _patchPath = patchPath; } - - public String PatchPath - { get { return _patchPath; } } - - public override string Message - { - get - { - return (_patchPath == null) ? base.Message : $"Patch file path {_patchPath}"; - } - } - } -} \ No newline at end of file diff --git a/src/c#/GeneralUpdate.Core/Exceptions/CustomArgs/UnZipExceptionArgs.cs b/src/c#/GeneralUpdate.Core/Exceptions/CustomArgs/UnZipExceptionArgs.cs deleted file mode 100644 index eeecb054..00000000 --- a/src/c#/GeneralUpdate.Core/Exceptions/CustomArgs/UnZipExceptionArgs.cs +++ /dev/null @@ -1,24 +0,0 @@ -using System; - -namespace GeneralUpdate.Core.Exceptions.CustomArgs -{ - [Serializable] - internal class UnZipExceptionArgs : ExceptionArgs - { - private readonly String _filePath; - - public UnZipExceptionArgs(String filePath) - { _filePath = filePath; } - - public String FilePath - { get { return _filePath; } } - - public override string Message - { - get - { - return (_filePath == null) ? base.Message : $"Unzip file failed : {_filePath} !"; - } - } - } -} \ No newline at end of file diff --git a/src/c#/GeneralUpdate.Core/Exceptions/CustomArgs/UpdateExceptionArgs.cs b/src/c#/GeneralUpdate.Core/Exceptions/CustomArgs/UpdateExceptionArgs.cs deleted file mode 100644 index 16c1fb06..00000000 --- a/src/c#/GeneralUpdate.Core/Exceptions/CustomArgs/UpdateExceptionArgs.cs +++ /dev/null @@ -1,32 +0,0 @@ -using GeneralUpdate.Core.Domain.Entity; -using System; - -namespace GeneralUpdate.Core.Exceptions.CustomArgs -{ - [Serializable] - internal class UpdateExceptionArgs : ExceptionArgs - { - private readonly VersionInfo _versionInfo; - private readonly String _excptionMessage; - - public UpdateExceptionArgs(VersionInfo info, String excptionMessage) - { - _versionInfo = info; - _excptionMessage = excptionMessage; - } - - public VersionInfo VersionInfo - { get { return _versionInfo; } } - - public String ExcptionMessage - { get { return _excptionMessage; } } - - public override string Message - { - get - { - return (_versionInfo == null) ? base.Message : $"An exception occurred updating the file {_versionInfo.Name} ,The version number is {_versionInfo.Version}. error message : {_excptionMessage} !"; - } - } - } -} \ No newline at end of file diff --git a/src/c#/GeneralUpdate.Core/Exceptions/CustomException/GeneralUpdateException.cs b/src/c#/GeneralUpdate.Core/Exceptions/CustomException/GeneralUpdateException.cs deleted file mode 100644 index 7e2d6059..00000000 --- a/src/c#/GeneralUpdate.Core/Exceptions/CustomException/GeneralUpdateException.cs +++ /dev/null @@ -1,54 +0,0 @@ -using GeneralUpdate.Core.Exceptions.CustomArgs; -using System; -using System.Runtime.Serialization; -using System.Security.Permissions; - -namespace GeneralUpdate.Core.Exceptions.CustomException -{ - /// - /// Exception of GeneralUpdate framework. - /// - [Serializable] - public sealed class GeneralUpdateException : Exception, ISerializable - where TExceptionArgs : ExceptionArgs - { - private const String c_args = "Args"; - private readonly TExceptionArgs m_args; - - public TExceptionArgs Args => m_args; - - public GeneralUpdateException(String message = null, Exception innerException = null) : this(null, message, innerException) - { - } - - public GeneralUpdateException(TExceptionArgs args, String message = null, Exception innerException = null) : base(message, innerException) => m_args = args; - - [SecurityPermission(SecurityAction.LinkDemand, Flags = SecurityPermissionFlag.SerializationFormatter)] - private GeneralUpdateException(SerializationInfo info, StreamingContext context) : base(info, context) => m_args = (TExceptionArgs)info.GetValue(c_args, typeof(TExceptionArgs)); - - [SecurityPermission(SecurityAction.LinkDemand, Flags = SecurityPermissionFlag.SerializationFormatter)] - public override void GetObjectData(SerializationInfo info, StreamingContext context) - { - info.AddValue(c_args, typeof(TExceptionArgs)); - base.GetObjectData(info, context); - } - - public override string Message - { - get - { - String baseMsg = base.Message; - return (m_args == null) ? baseMsg : $"{baseMsg}({m_args.Message})"; - } - } - - public override bool Equals(object obj) - { - GeneralUpdateException other = obj as GeneralUpdateException; - if (other == null) return false; - return Object.Equals(m_args, other.m_args) && base.Equals(obj); - } - - public override int GetHashCode() => base.GetHashCode(); - } -} \ No newline at end of file diff --git a/src/c#/GeneralUpdate.Core/Exceptions/ThrowExceptionUtility.cs b/src/c#/GeneralUpdate.Core/Exceptions/ThrowExceptionUtility.cs deleted file mode 100644 index 25c310d0..00000000 --- a/src/c#/GeneralUpdate.Core/Exceptions/ThrowExceptionUtility.cs +++ /dev/null @@ -1,57 +0,0 @@ -using GeneralUpdate.Core.Exceptions.CustomArgs; -using System; -using System.IO; - -namespace GeneralUpdate.Core.Exceptions -{ - internal sealed class ThrowExceptionUtility - { - public static void ThrowGeneralUpdateException(ExceptionArgs args) - => Throw(args.ToString(), args); - - #region Common - - public static void ThrowFileNotFound(string file) => Throw($"File cannot be accessed {file}!"); - - public static void ThrowIfNull(string errorMessage = null) - { - errorMessage = errorMessage ?? "Parameter cannot be null"; - Throw(errorMessage); - } - - /// - /// Checks if an object is empty and throws an exception if it is - /// - /// - /// - /// - public static void ThrowIfNull(object obj, string paramName) - { - if (obj == null) - Throw(paramName); - } - - /// - /// Checks if the string is empty or blank, and throws an exception if it is. - /// - /// - /// - /// - public static void ThrowIfNullOrWhiteSpace(string str, string paramName) - { - if (string.IsNullOrWhiteSpace(str)) - Throw("Parameter cannot be null or whitespace", paramName); - } - - /// - /// Basic method of exception declaration. - /// - /// - /// - /// - public static void Throw(string message, params object[] args) where T : Exception, new() - => throw (T)Activator.CreateInstance(typeof(T), message, args); - - #endregion Common - } -} \ No newline at end of file diff --git a/src/c#/GeneralUpdate.Core/HashAlgorithms/HashAlgorithmBase.cs b/src/c#/GeneralUpdate.Core/HashAlgorithms/HashAlgorithmBase.cs deleted file mode 100644 index c9db372c..00000000 --- a/src/c#/GeneralUpdate.Core/HashAlgorithms/HashAlgorithmBase.cs +++ /dev/null @@ -1,32 +0,0 @@ -using GeneralUpdate.Core.Exceptions; -using System.IO; -using System.Security.Cryptography; -using System.Text; - -namespace GeneralUpdate.Core.HashAlgorithms -{ - public abstract class HashAlgorithmBase - { - public string ComputeHash(string fileName) - { - if (!File.Exists(fileName)) - ThrowExceptionUtility.ThrowFileNotFound(fileName); - - using (var hashAlgorithm = GetHashAlgorithm()) - { - using (var file = new FileStream(fileName, FileMode.Open, FileAccess.Read, FileShare.Read)) - { - var dataArray = hashAlgorithm.ComputeHash(file); - var stringBuilder = new StringBuilder(); - for (int i = 0; i < dataArray.Length; i++) - { - stringBuilder.Append(dataArray[i].ToString("x2")); - } - return stringBuilder.ToString(); - } - } - } - - protected abstract HashAlgorithm GetHashAlgorithm(); - } -} \ No newline at end of file diff --git a/src/c#/GeneralUpdate.Core/HashAlgorithms/Md5HashAlgorithm.cs b/src/c#/GeneralUpdate.Core/HashAlgorithms/Md5HashAlgorithm.cs deleted file mode 100644 index 65020343..00000000 --- a/src/c#/GeneralUpdate.Core/HashAlgorithms/Md5HashAlgorithm.cs +++ /dev/null @@ -1,9 +0,0 @@ -using System.Security.Cryptography; - -namespace GeneralUpdate.Core.HashAlgorithms -{ - public class Md5HashAlgorithm : HashAlgorithmBase - { - protected override HashAlgorithm GetHashAlgorithm() => MD5.Create(); - } -} \ No newline at end of file diff --git a/src/c#/GeneralUpdate.Core/HashAlgorithms/Sha1HashAlgorithm.cs b/src/c#/GeneralUpdate.Core/HashAlgorithms/Sha1HashAlgorithm.cs deleted file mode 100644 index cec89ba1..00000000 --- a/src/c#/GeneralUpdate.Core/HashAlgorithms/Sha1HashAlgorithm.cs +++ /dev/null @@ -1,9 +0,0 @@ -using System.Security.Cryptography; - -namespace GeneralUpdate.Core.HashAlgorithms -{ - public class Sha1HashAlgorithm : HashAlgorithmBase - { - protected override HashAlgorithm GetHashAlgorithm() => new SHA1Managed(); - } -} \ No newline at end of file diff --git a/src/c#/GeneralUpdate.Core/HashAlgorithms/Sha256HashAlgorithm.cs b/src/c#/GeneralUpdate.Core/HashAlgorithms/Sha256HashAlgorithm.cs deleted file mode 100644 index d6135eae..00000000 --- a/src/c#/GeneralUpdate.Core/HashAlgorithms/Sha256HashAlgorithm.cs +++ /dev/null @@ -1,9 +0,0 @@ -using System.Security.Cryptography; - -namespace GeneralUpdate.Core.HashAlgorithms -{ - public class Sha256HashAlgorithm : HashAlgorithmBase - { - protected override HashAlgorithm GetHashAlgorithm() => SHA256.Create(); - } -} \ No newline at end of file diff --git a/src/c#/GeneralUpdate.Core/Internal/ExceptionEventArgs.cs b/src/c#/GeneralUpdate.Core/Internal/ExceptionEventArgs.cs new file mode 100644 index 00000000..3eecdd39 --- /dev/null +++ b/src/c#/GeneralUpdate.Core/Internal/ExceptionEventArgs.cs @@ -0,0 +1,15 @@ +using System; + +namespace GeneralUpdate.ClientCore.Internal; + +public class ExceptionEventArgs : EventArgs +{ + public ExceptionEventArgs(Exception exception = null, string message = null) + { + Exception = exception ?? throw new Exception(nameof(exception)); + Message = message ?? exception.Message; + } + + public Exception Exception { get; private set; } + public string Message { get; private set; } +} \ No newline at end of file diff --git a/src/c#/GeneralUpdate.Core/Internal/OSSDownloadArgs.cs b/src/c#/GeneralUpdate.Core/Internal/OSSDownloadArgs.cs new file mode 100644 index 00000000..0aaf7f9b --- /dev/null +++ b/src/c#/GeneralUpdate.Core/Internal/OSSDownloadArgs.cs @@ -0,0 +1,6 @@ +namespace GeneralUpdate.Core.Internal; + +public class OSSDownloadArgs +{ + +} \ No newline at end of file diff --git a/src/c#/GeneralUpdate.Core/Pipeline/DriverMiddleware.cs b/src/c#/GeneralUpdate.Core/Pipeline/DriverMiddleware.cs new file mode 100644 index 00000000..4f679481 --- /dev/null +++ b/src/c#/GeneralUpdate.Core/Pipeline/DriverMiddleware.cs @@ -0,0 +1,6 @@ +namespace GeneralUpdate.Core.Pipeline; + +public class DriverMiddleware +{ + +} \ No newline at end of file diff --git a/src/c#/GeneralUpdate.Core/Pipeline/HashMiddleware.cs b/src/c#/GeneralUpdate.Core/Pipeline/HashMiddleware.cs new file mode 100644 index 00000000..4b5d0bd9 --- /dev/null +++ b/src/c#/GeneralUpdate.Core/Pipeline/HashMiddleware.cs @@ -0,0 +1,29 @@ +using System; +using System.Security.Cryptography; +using System.Threading.Tasks; +using GeneralUpdate.Common.HashAlgorithms; +using GeneralUpdate.Common.Internal.Pipeline; + +namespace GeneralUpdate.ClientCore.Pipeline; + +public class HashMiddleware : IMiddleware +{ + public async Task InvokeAsync(PipelineContext context) + { + var fileName = context.Get("FileName"); + var hash = context.Get("Hash"); + + var isVerify = await VerifyFileHash(fileName, hash); + if (!isVerify) throw new CryptographicException("Hash verification failed ."); + } + + private Task VerifyFileHash(string fileName, string hash) + { + return Task.Run(() => + { + var hashAlgorithm = new Sha256HashAlgorithm(); + var hashSha256 = hashAlgorithm.ComputeHash(fileName); + return string.Equals(hash, hashSha256, StringComparison.OrdinalIgnoreCase); + }); + } +} \ No newline at end of file diff --git a/src/c#/GeneralUpdate.Core/Pipeline/PatchMiddleware.cs b/src/c#/GeneralUpdate.Core/Pipeline/PatchMiddleware.cs new file mode 100644 index 00000000..dadac887 --- /dev/null +++ b/src/c#/GeneralUpdate.Core/Pipeline/PatchMiddleware.cs @@ -0,0 +1,20 @@ +using System.Collections.Generic; +using System.Threading.Tasks; +using GeneralUpdate.Common.Internal.Pipeline; +using GeneralUpdate.Differential; + +namespace GeneralUpdate.ClientCore.Pipeline; + +public class PatchMiddleware : IMiddleware +{ + public async Task InvokeAsync(PipelineContext context) + { + var sourcePath = context.Get("SourcePath"); + var targetPath = context.Get("TargetPath"); + var blackFiles = context.Get>("BlackFiles"); + var blackFileFormats = context.Get>("BlackFileFormats"); + + DifferentialCore.Instance.SetBlocklist(blackFiles, blackFileFormats); + await DifferentialCore.Instance.Dirty(sourcePath, targetPath); + } +} \ No newline at end of file diff --git a/src/c#/GeneralUpdate.Core/Pipeline/ZipMiddleware.cs b/src/c#/GeneralUpdate.Core/Pipeline/ZipMiddleware.cs new file mode 100644 index 00000000..1089d88a --- /dev/null +++ b/src/c#/GeneralUpdate.Core/Pipeline/ZipMiddleware.cs @@ -0,0 +1,39 @@ +using System.Text; +using System.Threading.Tasks; +using GeneralUpdate.Common.Internal.Pipeline; +using GeneralUpdate.Zip; +using GeneralUpdate.Zip.Factory; + +namespace GeneralUpdate.ClientCore.Pipeline; + +public class ZipMiddleware : IMiddleware +{ + public Task InvokeAsync(PipelineContext context) + { + return Task.Run(() => + { + var type = MatchType(context.Get("Format")); + var name = context.Get("Name"); + var sourcePath = context.Get("SourcePath"); + var destinationPath = context.Get("DestinationPath"); + var encoding = context.Get("Encoding"); + + var generalZipfactory = new GeneralZipFactory(); + generalZipfactory.CompressProgress += (sender, args) => { }; + generalZipfactory.Completed += (sender, args) => { }; + generalZipfactory.CreateOperate(type, name, sourcePath, destinationPath, true, encoding); + generalZipfactory.UnZip(); + }); + } + + private static OperationType MatchType(string extensionName) + { + var type = extensionName switch + { + ".zip" => OperationType.GZip, + ".7z" => OperationType.G7z, + _ => OperationType.None + }; + return type; + } +} \ No newline at end of file diff --git a/src/c#/GeneralUpdate.Core/Pipelines/Attributes/DynamicallyAccessedMemberTypes.cs b/src/c#/GeneralUpdate.Core/Pipelines/Attributes/DynamicallyAccessedMemberTypes.cs deleted file mode 100644 index 92d15256..00000000 --- a/src/c#/GeneralUpdate.Core/Pipelines/Attributes/DynamicallyAccessedMemberTypes.cs +++ /dev/null @@ -1,38 +0,0 @@ -using System; - -namespace GeneralUpdate.Core.Pipelines.Attributes -{ - [Flags] - internal enum DynamicallyAccessedMemberTypes - { - None = 0, - - PublicParameterlessConstructor = 0x0001, - - PublicConstructors = 0x0002 | PublicParameterlessConstructor, - - NonPublicConstructors = 0x0004, - - PublicMethods = 0x0008, - - NonPublicMethods = 0x0010, - - PublicFields = 0x0020, - - NonPublicFields = 0x0040, - - PublicNestedTypes = 0x0080, - - NonPublicNestedTypes = 0x0100, - - PublicProperties = 0x0200, - - NonPublicProperties = 0x0400, - - PublicEvents = 0x0800, - - NonPublicEvents = 0x1000, - - All = ~None - } -} \ No newline at end of file diff --git a/src/c#/GeneralUpdate.Core/Pipelines/Attributes/DynamicallyAccessedMembersAttribute.cs b/src/c#/GeneralUpdate.Core/Pipelines/Attributes/DynamicallyAccessedMembersAttribute.cs deleted file mode 100644 index d3a279f8..00000000 --- a/src/c#/GeneralUpdate.Core/Pipelines/Attributes/DynamicallyAccessedMembersAttribute.cs +++ /dev/null @@ -1,15 +0,0 @@ -using System; - -namespace GeneralUpdate.Core.Pipelines.Attributes -{ - [AttributeUsage( - AttributeTargets.Field | AttributeTargets.ReturnValue | AttributeTargets.GenericParameter | - AttributeTargets.Parameter | AttributeTargets.Property | AttributeTargets.Method, - Inherited = false)] - internal sealed class DynamicallyAccessedMembersAttribute : Attribute - { - public DynamicallyAccessedMembersAttribute(DynamicallyAccessedMemberTypes memberTypes) => MemberTypes = memberTypes; - - public DynamicallyAccessedMemberTypes MemberTypes { get; } - } -} \ No newline at end of file diff --git a/src/c#/GeneralUpdate.Core/Pipelines/Context/BaseContext.cs b/src/c#/GeneralUpdate.Core/Pipelines/Context/BaseContext.cs deleted file mode 100644 index f00c56ca..00000000 --- a/src/c#/GeneralUpdate.Core/Pipelines/Context/BaseContext.cs +++ /dev/null @@ -1,88 +0,0 @@ -using GeneralUpdate.Core.Domain.Entity; -using System; -using System.Collections.Generic; -using System.Text; - -namespace GeneralUpdate.Core.Pipelines.Context -{ - /// - /// Pipeline common content. - /// - public class BaseContext - { - public VersionInfo Version { get; private set; } - public string Name { get; private set; } - public string ZipfilePath { get; private set; } - public string TargetPath { get; private set; } - public string SourcePath { get; private set; } - public string Format { get; private set; } - public int AppType { get; private set; } - public Encoding Encoding { get; private set; } - public List BlackFiles { get; private set; } - public List BlackFileFormats { get; private set; } - - private BaseContext() - { } - - public class Builder - { - private readonly BaseContext _context = new BaseContext(); - - public Builder SetVersion(VersionInfo version) - { - _context.Version = version ?? throw new ArgumentNullException($"{nameof(VersionInfo)} Cannot be empty"); - return this; - } - - public Builder SetZipfilePath(string zipfilePath) - { - _context.ZipfilePath = string.IsNullOrWhiteSpace(zipfilePath) ? throw new ArgumentNullException($"{nameof(zipfilePath)} Cannot be empty") : zipfilePath; - return this; - } - - public Builder SetTargetPath(string targetPath) - { - _context.TargetPath = string.IsNullOrWhiteSpace(targetPath) ? throw new ArgumentNullException($"{nameof(targetPath)} Cannot be empty") : targetPath; - return this; - } - - public Builder SetSourcePath(string sourcePath) - { - _context.SourcePath = string.IsNullOrWhiteSpace(sourcePath) ? throw new ArgumentNullException($"{nameof(sourcePath)} Cannot be empty") : sourcePath; - return this; - } - - public Builder SetFormat(string format) - { - _context.Format = string.IsNullOrWhiteSpace(format) ? throw new ArgumentNullException($"{nameof(format)} Cannot be empty") : format; - return this; - } - - public Builder SetEncoding(Encoding encoding) - { - _context.Encoding = encoding; - return this; - } - - public Builder SetBlackFiles(List files) - { - _context.BlackFiles = files; - return this; - } - - public Builder SetBlackFileFormats(List fileFormats) - { - _context.BlackFileFormats = fileFormats; - return this; - } - - public Builder SetAppType(int type) - { - _context.AppType = type; - return this; - } - - public BaseContext Build() => _context; - } - } -} \ No newline at end of file diff --git a/src/c#/GeneralUpdate.Core/Pipelines/Middleware/DriveMiddleware.cs b/src/c#/GeneralUpdate.Core/Pipelines/Middleware/DriveMiddleware.cs deleted file mode 100644 index 46f5ff3d..00000000 --- a/src/c#/GeneralUpdate.Core/Pipelines/Middleware/DriveMiddleware.cs +++ /dev/null @@ -1,76 +0,0 @@ -using GeneralUpdate.Core.Driver; -using GeneralUpdate.Core.Pipelines.Context; -using System; -using System.Collections.Generic; -using System.Diagnostics; -using System.IO; -using System.Threading.Tasks; - -namespace GeneralUpdate.Core.Pipelines.Middleware -{ - /// - /// Drive file processing class. - /// - public class DriveMiddleware : IMiddleware - { - public async Task InvokeAsync(BaseContext context, MiddlewareStack stack) - { - var drivers = GetAllDriverDirectories(context.TargetPath); - var information = new DriverInformation.Builder() - .SetInstallDirectory(Path.Combine(context.SourcePath, context.Version.ToString())) - .SetOutPutDirectory(Path.Combine(context.TargetPath, context.Version.ToString())) - .SetDriverNames(drivers) - .Build(); - - var processor = new DriverProcessor(); - processor.AddCommand(new BackupDriverCommand(information)); - processor.AddCommand(new DeleteDriverCommand(information)); - processor.AddCommand(new InstallDriverCommand(information)); - processor.ProcessCommands(); - - var node = stack.Pop(); - if (node != null) await node.Next.Invoke(context, stack); - } - - /// - /// Identifies all folders containing driver files in the specified directory and returns the directory collection. - /// - /// - /// - private List GetAllDriverDirectories(string path) - { - var driverDirectories = new HashSet(); - try - { - foreach (string filePath in Directory.GetFiles(path)) - { - if (IsDriverFile(filePath)) - driverDirectories.Add(filePath); - } - - foreach (string directory in Directory.GetDirectories(path)) - { - driverDirectories.UnionWith(GetAllDriverDirectories(directory)); - } - } - catch (UnauthorizedAccessException) - { - Trace.WriteLine("No access directory:" + path); - } - catch (PathTooLongException) - { - Trace.WriteLine("Path overlength:" + path); - } - - return new List(driverDirectories); - } - - /// - /// Match the driver installation boot file. - /// - /// - /// - private bool IsDriverFile(string filePath) => - string.Equals(Path.GetExtension(filePath), ".inf", StringComparison.OrdinalIgnoreCase); - } -} \ No newline at end of file diff --git a/src/c#/GeneralUpdate.Core/Pipelines/Middleware/HashMiddleware.cs b/src/c#/GeneralUpdate.Core/Pipelines/Middleware/HashMiddleware.cs deleted file mode 100644 index f4ba5cda..00000000 --- a/src/c#/GeneralUpdate.Core/Pipelines/Middleware/HashMiddleware.cs +++ /dev/null @@ -1,30 +0,0 @@ -using GeneralUpdate.Core.Domain.Enum; -using GeneralUpdate.Core.Events; -using GeneralUpdate.Core.Events.MultiEventArgs; -using GeneralUpdate.Core.HashAlgorithms; -using GeneralUpdate.Core.Pipelines.Context; -using System; -using System.Threading.Tasks; - -namespace GeneralUpdate.Core.Pipelines.Middleware -{ - public class HashMiddleware : IMiddleware - { - public async Task InvokeAsync(BaseContext context, MiddlewareStack stack) - { - EventManager.Instance.Dispatch>(this, new MultiDownloadProgressChangedEventArgs(context.Version, ProgressType.Hash, "Verify file MD5 code ...")); - var version = context.Version; - bool isVerify = VerifyFileHash(context.ZipfilePath, version.Hash); - if (!isVerify) throw new Exception($"The update package hash code is inconsistent ! version-{version.Version} hash-{version.Hash} ."); - var node = stack.Pop(); - if (node != null) await node.Next.Invoke(context, stack); - } - - private bool VerifyFileHash(string fileName, string hash) - { - var hashAlgorithm = new Sha256HashAlgorithm(); - var hashSha256 = hashAlgorithm.ComputeHash(fileName); - return string.Equals(hash, hashSha256, StringComparison.OrdinalIgnoreCase); - } - } -} \ No newline at end of file diff --git a/src/c#/GeneralUpdate.Core/Pipelines/Middleware/IMiddleware.cs b/src/c#/GeneralUpdate.Core/Pipelines/Middleware/IMiddleware.cs deleted file mode 100644 index 0ea34b58..00000000 --- a/src/c#/GeneralUpdate.Core/Pipelines/Middleware/IMiddleware.cs +++ /dev/null @@ -1,10 +0,0 @@ -using GeneralUpdate.Core.Pipelines.Context; -using System.Threading.Tasks; - -namespace GeneralUpdate.Core.Pipelines.Middleware -{ - public interface IMiddleware - { - Task InvokeAsync(BaseContext context, MiddlewareStack stack); - } -} \ No newline at end of file diff --git a/src/c#/GeneralUpdate.Core/Pipelines/Middleware/MiddlewareExtensions.cs b/src/c#/GeneralUpdate.Core/Pipelines/Middleware/MiddlewareExtensions.cs deleted file mode 100644 index a6363516..00000000 --- a/src/c#/GeneralUpdate.Core/Pipelines/Middleware/MiddlewareExtensions.cs +++ /dev/null @@ -1,64 +0,0 @@ -using GeneralUpdate.Core.Pipelines.Attributes; -using GeneralUpdate.Core.Pipelines.Context; -using GeneralUpdate.Core.Pipelines.MiddlewareResolver; -using GeneralUpdate.Core.Pipelines.Pipeline; -using System; -using System.Reflection; -using System.Threading.Tasks; - -namespace GeneralUpdate.Core.Pipelines.Middleware -{ - public static class MiddlewareExtensions - { - internal const string InvokeAsyncMethodName = "InvokeAsync"; - - private const DynamicallyAccessedMemberTypes MiddlewareAccessibility = - DynamicallyAccessedMemberTypes.PublicConstructors | DynamicallyAccessedMemberTypes.PublicMethods; - - public static IPipelineBuilder UseMiddleware<[DynamicallyAccessedMembers(MiddlewareAccessibility)] TMiddleware>(this IPipelineBuilder pipeline) => pipeline.UseMiddleware(typeof(TMiddleware), true); - - public static IPipelineBuilder UseMiddlewareIf<[DynamicallyAccessedMembers(MiddlewareAccessibility)] TMiddleware>(this IPipelineBuilder pipeline, bool condition = false) => pipeline.UseMiddleware(typeof(TMiddleware), condition); - - public static IPipelineBuilder UseMiddleware( - this IPipelineBuilder pipeline, - [DynamicallyAccessedMembers(MiddlewareAccessibility)] Type middleware, bool condition) - { - if (!condition) return pipeline; - - if (!typeof(IMiddleware).IsAssignableFrom(middleware)) - throw new ArgumentException($"The middleware type must implement \"{typeof(IMiddleware)}\"."); - - var methods = middleware.GetMethods(BindingFlags.Instance | BindingFlags.Public); - MethodInfo invokeMethod = null; - foreach (var method in methods) - { - if (string.Equals(method.Name, InvokeAsyncMethodName, StringComparison.OrdinalIgnoreCase)) - { - if (method == null) throw new InvalidOperationException(InvokeAsyncMethodName); - invokeMethod = method; - break; - } - } - - if (invokeMethod is null) - throw new InvalidOperationException("No suitable method matched ."); - - if (!typeof(Task).IsAssignableFrom(invokeMethod.ReturnType)) - throw new InvalidOperationException($"The method is not an awaitable method {nameof(Task)} !"); - - var parameters = invokeMethod.GetParameters(); - if (parameters.Length == 0 || parameters[0].ParameterType != typeof(BaseContext)) - throw new InvalidOperationException($" The method parameter does not contain an {nameof(BaseContext)} type parameter !"); - - return pipeline.Use(((IMiddleware)ActivatorMiddlewareResolver.Resolve(middleware))); - } - - private readonly struct InvokeMiddlewareState - { - public InvokeMiddlewareState([DynamicallyAccessedMembers(MiddlewareAccessibility)] Type middleware) => Middleware = middleware; - - [DynamicallyAccessedMembers(MiddlewareAccessibility)] - public Type Middleware { get; } - } - } -} \ No newline at end of file diff --git a/src/c#/GeneralUpdate.Core/Pipelines/Middleware/MiddlewareStack.cs b/src/c#/GeneralUpdate.Core/Pipelines/Middleware/MiddlewareStack.cs deleted file mode 100644 index b66943ed..00000000 --- a/src/c#/GeneralUpdate.Core/Pipelines/Middleware/MiddlewareStack.cs +++ /dev/null @@ -1,62 +0,0 @@ -using GeneralUpdate.Core.Pipelines.Context; -using System; -using System.Collections.Generic; -using System.Linq; -using System.Threading.Tasks; - -namespace GeneralUpdate.Core.Pipelines.Middleware -{ - public sealed class MiddlewareNode - { - /// - /// Go to the next middleware node. - /// - public Func Next { get; set; } - - public MiddlewareNode(Func next) => Next = next; - } - - /// - /// Middleware stack space. - /// - public sealed class MiddlewareStack - { - private int maxSize; - private MiddlewareNode[] stackArray; - private int top = -1; - - public MiddlewareStack(int maxSize) - { - this.maxSize = maxSize; - stackArray = new MiddlewareNode[maxSize]; - } - - public MiddlewareStack(IList nodes) - { - maxSize = nodes.Count; - top = maxSize - 1; - stackArray = nodes.Reverse().ToArray(); - } - - public bool IsFull() => top == maxSize - 1; - - public bool IsEmpty() => top == -1; - - /// - /// Add middleware. - /// - /// - public void Push(MiddlewareNode value) - { - if (IsFull()) return; - top++; - stackArray[top] = value; - } - - public MiddlewareNode Pop() - { - if (IsEmpty()) return null; - return stackArray[top--]; - } - } -} \ No newline at end of file diff --git a/src/c#/GeneralUpdate.Core/Pipelines/Middleware/PatchMiddleware.cs b/src/c#/GeneralUpdate.Core/Pipelines/Middleware/PatchMiddleware.cs deleted file mode 100644 index 5e1f8507..00000000 --- a/src/c#/GeneralUpdate.Core/Pipelines/Middleware/PatchMiddleware.cs +++ /dev/null @@ -1,22 +0,0 @@ -using GeneralUpdate.Core.Domain.Enum; -using GeneralUpdate.Core.Events; -using GeneralUpdate.Core.Events.MultiEventArgs; -using GeneralUpdate.Core.Pipelines.Context; -using GeneralUpdate.Differential; -using System; -using System.Threading.Tasks; - -namespace GeneralUpdate.Core.Pipelines.Middleware -{ - public class PatchMiddleware : IMiddleware - { - public async Task InvokeAsync(BaseContext context, MiddlewareStack stack) - { - EventManager.Instance.Dispatch>(this, new MultiDownloadProgressChangedEventArgs(context.Version, ProgressType.Patch, "Update patch file ...")); - DifferentialCore.Instance.SetBlocklist(context.BlackFiles, context.BlackFileFormats); - await DifferentialCore.Instance.Dirty(context.SourcePath, context.TargetPath); - var node = stack.Pop(); - if (node != null) await node.Next.Invoke(context, stack); - } - } -} \ No newline at end of file diff --git a/src/c#/GeneralUpdate.Core/Pipelines/Middleware/ZipMiddleware.cs b/src/c#/GeneralUpdate.Core/Pipelines/Middleware/ZipMiddleware.cs deleted file mode 100644 index 00402ca0..00000000 --- a/src/c#/GeneralUpdate.Core/Pipelines/Middleware/ZipMiddleware.cs +++ /dev/null @@ -1,68 +0,0 @@ -using GeneralUpdate.Core.Domain.Enum; -using GeneralUpdate.Core.Events; -using GeneralUpdate.Core.Events.CommonArgs; -using GeneralUpdate.Core.Events.MultiEventArgs; -using GeneralUpdate.Core.Pipelines.Context; -using GeneralUpdate.Zip; -using GeneralUpdate.Zip.Factory; -using System; -using System.Threading.Tasks; - -namespace GeneralUpdate.Core.Pipelines.Middleware -{ - public class ZipMiddleware : IMiddleware - { - public async Task InvokeAsync(BaseContext context, MiddlewareStack stack) - { - EventManager.Instance.Dispatch>(this, new MultiDownloadProgressChangedEventArgs(context.Version, ProgressType.Updatefile, "In the unzipped file ...")); - var version = context.Version; - bool isUnzip = UnZip(context); - if (!isUnzip) throw new Exception($"Unzip file failed , Version-{version.Version} Hash-{version.Hash} !"); - var node = stack.Pop(); - if (node != null) await node.Next.Invoke(context, stack); - } - - /// - /// UnZip - /// - /// - /// - /// - /// - protected bool UnZip(BaseContext context) - { - try - { - bool isComplated = false; - var generalZipfactory = new GeneralZipFactory(); - generalZipfactory.UnZipProgress += (sender, e) => - EventManager.Instance.Dispatch>(this, new MultiDownloadProgressChangedEventArgs(context.Version, ProgressType.Updatefile, "Updatting file...")); - generalZipfactory.Completed += (sender, e) => isComplated = true; - generalZipfactory.CreateOperate(MatchType(context.Format), context.Name, context.ZipfilePath, context.TargetPath, false, context.Encoding). - UnZip(); - return isComplated; - } - catch (Exception exception) - { - EventManager.Instance.Dispatch>(this, new ExceptionEventArgs(exception)); - return false; - } - } - - private OperationType MatchType(string extensionName) - { - OperationType type = OperationType.None; - switch (extensionName) - { - case ".zip": - type = OperationType.GZip; - break; - - case ".7z": - type = OperationType.G7z; - break; - } - return type; - } - } -} \ No newline at end of file diff --git a/src/c#/GeneralUpdate.Core/Pipelines/MiddlewareResolver/ActivatorMiddlewareResolver.cs b/src/c#/GeneralUpdate.Core/Pipelines/MiddlewareResolver/ActivatorMiddlewareResolver.cs deleted file mode 100644 index 55f2c79f..00000000 --- a/src/c#/GeneralUpdate.Core/Pipelines/MiddlewareResolver/ActivatorMiddlewareResolver.cs +++ /dev/null @@ -1,9 +0,0 @@ -using System; - -namespace GeneralUpdate.Core.Pipelines.MiddlewareResolver -{ - public class ActivatorMiddlewareResolver - { - public static object Resolve(Type type) => Activator.CreateInstance(type); - } -} \ No newline at end of file diff --git a/src/c#/GeneralUpdate.Core/Pipelines/Pipeline/IPipelineBuilder.cs b/src/c#/GeneralUpdate.Core/Pipelines/Pipeline/IPipelineBuilder.cs deleted file mode 100644 index 11c0e49b..00000000 --- a/src/c#/GeneralUpdate.Core/Pipelines/Pipeline/IPipelineBuilder.cs +++ /dev/null @@ -1,21 +0,0 @@ -using GeneralUpdate.Core.Pipelines.Middleware; -using System.Threading.Tasks; - -namespace GeneralUpdate.Core.Pipelines.Pipeline -{ - public interface IPipelineBuilder - { - /// - /// reference middleware. - /// - /// - /// - IPipelineBuilder Use(IMiddleware middleware); - - /// - /// start the pipeline. - /// - /// - Task Build(); - } -} \ No newline at end of file diff --git a/src/c#/GeneralUpdate.Core/Pipelines/PipelineBuilder.cs b/src/c#/GeneralUpdate.Core/Pipelines/PipelineBuilder.cs deleted file mode 100644 index 27531958..00000000 --- a/src/c#/GeneralUpdate.Core/Pipelines/PipelineBuilder.cs +++ /dev/null @@ -1,48 +0,0 @@ -using GeneralUpdate.Core.Pipelines.Context; -using GeneralUpdate.Core.Pipelines.Middleware; -using GeneralUpdate.Core.Pipelines.Pipeline; -using System; -using System.Collections.Generic; -using System.Threading.Tasks; - -namespace GeneralUpdate.Core.Pipelines -{ - public class PipelineBuilder : IPipelineBuilder where TContext : BaseContext - { - private IList nodes = new List(); - private MiddlewareStack _components; - private readonly TContext _context; - - public PipelineBuilder(TContext context) - { - if (context == null) throw new ArgumentNullException(nameof(context)); - _context = context; - } - - /// - /// Add middleware to the stack. - /// - /// - /// - /// - public IPipelineBuilder Use(IMiddleware middleware) - { - if (middleware == null) throw new ArgumentNullException(nameof(middleware)); - nodes.Add(new MiddlewareNode(middleware.InvokeAsync)); - return this; - } - - /// - /// Start the pipeline and execute the middleware sequentially. - /// - /// - /// - public async Task Build() - { - if (nodes == null || nodes.Count == 0) throw new ArgumentNullException(nameof(nodes)); - _components = new MiddlewareStack(nodes); - await _components.Pop().Next.Invoke(_context, _components); - return this; - } - } -} \ No newline at end of file diff --git a/src/c#/GeneralUpdate.Core/Strategys/AbstractStrategy.cs b/src/c#/GeneralUpdate.Core/Strategys/AbstractStrategy.cs deleted file mode 100644 index 5b7384ec..00000000 --- a/src/c#/GeneralUpdate.Core/Strategys/AbstractStrategy.cs +++ /dev/null @@ -1,23 +0,0 @@ -using System; -using System.Text; -using System.Threading.Tasks; - -namespace GeneralUpdate.Core.Strategys -{ - public abstract class AbstractStrategy : IStrategy - { - protected const string PATCHS = "patchs"; - - public virtual void Execute() => throw new NotImplementedException(); - - public virtual bool StartApp(string appName, int appType) => throw new NotImplementedException(); - - public virtual string GetPlatform() => throw new NotImplementedException(); - - public virtual Task ExecuteTaskAsync() => throw new NotImplementedException(); - - public virtual void Create(T parameter) where T : class => throw new NotImplementedException(); - - public virtual void Create(T parameter, Encoding encoding) where T : class => throw new NotImplementedException(); - } -} \ No newline at end of file diff --git a/src/c#/GeneralUpdate.Core/Strategys/IStrategy.cs b/src/c#/GeneralUpdate.Core/Strategys/IStrategy.cs deleted file mode 100644 index cc909d51..00000000 --- a/src/c#/GeneralUpdate.Core/Strategys/IStrategy.cs +++ /dev/null @@ -1,40 +0,0 @@ -using System.Threading.Tasks; - -namespace GeneralUpdate.Core.Strategys -{ - /// - /// Update the strategy, if you need to extend it, you need to inherit this interface. - /// - public interface IStrategy - { - /// - /// Execution strategy. - /// - void Execute(); - - /// - /// After the update is complete. - /// - /// - /// - /// - bool StartApp(string appName, int appType); - - /// - /// Get the platform for the current strategy. - /// - /// - string GetPlatform(); - - /// - /// Execution strategy. - /// - Task ExecuteTaskAsync(); - - /// - /// Create a strategy. - /// - /// Abstraction for updating package information. - void Create(T parameter) where T : class; - } -} \ No newline at end of file diff --git a/src/c#/GeneralUpdate.Core/Strategys/PlatformLinux/LinuxStrategy.cs b/src/c#/GeneralUpdate.Core/Strategys/LinuxStrategy.cs similarity index 100% rename from src/c#/GeneralUpdate.Core/Strategys/PlatformLinux/LinuxStrategy.cs rename to src/c#/GeneralUpdate.Core/Strategys/LinuxStrategy.cs diff --git a/src/c#/GeneralUpdate.Core/Strategys/PlatformWindows/WindowsStrategy.cs b/src/c#/GeneralUpdate.Core/Strategys/WindowsStrategy.cs similarity index 100% rename from src/c#/GeneralUpdate.Core/Strategys/PlatformWindows/WindowsStrategy.cs rename to src/c#/GeneralUpdate.Core/Strategys/WindowsStrategy.cs diff --git a/src/c#/GeneralUpdate.Differential/GStream/BZip2Constants.cs b/src/c#/GeneralUpdate.Differential/Binary/BZip2Constants.cs similarity index 100% rename from src/c#/GeneralUpdate.Differential/GStream/BZip2Constants.cs rename to src/c#/GeneralUpdate.Differential/Binary/BZip2Constants.cs diff --git a/src/c#/GeneralUpdate.Differential/GStream/BZip2InputStream.cs b/src/c#/GeneralUpdate.Differential/Binary/BZip2InputStream.cs similarity index 100% rename from src/c#/GeneralUpdate.Differential/GStream/BZip2InputStream.cs rename to src/c#/GeneralUpdate.Differential/Binary/BZip2InputStream.cs diff --git a/src/c#/GeneralUpdate.Differential/GStream/BZip2OutputStream.cs b/src/c#/GeneralUpdate.Differential/Binary/BZip2OutputStream.cs similarity index 100% rename from src/c#/GeneralUpdate.Differential/GStream/BZip2OutputStream.cs rename to src/c#/GeneralUpdate.Differential/Binary/BZip2OutputStream.cs diff --git a/src/c#/GeneralUpdate.Differential/Binary/BinaryHandle.cs b/src/c#/GeneralUpdate.Differential/Binary/BinaryHandle.cs deleted file mode 100644 index 14e334fd..00000000 --- a/src/c#/GeneralUpdate.Differential/Binary/BinaryHandle.cs +++ /dev/null @@ -1,640 +0,0 @@ -using GeneralUpdate.Differential.GStream; -using System; -using System.IO; -using System.Threading.Tasks; - -namespace GeneralUpdate.Differential.Binary -{ - /// - /// File binary differential processing. - /// - public class BinaryHandle : IBinary - { - #region Private Members - - private const long c_fileSignature = 0x3034464649445342L; - private const int c_headerSize = 32; - private string _oldfilePath, _newfilePath, _patchPath; - - #endregion Private Members - - #region Public Methods - - /// - /// Clean out the files that need to be updated and generate the update package. - /// - /// Old version file path. - /// New version file path - /// Patch file generation path. - /// - /// - public async Task Clean(string oldfilePath, string newfilePath, string patchPath) - { - _oldfilePath = oldfilePath; - _newfilePath = newfilePath; - _patchPath = patchPath; - ValidationParameters(); - try - { - await Task.Run(() => - { - using (FileStream output = new FileStream(patchPath, FileMode.Create)) - { - var oldBytes = File.ReadAllBytes(_oldfilePath); - var newBytes = File.ReadAllBytes(_newfilePath); - - /* Header is - 0 8 "BSDIFF40" - 8 8 length of bzip2ed ctrl block - 16 8 length of bzip2ed diff block - 24 8 length of new file */ - /* File is - 0 32 Header - 32 ?? Bzip2ed ctrl block - ?? ?? Bzip2ed diff block - ?? ?? Bzip2ed extra block */ - byte[] header = new byte[c_headerSize]; - WriteInt64(c_fileSignature, header, 0); // "BSDIFF40" - WriteInt64(0, header, 8); - WriteInt64(0, header, 16); - WriteInt64(newBytes.Length, header, 24); - - long startPosition = output.Position; - output.Write(header, 0, header.Length); - - int[] I = SuffixSort(oldBytes); - - byte[] db = new byte[newBytes.Length]; - byte[] eb = new byte[newBytes.Length]; - - int dblen = 0; - int eblen = 0; - - using (var bz2Stream = new BZip2OutputStream(output) { IsStreamOwner = false }) - { - // compute the differences, writing ctrl as we go - int scan = 0; - int pos = 0; - int len = 0; - int lastscan = 0; - int lastpos = 0; - int lastoffset = 0; - while (scan < newBytes.Length) - { - int oldscore = 0; - - for (int scsc = scan += len; scan < newBytes.Length; scan++) - { - len = Search(I, oldBytes, newBytes, scan, 0, oldBytes.Length, out pos); - - for (; scsc < scan + len; scsc++) - { - if ((scsc + lastoffset < oldBytes.Length) && (oldBytes[scsc + lastoffset] == newBytes[scsc])) - oldscore++; - } - - if ((len == oldscore && len != 0) || (len > oldscore + 8)) - break; - - if ((scan + lastoffset < oldBytes.Length) && (oldBytes[scan + lastoffset] == newBytes[scan])) - oldscore--; - } - - if (len != oldscore || scan == newBytes.Length) - { - int s = 0; - int sf = 0; - int lenf = 0; - for (int i = 0; (lastscan + i < scan) && (lastpos + i < oldBytes.Length);) - { - if (oldBytes[lastpos + i] == newBytes[lastscan + i]) - s++; - i++; - if (s * 2 - i > sf * 2 - lenf) - { - sf = s; - lenf = i; - } - } - - int lenb = 0; - if (scan < newBytes.Length) - { - s = 0; - int sb = 0; - for (int i = 1; (scan >= lastscan + i) && (pos >= i); i++) - { - if (oldBytes[pos - i] == newBytes[scan - i]) - s++; - if (s * 2 - i > sb * 2 - lenb) - { - sb = s; - lenb = i; - } - } - } - - if (lastscan + lenf > scan - lenb) - { - int overlap = (lastscan + lenf) - (scan - lenb); - s = 0; - int ss = 0; - int lens = 0; - for (int i = 0; i < overlap; i++) - { - if (newBytes[lastscan + lenf - overlap + i] == oldBytes[lastpos + lenf - overlap + i]) - s++; - if (newBytes[scan - lenb + i] == oldBytes[pos - lenb + i]) - s--; - if (s > ss) - { - ss = s; - lens = i + 1; - } - } - - lenf += lens - overlap; - lenb -= lens; - } - - for (int i = 0; i < lenf; i++) - db[dblen + i] = (byte)(newBytes[lastscan + i] - oldBytes[lastpos + i]); - for (int i = 0; i < (scan - lenb) - (lastscan + lenf); i++) - eb[eblen + i] = newBytes[lastscan + lenf + i]; - - dblen += lenf; - eblen += (scan - lenb) - (lastscan + lenf); - - byte[] buf = new byte[8]; - WriteInt64(lenf, buf, 0); - bz2Stream.Write(buf, 0, 8); - - WriteInt64((scan - lenb) - (lastscan + lenf), buf, 0); - bz2Stream.Write(buf, 0, 8); - - WriteInt64((pos - lenb) - (lastpos + lenf), buf, 0); - bz2Stream.Write(buf, 0, 8); - - lastscan = scan - lenb; - lastpos = pos - lenb; - lastoffset = pos - scan; - } - } - } - - // compute size of compressed ctrl data - long controlEndPosition = output.Position; - WriteInt64(controlEndPosition - startPosition - c_headerSize, header, 8); - - // write compressed diff data - using (var bz2Stream = new BZip2OutputStream(output) { IsStreamOwner = false }) - { - bz2Stream.Write(db, 0, dblen); - } - - // compute size of compressed diff data - long diffEndPosition = output.Position; - WriteInt64(diffEndPosition - controlEndPosition, header, 16); - - // write compressed extra data - using (var bz2Stream = new BZip2OutputStream(output) { IsStreamOwner = false }) - { - bz2Stream.Write(eb, 0, eblen); - } - - // seek to the beginning, write the header, then seek back to end - long endPosition = output.Position; - output.Position = startPosition; - output.Write(header, 0, header.Length); - output.Position = endPosition; - } - }); - } - catch (Exception ex) - { - throw new Exception($"Clean error : {ex.Message} !", ex.InnerException); - } - } - - /// - /// Update the patch file to the client application. - /// - /// - /// - /// - /// - /// - /// - public async Task Dirty(string oldfilePath, string newfilePath, string patchPath) - { - _oldfilePath = oldfilePath; - _newfilePath = newfilePath; - _patchPath = patchPath; - ValidationParameters(); - await Task.Run(() => - { - using (FileStream input = new FileStream(_oldfilePath, FileMode.Open, FileAccess.Read, FileShare.Read)) - { - using (FileStream output = new FileStream(_newfilePath, FileMode.Create)) - { - Func openPatchStream = () => new FileStream(patchPath, FileMode.Open, FileAccess.Read, FileShare.Read); - //File format: - // 0 8 "BSDIFF40" - // 8 8 X - // 16 8 Y - // 24 8 sizeof(newfile) - // 32 X bzip2(control block) - // 32 + X Y bzip2(diff block) - // 32 + X + Y ??? bzip2(extra block) - //with control block a set of triples(x, y, z) meaning "add x bytes - //from oldfile to x bytes from the diff block; copy y bytes from the - //extra block; seek forwards in oldfile by z bytes". - // read header - long controlLength, diffLength, newSize; - using (Stream patchStream = openPatchStream()) - { - // check patch stream capabilities - if (!patchStream.CanRead) - throw new ArgumentException("Patch stream must be readable.", "openPatchStream"); - if (!patchStream.CanSeek) - throw new ArgumentException("Patch stream must be seekable.", "openPatchStream"); - - byte[] header = ReadExactly(patchStream, c_headerSize); - - // check for appropriate magic - long signature = ReadInt64(header, 0); - if (signature != c_fileSignature) - throw new InvalidOperationException("Corrupt patch."); - - // read lengths from header - controlLength = ReadInt64(header, 8); - diffLength = ReadInt64(header, 16); - newSize = ReadInt64(header, 24); - if (controlLength < 0 || diffLength < 0 || newSize < 0) - throw new InvalidOperationException("Corrupt patch."); - } - - // preallocate buffers for reading and writing - const int c_bufferSize = 1048576; - byte[] newData = new byte[c_bufferSize]; - byte[] oldData = new byte[c_bufferSize]; - - // prepare to read three parts of the patch in parallel - using (Stream compressedControlStream = openPatchStream()) - using (Stream compressedDiffStream = openPatchStream()) - using (Stream compressedExtraStream = openPatchStream()) - { - // seek to the start of each part - compressedControlStream.Seek(c_headerSize, SeekOrigin.Current); - compressedDiffStream.Seek(c_headerSize + controlLength, SeekOrigin.Current); - compressedExtraStream.Seek(c_headerSize + controlLength + diffLength, SeekOrigin.Current); - - // decompress each part (to read it) - using (BZip2InputStream controlStream = new BZip2InputStream(compressedControlStream)) - using (BZip2InputStream diffStream = new BZip2InputStream(compressedDiffStream)) - using (BZip2InputStream extraStream = new BZip2InputStream(compressedExtraStream)) - { - long[] control = new long[3]; - byte[] buffer = new byte[8]; - - int oldPosition = 0; - int newPosition = 0; - while (newPosition < newSize) - { - // read control data - for (int i = 0; i < 3; i++) - { - ReadExactly(controlStream, buffer, 0, 8); - control[i] = ReadInt64(buffer, 0); - } - - // sanity-check - if (newPosition + control[0] > newSize) - throw new InvalidOperationException("Corrupt patch."); - - // seek old file to the position that the new data is diffed against - input.Position = oldPosition; - - int bytesToCopy = (int)control[0]; - while (bytesToCopy > 0) - { - int actualBytesToCopy = Math.Min(bytesToCopy, c_bufferSize); - - // read diff string - ReadExactly(diffStream, newData, 0, actualBytesToCopy); - - // add old data to diff string - int availableInputBytes = Math.Min(actualBytesToCopy, (int)(input.Length - input.Position)); - ReadExactly(input, oldData, 0, availableInputBytes); - - for (int index = 0; index < availableInputBytes; index++) - newData[index] += oldData[index]; - - output.Write(newData, 0, actualBytesToCopy); - - // adjust counters - newPosition += actualBytesToCopy; - oldPosition += actualBytesToCopy; - bytesToCopy -= actualBytesToCopy; - } - - // sanity-check - if (newPosition + control[1] > newSize) - throw new InvalidOperationException("Corrupt patch."); - - // read extra string - bytesToCopy = (int)control[1]; - while (bytesToCopy > 0) - { - int actualBytesToCopy = Math.Min(bytesToCopy, c_bufferSize); - - ReadExactly(extraStream, newData, 0, actualBytesToCopy); - output.Write(newData, 0, actualBytesToCopy); - - newPosition += actualBytesToCopy; - bytesToCopy -= actualBytesToCopy; - } - - // adjust position - oldPosition = (int)(oldPosition + control[2]); - } - } - } - } - } - - File.Delete(_oldfilePath); - File.Move(_newfilePath, _oldfilePath); - }); - } - - #endregion Public Methods - - #region Private Methods - - private void ValidationParameters() - { - if (string.IsNullOrWhiteSpace(_oldfilePath)) throw new ArgumentNullException("'oldfilePath' This parameter cannot be empty ."); - if (string.IsNullOrWhiteSpace(_newfilePath)) throw new ArgumentNullException("'newfilePath' This parameter cannot be empty ."); - if (string.IsNullOrWhiteSpace(_patchPath)) throw new ArgumentNullException("'patchPath' This parameter cannot be empty ."); - } - - private int CompareBytes(byte[] left, int leftOffset, byte[] right, int rightOffset) - { - for (int index = 0; index < left.Length - leftOffset && index < right.Length - rightOffset; index++) - { - int diff = left[index + leftOffset] - right[index + rightOffset]; - if (diff != 0) return diff; - } - return 0; - } - - private int MatchLength(byte[] oldBytes, int oldOffset, byte[] newBytes, int newOffset) - { - int i; - for (i = 0; i < oldBytes.Length - oldOffset && i < newBytes.Length - newOffset; i++) - { - if (oldBytes[i + oldOffset] != newBytes[i + newOffset]) - break; - } - return i; - } - - private int Search(int[] I, byte[] oldBytes, byte[] newBytes, int newOffset, int start, int end, out int pos) - { - if (end - start < 2) - { - int startLength = MatchLength(oldBytes, I[start], newBytes, newOffset); - int endLength = MatchLength(oldBytes, I[end], newBytes, newOffset); - - if (startLength > endLength) - { - pos = I[start]; - return startLength; - } - else - { - pos = I[end]; - return endLength; - } - } - else - { - int midPoint = start + (end - start) / 2; - return CompareBytes(oldBytes, I[midPoint], newBytes, newOffset) < 0 ? - Search(I, oldBytes, newBytes, newOffset, midPoint, end, out pos) : - Search(I, oldBytes, newBytes, newOffset, start, midPoint, out pos); - } - } - - private void Split(int[] I, int[] v, int start, int len, int h) - { - if (len < 16) - { - int j; - for (int k = start; k < start + len; k += j) - { - j = 1; - int x = v[I[k] + h]; - for (int i = 1; k + i < start + len; i++) - { - if (v[I[k + i] + h] < x) - { - x = v[I[k + i] + h]; - j = 0; - } - if (v[I[k + i] + h] == x) - { - Swap(ref I[k + j], ref I[k + i]); - j++; - } - } - for (int i = 0; i < j; i++) - v[I[k + i]] = k + j - 1; - if (j == 1) - I[k] = -1; - } - } - else - { - int x = v[I[start + len / 2] + h]; - int jj = 0; - int kk = 0; - for (int i2 = start; i2 < start + len; i2++) - { - if (v[I[i2] + h] < x) jj++; - if (v[I[i2] + h] == x) kk++; - } - jj += start; - kk += jj; - - int i = start; - int j = 0; - int k = 0; - while (i < jj) - { - if (v[I[i] + h] < x) - { - i++; - } - else if (v[I[i] + h] == x) - { - Swap(ref I[i], ref I[jj + j]); - j++; - } - else - { - Swap(ref I[i], ref I[kk + k]); - k++; - } - } - - while (jj + j < kk) - { - if (v[I[jj + j] + h] == x) - { - j++; - } - else - { - Swap(ref I[jj + j], ref I[kk + k]); - k++; - } - } - - if (jj > start) Split(I, v, start, jj - start, h); - - for (i = 0; i < kk - jj; i++) - v[I[jj + i]] = kk - 1; - if (jj == kk - 1) I[jj] = -1; - - if (start + len > kk) Split(I, v, kk, start + len - kk, h); - } - } - - private int[] SuffixSort(byte[] oldBytes) - { - int[] buckets = new int[256]; - foreach (byte oldByte in oldBytes) - buckets[oldByte]++; - for (int i = 1; i < 256; i++) - buckets[i] += buckets[i - 1]; - for (int i = 255; i > 0; i--) - buckets[i] = buckets[i - 1]; - buckets[0] = 0; - - int[] I = new int[oldBytes.Length + 1]; - for (int i = 0; i < oldBytes.Length; i++) - I[++buckets[oldBytes[i]]] = i; - - int[] v = new int[oldBytes.Length + 1]; - for (int i = 0; i < oldBytes.Length; i++) - v[i] = buckets[oldBytes[i]]; - - for (int i = 1; i < 256; i++) - if (buckets[i] == buckets[i - 1] + 1) I[buckets[i]] = -1; - I[0] = -1; - for (int h = 1; I[0] != -(oldBytes.Length + 1); h += h) - { - int len = 0; - int i = 0; - while (i < oldBytes.Length + 1) - { - if (I[i] < 0) - { - len -= I[i]; - i -= I[i]; - } - else - { - if (len != 0) I[i - len] = -len; - len = v[I[i]] + 1 - i; - Split(I, v, i, len, h); - i += len; - len = 0; - } - } - if (len != 0) I[i - len] = -len; - } - - for (int i = 0; i < oldBytes.Length + 1; i++) - I[v[i]] = i; - - return I; - } - - private void Swap(ref int first, ref int second) - { - int temp = first; - first = second; - second = temp; - } - - private long ReadInt64(byte[] buf, int offset) - { - long value = buf[offset + 7] & 0x7F; - for (int index = 6; index >= 0; index--) - { - value *= 256; - value += buf[offset + index]; - } - if ((buf[offset + 7] & 0x80) != 0) value = -value; - return value; - } - - private void WriteInt64(long value, byte[] buf, int offset) - { - long valueToWrite = value < 0 ? -value : value; - for (int byteIndex = 0; byteIndex < 8; byteIndex++) - { - buf[offset + byteIndex] = unchecked((byte)valueToWrite); - valueToWrite >>= 8; - } - if (value < 0) buf[offset + 7] |= 0x80; - } - - /// - /// Reads exactly bytes from . - /// - /// The stream to read from. - /// The count of bytes to read. - /// A new byte array containing the data read from the stream. - private byte[] ReadExactly(Stream stream, int count) - { - if (count < 0) throw new ArgumentOutOfRangeException("count"); - byte[] buffer = new byte[count]; - ReadExactly(stream, buffer, 0, count); - return buffer; - } - - /// - /// Reads exactly bytes from into - /// , starting at the byte given by . - /// - /// The stream to read from. - /// The buffer to read data into. - /// The offset within the buffer at which data is first written. - /// The count of bytes to read. - private void ReadExactly(Stream stream, byte[] buffer, int offset, int count) - { - // check arguments - if (stream == null) throw new ArgumentNullException("stream"); - if (buffer == null) throw new ArgumentNullException("buffer"); - if (offset < 0 || offset > buffer.Length) throw new ArgumentOutOfRangeException("offset"); - if (count < 0 || buffer.Length - offset < count) throw new ArgumentOutOfRangeException("count"); - - while (count > 0) - { - // read data - int bytesRead = stream.Read(buffer, offset, count); - // check for failure to read - if (bytesRead == 0) throw new EndOfStreamException(); - // move to next block - offset += bytesRead; - count -= bytesRead; - } - } - - #endregion Private Methods - } -} \ No newline at end of file diff --git a/src/c#/GeneralUpdate.Differential/Binary/IBinary.cs b/src/c#/GeneralUpdate.Differential/Binary/IBinary.cs deleted file mode 100644 index 2e99f55e..00000000 --- a/src/c#/GeneralUpdate.Differential/Binary/IBinary.cs +++ /dev/null @@ -1,25 +0,0 @@ -using System.Threading.Tasks; - -namespace GeneralUpdate.Differential.Binary -{ - public interface IBinary - { - /// - /// Sort out the patch . - /// - /// The file path of the previous version . - /// Current version file path . - /// Path to generate patch file . - /// future results . - Task Clean(string oldfilePath, string newfilePath, string patchPath); - - /// - /// Restore the patch. - /// - /// The file path of the previous version . - /// Current version file path . - /// Path to generate patch file . - /// future results . - Task Dirty(string oldfilePath, string newfilePath, string patchPath); - } -} \ No newline at end of file diff --git a/src/c#/GeneralUpdate.Differential/GStream/IChecksum.cs b/src/c#/GeneralUpdate.Differential/Binary/IChecksum.cs similarity index 100% rename from src/c#/GeneralUpdate.Differential/GStream/IChecksum.cs rename to src/c#/GeneralUpdate.Differential/Binary/IChecksum.cs diff --git a/src/c#/GeneralUpdate.Differential/GStream/StrangeCRC.cs b/src/c#/GeneralUpdate.Differential/Binary/StrangeCRC.cs similarity index 100% rename from src/c#/GeneralUpdate.Differential/GStream/StrangeCRC.cs rename to src/c#/GeneralUpdate.Differential/Binary/StrangeCRC.cs diff --git a/src/c#/GeneralUpdate.Differential/ContentProvider/.gitkeep b/src/c#/GeneralUpdate.Differential/ContentProvider/.gitkeep deleted file mode 100644 index e69de29b..00000000 diff --git a/src/c#/GeneralUpdate.Differential/CustomAwaiter/.gitkeep b/src/c#/GeneralUpdate.Differential/CustomAwaiter/.gitkeep deleted file mode 100644 index e69de29b..00000000 diff --git a/src/c#/GeneralUpdate.Differential/Domain/Entity/.gitkeep b/src/c#/GeneralUpdate.Differential/Domain/Entity/.gitkeep deleted file mode 100644 index e69de29b..00000000 diff --git a/src/c#/GeneralUpdate.Differential/Domain/PO/.gitkeep b/src/c#/GeneralUpdate.Differential/Domain/PO/.gitkeep deleted file mode 100644 index e69de29b..00000000 diff --git a/src/c#/GeneralUpdate.Differential/Exceptions/.gitkeep b/src/c#/GeneralUpdate.Differential/Exceptions/.gitkeep deleted file mode 100644 index e69de29b..00000000 diff --git a/src/c#/GeneralUpdate.Differential/Exceptions/CustomArgs/.gitkeep b/src/c#/GeneralUpdate.Differential/Exceptions/CustomArgs/.gitkeep deleted file mode 100644 index e69de29b..00000000 diff --git a/src/c#/GeneralUpdate.Differential/Exceptions/CustomException/.gitkeep b/src/c#/GeneralUpdate.Differential/Exceptions/CustomException/.gitkeep deleted file mode 100644 index e69de29b..00000000 diff --git a/src/c#/GeneralUpdate.Zip/GeneralUpdate.Zip.csproj b/src/c#/GeneralUpdate.Zip/GeneralUpdate.Zip.csproj index 7044d4d8..7438e740 100644 --- a/src/c#/GeneralUpdate.Zip/GeneralUpdate.Zip.csproj +++ b/src/c#/GeneralUpdate.Zip/GeneralUpdate.Zip.csproj @@ -23,13 +23,6 @@ - - - True - \ - - - From 06c5ed850ec3127230f07781b384934549b96efb Mon Sep 17 00:00:00 2001 From: justerzhu Date: Fri, 20 Sep 2024 22:56:03 +0800 Subject: [PATCH 08/33] refactor: Generalupdate.client --- src/c#/GeneralUpdate.Client/Program.cs | 20 +------------------- 1 file changed, 1 insertion(+), 19 deletions(-) diff --git a/src/c#/GeneralUpdate.Client/Program.cs b/src/c#/GeneralUpdate.Client/Program.cs index 1b9dd51c..9a993775 100644 --- a/src/c#/GeneralUpdate.Client/Program.cs +++ b/src/c#/GeneralUpdate.Client/Program.cs @@ -4,25 +4,7 @@ internal class Program { private static void Main(string[] args) { - MySample sample = new MySample(); - sample.TestFileProvider(); - //Task.Run(async() => - //{ - // //415eed05eb310f480d1e4d15516fa00e484ddb9f416908b217f17b782ded2030 - // //var zip1 = @"D:\github_project\WpfClient\WebApi\UpdateFiles\WpfClient_1_24.1.5.1218.zip"; - // //94bd3d806d39cd1b8813298ec0637c7f377658e766845a06cc50917306cb4ad9 - // //var zip2 = @"D:\github_project\WpfClient\WebApi\UpdateFiles\WpfClient_1_24.1.5.1224.zip"; - - // //var hashAlgorithm = new Sha256HashAlgorithm(); - // //var hashSha256 = hashAlgorithm.ComputeHash(zip1); - // //var hashSha2561 = hashAlgorithm.ComputeHash(zip2); - - // MySample sample = new MySample(); - // //await sample.TestDifferentialClean(); - // //await sample.TestDifferentialDirty(); - // await sample.Upgrade(); - //}); - Console.Read(); + } } } \ No newline at end of file From e400d9f6a5013f22938a6c9bbe6fe1360ac1cc15 Mon Sep 17 00:00:00 2001 From: justerzhu Date: Fri, 20 Sep 2024 22:56:20 +0800 Subject: [PATCH 09/33] refactor: refactor generalupdate.upgrad --- .../GeneralClientBootstrap.cs | 540 +++++++++--------- .../GeneralClientOSS.cs | 145 ++--- .../GeneralUpdate.ClientCore.csproj | 132 +---- .../Hubs/RandomRetryPolicy.cs | 34 +- .../Hubs/VersionHub.cs | 327 +++++------ .../Internal/ExceptionEventArgs.cs | 6 +- .../Pipeline/HashMiddleware.cs | 11 +- .../Pipeline/PatchMiddleware.cs | 2 +- .../Pipeline/ZipMiddleware.cs | 4 +- .../Strategys/LinuxStrategy.cs | 7 +- .../Strategys/WindowsStrategy.cs | 149 ++--- 11 files changed, 653 insertions(+), 704 deletions(-) diff --git a/src/c#/GeneralUpdate.ClientCore/GeneralClientBootstrap.cs b/src/c#/GeneralUpdate.ClientCore/GeneralClientBootstrap.cs index fb75e554..bb782691 100644 --- a/src/c#/GeneralUpdate.ClientCore/GeneralClientBootstrap.cs +++ b/src/c#/GeneralUpdate.ClientCore/GeneralClientBootstrap.cs @@ -1,4 +1,3 @@ -using GeneralUpdate.Core.Exceptions.CustomException; using System; using System.Collections.Generic; using System.Diagnostics; @@ -9,6 +8,7 @@ using System.Runtime.InteropServices; using System.Security; using System.Text.Json; +using System.Threading; using System.Threading.Tasks; using GeneralUpdate.ClientCore.Internal; using GeneralUpdate.ClientCore.Strategys; @@ -19,297 +19,319 @@ using GeneralUpdate.Common.Shared.Object; using GeneralUpdate.Common.Shared.Service; -namespace GeneralUpdate.ClientCore +namespace GeneralUpdate.ClientCore; + +/// +/// This component is used only for client application bootstrapping classes. +/// +public class GeneralClientBootstrap : AbstractBootstrap { + #region Public Properties + + /// + /// All update actions of the core object for automatic upgrades will be related to the Packet object. + /// + private Packet Packet { get; } + + #endregion + + #region Private Members + + private IStrategy _strategy; + + private Func _customSkipOption; + + private readonly List> _customOptions = new(); + + #endregion + + #region Public Methods + /// - /// This component is used only for client application bootstrapping classes. + /// Main function for booting the update startup. /// - public class GeneralClientBootstrap : AbstractBootstrap + /// + public override async Task LaunchAsync() { - #region Private Members - - private IStrategy _strategy; - - private Func _customSkipOption; - - private List> _customOptions = new List>(); - - #endregion - - #region Public Properties - - /// - /// All update actions of the core object for automatic upgrades will be related to the Packet object. - /// - private Packet Packet { get; set; } - - #endregion - - #region Public Methods - - /// - /// Main function for booting the update startup. - /// - /// - public override async Task LaunchAsync() - { - ExecuteCustomOptions(); - await InitializeData(); - var manager = new DownloadManager(Packet.InstallPath, Packet.Format, 30); - foreach (var versionInfo in Packet.UpdateVersions) - { - manager.Add(new DownloadTask(manager, versionInfo)); - } - await manager.LaunchTasksAsync(); - return this; - } + ExecuteCustomOptions(); + await InitializeData(); + var manager = new DownloadManager(Packet.InstallPath, Packet.Format, 30); + foreach (var versionInfo in Packet.UpdateVersions) manager.Add(new DownloadTask(manager, versionInfo)); + await manager.LaunchTasksAsync(); + return this; + } - /// - /// Configure server address (Recommended Windows,Linux,Mac). - /// - /// Remote server address. - /// The updater name does not need to contain an extension. - /// - /// Parameter initialization is abnormal. - public GeneralClientBootstrap SetConfig(string url, string appSecretKey, string appName) - { - if (string.IsNullOrEmpty(url)) throw new Exception("Url cannot be empty !"); - string basePath = System.Threading.Thread.GetDomain().BaseDirectory; - Packet.InstallPath = basePath; - Packet.AppSecretKey = appSecretKey; - //update app. - Packet.AppName = appName; - string clientVersion = GetFileVersion(Path.Combine(basePath, $"{Packet.AppName}.exe")); - Packet.ClientVersion = clientVersion; - Packet.AppType = AppType.ClientApp; - Packet.UpdateUrl = $"{url}/versions/{AppType.ClientApp}/{clientVersion}/{Packet.AppSecretKey}"; - //main app. - string mainAppName = Path.GetFileNameWithoutExtension(Process.GetCurrentProcess().MainModule.FileName); - string mainVersion = Assembly.GetExecutingAssembly().GetName().Version.ToString(); - Packet.MainUpdateUrl = $"{url}/versions/{AppType.ClientApp}/{mainVersion}/{Packet.AppSecretKey}"; - Packet.MainAppName = mainAppName; - return this; - } + /// + /// Configure server address (Recommended Windows,Linux,Mac). + /// + /// Remote server address. + /// The updater name does not need to contain an extension. + /// + /// Parameter initialization is abnormal. + public GeneralClientBootstrap SetConfig(string url, string appSecretKey, string appName) + { + if (string.IsNullOrEmpty(url)) throw new Exception("Url cannot be empty !"); + var basePath = Thread.GetDomain().BaseDirectory; + Packet.InstallPath = basePath; + Packet.AppSecretKey = appSecretKey; + //update app. + Packet.AppName = appName; + var clientVersion = GetFileVersion(Path.Combine(basePath, $"{Packet.AppName}.exe")); + Packet.ClientVersion = clientVersion; + Packet.AppType = AppType.ClientApp; + Packet.UpdateUrl = $"{url}/versions/{AppType.ClientApp}/{clientVersion}/{Packet.AppSecretKey}"; + //main app. + var mainAppName = Path.GetFileNameWithoutExtension(Process.GetCurrentProcess().MainModule.FileName); + var mainVersion = Assembly.GetExecutingAssembly().GetName().Version.ToString(); + Packet.MainUpdateUrl = $"{url}/versions/{AppType.ClientApp}/{mainVersion}/{Packet.AppSecretKey}"; + Packet.MainAppName = mainAppName; + return this; + } - /// - /// Custom Configuration (Recommended : All platforms). - /// - /// - /// - public GeneralClientBootstrap SetConfig(Configinfo info) - { - Packet.AppType = info.AppType; - Packet.AppName = info.AppName; - Packet.AppSecretKey = info.AppSecretKey; - Packet.ClientVersion = info.ClientVersion; - Packet.UpdateUrl = info.UpdateUrl; - Packet.MainUpdateUrl = info.MainUpdateUrl; - Packet.MainAppName = info.MainAppName; - Packet.InstallPath = info.InstallPath; - Packet.UpdateLogUrl = info.UpdateLogUrl; - return this; - } + /// + /// Custom Configuration (Recommended : All platforms). + /// + /// + /// + public GeneralClientBootstrap SetConfig(Configinfo info) + { + Packet.AppType = info.AppType; + Packet.AppName = info.AppName; + Packet.AppSecretKey = info.AppSecretKey; + Packet.ClientVersion = info.ClientVersion; + Packet.UpdateUrl = info.UpdateUrl; + Packet.MainUpdateUrl = info.MainUpdateUrl; + Packet.MainAppName = info.MainAppName; + Packet.InstallPath = info.InstallPath; + Packet.UpdateLogUrl = info.UpdateLogUrl; + return this; + } - /// - /// Let the user decide whether to update in the state of non-mandatory update. - /// - /// Custom function ,Custom actions to let users decide whether to update. true update false do not update . - /// - public GeneralClientBootstrap SetCustomSkipOption(Func func) - { - Contract.Requires(func != null); - _customSkipOption = func; - return this; - } + /// + /// Let the user decide whether to update in the state of non-mandatory update. + /// + /// + /// Custom function ,Custom actions to let users decide whether to update. true update false do not + /// update . + /// + /// + public GeneralClientBootstrap SetCustomSkipOption(Func func) + { + Contract.Requires(func != null); + _customSkipOption = func; + return this; + } - /// - /// Add an asynchronous custom operation. - /// In theory, any custom operation can be done. It is recommended to register the environment check method to ensure that there are normal dependencies and environments after the update is completed. - /// - /// - /// - /// - public GeneralClientBootstrap AddCustomOption(List> funcs) - { - Contract.Requires(funcs != null && funcs.Any()); - _customOptions.AddRange(funcs); - return this; - } + /// + /// Add an asynchronous custom operation. + /// In theory, any custom operation can be done. It is recommended to register the environment check method to ensure + /// that there are normal dependencies and environments after the update is completed. + /// + /// + /// + /// + public GeneralClientBootstrap AddCustomOption(List> funcs) + { + Contract.Requires(funcs != null && funcs.Any()); + _customOptions.AddRange(funcs); + return this; + } - public GeneralClientBootstrap AddListenerMultiAllDownloadCompleted(Action callbackAction) => AddListener(callbackAction); + public GeneralClientBootstrap AddListenerMultiAllDownloadCompleted( + Action callbackAction) + { + return AddListener(callbackAction); + } - public GeneralClientBootstrap AddListenerMultiDownloadProgress(Action callbackAction) => AddListener(callbackAction); + public GeneralClientBootstrap AddListenerMultiDownloadProgress( + Action callbackAction) + { + return AddListener(callbackAction); + } - public GeneralClientBootstrap AddListenerMultiDownloadCompleted(Action callbackAction) => AddListener(callbackAction); + public GeneralClientBootstrap AddListenerMultiDownloadCompleted( + Action callbackAction) + { + return AddListener(callbackAction); + } - public GeneralClientBootstrap AddListenerMultiDownloadError(Action callbackAction) => AddListener(callbackAction); + public GeneralClientBootstrap AddListenerMultiDownloadError( + Action callbackAction) + { + return AddListener(callbackAction); + } - public GeneralClientBootstrap AddListenerMultiDownloadStatistics(Action callbackAction) => AddListener(callbackAction); + public GeneralClientBootstrap AddListenerMultiDownloadStatistics( + Action callbackAction) + { + return AddListener(callbackAction); + } - public GeneralClientBootstrap AddListenerException(Action callbackAction) => AddListener(callbackAction); - - #endregion Public Methods + public GeneralClientBootstrap AddListenerException(Action callbackAction) + { + return AddListener(callbackAction); + } - #region Private Methods - - private async Task InitializeData() - { - ClearEnvironmentVariable(); - - //Request the upgrade information needed by the client and upgrade end, and determine if an upgrade is necessary. - var versionService = new VersionService(); - var mainResp = await versionService.ValidationVersion(Packet.MainUpdateUrl); - var upgradeResp = await versionService.ValidationVersion(Packet.UpdateUrl); - - Packet.IsUpgradeUpdate = upgradeResp.Body.IsUpdate; - Packet.IsMainUpdate = mainResp.Body.IsUpdate; - //No need to update, return directly. - if ((!Packet.IsMainUpdate) && (!Packet.IsUpgradeUpdate)) return; - - //If the main program needs to be forced to update, the skip will not take effect. - var isForcibly = mainResp.Body.IsForcibly || upgradeResp.Body.IsForcibly; - if (CanSkip(isForcibly)) return; - - Packet.UpdateVersions = VersionAssembler.ToEntitys(upgradeResp.Body.Versions); - Packet.LastVersion = Packet.UpdateVersions.Last().Version; - - //Initialize the process transfer parameter object. - var processInfo = new ProcessInfo(Packet.MainAppName - , Packet.InstallPath - , Packet.ClientVersion - , Packet.LastVersion - , Packet.UpdateLogUrl - , Packet.Encoding - , Packet.Format - , Packet.DownloadTimeOut - , Packet.AppSecretKey - , mainResp.Body.Versions); - Packet.ProcessInfo = JsonSerializer.Serialize(processInfo); - } + #endregion Public Methods - /// - ///Gets the application version number - /// - /// - /// - /// - private string GetFileVersion(string filePath) - { - var fileInfo = new FileInfo(filePath); - if (fileInfo.Exists) return FileVersionInfo.GetVersionInfo(filePath).FileVersion; - throw new FileNotFoundException($"Failed to obtain file '{filePath}' version. Procedure."); - } + #region Private Methods - /// - /// User decides if update is required. - /// - /// is false to continue execution. - private bool CanSkip(bool isForcibly) - { - if (isForcibly) return false; - Contract.Requires(_customSkipOption != null); - return _customSkipOption.Invoke(); - } + private async Task InitializeData() + { + ClearEnvironmentVariable(); + + //Request the upgrade information needed by the client and upgrade end, and determine if an upgrade is necessary. + var versionService = new VersionService(); + var mainResp = await versionService.ValidationVersion(Packet.MainUpdateUrl); + var upgradeResp = await versionService.ValidationVersion(Packet.UpdateUrl); + + Packet.IsUpgradeUpdate = upgradeResp.Body.IsUpdate; + Packet.IsMainUpdate = mainResp.Body.IsUpdate; + //No need to update, return directly. + if (!Packet.IsMainUpdate && !Packet.IsUpgradeUpdate) return; + + //If the main program needs to be forced to update, the skip will not take effect. + var isForcibly = mainResp.Body.IsForcibly || upgradeResp.Body.IsForcibly; + if (CanSkip(isForcibly)) return; + + Packet.UpdateVersions = VersionAssembler.ToEntitys(upgradeResp.Body.Versions); + Packet.LastVersion = Packet.UpdateVersions.Last().Version; + + //Initialize the process transfer parameter object. + var processInfo = new ProcessInfo(Packet.MainAppName + , Packet.InstallPath + , Packet.ClientVersion + , Packet.LastVersion + , Packet.UpdateLogUrl + , Packet.Encoding + , Packet.Format + , Packet.DownloadTimeOut + , Packet.AppSecretKey + , mainResp.Body.Versions); + Packet.ProcessInfo = JsonSerializer.Serialize(processInfo); + } + + /// + /// Gets the application version number + /// + /// + /// + /// + private string GetFileVersion(string filePath) + { + var fileInfo = new FileInfo(filePath); + if (fileInfo.Exists) return FileVersionInfo.GetVersionInfo(filePath).FileVersion; + throw new FileNotFoundException($"Failed to obtain file '{filePath}' version. Procedure."); + } + + /// + /// User decides if update is required. + /// + /// is false to continue execution. + private bool CanSkip(bool isForcibly) + { + if (isForcibly) return false; + Contract.Requires(_customSkipOption != null); + return _customSkipOption.Invoke(); + } + + /// + /// Performs all injected custom operations. + /// + /// + private void ExecuteCustomOptions() + { + Contract.Requires(_customOptions != null && _customOptions.Any()); + foreach (var option in _customOptions) + if (!option.Invoke()) + EventManager.Instance.Dispatch(this, + new ExceptionEventArgs(null, $"{nameof(option)}Execution failure!")); + } - /// - /// Performs all injected custom operations. - /// - /// - private void ExecuteCustomOptions() + /// + /// Clear the environment variable information needed to start the upgrade assistant process. + /// + private void ClearEnvironmentVariable() + { + try { - Contract.Requires(_customOptions != null && _customOptions.Any()); - foreach (var option in _customOptions) - { - if (!option.Invoke()) - { - EventManager.Instance.Dispatch(this, - new ExceptionEventArgs(null ,$"{nameof(option)}Execution failure!")); - } - } + Environment.SetEnvironmentVariable("ProcessInfo", null, EnvironmentVariableTarget.User); } - - /// - /// Clear the environment variable information needed to start the upgrade assistant process. - /// - private void ClearEnvironmentVariable() + catch (SecurityException ex) { - try - { - Environment.SetEnvironmentVariable("ProcessInfo", null, EnvironmentVariableTarget.User); - } - catch (SecurityException ex) - { - EventManager.Instance.Dispatch(this, - new ExceptionEventArgs(ex, - $"Error: You do not have sufficient permissions to delete this environment variable.")); - } - catch (ArgumentException ex) - { - EventManager.Instance.Dispatch(this, - new ExceptionEventArgs(ex, $"Error: The environment variable name is invalid.")); - } - catch (IOException ex) - { - EventManager.Instance.Dispatch(this, - new ExceptionEventArgs(ex, - $"Error: An I/O error occurred while deleting the environment variable.")); - } - catch (Exception ex) - { - EventManager.Instance.Dispatch(this, - new ExceptionEventArgs(ex, $"Error: An unknown error occurred while deleting the environment variable.")); - } + EventManager.Instance.Dispatch(this, + new ExceptionEventArgs(ex, + "Error: You do not have sufficient permissions to delete this environment variable.")); } - - protected override void ExecuteStrategy() + catch (ArgumentException ex) { - _strategy.Create(Packet); - _strategy.Execute(); + EventManager.Instance.Dispatch(this, + new ExceptionEventArgs(ex, "Error: The environment variable name is invalid.")); } - - protected override GeneralClientBootstrap StrategyFactory() + catch (IOException ex) { - if (RuntimeInformation.IsOSPlatform(OSPlatform.Windows)) - { - _strategy = new WindowsStrategy(); - } - else if (RuntimeInformation.IsOSPlatform(OSPlatform.Linux)) - { - _strategy = new LinuxStrategy(); - } - else - { - throw new PlatformNotSupportedException("The current operating system is not supported!"); - } - - return this; + EventManager.Instance.Dispatch(this, + new ExceptionEventArgs(ex, + "Error: An I/O error occurred while deleting the environment variable.")); } - - private GeneralClientBootstrap AddListener(Action callbackAction) where TArgs : EventArgs + catch (Exception ex) { - Contract.Requires(callbackAction != null); - EventManager.Instance.AddListener(callbackAction); - return this; + EventManager.Instance.Dispatch(this, + new ExceptionEventArgs(ex, + "Error: An unknown error occurred while deleting the environment variable.")); } + } - private void OnMultiDownloadStatistics(object sender, MultiDownloadStatisticsEventArgs e) - => EventManager.Instance.Dispatch(sender, e); + protected override void ExecuteStrategy() + { + _strategy.Create(Packet); + _strategy.Execute(); + } - private void OnMultiDownloadProgressChanged(object sender, MultiDownloadProgressChangedEventArgs e) - => EventManager.Instance.Dispatch(sender, e); + protected override GeneralClientBootstrap StrategyFactory() + { + if (RuntimeInformation.IsOSPlatform(OSPlatform.Windows)) + _strategy = new WindowsStrategy(); + else if (RuntimeInformation.IsOSPlatform(OSPlatform.Linux)) + _strategy = new LinuxStrategy(); + else + throw new PlatformNotSupportedException("The current operating system is not supported!"); - private void OnMultiDownloadCompleted(object sender, MultiDownloadCompletedEventArgs e) - => EventManager.Instance.Dispatch(sender, e); + return this; + } - private void OnMultiDownloadError(object sender, MultiDownloadErrorEventArgs e) - => EventManager.Instance.Dispatch(sender, e); + private GeneralClientBootstrap AddListener(Action callbackAction) where TArgs : EventArgs + { + Contract.Requires(callbackAction != null); + EventManager.Instance.AddListener(callbackAction); + return this; + } - private void OnMultiAllDownloadCompleted(object sender, MultiAllDownloadCompletedEventArgs e) - { - EventManager.Instance.Dispatch(sender, e); - ExecuteStrategy(); - } + private void OnMultiDownloadStatistics(object sender, MultiDownloadStatisticsEventArgs e) + { + EventManager.Instance.Dispatch(sender, e); + } - #endregion Private Methods + private void OnMultiDownloadProgressChanged(object sender, MultiDownloadProgressChangedEventArgs e) + { + EventManager.Instance.Dispatch(sender, e); } + + private void OnMultiDownloadCompleted(object sender, MultiDownloadCompletedEventArgs e) + { + EventManager.Instance.Dispatch(sender, e); + } + + private void OnMultiDownloadError(object sender, MultiDownloadErrorEventArgs e) + { + EventManager.Instance.Dispatch(sender, e); + } + + private void OnMultiAllDownloadCompleted(object sender, MultiAllDownloadCompletedEventArgs e) + { + EventManager.Instance.Dispatch(sender, e); + ExecuteStrategy(); + } + + #endregion Private Methods } \ No newline at end of file diff --git a/src/c#/GeneralUpdate.ClientCore/GeneralClientOSS.cs b/src/c#/GeneralUpdate.ClientCore/GeneralClientOSS.cs index 345a1b94..3a0b74eb 100644 --- a/src/c#/GeneralUpdate.ClientCore/GeneralClientOSS.cs +++ b/src/c#/GeneralUpdate.ClientCore/GeneralClientOSS.cs @@ -1,96 +1,97 @@ -using GeneralUpdate.Core.ContentProvider; -using System; +using System; using System.Collections.Generic; using System.Diagnostics; using System.IO; using System.Linq; using System.Net; using System.Text.Json; +using System.Threading; using System.Threading.Tasks; using GeneralUpdate.ClientCore.Internal; +using GeneralUpdate.Common; using GeneralUpdate.Common.Internal.Event; using GeneralUpdate.Common.Shared.Object; -namespace GeneralUpdate.ClientCore +namespace GeneralUpdate.ClientCore; + +public sealed class GeneralClientOSS { - public sealed class GeneralClientOSS + private GeneralClientOSS() { - private GeneralClientOSS() - { } + } - /// - /// Starting an OSS update for windows,Linux,mac platform. - /// - /// - public static async Task Start(ParamsOSS configParams, string upgradeAppName = "GeneralUpdate.Upgrade") + /// + /// Starting an OSS update for windows,Linux,mac platform. + /// + /// + public static async Task Start(ParamsOSS configParams, string upgradeAppName = "GeneralUpdate.Upgrade") + { + await Task.Run(() => { - await Task.Run(() => + try { - try - { - string basePath = System.Threading.Thread.GetDomain().BaseDirectory; - //Download the version information file from OSS to be updated.(JSON) - var versionsFilePath = Path.Combine(basePath, configParams.VersionFileName); - DownloadFile(configParams.Url + "/" + configParams.VersionFileName, versionsFilePath); - if (!File.Exists(versionsFilePath)) return; - var versions = FileProvider.GetJson>(versionsFilePath); - if (versions == null || versions.Count == 0) return; - versions = versions.OrderByDescending(x => x.PubTime).ToList(); - var newVersion = versions.First(); - //Determine whether the current client version needs to be upgraded. - if (!IsUpgrade(configParams.CurrentVersion, newVersion.Version)) return; - var appPath = Path.Combine(basePath, $"{upgradeAppName}.exe"); - if (!File.Exists(appPath)) throw new Exception($"The application does not exist {upgradeAppName} !"); - //If you confirm that an update is required, start the upgrade application. - var json = JsonSerializer.Serialize(configParams); - //TODO: set environment variable - Process.Start(appPath, json); - } - catch (Exception ex) - { - throw new Exception($"GeneralClientOSS update exception ! {ex.Message}", ex.InnerException); - } - finally - { - Process.GetCurrentProcess().Kill(); - } - }); - } - - /// - /// Determine whether the current client version needs to be upgraded. - /// - /// - /// - /// true: Upgrade required , false: No upgrade is required - private static bool IsUpgrade(string clientVersion, string serverVersion) - { - if (string.IsNullOrWhiteSpace(clientVersion) || string.IsNullOrWhiteSpace(serverVersion)) return false; - Version currentClientVersion = null; - Version currentServerVersion = null; - bool isParseClientVersion = Version.TryParse(clientVersion, out currentClientVersion); - bool isParseServerVersion = Version.TryParse(serverVersion, out currentServerVersion); - if (!isParseClientVersion || !isParseServerVersion) return false; - if (currentClientVersion < currentServerVersion) return true; - return false; - } - - private static void DownloadFile(string url, string path) - { - using (var webClient = new WebClient()) + var basePath = Thread.GetDomain().BaseDirectory; + //Download the version information file from OSS to be updated.(JSON) + var versionsFilePath = Path.Combine(basePath, configParams.VersionFileName); + DownloadFile(configParams.Url + "/" + configParams.VersionFileName, versionsFilePath); + if (!File.Exists(versionsFilePath)) return; + var versions = GeneralFileManager.GetJson>(versionsFilePath); + if (versions == null || versions.Count == 0) return; + versions = versions.OrderByDescending(x => x.PubTime).ToList(); + var newVersion = versions.First(); + //Determine whether the current client version needs to be upgraded. + if (!IsUpgrade(configParams.CurrentVersion, newVersion.Version)) return; + var appPath = Path.Combine(basePath, $"{upgradeAppName}.exe"); + if (!File.Exists(appPath)) throw new Exception($"The application does not exist {upgradeAppName} !"); + //If you confirm that an update is required, start the upgrade application. + var json = JsonSerializer.Serialize(configParams); + //TODO: set environment variable + Process.Start(appPath, json); + } + catch (Exception ex) { - webClient.DownloadFile(new Uri(url), path); + throw new Exception($"GeneralClientOSS update exception ! {ex.Message}", ex.InnerException); } - } + finally + { + Process.GetCurrentProcess().Kill(); + } + }); + } - public static void AddListenerException(Action callbackAction) - { - AddListener(callbackAction); - } + /// + /// Determine whether the current client version needs to be upgraded. + /// + /// + /// + /// true: Upgrade required , false: No upgrade is required + private static bool IsUpgrade(string clientVersion, string serverVersion) + { + if (string.IsNullOrWhiteSpace(clientVersion) || string.IsNullOrWhiteSpace(serverVersion)) return false; + Version currentClientVersion = null; + Version currentServerVersion = null; + var isParseClientVersion = Version.TryParse(clientVersion, out currentClientVersion); + var isParseServerVersion = Version.TryParse(serverVersion, out currentServerVersion); + if (!isParseClientVersion || !isParseServerVersion) return false; + if (currentClientVersion < currentServerVersion) return true; + return false; + } - private static void AddListener(Action callbackAction) where TArgs : EventArgs + private static void DownloadFile(string url, string path) + { + using (var webClient = new WebClient()) { - if (callbackAction != null) EventManager.Instance.AddListener(callbackAction); + webClient.DownloadFile(new Uri(url), path); } } + + public static void AddListenerException(Action callbackAction) + { + AddListener(callbackAction); + } + + private static void AddListener(Action callbackAction) where TArgs : EventArgs + { + if (callbackAction != null) EventManager.Instance.AddListener(callbackAction); + } } \ No newline at end of file diff --git a/src/c#/GeneralUpdate.ClientCore/GeneralUpdate.ClientCore.csproj b/src/c#/GeneralUpdate.ClientCore/GeneralUpdate.ClientCore.csproj index 9871d3b1..a39dacbe 100644 --- a/src/c#/GeneralUpdate.ClientCore/GeneralUpdate.ClientCore.csproj +++ b/src/c#/GeneralUpdate.ClientCore/GeneralUpdate.ClientCore.csproj @@ -1,110 +1,36 @@ - - netstandard2.0 - 2.12.10 - juster.zhu - Provides functions related to upgrade and update programs. - GeneralUpdate.ico - GeneralUpdate128.png - False - False - https://github.com/JusterZhu/GeneralUpdate - Copyright © 2023 - Provides high-performance, low-loss, resume-breakpoint, version-by-version update, binary differential update, incremental update function, configuration file retention update and other features - https://github.com/JusterZhu/GeneralUpdate - 12 - enable - + + netstandard2.0 + 2.12.10 + juster.zhu + Provides functions related to upgrade and update programs. + GeneralUpdate.ico + GeneralUpdate128.png + False + False + https://github.com/JusterZhu/GeneralUpdate + Copyright © 2024 + Provides high-performance, low-loss, resume-breakpoint, version-by-version update, binary differential update, incremental update function, configuration file retention update and other features + https://github.com/JusterZhu/GeneralUpdate + 12 + enable + - - - - - - - - - - - + + + + + - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - + + + + + - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - + + + diff --git a/src/c#/GeneralUpdate.ClientCore/Hubs/RandomRetryPolicy.cs b/src/c#/GeneralUpdate.ClientCore/Hubs/RandomRetryPolicy.cs index f274eaaf..93ef549c 100644 --- a/src/c#/GeneralUpdate.ClientCore/Hubs/RandomRetryPolicy.cs +++ b/src/c#/GeneralUpdate.ClientCore/Hubs/RandomRetryPolicy.cs @@ -1,25 +1,19 @@ -using Microsoft.AspNetCore.SignalR.Client; -using System; +using System; +using Microsoft.AspNetCore.SignalR.Client; -namespace GeneralUpdate.ClientCore.Hubs +namespace GeneralUpdate.ClientCore.Hubs; + +public class RandomRetryPolicy : IRetryPolicy { - public class RandomRetryPolicy : IRetryPolicy - { - private readonly Random _random = new Random(); + private readonly Random _random = new(); - public TimeSpan? NextRetryDelay(RetryContext retryContext) - { - // If we've been reconnecting for less than 60 seconds so far, - // wait between 0 and 10 seconds before the next reconnect attempt. - if (retryContext.ElapsedTime < TimeSpan.FromSeconds(60)) - { - return TimeSpan.FromSeconds(_random.NextDouble() * 10); - } - else - { - // If we've been reconnecting for more than 60 seconds so far, stop reconnecting. - return null; - } - } + public TimeSpan? NextRetryDelay(RetryContext retryContext) + { + // If we've been reconnecting for less than 60 seconds so far, + // wait between 0 and 10 seconds before the next reconnect attempt. + if (retryContext.ElapsedTime < TimeSpan.FromSeconds(60)) + return TimeSpan.FromSeconds(_random.NextDouble() * 10); + // If we've been reconnecting for more than 60 seconds so far, stop reconnecting. + return null; } } \ No newline at end of file diff --git a/src/c#/GeneralUpdate.ClientCore/Hubs/VersionHub.cs b/src/c#/GeneralUpdate.ClientCore/Hubs/VersionHub.cs index 6b656f14..b4e04cbd 100644 --- a/src/c#/GeneralUpdate.ClientCore/Hubs/VersionHub.cs +++ b/src/c#/GeneralUpdate.ClientCore/Hubs/VersionHub.cs @@ -1,196 +1,205 @@ -using GeneralUpdate.Core.ContentProvider; -using Microsoft.AspNetCore.SignalR.Client; -using System; +using System; +using System.Text.Json; using System.Threading.Tasks; +using Microsoft.AspNetCore.SignalR.Client; -namespace GeneralUpdate.ClientCore.Hubs -{ - public sealed class VersionHub where TParameter : class - { - #region Private Members - - private const string ReceiveMessageflag = "ReceiveMessage"; - private const string SendMessageflag = "SendMessage"; - private const string Onlineflag = "Online"; - - private HubConnection _connection = null; - private static VersionHub _instance; - private static readonly object _lock = new object(); - private Action _receiveMessageCallback; - private Action _onlineMessageCallback; - private Action _reconnectedCallback; - private string _name; - - #endregion Private Members +namespace GeneralUpdate.ClientCore.Hubs; - #region Constructors +public sealed class VersionHub where TParameter : class +{ + #region Constructors - private VersionHub() - { } + private VersionHub() + { + } - #endregion Constructors + #endregion Constructors - #region Public Properties + #region Public Properties - public static VersionHub Instance + public static VersionHub Instance + { + get { - get - { - if (_instance == null) + if (_instance == null) + lock (_lock) { - lock (_lock) - { - if (_instance == null) - _instance = new VersionHub(); - } + if (_instance == null) + _instance = new VersionHub(); } - return _instance; - } - } - #endregion Public Properties + return _instance; + } + } - #region Public Methods + #endregion Public Properties + + #region Private Members + + private const string ReceiveMessageflag = "ReceiveMessage"; + private const string SendMessageflag = "SendMessage"; + private const string Onlineflag = "Online"; + + private HubConnection _connection; + private static VersionHub _instance; + private static readonly object _lock = new(); + private Action _receiveMessageCallback; + private Action _onlineMessageCallback; + private Action _reconnectedCallback; + private string _name; + + #endregion Private Members + + #region Public Methods + + /// + /// Subscribe to the latest version. + /// + /// remote server address , E.g : https://127.0.0.1:8080/versionhub . + /// The name needs to be guaranteed to be unique. + /// + /// Receive server push callback function, The caller needs to implement the update + /// process. + /// + /// Receive online and offline notification callback function. + /// Reconnect notification callback function. + /// Subscribe exception. + public void Subscribe(string url, string name, Action receiveMessageCallback, + Action onlineMessageCallback = null, Action reconnectedCallback = null) + { + if (string.IsNullOrWhiteSpace(url) || receiveMessageCallback == null) + throw new Exception("Subscription key parameter cannot be null !"); - /// - /// Subscribe to the latest version. - /// - /// remote server address , E.g : https://127.0.0.1:8080/versionhub . - /// The name needs to be guaranteed to be unique. - /// Receive server push callback function, The caller needs to implement the update process. - /// Receive online and offline notification callback function. - /// Reconnect notification callback function. - /// Subscribe exception. - public void Subscribe(string url, string name, Action receiveMessageCallback, Action onlineMessageCallback = null, Action reconnectedCallback = null) + try { - if (string.IsNullOrWhiteSpace(url) || receiveMessageCallback == null) throw new Exception("Subscription key parameter cannot be null !"); - - try + _name = name; + _receiveMessageCallback = receiveMessageCallback; + _onlineMessageCallback = onlineMessageCallback; + _reconnectedCallback = reconnectedCallback; + if (_connection == null) { - _name = name; - _receiveMessageCallback = receiveMessageCallback; - _onlineMessageCallback = onlineMessageCallback; - _reconnectedCallback = reconnectedCallback; - if (_connection == null) - { - _connection = new HubConnectionBuilder() - .WithUrl(url) - .WithAutomaticReconnect(new RandomRetryPolicy()) - .Build(); - _connection.On(ReceiveMessageflag, OnReceiveMessage); - _connection.On(Onlineflag, OnOnlineMessage); - _connection.Reconnected += OnReconnected; - _connection.Closed += OnClosed; - } - _connection.StartAsync(); - } - catch (Exception ex) - { - throw new Exception($"'VersionHub' Subscribe error : {ex.Message}", ex.InnerException); + _connection = new HubConnectionBuilder() + .WithUrl(url) + .WithAutomaticReconnect(new RandomRetryPolicy()) + .Build(); + _connection.On(ReceiveMessageflag, OnReceiveMessage); + _connection.On(Onlineflag, OnOnlineMessage); + _connection.Reconnected += OnReconnected; + _connection.Closed += OnClosed; } + + _connection.StartAsync(); } + catch (Exception ex) + { + throw new Exception($"'VersionHub' Subscribe error : {ex.Message}", ex.InnerException); + } + } - /// - /// Send message to server.[Not recommended for now] - /// - /// - /// - /// - public async Task Send(string msg) + /// + /// Send message to server.[Not recommended for now] + /// + /// + /// + /// + public async Task Send(string msg) + { + try { - try - { - if (_connection == null) return; - await _connection.InvokeAsync(SendMessageflag, _name, msg); - } - catch (Exception ex) - { - throw new Exception($"'VersionHub' Send error : {ex.Message}", ex.InnerException); - } + if (_connection == null) return; + await _connection.InvokeAsync(SendMessageflag, _name, msg); } + catch (Exception ex) + { + throw new Exception($"'VersionHub' Send error : {ex.Message}", ex.InnerException); + } + } - #endregion Public Methods + #endregion Public Methods - #region Private Methods + #region Private Methods - /// - /// Receives the message. - /// - /// - private void OnReceiveMessage(string name, string message) + /// + /// Receives the message. + /// + /// + private void OnReceiveMessage(string name, string message) + { + if (_receiveMessageCallback == null || string.IsNullOrWhiteSpace(message)) return; + try { - if (_receiveMessageCallback == null || string.IsNullOrWhiteSpace(message)) return; - try - { - var clientParameter = FileProvider.Deserialize(message); - if (clientParameter == null) throw new ArgumentNullException($"'VersionHub' Receiving server push version information deserialization failed , receive content : {message} ."); - _receiveMessageCallback.Invoke(clientParameter); - } - catch (Exception ex) - { - throw new Exception($"'VersionHub' Receive message error : {ex.Message}", ex.InnerException); - } + var clientParameter = JsonSerializer.Deserialize(message); + if (clientParameter == null) + throw new ArgumentNullException( + $"'VersionHub' Receiving server push version information deserialization failed , receive content : {message} ."); + _receiveMessageCallback.Invoke(clientParameter); + } + catch (Exception ex) + { + throw new Exception($"'VersionHub' Receive message error : {ex.Message}", ex.InnerException); } + } - /// - /// Online and offline notification. - /// - /// - private void OnOnlineMessage(string message) + /// + /// Online and offline notification. + /// + /// + private void OnOnlineMessage(string message) + { + try { - try - { - if (_onlineMessageCallback != null) _onlineMessageCallback.Invoke(message); - } - catch (Exception ex) - { - throw new Exception($"'VersionHub' Online message error : {ex.Message}", ex.InnerException); - } + if (_onlineMessageCallback != null) _onlineMessageCallback.Invoke(message); } + catch (Exception ex) + { + throw new Exception($"'VersionHub' Online message error : {ex.Message}", ex.InnerException); + } + } - /// - /// Reconnection notice. - /// - /// - /// - private Task OnReconnected(string arg) + /// + /// Reconnection notice. + /// + /// + /// + private Task OnReconnected(string arg) + { + try { - try - { - if (_reconnectedCallback != null) _reconnectedCallback.Invoke(arg); - } - catch (Exception ex) - { - throw new Exception($"'VersionHub' On reconnected error : {ex.Message}", ex.InnerException); - } - return Task.CompletedTask; + if (_reconnectedCallback != null) _reconnectedCallback.Invoke(arg); + } + catch (Exception ex) + { + throw new Exception($"'VersionHub' On reconnected error : {ex.Message}", ex.InnerException); } - /// - /// Shut down. - /// - /// - /// - private async Task OnClosed(Exception arg) + return Task.CompletedTask; + } + + /// + /// Shut down. + /// + /// + /// + private async Task OnClosed(Exception arg) + { + try { - try - { - if (arg != null) throw new Exception($"'VersionHub' On closed internal exception : {arg.Message}", arg.InnerException); + if (arg != null) + throw new Exception($"'VersionHub' On closed internal exception : {arg.Message}", arg.InnerException); - if (_connection == null) return; - await Task.Delay(new Random().Next(0, 3) * 1000); - await _connection.StartAsync(); - } - catch (ArgumentOutOfRangeException ex) - { - throw new ArgumentOutOfRangeException(ex.Message); - } - catch (Exception ex) - { - throw new Exception($"'VersionHub' On closed error : {ex.Message}", ex.InnerException); - } + if (_connection == null) return; + await Task.Delay(new Random().Next(0, 3) * 1000); + await _connection.StartAsync(); + } + catch (ArgumentOutOfRangeException ex) + { + throw new ArgumentOutOfRangeException(ex.Message); + } + catch (Exception ex) + { + throw new Exception($"'VersionHub' On closed error : {ex.Message}", ex.InnerException); } - - #endregion Private Methods } + + #endregion Private Methods } \ No newline at end of file diff --git a/src/c#/GeneralUpdate.ClientCore/Internal/ExceptionEventArgs.cs b/src/c#/GeneralUpdate.ClientCore/Internal/ExceptionEventArgs.cs index 1a2c913b..3eecdd39 100644 --- a/src/c#/GeneralUpdate.ClientCore/Internal/ExceptionEventArgs.cs +++ b/src/c#/GeneralUpdate.ClientCore/Internal/ExceptionEventArgs.cs @@ -4,12 +4,12 @@ namespace GeneralUpdate.ClientCore.Internal; public class ExceptionEventArgs : EventArgs { - public Exception Exception { get; private set; } - public string Message { get; private set; } - public ExceptionEventArgs(Exception exception = null, string message = null) { Exception = exception ?? throw new Exception(nameof(exception)); Message = message ?? exception.Message; } + + public Exception Exception { get; private set; } + public string Message { get; private set; } } \ No newline at end of file diff --git a/src/c#/GeneralUpdate.ClientCore/Pipeline/HashMiddleware.cs b/src/c#/GeneralUpdate.ClientCore/Pipeline/HashMiddleware.cs index 9d78eec4..4b5d0bd9 100644 --- a/src/c#/GeneralUpdate.ClientCore/Pipeline/HashMiddleware.cs +++ b/src/c#/GeneralUpdate.ClientCore/Pipeline/HashMiddleware.cs @@ -12,14 +12,11 @@ public async Task InvokeAsync(PipelineContext context) { var fileName = context.Get("FileName"); var hash = context.Get("Hash"); - - bool isVerify = await VerifyFileHash(fileName, hash); - if (!isVerify) - { - throw new CryptographicException("Hash verification failed ."); - } + + var isVerify = await VerifyFileHash(fileName, hash); + if (!isVerify) throw new CryptographicException("Hash verification failed ."); } - + private Task VerifyFileHash(string fileName, string hash) { return Task.Run(() => diff --git a/src/c#/GeneralUpdate.ClientCore/Pipeline/PatchMiddleware.cs b/src/c#/GeneralUpdate.ClientCore/Pipeline/PatchMiddleware.cs index 968a14e0..dadac887 100644 --- a/src/c#/GeneralUpdate.ClientCore/Pipeline/PatchMiddleware.cs +++ b/src/c#/GeneralUpdate.ClientCore/Pipeline/PatchMiddleware.cs @@ -13,7 +13,7 @@ public async Task InvokeAsync(PipelineContext context) var targetPath = context.Get("TargetPath"); var blackFiles = context.Get>("BlackFiles"); var blackFileFormats = context.Get>("BlackFileFormats"); - + DifferentialCore.Instance.SetBlocklist(blackFiles, blackFileFormats); await DifferentialCore.Instance.Dirty(sourcePath, targetPath); } diff --git a/src/c#/GeneralUpdate.ClientCore/Pipeline/ZipMiddleware.cs b/src/c#/GeneralUpdate.ClientCore/Pipeline/ZipMiddleware.cs index 502f4ffa..1089d88a 100644 --- a/src/c#/GeneralUpdate.ClientCore/Pipeline/ZipMiddleware.cs +++ b/src/c#/GeneralUpdate.ClientCore/Pipeline/ZipMiddleware.cs @@ -17,7 +17,7 @@ public Task InvokeAsync(PipelineContext context) var sourcePath = context.Get("SourcePath"); var destinationPath = context.Get("DestinationPath"); var encoding = context.Get("Encoding"); - + var generalZipfactory = new GeneralZipFactory(); generalZipfactory.CompressProgress += (sender, args) => { }; generalZipfactory.Completed += (sender, args) => { }; @@ -25,7 +25,7 @@ public Task InvokeAsync(PipelineContext context) generalZipfactory.UnZip(); }); } - + private static OperationType MatchType(string extensionName) { var type = extensionName switch diff --git a/src/c#/GeneralUpdate.ClientCore/Strategys/LinuxStrategy.cs b/src/c#/GeneralUpdate.ClientCore/Strategys/LinuxStrategy.cs index 372154eb..f5e42dac 100644 --- a/src/c#/GeneralUpdate.ClientCore/Strategys/LinuxStrategy.cs +++ b/src/c#/GeneralUpdate.ClientCore/Strategys/LinuxStrategy.cs @@ -1,8 +1,7 @@ using GeneralUpdate.Common.Internal.Strategy; -namespace GeneralUpdate.ClientCore.Strategys +namespace GeneralUpdate.ClientCore.Strategys; + +public class LinuxStrategy : AbstractStrategy { - public class LinuxStrategy : AbstractStrategy - { - } } \ No newline at end of file diff --git a/src/c#/GeneralUpdate.ClientCore/Strategys/WindowsStrategy.cs b/src/c#/GeneralUpdate.ClientCore/Strategys/WindowsStrategy.cs index 2eb81ec4..e9f9a705 100644 --- a/src/c#/GeneralUpdate.ClientCore/Strategys/WindowsStrategy.cs +++ b/src/c#/GeneralUpdate.ClientCore/Strategys/WindowsStrategy.cs @@ -1,5 +1,4 @@ -using GeneralUpdate.Core.ContentProvider; -using System; +using System; using System.Diagnostics; using System.IO; using System.Linq; @@ -10,94 +9,96 @@ using GeneralUpdate.Common.Internal.Strategy; using GeneralUpdate.Common.Shared.Object; -namespace GeneralUpdate.ClientCore.Strategys +namespace GeneralUpdate.ClientCore.Strategys; + +/// +/// Update policy based on the Windows platform. +/// +public class WindowsStrategy : AbstractStrategy { + private Packet Packet { get; set; } + + #region Private Methods + /// - /// Update policy based on the Windows platform. + /// Remove update redundant files. /// - public class WindowsStrategy : AbstractStrategy + /// + private void Clear() { - private Packet Packet { get; set; } + if (File.Exists(Packet.TempPath)) File.Delete(Packet.TempPath); + var dirPath = Path.GetDirectoryName(Packet.TempPath); + if (Directory.Exists(dirPath)) Directory.Delete(dirPath, true); + } + + #endregion Private Methods - #region Public Methods + #region Public Methods - public override void Create(Packet parameter) => Packet = parameter; + public override void Create(Packet parameter) + { + Packet = parameter; + } - public override async Task ExecuteAsync() + public override async Task ExecuteAsync() + { + var updateVersions = Packet.UpdateVersions.OrderBy(x => x.PubTime).ToList(); + if (updateVersions.Count > 0) { - var updateVersions = Packet.UpdateVersions.OrderBy(x => x.PubTime).ToList(); - if (updateVersions.Count > 0) + foreach (var version in updateVersions) { - foreach (var version in updateVersions) - { - var patchPath = FileProvider.GetTempDirectory(PATCHS); - var zipFilePath = Path.Combine(Packet.TempPath, $"{version.Name}{Packet.Format}"); - - var context = new PipelineContext(); - //hash middleware - context.Add("Hash", version.Hash); - context.Add("FileName", zipFilePath); - //zip middleware - context.Add("Format", Packet.Format); - context.Add("Name", zipFilePath); - context.Add("SourcePath", Packet.TempPath); - context.Add("DestinationPath", Packet.InstallPath); - context.Add("Encoding", Packet.Encoding); - //patch middleware - context.Add("SourcePath", patchPath); - context.Add("TargetPath", Packet.InstallPath); - context.Add("BlackFiles", GeneralFileManager.BlackFiles); - context.Add("BlackFileFormats", GeneralFileManager.BlackFileFormats); - - var pipelineBuilder = new PipelineBuilder(context) - .UseMiddleware() - .UseMiddleware() - .UseMiddleware(); - await pipelineBuilder.Build(); - } - - if (!string.IsNullOrEmpty(Packet.UpdateLogUrl)) - OpenBrowser(Packet.UpdateLogUrl); - } + var patchPath = GeneralFileManager.GetTempDirectory(PATCHS); + var zipFilePath = Path.Combine(Packet.TempPath, $"{version.Name}{Packet.Format}"); - Clear(); - StartApp(Packet.AppName, Packet.AppType); - } + var context = new PipelineContext(); + //hash middleware + context.Add("Hash", version.Hash); + context.Add("FileName", zipFilePath); + //zip middleware + context.Add("Format", Packet.Format); + context.Add("Name", zipFilePath); + context.Add("SourcePath", Packet.TempPath); + context.Add("DestinationPath", Packet.InstallPath); + context.Add("Encoding", Packet.Encoding); + //patch middleware + context.Add("SourcePath", patchPath); + context.Add("TargetPath", Packet.InstallPath); + context.Add("BlackFiles", GeneralFileManager.BlackFiles); + context.Add("BlackFileFormats", GeneralFileManager.BlackFileFormats); - public override void StartApp(string appName, int appType) - { - var path = Path.Combine(Packet.InstallPath, appName); - switch (appType) - { - case AppType.ClientApp: - Environment.SetEnvironmentVariable("ProcessInfo", Packet.ProcessInfo, EnvironmentVariableTarget.User); - Process.Start(path); - break; - - case AppType.UpgradeApp: - Process.Start(path); - break; - - default: - throw new ArgumentException("Invalid app type"); + var pipelineBuilder = new PipelineBuilder(context) + .UseMiddleware() + .UseMiddleware() + .UseMiddleware(); + await pipelineBuilder.Build(); } - } - #endregion Public Methods + if (!string.IsNullOrEmpty(Packet.UpdateLogUrl)) + OpenBrowser(Packet.UpdateLogUrl); + } - #region Private Methods + Clear(); + StartApp(Packet.AppName, Packet.AppType); + } - /// - /// Remove update redundant files. - /// - /// - private void Clear() + public override void StartApp(string appName, int appType) + { + var path = Path.Combine(Packet.InstallPath, appName); + switch (appType) { - if (File.Exists(Packet.TempPath)) File.Delete(Packet.TempPath); - var dirPath = Path.GetDirectoryName(Packet.TempPath); - if (Directory.Exists(dirPath)) Directory.Delete(dirPath, true); + case AppType.ClientApp: + Environment.SetEnvironmentVariable("ProcessInfo", Packet.ProcessInfo, EnvironmentVariableTarget.User); + Process.Start(path); + break; + + case AppType.UpgradeApp: + Process.Start(path); + break; + + default: + throw new ArgumentException("Invalid app type"); } - - #endregion Private Methods } + + #endregion Public Methods } \ No newline at end of file From 94e4729ef63da47024e18a672de5b69d1fe356f5 Mon Sep 17 00:00:00 2001 From: justerzhu Date: Fri, 20 Sep 2024 22:56:39 +0800 Subject: [PATCH 10/33] refactor: refactor generalupdate.common --- .../File/GeneralFileManager.cs | 37 +++++++++++++++---- .../Shared/Object/Enum/ProgressType.cs | 2 +- 2 files changed, 30 insertions(+), 9 deletions(-) diff --git a/src/c#/GeneralUpdate.Common/File/GeneralFileManager.cs b/src/c#/GeneralUpdate.Common/File/GeneralFileManager.cs index 287ba037..3840b417 100644 --- a/src/c#/GeneralUpdate.Common/File/GeneralFileManager.cs +++ b/src/c#/GeneralUpdate.Common/File/GeneralFileManager.cs @@ -11,24 +11,24 @@ public sealed class GeneralFileManager { #region Private Members - private static readonly List _blackFileFormats = new List - { + private static readonly List _blackFileFormats = + [ ".patch", ".7z", ".zip", ".rar", ".tar", ".json" - }; + ]; - private static readonly List _blackFiles = new List { "Newtonsoft.Json.dll" }; + private static readonly List _blackFiles = ["Newtonsoft.Json.dll"]; #endregion #region Public Properties - - public static IList BlackFileFormats => _blackFileFormats.AsReadOnly(); - public static IList BlackFiles => _blackFiles.AsReadOnly(); + + public static IReadOnlyList BlackFileFormats => _blackFileFormats.AsReadOnly(); + public static IReadOnlyList BlackFiles => _blackFiles.AsReadOnly(); public ComparisonResult ComparisonResult { get; private set; } @@ -36,6 +36,19 @@ public sealed class GeneralFileManager #region Public Methods + public static List ToFileInfoList(IReadOnlyList filePaths) + { + return filePaths.Select(path => new FileInfo(path)).ToList(); + } + + public static void AddBlackFileFormats(List formats) + { + foreach (var format in formats) + { + AddBlackFileFormat(format); + } + } + public static void AddBlackFileFormat(string format) { if (!_blackFileFormats.Contains(format)) @@ -43,12 +56,20 @@ public static void AddBlackFileFormat(string format) _blackFileFormats.Add(format); } } - + public static void RemoveBlackFileFormat(string format) { _blackFileFormats.Remove(format); } + public static void AddBlackFiles(List fileNames) + { + foreach (var fileName in fileNames) + { + AddBlackFile(fileName); + } + } + public static void AddBlackFile(string fileName) { if (!_blackFiles.Contains(fileName)) diff --git a/src/c#/GeneralUpdate.Common/Shared/Object/Enum/ProgressType.cs b/src/c#/GeneralUpdate.Common/Shared/Object/Enum/ProgressType.cs index 0547e764..7cd00365 100644 --- a/src/c#/GeneralUpdate.Common/Shared/Object/Enum/ProgressType.cs +++ b/src/c#/GeneralUpdate.Common/Shared/Object/Enum/ProgressType.cs @@ -1,4 +1,4 @@ -namespace GeneralUpdate.Common.Shared.Object +namespace GeneralUpdate.Common.Shared.Object.Enum { public enum ProgressType { From 85c7a08d9fbf8b3f7b4fb826f12f42867a2457ba Mon Sep 17 00:00:00 2001 From: justerzhu Date: Fri, 20 Sep 2024 22:56:50 +0800 Subject: [PATCH 11/33] refactor: generalupdate.core --- .../Driver/CommandExecutor.cs | 5 +- .../Driver/DriverInformation.cs | 4 +- .../Driver/InstallDriverCommand.cs | 6 +- .../Driver/RestoreDriverCommand.cs | 5 +- .../GeneralUpdate.Core.csproj | 29 +-- .../GeneralUpdateBootstrap.cs | 43 ++-- src/c#/GeneralUpdate.Core/GeneralUpdateOSS.cs | 21 +- .../Internal/ExceptionEventArgs.cs | 2 +- .../Internal/OSSDownloadArgs.cs | 6 +- .../Pipeline/DriverMiddleware.cs | 12 +- .../Pipeline/HashMiddleware.cs | 2 +- .../Pipeline/PatchMiddleware.cs | 2 +- .../Pipeline/ZipMiddleware.cs | 2 +- .../Strategys/LinuxStrategy.cs | 20 +- .../Strategys/OSSStrategy.cs | 49 ++--- .../Strategys/WindowsStrategy.cs | 200 +++++++----------- 16 files changed, 160 insertions(+), 248 deletions(-) diff --git a/src/c#/GeneralUpdate.Core/Driver/CommandExecutor.cs b/src/c#/GeneralUpdate.Core/Driver/CommandExecutor.cs index aa4fb115..c7c5bbf5 100644 --- a/src/c#/GeneralUpdate.Core/Driver/CommandExecutor.cs +++ b/src/c#/GeneralUpdate.Core/Driver/CommandExecutor.cs @@ -1,5 +1,4 @@ -using GeneralUpdate.Core.Exceptions; -using System; +using System; using System.Diagnostics; namespace GeneralUpdate.Core.Driver @@ -39,7 +38,7 @@ Update the driver regularly to ensure that the driver is compatible with the cur process.WaitForExit(); if (process.ExitCode != 0) - ThrowExceptionUtility.Throw($"Operation failed code: {process.ExitCode}"); + throw new ApplicationException($"Operation failed code: {process.ExitCode}"); } } } diff --git a/src/c#/GeneralUpdate.Core/Driver/DriverInformation.cs b/src/c#/GeneralUpdate.Core/Driver/DriverInformation.cs index 3e1e1da4..6d1db4ab 100644 --- a/src/c#/GeneralUpdate.Core/Driver/DriverInformation.cs +++ b/src/c#/GeneralUpdate.Core/Driver/DriverInformation.cs @@ -1,4 +1,4 @@ -using GeneralUpdate.Core.Exceptions; +using System; using System.Collections.Generic; using System.Linq; @@ -60,7 +60,7 @@ public DriverInformation Build() string.IsNullOrWhiteSpace(_information.OutPutDirectory) || !_information.Drivers.Any()) { - ThrowExceptionUtility.ThrowIfNull("Cannot create DriverInformation, not all fields are set."); + throw new ArgumentNullException("Cannot create DriverInformation, not all fields are set."); } return _information; diff --git a/src/c#/GeneralUpdate.Core/Driver/InstallDriverCommand.cs b/src/c#/GeneralUpdate.Core/Driver/InstallDriverCommand.cs index 17953cd1..c4bb448f 100644 --- a/src/c#/GeneralUpdate.Core/Driver/InstallDriverCommand.cs +++ b/src/c#/GeneralUpdate.Core/Driver/InstallDriverCommand.cs @@ -1,5 +1,4 @@ -using GeneralUpdate.Core.Exceptions; -using System; +using System; using System.IO; using System.Text; @@ -39,7 +38,8 @@ public void Execute() { //restore all the drivers in the backup directory. new RestoreDriverCommand(_information).Execute(); - ThrowExceptionUtility.Throw($"Failed to execute install command for {_information.InstallDirectory}", ex); + throw new ApplicationException( + $"Failed to execute install command for {_information.InstallDirectory}"); } } } diff --git a/src/c#/GeneralUpdate.Core/Driver/RestoreDriverCommand.cs b/src/c#/GeneralUpdate.Core/Driver/RestoreDriverCommand.cs index f05c1679..182a820b 100644 --- a/src/c#/GeneralUpdate.Core/Driver/RestoreDriverCommand.cs +++ b/src/c#/GeneralUpdate.Core/Driver/RestoreDriverCommand.cs @@ -1,5 +1,4 @@ -using GeneralUpdate.Core.Exceptions; -using System; +using System; using System.IO; using System.Text; @@ -27,7 +26,7 @@ public void Execute() } catch (Exception ex) { - ThrowExceptionUtility.Throw($"Failed to execute restore command for {_information.OutPutDirectory}", ex); + throw new ApplicationException($"Failed to execute restore command for {_information.OutPutDirectory}"); } } } diff --git a/src/c#/GeneralUpdate.Core/GeneralUpdate.Core.csproj b/src/c#/GeneralUpdate.Core/GeneralUpdate.Core.csproj index cf581a51..4294f519 100644 --- a/src/c#/GeneralUpdate.Core/GeneralUpdate.Core.csproj +++ b/src/c#/GeneralUpdate.Core/GeneralUpdate.Core.csproj @@ -10,7 +10,7 @@ GeneralUpdate.ico GeneralUpdate128.png False - Copyright © 2023 + Copyright © 2024 This section describes how to upgrade client applications. Provides high-performance, low-loss, resume-breakpoint, version-by-version update, binary differential update, incremental update function, configuration file retention update and other features. https://github.com/JusterZhu/GeneralUpdate @@ -21,33 +21,10 @@ enable - - - - - - - - - - - - - + + - - - - - - - - - - - - \ No newline at end of file diff --git a/src/c#/GeneralUpdate.Core/GeneralUpdateBootstrap.cs b/src/c#/GeneralUpdate.Core/GeneralUpdateBootstrap.cs index 67913d54..714baadd 100644 --- a/src/c#/GeneralUpdate.Core/GeneralUpdateBootstrap.cs +++ b/src/c#/GeneralUpdate.Core/GeneralUpdateBootstrap.cs @@ -1,17 +1,18 @@ -using GeneralUpdate.Core.Bootstrap; -using GeneralUpdate.Core.ContentProvider; -using GeneralUpdate.Core.Domain.Entity; -using GeneralUpdate.Core.Domain.Entity.Assembler; -using GeneralUpdate.Core.Domain.Enum; -using GeneralUpdate.Core.Strategys; -using System; +using System; using System.IO; +using System.Text.Json; using System.Threading.Tasks; +using GeneralUpdate.Common; +using GeneralUpdate.Common.Internal.Bootstrap; +using GeneralUpdate.Common.Internal.Strategy; +using GeneralUpdate.Common.Shared.Object; namespace GeneralUpdate.Core { public class GeneralUpdateBootstrap : AbstractBootstrap { + private Packet Packet { get; set; } + public GeneralUpdateBootstrap() : base() => Remote(); /// @@ -21,11 +22,12 @@ private void Remote() { try { - var base64 = Environment.GetEnvironmentVariable("ProcessBase64", EnvironmentVariableTarget.User); - var processInfo = FileProvider.Deserialize(base64); - Packet = ProcessAssembler.ToPacket(processInfo); + + var json = Environment.GetEnvironmentVariable("ProcessInfo", EnvironmentVariableTarget.User); + var processInfo = JsonSerializer.Deserialize(json); + Packet = null; Packet.AppType = AppType.UpgradeApp; - Packet.TempPath = $"{FileProvider.GetTempDirectory(processInfo.LastVersion)}{Path.DirectorySeparatorChar}"; + Packet.TempPath = $"{GeneralFileManager.GetTempDirectory(processInfo.LastVersion)}{Path.DirectorySeparatorChar}"; } catch (Exception ex) { @@ -33,10 +35,19 @@ private void Remote() } } - /// - /// Start the update. - /// - /// - public Task LaunchTaskAsync() => Task.Run(() => base.LaunchAsync()); + public override Task LaunchAsync() + { + throw new NotImplementedException(); + } + + protected override void ExecuteStrategy() + { + throw new NotImplementedException(); + } + + protected override GeneralUpdateBootstrap StrategyFactory() + { + throw new NotImplementedException(); + } } } \ No newline at end of file diff --git a/src/c#/GeneralUpdate.Core/GeneralUpdateOSS.cs b/src/c#/GeneralUpdate.Core/GeneralUpdateOSS.cs index d6525c47..f21de3ab 100644 --- a/src/c#/GeneralUpdate.Core/GeneralUpdateOSS.cs +++ b/src/c#/GeneralUpdate.Core/GeneralUpdateOSS.cs @@ -1,12 +1,11 @@ -using GeneralUpdate.Core.Domain.Entity; -using GeneralUpdate.Core.Events; -using GeneralUpdate.Core.Events.CommonArgs; -using GeneralUpdate.Core.Events.MultiEventArgs; -using GeneralUpdate.Core.Events.OSSArgs; -using GeneralUpdate.Core.Strategys; -using System; +using System; using System.Text; using System.Threading.Tasks; +using GeneralUpdate.Common.Download; +using GeneralUpdate.Common.Internal.Event; +using GeneralUpdate.Common.Internal.Strategy; +using GeneralUpdate.Common.Shared.Object; +using GeneralUpdate.Core.Internal; namespace GeneralUpdate.Core { @@ -27,9 +26,9 @@ private GeneralUpdateOSS() /// /// /// - public static async Task Start(ParamsOSS parameter, Encoding encoding) where TStrategy : AbstractStrategy, new() + public static async Task Start(ParamsOSS parameter) where TStrategy : AbstractStrategy, new() { - await BaseStart(parameter, encoding); + await BaseStart(parameter); } public static void AddListenerMultiAllDownloadCompleted(Action callbackAction) @@ -82,12 +81,12 @@ private static void AddListener(Action callbackAction) whe /// The class that needs to be injected with the corresponding platform update policy or inherits the abstract update policy. /// List of parameter. /// - private static async Task BaseStart(TParams parameter, Encoding encoding) where TStrategy : AbstractStrategy, new() where TParams : class + private static async Task BaseStart(ParamsOSS parameter) where TStrategy : AbstractStrategy, new() { //Initializes and executes the policy. var strategyFunc = new Func(() => new TStrategy()); var strategy = strategyFunc(); - strategy.Create(parameter, encoding); + //strategy.Create(parameter); //Implement different update strategies depending on the platform. await strategy.ExecuteTaskAsync(); } diff --git a/src/c#/GeneralUpdate.Core/Internal/ExceptionEventArgs.cs b/src/c#/GeneralUpdate.Core/Internal/ExceptionEventArgs.cs index 3eecdd39..6603942e 100644 --- a/src/c#/GeneralUpdate.Core/Internal/ExceptionEventArgs.cs +++ b/src/c#/GeneralUpdate.Core/Internal/ExceptionEventArgs.cs @@ -1,6 +1,6 @@ using System; -namespace GeneralUpdate.ClientCore.Internal; +namespace GeneralUpdate.Core.Internal; public class ExceptionEventArgs : EventArgs { diff --git a/src/c#/GeneralUpdate.Core/Internal/OSSDownloadArgs.cs b/src/c#/GeneralUpdate.Core/Internal/OSSDownloadArgs.cs index 0aaf7f9b..1e4fe03a 100644 --- a/src/c#/GeneralUpdate.Core/Internal/OSSDownloadArgs.cs +++ b/src/c#/GeneralUpdate.Core/Internal/OSSDownloadArgs.cs @@ -1,6 +1,8 @@ -namespace GeneralUpdate.Core.Internal; +using System; -public class OSSDownloadArgs +namespace GeneralUpdate.Core.Internal; + +public class OSSDownloadArgs : EventArgs { } \ No newline at end of file diff --git a/src/c#/GeneralUpdate.Core/Pipeline/DriverMiddleware.cs b/src/c#/GeneralUpdate.Core/Pipeline/DriverMiddleware.cs index 4f679481..4e38cd44 100644 --- a/src/c#/GeneralUpdate.Core/Pipeline/DriverMiddleware.cs +++ b/src/c#/GeneralUpdate.Core/Pipeline/DriverMiddleware.cs @@ -1,6 +1,12 @@ -namespace GeneralUpdate.Core.Pipeline; +using System.Threading.Tasks; +using GeneralUpdate.Common.Internal.Pipeline; -public class DriverMiddleware +namespace GeneralUpdate.Core.Pipeline; + +public class DriverMiddleware : IMiddleware { - + public Task InvokeAsync(PipelineContext context) + { + throw new System.NotImplementedException(); + } } \ No newline at end of file diff --git a/src/c#/GeneralUpdate.Core/Pipeline/HashMiddleware.cs b/src/c#/GeneralUpdate.Core/Pipeline/HashMiddleware.cs index 4b5d0bd9..1fd378dc 100644 --- a/src/c#/GeneralUpdate.Core/Pipeline/HashMiddleware.cs +++ b/src/c#/GeneralUpdate.Core/Pipeline/HashMiddleware.cs @@ -4,7 +4,7 @@ using GeneralUpdate.Common.HashAlgorithms; using GeneralUpdate.Common.Internal.Pipeline; -namespace GeneralUpdate.ClientCore.Pipeline; +namespace GeneralUpdate.Core.Pipeline; public class HashMiddleware : IMiddleware { diff --git a/src/c#/GeneralUpdate.Core/Pipeline/PatchMiddleware.cs b/src/c#/GeneralUpdate.Core/Pipeline/PatchMiddleware.cs index dadac887..2a6e5b6d 100644 --- a/src/c#/GeneralUpdate.Core/Pipeline/PatchMiddleware.cs +++ b/src/c#/GeneralUpdate.Core/Pipeline/PatchMiddleware.cs @@ -3,7 +3,7 @@ using GeneralUpdate.Common.Internal.Pipeline; using GeneralUpdate.Differential; -namespace GeneralUpdate.ClientCore.Pipeline; +namespace GeneralUpdate.Core.Pipeline; public class PatchMiddleware : IMiddleware { diff --git a/src/c#/GeneralUpdate.Core/Pipeline/ZipMiddleware.cs b/src/c#/GeneralUpdate.Core/Pipeline/ZipMiddleware.cs index 1089d88a..12d14d64 100644 --- a/src/c#/GeneralUpdate.Core/Pipeline/ZipMiddleware.cs +++ b/src/c#/GeneralUpdate.Core/Pipeline/ZipMiddleware.cs @@ -4,7 +4,7 @@ using GeneralUpdate.Zip; using GeneralUpdate.Zip.Factory; -namespace GeneralUpdate.ClientCore.Pipeline; +namespace GeneralUpdate.Core.Pipeline; public class ZipMiddleware : IMiddleware { diff --git a/src/c#/GeneralUpdate.Core/Strategys/LinuxStrategy.cs b/src/c#/GeneralUpdate.Core/Strategys/LinuxStrategy.cs index 6ac653b0..20b18e21 100644 --- a/src/c#/GeneralUpdate.Core/Strategys/LinuxStrategy.cs +++ b/src/c#/GeneralUpdate.Core/Strategys/LinuxStrategy.cs @@ -1,24 +1,8 @@ -using GeneralUpdate.Core.Domain.Enum; +using GeneralUpdate.Common.Internal.Strategy; -namespace GeneralUpdate.Core.Strategys.PlatformLinux +namespace GeneralUpdate.Core.Strategys { public class LinuxStrategy : AbstractStrategy { - public override string GetPlatform() => PlatformType.Linux; - - public override void Create(T parameter) - { - base.Create(parameter); - } - - public override void Execute() - { - base.Execute(); - } - - public override bool StartApp(string appName, int appType) - { - return base.StartApp(appName, appType); - } } } \ No newline at end of file diff --git a/src/c#/GeneralUpdate.Core/Strategys/OSSStrategy.cs b/src/c#/GeneralUpdate.Core/Strategys/OSSStrategy.cs index 10f482ba..ac28519d 100644 --- a/src/c#/GeneralUpdate.Core/Strategys/OSSStrategy.cs +++ b/src/c#/GeneralUpdate.Core/Strategys/OSSStrategy.cs @@ -1,13 +1,4 @@ -using GeneralUpdate.Core.ContentProvider; -using GeneralUpdate.Core.Domain.Entity; -using GeneralUpdate.Core.Domain.Enum; -using GeneralUpdate.Core.Domain.PO; -using GeneralUpdate.Core.Domain.PO.Assembler; -using GeneralUpdate.Core.Download; -using GeneralUpdate.Core.Events; -using GeneralUpdate.Core.Events.CommonArgs; -using GeneralUpdate.Core.Events.MultiEventArgs; -using GeneralUpdate.Zip; +using GeneralUpdate.Zip; using GeneralUpdate.Zip.Factory; using System; using System.Collections.Generic; @@ -15,6 +6,12 @@ using System.IO; using System.Text; using System.Threading.Tasks; +using GeneralUpdate.Common; +using GeneralUpdate.Common.Download; +using GeneralUpdate.Common.Internal.Event; +using GeneralUpdate.Common.Internal.Strategy; +using GeneralUpdate.Common.Shared.Object; +using GeneralUpdate.Core.Internal; namespace GeneralUpdate.Core.Strategys { @@ -25,17 +22,16 @@ public sealed class OSSStrategy : AbstractStrategy private readonly string _appPath = AppDomain.CurrentDomain.BaseDirectory; private const string _format = ".zip"; private const int _timeOut = 60; - private ParamsOSS _parameter; + private Packet _parameter; private Encoding _encoding; #endregion Private Members #region Public Methods - public override void Create(T parameter, Encoding encoding) + public override void Create(Packet parameter) { - _parameter = parameter as ParamsOSS; - _encoding = encoding; + _parameter = parameter; } public override async Task ExecuteTaskAsync() @@ -45,23 +41,23 @@ await Task.Run(() => try { //1.Download the JSON version configuration file. - var jsonPath = Path.Combine(_appPath, _parameter.VersionFileName); + var jsonPath = Path.Combine(_appPath, "version.json"); if (!File.Exists(jsonPath)) throw new FileNotFoundException(jsonPath); //2.Parse the JSON version configuration file content. - var versions = FileProvider.GetJson>(jsonPath); + var versions = GeneralFileManager.GetJson>(jsonPath); if (versions == null) throw new NullReferenceException(nameof(versions)); //3.Download version by version according to the version of the configuration file. - var versionInfo = VersionAssembler.ToDataObjects(versions); - DownloadVersions(versionInfo); - UnZip(versionInfo); + //var versionInfo = VersionAssembler.ToDataObjects(versions); + //DownloadVersions(versionInfo); + //UnZip(versionInfo); //4.Launch the main application. LaunchApp(); } catch (Exception ex) { - EventManager.Instance.Dispatch>(this, new ExceptionEventArgs(ex)); + EventManager.Instance.Dispatch(this, new ExceptionEventArgs(ex)); } finally { @@ -80,14 +76,7 @@ await Task.Run(() => /// The collection of version information to be updated as described in the configuration file. private void DownloadVersions(List versions) { - var manager = new DownloadManager(_appPath, _format, _timeOut); - manager.MultiAllDownloadCompleted += (s, e) => EventManager.Instance.Dispatch>(this, e); - manager.MultiDownloadCompleted += (s, e) => EventManager.Instance.Dispatch>(this, e); - manager.MultiDownloadError += (s, e) => EventManager.Instance.Dispatch>(this, e); - manager.MultiDownloadProgressChanged += (s, e) => EventManager.Instance.Dispatch>(this, e); - manager.MultiDownloadStatistics += (s, e) => EventManager.Instance.Dispatch>(this, e); - versions.ForEach((v) => manager.Add(new DownloadTask(manager, v))); - manager.LaunchTaskAsync(); + //TODO: download version by version } /// @@ -111,7 +100,7 @@ private bool UnZip(List versions) var zipFilePath = Path.Combine(_appPath, $"{version.Name}.zip"); var zipFactory = new GeneralZipFactory(); zipFactory.UnZipProgress += (sender, e) => - EventManager.Instance.Dispatch>(this, new MultiDownloadProgressChangedEventArgs(version, ProgressType.Updatefile, "Updating file...")); + EventManager.Instance.Dispatch(this, new MultiDownloadProgressChangedEventArgs(version, ProgressType.Updatefile, "Updating file...")); zipFactory.Completed += (sender, e) => { isCompleted = e.IsCompleted; @@ -124,7 +113,7 @@ private bool UnZip(List versions) } catch (Exception exception) { - EventManager.Instance.Dispatch>(this, new ExceptionEventArgs(exception)); + EventManager.Instance.Dispatch(this, new ExceptionEventArgs(exception)); return false; } } diff --git a/src/c#/GeneralUpdate.Core/Strategys/WindowsStrategy.cs b/src/c#/GeneralUpdate.Core/Strategys/WindowsStrategy.cs index 352619c0..3fc4929f 100644 --- a/src/c#/GeneralUpdate.Core/Strategys/WindowsStrategy.cs +++ b/src/c#/GeneralUpdate.Core/Strategys/WindowsStrategy.cs @@ -1,161 +1,107 @@ -using GeneralUpdate.Core.ContentProvider; -using GeneralUpdate.Core.Domain.Entity; -using GeneralUpdate.Core.Domain.Enum; -using GeneralUpdate.Core.Events; -using GeneralUpdate.Core.Events.CommonArgs; -using GeneralUpdate.Core.Pipelines; -using GeneralUpdate.Core.Pipelines.Context; -using GeneralUpdate.Core.Pipelines.Middleware; -using System; +using System; using System.Diagnostics; using System.IO; using System.Linq; -using System.Threading; using System.Threading.Tasks; +using GeneralUpdate.Common; +using GeneralUpdate.Common.Internal.Pipeline; +using GeneralUpdate.Common.Internal.Strategy; +using GeneralUpdate.Common.Shared.Object; +using GeneralUpdate.Core.Pipeline; -namespace GeneralUpdate.Core.Strategys.PlatformWindows +namespace GeneralUpdate.Core.Strategys { /// /// Update policy based on the Windows platform. /// public class WindowsStrategy : AbstractStrategy { - #region Private Members + private Packet Packet { get; set; } - protected Packet Packet { get; set; } - - #endregion Private Members - - #region Public Methods - - public override void Create(T parameter) => Packet = parameter as Packet; + #region Private Methods - public override void Execute() + /// + /// Remove update redundant files. + /// + /// + private void Clear() { - Task.Run(async () => - { - try - { - var updateVersions = Packet.UpdateVersions.OrderBy(x => x.PubTime).ToList(); - if (updateVersions.Count > 0) - { - foreach (var version in updateVersions) - { - var patchPath = FileProvider.GetTempDirectory(PATCHS); - var zipFilePath = Path.Combine(Packet.TempPath, $"{version.Name}{Packet.Format}"); - - var context = new BaseContext.Builder() - .SetVersion(version) - .SetZipfilePath(zipFilePath) - .SetTargetPath(patchPath) - .SetSourcePath(Packet.InstallPath) - .SetFormat(Packet.Format) - .SetEncoding(Packet.Encoding) - .SetBlackFiles(Packet.BlackFiles) - .SetBlackFileFormats(Packet.BlackFormats) - .SetAppType(Packet.AppType) - .Build(); + if (File.Exists(Packet.TempPath)) File.Delete(Packet.TempPath); + var dirPath = Path.GetDirectoryName(Packet.TempPath); + if (Directory.Exists(dirPath)) Directory.Delete(dirPath, true); + } - var pipelineBuilder = new PipelineBuilder(context) - .UseMiddleware() - .UseMiddleware() - .UseMiddleware(); - await pipelineBuilder.Build(); - } + #endregion Private Methods - if (!string.IsNullOrEmpty(Packet.UpdateLogUrl)) - Process.Start("explorer.exe", Packet.UpdateLogUrl); - } + #region Public Methods - Clear(); - StartApp(Packet.AppName, Packet.AppType); - } - catch (Exception e) - { - EventManager.Instance.Dispatch>(this, new ExceptionEventArgs(e)); - } - }); + public override void Create(Packet parameter) + { + Packet = parameter; } - public override bool StartApp(string appName, int appType) + public override async Task ExecuteAsync() { - try + var updateVersions = Packet.UpdateVersions.OrderBy(x => x.PubTime).ToList(); + if (updateVersions.Count > 0) { - var path = Path.Combine(Packet.InstallPath, appName); - switch (appType) + foreach (var version in updateVersions) { - case AppType.ClientApp: - Environment.SetEnvironmentVariable("ProcessBase64", Packet.ProcessBase64, EnvironmentVariableTarget.User); - WaitForProcessToStart(path, 20); - break; - - case AppType.UpgradeApp: - WaitForProcessToStart(path, 20); - break; + var patchPath = GeneralFileManager.GetTempDirectory(PATCHS); + var zipFilePath = Path.Combine(Packet.TempPath, $"{version.Name}{Packet.Format}"); + + var context = new PipelineContext(); + //hash middleware + context.Add("Hash", version.Hash); + context.Add("FileName", zipFilePath); + //zip middleware + context.Add("Format", Packet.Format); + context.Add("Name", zipFilePath); + context.Add("SourcePath", Packet.TempPath); + context.Add("DestinationPath", Packet.InstallPath); + context.Add("Encoding", Packet.Encoding); + //patch middleware + context.Add("SourcePath", patchPath); + context.Add("TargetPath", Packet.InstallPath); + context.Add("BlackFiles", GeneralFileManager.BlackFiles); + context.Add("BlackFileFormats", GeneralFileManager.BlackFileFormats); + + var pipelineBuilder = new PipelineBuilder(context) + .UseMiddleware() + .UseMiddleware() + .UseMiddleware() + .UseMiddlewareIf(()=> Packet.DriveEnabled); + await pipelineBuilder.Build(); } - return true; - } - catch (Exception exception) - { - EventManager.Instance.Dispatch>(this, new ExceptionEventArgs(exception)); - return false; - } - finally - { - Process.GetCurrentProcess().Kill(); - } - } - - public override string GetPlatform() => PlatformType.Windows; - - #endregion Public Methods - #region Private Methods - - /// - /// Remove update redundant files. - /// - /// - private bool Clear() - { - try - { - if (File.Exists(Packet.TempPath)) File.Delete(Packet.TempPath); - var dirPath = Path.GetDirectoryName(Packet.TempPath); - if (Directory.Exists(dirPath)) Directory.Delete(dirPath, true); - return true; - } - catch (Exception exception) - { - EventManager.Instance.Dispatch>(this, new ExceptionEventArgs(exception)); - return false; + if (!string.IsNullOrEmpty(Packet.UpdateLogUrl)) + OpenBrowser(Packet.UpdateLogUrl); } + + Clear(); + StartApp(Packet.AppName, Packet.AppType); } - /// - /// Waits for the specified process to start within a given time. - /// - /// Process objects to monitor - /// The maximum interval for waiting for the process to start (The default value is 60 seconds). - /// - private void WaitForProcessToStart(string applicationPath, int timeout, Action callbackAction = null) + public override void StartApp(string appName, int appType) { - using (var process = Process.Start(applicationPath)) + var path = Path.Combine(Packet.InstallPath, appName); + switch (appType) { - var startTime = DateTime.UtcNow; - var timeSpan = TimeSpan.FromSeconds(timeout); - while (DateTime.UtcNow - startTime < timeSpan) - { - Thread.Sleep(2 * 1000); - if (!process.HasExited) - { - callbackAction?.Invoke(); - return; - } - } + case AppType.ClientApp: + Environment.SetEnvironmentVariable("ProcessInfo", Packet.ProcessInfo, + EnvironmentVariableTarget.User); + Process.Start(path); + break; + + case AppType.UpgradeApp: + Process.Start(path); + break; + + default: + throw new ArgumentException("Invalid app type"); } } - #endregion Private Methods + #endregion Public Methods } } \ No newline at end of file From 4bdd786ac258062f19527428bb36f0f1599709a7 Mon Sep 17 00:00:00 2001 From: justerzhu Date: Fri, 20 Sep 2024 22:57:14 +0800 Subject: [PATCH 12/33] refactor: refactor generalupdate.differential --- .../Binary/BZip2Constants.cs | 2 +- .../Binary/BZip2InputStream.cs | 2 +- .../Binary/BZip2OutputStream.cs | 2 +- .../Binary/BinaryHandler.cs | 3 +- .../Binary/IChecksum.cs | 2 +- .../Binary/StrangeCRC.cs | 2 +- .../DifferentialCore.cs | 58 ++++++++++-------- .../GeneralUpdate.Differential.csproj | 61 +------------------ 8 files changed, 42 insertions(+), 90 deletions(-) diff --git a/src/c#/GeneralUpdate.Differential/Binary/BZip2Constants.cs b/src/c#/GeneralUpdate.Differential/Binary/BZip2Constants.cs index ad77ca19..4bbcd70e 100644 --- a/src/c#/GeneralUpdate.Differential/Binary/BZip2Constants.cs +++ b/src/c#/GeneralUpdate.Differential/Binary/BZip2Constants.cs @@ -1,4 +1,4 @@ -namespace GeneralUpdate.Differential.GStream +namespace GeneralUpdate.Differential.Binary { internal sealed class BZip2Constants { diff --git a/src/c#/GeneralUpdate.Differential/Binary/BZip2InputStream.cs b/src/c#/GeneralUpdate.Differential/Binary/BZip2InputStream.cs index 8ff23140..0f067f97 100644 --- a/src/c#/GeneralUpdate.Differential/Binary/BZip2InputStream.cs +++ b/src/c#/GeneralUpdate.Differential/Binary/BZip2InputStream.cs @@ -1,7 +1,7 @@ using System; using System.IO; -namespace GeneralUpdate.Differential.GStream +namespace GeneralUpdate.Differential.Binary { public class BZip2InputStream : Stream { diff --git a/src/c#/GeneralUpdate.Differential/Binary/BZip2OutputStream.cs b/src/c#/GeneralUpdate.Differential/Binary/BZip2OutputStream.cs index 67d5b8c5..3096f235 100644 --- a/src/c#/GeneralUpdate.Differential/Binary/BZip2OutputStream.cs +++ b/src/c#/GeneralUpdate.Differential/Binary/BZip2OutputStream.cs @@ -1,7 +1,7 @@ using System; using System.IO; -namespace GeneralUpdate.Differential.GStream +namespace GeneralUpdate.Differential.Binary { public class BZip2OutputStream : Stream { diff --git a/src/c#/GeneralUpdate.Differential/Binary/BinaryHandler.cs b/src/c#/GeneralUpdate.Differential/Binary/BinaryHandler.cs index 6247a5bb..cddc3eef 100644 --- a/src/c#/GeneralUpdate.Differential/Binary/BinaryHandler.cs +++ b/src/c#/GeneralUpdate.Differential/Binary/BinaryHandler.cs @@ -1,5 +1,4 @@ -using GeneralUpdate.Differential.GStream; -using System; +using System; using System.IO; using System.Threading.Tasks; diff --git a/src/c#/GeneralUpdate.Differential/Binary/IChecksum.cs b/src/c#/GeneralUpdate.Differential/Binary/IChecksum.cs index c4faac39..1d77c707 100644 --- a/src/c#/GeneralUpdate.Differential/Binary/IChecksum.cs +++ b/src/c#/GeneralUpdate.Differential/Binary/IChecksum.cs @@ -1,4 +1,4 @@ -namespace GeneralUpdate.Differential.GStream +namespace GeneralUpdate.Differential.Binary { public interface IChecksum { diff --git a/src/c#/GeneralUpdate.Differential/Binary/StrangeCRC.cs b/src/c#/GeneralUpdate.Differential/Binary/StrangeCRC.cs index 43477e92..777da013 100644 --- a/src/c#/GeneralUpdate.Differential/Binary/StrangeCRC.cs +++ b/src/c#/GeneralUpdate.Differential/Binary/StrangeCRC.cs @@ -1,6 +1,6 @@ using System; -namespace GeneralUpdate.Differential.GStream +namespace GeneralUpdate.Differential.Binary { public class StrangeCRC : IChecksum { diff --git a/src/c#/GeneralUpdate.Differential/DifferentialCore.cs b/src/c#/GeneralUpdate.Differential/DifferentialCore.cs index 9fd835d3..3c99c8ab 100644 --- a/src/c#/GeneralUpdate.Differential/DifferentialCore.cs +++ b/src/c#/GeneralUpdate.Differential/DifferentialCore.cs @@ -1,11 +1,11 @@ -using GeneralUpdate.Core.ContentProvider; -using GeneralUpdate.Core.HashAlgorithms; -using GeneralUpdate.Differential.Binary; +using GeneralUpdate.Differential.Binary; using System; using System.Collections.Generic; using System.IO; using System.Linq; using System.Threading.Tasks; +using GeneralUpdate.Common; +using GeneralUpdate.Common.HashAlgorithms; namespace GeneralUpdate.Differential { @@ -74,12 +74,12 @@ public async Task Clean(string sourcePath, string targetPath, string patchPath = Directory.CreateDirectory(patchPath); //Take the left tree as the center to match the files that are not in the right tree . - var fileProvider = new FileProvider(); - var nodes = await fileProvider.Compare(sourcePath, targetPath); - var hashAlgorithm = new Sha256HashAlgorithm(); - + var fileManager = new GeneralFileManager(); + fileManager.CompareDirectories(sourcePath, targetPath); + var result = fileManager.ComparisonResult; + //Binary differencing of like terms . - foreach (var file in nodes.Item3) + foreach (var file in GeneralFileManager.ToFileInfoList(result.DifferentFiles)) { var dirSeparatorChar = Path.DirectorySeparatorChar.ToString().ToCharArray(); var tempPath = file.FullName.Replace(targetPath, "").Replace(Path.GetFileName(file.FullName), "").TrimStart(dirSeparatorChar).TrimEnd(dirSeparatorChar); @@ -97,12 +97,13 @@ public async Task Clean(string sourcePath, string targetPath, string patchPath = tempPath0 = Path.Combine(tempDir, $"{file.Name}{PATCH_FORMAT}"); } - var finOldFile = nodes.Item1.FirstOrDefault(i => i.Name.Equals(file.Name)); + var finOldFile = GeneralFileManager.ToFileInfoList(result.UniqueToA).FirstOrDefault(i => i.Name.Equals(file.Name)); var oldFile = finOldFile == null ? "" : finOldFile.FullName; var newFile = file.FullName; var extensionName = Path.GetExtension(file.FullName); - if (File.Exists(oldFile) && File.Exists(newFile) && !FileProvider.GetBlackFileFormats().Contains(extensionName)) + if (File.Exists(oldFile) && File.Exists(newFile) && !GeneralFileManager.BlackFileFormats.Contains(extensionName)) { + var hashAlgorithm = new Sha256HashAlgorithm(); if (hashAlgorithm.ComputeHash(oldFile) .Equals(hashAlgorithm.ComputeHash(newFile), StringComparison.OrdinalIgnoreCase)) { @@ -110,7 +111,7 @@ public async Task Clean(string sourcePath, string targetPath, string patchPath = } //Generate the difference file to the difference directory . - await new BinaryHandle().Clean(oldFile, newFile, tempPath0); + await new BinaryHandler().Clean(oldFile, newFile, tempPath0); } else { @@ -119,11 +120,11 @@ public async Task Clean(string sourcePath, string targetPath, string patchPath = } //If a file is found that needs to be deleted, a list of files is written to the update package. - var exceptFiles = (await fileProvider.Except(sourcePath, targetPath)).ToList(); + var exceptFiles = result.DifferentFiles; if (exceptFiles.Count != 0) { var path = Path.Combine(patchPath, DELETE_FILES_NAME); - FileProvider.CreateJson(path, exceptFiles); + GeneralFileManager.CreateJson(path, exceptFiles); } } catch (Exception ex) @@ -144,19 +145,23 @@ public async Task Dirty(string appPath, string patchPath) if (!Directory.Exists(appPath) || !Directory.Exists(patchPath)) return; try { - var patchFiles = FileProvider.GetAllfiles(patchPath); - var oldFiles = FileProvider.GetAllfiles(appPath); + var fileManager = new GeneralFileManager(); + fileManager.CompareDirectories(appPath, patchPath); + var result = fileManager.ComparisonResult; + var patchFiles = GeneralFileManager.ToFileInfoList(result.DifferentFiles); + var oldFiles = GeneralFileManager.ToFileInfoList(result.UniqueToA); //If a JSON file for the deletion list is found in the update package, it will be deleted based on its contents. var deleteListJson = patchFiles.FirstOrDefault(i => i.Name.Equals(DELETE_FILES_NAME)); if (deleteListJson != null) { - var deleteFiles = FileProvider.GetJson>(deleteListJson.FullName); + var deleteFiles = GeneralFileManager.ToFileInfoList(GeneralFileManager.GetJson>(deleteListJson.FullName)) ; var hashAlgorithm = new Sha256HashAlgorithm(); foreach (var file in deleteFiles) { + //file.Hash var resultFile = oldFiles.FirstOrDefault(i => - string.Equals(hashAlgorithm.ComputeHash(i.FullName), file.Hash, StringComparison.OrdinalIgnoreCase)); + string.Equals(hashAlgorithm.ComputeHash(i.FullName), null, StringComparison.OrdinalIgnoreCase)); if (resultFile == null) { continue; @@ -197,8 +202,12 @@ public async Task Dirty(string appPath, string patchPath) /// /// A collection of blacklist files that are skipped when updated. /// A collection of blacklist file name extensions that are skipped on update. - public void SetBlocklist(List blackFiles, List blackFileFormats) => FileProvider.SetBlacklist(blackFiles, blackFileFormats); - + public void SetBlocklist(List blackFiles, List blackFileFormats) + { + GeneralFileManager.AddBlackFiles(blackFiles); + GeneralFileManager.AddBlackFileFormats(blackFileFormats); + } + #endregion Public Methods #region Private Methods @@ -216,7 +225,7 @@ private async Task DirtyPatch(string appPath, string patchPath) { if (!File.Exists(appPath) || !File.Exists(patchPath)) return; var newPath = Path.Combine(Path.GetDirectoryName(appPath), $"{Path.GetRandomFileName()}_{Path.GetFileName(appPath)}"); - await new BinaryHandle().Dirty(appPath, newPath, patchPath); + await new BinaryHandler().Dirty(appPath, newPath, patchPath); } catch (Exception ex) { @@ -233,12 +242,13 @@ private Task DirtyUnknow(string appPath, string patchPath) { try { - var fileProvider = new FileProvider(); - var listExcept = fileProvider.Comparer(appPath, patchPath); - foreach (var file in listExcept) + var fileManager = new GeneralFileManager(); + fileManager.CompareDirectories(appPath, patchPath); + var result = fileManager.ComparisonResult; + foreach (var file in GeneralFileManager.ToFileInfoList(result.DifferentFiles)) { var extensionName = Path.GetExtension(file.FullName); - if (FileProvider.GetBlackFileFormats().Contains(extensionName)) continue; + if (GeneralFileManager.BlackFileFormats.Contains(extensionName)) continue; var targetFileName = file.FullName.Replace(patchPath, "").TrimStart("\\".ToCharArray()); var targetPath = Path.Combine(appPath, targetFileName); var parentFolder = Directory.GetParent(targetPath); diff --git a/src/c#/GeneralUpdate.Differential/GeneralUpdate.Differential.csproj b/src/c#/GeneralUpdate.Differential/GeneralUpdate.Differential.csproj index 0efc41da..b48d2f58 100644 --- a/src/c#/GeneralUpdate.Differential/GeneralUpdate.Differential.csproj +++ b/src/c#/GeneralUpdate.Differential/GeneralUpdate.Differential.csproj @@ -16,69 +16,12 @@ - - - + + - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - From ea0680202e25435939b1b0476c7f64e4cc934a0f Mon Sep 17 00:00:00 2001 From: justerzhu Date: Fri, 20 Sep 2024 22:57:33 +0800 Subject: [PATCH 13/33] refactor: generalupdate.upgrad --- src/c#/GeneralUpdate.Upgrad/Program.cs | 102 +------------------------ 1 file changed, 1 insertion(+), 101 deletions(-) diff --git a/src/c#/GeneralUpdate.Upgrad/Program.cs b/src/c#/GeneralUpdate.Upgrad/Program.cs index e4d9e542..ad307b74 100644 --- a/src/c#/GeneralUpdate.Upgrad/Program.cs +++ b/src/c#/GeneralUpdate.Upgrad/Program.cs @@ -1,109 +1,9 @@ -using GeneralUpdate.Core; -using GeneralUpdate.Core.Bootstrap; -using GeneralUpdate.Core.Domain.Enum; -using GeneralUpdate.Core.Events.CommonArgs; -using GeneralUpdate.Core.Events.MultiEventArgs; -using GeneralUpdate.Core.Strategys.PlatformWindows; -using System.Text; - -namespace GeneralUpdate.Upgrad +namespace GeneralUpdate.Upgrad { internal class Program { private static void Main(string[] args) { - Task.Run(async () => - { - //var url = "http://192.168.50.203"; - //var appName = "GeneralUpdate.Client"; - //var version = "1.0.0.0"; - - ////Part1 OSS服务示例 - //var versionFileName = "version.json"; - //ParamsOSS @params = new ParamsOSS(url, appName, version, versionFileName); - //GeneralUpdateOSS.AddListenerMultiDownloadProgress(OnMultiDownloadProgressChanged); - ////单个或多个更新包下载速度、剩余下载事件、当前下载版本信息通知事件 - //GeneralUpdateOSS.AddListenerMultiDownloadStatistics(OnMultiDownloadStatistics); - ////单个或多个更新包下载完成 - //GeneralUpdateOSS.AddListenerMultiDownloadCompleted(OnMultiDownloadCompleted); - ////完成所有的下载任务通知 - //GeneralUpdateOSS.AddListenerMultiAllDownloadCompleted(OnMultiAllDownloadCompleted); - ////下载过程出现的异常通知 - //GeneralUpdateOSS.AddListenerMultiDownloadError(OnMultiDownloadError); - ////整个更新过程出现的任何问题都会通过这个事件通知 - //GeneralUpdateOSS.AddListenerException(OnException); - //await GeneralUpdateOSS.Start(@params,Encoding.Default); - - //Part 2 常规更新示例 - var bootStrap = await new GeneralUpdateBootstrap() - //单个或多个更新包下载通知事件 - .AddListenerMultiDownloadProgress(OnMultiDownloadProgressChanged) - //单个或多个更新包下载速度、剩余下载事件、当前下载版本信息通知事件 - .AddListenerMultiDownloadStatistics(OnMultiDownloadStatistics) - //单个或多个更新包下载完成 - .AddListenerMultiDownloadCompleted(OnMultiDownloadCompleted) - //完成所有的下载任务通知 - .AddListenerMultiAllDownloadCompleted(OnMultiAllDownloadCompleted) - //下载过程出现的异常通知 - .AddListenerMultiDownloadError(OnMultiDownloadError) - //整个更新过程出现的任何问题都会通过这个事件通知 - .AddListenerException(OnException) - .Strategy() - //.Option(UpdateOption.Encoding, Encoding.Default) - //.Option(UpdateOption.DownloadTimeOut, 60) - //.Option(UpdateOption.Format, Format.ZIP) - .LaunchTaskAsync(); - }); - Console.Read(); - } - - private static void OnMultiDownloadStatistics(object sender, MultiDownloadStatisticsEventArgs e) - { - Console.WriteLine($" {e.Speed} , {e.Remaining.ToShortTimeString()}"); - } - - private static void OnMultiDownloadProgressChanged(object sender, MultiDownloadProgressChangedEventArgs e) - { - switch (e.Type) - { - case ProgressType.Check: - break; - - case ProgressType.Download: - Console.WriteLine($" {Math.Round(e.ProgressValue * 100, 2)}% , Receivedbyte:{e.BytesReceived}M ,Totalbyte:{e.TotalBytesToReceive}M"); - break; - - case ProgressType.Updatefile: - break; - - case ProgressType.Done: - break; - - case ProgressType.Fail: - break; - } - } - - private static void OnMultiDownloadCompleted(object sender, MultiDownloadCompletedEventArgs e) - { - //var info = e.Version as GeneralUpdate.Core.Domain.Entity.VersionInfo; - //Console.WriteLine($"{info.Name} download completed."); - } - - private static void OnMultiAllDownloadCompleted(object sender, MultiAllDownloadCompletedEventArgs e) - { - Console.WriteLine($"AllDownloadCompleted {e.IsAllDownloadCompleted}"); - } - - private static void OnMultiDownloadError(object sender, MultiDownloadErrorEventArgs e) - { - //var info = e.Version as GeneralUpdate.Core.Domain.Entity.VersionInfo; - //Console.WriteLine($"{info.Name},{e.Exception.Message}."); - } - - private static void OnException(object sender, ExceptionEventArgs e) - { - Console.WriteLine($"{e.Exception.Message}"); } } } \ No newline at end of file From 7c479c0b82012f9cbb7012947bc6d6858f6cca51 Mon Sep 17 00:00:00 2001 From: "Juster.zhu" Date: Sat, 21 Sep 2024 22:56:34 +0800 Subject: [PATCH 14/33] Update DriverMiddleware.cs --- .../Pipeline/DriverMiddleware.cs | 25 +++++++++++++++++-- 1 file changed, 23 insertions(+), 2 deletions(-) diff --git a/src/c#/GeneralUpdate.Core/Pipeline/DriverMiddleware.cs b/src/c#/GeneralUpdate.Core/Pipeline/DriverMiddleware.cs index 4e38cd44..d7a4595c 100644 --- a/src/c#/GeneralUpdate.Core/Pipeline/DriverMiddleware.cs +++ b/src/c#/GeneralUpdate.Core/Pipeline/DriverMiddleware.cs @@ -1,5 +1,8 @@ -using System.Threading.Tasks; +using System.Collections.Generic; +using System.IO; +using System.Threading.Tasks; using GeneralUpdate.Common.Internal.Pipeline; +using GeneralUpdate.Core.Driver; namespace GeneralUpdate.Core.Pipeline; @@ -7,6 +10,24 @@ public class DriverMiddleware : IMiddleware { public Task InvokeAsync(PipelineContext context) { - throw new System.NotImplementedException(); + return Task.Run(() => + { + var drivers = context.Get>("Drivers"); + var sourcesPath = context.Get("Sources"); + var targetPath = context.Get("Target"); + var version = context.Get("Version"); + + var information = new DriverInformation.Builder() + .SetInstallDirectory(Path.Combine(sourcesPath, version)) + .SetOutPutDirectory(Path.Combine(targetPath, version)) + .SetDriverNames(drivers) + .Build(); + + var processor = new DriverProcessor(); + processor.AddCommand(new BackupDriverCommand(information)); + processor.AddCommand(new DeleteDriverCommand(information)); + processor.AddCommand(new InstallDriverCommand(information)); + processor.ProcessCommands(); + }); } } \ No newline at end of file From cbbd68fcd7e0d6f5765df0d765efbe22ed788cd0 Mon Sep 17 00:00:00 2001 From: "Juster.zhu" Date: Sat, 21 Sep 2024 23:16:46 +0800 Subject: [PATCH 15/33] Update GeneralUpdateBootstrap.cs --- .../GeneralUpdateBootstrap.cs | 101 ++++++++++++++++-- 1 file changed, 95 insertions(+), 6 deletions(-) diff --git a/src/c#/GeneralUpdate.Core/GeneralUpdateBootstrap.cs b/src/c#/GeneralUpdate.Core/GeneralUpdateBootstrap.cs index 714baadd..5637f7ad 100644 --- a/src/c#/GeneralUpdate.Core/GeneralUpdateBootstrap.cs +++ b/src/c#/GeneralUpdate.Core/GeneralUpdateBootstrap.cs @@ -1,17 +1,25 @@ using System; +using System.Diagnostics.Contracts; using System.IO; +using System.Runtime.InteropServices; using System.Text.Json; using System.Threading.Tasks; using GeneralUpdate.Common; +using GeneralUpdate.Common.Download; using GeneralUpdate.Common.Internal.Bootstrap; +using GeneralUpdate.Common.Internal.Event; using GeneralUpdate.Common.Internal.Strategy; using GeneralUpdate.Common.Shared.Object; +using GeneralUpdate.Core.Internal; +using GeneralUpdate.Core.Strategys; namespace GeneralUpdate.Core { public class GeneralUpdateBootstrap : AbstractBootstrap { private Packet Packet { get; set; } + + private IStrategy _strategy; public GeneralUpdateBootstrap() : base() => Remote(); @@ -22,10 +30,8 @@ private void Remote() { try { - var json = Environment.GetEnvironmentVariable("ProcessInfo", EnvironmentVariableTarget.User); var processInfo = JsonSerializer.Deserialize(json); - Packet = null; Packet.AppType = AppType.UpgradeApp; Packet.TempPath = $"{GeneralFileManager.GetTempDirectory(processInfo.LastVersion)}{Path.DirectorySeparatorChar}"; } @@ -35,19 +41,102 @@ private void Remote() } } - public override Task LaunchAsync() + public override async Task LaunchAsync() + { + var manager = new DownloadManager(Packet.InstallPath, Packet.Format, 30); + foreach (var versionInfo in Packet.UpdateVersions) manager.Add(new DownloadTask(manager, versionInfo)); + await manager.LaunchTasksAsync(); + return this; + } + + #region public method + + public GeneralUpdateBootstrap AddListenerMultiAllDownloadCompleted( + Action callbackAction) + { + return AddListener(callbackAction); + } + + public GeneralUpdateBootstrap AddListenerMultiDownloadProgress( + Action callbackAction) + { + return AddListener(callbackAction); + } + + public GeneralUpdateBootstrap AddListenerMultiDownloadCompleted( + Action callbackAction) + { + return AddListener(callbackAction); + } + + public GeneralUpdateBootstrap AddListenerMultiDownloadError( + Action callbackAction) + { + return AddListener(callbackAction); + } + + public GeneralUpdateBootstrap AddListenerMultiDownloadStatistics( + Action callbackAction) + { + return AddListener(callbackAction); + } + + public GeneralUpdateBootstrap AddListenerException(Action callbackAction) { - throw new NotImplementedException(); + return AddListener(callbackAction); } + #endregion + protected override void ExecuteStrategy() { - throw new NotImplementedException(); + _strategy.Create(Packet); + _strategy.Execute(); } protected override GeneralUpdateBootstrap StrategyFactory() { - throw new NotImplementedException(); + if (RuntimeInformation.IsOSPlatform(OSPlatform.Windows)) + _strategy = new WindowsStrategy(); + else if (RuntimeInformation.IsOSPlatform(OSPlatform.Linux)) + _strategy = new LinuxStrategy(); + else + throw new PlatformNotSupportedException("The current operating system is not supported!"); + + return this; + } + + private GeneralUpdateBootstrap AddListener(Action callbackAction) where TArgs : EventArgs + { + Contract.Requires(callbackAction != null); + EventManager.Instance.AddListener(callbackAction); + return this; + } + + private void OnMultiDownloadStatistics(object sender, MultiDownloadStatisticsEventArgs e) + { + EventManager.Instance.Dispatch(sender, e); + } + + private void OnMultiDownloadProgressChanged(object sender, MultiDownloadProgressChangedEventArgs e) + { + EventManager.Instance.Dispatch(sender, e); + } + + private void OnMultiDownloadCompleted(object sender, MultiDownloadCompletedEventArgs e) + { + EventManager.Instance.Dispatch(sender, e); + } + + private void OnMultiDownloadError(object sender, MultiDownloadErrorEventArgs e) + { + EventManager.Instance.Dispatch(sender, e); + } + + private void OnMultiAllDownloadCompleted(object sender, MultiAllDownloadCompletedEventArgs e) + { + EventManager.Instance.Dispatch(sender, e); + ExecuteStrategy(); } } } \ No newline at end of file From cb7ff3fed66a152f6b2771e30bb83fd9daa70b28 Mon Sep 17 00:00:00 2001 From: "Juster.zhu" Date: Sat, 21 Sep 2024 23:17:05 +0800 Subject: [PATCH 16/33] Update WindowsStrategy.cs --- src/c#/GeneralUpdate.Core/Strategys/WindowsStrategy.cs | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/src/c#/GeneralUpdate.Core/Strategys/WindowsStrategy.cs b/src/c#/GeneralUpdate.Core/Strategys/WindowsStrategy.cs index 3fc4929f..c9308a4a 100644 --- a/src/c#/GeneralUpdate.Core/Strategys/WindowsStrategy.cs +++ b/src/c#/GeneralUpdate.Core/Strategys/WindowsStrategy.cs @@ -1,4 +1,5 @@ using System; +using System.Collections.Generic; using System.Diagnostics; using System.IO; using System.Linq; @@ -65,6 +66,10 @@ public override async Task ExecuteAsync() context.Add("TargetPath", Packet.InstallPath); context.Add("BlackFiles", GeneralFileManager.BlackFiles); context.Add("BlackFileFormats", GeneralFileManager.BlackFileFormats); + //driver middleware + context.Add("DriverPath", new List()); + context.Add("Version", version.Version); + var pipelineBuilder = new PipelineBuilder(context) .UseMiddleware() From 7dda81e20efd338b1dfcac6e4085333360b276dd Mon Sep 17 00:00:00 2001 From: "Juster.zhu" Date: Sat, 21 Sep 2024 23:17:08 +0800 Subject: [PATCH 17/33] Update RestoreDriverCommand.cs --- src/c#/GeneralUpdate.Core/Driver/RestoreDriverCommand.cs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/c#/GeneralUpdate.Core/Driver/RestoreDriverCommand.cs b/src/c#/GeneralUpdate.Core/Driver/RestoreDriverCommand.cs index 182a820b..e6a135d4 100644 --- a/src/c#/GeneralUpdate.Core/Driver/RestoreDriverCommand.cs +++ b/src/c#/GeneralUpdate.Core/Driver/RestoreDriverCommand.cs @@ -4,7 +4,7 @@ namespace GeneralUpdate.Core.Driver { - public class RestoreDriverCommand : IDriverCommand + public class RestoreDriverCommand { private DriverInformation _information; From 22fb2e17400c193ed7f58ffdad5806ec2409d5ac Mon Sep 17 00:00:00 2001 From: "Juster.zhu" Date: Sun, 22 Sep 2024 23:58:06 +0800 Subject: [PATCH 18/33] Create FileExtensions.cs --- .../File/FileExtensions.cs | 18 ++++++++++++++++++ 1 file changed, 18 insertions(+) create mode 100644 src/c#/GeneralUpdate.Common/File/FileExtensions.cs diff --git a/src/c#/GeneralUpdate.Common/File/FileExtensions.cs b/src/c#/GeneralUpdate.Common/File/FileExtensions.cs new file mode 100644 index 00000000..496ebfb8 --- /dev/null +++ b/src/c#/GeneralUpdate.Common/File/FileExtensions.cs @@ -0,0 +1,18 @@ +using System.Collections.Generic; +using System.IO; +using System.Linq; + +namespace GeneralUpdate.Common; + +public static class FileExtensions +{ + public static List ToFileInfoList(this List filePaths) + { + return filePaths.Select(path => new FileInfo(path)).ToList(); + } + + public static List ToFileInfoList(this IReadOnlyList filePaths) + { + return filePaths.Select(path => new FileInfo(path)).ToList(); + } +} \ No newline at end of file From 0e03e477d7bbb8947ec9ed909a620777d29fce45 Mon Sep 17 00:00:00 2001 From: "Juster.zhu" Date: Mon, 23 Sep 2024 22:50:58 +0800 Subject: [PATCH 19/33] =?UTF-8?q?=E6=8B=86=E5=88=86=E9=BB=91=E5=90=8D?= =?UTF-8?q?=E5=8D=95=E9=80=BB=E8=BE=91?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../Strategys/WindowsStrategy.cs | 4 +- .../File/BlackListManager.cs | 99 +++++++++++++++++++ .../File/FileExtensions.cs | 4 +- .../File/GeneralFileManager.cs | 78 +-------------- .../Strategys/WindowsStrategy.cs | 4 +- .../DifferentialCore.cs | 20 ++-- 6 files changed, 117 insertions(+), 92 deletions(-) create mode 100644 src/c#/GeneralUpdate.Common/File/BlackListManager.cs diff --git a/src/c#/GeneralUpdate.ClientCore/Strategys/WindowsStrategy.cs b/src/c#/GeneralUpdate.ClientCore/Strategys/WindowsStrategy.cs index e9f9a705..cd445dca 100644 --- a/src/c#/GeneralUpdate.ClientCore/Strategys/WindowsStrategy.cs +++ b/src/c#/GeneralUpdate.ClientCore/Strategys/WindowsStrategy.cs @@ -63,8 +63,8 @@ public override async Task ExecuteAsync() //patch middleware context.Add("SourcePath", patchPath); context.Add("TargetPath", Packet.InstallPath); - context.Add("BlackFiles", GeneralFileManager.BlackFiles); - context.Add("BlackFileFormats", GeneralFileManager.BlackFileFormats); + context.Add("BlackFiles", BlackListManager.Instance.BlackFiles); + context.Add("BlackFileFormats", BlackListManager.Instance.BlackFileFormats); var pipelineBuilder = new PipelineBuilder(context) .UseMiddleware() diff --git a/src/c#/GeneralUpdate.Common/File/BlackListManager.cs b/src/c#/GeneralUpdate.Common/File/BlackListManager.cs new file mode 100644 index 00000000..a45d138e --- /dev/null +++ b/src/c#/GeneralUpdate.Common/File/BlackListManager.cs @@ -0,0 +1,99 @@ +using System.Collections.Generic; +using System.IO; + +namespace GeneralUpdate.Common; + +public class BlackListManager +{ + private readonly static object _lockObject = new object(); + + private static BlackListManager _instance; + + private static readonly List _blackFileFormats = + [ + ".patch", + ".7z", + ".zip", + ".rar", + ".tar", + ".json" + ]; + + private static readonly List _blackFiles = ["Newtonsoft.Json.dll"]; + + private BlackListManager() + { + } + + public static BlackListManager Instance + { + get + { + if (_instance == null) + { + lock (_lockObject) + { + if (_instance == null) + { + _instance = new BlackListManager(); + } + } + } + + return _instance; + } + } + + public IReadOnlyList BlackFileFormats => _blackFileFormats.AsReadOnly(); + public IReadOnlyList BlackFiles => _blackFiles.AsReadOnly(); + + public void AddBlackFileFormats(List formats) + { + foreach (var format in formats) + { + AddBlackFileFormat(format); + } + } + + public void AddBlackFileFormat(string format) + { + if (!_blackFileFormats.Contains(format)) + { + _blackFileFormats.Add(format); + } + } + + public void RemoveBlackFileFormat(string format) + { + _blackFileFormats.Remove(format); + } + + public void AddBlackFiles(List fileNames) + { + foreach (var fileName in fileNames) + { + AddBlackFile(fileName); + } + } + + public void AddBlackFile(string fileName) + { + if (!_blackFiles.Contains(fileName)) + { + _blackFiles.Add(fileName); + } + } + + public void RemoveBlackFile(string fileName) + { + _blackFiles.Remove(fileName); + } + + public bool IsBlacklisted(string relativeFilePath) + { + var fileName = Path.GetFileName(relativeFilePath); + var fileExtension = Path.GetExtension(relativeFilePath); + + return _blackFiles.Contains(fileName) || _blackFileFormats.Contains(fileExtension); + } +} \ No newline at end of file diff --git a/src/c#/GeneralUpdate.Common/File/FileExtensions.cs b/src/c#/GeneralUpdate.Common/File/FileExtensions.cs index 496ebfb8..1bd33175 100644 --- a/src/c#/GeneralUpdate.Common/File/FileExtensions.cs +++ b/src/c#/GeneralUpdate.Common/File/FileExtensions.cs @@ -6,12 +6,12 @@ namespace GeneralUpdate.Common; public static class FileExtensions { - public static List ToFileInfoList(this List filePaths) + public static List AsFileInfo(this List filePaths) { return filePaths.Select(path => new FileInfo(path)).ToList(); } - public static List ToFileInfoList(this IReadOnlyList filePaths) + public static List AsFileInfo(this IReadOnlyList filePaths) { return filePaths.Select(path => new FileInfo(path)).ToList(); } diff --git a/src/c#/GeneralUpdate.Common/File/GeneralFileManager.cs b/src/c#/GeneralUpdate.Common/File/GeneralFileManager.cs index 3840b417..bbd408ed 100644 --- a/src/c#/GeneralUpdate.Common/File/GeneralFileManager.cs +++ b/src/c#/GeneralUpdate.Common/File/GeneralFileManager.cs @@ -9,26 +9,7 @@ namespace GeneralUpdate.Common { public sealed class GeneralFileManager { - #region Private Members - - private static readonly List _blackFileFormats = - [ - ".patch", - ".7z", - ".zip", - ".rar", - ".tar", - ".json" - ]; - - private static readonly List _blackFiles = ["Newtonsoft.Json.dll"]; - - #endregion - #region Public Properties - - public static IReadOnlyList BlackFileFormats => _blackFileFormats.AsReadOnly(); - public static IReadOnlyList BlackFiles => _blackFiles.AsReadOnly(); public ComparisonResult ComparisonResult { get; private set; } @@ -36,53 +17,6 @@ public sealed class GeneralFileManager #region Public Methods - public static List ToFileInfoList(IReadOnlyList filePaths) - { - return filePaths.Select(path => new FileInfo(path)).ToList(); - } - - public static void AddBlackFileFormats(List formats) - { - foreach (var format in formats) - { - AddBlackFileFormat(format); - } - } - - public static void AddBlackFileFormat(string format) - { - if (!_blackFileFormats.Contains(format)) - { - _blackFileFormats.Add(format); - } - } - - public static void RemoveBlackFileFormat(string format) - { - _blackFileFormats.Remove(format); - } - - public static void AddBlackFiles(List fileNames) - { - foreach (var fileName in fileNames) - { - AddBlackFile(fileName); - } - } - - public static void AddBlackFile(string fileName) - { - if (!_blackFiles.Contains(fileName)) - { - _blackFiles.Add(fileName); - } - } - - public static void RemoveBlackFile(string fileName) - { - _blackFiles.Remove(fileName); - } - /// /// Compare two directories. /// @@ -92,8 +26,8 @@ public void CompareDirectories(string dirA, string dirB) { ComparisonResult = new ComparisonResult(); - var filesA = GetRelativeFilePaths(dirA, dirA).Where(f => !IsBlacklisted(f)).ToList(); - var filesB = GetRelativeFilePaths(dirB, dirB).Where(f => !IsBlacklisted(f)).ToList(); + var filesA = GetRelativeFilePaths(dirA, dirA).Where(f => !BlackListManager.Instance.IsBlacklisted(f)).ToList(); + var filesB = GetRelativeFilePaths(dirB, dirB).Where(f => !BlackListManager.Instance.IsBlacklisted(f)).ToList(); ComparisonResult.AddUniqueToA(filesA.Except(filesB).Select(f => Path.Combine(dirA, f))); ComparisonResult.AddUniqueToB(filesB.Except(filesA).Select(f => Path.Combine(dirB, f))); @@ -187,14 +121,6 @@ private string GetRelativePath(string fromPath, string toPath) return relativePath; } - private bool IsBlacklisted(string relativeFilePath) - { - var fileName = Path.GetFileName(relativeFilePath); - var fileExtension = Path.GetExtension(relativeFilePath); - - return _blackFiles.Contains(fileName) || _blackFileFormats.Contains(fileExtension); - } - private bool FilesAreEqual(string fileA, string fileB) { var sha256 = new Sha256HashAlgorithm(); diff --git a/src/c#/GeneralUpdate.Core/Strategys/WindowsStrategy.cs b/src/c#/GeneralUpdate.Core/Strategys/WindowsStrategy.cs index c9308a4a..c3e910f6 100644 --- a/src/c#/GeneralUpdate.Core/Strategys/WindowsStrategy.cs +++ b/src/c#/GeneralUpdate.Core/Strategys/WindowsStrategy.cs @@ -64,8 +64,8 @@ public override async Task ExecuteAsync() //patch middleware context.Add("SourcePath", patchPath); context.Add("TargetPath", Packet.InstallPath); - context.Add("BlackFiles", GeneralFileManager.BlackFiles); - context.Add("BlackFileFormats", GeneralFileManager.BlackFileFormats); + context.Add("BlackFiles", BlackListManager.Instance.BlackFiles); + context.Add("BlackFileFormats", BlackListManager.Instance.BlackFileFormats); //driver middleware context.Add("DriverPath", new List()); context.Add("Version", version.Version); diff --git a/src/c#/GeneralUpdate.Differential/DifferentialCore.cs b/src/c#/GeneralUpdate.Differential/DifferentialCore.cs index 3c99c8ab..3ac58970 100644 --- a/src/c#/GeneralUpdate.Differential/DifferentialCore.cs +++ b/src/c#/GeneralUpdate.Differential/DifferentialCore.cs @@ -79,7 +79,7 @@ public async Task Clean(string sourcePath, string targetPath, string patchPath = var result = fileManager.ComparisonResult; //Binary differencing of like terms . - foreach (var file in GeneralFileManager.ToFileInfoList(result.DifferentFiles)) + foreach (var file in result.DifferentFiles.AsFileInfo()) { var dirSeparatorChar = Path.DirectorySeparatorChar.ToString().ToCharArray(); var tempPath = file.FullName.Replace(targetPath, "").Replace(Path.GetFileName(file.FullName), "").TrimStart(dirSeparatorChar).TrimEnd(dirSeparatorChar); @@ -97,11 +97,11 @@ public async Task Clean(string sourcePath, string targetPath, string patchPath = tempPath0 = Path.Combine(tempDir, $"{file.Name}{PATCH_FORMAT}"); } - var finOldFile = GeneralFileManager.ToFileInfoList(result.UniqueToA).FirstOrDefault(i => i.Name.Equals(file.Name)); + var finOldFile = (result.UniqueToA.AsFileInfo()).FirstOrDefault(i => i.Name.Equals(file.Name)); var oldFile = finOldFile == null ? "" : finOldFile.FullName; var newFile = file.FullName; var extensionName = Path.GetExtension(file.FullName); - if (File.Exists(oldFile) && File.Exists(newFile) && !GeneralFileManager.BlackFileFormats.Contains(extensionName)) + if (File.Exists(oldFile) && File.Exists(newFile) && !BlackListManager.Instance.BlackFileFormats.Contains(extensionName)) { var hashAlgorithm = new Sha256HashAlgorithm(); if (hashAlgorithm.ComputeHash(oldFile) @@ -148,14 +148,14 @@ public async Task Dirty(string appPath, string patchPath) var fileManager = new GeneralFileManager(); fileManager.CompareDirectories(appPath, patchPath); var result = fileManager.ComparisonResult; - var patchFiles = GeneralFileManager.ToFileInfoList(result.DifferentFiles); - var oldFiles = GeneralFileManager.ToFileInfoList(result.UniqueToA); + var patchFiles = result.DifferentFiles.AsFileInfo(); + var oldFiles = result.UniqueToA.AsFileInfo(); //If a JSON file for the deletion list is found in the update package, it will be deleted based on its contents. var deleteListJson = patchFiles.FirstOrDefault(i => i.Name.Equals(DELETE_FILES_NAME)); if (deleteListJson != null) { - var deleteFiles = GeneralFileManager.ToFileInfoList(GeneralFileManager.GetJson>(deleteListJson.FullName)) ; + var deleteFiles = GeneralFileManager.GetJson>(deleteListJson.FullName).AsFileInfo() ; var hashAlgorithm = new Sha256HashAlgorithm(); foreach (var file in deleteFiles) { @@ -204,8 +204,8 @@ public async Task Dirty(string appPath, string patchPath) /// A collection of blacklist file name extensions that are skipped on update. public void SetBlocklist(List blackFiles, List blackFileFormats) { - GeneralFileManager.AddBlackFiles(blackFiles); - GeneralFileManager.AddBlackFileFormats(blackFileFormats); + BlackListManager.Instance.AddBlackFiles(blackFiles); + BlackListManager.Instance.AddBlackFileFormats(blackFileFormats); } #endregion Public Methods @@ -245,10 +245,10 @@ private Task DirtyUnknow(string appPath, string patchPath) var fileManager = new GeneralFileManager(); fileManager.CompareDirectories(appPath, patchPath); var result = fileManager.ComparisonResult; - foreach (var file in GeneralFileManager.ToFileInfoList(result.DifferentFiles)) + foreach (var file in (result.DifferentFiles.AsFileInfo())) { var extensionName = Path.GetExtension(file.FullName); - if (GeneralFileManager.BlackFileFormats.Contains(extensionName)) continue; + if (BlackListManager.Instance.BlackFileFormats.Contains(extensionName)) continue; var targetFileName = file.FullName.Replace(patchPath, "").TrimStart("\\".ToCharArray()); var targetPath = Path.Combine(appPath, targetFileName); var parentFolder = Directory.GetParent(targetPath); From c025d337c48cb5ec86e2936419de47eec8c287a8 Mon Sep 17 00:00:00 2001 From: "Juster.zhu" Date: Tue, 24 Sep 2024 23:55:49 +0800 Subject: [PATCH 20/33] Update GeneralUpdateBootstrap.cs --- src/c#/GeneralUpdate.Core/GeneralUpdateBootstrap.cs | 4 +--- 1 file changed, 1 insertion(+), 3 deletions(-) diff --git a/src/c#/GeneralUpdate.Core/GeneralUpdateBootstrap.cs b/src/c#/GeneralUpdate.Core/GeneralUpdateBootstrap.cs index 5637f7ad..b235d48a 100644 --- a/src/c#/GeneralUpdate.Core/GeneralUpdateBootstrap.cs +++ b/src/c#/GeneralUpdate.Core/GeneralUpdateBootstrap.cs @@ -53,9 +53,7 @@ public override async Task LaunchAsync() public GeneralUpdateBootstrap AddListenerMultiAllDownloadCompleted( Action callbackAction) - { - return AddListener(callbackAction); - } + => AddListener(callbackAction); public GeneralUpdateBootstrap AddListenerMultiDownloadProgress( Action callbackAction) From 75dd3e83f4f1692d6bdfcfbc682cfa3cd2a3575e Mon Sep 17 00:00:00 2001 From: "Juster.zhu" Date: Wed, 25 Sep 2024 00:10:05 +0800 Subject: [PATCH 21/33] Update DifferentialCore.cs --- .../DifferentialCore.cs | 27 ++++++++----------- 1 file changed, 11 insertions(+), 16 deletions(-) diff --git a/src/c#/GeneralUpdate.Differential/DifferentialCore.cs b/src/c#/GeneralUpdate.Differential/DifferentialCore.cs index 3ac58970..6bd9ec8f 100644 --- a/src/c#/GeneralUpdate.Differential/DifferentialCore.cs +++ b/src/c#/GeneralUpdate.Differential/DifferentialCore.cs @@ -13,8 +13,8 @@ public sealed class DifferentialCore { #region Private Members - private static readonly object _lockObj = new object(); - private static DifferentialCore _instance; + private static readonly object LockObj = new object(); + private static DifferentialCore? _instance; /// /// Differential file format . @@ -35,19 +35,14 @@ public sealed class DifferentialCore #region Public Properties - public static DifferentialCore Instance + public static DifferentialCore? Instance { get { - if (_instance == null) + if (_instance != null) return _instance; + lock (LockObj) { - lock (_lockObj) - { - if (_instance == null) - { - _instance = new DifferentialCore(); - } - } + _instance ??= new DifferentialCore(); } return _instance; } @@ -64,7 +59,7 @@ public static DifferentialCore Instance /// Recent version folder path. /// Store discovered incremental update files in a temporary directory . /// - public async Task Clean(string sourcePath, string targetPath, string patchPath = null) + public async Task Clean(string sourcePath, string targetPath, string? patchPath = null) { try { @@ -83,8 +78,8 @@ public async Task Clean(string sourcePath, string targetPath, string patchPath = { var dirSeparatorChar = Path.DirectorySeparatorChar.ToString().ToCharArray(); var tempPath = file.FullName.Replace(targetPath, "").Replace(Path.GetFileName(file.FullName), "").TrimStart(dirSeparatorChar).TrimEnd(dirSeparatorChar); - var tempPath0 = string.Empty; - var tempDir = string.Empty; + string tempPath0; + string? tempDir; if (string.IsNullOrEmpty(tempPath)) { tempDir = patchPath; @@ -224,7 +219,7 @@ private async Task DirtyPatch(string appPath, string patchPath) try { if (!File.Exists(appPath) || !File.Exists(patchPath)) return; - var newPath = Path.Combine(Path.GetDirectoryName(appPath), $"{Path.GetRandomFileName()}_{Path.GetFileName(appPath)}"); + var newPath = Path.Combine(Path.GetDirectoryName(appPath) ?? string.Empty, $"{Path.GetRandomFileName()}_{Path.GetFileName(appPath)}"); await new BinaryHandler().Dirty(appPath, newPath, patchPath); } catch (Exception ex) @@ -252,7 +247,7 @@ private Task DirtyUnknow(string appPath, string patchPath) var targetFileName = file.FullName.Replace(patchPath, "").TrimStart("\\".ToCharArray()); var targetPath = Path.Combine(appPath, targetFileName); var parentFolder = Directory.GetParent(targetPath); - if (!parentFolder.Exists) parentFolder.Create(); + if (parentFolder is { Exists: false }) parentFolder.Create(); File.Copy(file.FullName, Path.Combine(appPath, targetPath), true); } if (Directory.Exists(patchPath)) Directory.Delete(patchPath, true); From 086622b7b159456a13c76b049366dc6a0578c7d2 Mon Sep 17 00:00:00 2001 From: "Juster.zhu" Date: Wed, 25 Sep 2024 00:10:08 +0800 Subject: [PATCH 22/33] Update FileExtensions.cs --- src/c#/GeneralUpdate.Common/File/FileExtensions.cs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/c#/GeneralUpdate.Common/File/FileExtensions.cs b/src/c#/GeneralUpdate.Common/File/FileExtensions.cs index 1bd33175..afcfc778 100644 --- a/src/c#/GeneralUpdate.Common/File/FileExtensions.cs +++ b/src/c#/GeneralUpdate.Common/File/FileExtensions.cs @@ -6,7 +6,7 @@ namespace GeneralUpdate.Common; public static class FileExtensions { - public static List AsFileInfo(this List filePaths) + public static List AsFileInfo(this List? filePaths) { return filePaths.Select(path => new FileInfo(path)).ToList(); } From ba8df21a7021155e1d1781825b7086048e7acb81 Mon Sep 17 00:00:00 2001 From: "Juster.zhu" Date: Tue, 1 Oct 2024 23:29:30 +0800 Subject: [PATCH 23/33] Update GeneralUpdateOSS.cs --- src/c#/GeneralUpdate.Core/GeneralUpdateOSS.cs | 32 ++++++------------- 1 file changed, 10 insertions(+), 22 deletions(-) diff --git a/src/c#/GeneralUpdate.Core/GeneralUpdateOSS.cs b/src/c#/GeneralUpdate.Core/GeneralUpdateOSS.cs index f21de3ab..b58c283f 100644 --- a/src/c#/GeneralUpdate.Core/GeneralUpdateOSS.cs +++ b/src/c#/GeneralUpdate.Core/GeneralUpdateOSS.cs @@ -1,4 +1,5 @@ using System; +using System.Diagnostics.Contracts; using System.Text; using System.Threading.Tasks; using GeneralUpdate.Common.Download; @@ -32,39 +33,25 @@ private GeneralUpdateOSS() } public static void AddListenerMultiAllDownloadCompleted(Action callbackAction) - { - AddListener(callbackAction); - } + => AddListener(callbackAction); public static void AddListenerMultiDownloadProgress(Action callbackAction) - { - AddListener(callbackAction); - } + => AddListener(callbackAction); public static void AddListenerMultiDownloadCompleted(Action callbackAction) - { - AddListener(callbackAction); - } + => AddListener(callbackAction); public static void AddListenerMultiDownloadError(Action callbackAction) - { - AddListener(callbackAction); - } + => AddListener(callbackAction); public static void AddListenerMultiDownloadStatistics(Action callbackAction) - { - AddListener(callbackAction); - } + => AddListener(callbackAction); public static void AddListenerException(Action callbackAction) - { - AddListener(callbackAction); - } + => AddListener(callbackAction); public static void AddListenerDownloadConfigProcess(Action callbackAction) - { - AddListener(callbackAction); - } + => AddListener(callbackAction); #endregion Public Methods @@ -72,7 +59,8 @@ public static void AddListenerDownloadConfigProcess(Action(Action callbackAction) where TArgs : EventArgs { - if (callbackAction != null) EventManager.Instance.AddListener(callbackAction); + Contract.Requires(callbackAction != null); + EventManager.Instance.AddListener(callbackAction); } /// From 371aa963c10002db12c06c0d7238b105030c5519 Mon Sep 17 00:00:00 2001 From: "Juster.zhu" Date: Tue, 1 Oct 2024 23:29:34 +0800 Subject: [PATCH 24/33] Update GeneralUpdateBootstrap.cs --- .../GeneralUpdateBootstrap.cs | 49 ++++++------------- 1 file changed, 15 insertions(+), 34 deletions(-) diff --git a/src/c#/GeneralUpdate.Core/GeneralUpdateBootstrap.cs b/src/c#/GeneralUpdate.Core/GeneralUpdateBootstrap.cs index b235d48a..2139ea6f 100644 --- a/src/c#/GeneralUpdate.Core/GeneralUpdateBootstrap.cs +++ b/src/c#/GeneralUpdate.Core/GeneralUpdateBootstrap.cs @@ -20,16 +20,12 @@ public class GeneralUpdateBootstrap : AbstractBootstrap Remote(); - /// - /// Gets values from system environment variables (ClientParameter object to base64 string). - /// - private void Remote() + public GeneralUpdateBootstrap() : base() { try { + //Gets values from system environment variables (ClientParameter object to base64 string). var json = Environment.GetEnvironmentVariable("ProcessInfo", EnvironmentVariableTarget.User); var processInfo = JsonSerializer.Deserialize(json); Packet.AppType = AppType.UpgradeApp; @@ -44,7 +40,10 @@ private void Remote() public override async Task LaunchAsync() { var manager = new DownloadManager(Packet.InstallPath, Packet.Format, 30); - foreach (var versionInfo in Packet.UpdateVersions) manager.Add(new DownloadTask(manager, versionInfo)); + + foreach (var versionInfo in Packet.UpdateVersions) + manager.Add(new DownloadTask(manager, versionInfo)); + await manager.LaunchTasksAsync(); return this; } @@ -57,32 +56,22 @@ public GeneralUpdateBootstrap AddListenerMultiAllDownloadCompleted( public GeneralUpdateBootstrap AddListenerMultiDownloadProgress( Action callbackAction) - { - return AddListener(callbackAction); - } + => AddListener(callbackAction); public GeneralUpdateBootstrap AddListenerMultiDownloadCompleted( Action callbackAction) - { - return AddListener(callbackAction); - } + => AddListener(callbackAction); public GeneralUpdateBootstrap AddListenerMultiDownloadError( Action callbackAction) - { - return AddListener(callbackAction); - } + => AddListener(callbackAction); public GeneralUpdateBootstrap AddListenerMultiDownloadStatistics( Action callbackAction) - { - return AddListener(callbackAction); - } + => AddListener(callbackAction); public GeneralUpdateBootstrap AddListenerException(Action callbackAction) - { - return AddListener(callbackAction); - } + => AddListener(callbackAction); #endregion @@ -112,24 +101,16 @@ private GeneralUpdateBootstrap AddListener(Action callback } private void OnMultiDownloadStatistics(object sender, MultiDownloadStatisticsEventArgs e) - { - EventManager.Instance.Dispatch(sender, e); - } + => EventManager.Instance.Dispatch(sender, e); private void OnMultiDownloadProgressChanged(object sender, MultiDownloadProgressChangedEventArgs e) - { - EventManager.Instance.Dispatch(sender, e); - } + => EventManager.Instance.Dispatch(sender, e); private void OnMultiDownloadCompleted(object sender, MultiDownloadCompletedEventArgs e) - { - EventManager.Instance.Dispatch(sender, e); - } + => EventManager.Instance.Dispatch(sender, e); private void OnMultiDownloadError(object sender, MultiDownloadErrorEventArgs e) - { - EventManager.Instance.Dispatch(sender, e); - } + => EventManager.Instance.Dispatch(sender, e); private void OnMultiAllDownloadCompleted(object sender, MultiAllDownloadCompletedEventArgs e) { From 4e44f0bee463b54ffcb139af090fe2667bb5eb48 Mon Sep 17 00:00:00 2001 From: "Juster.zhu" Date: Tue, 1 Oct 2024 23:29:36 +0800 Subject: [PATCH 25/33] Update GeneralClientBootstrap.cs --- .../GeneralClientBootstrap.cs | 44 ++++++------------- 1 file changed, 13 insertions(+), 31 deletions(-) diff --git a/src/c#/GeneralUpdate.ClientCore/GeneralClientBootstrap.cs b/src/c#/GeneralUpdate.ClientCore/GeneralClientBootstrap.cs index bb782691..a0c468aa 100644 --- a/src/c#/GeneralUpdate.ClientCore/GeneralClientBootstrap.cs +++ b/src/c#/GeneralUpdate.ClientCore/GeneralClientBootstrap.cs @@ -56,7 +56,9 @@ public override async Task LaunchAsync() ExecuteCustomOptions(); await InitializeData(); var manager = new DownloadManager(Packet.InstallPath, Packet.Format, 30); - foreach (var versionInfo in Packet.UpdateVersions) manager.Add(new DownloadTask(manager, versionInfo)); + foreach (var versionInfo in Packet.UpdateVersions) + manager.Add(new DownloadTask(manager, versionInfo)); + await manager.LaunchTasksAsync(); return this; } @@ -139,38 +141,26 @@ public GeneralClientBootstrap AddCustomOption(List> funcs) public GeneralClientBootstrap AddListenerMultiAllDownloadCompleted( Action callbackAction) - { - return AddListener(callbackAction); - } + => AddListener(callbackAction); public GeneralClientBootstrap AddListenerMultiDownloadProgress( Action callbackAction) - { - return AddListener(callbackAction); - } + => AddListener(callbackAction); public GeneralClientBootstrap AddListenerMultiDownloadCompleted( Action callbackAction) - { - return AddListener(callbackAction); - } + => AddListener(callbackAction); public GeneralClientBootstrap AddListenerMultiDownloadError( Action callbackAction) - { - return AddListener(callbackAction); - } + => AddListener(callbackAction); public GeneralClientBootstrap AddListenerMultiDownloadStatistics( Action callbackAction) - { - return AddListener(callbackAction); - } + => AddListener(callbackAction); public GeneralClientBootstrap AddListenerException(Action callbackAction) - { - return AddListener(callbackAction); - } + => AddListener(callbackAction); #endregion Public Methods @@ -308,24 +298,16 @@ private GeneralClientBootstrap AddListener(Action callback } private void OnMultiDownloadStatistics(object sender, MultiDownloadStatisticsEventArgs e) - { - EventManager.Instance.Dispatch(sender, e); - } + => EventManager.Instance.Dispatch(sender, e); private void OnMultiDownloadProgressChanged(object sender, MultiDownloadProgressChangedEventArgs e) - { - EventManager.Instance.Dispatch(sender, e); - } + => EventManager.Instance.Dispatch(sender, e); private void OnMultiDownloadCompleted(object sender, MultiDownloadCompletedEventArgs e) - { - EventManager.Instance.Dispatch(sender, e); - } + => EventManager.Instance.Dispatch(sender, e); private void OnMultiDownloadError(object sender, MultiDownloadErrorEventArgs e) - { - EventManager.Instance.Dispatch(sender, e); - } + => EventManager.Instance.Dispatch(sender, e); private void OnMultiAllDownloadCompleted(object sender, MultiAllDownloadCompletedEventArgs e) { From 99972c346de284569a82e901b30dd8723b8bc561 Mon Sep 17 00:00:00 2001 From: "Juster.zhu" Date: Wed, 2 Oct 2024 23:40:56 +0800 Subject: [PATCH 26/33] Update GeneralUpdateOSS.cs --- src/c#/GeneralUpdate.Core/GeneralUpdateOSS.cs | 25 ++++++++++++++----- 1 file changed, 19 insertions(+), 6 deletions(-) diff --git a/src/c#/GeneralUpdate.Core/GeneralUpdateOSS.cs b/src/c#/GeneralUpdate.Core/GeneralUpdateOSS.cs index b58c283f..77a1378c 100644 --- a/src/c#/GeneralUpdate.Core/GeneralUpdateOSS.cs +++ b/src/c#/GeneralUpdate.Core/GeneralUpdateOSS.cs @@ -1,5 +1,6 @@ using System; using System.Diagnostics.Contracts; +using System.Runtime.InteropServices; using System.Text; using System.Threading.Tasks; using GeneralUpdate.Common.Download; @@ -7,6 +8,7 @@ using GeneralUpdate.Common.Internal.Strategy; using GeneralUpdate.Common.Shared.Object; using GeneralUpdate.Core.Internal; +using GeneralUpdate.Core.Strategys; namespace GeneralUpdate.Core { @@ -27,9 +29,9 @@ private GeneralUpdateOSS() /// /// /// - public static async Task Start(ParamsOSS parameter) where TStrategy : AbstractStrategy, new() + public static async Task Start(ParamsOSS parameter) { - await BaseStart(parameter); + await BaseStart(parameter); } public static void AddListenerMultiAllDownloadCompleted(Action callbackAction) @@ -69,15 +71,26 @@ private static void AddListener(Action callbackAction) whe /// The class that needs to be injected with the corresponding platform update policy or inherits the abstract update policy. /// List of parameter. /// - private static async Task BaseStart(ParamsOSS parameter) where TStrategy : AbstractStrategy, new() + private static async Task BaseStart(ParamsOSS parameter) { - //Initializes and executes the policy. - var strategyFunc = new Func(() => new TStrategy()); - var strategy = strategyFunc(); + var strategy = StrategyFactory(); //strategy.Create(parameter); //Implement different update strategies depending on the platform. await strategy.ExecuteTaskAsync(); } + + private static IStrategy StrategyFactory() + { + IStrategy strategy = null; + if (RuntimeInformation.IsOSPlatform(OSPlatform.Windows)) + strategy = new WindowsStrategy(); + else if (RuntimeInformation.IsOSPlatform(OSPlatform.Linux)) + strategy = new LinuxStrategy(); + else + throw new PlatformNotSupportedException("The current operating system is not supported!"); + + return strategy; + } #endregion Private Methods } From 69a18a63926816acf7c37f97a585714daef4b07a Mon Sep 17 00:00:00 2001 From: "Juster.zhu" Date: Thu, 3 Oct 2024 21:24:29 +0800 Subject: [PATCH 27/33] Update GeneralClientOSS.cs --- .../GeneralUpdate.ClientCore/GeneralClientOSS.cs | 14 ++++++-------- 1 file changed, 6 insertions(+), 8 deletions(-) diff --git a/src/c#/GeneralUpdate.ClientCore/GeneralClientOSS.cs b/src/c#/GeneralUpdate.ClientCore/GeneralClientOSS.cs index 3a0b74eb..1f0f15c0 100644 --- a/src/c#/GeneralUpdate.ClientCore/GeneralClientOSS.cs +++ b/src/c#/GeneralUpdate.ClientCore/GeneralClientOSS.cs @@ -1,6 +1,7 @@ using System; using System.Collections.Generic; using System.Diagnostics; +using System.Diagnostics.Contracts; using System.IO; using System.Linq; using System.Net; @@ -79,19 +80,16 @@ private static bool IsUpgrade(string clientVersion, string serverVersion) private static void DownloadFile(string url, string path) { - using (var webClient = new WebClient()) - { - webClient.DownloadFile(new Uri(url), path); - } + using var webClient = new WebClient(); + webClient.DownloadFile(new Uri(url), path); } public static void AddListenerException(Action callbackAction) - { - AddListener(callbackAction); - } + => AddListener(callbackAction); private static void AddListener(Action callbackAction) where TArgs : EventArgs { - if (callbackAction != null) EventManager.Instance.AddListener(callbackAction); + Contract.Requires(callbackAction != null); + EventManager.Instance.AddListener(callbackAction); } } \ No newline at end of file From 2566e73c8f336daac237c0b90a6885684da9f312 Mon Sep 17 00:00:00 2001 From: justerzhu Date: Tue, 5 Nov 2024 01:25:02 +0800 Subject: [PATCH 28/33] refactor: Reconstruct the entire structure The old code was refactored and new syntax was added --- src/c#/GeneralUpdate.Client/Program.cs | 85 +- .../GeneralClientBootstrap.cs | 233 +++--- .../GeneralClientOSS.cs | 2 +- .../Internal/ExceptionEventArgs.cs | 15 - .../Pipeline/HashMiddleware.cs | 9 +- .../Pipeline/PatchMiddleware.cs | 8 +- .../Pipeline/ZipMiddleware.cs | 32 +- .../Strategys/WindowsStrategy.cs | 139 ++-- .../Download/DownloadManager.cs | 12 +- .../Download/DownloadTask.cs | 29 +- .../File/BlackListManager.cs | 31 +- .../File/ComparisonResult.cs | 25 +- .../File/FileExtensions.cs | 18 - src/c#/GeneralUpdate.Common/File/FileNode.cs | 147 ++++ src/c#/GeneralUpdate.Common/File/FileTree.cs | 172 ++++ .../File/GeneralFileManager.cs | 152 ++-- .../HashAlgorithms/HashAlgorithmBase.cs | 2 +- .../Internal/Bootstrap/AbstractBootstrap.cs | 10 +- .../Internal/Bootstrap/UpdateOption.cs | 25 +- .../Internal/Exception/BaseArgs.cs | 2 +- .../Internal/Exception}/ExceptionEventArgs.cs | 2 +- .../Exception/GeneralUpdateException.cs | 2 +- .../Internal/Pipeline/PipelineBuilder.cs | 7 +- .../Internal/Strategy/AbstractStrategy.cs | 19 +- .../Internal/Strategy/IStrategy.cs | 4 +- .../Object/Assembler/VersionAssembler.cs | 37 - .../Shared/Object/Configinfo.cs | 88 ++- .../Shared/Object/DTO/BaseResponseDTO.cs | 7 +- .../Shared/Object/DTO/ReportDTO.cs | 24 + .../Shared/Object/DTO/VersionRespDTO.cs | 57 +- .../Shared/Object/Enum/Format.cs | 4 +- .../Shared/Object/GlobalConfigInfo.cs | 116 +++ .../Shared/Object/Packet.cs | 131 ---- .../Shared/Object/ProcessInfo.cs | 38 +- .../Shared/Service/VersionService.cs | 105 +-- .../Driver/BackupDriverCommand.cs | 13 +- .../Driver/CommandExecutor.cs | 8 +- .../Driver/DriverInformation.cs | 41 +- .../Driver/DriverProcessor.cs | 7 +- .../Driver/InstallDriverCommand.cs | 6 +- .../Driver/RestoreDriverCommand.cs | 30 +- .../GeneralUpdateBootstrap.cs | 65 +- src/c#/GeneralUpdate.Core/GeneralUpdateOSS.cs | 2 +- .../Pipeline/DriverMiddleware.cs | 32 +- .../Pipeline/HashMiddleware.cs | 9 +- .../Pipeline/PatchMiddleware.cs | 8 +- .../Pipeline/ZipMiddleware.cs | 32 +- .../Strategys/OSSStrategy.cs | 6 +- .../Strategys/WindowsStrategy.cs | 143 ++-- .../Binary/BinaryHandler.cs | 741 +++++++++--------- .../DifferentialCore.cs | 244 ++---- src/c#/GeneralUpdate.Upgrad/Program.cs | 20 +- src/c#/GeneralUpdate.sln | 37 - 53 files changed, 1825 insertions(+), 1408 deletions(-) delete mode 100644 src/c#/GeneralUpdate.ClientCore/Internal/ExceptionEventArgs.cs delete mode 100644 src/c#/GeneralUpdate.Common/File/FileExtensions.cs create mode 100644 src/c#/GeneralUpdate.Common/File/FileNode.cs create mode 100644 src/c#/GeneralUpdate.Common/File/FileTree.cs rename src/c#/{GeneralUpdate.Core/Internal => GeneralUpdate.Common/Internal/Exception}/ExceptionEventArgs.cs (90%) delete mode 100644 src/c#/GeneralUpdate.Common/Shared/Object/Assembler/VersionAssembler.cs create mode 100644 src/c#/GeneralUpdate.Common/Shared/Object/DTO/ReportDTO.cs create mode 100644 src/c#/GeneralUpdate.Common/Shared/Object/GlobalConfigInfo.cs delete mode 100644 src/c#/GeneralUpdate.Common/Shared/Object/Packet.cs diff --git a/src/c#/GeneralUpdate.Client/Program.cs b/src/c#/GeneralUpdate.Client/Program.cs index 9a993775..8892ff06 100644 --- a/src/c#/GeneralUpdate.Client/Program.cs +++ b/src/c#/GeneralUpdate.Client/Program.cs @@ -1,10 +1,89 @@ -namespace GeneralUpdate.Client +using System.Text; +using GeneralUpdate.ClientCore; +using GeneralUpdate.Common.Download; +using GeneralUpdate.Common.Internal; +using GeneralUpdate.Common.Internal.Bootstrap; +using GeneralUpdate.Common.Shared.Object; + +namespace GeneralUpdate.Client { - internal class Program + internal class Progra { private static void Main(string[] args) { - + /*Task.Run(async () => + { + var source = @"D:\packet\app"; + var target = @"D:\packet\release"; + var patch = @"D:\packet\patch"; + + await DifferentialCore.Instance?.Clean(source, target, patch); + await DifferentialCore.Instance?.Dirty(source, patch); + });*/ + + /*Task.Run(() => + { + var configinfo = new Configinfo(); + configinfo.UpdateLogUrl = "https://www.baidu.com"; + configinfo.ReportUrl = "http://127.0.0.1:5008/Upgrade/Report"; + configinfo.UpdateUrl = "http://127.0.0.1:5008/Upgrade/Verification"; + + configinfo.AppName = "GeneralUpdate.Upgrade"; + configinfo.MainAppName = "GeneralUpdate.Client"; + configinfo.InstallPath = Thread.GetDomain().BaseDirectory; + + configinfo.ClientVersion = "1.0.0.0"; + configinfo.UpgradeClientVersion = "1.0.0.0"; + + configinfo.Platform = 1; + configinfo.ProductId = "9999"; + configinfo.AppSecretKey = "ABCDEFGHIJKLMNOPQRSTUVWXYZ"; + + _ = new GeneralClientBootstrap()//单个或多个更新包下载通知事件 + .AddListenerMultiDownloadProgress(OnMultiDownloadProgressChanged) + //单个或多个更新包下载速度、剩余下载事件、当前下载版本信息通知事件 + .AddListenerMultiDownloadStatistics(OnMultiDownloadStatistics) + //单个或多个更新包下载完成 + .AddListenerMultiDownloadCompleted(OnMultiDownloadCompleted) + //完成所有的下载任务通知 + .AddListenerMultiAllDownloadCompleted(OnMultiAllDownloadCompleted) + //下载过程出现的异常通知 + .AddListenerMultiDownloadError(OnMultiDownloadError) + //整个更新过程出现的任何问题都会通过这个事件通知 + .AddListenerException(OnException) + .SetConfig(configinfo) + .Option(UpdateOption.DownloadTimeOut, 60) + .Option(UpdateOption.Encoding, Encoding.Default) + .Option(UpdateOption.Format, Format.ZIP) + .Option(UpdateOption.Drive, false) + .LaunchAsync(); + });*/ + + Console.Read(); + } + + private static void OnMultiDownloadError(object arg1, MultiDownloadErrorEventArgs arg2) + { + } + + private static void OnMultiAllDownloadCompleted(object arg1, MultiAllDownloadCompletedEventArgs arg2) + { + } + + private static void OnMultiDownloadCompleted(object arg1, MultiDownloadCompletedEventArgs arg2) + { + } + + private static void OnMultiDownloadStatistics(object arg1, MultiDownloadStatisticsEventArgs arg2) + { + } + + private static void OnMultiDownloadProgressChanged(object arg1, MultiDownloadProgressChangedEventArgs arg2) + { + } + + private static void OnException(object arg1, ExceptionEventArgs arg2) + { } } } \ No newline at end of file diff --git a/src/c#/GeneralUpdate.ClientCore/GeneralClientBootstrap.cs b/src/c#/GeneralUpdate.ClientCore/GeneralClientBootstrap.cs index a0c468aa..3c63ac3a 100644 --- a/src/c#/GeneralUpdate.ClientCore/GeneralClientBootstrap.cs +++ b/src/c#/GeneralUpdate.ClientCore/GeneralClientBootstrap.cs @@ -1,18 +1,15 @@ using System; using System.Collections.Generic; using System.Diagnostics; -using System.Diagnostics.Contracts; -using System.IO; using System.Linq; -using System.Reflection; using System.Runtime.InteropServices; -using System.Security; +using System.Text; using System.Text.Json; -using System.Threading; using System.Threading.Tasks; -using GeneralUpdate.ClientCore.Internal; using GeneralUpdate.ClientCore.Strategys; +using GeneralUpdate.Common; using GeneralUpdate.Common.Download; +using GeneralUpdate.Common.Internal; using GeneralUpdate.Common.Internal.Bootstrap; using GeneralUpdate.Common.Internal.Event; using GeneralUpdate.Common.Internal.Strategy; @@ -26,39 +23,36 @@ namespace GeneralUpdate.ClientCore; /// public class GeneralClientBootstrap : AbstractBootstrap { - #region Public Properties - /// - /// All update actions of the core object for automatic upgrades will be related to the Packet object. + /// All update actions of the core object for automatic upgrades will be related to the packet object. /// - private Packet Packet { get; } - - #endregion - - #region Private Members - - private IStrategy _strategy; - - private Func _customSkipOption; - + private GlobalConfigInfo? _configinfo; + private IStrategy? _strategy; + private Func? _customSkipOption; private readonly List> _customOptions = new(); - #endregion - #region Public Methods /// - /// Main function for booting the update startup. + /// Main function for booting the update startup. /// /// public override async Task LaunchAsync() { ExecuteCustomOptions(); - await InitializeData(); - var manager = new DownloadManager(Packet.InstallPath, Packet.Format, 30); - foreach (var versionInfo in Packet.UpdateVersions) - manager.Add(new DownloadTask(manager, versionInfo)); + ClearEnvironmentVariable(); + await InitializeDataAsync(); + var manager = new DownloadManager(_configinfo.TempPath, _configinfo.Format, _configinfo.DownloadTimeOut); + manager.MultiAllDownloadCompleted += OnMultiAllDownloadCompleted; + manager.MultiDownloadCompleted += OnMultiDownloadCompleted; + manager.MultiDownloadError += OnMultiDownloadError; + manager.MultiDownloadProgressChanged += OnMultiDownloadProgressChanged; + manager.MultiDownloadStatistics += OnMultiDownloadStatistics; + foreach (var versionInfo in _configinfo.UpdateVersions) + { + manager.Add(new DownloadTask(manager, versionInfo)); + } await manager.LaunchTasksAsync(); return this; } @@ -70,42 +64,26 @@ public override async Task LaunchAsync() /// The updater name does not need to contain an extension. /// /// Parameter initialization is abnormal. - public GeneralClientBootstrap SetConfig(string url, string appSecretKey, string appName) + public GeneralClientBootstrap SetConfig(Configinfo configInfo) { - if (string.IsNullOrEmpty(url)) throw new Exception("Url cannot be empty !"); - var basePath = Thread.GetDomain().BaseDirectory; - Packet.InstallPath = basePath; - Packet.AppSecretKey = appSecretKey; - //update app. - Packet.AppName = appName; - var clientVersion = GetFileVersion(Path.Combine(basePath, $"{Packet.AppName}.exe")); - Packet.ClientVersion = clientVersion; - Packet.AppType = AppType.ClientApp; - Packet.UpdateUrl = $"{url}/versions/{AppType.ClientApp}/{clientVersion}/{Packet.AppSecretKey}"; - //main app. - var mainAppName = Path.GetFileNameWithoutExtension(Process.GetCurrentProcess().MainModule.FileName); - var mainVersion = Assembly.GetExecutingAssembly().GetName().Version.ToString(); - Packet.MainUpdateUrl = $"{url}/versions/{AppType.ClientApp}/{mainVersion}/{Packet.AppSecretKey}"; - Packet.MainAppName = mainAppName; - return this; - } - - /// - /// Custom Configuration (Recommended : All platforms). - /// - /// - /// - public GeneralClientBootstrap SetConfig(Configinfo info) - { - Packet.AppType = info.AppType; - Packet.AppName = info.AppName; - Packet.AppSecretKey = info.AppSecretKey; - Packet.ClientVersion = info.ClientVersion; - Packet.UpdateUrl = info.UpdateUrl; - Packet.MainUpdateUrl = info.MainUpdateUrl; - Packet.MainAppName = info.MainAppName; - Packet.InstallPath = info.InstallPath; - Packet.UpdateLogUrl = info.UpdateLogUrl; + Debug.Assert(configInfo != null, "configInfo should not be null"); + configInfo?.Validate(); + _configinfo = new GlobalConfigInfo + { + AppName = configInfo.AppName, + MainAppName = configInfo.MainAppName, + ClientVersion = configInfo.ClientVersion, + InstallPath = configInfo.InstallPath, + UpdateLogUrl = configInfo.UpdateLogUrl, + UpdateUrl = configInfo.UpdateUrl, + ReportUrl = configInfo.ReportUrl, + AppSecretKey = configInfo.AppSecretKey, + BlackFormats = configInfo.BlackFormats, + BlackFiles = configInfo.BlackFiles, + Platform = configInfo.Platform, + ProductId = configInfo.ProductId, + UpgradeClientVersion = configInfo.UpgradeClientVersion + }; return this; } @@ -119,7 +97,7 @@ public GeneralClientBootstrap SetConfig(Configinfo info) /// public GeneralClientBootstrap SetCustomSkipOption(Func func) { - Contract.Requires(func != null); + Debug.Assert(func != null); _customSkipOption = func; return this; } @@ -134,7 +112,7 @@ public GeneralClientBootstrap SetCustomSkipOption(Func func) /// public GeneralClientBootstrap AddCustomOption(List> funcs) { - Contract.Requires(funcs != null && funcs.Any()); + Debug.Assert(funcs != null && funcs.Any()); _customOptions.AddRange(funcs); return this; } @@ -166,52 +144,52 @@ public GeneralClientBootstrap AddListenerException(Action 0; + _configinfo.IsMainUpdate = mainResp.Body.Count > 0; //No need to update, return directly. - if (!Packet.IsMainUpdate && !Packet.IsUpgradeUpdate) return; + if (!_configinfo.IsMainUpdate && !_configinfo.IsUpgradeUpdate) return; //If the main program needs to be forced to update, the skip will not take effect. - var isForcibly = mainResp.Body.IsForcibly || upgradeResp.Body.IsForcibly; + var isForcibly = CheckForcibly(mainResp.Body) || CheckForcibly(upgradeResp.Body); if (CanSkip(isForcibly)) return; - Packet.UpdateVersions = VersionAssembler.ToEntitys(upgradeResp.Body.Versions); - Packet.LastVersion = Packet.UpdateVersions.Last().Version; - + _configinfo.UpdateVersions = upgradeResp.Body.OrderBy(x => x.ReleaseDate).ToList(); + _configinfo.LastVersion = _configinfo.UpdateVersions.Last().Version; + _configinfo.Encoding = GetOption(UpdateOption.Encoding) ?? Encoding.Default; + _configinfo.Format = GetOption(UpdateOption.Format)?? "zip"; + _configinfo.DownloadTimeOut = GetOption(UpdateOption.DownloadTimeOut) == 0 ? 60 : GetOption(UpdateOption.DownloadTimeOut); + _configinfo.DriveEnabled = GetOption(UpdateOption.Drive); + _configinfo.TempPath = GeneralFileManager.GetTempDirectory(_configinfo.LastVersion); + //Initialize the process transfer parameter object. - var processInfo = new ProcessInfo(Packet.MainAppName - , Packet.InstallPath - , Packet.ClientVersion - , Packet.LastVersion - , Packet.UpdateLogUrl - , Packet.Encoding - , Packet.Format - , Packet.DownloadTimeOut - , Packet.AppSecretKey - , mainResp.Body.Versions); - Packet.ProcessInfo = JsonSerializer.Serialize(processInfo); - } - - /// - /// Gets the application version number - /// - /// - /// - /// - private string GetFileVersion(string filePath) - { - var fileInfo = new FileInfo(filePath); - if (fileInfo.Exists) return FileVersionInfo.GetVersionInfo(filePath).FileVersion; - throw new FileNotFoundException($"Failed to obtain file '{filePath}' version. Procedure."); + var processInfo = new ProcessInfo(_configinfo.MainAppName + , _configinfo.InstallPath + , _configinfo.ClientVersion + , _configinfo.LastVersion + , _configinfo.UpdateLogUrl + , _configinfo.Encoding + , _configinfo.Format + , _configinfo.DownloadTimeOut + , _configinfo.AppSecretKey + , mainResp.Body); + _configinfo.ProcessInfo = JsonSerializer.Serialize(processInfo); } /// @@ -221,21 +199,25 @@ private string GetFileVersion(string filePath) private bool CanSkip(bool isForcibly) { if (isForcibly) return false; - Contract.Requires(_customSkipOption != null); - return _customSkipOption.Invoke(); + return _customSkipOption?.Invoke() == true; } /// - /// Performs all injected custom operations. + /// Performs all injected custom operations. /// /// private void ExecuteCustomOptions() { - Contract.Requires(_customOptions != null && _customOptions.Any()); + if (!_customOptions.Any()) return; + foreach (var option in _customOptions) + { if (!option.Invoke()) + { EventManager.Instance.Dispatch(this, new ExceptionEventArgs(null, $"{nameof(option)}Execution failure!")); + } + } } /// @@ -247,23 +229,6 @@ private void ClearEnvironmentVariable() { Environment.SetEnvironmentVariable("ProcessInfo", null, EnvironmentVariableTarget.User); } - catch (SecurityException ex) - { - EventManager.Instance.Dispatch(this, - new ExceptionEventArgs(ex, - "Error: You do not have sufficient permissions to delete this environment variable.")); - } - catch (ArgumentException ex) - { - EventManager.Instance.Dispatch(this, - new ExceptionEventArgs(ex, "Error: The environment variable name is invalid.")); - } - catch (IOException ex) - { - EventManager.Instance.Dispatch(this, - new ExceptionEventArgs(ex, - "Error: An I/O error occurred while deleting the environment variable.")); - } catch (Exception ex) { EventManager.Instance.Dispatch(this, @@ -274,8 +239,8 @@ private void ClearEnvironmentVariable() protected override void ExecuteStrategy() { - _strategy.Create(Packet); - _strategy.Execute(); + _strategy?.Create(_configinfo!); + _strategy?.Execute(); } protected override GeneralClientBootstrap StrategyFactory() @@ -289,10 +254,23 @@ protected override GeneralClientBootstrap StrategyFactory() return this; } + + private bool CheckForcibly(List versions) + { + foreach (var item in versions) + { + if (item.IsForcibly == true) + { + return true; + } + } + + return false; + } private GeneralClientBootstrap AddListener(Action callbackAction) where TArgs : EventArgs { - Contract.Requires(callbackAction != null); + Debug.Assert(callbackAction != null); EventManager.Instance.AddListener(callbackAction); return this; } @@ -307,13 +285,14 @@ private void OnMultiDownloadCompleted(object sender, MultiDownloadCompletedEvent => EventManager.Instance.Dispatch(sender, e); private void OnMultiDownloadError(object sender, MultiDownloadErrorEventArgs e) - => EventManager.Instance.Dispatch(sender, e); + => EventManager.Instance.Dispatch(sender, e); private void OnMultiAllDownloadCompleted(object sender, MultiAllDownloadCompletedEventArgs e) { EventManager.Instance.Dispatch(sender, e); + StrategyFactory(); ExecuteStrategy(); } - + #endregion Private Methods } \ No newline at end of file diff --git a/src/c#/GeneralUpdate.ClientCore/GeneralClientOSS.cs b/src/c#/GeneralUpdate.ClientCore/GeneralClientOSS.cs index 1f0f15c0..6e48c04b 100644 --- a/src/c#/GeneralUpdate.ClientCore/GeneralClientOSS.cs +++ b/src/c#/GeneralUpdate.ClientCore/GeneralClientOSS.cs @@ -8,8 +8,8 @@ using System.Text.Json; using System.Threading; using System.Threading.Tasks; -using GeneralUpdate.ClientCore.Internal; using GeneralUpdate.Common; +using GeneralUpdate.Common.Internal; using GeneralUpdate.Common.Internal.Event; using GeneralUpdate.Common.Shared.Object; diff --git a/src/c#/GeneralUpdate.ClientCore/Internal/ExceptionEventArgs.cs b/src/c#/GeneralUpdate.ClientCore/Internal/ExceptionEventArgs.cs deleted file mode 100644 index 3eecdd39..00000000 --- a/src/c#/GeneralUpdate.ClientCore/Internal/ExceptionEventArgs.cs +++ /dev/null @@ -1,15 +0,0 @@ -using System; - -namespace GeneralUpdate.ClientCore.Internal; - -public class ExceptionEventArgs : EventArgs -{ - public ExceptionEventArgs(Exception exception = null, string message = null) - { - Exception = exception ?? throw new Exception(nameof(exception)); - Message = message ?? exception.Message; - } - - public Exception Exception { get; private set; } - public string Message { get; private set; } -} \ No newline at end of file diff --git a/src/c#/GeneralUpdate.ClientCore/Pipeline/HashMiddleware.cs b/src/c#/GeneralUpdate.ClientCore/Pipeline/HashMiddleware.cs index 4b5d0bd9..64f23bf3 100644 --- a/src/c#/GeneralUpdate.ClientCore/Pipeline/HashMiddleware.cs +++ b/src/c#/GeneralUpdate.ClientCore/Pipeline/HashMiddleware.cs @@ -10,19 +10,18 @@ public class HashMiddleware : IMiddleware { public async Task InvokeAsync(PipelineContext context) { - var fileName = context.Get("FileName"); + var path = context.Get("ZipFilePath"); var hash = context.Get("Hash"); - - var isVerify = await VerifyFileHash(fileName, hash); + var isVerify = await VerifyFileHash(path, hash); if (!isVerify) throw new CryptographicException("Hash verification failed ."); } - private Task VerifyFileHash(string fileName, string hash) + private Task VerifyFileHash(string path, string hash) { return Task.Run(() => { var hashAlgorithm = new Sha256HashAlgorithm(); - var hashSha256 = hashAlgorithm.ComputeHash(fileName); + var hashSha256 = hashAlgorithm.ComputeHash(path); return string.Equals(hash, hashSha256, StringComparison.OrdinalIgnoreCase); }); } diff --git a/src/c#/GeneralUpdate.ClientCore/Pipeline/PatchMiddleware.cs b/src/c#/GeneralUpdate.ClientCore/Pipeline/PatchMiddleware.cs index dadac887..c0ea6587 100644 --- a/src/c#/GeneralUpdate.ClientCore/Pipeline/PatchMiddleware.cs +++ b/src/c#/GeneralUpdate.ClientCore/Pipeline/PatchMiddleware.cs @@ -1,5 +1,6 @@ using System.Collections.Generic; using System.Threading.Tasks; +using GeneralUpdate.Common; using GeneralUpdate.Common.Internal.Pipeline; using GeneralUpdate.Differential; @@ -10,11 +11,12 @@ public class PatchMiddleware : IMiddleware public async Task InvokeAsync(PipelineContext context) { var sourcePath = context.Get("SourcePath"); - var targetPath = context.Get("TargetPath"); + var targetPath = context.Get("PatchPath"); var blackFiles = context.Get>("BlackFiles"); var blackFileFormats = context.Get>("BlackFileFormats"); - DifferentialCore.Instance.SetBlocklist(blackFiles, blackFileFormats); - await DifferentialCore.Instance.Dirty(sourcePath, targetPath); + BlackListManager.Instance.AddBlackFiles(blackFiles); + BlackListManager.Instance.AddBlackFileFormats(blackFileFormats); + await DifferentialCore.Instance?.Dirty(sourcePath, targetPath); } } \ No newline at end of file diff --git a/src/c#/GeneralUpdate.ClientCore/Pipeline/ZipMiddleware.cs b/src/c#/GeneralUpdate.ClientCore/Pipeline/ZipMiddleware.cs index 1089d88a..5cd845ca 100644 --- a/src/c#/GeneralUpdate.ClientCore/Pipeline/ZipMiddleware.cs +++ b/src/c#/GeneralUpdate.ClientCore/Pipeline/ZipMiddleware.cs @@ -1,5 +1,8 @@ -using System.Text; +using System; +using System.Text; using System.Threading.Tasks; +using GeneralUpdate.Common.Internal; +using GeneralUpdate.Common.Internal.Event; using GeneralUpdate.Common.Internal.Pipeline; using GeneralUpdate.Zip; using GeneralUpdate.Zip.Factory; @@ -12,17 +15,24 @@ public Task InvokeAsync(PipelineContext context) { return Task.Run(() => { - var type = MatchType(context.Get("Format")); - var name = context.Get("Name"); - var sourcePath = context.Get("SourcePath"); - var destinationPath = context.Get("DestinationPath"); - var encoding = context.Get("Encoding"); + try + { + var type = MatchType(context.Get("Format")); + var name = context.Get("Name"); + var sourcePath = context.Get("ZipFilePath"); + var destinationPath = context.Get("PatchPath"); + var encoding = context.Get("Encoding"); - var generalZipfactory = new GeneralZipFactory(); - generalZipfactory.CompressProgress += (sender, args) => { }; - generalZipfactory.Completed += (sender, args) => { }; - generalZipfactory.CreateOperate(type, name, sourcePath, destinationPath, true, encoding); - generalZipfactory.UnZip(); + var generalZipfactory = new GeneralZipFactory(); + generalZipfactory.CompressProgress += (sender, args) => { }; + generalZipfactory.Completed += (sender, args) => { }; + generalZipfactory.CreateOperate(type, name, sourcePath, destinationPath, true, encoding); + generalZipfactory.UnZip(); + } + catch (Exception e) + { + EventManager.Instance.Dispatch(this, new ExceptionEventArgs(e, e.Message)); + } }); } diff --git a/src/c#/GeneralUpdate.ClientCore/Strategys/WindowsStrategy.cs b/src/c#/GeneralUpdate.ClientCore/Strategys/WindowsStrategy.cs index cd445dca..ef92acbc 100644 --- a/src/c#/GeneralUpdate.ClientCore/Strategys/WindowsStrategy.cs +++ b/src/c#/GeneralUpdate.ClientCore/Strategys/WindowsStrategy.cs @@ -1,13 +1,15 @@ using System; using System.Diagnostics; using System.IO; -using System.Linq; using System.Threading.Tasks; using GeneralUpdate.ClientCore.Pipeline; using GeneralUpdate.Common; +using GeneralUpdate.Common.Internal; +using GeneralUpdate.Common.Internal.Event; using GeneralUpdate.Common.Internal.Pipeline; using GeneralUpdate.Common.Internal.Strategy; using GeneralUpdate.Common.Shared.Object; +using GeneralUpdate.Common.Shared.Service; namespace GeneralUpdate.ClientCore.Strategys; @@ -16,89 +18,76 @@ namespace GeneralUpdate.ClientCore.Strategys; /// public class WindowsStrategy : AbstractStrategy { - private Packet Packet { get; set; } + private GlobalConfigInfo _configinfo = new(); - #region Private Methods + public override void Create(GlobalConfigInfo parameter) => _configinfo = parameter; - /// - /// Remove update redundant files. - /// - /// - private void Clear() + public override void Execute() { - if (File.Exists(Packet.TempPath)) File.Delete(Packet.TempPath); - var dirPath = Path.GetDirectoryName(Packet.TempPath); - if (Directory.Exists(dirPath)) Directory.Delete(dirPath, true); - } - - #endregion Private Methods - - #region Public Methods - - public override void Create(Packet parameter) - { - Packet = parameter; - } - - public override async Task ExecuteAsync() - { - var updateVersions = Packet.UpdateVersions.OrderBy(x => x.PubTime).ToList(); - if (updateVersions.Count > 0) + Task.Run(async () => { - foreach (var version in updateVersions) + try { + var status = 0; var patchPath = GeneralFileManager.GetTempDirectory(PATCHS); - var zipFilePath = Path.Combine(Packet.TempPath, $"{version.Name}{Packet.Format}"); - - var context = new PipelineContext(); - //hash middleware - context.Add("Hash", version.Hash); - context.Add("FileName", zipFilePath); - //zip middleware - context.Add("Format", Packet.Format); - context.Add("Name", zipFilePath); - context.Add("SourcePath", Packet.TempPath); - context.Add("DestinationPath", Packet.InstallPath); - context.Add("Encoding", Packet.Encoding); - //patch middleware - context.Add("SourcePath", patchPath); - context.Add("TargetPath", Packet.InstallPath); - context.Add("BlackFiles", BlackListManager.Instance.BlackFiles); - context.Add("BlackFileFormats", BlackListManager.Instance.BlackFileFormats); - - var pipelineBuilder = new PipelineBuilder(context) - .UseMiddleware() - .UseMiddleware() - .UseMiddleware(); - await pipelineBuilder.Build(); + foreach (var version in _configinfo.UpdateVersions) + { + try + { + var context = new PipelineContext(); + //Common + context.Add("ZipFilePath", + Path.Combine(_configinfo.TempPath, $"{version.Name}{_configinfo.Format}")); + //hash middleware + context.Add("Hash", version.Hash); + //zip middleware + context.Add("Format", _configinfo.Format); + context.Add("Name", version.Name); + context.Add("Encoding", _configinfo.Encoding); + //patch middleware + context.Add("SourcePath", _configinfo.InstallPath); + context.Add("PatchPath", patchPath); + context.Add("BlackFiles", BlackListManager.Instance.BlackFiles); + context.Add("BlackFileFormats", BlackListManager.Instance.BlackFileFormats); + + var pipelineBuilder = new PipelineBuilder(context) + .UseMiddleware() + .UseMiddleware() + .UseMiddleware(); + await pipelineBuilder.Build(); + status = 2; + } + catch (Exception e) + { + status = 3; + EventManager.Instance.Dispatch(this, new ExceptionEventArgs(e, e.Message)); + } + finally + { + await VersionService.Report(_configinfo.ReportUrl, version.RecordId, status, version.AppType); + } + } + + if (!string.IsNullOrEmpty(_configinfo.UpdateLogUrl)) + { + OpenBrowser(_configinfo.UpdateLogUrl); + } + + Clear(patchPath); + Clear(_configinfo.TempPath); + StartApp(_configinfo.AppName); } - - if (!string.IsNullOrEmpty(Packet.UpdateLogUrl)) - OpenBrowser(Packet.UpdateLogUrl); - } - - Clear(); - StartApp(Packet.AppName, Packet.AppType); + catch (Exception e) + { + EventManager.Instance.Dispatch(this, new ExceptionEventArgs(e, e.Message)); + } + }); } - public override void StartApp(string appName, int appType) + public override void StartApp(string appName) { - var path = Path.Combine(Packet.InstallPath, appName); - switch (appType) - { - case AppType.ClientApp: - Environment.SetEnvironmentVariable("ProcessInfo", Packet.ProcessInfo, EnvironmentVariableTarget.User); - Process.Start(path); - break; - - case AppType.UpgradeApp: - Process.Start(path); - break; - - default: - throw new ArgumentException("Invalid app type"); - } + var path = Path.Combine(_configinfo.InstallPath, appName); + Environment.SetEnvironmentVariable("ProcessInfo", _configinfo.ProcessInfo, EnvironmentVariableTarget.User); + Process.Start(path); } - - #endregion Public Methods } \ No newline at end of file diff --git a/src/c#/GeneralUpdate.Common/Download/DownloadManager.cs b/src/c#/GeneralUpdate.Common/Download/DownloadManager.cs index 247a6c2a..849c14e0 100644 --- a/src/c#/GeneralUpdate.Common/Download/DownloadManager.cs +++ b/src/c#/GeneralUpdate.Common/Download/DownloadManager.cs @@ -2,7 +2,9 @@ using System.Collections.Generic; using System.Threading.Tasks; using System.Collections.Immutable; +using System.Diagnostics; using System.Diagnostics.Contracts; +using System.Linq; namespace GeneralUpdate.Common.Download { @@ -58,11 +60,7 @@ public async Task LaunchTasksAsync() { try { - var downloadTasks = new List(); - foreach (var task in DownloadTasks) - { - downloadTasks.Add(task.LaunchAsync()); - } + var downloadTasks = DownloadTasks.Select(task => task.LaunchAsync()).ToList(); await Task.WhenAll(downloadTasks); MultiAllDownloadCompleted?.Invoke(this, new MultiAllDownloadCompletedEventArgs(true, _failedVersions)); } @@ -91,15 +89,13 @@ public void OnMultiDownloadError(object sender, MultiDownloadErrorEventArgs e) public void Add(DownloadTask task) { - Contract.Requires(task != null); + Debug.Assert(task != null); if (!_downloadTasksBuilder.Contains(task)) { _downloadTasksBuilder.Add(task); } } - public void Remove(DownloadTask task) => _downloadTasksBuilder.Remove(task); - #endregion Public Methods } } \ No newline at end of file diff --git a/src/c#/GeneralUpdate.Common/Download/DownloadTask.cs b/src/c#/GeneralUpdate.Common/Download/DownloadTask.cs index 2546faf7..002b8e50 100644 --- a/src/c#/GeneralUpdate.Common/Download/DownloadTask.cs +++ b/src/c#/GeneralUpdate.Common/Download/DownloadTask.cs @@ -1,6 +1,8 @@ using System; using System.Collections.Generic; +using System.ComponentModel; using System.IO; +using System.Net; using System.Net.Http; using System.Threading; using System.Threading.Tasks; @@ -14,7 +16,7 @@ public class DownloadTask private readonly HttpClient _httpClient; private readonly DownloadManager _manager; - private readonly VersionInfo _version; + private readonly VersionBodyDTO _version; private const int DEFAULT_DELTA = 1048576; // 1024*1024 private long _beforBytes; private long _receivedBytes; @@ -26,7 +28,7 @@ public class DownloadTask #region Constructors - public DownloadTask(DownloadManager manager, VersionInfo version) + public DownloadTask(DownloadManager manager, VersionBodyDTO version) { _manager = manager; _version = version; @@ -50,15 +52,11 @@ public async Task LaunchAsync() { try { - var url = _version.Url; - var name = _version.Name; - var installPath = $"{_manager.Path}{name}{_manager.Format}"; - + var path = Path.Combine(_manager.Path, $"{_version.Name}{_manager.Format}"); InitStatisticsEvent(); InitProgressEvent(); InitCompletedEvent(); - - await DownloadFileRangeAsync(url, installPath); + await DownloadFileRangeAsync(_version.Url, path); } catch (Exception ex) { @@ -73,19 +71,17 @@ public async Task LaunchAsync() private async Task DownloadFileRangeAsync(string url, string path) { var tempPath = path + ".temp"; - long startPos = CheckFile(tempPath); + var startPos = CheckFile(tempPath); using var response = await _httpClient.GetAsync(url, HttpCompletionOption.ResponseHeadersRead); + if (!response.IsSuccessStatusCode) - { throw new HttpRequestException($"Failed to download file: {response.ReasonPhrase}"); - } var totalBytes = response.Content.Headers.ContentLength ?? 0; if (startPos >= totalBytes) { File.Move(tempPath, path); - OnCompleted(); return; } @@ -125,13 +121,11 @@ private async Task WriteFileAsync(string tempPath, byte[] chunk, long totalBytes using var fileStream = new FileStream(tempPath, FileMode.Append, FileAccess.Write, FileShare.None); await fileStream.WriteAsync(chunk, 0, chunk.Length); _receivedBytes += chunk.Length; - OnProgressChanged(_receivedBytes, totalBytes); if (_receivedBytes >= totalBytes) { fileStream.Close(); File.Move(tempPath, tempPath.Replace(".temp", "")); - OnCompleted(); } } @@ -205,13 +199,6 @@ private void InitCompletedEvent() }; } - private void OnProgressChanged(long bytesReceived, long totalBytes) - => _manager.OnMultiDownloadProgressChanged(this, new MultiDownloadProgressChangedEventArgs( - bytesReceived, totalBytes, (float)bytesReceived / totalBytes, _version)); - - private void OnCompleted() - => _manager.OnMultiAsyncCompleted(this, new MultiDownloadCompletedEventArgs(_version, null, false, _version)); - private string ToUnit(long byteSize) { var tempSize = Convert.ToSingle(byteSize) / 1024; diff --git a/src/c#/GeneralUpdate.Common/File/BlackListManager.cs b/src/c#/GeneralUpdate.Common/File/BlackListManager.cs index a45d138e..1ca7db3c 100644 --- a/src/c#/GeneralUpdate.Common/File/BlackListManager.cs +++ b/src/c#/GeneralUpdate.Common/File/BlackListManager.cs @@ -16,7 +16,8 @@ public class BlackListManager ".zip", ".rar", ".tar", - ".json" + ".json", + ".pdb" ]; private static readonly List _blackFiles = ["Newtonsoft.Json.dll"]; @@ -47,8 +48,11 @@ public static BlackListManager Instance public IReadOnlyList BlackFileFormats => _blackFileFormats.AsReadOnly(); public IReadOnlyList BlackFiles => _blackFiles.AsReadOnly(); - public void AddBlackFileFormats(List formats) + public void AddBlackFileFormats(List? formats) { + if(formats == null) + return; + foreach (var format in formats) { AddBlackFileFormat(format); @@ -57,19 +61,20 @@ public void AddBlackFileFormats(List formats) public void AddBlackFileFormat(string format) { + if(string.IsNullOrWhiteSpace(format)) + return; + if (!_blackFileFormats.Contains(format)) { _blackFileFormats.Add(format); } } - - public void RemoveBlackFileFormat(string format) - { - _blackFileFormats.Remove(format); - } - - public void AddBlackFiles(List fileNames) + + public void AddBlackFiles(List? fileNames) { + if(fileNames == null) + return; + foreach (var fileName in fileNames) { AddBlackFile(fileName); @@ -78,17 +83,15 @@ public void AddBlackFiles(List fileNames) public void AddBlackFile(string fileName) { + if(string.IsNullOrWhiteSpace(fileName)) + return; + if (!_blackFiles.Contains(fileName)) { _blackFiles.Add(fileName); } } - public void RemoveBlackFile(string fileName) - { - _blackFiles.Remove(fileName); - } - public bool IsBlacklisted(string relativeFilePath) { var fileName = Path.GetFileName(relativeFilePath); diff --git a/src/c#/GeneralUpdate.Common/File/ComparisonResult.cs b/src/c#/GeneralUpdate.Common/File/ComparisonResult.cs index 37ab0f8c..089d54ed 100644 --- a/src/c#/GeneralUpdate.Common/File/ComparisonResult.cs +++ b/src/c#/GeneralUpdate.Common/File/ComparisonResult.cs @@ -7,28 +7,35 @@ namespace GeneralUpdate.Common; /// public class ComparisonResult { - private readonly List _uniqueToA = new List(); - private readonly List _uniqueToB = new List(); - private readonly List _differentFiles = new List(); + private List _leftNodes; + private List _rightNodes; + private List _differentNodes; + + public ComparisonResult() + { + _leftNodes = new List(); + _rightNodes = new List(); + _differentNodes = new List(); + } /// /// List of files that are unique to A. /// - public IReadOnlyList UniqueToA => _uniqueToA.AsReadOnly(); + public IReadOnlyList LeftNodes => _leftNodes.AsReadOnly(); /// /// List of files that are unique to B. /// - public IReadOnlyList UniqueToB => _uniqueToB.AsReadOnly(); + public IReadOnlyList RightNodes => _rightNodes.AsReadOnly(); /// /// List of files that are different between A and B. /// - public IReadOnlyList DifferentFiles => _differentFiles.AsReadOnly(); + public IReadOnlyList DifferentNodes => _differentNodes.AsReadOnly(); - public void AddUniqueToA(IEnumerable files) => _uniqueToA.AddRange(files); + public void AddToLeft(IEnumerable files) => _leftNodes.AddRange(files); - public void AddUniqueToB(IEnumerable files) => _uniqueToB.AddRange(files); + public void AddToRight(IEnumerable files) => _rightNodes.AddRange(files); - public void AddDifferentFiles(IEnumerable files) => _differentFiles.AddRange(files); + public void AddDifferent(IEnumerable files) => _differentNodes.AddRange(files); } \ No newline at end of file diff --git a/src/c#/GeneralUpdate.Common/File/FileExtensions.cs b/src/c#/GeneralUpdate.Common/File/FileExtensions.cs deleted file mode 100644 index afcfc778..00000000 --- a/src/c#/GeneralUpdate.Common/File/FileExtensions.cs +++ /dev/null @@ -1,18 +0,0 @@ -using System.Collections.Generic; -using System.IO; -using System.Linq; - -namespace GeneralUpdate.Common; - -public static class FileExtensions -{ - public static List AsFileInfo(this List? filePaths) - { - return filePaths.Select(path => new FileInfo(path)).ToList(); - } - - public static List AsFileInfo(this IReadOnlyList filePaths) - { - return filePaths.Select(path => new FileInfo(path)).ToList(); - } -} \ No newline at end of file diff --git a/src/c#/GeneralUpdate.Common/File/FileNode.cs b/src/c#/GeneralUpdate.Common/File/FileNode.cs new file mode 100644 index 00000000..04e1da83 --- /dev/null +++ b/src/c#/GeneralUpdate.Common/File/FileNode.cs @@ -0,0 +1,147 @@ +using System; + +namespace GeneralUpdate.Common; + + public class FileNode + { + #region Public Properties + + public long Id { get; set; } + + public string Name { get; set; } + + public string FullName { get; set; } + + public string Path { get; set; } + + public string Hash { get; set; } + + public FileNode Left { get; set; } + + public FileNode Right { get; set; } + + public int LeftType { get; set; } + + public int RightType { get; set; } + + public string RelativePath { get; set; } + + #endregion Public Properties + + #region Constructors + + public FileNode() + { } + + public FileNode(int id) + { + Id = id; + } + + #endregion Constructors + + #region Public Methods + + public void Add(FileNode node) + { + if (node == null) return; + + if (node.Id < Id) + { + if (Left == null) + { + Left = node; + } + else + { + Left.Add(node); + } + } + else + { + if (Right == null) + { + Right = node; + } + else + { + Right.Add(node); + } + } + } + + public void InfixOrder() + { + if (Left != null) + { + Left.InfixOrder(); + } + if (Right != null) + { + Right.InfixOrder(); + } + } + + public FileNode Search(long id) + { + if (id == Id) + { + return this; + } + else if (id < Id) + { + if (Left == null) return null; + return Left.Search(id); + } + else + { + if (Right == null) return null; + return Right.Search(id); + } + } + + /// + /// Find the parent node of the node that you want to delete. + /// + /// + /// + public FileNode SearchParent(long id) + { + if (Left != null && Left.Id == id || Right != null && Right.Id == id) + { + return this; + } + else + { + if (id < Id && Left != null) + { + return Left.SearchParent(id); + } + else if (id >= Id && Right != null) + { + return Right.SearchParent(id); + } + else + { + return null; + } + } + } + + /// + /// Compare tree nodes equally by Hash and file names. + /// + /// + /// + public override bool Equals(object obj) + { + if (obj == null) return false; + var tempNode = obj as FileNode; + if (tempNode == null) throw new ArgumentException(nameof(tempNode)); + return string.Equals(Hash, tempNode.Hash, StringComparison.OrdinalIgnoreCase) && string.Equals(Name, tempNode.Name, StringComparison.OrdinalIgnoreCase); + } + + public override int GetHashCode() => base.GetHashCode(); + + #endregion Public Methods + } diff --git a/src/c#/GeneralUpdate.Common/File/FileTree.cs b/src/c#/GeneralUpdate.Common/File/FileTree.cs new file mode 100644 index 00000000..c05289c6 --- /dev/null +++ b/src/c#/GeneralUpdate.Common/File/FileTree.cs @@ -0,0 +1,172 @@ +using System.Collections.Generic; +using System.Diagnostics; + +namespace GeneralUpdate.Common; + + /// + /// Simple file binary tree. + /// + public class FileTree + { + #region Private Members + + private FileNode _root; + + #endregion Private Members + + #region Constructors + + public FileTree() + { } + + public FileTree(IEnumerable nodes) + { + foreach (var node in nodes) Add(node); + } + + #endregion Constructors + + #region Public Methods + + public void Add(FileNode node) + { + if (_root == null) + { + _root = node; + } + else + { + _root.Add(node); + } + } + + public void InfixOrder() + { + if (_root != null) + { + _root.InfixOrder(); + } + else + { + Debug.WriteLine("The binary sort tree is empty and cannot be traversed!"); + } + } + + public FileNode Search(long id) => _root == null ? null : _root.Search(id); + + public FileNode SearchParent(long id) => _root == null ? null : _root.SearchParent(id); + + public long DelRightTreeMin(FileNode node) + { + FileNode target = node; + while (target.Left != null) + { + target = target.Left; + } + DelNode(target.Id); + return target.Id; + } + + public void DelNode(long id) + { + if (_root == null) + { + return; + } + else + { + FileNode targetNode = Search(id); + if (targetNode == null) + { + return; + } + if (_root.Left == null && _root.Right == null) + { + _root = null; + return; + } + + FileNode parent = SearchParent(id); + if (targetNode.Left == null && targetNode.Right == null) + { + if (parent.Left != null && parent.Left.Id == id) + { + parent.Left = null; + } + else if (parent.Right != null && parent.Right.Id == id) + { + parent.Right = null; + } + } + else if (targetNode.Left != null && targetNode.Right != null) + { + long minVal = DelRightTreeMin(targetNode.Right); + targetNode.Id = minVal; + } + else + { + if (targetNode.Left != null) + { + if (parent.Left.Id == id) + { + parent.Left = targetNode.Left; + } + else + { + parent.Right = targetNode.Left; + } + } + else + { + if (parent.Left.Id == id) + { + parent.Left = targetNode.Right; + } + else + { + parent.Right = targetNode.Right; + } + } + } + } + } + + /// + /// Starting from the root node, recursively compares two different child nodes of the binary tree and nodes that are not included. + /// + /// + /// + /// + public void Compare(FileNode node, FileNode node0, ref List nodes) + { + if (node != null && node.Left != null) + { + if (!node.Equals(node0) && node0 != null) nodes.Add(node0); + Compare(node.Left, node0.Left, ref nodes); + } + else if (node0 != null && node0.Left != null) + { + nodes.Add(node0); + Compare(node.Left, node0.Left, ref nodes); + } + + if (node != null && node.Right != null) + { + if (!node.Equals(node0) && node0 != null) nodes.Add(node0); + Compare(node.Right, node0 == null ? null : node0.Right, ref nodes); + } + else if (node0 != null && node0.Right != null) + { + nodes.Add(node0); + Compare(node == null ? null : node.Right, node0.Right, ref nodes); + } + else if (node0 != null) + { + nodes.Add(node0); + } + } + + public FileNode GetRoot() => _root; + + #endregion Public Methods + } \ No newline at end of file diff --git a/src/c#/GeneralUpdate.Common/File/GeneralFileManager.cs b/src/c#/GeneralUpdate.Common/File/GeneralFileManager.cs index bbd408ed..4a323694 100644 --- a/src/c#/GeneralUpdate.Common/File/GeneralFileManager.cs +++ b/src/c#/GeneralUpdate.Common/File/GeneralFileManager.cs @@ -3,49 +3,50 @@ using System.IO; using System.Linq; using System.Text.Json; +using System.Threading; using GeneralUpdate.Common.HashAlgorithms; namespace GeneralUpdate.Common { public sealed class GeneralFileManager { - #region Public Properties - + private long _fileCount = 0; public ComparisonResult ComparisonResult { get; private set; } - - #endregion #region Public Methods + /// + /// Using the list on the left as a baseline, find the set of differences between the two file lists. + /// + public IEnumerable Except(string leftPath, string rightPath) + { + var leftFileNodes = ReadFileNode(leftPath); + var rightFileNodes = ReadFileNode(rightPath); + var rightNodeDic = rightFileNodes.ToDictionary(x => x.RelativePath); + return leftFileNodes.Where(f => !rightNodeDic.ContainsKey(f.RelativePath)).ToList(); + } + /// /// Compare two directories. /// - /// - /// - public void CompareDirectories(string dirA, string dirB) + /// + /// + public ComparisonResult Compare(string leftDir, string rightDir) { + ResetId(); ComparisonResult = new ComparisonResult(); - - var filesA = GetRelativeFilePaths(dirA, dirA).Where(f => !BlackListManager.Instance.IsBlacklisted(f)).ToList(); - var filesB = GetRelativeFilePaths(dirB, dirB).Where(f => !BlackListManager.Instance.IsBlacklisted(f)).ToList(); - - ComparisonResult.AddUniqueToA(filesA.Except(filesB).Select(f => Path.Combine(dirA, f))); - ComparisonResult.AddUniqueToB(filesB.Except(filesA).Select(f => Path.Combine(dirB, f))); - - var commonFiles = filesA.Intersect(filesB); - - foreach (var file in commonFiles) - { - var fileA = Path.Combine(dirA, file); - var fileB = Path.Combine(dirB, file); - - if (!FilesAreEqual(fileA, fileB)) - { - ComparisonResult.AddDifferentFiles(new[] { file }); - } - } + var leftFileNodes = ReadFileNode(leftDir); + var rightFileNodes = ReadFileNode(rightDir); + var leftTree = new FileTree(leftFileNodes); + var rightTree = new FileTree(rightFileNodes); + var differentTreeNode = new List(); + leftTree.Compare(leftTree.GetRoot(), rightTree.GetRoot(), ref differentTreeNode); + ComparisonResult.AddToLeft(leftFileNodes); + ComparisonResult.AddToRight(rightFileNodes); + ComparisonResult.AddDifferent(differentTreeNode); + return ComparisonResult; } - + public static void CreateJson(string targetPath, T obj) where T : class { var folderPath = Path.GetDirectoryName(targetPath) ?? @@ -54,7 +55,7 @@ public static void CreateJson(string targetPath, T obj) where T : class { Directory.CreateDirectory(folderPath); } - + var jsonString = JsonSerializer.Serialize(obj); File.WriteAllText(targetPath, jsonString); } @@ -66,6 +67,7 @@ public static void CreateJson(string targetPath, T obj) where T : class var json = File.ReadAllText(path); return JsonSerializer.Deserialize(json); } + return default; } @@ -77,59 +79,89 @@ public static string GetTempDirectory(string name) { Directory.CreateDirectory(tempDir); } + return tempDir; } - - #endregion - #region Private Methods - - private IEnumerable GetRelativeFilePaths(string rootDir, string currentDir) + public static List GetAllfiles(string path) { - foreach (var file in Directory.GetFiles(currentDir)) + try { - yield return GetRelativePath(rootDir, file); - } - - foreach (var dir in Directory.GetDirectories(currentDir)) - { - foreach (var file in GetRelativeFilePaths(rootDir, dir)) + var files = new List(); + files.AddRange(new DirectoryInfo(path).GetFiles()); + var tmpDir = new DirectoryInfo(path).GetDirectories(); + foreach (var dic in tmpDir) { - yield return file; + files.AddRange(GetAllfiles(dic.FullName)); } + + return files; + } + catch (Exception) + { + return null; } } - private string GetRelativePath(string fromPath, string toPath) + public static bool HashEquals(string leftPath, string rightPath) { - var fromUri = new Uri(fromPath); - var toUri = new Uri(toPath); + var hashAlgorithm = new Sha256HashAlgorithm(); + var hashLeft = hashAlgorithm.ComputeHash(leftPath); + var hashRight = hashAlgorithm.ComputeHash(rightPath); + return hashLeft.SequenceEqual(hashRight); + } - if (fromUri.Scheme != toUri.Scheme) + #endregion + + #region Private Methods + + /// + /// Recursively read all files in the folder path. + /// + private IEnumerable ReadFileNode(string path, string rootPath = null) + { + var resultFiles = new List(); + rootPath ??= path; + if (!rootPath.EndsWith("/")) + { + rootPath += "/"; + } + var rootUri = new Uri(rootPath); + + foreach (var subPath in Directory.EnumerateFiles(path)) { - return toPath; - } // path can't be made relative. + if (BlackListManager.Instance.IsBlacklisted(subPath)) continue; - var relativeUri = fromUri.MakeRelativeUri(toUri); - var relativePath = Uri.UnescapeDataString(relativeUri.ToString()); + var hashAlgorithm = new Sha256HashAlgorithm(); + var hash = hashAlgorithm.ComputeHash(subPath); + var subFileInfo = new FileInfo(subPath); + var subUri = new Uri(subFileInfo.FullName); + resultFiles.Add(new FileNode + { + Id = GetId(), + Path = path, + Name = subFileInfo.Name, + Hash = hash, + FullName = subFileInfo.FullName, + RelativePath = rootUri.MakeRelativeUri(subUri).ToString() + }); + } - if (toUri.Scheme.Equals("file", StringComparison.InvariantCultureIgnoreCase)) + foreach (var subPath in Directory.EnumerateDirectories(path)) { - relativePath = relativePath.Replace(Path.AltDirectorySeparatorChar, Path.DirectorySeparatorChar); + resultFiles.AddRange(ReadFileNode(subPath, rootPath)); } - return relativePath; + return resultFiles; } - private bool FilesAreEqual(string fileA, string fileB) - { - var sha256 = new Sha256HashAlgorithm(); - var hashA = sha256.ComputeHash(fileA); - var hashB = sha256.ComputeHash(fileB); + /// + /// Self-growing file tree node ID. + /// + private long GetId() => Interlocked.Increment(ref _fileCount); + + private void ResetId() => Interlocked.Exchange(ref _fileCount, 0); - return hashA.SequenceEqual(hashB); - } - #endregion } } \ No newline at end of file diff --git a/src/c#/GeneralUpdate.Common/HashAlgorithms/HashAlgorithmBase.cs b/src/c#/GeneralUpdate.Common/HashAlgorithms/HashAlgorithmBase.cs index ef57e5da..d32ee2c8 100644 --- a/src/c#/GeneralUpdate.Common/HashAlgorithms/HashAlgorithmBase.cs +++ b/src/c#/GeneralUpdate.Common/HashAlgorithms/HashAlgorithmBase.cs @@ -15,7 +15,7 @@ public string ComputeHash(string fileName) { using (var file = new FileStream(fileName, FileMode.Open, FileAccess.Read, FileShare.Read)) { - var dataArray = GetHashAlgorithm().ComputeHash(file); + var dataArray = hashAlgorithm.ComputeHash(file); var stringBuilder = new StringBuilder(); for (int i = 0; i < dataArray.Length; i++) { diff --git a/src/c#/GeneralUpdate.Common/Internal/Bootstrap/AbstractBootstrap.cs b/src/c#/GeneralUpdate.Common/Internal/Bootstrap/AbstractBootstrap.cs index dbd7e0e8..a570dc2a 100644 --- a/src/c#/GeneralUpdate.Common/Internal/Bootstrap/AbstractBootstrap.cs +++ b/src/c#/GeneralUpdate.Common/Internal/Bootstrap/AbstractBootstrap.cs @@ -1,5 +1,5 @@ using System.Collections.Concurrent; -using System.Diagnostics.Contracts; +using System.Diagnostics; using System.Threading.Tasks; using GeneralUpdate.Common.Internal.Strategy; @@ -11,7 +11,8 @@ public abstract class AbstractBootstrap { private readonly ConcurrentDictionary _options; - protected internal AbstractBootstrap() => _options = new ConcurrentDictionary(); + protected internal AbstractBootstrap() => + _options = new ConcurrentDictionary(); /// /// Launch async udpate. @@ -43,10 +44,9 @@ public virtual TBootstrap Option(UpdateOption option, T value) return (TBootstrap)this; } - public virtual T? GetOption(UpdateOption option) + protected virtual T? GetOption(UpdateOption option) { - Contract.Requires(option != null); - if (_options.Count == 0) return default(T); + Debug.Assert(option != null && _options.Count != 0); var val = _options[option]; if (val != null) return (T)val.GetValue(); return default(T); diff --git a/src/c#/GeneralUpdate.Common/Internal/Bootstrap/UpdateOption.cs b/src/c#/GeneralUpdate.Common/Internal/Bootstrap/UpdateOption.cs index 131b0b59..804ac4ad 100644 --- a/src/c#/GeneralUpdate.Common/Internal/Bootstrap/UpdateOption.cs +++ b/src/c#/GeneralUpdate.Common/Internal/Bootstrap/UpdateOption.cs @@ -8,6 +8,15 @@ namespace GeneralUpdate.Common.Internal.Bootstrap { public abstract class UpdateOption : AbstractConstant { + private class UpdateOptionPool : ConstantPool + { + protected override IConstant NewConstant(int id, string name) => new UpdateOption(id, name); + } + + private static readonly UpdateOptionPool Pool = new(); + + public static UpdateOption ValueOf(string name) => (UpdateOption)Pool.ValueOf(name); + /// /// Update the file format of the package. /// @@ -18,11 +27,6 @@ public abstract class UpdateOption : AbstractConstant /// public static readonly UpdateOption Encoding = ValueOf("COMPRESSENCODING"); - /// - /// Main program name. - /// - public static readonly UpdateOption MainApp = ValueOf("MAINAPP"); - /// /// Timeout period (unit: second). If this parameter is not specified, the default timeout period is 30 seconds. /// @@ -32,16 +36,7 @@ public abstract class UpdateOption : AbstractConstant /// Whether to enable the driver upgrade function. /// public static readonly UpdateOption Drive = ValueOf("DRIVE"); - - private class UpdateOptionPool : ConstantPool - { - protected override IConstant NewConstant(int id, string name) => new UpdateOption(id, name); - } - - private static readonly UpdateOptionPool Pool = new UpdateOptionPool(); - - public static UpdateOption ValueOf(string name) => (UpdateOption)Pool.ValueOf(name); - + internal UpdateOption(int id, string name) : base(id, name) { } diff --git a/src/c#/GeneralUpdate.Common/Internal/Exception/BaseArgs.cs b/src/c#/GeneralUpdate.Common/Internal/Exception/BaseArgs.cs index b29af43a..d0aa03d2 100644 --- a/src/c#/GeneralUpdate.Common/Internal/Exception/BaseArgs.cs +++ b/src/c#/GeneralUpdate.Common/Internal/Exception/BaseArgs.cs @@ -1,6 +1,6 @@ using System; -namespace GeneralUpdate.Common.Internal.Exception +namespace GeneralUpdate.Common.Internal { [Serializable] public abstract class BaseArgs diff --git a/src/c#/GeneralUpdate.Core/Internal/ExceptionEventArgs.cs b/src/c#/GeneralUpdate.Common/Internal/Exception/ExceptionEventArgs.cs similarity index 90% rename from src/c#/GeneralUpdate.Core/Internal/ExceptionEventArgs.cs rename to src/c#/GeneralUpdate.Common/Internal/Exception/ExceptionEventArgs.cs index 6603942e..54850e56 100644 --- a/src/c#/GeneralUpdate.Core/Internal/ExceptionEventArgs.cs +++ b/src/c#/GeneralUpdate.Common/Internal/Exception/ExceptionEventArgs.cs @@ -1,6 +1,6 @@ using System; -namespace GeneralUpdate.Core.Internal; +namespace GeneralUpdate.Common.Internal; public class ExceptionEventArgs : EventArgs { diff --git a/src/c#/GeneralUpdate.Common/Internal/Exception/GeneralUpdateException.cs b/src/c#/GeneralUpdate.Common/Internal/Exception/GeneralUpdateException.cs index f5e05091..ff3f7fd0 100644 --- a/src/c#/GeneralUpdate.Common/Internal/Exception/GeneralUpdateException.cs +++ b/src/c#/GeneralUpdate.Common/Internal/Exception/GeneralUpdateException.cs @@ -2,7 +2,7 @@ using System.Runtime.Serialization; using System.Security.Permissions; -namespace GeneralUpdate.Common.Internal.Exception +namespace GeneralUpdate.Common.Internal { /// /// Exception of GeneralUpdate framework. diff --git a/src/c#/GeneralUpdate.Common/Internal/Pipeline/PipelineBuilder.cs b/src/c#/GeneralUpdate.Common/Internal/Pipeline/PipelineBuilder.cs index 7ff667ba..f198ef5e 100644 --- a/src/c#/GeneralUpdate.Common/Internal/Pipeline/PipelineBuilder.cs +++ b/src/c#/GeneralUpdate.Common/Internal/Pipeline/PipelineBuilder.cs @@ -21,10 +21,10 @@ public sealed class PipelineBuilder(PipelineContext context = null) return this; } - public PipelineBuilder UseMiddlewareIf(Func condition) + public PipelineBuilder UseMiddlewareIf(bool? condition) where TMiddleware : IMiddleware, new() { - if (!condition()) return this; + if (condition == false) return this; var middleware = new TMiddleware(); _middlewareStack = _middlewareStack.Push(middleware); return this; @@ -32,9 +32,8 @@ public PipelineBuilder UseMiddlewareIf(Func condition) public async Task Build() { - while (!_middlewareStack.IsEmpty) + foreach (var middleware in _middlewareStack) { - _middlewareStack.Pop(out var middleware); await middleware.InvokeAsync(context); } } diff --git a/src/c#/GeneralUpdate.Common/Internal/Strategy/AbstractStrategy.cs b/src/c#/GeneralUpdate.Common/Internal/Strategy/AbstractStrategy.cs index a88d164d..8bb83c5e 100644 --- a/src/c#/GeneralUpdate.Common/Internal/Strategy/AbstractStrategy.cs +++ b/src/c#/GeneralUpdate.Common/Internal/Strategy/AbstractStrategy.cs @@ -1,5 +1,6 @@ using System; using System.Diagnostics; +using System.IO; using System.Runtime.InteropServices; using System.Threading.Tasks; using GeneralUpdate.Common.Shared.Object; @@ -12,13 +13,11 @@ public abstract class AbstractStrategy : IStrategy public virtual void Execute() => throw new NotImplementedException(); - public virtual Task ExecuteAsync() => throw new NotImplementedException(); - - public virtual void StartApp(string appName, int appType) => throw new NotImplementedException(); + public virtual void StartApp(string appName) => throw new NotImplementedException(); public virtual Task ExecuteTaskAsync() => throw new NotImplementedException(); - public virtual void Create(Packet parameter) => throw new NotImplementedException(); + public virtual void Create(GlobalConfigInfo parameter) => throw new NotImplementedException(); protected void OpenBrowser(string url) { @@ -35,5 +34,17 @@ protected void OpenBrowser(string url) throw new PlatformNotSupportedException("Unsupported OS platform"); } } + + /// + /// Remove update redundant files. + /// + /// + protected void Clear(string path) + { + if (Directory.Exists(path)) + { + Directory.Delete(path, true); + } + } } } \ No newline at end of file diff --git a/src/c#/GeneralUpdate.Common/Internal/Strategy/IStrategy.cs b/src/c#/GeneralUpdate.Common/Internal/Strategy/IStrategy.cs index e3d8a3ee..3efe0f30 100644 --- a/src/c#/GeneralUpdate.Common/Internal/Strategy/IStrategy.cs +++ b/src/c#/GeneralUpdate.Common/Internal/Strategy/IStrategy.cs @@ -19,7 +19,7 @@ public interface IStrategy /// /// /// - void StartApp(string appName, int appType); + void StartApp(string appName); /// /// Execution strategy. @@ -29,6 +29,6 @@ public interface IStrategy /// /// Create a strategy. /// - void Create(Packet parameter); + void Create(GlobalConfigInfo parameter); } } \ No newline at end of file diff --git a/src/c#/GeneralUpdate.Common/Shared/Object/Assembler/VersionAssembler.cs b/src/c#/GeneralUpdate.Common/Shared/Object/Assembler/VersionAssembler.cs deleted file mode 100644 index 8c3f1ad4..00000000 --- a/src/c#/GeneralUpdate.Common/Shared/Object/Assembler/VersionAssembler.cs +++ /dev/null @@ -1,37 +0,0 @@ -using System.Collections.Generic; - -namespace GeneralUpdate.Common.Shared.Object -{ - public class VersionAssembler - { - public static List ToDataObjects(List versionDTO) - { - List entitys = new List(); - versionDTO.ForEach((v) => - { - entitys.Add(ToDataObject(v)); - }); - return entitys; - } - - public static VersionInfo ToDataObject(VersionConfigDO versionDO) - { - return new VersionInfo(versionDO.PubTime, versionDO.Name, versionDO.Hash, versionDO.Version, versionDO.Url); - } - - public static List ToEntitys(List versionDTO) - { - List entitys = new List(); - versionDTO.ForEach((v) => - { - entitys.Add(ToEntity(v)); - }); - return entitys; - } - - public static VersionInfo ToEntity(VersionDTO versionDTO) - { - return new VersionInfo(versionDTO.PubTime, versionDTO.Name, versionDTO.Hash, versionDTO.Version, versionDTO.Url); - } - } -} \ No newline at end of file diff --git a/src/c#/GeneralUpdate.Common/Shared/Object/Configinfo.cs b/src/c#/GeneralUpdate.Common/Shared/Object/Configinfo.cs index 116145e3..f111b33c 100644 --- a/src/c#/GeneralUpdate.Common/Shared/Object/Configinfo.cs +++ b/src/c#/GeneralUpdate.Common/Shared/Object/Configinfo.cs @@ -1,36 +1,38 @@ using System; -using System.IO; +using System.Collections.Generic; namespace GeneralUpdate.Common.Shared.Object { + /// + /// Global update parameters. + /// public class Configinfo { - public Configinfo() - { } - - public Configinfo(int appType, string appName, string appSecretKey, string clientVersion, string updateUrl, string updateLogUrl, string installPath, string mainUpdateUrl, string mainAppName) - { - AppType = appType; - AppName = appName ?? throw new ArgumentNullException(nameof(appName)); - AppSecretKey = appSecretKey ?? throw new ArgumentNullException(nameof(appSecretKey)); - ClientVersion = clientVersion ?? throw new ArgumentNullException(nameof(clientVersion)); - UpdateUrl = updateUrl ?? throw new ArgumentNullException(nameof(updateUrl)); - UpdateLogUrl = updateLogUrl ?? throw new ArgumentNullException(nameof(updateLogUrl)); - InstallPath = installPath ?? Directory.GetCurrentDirectory(); - MainUpdateUrl = mainUpdateUrl ?? throw new ArgumentNullException(nameof(mainUpdateUrl)); - MainAppName = mainAppName ?? throw new ArgumentNullException(nameof(mainAppName)); - } + /// + /// Update check api address. + /// + public string UpdateUrl { get; set; } /// - /// 1:ClientApp 2:UpdateApp + /// API address for reporting update status. /// - public int AppType { get; set; } + public string ReportUrl { get; set; } /// /// Need to start the name of the app. /// public string AppName { get; set; } + /// + /// The name of the main application, without .exe. + /// + public string MainAppName { get; set; } + + /// + /// Update log web address. + /// + public string UpdateLogUrl { get; set; } + /// /// application key /// @@ -40,27 +42,59 @@ public Configinfo(int appType, string appName, string appSecretKey, string clien /// Client current version. /// public string ClientVersion { get; set; } + + /// + /// Upgrade Client current version. + /// + public string UpgradeClientVersion { get; set; } /// - /// Update check api address. + /// installation path (for update file logic). /// - public string UpdateUrl { get; set; } + public string InstallPath { get; set; } /// - /// Update log web address. + /// Files in the blacklist will skip the update. /// - public string UpdateLogUrl { get; set; } + public List BlackFiles { get; set; } /// - /// installation path (for update file logic). + /// File formats in the blacklist will skip the update. /// - public string InstallPath { get; set; } + public List BlackFormats { get; set; } + + /// + /// The platform of the application. + /// + public int Platform { get; set; } /// - /// Update check api address. + /// Product ID. /// - public string MainUpdateUrl { get; set; } + public string ProductId { get; set; } - public string MainAppName { get; set; } + public void Validate() + { + if (string.IsNullOrWhiteSpace(UpdateUrl) || !Uri.IsWellFormedUriString(UpdateUrl, UriKind.Absolute)) + throw new ArgumentException("Invalid UpdateUrl"); + + if (!string.IsNullOrWhiteSpace(UpdateLogUrl) && !Uri.IsWellFormedUriString(UpdateLogUrl, UriKind.Absolute)) + throw new ArgumentException("Invalid UpdateLogUrl"); + + if (string.IsNullOrWhiteSpace(AppName)) + throw new ArgumentException("AppName cannot be empty"); + + if (string.IsNullOrWhiteSpace(MainAppName)) + throw new ArgumentException("MainAppName cannot be empty"); + + if (string.IsNullOrWhiteSpace(AppSecretKey)) + throw new ArgumentException("AppSecretKey cannot be empty"); + + if (string.IsNullOrWhiteSpace(ClientVersion)) + throw new ArgumentException("ClientVersion cannot be empty"); + + if (string.IsNullOrWhiteSpace(InstallPath)) + throw new ArgumentException("InstallPath cannot be empty"); + } } } \ No newline at end of file diff --git a/src/c#/GeneralUpdate.Common/Shared/Object/DTO/BaseResponseDTO.cs b/src/c#/GeneralUpdate.Common/Shared/Object/DTO/BaseResponseDTO.cs index 72b8a5fa..dfa9b49a 100644 --- a/src/c#/GeneralUpdate.Common/Shared/Object/DTO/BaseResponseDTO.cs +++ b/src/c#/GeneralUpdate.Common/Shared/Object/DTO/BaseResponseDTO.cs @@ -1,11 +1,16 @@ -namespace GeneralUpdate.Common.Shared.Object +using System.Text.Json.Serialization; + +namespace GeneralUpdate.Common.Shared.Object { public class BaseResponseDTO { + [JsonPropertyName("code")] public int Code { get; set; } + [JsonPropertyName("body")] public TBody Body { get; set; } + [JsonPropertyName("message")] public string Message { get; set; } } } \ No newline at end of file diff --git a/src/c#/GeneralUpdate.Common/Shared/Object/DTO/ReportDTO.cs b/src/c#/GeneralUpdate.Common/Shared/Object/DTO/ReportDTO.cs new file mode 100644 index 00000000..65c2c785 --- /dev/null +++ b/src/c#/GeneralUpdate.Common/Shared/Object/DTO/ReportDTO.cs @@ -0,0 +1,24 @@ +using System.Text.Json.Serialization; + +namespace GeneralUpdate.Common.Shared.Object; + +public class ReportDTO +{ + /// + /// 记录id + /// + [JsonPropertyName("recordId")] + public int RecordId { get; set; } + + /// + /// 更新状态 + /// + [JsonPropertyName("status")] + public int Status { get; set; } + + /// + /// 1升级 2推送 + /// + [JsonPropertyName("type")] + public int Type { get; set; } +} \ No newline at end of file diff --git a/src/c#/GeneralUpdate.Common/Shared/Object/DTO/VersionRespDTO.cs b/src/c#/GeneralUpdate.Common/Shared/Object/DTO/VersionRespDTO.cs index d42a71d6..a86b9d36 100644 --- a/src/c#/GeneralUpdate.Common/Shared/Object/DTO/VersionRespDTO.cs +++ b/src/c#/GeneralUpdate.Common/Shared/Object/DTO/VersionRespDTO.cs @@ -1,27 +1,48 @@ -using System.Collections.Generic; +using System; +using System.Collections.Generic; +using System.Text.Json.Serialization; namespace GeneralUpdate.Common.Shared.Object { - public class VersionRespDTO : BaseResponseDTO + public class VersionRespDTO : BaseResponseDTO> { } public class VersionBodyDTO { - public bool IsUpdate { get; set; } - - /// - /// Is forcibly update. - /// - public bool IsForcibly { get; set; } - - /// - /// 1:ClientApp 2:UpdateApp - /// - public int ClientType { get; set; } - - /// - /// Returns information about all versions that are different from the latest version based on the current version of the client. - /// - public List Versions { get; set; } + [JsonPropertyName("recordId")] + public int RecordId { get; set; } + + [JsonPropertyName("name")] + public string? Name { get; set; } + + [JsonPropertyName("hash")] + public string? Hash { get; set; } + + [JsonPropertyName("releaseDate")] + public DateTime? ReleaseDate { get; set; } + + [JsonPropertyName("url")] + public string? Url { get; set; } + + [JsonPropertyName("version")] + public string? Version { get; set; } + + [JsonPropertyName("appType")] + public int? AppType { get; set; } + + [JsonPropertyName("platform")] + public int? Platform { get; set; } + + [JsonPropertyName("productId")] + public string? ProductId { get; set; } + + [JsonPropertyName("isForcibly")] + public bool? IsForcibly { get; set; } + + [JsonPropertyName("format")] + public string Format { get; set; } + + [JsonPropertyName("size")] + public long? Size { get; set; } } } \ No newline at end of file diff --git a/src/c#/GeneralUpdate.Common/Shared/Object/Enum/Format.cs b/src/c#/GeneralUpdate.Common/Shared/Object/Enum/Format.cs index cae1d5d2..0548ee08 100644 --- a/src/c#/GeneralUpdate.Common/Shared/Object/Enum/Format.cs +++ b/src/c#/GeneralUpdate.Common/Shared/Object/Enum/Format.cs @@ -2,7 +2,7 @@ { public class Format { - public const string ZIP = "zip"; - public const string SEVENZIP = "7z"; + public const string ZIP = ".zip"; + public const string SEVENZIP = ".7z"; } } \ No newline at end of file diff --git a/src/c#/GeneralUpdate.Common/Shared/Object/GlobalConfigInfo.cs b/src/c#/GeneralUpdate.Common/Shared/Object/GlobalConfigInfo.cs new file mode 100644 index 00000000..67fa36c2 --- /dev/null +++ b/src/c#/GeneralUpdate.Common/Shared/Object/GlobalConfigInfo.cs @@ -0,0 +1,116 @@ +using System.Collections.Generic; +using System.Text; + +namespace GeneralUpdate.Common.Shared.Object; + +public class GlobalConfigInfo +{ + /// + /// Update check api address. + /// + public string UpdateUrl { get; set; } + + /// + /// API address for reporting update status. + /// + public string ReportUrl { get; set; } + + /// + /// Need to start the name of the app. + /// + public string AppName { get; set; } + + /// + /// The name of the main application, without .exe. + /// + public string MainAppName { get; set; } + + /// + /// Update package file format(Defult format is Zip). + /// + public string Format { get; set; } + + /// + /// Whether an update is required to upgrade the application. + /// + public bool IsUpgradeUpdate { get; set; } + + /// + /// Whether the main application needs to be updated. + /// + public bool IsMainUpdate { get; set; } + + /// + /// Update log web address. + /// + public string UpdateLogUrl { get; set; } + + /// + /// Version information that needs to be updated. + /// + public List UpdateVersions { get; set; } + + /// + /// The encoding format for file operations. + /// + public Encoding Encoding { get; set; } + + /// + /// Time-out event for file download. + /// + public int DownloadTimeOut { get; set; } + + /// + /// application key + /// + public string AppSecretKey { get; set; } + + /// + /// Client current version. + /// + public string ClientVersion { get; set; } + + /// + /// Upgrade Client current version. + /// + public string UpgradeClientVersion { get; set; } + + /// + /// The latest version. + /// + public string LastVersion { get; set; } + + /// + /// installation path (for update file logic). + /// + public string InstallPath { get; set; } + + /// + /// Download file temporary storage path (for update file logic). + /// + public string TempPath { get; set; } + + /// + /// Configuration parameters for upgrading the terminal program. + /// + public string ProcessInfo { get; set; } + + /// + /// Files in the blacklist will skip the update. + /// + public List BlackFiles { get; set; } + + /// + /// File formats in the blacklist will skip the update. + /// + public List BlackFormats { get; set; } + + /// + /// Whether to enable the driver upgrade function. + /// + public bool? DriveEnabled { get; set; } + + public int Platform { get; set; } + + public string ProductId { get; set; } +} \ No newline at end of file diff --git a/src/c#/GeneralUpdate.Common/Shared/Object/Packet.cs b/src/c#/GeneralUpdate.Common/Shared/Object/Packet.cs deleted file mode 100644 index 2fa473d8..00000000 --- a/src/c#/GeneralUpdate.Common/Shared/Object/Packet.cs +++ /dev/null @@ -1,131 +0,0 @@ -using System; -using System.Collections.Generic; -using System.Text; - -namespace GeneralUpdate.Common.Shared.Object -{ - public class Packet - { - public Packet(string mainUpdateUrl, int appType, string updateUrl, string appName, string mainAppName, string format, bool isUpdate, string updateLogUrl, Encoding encoding, int downloadTimeOut, string appSecretKey, string tempPath) - { - MainUpdateUrl = mainUpdateUrl ?? throw new ArgumentNullException(nameof(MainUpdateUrl)); - UpdateUrl = updateUrl ?? throw new ArgumentNullException(nameof(updateUrl)); - UpdateLogUrl = updateLogUrl ?? throw new ArgumentNullException(nameof(updateLogUrl)); - AppType = appType; - AppName = appName ?? throw new ArgumentNullException(nameof(appName)); - MainAppName = mainAppName ?? throw new ArgumentNullException(nameof(mainAppName)); - Format = format ?? throw new ArgumentNullException(nameof(format)); - IsUpgradeUpdate = isUpdate; - Encoding = encoding ?? throw new ArgumentNullException(nameof(encoding)); - DownloadTimeOut = downloadTimeOut; - AppSecretKey = appSecretKey ?? throw new ArgumentNullException(nameof(appSecretKey)); - TempPath = tempPath ?? throw new ArgumentNullException(nameof(tempPath)); - } - - /// - /// Update check api address. - /// - public string MainUpdateUrl { get; set; } - - /// - /// 1:ClientApp 2:UpdateApp - /// - public int AppType { get; set; } - - /// - /// Update check api address. - /// - public string UpdateUrl { get; set; } - - /// - /// Need to start the name of the app. - /// - public string AppName { get; set; } - - /// - /// The name of the main application, without .exe. - /// - public string MainAppName { get; set; } - - /// - /// Update package file format(Defult format is Zip). - /// - public string Format { get; set; } - - /// - /// Whether an update is required to upgrade the application. - /// - public bool IsUpgradeUpdate { get; set; } - - /// - /// Whether the main application needs to be updated. - /// - public bool IsMainUpdate { get; set; } - - /// - /// Update log web address. - /// - public string UpdateLogUrl { get; set; } - - /// - /// Version information that needs to be updated. - /// - public List UpdateVersions { get; set; } - - /// - /// The encoding format for file operations. - /// - public Encoding Encoding { get; set; } - - /// - /// Time-out event for file download. - /// - public int DownloadTimeOut { get; set; } - - /// - /// application key - /// - public string AppSecretKey { get; set; } - - /// - /// Client current version. - /// - public string ClientVersion { get; set; } - - /// - /// The latest version. - /// - public string LastVersion { get; set; } - - /// - /// installation path (for update file logic). - /// - public string InstallPath { get; set; } - - /// - /// Download file temporary storage path (for update file logic). - /// - public string TempPath { get; set; } - - /// - /// Configuration parameters for upgrading the terminal program. - /// - public string ProcessInfo { get; set; } - - /// - /// Files in the blacklist will skip the update. - /// - public List BlackFiles { get; set; } - - /// - /// File formats in the blacklist will skip the update. - /// - public List BlackFormats { get; set; } - - /// - /// Whether to enable the driver upgrade function. - /// - public bool DriveEnabled { get; set; } - - } -} \ No newline at end of file diff --git a/src/c#/GeneralUpdate.Common/Shared/Object/ProcessInfo.cs b/src/c#/GeneralUpdate.Common/Shared/Object/ProcessInfo.cs index 8e20661a..239c7fbf 100644 --- a/src/c#/GeneralUpdate.Common/Shared/Object/ProcessInfo.cs +++ b/src/c#/GeneralUpdate.Common/Shared/Object/ProcessInfo.cs @@ -7,66 +7,58 @@ namespace GeneralUpdate.Common.Shared.Object { public class ProcessInfo { - public ProcessInfo() - { } - - public ProcessInfo(string appName, string installPath, string currentVersion, string lastVersion, string logUrl, Encoding compressEncoding, string compressFormat, int downloadTimeOut, string appSecretKey, List updateVersions) + public ProcessInfo(string appName, string installPath, string currentVersion, string lastVersion, string updateLogUrl, Encoding compressEncoding, string compressFormat, int downloadTimeOut, string appSecretKey, List updateVersions) { AppName = appName ?? throw new ArgumentNullException(nameof(appName)); if (!Directory.Exists(installPath)) throw new ArgumentException($"{nameof(installPath)} path does not exist ! {installPath}."); InstallPath = installPath ?? throw new ArgumentNullException(nameof(installPath)); CurrentVersion = currentVersion ?? throw new ArgumentNullException(nameof(currentVersion)); LastVersion = lastVersion ?? throw new ArgumentNullException(nameof(lastVersion)); - LogUrl = logUrl; - compressEncoding = compressEncoding ?? Encoding.Default; + UpdateLogUrl = updateLogUrl; CompressEncoding = ToEncodingType(compressEncoding); CompressFormat = compressFormat; if (downloadTimeOut < 0) throw new ArgumentException("Timeout must be greater than 0 !"); DownloadTimeOut = downloadTimeOut; AppSecretKey = appSecretKey ?? throw new ArgumentNullException(nameof(appSecretKey)); if (updateVersions == null || updateVersions.Count == 0) throw new ArgumentException("Collection cannot be null or has 0 elements !"); - UpdateVersions = VersionAssembler.ToEntitys(updateVersions); + UpdateVersions = updateVersions; } - private int ToEncodingType(Encoding encoding) + private static int ToEncodingType(Encoding encoding) { - int type = -1; - if (encoding == Encoding.UTF8) + var type = -1; + if (Equals(encoding, Encoding.UTF8)) { type = 1; } - else if (encoding == Encoding.UTF7) + else if (Equals(encoding, Encoding.UTF7)) { type = 2; } - else if (encoding == Encoding.UTF32) + else if (Equals(encoding, Encoding.UTF32)) { type = 3; } - else if (encoding == Encoding.Unicode) + else if (Equals(encoding, Encoding.Unicode)) { type = 4; } - else if (encoding == Encoding.BigEndianUnicode) + else if (Equals(encoding, Encoding.BigEndianUnicode)) { type = 5; } - else if (encoding == Encoding.ASCII) + else if (Equals(encoding, Encoding.ASCII)) { type = 6; } - else if (encoding == Encoding.Default) + else if (Equals(encoding, Encoding.Default)) { type = 7; } + return type; } - /// - /// 1:MainApp 2:UpdateApp - /// - public int AppType { get; set; } - /// /// Need to start the name of the app. /// @@ -84,7 +76,7 @@ private int ToEncodingType(Encoding encoding) /// /// Update log web address. /// - public string LogUrl { get; set; } + public string UpdateLogUrl { get; set; } public int CompressEncoding { get; set; } @@ -100,6 +92,6 @@ private int ToEncodingType(Encoding encoding) /// /// One or more version update information. /// - public List UpdateVersions { get; set; } + public List UpdateVersions { get; set; } } } \ No newline at end of file diff --git a/src/c#/GeneralUpdate.Common/Shared/Service/VersionService.cs b/src/c#/GeneralUpdate.Common/Shared/Service/VersionService.cs index 934394be..61a2c83d 100644 --- a/src/c#/GeneralUpdate.Common/Shared/Service/VersionService.cs +++ b/src/c#/GeneralUpdate.Common/Shared/Service/VersionService.cs @@ -1,8 +1,12 @@ using System; +using System.Collections.Generic; +using System.IO; using System.Net.Http; using System.Security.Cryptography.X509Certificates; using System.Net.Security; +using System.Text; using System.Text.Json; +using System.Text.Json.Serialization; using System.Threading.Tasks; using GeneralUpdate.Common.Shared.Object; @@ -10,63 +14,70 @@ namespace GeneralUpdate.Common.Shared.Service { public class VersionService { - private static readonly HttpClient _httpClient; - - static VersionService() + /// + /// Report the result of this update: whether it was successful. + /// + /// + /// + /// + /// + /// + public static async Task> Report(string httpUrl + , int recordId + , int status + , int? type) { - _httpClient = new HttpClient(new HttpClientHandler + var parameters = new Dictionary { - ServerCertificateCustomValidationCallback = CheckValidationResult - }); - _httpClient.Timeout = TimeSpan.FromSeconds(15); - _httpClient.DefaultRequestHeaders.Accept.ParseAdd("text/html, application/xhtml+xml, */*"); + { "RecordId", recordId }, + { "Status", status }, + { "Type", type } + }; + return await PostTaskAsync>(httpUrl, parameters); } - public async Task ValidationVersion(string url) + /// + /// Verify whether the current version needs an update. + /// + /// + /// + /// + /// + /// + /// + /// + public static async Task Validate(string httpUrl + , string version + , int appType + , string appKey + , int platform, + string productId) { - var updateResp = await GetTaskAsync(url); - if (updateResp == null || updateResp.Body == null) - { - throw new ArgumentNullException( - nameof(updateResp), - "The verification request is abnormal, please check the network or parameter configuration!" - ); - } - - if (updateResp.Code == 200) - { - return updateResp; - } - else + var parameters = new Dictionary { - throw new HttpRequestException( - $"Request failed, Code: {updateResp.Code}, Message: {updateResp.Message}!" - ); - } + { "Version", version }, + { "AppType", appType }, + { "AppKey", appKey }, + { "Platform", platform }, + { "ProductId", productId } + }; + return await PostTaskAsync(httpUrl, parameters); } - private async Task GetTaskAsync(string url, string headerKey = null, string headerValue = null) + private static async Task PostTaskAsync(string httpUrl, Dictionary parameters) { - try + var uri = new Uri(httpUrl); + using var httpClient = new HttpClient(new HttpClientHandler { - var request = new HttpRequestMessage(HttpMethod.Get, url); - if (!string.IsNullOrEmpty(headerKey) && !string.IsNullOrEmpty(headerValue)) - { - request.Headers.Add(headerKey, headerValue); - } - - var response = await _httpClient.SendAsync(request); - response.EnsureSuccessStatusCode(); // Throw if not a success code. - var responseContent = await response.Content.ReadAsStringAsync(); - var result = JsonSerializer.Deserialize(responseContent); - - return result; - } - catch (Exception ex) - { - // Log the exception here as needed - return default; - } + ServerCertificateCustomValidationCallback = CheckValidationResult + }); + httpClient.Timeout = TimeSpan.FromSeconds(15); + httpClient.DefaultRequestHeaders.Accept.ParseAdd("text/html, application/xhtml+xml, */*"); + string parametersJson = JsonSerializer.Serialize(parameters); + var stringContent = new StringContent(parametersJson, Encoding.UTF8, "application/json"); + var result = await httpClient.PostAsync(uri, stringContent); + var reseponseJson = await result.Content.ReadAsStringAsync(); + return JsonSerializer.Deserialize(reseponseJson); } private static bool CheckValidationResult( diff --git a/src/c#/GeneralUpdate.Core/Driver/BackupDriverCommand.cs b/src/c#/GeneralUpdate.Core/Driver/BackupDriverCommand.cs index a98efe95..b528d05e 100644 --- a/src/c#/GeneralUpdate.Core/Driver/BackupDriverCommand.cs +++ b/src/c#/GeneralUpdate.Core/Driver/BackupDriverCommand.cs @@ -20,25 +20,22 @@ public void Execute() foreach (var driver in _information.Drivers) { //Export the backup according to the driver name. - var outPutDirectory = Path.Combine(_information.OutPutDirectory, Path.GetFileNameWithoutExtension(driver)); - - if (Directory.Exists(outPutDirectory)) - Directory.Delete(outPutDirectory, true); - - Directory.CreateDirectory(outPutDirectory); + if (Directory.Exists(_information.OutPutDirectory)) + Directory.Delete(_information.OutPutDirectory, true); + Directory.CreateDirectory(_information.OutPutDirectory); /* * If no test driver files are available, you can run the following command to export all installed driver files. * (1) dism /online /export-driver /destination:"D:\packet\cache\" * (2) pnputil /export-driver * D:\packet\cache * * The following code example exports the specified driver to the specified directory. - * pnputil /export-driver oem14.inf D:\packet\cache + * pnputil /export-driver oemXX.inf D:\packet\cache */ var command = new StringBuilder("/c pnputil /export-driver ") .Append(driver) .Append(' ') - .Append(outPutDirectory) + .Append(_information.OutPutDirectory) .ToString(); CommandExecutor.ExecuteCommand(command); diff --git a/src/c#/GeneralUpdate.Core/Driver/CommandExecutor.cs b/src/c#/GeneralUpdate.Core/Driver/CommandExecutor.cs index c7c5bbf5..b4d690a6 100644 --- a/src/c#/GeneralUpdate.Core/Driver/CommandExecutor.cs +++ b/src/c#/GeneralUpdate.Core/Driver/CommandExecutor.cs @@ -32,14 +32,20 @@ Update the driver regularly to ensure that the driver is compatible with the cur Verb = "runas" }; - using (var process = new Process { StartInfo = processStartInfo }) + var process = new Process(); + try { + process.StartInfo = processStartInfo; process.Start(); process.WaitForExit(); if (process.ExitCode != 0) throw new ApplicationException($"Operation failed code: {process.ExitCode}"); } + finally + { + process.Dispose(); + } } } } \ No newline at end of file diff --git a/src/c#/GeneralUpdate.Core/Driver/DriverInformation.cs b/src/c#/GeneralUpdate.Core/Driver/DriverInformation.cs index 6d1db4ab..95e045dd 100644 --- a/src/c#/GeneralUpdate.Core/Driver/DriverInformation.cs +++ b/src/c#/GeneralUpdate.Core/Driver/DriverInformation.cs @@ -1,6 +1,8 @@ using System; using System.Collections.Generic; +using System.IO; using System.Linq; +using GeneralUpdate.Common; namespace GeneralUpdate.Core.Driver { @@ -9,11 +11,8 @@ namespace GeneralUpdate.Core.Driver /// public class DriverInformation { - /// - /// Directory for storing the driver to be installed (Update the driver file in the package). - /// - public string InstallDirectory { get; private set; } - + public string DriverFileExtension { get; private set; } + /// /// All driver backup directories. /// @@ -22,21 +21,21 @@ public class DriverInformation /// /// A collection of driver files to be backed up. /// - public List Drivers { get; private set; } + public IEnumerable Drivers { get; private set; } private DriverInformation() { } - + public class Builder { private DriverInformation _information = new DriverInformation(); - public Builder SetInstallDirectory(string installDirectory) + public Builder SetDriverFileExtension(string fileExtension) { - _information.InstallDirectory = installDirectory; + _information.DriverFileExtension = fileExtension; return this; } - + public Builder SetOutPutDirectory(string outPutDirectory) { _information.OutPutDirectory = outPutDirectory; @@ -48,16 +47,19 @@ public Builder SetOutPutDirectory(string outPutDirectory) /// /// /// - public Builder SetDriverNames(List driverNames) + public Builder SetDrivers(string driversPath, string fileExtension) { - _information.Drivers = driverNames; + if(string.IsNullOrWhiteSpace(driversPath) || string.IsNullOrWhiteSpace(fileExtension)) + return this; + + _information.Drivers = SearchDrivers(driversPath, fileExtension); return this; } public DriverInformation Build() { - if (string.IsNullOrWhiteSpace(_information.InstallDirectory) || - string.IsNullOrWhiteSpace(_information.OutPutDirectory) || + if (string.IsNullOrWhiteSpace(_information.OutPutDirectory) || + string.IsNullOrWhiteSpace(_information.DriverFileExtension) || !_information.Drivers.Any()) { throw new ArgumentNullException("Cannot create DriverInformation, not all fields are set."); @@ -65,6 +67,17 @@ public DriverInformation Build() return _information; } + + /// + /// Search for driver files. + /// + /// + /// + private IEnumerable SearchDrivers(string patchPath, string fileExtension) + { + var files = GeneralFileManager.GetAllfiles(patchPath); + return files.Where(x => x.FullName.EndsWith(fileExtension)).ToList(); + } } } } \ No newline at end of file diff --git a/src/c#/GeneralUpdate.Core/Driver/DriverProcessor.cs b/src/c#/GeneralUpdate.Core/Driver/DriverProcessor.cs index 8923af2f..af831c0b 100644 --- a/src/c#/GeneralUpdate.Core/Driver/DriverProcessor.cs +++ b/src/c#/GeneralUpdate.Core/Driver/DriverProcessor.cs @@ -8,12 +8,9 @@ namespace GeneralUpdate.Core.Driver /// public class DriverProcessor { - private readonly List _commands = new List(); + private readonly List _commands = new (); - public void AddCommand(IDriverCommand command) - { - _commands.Add(command); - } + public void AddCommand(IDriverCommand command) => _commands.Add(command); /// /// Execute all driver-related commands. diff --git a/src/c#/GeneralUpdate.Core/Driver/InstallDriverCommand.cs b/src/c#/GeneralUpdate.Core/Driver/InstallDriverCommand.cs index c4bb448f..c6f3895d 100644 --- a/src/c#/GeneralUpdate.Core/Driver/InstallDriverCommand.cs +++ b/src/c#/GeneralUpdate.Core/Driver/InstallDriverCommand.cs @@ -26,9 +26,8 @@ public void Execute() * (On Windows, an ExitCode value of 259 (STILL_ACTIVE) means that the process is still running) * If you do not remove the previous installation 259 prompt will give you a misleading impression of what is running. */ - var path = Path.Combine(_information.InstallDirectory, Path.GetFileNameWithoutExtension(driver), driver); var command = new StringBuilder("/c pnputil /add-driver ") - .Append(path) + .Append(driver.FullName) .Append(" /install") .ToString(); CommandExecutor.ExecuteCommand(command); @@ -38,8 +37,7 @@ public void Execute() { //restore all the drivers in the backup directory. new RestoreDriverCommand(_information).Execute(); - throw new ApplicationException( - $"Failed to execute install command for {_information.InstallDirectory}"); + throw new ApplicationException("Failed to execute install command !"); } } } diff --git a/src/c#/GeneralUpdate.Core/Driver/RestoreDriverCommand.cs b/src/c#/GeneralUpdate.Core/Driver/RestoreDriverCommand.cs index e6a135d4..3d901355 100644 --- a/src/c#/GeneralUpdate.Core/Driver/RestoreDriverCommand.cs +++ b/src/c#/GeneralUpdate.Core/Driver/RestoreDriverCommand.cs @@ -1,6 +1,7 @@ using System; -using System.IO; +using System.Linq; using System.Text; +using GeneralUpdate.Common; namespace GeneralUpdate.Core.Driver { @@ -14,17 +15,28 @@ public void Execute() { try { - foreach (var driver in _information.Drivers) + var backupFiles = GeneralFileManager.GetAllfiles(_information.OutPutDirectory); + var fileExtension = _information.DriverFileExtension; + var drivers = backupFiles.Where(x => x.FullName.EndsWith(fileExtension)).ToList(); + + foreach (var driver in drivers) { - //Install all drivers in the specified directory, and if the installation fails, restore all the drivers in the backup directory. - var command = new StringBuilder("/c pnputil /add-driver ") - .Append(Path.Combine(_information.OutPutDirectory, Path.GetFileNameWithoutExtension(driver), driver)) - .Append(" /install") - .ToString(); - CommandExecutor.ExecuteCommand(command); + try + { + //Install all drivers in the specified directory, and if the installation fails, restore all the drivers in the backup directory. + var command = new StringBuilder("/c pnputil /add-driver ") + .Append(driver.FullName) + .Append(" /install") + .ToString(); + CommandExecutor.ExecuteCommand(command); + } + catch (Exception e) + { + throw new ApplicationException($"Failed to execute install command for {driver.FullName}, error: {e.Message} !"); + } } } - catch (Exception ex) + catch { throw new ApplicationException($"Failed to execute restore command for {_information.OutPutDirectory}"); } diff --git a/src/c#/GeneralUpdate.Core/GeneralUpdateBootstrap.cs b/src/c#/GeneralUpdate.Core/GeneralUpdateBootstrap.cs index 2139ea6f..5437a2fb 100644 --- a/src/c#/GeneralUpdate.Core/GeneralUpdateBootstrap.cs +++ b/src/c#/GeneralUpdate.Core/GeneralUpdateBootstrap.cs @@ -1,35 +1,53 @@ using System; -using System.Diagnostics.Contracts; +using System.Diagnostics; using System.IO; using System.Runtime.InteropServices; +using System.Text; using System.Text.Json; using System.Threading.Tasks; using GeneralUpdate.Common; using GeneralUpdate.Common.Download; +using GeneralUpdate.Common.Internal; using GeneralUpdate.Common.Internal.Bootstrap; using GeneralUpdate.Common.Internal.Event; using GeneralUpdate.Common.Internal.Strategy; using GeneralUpdate.Common.Shared.Object; -using GeneralUpdate.Core.Internal; using GeneralUpdate.Core.Strategys; namespace GeneralUpdate.Core { public class GeneralUpdateBootstrap : AbstractBootstrap { - private Packet Packet { get; set; } + private readonly GlobalConfigInfo _configInfo; + private IStrategy? _strategy; - private IStrategy _strategy; - - public GeneralUpdateBootstrap() : base() + public GeneralUpdateBootstrap() { try { //Gets values from system environment variables (ClientParameter object to base64 string). var json = Environment.GetEnvironmentVariable("ProcessInfo", EnvironmentVariableTarget.User); + if (string.IsNullOrWhiteSpace(json)) + throw new ArgumentException("ProcessInfo object cannot be null!"); + var processInfo = JsonSerializer.Deserialize(json); - Packet.AppType = AppType.UpgradeApp; - Packet.TempPath = $"{GeneralFileManager.GetTempDirectory(processInfo.LastVersion)}{Path.DirectorySeparatorChar}"; + if (processInfo == null) + throw new ArgumentException("ProcessInfo object cannot be null!"); + + _configInfo = new() + { + MainAppName = processInfo.AppName, + InstallPath = processInfo.InstallPath, + ClientVersion = processInfo.CurrentVersion, + LastVersion = processInfo.LastVersion, + UpdateLogUrl = processInfo.UpdateLogUrl, + Encoding = ToEncoding(processInfo.CompressEncoding), + Format = processInfo.CompressFormat, + DownloadTimeOut = processInfo.DownloadTimeOut, + AppSecretKey = processInfo.AppSecretKey, + UpdateVersions = processInfo.UpdateVersions, + TempPath = $"{GeneralFileManager.GetTempDirectory(processInfo.LastVersion)}{Path.DirectorySeparatorChar}" + }; } catch (Exception ex) { @@ -39,11 +57,16 @@ public GeneralUpdateBootstrap() : base() public override async Task LaunchAsync() { - var manager = new DownloadManager(Packet.InstallPath, Packet.Format, 30); - - foreach (var versionInfo in Packet.UpdateVersions) + var manager = new DownloadManager(_configInfo.TempPath, _configInfo.Format, _configInfo.DownloadTimeOut); + manager.MultiAllDownloadCompleted += OnMultiAllDownloadCompleted; + manager.MultiDownloadCompleted += OnMultiDownloadCompleted; + manager.MultiDownloadError += OnMultiDownloadError; + manager.MultiDownloadProgressChanged += OnMultiDownloadProgressChanged; + manager.MultiDownloadStatistics += OnMultiDownloadStatistics; + foreach (var versionInfo in _configInfo.UpdateVersions) + { manager.Add(new DownloadTask(manager, versionInfo)); - + } await manager.LaunchTasksAsync(); return this; } @@ -77,8 +100,8 @@ public GeneralUpdateBootstrap AddListenerException(Action(Action callbackAction) where TArgs : EventArgs { - Contract.Requires(callbackAction != null); + Debug.Assert(callbackAction!= null); EventManager.Instance.AddListener(callbackAction); return this; } @@ -117,5 +140,17 @@ private void OnMultiAllDownloadCompleted(object sender, MultiAllDownloadComplete EventManager.Instance.Dispatch(sender, e); ExecuteStrategy(); } + + private static Encoding ToEncoding(int encodingType) => encodingType switch + { + 1 => Encoding.UTF8, + 2 => Encoding.UTF7, + 3 => Encoding.UTF32, + 4 => Encoding.Unicode, + 5 => Encoding.BigEndianUnicode, + 6 => Encoding.ASCII, + 7 => Encoding.Default, + _ => throw new ArgumentException("Encoding type is not supported!") + }; } } \ No newline at end of file diff --git a/src/c#/GeneralUpdate.Core/GeneralUpdateOSS.cs b/src/c#/GeneralUpdate.Core/GeneralUpdateOSS.cs index 77a1378c..b9df255c 100644 --- a/src/c#/GeneralUpdate.Core/GeneralUpdateOSS.cs +++ b/src/c#/GeneralUpdate.Core/GeneralUpdateOSS.cs @@ -1,9 +1,9 @@ using System; using System.Diagnostics.Contracts; using System.Runtime.InteropServices; -using System.Text; using System.Threading.Tasks; using GeneralUpdate.Common.Download; +using GeneralUpdate.Common.Internal; using GeneralUpdate.Common.Internal.Event; using GeneralUpdate.Common.Internal.Strategy; using GeneralUpdate.Common.Shared.Object; diff --git a/src/c#/GeneralUpdate.Core/Pipeline/DriverMiddleware.cs b/src/c#/GeneralUpdate.Core/Pipeline/DriverMiddleware.cs index d7a4595c..c97b8a69 100644 --- a/src/c#/GeneralUpdate.Core/Pipeline/DriverMiddleware.cs +++ b/src/c#/GeneralUpdate.Core/Pipeline/DriverMiddleware.cs @@ -1,31 +1,43 @@ -using System.Collections.Generic; +using System; +using System.Collections.Generic; using System.IO; +using System.Linq; +using System.Runtime.InteropServices; using System.Threading.Tasks; +using GeneralUpdate.Common; using GeneralUpdate.Common.Internal.Pipeline; using GeneralUpdate.Core.Driver; namespace GeneralUpdate.Core.Pipeline; +/// +/// Driver update. +/// Use for Windows Vista/Windows 7/Windows 8/Windows 8.1/Windows 10/Windows 11/Windows Server 2008. +/// public class DriverMiddleware : IMiddleware { + private const string FileExtension = ".inf"; + public Task InvokeAsync(PipelineContext context) { return Task.Run(() => { - var drivers = context.Get>("Drivers"); - var sourcesPath = context.Get("Sources"); - var targetPath = context.Get("Target"); - var version = context.Get("Version"); - + var patchPath = context.Get("PatchPath"); + if(string.IsNullOrWhiteSpace(patchPath)) + return; + + var outPutPath = context.Get("DriverOutPut"); + if(string.IsNullOrWhiteSpace(outPutPath)) + return; + var information = new DriverInformation.Builder() - .SetInstallDirectory(Path.Combine(sourcesPath, version)) - .SetOutPutDirectory(Path.Combine(targetPath, version)) - .SetDriverNames(drivers) + .SetDriverFileExtension(FileExtension) + .SetOutPutDirectory(outPutPath) + .SetDrivers(patchPath, FileExtension) .Build(); var processor = new DriverProcessor(); processor.AddCommand(new BackupDriverCommand(information)); - processor.AddCommand(new DeleteDriverCommand(information)); processor.AddCommand(new InstallDriverCommand(information)); processor.ProcessCommands(); }); diff --git a/src/c#/GeneralUpdate.Core/Pipeline/HashMiddleware.cs b/src/c#/GeneralUpdate.Core/Pipeline/HashMiddleware.cs index 1fd378dc..90e2f1bc 100644 --- a/src/c#/GeneralUpdate.Core/Pipeline/HashMiddleware.cs +++ b/src/c#/GeneralUpdate.Core/Pipeline/HashMiddleware.cs @@ -10,19 +10,18 @@ public class HashMiddleware : IMiddleware { public async Task InvokeAsync(PipelineContext context) { - var fileName = context.Get("FileName"); + var path = context.Get("ZipFilePath"); var hash = context.Get("Hash"); - - var isVerify = await VerifyFileHash(fileName, hash); + var isVerify = await VerifyFileHash(path, hash); if (!isVerify) throw new CryptographicException("Hash verification failed ."); } - private Task VerifyFileHash(string fileName, string hash) + private Task VerifyFileHash(string path, string hash) { return Task.Run(() => { var hashAlgorithm = new Sha256HashAlgorithm(); - var hashSha256 = hashAlgorithm.ComputeHash(fileName); + var hashSha256 = hashAlgorithm.ComputeHash(path); return string.Equals(hash, hashSha256, StringComparison.OrdinalIgnoreCase); }); } diff --git a/src/c#/GeneralUpdate.Core/Pipeline/PatchMiddleware.cs b/src/c#/GeneralUpdate.Core/Pipeline/PatchMiddleware.cs index 2a6e5b6d..6f9807b1 100644 --- a/src/c#/GeneralUpdate.Core/Pipeline/PatchMiddleware.cs +++ b/src/c#/GeneralUpdate.Core/Pipeline/PatchMiddleware.cs @@ -1,5 +1,6 @@ using System.Collections.Generic; using System.Threading.Tasks; +using GeneralUpdate.Common; using GeneralUpdate.Common.Internal.Pipeline; using GeneralUpdate.Differential; @@ -10,11 +11,12 @@ public class PatchMiddleware : IMiddleware public async Task InvokeAsync(PipelineContext context) { var sourcePath = context.Get("SourcePath"); - var targetPath = context.Get("TargetPath"); + var targetPath = context.Get("PatchPath"); var blackFiles = context.Get>("BlackFiles"); var blackFileFormats = context.Get>("BlackFileFormats"); - DifferentialCore.Instance.SetBlocklist(blackFiles, blackFileFormats); - await DifferentialCore.Instance.Dirty(sourcePath, targetPath); + BlackListManager.Instance.AddBlackFiles(blackFiles); + BlackListManager.Instance.AddBlackFileFormats(blackFileFormats); + await DifferentialCore.Instance?.Dirty(sourcePath, targetPath); } } \ No newline at end of file diff --git a/src/c#/GeneralUpdate.Core/Pipeline/ZipMiddleware.cs b/src/c#/GeneralUpdate.Core/Pipeline/ZipMiddleware.cs index 12d14d64..5c82b1f5 100644 --- a/src/c#/GeneralUpdate.Core/Pipeline/ZipMiddleware.cs +++ b/src/c#/GeneralUpdate.Core/Pipeline/ZipMiddleware.cs @@ -1,5 +1,8 @@ -using System.Text; +using System; +using System.Text; using System.Threading.Tasks; +using GeneralUpdate.Common.Internal; +using GeneralUpdate.Common.Internal.Event; using GeneralUpdate.Common.Internal.Pipeline; using GeneralUpdate.Zip; using GeneralUpdate.Zip.Factory; @@ -12,17 +15,24 @@ public Task InvokeAsync(PipelineContext context) { return Task.Run(() => { - var type = MatchType(context.Get("Format")); - var name = context.Get("Name"); - var sourcePath = context.Get("SourcePath"); - var destinationPath = context.Get("DestinationPath"); - var encoding = context.Get("Encoding"); + try + { + var type = MatchType(context.Get("Format")); + var name = context.Get("Name"); + var sourcePath = context.Get("ZipFilePath"); + var destinationPath = context.Get("PatchPath"); + var encoding = context.Get("Encoding"); - var generalZipfactory = new GeneralZipFactory(); - generalZipfactory.CompressProgress += (sender, args) => { }; - generalZipfactory.Completed += (sender, args) => { }; - generalZipfactory.CreateOperate(type, name, sourcePath, destinationPath, true, encoding); - generalZipfactory.UnZip(); + var generalZipfactory = new GeneralZipFactory(); + generalZipfactory.CompressProgress += (sender, args) => { }; + generalZipfactory.Completed += (sender, args) => { }; + generalZipfactory.CreateOperate(type, name, sourcePath, destinationPath, true, encoding); + generalZipfactory.UnZip(); + } + catch (Exception e) + { + EventManager.Instance.Dispatch(this, new ExceptionEventArgs(e, e.Message)); + } }); } diff --git a/src/c#/GeneralUpdate.Core/Strategys/OSSStrategy.cs b/src/c#/GeneralUpdate.Core/Strategys/OSSStrategy.cs index ac28519d..9094cc77 100644 --- a/src/c#/GeneralUpdate.Core/Strategys/OSSStrategy.cs +++ b/src/c#/GeneralUpdate.Core/Strategys/OSSStrategy.cs @@ -8,10 +8,10 @@ using System.Threading.Tasks; using GeneralUpdate.Common; using GeneralUpdate.Common.Download; +using GeneralUpdate.Common.Internal; using GeneralUpdate.Common.Internal.Event; using GeneralUpdate.Common.Internal.Strategy; using GeneralUpdate.Common.Shared.Object; -using GeneralUpdate.Core.Internal; namespace GeneralUpdate.Core.Strategys { @@ -22,14 +22,14 @@ public sealed class OSSStrategy : AbstractStrategy private readonly string _appPath = AppDomain.CurrentDomain.BaseDirectory; private const string _format = ".zip"; private const int _timeOut = 60; - private Packet _parameter; + private GlobalConfigInfo _parameter; private Encoding _encoding; #endregion Private Members #region Public Methods - public override void Create(Packet parameter) + public override void Create(GlobalConfigInfo parameter) { _parameter = parameter; } diff --git a/src/c#/GeneralUpdate.Core/Strategys/WindowsStrategy.cs b/src/c#/GeneralUpdate.Core/Strategys/WindowsStrategy.cs index c3e910f6..0ad98412 100644 --- a/src/c#/GeneralUpdate.Core/Strategys/WindowsStrategy.cs +++ b/src/c#/GeneralUpdate.Core/Strategys/WindowsStrategy.cs @@ -2,12 +2,14 @@ using System.Collections.Generic; using System.Diagnostics; using System.IO; -using System.Linq; using System.Threading.Tasks; using GeneralUpdate.Common; +using GeneralUpdate.Common.Internal; +using GeneralUpdate.Common.Internal.Event; using GeneralUpdate.Common.Internal.Pipeline; using GeneralUpdate.Common.Internal.Strategy; using GeneralUpdate.Common.Shared.Object; +using GeneralUpdate.Common.Shared.Service; using GeneralUpdate.Core.Pipeline; namespace GeneralUpdate.Core.Strategys @@ -17,96 +19,79 @@ namespace GeneralUpdate.Core.Strategys /// public class WindowsStrategy : AbstractStrategy { - private Packet Packet { get; set; } + private GlobalConfigInfo _configinfo = new(); - #region Private Methods + public override void Create(GlobalConfigInfo parameter) => _configinfo = parameter; - /// - /// Remove update redundant files. - /// - /// - private void Clear() + public override void Execute() { - if (File.Exists(Packet.TempPath)) File.Delete(Packet.TempPath); - var dirPath = Path.GetDirectoryName(Packet.TempPath); - if (Directory.Exists(dirPath)) Directory.Delete(dirPath, true); - } - - #endregion Private Methods - - #region Public Methods - - public override void Create(Packet parameter) - { - Packet = parameter; - } - - public override async Task ExecuteAsync() - { - var updateVersions = Packet.UpdateVersions.OrderBy(x => x.PubTime).ToList(); - if (updateVersions.Count > 0) + Task.Run(async () => { - foreach (var version in updateVersions) + try { + var status = 0; var patchPath = GeneralFileManager.GetTempDirectory(PATCHS); - var zipFilePath = Path.Combine(Packet.TempPath, $"{version.Name}{Packet.Format}"); + foreach (var version in _configinfo.UpdateVersions) + { + try + { + var context = new PipelineContext(); + //Common + context.Add("ZipFilePath", + Path.Combine(_configinfo.TempPath, $"{version.Name}{_configinfo.Format}")); + //Hash middleware + context.Add("Hash", version.Hash); + //Zip middleware + context.Add("Format", _configinfo.Format); + context.Add("Name", version.Name); + context.Add("Encoding", _configinfo.Encoding); + //Patch middleware + context.Add("SourcePath", _configinfo.InstallPath); + context.Add("PatchPath", patchPath); + context.Add("BlackFiles", BlackListManager.Instance.BlackFiles); + context.Add("BlackFileFormats", BlackListManager.Instance.BlackFileFormats); + //Driver middleware + context.Add("DriverOutPut", GeneralFileManager.GetTempDirectory("DriverOutPut")); - var context = new PipelineContext(); - //hash middleware - context.Add("Hash", version.Hash); - context.Add("FileName", zipFilePath); - //zip middleware - context.Add("Format", Packet.Format); - context.Add("Name", zipFilePath); - context.Add("SourcePath", Packet.TempPath); - context.Add("DestinationPath", Packet.InstallPath); - context.Add("Encoding", Packet.Encoding); - //patch middleware - context.Add("SourcePath", patchPath); - context.Add("TargetPath", Packet.InstallPath); - context.Add("BlackFiles", BlackListManager.Instance.BlackFiles); - context.Add("BlackFileFormats", BlackListManager.Instance.BlackFileFormats); - //driver middleware - context.Add("DriverPath", new List()); - context.Add("Version", version.Version); - + var pipelineBuilder = new PipelineBuilder(context) + .UseMiddleware() + .UseMiddleware() + .UseMiddleware() + .UseMiddlewareIf(_configinfo.DriveEnabled); + await pipelineBuilder.Build(); + } + catch (Exception e) + { + status = 3; + EventManager.Instance.Dispatch(this, new ExceptionEventArgs(e, e.Message)); + } + finally + { + await VersionService.Report(_configinfo.ReportUrl, version.RecordId, status, + version.AppType); + } + } - var pipelineBuilder = new PipelineBuilder(context) - .UseMiddleware() - .UseMiddleware() - .UseMiddleware() - .UseMiddlewareIf(()=> Packet.DriveEnabled); - await pipelineBuilder.Build(); - } + if (!string.IsNullOrEmpty(_configinfo.UpdateLogUrl)) + { + OpenBrowser(_configinfo.UpdateLogUrl); + } - if (!string.IsNullOrEmpty(Packet.UpdateLogUrl)) - OpenBrowser(Packet.UpdateLogUrl); - } - - Clear(); - StartApp(Packet.AppName, Packet.AppType); + Clear(patchPath); + Clear(_configinfo.TempPath); + StartApp(_configinfo.AppName); + } + catch (Exception e) + { + EventManager.Instance.Dispatch(this, new ExceptionEventArgs(e, e.Message)); + } + }); } - public override void StartApp(string appName, int appType) + public override void StartApp(string appName) { - var path = Path.Combine(Packet.InstallPath, appName); - switch (appType) - { - case AppType.ClientApp: - Environment.SetEnvironmentVariable("ProcessInfo", Packet.ProcessInfo, - EnvironmentVariableTarget.User); - Process.Start(path); - break; - - case AppType.UpgradeApp: - Process.Start(path); - break; - - default: - throw new ArgumentException("Invalid app type"); - } + var path = Path.Combine(_configinfo.InstallPath, appName); + Process.Start(path); } - - #endregion Public Methods } } \ No newline at end of file diff --git a/src/c#/GeneralUpdate.Differential/Binary/BinaryHandler.cs b/src/c#/GeneralUpdate.Differential/Binary/BinaryHandler.cs index cddc3eef..7938f222 100644 --- a/src/c#/GeneralUpdate.Differential/Binary/BinaryHandler.cs +++ b/src/c#/GeneralUpdate.Differential/Binary/BinaryHandler.cs @@ -11,9 +11,9 @@ public class BinaryHandler { #region Private Members - private const long FileSignature = 0x3034464649445342L; - private const int HeaderSize = 32; - private string _oldFilePath, _newFilePath, _patchPath; + private const long c_fileSignature = 0x3034464649445342L; + private const int c_headerSize = 32; + private string _oldfilePath, _newfilePath, _patchPath; #endregion Private Members @@ -22,346 +22,375 @@ public class BinaryHandler /// /// Clean out the files that need to be updated and generate the update package. /// - /// Old version file path. - /// New version file path + /// Old version file path. + /// New version file path /// Patch file generation path. /// /// - public async Task Clean(string oldFilePath, string newFilePath, string patchPath) + public async Task Clean(string oldfilePath, string newfilePath, string patchPath) { - _oldFilePath = oldFilePath; - _newFilePath = newFilePath; - _patchPath = patchPath; - ValidateParameters(); - - try - { - await Task.Run(() => GeneratePatch()); - } - catch (Exception ex) - { - throw new Exception($"Clean error: {ex.Message}", ex.InnerException); - } - } - - /// - /// Update the patch file to the client application. - /// - /// Old version file path. - /// New version file path - /// Patch file path. - /// - /// - /// - public async Task Dirty(string oldFilePath, string newFilePath, string patchPath) - { - _oldFilePath = oldFilePath; - _newFilePath = newFilePath; - _patchPath = patchPath; - ValidateParameters(); - - await Task.Run(() => ApplyPatch()); - } - - #endregion Public Methods - - #region Private Methods - - private void ValidateParameters() - { - if (string.IsNullOrWhiteSpace(_oldFilePath)) throw new ArgumentNullException(nameof(_oldFilePath), "This parameter cannot be empty."); - if (string.IsNullOrWhiteSpace(_newFilePath)) throw new ArgumentNullException(nameof(_newFilePath), "This parameter cannot be empty."); - if (string.IsNullOrWhiteSpace(_patchPath)) throw new ArgumentNullException(nameof(_patchPath), "This parameter cannot be empty."); - } - - private void GeneratePatch() - { - using (FileStream output = new FileStream(_patchPath, FileMode.Create)) + await Task.Run(() => { - var oldBytes = File.ReadAllBytes(_oldFilePath); - var newBytes = File.ReadAllBytes(_newFilePath); - - byte[] header = new byte[HeaderSize]; - WriteInt64(FileSignature, header, 0); // "BSDIFF40" - WriteInt64(0, header, 8); - WriteInt64(0, header, 16); - WriteInt64(newBytes.Length, header, 24); - - long startPosition = output.Position; - output.Write(header, 0, header.Length); - - int[] suffixArray = SuffixSort(oldBytes); - - byte[] diffBytes = new byte[newBytes.Length]; - byte[] extraBytes = new byte[newBytes.Length]; - - int diffLength = 0; - int extraLength = 0; - - using (var bz2Stream = new BZip2OutputStream(output) { IsStreamOwner = false }) + try { - ComputeDifferences(oldBytes, newBytes, suffixArray, diffBytes, extraBytes, ref diffLength, ref extraLength, bz2Stream); - } + _oldfilePath = oldfilePath; + _newfilePath = newfilePath; + _patchPath = patchPath; + ValidationParameters(); - long controlEndPosition = output.Position; - WriteInt64(controlEndPosition - startPosition - HeaderSize, header, 8); - - using (var bz2Stream = new BZip2OutputStream(output) { IsStreamOwner = false }) - { - bz2Stream.Write(diffBytes, 0, diffLength); - } + using (FileStream output = new FileStream(patchPath, FileMode.Create)) + { + var oldBytes = File.ReadAllBytes(_oldfilePath); + var newBytes = File.ReadAllBytes(_newfilePath); + + /* Header is + 0 8 "BSDIFF40" + 8 8 length of bzip2ed ctrl block + 16 8 length of bzip2ed diff block + 24 8 length of new file */ + /* File is + 0 32 Header + 32 ?? Bzip2ed ctrl block + ?? ?? Bzip2ed diff block + ?? ?? Bzip2ed extra block */ + byte[] header = new byte[c_headerSize]; + WriteInt64(c_fileSignature, header, 0); // "BSDIFF40" + WriteInt64(0, header, 8); + WriteInt64(0, header, 16); + WriteInt64(newBytes.Length, header, 24); + + long startPosition = output.Position; + output.Write(header, 0, header.Length); + + int[] I = SuffixSort(oldBytes); + + byte[] db = new byte[newBytes.Length]; + byte[] eb = new byte[newBytes.Length]; + + int dblen = 0; + int eblen = 0; + + using (var bz2Stream = new BZip2OutputStream(output) { IsStreamOwner = false }) + { + // compute the differences, writing ctrl as we go + int scan = 0; + int pos = 0; + int len = 0; + int lastscan = 0; + int lastpos = 0; + int lastoffset = 0; + while (scan < newBytes.Length) + { + int oldscore = 0; - long diffEndPosition = output.Position; - WriteInt64(diffEndPosition - controlEndPosition, header, 16); + for (int scsc = scan += len; scan < newBytes.Length; scan++) + { + len = Search(I, oldBytes, newBytes, scan, 0, oldBytes.Length, out pos); - using (var bz2Stream = new BZip2OutputStream(output) { IsStreamOwner = false }) - { - bz2Stream.Write(extraBytes, 0, extraLength); - } + for (; scsc < scan + len; scsc++) + { + if ((scsc + lastoffset < oldBytes.Length) && + (oldBytes[scsc + lastoffset] == newBytes[scsc])) + oldscore++; + } - long endPosition = output.Position; - output.Position = startPosition; - output.Write(header, 0, header.Length); - output.Position = endPosition; - } - } + if ((len == oldscore && len != 0) || (len > oldscore + 8)) + break; - private void ComputeDifferences(byte[] oldBytes, byte[] newBytes, int[] suffixArray, byte[] diffBytes, byte[] extraBytes, ref int diffLength, ref int extraLength, BZip2OutputStream bz2Stream) - { - int scan = 0; - int pos = 0; - int len = 0; - int lastScan = 0; - int lastPos = 0; - int lastOffset = 0; - - while (scan < newBytes.Length) - { - int oldScore = 0; - - for (int scsc = scan += len; scan < newBytes.Length; scan++) - { - len = Search(suffixArray, oldBytes, newBytes, scan, 0, oldBytes.Length, out pos); - - for (; scsc < scan + len; scsc++) - { - if ((scsc + lastOffset < oldBytes.Length) && (oldBytes[scsc + lastOffset] == newBytes[scsc])) - oldScore++; - } + if ((scan + lastoffset < oldBytes.Length) && + (oldBytes[scan + lastoffset] == newBytes[scan])) + oldscore--; + } - if ((len == oldScore && len != 0) || (len > oldScore + 8)) - break; + if (len != oldscore || scan == newBytes.Length) + { + int s = 0; + int sf = 0; + int lenf = 0; + for (int i = 0; (lastscan + i < scan) && (lastpos + i < oldBytes.Length);) + { + if (oldBytes[lastpos + i] == newBytes[lastscan + i]) + s++; + i++; + if (s * 2 - i > sf * 2 - lenf) + { + sf = s; + lenf = i; + } + } + + int lenb = 0; + if (scan < newBytes.Length) + { + s = 0; + int sb = 0; + for (int i = 1; (scan >= lastscan + i) && (pos >= i); i++) + { + if (oldBytes[pos - i] == newBytes[scan - i]) + s++; + if (s * 2 - i > sb * 2 - lenb) + { + sb = s; + lenb = i; + } + } + } + + if (lastscan + lenf > scan - lenb) + { + int overlap = (lastscan + lenf) - (scan - lenb); + s = 0; + int ss = 0; + int lens = 0; + for (int i = 0; i < overlap; i++) + { + if (newBytes[lastscan + lenf - overlap + i] == + oldBytes[lastpos + lenf - overlap + i]) + s++; + if (newBytes[scan - lenb + i] == oldBytes[pos - lenb + i]) + s--; + if (s > ss) + { + ss = s; + lens = i + 1; + } + } + + lenf += lens - overlap; + lenb -= lens; + } + + for (int i = 0; i < lenf; i++) + db[dblen + i] = (byte)(newBytes[lastscan + i] - oldBytes[lastpos + i]); + for (int i = 0; i < (scan - lenb) - (lastscan + lenf); i++) + eb[eblen + i] = newBytes[lastscan + lenf + i]; + + dblen += lenf; + eblen += (scan - lenb) - (lastscan + lenf); + + byte[] buf = new byte[8]; + WriteInt64(lenf, buf, 0); + bz2Stream.Write(buf, 0, 8); + + WriteInt64((scan - lenb) - (lastscan + lenf), buf, 0); + bz2Stream.Write(buf, 0, 8); + + WriteInt64((pos - lenb) - (lastpos + lenf), buf, 0); + bz2Stream.Write(buf, 0, 8); + + lastscan = scan - lenb; + lastpos = pos - lenb; + lastoffset = pos - scan; + } + } + } - if ((scan + lastOffset < oldBytes.Length) && (oldBytes[scan + lastOffset] == newBytes[scan])) - oldScore--; - } + // compute size of compressed ctrl data + long controlEndPosition = output.Position; + WriteInt64(controlEndPosition - startPosition - c_headerSize, header, 8); - if (len != oldScore || scan == newBytes.Length) - { - int s = 0; - int sf = 0; - int lenf = 0; - for (int i = 0; (lastScan + i < scan) && (lastPos + i < oldBytes.Length);) - { - if (oldBytes[lastPos + i] == newBytes[lastScan + i]) - s++; - i++; - if (s * 2 - i > sf * 2 - lenf) + // write compressed diff data + using (var bz2Stream = new BZip2OutputStream(output) { IsStreamOwner = false }) { - sf = s; - lenf = i; + bz2Stream.Write(db, 0, dblen); } - } - int lenb = 0; - if (scan < newBytes.Length) - { - s = 0; - int sb = 0; - for (int i = 1; (scan >= lastScan + i) && (pos >= i); i++) - { - if (oldBytes[pos - i] == newBytes[scan - i]) - s++; - if (s * 2 - i > sb * 2 - lenb) - { - sb = s; - lenb = i; - } - } - } + // compute size of compressed diff data + long diffEndPosition = output.Position; + WriteInt64(diffEndPosition - controlEndPosition, header, 16); - if (lastScan + lenf > scan - lenb) - { - int overlap = (lastScan + lenf) - (scan - lenb); - s = 0; - int ss = 0; - int lens = 0; - for (int i = 0; i < overlap; i++) + // write compressed extra data + using (var bz2Stream = new BZip2OutputStream(output) { IsStreamOwner = false }) { - if (newBytes[lastScan + lenf - overlap + i] == oldBytes[lastPos + lenf - overlap + i]) - s++; - if (newBytes[scan - lenb + i] == oldBytes[pos - lenb + i]) - s--; - if (s > ss) - { - ss = s; - lens = i + 1; - } + bz2Stream.Write(eb, 0, eblen); } - lenf += lens - overlap; - lenb -= lens; + // seek to the beginning, write the header, then seek back to end + long endPosition = output.Position; + output.Position = startPosition; + output.Write(header, 0, header.Length); + output.Position = endPosition; } - - for (int i = 0; i < lenf; i++) - diffBytes[diffLength + i] = (byte)(newBytes[lastScan + i] - oldBytes[lastPos + i]); - for (int i = 0; i < (scan - lenb) - (lastScan + lenf); i++) - extraBytes[extraLength + i] = newBytes[lastScan + lenf + i]; - - diffLength += lenf; - extraLength += (scan - lenb) - (lastScan + lenf); - - byte[] buffer = new byte[8]; - WriteInt64(lenf, buffer, 0); - bz2Stream.Write(buffer, 0, 8); - - WriteInt64((scan - lenb) - (lastScan + lenf), buffer, 0); - bz2Stream.Write(buffer, 0, 8); - - WriteInt64((pos - lenb) - (lastPos + lenf), buffer, 0); - bz2Stream.Write(buffer, 0, 8); - - lastScan = scan - lenb; - lastPos = pos - lenb; - lastOffset = pos - scan; } - } + catch (Exception ex) + { + throw new Exception($"Clean error : {ex.Message} !", ex.InnerException); + } + }); } - private void ApplyPatch() + /// + /// Update the patch file to the client application. + /// + /// + /// + /// + /// + /// + /// + public async Task Dirty(string oldfilePath, string newfilePath, string patchPath) { - using (FileStream input = new FileStream(_oldFilePath, FileMode.Open, FileAccess.Read, FileShare.Read)) + await Task.Run(() => { - using (FileStream output = new FileStream(_newFilePath, FileMode.Create)) + try { - Func openPatchStream = () => new FileStream(_patchPath, FileMode.Open, FileAccess.Read, FileShare.Read); - - // Read header - long controlLength, diffLength, newSize; - using (Stream patchStream = openPatchStream()) - { - // Check patch stream capabilities - if (!patchStream.CanRead) - throw new ArgumentException("Patch stream must be readable.", nameof(openPatchStream)); - if (!patchStream.CanSeek) - throw new ArgumentException("Patch stream must be seekable.", nameof(openPatchStream)); - - byte[] header = ReadExactly(patchStream, HeaderSize); - - // Check for appropriate magic - long signature = ReadInt64(header, 0); - if (signature != FileSignature) - throw new InvalidOperationException("Corrupt patch."); - - // Read lengths from header - controlLength = ReadInt64(header, 8); - diffLength = ReadInt64(header, 16); - newSize = ReadInt64(header, 24); - if (controlLength < 0 || diffLength < 0 || newSize < 0) - throw new InvalidOperationException("Corrupt patch."); - } - - // Preallocate buffers for reading and writing - const int BufferSize = 1048576; - byte[] newData = new byte[BufferSize]; - byte[] oldData = new byte[BufferSize]; - - // Prepare to read three parts of the patch in parallel - using (Stream compressedControlStream = openPatchStream()) - using (Stream compressedDiffStream = openPatchStream()) - using (Stream compressedExtraStream = openPatchStream()) + _oldfilePath = oldfilePath; + _newfilePath = newfilePath; + _patchPath = patchPath; + ValidationParameters(); + using (FileStream input = + new FileStream(_oldfilePath, FileMode.Open, FileAccess.Read, FileShare.Read)) { - // Seek to the start of each part - compressedControlStream.Seek(HeaderSize, SeekOrigin.Current); - compressedDiffStream.Seek(HeaderSize + controlLength, SeekOrigin.Current); - compressedExtraStream.Seek(HeaderSize + controlLength + diffLength, SeekOrigin.Current); - - // Decompress each part (to read it) - using (BZip2InputStream controlStream = new BZip2InputStream(compressedControlStream)) - using (BZip2InputStream diffStream = new BZip2InputStream(compressedDiffStream)) - using (BZip2InputStream extraStream = new BZip2InputStream(compressedExtraStream)) + using (FileStream output = new FileStream(_newfilePath, FileMode.Create)) { - long[] control = new long[3]; - byte[] buffer = new byte[8]; - - int oldPosition = 0; - int newPosition = 0; - while (newPosition < newSize) + Func openPatchStream = () => + new FileStream(patchPath, FileMode.Open, FileAccess.Read, FileShare.Read); + //File format: + // 0 8 "BSDIFF40" + // 8 8 X + // 16 8 Y + // 24 8 sizeof(newfile) + // 32 X bzip2(control block) + // 32 + X Y bzip2(diff block) + // 32 + X + Y ??? bzip2(extra block) + //with control block a set of triples(x, y, z) meaning "add x bytes + //from oldfile to x bytes from the diff block; copy y bytes from the + //extra block; seek forwards in oldfile by z bytes". + // read header + long controlLength, diffLength, newSize; + using (Stream patchStream = openPatchStream()) { - // Read control data - for (int i = 0; i < 3; i++) - { - ReadExactly(controlStream, buffer, 0, 8); - control[i] = ReadInt64(buffer, 0); - } - - // Sanity-check - if (newPosition + control[0] > newSize) - throw new InvalidOperationException("Corrupt patch."); - - // Seek old file to the position that the new data is diffed against - input.Position = oldPosition; - - int bytesToCopy = (int)control[0]; - while (bytesToCopy > 0) - { - int actualBytesToCopy = Math.Min(bytesToCopy, BufferSize); + // check patch stream capabilities + if (!patchStream.CanRead) + throw new ArgumentException("Patch stream must be readable.", "openPatchStream"); + if (!patchStream.CanSeek) + throw new ArgumentException("Patch stream must be seekable.", "openPatchStream"); - // Read diff string - ReadExactly(diffStream, newData, 0, actualBytesToCopy); + byte[] header = ReadExactly(patchStream, c_headerSize); - // Add old data to diff string - int availableInputBytes = Math.Min(actualBytesToCopy, (int)(input.Length - input.Position)); - ReadExactly(input, oldData, 0, availableInputBytes); - - for (int index = 0; index < availableInputBytes; index++) - newData[index] += oldData[index]; - - output.Write(newData, 0, actualBytesToCopy); - - // Adjust counters - newPosition += actualBytesToCopy; - oldPosition += actualBytesToCopy; - bytesToCopy -= actualBytesToCopy; - } - - // Sanity-check - if (newPosition + control[1] > newSize) + // check for appropriate magic + long signature = ReadInt64(header, 0); + if (signature != c_fileSignature) throw new InvalidOperationException("Corrupt patch."); - // Read extra string - bytesToCopy = (int)control[1]; - while (bytesToCopy > 0) - { - int actualBytesToCopy = Math.Min(bytesToCopy, BufferSize); + // read lengths from header + controlLength = ReadInt64(header, 8); + diffLength = ReadInt64(header, 16); + newSize = ReadInt64(header, 24); + if (controlLength < 0 || diffLength < 0 || newSize < 0) + throw new InvalidOperationException("Corrupt patch."); + } - ReadExactly(extraStream, newData, 0, actualBytesToCopy); - output.Write(newData, 0, actualBytesToCopy); + // preallocate buffers for reading and writing + const int c_bufferSize = 1048576; + byte[] newData = new byte[c_bufferSize]; + byte[] oldData = new byte[c_bufferSize]; - newPosition += actualBytesToCopy; - bytesToCopy -= actualBytesToCopy; + // prepare to read three parts of the patch in parallel + using (Stream compressedControlStream = openPatchStream()) + using (Stream compressedDiffStream = openPatchStream()) + using (Stream compressedExtraStream = openPatchStream()) + { + // seek to the start of each part + compressedControlStream.Seek(c_headerSize, SeekOrigin.Current); + compressedDiffStream.Seek(c_headerSize + controlLength, SeekOrigin.Current); + compressedExtraStream.Seek(c_headerSize + controlLength + diffLength, + SeekOrigin.Current); + + // decompress each part (to read it) + using (BZip2InputStream controlStream = new BZip2InputStream(compressedControlStream)) + using (BZip2InputStream diffStream = new BZip2InputStream(compressedDiffStream)) + using (BZip2InputStream extraStream = new BZip2InputStream(compressedExtraStream)) + { + long[] control = new long[3]; + byte[] buffer = new byte[8]; + + int oldPosition = 0; + int newPosition = 0; + while (newPosition < newSize) + { + // read control data + for (int i = 0; i < 3; i++) + { + ReadExactly(controlStream, buffer, 0, 8); + control[i] = ReadInt64(buffer, 0); + } + + // sanity-check + if (newPosition + control[0] > newSize) + throw new InvalidOperationException("Corrupt patch."); + + // seek old file to the position that the new data is diffed against + input.Position = oldPosition; + + int bytesToCopy = (int)control[0]; + while (bytesToCopy > 0) + { + int actualBytesToCopy = Math.Min(bytesToCopy, c_bufferSize); + + // read diff string + ReadExactly(diffStream, newData, 0, actualBytesToCopy); + + // add old data to diff string + int availableInputBytes = Math.Min(actualBytesToCopy, + (int)(input.Length - input.Position)); + ReadExactly(input, oldData, 0, availableInputBytes); + + for (int index = 0; index < availableInputBytes; index++) + newData[index] += oldData[index]; + + output.Write(newData, 0, actualBytesToCopy); + + // adjust counters + newPosition += actualBytesToCopy; + oldPosition += actualBytesToCopy; + bytesToCopy -= actualBytesToCopy; + } + + // sanity-check + if (newPosition + control[1] > newSize) + throw new InvalidOperationException("Corrupt patch."); + + // read extra string + bytesToCopy = (int)control[1]; + while (bytesToCopy > 0) + { + int actualBytesToCopy = Math.Min(bytesToCopy, c_bufferSize); + + ReadExactly(extraStream, newData, 0, actualBytesToCopy); + output.Write(newData, 0, actualBytesToCopy); + + newPosition += actualBytesToCopy; + bytesToCopy -= actualBytesToCopy; + } + + // adjust position + oldPosition = (int)(oldPosition + control[2]); + } } - - // Adjust position - oldPosition = (int)(oldPosition + control[2]); } } } + + File.Delete(_oldfilePath); + File.Move(_newfilePath, _oldfilePath); } - } + catch (Exception ex) + { + throw new Exception($"Dirty error : {ex.Message} !", ex.InnerException); + } + }); + } + + #endregion Public Methods - File.Delete(_oldFilePath); - File.Move(_newFilePath, _oldFilePath); + #region Private Methods + + private void ValidationParameters() + { + if (string.IsNullOrWhiteSpace(_oldfilePath)) throw new ArgumentNullException("'oldfilePath' This parameter cannot be empty ."); + if (string.IsNullOrWhiteSpace(_newfilePath)) throw new ArgumentNullException("'newfilePath' This parameter cannot be empty ."); + if (string.IsNullOrWhiteSpace(_patchPath)) throw new ArgumentNullException("'patchPath' This parameter cannot be empty ."); } private int CompareBytes(byte[] left, int leftOffset, byte[] right, int rightOffset) @@ -385,34 +414,34 @@ private int MatchLength(byte[] oldBytes, int oldOffset, byte[] newBytes, int new return i; } - private int Search(int[] suffixArray, byte[] oldBytes, byte[] newBytes, int newOffset, int start, int end, out int pos) + private int Search(int[] I, byte[] oldBytes, byte[] newBytes, int newOffset, int start, int end, out int pos) { if (end - start < 2) { - int startLength = MatchLength(oldBytes, suffixArray[start], newBytes, newOffset); - int endLength = MatchLength(oldBytes, suffixArray[end], newBytes, newOffset); + int startLength = MatchLength(oldBytes, I[start], newBytes, newOffset); + int endLength = MatchLength(oldBytes, I[end], newBytes, newOffset); if (startLength > endLength) { - pos = suffixArray[start]; + pos = I[start]; return startLength; } else { - pos = suffixArray[end]; + pos = I[end]; return endLength; } } else { int midPoint = start + (end - start) / 2; - return CompareBytes(oldBytes, suffixArray[midPoint], newBytes, newOffset) < 0 ? - Search(suffixArray, oldBytes, newBytes, newOffset, midPoint, end, out pos) : - Search(suffixArray, oldBytes, newBytes, newOffset, start, midPoint, out pos); + return CompareBytes(oldBytes, I[midPoint], newBytes, newOffset) < 0 ? + Search(I, oldBytes, newBytes, newOffset, midPoint, end, out pos) : + Search(I, oldBytes, newBytes, newOffset, start, midPoint, out pos); } } - private void Split(int[] suffixArray, int[] rankArray, int start, int len, int h) + private void Split(int[] I, int[] v, int start, int len, int h) { if (len < 16) { @@ -420,35 +449,35 @@ private void Split(int[] suffixArray, int[] rankArray, int start, int len, int h for (int k = start; k < start + len; k += j) { j = 1; - int x = rankArray[suffixArray[k] + h]; + int x = v[I[k] + h]; for (int i = 1; k + i < start + len; i++) { - if (rankArray[suffixArray[k + i] + h] < x) + if (v[I[k + i] + h] < x) { - x = rankArray[suffixArray[k + i] + h]; + x = v[I[k + i] + h]; j = 0; } - if (rankArray[suffixArray[k + i] + h] == x) + if (v[I[k + i] + h] == x) { - Swap(ref suffixArray[k + j], ref suffixArray[k + i]); + Swap(ref I[k + j], ref I[k + i]); j++; } } for (int i = 0; i < j; i++) - rankArray[suffixArray[k + i]] = k + j - 1; + v[I[k + i]] = k + j - 1; if (j == 1) - suffixArray[k] = -1; + I[k] = -1; } } else { - int x = rankArray[suffixArray[start + len / 2] + h]; + int x = v[I[start + len / 2] + h]; int jj = 0; int kk = 0; for (int i2 = start; i2 < start + len; i2++) { - if (rankArray[suffixArray[i2] + h] < x) jj++; - if (rankArray[suffixArray[i2] + h] == x) kk++; + if (v[I[i2] + h] < x) jj++; + if (v[I[i2] + h] == x) kk++; } jj += start; kk += jj; @@ -458,42 +487,42 @@ private void Split(int[] suffixArray, int[] rankArray, int start, int len, int h int k = 0; while (i < jj) { - if (rankArray[suffixArray[i] + h] < x) + if (v[I[i] + h] < x) { i++; } - else if (rankArray[suffixArray[i] + h] == x) + else if (v[I[i] + h] == x) { - Swap(ref suffixArray[i], ref suffixArray[jj + j]); + Swap(ref I[i], ref I[jj + j]); j++; } else { - Swap(ref suffixArray[i], ref suffixArray[kk + k]); + Swap(ref I[i], ref I[kk + k]); k++; } } while (jj + j < kk) { - if (rankArray[suffixArray[jj + j] + h] == x) + if (v[I[jj + j] + h] == x) { j++; } else { - Swap(ref suffixArray[jj + j], ref suffixArray[kk + k]); + Swap(ref I[jj + j], ref I[kk + k]); k++; } } - if (jj > start) Split(suffixArray, rankArray, start, jj - start, h); + if (jj > start) Split(I, v, start, jj - start, h); for (i = 0; i < kk - jj; i++) - rankArray[suffixArray[jj + i]] = kk - 1; - if (jj == kk - 1) suffixArray[jj] = -1; + v[I[jj + i]] = kk - 1; + if (jj == kk - 1) I[jj] = -1; - if (start + len > kk) Split(suffixArray, rankArray, kk, start + len -kk, h); + if (start + len > kk) Split(I, v, kk, start + len - kk, h); } } @@ -508,49 +537,51 @@ private int[] SuffixSort(byte[] oldBytes) buckets[i] = buckets[i - 1]; buckets[0] = 0; - int[] suffixArray = new int[oldBytes.Length + 1]; + int[] I = new int[oldBytes.Length + 1]; for (int i = 0; i < oldBytes.Length; i++) - suffixArray[++buckets[oldBytes[i]]] = i; + I[++buckets[oldBytes[i]]] = i; - int[] rankArray = new int[oldBytes.Length + 1]; + int[] v = new int[oldBytes.Length + 1]; for (int i = 0; i < oldBytes.Length; i++) - rankArray[i] = buckets[oldBytes[i]]; + v[i] = buckets[oldBytes[i]]; for (int i = 1; i < 256; i++) - if (buckets[i] == buckets[i - 1] + 1) suffixArray[buckets[i]] = -1; - suffixArray[0] = -1; - for (int h = 1; suffixArray[0] != -(oldBytes.Length + 1); h += h) + if (buckets[i] == buckets[i - 1] + 1) I[buckets[i]] = -1; + I[0] = -1; + for (int h = 1; I[0] != -(oldBytes.Length + 1); h += h) { int len = 0; int i = 0; while (i < oldBytes.Length + 1) { - if (suffixArray[i] < 0) + if (I[i] < 0) { - len -= suffixArray[i]; - i -= suffixArray[i]; + len -= I[i]; + i -= I[i]; } else { - if (len != 0) suffixArray[i - len] = -len; - len = rankArray[suffixArray[i]] + 1 - i; - Split(suffixArray, rankArray, i, len, h); + if (len != 0) I[i - len] = -len; + len = v[I[i]] + 1 - i; + Split(I, v, i, len, h); i += len; len = 0; } } - if (len != 0) suffixArray[i - len] = -len; + if (len != 0) I[i - len] = -len; } for (int i = 0; i < oldBytes.Length + 1; i++) - suffixArray[rankArray[i]] = i; + I[v[i]] = i; - return suffixArray; + return I; } private void Swap(ref int first, ref int second) { - (first, second) = (second, first); + int temp = first; + first = second; + second = temp; } private long ReadInt64(byte[] buf, int offset) @@ -584,7 +615,7 @@ private void WriteInt64(long value, byte[] buf, int offset) /// A new byte array containing the data read from the stream. private byte[] ReadExactly(Stream stream, int count) { - if (count < 0) throw new ArgumentOutOfRangeException(nameof(count)); + if (count < 0) throw new ArgumentOutOfRangeException("count"); byte[] buffer = new byte[count]; ReadExactly(stream, buffer, 0, count); return buffer; @@ -600,19 +631,19 @@ private byte[] ReadExactly(Stream stream, int count) /// The count of bytes to read. private void ReadExactly(Stream stream, byte[] buffer, int offset, int count) { - // Check arguments - if (stream == null) throw new ArgumentNullException(nameof(stream)); - if (buffer == null) throw new ArgumentNullException(nameof(buffer)); - if (offset < 0 || offset > buffer.Length) throw new ArgumentOutOfRangeException(nameof(offset)); - if (count < 0 || buffer.Length - offset < count) throw new ArgumentOutOfRangeException(nameof(count)); + // check arguments + if (stream == null) throw new ArgumentNullException("stream"); + if (buffer == null) throw new ArgumentNullException("buffer"); + if (offset < 0 || offset > buffer.Length) throw new ArgumentOutOfRangeException("offset"); + if (count < 0 || buffer.Length - offset < count) throw new ArgumentOutOfRangeException("count"); while (count > 0) { - // Read data + // read data int bytesRead = stream.Read(buffer, offset, count); - // Check for failure to read + // check for failure to read if (bytesRead == 0) throw new EndOfStreamException(); - // Move to next block + // move to next block offset += bytesRead; count -= bytesRead; } diff --git a/src/c#/GeneralUpdate.Differential/DifferentialCore.cs b/src/c#/GeneralUpdate.Differential/DifferentialCore.cs index 6bd9ec8f..8fc1cec1 100644 --- a/src/c#/GeneralUpdate.Differential/DifferentialCore.cs +++ b/src/c#/GeneralUpdate.Differential/DifferentialCore.cs @@ -11,112 +11,59 @@ namespace GeneralUpdate.Differential { public sealed class DifferentialCore { - #region Private Members + private static readonly object _lockObj = new object(); + private static DifferentialCore _instance; - private static readonly object LockObj = new object(); - private static DifferentialCore? _instance; - - /// - /// Differential file format . - /// private const string PATCH_FORMAT = ".patch"; - - /// - /// Patch catalog. - /// private const string PATCHS = "patchs"; - - /// - /// List of files that need to be deleted. - /// private const string DELETE_FILES_NAME = "generalupdate_delete_files.json"; - #endregion Private Members - - #region Public Properties - - public static DifferentialCore? Instance + public static DifferentialCore Instance { get { - if (_instance != null) return _instance; - lock (LockObj) + if (_instance == null) { - _instance ??= new DifferentialCore(); + lock (_lockObj) + { + if (_instance == null) + { + _instance = new DifferentialCore(); + } + } } return _instance; } } - #endregion Public Properties - - #region Public Methods - - /// - /// Generate patch file [Cannot contain files with the same name but different extensions] . - /// - /// Previous version folder path . - /// Recent version folder path. - /// Store discovered incremental update files in a temporary directory . - /// - public async Task Clean(string sourcePath, string targetPath, string? patchPath = null) + public async Task Clean(string sourcePath, string targetPath, string patchPath) { try { - if (string.IsNullOrWhiteSpace(patchPath)) - patchPath = Path.Combine(Environment.CurrentDirectory, PATCHS); - if (!Directory.Exists(patchPath)) - Directory.CreateDirectory(patchPath); - - //Take the left tree as the center to match the files that are not in the right tree . var fileManager = new GeneralFileManager(); - fileManager.CompareDirectories(sourcePath, targetPath); - var result = fileManager.ComparisonResult; - - //Binary differencing of like terms . - foreach (var file in result.DifferentFiles.AsFileInfo()) + var comparisonResult = fileManager.Compare(sourcePath, targetPath); + foreach (var file in comparisonResult.DifferentNodes) { - var dirSeparatorChar = Path.DirectorySeparatorChar.ToString().ToCharArray(); - var tempPath = file.FullName.Replace(targetPath, "").Replace(Path.GetFileName(file.FullName), "").TrimStart(dirSeparatorChar).TrimEnd(dirSeparatorChar); - string tempPath0; - string? tempDir; - if (string.IsNullOrEmpty(tempPath)) - { - tempDir = patchPath; - tempPath0 = Path.Combine(patchPath, $"{file.Name}{PATCH_FORMAT}"); - } - else - { - tempDir = Path.Combine(patchPath, tempPath); - if (!Directory.Exists(tempDir)) Directory.CreateDirectory(tempDir); - tempPath0 = Path.Combine(tempDir, $"{file.Name}{PATCH_FORMAT}"); - } - - var finOldFile = (result.UniqueToA.AsFileInfo()).FirstOrDefault(i => i.Name.Equals(file.Name)); - var oldFile = finOldFile == null ? "" : finOldFile.FullName; - var newFile = file.FullName; - var extensionName = Path.GetExtension(file.FullName); - if (File.Exists(oldFile) && File.Exists(newFile) && !BlackListManager.Instance.BlackFileFormats.Contains(extensionName)) + var tempDir = GetTempDirectory(file, targetPath, patchPath); + var oldFile = comparisonResult.LeftNodes.FirstOrDefault(i => i.Name.Equals(file.Name)); + var newFile = file; + + if (File.Exists(oldFile.FullName) && File.Exists(newFile.FullName) && string.Equals(oldFile.RelativePath, newFile.RelativePath)) { - var hashAlgorithm = new Sha256HashAlgorithm(); - if (hashAlgorithm.ComputeHash(oldFile) - .Equals(hashAlgorithm.ComputeHash(newFile), StringComparison.OrdinalIgnoreCase)) + if (!GeneralFileManager.HashEquals(oldFile.FullName, newFile.FullName)) { - continue; + var tempPatchPath = Path.Combine(tempDir, $"{file.Name}{PATCH_FORMAT}"); + await new BinaryHandler().Clean(oldFile.FullName, newFile.FullName, tempPatchPath); } - - //Generate the difference file to the difference directory . - await new BinaryHandler().Clean(oldFile, newFile, tempPath0); } else { - File.Copy(newFile, Path.Combine(tempDir, Path.GetFileName(newFile)), true); + File.Copy(newFile.FullName, Path.Combine(tempDir, Path.GetFileName(newFile.FullName)), true); } } - //If a file is found that needs to be deleted, a list of files is written to the update package. - var exceptFiles = result.DifferentFiles; - if (exceptFiles.Count != 0) + var exceptFiles = fileManager.Except(sourcePath, targetPath); + if (exceptFiles != null && exceptFiles.Any()) { var path = Path.Combine(patchPath, DELETE_FILES_NAME); GeneralFileManager.CreateJson(path, exceptFiles); @@ -128,62 +75,28 @@ public async Task Clean(string sourcePath, string targetPath, string? patchPath } } - /// - /// Apply patch [Cannot contain files with the same name but different extensions] . - /// - /// Client application directory . - /// Patch file path. - /// - /// public async Task Dirty(string appPath, string patchPath) { if (!Directory.Exists(appPath) || !Directory.Exists(patchPath)) return; + try { - var fileManager = new GeneralFileManager(); - fileManager.CompareDirectories(appPath, patchPath); - var result = fileManager.ComparisonResult; - var patchFiles = result.DifferentFiles.AsFileInfo(); - var oldFiles = result.UniqueToA.AsFileInfo(); - - //If a JSON file for the deletion list is found in the update package, it will be deleted based on its contents. - var deleteListJson = patchFiles.FirstOrDefault(i => i.Name.Equals(DELETE_FILES_NAME)); - if (deleteListJson != null) - { - var deleteFiles = GeneralFileManager.GetJson>(deleteListJson.FullName).AsFileInfo() ; - var hashAlgorithm = new Sha256HashAlgorithm(); - foreach (var file in deleteFiles) - { - //file.Hash - var resultFile = oldFiles.FirstOrDefault(i => - string.Equals(hashAlgorithm.ComputeHash(i.FullName), null, StringComparison.OrdinalIgnoreCase)); - if (resultFile == null) - { - continue; - } - if (File.Exists(resultFile.FullName)) - { - File.Delete(resultFile.FullName); - } - } - } - + var patchFiles = GeneralFileManager.GetAllfiles(patchPath); + var oldFiles = GeneralFileManager.GetAllfiles(appPath); + //Refresh the collection after deleting the file. + HandleDeleteList(patchFiles, oldFiles); + oldFiles = GeneralFileManager.GetAllfiles(appPath); foreach (var oldFile in oldFiles) { - //Only the difference file (.patch) can be updated here. var findFile = patchFiles.FirstOrDefault(f => + Path.GetFileNameWithoutExtension(f.Name).Replace(PATCH_FORMAT, "").Equals(oldFile.Name)); + + if (findFile != null && Path.GetExtension(findFile.FullName).Equals(PATCH_FORMAT)) { - var tempName = Path.GetFileNameWithoutExtension(f.Name).Replace(PATCH_FORMAT, ""); - return tempName.Equals(oldFile.Name); - }); - if (findFile != null) - { - var extensionName = Path.GetExtension(findFile.FullName); - if (!extensionName.Equals(PATCH_FORMAT)) continue; await DirtyPatch(oldFile.FullName, findFile.FullName); } } - //Update does not include files or copies configuration files. + await DirtyUnknow(appPath, patchPath); } catch (Exception ex) @@ -192,34 +105,48 @@ public async Task Dirty(string appPath, string patchPath) } } - /// - /// Set a blacklist. - /// - /// A collection of blacklist files that are skipped when updated. - /// A collection of blacklist file name extensions that are skipped on update. - public void SetBlocklist(List blackFiles, List blackFileFormats) + #region Private Methods + + private static string GetTempDirectory(FileNode file, string targetPath, string patchPath) { - BlackListManager.Instance.AddBlackFiles(blackFiles); - BlackListManager.Instance.AddBlackFileFormats(blackFileFormats); + var tempPath = file.FullName.Replace(targetPath, "").Replace(Path.GetFileName(file.FullName), "").Trim(Path.DirectorySeparatorChar); + var tempDir = string.IsNullOrEmpty(tempPath) ? patchPath : Path.Combine(patchPath, tempPath); + Directory.CreateDirectory(tempDir); + return tempDir; } - - #endregion Public Methods - #region Private Methods + private void HandleDeleteList(IEnumerable patchFiles, IEnumerable oldFiles) + { + var json = patchFiles.FirstOrDefault(i => i.Name.Equals(DELETE_FILES_NAME)); + if (json == null) + return; + + var deleteFiles = GeneralFileManager.GetJson>(json.FullName); + if (deleteFiles == null) + return; + + //Match the collection of files to be deleted based on the file hash values stored in the JSON file. + var hashAlgorithm = new Sha256HashAlgorithm(); + var tempDeleteFiles = oldFiles.Where(old => deleteFiles.Any(del => del.Hash.SequenceEqual(hashAlgorithm.ComputeHash(old.FullName)))).ToList(); + foreach (var file in tempDeleteFiles) + { + if (File.Exists(file.FullName)) + { + File.Delete(file.FullName); + } + } + } - /// - /// Apply patch file . - /// - /// Client application directory . - /// - /// - /// private async Task DirtyPatch(string appPath, string patchPath) { try { - if (!File.Exists(appPath) || !File.Exists(patchPath)) return; - var newPath = Path.Combine(Path.GetDirectoryName(appPath) ?? string.Empty, $"{Path.GetRandomFileName()}_{Path.GetFileName(appPath)}"); + if (!File.Exists(appPath) || !File.Exists(patchPath)) + { + return; + } + + var newPath = Path.Combine(Path.GetDirectoryName(appPath)!, $"{Path.GetRandomFileName()}_{Path.GetFileName(appPath)}"); await new BinaryHandler().Dirty(appPath, newPath, patchPath); } catch (Exception ex) @@ -228,37 +155,40 @@ private async Task DirtyPatch(string appPath, string patchPath) } } - /// - /// Add new files . - /// - /// Client application directory . - /// Patch file path. private Task DirtyUnknow(string appPath, string patchPath) { try { var fileManager = new GeneralFileManager(); - fileManager.CompareDirectories(appPath, patchPath); - var result = fileManager.ComparisonResult; - foreach (var file in (result.DifferentFiles.AsFileInfo())) + var comparisonResult = fileManager.Compare(appPath, patchPath); + foreach (var file in comparisonResult.DifferentNodes) { var extensionName = Path.GetExtension(file.FullName); - if (BlackListManager.Instance.BlackFileFormats.Contains(extensionName)) continue; - var targetFileName = file.FullName.Replace(patchPath, "").TrimStart("\\".ToCharArray()); + if (BlackListManager.Instance.IsBlacklisted(extensionName)) continue; + + var targetFileName = file.FullName.Replace(patchPath, "").TrimStart(Path.DirectorySeparatorChar); var targetPath = Path.Combine(appPath, targetFileName); var parentFolder = Directory.GetParent(targetPath); - if (parentFolder is { Exists: false }) parentFolder.Create(); - File.Copy(file.FullName, Path.Combine(appPath, targetPath), true); + if (parentFolder?.Exists == false) + { + parentFolder.Create(); + } + + File.Copy(file.FullName, targetPath, true); + } + + if (Directory.Exists(patchPath)) + { + Directory.Delete(patchPath, true); } - if (Directory.Exists(patchPath)) Directory.Delete(patchPath, true); return Task.CompletedTask; } catch (Exception ex) { - throw new Exception($" DirtyNew error : {ex.Message} !", ex.InnerException); + throw new Exception($"DirtyNew error : {ex.Message} !", ex.InnerException); } } - #endregion Private Methods + #endregion } } \ No newline at end of file diff --git a/src/c#/GeneralUpdate.Upgrad/Program.cs b/src/c#/GeneralUpdate.Upgrad/Program.cs index ad307b74..d2e1d25f 100644 --- a/src/c#/GeneralUpdate.Upgrad/Program.cs +++ b/src/c#/GeneralUpdate.Upgrad/Program.cs @@ -1,9 +1,27 @@ -namespace GeneralUpdate.Upgrad +using GeneralUpdate.Core.Driver; + +namespace GeneralUpdate.Upgrad { internal class Program { private static void Main(string[] args) { + var fileExtension = ".inf"; + var outPutPath = @"D:\Temp\"; + var driversPath = @"D:\Temp\"; + + var information = new DriverInformation.Builder() + .SetDriverFileExtension(fileExtension) + .SetOutPutDirectory(outPutPath) + .SetDrivers(driversPath, fileExtension) + .Build(); + + var processor = new DriverProcessor(); + processor.AddCommand(new BackupDriverCommand(information)); + processor.AddCommand(new InstallDriverCommand(information)); + processor.ProcessCommands(); + + Console.Read(); } } } \ No newline at end of file diff --git a/src/c#/GeneralUpdate.sln b/src/c#/GeneralUpdate.sln index 6ccfb902..cf1cae86 100644 --- a/src/c#/GeneralUpdate.sln +++ b/src/c#/GeneralUpdate.sln @@ -27,18 +27,6 @@ Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "GeneralUpdate.Common", "Gen EndProject Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "GeneralUpdate.Bowl", "GeneralUpdate.Bowl\GeneralUpdate.Bowl.csproj", "{49D0687D-1321-48E9-84C3-936B10532367}" EndProject -Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "test", "test", "{305810CB-3BBB-4BDF-A718-F68DA1CFC5F5}" -EndProject -Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "GeneralUpdate.Client.Test", "GeneralUpdate.Client.Test\GeneralUpdate.Client.Test.csproj", "{D02F729E-2A54-4667-88CA-7EB1C94E0A68}" -EndProject -Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "GeneralUpdate.Upgrad.Test", "GeneralUpdate.Upgrad.Test\GeneralUpdate.Upgrad.Test.csproj", "{665F4A98-C6D3-42E7-BFAE-B4E8FC938FFA}" -EndProject -Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "GeneralUpdate.Zip.Test", "GeneralUpdate.Zip.Test\GeneralUpdate.Zip.Test.csproj", "{A7B03A99-3C82-4B1A-B34D-9D43E25EF598}" -EndProject -Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "GeneralUpdate.Differential.Test", "GeneralUpdate.Differential.Test\GeneralUpdate.Differential.Test.csproj", "{B4462DE1-1978-4871-AE51-3A6A2BAF22DC}" -EndProject -Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "GeneralUpdate.Bowl.Test", "GeneralUpdate.Bowl.Test\GeneralUpdate.Bowl.Test.csproj", "{B1FE5A6A-F8B4-473D-8492-E43DDC6D73E0}" -EndProject Global GlobalSection(SolutionConfigurationPlatforms) = preSolution Debug|Any CPU = Debug|Any CPU @@ -87,26 +75,6 @@ Global {49D0687D-1321-48E9-84C3-936B10532367}.Debug|Any CPU.Build.0 = Debug|Any CPU {49D0687D-1321-48E9-84C3-936B10532367}.Release|Any CPU.ActiveCfg = Release|Any CPU {49D0687D-1321-48E9-84C3-936B10532367}.Release|Any CPU.Build.0 = Release|Any CPU - {D02F729E-2A54-4667-88CA-7EB1C94E0A68}.Debug|Any CPU.ActiveCfg = Debug|Any CPU - {D02F729E-2A54-4667-88CA-7EB1C94E0A68}.Debug|Any CPU.Build.0 = Debug|Any CPU - {D02F729E-2A54-4667-88CA-7EB1C94E0A68}.Release|Any CPU.ActiveCfg = Release|Any CPU - {D02F729E-2A54-4667-88CA-7EB1C94E0A68}.Release|Any CPU.Build.0 = Release|Any CPU - {665F4A98-C6D3-42E7-BFAE-B4E8FC938FFA}.Debug|Any CPU.ActiveCfg = Debug|Any CPU - {665F4A98-C6D3-42E7-BFAE-B4E8FC938FFA}.Debug|Any CPU.Build.0 = Debug|Any CPU - {665F4A98-C6D3-42E7-BFAE-B4E8FC938FFA}.Release|Any CPU.ActiveCfg = Release|Any CPU - {665F4A98-C6D3-42E7-BFAE-B4E8FC938FFA}.Release|Any CPU.Build.0 = Release|Any CPU - {A7B03A99-3C82-4B1A-B34D-9D43E25EF598}.Debug|Any CPU.ActiveCfg = Debug|Any CPU - {A7B03A99-3C82-4B1A-B34D-9D43E25EF598}.Debug|Any CPU.Build.0 = Debug|Any CPU - {A7B03A99-3C82-4B1A-B34D-9D43E25EF598}.Release|Any CPU.ActiveCfg = Release|Any CPU - {A7B03A99-3C82-4B1A-B34D-9D43E25EF598}.Release|Any CPU.Build.0 = Release|Any CPU - {B4462DE1-1978-4871-AE51-3A6A2BAF22DC}.Debug|Any CPU.ActiveCfg = Debug|Any CPU - {B4462DE1-1978-4871-AE51-3A6A2BAF22DC}.Debug|Any CPU.Build.0 = Debug|Any CPU - {B4462DE1-1978-4871-AE51-3A6A2BAF22DC}.Release|Any CPU.ActiveCfg = Release|Any CPU - {B4462DE1-1978-4871-AE51-3A6A2BAF22DC}.Release|Any CPU.Build.0 = Release|Any CPU - {B1FE5A6A-F8B4-473D-8492-E43DDC6D73E0}.Debug|Any CPU.ActiveCfg = Debug|Any CPU - {B1FE5A6A-F8B4-473D-8492-E43DDC6D73E0}.Debug|Any CPU.Build.0 = Debug|Any CPU - {B1FE5A6A-F8B4-473D-8492-E43DDC6D73E0}.Release|Any CPU.ActiveCfg = Release|Any CPU - {B1FE5A6A-F8B4-473D-8492-E43DDC6D73E0}.Release|Any CPU.Build.0 = Release|Any CPU EndGlobalSection GlobalSection(SolutionProperties) = preSolution HideSolutionNode = FALSE @@ -122,11 +90,6 @@ Global {7779FB4A-D121-48CC-B033-C3D36BD5D4FF} = {74BE0282-A10D-4A81-A0F0-FAA79A6152B7} {D14E59CD-404B-467B-9C6D-91EFC5994D37} = {91F059E6-7AD3-4FB7-9604-30A7849C6EFF} {49D0687D-1321-48E9-84C3-936B10532367} = {91F059E6-7AD3-4FB7-9604-30A7849C6EFF} - {D02F729E-2A54-4667-88CA-7EB1C94E0A68} = {305810CB-3BBB-4BDF-A718-F68DA1CFC5F5} - {665F4A98-C6D3-42E7-BFAE-B4E8FC938FFA} = {305810CB-3BBB-4BDF-A718-F68DA1CFC5F5} - {A7B03A99-3C82-4B1A-B34D-9D43E25EF598} = {305810CB-3BBB-4BDF-A718-F68DA1CFC5F5} - {B4462DE1-1978-4871-AE51-3A6A2BAF22DC} = {305810CB-3BBB-4BDF-A718-F68DA1CFC5F5} - {B1FE5A6A-F8B4-473D-8492-E43DDC6D73E0} = {305810CB-3BBB-4BDF-A718-F68DA1CFC5F5} EndGlobalSection GlobalSection(ExtensibilityGlobals) = postSolution SolutionGuid = {A7B2D0AD-E000-4749-BAC0-FF21B9872805} From a6d06795458bb63f817c64ae1e07aadc06ff28d4 Mon Sep 17 00:00:00 2001 From: justerzhu Date: Wed, 6 Nov 2024 20:25:55 +0800 Subject: [PATCH 29/33] =?UTF-8?q?=E9=A9=B1=E5=8A=A8=E6=9B=B4=E6=96=B0?= =?UTF-8?q?=E5=AE=9E=E7=8E=B0?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/c#/GeneralUpdate.Bowl/Bowl.cs | 5 +- .../Strategys/AbstractStrategy.cs | 3 +- .../Strategys/LinuxStrategy.cs | 10 +- .../Driver/BackupDriverCommand.cs | 108 +++++++++++++++--- .../Driver/CommandExecutor.cs | 18 ++- .../Driver/DeleteDriverCommand.cs | 25 ++-- .../Driver/DriverCommand.cs | 24 ++++ .../GeneralUpdate.Core/Driver/DriverInfo.cs | 12 ++ .../Driver/DriverInformation.cs | 45 +++----- .../Driver/DriverProcessor.cs | 4 +- .../Driver/IDriverCommand.cs | 7 -- .../Driver/InstallDriverCommand.cs | 27 ++--- .../Driver/RestoreDriverCommand.cs | 12 +- .../Pipeline/DriverMiddleware.cs | 9 +- .../Pipeline/HashMiddleware.cs | 2 +- .../DifferentialCore.cs | 3 +- src/c#/GeneralUpdate.Upgrad/Program.cs | 28 ++++- .../Properties/launchSettings.json | 2 +- 18 files changed, 232 insertions(+), 112 deletions(-) create mode 100644 src/c#/GeneralUpdate.Core/Driver/DriverCommand.cs create mode 100644 src/c#/GeneralUpdate.Core/Driver/DriverInfo.cs delete mode 100644 src/c#/GeneralUpdate.Core/Driver/IDriverCommand.cs diff --git a/src/c#/GeneralUpdate.Bowl/Bowl.cs b/src/c#/GeneralUpdate.Bowl/Bowl.cs index fd0fa2a3..9e2bc1ea 100644 --- a/src/c#/GeneralUpdate.Bowl/Bowl.cs +++ b/src/c#/GeneralUpdate.Bowl/Bowl.cs @@ -7,7 +7,7 @@ namespace GeneralUpdate.Bowl; /// /// Surveillance Main Program. /// -public class Bowl +public sealed class Bowl { private IStrategy _strategy; @@ -27,6 +27,9 @@ private void CreateStrategy() { _strategy = new LinuxStrategy(); } + + if (_strategy == null) + throw new PlatformNotSupportedException("Unsupported operating system"); } public Bowl SetParameter(MonitorParameter parameter) diff --git a/src/c#/GeneralUpdate.Bowl/Strategys/AbstractStrategy.cs b/src/c#/GeneralUpdate.Bowl/Strategys/AbstractStrategy.cs index dcf8f275..9b06b153 100644 --- a/src/c#/GeneralUpdate.Bowl/Strategys/AbstractStrategy.cs +++ b/src/c#/GeneralUpdate.Bowl/Strategys/AbstractStrategy.cs @@ -11,7 +11,8 @@ public abstract class AbstractStrategy : IStrategy private readonly IReadOnlyList _sensitiveCharacter = new List { "Exit", - "exit" + "exit", + "EXIT" }; public virtual void Launch() diff --git a/src/c#/GeneralUpdate.Bowl/Strategys/LinuxStrategy.cs b/src/c#/GeneralUpdate.Bowl/Strategys/LinuxStrategy.cs index 2829493e..bb1c93bf 100644 --- a/src/c#/GeneralUpdate.Bowl/Strategys/LinuxStrategy.cs +++ b/src/c#/GeneralUpdate.Bowl/Strategys/LinuxStrategy.cs @@ -66,8 +66,8 @@ private void Install() private string GetPacketName() { - string packageFileName = string.Empty; - LinuxSystem system = GetSystem(); + var packageFileName = string.Empty; + var system = GetSystem(); if (_rocdumpAmd64.Contains(system.Name)) { packageFileName = $"procdump_3.3.0_amd64.deb"; @@ -107,9 +107,7 @@ private LinuxSystem GetSystem() return new LinuxSystem(distro, version); } - else - { - throw new FileNotFoundException("Cannot determine the Linux distribution. The /etc/os-release file does not exist."); - } + + throw new FileNotFoundException("Cannot determine the Linux distribution. The /etc/os-release file does not exist."); } } \ No newline at end of file diff --git a/src/c#/GeneralUpdate.Core/Driver/BackupDriverCommand.cs b/src/c#/GeneralUpdate.Core/Driver/BackupDriverCommand.cs index b528d05e..8b22450b 100644 --- a/src/c#/GeneralUpdate.Core/Driver/BackupDriverCommand.cs +++ b/src/c#/GeneralUpdate.Core/Driver/BackupDriverCommand.cs @@ -1,4 +1,8 @@ -using System.IO; +using System; +using System.Collections.Generic; +using System.Diagnostics; +using System.IO; +using System.Linq; using System.Text; namespace GeneralUpdate.Core.Driver @@ -6,24 +10,30 @@ namespace GeneralUpdate.Core.Driver /// /// When the /export-driver command backs up a driver, it backs up the driver package along with all its dependencies, such as associated library files and other related files. /// - public class BackupDriverCommand : IDriverCommand + public class BackupDriverCommand(DriverInformation information) : DriverCommand { - private DriverInformation _information; + private readonly string _driverExtension = $"*{information.DriverFileExtension}"; - public BackupDriverCommand(DriverInformation information) => _information = information; - - public void Execute() + public override void Execute() { + var uninstalledDrivers = Directory.GetFiles(information.DriverDirectory, _driverExtension, SearchOption.AllDirectories).ToList(); + var installedDrivers = GetInstalledDrivers(information.FieldMappings); + var tempDrivers = installedDrivers.Where(a => uninstalledDrivers.Any(b => string.Equals(a.OriginalName, Path.GetFileName(b)))).ToList(); + information.Drivers = tempDrivers; + + //Export the backup according to the driver name. + if (Directory.Exists(information.OutPutDirectory)) + { + Directory.Delete(information.OutPutDirectory, true); + } + + Directory.CreateDirectory(information.OutPutDirectory); + /* * Back up the specified list of drives. */ - foreach (var driver in _information.Drivers) + foreach (var driver in tempDrivers) { - //Export the backup according to the driver name. - if (Directory.Exists(_information.OutPutDirectory)) - Directory.Delete(_information.OutPutDirectory, true); - - Directory.CreateDirectory(_information.OutPutDirectory); /* * If no test driver files are available, you can run the following command to export all installed driver files. * (1) dism /online /export-driver /destination:"D:\packet\cache\" @@ -32,14 +42,82 @@ public void Execute() * The following code example exports the specified driver to the specified directory. * pnputil /export-driver oemXX.inf D:\packet\cache */ + var path = Path.Combine(information.OutPutDirectory, driver.PublishedName); var command = new StringBuilder("/c pnputil /export-driver ") - .Append(driver) + .Append(driver.PublishedName) .Append(' ') - .Append(_information.OutPutDirectory) + .Append(path) .ToString(); - + + if (!Directory.Exists(path)) + { + Directory.CreateDirectory(path); + } + CommandExecutor.ExecuteCommand(command); } } + + private IEnumerable GetInstalledDrivers(Dictionary fieldMappings) + { + var drivers = new List(); + var process = new Process(); + process.StartInfo.FileName = "pnputil"; + process.StartInfo.Arguments = "/enum-drivers"; + process.StartInfo.RedirectStandardOutput = true; + process.StartInfo.UseShellExecute = false; + process.StartInfo.CreateNoWindow = true; + process.Start(); + + var output = process.StandardOutput.ReadToEnd(); + process.WaitForExit(); + + var lines = output.Split(new[] { Environment.NewLine }, StringSplitOptions.RemoveEmptyEntries); + + DriverInfo currentDriver = null; + foreach (var line in lines) + { + if (line.StartsWith(fieldMappings["PublishedName"])) + { + if (currentDriver != null) + { + drivers.Add(currentDriver); + } + currentDriver = new (); + currentDriver.PublishedName = line.Split(new[] { ':' }, 2)[1].Trim(); + } + else if (line.StartsWith(fieldMappings["OriginalName"]) && currentDriver != null) + { + currentDriver.OriginalName = line.Split(new[] { ':' }, 2)[1].Trim(); + } + else if (line.StartsWith(fieldMappings["Provider"]) && currentDriver != null) + { + currentDriver.Provider = line.Split(new[] { ':' }, 2)[1].Trim(); + } + else if (line.StartsWith(fieldMappings["ClassName"]) && currentDriver != null) + { + currentDriver.ClassName = line.Split(new[] { ':' }, 2)[1].Trim(); + } + else if (line.StartsWith(fieldMappings["ClassGUID"]) && currentDriver != null) + { + currentDriver.ClassGUID = line.Split(new[] { ':' }, 2)[1].Trim(); + } + else if (line.StartsWith(fieldMappings["Version"]) && currentDriver != null) + { + currentDriver.Version = line.Split(new[] { ':' }, 2)[1].Trim(); + } + else if (line.StartsWith(fieldMappings["Signer"]) && currentDriver != null) + { + currentDriver.Signer = line.Split(new[] { ':' }, 2)[1].Trim(); + } + } + + if (currentDriver != null) + { + drivers.Add(currentDriver); + } + + return drivers; + } } } \ No newline at end of file diff --git a/src/c#/GeneralUpdate.Core/Driver/CommandExecutor.cs b/src/c#/GeneralUpdate.Core/Driver/CommandExecutor.cs index b4d690a6..a22284bf 100644 --- a/src/c#/GeneralUpdate.Core/Driver/CommandExecutor.cs +++ b/src/c#/GeneralUpdate.Core/Driver/CommandExecutor.cs @@ -8,6 +8,9 @@ namespace GeneralUpdate.Core.Driver /// public class CommandExecutor { + private CommandExecutor() + { } + public static void ExecuteCommand(string command) { /* @@ -28,7 +31,9 @@ Update the driver regularly to ensure that the driver is compatible with the cur WindowStyle = ProcessWindowStyle.Hidden, FileName = "cmd.exe", Arguments = command, - UseShellExecute = true, + UseShellExecute = false, + RedirectStandardOutput = true, + RedirectStandardError = true, Verb = "runas" }; @@ -39,6 +44,17 @@ Update the driver regularly to ensure that the driver is compatible with the cur process.Start(); process.WaitForExit(); + // 读取标准输出 + var output = process.StandardOutput.ReadToEnd(); + Debug.WriteLine(output); + + // 读取错误输出 + var error = process.StandardError.ReadToEnd(); + if (!string.IsNullOrEmpty(error)) + { + Debug.WriteLine("Error: " + error); + } + if (process.ExitCode != 0) throw new ApplicationException($"Operation failed code: {process.ExitCode}"); } diff --git a/src/c#/GeneralUpdate.Core/Driver/DeleteDriverCommand.cs b/src/c#/GeneralUpdate.Core/Driver/DeleteDriverCommand.cs index 0d21452f..f0fe0e7a 100644 --- a/src/c#/GeneralUpdate.Core/Driver/DeleteDriverCommand.cs +++ b/src/c#/GeneralUpdate.Core/Driver/DeleteDriverCommand.cs @@ -1,23 +1,18 @@ using System.Text; -namespace GeneralUpdate.Core.Driver +namespace GeneralUpdate.Core.Driver; + +public class DeleteDriverCommand(DriverInformation information) : DriverCommand { - public class DeleteDriverCommand : IDriverCommand + public override void Execute() { - private DriverInformation _information; - - public DeleteDriverCommand(DriverInformation information) => _information = information; - - public void Execute() + //Before installing the driver, delete the driver that has been installed on the local system. Otherwise, an exception may occur. + foreach (var driver in information.Drivers) { - //Before installing the driver, delete the driver that has been installed on the local system. Otherwise, an exception may occur. - foreach (var driver in _information.Drivers) - { - var command = new StringBuilder("/c pnputil /delete-driver ") - .Append(driver) - .ToString(); - CommandExecutor.ExecuteCommand(command); - } + var command = new StringBuilder("/c pnputil /delete-driver ") + .Append(driver.PublishedName) + .ToString(); + CommandExecutor.ExecuteCommand(command); } } } \ No newline at end of file diff --git a/src/c#/GeneralUpdate.Core/Driver/DriverCommand.cs b/src/c#/GeneralUpdate.Core/Driver/DriverCommand.cs new file mode 100644 index 00000000..d5c57c3b --- /dev/null +++ b/src/c#/GeneralUpdate.Core/Driver/DriverCommand.cs @@ -0,0 +1,24 @@ +using System.Collections; +using System.Collections.Generic; +using System.IO; +using System.Linq; +using GeneralUpdate.Common; + +namespace GeneralUpdate.Core.Driver +{ + public abstract class DriverCommand + { + public abstract void Execute(); + + /// + /// Search for driver files. + /// + /// + /// + protected static IEnumerable SearchDrivers(string patchPath, string fileExtension) + { + var files = GeneralFileManager.GetAllfiles(patchPath); + return files.Where(x => x.FullName.EndsWith(fileExtension)).ToList(); + } + } +} \ No newline at end of file diff --git a/src/c#/GeneralUpdate.Core/Driver/DriverInfo.cs b/src/c#/GeneralUpdate.Core/Driver/DriverInfo.cs new file mode 100644 index 00000000..539cfb1b --- /dev/null +++ b/src/c#/GeneralUpdate.Core/Driver/DriverInfo.cs @@ -0,0 +1,12 @@ +namespace GeneralUpdate.Core.Driver; + +public class DriverInfo +{ + public string PublishedName { get; set; } + public string OriginalName { get; set; } + public string Provider { get; set; } + public string ClassName { get; set; } + public string ClassGUID { get; set; } + public string Version { get; set; } + public string Signer { get; set; } +} \ No newline at end of file diff --git a/src/c#/GeneralUpdate.Core/Driver/DriverInformation.cs b/src/c#/GeneralUpdate.Core/Driver/DriverInformation.cs index 95e045dd..e396e2a6 100644 --- a/src/c#/GeneralUpdate.Core/Driver/DriverInformation.cs +++ b/src/c#/GeneralUpdate.Core/Driver/DriverInformation.cs @@ -1,8 +1,5 @@ using System; using System.Collections.Generic; -using System.IO; -using System.Linq; -using GeneralUpdate.Common; namespace GeneralUpdate.Core.Driver { @@ -11,24 +8,28 @@ namespace GeneralUpdate.Core.Driver /// public class DriverInformation { + public Dictionary FieldMappings { get; private set; } + public string DriverFileExtension { get; private set; } /// /// All driver backup directories. /// public string OutPutDirectory { get; private set; } + + public string DriverDirectory { get; private set; } /// /// A collection of driver files to be backed up. /// - public IEnumerable Drivers { get; private set; } + public IEnumerable Drivers { get; set; } private DriverInformation() { } public class Builder { - private DriverInformation _information = new DriverInformation(); + private DriverInformation _information = new (); public Builder SetDriverFileExtension(string fileExtension) { @@ -41,43 +42,29 @@ public Builder SetOutPutDirectory(string outPutDirectory) _information.OutPutDirectory = outPutDirectory; return this; } - - /// - /// Find the collection of driver names that need to be updated from the update package. - /// - /// - /// - public Builder SetDrivers(string driversPath, string fileExtension) + + public Builder SetDriverDirectory(string driverDirectory) { - if(string.IsNullOrWhiteSpace(driversPath) || string.IsNullOrWhiteSpace(fileExtension)) - return this; - - _information.Drivers = SearchDrivers(driversPath, fileExtension); + _information.DriverDirectory = driverDirectory; + return this; + } + + public Builder SetFieldMappings(Dictionary fieldMappings) + { + _information.FieldMappings = fieldMappings; return this; } public DriverInformation Build() { if (string.IsNullOrWhiteSpace(_information.OutPutDirectory) || - string.IsNullOrWhiteSpace(_information.DriverFileExtension) || - !_information.Drivers.Any()) + string.IsNullOrWhiteSpace(_information.DriverFileExtension)) { throw new ArgumentNullException("Cannot create DriverInformation, not all fields are set."); } return _information; } - - /// - /// Search for driver files. - /// - /// - /// - private IEnumerable SearchDrivers(string patchPath, string fileExtension) - { - var files = GeneralFileManager.GetAllfiles(patchPath); - return files.Where(x => x.FullName.EndsWith(fileExtension)).ToList(); - } } } } \ No newline at end of file diff --git a/src/c#/GeneralUpdate.Core/Driver/DriverProcessor.cs b/src/c#/GeneralUpdate.Core/Driver/DriverProcessor.cs index af831c0b..3f4b848d 100644 --- a/src/c#/GeneralUpdate.Core/Driver/DriverProcessor.cs +++ b/src/c#/GeneralUpdate.Core/Driver/DriverProcessor.cs @@ -8,9 +8,9 @@ namespace GeneralUpdate.Core.Driver /// public class DriverProcessor { - private readonly List _commands = new (); + private readonly List _commands = new (); - public void AddCommand(IDriverCommand command) => _commands.Add(command); + public void AddCommand(DriverCommand command) => _commands.Add(command); /// /// Execute all driver-related commands. diff --git a/src/c#/GeneralUpdate.Core/Driver/IDriverCommand.cs b/src/c#/GeneralUpdate.Core/Driver/IDriverCommand.cs deleted file mode 100644 index 76e24f92..00000000 --- a/src/c#/GeneralUpdate.Core/Driver/IDriverCommand.cs +++ /dev/null @@ -1,7 +0,0 @@ -namespace GeneralUpdate.Core.Driver -{ - public interface IDriverCommand - { - void Execute(); - } -} \ No newline at end of file diff --git a/src/c#/GeneralUpdate.Core/Driver/InstallDriverCommand.cs b/src/c#/GeneralUpdate.Core/Driver/InstallDriverCommand.cs index c6f3895d..cf42b8b3 100644 --- a/src/c#/GeneralUpdate.Core/Driver/InstallDriverCommand.cs +++ b/src/c#/GeneralUpdate.Core/Driver/InstallDriverCommand.cs @@ -7,17 +7,13 @@ namespace GeneralUpdate.Core.Driver /// /// Install the new driver, and if the installation fails, the backup is automatically restored. /// - public class InstallDriverCommand : IDriverCommand + public class InstallDriverCommand(DriverInformation information) : DriverCommand { - private DriverInformation _information; - - public InstallDriverCommand(DriverInformation information) => _information = information; - - public void Execute() + public override void Execute() { - try + foreach (var driver in information.Drivers) { - foreach (var driver in _information.Drivers) + try { /* * 1.It is best to ensure that the installed file is OEM INF, otherwise PnPUtil may indicate that non-OEM INF cannot perform the current operation. @@ -26,18 +22,19 @@ public void Execute() * (On Windows, an ExitCode value of 259 (STILL_ACTIVE) means that the process is still running) * If you do not remove the previous installation 259 prompt will give you a misleading impression of what is running. */ + var path = Path.Combine(information.DriverDirectory, driver.OriginalName); var command = new StringBuilder("/c pnputil /add-driver ") - .Append(driver.FullName) + .Append(path) .Append(" /install") .ToString(); CommandExecutor.ExecuteCommand(command); } - } - catch (Exception ex) - { - //restore all the drivers in the backup directory. - new RestoreDriverCommand(_information).Execute(); - throw new ApplicationException("Failed to execute install command !"); + catch (Exception ex) + { + //restore all the drivers in the backup directory. + new RestoreDriverCommand(information).Execute(); + throw new ApplicationException($"Failed to execute driver command: {ex.Message}, details: {ex} !"); + } } } } diff --git a/src/c#/GeneralUpdate.Core/Driver/RestoreDriverCommand.cs b/src/c#/GeneralUpdate.Core/Driver/RestoreDriverCommand.cs index 3d901355..e30d9b6f 100644 --- a/src/c#/GeneralUpdate.Core/Driver/RestoreDriverCommand.cs +++ b/src/c#/GeneralUpdate.Core/Driver/RestoreDriverCommand.cs @@ -5,18 +5,14 @@ namespace GeneralUpdate.Core.Driver { - public class RestoreDriverCommand + public class RestoreDriverCommand(DriverInformation information) { - private DriverInformation _information; - - public RestoreDriverCommand(DriverInformation information) => _information = information; - public void Execute() { try { - var backupFiles = GeneralFileManager.GetAllfiles(_information.OutPutDirectory); - var fileExtension = _information.DriverFileExtension; + var backupFiles = GeneralFileManager.GetAllfiles(information.OutPutDirectory); + var fileExtension = information.DriverFileExtension; var drivers = backupFiles.Where(x => x.FullName.EndsWith(fileExtension)).ToList(); foreach (var driver in drivers) @@ -38,7 +34,7 @@ public void Execute() } catch { - throw new ApplicationException($"Failed to execute restore command for {_information.OutPutDirectory}"); + throw new ApplicationException($"Failed to execute restore command for {information.OutPutDirectory}"); } } } diff --git a/src/c#/GeneralUpdate.Core/Pipeline/DriverMiddleware.cs b/src/c#/GeneralUpdate.Core/Pipeline/DriverMiddleware.cs index c97b8a69..60a0dd47 100644 --- a/src/c#/GeneralUpdate.Core/Pipeline/DriverMiddleware.cs +++ b/src/c#/GeneralUpdate.Core/Pipeline/DriverMiddleware.cs @@ -22,18 +22,17 @@ public Task InvokeAsync(PipelineContext context) { return Task.Run(() => { + var outPutPath = context.Get("DriverOutPut"); + if(string.IsNullOrWhiteSpace(outPutPath)) + return; + var patchPath = context.Get("PatchPath"); if(string.IsNullOrWhiteSpace(patchPath)) return; - var outPutPath = context.Get("DriverOutPut"); - if(string.IsNullOrWhiteSpace(outPutPath)) - return; - var information = new DriverInformation.Builder() .SetDriverFileExtension(FileExtension) .SetOutPutDirectory(outPutPath) - .SetDrivers(patchPath, FileExtension) .Build(); var processor = new DriverProcessor(); diff --git a/src/c#/GeneralUpdate.Core/Pipeline/HashMiddleware.cs b/src/c#/GeneralUpdate.Core/Pipeline/HashMiddleware.cs index 90e2f1bc..b0cd0511 100644 --- a/src/c#/GeneralUpdate.Core/Pipeline/HashMiddleware.cs +++ b/src/c#/GeneralUpdate.Core/Pipeline/HashMiddleware.cs @@ -13,7 +13,7 @@ public async Task InvokeAsync(PipelineContext context) var path = context.Get("ZipFilePath"); var hash = context.Get("Hash"); var isVerify = await VerifyFileHash(path, hash); - if (!isVerify) throw new CryptographicException("Hash verification failed ."); + if (!isVerify) throw new CryptographicException("Hash verification failed !"); } private Task VerifyFileHash(string path, string hash) diff --git a/src/c#/GeneralUpdate.Differential/DifferentialCore.cs b/src/c#/GeneralUpdate.Differential/DifferentialCore.cs index 8fc1cec1..420046c3 100644 --- a/src/c#/GeneralUpdate.Differential/DifferentialCore.cs +++ b/src/c#/GeneralUpdate.Differential/DifferentialCore.cs @@ -11,11 +11,10 @@ namespace GeneralUpdate.Differential { public sealed class DifferentialCore { - private static readonly object _lockObj = new object(); + private static readonly object _lockObj = new (); private static DifferentialCore _instance; private const string PATCH_FORMAT = ".patch"; - private const string PATCHS = "patchs"; private const string DELETE_FILES_NAME = "generalupdate_delete_files.json"; public static DifferentialCore Instance diff --git a/src/c#/GeneralUpdate.Upgrad/Program.cs b/src/c#/GeneralUpdate.Upgrad/Program.cs index d2e1d25f..06ba8555 100644 --- a/src/c#/GeneralUpdate.Upgrad/Program.cs +++ b/src/c#/GeneralUpdate.Upgrad/Program.cs @@ -7,17 +7,39 @@ internal class Program private static void Main(string[] args) { var fileExtension = ".inf"; - var outPutPath = @"D:\Temp\"; - var driversPath = @"D:\Temp\"; + var outPutPath = @"D:\drivers\"; + var driversPath = @"D:\driverslocal\"; + + var fieldMappingsCN = new Dictionary + { + { "PublishedName", "发布名称" }, + { "OriginalName", "原始名称" }, + { "Provider", "提供程序名称" }, + { "ClassName", "类名" }, + { "ClassGUID", "类 GUID" }, + { "Version", "驱动程序版本" }, + { "Signer", "签名者姓名" } + }; + + var fieldMappingsEN = new Dictionary + { + { "PublishedName", "Driver" }, + { "OriginalName", "OriginalFileName" }, + { "Provider", "ProviderName" }, + { "ClassName", "ClassName" }, + { "Version", "Version" } + }; var information = new DriverInformation.Builder() .SetDriverFileExtension(fileExtension) .SetOutPutDirectory(outPutPath) - .SetDrivers(driversPath, fileExtension) + .SetDriverDirectory(driversPath) + .SetFieldMappings(fieldMappingsCN) .Build(); var processor = new DriverProcessor(); processor.AddCommand(new BackupDriverCommand(information)); + processor.AddCommand(new DeleteDriverCommand(information)); processor.AddCommand(new InstallDriverCommand(information)); processor.ProcessCommands(); diff --git a/src/c#/GeneralUpdate.Upgrad/Properties/launchSettings.json b/src/c#/GeneralUpdate.Upgrad/Properties/launchSettings.json index ffe508a8..e1b5724e 100644 --- a/src/c#/GeneralUpdate.Upgrad/Properties/launchSettings.json +++ b/src/c#/GeneralUpdate.Upgrad/Properties/launchSettings.json @@ -2,7 +2,7 @@ "profiles": { "GeneralUpdate.Upgrad": { "commandName": "Project", - "launchBrowser": true, + "launchBrowser": false, "environmentVariables": { "ASPNETCORE_ENVIRONMENT": "Development" }, From 855ce6721b709c06c0b3b06264bca2a2e5462692 Mon Sep 17 00:00:00 2001 From: justerzhu Date: Sun, 10 Nov 2024 22:30:45 +0800 Subject: [PATCH 30/33] =?UTF-8?q?=E8=B0=83=E6=95=B4=E5=91=BD=E5=90=8D?= =?UTF-8?q?=E7=A9=BA=E9=97=B4?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/c#/GeneralUpdate.Bowl/Bowl.cs | 10 +- src/c#/GeneralUpdate.Client/Program.cs | 5 +- .../GeneralClientBootstrap.cs | 2 +- .../GeneralClientOSS.cs | 2 +- .../Pipeline/PatchMiddleware.cs | 2 +- .../Strategys/WindowsStrategy.cs | 2 +- .../Download/DownloadManager.cs | 1 - src/c#/GeneralUpdate.Common/File/FileNode.cs | 147 ----------------- .../{File => FileBasic}/BlackListManager.cs | 2 +- .../{File => FileBasic}/ComparisonResult.cs | 2 +- .../FileBasic/FileNode.cs | 150 ++++++++++++++++++ .../{File => FileBasic}/FileTree.cs | 2 +- .../{File => FileBasic}/GeneralFileManager.cs | 2 +- .../Shared/Object/GlobalConfigInfo.cs | 2 + .../Driver/DriverCommand.cs | 2 +- .../Driver/RestoreDriverCommand.cs | 2 +- .../GeneralUpdateBootstrap.cs | 9 +- .../Pipeline/DriverMiddleware.cs | 7 + .../Pipeline/PatchMiddleware.cs | 2 +- .../Strategys/OSSStrategy.cs | 2 +- .../Strategys/WindowsStrategy.cs | 3 +- .../DifferentialCore.cs | 2 +- .../GeneralUpdate.Differential.csproj | 2 - src/c#/GeneralUpdate.Upgrad/Program.cs | 65 +++++++- 24 files changed, 252 insertions(+), 175 deletions(-) delete mode 100644 src/c#/GeneralUpdate.Common/File/FileNode.cs rename src/c#/GeneralUpdate.Common/{File => FileBasic}/BlackListManager.cs (98%) rename src/c#/GeneralUpdate.Common/{File => FileBasic}/ComparisonResult.cs (96%) create mode 100644 src/c#/GeneralUpdate.Common/FileBasic/FileNode.cs rename src/c#/GeneralUpdate.Common/{File => FileBasic}/FileTree.cs (99%) rename src/c#/GeneralUpdate.Common/{File => FileBasic}/GeneralFileManager.cs (99%) diff --git a/src/c#/GeneralUpdate.Bowl/Bowl.cs b/src/c#/GeneralUpdate.Bowl/Bowl.cs index 9e2bc1ea..25d84697 100644 --- a/src/c#/GeneralUpdate.Bowl/Bowl.cs +++ b/src/c#/GeneralUpdate.Bowl/Bowl.cs @@ -17,7 +17,7 @@ public Bowl(MonitorParameter parameter = null) _strategy!.SetParameter(parameter); } - private void CreateStrategy() + private Bowl CreateStrategy() { if (RuntimeInformation.IsOSPlatform(OSPlatform.Windows)) { @@ -30,6 +30,8 @@ private void CreateStrategy() if (_strategy == null) throw new PlatformNotSupportedException("Unsupported operating system"); + + return this; } public Bowl SetParameter(MonitorParameter parameter) @@ -41,5 +43,9 @@ public Bowl SetParameter(MonitorParameter parameter) return this; } - public void Launch() => _strategy.Launch(); + public Bowl Launch() + { + _strategy.Launch(); + return this; + } } \ No newline at end of file diff --git a/src/c#/GeneralUpdate.Client/Program.cs b/src/c#/GeneralUpdate.Client/Program.cs index 8892ff06..354cecb4 100644 --- a/src/c#/GeneralUpdate.Client/Program.cs +++ b/src/c#/GeneralUpdate.Client/Program.cs @@ -4,6 +4,7 @@ using GeneralUpdate.Common.Internal; using GeneralUpdate.Common.Internal.Bootstrap; using GeneralUpdate.Common.Shared.Object; +using GeneralUpdate.Differential; namespace GeneralUpdate.Client { @@ -11,7 +12,7 @@ internal class Progra { private static void Main(string[] args) { - /*Task.Run(async () => + Task.Run(async () => { var source = @"D:\packet\app"; var target = @"D:\packet\release"; @@ -19,7 +20,7 @@ private static void Main(string[] args) await DifferentialCore.Instance?.Clean(source, target, patch); await DifferentialCore.Instance?.Dirty(source, patch); - });*/ + }); /*Task.Run(() => { diff --git a/src/c#/GeneralUpdate.ClientCore/GeneralClientBootstrap.cs b/src/c#/GeneralUpdate.ClientCore/GeneralClientBootstrap.cs index 3c63ac3a..22d7aa1e 100644 --- a/src/c#/GeneralUpdate.ClientCore/GeneralClientBootstrap.cs +++ b/src/c#/GeneralUpdate.ClientCore/GeneralClientBootstrap.cs @@ -7,7 +7,7 @@ using System.Text.Json; using System.Threading.Tasks; using GeneralUpdate.ClientCore.Strategys; -using GeneralUpdate.Common; +using GeneralUpdate.Common.FileBasic; using GeneralUpdate.Common.Download; using GeneralUpdate.Common.Internal; using GeneralUpdate.Common.Internal.Bootstrap; diff --git a/src/c#/GeneralUpdate.ClientCore/GeneralClientOSS.cs b/src/c#/GeneralUpdate.ClientCore/GeneralClientOSS.cs index 6e48c04b..548bac28 100644 --- a/src/c#/GeneralUpdate.ClientCore/GeneralClientOSS.cs +++ b/src/c#/GeneralUpdate.ClientCore/GeneralClientOSS.cs @@ -8,7 +8,7 @@ using System.Text.Json; using System.Threading; using System.Threading.Tasks; -using GeneralUpdate.Common; +using GeneralUpdate.Common.FileBasic; using GeneralUpdate.Common.Internal; using GeneralUpdate.Common.Internal.Event; using GeneralUpdate.Common.Shared.Object; diff --git a/src/c#/GeneralUpdate.ClientCore/Pipeline/PatchMiddleware.cs b/src/c#/GeneralUpdate.ClientCore/Pipeline/PatchMiddleware.cs index c0ea6587..8c9c2ecf 100644 --- a/src/c#/GeneralUpdate.ClientCore/Pipeline/PatchMiddleware.cs +++ b/src/c#/GeneralUpdate.ClientCore/Pipeline/PatchMiddleware.cs @@ -1,6 +1,6 @@ using System.Collections.Generic; using System.Threading.Tasks; -using GeneralUpdate.Common; +using GeneralUpdate.Common.FileBasic; using GeneralUpdate.Common.Internal.Pipeline; using GeneralUpdate.Differential; diff --git a/src/c#/GeneralUpdate.ClientCore/Strategys/WindowsStrategy.cs b/src/c#/GeneralUpdate.ClientCore/Strategys/WindowsStrategy.cs index ef92acbc..0edc1c91 100644 --- a/src/c#/GeneralUpdate.ClientCore/Strategys/WindowsStrategy.cs +++ b/src/c#/GeneralUpdate.ClientCore/Strategys/WindowsStrategy.cs @@ -3,7 +3,7 @@ using System.IO; using System.Threading.Tasks; using GeneralUpdate.ClientCore.Pipeline; -using GeneralUpdate.Common; +using GeneralUpdate.Common.FileBasic; using GeneralUpdate.Common.Internal; using GeneralUpdate.Common.Internal.Event; using GeneralUpdate.Common.Internal.Pipeline; diff --git a/src/c#/GeneralUpdate.Common/Download/DownloadManager.cs b/src/c#/GeneralUpdate.Common/Download/DownloadManager.cs index 849c14e0..a6694103 100644 --- a/src/c#/GeneralUpdate.Common/Download/DownloadManager.cs +++ b/src/c#/GeneralUpdate.Common/Download/DownloadManager.cs @@ -3,7 +3,6 @@ using System.Threading.Tasks; using System.Collections.Immutable; using System.Diagnostics; -using System.Diagnostics.Contracts; using System.Linq; namespace GeneralUpdate.Common.Download diff --git a/src/c#/GeneralUpdate.Common/File/FileNode.cs b/src/c#/GeneralUpdate.Common/File/FileNode.cs deleted file mode 100644 index 04e1da83..00000000 --- a/src/c#/GeneralUpdate.Common/File/FileNode.cs +++ /dev/null @@ -1,147 +0,0 @@ -using System; - -namespace GeneralUpdate.Common; - - public class FileNode - { - #region Public Properties - - public long Id { get; set; } - - public string Name { get; set; } - - public string FullName { get; set; } - - public string Path { get; set; } - - public string Hash { get; set; } - - public FileNode Left { get; set; } - - public FileNode Right { get; set; } - - public int LeftType { get; set; } - - public int RightType { get; set; } - - public string RelativePath { get; set; } - - #endregion Public Properties - - #region Constructors - - public FileNode() - { } - - public FileNode(int id) - { - Id = id; - } - - #endregion Constructors - - #region Public Methods - - public void Add(FileNode node) - { - if (node == null) return; - - if (node.Id < Id) - { - if (Left == null) - { - Left = node; - } - else - { - Left.Add(node); - } - } - else - { - if (Right == null) - { - Right = node; - } - else - { - Right.Add(node); - } - } - } - - public void InfixOrder() - { - if (Left != null) - { - Left.InfixOrder(); - } - if (Right != null) - { - Right.InfixOrder(); - } - } - - public FileNode Search(long id) - { - if (id == Id) - { - return this; - } - else if (id < Id) - { - if (Left == null) return null; - return Left.Search(id); - } - else - { - if (Right == null) return null; - return Right.Search(id); - } - } - - /// - /// Find the parent node of the node that you want to delete. - /// - /// - /// - public FileNode SearchParent(long id) - { - if (Left != null && Left.Id == id || Right != null && Right.Id == id) - { - return this; - } - else - { - if (id < Id && Left != null) - { - return Left.SearchParent(id); - } - else if (id >= Id && Right != null) - { - return Right.SearchParent(id); - } - else - { - return null; - } - } - } - - /// - /// Compare tree nodes equally by Hash and file names. - /// - /// - /// - public override bool Equals(object obj) - { - if (obj == null) return false; - var tempNode = obj as FileNode; - if (tempNode == null) throw new ArgumentException(nameof(tempNode)); - return string.Equals(Hash, tempNode.Hash, StringComparison.OrdinalIgnoreCase) && string.Equals(Name, tempNode.Name, StringComparison.OrdinalIgnoreCase); - } - - public override int GetHashCode() => base.GetHashCode(); - - #endregion Public Methods - } diff --git a/src/c#/GeneralUpdate.Common/File/BlackListManager.cs b/src/c#/GeneralUpdate.Common/FileBasic/BlackListManager.cs similarity index 98% rename from src/c#/GeneralUpdate.Common/File/BlackListManager.cs rename to src/c#/GeneralUpdate.Common/FileBasic/BlackListManager.cs index 1ca7db3c..aea2517d 100644 --- a/src/c#/GeneralUpdate.Common/File/BlackListManager.cs +++ b/src/c#/GeneralUpdate.Common/FileBasic/BlackListManager.cs @@ -1,7 +1,7 @@ using System.Collections.Generic; using System.IO; -namespace GeneralUpdate.Common; +namespace GeneralUpdate.Common.FileBasic; public class BlackListManager { diff --git a/src/c#/GeneralUpdate.Common/File/ComparisonResult.cs b/src/c#/GeneralUpdate.Common/FileBasic/ComparisonResult.cs similarity index 96% rename from src/c#/GeneralUpdate.Common/File/ComparisonResult.cs rename to src/c#/GeneralUpdate.Common/FileBasic/ComparisonResult.cs index 089d54ed..41dbf234 100644 --- a/src/c#/GeneralUpdate.Common/File/ComparisonResult.cs +++ b/src/c#/GeneralUpdate.Common/FileBasic/ComparisonResult.cs @@ -1,6 +1,6 @@ using System.Collections.Generic; -namespace GeneralUpdate.Common; +namespace GeneralUpdate.Common.FileBasic; /// /// Result of a comparison between two directories. diff --git a/src/c#/GeneralUpdate.Common/FileBasic/FileNode.cs b/src/c#/GeneralUpdate.Common/FileBasic/FileNode.cs new file mode 100644 index 00000000..90b3831a --- /dev/null +++ b/src/c#/GeneralUpdate.Common/FileBasic/FileNode.cs @@ -0,0 +1,150 @@ +using System; + +namespace GeneralUpdate.Common.FileBasic; + +public class FileNode +{ + #region Public Properties + + public long Id { get; set; } + + public string Name { get; set; } + + public string FullName { get; set; } + + public string Path { get; set; } + + public string Hash { get; set; } + + public FileNode Left { get; set; } + + public FileNode Right { get; set; } + + public int LeftType { get; set; } + + public int RightType { get; set; } + + public string RelativePath { get; set; } + + #endregion Public Properties + + #region Constructors + + public FileNode() + { + } + + public FileNode(int id) + { + Id = id; + } + + #endregion Constructors + + #region Public Methods + + public void Add(FileNode node) + { + if (node == null) return; + + if (node.Id < Id) + { + if (Left == null) + { + Left = node; + } + else + { + Left.Add(node); + } + } + else + { + if (Right == null) + { + Right = node; + } + else + { + Right.Add(node); + } + } + } + + public void InfixOrder() + { + if (Left != null) + { + Left.InfixOrder(); + } + + if (Right != null) + { + Right.InfixOrder(); + } + } + + public FileNode Search(long id) + { + if (id == Id) + { + return this; + } + else if (id < Id) + { + if (Left == null) return null; + return Left.Search(id); + } + else + { + if (Right == null) return null; + return Right.Search(id); + } + } + + /// + /// Find the parent node of the node that you want to delete. + /// + /// + /// + public FileNode SearchParent(long id) + { + if (Left != null && Left.Id == id || Right != null && Right.Id == id) + { + return this; + } + else + { + if (id < Id && Left != null) + { + return Left.SearchParent(id); + } + else if (id >= Id && Right != null) + { + return Right.SearchParent(id); + } + else + { + return null; + } + } + } + + /// + /// Compare tree nodes equally by Hash and file names. + /// + /// + /// + public override bool Equals(object obj) + { + if (obj == null) return false; + var tempNode = obj as FileNode; + if (tempNode == null) throw new ArgumentException(nameof(tempNode)); + return string.Equals(Hash, tempNode.Hash, StringComparison.OrdinalIgnoreCase) && + string.Equals(Name, tempNode.Name, StringComparison.OrdinalIgnoreCase); + } + + public override int GetHashCode() => base.GetHashCode(); + + #endregion Public Methods +} diff --git a/src/c#/GeneralUpdate.Common/File/FileTree.cs b/src/c#/GeneralUpdate.Common/FileBasic/FileTree.cs similarity index 99% rename from src/c#/GeneralUpdate.Common/File/FileTree.cs rename to src/c#/GeneralUpdate.Common/FileBasic/FileTree.cs index c05289c6..085013c7 100644 --- a/src/c#/GeneralUpdate.Common/File/FileTree.cs +++ b/src/c#/GeneralUpdate.Common/FileBasic/FileTree.cs @@ -1,7 +1,7 @@ using System.Collections.Generic; using System.Diagnostics; -namespace GeneralUpdate.Common; +namespace GeneralUpdate.Common.FileBasic; /// /// Simple file binary tree. diff --git a/src/c#/GeneralUpdate.Common/File/GeneralFileManager.cs b/src/c#/GeneralUpdate.Common/FileBasic/GeneralFileManager.cs similarity index 99% rename from src/c#/GeneralUpdate.Common/File/GeneralFileManager.cs rename to src/c#/GeneralUpdate.Common/FileBasic/GeneralFileManager.cs index 4a323694..52da377b 100644 --- a/src/c#/GeneralUpdate.Common/File/GeneralFileManager.cs +++ b/src/c#/GeneralUpdate.Common/FileBasic/GeneralFileManager.cs @@ -6,7 +6,7 @@ using System.Threading; using GeneralUpdate.Common.HashAlgorithms; -namespace GeneralUpdate.Common +namespace GeneralUpdate.Common.FileBasic { public sealed class GeneralFileManager { diff --git a/src/c#/GeneralUpdate.Common/Shared/Object/GlobalConfigInfo.cs b/src/c#/GeneralUpdate.Common/Shared/Object/GlobalConfigInfo.cs index 67fa36c2..96400bc3 100644 --- a/src/c#/GeneralUpdate.Common/Shared/Object/GlobalConfigInfo.cs +++ b/src/c#/GeneralUpdate.Common/Shared/Object/GlobalConfigInfo.cs @@ -113,4 +113,6 @@ public class GlobalConfigInfo public int Platform { get; set; } public string ProductId { get; set; } + + public Dictionary FieldMappings { get; set; } } \ No newline at end of file diff --git a/src/c#/GeneralUpdate.Core/Driver/DriverCommand.cs b/src/c#/GeneralUpdate.Core/Driver/DriverCommand.cs index d5c57c3b..00cb4500 100644 --- a/src/c#/GeneralUpdate.Core/Driver/DriverCommand.cs +++ b/src/c#/GeneralUpdate.Core/Driver/DriverCommand.cs @@ -2,7 +2,7 @@ using System.Collections.Generic; using System.IO; using System.Linq; -using GeneralUpdate.Common; +using GeneralUpdate.Common.FileBasic; namespace GeneralUpdate.Core.Driver { diff --git a/src/c#/GeneralUpdate.Core/Driver/RestoreDriverCommand.cs b/src/c#/GeneralUpdate.Core/Driver/RestoreDriverCommand.cs index e30d9b6f..064735b0 100644 --- a/src/c#/GeneralUpdate.Core/Driver/RestoreDriverCommand.cs +++ b/src/c#/GeneralUpdate.Core/Driver/RestoreDriverCommand.cs @@ -1,7 +1,7 @@ using System; using System.Linq; using System.Text; -using GeneralUpdate.Common; +using GeneralUpdate.Common.FileBasic; namespace GeneralUpdate.Core.Driver { diff --git a/src/c#/GeneralUpdate.Core/GeneralUpdateBootstrap.cs b/src/c#/GeneralUpdate.Core/GeneralUpdateBootstrap.cs index 5437a2fb..33269db9 100644 --- a/src/c#/GeneralUpdate.Core/GeneralUpdateBootstrap.cs +++ b/src/c#/GeneralUpdate.Core/GeneralUpdateBootstrap.cs @@ -1,11 +1,12 @@ using System; +using System.Collections.Generic; using System.Diagnostics; using System.IO; using System.Runtime.InteropServices; using System.Text; using System.Text.Json; using System.Threading.Tasks; -using GeneralUpdate.Common; +using GeneralUpdate.Common.FileBasic; using GeneralUpdate.Common.Download; using GeneralUpdate.Common.Internal; using GeneralUpdate.Common.Internal.Bootstrap; @@ -73,6 +74,12 @@ public override async Task LaunchAsync() #region public method + public GeneralUpdateBootstrap SetFieldMappings(Dictionary fieldMappings) + { + _configInfo.FieldMappings = fieldMappings; + return this; + } + public GeneralUpdateBootstrap AddListenerMultiAllDownloadCompleted( Action callbackAction) => AddListener(callbackAction); diff --git a/src/c#/GeneralUpdate.Core/Pipeline/DriverMiddleware.cs b/src/c#/GeneralUpdate.Core/Pipeline/DriverMiddleware.cs index 60a0dd47..0c17535e 100644 --- a/src/c#/GeneralUpdate.Core/Pipeline/DriverMiddleware.cs +++ b/src/c#/GeneralUpdate.Core/Pipeline/DriverMiddleware.cs @@ -30,13 +30,20 @@ public Task InvokeAsync(PipelineContext context) if(string.IsNullOrWhiteSpace(patchPath)) return; + var fieldMappings = context.Get>("FieldMappings"); + if(fieldMappings == null || fieldMappings.Count == 0) + return; + var information = new DriverInformation.Builder() .SetDriverFileExtension(FileExtension) .SetOutPutDirectory(outPutPath) + .SetDriverDirectory(patchPath) + .SetFieldMappings(fieldMappings) .Build(); var processor = new DriverProcessor(); processor.AddCommand(new BackupDriverCommand(information)); + processor.AddCommand(new DeleteDriverCommand(information)); processor.AddCommand(new InstallDriverCommand(information)); processor.ProcessCommands(); }); diff --git a/src/c#/GeneralUpdate.Core/Pipeline/PatchMiddleware.cs b/src/c#/GeneralUpdate.Core/Pipeline/PatchMiddleware.cs index 6f9807b1..fcf1b8c7 100644 --- a/src/c#/GeneralUpdate.Core/Pipeline/PatchMiddleware.cs +++ b/src/c#/GeneralUpdate.Core/Pipeline/PatchMiddleware.cs @@ -1,6 +1,6 @@ using System.Collections.Generic; using System.Threading.Tasks; -using GeneralUpdate.Common; +using GeneralUpdate.Common.FileBasic; using GeneralUpdate.Common.Internal.Pipeline; using GeneralUpdate.Differential; diff --git a/src/c#/GeneralUpdate.Core/Strategys/OSSStrategy.cs b/src/c#/GeneralUpdate.Core/Strategys/OSSStrategy.cs index 9094cc77..83c0ae02 100644 --- a/src/c#/GeneralUpdate.Core/Strategys/OSSStrategy.cs +++ b/src/c#/GeneralUpdate.Core/Strategys/OSSStrategy.cs @@ -6,7 +6,7 @@ using System.IO; using System.Text; using System.Threading.Tasks; -using GeneralUpdate.Common; +using GeneralUpdate.Common.FileBasic; using GeneralUpdate.Common.Download; using GeneralUpdate.Common.Internal; using GeneralUpdate.Common.Internal.Event; diff --git a/src/c#/GeneralUpdate.Core/Strategys/WindowsStrategy.cs b/src/c#/GeneralUpdate.Core/Strategys/WindowsStrategy.cs index 0ad98412..279e3819 100644 --- a/src/c#/GeneralUpdate.Core/Strategys/WindowsStrategy.cs +++ b/src/c#/GeneralUpdate.Core/Strategys/WindowsStrategy.cs @@ -3,7 +3,7 @@ using System.Diagnostics; using System.IO; using System.Threading.Tasks; -using GeneralUpdate.Common; +using GeneralUpdate.Common.FileBasic; using GeneralUpdate.Common.Internal; using GeneralUpdate.Common.Internal.Event; using GeneralUpdate.Common.Internal.Pipeline; @@ -52,6 +52,7 @@ public override void Execute() context.Add("BlackFileFormats", BlackListManager.Instance.BlackFileFormats); //Driver middleware context.Add("DriverOutPut", GeneralFileManager.GetTempDirectory("DriverOutPut")); + context.Add("FieldMappings", _configinfo.FieldMappings); var pipelineBuilder = new PipelineBuilder(context) .UseMiddleware() diff --git a/src/c#/GeneralUpdate.Differential/DifferentialCore.cs b/src/c#/GeneralUpdate.Differential/DifferentialCore.cs index 420046c3..a25f84e2 100644 --- a/src/c#/GeneralUpdate.Differential/DifferentialCore.cs +++ b/src/c#/GeneralUpdate.Differential/DifferentialCore.cs @@ -4,7 +4,7 @@ using System.IO; using System.Linq; using System.Threading.Tasks; -using GeneralUpdate.Common; +using GeneralUpdate.Common.FileBasic; using GeneralUpdate.Common.HashAlgorithms; namespace GeneralUpdate.Differential diff --git a/src/c#/GeneralUpdate.Differential/GeneralUpdate.Differential.csproj b/src/c#/GeneralUpdate.Differential/GeneralUpdate.Differential.csproj index b48d2f58..ae772653 100644 --- a/src/c#/GeneralUpdate.Differential/GeneralUpdate.Differential.csproj +++ b/src/c#/GeneralUpdate.Differential/GeneralUpdate.Differential.csproj @@ -4,7 +4,6 @@ netstandard2.0 GeneralUpdate.ico GeneralUpdate128.png - False https://github.com/JusterZhu/GeneralUpdate The binary differential update function is provided, but the configuration file update function is reserved. juster.zhu @@ -17,7 +16,6 @@ - diff --git a/src/c#/GeneralUpdate.Upgrad/Program.cs b/src/c#/GeneralUpdate.Upgrad/Program.cs index 06ba8555..947fdca7 100644 --- a/src/c#/GeneralUpdate.Upgrad/Program.cs +++ b/src/c#/GeneralUpdate.Upgrad/Program.cs @@ -1,4 +1,10 @@ -using GeneralUpdate.Core.Driver; +using System.Text; +using GeneralUpdate.Common.Download; +using GeneralUpdate.Common.Internal; +using GeneralUpdate.Common.Internal.Bootstrap; +using GeneralUpdate.Common.Shared.Object; +using GeneralUpdate.Core; +using GeneralUpdate.Core.Driver; namespace GeneralUpdate.Upgrad { @@ -9,7 +15,8 @@ private static void Main(string[] args) var fileExtension = ".inf"; var outPutPath = @"D:\drivers\"; var driversPath = @"D:\driverslocal\"; - + + //中文操作系统的驱动包字段映射表,用于解析所有驱动包的信息的字符串 var fieldMappingsCN = new Dictionary { { "PublishedName", "发布名称" }, @@ -20,7 +27,8 @@ private static void Main(string[] args) { "Version", "驱动程序版本" }, { "Signer", "签名者姓名" } }; - + + //英文操作系统的驱动包字段映射表,用于解析所有驱动包的信息的字符串 var fieldMappingsEN = new Dictionary { { "PublishedName", "Driver" }, @@ -29,8 +37,8 @@ private static void Main(string[] args) { "ClassName", "ClassName" }, { "Version", "Version" } }; - - var information = new DriverInformation.Builder() + + /*var information = new DriverInformation.Builder() .SetDriverFileExtension(fileExtension) .SetOutPutDirectory(outPutPath) .SetDriverDirectory(driversPath) @@ -41,9 +49,54 @@ private static void Main(string[] args) processor.AddCommand(new BackupDriverCommand(information)); processor.AddCommand(new DeleteDriverCommand(information)); processor.AddCommand(new InstallDriverCommand(information)); - processor.ProcessCommands(); + processor.ProcessCommands();*/ + + Task.Run(() => + { + _ = new GeneralUpdateBootstrap() //单个或多个更新包下载通知事件 + .AddListenerMultiDownloadProgress(OnMultiDownloadProgressChanged) + //单个或多个更新包下载速度、剩余下载事件、当前下载版本信息通知事件 + .AddListenerMultiDownloadStatistics(OnMultiDownloadStatistics) + //单个或多个更新包下载完成 + .AddListenerMultiDownloadCompleted(OnMultiDownloadCompleted) + //完成所有的下载任务通知 + .AddListenerMultiAllDownloadCompleted(OnMultiAllDownloadCompleted) + //下载过程出现的异常通知 + .AddListenerMultiDownloadError(OnMultiDownloadError) + //整个更新过程出现的任何问题都会通过这个事件通知 + .AddListenerException(OnException) + //设置字段映射表,用于解析所有驱动包的信息的字符串 + //.SetFieldMappings(fieldMappingsCN) + //是否开启驱动更新 + //.Option(UpdateOption.Drive, true) + .LaunchAsync(); + }); Console.Read(); } + + private static void OnMultiDownloadError(object arg1, MultiDownloadErrorEventArgs arg2) + { + } + + private static void OnMultiAllDownloadCompleted(object arg1, MultiAllDownloadCompletedEventArgs arg2) + { + } + + private static void OnMultiDownloadCompleted(object arg1, MultiDownloadCompletedEventArgs arg2) + { + } + + private static void OnMultiDownloadStatistics(object arg1, MultiDownloadStatisticsEventArgs arg2) + { + } + + private static void OnMultiDownloadProgressChanged(object arg1, MultiDownloadProgressChangedEventArgs arg2) + { + } + + private static void OnException(object arg1, ExceptionEventArgs arg2) + { + } } } \ No newline at end of file From 5e9018c25221b31babc8b657d5c9e1bbd5eddac6 Mon Sep 17 00:00:00 2001 From: justerzhu Date: Wed, 13 Nov 2024 20:18:51 +0800 Subject: [PATCH 31/33] =?UTF-8?q?=E6=9B=B4=E6=96=B0=E6=9C=80=E6=96=B0C#=20?= =?UTF-8?q?=E8=AF=AD=E6=B3=95?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/c#/GeneralUpdate.Client/Program.cs | 30 +++- .../GeneralClientBootstrap.cs | 161 +++++++++++------- .../Pipeline/ZipMiddleware.cs | 2 +- .../Strategys/WindowsStrategy.cs | 118 +++++++------ .../Download/DownloadTask.cs | 16 +- .../Internal/Bootstrap/AbstractBootstrap.cs | 8 +- .../Internal/Pipeline/PipelineBuilder.cs | 4 +- .../Internal/Strategy/AbstractStrategy.cs | 4 +- .../Internal/Strategy/IStrategy.cs | 7 +- .../Shared/Object/DTO/ReportDTO.cs | 24 --- .../Shared/Object/DTO/UploadReapDTO.cs | 6 - .../Shared/Object/DTO/VersionDTO.cs | 24 --- .../Shared/Object/ProcessInfo.cs | 117 +++++++++---- .../Shared/Object/VersionConfigDO.cs | 64 ------- .../GeneralUpdateBootstrap.cs | 50 +++--- src/c#/GeneralUpdate.Core/GeneralUpdateOSS.cs | 2 +- .../Strategys/OSSStrategy.cs | 2 +- .../Strategys/WindowsStrategy.cs | 30 +++- src/c#/GeneralUpdate.Upgrad/Program.cs | 13 +- 19 files changed, 349 insertions(+), 333 deletions(-) delete mode 100644 src/c#/GeneralUpdate.Common/Shared/Object/DTO/ReportDTO.cs delete mode 100644 src/c#/GeneralUpdate.Common/Shared/Object/DTO/UploadReapDTO.cs delete mode 100644 src/c#/GeneralUpdate.Common/Shared/Object/DTO/VersionDTO.cs delete mode 100644 src/c#/GeneralUpdate.Common/Shared/Object/VersionConfigDO.cs diff --git a/src/c#/GeneralUpdate.Client/Program.cs b/src/c#/GeneralUpdate.Client/Program.cs index 354cecb4..5c78ef37 100644 --- a/src/c#/GeneralUpdate.Client/Program.cs +++ b/src/c#/GeneralUpdate.Client/Program.cs @@ -1,10 +1,10 @@ -using System.Text; +using System.Diagnostics; +using System.Text; using GeneralUpdate.ClientCore; using GeneralUpdate.Common.Download; using GeneralUpdate.Common.Internal; using GeneralUpdate.Common.Internal.Bootstrap; using GeneralUpdate.Common.Shared.Object; -using GeneralUpdate.Differential; namespace GeneralUpdate.Client { @@ -12,7 +12,7 @@ internal class Progra { private static void Main(string[] args) { - Task.Run(async () => + /*Task.Run(async () => { var source = @"D:\packet\app"; var target = @"D:\packet\release"; @@ -20,24 +20,29 @@ private static void Main(string[] args) await DifferentialCore.Instance?.Clean(source, target, patch); await DifferentialCore.Instance?.Dirty(source, patch); - }); + });*/ - /*Task.Run(() => + Task.Run(() => { var configinfo = new Configinfo(); configinfo.UpdateLogUrl = "https://www.baidu.com"; configinfo.ReportUrl = "http://127.0.0.1:5008/Upgrade/Report"; configinfo.UpdateUrl = "http://127.0.0.1:5008/Upgrade/Verification"; - configinfo.AppName = "GeneralUpdate.Upgrade"; - configinfo.MainAppName = "GeneralUpdate.Client"; + configinfo.AppName = "GeneralUpdate.Upgrade.exe"; + configinfo.MainAppName = "GeneralUpdate.Client.exe"; configinfo.InstallPath = Thread.GetDomain().BaseDirectory; + //当前客户端的版本号 configinfo.ClientVersion = "1.0.0.0"; + //当前升级端的版本号 configinfo.UpgradeClientVersion = "1.0.0.0"; + //平台 configinfo.Platform = 1; - configinfo.ProductId = "9999"; + //产品id + configinfo.ProductId = "a77c9df5-45f8-4ee9-b3ad-b9431ce0b51c"; + //应用密钥 configinfo.AppSecretKey = "ABCDEFGHIJKLMNOPQRSTUVWXYZ"; _ = new GeneralClientBootstrap()//单个或多个更新包下载通知事件 @@ -58,33 +63,40 @@ private static void Main(string[] args) .Option(UpdateOption.Format, Format.ZIP) .Option(UpdateOption.Drive, false) .LaunchAsync(); - });*/ + }); Console.Read(); } private static void OnMultiDownloadError(object arg1, MultiDownloadErrorEventArgs arg2) { + Debug.WriteLine(arg2.Exception); } private static void OnMultiAllDownloadCompleted(object arg1, MultiAllDownloadCompletedEventArgs arg2) { + Debug.WriteLine(arg2.IsAllDownloadCompleted); } private static void OnMultiDownloadCompleted(object arg1, MultiDownloadCompletedEventArgs arg2) { + var v = arg2.Version as VersionInfo; + Debug.WriteLine(v.Version); } private static void OnMultiDownloadStatistics(object arg1, MultiDownloadStatisticsEventArgs arg2) { + Debug.WriteLine(arg2.Speed); } private static void OnMultiDownloadProgressChanged(object arg1, MultiDownloadProgressChangedEventArgs arg2) { + Debug.WriteLine(arg2.ProgressValue); } private static void OnException(object arg1, ExceptionEventArgs arg2) { + Debug.WriteLine(arg2.Exception); } } } \ No newline at end of file diff --git a/src/c#/GeneralUpdate.ClientCore/GeneralClientBootstrap.cs b/src/c#/GeneralUpdate.ClientCore/GeneralClientBootstrap.cs index 22d7aa1e..5ccc92ba 100644 --- a/src/c#/GeneralUpdate.ClientCore/GeneralClientBootstrap.cs +++ b/src/c#/GeneralUpdate.ClientCore/GeneralClientBootstrap.cs @@ -19,7 +19,7 @@ namespace GeneralUpdate.ClientCore; /// -/// This component is used only for client application bootstrapping classes. +/// This component is used only for client application bootstrapping classes. /// public class GeneralClientBootstrap : AbstractBootstrap { @@ -41,19 +41,7 @@ public override async Task LaunchAsync() { ExecuteCustomOptions(); ClearEnvironmentVariable(); - await InitializeDataAsync(); - - var manager = new DownloadManager(_configinfo.TempPath, _configinfo.Format, _configinfo.DownloadTimeOut); - manager.MultiAllDownloadCompleted += OnMultiAllDownloadCompleted; - manager.MultiDownloadCompleted += OnMultiDownloadCompleted; - manager.MultiDownloadError += OnMultiDownloadError; - manager.MultiDownloadProgressChanged += OnMultiDownloadProgressChanged; - manager.MultiDownloadStatistics += OnMultiDownloadStatistics; - foreach (var versionInfo in _configinfo.UpdateVersions) - { - manager.Add(new DownloadTask(manager, versionInfo)); - } - await manager.LaunchTasksAsync(); + await ExecuteWorkflowAsync(); return this; } @@ -144,7 +132,7 @@ public GeneralClientBootstrap AddListenerException(Action 0; - _configinfo.IsMainUpdate = mainResp.Body.Count > 0; - //No need to update, return directly. - if (!_configinfo.IsMainUpdate && !_configinfo.IsUpgradeUpdate) return; - + + _configinfo.IsUpgradeUpdate = CheckUpgrade(upgradeResp); + _configinfo.IsMainUpdate = CheckUpgrade(mainResp); + //If the main program needs to be forced to update, the skip will not take effect. var isForcibly = CheckForcibly(mainResp.Body) || CheckForcibly(upgradeResp.Body); if (CanSkip(isForcibly)) return; - _configinfo.UpdateVersions = upgradeResp.Body.OrderBy(x => x.ReleaseDate).ToList(); - _configinfo.LastVersion = _configinfo.UpdateVersions.Last().Version; _configinfo.Encoding = GetOption(UpdateOption.Encoding) ?? Encoding.Default; - _configinfo.Format = GetOption(UpdateOption.Format)?? "zip"; + _configinfo.Format = GetOption(UpdateOption.Format) ?? ".zip"; _configinfo.DownloadTimeOut = GetOption(UpdateOption.DownloadTimeOut) == 0 ? 60 : GetOption(UpdateOption.DownloadTimeOut); _configinfo.DriveEnabled = GetOption(UpdateOption.Drive); - _configinfo.TempPath = GeneralFileManager.GetTempDirectory(_configinfo.LastVersion); + _configinfo.TempPath = GeneralFileManager.GetTempDirectory("main_temp"); + + if (_configinfo.IsMainUpdate) + { + _configinfo.UpdateVersions = mainResp.Body.OrderBy(x => x.ReleaseDate).ToList(); + _configinfo.LastVersion = _configinfo.UpdateVersions.Last().Version; + + //Initialize the process transfer parameter object. + var processInfo = new ProcessInfo(_configinfo.MainAppName + , _configinfo.InstallPath + , _configinfo.ClientVersion + , _configinfo.LastVersion + , _configinfo.UpdateLogUrl + , _configinfo.Encoding + , _configinfo.Format + , _configinfo.DownloadTimeOut + , _configinfo.AppSecretKey + , mainResp.Body + , _configinfo.ReportUrl); + + _configinfo.ProcessInfo = JsonSerializer.Serialize(processInfo); + } + + StrategyFactory(); - //Initialize the process transfer parameter object. - var processInfo = new ProcessInfo(_configinfo.MainAppName - , _configinfo.InstallPath - , _configinfo.ClientVersion - , _configinfo.LastVersion - , _configinfo.UpdateLogUrl - , _configinfo.Encoding - , _configinfo.Format - , _configinfo.DownloadTimeOut - , _configinfo.AppSecretKey - , mainResp.Body); - _configinfo.ProcessInfo = JsonSerializer.Serialize(processInfo); + switch (_configinfo.IsUpgradeUpdate) + { + case true when _configinfo.IsMainUpdate: + //Both upgrade and main program update. + await Download(); + await _strategy?.ExecuteAsync()!; + _strategy?.StartApp(); + break; + case true when !_configinfo.IsMainUpdate: + //Upgrade program update. + await Download(); + await _strategy?.ExecuteAsync()!; + break; + case false when _configinfo.IsMainUpdate: + //Main program update. + _strategy?.StartApp(); + break; + } } + + private async Task Download() + { + var manager = new DownloadManager(_configinfo.TempPath, _configinfo.Format, _configinfo.DownloadTimeOut); + manager.MultiAllDownloadCompleted += OnMultiAllDownloadCompleted; + manager.MultiDownloadCompleted += OnMultiDownloadCompleted; + manager.MultiDownloadError += OnMultiDownloadError; + manager.MultiDownloadProgressChanged += OnMultiDownloadProgressChanged; + manager.MultiDownloadStatistics += OnMultiDownloadStatistics; + foreach (var versionInfo in _configinfo.UpdateVersions) + { + manager.Add(new DownloadTask(manager, versionInfo)); + } + await manager.LaunchTasksAsync(); + } + + private bool CheckUpgrade(VersionRespDTO? response) + { + if (response == null) + { + return false; + } + if (response.Code == HttpStatus.OK) + { + return response.Body.Count > 0; + } + + return false; + } + /// - /// User decides if update is required. + /// User decides if update is required. /// /// is false to continue execution. private bool CanSkip(bool isForcibly) @@ -221,7 +264,7 @@ private void ExecuteCustomOptions() } /// - /// Clear the environment variable information needed to start the upgrade assistant process. + /// Clear the environment variable information needed to start the upgrade assistant process. /// private void ClearEnvironmentVariable() { @@ -237,11 +280,25 @@ private void ClearEnvironmentVariable() } } - protected override void ExecuteStrategy() + private bool CheckForcibly(List? versions) { - _strategy?.Create(_configinfo!); - _strategy?.Execute(); + if (versions == null) + return false; + + foreach (var item in versions) + { + if (item.IsForcibly == true) + { + return true; + } + } + + return false; } + + protected override void ExecuteStrategy()=> throw new NotImplementedException(); + + protected override Task ExecuteStrategyAsync()=> throw new NotImplementedException(); protected override GeneralClientBootstrap StrategyFactory() { @@ -252,22 +309,10 @@ protected override GeneralClientBootstrap StrategyFactory() else throw new PlatformNotSupportedException("The current operating system is not supported!"); + _strategy?.Create(_configinfo!); return this; } - private bool CheckForcibly(List versions) - { - foreach (var item in versions) - { - if (item.IsForcibly == true) - { - return true; - } - } - - return false; - } - private GeneralClientBootstrap AddListener(Action callbackAction) where TArgs : EventArgs { Debug.Assert(callbackAction != null); @@ -288,11 +333,7 @@ private void OnMultiDownloadError(object sender, MultiDownloadErrorEventArgs e) => EventManager.Instance.Dispatch(sender, e); private void OnMultiAllDownloadCompleted(object sender, MultiAllDownloadCompletedEventArgs e) - { - EventManager.Instance.Dispatch(sender, e); - StrategyFactory(); - ExecuteStrategy(); - } + => EventManager.Instance.Dispatch(sender, e); #endregion Private Methods } \ No newline at end of file diff --git a/src/c#/GeneralUpdate.ClientCore/Pipeline/ZipMiddleware.cs b/src/c#/GeneralUpdate.ClientCore/Pipeline/ZipMiddleware.cs index 5cd845ca..9b591151 100644 --- a/src/c#/GeneralUpdate.ClientCore/Pipeline/ZipMiddleware.cs +++ b/src/c#/GeneralUpdate.ClientCore/Pipeline/ZipMiddleware.cs @@ -11,7 +11,7 @@ namespace GeneralUpdate.ClientCore.Pipeline; public class ZipMiddleware : IMiddleware { - public Task InvokeAsync(PipelineContext context) + public Task InvokeAsync(PipelineContext? context) { return Task.Run(() => { diff --git a/src/c#/GeneralUpdate.ClientCore/Strategys/WindowsStrategy.cs b/src/c#/GeneralUpdate.ClientCore/Strategys/WindowsStrategy.cs index 0edc1c91..0231951a 100644 --- a/src/c#/GeneralUpdate.ClientCore/Strategys/WindowsStrategy.cs +++ b/src/c#/GeneralUpdate.ClientCore/Strategys/WindowsStrategy.cs @@ -20,74 +20,84 @@ public class WindowsStrategy : AbstractStrategy { private GlobalConfigInfo _configinfo = new(); - public override void Create(GlobalConfigInfo parameter) => _configinfo = parameter; + public override void Create(GlobalConfigInfo parameter)=> _configinfo = parameter; - public override void Execute() + public override async Task ExecuteAsync() { - Task.Run(async () => + try { - try + var status = 0; + var patchPath = GeneralFileManager.GetTempDirectory(PATCHS); + foreach (var version in _configinfo.UpdateVersions) { - var status = 0; - var patchPath = GeneralFileManager.GetTempDirectory(PATCHS); - foreach (var version in _configinfo.UpdateVersions) + try { - try - { - var context = new PipelineContext(); - //Common - context.Add("ZipFilePath", - Path.Combine(_configinfo.TempPath, $"{version.Name}{_configinfo.Format}")); - //hash middleware - context.Add("Hash", version.Hash); - //zip middleware - context.Add("Format", _configinfo.Format); - context.Add("Name", version.Name); - context.Add("Encoding", _configinfo.Encoding); - //patch middleware - context.Add("SourcePath", _configinfo.InstallPath); - context.Add("PatchPath", patchPath); - context.Add("BlackFiles", BlackListManager.Instance.BlackFiles); - context.Add("BlackFileFormats", BlackListManager.Instance.BlackFileFormats); + var context = new PipelineContext(); + //Common + context.Add("ZipFilePath", + Path.Combine(_configinfo.TempPath, $"{version.Name}{_configinfo.Format}")); + //hash middleware + context.Add("Hash", version.Hash); + //zip middleware + context.Add("Format", _configinfo.Format); + context.Add("Name", version.Name); + context.Add("Encoding", _configinfo.Encoding); + //patch middleware + context.Add("SourcePath", _configinfo.InstallPath); + context.Add("PatchPath", patchPath); + context.Add("BlackFiles", BlackListManager.Instance.BlackFiles); + context.Add("BlackFileFormats", BlackListManager.Instance.BlackFileFormats); - var pipelineBuilder = new PipelineBuilder(context) - .UseMiddleware() - .UseMiddleware() - .UseMiddleware(); - await pipelineBuilder.Build(); - status = 2; - } - catch (Exception e) - { - status = 3; - EventManager.Instance.Dispatch(this, new ExceptionEventArgs(e, e.Message)); - } - finally - { - await VersionService.Report(_configinfo.ReportUrl, version.RecordId, status, version.AppType); - } + var pipelineBuilder = new PipelineBuilder(context) + .UseMiddleware() + .UseMiddleware() + .UseMiddleware(); + await pipelineBuilder.Build(); + status = 2; } - - if (!string.IsNullOrEmpty(_configinfo.UpdateLogUrl)) + catch (Exception e) { - OpenBrowser(_configinfo.UpdateLogUrl); + status = 3; + EventManager.Instance.Dispatch(this, new ExceptionEventArgs(e, e.Message)); + } + finally + { + await VersionService.Report(_configinfo.ReportUrl, version.RecordId, status, version.AppType); } - - Clear(patchPath); - Clear(_configinfo.TempPath); - StartApp(_configinfo.AppName); } - catch (Exception e) + + if (!string.IsNullOrEmpty(_configinfo.UpdateLogUrl)) { - EventManager.Instance.Dispatch(this, new ExceptionEventArgs(e, e.Message)); + OpenBrowser(_configinfo.UpdateLogUrl); } - }); + + Clear(patchPath); + Clear(_configinfo.TempPath); + } + catch (Exception e) + { + EventManager.Instance.Dispatch(this, new ExceptionEventArgs(e, e.Message)); + } } - public override void StartApp(string appName) + public override void StartApp() { - var path = Path.Combine(_configinfo.InstallPath, appName); - Environment.SetEnvironmentVariable("ProcessInfo", _configinfo.ProcessInfo, EnvironmentVariableTarget.User); - Process.Start(path); + try + { + var appPath = Path.Combine(_configinfo.InstallPath, _configinfo.AppName); + if (File.Exists(appPath)) + { + Environment.SetEnvironmentVariable("ProcessInfo", _configinfo.ProcessInfo, EnvironmentVariableTarget.User); + Process.Start(appPath); + } + } + catch (Exception e) + { + EventManager.Instance.Dispatch(this, new ExceptionEventArgs(e, e.Message)); + } + finally + { + Process.GetCurrentProcess().Kill(); + } } } \ No newline at end of file diff --git a/src/c#/GeneralUpdate.Common/Download/DownloadTask.cs b/src/c#/GeneralUpdate.Common/Download/DownloadTask.cs index 002b8e50..3409acdb 100644 --- a/src/c#/GeneralUpdate.Common/Download/DownloadTask.cs +++ b/src/c#/GeneralUpdate.Common/Download/DownloadTask.cs @@ -1,8 +1,6 @@ using System; using System.Collections.Generic; -using System.ComponentModel; using System.IO; -using System.Net; using System.Net.Http; using System.Threading; using System.Threading.Tasks; @@ -52,10 +50,10 @@ public async Task LaunchAsync() { try { - var path = Path.Combine(_manager.Path, $"{_version.Name}{_manager.Format}"); InitStatisticsEvent(); InitProgressEvent(); InitCompletedEvent(); + var path = Path.Combine(_manager.Path, $"{_version.Name}{_manager.Format}"); await DownloadFileRangeAsync(_version.Url, path); } catch (Exception ex) @@ -81,6 +79,9 @@ private async Task DownloadFileRangeAsync(string url, string path) var totalBytes = response.Content.Headers.ContentLength ?? 0; if (startPos >= totalBytes) { + if (File.Exists(path)) + File.Delete(path); + File.Move(tempPath, path); return; } @@ -125,7 +126,14 @@ private async Task WriteFileAsync(string tempPath, byte[] chunk, long totalBytes if (_receivedBytes >= totalBytes) { fileStream.Close(); - File.Move(tempPath, tempPath.Replace(".temp", "")); + + var path = tempPath.Replace(".temp", ""); + if (File.Exists(path)) + { + File.Delete(path); + } + + File.Move(tempPath, path); } } diff --git a/src/c#/GeneralUpdate.Common/Internal/Bootstrap/AbstractBootstrap.cs b/src/c#/GeneralUpdate.Common/Internal/Bootstrap/AbstractBootstrap.cs index a570dc2a..5bec288e 100644 --- a/src/c#/GeneralUpdate.Common/Internal/Bootstrap/AbstractBootstrap.cs +++ b/src/c#/GeneralUpdate.Common/Internal/Bootstrap/AbstractBootstrap.cs @@ -21,6 +21,8 @@ protected internal AbstractBootstrap() => public abstract Task LaunchAsync(); protected abstract void ExecuteStrategy(); + + protected abstract Task ExecuteStrategyAsync(); protected abstract TBootstrap StrategyFactory(); @@ -31,7 +33,7 @@ protected internal AbstractBootstrap() => /// Configuration Action Enumeration. /// Value /// - public virtual TBootstrap Option(UpdateOption option, T value) + public TBootstrap Option(UpdateOption option, T value) { if (value == null) { @@ -44,12 +46,12 @@ public virtual TBootstrap Option(UpdateOption option, T value) return (TBootstrap)this; } - protected virtual T? GetOption(UpdateOption option) + protected T? GetOption(UpdateOption? option) { Debug.Assert(option != null && _options.Count != 0); var val = _options[option]; if (val != null) return (T)val.GetValue(); - return default(T); + return default; } } } \ No newline at end of file diff --git a/src/c#/GeneralUpdate.Common/Internal/Pipeline/PipelineBuilder.cs b/src/c#/GeneralUpdate.Common/Internal/Pipeline/PipelineBuilder.cs index f198ef5e..e0ffcc3c 100644 --- a/src/c#/GeneralUpdate.Common/Internal/Pipeline/PipelineBuilder.cs +++ b/src/c#/GeneralUpdate.Common/Internal/Pipeline/PipelineBuilder.cs @@ -24,7 +24,9 @@ public sealed class PipelineBuilder(PipelineContext context = null) public PipelineBuilder UseMiddlewareIf(bool? condition) where TMiddleware : IMiddleware, new() { - if (condition == false) return this; + if (condition is null or false) + return this; + var middleware = new TMiddleware(); _middlewareStack = _middlewareStack.Push(middleware); return this; diff --git a/src/c#/GeneralUpdate.Common/Internal/Strategy/AbstractStrategy.cs b/src/c#/GeneralUpdate.Common/Internal/Strategy/AbstractStrategy.cs index 8bb83c5e..44f87cba 100644 --- a/src/c#/GeneralUpdate.Common/Internal/Strategy/AbstractStrategy.cs +++ b/src/c#/GeneralUpdate.Common/Internal/Strategy/AbstractStrategy.cs @@ -13,9 +13,9 @@ public abstract class AbstractStrategy : IStrategy public virtual void Execute() => throw new NotImplementedException(); - public virtual void StartApp(string appName) => throw new NotImplementedException(); + public virtual void StartApp() => throw new NotImplementedException(); - public virtual Task ExecuteTaskAsync() => throw new NotImplementedException(); + public virtual Task ExecuteAsync() => throw new NotImplementedException(); public virtual void Create(GlobalConfigInfo parameter) => throw new NotImplementedException(); diff --git a/src/c#/GeneralUpdate.Common/Internal/Strategy/IStrategy.cs b/src/c#/GeneralUpdate.Common/Internal/Strategy/IStrategy.cs index 3efe0f30..bc583b71 100644 --- a/src/c#/GeneralUpdate.Common/Internal/Strategy/IStrategy.cs +++ b/src/c#/GeneralUpdate.Common/Internal/Strategy/IStrategy.cs @@ -16,15 +16,12 @@ public interface IStrategy /// /// After the update is complete. /// - /// - /// - /// - void StartApp(string appName); + void StartApp(); /// /// Execution strategy. /// - Task ExecuteTaskAsync(); + Task ExecuteAsync(); /// /// Create a strategy. diff --git a/src/c#/GeneralUpdate.Common/Shared/Object/DTO/ReportDTO.cs b/src/c#/GeneralUpdate.Common/Shared/Object/DTO/ReportDTO.cs deleted file mode 100644 index 65c2c785..00000000 --- a/src/c#/GeneralUpdate.Common/Shared/Object/DTO/ReportDTO.cs +++ /dev/null @@ -1,24 +0,0 @@ -using System.Text.Json.Serialization; - -namespace GeneralUpdate.Common.Shared.Object; - -public class ReportDTO -{ - /// - /// 记录id - /// - [JsonPropertyName("recordId")] - public int RecordId { get; set; } - - /// - /// 更新状态 - /// - [JsonPropertyName("status")] - public int Status { get; set; } - - /// - /// 1升级 2推送 - /// - [JsonPropertyName("type")] - public int Type { get; set; } -} \ No newline at end of file diff --git a/src/c#/GeneralUpdate.Common/Shared/Object/DTO/UploadReapDTO.cs b/src/c#/GeneralUpdate.Common/Shared/Object/DTO/UploadReapDTO.cs deleted file mode 100644 index bd1ab92c..00000000 --- a/src/c#/GeneralUpdate.Common/Shared/Object/DTO/UploadReapDTO.cs +++ /dev/null @@ -1,6 +0,0 @@ -namespace GeneralUpdate.Common.Shared.Object -{ - public class UploadReapDTO : BaseResponseDTO - { - } -} \ No newline at end of file diff --git a/src/c#/GeneralUpdate.Common/Shared/Object/DTO/VersionDTO.cs b/src/c#/GeneralUpdate.Common/Shared/Object/DTO/VersionDTO.cs deleted file mode 100644 index 502bd92e..00000000 --- a/src/c#/GeneralUpdate.Common/Shared/Object/DTO/VersionDTO.cs +++ /dev/null @@ -1,24 +0,0 @@ -namespace GeneralUpdate.Common.Shared.Object -{ - public class VersionDTO - { - public VersionDTO(string hash, long pubTime, string version, string url, string name) - { - Hash = hash; - PubTime = pubTime; - Version = version; - Url = url; - Name = name; - } - - public string Hash { get; set; } - - public long PubTime { get; set; } - - public string Version { get; set; } - - public string Url { get; set; } - - public string Name { get; set; } - } -} \ No newline at end of file diff --git a/src/c#/GeneralUpdate.Common/Shared/Object/ProcessInfo.cs b/src/c#/GeneralUpdate.Common/Shared/Object/ProcessInfo.cs index 239c7fbf..aacfb604 100644 --- a/src/c#/GeneralUpdate.Common/Shared/Object/ProcessInfo.cs +++ b/src/c#/GeneralUpdate.Common/Shared/Object/ProcessInfo.cs @@ -2,12 +2,25 @@ using System.Collections.Generic; using System.IO; using System.Text; +using System.Text.Json.Serialization; namespace GeneralUpdate.Common.Shared.Object { public class ProcessInfo { - public ProcessInfo(string appName, string installPath, string currentVersion, string lastVersion, string updateLogUrl, Encoding compressEncoding, string compressFormat, int downloadTimeOut, string appSecretKey, List updateVersions) + public ProcessInfo() { } + + public ProcessInfo(string appName + , string installPath + , string currentVersion + , string lastVersion + , string updateLogUrl + , Encoding compressEncoding + , string compressFormat + , int downloadTimeOut + , string appSecretKey + , List updateVersions + , string reportUrl) { AppName = appName ?? throw new ArgumentNullException(nameof(appName)); if (!Directory.Exists(installPath)) throw new ArgumentException($"{nameof(installPath)} path does not exist ! {installPath}."); @@ -22,76 +35,108 @@ public ProcessInfo(string appName, string installPath, string currentVersion, st AppSecretKey = appSecretKey ?? throw new ArgumentNullException(nameof(appSecretKey)); if (updateVersions == null || updateVersions.Count == 0) throw new ArgumentException("Collection cannot be null or has 0 elements !"); UpdateVersions = updateVersions; - } - - private static int ToEncodingType(Encoding encoding) - { - var type = -1; - if (Equals(encoding, Encoding.UTF8)) - { - type = 1; - } - else if (Equals(encoding, Encoding.UTF7)) - { - type = 2; - } - else if (Equals(encoding, Encoding.UTF32)) - { - type = 3; - } - else if (Equals(encoding, Encoding.Unicode)) - { - type = 4; - } - else if (Equals(encoding, Encoding.BigEndianUnicode)) - { - type = 5; - } - else if (Equals(encoding, Encoding.ASCII)) - { - type = 6; - } - else if (Equals(encoding, Encoding.Default)) - { - type = 7; - } - - return type; + ReportUrl = reportUrl ?? throw new ArgumentNullException(nameof(reportUrl)); } /// /// Need to start the name of the app. /// + [JsonPropertyName("AppName")] public string AppName { get; set; } /// /// Installation directory (the path where the update package is decompressed). /// + [JsonPropertyName("InstallPath")] public string InstallPath { get; set; } + /// + /// Current version. + /// + [JsonPropertyName("CurrentVersion")] public string CurrentVersion { get; set; } + /// + /// The version of the last update. + /// + [JsonPropertyName("LastVersion")] public string LastVersion { get; set; } /// /// Update log web address. /// + [JsonPropertyName("UpdateLogUrl")] public string UpdateLogUrl { get; set; } + /// + /// The encoding type of the update package. + /// + [JsonPropertyName("CompressEncoding")] public int CompressEncoding { get; set; } + /// + /// The compression format of the update package. + /// + [JsonPropertyName("CompressFormat")] public string CompressFormat { get; set; } + /// + /// The timeout of the download. + /// + [JsonPropertyName("DownloadTimeOut")] public int DownloadTimeOut { get; set; } /// /// application key /// + [JsonPropertyName("AppSecretKey")] public string AppSecretKey { get; set; } /// /// One or more version update information. /// + [JsonPropertyName("UpdateVersions")] public List UpdateVersions { get; set; } + + /// + /// update report web address + /// + [JsonPropertyName("ReportUrl")] + public string ReportUrl { get; set; } + + private static int ToEncodingType(Encoding encoding) + { + var type = -1; + if (Equals(encoding, Encoding.UTF8)) + { + type = 1; + } + else if (Equals(encoding, Encoding.UTF7)) + { + type = 2; + } + else if (Equals(encoding, Encoding.UTF32)) + { + type = 3; + } + else if (Equals(encoding, Encoding.Unicode)) + { + type = 4; + } + else if (Equals(encoding, Encoding.BigEndianUnicode)) + { + type = 5; + } + else if (Equals(encoding, Encoding.ASCII)) + { + type = 6; + } + else if (Equals(encoding, Encoding.Default)) + { + type = 7; + } + + return type; + } } } \ No newline at end of file diff --git a/src/c#/GeneralUpdate.Common/Shared/Object/VersionConfigDO.cs b/src/c#/GeneralUpdate.Common/Shared/Object/VersionConfigDO.cs deleted file mode 100644 index edeca18b..00000000 --- a/src/c#/GeneralUpdate.Common/Shared/Object/VersionConfigDO.cs +++ /dev/null @@ -1,64 +0,0 @@ -using System; - -namespace GeneralUpdate.Common.Shared.Object -{ - public class VersionConfigDO - { - /// - /// Product branch ID (Used to distinguish multiple branches under the same product). - /// - public string Guid { get; set; } - - /// - /// Update package download location. - /// - public string Url { get; set; } - - /// - /// Hash verification code - /// - public string Hash { get; set; } - - /// - /// Update the package name. - /// - public string Name { get; set; } - - /// - /// Update the package file format. - /// - public string Format { get; set; } - - /// - /// The version number that will be updated. - /// - public string Version { get; set; } - - /// - /// Update package release time. - /// - public long PubTime { get; set; } - - /// - /// Init version config infomation. - /// - /// - /// - /// - /// - /// - /// - /// - /// - public VersionConfigDO(string guid, string url, string hash, string name, string format, string version, long pubTime) - { - Guid = guid ?? throw new ArgumentNullException(nameof(guid)); - Url = url ?? throw new ArgumentNullException(nameof(url)); - Hash = hash ?? throw new ArgumentNullException(nameof(hash)); - Name = name ?? throw new ArgumentNullException(nameof(name)); - Format = format ?? throw new ArgumentNullException(nameof(format)); - Version = version ?? throw new ArgumentNullException(nameof(version)); - PubTime = pubTime; - } - } -} \ No newline at end of file diff --git a/src/c#/GeneralUpdate.Core/GeneralUpdateBootstrap.cs b/src/c#/GeneralUpdate.Core/GeneralUpdateBootstrap.cs index 33269db9..0efe0a6f 100644 --- a/src/c#/GeneralUpdate.Core/GeneralUpdateBootstrap.cs +++ b/src/c#/GeneralUpdate.Core/GeneralUpdateBootstrap.cs @@ -24,40 +24,34 @@ public class GeneralUpdateBootstrap : AbstractBootstrap(json); - if (processInfo == null) - throw new ArgumentException("ProcessInfo object cannot be null!"); + var processInfo = JsonSerializer.Deserialize(json); + if (processInfo == null) + throw new ArgumentException("ProcessInfo object cannot be null!"); - _configInfo = new() - { - MainAppName = processInfo.AppName, - InstallPath = processInfo.InstallPath, - ClientVersion = processInfo.CurrentVersion, - LastVersion = processInfo.LastVersion, - UpdateLogUrl = processInfo.UpdateLogUrl, - Encoding = ToEncoding(processInfo.CompressEncoding), - Format = processInfo.CompressFormat, - DownloadTimeOut = processInfo.DownloadTimeOut, - AppSecretKey = processInfo.AppSecretKey, - UpdateVersions = processInfo.UpdateVersions, - TempPath = $"{GeneralFileManager.GetTempDirectory(processInfo.LastVersion)}{Path.DirectorySeparatorChar}" - }; - } - catch (Exception ex) + _configInfo = new() { - throw new ArgumentException($"Client parameter json conversion failed, please check whether the parameter content is legal : {ex.Message},{ex.StackTrace}."); - } + MainAppName = processInfo.AppName, + InstallPath = processInfo.InstallPath, + ClientVersion = processInfo.CurrentVersion, + LastVersion = processInfo.LastVersion, + UpdateLogUrl = processInfo.UpdateLogUrl, + Encoding = ToEncoding(processInfo.CompressEncoding), + Format = processInfo.CompressFormat, + DownloadTimeOut = processInfo.DownloadTimeOut, + AppSecretKey = processInfo.AppSecretKey, + UpdateVersions = processInfo.UpdateVersions, + TempPath = GeneralFileManager.GetTempDirectory("upgrade_temp"), + ReportUrl = processInfo.ReportUrl + }; } public override async Task LaunchAsync() { + StrategyFactory(); var manager = new DownloadManager(_configInfo.TempPath, _configInfo.Format, _configInfo.DownloadTimeOut); manager.MultiAllDownloadCompleted += OnMultiAllDownloadCompleted; manager.MultiDownloadCompleted += OnMultiDownloadCompleted; @@ -105,6 +99,8 @@ public GeneralUpdateBootstrap AddListenerException(Action throw new NotImplementedException(); + protected override void ExecuteStrategy() { _strategy?.Create(_configInfo); diff --git a/src/c#/GeneralUpdate.Core/GeneralUpdateOSS.cs b/src/c#/GeneralUpdate.Core/GeneralUpdateOSS.cs index b9df255c..55d6af4d 100644 --- a/src/c#/GeneralUpdate.Core/GeneralUpdateOSS.cs +++ b/src/c#/GeneralUpdate.Core/GeneralUpdateOSS.cs @@ -76,7 +76,7 @@ private static async Task BaseStart(ParamsOSS parameter) var strategy = StrategyFactory(); //strategy.Create(parameter); //Implement different update strategies depending on the platform. - await strategy.ExecuteTaskAsync(); + await strategy.ExecuteAsync(); } private static IStrategy StrategyFactory() diff --git a/src/c#/GeneralUpdate.Core/Strategys/OSSStrategy.cs b/src/c#/GeneralUpdate.Core/Strategys/OSSStrategy.cs index 83c0ae02..cac3c4ee 100644 --- a/src/c#/GeneralUpdate.Core/Strategys/OSSStrategy.cs +++ b/src/c#/GeneralUpdate.Core/Strategys/OSSStrategy.cs @@ -34,7 +34,7 @@ public override void Create(GlobalConfigInfo parameter) _parameter = parameter; } - public override async Task ExecuteTaskAsync() + public override async Task ExecuteAsync() { await Task.Run(() => { diff --git a/src/c#/GeneralUpdate.Core/Strategys/WindowsStrategy.cs b/src/c#/GeneralUpdate.Core/Strategys/WindowsStrategy.cs index 279e3819..1b047496 100644 --- a/src/c#/GeneralUpdate.Core/Strategys/WindowsStrategy.cs +++ b/src/c#/GeneralUpdate.Core/Strategys/WindowsStrategy.cs @@ -1,5 +1,4 @@ using System; -using System.Collections.Generic; using System.Diagnostics; using System.IO; using System.Threading.Tasks; @@ -51,8 +50,11 @@ public override void Execute() context.Add("BlackFiles", BlackListManager.Instance.BlackFiles); context.Add("BlackFileFormats", BlackListManager.Instance.BlackFileFormats); //Driver middleware - context.Add("DriverOutPut", GeneralFileManager.GetTempDirectory("DriverOutPut")); - context.Add("FieldMappings", _configinfo.FieldMappings); + if (_configinfo.DriveEnabled == true) + { + context.Add("DriverOutPut", GeneralFileManager.GetTempDirectory("DriverOutPut")); + context.Add("FieldMappings", _configinfo.FieldMappings); + } var pipelineBuilder = new PipelineBuilder(context) .UseMiddleware() @@ -80,7 +82,7 @@ await VersionService.Report(_configinfo.ReportUrl, version.RecordId, status, Clear(patchPath); Clear(_configinfo.TempPath); - StartApp(_configinfo.AppName); + StartApp(); } catch (Exception e) { @@ -89,10 +91,24 @@ await VersionService.Report(_configinfo.ReportUrl, version.RecordId, status, }); } - public override void StartApp(string appName) + public override void StartApp() { - var path = Path.Combine(_configinfo.InstallPath, appName); - Process.Start(path); + try + { + var appPath = Path.Combine(_configinfo.InstallPath, _configinfo.MainAppName); + if (File.Exists(appPath)) + Process.Start(appPath); + + Environment.SetEnvironmentVariable("ProcessInfo", null, EnvironmentVariableTarget.User); + } + catch (Exception e) + { + EventManager.Instance.Dispatch(this, new ExceptionEventArgs(e, e.Message)); + } + finally + { + Process.GetCurrentProcess().Kill(); + } } } } \ No newline at end of file diff --git a/src/c#/GeneralUpdate.Upgrad/Program.cs b/src/c#/GeneralUpdate.Upgrad/Program.cs index 947fdca7..3057da58 100644 --- a/src/c#/GeneralUpdate.Upgrad/Program.cs +++ b/src/c#/GeneralUpdate.Upgrad/Program.cs @@ -1,5 +1,6 @@ using System.Text; using GeneralUpdate.Common.Download; +using GeneralUpdate.Common.FileBasic; using GeneralUpdate.Common.Internal; using GeneralUpdate.Common.Internal.Bootstrap; using GeneralUpdate.Common.Shared.Object; @@ -12,10 +13,6 @@ internal class Program { private static void Main(string[] args) { - var fileExtension = ".inf"; - var outPutPath = @"D:\drivers\"; - var driversPath = @"D:\driverslocal\"; - //中文操作系统的驱动包字段映射表,用于解析所有驱动包的信息的字符串 var fieldMappingsCN = new Dictionary { @@ -37,6 +34,10 @@ private static void Main(string[] args) { "ClassName", "ClassName" }, { "Version", "Version" } }; + + //var fileExtension = ".inf"; + //var outPutPath = @"D:\drivers\"; + //var driversPath = @"D:\driverslocal\"; /*var information = new DriverInformation.Builder() .SetDriverFileExtension(fileExtension) @@ -53,6 +54,10 @@ private static void Main(string[] args) Task.Run(() => { + var jsonPath = @"D:\packet\test.json"; + var json = File.ReadAllText(jsonPath); + Environment.SetEnvironmentVariable("ProcessInfo", json, EnvironmentVariableTarget.User); + _ = new GeneralUpdateBootstrap() //单个或多个更新包下载通知事件 .AddListenerMultiDownloadProgress(OnMultiDownloadProgressChanged) //单个或多个更新包下载速度、剩余下载事件、当前下载版本信息通知事件 From 2b8a7d100af3a0b931445058dadd26e3b2fb570e Mon Sep 17 00:00:00 2001 From: justerzhu Date: Wed, 13 Nov 2024 23:17:20 +0800 Subject: [PATCH 32/33] =?UTF-8?q?=E6=B7=BB=E5=8A=A0=E6=B5=8B=E8=AF=95?= =?UTF-8?q?=E4=BB=A3=E7=A0=81?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/c#/GeneralUpdate.Client/Program.cs | 12 ++-- .../GeneralClientBootstrap.cs | 71 +++++++++++++------ .../Strategys/WindowsStrategy.cs | 7 +- .../Internal/Bootstrap/AbstractBootstrap.cs | 18 +++-- .../Strategys/WindowsStrategy.cs | 10 ++- .../DifferentialCore.cs | 2 - src/c#/GeneralUpdate.Upgrad/Program.cs | 11 +-- 7 files changed, 92 insertions(+), 39 deletions(-) diff --git a/src/c#/GeneralUpdate.Client/Program.cs b/src/c#/GeneralUpdate.Client/Program.cs index 5c78ef37..dc9b8f2b 100644 --- a/src/c#/GeneralUpdate.Client/Program.cs +++ b/src/c#/GeneralUpdate.Client/Program.cs @@ -22,14 +22,17 @@ private static void Main(string[] args) await DifferentialCore.Instance?.Dirty(source, patch); });*/ - Task.Run(() => + Task.Run(async () => { + Console.WriteLine("主程序启动辣!!!!"); + await Task.Delay(3000); + var configinfo = new Configinfo(); - configinfo.UpdateLogUrl = "https://www.baidu.com"; + //configinfo.UpdateLogUrl = "https://www.baidu.com"; configinfo.ReportUrl = "http://127.0.0.1:5008/Upgrade/Report"; configinfo.UpdateUrl = "http://127.0.0.1:5008/Upgrade/Verification"; - configinfo.AppName = "GeneralUpdate.Upgrade.exe"; + configinfo.AppName = "GeneralUpdate.Upgrad.exe"; configinfo.MainAppName = "GeneralUpdate.Client.exe"; configinfo.InstallPath = Thread.GetDomain().BaseDirectory; @@ -59,9 +62,8 @@ private static void Main(string[] args) .AddListenerException(OnException) .SetConfig(configinfo) .Option(UpdateOption.DownloadTimeOut, 60) - .Option(UpdateOption.Encoding, Encoding.Default) + .Option(UpdateOption.Encoding, Encoding.UTF8) .Option(UpdateOption.Format, Format.ZIP) - .Option(UpdateOption.Drive, false) .LaunchAsync(); }); diff --git a/src/c#/GeneralUpdate.ClientCore/GeneralClientBootstrap.cs b/src/c#/GeneralUpdate.ClientCore/GeneralClientBootstrap.cs index 5ccc92ba..fe14f11e 100644 --- a/src/c#/GeneralUpdate.ClientCore/GeneralClientBootstrap.cs +++ b/src/c#/GeneralUpdate.ClientCore/GeneralClientBootstrap.cs @@ -159,7 +159,7 @@ private async Task ExecuteWorkflowAsync() _configinfo.Encoding = GetOption(UpdateOption.Encoding) ?? Encoding.Default; _configinfo.Format = GetOption(UpdateOption.Format) ?? ".zip"; _configinfo.DownloadTimeOut = GetOption(UpdateOption.DownloadTimeOut) == 0 ? 60 : GetOption(UpdateOption.DownloadTimeOut); - _configinfo.DriveEnabled = GetOption(UpdateOption.Drive); + _configinfo.DriveEnabled = GetOption(UpdateOption.Drive) ?? false; _configinfo.TempPath = GeneralFileManager.GetTempDirectory("main_temp"); if (_configinfo.IsMainUpdate) @@ -167,6 +167,9 @@ private async Task ExecuteWorkflowAsync() _configinfo.UpdateVersions = mainResp.Body.OrderBy(x => x.ReleaseDate).ToList(); _configinfo.LastVersion = _configinfo.UpdateVersions.Last().Version; + //var failed = CheckFail(_configinfo.LastVersion); + //if (failed) return; + //Initialize the process transfer parameter object. var processInfo = new ProcessInfo(_configinfo.MainAppName , _configinfo.InstallPath @@ -220,6 +223,31 @@ private async Task Download() await manager.LaunchTasksAsync(); } + /// + /// Check if there has been a recent update failure. + /// + /// + /// + private bool CheckFail(string version) + { + /* + Read the version number of the last failed upgrade from the system environment variables, then compare it with the version number of the current request. + If it is less than or equal to the failed version number, do not perform the update. + */ + var fail = Environment.GetEnvironmentVariable("UpgradeFail", EnvironmentVariableTarget.User); + if (string.IsNullOrEmpty(fail) || string.IsNullOrEmpty(version)) + return false; + + var failVersion = new Version(fail); + var lastVersion = new Version(version); + return failVersion >= lastVersion; + } + + /// + /// Determine whether the current version verification result indicates that an update is needed. + /// + /// + /// private bool CheckUpgrade(VersionRespDTO? response) { if (response == null) @@ -235,6 +263,27 @@ private bool CheckUpgrade(VersionRespDTO? response) return false; } + /// + /// During the iteration process, if any version requires a mandatory update, all the update content from this request should be updated. + /// + /// + /// + private bool CheckForcibly(List? versions) + { + if (versions == null) + return false; + + foreach (var item in versions) + { + if (item.IsForcibly == true) + { + return true; + } + } + + return false; + } + /// /// User decides if update is required. /// @@ -274,28 +323,10 @@ private void ClearEnvironmentVariable() } catch (Exception ex) { - EventManager.Instance.Dispatch(this, - new ExceptionEventArgs(ex, - "Error: An unknown error occurred while deleting the environment variable.")); + EventManager.Instance.Dispatch(this, new ExceptionEventArgs(ex, "Error: An unknown error occurred while deleting the environment variable.")); } } - private bool CheckForcibly(List? versions) - { - if (versions == null) - return false; - - foreach (var item in versions) - { - if (item.IsForcibly == true) - { - return true; - } - } - - return false; - } - protected override void ExecuteStrategy()=> throw new NotImplementedException(); protected override Task ExecuteStrategyAsync()=> throw new NotImplementedException(); diff --git a/src/c#/GeneralUpdate.ClientCore/Strategys/WindowsStrategy.cs b/src/c#/GeneralUpdate.ClientCore/Strategys/WindowsStrategy.cs index 0231951a..7bfd423c 100644 --- a/src/c#/GeneralUpdate.ClientCore/Strategys/WindowsStrategy.cs +++ b/src/c#/GeneralUpdate.ClientCore/Strategys/WindowsStrategy.cs @@ -1,6 +1,7 @@ using System; using System.Diagnostics; using System.IO; +using System.Threading; using System.Threading.Tasks; using GeneralUpdate.ClientCore.Pipeline; using GeneralUpdate.Common.FileBasic; @@ -88,7 +89,11 @@ public override void StartApp() if (File.Exists(appPath)) { Environment.SetEnvironmentVariable("ProcessInfo", _configinfo.ProcessInfo, EnvironmentVariableTarget.User); - Process.Start(appPath); + Process.Start(new ProcessStartInfo + { + UseShellExecute = true, + FileName = appPath + }); } } catch (Exception e) diff --git a/src/c#/GeneralUpdate.Common/Internal/Bootstrap/AbstractBootstrap.cs b/src/c#/GeneralUpdate.Common/Internal/Bootstrap/AbstractBootstrap.cs index 5bec288e..e9088041 100644 --- a/src/c#/GeneralUpdate.Common/Internal/Bootstrap/AbstractBootstrap.cs +++ b/src/c#/GeneralUpdate.Common/Internal/Bootstrap/AbstractBootstrap.cs @@ -1,4 +1,5 @@ -using System.Collections.Concurrent; +using System; +using System.Collections.Concurrent; using System.Diagnostics; using System.Threading.Tasks; using GeneralUpdate.Common.Internal.Strategy; @@ -48,10 +49,17 @@ public TBootstrap Option(UpdateOption option, T value) protected T? GetOption(UpdateOption? option) { - Debug.Assert(option != null && _options.Count != 0); - var val = _options[option]; - if (val != null) return (T)val.GetValue(); - return default; + try + { + Debug.Assert(option != null && _options.Count != 0); + var val = _options[option]; + if (val != null) return (T)val.GetValue(); + return default; + } + catch + { + return default; + } } } } \ No newline at end of file diff --git a/src/c#/GeneralUpdate.Core/Strategys/WindowsStrategy.cs b/src/c#/GeneralUpdate.Core/Strategys/WindowsStrategy.cs index 1b047496..0ecb0a25 100644 --- a/src/c#/GeneralUpdate.Core/Strategys/WindowsStrategy.cs +++ b/src/c#/GeneralUpdate.Core/Strategys/WindowsStrategy.cs @@ -97,8 +97,14 @@ public override void StartApp() { var appPath = Path.Combine(_configinfo.InstallPath, _configinfo.MainAppName); if (File.Exists(appPath)) - Process.Start(appPath); - + { + Process.Start(new ProcessStartInfo + { + FileName = appPath, + UseShellExecute = true + }); + } + Environment.SetEnvironmentVariable("ProcessInfo", null, EnvironmentVariableTarget.User); } catch (Exception e) diff --git a/src/c#/GeneralUpdate.Differential/DifferentialCore.cs b/src/c#/GeneralUpdate.Differential/DifferentialCore.cs index a25f84e2..6fc05ced 100644 --- a/src/c#/GeneralUpdate.Differential/DifferentialCore.cs +++ b/src/c#/GeneralUpdate.Differential/DifferentialCore.cs @@ -141,9 +141,7 @@ private async Task DirtyPatch(string appPath, string patchPath) try { if (!File.Exists(appPath) || !File.Exists(patchPath)) - { return; - } var newPath = Path.Combine(Path.GetDirectoryName(appPath)!, $"{Path.GetRandomFileName()}_{Path.GetFileName(appPath)}"); await new BinaryHandler().Dirty(appPath, newPath, patchPath); diff --git a/src/c#/GeneralUpdate.Upgrad/Program.cs b/src/c#/GeneralUpdate.Upgrad/Program.cs index 3057da58..4ad1d6c0 100644 --- a/src/c#/GeneralUpdate.Upgrad/Program.cs +++ b/src/c#/GeneralUpdate.Upgrad/Program.cs @@ -52,11 +52,14 @@ private static void Main(string[] args) processor.AddCommand(new InstallDriverCommand(information)); processor.ProcessCommands();*/ - Task.Run(() => + Task.Run(async () => { - var jsonPath = @"D:\packet\test.json"; - var json = File.ReadAllText(jsonPath); - Environment.SetEnvironmentVariable("ProcessInfo", json, EnvironmentVariableTarget.User); + Console.WriteLine("升级程序启动辣!!!!"); + await Task.Delay(3000); + + //var jsonPath = @"D:\packet\test.json"; + //var json = File.ReadAllText(jsonPath); + //Environment.SetEnvironmentVariable("ProcessInfo", json, EnvironmentVariableTarget.User); _ = new GeneralUpdateBootstrap() //单个或多个更新包下载通知事件 .AddListenerMultiDownloadProgress(OnMultiDownloadProgressChanged) From a64f25cdb7cc88304d6d235ed67f5b7d4898abd9 Mon Sep 17 00:00:00 2001 From: justerzhu Date: Thu, 14 Nov 2024 23:11:10 +0800 Subject: [PATCH 33/33] =?UTF-8?q?=E5=AE=8C=E5=96=84bowl=E7=BB=84=E4=BB=B6?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../Applications/Windows/export.bat | 40 +++++++++ src/c#/GeneralUpdate.Bowl/Bowl.cs | 51 ++++++----- .../GeneralUpdate.Bowl.csproj | 11 +++ .../Strategys/AbstractStrategy.cs | 88 ++++-------------- src/c#/GeneralUpdate.Bowl/Strategys/Crash.cs | 10 +++ .../Strategys/MonitorParameter.cs | 36 ++++---- .../Strategys/WindowStrategy.cs | 90 +++++++++++++++++-- src/c#/GeneralUpdate.Client/Program.cs | 8 +- .../GeneralClientBootstrap.cs | 20 +++-- .../FileBasic/GeneralFileManager.cs | 66 ++++++++++++++ .../Shared/Object/Configinfo.cs | 2 + .../Shared/Object/GlobalConfigInfo.cs | 6 +- .../Shared/Object/ProcessInfo.cs | 15 +++- .../GeneralUpdateBootstrap.cs | 3 +- .../Strategys/WindowsStrategy.cs | 12 ++- src/c#/GeneralUpdate.sln | 7 ++ .../Generalupdate.CatBowl.csproj | 14 +++ src/c#/Generalupdate.CatBowl/Program.cs | 33 +++++++ 18 files changed, 380 insertions(+), 132 deletions(-) create mode 100644 src/c#/GeneralUpdate.Bowl/Applications/Windows/export.bat create mode 100644 src/c#/GeneralUpdate.Bowl/Strategys/Crash.cs create mode 100644 src/c#/Generalupdate.CatBowl/Generalupdate.CatBowl.csproj create mode 100644 src/c#/Generalupdate.CatBowl/Program.cs diff --git a/src/c#/GeneralUpdate.Bowl/Applications/Windows/export.bat b/src/c#/GeneralUpdate.Bowl/Applications/Windows/export.bat new file mode 100644 index 00000000..80921e8e --- /dev/null +++ b/src/c#/GeneralUpdate.Bowl/Applications/Windows/export.bat @@ -0,0 +1,40 @@ +@echo off +setlocal + +if "%~1"=="" ( + echo Please provide the export path as the first parameter. + exit /b 1 +) + +set exportDir=%~1 + +if not exist "%exportDir%" ( + mkdir "%exportDir%" +) + +set outputFile=%exportDir%\driverInfo.txt + +:: 导出驱动信息 +driverquery /v /fo table > "%outputFile%" +echo %outputFile% Export successfully. + +:: 导出系统信息 +set systemInfoFile=%exportDir%\systeminfo.txt +systeminfo > "%systemInfoFile%" +echo %systemInfoFile% Export successfully. + +:: 获取当前日期 +for /f "tokens=1-4 delims=/- " %%i in ('date /t') do ( + set yyyy=%%i + set mm=%%j + set dd=%%k +) + +:: 设置日志文件名 +set logFile=%exportDir%\systemlog.evtx + +:: 导出系统日志 +wevtutil epl System "%logFile%" /q:"*[System[TimeCreated[timediff(@SystemTime) <= 86400000]]]" +echo %logFile% Export successfully. + +endlocal \ No newline at end of file diff --git a/src/c#/GeneralUpdate.Bowl/Bowl.cs b/src/c#/GeneralUpdate.Bowl/Bowl.cs index 25d84697..62a06fbf 100644 --- a/src/c#/GeneralUpdate.Bowl/Bowl.cs +++ b/src/c#/GeneralUpdate.Bowl/Bowl.cs @@ -1,6 +1,9 @@ using System; +using System.IO; using System.Runtime.InteropServices; +using System.Text.Json; using GeneralUpdate.Bowl.Strategys; +using GeneralUpdate.Common.Shared.Object; namespace GeneralUpdate.Bowl; @@ -9,43 +12,49 @@ namespace GeneralUpdate.Bowl; /// public sealed class Bowl { - private IStrategy _strategy; + private static IStrategy? _strategy; - public Bowl(MonitorParameter parameter = null) - { - CreateStrategy(); - _strategy!.SetParameter(parameter); - } + private Bowl() { } - private Bowl CreateStrategy() + private static void CreateStrategy() { if (RuntimeInformation.IsOSPlatform(OSPlatform.Windows)) { _strategy = new WindowStrategy(); } - else if (RuntimeInformation.IsOSPlatform(OSPlatform.Linux)) + /*else if (RuntimeInformation.IsOSPlatform(OSPlatform.Linux)) { _strategy = new LinuxStrategy(); - } + }*/ if (_strategy == null) throw new PlatformNotSupportedException("Unsupported operating system"); - - return this; } - - public Bowl SetParameter(MonitorParameter parameter) + + public static void Launch(MonitorParameter? monitorParameter = null) { - if(parameter.Verify()) - throw new ArgumentException("Parameter contains illegal values"); - - _strategy.SetParameter(parameter); - return this; + monitorParameter ??= CreateParameter(); + CreateStrategy(); + _strategy?.SetParameter(monitorParameter); + _strategy?.Launch(); } - public Bowl Launch() + private static MonitorParameter CreateParameter() { - _strategy.Launch(); - return this; + var json = Environment.GetEnvironmentVariable("ProcessInfo", EnvironmentVariableTarget.User); + if(string.IsNullOrWhiteSpace(json)) + throw new ArgumentNullException("ProcessInfo environment variable not set !"); + + var processInfo = JsonSerializer.Deserialize(json); + return new MonitorParameter + { + ProcessNameOrId = processInfo.AppName, + DumpFileName = $"{processInfo.LastVersion}_fail.dmp", + FailFileName = $"{processInfo.LastVersion}_fail.json", + TargetPath = processInfo.InstallPath, + FailDirectory = Path.Combine(processInfo.InstallPath, "fail", processInfo.LastVersion), + BackupDirectory = Path.Combine(processInfo.InstallPath, processInfo.LastVersion), + ExtendedField = processInfo.LastVersion + }; } } \ No newline at end of file diff --git a/src/c#/GeneralUpdate.Bowl/GeneralUpdate.Bowl.csproj b/src/c#/GeneralUpdate.Bowl/GeneralUpdate.Bowl.csproj index 3c0d6140..0c47f789 100644 --- a/src/c#/GeneralUpdate.Bowl/GeneralUpdate.Bowl.csproj +++ b/src/c#/GeneralUpdate.Bowl/GeneralUpdate.Bowl.csproj @@ -25,6 +25,17 @@ PreserveNewest + + PreserveNewest + + + + + + + + + diff --git a/src/c#/GeneralUpdate.Bowl/Strategys/AbstractStrategy.cs b/src/c#/GeneralUpdate.Bowl/Strategys/AbstractStrategy.cs index 9b06b153..4feb140a 100644 --- a/src/c#/GeneralUpdate.Bowl/Strategys/AbstractStrategy.cs +++ b/src/c#/GeneralUpdate.Bowl/Strategys/AbstractStrategy.cs @@ -1,33 +1,37 @@ -using System.Collections.Generic; +using System; +using System.Collections.Generic; using System.Diagnostics; using System.IO; +using GeneralUpdate.Common.FileBasic; namespace GeneralUpdate.Bowl.Strategys; public abstract class AbstractStrategy : IStrategy { protected MonitorParameter _parameter; - - private readonly IReadOnlyList _sensitiveCharacter = new List - { - "Exit", - "exit", - "EXIT" - }; + protected List OutputList = new (); + + public void SetParameter(MonitorParameter parameter) => _parameter = parameter; public virtual void Launch() { - Backup(); - Startup(_parameter.ProcessNameOrId, _parameter.InnerArguments); + Startup(_parameter.InnerApp, _parameter.InnerArguments); } private void Startup(string appName, string arguments) { + if (Directory.Exists(_parameter.FailDirectory)) + { + Directory.Delete(_parameter.FailDirectory, true); + } + Directory.CreateDirectory(_parameter.FailDirectory); + var startInfo = new ProcessStartInfo { FileName = appName, Arguments = arguments, RedirectStandardOutput = true, + RedirectStandardError = true, UseShellExecute = false, CreateNoWindow = true }; @@ -36,71 +40,15 @@ private void Startup(string appName, string arguments) process.OutputDataReceived += OutputHandler; process.ErrorDataReceived += OutputHandler; process.Start(); - process.StandardOutput.ReadToEnd(); - process.WaitForExit(); + process.BeginOutputReadLine(); + process.BeginErrorReadLine(); + process.WaitForExit(1000 * 10); } private void OutputHandler(object sendingProcess, DataReceivedEventArgs outLine) { var data = outLine.Data; if (!string.IsNullOrEmpty(data)) - { - foreach (var sensitive in _sensitiveCharacter) - { - if (data.Contains(sensitive)){ - Restore(); - Process.Start(_parameter.ProcessNameOrId, _parameter.Arguments); - break; - } - } - } + OutputList.Add(data); } - - private void Backup() - { - var backupPath = _parameter.Target; - var sourcePath = _parameter.Source; - - if (Directory.Exists(backupPath)) - { - Directory.Delete(backupPath, true); - } - - Directory.CreateDirectory(backupPath); - - foreach (string dirPath in Directory.GetDirectories(sourcePath, "*", SearchOption.AllDirectories)) - { - Directory.CreateDirectory(dirPath.Replace(sourcePath, backupPath)); - } - - foreach (string newPath in Directory.GetFiles(sourcePath, "*.*", SearchOption.AllDirectories)) - { - File.Copy(newPath, newPath.Replace(sourcePath, backupPath), true); - } - } - - private void Restore() - { - var restorePath = _parameter.Target; - var backupPath = _parameter.Source; - - if (Directory.Exists(restorePath)) - { - Directory.Delete(restorePath, true); - } - - Directory.CreateDirectory(restorePath); - - foreach (string dirPath in Directory.GetDirectories(backupPath, "*", SearchOption.AllDirectories)) - { - Directory.CreateDirectory(dirPath.Replace(backupPath, restorePath)); - } - - foreach (string newPath in Directory.GetFiles(backupPath, "*.*", SearchOption.AllDirectories)) - { - File.Copy(newPath, newPath.Replace(backupPath, restorePath), true); - } - } - - public void SetParameter(MonitorParameter parameter) => _parameter = parameter; } \ No newline at end of file diff --git a/src/c#/GeneralUpdate.Bowl/Strategys/Crash.cs b/src/c#/GeneralUpdate.Bowl/Strategys/Crash.cs new file mode 100644 index 00000000..3982504d --- /dev/null +++ b/src/c#/GeneralUpdate.Bowl/Strategys/Crash.cs @@ -0,0 +1,10 @@ +using System.Collections.Generic; + +namespace GeneralUpdate.Bowl.Strategys; + +public class Crash +{ + public MonitorParameter Parameter { get; set; } + + public List ProcdumpOutPutLines { get; set; } +} \ No newline at end of file diff --git a/src/c#/GeneralUpdate.Bowl/Strategys/MonitorParameter.cs b/src/c#/GeneralUpdate.Bowl/Strategys/MonitorParameter.cs index 53d49e61..d53d2f30 100644 --- a/src/c#/GeneralUpdate.Bowl/Strategys/MonitorParameter.cs +++ b/src/c#/GeneralUpdate.Bowl/Strategys/MonitorParameter.cs @@ -2,29 +2,29 @@ public class MonitorParameter { - public string Target { get; set; } - - public string Source { get; set; } + public MonitorParameter() { } - public string ProcessNameOrId { get; set; } + public string TargetPath { get; set; } - public string DumpPath { get; set; } + public string FailDirectory { get; set; } + public string BackupDirectory { get; set; } + + public string ProcessNameOrId { get; set; } + public string DumpFileName { get; set; } - public string Arguments { get; set; } + public string FailFileName { get; set; } - internal string InnerArguments => $"-e -ma {ProcessNameOrId} {DumpPath}"; + internal string InnerArguments { get; set; } - internal string InnerAppName { get; set; } - - public bool Verify() - { - return string.IsNullOrEmpty(Target) && - string.IsNullOrEmpty(Source) && - string.IsNullOrEmpty(ProcessNameOrId) && - string.IsNullOrEmpty(DumpPath) && - string.IsNullOrEmpty(DumpFileName) && - string.IsNullOrEmpty(Arguments); - } + internal string InnerApp { get; set; } + + /// + /// Upgrade: upgrade mode. This mode is primarily used in conjunction with GeneralUpdate for internal use. Please do not modify it arbitrarily when the default mode is activated. + /// Normal: Normal mode,This mode can be used independently to monitor a single program. If the program crashes, it will export the crash information. + /// + public string WorkModel { get; set; } = "Upgrade"; + + public string ExtendedField { get; set; } } \ No newline at end of file diff --git a/src/c#/GeneralUpdate.Bowl/Strategys/WindowStrategy.cs b/src/c#/GeneralUpdate.Bowl/Strategys/WindowStrategy.cs index da32bcd6..36b33506 100644 --- a/src/c#/GeneralUpdate.Bowl/Strategys/WindowStrategy.cs +++ b/src/c#/GeneralUpdate.Bowl/Strategys/WindowStrategy.cs @@ -1,23 +1,97 @@ -using System.Runtime.InteropServices; +using System; +using System.Collections.Generic; +using System.Diagnostics; +using System.IO; +using System.Runtime.InteropServices; +using GeneralUpdate.Common.FileBasic; namespace GeneralUpdate.Bowl.Strategys; public class WindowStrategy : AbstractStrategy { + private const string WorkModel = "Upgrade"; + private string? _applicationsDirectory; + private List _actions = new(); + public override void Launch() { - _parameter.InnerAppName = GetAppName(); + InitializeActions(); + _applicationsDirectory = Path.Combine(_parameter.TargetPath, "Applications", "Windows"); + _parameter.InnerApp = Path.Combine(_applicationsDirectory, GetAppName()); + var dmpFullName = Path.Combine(_parameter.FailDirectory, _parameter.DumpFileName); + _parameter.InnerArguments = $"-e -ma {_parameter.ProcessNameOrId} {dmpFullName}"; base.Launch(); + ExecuteFinalTreatment(); } - private string GetAppName() + private string GetAppName() => RuntimeInformation.OSArchitecture switch { - string appName = RuntimeInformation.OSArchitecture switch + Architecture.X86 => "procdump.exe", + Architecture.X64 => "procdump64.exe", + _ => "procdump64a.exe" + }; + + private void ExecuteFinalTreatment() + { + var dumpFile = Path.Combine(_parameter.FailDirectory, _parameter.DumpFileName); + if (File.Exists(dumpFile)) + { + foreach (var action in _actions) + { + action.Invoke(); + } + } + } + + private void InitializeActions() + { + _actions.Add(CreateCrash); + _actions.Add(Export); + _actions.Add(Restore); + _actions.Add(SetEnvironment); + } + + /// + /// Export the crash output information from procdump.exe and the monitoring parameters of Bowl. + /// + private void CreateCrash() + { + var crash = new Crash { - Architecture.X86 => "procdump.exe", - Architecture.X64 => "procdump64.exe", - _ => "procdump64a.exe" + Parameter = _parameter, + ProcdumpOutPutLines = OutputList }; - return appName; + var failJsonPath = Path.Combine(_parameter.FailDirectory, _parameter.FailFileName); + GeneralFileManager.CreateJson(failJsonPath, crash); + } + + /// + /// Export operating system information, system logs, and system driver information. + /// + private void Export() + { + var batPath = Path.Combine(_applicationsDirectory, "export.bat"); + if(!File.Exists(batPath)) + throw new FileNotFoundException("export.bat not found!"); + + Process.Start(batPath, _parameter.FailDirectory); + } + + /// + /// Within the GeneralUpdate upgrade system, restore the specified backup version files to the current working directory. + /// + private void Restore() + { + if (string.Equals(_parameter.WorkModel, WorkModel)) + GeneralFileManager.Restore(_parameter.BackupDirectory, _parameter.TargetPath); + } + + + private void SetEnvironment() + { + if (!string.Equals(_parameter.WorkModel, WorkModel)) + return; + + Environment.SetEnvironmentVariable("UpgradeFail", _parameter.ExtendedField, EnvironmentVariableTarget.User); } } \ No newline at end of file diff --git a/src/c#/GeneralUpdate.Client/Program.cs b/src/c#/GeneralUpdate.Client/Program.cs index dc9b8f2b..10626655 100644 --- a/src/c#/GeneralUpdate.Client/Program.cs +++ b/src/c#/GeneralUpdate.Client/Program.cs @@ -22,10 +22,10 @@ private static void Main(string[] args) await DifferentialCore.Instance?.Dirty(source, patch); });*/ - Task.Run(async () => + Task.Run(() => { - Console.WriteLine("主程序启动辣!!!!"); - await Task.Delay(3000); + //Console.WriteLine("主程序启动辣!!!!"); + //await Task.Delay(3000); var configinfo = new Configinfo(); //configinfo.UpdateLogUrl = "https://www.baidu.com"; @@ -35,6 +35,7 @@ private static void Main(string[] args) configinfo.AppName = "GeneralUpdate.Upgrad.exe"; configinfo.MainAppName = "GeneralUpdate.Client.exe"; configinfo.InstallPath = Thread.GetDomain().BaseDirectory; + configinfo.Bowl = "Generalupdate.CatBowl.exe"; //当前客户端的版本号 configinfo.ClientVersion = "1.0.0.0"; @@ -66,7 +67,6 @@ private static void Main(string[] args) .Option(UpdateOption.Format, Format.ZIP) .LaunchAsync(); }); - Console.Read(); } diff --git a/src/c#/GeneralUpdate.ClientCore/GeneralClientBootstrap.cs b/src/c#/GeneralUpdate.ClientCore/GeneralClientBootstrap.cs index fe14f11e..e797135d 100644 --- a/src/c#/GeneralUpdate.ClientCore/GeneralClientBootstrap.cs +++ b/src/c#/GeneralUpdate.ClientCore/GeneralClientBootstrap.cs @@ -1,6 +1,7 @@ using System; using System.Collections.Generic; using System.Diagnostics; +using System.IO; using System.Linq; using System.Runtime.InteropServices; using System.Text; @@ -30,6 +31,8 @@ public class GeneralClientBootstrap : AbstractBootstrap? _customSkipOption; private readonly List> _customOptions = new(); + private const string DirectoryName = "app-"; + private readonly List _notBackupDirectory = ["fail", DirectoryName]; #region Public Methods @@ -70,7 +73,8 @@ public GeneralClientBootstrap SetConfig(Configinfo configInfo) BlackFiles = configInfo.BlackFiles, Platform = configInfo.Platform, ProductId = configInfo.ProductId, - UpgradeClientVersion = configInfo.UpgradeClientVersion + UpgradeClientVersion = configInfo.UpgradeClientVersion, + Bowl = configInfo.Bowl }; return this; } @@ -161,14 +165,15 @@ private async Task ExecuteWorkflowAsync() _configinfo.DownloadTimeOut = GetOption(UpdateOption.DownloadTimeOut) == 0 ? 60 : GetOption(UpdateOption.DownloadTimeOut); _configinfo.DriveEnabled = GetOption(UpdateOption.Drive) ?? false; _configinfo.TempPath = GeneralFileManager.GetTempDirectory("main_temp"); - + _configinfo.BackupDirectory = Path.Combine(_configinfo.InstallPath, $"{DirectoryName}{_configinfo.ClientVersion}"); + if (_configinfo.IsMainUpdate) { _configinfo.UpdateVersions = mainResp.Body.OrderBy(x => x.ReleaseDate).ToList(); _configinfo.LastVersion = _configinfo.UpdateVersions.Last().Version; - //var failed = CheckFail(_configinfo.LastVersion); - //if (failed) return; + var failed = CheckFail(_configinfo.LastVersion); + if (failed) return; //Initialize the process transfer parameter object. var processInfo = new ProcessInfo(_configinfo.MainAppName @@ -181,11 +186,14 @@ private async Task ExecuteWorkflowAsync() , _configinfo.DownloadTimeOut , _configinfo.AppSecretKey , mainResp.Body - , _configinfo.ReportUrl); + , _configinfo.ReportUrl + , _configinfo.BackupDirectory + , _configinfo.Bowl); _configinfo.ProcessInfo = JsonSerializer.Serialize(processInfo); } + GeneralFileManager.Backup(_configinfo.InstallPath, _configinfo.BackupDirectory, _notBackupDirectory); StrategyFactory(); switch (_configinfo.IsUpgradeUpdate) @@ -222,7 +230,7 @@ private async Task Download() } await manager.LaunchTasksAsync(); } - + /// /// Check if there has been a recent update failure. /// diff --git a/src/c#/GeneralUpdate.Common/FileBasic/GeneralFileManager.cs b/src/c#/GeneralUpdate.Common/FileBasic/GeneralFileManager.cs index 52da377b..b366cddd 100644 --- a/src/c#/GeneralUpdate.Common/FileBasic/GeneralFileManager.cs +++ b/src/c#/GeneralUpdate.Common/FileBasic/GeneralFileManager.cs @@ -110,7 +110,73 @@ public static bool HashEquals(string leftPath, string rightPath) var hashRight = hashAlgorithm.ComputeHash(rightPath); return hashLeft.SequenceEqual(hashRight); } + + /// + /// Backup the all program. + /// + /// + /// + /// + public static void Backup(string sourcePath, string backupPath, List directoryNames) + { + if (Directory.Exists(backupPath)) + { + Directory.Delete(backupPath, true); + } + Directory.CreateDirectory(backupPath); + CopyDirectory(sourcePath, backupPath, directoryNames); + } + + private static void CopyDirectory(string sourceDir, string targetDir, List directoryNames) + { + foreach (string dirPath in Directory.GetDirectories(sourceDir, "*", SearchOption.TopDirectoryOnly)) + { + if (!directoryNames.Any(name => Path.GetFileName(dirPath).Contains(name))) + { + string newTargetDir = Path.Combine(targetDir, Path.GetFileName(dirPath)); + Directory.CreateDirectory(newTargetDir); + CopyDirectory(dirPath, newTargetDir, directoryNames); + } + } + + foreach (string filePath in Directory.GetFiles(sourceDir, "*.*", SearchOption.TopDirectoryOnly)) + { + string newFilePath = Path.Combine(targetDir, Path.GetFileName(filePath)); + File.Copy(filePath, newFilePath, true); + } + } + /// + /// Restore the all program. + /// + /// + /// + public static void Restore(string backupPath, string sourcePath) + { + if (!Directory.Exists(sourcePath)) + { + Directory.CreateDirectory(sourcePath); + } + + CopyDirectory(backupPath, sourcePath); + } + + private static void CopyDirectory(string sourceDir, string targetDir) + { + foreach (string dirPath in Directory.GetDirectories(sourceDir, "*", SearchOption.TopDirectoryOnly)) + { + string newTargetDir = Path.Combine(targetDir, Path.GetFileName(dirPath)); + Directory.CreateDirectory(newTargetDir); + CopyDirectory(dirPath, newTargetDir); + } + + foreach (string filePath in Directory.GetFiles(sourceDir, "*.*", SearchOption.TopDirectoryOnly)) + { + string newFilePath = Path.Combine(targetDir, Path.GetFileName(filePath)); + File.Copy(filePath, newFilePath, true); + } + } + #endregion #region Private Methods diff --git a/src/c#/GeneralUpdate.Common/Shared/Object/Configinfo.cs b/src/c#/GeneralUpdate.Common/Shared/Object/Configinfo.cs index f111b33c..d6e09fc6 100644 --- a/src/c#/GeneralUpdate.Common/Shared/Object/Configinfo.cs +++ b/src/c#/GeneralUpdate.Common/Shared/Object/Configinfo.cs @@ -73,6 +73,8 @@ public class Configinfo /// public string ProductId { get; set; } + public string Bowl { get; set; } + public void Validate() { if (string.IsNullOrWhiteSpace(UpdateUrl) || !Uri.IsWellFormedUriString(UpdateUrl, UriKind.Absolute)) diff --git a/src/c#/GeneralUpdate.Common/Shared/Object/GlobalConfigInfo.cs b/src/c#/GeneralUpdate.Common/Shared/Object/GlobalConfigInfo.cs index 96400bc3..be0d8c79 100644 --- a/src/c#/GeneralUpdate.Common/Shared/Object/GlobalConfigInfo.cs +++ b/src/c#/GeneralUpdate.Common/Shared/Object/GlobalConfigInfo.cs @@ -76,7 +76,7 @@ public class GlobalConfigInfo public string UpgradeClientVersion { get; set; } /// - /// The latest version. + /// The main program latest version. /// public string LastVersion { get; set; } @@ -115,4 +115,8 @@ public class GlobalConfigInfo public string ProductId { get; set; } public Dictionary FieldMappings { get; set; } + + public string BackupDirectory { get; set; } + + public string Bowl { get; set; } } \ No newline at end of file diff --git a/src/c#/GeneralUpdate.Common/Shared/Object/ProcessInfo.cs b/src/c#/GeneralUpdate.Common/Shared/Object/ProcessInfo.cs index aacfb604..a116a04d 100644 --- a/src/c#/GeneralUpdate.Common/Shared/Object/ProcessInfo.cs +++ b/src/c#/GeneralUpdate.Common/Shared/Object/ProcessInfo.cs @@ -20,7 +20,9 @@ public ProcessInfo(string appName , int downloadTimeOut , string appSecretKey , List updateVersions - , string reportUrl) + , string reportUrl + , string backupDirectory + , string bowl) { AppName = appName ?? throw new ArgumentNullException(nameof(appName)); if (!Directory.Exists(installPath)) throw new ArgumentException($"{nameof(installPath)} path does not exist ! {installPath}."); @@ -36,6 +38,8 @@ public ProcessInfo(string appName if (updateVersions == null || updateVersions.Count == 0) throw new ArgumentException("Collection cannot be null or has 0 elements !"); UpdateVersions = updateVersions; ReportUrl = reportUrl ?? throw new ArgumentNullException(nameof(reportUrl)); + BackupDirectory = backupDirectory ?? throw new ArgumentNullException(nameof(backupDirectory)); + Bowl = bowl; } /// @@ -104,6 +108,15 @@ public ProcessInfo(string appName [JsonPropertyName("ReportUrl")] public string ReportUrl { get; set; } + /// + /// Back up the current version files that have not been updated. + /// + [JsonPropertyName("BackupDirectory")] + public string BackupDirectory { get; set; } + + [JsonPropertyName("Bowl")] + public string Bowl { get; set; } + private static int ToEncodingType(Encoding encoding) { var type = -1; diff --git a/src/c#/GeneralUpdate.Core/GeneralUpdateBootstrap.cs b/src/c#/GeneralUpdate.Core/GeneralUpdateBootstrap.cs index 0efe0a6f..cfb567b0 100644 --- a/src/c#/GeneralUpdate.Core/GeneralUpdateBootstrap.cs +++ b/src/c#/GeneralUpdate.Core/GeneralUpdateBootstrap.cs @@ -45,7 +45,8 @@ public GeneralUpdateBootstrap() AppSecretKey = processInfo.AppSecretKey, UpdateVersions = processInfo.UpdateVersions, TempPath = GeneralFileManager.GetTempDirectory("upgrade_temp"), - ReportUrl = processInfo.ReportUrl + ReportUrl = processInfo.ReportUrl, + BackupDirectory = processInfo.BackupDirectory }; } diff --git a/src/c#/GeneralUpdate.Core/Strategys/WindowsStrategy.cs b/src/c#/GeneralUpdate.Core/Strategys/WindowsStrategy.cs index 0ecb0a25..4236f669 100644 --- a/src/c#/GeneralUpdate.Core/Strategys/WindowsStrategy.cs +++ b/src/c#/GeneralUpdate.Core/Strategys/WindowsStrategy.cs @@ -104,8 +104,16 @@ public override void StartApp() UseShellExecute = true }); } - - Environment.SetEnvironmentVariable("ProcessInfo", null, EnvironmentVariableTarget.User); + + var bowlPath = Path.Combine(_configinfo.InstallPath, _configinfo.Bowl); + if (File.Exists(bowlPath)) + { + Process.Start(new ProcessStartInfo + { + FileName = bowlPath, + UseShellExecute = true + }); + } } catch (Exception e) { diff --git a/src/c#/GeneralUpdate.sln b/src/c#/GeneralUpdate.sln index cf1cae86..5df7cf3f 100644 --- a/src/c#/GeneralUpdate.sln +++ b/src/c#/GeneralUpdate.sln @@ -27,6 +27,8 @@ Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "GeneralUpdate.Common", "Gen EndProject Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "GeneralUpdate.Bowl", "GeneralUpdate.Bowl\GeneralUpdate.Bowl.csproj", "{49D0687D-1321-48E9-84C3-936B10532367}" EndProject +Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Generalupdate.CatBowl", "Generalupdate.CatBowl\Generalupdate.CatBowl.csproj", "{1BA0EEDF-D75A-49E9-9244-EA32DFA130B3}" +EndProject Global GlobalSection(SolutionConfigurationPlatforms) = preSolution Debug|Any CPU = Debug|Any CPU @@ -75,6 +77,10 @@ Global {49D0687D-1321-48E9-84C3-936B10532367}.Debug|Any CPU.Build.0 = Debug|Any CPU {49D0687D-1321-48E9-84C3-936B10532367}.Release|Any CPU.ActiveCfg = Release|Any CPU {49D0687D-1321-48E9-84C3-936B10532367}.Release|Any CPU.Build.0 = Release|Any CPU + {1BA0EEDF-D75A-49E9-9244-EA32DFA130B3}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {1BA0EEDF-D75A-49E9-9244-EA32DFA130B3}.Debug|Any CPU.Build.0 = Debug|Any CPU + {1BA0EEDF-D75A-49E9-9244-EA32DFA130B3}.Release|Any CPU.ActiveCfg = Release|Any CPU + {1BA0EEDF-D75A-49E9-9244-EA32DFA130B3}.Release|Any CPU.Build.0 = Release|Any CPU EndGlobalSection GlobalSection(SolutionProperties) = preSolution HideSolutionNode = FALSE @@ -90,6 +96,7 @@ Global {7779FB4A-D121-48CC-B033-C3D36BD5D4FF} = {74BE0282-A10D-4A81-A0F0-FAA79A6152B7} {D14E59CD-404B-467B-9C6D-91EFC5994D37} = {91F059E6-7AD3-4FB7-9604-30A7849C6EFF} {49D0687D-1321-48E9-84C3-936B10532367} = {91F059E6-7AD3-4FB7-9604-30A7849C6EFF} + {1BA0EEDF-D75A-49E9-9244-EA32DFA130B3} = {74BE0282-A10D-4A81-A0F0-FAA79A6152B7} EndGlobalSection GlobalSection(ExtensibilityGlobals) = postSolution SolutionGuid = {A7B2D0AD-E000-4749-BAC0-FF21B9872805} diff --git a/src/c#/Generalupdate.CatBowl/Generalupdate.CatBowl.csproj b/src/c#/Generalupdate.CatBowl/Generalupdate.CatBowl.csproj new file mode 100644 index 00000000..43b5bfa7 --- /dev/null +++ b/src/c#/Generalupdate.CatBowl/Generalupdate.CatBowl.csproj @@ -0,0 +1,14 @@ + + + + Exe + net8.0 + enable + enable + + + + + + + diff --git a/src/c#/Generalupdate.CatBowl/Program.cs b/src/c#/Generalupdate.CatBowl/Program.cs new file mode 100644 index 00000000..bae9309e --- /dev/null +++ b/src/c#/Generalupdate.CatBowl/Program.cs @@ -0,0 +1,33 @@ +using System.Text.Json; +using GeneralUpdate.Bowl; +using GeneralUpdate.Bowl.Strategys; +using GeneralUpdate.Common.Shared.Object; + +namespace Generalupdate.CatBowl; + +class Program +{ + static void Main(string[] args) + { + var processInfo = TestProcessInfo(); + Bowl.Launch(processInfo); + Console.Read(); + } + + private static MonitorParameter TestProcessInfo() + { + var path = @"D:\packet\test.json"; + var json = File.ReadAllText(path); + var processInfo = JsonSerializer.Deserialize(json); + return new MonitorParameter + { + ProcessNameOrId = processInfo.AppName, + DumpFileName = $"{processInfo.LastVersion}_fail.dmp", + FailFileName = $"{processInfo.LastVersion}_fail.json", + TargetPath = processInfo.InstallPath, + FailDirectory = Path.Combine(processInfo.InstallPath, "fail", processInfo.LastVersion), + BackupDirectory = Path.Combine(processInfo.InstallPath, processInfo.LastVersion), + WorkModel = "Normal" + }; + } +} \ No newline at end of file