C#在线更新程序
2013-12-22 12:18
429 查看
参考文章
通用的在线升级程序。更新的内容包括一些dll或exe或、配置文件。升级的大致流程是这样的,从服务器获取一个更新的配置文件,经过核对后如有新的更新,则会从服务器下载相应的文件更新到被升级的程序目录下。如果被升级的程序在升级之前已经启动,程序则会强制关闭它,待到升级完成之后重新启动相应的程序。在升级之前程序会自动备份一次,以防升级失败造成程序不能运行。
新建一窗体应用程序UpdateWindow
数据实体
程序配置类
窗体代码文件中添加using System.Configuration;引用
主程序
ConfigHelper
HttpHelper
ZipHelper
ProcessHelper
配置文件
服务器下载的更新配置文件
通用的在线升级程序。更新的内容包括一些dll或exe或、配置文件。升级的大致流程是这样的,从服务器获取一个更新的配置文件,经过核对后如有新的更新,则会从服务器下载相应的文件更新到被升级的程序目录下。如果被升级的程序在升级之前已经启动,程序则会强制关闭它,待到升级完成之后重新启动相应的程序。在升级之前程序会自动备份一次,以防升级失败造成程序不能运行。
新建一窗体应用程序UpdateWindow
数据实体
class FileEntry { public string FileFullName { get; set; } public string Src { get; set; } public string Version { get; set; } public int Size { get; set; } public UpdateOption Option { get; set; } } public enum UpdateOption { add, del }
程序配置类
窗体代码文件中添加using System.Configuration;引用
/// <summary> /// 程序类,包括系统配置参数 /// </summary> public class AppParameter { /// <summary> /// 备份路径 /// </summary> public static string BackupPath = ConfigurationManager.AppSettings["backupPath"]; /// <summary> /// 更新服务器的URL /// </summary> public static string ServerUrl = ConfigurationManager.AppSettings["serverURL"]; /// <summary> /// 本地更新文件全名 /// </summary> public static string LocalUpdateConfig = ConfigurationManager.AppSettings["localUpdateConfig"]; /// <summary> /// 版本号 /// </summary> public static string Version = ConfigurationManager.AppSettings["version"]; /// <summary> /// 更新程序路径 /// </summary> public static string LocalPath = AppDomain.CurrentDomain.BaseDirectory; /// <summary> /// 主程序路径 /// </summary> public static string MainPath = ConfigurationManager.AppSettings["mainPath"]; /// <summary> /// 有否启动主程序 /// </summary> public static bool IsRunning = false; /// <summary> /// 主程序名 /// </summary> public static List<string> AppNames = ConfigurationManager.AppSettings["appName"].Split(';').ToList(); }
主程序
using System; using System.Collections.Generic; using System.ComponentModel; using System.Data; using System.Drawing; using System.Linq; using System.Text; using System.Windows.Forms; using System.Configuration; using System.Threading; using System.IO; namespace UpdateWindow { public partial class Form1 : Form { private bool isDelete = true;//是否要删除升级配置 private bool runningLock = false;//是否正在升级 private Thread thread;//升级的线程载入窗体时需要检查更新,如果有更新就先进行备份,备份失败的话提示错误 //退出更新,如果没有更新就提示”暂时无更新“ public Form1() { InitializeComponent(); } protected void CloseApp() { #region Process Handling List<string> processNames = new List<string>(); string mainPro = string.Empty; processNames.AddRange(AppParameter.AppNames); for (int i = 0; i < processNames.Count; i++) { processNames[i] = processNames[i].Substring(processNames[i].LastIndexOf('\\')).Trim('\\').Replace(".exe", ""); } mainPro = processNames.FirstOrDefault(); AppParameter.IsRunning = ProcessHelper.IsRunningProcess(mainPro); if (AppParameter.IsRunning) { MessageBox.Show("此操作需要关闭要更新的程序,请保存相关数据按确定继续", "提示", MessageBoxButtons.OK, MessageBoxIcon.Warning); foreach (string item in processNames) ProcessHelper.CloseProcess(item); } #endregion } protected void StartApp() { try { if (AppParameter.IsRunning) ProcessHelper.StartProcess(AppParameter.AppNames.First()); } catch (Exception ex) { MessageBox.Show("程序无法启动!" + ex.Message, "错误", MessageBoxButtons.OK, MessageBoxIcon.Error); } } private void Form1_Load(object sender, EventArgs e) { if (CheckUpdate()) { if (!Backup()) { MessageBox.Show("备份失败!"); btnStart.Enabled = false; isDelete = true; return; } } else { MessageBox.Show("暂时无更新"); this.btnFinish.Enabled = true; this.btnStart.Enabled = false; isDelete = false; this.Close(); } } private void Form1_FormClosing(object sender, FormClosingEventArgs e) { if (runningLock) { if (MessageBox.Show("升级还在进行中,中断升级会导致程序不可用,是否中断", "提示", MessageBoxButtons.YesNo, MessageBoxIcon.Asterisk) == DialogResult.Yes) { if (thread != null) thread.Abort(); isDelete = true; AppParameter.IsRunning = false; } else { e.Cancel = true; return; } } if (isDelete) File.Delete(AppParameter.LocalUpdateConfig); StartApp(); } private void btnStart_Click(object sender, EventArgs e) { btnStart.Enabled = false; UpdateApp(); } private void btnFinish_Click(object sender, EventArgs e) { this.Close(); } /// <summary> /// 更新 /// </summary> public void UpdateApp() { //bool flag = false; int successCount = 0; int failCount = 0; int itemIndex = 0; List<FileEntry> list = ConfigHelper.GetUpdateList(); if (list.Count == 0) { MessageBox.Show("版本已是最新", "提示", MessageBoxButtons.OK, MessageBoxIcon.Information); this.btnFinish.Enabled = true; this.btnStart.Enabled = false; isDelete = false; this.Close(); return; } //Thread[] arrThread = new Thread[5]; //for (int i = 0; i < arrThread.Length; i++) //{ // arrThread[i] = new Thread(new ThreadStart(delegate() // { thread = new Thread(new ThreadStart(delegate { #region thread method FileEntry ent = null; while (true) { lock (this) { if (itemIndex >= list.Count) break; ent = list[itemIndex]; //PrintResultDelegate pd = PrintResult; string msg = string.Empty; if (ExecUpdateItem(ent)) { msg = ent.FileFullName + "更新成功"; successCount++; } else { msg = ent.FileFullName + "更新失败"; failCount++; } if (this.InvokeRequired) { //this.Invoke(pd, msg, // (int)Math.Ceiling(1f / list.Count * 100)); this.Invoke((Action)delegate() { listBox1.Items.Add(msg); int val = (int)Math.Ceiling(1f / list.Count * 100); progressBar1.Value = progressBar1.Value + val > 100 ? 100 : progressBar1.Value + val; }); } itemIndex++; if (successCount + failCount == list.Count && this.InvokeRequired) { string finishMessage = string.Empty; //SetButtonDelegate sbtn = SetButton; //this.Invoke(sbtn); if (this.InvokeRequired) { this.Invoke((Action)delegate() { btnFinish.Enabled = true; }); } isDelete = failCount != 0; if (!isDelete) { AppParameter.Version = list.Last().Version; ConfigHelper.UpdateAppConfig("version", AppParameter.Version); finishMessage = "升级完成,程序已成功升级到" + AppParameter.Version; } else finishMessage = "升级完成,但不成功"; MessageBox.Show(finishMessage, "提示", MessageBoxButtons.OK, MessageBoxIcon.Information); runningLock = false; } } } #endregion })); // })); //} runningLock = true; thread.Start(); //foreach (Thread t in arrThread) //{ // t.Start(); //} } /// <summary> /// 执行单个更新 /// </summary> /// <param name="ent"></param> /// <returns></returns> public bool ExecUpdateItem(FileEntry ent) { bool result = true; try { if (ent.Option == UpdateOption.del) File.Delete(ent.FileFullName); else HttpHelper.DownLoadFile(ent.Src, Path.Combine(AppParameter.MainPath, ent.FileFullName)); } catch { result = false; } return result; } /// <summary> /// 检查更新 有则提示用户 确认后下载新的更新配置 /// </summary> /// <returns>用户确认信息</returns> public static bool CheckUpdate() { bool result = false; HttpHelper.DownLoadFile(AppParameter.ServerURL, AppParameter.LocalPath + "temp_config.xml"); if (!File.Exists(AppParameter.LocalUpdateConfig)) result = true; else { long localSize = new FileInfo(AppParameter.LocalUpdateConfig).Length; long tempSize = new FileInfo(AppParameter.LocalPath + "temp_config.xml").Length; if (localSize >= tempSize) result = false; else result = true; } if (result) { if (File.Exists(AppParameter.LocalUpdateConfig)) File.Delete(AppParameter.LocalUpdateConfig); File.Copy(AppParameter.LocalPath + "temp_config.xml", AppParameter.LocalUpdateConfig); } else result = false; File.Delete(AppParameter.LocalPath + "temp_config.xml"); return result; } /// <summary> /// 备份 /// </summary> public static bool Backup() { string sourcePath = Path.Combine(AppParameter.BackupPath, DateTime.Now.ToString("yyyy-MM-dd HH_mm_ss")+"_v_"+AppParameter.Version + ".rar"); return ZipHelper.Zip(AppParameter.MainPath.Trim() , sourcePath); } //private void PrintResult(string msg, int val) //{ // listBox1.Items.Add(msg); // progressBar1.Value = progressBar1.Value + val > 100 ? 100 : progressBar1.Value + val; //} //private void SetButton() //{ // btnFinish.Enabled = true; //} } }
ConfigHelper
using System; using System.Collections.Generic; using System.Linq; using System.Text; using System.Xml; using System.Configuration; namespace UpdateWindow { public class ConfigHelper { public static List<FileEntry> GetUpdateList() { List<FileEntry> list = new List<FileEntry>(); XmlDocument xml = new XmlDocument(); xml.Load(AppParameter.LocalUpdateConfig); XmlNodeList nodeList = xml.SelectNodes("/updateFiles/file[@version>" + AppParameter.Version + "]"); FileEntry ent = null; foreach (XmlNode node in nodeList) { ent = new FileEntry(); ent.FileFullName = node.Attributes["name"].Value; ent.Src = node.Attributes["src"].Value; ent.Version = node.Attributes["version"].Value; ent.Size =Convert.ToInt32( node.Attributes["size"].Value); ent.Option = (UpdateOption)Enum.Parse(typeof(UpdateOption), node.Attributes["option"].Value); list.Add(ent); } return list; } public static void UpdateAppConfig(string key,string value) { Configuration config = ConfigurationManager.OpenExeConfiguration(ConfigurationUserLevel.None); config.AppSettings.Settings[key].Value = value; config.Save(ConfigurationSaveMode.Full); ConfigurationManager.RefreshSection("appSettings"); } } }
HttpHelper
using System; using System.Collections.Generic; using System.Linq; using System.Text; using System.IO; using System.Net; namespace UpdateWindow { public class HttpHelper { public static void DownLoadFile(string url,string fileName) { HttpWebRequest request = (HttpWebRequest)WebRequest.Create(url); WebResponse response = request.GetResponse(); Stream stream = response.GetResponseStream(); //if (!response.ContentType.ToLower().StartsWith("text/")) //{ byte[] buffer = new byte[1024]; Stream outStream = CreateFile(fileName); Stream inStream = response.GetResponseStream(); int l; do { l = inStream.Read(buffer, 0, buffer.Length); if (l > 0) outStream.Write(buffer, 0, l); } while (l > 0); outStream.Close(); inStream.Close(); //} } private static FileStream CreateFile(string fileName) { string filePath = Path.GetDirectoryName(fileName); if (!Directory.Exists(filePath)) Directory.CreateDirectory(filePath); return File.Create(fileName); } } }
ZipHelper
using System; using System.Collections.Generic; using System.Text; using System.ComponentModel; using System.Data; using System.Drawing; using System.Windows.Forms; using System.IO; using ICSharpCode.SharpZipLib; using ICSharpCode.SharpZipLib.Checksums; using ICSharpCode.SharpZipLib.Zip; namespace UpdateWindow { public class ZipHelper { #region 压缩文件 /// 递归压缩文件夹方法 public static bool ZipFileDictory(string FolderToZip, ZipOutputStream s, string ParentFolderName) { bool res = true; string[] folders, filenames; ZipEntry entry = null; FileStream fs = null; Crc32 crc = new Crc32(); try { //创建当前文件夹 entry = new ZipEntry(Path.Combine(ParentFolderName, Path.GetFileName(FolderToZip) + "/")); //加上 “/” 才会当成是文件夹创建 s.PutNextEntry(entry); s.Flush(); //先压缩文件,再递归压缩文件夹 filenames = Directory.GetFiles(FolderToZip); foreach (string file in filenames) { //打开压缩文件 fs = File.OpenRead(file); byte[] buffer = new byte[fs.Length]; fs.Read(buffer, 0, buffer.Length); entry = new ZipEntry(Path.Combine(ParentFolderName, Path.GetFileName(FolderToZip) + "/" + Path.GetFileName(file))); entry.DateTime = DateTime.Now; entry.Size = fs.Length; fs.Close(); crc.Reset(); crc.Update(buffer); entry.Crc = crc.Value; s.PutNextEntry(entry); s.Write(buffer, 0, buffer.Length); } } catch { res = false; } finally { if (fs != null) { fs.Close(); fs = null; } if (entry != null) { entry = null; } GC.Collect(); GC.Collect(1); } folders = Directory.GetDirectories(FolderToZip); foreach (string folder in folders) { if (!ZipFileDictory(folder, s, Path.Combine(ParentFolderName, Path.GetFileName(FolderToZip)))) { return false; } } return res; } /// <summary> /// 压缩目录 /// </summary> /// <param name="FolderToZip">待压缩的文件夹,全路径格式</param> /// <param name="ZipedFile">压缩后的文件名,全路径格式</param> /// <param name="Password"></param> /// <returns></returns> public static bool ZipFileDictory(string FolderToZip, string ZipedFile, String Password) { bool res; if (!Directory.Exists(FolderToZip)) { return false; } ZipOutputStream s = new ZipOutputStream(File.Create(ZipedFile)); s.SetLevel(6); s.Password = Password; res = ZipFileDictory(FolderToZip, s, ""); s.Finish(); s.Close(); return res; } /// <summary> /// 压缩文件 /// </summary> /// <param name="FileToZip">要进行压缩的文件名</param> /// <param name="ZipedFile">压缩后生成的压缩文件名</param> /// <param name="Password"></param> /// <returns></returns> public static bool ZipFile(string FileToZip, string ZipedFile, String Password) { //如果文件没有找到,则报错 if (!File.Exists(FileToZip)) { throw new System.IO.FileNotFoundException("指定要压缩的文件: " + FileToZip + " 不存在!"); } //FileStream fs = null; FileStream ZipFile = null; ZipOutputStream ZipStream = null; ZipEntry ZipEntry = null; bool res = true; try { ZipFile = File.OpenRead(FileToZip); byte[] buffer = new byte[ZipFile.Length]; ZipFile.Read(buffer, 0, buffer.Length); ZipFile.Close(); ZipFile = File.Create(ZipedFile); ZipStream = new ZipOutputStream(ZipFile); ZipStream.Password = Password; ZipEntry = new ZipEntry(Path.GetFileName(FileToZip)); ZipStream.PutNextEntry(ZipEntry); ZipStream.SetLevel(6); ZipStream.Write(buffer, 0, buffer.Length); } catch { res = false; } finally { if (ZipEntry != null) { ZipEntry = null; } if (ZipStream != null) { ZipStream.Finish(); ZipStream.Close(); } if (ZipFile != null) { ZipFile.Close(); ZipFile = null; } GC.Collect(); GC.Collect(1); } return res; } /// <summary> /// 压缩文件 和 文件夹 /// </summary> /// <param name="FileToZip">待压缩的文件或文件夹,全路径格式</param> /// <param name="ZipedFile">压缩后生成的压缩文件名,全路径格式</param> /// <param name="Password"></param> /// <returns></returns> public static bool Zip(String FileToZip, String ZipedFile, String Password) { if (Directory.Exists(FileToZip)) { return ZipFileDictory(FileToZip, ZipedFile, Password); } else if (File.Exists(FileToZip)) { return ZipFile(FileToZip, ZipedFile, Password); } else { return false; } } public static bool Zip(String FileToZip, String ZipedFile) { return Zip(FileToZip, ZipedFile, ""); } #endregion #region 解压文件 /// <summary> /// 解压功能(解压压缩文件到指定目录) /// </summary> /// <param name="FileToUpZip">待解压的文件</param> /// <param name="ZipedFolder">指定解压目标目录</param> /// <param name="Password"></param> public static void UnZip(string FileToUpZip, string ZipedFolder, string Password) { if (!File.Exists(FileToUpZip)) { return; } if (!Directory.Exists(ZipedFolder)) { Directory.CreateDirectory(ZipedFolder); } ZipInputStream s = null; ZipEntry theEntry = null; string fileName; FileStream streamWriter = null; try { s = new ZipInputStream(File.OpenRead(FileToUpZip)); s.Password = Password; while ((theEntry = s.GetNextEntry()) != null) { if (theEntry.Name != String.Empty) { fileName = Path.Combine(ZipedFolder, theEntry.Name); fileName = fileName.Replace("/", "\\");//change by Mr.HopeGi /**/ ///判断文件路径是否是文件夹 if (fileName.EndsWith("/") || fileName.EndsWith("\\")) { Directory.CreateDirectory(fileName); continue; } streamWriter = File.Create(fileName); 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; } } } } } finally { if (streamWriter != null) { streamWriter.Close(); streamWriter = null; } if (theEntry != null) { theEntry = null; } if (s != null) { s.Close(); s = null; } GC.Collect(); GC.Collect(1); } } public static void UnZip(string FileToUpZip, string ZipedFolder) { UnZip(FileToUpZip, ZipedFolder, ""); } #endregion } }
ProcessHelper
using System; using System.Collections.Generic; using System.Linq; using System.Text; using System.Diagnostics; namespace UpdateWindow { public class ProcessHelper { public static bool IsRunningProcess(string processName) { Process[] arrPro = GetProcess(); foreach (Process p in arrPro) { if (p.ProcessName == processName) return true; } return false; } public static void CloseProcess(string processName) { Process process = Process.GetProcessesByName(processName).FirstOrDefault(); process.CloseMainWindow(); //if (!result) process.Kill(); if (Process.GetProcessesByName(processName).Length != 0) process.Kill(); //return result; } public static void StartProcess(string fileName) { Process process = new Process(); process.StartInfo = new ProcessStartInfo(fileName); process.Start(); } public static Process[] GetProcess(string ip = "") { if (string.IsNullOrEmpty(ip)) return Process.GetProcesses(); return Process.GetProcesses(ip); } } }
配置文件
<?xml version="1.0"?> <configuration> <appSettings> <add key="backupPath" value="D:\我的文档\Visual Studio 2010\Projects\UpdateWindow\UpdateWindow\backup"/> <add key="serverURL" value="D:\我的文档\Visual Studio 2010\Projects\UpdateWindow\UpdateWindow\test"/> <add key="localUpdateConfig" value="D:\我的文档\Visual Studio 2010\Projects\UpdateWindow\UpdateWindow\bin\Debug\updateconfig.xml"/> <add key="version" value="2"/> <add key="mainPath" value="D:\我的文档\Visual Studio 2010\Projects\UpdateWindow\UpdateWindow\bin\Debug"/> <add key="appName" value="D:\我的文档\Visual Studio 2010\Projects\UpdateWindow\UpdateWindow\bin\Debug\UpdateWindow.exe"/> </appSettings> <startup> <supportedRuntime version="v4.0" sku=".NETFramework,Version=v4.0"/> </startup> </configuration>
服务器下载的更新配置文件
<?xml version="1.0" encoding="utf-8"?> <updateFiles> <file name="doc\clientaccesspolicy.xml" src="http://localhost:8015/clientaccesspolicy.xml" version="3" size="1394007" option="add"/> <file name="doc\CnemcWebService.asmx" src="http://localhost:8015/CnemcWebService.asmx" version="3" size="1394007" option="add"/> <file name="doc\default.aspx" src="http://localhost:8015/default.aspx" version="3" size="1191936" option="add"/> <file name="doc\Silverlight.js" src="http://localhost:8015/Silverlight.js" version="3" size="1191936" option="add"/> <file name="doc\index.html" src="http://localhost:8015/index.html" version="3" size="1191936" option="add"/> <file name="Index.aspx" src="http://localhost:8015/Index.aspx" version="3" size="14336" option="add"/> <file name="ClientBin\Images\Login\CNEMCLogo.png" src="http://localhost:8015/ClientBin/Images/Login/CNEMCLogo.png" version="3" size="14336" option="add"/> <file name="ClientBin\Images\MainVisual\back.jpg" src="http://localhost:8015/ClientBin/Images/MainVisual/back.jpg" version="4" size="14336" option="add"/> <file name="ClientBin\Images\MainVisual\Flag.png" src="http://localhost:8015/ClientBin/Images/MainVisual/Flag.png" version="4" size="14336" option="add"/> <file name="ClientBin\Images\MainVisual\help_icon.png" src="http://localhost:8015/ClientBin/Images/MainVisual/help_icon.png" version="4" size="14336" option="add"/> <file name="ClientBin\Images\MainVisual\help_icon_big.png" src="http://localhost:8015/ClientBin/Images/MainVisual/help_icon_big.png" version="4" size="14336" option="add"/> <file name="ClientBin\Images\MainVisual\loading.png" src="http://localhost:8015/ClientBin/Images/MainVisual/loading.png" version="4" size="14336" option="add"/> <file name="ClientBin\Images\MainVisual\logout_icon.png" src="http://localhost:8015/ClientBin/Images/MainVisual/logout_icon.png" version="4" size="14336" option="add"/> <file name="ClientBin\Images\MainVisual\message_icon.png" src="http://localhost:8015/ClientBin/Images/MainVisual/message_icon.png" version="4" size="14336" option="add"/> <file name="ClientBin\Images\MainVisual\point.png" src="http://localhost:8015/ClientBin/Images/MainVisual/point.png" version="4" size="14336" option="add"/> <file name="ClientBin\Images\MainVisual\profile_edit_icon.png" src="http://localhost:8015/ClientBin/Images/MainVisual/profile_edit_icon.png" version="4" size="14336" option="add"/> <file name="ClientBin\Images\MainVisual\square.png" src="http://localhost:8015/ClientBin/Images/MainVisual/square.png" version="4" size="14336" option="add"/> </updateFiles>
相关文章推荐
- C#在线更新程序[下载程序、解压缩程序、控制台程序]
- C#在线更新程序[下载程序、解压缩程序、控制台程序]
- 如何构造一个C#语言的爬虫程序开发者在线 Builder.com.cn 更新时间:2008-07-20作者: 来源:
- c# 软件自动在线更新代码(二)
- C#窗体程序设计小技巧(不断更新中)
- 当数据库某张表数据发生变化时,更新c#程序中缓存的用法
- 个人开发者帐号+wireless install 实现非app store程序的在线更新功能
- C# 实现在线软件自动升级程序
- 用VB6写在线更新程序(上篇)(2/3)
- 列出C#进程以及详细信息开发者在线 Builder.com.cn 更新时间:2008-08-05作者: 来源:IT168
- C#中不需要用锁的线程安全的Singleton设计模式开发者在线 Builder.com.cn 更新时间:2008-07-19作者: 来源:
- 自动更新程序源码下载(C#.Net)
- C#编写软件自动升级更新程序
- C#实现程序的版本升级更新
- asp.net(c#)程序版本升级更新的实现代码
- 在线更新dede程序后 网站出现错误 DedeCMS Error:Tag disabled:\"php\" more...!
- C# Mandelbrot和Julia分形图像生成程序更新到2010-9-14版 支持多线程计算 多核处理器
- 用VB6写在线更新程序(上篇)(3/3)
- 提高C#编程水平不可不读的50个要诀 开发者在线 Builder.com.cn 更新时间:2008-08-05作者: 来源:开发者在线