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 ee77f42..b928a27 100644 Binary files a/.vs/WinformGeneralDeveloperFrame/v17/.suo and b/.vs/WinformGeneralDeveloperFrame/v17/.suo differ diff --git a/.vs/WinformGeneralDeveloperFrame/v17/fileList.bin b/.vs/WinformGeneralDeveloperFrame/v17/fileList.bin index 43ef3fa..95cf36e 100644 Binary files a/.vs/WinformGeneralDeveloperFrame/v17/fileList.bin and b/.vs/WinformGeneralDeveloperFrame/v17/fileList.bin differ 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);