From 90e07b6f2f33a703c6c9754ea58d32f669b17881 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E5=87=AF=20=E7=8E=8B?= <648428741@qq.com> Date: Tue, 14 Jun 2022 23:02:55 +0800 Subject: [PATCH] =?UTF-8?q?=E5=A2=9E=E5=8A=A0=E7=A8=8B=E5=BA=8F=E6=9B=B4?= =?UTF-8?q?=E6=96=B0=E5=8A=9F=E8=83=BD?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../.idea/workspace.xml | 37 +- .vs/WinformGeneralDeveloperFrame/v17/.suo | Bin 896512 -> 896512 bytes .../v17/fileList.bin | Bin 446055 -> 446273 bytes Update.Core/Downloader.cs | 385 ++++++++++ Update.Core/FileCopyClass.cs | 411 +++++++++++ Update.Core/GZip.cs | 655 ++++++++++++++++++ Update.Core/Model.cs | 184 +++++ Update.Core/Properties/AssemblyInfo.cs | 36 + Update.Core/Update.Core.csproj | 59 ++ Update.Core/UpdateClass.cs | 386 +++++++++++ Update.Core/ZipUtility.cs | 153 ++++ Update.Core/packages.config | 4 + Update/App.config | 6 + Update/Class1.cs | 12 - Update/Form1.Designer.cs | 118 ++++ Update/Form1.cs | 336 +++++++++ Update/Form1.resx | 120 ++++ Update/Program.cs | 22 + Update/Properties/AssemblyInfo.cs | 4 +- Update/Properties/Resources.Designer.cs | 71 ++ Update/Properties/Resources.resx | 117 ++++ Update/Properties/Settings.Designer.cs | 30 + Update/Properties/Settings.settings | 7 + Update/Update.csproj | 88 ++- Update/UpdateLog.cs | 50 ++ Update/updateconfiguration.config | 6 + .../GetDataTableUtils.cs | 4 +- WinformGeneralDeveloperFrame.Start/App.config | 2 +- .../Start.csproj | 8 + WinformGeneralDeveloperFrame.sln | 18 +- ...GeneralDeveloperFrame.sln.DotSettings.user | 2 + WinformGeneralDeveloperFrame/DB.cs | 3 +- WinformGeneralDeveloperFrame/DB/MESDB.cs | 4 +- .../DevexpressForm.csproj | 8 + WinformGeneralDeveloperFrame/LoginView.cs | 33 +- 35 files changed, 3331 insertions(+), 48 deletions(-) create mode 100644 Update.Core/Downloader.cs create mode 100644 Update.Core/FileCopyClass.cs create mode 100644 Update.Core/GZip.cs create mode 100644 Update.Core/Model.cs create mode 100644 Update.Core/Properties/AssemblyInfo.cs create mode 100644 Update.Core/Update.Core.csproj create mode 100644 Update.Core/UpdateClass.cs create mode 100644 Update.Core/ZipUtility.cs create mode 100644 Update.Core/packages.config create mode 100644 Update/App.config delete mode 100644 Update/Class1.cs create mode 100644 Update/Form1.Designer.cs create mode 100644 Update/Form1.cs create mode 100644 Update/Form1.resx create mode 100644 Update/Program.cs create mode 100644 Update/Properties/Resources.Designer.cs create mode 100644 Update/Properties/Resources.resx create mode 100644 Update/Properties/Settings.Designer.cs create mode 100644 Update/Properties/Settings.settings create mode 100644 Update/UpdateLog.cs create mode 100644 Update/updateconfiguration.config create mode 100644 WinformGeneralDeveloperFrame.sln.DotSettings.user diff --git a/.idea/.idea.WinformGeneralDeveloperFrame/.idea/workspace.xml b/.idea/.idea.WinformGeneralDeveloperFrame/.idea/workspace.xml index a585f86..74b880e 100644 --- a/.idea/.idea.WinformGeneralDeveloperFrame/.idea/workspace.xml +++ b/.idea/.idea.WinformGeneralDeveloperFrame/.idea/workspace.xml @@ -3,9 +3,23 @@ WinformGeneralDeveloperFrame.Start/Start.csproj WinformGeneralPrimordialForm/PrimordialForm.csproj + Update/Update.csproj - + + + + + + + + + + + + + + @@ -77,6 +109,9 @@ 1637673594984 + + + diff --git a/.vs/WinformGeneralDeveloperFrame/v17/.suo b/.vs/WinformGeneralDeveloperFrame/v17/.suo index ee77f42ee393ff0faabeb88f7dba4bf8c4aa7137..b928a27f0144f9f06e17e2e590a0e047e6d28033 100644 GIT binary patch delta 45107 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 delta 44449 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 UlNTz=PM5A{6WTtto^6dk0I3QT=>Px# diff --git a/Update.Core/Downloader.cs b/Update.Core/Downloader.cs new file mode 100644 index 0000000..205a63a --- /dev/null +++ b/Update.Core/Downloader.cs @@ -0,0 +1,385 @@ +using System; +using System.Collections.Generic; +using System.Text; +using System.Net; +using System.ComponentModel; +using System.Xml.Serialization; +using System.IO; +using System.Threading; +using System.Collections.Specialized; + +namespace Updater.Core +{ + /// + /// 下载错误事件数据 + /// + public class DownloadErrorEventArgs : EventArgs + { + public Exception Error { get; set; } + + public Manifest Manifest { get; set; } + } + + /// + /// 下载进度事件数据 + /// + public class DownloadProgressEventArgs : ProgressChangedEventArgs + { + public DownloadProgressEventArgs(int progressPercentage, object userState) + : base(progressPercentage, userState) + { } + + /// + /// 当前下载的文件名 + /// + public string FileName { get; set; } + + /// + /// 获取收到的字节数。 + /// + public long BytesReceived { get; set; } + /// + /// 获取 System.Net.WebClient 数据下载操作中的字节总数。 + /// + public long TotalBytesToReceive { get; set; } + } + + /// + /// 下载完成事件数据 + /// + public class DownloadCompleteEventArgs : AsyncCompletedEventArgs + { + public DownloadCompleteEventArgs(Exception error, bool cancelled, object userState) + : base(error, cancelled, userState) + { + } + + public Manifest Manifest { get; set; } + } + + /// + /// 服务器文件下载类 + /// + public class DownloadClass : Component + { + #region 变量定义 + private WebClient webClient = new WebClient(); + private Manifest manifest; + private int fileCount = 0; + private bool cancel = false; + private string tempPath = Path.Combine(AppDomain.CurrentDomain.BaseDirectory, "temp"); + + private HybridDictionary userStateToLifetime = new HybridDictionary(); + private object defaultTaskId = new object(); + private delegate void WorkerEventHandler(AsyncOperation asyncOp); + private System.ComponentModel.Container components = null; + private SendOrPostCallback onProgressReportDelegate; + private SendOrPostCallback onCompletedDelegate; + private AsyncOperation current; + #endregion + + #region 事件 + /// + /// 下载进度 + /// + public event EventHandler DownloadProgressChanged; + + /// + /// 下载完成事件 + /// + public event EventHandler DownloadCompleted; + + /// + /// 下载错误触发的事件 + /// + public event EventHandler DownloadError; + #endregion + + #region 构造及析构 + public DownloadClass(IContainer container) + { + container.Add(this); + InitializeComponent(); + InitializeDelegates(); + } + + public DownloadClass() + { + InitializeComponent(); + InitializeDelegates(); + } + + /// + /// 初始化代理 + /// + protected virtual void InitializeDelegates() + { + onProgressReportDelegate = new SendOrPostCallback(ReportProgress); + onCompletedDelegate = new SendOrPostCallback(DoDownloadCompleted); + } + + private void InitializeComponent() + { + components = new System.ComponentModel.Container(); + } + + protected override void Dispose(bool disposing) + { + if (disposing) + { + if (components != null) + { + components.Dispose(); + } + } + base.Dispose(disposing); + } + #endregion + + /// + /// 触发下载进度事件 + /// + /// + protected virtual void OnDownloadProgressChanged(DownloadProgressEventArgs e) + { + if (DownloadProgressChanged != null) + { + DownloadProgressChanged(this, e); + } + } + + /// + /// 触发下载完成事件 + /// + /// + protected virtual void OnDownloadCompleted(DownloadCompleteEventArgs e) + { + if (DownloadCompleted != null) + { + DownloadCompleted(this, e); + } + } + + /// + /// 触发下载错误事件 + /// + /// + protected virtual void OnDownloadError(DownloadErrorEventArgs e) + { + if (DownloadError != null) + { + DownloadError(this, e); + } + } + + /// + /// 下载文字保存的临时目录 + /// + public string TempPath + { + get + { + return tempPath; + } + set + { + tempPath = value; + } + } + + /// + /// 同步下载 + /// + /// 文件下载清单 + public void Download(Manifest manifest) + { + Init(manifest); + foreach (var file in manifest.ManifestFiles.Files) + { + string serverFileName = Path.Combine(manifest.ManifestFiles.BaseUrl, file.Source); + string clientFileName = Path.Combine(tempPath, file.Source); + Uri uri = new Uri(serverFileName); + if (!Directory.Exists(Path.GetDirectoryName(clientFileName))) + { + Directory.CreateDirectory(Path.GetDirectoryName(clientFileName)); + } + webClient.DownloadFile(uri, clientFileName); + } + } + + /// + /// 异步下载 + /// + /// 文件下载清单 + public void DownloadAsync(Manifest manifest) + { + Init(manifest); + DownloadAsync(manifest, defaultTaskId); + } + + /// + /// 异步下载并指定任务Id + /// + /// 文件下载清单 + /// 任务Id + public void DownloadAsync(Manifest manifest, object taskId) + { + AsyncOperation asyncOp = AsyncOperationManager.CreateOperation(taskId); + lock (userStateToLifetime.SyncRoot) + { + if (userStateToLifetime.Contains(taskId)) + { + throw new ArgumentException("参数taskId必须是唯一的", "taskId"); + } + userStateToLifetime[taskId] = asyncOp; + } + WorkerEventHandler workerDelegate = new WorkerEventHandler(DownloadWorker); + workerDelegate.BeginInvoke(asyncOp, null, null); + } + + private void Init(Manifest manifest) + { + this.manifest = manifest; + webClient.BaseAddress = manifest.ManifestFiles.BaseUrl; + webClient.Credentials = CredentialCache.DefaultCredentials; + webClient.Encoding = Encoding.UTF8; + } + + /// + /// 异步下载方法 + /// + /// + private void DownloadWorker(AsyncOperation asyncOp) + { + current = asyncOp; + if (!TaskCanceled(asyncOp.UserSuppliedState)) + { + try + { + webClient.DownloadFileCompleted += new AsyncCompletedEventHandler(webClient_DownloadFileCompleted); + webClient.DownloadProgressChanged += new DownloadProgressChangedEventHandler(webClient_DownloadProgressChanged); + foreach (var file in manifest.ManifestFiles.Files) + { + string serverFileName = Path.Combine(manifest.ManifestFiles.BaseUrl, file.Source); + string clientFileName = Path.Combine(tempPath, file.Source); + Uri uri = new Uri(serverFileName); + if (!Directory.Exists(Path.GetDirectoryName(clientFileName))) + { + Directory.CreateDirectory(Path.GetDirectoryName(clientFileName)); + } + while (webClient.IsBusy) + { + //阻塞异步下载 + } + if (!cancel) + { + webClient.DownloadFileAsync(uri, clientFileName, file.Source); + } + } + } + catch (Exception ex) + { + DownloadErrorEventArgs e = new DownloadErrorEventArgs(); + e.Error = ex; + e.Manifest = manifest; + OnDownloadError(e); + } + } + } + + /// + /// 异步完成方法 + /// + /// 异常数据 + /// 是否取消 + /// + private void CompletionMethod(Exception exception, bool canceled, AsyncOperation asyncOp) + { + if (!canceled) + { + lock (userStateToLifetime.SyncRoot) + { + userStateToLifetime.Remove(asyncOp.UserSuppliedState); + } + } + + DownloadCompleteEventArgs e = new DownloadCompleteEventArgs(exception, canceled, asyncOp.UserSuppliedState); + e.Manifest = manifest; + asyncOp.PostOperationCompleted(onCompletedDelegate, e); + current = null; + } + + /// + /// 异步下载进度事件(仅对于单个文件) + /// + void webClient_DownloadProgressChanged(object sender, DownloadProgressChangedEventArgs e) + { + DownloadProgressEventArgs args = new DownloadProgressEventArgs(e.ProgressPercentage, e.UserState); + args.BytesReceived = e.BytesReceived; + args.FileName = e.UserState.ToString(); + args.TotalBytesToReceive = e.TotalBytesToReceive; + if (current != null) + { + current.Post(onProgressReportDelegate, args); + } + } + + /// + /// 异步下载完成事件(仅对于单个文件) + /// + /// + /// + void webClient_DownloadFileCompleted(object sender, AsyncCompletedEventArgs e) + { + fileCount++; + if (fileCount == manifest.ManifestFiles.Files.Length) + { + this.CompletionMethod(e.Error, TaskCanceled(current.UserSuppliedState), current); + } + } + + /// + /// 取消异步下载 + /// + public void CancelAsync() + { + CancelAsync(defaultTaskId); + } + + /// + /// 取消异步下载 + /// + public void CancelAsync(object taskId) + { + webClient.CancelAsync(); + cancel = true; + current = null; + AsyncOperation asyncOp = userStateToLifetime[taskId] as AsyncOperation; + if (asyncOp != null) + { + lock (userStateToLifetime.SyncRoot) + { + userStateToLifetime.Remove(taskId); + } + } + } + + private bool TaskCanceled(object taskId) + { + return cancel || (userStateToLifetime[taskId] == null); + } + + private void DoDownloadCompleted(object operationState) + { + DownloadCompleteEventArgs e = operationState as DownloadCompleteEventArgs; + OnDownloadCompleted(e); + } + + private void ReportProgress(object state) + { + DownloadProgressEventArgs e = state as DownloadProgressEventArgs; + OnDownloadProgressChanged(e); + } + } + +} diff --git a/Update.Core/FileCopyClass.cs b/Update.Core/FileCopyClass.cs new file mode 100644 index 0000000..ef209e1 --- /dev/null +++ b/Update.Core/FileCopyClass.cs @@ -0,0 +1,411 @@ +using System; +using System.Collections.Generic; +using System.Text; +using System.ComponentModel; +using System.Threading; +using System.Collections.Specialized; +using System.Collections; +using System.IO; + +namespace Updater.Core +{ + /// + /// 文件复制进度报告事件参数 + /// + public class FileCopyProgressChangedEventArgs : ProgressChangedEventArgs + { + public FileCopyProgressChangedEventArgs(int progressPercentage, object userState) + : base(progressPercentage, userState) + { + } + + /// + /// 当前复制的字节数 + /// + public double BytesToCopy { get; set; } + + /// + /// 当前复制操作中的字节总数 + /// + public double TotalBytesToCopy { get; set; } + + /// + /// 当前复制的源文件名 + /// + public string SourceFileName { get; set; } + + /// + /// 当前复制的目标文件名 + /// + public string TargetFileName { get; set; } + + public Manifest Manifest { get; set; } + } + + /// + /// 文件复制完成事件参数 + /// + public class FileCopyCompletedEventArgs : AsyncCompletedEventArgs + { + public FileCopyCompletedEventArgs(Exception error, bool cancelled, object userState) + : base(error, cancelled, userState) + { + } + + public Manifest Manifest { get; set; } + } + + /// + /// 文件复制错误事件参数 + /// + public class FileCopyErrorEventArgs : EventArgs + { + public Exception Error { get; set; } + + public Manifest Manifest { get; set; } + } + + /// + /// 文件复制组件类 + /// + public class FileCopyClass : Component + { + #region 变量定义 + private object defaultTaskId = new object(); + private int writeFileLength = 1024 * 64; + + private delegate void WorkerEventHandler(Manifest manifest, string sourcePath, AsyncOperation asyncOp); + + private SendOrPostCallback onProgressReportDelegate; + private SendOrPostCallback onCompletedDelegate; + + private HybridDictionary userStateToLifetime = new HybridDictionary(); + + private System.ComponentModel.Container components = null; + #endregion + + #region 事件 + + /// + /// 文件复制进度事件 + /// + public event EventHandler FileCopyProgressChanged; + + /// + /// 文件复制完成事件 + /// + public event EventHandler FileCopyCompleted; + + /// + /// 文件复制错误事件 + /// + public event EventHandler FileCopyError; + + #endregion + + #region 构造及析构 + + public FileCopyClass(IContainer container) + { + container.Add(this); + InitializeComponent(); + InitializeDelegates(); + } + + public FileCopyClass() + { + InitializeComponent(); + InitializeDelegates(); + } + + protected virtual void InitializeDelegates() + { + onProgressReportDelegate = new SendOrPostCallback(ReportProgress); + onCompletedDelegate = new SendOrPostCallback(CopyCompleted); + } + + private void InitializeComponent() + { + components = new System.ComponentModel.Container(); + } + + protected override void Dispose(bool disposing) + { + if (disposing) + { + if (components != null) + { + components.Dispose(); + } + } + base.Dispose(disposing); + } + + #endregion + + #region 实现 + + public int WriteFileLength + { + set + { + writeFileLength = value; + } + } + + public void Copy(Manifest manifest, string sourcePath) + { + string[] sourceFiles = null; + string[] targetFiles = null; + GetFiles(manifest, sourcePath, out sourceFiles, out targetFiles); + for (int i = 0; i < sourceFiles.Length; i++) + { + if (!Directory.Exists(Path.GetDirectoryName(targetFiles[i]))) + { + Directory.CreateDirectory(Path.GetDirectoryName(targetFiles[i])); + } + File.Copy(sourceFiles[i], targetFiles[i], true); + } + } + + public void CopyAsync(Manifest manifest, string sourcePath) + { + CopyAsync(manifest, sourcePath, defaultTaskId); + } + + public void CopyAsync(Manifest manifest, string sourcePath, object taskId) + { + AsyncOperation asyncOp = AsyncOperationManager.CreateOperation(taskId); + lock (userStateToLifetime.SyncRoot) + { + if (userStateToLifetime.Contains(taskId)) + { + throw new ArgumentException("参数taskId必须是唯一的", "taskId"); + } + userStateToLifetime[taskId] = asyncOp; + } + + WorkerEventHandler workerDelegate = new WorkerEventHandler(FileCopyWorker); + workerDelegate.BeginInvoke(manifest, sourcePath, asyncOp, null, null); + } + + private bool TaskCanceled(object taskId) + { + return (userStateToLifetime[taskId] == null); + } + + public void CancelAsync() + { + CancelAsync(defaultTaskId); + } + + public void CancelAsync(object taskId) + { + AsyncOperation asyncOp = userStateToLifetime[taskId] as AsyncOperation; + if (asyncOp != null) + { + lock (userStateToLifetime.SyncRoot) + { + userStateToLifetime.Remove(taskId); + } + } + } + + private void FileCopyWorker(Manifest manifest, string sourcePath, AsyncOperation asyncOp) + { + Exception exception = null; + FileCopyProgressChangedEventArgs e = null; + Stream rStream = null; + Stream wStream = null; + double writeBytes = 0; + string[] sourceFiles = null; + string[] targetFiles = null; + GetFiles(manifest, sourcePath, out sourceFiles, out targetFiles); + + if (!TaskCanceled(asyncOp.UserSuppliedState)) + { + try + { + double totalBytes = GetFileLength(sourceFiles); + byte[] buffer = new byte[writeFileLength]; + int len = 0; + int offset = 0; + for (int i = 0; i < sourceFiles.Length; i++) + { + try + { + if (!Directory.Exists(Path.GetDirectoryName(targetFiles[i]))) + { + Directory.CreateDirectory(Path.GetDirectoryName(targetFiles[i])); + } + + rStream = new FileStream(sourceFiles[i], FileMode.Open, FileAccess.Read, FileShare.None); + wStream = new FileStream(targetFiles[i], FileMode.Create, FileAccess.Write, FileShare.None); + while ((len = rStream.Read(buffer, offset, writeFileLength)) > 0) + { + wStream.Write(buffer, offset, len); + writeBytes += len; + e = new FileCopyProgressChangedEventArgs((int)(writeBytes / totalBytes * 100), asyncOp.UserSuppliedState); + e.SourceFileName = sourceFiles[i]; + e.TargetFileName = targetFiles[i]; + e.TotalBytesToCopy = totalBytes; + e.BytesToCopy = len; + e.Manifest = manifest; + asyncOp.Post(this.onProgressReportDelegate, e); + Thread.Sleep(1); + } + } + finally + { + DisposeStream(wStream); + DisposeStream(rStream); + } + } + } + catch (Exception ex) + { + exception = ex; + OnFileCopyError(new FileCopyErrorEventArgs() { Error = ex, Manifest = manifest }); + } + } + + this.CompletionMethod(manifest, exception, TaskCanceled(asyncOp.UserSuppliedState), asyncOp); + + //如果文件是压缩文件,则解压这些文件 + ZipFiles(e.Manifest); + } + + private void GetFiles(Manifest manifest, string sourcePath, out string[] sourceFiles, out string[] targetFiles) + { + sourceFiles = new string[manifest.ManifestFiles.Files.Length]; + targetFiles = new string[manifest.ManifestFiles.Files.Length]; + string path = Path.GetFullPath(manifest.MyApplication.Location); + for (int i = 0; i < manifest.ManifestFiles.Files.Length; i++) + { + sourceFiles[i] = Path.Combine(sourcePath, manifest.ManifestFiles.Files[i].Source); + targetFiles[i] = Path.Combine(path, manifest.ManifestFiles.Files[i].Source); + } + } + + private void DisposeStream(Stream stream) + { + if (stream != null) + { + stream.Flush(); + stream.Close(); + stream.Dispose(); + } + } + + private double GetFileLength(string[] sourceFiles) + { + double bytes = 0; + foreach (var file in sourceFiles) + { + FileInfo fileInfo = new FileInfo(file); + bytes += fileInfo.Length; + } + return bytes; + } + + private void CopyCompleted(object operationState) + { + FileCopyCompletedEventArgs e = operationState as FileCopyCompletedEventArgs; + + OnFileCopyCompleted(e); + } + + private void ReportProgress(object state) + { + FileCopyProgressChangedEventArgs e = state as FileCopyProgressChangedEventArgs; + + OnProgressChanged(e); + } + + protected void OnFileCopyCompleted(FileCopyCompletedEventArgs e) + { + if (FileCopyCompleted != null) + { + FileCopyCompleted(this, e); + } + } + + /// + /// 如果文件是压缩文件,则解压这些文件 + /// + /// + private void ZipFiles(Manifest manifest) + { + if (manifest != null) + { + string path = Path.GetFullPath(manifest.MyApplication.Location); + foreach (ManifestFile file in manifest.ManifestFiles.Files) + { + bool unzip = false; + bool.TryParse(file.Unzip, out unzip); + + if (file.Source.EndsWith(".zip", StringComparison.OrdinalIgnoreCase) && unzip) + { + string zipFile = Path.Combine(path, file.Source); + try + { + ZipUtility.UnZipFiles(zipFile, path, null, true); + } + catch (Exception ex) + { + WriteLine(ex.ToString()); + } + } + } + } + } + + public static void WriteLine(string message) + { + string temp = DateTime.Now.ToString("[yyyy-MM-dd HH:mm:ss] ") + message + "\r\n\r\n"; + string fileName = DateTime.Now.ToString("yyyyMMdd") + ".log"; + try + { + File.AppendAllText(Path.Combine(AppDomain.CurrentDomain.BaseDirectory, fileName), temp, Encoding.GetEncoding("GB2312")); + } + catch + { + } + } + + protected void OnProgressChanged(FileCopyProgressChangedEventArgs e) + { + if (FileCopyProgressChanged != null) + { + FileCopyProgressChanged(this, e); + } + } + + protected void OnFileCopyError(FileCopyErrorEventArgs e) + { + if (FileCopyError != null) + { + FileCopyError(this, e); + } + } + + private void CompletionMethod(Manifest manifest, Exception exception, bool canceled, AsyncOperation asyncOp) + { + if (!canceled) + { + lock (userStateToLifetime.SyncRoot) + { + userStateToLifetime.Remove(asyncOp.UserSuppliedState); + } + } + + FileCopyCompletedEventArgs e = new FileCopyCompletedEventArgs(exception, canceled, asyncOp.UserSuppliedState); + e.Manifest = manifest; + asyncOp.PostOperationCompleted(onCompletedDelegate, e); + } + + #endregion + + } + +} diff --git a/Update.Core/GZip.cs b/Update.Core/GZip.cs new file mode 100644 index 0000000..0ef55db --- /dev/null +++ b/Update.Core/GZip.cs @@ -0,0 +1,655 @@ +using System; +using System.Collections.Generic; +using System.Text; +using System.IO; +using System.Collections; +using System.IO.Compression; + +namespace Updater.Core +{ + /// + /// 压缩文件类 + /// + public class GZip + { + #region MyRegion + /* + //Compress三个参数分别是“要压缩的目标目录”,“保存压缩文件的目录”,压缩文件名 + GZip.Compress(@"E:\much\", @"E:\much\", "ziptest"); + + //Decompress三个参数分别是"压缩包所在目录","要解压到的目录",“压缩包名” + //如果压缩包所在目录不存在则解压不会成功 + GZip.Decompress(@"E:\much\zip\", @"E:\much\zip\", "ziptest"); + */ + #endregion + + /// + /// Compress + /// + /// The location of the files to include in the zip file, all files including files in subfolders will be included. + /// Folder to write the zip file into + /// Name of the zip file to write + public static GZipResult Compress(string lpSourceFolder, string lpDestFolder, string zipFileName) + { + return Compress(lpSourceFolder, "*.*", SearchOption.AllDirectories, lpDestFolder, zipFileName, true); + } + + /// + /// Compress + /// + /// The location of the files to include in the zip file + /// Search pattern (ie "*.*" or "*.txt" or "*.gif") to idendify what files in lpSourceFolder to include in the zip file + /// Only files in lpSourceFolder or include files in subfolders also + /// Folder to write the zip file into + /// Name of the zip file to write + /// Boolean, true deleted the intermediate temp file, false leaves the temp file in lpDestFolder (for debugging) + public static GZipResult Compress(string lpSourceFolder, string searchPattern, SearchOption searchOption, string lpDestFolder, string zipFileName, bool deleteTempFile) + { + DirectoryInfo di = new DirectoryInfo(lpSourceFolder); + FileInfo[] files = di.GetFiles("*.*", searchOption); + return Compress(files, lpSourceFolder, lpDestFolder, zipFileName, deleteTempFile); + } + + /// + /// Compress + /// + /// Array of FileInfo objects to be included in the zip file + /// Base folder to use when creating relative paths for the files + /// stored in the zip file. For example, if lpBaseFolder is 'C:\zipTest\Files\', and there is a file + /// 'C:\zipTest\Files\folder1\sample.txt' in the 'files' array, the relative path for sample.txt + /// will be 'folder1/sample.txt' + /// Folder to write the zip file into + /// Name of the zip file to write + public static GZipResult Compress(FileInfo[] files, string lpBaseFolder, string lpDestFolder, string zipFileName) + { + return Compress(files, lpBaseFolder, lpDestFolder, zipFileName, true); + } + + /// + /// Compress + /// + /// Array of FileInfo objects to be included in the zip file + /// Base folder to use when creating relative paths for the files + /// stored in the zip file. For example, if lpBaseFolder is 'C:\zipTest\Files\', and there is a file + /// 'C:\zipTest\Files\folder1\sample.txt' in the 'files' array, the relative path for sample.txt + /// will be 'folder1/sample.txt' + /// Folder to write the zip file into + /// Name of the zip file to write + /// Boolean, true deleted the intermediate temp file, false leaves the temp file in lpDestFolder (for debugging) + public static GZipResult Compress(FileInfo[] files, string lpBaseFolder, string lpDestFolder, string zipFileName, bool deleteTempFile) + { + GZipResult result = new GZipResult(); + + try + { + if (!lpDestFolder.EndsWith("\\")) + { + lpDestFolder += "\\"; + } + + string lpTempFile = lpDestFolder + zipFileName + ".tmp"; + string lpZipFile = lpDestFolder + zipFileName; + + result.TempFile = lpTempFile; + result.ZipFile = lpZipFile; + + if (files != null && files.Length > 0) + { + CreateTempFile(files, lpBaseFolder, lpTempFile, result); + + if (result.FileCount > 0) + { + CreateZipFile(lpTempFile, lpZipFile, result); + } + + // delete the temp file + if (deleteTempFile) + { + File.Delete(lpTempFile); + result.TempFileDeleted = true; + } + } + } + catch //(Exception ex4) + { + result.Errors = true; + } + return result; + } + + private static void CreateZipFile(string lpSourceFile, string lpZipFile, GZipResult result) + { + byte[] buffer; + int count = 0; + FileStream fsOut = null; + FileStream fsIn = null; + GZipStream gzip = null; + + // compress the file into the zip file + try + { + fsOut = new FileStream(lpZipFile, FileMode.Create, FileAccess.Write, FileShare.None); + gzip = new GZipStream(fsOut, CompressionMode.Compress, true); + + fsIn = new FileStream(lpSourceFile, FileMode.Open, FileAccess.Read, FileShare.Read); + buffer = new byte[fsIn.Length]; + count = fsIn.Read(buffer, 0, buffer.Length); + fsIn.Close(); + fsIn = null; + + // compress to the zip file + gzip.Write(buffer, 0, buffer.Length); + + result.ZipFileSize = fsOut.Length; + result.CompressionPercent = GetCompressionPercent(result.TempFileSize, result.ZipFileSize); + } + catch //(Exception ex1) + { + result.Errors = true; + } + finally + { + if (gzip != null) + { + gzip.Close(); + gzip = null; + } + if (fsOut != null) + { + fsOut.Close(); + fsOut = null; + } + if (fsIn != null) + { + fsIn.Close(); + fsIn = null; + } + } + } + + private static void CreateTempFile(FileInfo[] files, string lpBaseFolder, string lpTempFile, GZipResult result) + { + byte[] buffer; + int count = 0; + byte[] header; + string fileHeader = null; + string fileModDate = null; + string lpFolder = null; + int fileIndex = 0; + string lpSourceFile = null; + string vpSourceFile = null; + GZipFileInfo gzf = null; + FileStream fsOut = null; + FileStream fsIn = null; + + if (files != null && files.Length > 0) + { + try + { + result.Files = new GZipFileInfo[files.Length]; + + // open the temp file for writing + fsOut = new FileStream(lpTempFile, FileMode.Create, FileAccess.Write, FileShare.None); + + foreach (FileInfo fi in files) + { + lpFolder = fi.DirectoryName + "\\"; + try + { + gzf = new GZipFileInfo(); + gzf.Index = fileIndex; + + // read the source file, get its virtual path within the source folder + lpSourceFile = fi.FullName; + gzf.LocalPath = lpSourceFile; + vpSourceFile = lpSourceFile.Replace(lpBaseFolder, string.Empty); + vpSourceFile = vpSourceFile.Replace("\\", "/"); + gzf.RelativePath = vpSourceFile; + + fsIn = new FileStream(lpSourceFile, FileMode.Open, FileAccess.Read, FileShare.Read); + buffer = new byte[fsIn.Length]; + count = fsIn.Read(buffer, 0, buffer.Length); + fsIn.Close(); + fsIn = null; + + fileModDate = fi.LastWriteTimeUtc.ToString(); + gzf.ModifiedDate = fi.LastWriteTimeUtc; + gzf.Length = buffer.Length; + + fileHeader = fileIndex.ToString() + "," + vpSourceFile + "," + fileModDate + "," + buffer.Length.ToString() + "\n"; + header = Encoding.Default.GetBytes(fileHeader); + + fsOut.Write(header, 0, header.Length); + fsOut.Write(buffer, 0, buffer.Length); + fsOut.WriteByte(10); // linefeed + + gzf.AddedToTempFile = true; + + // update the result object + result.Files[fileIndex] = gzf; + + // increment the fileIndex + fileIndex++; + } + catch //(Exception ex1) + { + result.Errors = true; + } + finally + { + if (fsIn != null) + { + fsIn.Close(); + fsIn = null; + } + } + if (fsOut != null) + { + result.TempFileSize = fsOut.Length; + } + } + } + catch //(Exception ex2) + { + result.Errors = true; + } + finally + { + if (fsOut != null) + { + fsOut.Close(); + fsOut = null; + } + } + } + + result.FileCount = fileIndex; + } + + public static GZipResult Decompress(string lpSourceFolder, string lpDestFolder, string zipFileName) + { + return Decompress(lpSourceFolder, lpDestFolder, zipFileName, true, true, null, null, 4096); + } + + public static GZipResult Decompress(string lpSourceFolder, string lpDestFolder, string zipFileName, bool writeFiles, string addExtension) + { + return Decompress(lpSourceFolder, lpDestFolder, zipFileName, true, writeFiles, addExtension, null, 4096); + } + + public static GZipResult Decompress(string lpSrcFolder, string lpDestFolder, string zipFileName, + bool deleteTempFile, bool writeFiles, string addExtension, Hashtable htFiles, int bufferSize) + { + GZipResult result = new GZipResult(); + + if (!lpDestFolder.EndsWith("\\")) + { + lpDestFolder += "\\"; + } + + string lpTempFile = lpSrcFolder + zipFileName + ".tmp"; + string lpZipFile = lpSrcFolder + zipFileName; + + result.TempFile = lpTempFile; + result.ZipFile = lpZipFile; + + string line = null; + string lpFilePath = null; + string lpFolder = null; + GZipFileInfo gzf = null; + FileStream fsTemp = null; + ArrayList gzfs = new ArrayList(); + bool write = false; + + if (string.IsNullOrEmpty(addExtension)) + { + addExtension = string.Empty; + } + else if (!addExtension.StartsWith(".")) + { + addExtension = "." + addExtension; + } + + // extract the files from the temp file + try + { + fsTemp = UnzipToTempFile(lpZipFile, lpTempFile, result); + if (fsTemp != null) + { + while (fsTemp.Position != fsTemp.Length) + { + line = null; + while (string.IsNullOrEmpty(line) && fsTemp.Position != fsTemp.Length) + { + line = ReadLine(fsTemp); + } + + if (!string.IsNullOrEmpty(line)) + { + gzf = new GZipFileInfo(); + if (gzf.ParseFileInfo(line) && gzf.Length > 0) + { + gzfs.Add(gzf); + lpFilePath = lpDestFolder + gzf.RelativePath; + lpFolder = GetFolder(lpFilePath); + gzf.LocalPath = lpFilePath; + + write = false; + if (htFiles == null || htFiles.ContainsKey(gzf.RelativePath)) + { + gzf.RestoreRequested = true; + write = writeFiles; + } + + if (write) + { + // make sure the folder exists + if (!Directory.Exists(lpFolder)) + { + Directory.CreateDirectory(lpFolder); + } + + // read from fsTemp and write out the file + gzf.Restored = WriteFile(fsTemp, gzf.Length, lpFilePath + addExtension, bufferSize); + } + else + { + // need to advance fsTemp + fsTemp.Position += gzf.Length; + } + } + } + } + } + } + catch //(Exception ex3) + { + result.Errors = true; + } + finally + { + if (fsTemp != null) + { + fsTemp.Close(); + fsTemp = null; + } + } + + // delete the temp file + try + { + if (deleteTempFile) + { + File.Delete(lpTempFile); + result.TempFileDeleted = true; + } + } + catch //(Exception ex4) + { + result.Errors = true; + } + + result.FileCount = gzfs.Count; + result.Files = new GZipFileInfo[gzfs.Count]; + gzfs.CopyTo(result.Files); + return result; + } + + private static string ReadLine(FileStream fs) + { + string line = string.Empty; + + const int bufferSize = 4096; + byte[] buffer = new byte[bufferSize]; + byte b = 0; + byte lf = 10; + int i = 0; + + while (b != lf) + { + b = (byte)fs.ReadByte(); + buffer[i] = b; + i++; + } + + line = System.Text.Encoding.Default.GetString(buffer, 0, i - 1); + + return line; + } + + private static bool WriteFile(FileStream fs, int fileLength, string lpFile, int bufferSize) + { + bool success = false; + FileStream fsFile = null; + + if (bufferSize == 0 || fileLength < bufferSize) + { + bufferSize = fileLength; + } + + int count = 0; + int remaining = fileLength; + int readSize = 0; + + try + { + byte[] buffer = new byte[bufferSize]; + fsFile = new FileStream(lpFile, FileMode.Create, FileAccess.Write, FileShare.None); + + while (remaining > 0) + { + if (remaining > bufferSize) + { + readSize = bufferSize; + } + else + { + readSize = remaining; + } + + count = fs.Read(buffer, 0, readSize); + remaining -= count; + + if (count == 0) + { + break; + } + + fsFile.Write(buffer, 0, count); + fsFile.Flush(); + + } + fsFile.Flush(); + fsFile.Close(); + fsFile = null; + + success = true; + } + catch //(Exception ex2) + { + success = false; + } + finally + { + if (fsFile != null) + { + fsFile.Flush(); + fsFile.Close(); + fsFile = null; + } + } + return success; + } + + private static string GetFolder(string lpFilePath) + { + string lpFolder = lpFilePath; + int index = lpFolder.LastIndexOf("\\"); + if (index != -1) + { + lpFolder = lpFolder.Substring(0, index + 1); + } + return lpFolder; + } + private static FileStream UnzipToTempFile(string lpZipFile, string lpTempFile, GZipResult result) + { + FileStream fsIn = null; + GZipStream gzip = null; + FileStream fsOut = null; + FileStream fsTemp = null; + + const int bufferSize = 4096; + byte[] buffer = new byte[bufferSize]; + int count = 0; + + try + { + fsIn = new FileStream(lpZipFile, FileMode.Open, FileAccess.Read, FileShare.Read); + result.ZipFileSize = fsIn.Length; + + fsOut = new FileStream(lpTempFile, FileMode.Create, FileAccess.Write, FileShare.None); + gzip = new GZipStream(fsIn, CompressionMode.Decompress, true); + while (true) + { + count = gzip.Read(buffer, 0, bufferSize); + if (count != 0) + { + fsOut.Write(buffer, 0, count); + } + if (count != bufferSize) + { + break; + } + } + } + catch (Exception ex1) + { + result.Errors = true; + } + finally + { + if (gzip != null) + { + gzip.Close(); + gzip = null; + } + if (fsOut != null) + { + fsOut.Close(); + fsOut = null; + } + if (fsIn != null) + { + fsIn.Close(); + fsIn = null; + } + } + + fsTemp = new FileStream(lpTempFile, FileMode.Open, FileAccess.Read, FileShare.None); + if (fsTemp != null) + { + result.TempFileSize = fsTemp.Length; + } + return fsTemp; + } + + private static int GetCompressionPercent(long tempLen, long zipLen) + { + double tmp = (double)tempLen; + double zip = (double)zipLen; + double hundred = 100; + + double ratio = (tmp - zip) / tmp; + double pcnt = ratio * hundred; + + return (int)pcnt; + } + } + + /// + /// 要压缩的文件信息 + /// + public class GZipFileInfo + { + /// + /// 文件索引 + /// + public int Index = 0; + /// + /// 文件相对路径,'/' + /// + public string RelativePath = null; + public DateTime ModifiedDate; + /// + /// 文件内容长度 + /// + public int Length = 0; + public bool AddedToTempFile = false; + public bool RestoreRequested = false; + public bool Restored = false; + /// + /// 文件绝对路径,'\' + /// + public string LocalPath = null; + public string Folder = null; + + public bool ParseFileInfo(string fileInfo) + { + bool success = false; + try + { + if (!string.IsNullOrEmpty(fileInfo)) + { + // get the file information + string[] info = fileInfo.Split(','); + if (info != null && info.Length == 4) + { + this.Index = Convert.ToInt32(info[0]); + this.RelativePath = info[1].Replace("/", "\\"); + this.ModifiedDate = Convert.ToDateTime(info[2]); + this.Length = Convert.ToInt32(info[3]); + success = true; + } + } + } + catch + { + success = false; + } + return success; + } + } + + /// + /// 文件压缩后的压缩包类 + /// + public class GZipResult + { + /// + /// 压缩包中包含的所有文件,包括子目录下的文件 + /// + public GZipFileInfo[] Files = null; + /// + /// 要压缩的文件数 + /// + public int FileCount = 0; + public long TempFileSize = 0; + public long ZipFileSize = 0; + /// + /// 压缩百分比 + /// + public int CompressionPercent = 0; + /// + /// 临时文件 + /// + public string TempFile = null; + /// + /// 压缩文件 + /// + public string ZipFile = null; + /// + /// 是否删除临时文件 + /// + public bool TempFileDeleted = false; + public bool Errors = false; + } +} diff --git a/Update.Core/Model.cs b/Update.Core/Model.cs new file mode 100644 index 0000000..e2ae797 --- /dev/null +++ b/Update.Core/Model.cs @@ -0,0 +1,184 @@ +using System; +using System.Collections.Generic; +using System.Text; +using System.Xml; +using System.IO; +using System.Xml.Serialization; + +namespace Updater.Core +{ + /// + /// 文件清单对象 + /// + [XmlRoot("manifest")] + public class Manifest + { + [XmlElement("version")] + public string Version { get; set; } + + [XmlElement("description")] + public string Description { get; set; } + + [XmlElement("fileBytes")] + public long FileBytes { get; set; } + + [XmlElement("myapplication")] + public MyApplication MyApplication { get; set; } + + [XmlElement("files")] + public ManifestFiles ManifestFiles { get; set; } + } + + /// + /// 更新的文件列表 + /// + public class ManifestFiles + { + [XmlElement("file")] + public ManifestFile[] Files { get; set; } + + [XmlAttribute("base")] + public string BaseUrl { get; set; } + } + + /// + /// 更新的单个文件对象 + /// + public class ManifestFile + { + [XmlAttribute("source")] + public string Source + { + get; + set; + } + + [XmlAttribute("hash")] + public string Hash + { + get; + set; + } + + [XmlAttribute("unzip")] + public string Unzip + { + get; + set; + } + } + + /// + /// 更新的程序对象 + /// + public class MyApplication + { + [XmlAttribute("applicationId")] + public string ApplicationId { get; set; } + + [XmlElement("location")] + public string Location { get; set; } + + [XmlElement("entryPoint")] + public EntryPoint EntryPoint { get; set; } + } + + /// + /// 程序启动对象 + /// + public class EntryPoint + { + [XmlAttribute("file")] + public string File { get; set; } + + [XmlAttribute("parameters")] + public string Parameters { get; set; } + } + + /// + /// 客户端配置文件对象 + /// + public class UpdaterConfigurationView + { + private static XmlDocument document = new XmlDocument(); + private static readonly string xmlFileName = Path.Combine(AppDomain.CurrentDomain.BaseDirectory, "updateconfiguration.config"); + + static UpdaterConfigurationView() + { + document.Load(xmlFileName); + } + + /// + /// 刷新配置文件信息 + /// + public void Refresh() + { + document.RemoveAll(); + document.Load(xmlFileName); + } + + /// + /// 程序版本 + /// + public string Version + { + get + { + return document.SelectSingleNode("applicationUpdater").Attributes["version"].Value; + } + set + { + document.SelectSingleNode("applicationUpdater").Attributes["version"].Value = value; + document.Save(xmlFileName); + } + } + + /// + /// 应用程序唯一标识 + /// + public string ApplicationId + { + get + { + return document.SelectSingleNode("applicationUpdater").Attributes["applicationId"].Value; + } + set + { + document.SelectSingleNode("applicationUpdater").Attributes["applicationId"].Value = value; + document.Save(xmlFileName); + } + } + + /// + /// 远程更新文件的清单路径 + /// + public string ManifestUri + { + get + { + return document.SelectSingleNode("applicationUpdater").Attributes["manifestUri"].Value; + } + set + { + document.SelectSingleNode("applicationUpdater").Attributes["manifestUri"].Value = value; + document.Save(xmlFileName); + } + } + + /// + /// 程序名称 + /// + public string Title + { + get + { + return document.SelectSingleNode("applicationUpdater").Attributes["title"].Value; + } + set + { + document.SelectSingleNode("applicationUpdater").Attributes["title"].Value = value; + document.Save(xmlFileName); + } + } + } +} diff --git a/Update.Core/Properties/AssemblyInfo.cs b/Update.Core/Properties/AssemblyInfo.cs new file mode 100644 index 0000000..c92652b --- /dev/null +++ b/Update.Core/Properties/AssemblyInfo.cs @@ -0,0 +1,36 @@ +using System.Reflection; +using System.Runtime.CompilerServices; +using System.Runtime.InteropServices; + +// 有关程序集的一般信息由以下 +// 控制。更改这些特性值可修改 +// 与程序集关联的信息。 +[assembly: AssemblyTitle("Update.Core")] +[assembly: AssemblyDescription("")] +[assembly: AssemblyConfiguration("")] +[assembly: AssemblyCompany("")] +[assembly: AssemblyProduct("Update.Core")] +[assembly: AssemblyCopyright("Copyright © 2022")] +[assembly: AssemblyTrademark("")] +[assembly: AssemblyCulture("")] + +// 将 ComVisible 设置为 false 会使此程序集中的类型 +//对 COM 组件不可见。如果需要从 COM 访问此程序集中的类型 +//请将此类型的 ComVisible 特性设置为 true。 +[assembly: ComVisible(false)] + +// 如果此项目向 COM 公开,则下列 GUID 用于类型库的 ID +[assembly: Guid("4903d5f5-ff01-426d-b5bb-aadf7c4232fa")] + +// 程序集的版本信息由下列四个值组成: +// +// 主版本 +// 次版本 +// 生成号 +// 修订号 +// +//可以指定所有这些值,也可以使用“生成号”和“修订号”的默认值 +//通过使用 "*",如下所示: +// [assembly: AssemblyVersion("1.0.*")] +[assembly: AssemblyVersion("1.0.0.0")] +[assembly: AssemblyFileVersion("1.0.0.0")] diff --git a/Update.Core/Update.Core.csproj b/Update.Core/Update.Core.csproj new file mode 100644 index 0000000..56e18a1 --- /dev/null +++ b/Update.Core/Update.Core.csproj @@ -0,0 +1,59 @@ + + + + + Debug + AnyCPU + {4903D5F5-FF01-426D-B5BB-AADF7C4232FA} + Library + Properties + Update.Core + Update.Core + v4.8 + 512 + true + + + true + full + false + bin\Debug\ + DEBUG;TRACE + prompt + 4 + + + pdbonly + true + bin\Release\ + TRACE + prompt + 4 + + + + ..\packages\SharpZipLib.1.3.3\lib\net45\ICSharpCode.SharpZipLib.dll + + + + + + + + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/Update.Core/UpdateClass.cs b/Update.Core/UpdateClass.cs new file mode 100644 index 0000000..1ad6a2d --- /dev/null +++ b/Update.Core/UpdateClass.cs @@ -0,0 +1,386 @@ +using System; +using System.Collections.Generic; +using System.Text; +using System.Net; +using System.IO; +using System.Xml.Serialization; +using System.Xml; +using System.ComponentModel; + +namespace Updater.Core +{ + /// + /// 程序更新事件参数 + /// + public class ManifestEventArgs : EventArgs + { + public Manifest Manifest { get; set; } + } + + /// + /// 激活安装开始事件参数 + /// + public class ActivationStartedEventArgs : EventArgs + { + public Manifest Manifest { get; set; } + + public bool Cancel { get; set; } + } + + /// + /// 安装完成事件参数 + /// + public class ActivationCompletedEventArgs : AsyncCompletedEventArgs + { + public ActivationCompletedEventArgs(Exception error, bool cancelled, object userState) + : base(error, cancelled, userState) + { + } + + public Manifest Manifest { get; set; } + } + + /// + /// 程序自动更新操作类,封装了文件下载、文件复制、文件解压等操作 + /// + public class UpdateClass + { + #region 变量属性 + private DownloadClass downloader = new DownloadClass(); + private FileCopyClass fileCopyer = new FileCopyClass(); + private UpdaterConfigurationView updateCfgView = new UpdaterConfigurationView(); + private string backupFilePath = Path.Combine(AppDomain.CurrentDomain.BaseDirectory, "backup"); + + /// + /// 封装的文件下载操作类 + /// + public DownloadClass Downloader + { + get + { + return downloader; + } + } + + /// + /// 封装的文件复制操作类 + /// + public FileCopyClass FileCopyer + { + get + { + return fileCopyer; + } + } + #endregion + + #region 事件 + + /// + /// 下载进度 + /// + public event EventHandler DownloadProgressChanged; + + /// + /// 下载完成事件 + /// + public event EventHandler DownloadCompleted; + + /// + /// 下载错误触发的事件 + /// + public event EventHandler DownloadError; + + public event EventHandler ActivationInitializing; + + public event EventHandler ActivationCompleted; + + public event EventHandler ActivationStarted; + + public event EventHandler ActivationProgressChanged; + + public event EventHandler ActivationError; + + #endregion + + #region 下载更新实现 + + public UpdateClass() + { + downloader.DownloadCompleted += new EventHandler(downloader_DownloadCompleted); + downloader.DownloadError += new EventHandler(downloader_DownloadError); + downloader.DownloadProgressChanged += new EventHandler(downloader_DownloadProgressChanged); + fileCopyer.FileCopyError += new EventHandler(fileCopyer_FileCopyError); + fileCopyer.FileCopyCompleted += new EventHandler(fileCopyer_FileCopyCompleted); + fileCopyer.FileCopyProgressChanged += new EventHandler(fileCopyer_FileCopyProgressChanged); + } + + /// + /// 是否有最新的版本 + /// + public bool HasNewVersion + { + get + { + var m = CheckForUpdates(); + return m.Length > 0; + } + } + + /// + /// 检查更新,返回更新清单列表 + /// + /// + public Manifest[] CheckForUpdates() + { + updateCfgView.Refresh(); + + Uri uri = new Uri(updateCfgView.ManifestUri); + string doc = DownLoadFile(uri); + XmlSerializer xser = new XmlSerializer(typeof(Manifest)); + var manifest = xser.Deserialize(new XmlTextReader(doc, XmlNodeType.Document, null)) as Manifest; + if (manifest == null || + manifest.Version == updateCfgView.Version || + manifest.MyApplication.ApplicationId != updateCfgView.ApplicationId) + { + return new Manifest[0]; + } + return new Manifest[] { manifest }; + } + + /// + /// 用于远程下载文件清单 + /// + /// 文件清单网络路径 + /// + private string DownLoadFile(Uri uri) + { + WebRequest request = WebRequest.Create(uri); + request.Credentials = CredentialCache.DefaultCredentials; + string response = String.Empty; + using (WebResponse res = request.GetResponse()) + { + using (StreamReader reader = new StreamReader(res.GetResponseStream(), true)) + { + response = reader.ReadToEnd(); + } + } + return response; + } + + /// + /// 同步下载文件清单中的文件 + /// + /// 下载文件清单 + public void Download(Manifest[] manifests) + { + foreach (var m in manifests) + { + downloader.Download(m); + } + } + + /// + /// 异步下载文件清单中的文件 + /// + /// 下载文件清单 + public void DownloadAsync(Manifest[] manifests) + { + foreach (var m in manifests) + { + downloader.DownloadAsync(m); + } + } + + /// + /// 下载完毕后执行的启动操作 + /// + /// + public void Activate(Manifest[] manifests) + { + foreach (var m in manifests) + { + OnActivationInitializing(new ManifestEventArgs() { Manifest = m }); + Backup(m); + ActivationStartedEventArgs e = new ActivationStartedEventArgs() { Manifest = m }; + OnActivationStarted(e); + if (e.Cancel) + { + Clear(); + break; + } + else + { + fileCopyer.CopyAsync(m, downloader.TempPath); + } + } + } + + /// + /// 备份操作 + /// + /// 文件清单 + private void Backup(Manifest manifest) + { + try + { + string sourcePath = Path.GetFullPath(manifest.MyApplication.Location); + string s_filename = string.Empty; + string t_filename = string.Empty; + if (!Directory.Exists(backupFilePath)) + { + Directory.CreateDirectory(backupFilePath); + } + foreach (var file in manifest.ManifestFiles.Files) + { + t_filename = Path.Combine(backupFilePath, file.Source); + s_filename = Path.Combine(sourcePath, file.Source); + if (File.Exists(s_filename)) + { + File.Copy(s_filename, t_filename, true); + } + } + } + catch + { + } + } + + /// + /// 回滚文件下载内容 + /// + /// + public void Rollback(Manifest manifest) + { + try + { + string filename = string.Empty; + foreach (var file in manifest.ManifestFiles.Files) + { + filename = Path.Combine(backupFilePath, file.Source); + File.Copy(filename, Path.Combine(Path.GetFullPath(manifest.MyApplication.Location), file.Source)); + } + Directory.Delete(backupFilePath, true); + } + catch + { + } + } + + /// + /// 清除临时文件 + /// + private void Clear() + { + try + { + Directory.Delete(backupFilePath, true); + Directory.Delete(downloader.TempPath, true); + } + catch + { } + } + + #endregion + + #region 事件处理 + + private void fileCopyer_FileCopyError(object sender, FileCopyErrorEventArgs e) + { + OnActivationError(e); + } + + private void fileCopyer_FileCopyProgressChanged(object sender, FileCopyProgressChangedEventArgs e) + { + if (ActivationProgressChanged != null) + { + ActivationProgressChanged(sender, e); + } + } + + private void fileCopyer_FileCopyCompleted(object sender, FileCopyCompletedEventArgs e) + { + Clear(); + try + { + updateCfgView.Version = e.Manifest.Version; + } + catch + { } + + if (ActivationCompleted != null) + { + ActivationCompletedEventArgs evt = new ActivationCompletedEventArgs(e.Error, e.Cancelled, e.UserState); + evt.Manifest = e.Manifest; + OnActivationCompleted(evt); + } + } + + private void downloader_DownloadProgressChanged(object sender, DownloadProgressEventArgs e) + { + if (DownloadProgressChanged != null) + { + DownloadProgressChanged(sender, e); + } + } + + private void downloader_DownloadError(object sender, DownloadErrorEventArgs e) + { + if (DownloadError != null) + { + DownloadError(sender, e); + } + } + + private void downloader_DownloadCompleted(object sender, DownloadCompleteEventArgs e) + { + if (DownloadCompleted != null) + { + DownloadCompleted(sender, e); + } + } + + private void OnActivationInitializing(ManifestEventArgs e) + { + if (ActivationInitializing != null) + { + ActivationInitializing(this, e); + } + } + + private void OnActivationStarted(ActivationStartedEventArgs e) + { + if (ActivationStarted != null) + { + ActivationStarted(this, e); + } + } + + private void OnActivationCompleted(ActivationCompletedEventArgs e) + { + if (ActivationCompleted != null) + { + ActivationCompleted(this, e); + } + } + + private void OnActivationError(FileCopyErrorEventArgs e) + { + if (ActivationError != null) + { + ActivationError(this, e); + } + } + + private void OnActivationProgressChanged(FileCopyProgressChangedEventArgs e) + { + if (ActivationProgressChanged != null) + { + ActivationProgressChanged(this, e); + } + } + + #endregion + + } +} diff --git a/Update.Core/ZipUtility.cs b/Update.Core/ZipUtility.cs new file mode 100644 index 0000000..9a4d837 --- /dev/null +++ b/Update.Core/ZipUtility.cs @@ -0,0 +1,153 @@ +using System; +using System.Collections.Generic; +using System.Text; +using System.Collections; +using ICSharpCode.SharpZipLib.Zip; +using System.IO; + +namespace Updater.Core +{ + public static class ZipUtility + { + /// + /// 压缩文件中的文件,可设置密码 + /// + /// 输入的文件夹 + /// 输出的压缩文件全名 + /// 压缩密码 + public static void ZipFiles(string inputFolderPath, string outputPathAndFile, string password) + { + ArrayList ar = GenerateFileList(inputFolderPath); + int TrimLength = (Directory.GetParent(inputFolderPath)).ToString().Length; + // find number of chars to remove // from orginal file path + TrimLength += 1; //remove '\' + FileStream ostream; + byte[] obuffer; + string outPath = inputFolderPath + @"\" + outputPathAndFile; + ZipOutputStream oZipStream = new ZipOutputStream(File.Create(outPath)); + if (!string.IsNullOrEmpty(password)) + { + oZipStream.Password = password; + } + oZipStream.SetLevel(9); // 设置最大压缩率 + + ZipEntry oZipEntry; + foreach (string Fil in ar) + { + oZipEntry = new ZipEntry(Fil.Remove(0, TrimLength)); + oZipStream.PutNextEntry(oZipEntry); + + if (!Fil.EndsWith(@"/")) // 如果文件以 '/' 结束,则是目录 + { + ostream = File.OpenRead(Fil); + obuffer = new byte[ostream.Length]; + ostream.Read(obuffer, 0, obuffer.Length); + oZipStream.Write(obuffer, 0, obuffer.Length); + } + } + oZipStream.Finish(); + oZipStream.Close(); + } + + /// + /// 根据文件夹生成文件列表 + /// + /// + /// + private static ArrayList GenerateFileList(string Dir) + { + ArrayList fils = new ArrayList(); + bool Empty = true; + foreach (string file in Directory.GetFiles(Dir)) + { + fils.Add(file); + Empty = false; + } + + if (Empty) + { + //加入完全为空的目录 + if (Directory.GetDirectories(Dir).Length == 0) + { + fils.Add(Dir + @"/"); + } + } + + foreach (string dirs in Directory.GetDirectories(Dir)) // 递归目录 + { + foreach (object obj in GenerateFileList(dirs)) + { + fils.Add(obj); + } + } + return fils; + } + + /// + /// 解压文件到指定的目录,可设置密码、删除原文件等 + /// + /// 压缩文件全名 + /// 解压输出文件目录 + /// 解压密码 + /// 是否删除原文件(压缩文件) + public static void UnZipFiles(string zipPathAndFile, string outputFolder, string password, bool deleteZipFile) + { + using (ZipInputStream s = new ZipInputStream(File.OpenRead(zipPathAndFile))) + { + if (password != null && password != String.Empty) + { + s.Password = password; + } + + ZipEntry theEntry; + string tmpEntry = String.Empty; + while ((theEntry = s.GetNextEntry()) != null) + { + #region 遍历每个Entry对象进行解压处理 + string directoryName = outputFolder; + string fileName = Path.GetFileName(theEntry.Name); + if (directoryName != "") + { + Directory.CreateDirectory(directoryName); + } + + if (fileName != String.Empty) + { + if (theEntry.Name.IndexOf(".ini") < 0) + { + string fullPath = directoryName + "\\" + theEntry.Name; + fullPath = fullPath.Replace("\\ ", "\\"); + string fullDirPath = Path.GetDirectoryName(fullPath); + if (!Directory.Exists(fullDirPath)) Directory.CreateDirectory(fullDirPath); + using (FileStream streamWriter = File.Create(fullPath)) + { + #region 写入文件流 + int size = 2048; + byte[] data = new byte[2048]; + while (true) + { + size = s.Read(data, 0, data.Length); + if (size > 0) + { + streamWriter.Write(data, 0, size); + } + else + { + break; + } + } + #endregion + } + } + } + #endregion + } + } + + if (deleteZipFile) + { + File.Delete(zipPathAndFile); + } + } + } +} diff --git a/Update.Core/packages.config b/Update.Core/packages.config new file mode 100644 index 0000000..b99f329 --- /dev/null +++ b/Update.Core/packages.config @@ -0,0 +1,4 @@ + + + + \ No newline at end of file diff --git a/Update/App.config b/Update/App.config new file mode 100644 index 0000000..193aecc --- /dev/null +++ b/Update/App.config @@ -0,0 +1,6 @@ + + + + + + \ No newline at end of file diff --git a/Update/Class1.cs b/Update/Class1.cs deleted file mode 100644 index ae0723d..0000000 --- a/Update/Class1.cs +++ /dev/null @@ -1,12 +0,0 @@ -using System; -using System.Collections.Generic; -using System.Linq; -using System.Text; -using System.Threading.Tasks; - -namespace Update -{ - public class Class1 - { - } -} diff --git a/Update/Form1.Designer.cs b/Update/Form1.Designer.cs new file mode 100644 index 0000000..2aa0859 --- /dev/null +++ b/Update/Form1.Designer.cs @@ -0,0 +1,118 @@ +namespace Update +{ + partial class Form1 + { + /// + /// Required designer variable. + /// + private System.ComponentModel.IContainer components = null; + + /// + /// Clean up any resources being used. + /// + /// true if managed resources should be disposed; otherwise, false. + protected override void Dispose(bool disposing) + { + if (disposing && (components != null)) + { + components.Dispose(); + } + base.Dispose(disposing); + } + + #region Windows Form Designer generated code + + /// + /// Required method for Designer support - do not modify + /// the contents of this method with the code editor. + /// + private void InitializeComponent() + { + this.progressBarControl1 = new DevExpress.XtraEditors.ProgressBarControl(); + this.lab_percent = new DevExpress.XtraEditors.LabelControl(); + this.lab_filename = new System.Windows.Forms.Label(); + this.lab_fileinfo = new System.Windows.Forms.Label(); + this.lblUpdateLog = new System.Windows.Forms.Label(); + ((System.ComponentModel.ISupportInitialize)(this.progressBarControl1.Properties)).BeginInit(); + this.SuspendLayout(); + // + // progressBarControl1 + // + this.progressBarControl1.Location = new System.Drawing.Point(25, 63); + this.progressBarControl1.Name = "progressBarControl1"; + this.progressBarControl1.Properties.Step = 1; + this.progressBarControl1.Size = new System.Drawing.Size(438, 35); + this.progressBarControl1.TabIndex = 0; + // + // lab_percent + // + this.lab_percent.Location = new System.Drawing.Point(469, 74); + this.lab_percent.Name = "lab_percent"; + this.lab_percent.Size = new System.Drawing.Size(18, 15); + this.lab_percent.TabIndex = 1; + this.lab_percent.Text = "0%"; + // + // lab_filename + // + this.lab_filename.BackColor = System.Drawing.Color.Transparent; + this.lab_filename.Font = new System.Drawing.Font("宋体", 9F, System.Drawing.FontStyle.Bold, System.Drawing.GraphicsUnit.Point, ((byte)(134))); + this.lab_filename.ForeColor = System.Drawing.SystemColors.ActiveCaptionText; + this.lab_filename.Location = new System.Drawing.Point(31, 31); + this.lab_filename.Name = "lab_filename"; + this.lab_filename.Size = new System.Drawing.Size(410, 19); + this.lab_filename.TabIndex = 3; + this.lab_filename.TextAlign = System.Drawing.ContentAlignment.MiddleLeft; + // + // lab_fileinfo + // + this.lab_fileinfo.BackColor = System.Drawing.Color.Transparent; + this.lab_fileinfo.Font = new System.Drawing.Font("宋体", 9F, System.Drawing.FontStyle.Bold, System.Drawing.GraphicsUnit.Point, ((byte)(134))); + this.lab_fileinfo.ForeColor = System.Drawing.SystemColors.ActiveCaptionText; + this.lab_fileinfo.Location = new System.Drawing.Point(23, 114); + this.lab_fileinfo.Name = "lab_fileinfo"; + this.lab_fileinfo.Size = new System.Drawing.Size(410, 19); + this.lab_fileinfo.TabIndex = 4; + this.lab_fileinfo.TextAlign = System.Drawing.ContentAlignment.MiddleRight; + // + // lblUpdateLog + // + this.lblUpdateLog.Font = new System.Drawing.Font("宋体", 9F, System.Drawing.FontStyle.Bold, System.Drawing.GraphicsUnit.Point, ((byte)(134))); + this.lblUpdateLog.ForeColor = System.Drawing.SystemColors.ActiveCaptionText; + this.lblUpdateLog.Location = new System.Drawing.Point(23, 133); + this.lblUpdateLog.Name = "lblUpdateLog"; + this.lblUpdateLog.Size = new System.Drawing.Size(440, 85); + this.lblUpdateLog.TabIndex = 6; + this.lblUpdateLog.Text = "更新说明:(无)"; + // + // Form1 + // + this.AutoScaleDimensions = new System.Drawing.SizeF(7F, 15F); + this.AutoScaleMode = System.Windows.Forms.AutoScaleMode.Font; + this.ClientSize = new System.Drawing.Size(515, 233); + this.Controls.Add(this.lblUpdateLog); + this.Controls.Add(this.lab_fileinfo); + this.Controls.Add(this.lab_filename); + this.Controls.Add(this.lab_percent); + this.Controls.Add(this.progressBarControl1); + this.MaximizeBox = false; + this.MinimizeBox = false; + this.Name = "Form1"; + this.StartPosition = System.Windows.Forms.FormStartPosition.CenterScreen; + this.Text = "版本更新"; + this.Load += new System.EventHandler(this.Form1_Load); + ((System.ComponentModel.ISupportInitialize)(this.progressBarControl1.Properties)).EndInit(); + this.ResumeLayout(false); + this.PerformLayout(); + + } + + #endregion + + private DevExpress.XtraEditors.ProgressBarControl progressBarControl1; + private DevExpress.XtraEditors.LabelControl lab_percent; + private System.Windows.Forms.Label lab_filename; + private System.Windows.Forms.Label lab_fileinfo; + private System.Windows.Forms.Label lblUpdateLog; + } +} + diff --git a/Update/Form1.cs b/Update/Form1.cs new file mode 100644 index 0000000..871a447 --- /dev/null +++ b/Update/Form1.cs @@ -0,0 +1,336 @@ +using DevExpress.XtraEditors; +using System; +using System.Collections.Generic; +using System.ComponentModel; +using System.Data; +using System.Diagnostics; +using System.Drawing; +using System.IO; +using System.Linq; +using System.Text; +using System.Windows.Forms; +using Updater.Core; + +namespace Update +{ + public partial class Form1 : DevExpress.XtraEditors.XtraForm + { + //主程序传入的参数,系统标示 是否需要重新启动主程序 + private string[] args; + //表示主程序打开时传入的参数 + private readonly static string OPEN_FLAG = "121"; + private bool isComplete = true; + + private UpdateClass updater; + private List mList = new List(); + private int mLen = 0; + public Form1() + { + InitializeComponent(); + } + + private void simpleButton1_Click(object sender, EventArgs e) + { + + } + + private void simpleButton1_Click_1(object sender, EventArgs e) + { + for (int i = 0; i <= 100; i++) + { + + System.Threading.Thread.Sleep(10); + //progressBarControl1.PerformStep(); + progressBarControl1.Position = i; + lab_percent.Text = i + "%"; + //progressBarControl1.EditValue = i + 1; + //处理当前消息队列中的所有windows消息,不然进度条会不同步 + System.Windows.Forms.Application.DoEvents(); + } + } + + private void Form1_Load(object sender, EventArgs e) + { + try + { + updater = new UpdateClass(); + updater.ActivationCompleted += new EventHandler(ActivationCompleted); + updater.ActivationError += new EventHandler(ActivationError); + updater.ActivationInitializing += new EventHandler(ActivationInitializing); + updater.ActivationProgressChanged += new EventHandler(ActivationProgressChanged); + updater.ActivationStarted += new EventHandler(ActivationStarted); + updater.DownloadCompleted += new EventHandler(DownloadCompleted); + updater.DownloadError += new EventHandler(DownloadError); + updater.DownloadProgressChanged += new EventHandler(DownloadProgressChanged); + + InitUpdater(); + } + catch (Exception ex) + { + Log.Write("更新错误:" + ex.Message); + XtraMessageBox.Show("更新错误", "系统提示"); + } + } + void ActivationCompleted(object sender, ActivationCompletedEventArgs e) + { + //安装完成 + isComplete = true; + lab_filename.Text = "安装完成"; + lab_percent.Text = "100%"; + if (progressBarControl1.Position != 100) + { + progressBarControl1.Position = 100; + } + if (e.Error != null) + { + lab_filename.Text += ",但出现错误"; + lab_filename.Update(); + } + else + { + lab_filename.Update(); + System.Threading.Thread.Sleep(3000); + string filename = GetFileName(e.Manifest.MyApplication.Location, e.Manifest.MyApplication.EntryPoint.File); + Startup(filename, e.Manifest.MyApplication.EntryPoint.Parameters); + //if (args != null && args.Length > 0) + { + Exit(); + } + } + } + private string GetFileName(string location, string file) + { + return Path.Combine(Path.GetFullPath(location), file); + } + + void ActivationError(object sender, FileCopyErrorEventArgs e) + { + Log.Write("安装过程中出现错误,错误描述:" + e.Error.Message + System.Environment.NewLine + "Version:" + e.Manifest.Version); + XtraMessageBox.Show(this, "安装错误:" + e.Error.Message, "系统提示"); + lab_filename.Text = "系统正在回滚"; + updater.Rollback(e.Manifest); + } + void ActivationInitializing(object sender, ManifestEventArgs e) + { + lab_filename.Text = "正在初始化安装,请稍后......"; + lab_filename.Update(); + lab_percent.Text = "0%"; + lab_percent.Update(); + lab_fileinfo.Text = ""; + lab_fileinfo.Update(); + //progressBar1.Value = 0; + } + void ActivationProgressChanged(object sender, FileCopyProgressChangedEventArgs e) + { + progressBarControl1.Position = e.ProgressPercentage; + lab_percent.Text = e.ProgressPercentage.ToString() + "%"; + lab_percent.Update(); + lab_fileinfo.Text = string.Format("字节数:{0}/{1}", e.BytesToCopy, e.TotalBytesToCopy); + lab_fileinfo.Update(); + lab_filename.Text = "正在安装:" + e.SourceFileName; + lab_filename.Update(); + } + + void ActivationStarted(object sender, ActivationStartedEventArgs e) + { + lab_filename.Text = "开始安装,请稍后......"; + lab_filename.Update(); + e.Cancel = CheckActivation(); + if (e.Cancel) + { + lab_filename.Text = "安装已被取消"; + isComplete = true; + } + } + private bool CheckActivation() + { + bool cancel = false; + //检查主程序(进程名称)是否打开,如果打开则提示 + string[] processName = { "Client", "Server" }; + foreach (string name in processName) + { + System.Diagnostics.Process[] processes = System.Diagnostics.Process.GetProcessesByName(name); + if (processes != null && processes.Length != 0) + { + if (XtraMessageBox.Show(string.Format("进程{0}正在运行中,请关闭后重试。", name), "系统提示", + MessageBoxButtons.RetryCancel, MessageBoxIcon.Information) == System.Windows.Forms.DialogResult.Cancel) + { + cancel = true; + break; + } + else + { + return CheckActivation(); + } + } + } + return cancel; + } + + /// + /// 文件下载完毕执行的操作 + /// + void DownloadCompleted(object sender, DownloadCompleteEventArgs e) + { + mList.Add(e.Manifest); + if (mList.Count == mLen) + { + updater.Activate(mList.ToArray()); + mList.Clear(); + } + } + void DownloadError(object sender, DownloadErrorEventArgs e) + { + Log.Write("下载过程中出现错误,错误描述:" + e.Error.Message + System.Environment.NewLine + "Version:" + e.Manifest.Version); + XtraMessageBox.Show("下载出错:" + e.Error.Message, "系统提示", MessageBoxButtons.OK, MessageBoxIcon.Information); + } + + void DownloadProgressChanged(object sender, DownloadProgressEventArgs e) + { + progressBarControl1.Position = e.ProgressPercentage; + lab_percent.Text = e.ProgressPercentage.ToString() + "%"; + lab_percent.Update(); + lab_fileinfo.Text = string.Format("字节数:{0}/{1}", e.BytesReceived, e.TotalBytesToReceive); + lab_fileinfo.Update(); + lab_filename.Text = "正在下载文件:" + e.FileName; + lab_filename.Update(); + } + private void InitUpdater() + { + //从配置文件动态设置更新标题 + UpdaterConfigurationView updateCfgView = new UpdaterConfigurationView(); + + var manifests = updater.CheckForUpdates(); + mLen = manifests.Length; + if (updater.HasNewVersion) + { + //显示本次更新内容 + string updateDescription = manifests[0].Description; + this.lblUpdateLog.Text = string.Format("更新说明:{0}", updateDescription); + + if (args != null && args.Length > 0) + { + #region 关闭主程序 + try + { + string entryPoint = manifests[0].MyApplication.EntryPoint.File; + KillProcessDos(entryPoint); + } + catch (Exception ex) + { + Log.Write(ex.ToString()); + + } + #endregion + } + isComplete = false; + updater.DownloadAsync(manifests); + } + else + { + lab_filename.Text = ""; + XtraMessageBox.Show("您当前的版本已经是最新,不需要更新。", "系统提示", MessageBoxButtons.OK, MessageBoxIcon.Information); + Exit(); + } + } + /// + /// 使用DOS关闭进程 + /// + /// 进程名称 + private void KillProcessDos(string processName) + { + RunCmd("taskkill /im " + processName + " /f "); + } + /// + /// 系统退出 + /// + private void Exit() + { + this.Close(); + Environment.Exit(0); + } + /// + /// 带参数启动指定的应用程序 + /// + /// 入口的应用程序 + /// 程序启动参数 + private void Startup(string entryPoint, string parameters) + { + try + { + // if (args != null && args.Length > 0) + { + // if (args[0] == OPEN_FLAG) + { + //关闭主程序 + ExeCommand("taskkill /im " + Path.GetFileName(entryPoint) + " /f "); + //启动主程序 + System.Threading.Thread.Sleep(1000); + System.Diagnostics.Process.Start(entryPoint, parameters); + } + } + } + catch (Exception ex) + { + Log.Write(ex); + } + } + + /// + /// DOS命令运行函数 + /// + /// + private void ExeCommand(string commandText) + { + Process p = new Process(); + p.StartInfo.FileName = "cmd.exe"; + p.StartInfo.UseShellExecute = false; + p.StartInfo.RedirectStandardInput = true; + p.StartInfo.RedirectStandardOutput = true; + p.StartInfo.RedirectStandardError = true; + p.StartInfo.CreateNoWindow = true; + try + { + p.Start(); + p.StandardInput.WriteLine(commandText); + p.StandardInput.WriteLine("exit"); + //p.StandardOutput.ReadToEnd(); + } + catch + { + + } + } + /// + /// 运行DOS命令 + /// DOS关闭进程命令(ntsd -c q -p PID )PID为进程的ID + /// + /// + /// + private void RunCmd(string command) + { + //實例一個Process類,啟動一個獨立進程 + System.Diagnostics.Process p = new System.Diagnostics.Process(); + + //Process類有一個StartInfo屬性,這個是ProcessStartInfo類,包括了一些屬性和方法,下面我們用到了他的幾個屬性: + p.StartInfo.FileName = "cmd.exe"; //設定程序名 + p.StartInfo.Arguments = "/c " + command; //設定程式執行參數 + p.StartInfo.UseShellExecute = false; //關閉Shell的使用 + p.StartInfo.RedirectStandardInput = true; //重定向標準輸入 + p.StartInfo.RedirectStandardOutput = true; //重定向標準輸出 + p.StartInfo.RedirectStandardError = true; //重定向錯誤輸出 + p.StartInfo.CreateNoWindow = true; //設置不顯示窗口 + p.Start(); //啟動 + + //p.StandardInput.WriteLine(command); //也可以用這種方式輸入要執行的命令 + //p.StandardInput.WriteLine("exit"); //不過要記得加上Exit要不然下一行程式執行的時候會當機 + p.StandardOutput.ReadToEnd(); //從輸出流取得命令執行結果 + + while (!p.HasExited) + { + p.WaitForExit(1000); + } + } + + } +} diff --git a/Update/Form1.resx b/Update/Form1.resx new file mode 100644 index 0000000..1af7de1 --- /dev/null +++ b/Update/Form1.resx @@ -0,0 +1,120 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + text/microsoft-resx + + + 2.0 + + + System.Resources.ResXResourceReader, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 + + + System.Resources.ResXResourceWriter, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 + + \ No newline at end of file diff --git a/Update/Program.cs b/Update/Program.cs new file mode 100644 index 0000000..5c257e6 --- /dev/null +++ b/Update/Program.cs @@ -0,0 +1,22 @@ +using System; +using System.Collections.Generic; +using System.Linq; +using System.Threading.Tasks; +using System.Windows.Forms; + +namespace Update +{ + internal static class Program + { + /// + /// 应用程序的主入口点。 + /// + [STAThread] + static void Main() + { + Application.EnableVisualStyles(); + Application.SetCompatibleTextRenderingDefault(false); + Application.Run(new Form1()); + } + } +} diff --git a/Update/Properties/AssemblyInfo.cs b/Update/Properties/AssemblyInfo.cs index d6b98b8..9f31f9e 100644 --- a/Update/Properties/AssemblyInfo.cs +++ b/Update/Properties/AssemblyInfo.cs @@ -10,7 +10,7 @@ using System.Runtime.InteropServices; [assembly: AssemblyConfiguration("")] [assembly: AssemblyCompany("")] [assembly: AssemblyProduct("Update")] -[assembly: AssemblyCopyright("Copyright © 2021")] +[assembly: AssemblyCopyright("Copyright © 2022")] [assembly: AssemblyTrademark("")] [assembly: AssemblyCulture("")] @@ -20,7 +20,7 @@ using System.Runtime.InteropServices; [assembly: ComVisible(false)] // 如果此项目向 COM 公开,则下列 GUID 用于类型库的 ID -[assembly: Guid("295218b6-0c7e-4d1b-ad85-ab0636a83323")] +[assembly: Guid("40fb5be4-89be-4717-9378-e85de86d56dd")] // 程序集的版本信息由下列四个值组成: // diff --git a/Update/Properties/Resources.Designer.cs b/Update/Properties/Resources.Designer.cs new file mode 100644 index 0000000..4ec49c4 --- /dev/null +++ b/Update/Properties/Resources.Designer.cs @@ -0,0 +1,71 @@ +//------------------------------------------------------------------------------ +// +// 此代码由工具生成。 +// 运行时版本: 4.0.30319.42000 +// +// 对此文件的更改可能导致不正确的行为,如果 +// 重新生成代码,则所做更改将丢失。 +// +//------------------------------------------------------------------------------ + +namespace Update.Properties +{ + + + /// + /// 强类型资源类,用于查找本地化字符串等。 + /// + // 此类是由 StronglyTypedResourceBuilder + // 类通过类似于 ResGen 或 Visual Studio 的工具自动生成的。 + // 若要添加或删除成员,请编辑 .ResX 文件,然后重新运行 ResGen + // (以 /str 作为命令选项),或重新生成 VS 项目。 + [global::System.CodeDom.Compiler.GeneratedCodeAttribute("System.Resources.Tools.StronglyTypedResourceBuilder", "4.0.0.0")] + [global::System.Diagnostics.DebuggerNonUserCodeAttribute()] + [global::System.Runtime.CompilerServices.CompilerGeneratedAttribute()] + internal class Resources + { + + private static global::System.Resources.ResourceManager resourceMan; + + private static global::System.Globalization.CultureInfo resourceCulture; + + [global::System.Diagnostics.CodeAnalysis.SuppressMessageAttribute("Microsoft.Performance", "CA1811:AvoidUncalledPrivateCode")] + internal Resources() + { + } + + /// + /// 返回此类使用的缓存 ResourceManager 实例。 + /// + [global::System.ComponentModel.EditorBrowsableAttribute(global::System.ComponentModel.EditorBrowsableState.Advanced)] + internal static global::System.Resources.ResourceManager ResourceManager + { + get + { + if ((resourceMan == null)) + { + global::System.Resources.ResourceManager temp = new global::System.Resources.ResourceManager("Update.Properties.Resources", typeof(Resources).Assembly); + resourceMan = temp; + } + return resourceMan; + } + } + + /// + /// 重写当前线程的 CurrentUICulture 属性,对 + /// 使用此强类型资源类的所有资源查找执行重写。 + /// + [global::System.ComponentModel.EditorBrowsableAttribute(global::System.ComponentModel.EditorBrowsableState.Advanced)] + internal static global::System.Globalization.CultureInfo Culture + { + get + { + return resourceCulture; + } + set + { + resourceCulture = value; + } + } + } +} diff --git a/Update/Properties/Resources.resx b/Update/Properties/Resources.resx new file mode 100644 index 0000000..af7dbeb --- /dev/null +++ b/Update/Properties/Resources.resx @@ -0,0 +1,117 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + text/microsoft-resx + + + 2.0 + + + System.Resources.ResXResourceReader, System.Windows.Forms, Version=2.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 + + + System.Resources.ResXResourceWriter, System.Windows.Forms, Version=2.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 + + \ No newline at end of file diff --git a/Update/Properties/Settings.Designer.cs b/Update/Properties/Settings.Designer.cs new file mode 100644 index 0000000..37ea7c8 --- /dev/null +++ b/Update/Properties/Settings.Designer.cs @@ -0,0 +1,30 @@ +//------------------------------------------------------------------------------ +// +// This code was generated by a tool. +// Runtime Version:4.0.30319.42000 +// +// Changes to this file may cause incorrect behavior and will be lost if +// the code is regenerated. +// +//------------------------------------------------------------------------------ + +namespace Update.Properties +{ + + + [global::System.Runtime.CompilerServices.CompilerGeneratedAttribute()] + [global::System.CodeDom.Compiler.GeneratedCodeAttribute("Microsoft.VisualStudio.Editors.SettingsDesigner.SettingsSingleFileGenerator", "11.0.0.0")] + internal sealed partial class Settings : global::System.Configuration.ApplicationSettingsBase + { + + private static Settings defaultInstance = ((Settings)(global::System.Configuration.ApplicationSettingsBase.Synchronized(new Settings()))); + + public static Settings Default + { + get + { + return defaultInstance; + } + } + } +} diff --git a/Update/Properties/Settings.settings b/Update/Properties/Settings.settings new file mode 100644 index 0000000..3964565 --- /dev/null +++ b/Update/Properties/Settings.settings @@ -0,0 +1,7 @@ + + + + + + + diff --git a/Update/Update.csproj b/Update/Update.csproj index 596dbb4..1697a69 100644 --- a/Update/Update.csproj +++ b/Update/Update.csproj @@ -1,19 +1,20 @@ - + Debug AnyCPU - 295218b6-0c7e-4d1b-ad85-ab0636a83323 - Library - Properties + {40FB5BE4-89BE-4717-9378-E85DE86D56DD} + WinExe Update Update v4.8 512 + true true + AnyCPU true full false @@ -23,6 +24,7 @@ 4 + AnyCPU pdbonly true bin\Release\ @@ -31,24 +33,70 @@ 4 - - - - - - - - - - - - - - + + False + ..\..\..\..\..\Program Files (x86)\DevExpress 19.2\Components\Bin\Framework\DevExpress.Data.v19.2.dll + + + + False + ..\..\..\..\..\Program Files (x86)\DevExpress 19.2\Components\Bin\Framework\DevExpress.XtraEditors.v19.2.dll + + + + + + + + + + + + - + + Form + + + Form1.cs + + + + + Form1.cs + + + ResXFileCodeGenerator + Resources.Designer.cs + Designer + + + True + Resources.resx + + + SettingsSingleFileGenerator + Settings.Designer.cs + + + True + Settings.settings + True + + + Always + + + + + + + + {4903D5F5-FF01-426D-B5BB-AADF7C4232FA} + Update.Core + - + \ No newline at end of file diff --git a/Update/UpdateLog.cs b/Update/UpdateLog.cs new file mode 100644 index 0000000..9902cd9 --- /dev/null +++ b/Update/UpdateLog.cs @@ -0,0 +1,50 @@ +using System; +using System.Collections.Generic; +using System.Text; +using System.IO; + +namespace Update +{ + /// + /// 记录操作过程的信息 + /// + public class Log + { + public static void Write(string msg) + { + Write(msg, true); + } + + public static void Write(string msg, bool isAppend) + { + try + { + string filename = Path.Combine(AppDomain.CurrentDomain.BaseDirectory, "update.txt"); + if (!Directory.Exists(Path.GetDirectoryName(filename))) + { + Directory.CreateDirectory(Path.GetDirectoryName(filename)); + } + using (FileStream stream = new FileStream(filename, isAppend ? FileMode.Append : FileMode.Create, FileAccess.Write, FileShare.None)) + { + StreamWriter writer = new StreamWriter(stream); + writer.WriteLine(msg); + writer.Close(); + stream.Close(); + } + } + catch + { + } + } + + public static void Write(Exception ex) + { + string msg = DateTime.Now + System.Environment.NewLine + + ex.Message + System.Environment.NewLine + + ex.Source + System.Environment.NewLine + + ex.StackTrace + System.Environment.NewLine + + ex.TargetSite.Name; + Write(msg); + } + } +} \ No newline at end of file diff --git a/Update/updateconfiguration.config b/Update/updateconfiguration.config new file mode 100644 index 0000000..7ed1f8e --- /dev/null +++ b/Update/updateconfiguration.config @@ -0,0 +1,6 @@ + + + diff --git a/WinformGeneralDeveloperFrame.Commons/GetDataTableUtils.cs b/WinformGeneralDeveloperFrame.Commons/GetDataTableUtils.cs index a0fe7cb..e269842 100644 --- a/WinformGeneralDeveloperFrame.Commons/GetDataTableUtils.cs +++ b/WinformGeneralDeveloperFrame.Commons/GetDataTableUtils.cs @@ -17,8 +17,8 @@ namespace WinformGeneralDeveloperFrame.Commons public static DataTable SqlTable(string name) { - string connstring = EncodeHelper.AES_Decrypt(ConfigurationManager.ConnectionStrings["DB"].ConnectionString); - // string connstring=ConfigurationManager.ConnectionStrings["DB"].ConnectionString; + //string connstring = EncodeHelper.AES_Decrypt(ConfigurationManager.ConnectionStrings["DB"].ConnectionString); + string connstring=ConfigurationManager.ConnectionStrings["DB"].ConnectionString; string sql = ""; string url = ""; DataTable dt1 = new DataTable(); diff --git a/WinformGeneralDeveloperFrame.Start/App.config b/WinformGeneralDeveloperFrame.Start/App.config index 567b1aa..b43d7d0 100644 --- a/WinformGeneralDeveloperFrame.Start/App.config +++ b/WinformGeneralDeveloperFrame.Start/App.config @@ -51,7 +51,7 @@ - + diff --git a/WinformGeneralDeveloperFrame.Start/Start.csproj b/WinformGeneralDeveloperFrame.Start/Start.csproj index ab2e360..719b5b9 100644 --- a/WinformGeneralDeveloperFrame.Start/Start.csproj +++ b/WinformGeneralDeveloperFrame.Start/Start.csproj @@ -76,6 +76,14 @@ + + {4903d5f5-ff01-426d-b5bb-aadf7c4232fa} + Update.Core + + + {40fb5be4-89be-4717-9378-e85de86d56dd} + Update + {6f2b061d-6116-45a4-9649-49ae4981c496} Commons diff --git a/WinformGeneralDeveloperFrame.sln b/WinformGeneralDeveloperFrame.sln index 158a207..41f1a3f 100644 --- a/WinformGeneralDeveloperFrame.sln +++ b/WinformGeneralDeveloperFrame.sln @@ -15,12 +15,14 @@ Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "PrimordialForm", "WinformGe EndProject Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Entity", "Entity\Entity.csproj", "{524A09B1-52EE-49C6-ACD2-CEC2AEB8D2F6}" EndProject -Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Update", "Update\Update.csproj", "{295218B6-0C7E-4D1B-AD85-AB0636A83323}" -EndProject Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "CodeGeneration", "CodeGeneration\CodeGeneration.csproj", "{D7D32522-8FA4-4B12-ADB1-72A74F0B3964}" EndProject Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Business", "Business\Business.csproj", "{3A1FD334-A7FD-4815-A745-ACC07A7C367F}" EndProject +Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Update.Core", "Update.Core\Update.Core.csproj", "{4903D5F5-FF01-426D-B5BB-AADF7C4232FA}" +EndProject +Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Update", "Update\Update.csproj", "{40FB5BE4-89BE-4717-9378-E85DE86D56DD}" +EndProject Global GlobalSection(SolutionConfigurationPlatforms) = preSolution Debug|Any CPU = Debug|Any CPU @@ -51,10 +53,6 @@ Global {524A09B1-52EE-49C6-ACD2-CEC2AEB8D2F6}.Debug|Any CPU.Build.0 = Debug|Any CPU {524A09B1-52EE-49C6-ACD2-CEC2AEB8D2F6}.Release|Any CPU.ActiveCfg = Release|Any CPU {524A09B1-52EE-49C6-ACD2-CEC2AEB8D2F6}.Release|Any CPU.Build.0 = Release|Any CPU - {295218B6-0C7E-4D1B-AD85-AB0636A83323}.Debug|Any CPU.ActiveCfg = Debug|Any CPU - {295218B6-0C7E-4D1B-AD85-AB0636A83323}.Debug|Any CPU.Build.0 = Debug|Any CPU - {295218B6-0C7E-4D1B-AD85-AB0636A83323}.Release|Any CPU.ActiveCfg = Release|Any CPU - {295218B6-0C7E-4D1B-AD85-AB0636A83323}.Release|Any CPU.Build.0 = Release|Any CPU {D7D32522-8FA4-4B12-ADB1-72A74F0B3964}.Debug|Any CPU.ActiveCfg = Debug|Any CPU {D7D32522-8FA4-4B12-ADB1-72A74F0B3964}.Debug|Any CPU.Build.0 = Debug|Any CPU {D7D32522-8FA4-4B12-ADB1-72A74F0B3964}.Release|Any CPU.ActiveCfg = Release|Any CPU @@ -63,6 +61,14 @@ Global {3A1FD334-A7FD-4815-A745-ACC07A7C367F}.Debug|Any CPU.Build.0 = Debug|Any CPU {3A1FD334-A7FD-4815-A745-ACC07A7C367F}.Release|Any CPU.ActiveCfg = Release|Any CPU {3A1FD334-A7FD-4815-A745-ACC07A7C367F}.Release|Any CPU.Build.0 = Release|Any CPU + {4903D5F5-FF01-426D-B5BB-AADF7C4232FA}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {4903D5F5-FF01-426D-B5BB-AADF7C4232FA}.Debug|Any CPU.Build.0 = Debug|Any CPU + {4903D5F5-FF01-426D-B5BB-AADF7C4232FA}.Release|Any CPU.ActiveCfg = Release|Any CPU + {4903D5F5-FF01-426D-B5BB-AADF7C4232FA}.Release|Any CPU.Build.0 = Release|Any CPU + {40FB5BE4-89BE-4717-9378-E85DE86D56DD}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {40FB5BE4-89BE-4717-9378-E85DE86D56DD}.Debug|Any CPU.Build.0 = Debug|Any CPU + {40FB5BE4-89BE-4717-9378-E85DE86D56DD}.Release|Any CPU.ActiveCfg = Release|Any CPU + {40FB5BE4-89BE-4717-9378-E85DE86D56DD}.Release|Any CPU.Build.0 = Release|Any CPU EndGlobalSection GlobalSection(SolutionProperties) = preSolution HideSolutionNode = FALSE diff --git a/WinformGeneralDeveloperFrame.sln.DotSettings.user b/WinformGeneralDeveloperFrame.sln.DotSettings.user new file mode 100644 index 0000000..6f84b54 --- /dev/null +++ b/WinformGeneralDeveloperFrame.sln.DotSettings.user @@ -0,0 +1,2 @@ + + True \ No newline at end of file diff --git a/WinformGeneralDeveloperFrame/DB.cs b/WinformGeneralDeveloperFrame/DB.cs index 9d0ac89..a3d4744 100644 --- a/WinformGeneralDeveloperFrame/DB.cs +++ b/WinformGeneralDeveloperFrame/DB.cs @@ -13,7 +13,8 @@ namespace WinformGeneralDeveloperFrame public partial class DB : DbContext { public DB() - : base(EncodeHelper.AES_Decrypt(ConfigurationManager.ConnectionStrings["DB"].ConnectionString)) + :base("DB") + //: base(EncodeHelper.AES_Decrypt(ConfigurationManager.ConnectionStrings["DB"].ConnectionString)) { } diff --git a/WinformGeneralDeveloperFrame/DB/MESDB.cs b/WinformGeneralDeveloperFrame/DB/MESDB.cs index 85a732f..e1903f3 100644 --- a/WinformGeneralDeveloperFrame/DB/MESDB.cs +++ b/WinformGeneralDeveloperFrame/DB/MESDB.cs @@ -11,8 +11,8 @@ namespace MES public partial class MESDB : DbContext { public MESDB() - //: base("name=DB") - : base(EncodeHelper.AES_Decrypt(ConfigurationManager.ConnectionStrings["DB"].ConnectionString)) + : base("name=DB") + //: base(EncodeHelper.AES_Decrypt(ConfigurationManager.ConnectionStrings["DB"].ConnectionString)) { } public virtual DbSet stockDataInfo { get; set; } diff --git a/WinformGeneralDeveloperFrame/DevexpressForm.csproj b/WinformGeneralDeveloperFrame/DevexpressForm.csproj index 92d254b..4df135b 100644 --- a/WinformGeneralDeveloperFrame/DevexpressForm.csproj +++ b/WinformGeneralDeveloperFrame/DevexpressForm.csproj @@ -742,6 +742,14 @@ + + {4903D5F5-FF01-426D-B5BB-AADF7C4232FA} + Update.Core + + + {40FB5BE4-89BE-4717-9378-E85DE86D56DD} + Update + {6f2b061d-6116-45a4-9649-49ae4981c496} Commons diff --git a/WinformGeneralDeveloperFrame/LoginView.cs b/WinformGeneralDeveloperFrame/LoginView.cs index f413cd3..c8e5c43 100644 --- a/WinformGeneralDeveloperFrame/LoginView.cs +++ b/WinformGeneralDeveloperFrame/LoginView.cs @@ -10,11 +10,18 @@ using System.Text; using System.Windows.Forms; using MES.Entity; using WinformGeneralDeveloperFrame.Commons; +using Updater.Core; +using System.Diagnostics; +using System.IO; +using DevExpress.Utils; +using DevExpress.XtraEditors; +using Update; namespace Login { public partial class LoginView : DevExpress.XtraEditors.XtraForm { + private BackgroundWorker updateWorker; public bool bLogin = false; //判断用户是否登录 public LoginView() { @@ -45,8 +52,32 @@ namespace Login private void LoginView_Load(object sender, EventArgs e) { + updateWorker = new BackgroundWorker(); + updateWorker.DoWork += new DoWorkEventHandler(updateWorker_DoWork); + updateWorker.RunWorkerAsync(); + + } + private void updateWorker_DoWork(object sender, DoWorkEventArgs e) + { + try + { + UpdateClass update = new UpdateClass(); + bool newVersion = update.HasNewVersion; + if (newVersion) + { + //if (MessageUtil.ShowYesNoAndTips(Portal.gc.DicLag["Version"].Where(p => p.Name == Portal.gc.Language).First().Value) == DialogResult.Yes) + { + //Thread.Sleep(1500); + Process.Start(Path.Combine(Application.StartupPath, "Update.exe"), "121"); + Application.Exit(); + } + } + } + catch (Exception ex) + { + XtraMessageBox.Show(ex.Message); + } } - private void User_MouseEnter(object sender, EventArgs e) { skinPanel2.BackColor = Color.FromArgb(69, 159, 176);