diff --git a/.github/workflows/dotnet-ci.yaml b/.github/workflows/dotnet-ci.yaml index a239ad23d0f553ad2d362e269dc2f553e5835cf8..7020725481058328c92e8c3eb071e693baad2bd0 100644 --- a/.github/workflows/dotnet-ci.yaml +++ b/.github/workflows/dotnet-ci.yaml @@ -11,9 +11,7 @@ jobs: - name: Setup .NET SDK uses: actions/setup-dotnet@v2 with: - dotnet-version: '8.0.x' - - name: Install .NET MAUI - run: dotnet workload install maui + dotnet-version: '9.0.x' - name: Restore dependencies run: dotnet restore ./src/c#/GeneralUpdate.sln - name: build diff --git a/README.md b/README.md index 2c9d97f2b8d9682b201a0908d4d56edebde320d6..f66208c11a1168f9f96b16fa40a90da0bedb063f 100644 --- a/README.md +++ b/README.md @@ -2,133 +2,90 @@ ![](https://img.shields.io/github/license/JusterZhu/GeneralUpdate?color=blue) -![](imgs/GeneralUpdate_h.png) +![](imgs/GeneralUpdate_h2.png) [English introduction](https://github.com/JusterZhu/GeneralUpdate/blob/master/README_en.md) ## 1.组件介绍 ## -- GeneralUpdate是一款基于.NET Standard2.0开源自动升级组件。 -- 运行环境:.NET7、.NET MAUI、Visual studio 2022(Preview) - -![1708869360661](imgs/1708869360661.jpg) - -| 功能 | 是否支持 | 备注 | -| ------------------------------ | -------- | ------------------------------------------------------------ | -| 断点续传 | 支持 | 单次更新失败时,下次一次启动时继续上一次更新下载更新包内容。(引用组件默认生效) | -| 逐版本更新 | 支持 | 客户端当前版本如果与服务器相差多个版本,则根据多个版本的发布日期逐个更新。(引用组件默认生效) | -| 二进制差分更新 | 支持 | 对比新老版本通过差分算法生成补丁文件。(引用组件默认生效) | -| 增量更新功能 | 支持 | 相比上一个版本只更新当前修改过的文件,并且删除当前版本不存在的文件。(引用组件默认生效) | -| 强制更新 | 支持 | 打开客户端之后直接强制更新。 | -| 多分支更新 | 支持 | 当一个产品有多个分支时,需要根据不同的分支更新对应的内容。 | -| 最新版本推送 | 支持 | 基于Signal R实现,推送当前最新版本。 | -| 客户端程序、服务端程序应用更新 | 支持 | C/S和B/S程序均可使用。 | -| 多平台、操作系统 | 部分支持 | Windows、MAUI Android平台 | -| 多语言 | 待验证 | 也可将本组件编写为控制台程序,作为更新“脚本”。更新其他语言的应用程序。 | -| 跳过更新 | 支持 | 支持注入弹窗让用户决定是否更新本次发布,服务端决定强制时更新不生效。 | -| 相互升级 | 支持 | 主程序可更新升级程序,升级程序可更新主程序。 | -| 黑名单 | 支持 | 在更新过程中会跳过黑名单中的文件列表和文件扩展名列表。 | -| OSS | 支持 | 极简化更新,是一套独立的更新机制。只需要在文件服务器中放置version.json的版本配置文件。组件会根据配置文件中的版本信息进行更新下载。(支持Windows,MAUI Android) | -| 回滚 | 待测试 | 逐版本更新时会备份每个版本,如果更新失败则逐版本回滚。 | -| 驱动更新 | 待测试 | 逐版本更新时会备份每个版本的驱动文件(.inf),如果更新失败则逐版本回滚。 | -| 遗言 | 待测试 | 开机时和升级时会检查升级是否成功,如果失败则根据遗言还原之前的备份。遗言是更新之前就已经自动创建在C:\generalupdate_willmessages目录下的will_message.json文件。will_message.json的内容是持久化回滚备份的文件目录相关信息。(需要部署GeneralUpdate.SystemService系统服务) | -| 自定义方法列表 | 支持 | 注入一个自定义方法集合,该集合会在更新启动前执行。执行自定义方法列表如果出现任何异常,将通过异常订阅通知。(推荐在更新之前检查当前软件环境) | - - - -## 2.帮助文档 ## - -- 讲解视频: https://www.bilibili.com/video/BV1aX4y137dd -- 官方网站: http://justerzhu.cn/ -- 快速启动: https://mp.weixin.qq.com/s/pRKPFe3eC0NSqv9ixXEiTg -- 使用教程视频:https://www.bilibili.com/video/BV1FT4y1Y7hV -- 文档:https://gitee.com/GeneralLibrary/GeneralUpdate/tree/master/doc +GeneralUpdate是一款基于.NET Standard2.0开源自动升级组件。 + +帮助文档 -## 3.开源地址 ## +- 官方网站: https://www.justerzhu.cn/ +- 使用教程视频:https://www.bilibili.com/video/BV1FT4y1Y7hV -### 3.1当前项目GeneralUpdate +当前项目GeneralUpdate - https://github.com/JusterZhu/GeneralUpdate - https://gitee.com/Juster-zhu/GeneralUpdate -### 3.2打包工具项目地址GeneralUpdate.Tools +MAUI GeneralUpdate.Maui + +- https://github.com/GeneralLibrary/GeneralUpdate.Maui + +打包工具项目地址GeneralUpdate.Tools - https://github.com/GeneralLibrary/GeneralUpdate.Tools - https://gitee.com/GeneralTeam/GeneralUpdate.Tools -### 3.3示例项目地址GeneralUpdate-Samples +示例项目地址GeneralUpdate-Samples - https://github.com/GeneralLibrary/GeneralUpdate-Samples - https://gitee.com/GeneralTeam/GeneralUpdate-Samples +功能介绍: + +| 功能 | 是否支持 | 备注 | +| -------------- | -------- | ------------------------------------------------------------ | +| 断点续传 | 支持 | 单次更新失败时,下次一次启动时继续上一次更新下载更新包内容。(引用组件默认生效) | +| 逐版本更新 | 支持 | 客户端当前版本如果与服务器相差多个版本,则根据多个版本的发布日期逐个更新。(引用组件默认生效) | +| 二进制差分更新 | 支持 | 对比新老版本通过差分算法生成补丁文件。(引用组件默认生效) | +| 增量更新功能 | 支持 | 相比上一个版本只更新当前修改过的文件,并且删除当前版本不存在的文件。(引用组件默认生效) | +| 强制更新 | 支持 | 打开客户端之后直接强制更新。 | +| 多分支更新 | 支持 | 当一个产品有多个分支时,需要根据不同的分支更新对应的内容。 | +| 最新版本推送 | 支持 | 基于Signal R实现,推送当前最新版本。 | +| 多语言 | 待验证 | 也可将本组件编写为控制台程序,作为更新“脚本”。更新其他语言的应用程序。 | +| 跳过更新 | 支持 | 支持注入弹窗让用户决定是否更新本次发布,服务端决定强制时更新不生效。 | +| 相互升级 | 支持 | 主程序可更新升级程序,升级程序可更新主程序。 | +| 黑名单 | 支持 | 在更新过程中会跳过黑名单中的文件列表和文件扩展名列表。 | +| OSS | 支持 | 极简化更新,是一套独立的更新机制。只需要在文件服务器中放置version.json的版本配置文件。组件会根据配置文件中的版本信息进行更新下载。 | +| 回滚、备份 | 支持 | 更新之前会将客户端本地文件备份,如果客户端启动失败或崩溃则回滚覆盖。 | +| 驱动更新 | 待验证 | 更新之前会将驱动备份到本地,如果客户端启动失败或崩溃则回滚覆盖。 | +| 自定义方法列表 | 支持 | 注入一个自定义方法集合,该集合会在更新启动前执行。执行自定义方法列表如果出现任何异常,将通过异常订阅通知。(推荐在更新之前检查当前软件环境) | +| AOT | 支持 | 支持AOT编译发布。 | -## 4.支持框架 -| 框架名称 | 是否支持 | +## 2.支持框架 + +| .NET框架名称 | 是否支持 | | -------------------------- | -------- | | .NET Core 2.0 | 支持 | | .NET 5 ... to last version | 支持 | | .NET Framework 4.6.1 | 支持 | -| UI框架名称 | 是否支持 | -| ----------------- | --------------------- | -| WPF | 支持 | -| UWP | 商店模式下不可更新 | -| MAUI | 目前仅支持Android平台 | -| Avalonia | 支持 | -| WinUI | 待验证,等待反馈 | -| Console(控制台) | 支持 | -| Winform | 支持 | - -| 服务端框架 | 是否支持 | -| ---------- | -------- | -| ASP.NET | 待验证 | +| UI框架名称 | 是否支持 | +| ---------- | --------------------- | +| WPF | 支持 | +| UWP | 商店模式下不可更新 | +| MAUI | 目前仅支持Android平台 | +| Avalonia | 支持 | +| WinUI | 支持 | +| Console | 支持 | +| WinForms | 支持 | -## 5.操作系统 +## 3.操作系统 | 操作系统名称 | 是否支持 | | ------------ | -------- | | Windows | 支持 | -| Linux | 待验证 | -| Mac | 待验证 | +| Linux | 支持 | | Android | 支持 | -| 树莓派(IoT) | 待验证 | | 麒麟V10(飞腾S2500) | 支持 | | 麒麟V10(x64) | 支持 | - - - -## 6.GeneralUpdate.SystemService发布/部署 - -GeneralUpdate.SystemService是一个windows系统服务,并不是部署在服务端的web api。它的主要作用是监听更新过程,以及更新崩溃之后还原。 - -**发布:** - -推荐发布Single file,如果想发布AOT版本需要移除源码中映射代码。 - -```shell -dotnet publish -r win-x64 -p:PublishSingleFile=true -p:IncludeNativeLibrariesForSelfExtract=true --self-contained true -``` - -**创建/部署windows服务:** - -```shell -sc create MyWorkerService binPath="C:\your_path\GeneralUpdate.SystemService.exe" -``` - -**启动已部署的windows服务:** - -```shell -sc start GeneralUpdate.SystemService -``` - -**删除已部署的windows服务:** - -```shell -sc delete GeneralUpdate.SystemService -``` - +| Ubuntu | 支持 | +| 龙芯(Loongnix) | 待验证 | diff --git a/README_en.md b/README_en.md index 537126a1e947f2114cb379dc51bf2e2b2fa46da5..d9f6e9315a3d69660ac049e21834baf17b450e6f 100644 --- a/README_en.md +++ b/README_en.md @@ -92,36 +92,3 @@ | raspberry pie | Not currently supported | | Kylin V10 (FT-S2500) | yes | | Kylin V10 (x64) | yes | - - - -## GeneralUpdate.SystemService Publish/Deploy - -GeneralUpdate.SystemService is a Windows system service, not a web API deployed on the server. Its main purpose is to listen for the update process and restore after an update crash. - -**Publish:** - -It is recommended to release a single file, if you want to release the AOT version, you need to remove the mapping code from the source code. - -```shell -dotnet publish -r win-x64 -p:PublishSingleFile=true -p:IncludeNativeLibrariesForSelfExtract=true --self-contained true -``` - -**Create/deploy Windows services:** - -```shell -sc create MyWorkerService binPath="C:\your_path\GeneralUpdate.SystemService.exe" -``` - -**Start the deployed Windows service:** - -```shell -sc start GeneralUpdate.SystemService -``` - -**Delete the deployed Windows service:** - -```shell -sc delete GeneralUpdate.SystemService -``` - diff --git a/imgs/1708869360661.jpg b/imgs/1708869360661.jpg deleted file mode 100644 index d2ddd8c5d6c9afdde41f8f2a2c3f0b070cde3493..0000000000000000000000000000000000000000 Binary files a/imgs/1708869360661.jpg and /dev/null differ diff --git a/imgs/GeneralUpdate_h2.png b/imgs/GeneralUpdate_h2.png new file mode 100644 index 0000000000000000000000000000000000000000..1e77f86315cd676e3336249689d7213d3628f2f0 Binary files /dev/null and b/imgs/GeneralUpdate_h2.png differ diff --git a/imgs/bowl.jpeg b/imgs/bowl.jpeg new file mode 100644 index 0000000000000000000000000000000000000000..b099e2163483804fb316c5e3175e91529696f139 Binary files /dev/null and b/imgs/bowl.jpeg differ 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 0000000000000000000000000000000000000000..80921e8ec4144b4314045f9b6c6c0d53494e9f54 --- /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 fc828ebbb201e5b2214381b8c19670bfeac8d8c0..e9556b4d335d68249a6c12960df22023196cb525 100644 --- a/src/c#/GeneralUpdate.Bowl/Bowl.cs +++ b/src/c#/GeneralUpdate.Bowl/Bowl.cs @@ -1,39 +1,58 @@ using System; +using System.IO; using System.Runtime.InteropServices; +using System.Text.Json; using GeneralUpdate.Bowl.Strategys; +using GeneralUpdate.Common.Internal.JsonContext; +using GeneralUpdate.Common.Shared.Object; namespace GeneralUpdate.Bowl; -public class Bowl +/// +/// Surveillance Main Program. +/// +public sealed class Bowl { - private IStrategy _strategy; + private static IStrategy? _strategy; - public Bowl(MonitorParameter parameter = null) - { - CreateStrategy(); - _strategy!.SetParameter(parameter); - } + private Bowl() { } - private void CreateStrategy() + private static void CreateStrategy() { if (RuntimeInformation.IsOSPlatform(OSPlatform.Windows)) - { _strategy = new WindowStrategy(); - } - else if (RuntimeInformation.IsOSPlatform(OSPlatform.Linux)) - { - _strategy = new LinuxStrategy(); - } + + if (_strategy == null) + throw new PlatformNotSupportedException("Unsupported operating system"); + } + + public static void Launch(MonitorParameter? monitorParameter = null) + { + monitorParameter ??= CreateParameter(); + CreateStrategy(); + _strategy?.SetParameter(monitorParameter); + _strategy?.Launch(); } - public Bowl SetParameter(MonitorParameter parameter) + private static MonitorParameter CreateParameter() { - if(parameter.Verify()) - throw new ArgumentException("Parameter contains illegal values"); + var json = Environment.GetEnvironmentVariable("ProcessInfo", EnvironmentVariableTarget.User); + if(string.IsNullOrWhiteSpace(json)) + throw new ArgumentNullException("ProcessInfo environment variable not set !"); - _strategy.SetParameter(parameter); - return this; + var processInfo = JsonSerializer.Deserialize(json, ProcessInfoJsonContext.Default.ProcessInfo); + if(processInfo == null) + throw new ArgumentNullException("ProcessInfo json deserialize fail!"); + + 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 + }; } - - public void Launch() => _strategy.Launch(); } \ 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 3c0d6140729b9d79d2041cdfb1ac0587211403d1..0c47f789a967899071f599befa9aa37686d53ffd 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/Internal/Crash.cs b/src/c#/GeneralUpdate.Bowl/Internal/Crash.cs new file mode 100644 index 0000000000000000000000000000000000000000..2b783c1194363a4054df8ad4dad33d2cfbb5329f --- /dev/null +++ b/src/c#/GeneralUpdate.Bowl/Internal/Crash.cs @@ -0,0 +1,11 @@ +using System.Collections.Generic; +using GeneralUpdate.Bowl.Strategys; + +namespace GeneralUpdate.Bowl.Internal; + +internal 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/Internal/CrashJsonContext.cs b/src/c#/GeneralUpdate.Bowl/Internal/CrashJsonContext.cs new file mode 100644 index 0000000000000000000000000000000000000000..1c3f8ab1f144a0d0b475d5924f52b85acccaea2b --- /dev/null +++ b/src/c#/GeneralUpdate.Bowl/Internal/CrashJsonContext.cs @@ -0,0 +1,6 @@ +using System.Text.Json.Serialization; + +namespace GeneralUpdate.Bowl.Internal; + +[JsonSerializable(typeof(Crash))] +internal partial class CrashJsonContext : JsonSerializerContext; \ No newline at end of file diff --git a/src/c#/GeneralUpdate.Bowl/Strategys/LinuxSystem.cs b/src/c#/GeneralUpdate.Bowl/Internal/LinuxSystem.cs similarity index 84% rename from src/c#/GeneralUpdate.Bowl/Strategys/LinuxSystem.cs rename to src/c#/GeneralUpdate.Bowl/Internal/LinuxSystem.cs index 4db1dbec39a502733f76e2e46ad8a4cf996354d4..66903efb6ed4c4af5fc3075a5f0e625251d98d5f 100644 --- a/src/c#/GeneralUpdate.Bowl/Strategys/LinuxSystem.cs +++ b/src/c#/GeneralUpdate.Bowl/Internal/LinuxSystem.cs @@ -1,4 +1,4 @@ -namespace GeneralUpdate.Bowl.Strategys; +namespace GeneralUpdate.Bowl.Internal; internal class LinuxSystem { diff --git a/src/c#/GeneralUpdate.Bowl/Strategys/AbstractStrategy.cs b/src/c#/GeneralUpdate.Bowl/Strategys/AbstractStrategy.cs index dcf8f2750fdff690818628b26b954cbcb1022f07..c6bc12c3222fedc0ab4a7a33c67557deed41f714 100644 --- a/src/c#/GeneralUpdate.Bowl/Strategys/AbstractStrategy.cs +++ b/src/c#/GeneralUpdate.Bowl/Strategys/AbstractStrategy.cs @@ -1,32 +1,36 @@ using System.Collections.Generic; using System.Diagnostics; using System.IO; +using GeneralUpdate.Common.FileBasic; namespace GeneralUpdate.Bowl.Strategys; -public abstract class AbstractStrategy : IStrategy +internal abstract class AbstractStrategy : IStrategy { protected MonitorParameter _parameter; - - private readonly IReadOnlyList _sensitiveCharacter = new List - { - "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)) + { + StorageManager.DeleteDirectory(_parameter.FailDirectory); + } + Directory.CreateDirectory(_parameter.FailDirectory); + var startInfo = new ProcessStartInfo { FileName = appName, Arguments = arguments, RedirectStandardOutput = true, + RedirectStandardError = true, UseShellExecute = false, CreateNoWindow = true }; @@ -35,71 +39,15 @@ public abstract class AbstractStrategy : IStrategy 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/IStrategy.cs b/src/c#/GeneralUpdate.Bowl/Strategys/IStrategy.cs index d0a58e9f9046bc9ef4d483718dcb07778b79d96d..6b4298f0d2a612b18d4926fa2b7cb129e8546a75 100644 --- a/src/c#/GeneralUpdate.Bowl/Strategys/IStrategy.cs +++ b/src/c#/GeneralUpdate.Bowl/Strategys/IStrategy.cs @@ -1,6 +1,8 @@ -namespace GeneralUpdate.Bowl.Strategys; +using GeneralUpdate.Bowl.Internal; -public interface IStrategy +namespace GeneralUpdate.Bowl.Strategys; + +internal interface IStrategy { void Launch(); diff --git a/src/c#/GeneralUpdate.Bowl/Strategys/LinuxStrategy.cs b/src/c#/GeneralUpdate.Bowl/Strategys/LinuxStrategy.cs index 64f300c515900e5d04655dc7bb2cfb7580a255bc..7f38e923d518c5331f298354e614054a71c06ad0 100644 --- a/src/c#/GeneralUpdate.Bowl/Strategys/LinuxStrategy.cs +++ b/src/c#/GeneralUpdate.Bowl/Strategys/LinuxStrategy.cs @@ -3,19 +3,22 @@ using System.Collections.Generic; using System.Diagnostics; using System.IO; using System.Linq; +using GeneralUpdate.Bowl.Internal; namespace GeneralUpdate.Bowl.Strategys; -public class LinuxStrategy : AbstractStrategy +internal 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" }; @@ -64,9 +67,9 @@ public class LinuxStrategy : AbstractStrategy private string GetPacketName() { - string packageFileName = string.Empty; - LinuxSystem system = GetSystem(); - if (procdump_amd64.Contains(system.Name)) + var packageFileName = string.Empty; + var system = GetSystem(); + if (_rocdumpAmd64.Contains(system.Name)) { packageFileName = $"procdump_3.3.0_amd64.deb"; } @@ -105,9 +108,7 @@ public class LinuxStrategy : AbstractStrategy 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.Bowl/Strategys/MonitorParameter.cs b/src/c#/GeneralUpdate.Bowl/Strategys/MonitorParameter.cs index 53d49e612eb96c49dcbb2e2bcfbe21e648987305..d53d2f309f0c2b453b3c84266a3d7a3faa55856c 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 da32bcd6c977c1a40dd6b1c2f9a1c0ccb60865ca..dbc77f5bbfa6c5c0550b92bde69d4993d4c2146d 100644 --- a/src/c#/GeneralUpdate.Bowl/Strategys/WindowStrategy.cs +++ b/src/c#/GeneralUpdate.Bowl/Strategys/WindowStrategy.cs @@ -1,23 +1,105 @@ -using System.Runtime.InteropServices; +using System; +using System.Collections.Generic; +using System.Diagnostics; +using System.IO; +using System.Runtime.InteropServices; +using GeneralUpdate.Bowl.Internal; +using GeneralUpdate.Common.FileBasic; namespace GeneralUpdate.Bowl.Strategys; -public class WindowStrategy : AbstractStrategy +internal 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); + StorageManager.CreateJson(failJsonPath, crash, CrashJsonContext.Default.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)) + StorageManager.Restore(_parameter.BackupDirectory, _parameter.TargetPath); + } + + /// + /// Write the failed update version number to the local environment variable. + /// + private void SetEnvironment() + { + if (!string.Equals(_parameter.WorkModel, WorkModel)) + return; + + /* + * The `UpgradeFail` environment variable is used to mark an exception version number during updates. + * If the latest version number obtained via an HTTP request is less than or equal to the exception version number, the update is skipped. + * Once this version number is set, it will not be removed, and updates will not proceed until a version greater than the exception version number is obtained through the HTTP request. + */ + Environment.SetEnvironmentVariable("UpgradeFail", _parameter.ExtendedField, EnvironmentVariableTarget.User); } } \ No newline at end of file diff --git a/src/c#/GeneralUpdate.Client/GeneralUpdate.Client.csproj b/src/c#/GeneralUpdate.Client/GeneralUpdate.Client.csproj index 94ea81d5b8a99c500de3b09bda23e96227a07b29..3c07e165e287c053adf840647e9d04263fff55ea 100644 --- a/src/c#/GeneralUpdate.Client/GeneralUpdate.Client.csproj +++ b/src/c#/GeneralUpdate.Client/GeneralUpdate.Client.csproj @@ -5,9 +5,14 @@ net8.0 enable enable - + true + default + + + + diff --git a/src/c#/GeneralUpdate.Client/MySample.cs b/src/c#/GeneralUpdate.Client/MySample.cs deleted file mode 100644 index d602edbcd514c288bdff9a812648c2dfb4d66f3f..0000000000000000000000000000000000000000 --- a/src/c#/GeneralUpdate.Client/MySample.cs +++ /dev/null @@ -1,322 +0,0 @@ -using GeneralUpdate.ClientCore; -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; -using GeneralUpdate.Differential; -using System.Diagnostics; -using System.Text; - -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) - .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(); - } - - 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 测试驱动功能 - - 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. - /// - /// - /// - 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.Client/Program.cs b/src/c#/GeneralUpdate.Client/Program.cs index 1b9dd51c972a8f7125b322c96aa9cb064d97f72a..328636ac816cb6eb28981234cc9279bdd0a6ebf7 100644 --- a/src/c#/GeneralUpdate.Client/Program.cs +++ b/src/c#/GeneralUpdate.Client/Program.cs @@ -1,28 +1,123 @@ -namespace GeneralUpdate.Client +using System.Diagnostics; +using System.Text; +using GeneralUpdate.ClientCore; +using GeneralUpdate.ClientCore.Hubs; +using GeneralUpdate.Common.Download; +using GeneralUpdate.Common.Internal; +using GeneralUpdate.Common.Internal.Bootstrap; +using GeneralUpdate.Common.Shared.Object; +using GeneralUpdate.Common.Shared.Object.Enum; + +namespace GeneralUpdate.Client { internal class Program { - private static void Main(string[] args) + static async Task Main(string[] args) + { + /*try + { + Console.WriteLine($"主程序初始化,{DateTime.Now}!"); + Console.WriteLine("当前运行目录:" + Thread.GetDomain().BaseDirectory); + await Task.Delay(2000); + var configinfo = new Configinfo + { + //configinfo.UpdateLogUrl = "https://www.baidu.com"; + ReportUrl = "http://127.0.0.1:5000/Upgrade/Report", + UpdateUrl = "http://127.0.0.1:5000/Upgrade/Verification", + AppName = "GeneralUpdate.Upgrad.exe", + MainAppName = "GeneralUpdate.Client.exe", + InstallPath = Thread.GetDomain().BaseDirectory, + //configinfo.Bowl = "Generalupdate.CatBowl.exe"; + //当前客户端的版本号 + ClientVersion = "1.0.0.0", + //当前升级端的版本号 + UpgradeClientVersion = "1.0.0.0", + //产品id + ProductId = "2d974e2a-31e6-4887-9bb1-b4689e98c77a", + //应用密钥 + AppSecretKey = "dfeb5833-975e-4afb-88f1-6278ee9aeff6" + }; + _ = await new GeneralClientBootstrap() //单个或多个更新包下载通知事件 + //单个或多个更新包下载速度、剩余下载事件、当前下载版本信息通知事件 + .AddListenerMultiDownloadStatistics(OnMultiDownloadStatistics) + //单个或多个更新包下载完成 + .AddListenerMultiDownloadCompleted(OnMultiDownloadCompleted) + //完成所有的下载任务通知 + .AddListenerMultiAllDownloadCompleted(OnMultiAllDownloadCompleted) + //下载过程出现的异常通知 + .AddListenerMultiDownloadError(OnMultiDownloadError) + //整个更新过程出现的任何问题都会通过这个事件通知 + .AddListenerException(OnException) + .SetConfig(configinfo) + .Option(UpdateOption.DownloadTimeOut, 60) + .Option(UpdateOption.Encoding, Encoding.UTF8) + .LaunchAsync(); + Console.WriteLine($"主程序已启动,{DateTime.Now}!"); + } + catch (Exception e) + { + Console.WriteLine(e.Message + "\n" + e.StackTrace); + }*/ + + /*var paramsOSS = new GlobalConfigInfoOSS(); + paramsOSS.Url = "http://192.168.50.203/versions.json"; + paramsOSS.CurrentVersion = "1.0.0.0"; + paramsOSS.VersionFileName = "versions.json"; + paramsOSS.AppName = "GeneralUpdate.Client.exe"; + paramsOSS.Encoding = Encoding.UTF8.WebName; + GeneralClientOSS.Start(paramsOSS);*/ + + var hub = new UpgradeHubService("http://localhost:5000/UpgradeHub" + , null,"dfeb5833-975e-4afb-88f1-6278ee9aeff6"); + hub.AddListenerReceive((message) => + { + Debug.WriteLine(message); + }); + await hub.StartAsync(); + + /*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); + });*/ + + while (true) + { + var content = Console.ReadLine(); + if (content == "exit") break; + } + } + + private static void OnMultiDownloadError(object arg1, MultiDownloadErrorEventArgs arg2) + { + var version = arg2.Version as VersionInfo; + Console.WriteLine($"{version.Version} {arg2.Exception}"); + } + + private static void OnMultiAllDownloadCompleted(object arg1, MultiAllDownloadCompletedEventArgs arg2) + { + Console.WriteLine(arg2.IsAllDownloadCompleted ? "所有的下载任务已完成!" : $"下载任务已失败!{arg2.FailedVersions.Count}"); + } + + private static void OnMultiDownloadCompleted(object arg1, MultiDownloadCompletedEventArgs arg2) + { + var version = arg2.Version as VersionInfo; + Console.WriteLine(arg2.IsComplated ? $"当前下载版本:{version.Version}, 下载完成!" : $"当前下载版本:{version.Version}, 下载失败!"); + } + + private static void OnMultiDownloadStatistics(object arg1, MultiDownloadStatisticsEventArgs arg2) + { + var version = arg2.Version as VersionInfo; + Console.WriteLine($"当前下载版本:{version.Version},下载速度:{arg2.Speed},剩余下载时间:{arg2.Remaining},已下载大小:{arg2.BytesReceived},总大小:{arg2.TotalBytesToReceive}, 进度百分比:{arg2.ProgressPercentage}%"); + } + + private static void OnException(object arg1, ExceptionEventArgs arg2) { - 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(); + Console.WriteLine($"{arg2.Exception}"); } } } \ 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 e69de29bb2d1d6434b8b29ae775ad8c2e48c5391..0000000000000000000000000000000000000000 diff --git a/src/c#/GeneralUpdate.ClientCore/CustomAwaiter/.gitkeep b/src/c#/GeneralUpdate.ClientCore/CustomAwaiter/.gitkeep deleted file mode 100644 index e69de29bb2d1d6434b8b29ae775ad8c2e48c5391..0000000000000000000000000000000000000000 diff --git a/src/c#/GeneralUpdate.ClientCore/Differential/.gitkeep b/src/c#/GeneralUpdate.ClientCore/Differential/.gitkeep deleted file mode 100644 index e69de29bb2d1d6434b8b29ae775ad8c2e48c5391..0000000000000000000000000000000000000000 diff --git a/src/c#/GeneralUpdate.ClientCore/Differential/Binary/.gitkeep b/src/c#/GeneralUpdate.ClientCore/Differential/Binary/.gitkeep deleted file mode 100644 index e69de29bb2d1d6434b8b29ae775ad8c2e48c5391..0000000000000000000000000000000000000000 diff --git a/src/c#/GeneralUpdate.ClientCore/Differential/ContentProvider/.gitkeep b/src/c#/GeneralUpdate.ClientCore/Differential/ContentProvider/.gitkeep deleted file mode 100644 index e69de29bb2d1d6434b8b29ae775ad8c2e48c5391..0000000000000000000000000000000000000000 diff --git a/src/c#/GeneralUpdate.ClientCore/Differential/GStream/.gitkeep b/src/c#/GeneralUpdate.ClientCore/Differential/GStream/.gitkeep deleted file mode 100644 index e69de29bb2d1d6434b8b29ae775ad8c2e48c5391..0000000000000000000000000000000000000000 diff --git a/src/c#/GeneralUpdate.ClientCore/Domain/.gitkeep b/src/c#/GeneralUpdate.ClientCore/Domain/.gitkeep deleted file mode 100644 index e69de29bb2d1d6434b8b29ae775ad8c2e48c5391..0000000000000000000000000000000000000000 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 e69de29bb2d1d6434b8b29ae775ad8c2e48c5391..0000000000000000000000000000000000000000 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 e69de29bb2d1d6434b8b29ae775ad8c2e48c5391..0000000000000000000000000000000000000000 diff --git a/src/c#/GeneralUpdate.ClientCore/Domain/Enum/.gitkeep b/src/c#/GeneralUpdate.ClientCore/Domain/Enum/.gitkeep deleted file mode 100644 index e69de29bb2d1d6434b8b29ae775ad8c2e48c5391..0000000000000000000000000000000000000000 diff --git a/src/c#/GeneralUpdate.ClientCore/Domain/PO/.gitkeep b/src/c#/GeneralUpdate.ClientCore/Domain/PO/.gitkeep deleted file mode 100644 index e69de29bb2d1d6434b8b29ae775ad8c2e48c5391..0000000000000000000000000000000000000000 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 e69de29bb2d1d6434b8b29ae775ad8c2e48c5391..0000000000000000000000000000000000000000 diff --git a/src/c#/GeneralUpdate.ClientCore/Domain/Service/.gitkeep b/src/c#/GeneralUpdate.ClientCore/Domain/Service/.gitkeep deleted file mode 100644 index e69de29bb2d1d6434b8b29ae775ad8c2e48c5391..0000000000000000000000000000000000000000 diff --git a/src/c#/GeneralUpdate.ClientCore/Domain/VO/.gitkeep b/src/c#/GeneralUpdate.ClientCore/Domain/VO/.gitkeep deleted file mode 100644 index e69de29bb2d1d6434b8b29ae775ad8c2e48c5391..0000000000000000000000000000000000000000 diff --git a/src/c#/GeneralUpdate.ClientCore/Download/.gitkeep b/src/c#/GeneralUpdate.ClientCore/Download/.gitkeep deleted file mode 100644 index e69de29bb2d1d6434b8b29ae775ad8c2e48c5391..0000000000000000000000000000000000000000 diff --git a/src/c#/GeneralUpdate.ClientCore/Driver/.gitkeep b/src/c#/GeneralUpdate.ClientCore/Driver/.gitkeep deleted file mode 100644 index e69de29bb2d1d6434b8b29ae775ad8c2e48c5391..0000000000000000000000000000000000000000 diff --git a/src/c#/GeneralUpdate.ClientCore/Events/CommonArgs/.gitkeep b/src/c#/GeneralUpdate.ClientCore/Events/CommonArgs/.gitkeep deleted file mode 100644 index e69de29bb2d1d6434b8b29ae775ad8c2e48c5391..0000000000000000000000000000000000000000 diff --git a/src/c#/GeneralUpdate.ClientCore/Events/MultiEventArgs/.gitkeep b/src/c#/GeneralUpdate.ClientCore/Events/MultiEventArgs/.gitkeep deleted file mode 100644 index e69de29bb2d1d6434b8b29ae775ad8c2e48c5391..0000000000000000000000000000000000000000 diff --git a/src/c#/GeneralUpdate.ClientCore/Exceptions/.gitkeep b/src/c#/GeneralUpdate.ClientCore/Exceptions/.gitkeep deleted file mode 100644 index e69de29bb2d1d6434b8b29ae775ad8c2e48c5391..0000000000000000000000000000000000000000 diff --git a/src/c#/GeneralUpdate.ClientCore/Exceptions/CustomArgs/.gitkeep b/src/c#/GeneralUpdate.ClientCore/Exceptions/CustomArgs/.gitkeep deleted file mode 100644 index e69de29bb2d1d6434b8b29ae775ad8c2e48c5391..0000000000000000000000000000000000000000 diff --git a/src/c#/GeneralUpdate.ClientCore/Exceptions/CustomException/.gitkeep b/src/c#/GeneralUpdate.ClientCore/Exceptions/CustomException/.gitkeep deleted file mode 100644 index e69de29bb2d1d6434b8b29ae775ad8c2e48c5391..0000000000000000000000000000000000000000 diff --git a/src/c#/GeneralUpdate.ClientCore/GeneralClientBootstrap.cs b/src/c#/GeneralUpdate.ClientCore/GeneralClientBootstrap.cs index 11bded52fce1b7146d9bffc5762d295a8c6dd6f5..ff05faf0694781e316e8f24ee749143ac1953629 100644 --- a/src/c#/GeneralUpdate.ClientCore/GeneralClientBootstrap.cs +++ b/src/c#/GeneralUpdate.ClientCore/GeneralClientBootstrap.cs @@ -1,271 +1,454 @@ -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.IO; using System.Linq; -using System.Reflection; -using System.Security; +using System.Runtime.InteropServices; +using System.Text; +using System.Text.Json; using System.Threading.Tasks; +using GeneralUpdate.ClientCore.Strategys; +using GeneralUpdate.Common.FileBasic; +using GeneralUpdate.Common.Download; +using GeneralUpdate.Common.Internal; +using GeneralUpdate.Common.Internal.Bootstrap; +using GeneralUpdate.Common.Internal.Event; +using GeneralUpdate.Common.Internal.JsonContext; +using GeneralUpdate.Common.Internal.Strategy; +using GeneralUpdate.Common.Shared.Object; +using GeneralUpdate.Common.Shared.Object.Enum; +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 { /// - /// This component is used only for client application bootstrapping classes. + /// All update actions of the core object for automatic upgrades will be related to the packet object. /// - public class GeneralClientBootstrap : AbstractBootstrap - { - private Func _customSkipOption; - private Func> _customSkipTaskOption; + private GlobalConfigInfo? _configInfo; + private IStrategy? _strategy; + private Func? _customSkipOption; + private readonly List> _customOptions = new(); - private List> _customOptions; - private List>> _customTaskOptions; + #region Public Methods - public GeneralClientBootstrap() : base() + /// + /// Main function for booting the update startup. + /// + /// + public override async Task LaunchAsync() + { + try { - _customOptions = new List>(); - _customTaskOptions = new List>>(); + CallSmallBowlHome(_configInfo.Bowl); + ExecuteCustomOptions(); + ClearEnvironmentVariable(); + await ExecuteWorkflowAsync(); } - - #region Public Methods - - /// - /// Start the update. - /// - /// - public override GeneralClientBootstrap LaunchAsync() + catch (Exception exception) { - Task.Run(() => BaseLaunch()); - return this; + Debug.WriteLine(exception.Message); + EventManager.Instance.Dispatch(this, new ExceptionEventArgs(exception, exception.Message)); } + return this; + } + + /// + /// Configure server address (Recommended Windows,Linux,Mac). + /// + public GeneralClientBootstrap SetConfig(Configinfo configInfo) + { + 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, + ProductId = configInfo.ProductId, + UpgradeClientVersion = configInfo.UpgradeClientVersion, + Bowl = configInfo.Bowl + }; + 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) + { + Debug.Assert(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> funcList) + { + Debug.Assert(funcList != null && funcList.Any()); + _customOptions.AddRange(funcList); + return this; + } + + public GeneralClientBootstrap AddListenerMultiAllDownloadCompleted( + Action callbackAction) + => AddListener(callbackAction); + + public GeneralClientBootstrap AddListenerMultiDownloadCompleted( + Action callbackAction) + => AddListener(callbackAction); + + public GeneralClientBootstrap AddListenerMultiDownloadError( + Action callbackAction) + => AddListener(callbackAction); - /// - /// Start the update. - /// - /// - public Task LaunchTaskAsync() => BaseLaunch(); + public GeneralClientBootstrap AddListenerMultiDownloadStatistics( + Action callbackAction) + => AddListener(callbackAction); - private async Task BaseLaunch() + public GeneralClientBootstrap AddListenerException(Action callbackAction) + => AddListener(callbackAction); + + #endregion Public Methods + + #region Private Methods + + private async Task ExecuteWorkflowAsync() + { + try { - 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; + Debug.Assert(_configInfo != null); + //Request the upgrade information needed by the client and upgrade end, and determine if an upgrade is necessary. + var mainResp = await VersionService.Validate(_configInfo.UpdateUrl + , _configInfo.ClientVersion + , AppType.ClientApp + , _configInfo.AppSecretKey + , GetPlatform() + , _configInfo.ProductId); + + var upgradeResp = await VersionService.Validate(_configInfo.UpdateUrl + , _configInfo.UpgradeClientVersion + , AppType.UpgradeApp + , _configInfo.AppSecretKey + , GetPlatform() + , _configInfo.ProductId); + + _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 = 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(); - } + var isForcibly = CheckForcibly(mainResp.Body) || CheckForcibly(upgradeResp.Body); + if (CanSkip(isForcibly)) return; - /// - /// 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 Config(string url, string appSecretKey, string appName = "GeneralUpdate.Upgrade") - { - if (string.IsNullOrEmpty(url)) throw new Exception("Url cannot be empty !"); - try + _configInfo.Encoding = GetOption(UpdateOption.Encoding) ?? Encoding.Default; + _configInfo.Format = GetOption(UpdateOption.Format) ?? Format.ZIP; + _configInfo.DownloadTimeOut = GetOption(UpdateOption.DownloadTimeOut) == 0 + ? 60 + : GetOption(UpdateOption.DownloadTimeOut); + _configInfo.DriveEnabled = GetOption(UpdateOption.Drive) ?? false; + _configInfo.TempPath = StorageManager.GetTempDirectory("main_temp"); + _configInfo.BackupDirectory = Path.Combine(_configInfo.InstallPath, + $"{StorageManager.DirectoryName}{_configInfo.ClientVersion}"); + + if (_configInfo.IsMainUpdate) { - 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; + _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 + , _configInfo.ClientVersion + , _configInfo.LastVersion + , _configInfo.UpdateLogUrl + , _configInfo.Encoding + , _configInfo.Format + , _configInfo.DownloadTimeOut + , _configInfo.AppSecretKey + , mainResp.Body + , _configInfo.ReportUrl + , _configInfo.BackupDirectory + , _configInfo.Bowl); + + _configInfo.ProcessInfo = + JsonSerializer.Serialize(processInfo, ProcessInfoJsonContext.Default.ProcessInfo); } - catch (Exception ex) + + StorageManager.Backup(_configInfo.InstallPath + , _configInfo.BackupDirectory + , StorageManager.SkipDirectorys); + + StrategyFactory(); + switch (_configInfo.IsUpgradeUpdate) { - throw new GeneralUpdateException(ex.Message, ex.InnerException); + 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; } } - - /// - /// Custom Configuration (Recommended : All platforms). - /// - /// - /// - public GeneralClientBootstrap Config(Configinfo info) + catch (Exception exception) { - 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; + Debug.WriteLine(exception.Message); + EventManager.Instance.Dispatch(this, new ExceptionEventArgs(exception, exception.Message)); } - - /// - /// 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) + } + + private async Task Download() + { + try { - if (func == null) throw new ArgumentNullException(nameof(func)); - _customSkipOption = func; - return this; - } + var manager = new DownloadManager(_configInfo.TempPath, _configInfo.Format, _configInfo.DownloadTimeOut); + manager.MultiAllDownloadCompleted += OnMultiAllDownloadCompleted; + manager.MultiDownloadCompleted += OnMultiDownloadCompleted; + manager.MultiDownloadError += OnMultiDownloadError; + manager.MultiDownloadStatistics += OnMultiDownloadStatistics; + foreach (var versionInfo in _configInfo.UpdateVersions) + { + manager.Add(new DownloadTask(manager, versionInfo)); + } - /// - /// 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) + await manager.LaunchTasksAsync(); + } + catch (Exception exception) { - if (func == null) throw new ArgumentNullException(nameof(func)); - _customSkipTaskOption = func; - return this; + Debug.WriteLine(exception.Message); + EventManager.Instance.Dispatch(this, new ExceptionEventArgs(exception, exception.Message)); } + } - /// - /// 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) + private int GetPlatform() + { + if (RuntimeInformation.IsOSPlatform(OSPlatform.Windows)) { - if (funcs == null || !funcs.Any()) throw new ArgumentNullException(nameof(funcs)); - _customOptions.AddRange(funcs); - return this; + return PlatformType.Windows; } - /// - /// 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 (RuntimeInformation.IsOSPlatform(OSPlatform.Linux)) { - if (funcs == null || !funcs.Any()) throw new ArgumentNullException(nameof(funcs)); - _customTaskOptions.AddRange(funcs); - return this; + return PlatformType.Linux; } + + return -1; + } - #endregion Public Methods + /// + /// Check if there has been a recent update failure.(only windows) + /// + /// + /// + 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; + } - #region Private Methods + /// + /// Determine whether the current version verification result indicates that an update is needed. + /// + /// + /// + private bool CheckUpgrade(VersionRespDTO? response) + { + if (response == null) + return false; - /// - ///Gets the application version number - /// - /// - /// - /// - private string GetFileVersion(string filePath) + if (response.Code == 200) + return response.Body.Count > 0; + + 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) { - 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 (item.IsForcibly == true) + { + return true; + } } - /// - /// User decides if update is required. - /// - /// is false to continue execution. - private async Task IsSkip(bool isForcibly) + return false; + } + + /// + /// User decides if update is required. + /// + /// is false to continue execution. + private bool CanSkip(bool isForcibly) + { + if (isForcibly) return false; + return _customSkipOption?.Invoke() == true; + } + + private void CallSmallBowlHome(string processName) + { + if(string.IsNullOrWhiteSpace(processName)) + return; + + try { - try + var processes = Process.GetProcessesByName(processName); + if (processes.Length == 0) { - bool isSkip = false; - if (isForcibly) return false; - if (_customSkipTaskOption != null) isSkip = await _customSkipTaskOption.Invoke(); - if (_customSkipOption != null) isSkip = _customSkipOption.Invoke(); - return isSkip; + Debug.WriteLine($"No process named {processName} found."); + return; } - catch (Exception ex) + + foreach (var process in processes) { - throw new GeneralUpdateException($"The injected user skips this update with an exception ! {ex.Message}", ex.InnerException); + Debug.WriteLine($"Killing process {process.ProcessName} (ID: {process.Id})"); + process.Kill(); } } + catch (Exception ex) + { + EventManager.Instance.Dispatch(this, new ExceptionEventArgs(ex, ex.Message)); + } + } + + /// + /// Performs all injected custom operations. + /// + /// + private void ExecuteCustomOptions() + { + if (!_customOptions.Any()) return; - /// - /// Performs all injected custom operations. - /// - /// - private async Task ExecuteCustomOptions() + foreach (var option in _customOptions) { - if (_customTaskOptions.Any()) + try { - foreach (var option in _customTaskOptions) + if (!option.Invoke()) { - var isSuccess = await option.Invoke(); - if (!isSuccess) - Core.Events.EventManager.Instance.Dispatch>(this, new Core.Events.CommonArgs.ExceptionEventArgs($"{nameof(option)}Execution failure!")); + var exception = new Exception($"{nameof(option)}Execution failure!"); + var args = new ExceptionEventArgs(exception, exception.Message); + EventManager.Instance.Dispatch(this,args); } } - else if (_customOptions.Any()) + catch (Exception exception) { - foreach (var option in _customOptions) - { - var isSuccess = option.Invoke(); - if (!isSuccess) - Core.Events.EventManager.Instance.Dispatch>(this, new Core.Events.CommonArgs.ExceptionEventArgs($"{nameof(option)}Execution failure!")); - } + Debug.WriteLine(exception.Message); + var args = new ExceptionEventArgs(exception, $"{nameof(option)}Execution failure!"); + EventManager.Instance.Dispatch(this,args); } } + } - /// - /// The values passed between processes during previous updates are cleared when the client starts. - /// - private void ClearEnvironmentVariable() + /// + /// Clear the environment variable information needed to start the upgrade assistant process. + /// + private void ClearEnvironmentVariable() + { + try { - try + if (RuntimeInformation.IsOSPlatform(OSPlatform.Windows)) { - Environment.SetEnvironmentVariable("ProcessBase64", null, EnvironmentVariableTarget.User); + Environment.SetEnvironmentVariable("ProcessInfo", null, EnvironmentVariableTarget.User); } - catch (SecurityException ex) + else if (RuntimeInformation.IsOSPlatform(OSPlatform.Linux)) { - Trace.WriteLine($"Error: You do not have sufficient permissions to delete this environment variable.{ex}"); - } - catch (ArgumentException ex) - { - Trace.WriteLine($"Error: The environment variable name is invalid. {ex}"); + if (File.Exists("ProcessInfo.json")) + { + File.SetAttributes("ProcessInfo.json", FileAttributes.Normal); + File.Delete("ProcessInfo.json"); + } } } + catch (Exception ex) + { + Debug.WriteLine(ex); + EventManager.Instance.Dispatch(this, new ExceptionEventArgs(ex, "Error: An unknown error occurred while deleting the environment variable.")); + } + } + + protected override void ExecuteStrategy()=> throw new NotImplementedException(); + + protected override Task ExecuteStrategyAsync()=> throw new NotImplementedException(); + + 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!"); - #endregion Private Methods + _strategy?.Create(_configInfo!); + return this; } + + private GeneralClientBootstrap AddListener(Action callbackAction) where TArgs : EventArgs + { + Debug.Assert(callbackAction != null); + EventManager.Instance.AddListener(callbackAction); + return this; + } + + private void OnMultiDownloadStatistics(object sender, MultiDownloadStatisticsEventArgs 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); + + #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 c08f1e5744710b81d59c0769b7dc074fae94a510..76be92fbbd63c0f1e6efb469b2d31d96fe8f41bd 100644 --- a/src/c#/GeneralUpdate.ClientCore/GeneralClientOSS.cs +++ b/src/c#/GeneralUpdate.ClientCore/GeneralClientOSS.cs @@ -1,95 +1,86 @@ -using GeneralUpdate.Core.ContentProvider; -using GeneralUpdate.Core.Domain.Entity; -using GeneralUpdate.Core.Domain.Entity.Assembler; -using GeneralUpdate.Core.Domain.PO; -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.Common.FileBasic; +using GeneralUpdate.Common.Internal.JsonContext; +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 platform. + /// + public static async Task Start(GlobalConfigInfoOSS configGlobalConfigInfo, string upgradeAppName = "GeneralUpdate.Upgrade.exe") + { + 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 processBase64 = ProcessAssembler.ToBase64(configParams); - Process.Start(appPath, processBase64); - } - 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 - { - 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, configGlobalConfigInfo.VersionFileName); + DownloadFile(configGlobalConfigInfo.Url, versionsFilePath); + if (!File.Exists(versionsFilePath)) return; + var versions = StorageManager.GetJson>(versionsFilePath, VersionOSSJsonContext.Default.ListVersionOSS); + 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(configGlobalConfigInfo.CurrentVersion, newVersion.Version)) + return; + + //If you confirm that an update is required, start the upgrade application. + var appPath = Path.Combine(basePath, $"{upgradeAppName}"); + if (!File.Exists(appPath)) + throw new Exception($"The application does not exist {upgradeAppName} !"); + + var json = JsonSerializer.Serialize(configGlobalConfigInfo, GlobalConfigInfoOSSJsonContext.Default.GlobalConfigInfoOSS); + Environment.SetEnvironmentVariable("GlobalConfigInfoOSS", json, EnvironmentVariableTarget.User); + Process.Start(appPath); + } + catch (Exception ex) { - webClient.DownloadFile(new Uri(url), path); + Debug.WriteLine(ex.Message); + throw new Exception(ex.Message + "\n" + ex.StackTrace); } - } + 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; + + var isParseClientVersion = Version.TryParse(clientVersion, out var currentClientVersion); + var isParseServerVersion = Version.TryParse(serverVersion, out var currentServerVersion); + if (!isParseClientVersion || !isParseServerVersion) return false; + if (currentClientVersion < currentServerVersion) return true; + return false; + } - private static void AddListener(Action callbackAction) where TArgs : EventArgs - { - if (callbackAction != null) GeneralUpdate.Core.Events.EventManager.Instance.AddListener(callbackAction); - } + private static void DownloadFile(string url, string path) + { + using var webClient = new WebClient(); + webClient.DownloadFile(new Uri(url), path); } } \ 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 1c03115f0ed377d21c333631a9b321887394156e..43b09695e0c2d6f1735c2905d198c2cfce636d78 100644 --- a/src/c#/GeneralUpdate.ClientCore/GeneralUpdate.ClientCore.csproj +++ b/src/c#/GeneralUpdate.ClientCore/GeneralUpdate.ClientCore.csproj @@ -1,258 +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/HashAlgorithms/.gitkeep b/src/c#/GeneralUpdate.ClientCore/HashAlgorithms/.gitkeep deleted file mode 100644 index e69de29bb2d1d6434b8b29ae775ad8c2e48c5391..0000000000000000000000000000000000000000 diff --git a/src/c#/GeneralUpdate.ClientCore/Hubs/IUpgradeHubService.cs b/src/c#/GeneralUpdate.ClientCore/Hubs/IUpgradeHubService.cs new file mode 100644 index 0000000000000000000000000000000000000000..ee29b3991487ef06c4603a3aa1caa71cc4ad80c0 --- /dev/null +++ b/src/c#/GeneralUpdate.ClientCore/Hubs/IUpgradeHubService.cs @@ -0,0 +1,47 @@ +using System; +using System.Threading.Tasks; + +namespace GeneralUpdate.ClientCore.Hubs; + +public interface IUpgradeHubService +{ + /// + /// Add a listener to receive upgrade information pushed from the server. + /// + /// string : group name , string : received message content. + public void AddListenerReceive(Action receiveMessageCallback); + + /// + /// Add a listener to receive online and offline notifications. + /// + /// string : Offline or online information. + public void AddListenerOnline(Action onlineMessageCallback); + + /// + /// Add a listener to receive reconnection notifications. + /// + /// string? : Reconnection information. + public void AddListenerReconnected(Func? reconnectedCallback); + + /// + /// Add a listener to receive disconnection notifications. + /// + /// Exception? : Offline exception information. + public void AddListenerClosed(Func closeCallback); + + /// + /// Start subscribing to upgrade push notifications, and the content of the notifications should be agreed upon independently (it is recommended to use JSON data format). + /// + public Task StartAsync(); + + /// + /// When closing the connection, any ongoing message processing will be completed, but no new messages will be accepted. + /// This should be called before the application closes or goes to sleep, so it can reconnect when it resumes next time. + /// + public Task StopAsync(); + + /// + /// The Hub instance will be completely disposed of and cannot be used for reconnection. + /// + public Task DisposeAsync(); +} \ No newline at end of file diff --git a/src/c#/GeneralUpdate.ClientCore/Hubs/RandomRetryPolicy.cs b/src/c#/GeneralUpdate.ClientCore/Hubs/RandomRetryPolicy.cs index f274eaaf75cedbba8031d14b31a88d87c775e15b..93ef549cf641bf39b5514a3ba2319143f736ecb3 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/UpgradeHubService.cs b/src/c#/GeneralUpdate.ClientCore/Hubs/UpgradeHubService.cs new file mode 100644 index 0000000000000000000000000000000000000000..a583cfecaeebcd8a40dde5e35d15d04166336fd1 --- /dev/null +++ b/src/c#/GeneralUpdate.ClientCore/Hubs/UpgradeHubService.cs @@ -0,0 +1,91 @@ +using System; +using System.Diagnostics; +using System.Threading.Tasks; +using GeneralUpdate.Common.Internal.JsonContext; +using Microsoft.AspNetCore.SignalR; +using Microsoft.AspNetCore.SignalR.Client; +using Microsoft.Extensions.DependencyInjection; + +namespace GeneralUpdate.ClientCore.Hubs; + +/// +/// Upgrade the push notification service. +/// +/// Subscription address, for example: http://127.0.0.1/UpgradeHub +/// ID4 authentication token string. +/// Parameters to be sent to the server upon connection (recommended as a JSON string). +public class UpgradeHubService : IUpgradeHubService +{ + private const string Onlineflag = "Online"; + private const string ReceiveMessageflag = "ReceiveMessage"; + private HubConnection? _connection; + + public UpgradeHubService(string url, string? token = null, string? appkey = null) + => _connection = BuildHubConnection(url, token, appkey); + + private HubConnection BuildHubConnection(string url, string? token = null, string? appkey = null) + { + var builder = new HubConnectionBuilder() + .WithUrl(url, config => + { + if (!string.IsNullOrWhiteSpace(token)) + config.AccessTokenProvider = () => Task.FromResult(token); + + if (!string.IsNullOrWhiteSpace(appkey)) + config.Headers.Add("appkey", appkey); + }).WithAutomaticReconnect(new RandomRetryPolicy()); + builder.Services.Configure(o => + { + o.PayloadSerializerOptions.TypeInfoResolverChain.Insert(0, PacketJsonContext.Default); + }); + return builder.Build(); + } + + public void AddListenerReceive(Action receiveMessageCallback) + => _connection?.On(ReceiveMessageflag, receiveMessageCallback); + + public void AddListenerOnline(Action onlineMessageCallback) + => _connection?.On(Onlineflag, onlineMessageCallback); + + public void AddListenerReconnected(Func? reconnectedCallback) + => _connection!.Reconnected += reconnectedCallback; + + public void AddListenerClosed(Func closeCallback) + => _connection!.Closed += closeCallback; + + public async Task StartAsync() + { + try + { + await _connection!.StartAsync(); + } + catch (Exception e) + { + Debug.WriteLine(e); + } + } + + public async Task StopAsync() + { + try + { + await _connection!.StopAsync(); + } + catch (Exception e) + { + Debug.WriteLine(e); + } + } + + public async Task DisposeAsync() + { + try + { + await _connection!.DisposeAsync(); + } + catch (Exception e) + { + Debug.WriteLine(e); + } + } +} \ No newline at end of file diff --git a/src/c#/GeneralUpdate.ClientCore/Hubs/VersionHub.cs b/src/c#/GeneralUpdate.ClientCore/Hubs/VersionHub.cs deleted file mode 100644 index 6b656f141c74613feb7a41bec04c9f562ec5516c..0000000000000000000000000000000000000000 --- a/src/c#/GeneralUpdate.ClientCore/Hubs/VersionHub.cs +++ /dev/null @@ -1,196 +0,0 @@ -using GeneralUpdate.Core.ContentProvider; -using Microsoft.AspNetCore.SignalR.Client; -using System; -using System.Threading.Tasks; - -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 - - #region Constructors - - private VersionHub() - { } - - #endregion Constructors - - #region Public Properties - - public static VersionHub Instance - { - get - { - if (_instance == null) - { - lock (_lock) - { - if (_instance == null) - _instance = new VersionHub(); - } - } - return _instance; - } - } - - #endregion Public Properties - - #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 !"); - - try - { - _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); - } - } - - /// - /// Send message to server.[Not recommended for now] - /// - /// - /// - /// - public async Task Send(string msg) - { - try - { - 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 - - #region Private Methods - - /// - /// Receives the message. - /// - /// - private void OnReceiveMessage(string name, string message) - { - 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); - } - } - - /// - /// Online and offline notification. - /// - /// - private void OnOnlineMessage(string message) - { - try - { - 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) - { - try - { - if (_reconnectedCallback != null) _reconnectedCallback.Invoke(arg); - } - catch (Exception ex) - { - throw new Exception($"'VersionHub' On reconnected error : {ex.Message}", ex.InnerException); - } - return Task.CompletedTask; - } - - /// - /// Shut down. - /// - /// - /// - private async Task OnClosed(Exception arg) - { - try - { - 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); - } - } - - #endregion Private Methods - } -} \ No newline at end of file diff --git a/src/c#/GeneralUpdate.ClientCore/Pipeline/CompressMiddleware.cs b/src/c#/GeneralUpdate.ClientCore/Pipeline/CompressMiddleware.cs new file mode 100644 index 0000000000000000000000000000000000000000..be644f58f156a7cae283360d22a8a3a01324e3ab --- /dev/null +++ b/src/c#/GeneralUpdate.ClientCore/Pipeline/CompressMiddleware.cs @@ -0,0 +1,34 @@ +using System; +using System.Diagnostics; +using System.IO; +using System.Text; +using System.Threading.Tasks; +using GeneralUpdate.Common.Compress; +using GeneralUpdate.Common.Internal; +using GeneralUpdate.Common.Internal.Event; +using GeneralUpdate.Common.Internal.Pipeline; + +namespace GeneralUpdate.ClientCore.Pipeline; + +public class CompressMiddleware : IMiddleware +{ + public Task InvokeAsync(PipelineContext? context) + { + return Task.Run(() => + { + try + { + var format = context.Get("Format"); + var sourcePath = context.Get("ZipFilePath"); + var patchPath = context.Get("PatchPath"); + var encoding = context.Get("Encoding"); + CompressProvider.Decompress(format,sourcePath,patchPath, encoding); + } + catch (Exception e) + { + Debug.WriteLine(e); + EventManager.Instance.Dispatch(this, new ExceptionEventArgs(e, e.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 0000000000000000000000000000000000000000..0e75b1b0a84c1681c8f15a2c7bffd1fedbd07b57 --- /dev/null +++ b/src/c#/GeneralUpdate.ClientCore/Pipeline/HashMiddleware.cs @@ -0,0 +1,48 @@ +using System; +using System.Diagnostics; +using System.Security.Cryptography; +using System.Threading.Tasks; +using GeneralUpdate.Common.HashAlgorithms; +using GeneralUpdate.Common.Internal; +using GeneralUpdate.Common.Internal.Event; +using GeneralUpdate.Common.Internal.Pipeline; + +namespace GeneralUpdate.ClientCore.Pipeline; + +public class HashMiddleware : IMiddleware +{ + public async Task InvokeAsync(PipelineContext context) + { + try + { + var path = context.Get("ZipFilePath"); + var hash = context.Get("Hash"); + var isVerify = await VerifyFileHash(path, hash); + if (!isVerify) throw new CryptographicException("Hash verification failed ."); + } + catch (Exception exception) + { + Debug.WriteLine(exception.Message); + EventManager.Instance.Dispatch(this, new ExceptionEventArgs(exception, exception.Message)); + } + } + + private Task VerifyFileHash(string path, string hash) + { + return Task.Run(() => + { + try + { + var hashAlgorithm = new Sha256HashAlgorithm(); + var hashSha256 = hashAlgorithm.ComputeHash(path); + return string.Equals(hash, hashSha256, StringComparison.OrdinalIgnoreCase); + } + catch (Exception exception) + { + Debug.WriteLine(exception.Message); + EventManager.Instance.Dispatch(this, new ExceptionEventArgs(exception, exception.Message)); + } + return false; + }); + } +} \ 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 0000000000000000000000000000000000000000..7329955f9b93d64c3f2789b763c4f8e29223051d --- /dev/null +++ b/src/c#/GeneralUpdate.ClientCore/Pipeline/PatchMiddleware.cs @@ -0,0 +1,34 @@ +using System; +using System.Collections.Generic; +using System.Diagnostics; +using System.Threading.Tasks; +using GeneralUpdate.Common.FileBasic; +using GeneralUpdate.Common.Internal; +using GeneralUpdate.Common.Internal.Event; +using GeneralUpdate.Common.Internal.Pipeline; +using GeneralUpdate.Differential; + +namespace GeneralUpdate.ClientCore.Pipeline; + +public class PatchMiddleware : IMiddleware +{ + public async Task InvokeAsync(PipelineContext context) + { + try + { + var sourcePath = context.Get("SourcePath"); + var targetPath = context.Get("PatchPath"); + var blackFiles = context.Get>("BlackFiles"); + var blackFileFormats = context.Get>("BlackFileFormats"); + + BlackListManager.Instance.AddBlackFiles(blackFiles); + BlackListManager.Instance.AddBlackFileFormats(blackFileFormats); + await DifferentialCore.Instance.Dirty(sourcePath, targetPath); + } + catch (Exception exception) + { + Debug.WriteLine(exception.Message); + EventManager.Instance.Dispatch(this, new ExceptionEventArgs(exception, exception.Message)); + } + } +} \ 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 e69de29bb2d1d6434b8b29ae775ad8c2e48c5391..0000000000000000000000000000000000000000 diff --git a/src/c#/GeneralUpdate.ClientCore/Pipelines/Context/.gitkeep b/src/c#/GeneralUpdate.ClientCore/Pipelines/Context/.gitkeep deleted file mode 100644 index e69de29bb2d1d6434b8b29ae775ad8c2e48c5391..0000000000000000000000000000000000000000 diff --git a/src/c#/GeneralUpdate.ClientCore/Pipelines/Middleware/.gitkeep b/src/c#/GeneralUpdate.ClientCore/Pipelines/Middleware/.gitkeep deleted file mode 100644 index e69de29bb2d1d6434b8b29ae775ad8c2e48c5391..0000000000000000000000000000000000000000 diff --git a/src/c#/GeneralUpdate.ClientCore/Pipelines/MiddlewareResolver/.gitkeep b/src/c#/GeneralUpdate.ClientCore/Pipelines/MiddlewareResolver/.gitkeep deleted file mode 100644 index e69de29bb2d1d6434b8b29ae775ad8c2e48c5391..0000000000000000000000000000000000000000 diff --git a/src/c#/GeneralUpdate.ClientCore/Pipelines/Pipeline/.gitkeep b/src/c#/GeneralUpdate.ClientCore/Pipelines/Pipeline/.gitkeep deleted file mode 100644 index e69de29bb2d1d6434b8b29ae775ad8c2e48c5391..0000000000000000000000000000000000000000 diff --git a/src/c#/GeneralUpdate.ClientCore/Strategys/LinuxStrategy.cs b/src/c#/GeneralUpdate.ClientCore/Strategys/LinuxStrategy.cs new file mode 100644 index 0000000000000000000000000000000000000000..db1aca2ca0d926c4fa3c49a3a3ccb111301e727e --- /dev/null +++ b/src/c#/GeneralUpdate.ClientCore/Strategys/LinuxStrategy.cs @@ -0,0 +1,113 @@ +using System; +using System.Diagnostics; +using System.IO; +using System.Threading; +using System.Threading.Tasks; +using GeneralUpdate.ClientCore.Pipeline; +using GeneralUpdate.Common.FileBasic; +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; + +/// +/// Update policy based on the linux platform. +/// +public class LinuxStrategy : AbstractStrategy +{ + private GlobalConfigInfo _configinfo = new(); + private const string ProcessInfoFileName = "ProcessInfo.json"; + + public override void Create(GlobalConfigInfo parameter)=> _configinfo = parameter; + + public override async Task ExecuteAsync() + { + try + { + var status = 0; + var patchPath = StorageManager.GetTempDirectory(Patchs); + 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); + } + catch (Exception e) + { + Debug.WriteLine(e); + EventManager.Instance.Dispatch(this, new ExceptionEventArgs(e, e.Message)); + } + } + + public override void StartApp() + { + try + { + var appPath = Path.Combine(_configinfo.InstallPath, _configinfo.AppName); + if (File.Exists(appPath)) + { + if (File.Exists(ProcessInfoFileName)) + { + File.SetAttributes(ProcessInfoFileName,FileAttributes.Normal); + File.Delete(ProcessInfoFileName); + } + + File.WriteAllText(ProcessInfoFileName, _configinfo.ProcessInfo); + Process.Start(appPath); + } + } + catch (Exception e) + { + Debug.WriteLine(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.ClientCore/Strategys/PlatformLinux/.gitkeep b/src/c#/GeneralUpdate.ClientCore/Strategys/PlatformLinux/.gitkeep deleted file mode 100644 index e69de29bb2d1d6434b8b29ae775ad8c2e48c5391..0000000000000000000000000000000000000000 diff --git a/src/c#/GeneralUpdate.ClientCore/Strategys/PlatformWindows/.gitkeep b/src/c#/GeneralUpdate.ClientCore/Strategys/PlatformWindows/.gitkeep deleted file mode 100644 index e69de29bb2d1d6434b8b29ae775ad8c2e48c5391..0000000000000000000000000000000000000000 diff --git a/src/c#/GeneralUpdate.ClientCore/Strategys/WindowsStrategy.cs b/src/c#/GeneralUpdate.ClientCore/Strategys/WindowsStrategy.cs new file mode 100644 index 0000000000000000000000000000000000000000..a896f4b140b9bc596d2e60f024e5080b82cd0cb2 --- /dev/null +++ b/src/c#/GeneralUpdate.ClientCore/Strategys/WindowsStrategy.cs @@ -0,0 +1,111 @@ +using System; +using System.Diagnostics; +using System.IO; +using System.Threading.Tasks; +using GeneralUpdate.ClientCore.Pipeline; +using GeneralUpdate.Common.FileBasic; +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.Object.Enum; +using GeneralUpdate.Common.Shared.Service; + +namespace GeneralUpdate.ClientCore.Strategys; + +/// +/// Update policy based on the Windows platform. +/// +public class WindowsStrategy : AbstractStrategy +{ + private GlobalConfigInfo _configinfo = new(); + + public override void Create(GlobalConfigInfo parameter)=> _configinfo = parameter; + + public override async Task ExecuteAsync() + { + try + { + var status = ReportType.None; + var patchPath = StorageManager.GetTempDirectory(Patchs); + 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 = ReportType.Success; + } + catch (Exception e) + { + status = ReportType.Failure; + 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); + } + catch (Exception e) + { + Debug.WriteLine(e); + EventManager.Instance.Dispatch(this, new ExceptionEventArgs(e, e.Message)); + } + } + + public override void StartApp() + { + try + { + Environment.SetEnvironmentVariable("ProcessInfo", _configinfo.ProcessInfo, EnvironmentVariableTarget.User); + var appPath = Path.Combine(_configinfo.InstallPath, _configinfo.AppName); + if (File.Exists(appPath)) + { + Environment.SetEnvironmentVariable("ProcessInfo", _configinfo.ProcessInfo, EnvironmentVariableTarget.User); + Process.Start(new ProcessStartInfo + { + UseShellExecute = true, + FileName = appPath + }); + } + } + catch (Exception e) + { + Debug.WriteLine(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.ClientCore/WillMessage/.gitkeep b/src/c#/GeneralUpdate.ClientCore/WillMessage/.gitkeep deleted file mode 100644 index e69de29bb2d1d6434b8b29ae775ad8c2e48c5391..0000000000000000000000000000000000000000 diff --git a/src/c#/GeneralUpdate.ClientCore/ZipFactory/Events/.gitkeep b/src/c#/GeneralUpdate.ClientCore/ZipFactory/Events/.gitkeep deleted file mode 100644 index e69de29bb2d1d6434b8b29ae775ad8c2e48c5391..0000000000000000000000000000000000000000 diff --git a/src/c#/GeneralUpdate.ClientCore/ZipFactory/Factory/.gitkeep b/src/c#/GeneralUpdate.ClientCore/ZipFactory/Factory/.gitkeep deleted file mode 100644 index e69de29bb2d1d6434b8b29ae775ad8c2e48c5391..0000000000000000000000000000000000000000 diff --git a/src/c#/GeneralUpdate.Common/Compress/CompressProvider.cs b/src/c#/GeneralUpdate.Common/Compress/CompressProvider.cs new file mode 100644 index 0000000000000000000000000000000000000000..06f9fa75cd66b3c938dbb4e1d36e4c7e8694a967 --- /dev/null +++ b/src/c#/GeneralUpdate.Common/Compress/CompressProvider.cs @@ -0,0 +1,30 @@ +using System; +using System.Text; +using GeneralUpdate.Common.Shared.Object; + +namespace GeneralUpdate.Common.Compress; + +public class CompressProvider +{ + private static ICompressionStrategy _compressionStrategy; + + private CompressProvider() { } + + public static void Compress(string compressType,string sourcePath, string destinationPath, bool includeRootDirectory, Encoding encoding) + { + _compressionStrategy = GetCompressionStrategy(compressType); + _compressionStrategy.Compress(sourcePath, destinationPath, includeRootDirectory, encoding); + } + + public static void Decompress(string compressType, string archivePath, string destinationPath, Encoding encoding) + { + _compressionStrategy = GetCompressionStrategy(compressType); + _compressionStrategy.Decompress(archivePath, destinationPath, encoding); + } + + private static ICompressionStrategy GetCompressionStrategy(string compressType) => compressType switch + { + Format.ZIP => new ZipCompressionStrategy(), + _ => throw new ArgumentException("Compression format is not supported!") + }; +} \ No newline at end of file diff --git a/src/c#/GeneralUpdate.Common/Compress/ICompressionStrategy.cs b/src/c#/GeneralUpdate.Common/Compress/ICompressionStrategy.cs new file mode 100644 index 0000000000000000000000000000000000000000..be4f22e5b06da61a2df094422461c1ce71a97603 --- /dev/null +++ b/src/c#/GeneralUpdate.Common/Compress/ICompressionStrategy.cs @@ -0,0 +1,9 @@ +using System.Text; + +namespace GeneralUpdate.Common.Compress; + +public interface ICompressionStrategy +{ + void Compress(string sourcePath, string destinationPath, bool includeRootDirectory, Encoding encoding); + void Decompress(string archivePath, string destinationPath, Encoding encoding); +} \ No newline at end of file diff --git a/src/c#/GeneralUpdate.Common/Compress/ZipCompressionStrategy.cs b/src/c#/GeneralUpdate.Common/Compress/ZipCompressionStrategy.cs new file mode 100644 index 0000000000000000000000000000000000000000..90345432b0b8800c325d71c3b28235c1b19c363d --- /dev/null +++ b/src/c#/GeneralUpdate.Common/Compress/ZipCompressionStrategy.cs @@ -0,0 +1,196 @@ +using System; +using System.Collections.Generic; +using System.Diagnostics; +using System.IO; +using System.IO.Compression; +using System.Linq; +using System.Text; +using System.Text.RegularExpressions; +using GeneralUpdate.Common.FileBasic; + +namespace GeneralUpdate.Common.Compress; + +public class ZipCompressionStrategy : ICompressionStrategy +{ + /// + /// Creates a zip archive containing the files and subdirectories of the specified directory. + /// + /// The path of the file directory to be compressed and archived, which can be a relative path or an absolute path. A relative path is a path relative to the current working directory. + /// The archive path of the compressed package to be generated, which can be a relative path or an absolute path. A relative path is a path relative to the current working directory. + /// Enumeration value indicating whether the compression operation emphasizes speed or compression size . + /// Whether the archive contains the parent directory . + public void Compress(string sourceDirectoryName + , string destinationArchiveFileName + , bool includeBaseDirectory + , Encoding encoding) + { + try + { + var compressionLevel = CompressionLevel.Optimal; + if (Directory.Exists(sourceDirectoryName)) + { + if (!File.Exists(destinationArchiveFileName)) + { + ZipFile.CreateFromDirectory(sourceDirectoryName + , destinationArchiveFileName + , compressionLevel + , includeBaseDirectory + , encoding); + } + else + { + var toZipFileDictionaryList = GetAllDirList(sourceDirectoryName, includeBaseDirectory); + using var archive = ZipFile.Open(destinationArchiveFileName, ZipArchiveMode.Update, encoding); + foreach (var toZipFileKey in toZipFileDictionaryList.Keys) + { + if (toZipFileKey == destinationArchiveFileName) continue; + + var toZipedFileName = Path.GetFileName(toZipFileKey); + var toDelArchives = new List(); + foreach (var zipArchiveEntry in archive.Entries) + { + if (toZipedFileName != null && + (zipArchiveEntry.FullName.StartsWith(toZipedFileName) || toZipedFileName.StartsWith(zipArchiveEntry.FullName))) + { + toDelArchives.Add(zipArchiveEntry); + } + } + + foreach (var zipArchiveEntry in toDelArchives) + { + zipArchiveEntry.Delete(); + } + + archive.CreateEntryFromFile(toZipFileKey, toZipFileDictionaryList[toZipFileKey], compressionLevel); + } + } + } + else if (File.Exists(sourceDirectoryName)) + { + if (!File.Exists(destinationArchiveFileName)) + { + ZipFile.CreateFromDirectory(sourceDirectoryName + , destinationArchiveFileName + , compressionLevel + , false + , encoding); + } + else + { + using var archive = ZipFile.Open(destinationArchiveFileName, ZipArchiveMode.Update, encoding); + if (sourceDirectoryName != destinationArchiveFileName) + { + var toZipedFileName = Path.GetFileName(sourceDirectoryName); + var toDelArchives = new List(); + foreach (var zipArchiveEntry in archive.Entries) + { + if (toZipedFileName != null + && (zipArchiveEntry.FullName.StartsWith(toZipedFileName)|| toZipedFileName.StartsWith(zipArchiveEntry.FullName))) + { + toDelArchives.Add(zipArchiveEntry); + } + } + + foreach (var zipArchiveEntry in toDelArchives) + { + zipArchiveEntry.Delete(); + } + + archive.CreateEntryFromFile(sourceDirectoryName, toZipedFileName, compressionLevel); + } + } + } + } + catch(Exception exception) + { + Debug.WriteLine(exception); + throw new Exception($"Failed to compress archive: {exception.Message}"); + } + } + + /// + /// Unzip the Zip file and save it to the specified target path folder . + /// + /// + /// + /// + public void Decompress(string zipFilePath, string unZipDir, Encoding encoding) + { + try + { + if (Directory.Exists(unZipDir)) + { + StorageManager.DeleteDirectory(unZipDir); + } + Directory.CreateDirectory(unZipDir); + + var dirSeparatorChar = Path.DirectorySeparatorChar.ToString(); + unZipDir = unZipDir.EndsWith(dirSeparatorChar) ? unZipDir : unZipDir + dirSeparatorChar; + + var directoryInfo = new DirectoryInfo(unZipDir); + if (!directoryInfo.Exists) + { + directoryInfo.Create(); + } + + var fileInfo = new FileInfo(zipFilePath); + if (!fileInfo.Exists) + { + return; + } + + using var zipToOpen = new FileStream(zipFilePath, FileMode.Open, FileAccess.ReadWrite, FileShare.Read); + using var archive = new ZipArchive(zipToOpen, ZipArchiveMode.Read, false, encoding); + for (int i = 0; i < archive.Entries.Count; i++) + { + var entries = archive.Entries[i]; + if (entries.FullName.EndsWith(dirSeparatorChar)) + { + continue; + } + + var pattern = $"^{dirSeparatorChar}*"; + var entryFilePath = Regex.Replace(entries.FullName.Replace("/", dirSeparatorChar), pattern, + ""); + var filePath = directoryInfo + entryFilePath; + var greatFolder = Directory.GetParent(filePath); + if (greatFolder is not null && !greatFolder.Exists) + { + greatFolder.Create(); + } + entries.ExtractToFile(filePath); + } + } + catch (Exception exception) + { + Debug.WriteLine(exception); + throw new Exception($"Failed to decompress archive: {exception.Message}"); + } + } + + /// + /// Recursively get the set of all files in the specified directory on the disk, the return type is: dictionary [file name, relative file name to be compressed] + /// + /// + /// + /// + /// + private Dictionary GetAllDirList(string strBaseDir + , bool includeBaseDirectory = false + , string namePrefix = "") + { + var resultDictionary = new Dictionary(); + var directoryInfo = new DirectoryInfo(strBaseDir); + var directories = directoryInfo.GetDirectories(); + var fileInfos = directoryInfo.GetFiles(); + if (includeBaseDirectory) + namePrefix += directoryInfo.Name + "\\"; + foreach (var directory in directories) + resultDictionary = resultDictionary.Concat(GetAllDirList(directory.FullName, true, namePrefix)) + .ToDictionary(k => k.Key, k => k.Value); + foreach (var fileInfo in fileInfos) + if (!resultDictionary.ContainsKey(fileInfo.FullName)) + resultDictionary.Add(fileInfo.FullName, namePrefix + fileInfo.Name); + return resultDictionary; + } +} \ 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 cf8f60baa535f628f9b3471c72e833fa5e19ea18..03ab5ba6bc0798ac40e84e4bdf44c9528f0b21bf 100644 --- a/src/c#/GeneralUpdate.Common/Download/DownloadManager.cs +++ b/src/c#/GeneralUpdate.Common/Download/DownloadManager.cs @@ -2,50 +2,33 @@ using System.Collections.Generic; using System.Threading.Tasks; using System.Collections.Immutable; -using GeneralUpdate.Common.Download; +using System.Diagnostics; +using System.Linq; -namespace GeneralUpdate.Core.Download +namespace GeneralUpdate.Common.Download { - public class DownloadManager + public class DownloadManager(string path, string format, int timeOut) { #region Private Members - private readonly string _path; - private readonly string _format; - private readonly int _timeOut; - private readonly IList<(object, string)> _failedVersions; - private ImmutableList>.Builder _downloadTasksBuilder; - private ImmutableList> _downloadTasks; + private readonly ImmutableList.Builder _downloadTasksBuilder = ImmutableList.Create().ToBuilder(); + private ImmutableList _downloadTasks; #endregion Private Members - #region Constructors - - public DownloadManager(string path, string format, int timeOut) - { - _path = path; - _format = format; - _timeOut = timeOut; - _failedVersions = new List<(object, string)>(); - _downloadTasksBuilder = ImmutableList.Create>().ToBuilder(); - } - - #endregion Constructors - #region Public Properties - public IList<(object, string)> FailedVersions => _failedVersions; + public List<(object, string)> FailedVersions { get; } = new(); - public string Path => _path; + public string Path => path; - public string Format => _format; + public string Format => format; - public int TimeOut => _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; public event EventHandler MultiDownloadCompleted; public event EventHandler MultiDownloadError; public event EventHandler MultiDownloadStatistics; @@ -58,53 +41,38 @@ namespace GeneralUpdate.Core.Download { 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)); + MultiAllDownloadCompleted.Invoke(this, new MultiAllDownloadCompletedEventArgs(true, FailedVersions)); } catch (Exception ex) { - _failedVersions.Add((null, ex.Message)); - MultiAllDownloadCompleted?.Invoke(this, new MultiAllDownloadCompletedEventArgs(false, _failedVersions)); + MultiAllDownloadCompleted.Invoke(this, new MultiAllDownloadCompletedEventArgs(false, FailedVersions)); throw new Exception($"Download manager error: {ex.Message}", ex); } } public void OnMultiDownloadStatistics(object sender, MultiDownloadStatisticsEventArgs e) - { - MultiDownloadStatistics?.Invoke(this, e); - } - - public void OnMultiDownloadProgressChanged(object sender, MultiDownloadProgressChangedEventArgs e) - { - MultiDownloadProgressChanged?.Invoke(this, e); - } + => MultiDownloadStatistics?.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) { MultiDownloadError?.Invoke(this, e); - _failedVersions.Add((e.Version, e.Exception.Message)); + FailedVersions.Add((e.Version, e.Exception.Message)); } - public void Add(DownloadTask task) + public void Add(DownloadTask task) { - if (task != null && !_downloadTasksBuilder.Contains(task)) + 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 05bd92fb2de08c906fb80a85f85c18fe436fe2b5..52b773669ce2f249256e38a83672d59ce9af5033 100644 --- a/src/c#/GeneralUpdate.Common/Download/DownloadTask.cs +++ b/src/c#/GeneralUpdate.Common/Download/DownloadTask.cs @@ -1,98 +1,89 @@ using System; using System.Collections.Generic; +using System.Diagnostics; using System.IO; using System.Net.Http; using System.Threading; using System.Threading.Tasks; -using GeneralUpdate.Common.Download; +using GeneralUpdate.Common.Shared.Object; -namespace GeneralUpdate.Core.Download +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 const int DEFAULT_DELTA = 1048576; // 1024*1024 - private long _beforBytes; + private readonly DownloadManager _manager; + private readonly VersionInfo? _version; + private Timer? _timer; + private DateTime _startTime; private long _receivedBytes; private long _totalBytes; - private Timer _speedTimer; - private DateTime _startTime; + private long _currentBytes; #endregion Private Members - #region Constructors - - public DownloadTask(DownloadManager manager, TVersion version) + public DownloadTask(DownloadManager manager, VersionInfo version) { _manager = manager; _version = version; - _httpClient = new HttpClient - { - Timeout = TimeSpan.FromSeconds(_manager.TimeOut) - }; + _httpClient = new HttpClient { Timeout = TimeSpan.FromSeconds(_manager.TimeOut) }; + _timer = new Timer(_=> Statistics(), null, 0, 1000); } - #endregion Constructors - - #region Public Properties - - public bool IsCompleted { get; private set; } - - #endregion Public Properties - - #region Public Methods - public async Task LaunchAsync() { try { - var url = GetPropertyValue(_version, "Url"); - var name = GetPropertyValue(_version, "Name"); - var installPath = $"{_manager.Path}{name}{_manager.Format}"; - - InitStatisticsEvent(); - InitProgressEvent(); - InitCompletedEvent(); - - await DownloadFileRangeAsync(url, installPath); + var path = Path.Combine(_manager.Path, $"{_version?.Name}{_manager.Format}"); + await DownloadFileRangeAsync(_version.Url, path); } - catch (Exception ex) + catch (Exception exception) { - _manager.OnMultiDownloadError(this, new MultiDownloadErrorEventArgs(ex, _version)); - //throw new GeneralUpdateException("'download task' The executes abnormally !", ex); + Debug.WriteLine(exception.Message); + _manager.OnMultiDownloadError(this, new MultiDownloadErrorEventArgs(exception, _version)); } } - #endregion Public Methods - #region Private Methods private async Task DownloadFileRangeAsync(string url, string path) { - var tempPath = path + ".temp"; - long startPos = CheckFile(tempPath); - - using var response = await _httpClient.GetAsync(url, HttpCompletionOption.ResponseHeadersRead); - if (!response.IsSuccessStatusCode) + try { - throw new HttpRequestException($"Failed to download file: {response.ReasonPhrase}"); - } + var tempPath = path + ".temp"; + 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; - } + var totalBytes = response.Content.Headers.ContentLength ?? 0; + Interlocked.Exchange(ref _totalBytes, totalBytes); + + if (startPos >= totalBytes) + { + if (File.Exists(path)) + { + File.SetAttributes(path, FileAttributes.Normal); + File.Delete(path); + } + + File.Move(tempPath, path); + OnDownloadCompleted(true); + return; + } - await foreach (var chunk in DownloadChunksAsync(response)) + await foreach (var chunk in DownloadChunksAsync(response)) + { + await WriteFileAsync(tempPath, chunk, totalBytes); + } + } + catch (Exception exception) { - await WriteFileAsync(tempPath, chunk, totalBytes); + OnDownloadCompleted(false); + Debug.WriteLine(exception.Message); + _manager.OnMultiDownloadError(this, new MultiDownloadErrorEventArgs(exception, _version)); } } @@ -108,141 +99,189 @@ namespace GeneralUpdate.Core.Download yield return chunk; } } - - private long CheckFile(string tempPath) + + private async Task WriteFileAsync(string tempPath, byte[] chunk, long totalBytes) { - long startPos = 0; - if (File.Exists(tempPath)) + try { - using var fileStream = File.OpenWrite(tempPath); - startPos = fileStream.Length; - fileStream.Seek(startPos, SeekOrigin.Current); + using var fileStream = new FileStream(tempPath, FileMode.Append, FileAccess.Write, FileShare.None); + await fileStream.WriteAsync(chunk, 0, chunk.Length); + Interlocked.Add(ref _receivedBytes, chunk.Length); + if (_receivedBytes >= totalBytes) + { + fileStream.Close(); + var path = tempPath.Replace(".temp", ""); + if (File.Exists(path)) + { + File.SetAttributes(path, FileAttributes.Normal); + File.Delete(path); + } + + File.Move(tempPath, path); + OnDownloadCompleted(true); + } + } + catch (Exception exception) + { + OnDownloadCompleted(false); + Debug.WriteLine(exception); + _manager.OnMultiDownloadError(this, new MultiDownloadErrorEventArgs(exception, _version)); } - return startPos; } - private async Task WriteFileAsync(string tempPath, byte[] chunk, long totalBytes) + private void Statistics() { - 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) + try + { + var interval = DateTime.Now - _startTime; + var tempTotalBytes = Interlocked.Read(ref _totalBytes); + //Accumulate the downloaded size. + var tempReceivedBytes = Interlocked.Read(ref _receivedBytes); + //Current downloaded size. + var tempCurrentBytes = tempReceivedBytes - Interlocked.Read(ref _currentBytes); + var speed = CalculateDownloadSpeed(tempCurrentBytes, interval); + var formatSpeed = FormatDownloadSpeed(speed); + var remainingTime = CalculateRemainingTime(tempTotalBytes, tempReceivedBytes, speed); + var progress = CalculateDownloadProgress(tempTotalBytes, tempReceivedBytes); + + var args = new MultiDownloadStatisticsEventArgs(_version + , remainingTime + , formatSpeed + , tempTotalBytes + , tempReceivedBytes + , progress); + _manager.OnMultiDownloadStatistics(this, args); + Interlocked.Exchange(ref _currentBytes, tempReceivedBytes); + _startTime = DateTime.Now; + } + catch (Exception exception) { - fileStream.Close(); - File.Move(tempPath, tempPath.Replace(".temp", "")); - OnCompleted(); + _manager.OnMultiDownloadError(this, new MultiDownloadErrorEventArgs(exception, _version)); } } - private void InitStatisticsEvent() + private void OnDownloadCompleted(bool isComplated) { - if (_speedTimer != null) return; - - _speedTimer = new Timer(_ => + try { - 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); + DisposeTimer(); + var eventArgs = new MultiDownloadCompletedEventArgs(_version, isComplated); + _manager.OnMultiAsyncCompleted(this, eventArgs); + } + catch (Exception exception) + { + Debug.WriteLine(exception.Message); + _manager.OnMultiDownloadError(this, new MultiDownloadErrorEventArgs(exception, _version)); + } } - private void InitProgressEvent() + /// + /// Calculate the remaining download time (in seconds). + /// + /// + /// + /// + /// + private static TimeSpan CalculateRemainingTime(long totalBytes, long bytesReceived, double downloadSpeed) { - _manager.MultiDownloadProgressChanged += (sender, e) => + if (downloadSpeed == 0) { - try - { - _receivedBytes = e.BytesReceived; - _totalBytes = e.TotalBytesToReceive; - - var eventArgs = new MultiDownloadProgressChangedEventArgs(_version, - e.BytesReceived / DEFAULT_DELTA, - e.TotalBytesToReceive / DEFAULT_DELTA, - e.ProgressPercentage, - e.UserState); + return new TimeSpan(0, 0, 0, 0); + } - _manager.OnMultiDownloadProgressChanged(this, eventArgs); - } - catch (Exception exception) - { - _manager.OnMultiDownloadError(this, new MultiDownloadErrorEventArgs(exception, _version)); - } - }; + var bytesRemaining = totalBytes - bytesReceived; + var secondsRemaining = bytesRemaining / downloadSpeed; + return TimeSpan.FromSeconds(secondsRemaining); } - - private void InitCompletedEvent() + + /// + /// Calculate the download speed (in bytes per second). + /// + /// + /// + /// + private static double CalculateDownloadSpeed(long bytesReceived, TimeSpan elapsedTime) { - _manager.MultiDownloadCompleted += (sender, e) => - { - try - { - _speedTimer?.Dispose(); - var eventArgs = new MultiDownloadCompletedEventArgs(_version, e.Error, e.Cancelled, e.UserState); - _manager.OnMultiAsyncCompleted(this, eventArgs); - } - catch (Exception exception) - { - _manager.OnMultiDownloadError(this, new MultiDownloadErrorEventArgs(exception, _version)); - } - finally - { - IsCompleted = true; - } - }; + if (elapsedTime.TotalSeconds == 0) + return 0; + + return bytesReceived / elapsedTime.TotalSeconds; } - - private void OnProgressChanged(long bytesReceived, long totalBytes) + + /// + /// Calculate the download progress (percentage %). + /// + /// + /// + /// + private static double CalculateDownloadProgress(long totalBytes, long bytesReceived) { - _manager.OnMultiDownloadProgressChanged(this, new MultiDownloadProgressChangedEventArgs( - bytesReceived, totalBytes, (float)bytesReceived / totalBytes, _version)); + if (totalBytes == 0) + return 0; + + return (double)bytesReceived / totalBytes * 100; } + + /// + /// Convert the download speed to an appropriate unit (B, KB, MB, GB). + /// + /// + /// + private static string FormatDownloadSpeed(double speedInBytesPerSecond) + { + const double kiloByte = 1024; + const double megaByte = kiloByte * 1024; + const double gigaByte = megaByte * 1024; - private void OnCompleted() + return speedInBytesPerSecond switch + { + >= gigaByte => $"{speedInBytesPerSecond / gigaByte:F2} GB/s", + >= megaByte => $"{speedInBytesPerSecond / megaByte:F2} MB/s", + _ => speedInBytesPerSecond >= kiloByte + ? $"{speedInBytesPerSecond / kiloByte:F2} KB/s" + : $"{speedInBytesPerSecond:F2} B/s" + }; + } + + /// + /// Get the size of the downloaded file for resuming interrupted downloads. + /// + /// + /// + private static long CheckFile(string tempPath) { - _manager.OnMultiAsyncCompleted(this, new MultiDownloadCompletedEventArgs(_version, null, false, _version)); + long startPos = 0; + if (!File.Exists(tempPath)) return startPos; + using var fileStream = File.OpenWrite(tempPath); + startPos = fileStream.Length; + fileStream.Seek(startPos, SeekOrigin.Current); + return startPos; } - private TResult GetPropertyValue(TVersion entity, string propertyName) + private void DisposeTimer() { - TResult result = default(TResult); + if (_timer == null) return; try { - var propertyInfo = typeof(TVersion).GetProperty(propertyName); - result = (TResult)propertyInfo?.GetValue(entity); + _timer.Change(Timeout.Infinite, Timeout.Infinite); + using var waitHandle = new ManualResetEvent(false); + _timer.Dispose(waitHandle); + waitHandle.WaitOne(); } - catch (Exception ex) + catch (ObjectDisposedException exception) { - //throw new GeneralUpdateException($"Error getting property value: {ex.Message}", ex); + Debug.WriteLine("Timer has already been disposed: " + exception.Message); + _manager.OnMultiDownloadError(this, new MultiDownloadErrorEventArgs(exception, _version)); } - return result; - } - - private string ToUnit(long byteSize) - { - var tempSize = Convert.ToSingle(byteSize) / 1024; - if (tempSize > 1) + catch (Exception exception) + { + Debug.WriteLine("An error occurred while disposing the timer: " + exception.Message); + _manager.OnMultiDownloadError(this, new MultiDownloadErrorEventArgs(exception, _version)); + } + finally { - var tempMbyte = tempSize / 1024; - return tempMbyte > 1 - ? $"{tempMbyte:##0.00}MB/S" - : $"{tempSize:##0.00}KB/S"; + _timer = null; } - return $"{byteSize}B/S"; } #endregion Private Methods diff --git a/src/c#/GeneralUpdate.Common/Download/MultiEventArgs/MutiAllDownloadCompletedEventArgs.cs b/src/c#/GeneralUpdate.Common/Download/MultiEventArgs/MutiAllDownloadCompletedEventArgs.cs index 0f9fca13d82e0a2cfcd00905f3416416ec071f54..aeec9ee6329634d9edf230d83818fdd2e4624346 100644 --- a/src/c#/GeneralUpdate.Common/Download/MultiEventArgs/MutiAllDownloadCompletedEventArgs.cs +++ b/src/c#/GeneralUpdate.Common/Download/MultiEventArgs/MutiAllDownloadCompletedEventArgs.cs @@ -5,9 +5,6 @@ namespace GeneralUpdate.Common.Download { public class MultiAllDownloadCompletedEventArgs : EventArgs { - public MultiAllDownloadCompletedEventArgs() - { } - public MultiAllDownloadCompletedEventArgs(bool isAllDownloadCompleted, IList<(object, string)> failedVersions) { IsAllDownloadCompleted = isAllDownloadCompleted; diff --git a/src/c#/GeneralUpdate.Common/Download/MultiEventArgs/MutiDownloadCompletedEventArgs.cs b/src/c#/GeneralUpdate.Common/Download/MultiEventArgs/MutiDownloadCompletedEventArgs.cs index 4cc47fec5621c9b0a416a4a0fb28c0c4332f7d40..3f6841620f789a38f848b5c166c9155b82ab0ce2 100644 --- a/src/c#/GeneralUpdate.Common/Download/MultiEventArgs/MutiDownloadCompletedEventArgs.cs +++ b/src/c#/GeneralUpdate.Common/Download/MultiEventArgs/MutiDownloadCompletedEventArgs.cs @@ -1,19 +1,11 @@ using System; -using System.ComponentModel; namespace GeneralUpdate.Common.Download { - public class MultiDownloadCompletedEventArgs : AsyncCompletedEventArgs + public class MultiDownloadCompletedEventArgs(object version, bool isComplated) : EventArgs { - public MultiDownloadCompletedEventArgs(Exception error, bool cancelled, object userState) : base(error, cancelled, userState) - { - } + public object Version { get; private set; } = version; - public MultiDownloadCompletedEventArgs(object version, Exception error, bool cancelled, object userState) : base(error, cancelled, userState) - { - Version = version; - } - - public object Version { get; set; } + public bool IsComplated { get; private set; } = isComplated; } } \ No newline at end of file diff --git a/src/c#/GeneralUpdate.Common/Download/MultiEventArgs/MutiDownloadProgressChangedEventArgs.cs b/src/c#/GeneralUpdate.Common/Download/MultiEventArgs/MutiDownloadProgressChangedEventArgs.cs deleted file mode 100644 index c7ebb04c5e5a7e263279e46bdf01aece7ddfe014..0000000000000000000000000000000000000000 --- a/src/c#/GeneralUpdate.Common/Download/MultiEventArgs/MutiDownloadProgressChangedEventArgs.cs +++ /dev/null @@ -1,54 +0,0 @@ -using System; - -namespace GeneralUpdate.Common.Download -{ - 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, double 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 double 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.Common/Download/MultiEventArgs/MutiDownloadStatisticsEventArgs.cs b/src/c#/GeneralUpdate.Common/Download/MultiEventArgs/MutiDownloadStatisticsEventArgs.cs index 1389c0bf923b5da98d4e7326a0698695c6ba709e..4dad6104df48e8c954d6158fa0b8c05e99a724e2 100644 --- a/src/c#/GeneralUpdate.Common/Download/MultiEventArgs/MutiDownloadStatisticsEventArgs.cs +++ b/src/c#/GeneralUpdate.Common/Download/MultiEventArgs/MutiDownloadStatisticsEventArgs.cs @@ -1,20 +1,23 @@ using System; -namespace GeneralUpdate.Common.Download +namespace GeneralUpdate.Common.Download; + +public class MultiDownloadStatisticsEventArgs(object version + , TimeSpan remaining + , string speed + , long totalBytes + , long bytesReceived + , double progressPercentage) : EventArgs { - public class MultiDownloadStatisticsEventArgs : EventArgs - { - public object Version { get; set; } + public object Version { get; private set; } = version; + + public TimeSpan Remaining { get; private set; } = remaining; + + public string Speed { get; private set; } = speed; - public DateTime Remaining { get; set; } + public long TotalBytesToReceive { get; private set; } = totalBytes; - public string Speed { get; set; } + public long BytesReceived { get; private set; } = bytesReceived; - public MultiDownloadStatisticsEventArgs(object version, DateTime remaining, string speed) - { - Version = version ?? throw new ArgumentNullException(nameof(version)); - Remaining = remaining; - Speed = speed ?? throw new ArgumentNullException(nameof(speed)); - } - } + public double ProgressPercentage { get; private set; } = progressPercentage; } \ No newline at end of file diff --git a/src/c#/GeneralUpdate.Common/Download/MultiEventArgs/ProgressType.cs b/src/c#/GeneralUpdate.Common/Download/MultiEventArgs/ProgressType.cs deleted file mode 100644 index 174b7113dd81d4780dfce881814eb770c49e337d..0000000000000000000000000000000000000000 --- a/src/c#/GeneralUpdate.Common/Download/MultiEventArgs/ProgressType.cs +++ /dev/null @@ -1,44 +0,0 @@ -namespace GeneralUpdate.Common.Download; - -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.Common/Driver/BackupDriverCommand.cs b/src/c#/GeneralUpdate.Common/Driver/BackupDriverCommand.cs deleted file mode 100644 index cf339c49d20811b46bcf7ba96d8a690e0da97d75..0000000000000000000000000000000000000000 --- 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 a6c55651e760d1288564a2b64ea00506f4dacd01..0000000000000000000000000000000000000000 --- 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 64c4319857c13ccca84b65b32690daad748bf43e..0000000000000000000000000000000000000000 --- 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 8a6a85eeb7ea252136d61d599371ad3512d6f7f6..0000000000000000000000000000000000000000 --- 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 a8d5f970fea031c17199dc7f11e584b977b245c5..0000000000000000000000000000000000000000 --- 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 4e10a51a3211052ba6d1a74a457e1a4603dda4ea..0000000000000000000000000000000000000000 --- 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 dc0b8654a8ed55a188d02451f6a7882d84830fb4..0000000000000000000000000000000000000000 --- 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 504b241fbecd29de114f4a9bd0371c1c810c418f..0000000000000000000000000000000000000000 --- 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/File/ComparisonResult.cs b/src/c#/GeneralUpdate.Common/File/ComparisonResult.cs deleted file mode 100644 index 6a8d165a7a310bf40ee590f668b0cb922760ee7c..0000000000000000000000000000000000000000 --- a/src/c#/GeneralUpdate.Common/File/ComparisonResult.cs +++ /dev/null @@ -1,20 +0,0 @@ -using System.Collections.Generic; - -namespace GeneralUpdate.Common; - -public class ComparisonResult -{ - private readonly List _uniqueToA = new List(); - private readonly List _uniqueToB = new List(); - private readonly List _differentFiles = new List(); - - public IReadOnlyList UniqueToA => _uniqueToA.AsReadOnly(); - public IReadOnlyList UniqueToB => _uniqueToB.AsReadOnly(); - public IReadOnlyList DifferentFiles => _differentFiles.AsReadOnly(); - - public void AddUniqueToA(IEnumerable files) => _uniqueToA.AddRange(files); - - public void AddUniqueToB(IEnumerable files) => _uniqueToB.AddRange(files); - - public void AddDifferentFiles(IEnumerable files) => _differentFiles.AddRange(files); -} \ No newline at end of file diff --git a/src/c#/GeneralUpdate.Common/File/GeneralFileManager.cs b/src/c#/GeneralUpdate.Common/File/GeneralFileManager.cs deleted file mode 100644 index cf41e9cdb63d6eca41266f2cc55a6ae4543e4fc2..0000000000000000000000000000000000000000 --- a/src/c#/GeneralUpdate.Common/File/GeneralFileManager.cs +++ /dev/null @@ -1,214 +0,0 @@ -using System; -using System.Collections.Generic; -using System.IO; -using System.Linq; -using System.Text; -using GeneralUpdate.Common.HashAlgorithms; -using Newtonsoft.Json; - -namespace GeneralUpdate.Common -{ - public sealed class GeneralFileManager - { - #region Private Members - - private static readonly List _blackFileFormats = new List - { - ".patch", - ".7z", - ".zip", - ".rar", - ".tar", - ".json" - }; - - private static readonly List _blackFiles = new List { "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; } - - #endregion - - #region Public Methods - - 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 AddBlackFile(string fileName) - { - if (!_blackFiles.Contains(fileName)) - { - _blackFiles.Add(fileName); - } - } - - public static void RemoveBlackFile(string fileName) - { - _blackFiles.Remove(fileName); - } - - 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(); - - 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 }); - } - } - } - - 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; - } - - 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; - } - - #endregion - - #region Private Methods - - private IEnumerable GetRelativeFilePaths(string rootDir, string currentDir) - { - foreach (var file in Directory.GetFiles(currentDir)) - { - yield return GetRelativePath(rootDir, file); - } - - foreach (var dir in Directory.GetDirectories(currentDir)) - { - foreach (var file in GetRelativeFilePaths(rootDir, dir)) - { - yield return file; - } - } - } - - private string GetRelativePath(string fromPath, string toPath) - { - var fromUri = new Uri(fromPath); - var toUri = new Uri(toPath); - - if (fromUri.Scheme != toUri.Scheme) - { - return toPath; - } // path can't be made relative. - - var relativeUri = fromUri.MakeRelativeUri(toUri); - var relativePath = Uri.UnescapeDataString(relativeUri.ToString()); - - if (toUri.Scheme.Equals("file", StringComparison.InvariantCultureIgnoreCase)) - { - relativePath = relativePath.Replace(Path.AltDirectorySeparatorChar, Path.DirectorySeparatorChar); - } - - 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(); - var hashA = sha256.ComputeHash(fileA); - var hashB = sha256.ComputeHash(fileB); - - return hashA.SequenceEqual(hashB); - } - - #endregion - } -} \ No newline at end of file diff --git a/src/c#/GeneralUpdate.Common/FileBasic/BlackListManager.cs b/src/c#/GeneralUpdate.Common/FileBasic/BlackListManager.cs new file mode 100644 index 0000000000000000000000000000000000000000..eab7f96fe55350f064e08063005c82293784f633 --- /dev/null +++ b/src/c#/GeneralUpdate.Common/FileBasic/BlackListManager.cs @@ -0,0 +1,99 @@ +using System.Collections.Generic; +using System.IO; +using GeneralUpdate.Common.Shared.Object; + +namespace GeneralUpdate.Common.FileBasic; + +public class BlackListManager +{ + private static readonly object LockObject = new object(); + private static BlackListManager? _instance; + + private static readonly List _blackFileFormats = + [ + ".patch", + Format.ZIP, + ".rar", + ".tar", + ".json", + ".pdb" + ]; + + 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) + { + if(formats == null) + return; + + foreach (var format in formats) + { + AddBlackFileFormat(format); + } + } + + public void AddBlackFileFormat(string format) + { + if(string.IsNullOrWhiteSpace(format)) + return; + + if (!_blackFileFormats.Contains(format)) + { + _blackFileFormats.Add(format); + } + } + + public void AddBlackFiles(List? fileNames) + { + if(fileNames == null) + return; + + foreach (var fileName in fileNames) + { + AddBlackFile(fileName); + } + } + + public void AddBlackFile(string fileName) + { + if(string.IsNullOrWhiteSpace(fileName)) + return; + + if (!_blackFiles.Contains(fileName)) + { + _blackFiles.Add(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/FileBasic/ComparisonResult.cs b/src/c#/GeneralUpdate.Common/FileBasic/ComparisonResult.cs new file mode 100644 index 0000000000000000000000000000000000000000..41dbf234e2fd4a56fcbb8cd6a70ebfc6f6b76eaa --- /dev/null +++ b/src/c#/GeneralUpdate.Common/FileBasic/ComparisonResult.cs @@ -0,0 +1,41 @@ +using System.Collections.Generic; + +namespace GeneralUpdate.Common.FileBasic; + +/// +/// Result of a comparison between two directories. +/// +public class ComparisonResult +{ + 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 LeftNodes => _leftNodes.AsReadOnly(); + + /// + /// List of files that are unique to B. + /// + public IReadOnlyList RightNodes => _rightNodes.AsReadOnly(); + + /// + /// List of files that are different between A and B. + /// + public IReadOnlyList DifferentNodes => _differentNodes.AsReadOnly(); + + public void AddToLeft(IEnumerable files) => _leftNodes.AddRange(files); + + public void AddToRight(IEnumerable files) => _rightNodes.AddRange(files); + + public void AddDifferent(IEnumerable files) => _differentNodes.AddRange(files); +} \ No newline at end of file diff --git a/src/c#/GeneralUpdate.Common/FileBasic/FileNode.cs b/src/c#/GeneralUpdate.Common/FileBasic/FileNode.cs new file mode 100644 index 0000000000000000000000000000000000000000..90b3831af3ef53bcbe278f5a7c087c61f0e95432 --- /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.Core/ContentProvider/FileTree.cs b/src/c#/GeneralUpdate.Common/FileBasic/FileTree.cs similarity index 98% rename from src/c#/GeneralUpdate.Core/ContentProvider/FileTree.cs rename to src/c#/GeneralUpdate.Common/FileBasic/FileTree.cs index 31fd7f00fa328858737606f2e1130c1bd5c8136c..085013c751f4b4cb6812e6621c3f48989abcdd3c 100644 --- a/src/c#/GeneralUpdate.Core/ContentProvider/FileTree.cs +++ b/src/c#/GeneralUpdate.Common/FileBasic/FileTree.cs @@ -1,8 +1,8 @@ using System.Collections.Generic; using System.Diagnostics; -namespace GeneralUpdate.Core.ContentProvider -{ +namespace GeneralUpdate.Common.FileBasic; + /// /// Simple file binary tree. /// @@ -169,5 +169,4 @@ namespace GeneralUpdate.Core.ContentProvider public FileNode GetRoot() => _root; #endregion Public Methods - } -} \ No newline at end of file + } \ No newline at end of file diff --git a/src/c#/GeneralUpdate.Common/FileBasic/StorageManager.cs b/src/c#/GeneralUpdate.Common/FileBasic/StorageManager.cs new file mode 100644 index 0000000000000000000000000000000000000000..441cbd7f3c101971af829daa39acd0d40e402a97 --- /dev/null +++ b/src/c#/GeneralUpdate.Common/FileBasic/StorageManager.cs @@ -0,0 +1,287 @@ +using System; +using System.Collections.Generic; +using System.IO; +using System.Linq; +using System.Text.Json; +using System.Text.Json.Serialization.Metadata; +using System.Threading; +using GeneralUpdate.Common.HashAlgorithms; + +namespace GeneralUpdate.Common.FileBasic +{ + public sealed class StorageManager + { + private long _fileCount = 0; + public const string DirectoryName = "app-"; + public static readonly List SkipDirectorys = ["fail", DirectoryName]; + private ComparisonResult ComparisonResult { get; set; } + + #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 ComparisonResult Compare(string leftDir, string rightDir) + { + ResetId(); + ComparisonResult = new ComparisonResult(); + 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, JsonTypeInfo? typeInfo = null) where T : class + { + var folderPath = Path.GetDirectoryName(targetPath) ?? + throw new ArgumentException("invalid path", nameof(targetPath)); + + if (!Directory.Exists(folderPath)) + Directory.CreateDirectory(folderPath); + + var jsonString = typeInfo!= null ? JsonSerializer.Serialize(obj, typeInfo) : JsonSerializer.Serialize(obj); + File.WriteAllText(targetPath, jsonString); + } + + public static T? GetJson(string path, JsonTypeInfo? typeInfo = null) where T : class + { + if (File.Exists(path)) + { + var json = File.ReadAllText(path); + if (typeInfo != null) + { + return JsonSerializer.Deserialize(json, typeInfo); + } + return JsonSerializer.Deserialize(json); + } + + return default; + } + + public static string GetTempDirectory(string name) + { + var path = $"generalupdate_{DateTime.Now:yyyy-MM-dd}_{name}"; + var tempDir = Path.Combine(Path.GetTempPath(), path); + if (!Directory.Exists(tempDir)) + { + Directory.CreateDirectory(tempDir); + } + + return tempDir; + } + + public static void DeleteDirectory(string targetDir) + { + foreach (var file in Directory.GetFiles(targetDir)) + { + File.SetAttributes(file, FileAttributes.Normal); + File.Delete(file); + } + + foreach (var dir in Directory.GetDirectories(targetDir)) + { + DeleteDirectory(dir); + } + + Directory.Delete(targetDir, false); + } + + public static List GetAllFiles(string path, List skipDirectorys) + { + try + { + var files = new List(); + files.AddRange(new DirectoryInfo(path).GetFiles()); + var tmpDir = new DirectoryInfo(path).GetDirectories(); + + foreach (var dic in tmpDir) + { + bool shouldSkip = false; + foreach (var notBackup in skipDirectorys) + { + if (dic.FullName.Contains(notBackup)) + { + shouldSkip = true; + break; + } + } + + if (!shouldSkip) + files.AddRange(GetAllfiles(dic.FullName)); + } + + return files; + } + catch + { + return new List(); + } + } + + private 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; + } + } + + public static bool HashEquals(string leftPath, string rightPath) + { + var hashAlgorithm = new Sha256HashAlgorithm(); + var hashLeft = hashAlgorithm.ComputeHash(leftPath); + 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)) + { + DeleteDirectory(backupPath); + } + 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 + + /// + /// 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)) + { + if (BlackListManager.Instance.IsBlacklisted(subPath)) continue; + + 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() + }); + } + + foreach (var subPath in Directory.EnumerateDirectories(path)) + { + resultFiles.AddRange(ReadFileNode(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); + + #endregion + } +} \ No newline at end of file diff --git a/src/c#/GeneralUpdate.Common/GeneralUpdate.Common.csproj b/src/c#/GeneralUpdate.Common/GeneralUpdate.Common.csproj index 05f942bec80ef2a0146c976325767c1448052e2c..d4cfe5caa56e22186b446265a95805024a9b6130 100644 --- a/src/c#/GeneralUpdate.Common/GeneralUpdate.Common.csproj +++ b/src/c#/GeneralUpdate.Common/GeneralUpdate.Common.csproj @@ -7,13 +7,8 @@ - - - + + + - - - - - diff --git a/src/c#/GeneralUpdate.Common/GeneralUpdate.Common.csproj.DotSettings b/src/c#/GeneralUpdate.Common/GeneralUpdate.Common.csproj.DotSettings deleted file mode 100644 index 632c268160169472d5ee9f501a70b288f556dae9..0000000000000000000000000000000000000000 --- a/src/c#/GeneralUpdate.Common/GeneralUpdate.Common.csproj.DotSettings +++ /dev/null @@ -1,2 +0,0 @@ - - CSharp120 \ 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 ef57e5dab1a86e775389edbce8a1a128caa4a876..fcf90816cd4e46c1cc7bda84ef7c2a89e036ba08 100644 --- a/src/c#/GeneralUpdate.Common/HashAlgorithms/HashAlgorithmBase.cs +++ b/src/c#/GeneralUpdate.Common/HashAlgorithms/HashAlgorithmBase.cs @@ -11,19 +11,15 @@ namespace GeneralUpdate.Common.HashAlgorithms if (!System.IO.File.Exists(fileName)) throw new FileNotFoundException(nameof(fileName)); - using (var hashAlgorithm = GetHashAlgorithm()) + 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++) { - using (var file = new FileStream(fileName, FileMode.Open, FileAccess.Read, FileShare.Read)) - { - var dataArray = GetHashAlgorithm().ComputeHash(file); - var stringBuilder = new StringBuilder(); - for (int i = 0; i < dataArray.Length; i++) - { - stringBuilder.Append(dataArray[i].ToString("x2")); - } - return stringBuilder.ToString(); - } + stringBuilder.Append(dataArray[i].ToString("x2")); } + return stringBuilder.ToString(); } public byte[] ComputeHashBytes(string fileName) @@ -31,13 +27,9 @@ namespace GeneralUpdate.Common.HashAlgorithms if (!File.Exists(fileName)) throw new FileNotFoundException(nameof(fileName)); - using (var hashAlgorithm = GetHashAlgorithm()) - { - using (var file = new FileStream(fileName, FileMode.Open, FileAccess.Read, FileShare.Read)) - { - return hashAlgorithm.ComputeHash(file); - } - } + using var hashAlgorithm = GetHashAlgorithm(); + using var file = new FileStream(fileName, FileMode.Open, FileAccess.Read, FileShare.Read); + return hashAlgorithm.ComputeHash(file); } protected abstract HashAlgorithm GetHashAlgorithm(); diff --git a/src/c#/GeneralUpdate.Common/Internal/Bootstrap/AbstractBootstrap.cs b/src/c#/GeneralUpdate.Common/Internal/Bootstrap/AbstractBootstrap.cs index c388b2488083e8235eab84e476837dc331533b1a..212e82d5e54214db145f85b8bee7f72b1e65e8b4 100644 --- a/src/c#/GeneralUpdate.Common/Internal/Bootstrap/AbstractBootstrap.cs +++ b/src/c#/GeneralUpdate.Common/Internal/Bootstrap/AbstractBootstrap.cs @@ -1,7 +1,6 @@ -using System; -using System.Collections.Concurrent; -using System.Collections.Generic; -using System.Diagnostics.Contracts; +using System.Collections.Concurrent; +using System.Diagnostics; +using System.Threading.Tasks; using GeneralUpdate.Common.Internal.Strategy; namespace GeneralUpdate.Common.Internal.Bootstrap @@ -10,73 +9,22 @@ namespace GeneralUpdate.Common.Internal.Bootstrap 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. + /// Launch async udpate. /// /// - public virtual TBootstrap LaunchAsync() - { - return (TBootstrap)this; - } - - #region Strategy + public abstract Task LaunchAsync(); - protected IStrategy InitStrategy() - { - return _strategy; - } - - protected string GetPlatform() => _strategy.GetPlatform(); + protected abstract void ExecuteStrategy(); + + protected abstract Task ExecuteStrategyAsync(); - 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 - /// - public virtual TBootstrap SetBlacklist(List files = null, List fileFormats = null) - { - return (TBootstrap)this; - } + protected abstract TBootstrap StrategyFactory(); /// /// Setting update configuration. @@ -85,38 +33,32 @@ namespace GeneralUpdate.Common.Internal.Bootstrap /// Configuration Action Enumeration. /// Value /// - public virtual TBootstrap Option(UpdateOption option, T value) + public TBootstrap Option(UpdateOption option, T value) { - Contract.Requires(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) + protected T? GetOption(UpdateOption? option) { try { - if (_options == null || _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); + return default; } catch { - return default(T); + return default; } } - - #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 e0455d17840f79b7dbde3905020794ad7ae8fba9..804ac4ad78ea30a66922cc363ed5b8ef06ed6293 100644 --- a/src/c#/GeneralUpdate.Common/Internal/Bootstrap/UpdateOption.cs +++ b/src/c#/GeneralUpdate.Common/Internal/Bootstrap/UpdateOption.cs @@ -1,6 +1,7 @@ using System; using System.Collections.Generic; using System.Diagnostics.Contracts; +using System.Text; using System.Threading; namespace GeneralUpdate.Common.Internal.Bootstrap @@ -12,10 +13,30 @@ namespace GeneralUpdate.Common.Internal.Bootstrap protected override IConstant NewConstant(int id, string name) => new UpdateOption(id, name); } - private static readonly UpdateOptionPool Pool = new UpdateOptionPool(); + private static readonly UpdateOptionPool Pool = new(); public static UpdateOption ValueOf(string name) => (UpdateOption)Pool.ValueOf(name); + /// + /// Update the file format of the package. + /// + public static readonly UpdateOption Format = ValueOf("COMPRESSFORMAT"); + + /// + /// Compress encoding. + /// + public static readonly UpdateOption Encoding = ValueOf("COMPRESSENCODING"); + + /// + /// 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"); + internal UpdateOption(int id, string name) : base(id, name) { } diff --git a/src/c#/GeneralUpdate.Common/Internal/Event/EventManager.cs b/src/c#/GeneralUpdate.Common/Internal/Event/EventManager.cs index f31f430b3effdcdabc98e9464ed4108e6c61144c..b29c77f1696da29d22eccdd106eeb47161d8e802 100644 --- a/src/c#/GeneralUpdate.Common/Internal/Event/EventManager.cs +++ b/src/c#/GeneralUpdate.Common/Internal/Event/EventManager.cs @@ -1,46 +1,17 @@ using System; using System.Collections.Generic; -using System.ComponentModel; +using System.Diagnostics; 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 readonly object _lockObj = new(); 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; + private Dictionary _dicDelegates = new(); + private bool _disposed = false; - // 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,108 +29,77 @@ namespace GeneralUpdate.Common.Internal.Event } } - /// - /// 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); - } - - /// - /// 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)) + try { - if (tempDelegate == null) + if (listener == null) throw new ArgumentNullException(nameof(listener)); + var delegateType = typeof(Action); + if (_dicDelegates.ContainsKey(delegateType)) { - _dicDelegates.Remove(delegateType); + _dicDelegates[delegateType] = Delegate.Combine(_dicDelegates[delegateType], listener); } else { - _dicDelegates[delegateType] = tempDelegate; + _dicDelegates.Add(delegateType, listener); + } + } + catch (Exception e) + { + Debug.WriteLine(e); + } + } + + public void RemoveListener(Action listener) where TEventArgs : EventArgs + { + try + { + if (listener == null) throw new ArgumentNullException(nameof(listener)); + var delegateType = typeof(Action); + if (_dicDelegates.TryGetValue(delegateType, out var existingDelegate)) + { + _dicDelegates[delegateType] = Delegate.Remove(existingDelegate, listener); } } + catch (Exception e) + { + Debug.WriteLine(e); + } } - /// - /// 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); + try + { + if (sender == null) throw new ArgumentNullException(nameof(sender)); + if (eventArgs == null) throw new ArgumentNullException(nameof(eventArgs)); + var delegateType = typeof(Action); + if (_dicDelegates.TryGetValue(delegateType, out var existingDelegate)) + { + ((Action)existingDelegate)?.Invoke(sender, eventArgs); + } + } + catch (Exception e) + { + Debug.WriteLine(e); + } } - /// - /// 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) + try { - // If disposing equals true, dispose all managed - // and unmanaged resources. - if (disposing) + if (!this._disposed) { - // Dispose managed resources. - component.Dispose(); + _dicDelegates.Clear(); + _disposed = true; } - - // 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; + } + catch (Exception e) + { + Debug.WriteLine(e); } } } diff --git a/src/c#/GeneralUpdate.Common/Internal/Exception/BaseArgs.cs b/src/c#/GeneralUpdate.Common/Internal/Exception/BaseArgs.cs deleted file mode 100644 index b29af43af7c46957d7d6d446c17e5608580147a3..0000000000000000000000000000000000000000 --- a/src/c#/GeneralUpdate.Common/Internal/Exception/BaseArgs.cs +++ /dev/null @@ -1,11 +0,0 @@ -using System; - -namespace GeneralUpdate.Common.Internal.Exception -{ - [Serializable] - public abstract class BaseArgs - { - public virtual string Message - { get { return String.Empty; } } - } -} \ No newline at end of file diff --git a/src/c#/GeneralUpdate.Common/Internal/Exception/ExceptionEventArgs.cs b/src/c#/GeneralUpdate.Common/Internal/Exception/ExceptionEventArgs.cs new file mode 100644 index 0000000000000000000000000000000000000000..ab5634adef2de1daf6657dc1015691620c221bcc --- /dev/null +++ b/src/c#/GeneralUpdate.Common/Internal/Exception/ExceptionEventArgs.cs @@ -0,0 +1,9 @@ +using System; + +namespace GeneralUpdate.Common.Internal; + +public class ExceptionEventArgs(Exception? exception = null, string? message = null) : EventArgs +{ + public Exception Exception { get; private set; } = exception; + public string Message { get; private set; } = message; +} \ No newline at end of file diff --git a/src/c#/GeneralUpdate.Common/Internal/Exception/GeneralUpdateException.cs b/src/c#/GeneralUpdate.Common/Internal/Exception/GeneralUpdateException.cs deleted file mode 100644 index f5e050914a0aa467e0b87bc800d760c8d807cf9c..0000000000000000000000000000000000000000 --- a/src/c#/GeneralUpdate.Common/Internal/Exception/GeneralUpdateException.cs +++ /dev/null @@ -1,53 +0,0 @@ -using System; -using System.Runtime.Serialization; -using System.Security.Permissions; - -namespace GeneralUpdate.Common.Internal.Exception -{ - /// - /// Exception of GeneralUpdate framework. - /// - [Serializable] - public sealed class GeneralUpdateException : System.Exception, ISerializable - where TExceptionArgs : BaseArgs - { - private const String c_args = "Args"; - private readonly TExceptionArgs m_args; - - public TExceptionArgs Args => m_args; - - public GeneralUpdateException(String message = null, System.Exception innerException = null) : this(null, message, innerException) - { - } - - public GeneralUpdateException(TExceptionArgs args, String message = null, System.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.Common/Internal/JsonContext/FileNodesJsonContext.cs b/src/c#/GeneralUpdate.Common/Internal/JsonContext/FileNodesJsonContext.cs new file mode 100644 index 0000000000000000000000000000000000000000..b0ca8541672fe4c30f040e7de68cd63f2263c3ea --- /dev/null +++ b/src/c#/GeneralUpdate.Common/Internal/JsonContext/FileNodesJsonContext.cs @@ -0,0 +1,8 @@ +using System.Collections.Generic; +using System.Text.Json.Serialization; +using GeneralUpdate.Common.FileBasic; + +namespace GeneralUpdate.Common.Internal.JsonContext; + +[JsonSerializable(typeof(List))] +public partial class FileNodesJsonContext : JsonSerializerContext; \ No newline at end of file diff --git a/src/c#/GeneralUpdate.Common/Internal/JsonContext/GlobalConfigInfoOSSJsonContext.cs b/src/c#/GeneralUpdate.Common/Internal/JsonContext/GlobalConfigInfoOSSJsonContext.cs new file mode 100644 index 0000000000000000000000000000000000000000..bcb9fbaac4b6d386331a334d23c0b2b0f016661a --- /dev/null +++ b/src/c#/GeneralUpdate.Common/Internal/JsonContext/GlobalConfigInfoOSSJsonContext.cs @@ -0,0 +1,7 @@ +using System.Text.Json.Serialization; +using GeneralUpdate.Common.Shared.Object; + +namespace GeneralUpdate.Common.Internal.JsonContext; + +[JsonSerializable(typeof(GlobalConfigInfoOSS))] +public partial class GlobalConfigInfoOSSJsonContext : JsonSerializerContext; \ No newline at end of file diff --git a/src/c#/GeneralUpdate.Common/Internal/JsonContext/HttpParameterJsonContext.cs b/src/c#/GeneralUpdate.Common/Internal/JsonContext/HttpParameterJsonContext.cs new file mode 100644 index 0000000000000000000000000000000000000000..2df040f0f5f35c3977bafb12729f044c063dda01 --- /dev/null +++ b/src/c#/GeneralUpdate.Common/Internal/JsonContext/HttpParameterJsonContext.cs @@ -0,0 +1,12 @@ +using System.Collections.Generic; +using System.Text.Json.Serialization; + +namespace GeneralUpdate.Common.Internal.JsonContext; + +[JsonSerializable(typeof(bool))] +[JsonSerializable(typeof(bool?))] +[JsonSerializable(typeof(int))] +[JsonSerializable(typeof(int?))] +[JsonSerializable(typeof(string))] +[JsonSerializable(typeof(Dictionary))] +public partial class HttpParameterJsonContext: JsonSerializerContext; \ No newline at end of file diff --git a/src/c#/GeneralUpdate.Common/Internal/JsonContext/PacketJsonContext.cs b/src/c#/GeneralUpdate.Common/Internal/JsonContext/PacketJsonContext.cs new file mode 100644 index 0000000000000000000000000000000000000000..7b64503d58040d7f4af63759ae6815db975467c8 --- /dev/null +++ b/src/c#/GeneralUpdate.Common/Internal/JsonContext/PacketJsonContext.cs @@ -0,0 +1,7 @@ +using System.Text.Json.Serialization; +using GeneralUpdate.Common.Shared.Object; + +namespace GeneralUpdate.Common.Internal.JsonContext; + +[JsonSerializable(typeof(Packet))] +public partial class PacketJsonContext : JsonSerializerContext; \ No newline at end of file diff --git a/src/c#/GeneralUpdate.Common/Internal/JsonContext/ProcessInfoJsonContext.cs b/src/c#/GeneralUpdate.Common/Internal/JsonContext/ProcessInfoJsonContext.cs new file mode 100644 index 0000000000000000000000000000000000000000..ccd4f493bf1cddf0146a94c3fa0547fd2e6ab700 --- /dev/null +++ b/src/c#/GeneralUpdate.Common/Internal/JsonContext/ProcessInfoJsonContext.cs @@ -0,0 +1,7 @@ +using System.Text.Json.Serialization; +using GeneralUpdate.Common.Shared.Object; + +namespace GeneralUpdate.Common.Internal.JsonContext; + +[JsonSerializable(typeof(ProcessInfo))] +public partial class ProcessInfoJsonContext : JsonSerializerContext; \ No newline at end of file diff --git a/src/c#/GeneralUpdate.Common/Internal/JsonContext/ReportRespJsonContext.cs b/src/c#/GeneralUpdate.Common/Internal/JsonContext/ReportRespJsonContext.cs new file mode 100644 index 0000000000000000000000000000000000000000..bb6eeb96ee80ea7d9874b0534fd12cad7a6c2ca3 --- /dev/null +++ b/src/c#/GeneralUpdate.Common/Internal/JsonContext/ReportRespJsonContext.cs @@ -0,0 +1,7 @@ +using System.Text.Json.Serialization; +using GeneralUpdate.Common.Shared.Object; + +namespace GeneralUpdate.Common.Internal.JsonContext; + +[JsonSerializable(typeof(BaseResponseDTO))] +public partial class ReportRespJsonContext : JsonSerializerContext; \ No newline at end of file diff --git a/src/c#/GeneralUpdate.Common/Internal/JsonContext/VersionOSSJsonContext.cs b/src/c#/GeneralUpdate.Common/Internal/JsonContext/VersionOSSJsonContext.cs new file mode 100644 index 0000000000000000000000000000000000000000..bd908333c609dcf4864e106bc9d613c90c1a2242 --- /dev/null +++ b/src/c#/GeneralUpdate.Common/Internal/JsonContext/VersionOSSJsonContext.cs @@ -0,0 +1,8 @@ +using System.Collections.Generic; +using System.Text.Json.Serialization; +using GeneralUpdate.Common.Shared.Object; + +namespace GeneralUpdate.Common.Internal.JsonContext; + +[JsonSerializable(typeof(List))] +public partial class VersionOSSJsonContext : JsonSerializerContext; \ No newline at end of file diff --git a/src/c#/GeneralUpdate.Common/Internal/JsonContext/VersionRespJsonContext.cs b/src/c#/GeneralUpdate.Common/Internal/JsonContext/VersionRespJsonContext.cs new file mode 100644 index 0000000000000000000000000000000000000000..c6aa00696f8f37e40a7a7432375bf47f2cb4f422 --- /dev/null +++ b/src/c#/GeneralUpdate.Common/Internal/JsonContext/VersionRespJsonContext.cs @@ -0,0 +1,7 @@ +using System.Text.Json.Serialization; +using GeneralUpdate.Common.Shared.Object; + +namespace GeneralUpdate.Common.Internal.JsonContext; + +[JsonSerializable(typeof(VersionRespDTO))] +public partial class VersionRespJsonContext : JsonSerializerContext; \ No newline at end of file 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 2b8400b195e4e58927bcfd79f6b1a19b59108301..0000000000000000000000000000000000000000 --- 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 910fa130b15513a8a5046636d6cca89aed72765a..606b1c7cc737d19a47df3a5068c4b14fbc8e7a24 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); } } \ 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 5d0fb84bccc4e7f7b7b01ab2d6c30829cedba147..f8e45ca5de18b6e4c7602baac2a54c90137fa15f 100644 --- a/src/c#/GeneralUpdate.Common/Internal/Pipeline/PipelineBuilder.cs +++ b/src/c#/GeneralUpdate.Common/Internal/Pipeline/PipelineBuilder.cs @@ -7,28 +7,37 @@ namespace GeneralUpdate.Common.Internal.Pipeline /// /// Pipeline builder. /// - public sealed class PipelineBuilder(IContext context = null) + public sealed class PipelineBuilder(PipelineContext context) { + /// + /// LIFO,Last In First Out. + /// private ImmutableStack _middlewareStack = ImmutableStack.Empty; - public PipelineBuilder Use(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 UseIf(TMiddleware middleware, Func condition) + public PipelineBuilder UseMiddlewareIf(bool? condition) where TMiddleware : IMiddleware, new() { - if (!condition()) return this; + if (condition is null or false) + return this; + + var middleware = new TMiddleware(); _middlewareStack = _middlewareStack.Push(middleware); return this; } public async Task Build() { - var middleware = _middlewareStack.Peek(); - await middleware.InvokeAsync(context, _middlewareStack.Peek()); + foreach (var middleware in _middlewareStack) + { + await middleware.InvokeAsync(context); + } } } } \ No newline at end of file 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 0000000000000000000000000000000000000000..8a85686fe86a39b8c582b912266727d1cb1d3500 --- /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 527325d6fe6773aae7d2d68ad5894b53cd1b4ab8..dacb0d9728388194bf8290fffc7ab51f4bf6c985 100644 --- a/src/c#/GeneralUpdate.Common/Internal/Strategy/AbstractStrategy.cs +++ b/src/c#/GeneralUpdate.Common/Internal/Strategy/AbstractStrategy.cs @@ -1,23 +1,46 @@ using System; -using System.Text; +using System.Diagnostics; +using System.IO; +using System.Runtime.InteropServices; using System.Threading.Tasks; +using GeneralUpdate.Common.FileBasic; +using GeneralUpdate.Common.Shared.Object; namespace GeneralUpdate.Common.Internal.Strategy { public abstract class AbstractStrategy : IStrategy { - protected const string PATCHS = "patchs"; - + protected const string Patchs = "patchs"; + public virtual void Execute() => throw new NotImplementedException(); + + public virtual void StartApp() => throw new NotImplementedException(); + + public virtual Task ExecuteAsync() => 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(GlobalConfigInfo parameter) => throw new NotImplementedException(); - public virtual void Create(T parameter, Encoding encoding) where T : class => throw new NotImplementedException(); + protected static void OpenBrowser(string url) + { + if (RuntimeInformation.IsOSPlatform(OSPlatform.Windows)) + { + Process.Start(new ProcessStartInfo("cmd", $"/c start {url}") { CreateNoWindow = true }); + return; + } + + if (RuntimeInformation.IsOSPlatform(OSPlatform.Linux)) + { + Process.Start("xdg-open", url); + return; + } + + throw new PlatformNotSupportedException("Unsupported OS platform"); + } + + protected static void Clear(string path) + { + if (Directory.Exists(path)) + StorageManager.DeleteDirectory(path); + } } } \ 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 5295a586c988649f16922531f43f810d5b328c52..bc583b718836cc7f2fefcf2a6509a7ef7c5f3aa8 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 { @@ -15,26 +16,16 @@ namespace GeneralUpdate.Common.Internal.Strategy /// /// After the update is complete. /// - /// - /// - /// - bool StartApp(string appName, int appType); - - /// - /// Get the platform for the current strategy. - /// - /// - string GetPlatform(); - + void StartApp(); + /// /// Execution strategy. /// - Task ExecuteTaskAsync(); + Task ExecuteAsync(); /// /// Create a strategy. /// - /// Abstraction for updating package information. - void Create(T parameter) where T : class; + void Create(GlobalConfigInfo 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 4c30240a3e636c3c9ae4c12d7f23d64e4860b79c..0000000000000000000000000000000000000000 --- 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/Assembler/VersionAssembler.cs b/src/c#/GeneralUpdate.Common/Shared/Object/Assembler/VersionAssembler.cs deleted file mode 100644 index 8c3f1ad4d88ac452fc593da7128ba726fced4e88..0000000000000000000000000000000000000000 --- 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 1b7f541df4e3551906f1ede7cea46dd088a663b3..58984300638439d27189a17a03131ad33552c891 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 { - public class Configinfo : Entity + /// + /// 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,56 @@ namespace GeneralUpdate.Common.Shared.Object /// Client current version. /// public string ClientVersion { get; set; } - + /// - /// Update check api address. + /// Upgrade Client current version. /// - public string UpdateUrl { get; set; } + public string UpgradeClientVersion { get; set; } /// - /// Update log web address. + /// installation path (for update file logic). /// - public string UpdateLogUrl { get; set; } + public string InstallPath { get; set; } /// - /// installation path (for update file logic). + /// Files in the blacklist will skip the update. /// - public string InstallPath { get; set; } + public List BlackFiles { get; set; } /// - /// Update check api address. + /// File formats in the blacklist will skip the update. /// - public string MainUpdateUrl { get; set; } + public List BlackFormats { get; set; } + + /// + /// Product ID. + /// + public string ProductId { get; set; } - public string MainAppName { get; set; } + public string Bowl { 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 72b8a5fa5c0344bdb61a820c44782ff357b55b7b..dfa9b49a90a68bc8b20f060943dc1d71bbb3f7ae 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/UploadReapDTO.cs b/src/c#/GeneralUpdate.Common/Shared/Object/DTO/UploadReapDTO.cs deleted file mode 100644 index bd1ab92c996518d78b876b2124497b142ac64f15..0000000000000000000000000000000000000000 --- 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 502bd92ed3ece51b533bb4056496e1e7d485a4ab..0000000000000000000000000000000000000000 --- 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/DTO/VersionRespDTO.cs b/src/c#/GeneralUpdate.Common/Shared/Object/DTO/VersionRespDTO.cs index d42a71d63e52cb961a29b0b3bdbe79307af4affc..d1c9eca6e0250f5d7f7683230b1b78d7af28334c 100644 --- a/src/c#/GeneralUpdate.Common/Shared/Object/DTO/VersionRespDTO.cs +++ b/src/c#/GeneralUpdate.Common/Shared/Object/DTO/VersionRespDTO.cs @@ -1,27 +1,4 @@ using System.Collections.Generic; -namespace GeneralUpdate.Common.Shared.Object -{ - 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 +namespace GeneralUpdate.Common.Shared.Object; +public class VersionRespDTO : BaseResponseDTO>; \ No newline at end of file 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 c40237f69652e2846796b2b8c1970bb15cca40df..0000000000000000000000000000000000000000 --- 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/Enum/Format.cs b/src/c#/GeneralUpdate.Common/Shared/Object/Enum/Format.cs index cae1d5d273ec26badc3668313cd22ee3ca229399..abb8efdd9d0068472c52580c6f49788e13677c85 100644 --- a/src/c#/GeneralUpdate.Common/Shared/Object/Enum/Format.cs +++ b/src/c#/GeneralUpdate.Common/Shared/Object/Enum/Format.cs @@ -2,7 +2,6 @@ { public class Format { - public const string ZIP = "zip"; - public const string SEVENZIP = "7z"; + public const string ZIP = ".zip"; } } \ No newline at end of file diff --git a/src/c#/GeneralUpdate.Common/Shared/Object/Enum/HttpStatus.cs b/src/c#/GeneralUpdate.Common/Shared/Object/Enum/HttpStatus.cs deleted file mode 100644 index fa31e8d4c1ee17765b27a08bf70df23d4b92f616..0000000000000000000000000000000000000000 --- a/src/c#/GeneralUpdate.Common/Shared/Object/Enum/HttpStatus.cs +++ /dev/null @@ -1,12 +0,0 @@ -namespace GeneralUpdate.Common.Shared.Object -{ - 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.Common/Shared/Object/Enum/PlatformType.cs b/src/c#/GeneralUpdate.Common/Shared/Object/Enum/PlatformType.cs index 65256da7c799e9ccf32d32709cdd55586a76947c..1382a881249a969eee837281e800440302a5f900 100644 --- a/src/c#/GeneralUpdate.Common/Shared/Object/Enum/PlatformType.cs +++ b/src/c#/GeneralUpdate.Common/Shared/Object/Enum/PlatformType.cs @@ -1,48 +1,8 @@ -namespace GeneralUpdate.Common.Shared.Object -{ - /// - /// 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"; +namespace GeneralUpdate.Common.Shared.Object.Enum; - /// - /// 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"; - } +public class PlatformType +{ + public const int Windows = 1; + + public const int Linux = 2; } \ No newline at end of file diff --git a/src/c#/GeneralUpdate.Common/Shared/Object/Enum/ProgressType.cs b/src/c#/GeneralUpdate.Common/Shared/Object/Enum/ProgressType.cs deleted file mode 100644 index 0547e7648c30eeb8d9c9961c121f05648100a277..0000000000000000000000000000000000000000 --- a/src/c#/GeneralUpdate.Common/Shared/Object/Enum/ProgressType.cs +++ /dev/null @@ -1,45 +0,0 @@ -namespace GeneralUpdate.Common.Shared.Object -{ - 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.Common/Shared/Object/Enum/ReportType.cs b/src/c#/GeneralUpdate.Common/Shared/Object/Enum/ReportType.cs new file mode 100644 index 0000000000000000000000000000000000000000..c792ba6bffa4ad1f1b3af85888f1cdf7c3dadb12 --- /dev/null +++ b/src/c#/GeneralUpdate.Common/Shared/Object/Enum/ReportType.cs @@ -0,0 +1,10 @@ +namespace GeneralUpdate.Common.Shared.Object.Enum; + +public class ReportType +{ + public const int None = 0; + + public const int Success = 2; + + public const int Failure = 3; +} \ 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 0000000000000000000000000000000000000000..755b7396216c9dbd6e65b350b9513505f70c9ea1 --- /dev/null +++ b/src/c#/GeneralUpdate.Common/Shared/Object/GlobalConfigInfo.cs @@ -0,0 +1,120 @@ +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 main program 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 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/ParamsOSS.cs b/src/c#/GeneralUpdate.Common/Shared/Object/GlobalConfigInfoOSS.cs similarity index 55% rename from src/c#/GeneralUpdate.Common/Shared/Object/ParamsOSS.cs rename to src/c#/GeneralUpdate.Common/Shared/Object/GlobalConfigInfoOSS.cs index 3f8619050ff3141810d47029b254d088a0e3321b..1bd152ba6b005201b9f61c40ec8ac7dfdaf9b615 100644 --- a/src/c#/GeneralUpdate.Common/Shared/Object/ParamsOSS.cs +++ b/src/c#/GeneralUpdate.Common/Shared/Object/GlobalConfigInfoOSS.cs @@ -1,18 +1,30 @@ using System; +using System.Text.Json.Serialization; namespace GeneralUpdate.Common.Shared.Object { - public class ParamsOSS : Entity + public class GlobalConfigInfoOSS { + [JsonPropertyName("Url")] public string Url { get; set; } + [JsonPropertyName("AppName")] public string AppName { get; set; } + [JsonPropertyName("CurrentVersion")] public string CurrentVersion { get; set; } + [JsonPropertyName("VersionFileName")] public string VersionFileName { get; set; } + + [JsonPropertyName("Encoding")] + public string Encoding { get; set; } - public ParamsOSS(string url, string appName, string currentVersion, string versionFileName) + public GlobalConfigInfoOSS() + { + } + + public GlobalConfigInfoOSS(string url, string appName, string currentVersion, string versionFileName) { Url = url ?? throw new ArgumentNullException(nameof(url)); AppName = appName ?? throw new ArgumentNullException(nameof(appName)); diff --git a/src/c#/GeneralUpdate.Common/Shared/Object/Packet.cs b/src/c#/GeneralUpdate.Common/Shared/Object/Packet.cs index a2edd846cb7fd3e8f16eeccca47574c30c51b9a0..19fa1f4b2dc312e5cc55dd998f6c056e470fc90f 100644 --- a/src/c#/GeneralUpdate.Common/Shared/Object/Packet.cs +++ b/src/c#/GeneralUpdate.Common/Shared/Object/Packet.cs @@ -1,145 +1,40 @@ using System; -using System.Collections.Generic; -using System.Text; +using System.Text.Json.Serialization; -namespace GeneralUpdate.Common.Shared.Object -{ - 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; } +namespace GeneralUpdate.Common.Shared.Object; - /// - /// 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; } +/// +/// Currently used only for upgrade push. +/// +public class Packet +{ + [JsonPropertyName("Name")] + public string? Name { get; set; } - /// - /// The latest version. - /// - public string LastVersion { get; set; } + [JsonPropertyName("Hash")] + public string Hash { get; set; } - /// - /// installation path (for update file logic). - /// - public string InstallPath { get; set; } + [JsonPropertyName("ReleaseDate")] + public DateTime? ReleaseDate { get; set; } - /// - /// Download file temporary storage path (for update file logic). - /// - public string TempPath { get; set; } + [JsonPropertyName("Url")] + public string? Url { get; set; } - /// - /// Configuration parameters for upgrading the terminal program. - /// - public string ProcessBase64 { get; set; } + [JsonPropertyName("Version")] + public string? Version { get; set; } - /// - /// The platform to which the current strategy belongs. - /// - public string Platform { get; set; } + [JsonPropertyName("AppType")] + public int? AppType { get; set; } - /// - /// Files in the blacklist will skip the update. - /// - public List BlackFiles { get; set; } + [JsonPropertyName("Platform")] + public int? Platform { get; set; } - /// - /// File formats in the blacklist will skip the update. - /// - public List BlackFormats { get; set; } + [JsonPropertyName("ProductId")] + public string? ProductId { get; set; } - /// - /// Whether to enable the driver upgrade function. - /// - public bool DriveEnabled { get; set; } + [JsonPropertyName("IsForcibly")] + public bool? IsForcibly { 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; } - } + [JsonPropertyName("IsFreeze")] + public bool? IsFreeze { 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 00062bdd83ee0bbdc6c0aaafd90fa1c6788b0161..b004c969e8ab6d4992e99651efce407dc77a21f5 100644 --- a/src/c#/GeneralUpdate.Common/Shared/Object/ProcessInfo.cs +++ b/src/c#/GeneralUpdate.Common/Shared/Object/ProcessInfo.cs @@ -2,104 +2,119 @@ using System.Collections.Generic; using System.IO; using System.Text; +using System.Text.Json.Serialization; namespace GeneralUpdate.Common.Shared.Object { - public class ProcessInfo : Entity + public class ProcessInfo { - public 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 + , 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}."); 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); + UpdateLogUrl = updateLogUrl; + CompressEncoding = compressEncoding.WebName; 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; + ReportUrl = reportUrl ?? throw new ArgumentNullException(nameof(reportUrl)); + BackupDirectory = backupDirectory ?? throw new ArgumentNullException(nameof(backupDirectory)); + Bowl = bowl; } - 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. /// + [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. /// - public string LogUrl { get; set; } + [JsonPropertyName("UpdateLogUrl")] + public string UpdateLogUrl { get; set; } - public int CompressEncoding { get; set; } + /// + /// The encoding type of the update package. + /// + [JsonPropertyName("CompressEncoding")] + public string 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; } + + /// + /// 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; } } } \ 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 edeca18b6ebbf3f7f57c66948ef40b5152931d57..0000000000000000000000000000000000000000 --- 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.Common/Shared/Object/VersionInfo.cs b/src/c#/GeneralUpdate.Common/Shared/Object/VersionInfo.cs index e118cbbf8a7103f50ebbe4ea83a1e9726154a690..47b8659ee6c8e342757f0b97681b81b8e37685db 100644 --- a/src/c#/GeneralUpdate.Common/Shared/Object/VersionInfo.cs +++ b/src/c#/GeneralUpdate.Common/Shared/Object/VersionInfo.cs @@ -1,50 +1,43 @@ using System; +using System.Text.Json.Serialization; -namespace GeneralUpdate.Common.Shared.Object +namespace GeneralUpdate.Common.Shared.Object; + +public class VersionInfo { - 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; - } - } + [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.Core/Domain/PO/VersionPO.cs b/src/c#/GeneralUpdate.Common/Shared/Object/VersionOSS.cs similarity index 61% rename from src/c#/GeneralUpdate.Core/Domain/PO/VersionPO.cs rename to src/c#/GeneralUpdate.Common/Shared/Object/VersionOSS.cs index bca517a3cee628944ee34477cd381a6d48a91768..6fbcf120e0e74801905d8af457046fdab2f95ca1 100644 --- a/src/c#/GeneralUpdate.Core/Domain/PO/VersionPO.cs +++ b/src/c#/GeneralUpdate.Common/Shared/Object/VersionOSS.cs @@ -1,33 +1,42 @@ -namespace GeneralUpdate.Core.Domain.PO +using System; +using System.Text.Json.Serialization; + +namespace GeneralUpdate.Common.Shared.Object { /// /// Version data persistence. /// - public class VersionPO + public class VersionOSS { /// /// Update package release time. /// - public long PubTime { get; set; } + + [JsonPropertyName("PubTime")] + public DateTime PubTime { get; set; } /// /// Update package name. /// - public string Name { get; set; } + [JsonPropertyName("PacketName")] + public string PacketName { get; set; } /// /// Compare and verify with the downloaded update package. /// + [JsonPropertyName("Hash")] public string Hash { get; set; } /// /// The version number. /// + [JsonPropertyName("Version")] public string Version { get; set; } /// /// Remote service url address. /// + [JsonPropertyName("Url")] public string Url { get; set; } } } \ No newline at end of file diff --git a/src/c#/GeneralUpdate.Common/Shared/Object/VersionPO.cs b/src/c#/GeneralUpdate.Common/Shared/Object/VersionPO.cs deleted file mode 100644 index d1c8ba33be8671e22a176a821c3c4ce8f0470cc3..0000000000000000000000000000000000000000 --- a/src/c#/GeneralUpdate.Common/Shared/Object/VersionPO.cs +++ /dev/null @@ -1,33 +0,0 @@ -namespace GeneralUpdate.Common.Shared.Object -{ - /// - /// 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.Common/Shared/Service/VersionService.cs b/src/c#/GeneralUpdate.Common/Shared/Service/VersionService.cs index 52e74adac4124421cc62ac235c5b35507f02762f..c57fe32411eb7a75dbde485b5a86f68a9d031632 100644 --- a/src/c#/GeneralUpdate.Common/Shared/Service/VersionService.cs +++ b/src/c#/GeneralUpdate.Common/Shared/Service/VersionService.cs @@ -1,71 +1,96 @@ using System; +using System.Collections.Generic; +using System.Diagnostics; 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.Metadata; using System.Threading.Tasks; +using GeneralUpdate.Common.Internal.JsonContext; using GeneralUpdate.Common.Shared.Object; -using Newtonsoft.Json; namespace GeneralUpdate.Common.Shared.Service { public class VersionService { - private static readonly HttpClient _httpClient; - - static VersionService() + private 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 } + }; + await PostTaskAsync>(httpUrl, parameters, ReportRespJsonContext.Default.BaseResponseDTOBoolean); } - 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) + var parameters = new Dictionary { - throw new ArgumentNullException( - nameof(updateResp), - "The verification request is abnormal, please check the network or parameter configuration!" - ); - } - - if (updateResp.Code == 200) - { - return updateResp; - } - else - { - 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, VersionRespJsonContext.Default.VersionRespDTO); } - private async Task GetTaskAsync(string url, string headerKey = null, string headerValue = null) + private static async Task PostTaskAsync(string httpUrl, Dictionary parameters, JsonTypeInfo? typeInfo = null) { try { - var request = new HttpRequestMessage(HttpMethod.Get, url); - if (!string.IsNullOrEmpty(headerKey) && !string.IsNullOrEmpty(headerValue)) + var uri = new Uri(httpUrl); + using var httpClient = new HttpClient(new HttpClientHandler { - 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 = JsonConvert.DeserializeObject(responseContent); - - return result; + ServerCertificateCustomValidationCallback = CheckValidationResult + }); + httpClient.Timeout = TimeSpan.FromSeconds(15); + httpClient.DefaultRequestHeaders.Accept.ParseAdd("text/html, application/xhtml+xml, */*"); + var parametersJson = + JsonSerializer.Serialize(parameters, HttpParameterJsonContext.Default.DictionaryStringObject); + var stringContent = new StringContent(parametersJson, Encoding.UTF8, "application/json"); + var result = await httpClient.PostAsync(uri, stringContent); + var reseponseJson = await result.Content.ReadAsStringAsync(); + return typeInfo == null + ? JsonSerializer.Deserialize(reseponseJson) + : JsonSerializer.Deserialize(reseponseJson, typeInfo); } - catch (Exception ex) + catch (Exception e) { - // Log the exception here as needed - return default; + Debug.WriteLine(e.Message); + throw; } } diff --git a/src/c#/GeneralUpdate.Core/Bootstrap/AbstractBootstrap.cs b/src/c#/GeneralUpdate.Core/Bootstrap/AbstractBootstrap.cs deleted file mode 100644 index 637f467720280fb8347fe63677a790206969c0a9..0000000000000000000000000000000000000000 --- a/src/c#/GeneralUpdate.Core/Bootstrap/AbstractBootstrap.cs +++ /dev/null @@ -1,223 +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 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()); - - 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 - /// - public 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 - /// - public 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; - } - - 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); - } - } - - #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); - - protected 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 13c09a3b5dff739033e158f58a167c7f6dad85de..0000000000000000000000000000000000000000 --- 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 46e3c09a1a34900a81fd16357aec2115f2fe66a3..0000000000000000000000000000000000000000 --- 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 e5d50689dd8d545c290012333c2cf0010b25f1c9..0000000000000000000000000000000000000000 --- 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 b74a0594a2c0936ec4c3f55bc2d98b2699354595..0000000000000000000000000000000000000000 --- 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 be5f91e732a11ab962567bbb48489242febcdaf8..0000000000000000000000000000000000000000 --- 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 6fe0132348c45bcb0fba2098fbbb446cc9c1add0..0000000000000000000000000000000000000000 --- 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 97580db8837532ce2d9267c5c7abb2945f6ad78e..0000000000000000000000000000000000000000 --- 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 9dd15143224a829eeec25b7b465363b155d2445a..0000000000000000000000000000000000000000 --- 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 5a89c1d1c2bd7ca912ee44a55cbc2dc7b5ec6fa7..0000000000000000000000000000000000000000 --- 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/CustomAwaiter/IAwaitable.cs b/src/c#/GeneralUpdate.Core/CustomAwaiter/IAwaitable.cs deleted file mode 100644 index 0c854044ef59f2bba3f9a7787cdd875e50f047cd..0000000000000000000000000000000000000000 --- 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 42255ef04049a95f5be13fe959d3edc71465c842..0000000000000000000000000000000000000000 --- 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 f4876ca403c570162f726608567068b5a6108ed2..0000000000000000000000000000000000000000 --- 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 867c325fa33cd341a418e7fee22de478f62acb85..0000000000000000000000000000000000000000 --- 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 a9829bd6474a79e4f42240463eb2ba7c5be5237d..0000000000000000000000000000000000000000 --- 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 ac850e2b0bac3f01a481ff8b71728b4ed88d1d1e..0000000000000000000000000000000000000000 --- 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 3ebb1f93d46fb3abde0be74ef8368becbe837a42..0000000000000000000000000000000000000000 --- 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 c3c59264110627b831dd0cd0f18bfb1e9ff05d91..0000000000000000000000000000000000000000 --- 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 6f070e7b4aea69075399847a9ca5baaa283bd858..0000000000000000000000000000000000000000 --- 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 eda2a878e8d29a1095a9e1488dbdaf6edfd60586..0000000000000000000000000000000000000000 --- 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 88eb236c251998df96eef8cccaf96b71b1aa37a7..0000000000000000000000000000000000000000 --- 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 4e4560e718bfc1f3a421dfd210b129445c96feb9..0000000000000000000000000000000000000000 --- 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 a33975edad961a2478d43bce7d61ef2c99328f5b..0000000000000000000000000000000000000000 --- 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 8b5e76a15ee23e3d59e967fdec98924f94c2b03c..0000000000000000000000000000000000000000 --- 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 8283cd8a9ee98ea964d916221600106b3d187402..0000000000000000000000000000000000000000 --- 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 4082dd9f93708c69c4745eb047fbc396365c113e..0000000000000000000000000000000000000000 --- 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 e1d611b71e38df322a26ab57423ae94194fa71e2..0000000000000000000000000000000000000000 --- 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 b983ff24b6298defaa76aa55c990614717446c90..0000000000000000000000000000000000000000 --- 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 081e69fc910a812fd46443b6610615f27bd83ce5..0000000000000000000000000000000000000000 --- 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 3ee8cc306c86a39376f9952c26561735b592ee5d..0000000000000000000000000000000000000000 --- 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 fb1e8aeeb574a3c65879f99adef140b0f7b52c37..0000000000000000000000000000000000000000 --- 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 027268be8a48c3a1c2000e97306e2269123cc409..0000000000000000000000000000000000000000 --- 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 57cea11a3d23e52168496d329bc01cf5ac9bd9b3..0000000000000000000000000000000000000000 --- 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/WillMessagePO.cs b/src/c#/GeneralUpdate.Core/Domain/PO/WillMessagePO.cs deleted file mode 100644 index 30dca5029e04f2d09ea9855b3a7324737ef78be8..0000000000000000000000000000000000000000 --- 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 4c3af35556174669c3adad42ebe2085744a76b10..0000000000000000000000000000000000000000 --- 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 a8156c49a4972467b3e402a918a3809f65c3766d..0000000000000000000000000000000000000000 --- 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 746c40fa4f9f0646e9001963d3c145c1f62d7ffd..0000000000000000000000000000000000000000 --- 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 aa4d292191fdfbdaaf095106f317a1fd6fe370ad..0000000000000000000000000000000000000000 --- 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 f22c7cbb3cf2c0de8fb081f6a9aba9a335aa7a64..0000000000000000000000000000000000000000 --- 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 146ec54510e89f24e47d51eab42517252d54fdb0..0000000000000000000000000000000000000000 --- 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 f1d2856f809a626b86e765fdfc0c118f41598749..0000000000000000000000000000000000000000 --- 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 c6502641fda78ec2d7ea6f7baccad0bfcf5f8107..0000000000000000000000000000000000000000 --- 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/Driver/BackupDriverCommand.cs b/src/c#/GeneralUpdate.Core/Driver/BackupDriverCommand.cs index a98efe95d24ff65aaf87cabdf5fc4ff0d17abf45..8613b547dbce590377b59de6897e4b001046c857 100644 --- a/src/c#/GeneralUpdate.Core/Driver/BackupDriverCommand.cs +++ b/src/c#/GeneralUpdate.Core/Driver/BackupDriverCommand.cs @@ -1,48 +1,124 @@ -using System.IO; +using System; +using System.Collections.Generic; +using System.Diagnostics; +using System.IO; +using System.Linq; using System.Text; +using GeneralUpdate.Common.FileBasic; 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)) + { + StorageManager.DeleteDirectory(information.OutPutDirectory); + } + + 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. - 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 + * 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(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 aa4fb1156f327f65c9e268110ad92ccdd08d3e46..f0d26cea1f8e2888e0a80aca2f85104ee201a3dc 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 @@ -9,6 +8,9 @@ namespace GeneralUpdate.Core.Driver /// public class CommandExecutor { + private CommandExecutor() + { } + public static void ExecuteCommand(string command) { /* @@ -29,17 +31,34 @@ If possible, use pre-tested drivers that are proven to work. WindowStyle = ProcessWindowStyle.Hidden, FileName = "cmd.exe", Arguments = command, - UseShellExecute = true, + UseShellExecute = false, + RedirectStandardOutput = true, + RedirectStandardError = true, Verb = "runas" }; - using (var process = new Process { StartInfo = processStartInfo }) + var process = new Process(); + try { + process.StartInfo = processStartInfo; 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) - ThrowExceptionUtility.Throw($"Operation failed code: {process.ExitCode}"); + throw new ApplicationException($"Operation failed code: {process.ExitCode}"); + } + finally + { + process.Dispose(); } } } diff --git a/src/c#/GeneralUpdate.Core/Driver/DeleteDriverCommand.cs b/src/c#/GeneralUpdate.Core/Driver/DeleteDriverCommand.cs index 0d21452ff0689931fe7e1ed6de68245ea9340f24..f0fe0e7af97d21707e33499b0c1a10d4a661fd94 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 0000000000000000000000000000000000000000..348db74f23ecae0140484a90a9364c14bc910bc6 --- /dev/null +++ b/src/c#/GeneralUpdate.Core/Driver/DriverCommand.cs @@ -0,0 +1,23 @@ +using System.Collections.Generic; +using System.IO; +using System.Linq; +using GeneralUpdate.Common.FileBasic; + +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 = StorageManager.GetAllFiles(patchPath, StorageManager.SkipDirectorys); + 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 0000000000000000000000000000000000000000..539cfb1ba5927a83071d973cd8171f8e8cecdb59 --- /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 3e1e1da45d332e92a6fc752a86be973895c016d1..e396e2a6d71c77b8215887172e9d83b73d4c5e11 100644 --- a/src/c#/GeneralUpdate.Core/Driver/DriverInformation.cs +++ b/src/c#/GeneralUpdate.Core/Driver/DriverInformation.cs @@ -1,6 +1,5 @@ -using GeneralUpdate.Core.Exceptions; +using System; using System.Collections.Generic; -using System.Linq; namespace GeneralUpdate.Core.Driver { @@ -9,58 +8,59 @@ 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 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 List 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 SetInstallDirectory(string installDirectory) + public Builder SetDriverFileExtension(string fileExtension) { - _information.InstallDirectory = installDirectory; + _information.DriverFileExtension = fileExtension; 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) + + public Builder SetDriverDirectory(string driverDirectory) + { + _information.DriverDirectory = driverDirectory; + return this; + } + + public Builder SetFieldMappings(Dictionary fieldMappings) { - _information.Drivers = driverNames; + _information.FieldMappings = fieldMappings; return this; } public DriverInformation Build() { - if (string.IsNullOrWhiteSpace(_information.InstallDirectory) || - string.IsNullOrWhiteSpace(_information.OutPutDirectory) || - !_information.Drivers.Any()) + if (string.IsNullOrWhiteSpace(_information.OutPutDirectory) || + string.IsNullOrWhiteSpace(_information.DriverFileExtension)) { - 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/DriverProcessor.cs b/src/c#/GeneralUpdate.Core/Driver/DriverProcessor.cs index 8923af2f7dd3dfd4efed6d5367db32a050b00cfb..3f4b848df5a7a9ef167fb74ca3a67075179ff55f 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(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 76e24f922f9aac9b2c8fc48c76b161b79a426c56..0000000000000000000000000000000000000000 --- 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 17953cd1d9c1060732c2a8e3e448976b5dfd8bff..cf42b8b35dfd73716a11e7339f07a334821eab09 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; @@ -8,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. @@ -27,19 +22,19 @@ namespace GeneralUpdate.Core.Driver * (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 path = Path.Combine(information.DriverDirectory, driver.OriginalName); var command = new StringBuilder("/c pnputil /add-driver ") .Append(path) .Append(" /install") .ToString(); CommandExecutor.ExecuteCommand(command); } - } - catch (Exception ex) - { - //restore all the drivers in the backup directory. - new RestoreDriverCommand(_information).Execute(); - ThrowExceptionUtility.Throw($"Failed to execute install command for {_information.InstallDirectory}", ex); + 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 f05c1679ed8cfa0f3bd7527b8e0c4a4fea1b659e..d277ac4a923a92040393e9af63e2f115db074759 100644 --- a/src/c#/GeneralUpdate.Core/Driver/RestoreDriverCommand.cs +++ b/src/c#/GeneralUpdate.Core/Driver/RestoreDriverCommand.cs @@ -1,33 +1,40 @@ -using GeneralUpdate.Core.Exceptions; -using System; -using System.IO; +using System; +using System.Linq; using System.Text; +using GeneralUpdate.Common.FileBasic; namespace GeneralUpdate.Core.Driver { - public class RestoreDriverCommand : IDriverCommand + public class RestoreDriverCommand(DriverInformation information) { - private DriverInformation _information; - - public RestoreDriverCommand(DriverInformation information) => _information = information; - public void Execute() { try { - foreach (var driver in _information.Drivers) + var backupFiles = StorageManager.GetAllFiles(information.OutPutDirectory, StorageManager.SkipDirectorys); + 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 { - 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/Events/CommonArgs/ExceptionEventArgs.cs b/src/c#/GeneralUpdate.Core/Events/CommonArgs/ExceptionEventArgs.cs deleted file mode 100644 index 2b54d23d2b289d2853b8fabf34bb7688d5df293a..0000000000000000000000000000000000000000 --- 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 66ee85e2510c2175bc0c8ffaef626b675c90f448..0000000000000000000000000000000000000000 --- 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 6bf07c608598bd1c8077938cc22c108607398ec3..0000000000000000000000000000000000000000 --- 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 e19511698dc6c7b48ec856d1a61ea9bf2174fa4c..0000000000000000000000000000000000000000 --- 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 cdf8bd3edcd1b96360d77e469f11da11f8859541..0000000000000000000000000000000000000000 --- 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 dbecc3eadb8559fad95b9044ee06b83707ffb677..0000000000000000000000000000000000000000 --- 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 08dc8e35674eb38759ddaeb134037d334beb00b8..0000000000000000000000000000000000000000 --- 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 183c6b72e5d306ece66501a7f9d4890af0492c70..0000000000000000000000000000000000000000 --- 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 44cb32643571ccb37a3c3c8208ba6edefd41dd50..0000000000000000000000000000000000000000 --- 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 fac82b7983ee4107c17b7877cd1d3e167a19625c..0000000000000000000000000000000000000000 --- 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 46d976b12301cf0d1d50dc55c82cfa92c023426f..0000000000000000000000000000000000000000 --- 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 f9bb6e160ec7f0d6f5ddb46ccc9a16d0a95ad97b..0000000000000000000000000000000000000000 --- 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 eeecb0540dd9ae09736259c55ac3c9c2826d6fb3..0000000000000000000000000000000000000000 --- 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 16c1fb061ccd810c8e5b5e8e340eae7135f096fe..0000000000000000000000000000000000000000 --- 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 7e2d60594bd77e3932325a469a0513fb9fab0536..0000000000000000000000000000000000000000 --- 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 25c310d08cfdb89a72f3c52b75cc39f0c4a76283..0000000000000000000000000000000000000000 --- 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/GeneralUpdate.Core.csproj b/src/c#/GeneralUpdate.Core/GeneralUpdate.Core.csproj index cf581a51e8d2ccdde67b8e07376bde1964f497d9..06639130e21f21cb0c0646c8a200ecbf4434a9f6 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,9 @@ enable - - - - - - - - - - - - - + - - - - - - - - - - - - \ No newline at end of file diff --git a/src/c#/GeneralUpdate.Core/GeneralUpdateBootstrap.cs b/src/c#/GeneralUpdate.Core/GeneralUpdateBootstrap.cs index 67913d5413881fb2c541e190524b5cb8cbae7c70..bbcba76a542a5b8378f3823157d5dbaaaca06f95 100644 --- a/src/c#/GeneralUpdate.Core/GeneralUpdateBootstrap.cs +++ b/src/c#/GeneralUpdate.Core/GeneralUpdateBootstrap.cs @@ -1,42 +1,166 @@ -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.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.FileBasic; +using GeneralUpdate.Common.Download; +using GeneralUpdate.Common.Internal; +using GeneralUpdate.Common.Internal.Bootstrap; +using GeneralUpdate.Common.Internal.Event; +using GeneralUpdate.Common.Internal.JsonContext; +using GeneralUpdate.Common.Internal.Strategy; +using GeneralUpdate.Common.Shared.Object; +using GeneralUpdate.Core.Strategys; namespace GeneralUpdate.Core { public class GeneralUpdateBootstrap : AbstractBootstrap { - public GeneralUpdateBootstrap() : base() => Remote(); + private readonly GlobalConfigInfo _configInfo; + private IStrategy? _strategy; - /// - /// Gets values from system environment variables (ClientParameter object to base64 string). - /// - private void Remote() + public GeneralUpdateBootstrap() + { + var json = GetProcessInfoJsonContext(); + if (string.IsNullOrWhiteSpace(json)) + throw new ArgumentException("json environment variable is not defined"); + + var processInfo = JsonSerializer.Deserialize(json, ProcessInfoJsonContext.Default.ProcessInfo); + 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 = Encoding.GetEncoding(processInfo.CompressEncoding), + Format = processInfo.CompressFormat, + DownloadTimeOut = processInfo.DownloadTimeOut, + AppSecretKey = processInfo.AppSecretKey, + UpdateVersions = processInfo.UpdateVersions, + TempPath = StorageManager.GetTempDirectory("upgrade_temp"), + ReportUrl = processInfo.ReportUrl, + BackupDirectory = processInfo.BackupDirectory + }; + } + + public override async Task LaunchAsync() { try { - var base64 = Environment.GetEnvironmentVariable("ProcessBase64", EnvironmentVariableTarget.User); - var processInfo = FileProvider.Deserialize(base64); - Packet = ProcessAssembler.ToPacket(processInfo); - Packet.AppType = AppType.UpgradeApp; - Packet.TempPath = $"{FileProvider.GetTempDirectory(processInfo.LastVersion)}{Path.DirectorySeparatorChar}"; + StrategyFactory(); + var manager = + new DownloadManager(_configInfo.TempPath, _configInfo.Format, _configInfo.DownloadTimeOut); + manager.MultiAllDownloadCompleted += OnMultiAllDownloadCompleted; + manager.MultiDownloadCompleted += OnMultiDownloadCompleted; + manager.MultiDownloadError += OnMultiDownloadError; + manager.MultiDownloadStatistics += OnMultiDownloadStatistics; + foreach (var versionInfo in _configInfo.UpdateVersions) + { + manager.Add(new DownloadTask(manager, versionInfo)); + } + + await manager.LaunchTasksAsync(); } - catch (Exception ex) + catch (Exception exception) { - throw new ArgumentException($"Client parameter json conversion failed, please check whether the parameter content is legal : {ex.Message},{ex.StackTrace}."); + Debug.WriteLine(exception.Message); + EventManager.Instance.Dispatch(this, new ExceptionEventArgs(exception, exception.Message)); } + return this; + } + + #region public method + + public GeneralUpdateBootstrap SetFieldMappings(Dictionary fieldMappings) + { + _configInfo.FieldMappings = fieldMappings; + return this; } - /// - /// Start the update. - /// - /// - public Task LaunchTaskAsync() => Task.Run(() => base.LaunchAsync()); + public GeneralUpdateBootstrap AddListenerMultiAllDownloadCompleted( + Action callbackAction) + => AddListener(callbackAction); + + public GeneralUpdateBootstrap AddListenerMultiDownloadCompleted( + Action callbackAction) + => AddListener(callbackAction); + + public GeneralUpdateBootstrap AddListenerMultiDownloadError( + Action callbackAction) + => AddListener(callbackAction); + + public GeneralUpdateBootstrap AddListenerMultiDownloadStatistics( + Action callbackAction) + => AddListener(callbackAction); + + public GeneralUpdateBootstrap AddListenerException(Action callbackAction) + => AddListener(callbackAction); + + #endregion + + private static string? GetProcessInfoJsonContext() + { + if (RuntimeInformation.IsOSPlatform(OSPlatform.Windows)) + return Environment.GetEnvironmentVariable("ProcessInfo", EnvironmentVariableTarget.User); + + if (RuntimeInformation.IsOSPlatform(OSPlatform.Linux)) + { + var jsonFileName = "ProcessInfo.json"; + if (File.Exists(jsonFileName)) + return File.ReadAllText(jsonFileName); + } + + return null; + } + + protected override Task ExecuteStrategyAsync()=> throw new NotImplementedException(); + + protected override void ExecuteStrategy() + { + _strategy?.Create(_configInfo); + _strategy?.Execute(); + } + + protected override GeneralUpdateBootstrap 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 GeneralUpdateBootstrap AddListener(Action callbackAction) where TArgs : EventArgs + { + Debug.Assert(callbackAction != null); + EventManager.Instance.AddListener(callbackAction); + return this; + } + + private void OnMultiDownloadStatistics(object sender, MultiDownloadStatisticsEventArgs 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 diff --git a/src/c#/GeneralUpdate.Core/GeneralUpdateOSS.cs b/src/c#/GeneralUpdate.Core/GeneralUpdateOSS.cs index d6525c47569390d6c2ccc4eae0d65e5e3f8aa232..b22181d441b8418d116595d282074e3175ffe068 100644 --- a/src/c#/GeneralUpdate.Core/GeneralUpdateOSS.cs +++ b/src/c#/GeneralUpdate.Core/GeneralUpdateOSS.cs @@ -1,97 +1,52 @@ -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.Text; +using System; +using System.Diagnostics; +using System.Text.Json; using System.Threading.Tasks; +using GeneralUpdate.Common.Internal.JsonContext; +using GeneralUpdate.Common.Shared.Object; +using GeneralUpdate.Core.Strategys; namespace GeneralUpdate.Core { public sealed class GeneralUpdateOSS { - #region Constructors - - private GeneralUpdateOSS() - { } - - #endregion Constructors + private GeneralUpdateOSS() { } #region Public Methods /// /// Starting an OSS update for windows,Linux,mac platform. /// - /// - /// /// - public static async Task Start(ParamsOSS parameter, Encoding encoding) where TStrategy : AbstractStrategy, new() - { - await BaseStart(parameter, encoding); - } - - public static void AddListenerMultiAllDownloadCompleted(Action callbackAction) - { - AddListener(callbackAction); - } - - public static void AddListenerMultiDownloadProgress(Action callbackAction) - { - AddListener(callbackAction); - } - - public static void AddListenerMultiDownloadCompleted(Action callbackAction) - { - AddListener(callbackAction); - } - - public static void AddListenerMultiDownloadError(Action callbackAction) - { - AddListener(callbackAction); - } - - public static void AddListenerMultiDownloadStatistics(Action callbackAction) - { - AddListener(callbackAction); - } - - public static void AddListenerException(Action callbackAction) - { - AddListener(callbackAction); - } - - public static void AddListenerDownloadConfigProcess(Action callbackAction) - { - AddListener(callbackAction); - } + public static async Task Start()=> await BaseStart(); #endregion Public Methods #region Private Methods - - private static void AddListener(Action callbackAction) where TArgs : EventArgs - { - if (callbackAction != null) EventManager.Instance.AddListener(callbackAction); - } - + /// /// The underlying update method. /// - /// 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 - { - //Initializes and executes the policy. - var strategyFunc = new Func(() => new TStrategy()); - var strategy = strategyFunc(); - strategy.Create(parameter, encoding); - //Implement different update strategies depending on the platform. - await strategy.ExecuteTaskAsync(); - } - + private static async Task BaseStart() + { + try + { + var json = Environment.GetEnvironmentVariable("GlobalConfigInfoOSS", EnvironmentVariableTarget.User); + if (string.IsNullOrWhiteSpace(json)) + return; + + var parameter = JsonSerializer.Deserialize(json, GlobalConfigInfoOSSJsonContext.Default.GlobalConfigInfoOSS); + var strategy = new OSSStrategy(); + strategy.Create(parameter); + await strategy.ExecuteAsync(); + } + catch (Exception exception) + { + Debug.WriteLine(exception); + throw new Exception(exception.Message + "\n" + exception.StackTrace); + } + } + #endregion Private Methods } } \ 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 c9db372c885632b414421c8c64e5fbb3c9668361..0000000000000000000000000000000000000000 --- 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 650203436d7873497ad906ae9780f3c3cb4d4375..0000000000000000000000000000000000000000 --- 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 cec89ba1e735e983d3386d3467f7a1d6fb1fad94..0000000000000000000000000000000000000000 --- 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 d6135eaee2621cad9a8fe6f6c8c4f2c75e0f138a..0000000000000000000000000000000000000000 --- 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/Pipeline/CompressMiddleware.cs b/src/c#/GeneralUpdate.Core/Pipeline/CompressMiddleware.cs new file mode 100644 index 0000000000000000000000000000000000000000..27338fcfbe2a6d70f65df864c8aa955f54ec3dc2 --- /dev/null +++ b/src/c#/GeneralUpdate.Core/Pipeline/CompressMiddleware.cs @@ -0,0 +1,34 @@ +using System; +using System.Diagnostics; +using System.IO; +using System.Text; +using System.Threading.Tasks; +using GeneralUpdate.Common.Compress; +using GeneralUpdate.Common.Internal; +using GeneralUpdate.Common.Internal.Event; +using GeneralUpdate.Common.Internal.Pipeline; + +namespace GeneralUpdate.Core.Pipeline; + +public class CompressMiddleware : IMiddleware +{ + public Task InvokeAsync(PipelineContext context) + { + return Task.Run(() => + { + try + { + var format = context.Get("Format"); + var sourcePath = context.Get("ZipFilePath"); + var patchPath = context.Get("PatchPath"); + var encoding = context.Get("Encoding"); + CompressProvider.Decompress(format, sourcePath, patchPath, encoding); + } + catch (Exception e) + { + Debug.WriteLine(e.Message); + EventManager.Instance.Dispatch(this, new ExceptionEventArgs(e, e.Message)); + } + }); + } +} \ 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 0000000000000000000000000000000000000000..e1e834120fc9280090ca2a500841ec31ebc2696f --- /dev/null +++ b/src/c#/GeneralUpdate.Core/Pipeline/DriverMiddleware.cs @@ -0,0 +1,62 @@ +using System; +using System.Collections.Generic; +using System.Diagnostics; +using System.IO; +using System.Linq; +using System.Runtime.InteropServices; +using System.Threading.Tasks; +using GeneralUpdate.Common; +using GeneralUpdate.Common.Internal; +using GeneralUpdate.Common.Internal.Event; +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(() => + { + try + { + var outPutPath = context.Get("DriverOutPut"); + if (string.IsNullOrWhiteSpace(outPutPath)) + return; + + var patchPath = context.Get("PatchPath"); + 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(); + } + catch (Exception exception) + { + Debug.WriteLine(exception.Message); + EventManager.Instance.Dispatch(this, new ExceptionEventArgs(exception, exception.Message)); + } + }); + } +} \ 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 0000000000000000000000000000000000000000..4453fe7db9e45078b2bc9e7bdeb030557dbab0b0 --- /dev/null +++ b/src/c#/GeneralUpdate.Core/Pipeline/HashMiddleware.cs @@ -0,0 +1,49 @@ +using System; +using System.Diagnostics; +using System.Security.Cryptography; +using System.Threading.Tasks; +using GeneralUpdate.Common.HashAlgorithms; +using GeneralUpdate.Common.Internal; +using GeneralUpdate.Common.Internal.Event; +using GeneralUpdate.Common.Internal.Pipeline; + +namespace GeneralUpdate.Core.Pipeline; + +public class HashMiddleware : IMiddleware +{ + public async Task InvokeAsync(PipelineContext context) + { + try + { + var path = context.Get("ZipFilePath"); + var hash = context.Get("Hash"); + var isVerify = await VerifyFileHash(path, hash); + if (!isVerify) throw new CryptographicException("Hash verification failed !"); + } + catch (Exception exception) + { + Debug.WriteLine(exception.Message); + EventManager.Instance.Dispatch(this, new ExceptionEventArgs(exception, exception.Message)); + } + } + + private Task VerifyFileHash(string path, string hash) + { + return Task.Run(() => + { + try + { + var hashAlgorithm = new Sha256HashAlgorithm(); + var hashSha256 = hashAlgorithm.ComputeHash(path); + return string.Equals(hash, hashSha256, StringComparison.OrdinalIgnoreCase); + } + catch (Exception exception) + { + Debug.WriteLine(exception.Message); + EventManager.Instance.Dispatch(this, new ExceptionEventArgs(exception, exception.Message)); + } + + return false; + }); + } +} \ 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 0000000000000000000000000000000000000000..312b287984c3d669b817e7f0b7b3b9a2ec13c535 --- /dev/null +++ b/src/c#/GeneralUpdate.Core/Pipeline/PatchMiddleware.cs @@ -0,0 +1,34 @@ +using System; +using System.Collections.Generic; +using System.Diagnostics; +using System.Threading.Tasks; +using GeneralUpdate.Common.FileBasic; +using GeneralUpdate.Common.Internal; +using GeneralUpdate.Common.Internal.Event; +using GeneralUpdate.Common.Internal.Pipeline; +using GeneralUpdate.Differential; + +namespace GeneralUpdate.Core.Pipeline; + +public class PatchMiddleware : IMiddleware +{ + public async Task InvokeAsync(PipelineContext context) + { + try + { + var sourcePath = context.Get("SourcePath"); + var targetPath = context.Get("PatchPath"); + var blackFiles = context.Get>("BlackFiles"); + var blackFileFormats = context.Get>("BlackFileFormats"); + + BlackListManager.Instance.AddBlackFiles(blackFiles); + BlackListManager.Instance.AddBlackFileFormats(blackFileFormats); + await DifferentialCore.Instance?.Dirty(sourcePath, targetPath); + } + catch (Exception exception) + { + Debug.WriteLine(exception.Message); + EventManager.Instance.Dispatch(this, new ExceptionEventArgs(exception, exception.Message)); + } + } +} \ 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 92d152566fa02ef4fffb72d8bc7abce9855edd42..0000000000000000000000000000000000000000 --- 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 d3a279f8945c44ab6d61c65716d614d273d8700a..0000000000000000000000000000000000000000 --- 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 f00c56ca61a2b147988ddfeb2a4135f958359314..0000000000000000000000000000000000000000 --- 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 46f5ff3d9b8ad954e384041722a27c05fc0a3578..0000000000000000000000000000000000000000 --- 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 f4ba5cda1a1a9963d1755bd3faf33fcee8da76d5..0000000000000000000000000000000000000000 --- 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 0ea34b582df3a7b82fe6289573d4b51b5d5c3186..0000000000000000000000000000000000000000 --- 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 a6363516322106725d9738d3450495c5b2e99178..0000000000000000000000000000000000000000 --- 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 b66943ed8b198b99a53f37de37857917ee3908f3..0000000000000000000000000000000000000000 --- 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 5e1f8507d5288dd7c0433768093ca469f69aa836..0000000000000000000000000000000000000000 --- 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 00402ca0c51e296f6fc061c1ba18c87b93e658ee..0000000000000000000000000000000000000000 --- 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 55f2c79f3863df4f8e1c0e8b46ad3a1c56d4f546..0000000000000000000000000000000000000000 --- 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 11c0e49bbca651ba4ba7357f534cd7f7b59e352d..0000000000000000000000000000000000000000 --- 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 27531958cbf7b8ce4e7e57992a27a29ba6d10999..0000000000000000000000000000000000000000 --- 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 5b7384ec546216fb25d6195bd7849ddc88805396..0000000000000000000000000000000000000000 --- 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 cc909d518e5396b7a33c4acb8b36c8a3bc129551..0000000000000000000000000000000000000000 --- 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/LinuxStrategy.cs b/src/c#/GeneralUpdate.Core/Strategys/LinuxStrategy.cs new file mode 100644 index 0000000000000000000000000000000000000000..323bde7c4c8ffc63ef060aead856838186458bfc --- /dev/null +++ b/src/c#/GeneralUpdate.Core/Strategys/LinuxStrategy.cs @@ -0,0 +1,2 @@ +namespace GeneralUpdate.Core.Strategys; +public class LinuxStrategy : WindowsStrategy; \ 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 10f482baa90c7c65474be61da2a6a5343422b997..bf0e12c14548657d705c2171682e8f15c25936e6 100644 --- a/src/c#/GeneralUpdate.Core/Strategys/OSSStrategy.cs +++ b/src/c#/GeneralUpdate.Core/Strategys/OSSStrategy.cs @@ -1,73 +1,63 @@ -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.Factory; -using System; +using System; using System.Collections.Generic; using System.Diagnostics; using System.IO; +using System.Linq; using System.Text; using System.Threading.Tasks; +using GeneralUpdate.Common.Compress; +using GeneralUpdate.Common.FileBasic; +using GeneralUpdate.Common.Download; +using GeneralUpdate.Common.Internal.JsonContext; +using GeneralUpdate.Common.Shared.Object; namespace GeneralUpdate.Core.Strategys { - public sealed class OSSStrategy : AbstractStrategy + public class OSSStrategy { #region Private Members private readonly string _appPath = AppDomain.CurrentDomain.BaseDirectory; - private const string _format = ".zip"; - private const int _timeOut = 60; - private ParamsOSS _parameter; - private Encoding _encoding; + private const int TimeOut = 60; + private GlobalConfigInfoOSS? _parameter; #endregion Private Members #region Public Methods - public override void Create(T parameter, Encoding encoding) - { - _parameter = parameter as ParamsOSS; - _encoding = encoding; - } + public void Create(GlobalConfigInfoOSS parameter) + => _parameter = parameter; - public override async Task ExecuteTaskAsync() + public async Task ExecuteAsync() { - await Task.Run(() => + try { - try - { - //1.Download the JSON version configuration file. - var jsonPath = Path.Combine(_appPath, _parameter.VersionFileName); - if (!File.Exists(jsonPath)) throw new FileNotFoundException(jsonPath); + //1.Download the JSON version configuration file. + var jsonPath = Path.Combine(_appPath, _parameter.VersionFileName); + if (!File.Exists(jsonPath)) + throw new FileNotFoundException(jsonPath); - //2.Parse the JSON version configuration file content. - var versions = FileProvider.GetJson>(jsonPath); - if (versions == null) throw new NullReferenceException(nameof(versions)); + //2.Parse the JSON version configuration file content. + var versions = StorageManager.GetJson>(jsonPath, VersionOSSJsonContext.Default.ListVersionOSS); + 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); - //4.Launch the main application. - LaunchApp(); - } - catch (Exception ex) - { - EventManager.Instance.Dispatch>(this, new ExceptionEventArgs(ex)); - } - finally - { - Process.GetCurrentProcess().Kill(); - } - }); + versions = versions.OrderBy(v => v.PubTime).ToList(); + //3.Download version by version according to the version of the configuration file. + await DownloadVersions(versions); + Decompress(versions); + + //4.Launch the main application. + LaunchApp(); + } + catch (Exception ex) + { + throw new Exception(ex.Message + "\n" + ex.StackTrace); + } + finally + { + Process.GetCurrentProcess().Kill(); + } } #endregion Public Methods @@ -78,16 +68,30 @@ namespace GeneralUpdate.Core.Strategys /// Download all updated versions version by version. /// /// The collection of version information to be updated as described in the configuration file. - private void DownloadVersions(List versions) + private async Task 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(); + try + { + var manager = new DownloadManager(_appPath, Format.ZIP, TimeOut); + foreach (var versionInfo in versions) + { + var version = new VersionInfo + { + Name = versionInfo.PacketName, + Version = versionInfo.Version, + Url = versionInfo.Url, + Format = Format.ZIP, + Hash = versionInfo.Hash + }; + manager.Add(new DownloadTask(manager, version)); + } + + await manager.LaunchTasksAsync(); + } + catch (Exception e) + { + throw new Exception(e.Message + "\n" + e.StackTrace); + } } /// @@ -96,36 +100,18 @@ namespace GeneralUpdate.Core.Strategys /// private void LaunchApp() { - string appPath = Path.Combine(_appPath, _parameter.AppName + ".exe"); + var appPath = Path.Combine(_appPath, _parameter.AppName); if (!File.Exists(appPath)) throw new FileNotFoundException($"{nameof(appPath)} , The application is not accessible !"); Process.Start(appPath); } - private bool UnZip(List versions) + private void Decompress(List versions) { - try - { - bool isCompleted = true; - foreach (VersionInfo version in 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...")); - zipFactory.Completed += (sender, e) => - { - isCompleted = e.IsCompleted; - if (File.Exists(zipFilePath)) File.Delete(zipFilePath); - }; - zipFactory.CreateOperate(OperationType.GZip, version.Name, zipFilePath, _appPath, false, _encoding); - zipFactory.UnZip(); - } - return isCompleted; - } - catch (Exception exception) + var encoding = Encoding.GetEncoding(_parameter.Encoding); + foreach (var version in versions) { - EventManager.Instance.Dispatch>(this, new ExceptionEventArgs(exception)); - return false; + var zipFilePath = Path.Combine(_appPath, $"{version.PacketName}{Format.ZIP}"); + CompressProvider.Decompress(Format.ZIP,zipFilePath,_appPath, encoding); } } diff --git a/src/c#/GeneralUpdate.Core/Strategys/PlatformLinux/LinuxStrategy.cs b/src/c#/GeneralUpdate.Core/Strategys/PlatformLinux/LinuxStrategy.cs deleted file mode 100644 index 6ac653b0f72971f701e3670e75edafe32797f11a..0000000000000000000000000000000000000000 --- a/src/c#/GeneralUpdate.Core/Strategys/PlatformLinux/LinuxStrategy.cs +++ /dev/null @@ -1,24 +0,0 @@ -using GeneralUpdate.Core.Domain.Enum; - -namespace GeneralUpdate.Core.Strategys.PlatformLinux -{ - 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/PlatformWindows/WindowsStrategy.cs b/src/c#/GeneralUpdate.Core/Strategys/PlatformWindows/WindowsStrategy.cs deleted file mode 100644 index 34bfcbfee3d8204b4bda14bd0724ae63ba76538d..0000000000000000000000000000000000000000 --- a/src/c#/GeneralUpdate.Core/Strategys/PlatformWindows/WindowsStrategy.cs +++ /dev/null @@ -1,161 +0,0 @@ -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.Diagnostics; -using System.IO; -using System.Linq; -using System.Threading; -using System.Threading.Tasks; - -namespace GeneralUpdate.Core.Strategys.PlatformWindows -{ - /// - /// Update policy based on the Windows platform. - /// - public class WindowsStrategy : AbstractStrategy - { - #region Private Members - - protected Packet Packet { get; set; } - - #endregion Private Members - - #region Public Methods - - public override void Create(T parameter) => Packet = parameter as Packet; - - public override void Execute() - { - 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(); - - var pipelineBuilder = new PipelineBuilder(context) - .UseMiddleware().UseMiddleware() - .UseMiddlewareIf(Packet.DriveEnabled) - .UseMiddleware(); - await pipelineBuilder.Build(); - } - - if (!string.IsNullOrEmpty(Packet.UpdateLogUrl)) - Process.Start("explorer.exe", Packet.UpdateLogUrl); - } - - Clear(); - StartApp(Packet.AppName, Packet.AppType); - } - catch (Exception e) - { - EventManager.Instance.Dispatch>(this, new ExceptionEventArgs(e)); - } - }); - } - - public override bool StartApp(string appName, int appType) - { - try - { - var path = Path.Combine(Packet.InstallPath, appName); - switch (appType) - { - case AppType.ClientApp: - Environment.SetEnvironmentVariable("ProcessBase64", Packet.ProcessBase64, EnvironmentVariableTarget.User); - WaitForProcessToStart(path, 20); - break; - - case AppType.UpgradeApp: - WaitForProcessToStart(path, 20); - break; - } - 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; - } - } - - /// - /// 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) - { - using (var process = Process.Start(applicationPath)) - { - var startTime = DateTime.UtcNow; - var timeSpan = TimeSpan.FromSeconds(timeout); - while (DateTime.UtcNow - startTime < timeSpan) - { - Thread.Sleep(2 * 1000); - if (!process.HasExited) - { - callbackAction?.Invoke(); - return; - } - } - } - } - - #endregion Private Methods - } -} \ No newline at end of file diff --git a/src/c#/GeneralUpdate.Core/Strategys/WindowsStrategy.cs b/src/c#/GeneralUpdate.Core/Strategys/WindowsStrategy.cs new file mode 100644 index 0000000000000000000000000000000000000000..b82d94e2448f5854fcef263110daebcffeba8314 --- /dev/null +++ b/src/c#/GeneralUpdate.Core/Strategys/WindowsStrategy.cs @@ -0,0 +1,127 @@ +using System; +using System.Diagnostics; +using System.IO; +using System.Runtime.InteropServices; +using System.Threading.Tasks; +using GeneralUpdate.Common.FileBasic; +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.Object.Enum; +using GeneralUpdate.Common.Shared.Service; +using GeneralUpdate.Core.Pipeline; + +namespace GeneralUpdate.Core.Strategys +{ + /// + /// Update policy based on the Windows platform. + /// + public class WindowsStrategy : AbstractStrategy + { + private GlobalConfigInfo _configinfo = new(); + + public override void Create(GlobalConfigInfo parameter) => _configinfo = parameter; + + public override void Execute() + { + Task.Run(async () => + { + try + { + var status = ReportType.None; + var patchPath = StorageManager.GetTempDirectory(Patchs); + 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 + if (_configinfo.DriveEnabled == true) + { + context.Add("DriverOutPut", StorageManager.GetTempDirectory("DriverOutPut")); + context.Add("FieldMappings", _configinfo.FieldMappings); + } + + var pipelineBuilder = new PipelineBuilder(context) + .UseMiddleware() + .UseMiddleware() + .UseMiddleware() + .UseMiddlewareIf(_configinfo.DriveEnabled); + await pipelineBuilder.Build(); + status = ReportType.Success; + } + catch (Exception e) + { + status = ReportType.Failure; + 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(); + } + catch (Exception e) + { + Debug.WriteLine(e.Message); + EventManager.Instance.Dispatch(this, new ExceptionEventArgs(e, e.Message)); + } + }); + } + + public override void StartApp() + { + try + { + var appBowlPath = RuntimeInformation.IsOSPlatform(OSPlatform.Linux) ? null : CheckPath(_configinfo.InstallPath, _configinfo.Bowl); + var appPath = string.IsNullOrWhiteSpace(appBowlPath) ? CheckPath(_configinfo.InstallPath, _configinfo.MainAppName) : appBowlPath; + if(string.IsNullOrEmpty(appPath)) + throw new Exception($"Can't find the app {appPath}!"); + + Process.Start(appPath); + } + catch (Exception e) + { + Debug.WriteLine(e.Message); + EventManager.Instance.Dispatch(this, new ExceptionEventArgs(e, e.Message)); + } + finally + { + Process.GetCurrentProcess().Kill(); + } + } + + private string CheckPath(string path,string name) + { + if (string.IsNullOrWhiteSpace(path) || string.IsNullOrWhiteSpace(name)) return string.Empty; + var tempPath = Path.Combine(path, name); + return File.Exists(tempPath) ? tempPath : string.Empty; + } + } +} \ No newline at end of file diff --git a/src/c#/GeneralUpdate.Differential/GStream/BZip2Constants.cs b/src/c#/GeneralUpdate.Differential/Binary/BZip2Constants.cs similarity index 98% rename from src/c#/GeneralUpdate.Differential/GStream/BZip2Constants.cs rename to src/c#/GeneralUpdate.Differential/Binary/BZip2Constants.cs index ad77ca19c534e7d68a039856a45920940bb06cf3..4bbcd70e3c46c5e89a7781d49cc0eb27a410dc12 100644 --- a/src/c#/GeneralUpdate.Differential/GStream/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/GStream/BZip2InputStream.cs b/src/c#/GeneralUpdate.Differential/Binary/BZip2InputStream.cs similarity index 99% rename from src/c#/GeneralUpdate.Differential/GStream/BZip2InputStream.cs rename to src/c#/GeneralUpdate.Differential/Binary/BZip2InputStream.cs index 8ff23140582232aa1c2020e68f597f5972b90083..0f067f974cff5fec092ec8f12a4d7b4c235a7c86 100644 --- a/src/c#/GeneralUpdate.Differential/GStream/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/GStream/BZip2OutputStream.cs b/src/c#/GeneralUpdate.Differential/Binary/BZip2OutputStream.cs similarity index 99% rename from src/c#/GeneralUpdate.Differential/GStream/BZip2OutputStream.cs rename to src/c#/GeneralUpdate.Differential/Binary/BZip2OutputStream.cs index 67d5b8c568bf34210148c0e78f71eb96608b616d..3096f235e5c35138ae21e4221d19e953353a276e 100644 --- a/src/c#/GeneralUpdate.Differential/GStream/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/BinaryHandle.cs b/src/c#/GeneralUpdate.Differential/Binary/BinaryHandle.cs deleted file mode 100644 index 14e334fd7ba843b1c78c07260c6ed6e0d624c63f..0000000000000000000000000000000000000000 --- 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/BinaryHandler.cs b/src/c#/GeneralUpdate.Differential/Binary/BinaryHandler.cs index 6247a5bb21b7159284bc5c8e13fa3e7302fa95e6..daffb8c4fd97a48d7179fea625250f156e354f5b 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; @@ -12,9 +11,9 @@ namespace GeneralUpdate.Differential.Binary { #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 @@ -23,346 +22,376 @@ namespace GeneralUpdate.Differential.Binary /// /// 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.SetAttributes(_oldfilePath, FileAttributes.Normal); + 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) @@ -386,34 +415,34 @@ namespace GeneralUpdate.Differential.Binary 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) { @@ -421,35 +450,35 @@ namespace GeneralUpdate.Differential.Binary 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; @@ -459,42 +488,42 @@ namespace GeneralUpdate.Differential.Binary 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); } } @@ -509,49 +538,51 @@ namespace GeneralUpdate.Differential.Binary 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) @@ -585,7 +616,7 @@ namespace GeneralUpdate.Differential.Binary /// 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; @@ -601,19 +632,19 @@ namespace GeneralUpdate.Differential.Binary /// 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/Binary/IBinary.cs b/src/c#/GeneralUpdate.Differential/Binary/IBinary.cs deleted file mode 100644 index 2e99f55e423a6599e4d50b81a54b87918261cb37..0000000000000000000000000000000000000000 --- 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 82% rename from src/c#/GeneralUpdate.Differential/GStream/IChecksum.cs rename to src/c#/GeneralUpdate.Differential/Binary/IChecksum.cs index c4faac392e4bc7e6d4fc2d248d6a2f9fd874f068..1d77c707b21587bbfa6d3b293b85debe2331f9e1 100644 --- a/src/c#/GeneralUpdate.Differential/GStream/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/GStream/StrangeCRC.cs b/src/c#/GeneralUpdate.Differential/Binary/StrangeCRC.cs similarity index 99% rename from src/c#/GeneralUpdate.Differential/GStream/StrangeCRC.cs rename to src/c#/GeneralUpdate.Differential/Binary/StrangeCRC.cs index 43477e92846a141503ef00aa471764f51fdcfa0d..777da013d59b240e8ad791ac6df9b5848910bc7c 100644 --- a/src/c#/GeneralUpdate.Differential/GStream/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/ContentProvider/.gitkeep b/src/c#/GeneralUpdate.Differential/ContentProvider/.gitkeep deleted file mode 100644 index e69de29bb2d1d6434b8b29ae775ad8c2e48c5391..0000000000000000000000000000000000000000 diff --git a/src/c#/GeneralUpdate.Differential/CustomAwaiter/.gitkeep b/src/c#/GeneralUpdate.Differential/CustomAwaiter/.gitkeep deleted file mode 100644 index e69de29bb2d1d6434b8b29ae775ad8c2e48c5391..0000000000000000000000000000000000000000 diff --git a/src/c#/GeneralUpdate.Differential/DifferentialCore.cs b/src/c#/GeneralUpdate.Differential/DifferentialCore.cs index 9fd835d3eda6e7c784ccffe93f0ac910fe73a063..3ed4a3d7514bd6a1c27635712f1ab4efd5eebf61 100644 --- a/src/c#/GeneralUpdate.Differential/DifferentialCore.cs +++ b/src/c#/GeneralUpdate.Differential/DifferentialCore.cs @@ -1,40 +1,22 @@ -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.FileBasic; +using GeneralUpdate.Common.HashAlgorithms; +using GeneralUpdate.Common.Internal.JsonContext; namespace GeneralUpdate.Differential { public sealed class DifferentialCore { - #region Private Members - - private static readonly object _lockObj = new object(); - private static DifferentialCore _instance; - - /// - /// Differential file format . - /// + private static readonly object _lockObj = new (); + private static DifferentialCore? _instance; 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 { get @@ -43,87 +25,48 @@ namespace GeneralUpdate.Differential { lock (_lockObj) { - if (_instance == null) - { - _instance = new DifferentialCore(); - } + _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 fileProvider = new FileProvider(); - var nodes = await fileProvider.Compare(sourcePath, targetPath); - var hashAlgorithm = new Sha256HashAlgorithm(); - - //Binary differencing of like terms . - foreach (var file in nodes.Item3) + var fileManager = new StorageManager(); + 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); - var tempPath0 = string.Empty; - var tempDir = string.Empty; - if (string.IsNullOrEmpty(tempPath)) - { - tempDir = patchPath; - tempPath0 = Path.Combine(patchPath, $"{file.Name}{PATCH_FORMAT}"); - } - else + var tempDir = GetTempDirectory(file, targetPath, patchPath); + var oldFile = comparisonResult.LeftNodes.FirstOrDefault(i => i.Name.Equals(file.Name)); + var newFile = file; + + if (oldFile is not null + && File.Exists(oldFile.FullName) + && File.Exists(newFile.FullName) + && string.Equals(oldFile.RelativePath, newFile.RelativePath)) { - tempDir = Path.Combine(patchPath, tempPath); - if (!Directory.Exists(tempDir)) Directory.CreateDirectory(tempDir); - tempPath0 = Path.Combine(tempDir, $"{file.Name}{PATCH_FORMAT}"); - } - - var finOldFile = nodes.Item1.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 (hashAlgorithm.ComputeHash(oldFile) - .Equals(hashAlgorithm.ComputeHash(newFile), StringComparison.OrdinalIgnoreCase)) + if (!StorageManager.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 BinaryHandle().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 = (await fileProvider.Except(sourcePath, targetPath)).ToList(); - if (exceptFiles.Count != 0) + var exceptFiles = fileManager.Except(sourcePath, targetPath); + if (exceptFiles is not null + && exceptFiles.Any()) { var path = Path.Combine(patchPath, DELETE_FILES_NAME); - FileProvider.CreateJson(path, exceptFiles); + StorageManager.CreateJson(path, exceptFiles); } } catch (Exception ex) @@ -131,59 +74,30 @@ namespace GeneralUpdate.Differential throw new Exception($"Generate error : {ex.Message} !", ex.InnerException); } } - - /// - /// 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 patchFiles = FileProvider.GetAllfiles(patchPath); - var oldFiles = FileProvider.GetAllfiles(appPath); - - //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 hashAlgorithm = new Sha256HashAlgorithm(); - foreach (var file in deleteFiles) - { - var resultFile = oldFiles.FirstOrDefault(i => - string.Equals(hashAlgorithm.ComputeHash(i.FullName), file.Hash, StringComparison.OrdinalIgnoreCase)); - if (resultFile == null) - { - continue; - } - if (File.Exists(resultFile.FullName)) - { - File.Delete(resultFile.FullName); - } - } - } - + var skipDirectory = StorageManager.SkipDirectorys; + var patchFiles = StorageManager.GetAllFiles(patchPath, skipDirectory); + var oldFiles = StorageManager.GetAllFiles(appPath, skipDirectory); + //Refresh the collection after deleting the file. + HandleDeleteList(patchFiles, oldFiles); + oldFiles = StorageManager.GetAllFiles(appPath, skipDirectory); 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 && string.Equals(Path.GetExtension(findFile.FullName), 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,31 +106,48 @@ namespace GeneralUpdate.Differential } } - /// - /// 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) => FileProvider.SetBlacklist(blackFiles, blackFileFormats); - - #endregion Public Methods - #region Private Methods - /// - /// Apply patch file . - /// - /// Client application directory . - /// - /// - /// + private static string GetTempDirectory(FileNode file, string targetPath, string patchPath) + { + 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; + } + + private void HandleDeleteList(IEnumerable patchFiles, IEnumerable oldFiles) + { + var json = patchFiles.FirstOrDefault(i => i.Name.Equals(DELETE_FILES_NAME)); + if (json == null) + return; + + var deleteFiles = StorageManager.GetJson>(json.FullName, FileNodesJsonContext.Default.ListFileNode); + 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)) + continue; + + File.SetAttributes(file.FullName, FileAttributes.Normal); + File.Delete(file.FullName); + } + } + 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 BinaryHandle().Dirty(appPath, newPath, patchPath); + 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) { @@ -224,36 +155,42 @@ namespace GeneralUpdate.Differential } } - /// - /// Add new files . - /// - /// Client application directory . - /// Patch file path. - private Task DirtyUnknow(string appPath, string patchPath) + private async Task DirtyUnknow(string appPath, string patchPath) { - try + await Task.Run(() => { - var fileProvider = new FileProvider(); - var listExcept = fileProvider.Comparer(appPath, patchPath); - foreach (var file in listExcept) + try { - var extensionName = Path.GetExtension(file.FullName); - if (FileProvider.GetBlackFileFormats().Contains(extensionName)) continue; - var targetFileName = file.FullName.Replace(patchPath, "").TrimStart("\\".ToCharArray()); - var targetPath = Path.Combine(appPath, targetFileName); - var parentFolder = Directory.GetParent(targetPath); - if (!parentFolder.Exists) parentFolder.Create(); - File.Copy(file.FullName, Path.Combine(appPath, targetPath), true); + var fileManager = new StorageManager(); + var comparisonResult = fileManager.Compare(appPath, patchPath); + foreach (var file in comparisonResult.DifferentNodes) + { + var extensionName = Path.GetExtension(file.FullName); + 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?.Exists == false) + { + parentFolder.Create(); + } + + File.Copy(file.FullName, targetPath, true); + } + + if (Directory.Exists(patchPath)) + { + StorageManager.DeleteDirectory(patchPath); + } } - if (Directory.Exists(patchPath)) Directory.Delete(patchPath, true); - return Task.CompletedTask; - } - catch (Exception ex) - { - throw new Exception($" DirtyNew error : {ex.Message} !", ex.InnerException); - } + catch (Exception ex) + { + 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.Differential/Domain/Entity/.gitkeep b/src/c#/GeneralUpdate.Differential/Domain/Entity/.gitkeep deleted file mode 100644 index e69de29bb2d1d6434b8b29ae775ad8c2e48c5391..0000000000000000000000000000000000000000 diff --git a/src/c#/GeneralUpdate.Differential/Domain/PO/.gitkeep b/src/c#/GeneralUpdate.Differential/Domain/PO/.gitkeep deleted file mode 100644 index e69de29bb2d1d6434b8b29ae775ad8c2e48c5391..0000000000000000000000000000000000000000 diff --git a/src/c#/GeneralUpdate.Differential/Exceptions/.gitkeep b/src/c#/GeneralUpdate.Differential/Exceptions/.gitkeep deleted file mode 100644 index e69de29bb2d1d6434b8b29ae775ad8c2e48c5391..0000000000000000000000000000000000000000 diff --git a/src/c#/GeneralUpdate.Differential/Exceptions/CustomArgs/.gitkeep b/src/c#/GeneralUpdate.Differential/Exceptions/CustomArgs/.gitkeep deleted file mode 100644 index e69de29bb2d1d6434b8b29ae775ad8c2e48c5391..0000000000000000000000000000000000000000 diff --git a/src/c#/GeneralUpdate.Differential/Exceptions/CustomException/.gitkeep b/src/c#/GeneralUpdate.Differential/Exceptions/CustomException/.gitkeep deleted file mode 100644 index e69de29bb2d1d6434b8b29ae775ad8c2e48c5391..0000000000000000000000000000000000000000 diff --git a/src/c#/GeneralUpdate.Differential/GeneralUpdate.Differential.csproj b/src/c#/GeneralUpdate.Differential/GeneralUpdate.Differential.csproj index 0efc41da3d584b1cb78ba0bd870aba6c6d76d79e..ae772653e2f6a855617db0215b63ad7cb2c57968 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 @@ -16,69 +15,11 @@ - - - + - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - diff --git a/src/c#/GeneralUpdate.Maui.OSS/ContentProvider/.gitkeep b/src/c#/GeneralUpdate.Maui.OSS/ContentProvider/.gitkeep deleted file mode 100644 index e69de29bb2d1d6434b8b29ae775ad8c2e48c5391..0000000000000000000000000000000000000000 diff --git a/src/c#/GeneralUpdate.Maui.OSS/Domain/DO/.gitkeep b/src/c#/GeneralUpdate.Maui.OSS/Domain/DO/.gitkeep deleted file mode 100644 index e69de29bb2d1d6434b8b29ae775ad8c2e48c5391..0000000000000000000000000000000000000000 diff --git a/src/c#/GeneralUpdate.Maui.OSS/Domain/Entity/ParamsAndroid.cs b/src/c#/GeneralUpdate.Maui.OSS/Domain/Entity/ParamsAndroid.cs deleted file mode 100644 index 015a9ea74f4b4ff924d8c20a63e0fcb0c40eb3ae..0000000000000000000000000000000000000000 --- a/src/c#/GeneralUpdate.Maui.OSS/Domain/Entity/ParamsAndroid.cs +++ /dev/null @@ -1,24 +0,0 @@ -namespace GeneralUpdate.Maui.OSS.Domain.Entity -{ - public class ParamsAndroid : GeneralUpdate.Core.Domain.Entity.Entity - { - public string Url { get; set; } - - public string Apk { get; set; } - - public string CurrentVersion { get; set; } - - public string Authority { get; set; } - - public string VersionFileName { get; set; } - - public ParamsAndroid(string url, string apk, string authority, string currentVersion, string versionFileName) - { - Url = IsURL(url) ? url : throw new ArgumentNullException(nameof(url)); - Apk = apk ?? throw new ArgumentNullException(nameof(apk)); - CurrentVersion = IsVersion(currentVersion) ? currentVersion : throw new ArgumentNullException(nameof(currentVersion)); - Authority = authority ?? throw new ArgumentNullException(nameof(authority)); - VersionFileName = versionFileName ?? "versions.json"; - } - } -} \ No newline at end of file diff --git a/src/c#/GeneralUpdate.Maui.OSS/Domain/Enum/.gitkeep b/src/c#/GeneralUpdate.Maui.OSS/Domain/Enum/.gitkeep deleted file mode 100644 index e69de29bb2d1d6434b8b29ae775ad8c2e48c5391..0000000000000000000000000000000000000000 diff --git a/src/c#/GeneralUpdate.Maui.OSS/Domain/PO/Assembler/.gitkeep b/src/c#/GeneralUpdate.Maui.OSS/Domain/PO/Assembler/.gitkeep deleted file mode 100644 index e69de29bb2d1d6434b8b29ae775ad8c2e48c5391..0000000000000000000000000000000000000000 diff --git a/src/c#/GeneralUpdate.Maui.OSS/GeneralUpdate.Maui.OSS.csproj b/src/c#/GeneralUpdate.Maui.OSS/GeneralUpdate.Maui.OSS.csproj deleted file mode 100644 index ed412e67912256d73b7737f59929e2b30f9fab17..0000000000000000000000000000000000000000 --- a/src/c#/GeneralUpdate.Maui.OSS/GeneralUpdate.Maui.OSS.csproj +++ /dev/null @@ -1,116 +0,0 @@ - - - - net8.0;net8.0-maccatalyst;net8.0-android - - - true - true - enable - false - 11.0 - 14.0 - 21.0 - 10.0.17763.0 - 10.0.17763.0 - 6.5 - juster.zhu - Based on. Automatic updates for NET MAUI multiplatform. - Copyright © 2023 - https://github.com/JusterZhu/GeneralUpdate - GeneralUpdate128.png - - https://github.com/JusterZhu/GeneralUpdate - False - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - True - \ - - - - - - - - - - - - - diff --git a/src/c#/GeneralUpdate.Maui.OSS/GeneralUpdateOSS.cs b/src/c#/GeneralUpdate.Maui.OSS/GeneralUpdateOSS.cs deleted file mode 100644 index 30e16c3cd368b0beefa477221bb3535ea67187fd..0000000000000000000000000000000000000000 --- a/src/c#/GeneralUpdate.Maui.OSS/GeneralUpdateOSS.cs +++ /dev/null @@ -1,77 +0,0 @@ -using GeneralUpdate.Core.Events; -using GeneralUpdate.Core.Events.CommonArgs; -using GeneralUpdate.Core.Events.OSSArgs; -using GeneralUpdate.Maui.OSS.Domain.Entity; -using GeneralUpdate.Maui.OSS.Strategys; - -namespace GeneralUpdate.Maui.OSS -{ - /// - /// Update applications based on OSS services. - /// - public sealed class GeneralUpdateOSS - { - #region Constructors - - private GeneralUpdateOSS() - { } - - #endregion Constructors - - #region Public Methods - - /// - /// Starting an OSS update for android platform. - /// - /// The class that needs to be injected with the corresponding platform update policy or inherits the abstract update policy. - /// Remote server address. - /// apk name. - /// Version number of the current application. - /// Application authority. - /// Updated version configuration file name. - /// Method entry parameter is null exception. - public static async Task Start(ParamsAndroid parameter) where TStrategy : AbstractStrategy, new() - => await BaseStart(parameter); - - /// - /// Monitor download progress. - /// - /// - public static void AddListenerDownloadProcess(Action callbackAction) - => AddListener(callbackAction); - - /// - /// Listen for internal exception information. - /// - /// - public static void AddListenerException(Action callbackAction) - => AddListener(callbackAction); - - #endregion Public Methods - - #region Private Methods - - /// - /// The underlying update method. - /// - /// 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) where TStrategy : AbstractStrategy, new() where TParams : class - { - //Initializes and executes the policy. - var strategyFunc = new Func(() => new TStrategy()); - var strategy = strategyFunc(); - strategy.Create(parameter); - //Implement different update strategies depending on the platform. - await strategy.Execute(); - } - - private static void AddListener(Action callbackAction) where TArgs : EventArgs - { - if (callbackAction != null) EventManager.Instance.AddListener(callbackAction); - } - - #endregion Private Methods - } -} \ No newline at end of file diff --git a/src/c#/GeneralUpdate.Maui.OSS/Platforms/Android/AndroidManifest.xml b/src/c#/GeneralUpdate.Maui.OSS/Platforms/Android/AndroidManifest.xml deleted file mode 100644 index b0f2e2a3e219b720565d77ce3f3a94d3e6f21514..0000000000000000000000000000000000000000 --- a/src/c#/GeneralUpdate.Maui.OSS/Platforms/Android/AndroidManifest.xml +++ /dev/null @@ -1,18 +0,0 @@ - - - - - - - - - - - \ No newline at end of file diff --git a/src/c#/GeneralUpdate.Maui.OSS/Platforms/Android/Strategy.cs b/src/c#/GeneralUpdate.Maui.OSS/Platforms/Android/Strategy.cs deleted file mode 100644 index 0921f25de947f089c47793768a81da83c2d1210b..0000000000000000000000000000000000000000 --- a/src/c#/GeneralUpdate.Maui.OSS/Platforms/Android/Strategy.cs +++ /dev/null @@ -1,157 +0,0 @@ -using Android.Content; -using Android.OS; -using GeneralUpdate.Core.Domain.PO; -using GeneralUpdate.Core.Events; -using GeneralUpdate.Core.Events.CommonArgs; -using GeneralUpdate.Core.Events.OSSArgs; -using GeneralUpdate.Maui.OSS.Domain.Entity; -using GeneralUpdate.Maui.OSS.Strategys; -using Java.IO; -using Java.Math; -using Java.Security; -using Newtonsoft.Json; -using System.Text; - -namespace GeneralUpdate.Maui.OSS -{ - /// - /// All the code in this file is only included on Android. - /// - public class Strategy : AbstractStrategy - { - #region Private Members - - private readonly string _appPath = FileSystem.AppDataDirectory; - private ParamsAndroid _parameter; - - #endregion Private Members - - #region Public Methods - - public override void Create(T parameter) - { - _parameter = parameter as ParamsAndroid; - } - - public override async Task Execute() - { - try - { - //1.Download the JSON version configuration file. - var jsonUrl = $"{_parameter.Url}/{_parameter.VersionFileName}"; - var jsonPath = Path.Combine(_appPath, _parameter.VersionFileName); - await DownloadFileAsync(jsonUrl, jsonPath, (readLength, totalLength) - => EventManager.Instance.Dispatch>(this, new OSSDownloadArgs(readLength, totalLength))); - var jsonFile = new Java.IO.File(jsonPath); - if (!jsonFile.Exists()) throw new Java.IO.FileNotFoundException(jsonPath); - - //2.Parse the JSON version configuration file content. - byte[] jsonBytes = ReadFile(jsonFile); - string json = Encoding.Default.GetString(jsonBytes); - var versionConfig = JsonConvert.DeserializeObject(json); - if (versionConfig == null) throw new NullReferenceException(nameof(versionConfig)); - - //3.Compare with the latest version. - var currentVersion = new Version(_parameter.CurrentVersion); - var lastVersion = new Version(versionConfig.Version); - if (currentVersion.Equals(lastVersion)) return; - - //4.Download the apk file. - var file = Path.Combine(_appPath, _parameter.Apk); - await DownloadFileAsync(versionConfig.Url, file, (readLength, totalLength) - => EventManager.Instance.Dispatch>(this, new OSSDownloadArgs(readLength, totalLength))); - var apkFile = new Java.IO.File(file); - if (!apkFile.Exists()) throw new Java.IO.FileNotFoundException(jsonPath); - if (!versionConfig.Hash.Equals(GetFileMD5(apkFile, 64))) throw new Exception("The apk MD5 value does not match !"); - - //5.Launch the apk to install. - var intent = new Android.Content.Intent(Android.Content.Intent.ActionView); - if (Build.VERSION.SdkInt >= BuildVersionCodes.N) - { - intent.SetFlags(ActivityFlags.GrantReadUriPermission);//Give temporary read permissions. - var uri = FileProvider.GetUriForFile(Android.App.Application.Context, _parameter.Authority, apkFile); - intent.SetDataAndType(uri, "application/vnd.android.package-archive");//Sets the explicit MIME data type. - } - else - { - intent.SetDataAndType(Android.Net.Uri.FromFile(new Java.IO.File(file)), "application/vnd.android.package-archive"); - } - intent.AddFlags(ActivityFlags.NewTask); - Android.App.Application.Context.StartActivity(intent); - } - catch (Exception ex) - { - EventManager.Instance.Dispatch>(this, new ExceptionEventArgs(ex)); - } - } - - #endregion Public Methods - - #region Private Methods - - /// - /// Android OS read file bytes. - /// - /// - /// - private byte[] ReadFile(Java.IO.File file) - { - try - { - var fileLength = file.Length(); - var buffer = new byte[fileLength]; - var inputStream = new Java.IO.FileInputStream(file); - if (file.IsDirectory) return null; - inputStream.Read(buffer, 0, (int)fileLength); - inputStream.Close(); - return buffer; - } - catch (FileLoadException ex) - { - throw new FileLoadException(ex.Message, ex.InnerException); - } - catch (Exception ex) - { - throw new Exception(ex.Message, ex.InnerException); - } - } - - /// - /// Example Get the md5 value of the file. - /// - /// target file. - /// radix 16 32 64 - /// - /// - private string GetFileMD5(Java.IO.File file, int radix) - { - if (!file.IsFile) return null; - MessageDigest digest = null; - FileInputStream inputStream = null; - byte[] buffer = new byte[1024]; - int len; - try - { - digest = MessageDigest.GetInstance("MD5"); - inputStream = new FileInputStream(file); - while ((len = inputStream.Read(buffer, 0, 1024)) != -1) - { - digest.Update(buffer, 0, len); - } - inputStream.Close(); - } - catch (DigestException ex) - { - throw new DigestException(ex.Message, ex.Cause); - } - catch (Exception e) - { - throw new Exception(e.Message, e.InnerException); - } - var bigInt = new BigInteger(1, digest.Digest()); - return bigInt.ToString(radix); - } - - #endregion Private Methods - } -} \ No newline at end of file diff --git a/src/c#/GeneralUpdate.Maui.OSS/Platforms/Android/XML/provider_paths.xml b/src/c#/GeneralUpdate.Maui.OSS/Platforms/Android/XML/provider_paths.xml deleted file mode 100644 index cdf782cff23b43bba2ab4d418bef7a0b3d4de1fe..0000000000000000000000000000000000000000 --- a/src/c#/GeneralUpdate.Maui.OSS/Platforms/Android/XML/provider_paths.xml +++ /dev/null @@ -1,11 +0,0 @@ - - - - - - - - - - - \ No newline at end of file diff --git a/src/c#/GeneralUpdate.Maui.OSS/Platforms/MacCatalyst/Strategy.cs b/src/c#/GeneralUpdate.Maui.OSS/Platforms/MacCatalyst/Strategy.cs deleted file mode 100644 index 2a46dc8a585a3e551ccc1800e2ca1cc82a2c1fd3..0000000000000000000000000000000000000000 --- a/src/c#/GeneralUpdate.Maui.OSS/Platforms/MacCatalyst/Strategy.cs +++ /dev/null @@ -1,18 +0,0 @@ -using GeneralUpdate.Maui.OSS.Strategys; - -namespace GeneralUpdate.Maui.OSS -{ - // All the code in this file is only included on Mac Catalyst. - public class Strategy : AbstractStrategy - { - public override void Create(T parameter) - { - throw new NotImplementedException(); - } - - public override Task Execute() - { - throw new NotImplementedException(); - } - } -} \ No newline at end of file diff --git a/src/c#/GeneralUpdate.Maui.OSS/Platforms/Tizen/Strategy.cs b/src/c#/GeneralUpdate.Maui.OSS/Platforms/Tizen/Strategy.cs deleted file mode 100644 index dcaad8be67912db8d0d621a6a3df6dfd6f0716b9..0000000000000000000000000000000000000000 --- a/src/c#/GeneralUpdate.Maui.OSS/Platforms/Tizen/Strategy.cs +++ /dev/null @@ -1,9 +0,0 @@ -using System; - -namespace GeneralUpdate.Maui.OSS -{ - // All the code in this file is only included on Tizen. - public class Strategy - { - } -} \ No newline at end of file diff --git a/src/c#/GeneralUpdate.Maui.OSS/Platforms/Windows/Strategy.cs b/src/c#/GeneralUpdate.Maui.OSS/Platforms/Windows/Strategy.cs deleted file mode 100644 index c6759160339ddcaacb47aaea5dabcef6382e991d..0000000000000000000000000000000000000000 --- a/src/c#/GeneralUpdate.Maui.OSS/Platforms/Windows/Strategy.cs +++ /dev/null @@ -1,73 +0,0 @@ -using GeneralUpdate.Core.Domain.DO; -using GeneralUpdate.Core.Events; -using GeneralUpdate.Core.Events.OSSArgs; -using GeneralUpdate.Maui.OSS.Domain.Entity; -using GeneralUpdate.Maui.OSS.Strategys; -using GeneralUpdate.Zip; -using Newtonsoft.Json; -using System.Diagnostics; -using System.Text; - -namespace GeneralUpdate.Maui.OSS -{ - /// - /// All the code in this file is only included on Windows. - /// - public class Strategy : AbstractStrategy - { - private readonly string _appPath = AppDomain.CurrentDomain.BaseDirectory; - private ParamsAndroid _parameter; - - public override void Create(T parameter) - { - _parameter = parameter as ParamsAndroid; - } - - public override async Task Execute() - { - try - { - //1.Download the JSON version configuration file. - var jsonUrl = $"{_parameter.Url}/{_parameter.VersionFileName}"; - var jsonPath = Path.Combine(_appPath, _parameter.VersionFileName); - await DownloadFileAsync(jsonUrl, jsonPath, (e, s) => EventManager.Instance.Dispatch>(this, new OSSDownloadArgs(e, s))); - if (!File.Exists(jsonPath)) throw new FileNotFoundException(jsonPath); - - //2.Parse the JSON version configuration file content. - byte[] jsonBytes = File.ReadAllBytes(jsonPath); - string json = Encoding.Default.GetString(jsonBytes); - var versions = JsonConvert.DeserializeObject>(json); - if (versions == null) throw new NullReferenceException(nameof(versions)); - - //3.Compare with the latest version. - versions = versions.OrderBy(v => v.PubTime).ToList(); - var currentVersion = new Version(_parameter.CurrentVersion); - var lastVersion = new Version(versions[0].Version); - if (currentVersion.Equals(lastVersion)) return; - - foreach (var version in versions) - { - string filePath = _appPath; - //await DownloadFileAsync(version.Url, _appPath, (e, s) => EventManager.Instance.Dispatch(this, new OSSDownloadArgs(e, s))); - var factory = new GeneralZipFactory(); - factory.CreateOperate(Zip.Factory.OperationType.GZip, version.Name, _appPath, _appPath, true, Encoding.UTF8); - //factory.Completed += (s, e) => EventManager.Instance.Dispatch(this, e); - //factory.UnZipProgress += (s, e) => EventManager.Instance.Dispatch(this, e); - factory.UnZip(); - } - - //5.Launch the main application. - string appPath = Path.Combine(_appPath, _parameter.Apk); - Process.Start(appPath); - } - catch (Exception ex) - { - throw new Exception(ex.StackTrace); - } - finally - { - Process.GetCurrentProcess().Kill(); - } - } - } -} \ No newline at end of file diff --git a/src/c#/GeneralUpdate.Maui.OSS/Platforms/iOS/Strategy.cs b/src/c#/GeneralUpdate.Maui.OSS/Platforms/iOS/Strategy.cs deleted file mode 100644 index e9b6fc402d35fdbbc6c07c7b9fbf3c3b18ef2866..0000000000000000000000000000000000000000 --- a/src/c#/GeneralUpdate.Maui.OSS/Platforms/iOS/Strategy.cs +++ /dev/null @@ -1,18 +0,0 @@ -using GeneralUpdate.Maui.OSS.Strategys; - -namespace GeneralUpdate.Maui.OSS -{ - // All the code in this file is only included on iOS. - public class Strategy : AbstractStrategy - { - public override void Create(T parameter) - { - throw new NotImplementedException(); - } - - public override Task Execute() - { - throw new NotImplementedException(); - } - } -} \ No newline at end of file diff --git a/src/c#/GeneralUpdate.Maui.OSS/Strategys/AbstractStrategy.cs b/src/c#/GeneralUpdate.Maui.OSS/Strategys/AbstractStrategy.cs deleted file mode 100644 index 6a4c60364ada49679107840a62432a064674feca..0000000000000000000000000000000000000000 --- a/src/c#/GeneralUpdate.Maui.OSS/Strategys/AbstractStrategy.cs +++ /dev/null @@ -1,51 +0,0 @@ -namespace GeneralUpdate.Maui.OSS.Strategys -{ - /// - /// OSS updates abstract Strategy. - /// - public abstract class AbstractStrategy : IStrategy - { - /// - /// download file. - /// - /// remote service address - /// download file path. - /// progress report. - /// progress report. - /// - public async Task DownloadFileAsync(string url, string filePath, Action action) - { - var request = new HttpRequestMessage(new HttpMethod("GET"), url); - var client = new HttpClient(); - var response = await client.SendAsync(request, HttpCompletionOption.ResponseHeadersRead); - var totalLength = response.Content.Headers.ContentLength; - var stream = await response.Content.ReadAsStreamAsync(); - await using var fileStream = new FileStream(filePath, FileMode.Create); - await using (stream) - { - var buffer = new byte[10240]; - var readLength = 0; - int length; - while ((length = await stream.ReadAsync(buffer, 0, buffer.Length)) != 0) - { - readLength += length; - if (action != null) action(readLength, totalLength!.Value); - fileStream.Write(buffer, 0, length); - } - } - } - - /// - /// Example Initialize the creation strategy. - /// - /// - /// - public abstract void Create(T parameter) where T : class; - - /// - /// Execute the injected strategy. - /// - /// - public abstract Task Execute(); - } -} \ No newline at end of file diff --git a/src/c#/GeneralUpdate.Maui.OSS/Strategys/IStrategy.cs b/src/c#/GeneralUpdate.Maui.OSS/Strategys/IStrategy.cs deleted file mode 100644 index b72c7077b029d8afe9470467326fee79ed2a29b6..0000000000000000000000000000000000000000 --- a/src/c#/GeneralUpdate.Maui.OSS/Strategys/IStrategy.cs +++ /dev/null @@ -1,16 +0,0 @@ -namespace GeneralUpdate.Maui.OSS.Strategys -{ - public interface IStrategy - { - /// - /// Execution strategy. - /// - Task Execute(); - - /// - /// Create a policy. - /// - /// Abstraction for updating package information. - void Create(T parameter) where T : class; - } -} \ No newline at end of file diff --git a/src/c#/GeneralUpdate.OSSClient/App.xaml b/src/c#/GeneralUpdate.OSSClient/App.xaml deleted file mode 100644 index 428fa0091a2a5a7f5f429a0db8f5ee8cb48602c7..0000000000000000000000000000000000000000 --- a/src/c#/GeneralUpdate.OSSClient/App.xaml +++ /dev/null @@ -1,14 +0,0 @@ - - - - - - - - - - - \ No newline at end of file diff --git a/src/c#/GeneralUpdate.OSSClient/App.xaml.cs b/src/c#/GeneralUpdate.OSSClient/App.xaml.cs deleted file mode 100644 index 0548c9a6d02ccd9a83deffd9b57105cad6ca2ece..0000000000000000000000000000000000000000 --- a/src/c#/GeneralUpdate.OSSClient/App.xaml.cs +++ /dev/null @@ -1,12 +0,0 @@ -namespace GeneralUpdate.OSSClient -{ - public partial class App : Application - { - public App() - { - InitializeComponent(); - - MainPage = new AppShell(); - } - } -} \ No newline at end of file diff --git a/src/c#/GeneralUpdate.OSSClient/AppShell.xaml b/src/c#/GeneralUpdate.OSSClient/AppShell.xaml deleted file mode 100644 index 05b5b0518e0253a07995dc9118a7900e8d2673e1..0000000000000000000000000000000000000000 --- a/src/c#/GeneralUpdate.OSSClient/AppShell.xaml +++ /dev/null @@ -1,13 +0,0 @@ - - - - - \ No newline at end of file diff --git a/src/c#/GeneralUpdate.OSSClient/AppShell.xaml.cs b/src/c#/GeneralUpdate.OSSClient/AppShell.xaml.cs deleted file mode 100644 index 9f7acba40896cfe60d9418ff643345725f7a74ea..0000000000000000000000000000000000000000 --- a/src/c#/GeneralUpdate.OSSClient/AppShell.xaml.cs +++ /dev/null @@ -1,10 +0,0 @@ -namespace GeneralUpdate.OSSClient -{ - public partial class AppShell : Shell - { - public AppShell() - { - InitializeComponent(); - } - } -} \ No newline at end of file diff --git a/src/c#/GeneralUpdate.OSSClient/GeneralUpdate.OSSClient.csproj b/src/c#/GeneralUpdate.OSSClient/GeneralUpdate.OSSClient.csproj deleted file mode 100644 index b9b889714dfe50061227b3a8e85f4879b2c279c7..0000000000000000000000000000000000000000 --- a/src/c#/GeneralUpdate.OSSClient/GeneralUpdate.OSSClient.csproj +++ /dev/null @@ -1,103 +0,0 @@ - - - - net8.0-android - Exe - GeneralUpdate.OSSClient - true - true - enable - - - GeneralUpdate.OSSClient - - - com.companyname.generalupdate.ossclient - c30691e5-6d99-469c-896f-b3be66041119 - - - 1.0 - 1 - - 11.0 - 13.1 - 33.0 - 10.0.17763.0 - 10.0.17763.0 - 6.5 - - - - 1.0.0 - - - - 1.0.0 - - - - 1.0.0 - - - - 1.0.0 - - - - 1.0.0 - - - - 1.0.0 - - - - 1.0.0 - - - - 1.0.0 - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - Never - - - Never - - - - - - PreserveNewest - - - - diff --git a/src/c#/GeneralUpdate.OSSClient/MainPage.xaml b/src/c#/GeneralUpdate.OSSClient/MainPage.xaml deleted file mode 100644 index 636a0e891d0b3632d4077438325c57c1826191b9..0000000000000000000000000000000000000000 --- a/src/c#/GeneralUpdate.OSSClient/MainPage.xaml +++ /dev/null @@ -1,39 +0,0 @@ - - - - - - - - -