我是靠谱客的博主 善良小笼包,这篇文章主要介绍c# 如何实现自动更新程序,现在分享给大家,希望可以做个参考。

主要功能介绍

实现文件的自动更新。主要功能:

  1. 支持整包完全更新,即客户端只需输入一个服务器地址,即可下载所有文件。
  2. 支持增量更新,即只更新指定的某几个文件。
  3. 支持自动更新程序的更新

更新界面如图:

客户端

main方法入口

复制代码
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
/// <summary> /// 应用程序的主入口点。 /// </summary> [STAThread] static void Main() { //在主程序中 更新替换自动升级程序 //ReplaceAutoUpgrade(); bool isEnterMain = false; try { //设置默认更新地址,如果不设置,后面会从配置文件,或界面上进行设置 UpgradeHelper.Instance.DefaultUrl = "http://localhost:17580"; if (UpgradeHelper.Instance.Local_UpgradeModel != null) { UpgradeHelper.Instance.UpgradeUrl = UpgradeHelper.Instance.Local_UpgradeModel.UpgradeUrl; } if (UpgradeHelper.Instance.WillUpgrades.Count == 0 && UpgradeHelper.Instance.Local_UpgradeModel != null) { //没有待更新,并且本地版本信息文件不为空,则直接启动主程序 bool isSucced = UpgradeHelper.StartRunMain(UpgradeHelper.Instance.Local_UpgradeModel.RunMain); if (isSucced) { Application.Exit(); } else { //清理版本信息 以便重新检测版本 UpgradeHelper.Instance.ClearUpgradeModel(); isEnterMain = true; } } else { isEnterMain = true; } } catch (Exception ex) { isEnterMain = true; MessageBox.Show("运行更新程序异常:n" + ex.Message, "错误提示", MessageBoxButtons.OK, MessageBoxIcon.Error); } if (isEnterMain) { //进入更新主界面 Application.EnableVisualStyles(); Application.SetCompatibleTextRenderingDefault(false); Application.Run(new FrmUpdate()); } }

主窗体代码

复制代码
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
public partial class FrmUpdate: Form { /// <summary> /// 构造函数 /// </summary> /// <param name="tempPath"></param> /// <param name="updateFiles"></param> public FrmUpdate() { InitializeComponent(); } /// <summary> /// 窗体加载事件 /// </summary> /// <param name="sender"></param> /// <param name="e"></param> private void FrmUpdate_Load(object sender, EventArgs e) { try { //加载服务器地址 txtHostUrl.Text = UpgradeHelper.Instance.UpgradeUrl; BeginUpgrade(); } catch(Exception ex) { Output("初始化异常:" + ex.Message); } } /// <summary> /// 手动更新 /// </summary> /// <param name="sender"></param> /// <param name="e"></param> private void butBegin_Click(object sender, EventArgs e) { try { if(string.IsNullOrWhiteSpace(txtHostUrl.Text)) { Output("请先输入服务器地址!"); return; } UpgradeHelper.Instance.UpgradeUrl = txtHostUrl.Text.Trim(); //清理版本信息 以便重新检测版本 UpgradeHelper.Instance.ClearUpgradeModel(); BeginUpgrade(); } catch(Exception ex) { Output("更新异常:" + ex.Message); } } private void BeginUpgrade() { try { if(string.IsNullOrWhiteSpace(UpgradeHelper.Instance.UpgradeUrl)) { return; } if(!(UpgradeHelper.Instance.UpgradeUrl.StartsWith("http://") || UpgradeHelper.Instance.UpgradeUrl.StartsWith("https://"))) { Output("错误的服务器地址,地址必须以http://或者https://开头"); return; } //判断是否有更新 if(UpgradeHelper.Instance.WillUpgrades.Count > 0 && UpgradeHelper.Instance.Server_UpgradeModel != null) { SetWinControl(false); //杀死主进程 UpgradeHelper.KillProcess(UpgradeHelper.Instance.Server_UpgradeModel.RunMain); RunUpgrade(); //启动更新 } } catch(Exception ex) { Output("更新异常:" + ex.Message); } } /// <summary> /// 启动更新 /// </summary> private void RunUpgrade() { //启动更新 SetCaption(string.Format("共需更新文件{0}个,已更新0个。正在更新下列文件:", UpgradeHelper.Instance.WillUpgrades.Count)); Task.Factory.StartNew(() => { string curFile = ""; try { int idx = 0; foreach(KeyValuePair < string, string > item in UpgradeHelper.Instance.WillUpgrades) { curFile = item.Key; string filePath = string.Format("{0}\{1}", Application.StartupPath, item.Key); if(item.Key.IndexOf(UpgradeHelper.Instance.Server_UpgradeModel.AutoUpgrade) >= 0) { //如果当前文件为更新主程序 filePath = string.Format("{0}\AutoUpgradeTemp\{1}", Application.StartupPath, item.Key); } string directory = Path.GetDirectoryName(filePath); if(!Directory.Exists(directory)) { Directory.CreateDirectory(directory); } MyWebResquest.DownloadFile(UpgradeHelper.Instance.UpgradeUrl, item.Key, filePath); idx++; SetCaption(string.Format("共需更新文件{0}个,已更新{1}个。更新文件列表:", UpgradeHelper.Instance.WillUpgrades.Count, idx)); Output(string.Format("更新文件{0}完成", curFile)); } //保存版本文件 File.WriteAllText(UpgradeHelper.Instance.Local_UpgradeXmlPath, UpgradeHelper.Instance.Server_UpgradeXml); SetCaption(string.Format("更新完成,共更新文件{0}个", UpgradeHelper.Instance.WillUpgrades.Count)); Output(string.Format("更新完成,共更新文件{0}个", UpgradeHelper.Instance.WillUpgrades.Count)); //下载完成后处理 UpgradeHelper.StartRunMain(UpgradeHelper.Instance.Server_UpgradeModel.RunMain); //退出当前程序 ExitCurrent(); } catch(Exception ex) { Output(string.Format("更新文件{0}异常:{1}", curFile, ex.Message)); SetWinControl(true); } }); } /// <summary> /// 设置界面控件是否可用 /// </summary> /// <param name="enabled"></param> private void SetWinControl(bool enabled) { if(this.InvokeRequired) { Action < bool > d = new Action < bool > (SetWinControl); this.Invoke(d, enabled); } else { txtHostUrl.Enabled = enabled; butBegin.Enabled = enabled; } } /// <summary> /// 退出当前程序 /// </summary> private void ExitCurrent() { if(this.InvokeRequired) { Action d = new Action(ExitCurrent); this.Invoke(d); } else { Application.Exit(); } }# region 日志输出 /// <summary> /// 设置跟踪状态 /// </summary> /// <param name="caption"></param> private void SetCaption(string caption) { if(this.lblCaption.InvokeRequired) { Action < string > d = new Action < string > (SetCaption); this.Invoke(d, caption); } else { this.lblCaption.Text = caption; } } /// <summary> /// 设置跟踪状态 /// </summary> /// <param name="caption"></param> private void Output(string log) { if(this.txtLog.InvokeRequired) { Action < string > d = new Action < string > (Output); this.Invoke(d, log); } else { txtLog.AppendText(string.Format("{0}:{1}rn", DateTime.Now.ToString("yyyy-MM-dd HH:mm:ss"), log)); txtLog.ScrollToCaret(); } } private void ClearOutput() { if(this.txtLog.InvokeRequired) { Action d = new Action(ClearOutput); this.Invoke(d); } else { txtLog.Text = ""; } }# endregion private void FrmUpdate_FormClosing(object sender, FormClosingEventArgs e) { if(e.CloseReason == CloseReason.UserClosing) { if(MessageBox.Show("升级未完成,退出后将导致软件无法正常使用,你确定要退出吗?", "退出提示", MessageBoxButtons.YesNo) != System.Windows.Forms.DialogResult.Yes) { //取消"关闭窗口"事件 e.Cancel = true; } } } }

更新帮助类

复制代码
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
/// <summary> /// 更新帮助类 /// </summary> public class UpgradeHelper { /// <summary> /// 默认服务器地址 /// 在配置文件中未找到地址时,使用此地址进行更新 /// </summary> public string DefaultUrl { get; set; } public string _upgradeUrl; /// <summary> /// 获取或设置服务器地址 /// </summary> public string UpgradeUrl { get { if(string.IsNullOrWhiteSpace(_upgradeUrl)) { return DefaultUrl; } return _upgradeUrl; } set { _upgradeUrl = value; } } /// <summary> /// 本地配置文件路径 /// </summary> public string Local_UpgradeXmlPath = Path.Combine(Application.StartupPath, "UpgradeList.xml"); private UpgradeModel _local_UpgradeModel; /// <summary> /// 本地版本信息 /// </summary> public UpgradeModel Local_UpgradeModel { get { try { if(_local_UpgradeModel == null) { if(File.Exists(Local_UpgradeXmlPath)) { _local_UpgradeModel = new UpgradeModel(); _local_UpgradeModel.LoadUpgrade(File.ReadAllText(Local_UpgradeXmlPath)); } } return _local_UpgradeModel; } catch(Exception ex) { throw new Exception(string.Format("获取本地版本文件UpgradeList.xml异常:{0}", ex.Message)); } } } private UpgradeModel _server_UpgradeModel; /// <summary> /// 服务器版本信息 /// </summary> public UpgradeModel Server_UpgradeModel { get { try { if(_server_UpgradeModel == null && !string.IsNullOrWhiteSpace(UpgradeUrl)) { string resXml = MyWebResquest.GetUpgradeList(UpgradeUrl); if(!string.IsNullOrWhiteSpace(resXml)) { _server_UpgradeModel = new UpgradeModel(); _server_UpgradeModel.LoadUpgrade(resXml); _server_UpgradeXml = resXml; } } return _server_UpgradeModel; } catch(Exception ex) { throw new Exception(string.Format("获取服务端版本文件UpgradeList.xml异常:{0}", ex.Message)); } } } private string _server_UpgradeXml; /// <summary> /// 服务端版本配置xml /// </summary> public string Server_UpgradeXml { get { return _server_UpgradeXml; } } private Dictionary < string, string > _willUpgrades; /// <summary> /// 待更新文件列表,如果为0,则表示不需要更新 /// </summary> public Dictionary < string, string > WillUpgrades { get { if(_willUpgrades == null) { _willUpgrades = new Dictionary < string, string > (); //如果服务器端未获取到版本信息 则不更新 if(Server_UpgradeModel != null) { if(Local_UpgradeModel == null) //本地版本信息为空 全部更新 { _willUpgrades = Server_UpgradeModel.DictFiles; } else { //对比需要更新的文件 foreach(var item in Server_UpgradeModel.DictFiles) { //如果找到 if(Local_UpgradeModel.DictFiles.ContainsKey(item.Key)) { //如果版本不匹配 if(Local_UpgradeModel.DictFiles[item.Key] != item.Value) { _willUpgrades.Add(item.Key, item.Value); } } else { //没有找到 _willUpgrades.Add(item.Key, item.Value); } } } } } return _willUpgrades; } } /// <summary> /// 清空版本信息 /// </summary> public void ClearUpgradeModel() { if(File.Exists(Local_UpgradeXmlPath)) { try { string xmlStr = File.ReadAllText(Local_UpgradeXmlPath); XmlDocument xmlDoc = new XmlDocument(); xmlDoc.LoadXml(xmlStr); XmlNode node = xmlDoc.SelectSingleNode("Upgrade/Files"); if(node != null && node.ChildNodes.Count > 0) { node.RemoveAll(); } File.WriteAllText(UpgradeHelper.Instance.Local_UpgradeXmlPath, xmlDoc.InnerXml); } catch(Exception) {} } _local_UpgradeModel = null; _server_UpgradeModel = null; _willUpgrades = null; }# region 单例对象 private static UpgradeHelper _instance; /// <summary> /// 单例对象 /// </summary> public static UpgradeHelper Instance { get { if(_instance == null) { _instance = new UpgradeHelper(); //初始化本地配置文件,以及服务器地址 if(_instance.Local_UpgradeModel != null) { _instance.UpgradeUrl = _instance.Local_UpgradeModel.UpgradeUrl; } } return _instance; } }# endregion# region 静态方法 /// <summary> /// 启动主程序 /// </summary> /// <param name="fileName"></param> public static bool StartRunMain(string fileName) { string fullPath = fileName; try { Process process = GetProcess(fileName); if(process != null) //以及存在运行中的主进程 { return true; } fullPath = string.Format("{0}\{1}", Application.StartupPath, fileName); ProcessStartInfo main = new ProcessStartInfo(fullPath); Process.Start(fullPath); return true; } catch(Exception ex) { MessageBox.Show(string.Format("主程序{0}调用失败:n{1}", fullPath, ex.Message), "错误提示", MessageBoxButtons.OK, MessageBoxIcon.Error); } return false; } /// <summary> /// 杀死进程 /// </summary> /// <param name="process"></param> public static void KillProcess(string processName) { if(string.IsNullOrWhiteSpace(processName)) return; processName = processName.ToLower(); processName = processName.Replace(".exe", ""); //杀死主进程 Process[] processes = Process.GetProcesses(); foreach(Process process in processes) { if(!string.IsNullOrWhiteSpace(process.ProcessName)) { if(process.ProcessName.ToLower() == processName) { process.Kill(); } } } } /// <summary> /// 获取进程 /// </summary> /// <param name="pName"></param> /// <returns></returns> public static Process GetProcess(string pName) { if(string.IsNullOrWhiteSpace(pName)) return null; pName = pName.ToLower(); pName = pName.Replace(".exe", ""); //杀死主进程 Process[] processes = Process.GetProcesses(); foreach(Process process in processes) { if(!string.IsNullOrWhiteSpace(process.ProcessName)) { if(process.ProcessName.ToLower() == pName) { return process; } } } return null; }# endregion }

版本xml文件解析

复制代码
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
public class UpgradeModel { /// <summary> /// 初始化对象 /// </summary> /// <param name="xml"></param> public void LoadUpgrade(string xml) { XmlDocument xmlDoc = new XmlDocument(); xmlDoc.LoadXml(xml); //读取UpgradeUrl XmlNode node = xmlDoc.SelectSingleNode("//UpgradeUrl"); if(node != null) { this.UpgradeUrl = node.InnerText; } //读取RunMain node = xmlDoc.SelectSingleNode("//RunMain"); if(node != null) { this.RunMain = node.InnerText; } //读取RunMain node = xmlDoc.SelectSingleNode("//AutoUpgrade"); if(node != null) { this.AutoUpgrade = node.InnerText; } //读取Files node = xmlDoc.SelectSingleNode("Upgrade/Files"); this.DictFiles = new Dictionary < string, string > (); if(node != null && node.ChildNodes.Count > 0) { foreach(XmlNode item in node.ChildNodes) { if(item.Name != "#comment") { string name = GetNodeAttrVal(item, "Name"); string version = GetNodeAttrVal(item, "Version"); if(!this.DictFiles.ContainsKey(name)) { this.DictFiles.Add(name, version); } } } } } private string GetNodeAttrVal(XmlNode node, string attr) { if(node != null && node.Attributes != null && node.Attributes[attr] != null) { string val = node.Attributes[attr].Value; if(!string.IsNullOrWhiteSpace(val)) { return val.Trim(); } return val; } return string.Empty; } /// <summary> /// 服务器地址 /// </summary> public string UpgradeUrl { get; set; } /// <summary> /// 更新完成后运行的主程序名称 /// </summary> public string RunMain { get; set; } /// <summary> /// 更新程序名称 /// </summary> public string AutoUpgrade { get; set; } /// <summary> /// 文件列表 /// string 文件名 /// string 版本号 /// </summary> public Dictionary < string, string > DictFiles { get; set; } }

服务端

服务端主Xml版本文件,包含所有的项目文件,客户端根据每个文件的版本号进行判断是否需要更新。如果需只更新某几个文件,则将对应文件的版本号更改只更高的版本号即可

版本xml文件

复制代码
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
<?xml version="1.0" encoding="utf-8" ?> <Upgrade> <!--服务器地址--> <UpgradeUrl>http://localhost:17580</UpgradeUrl> <!--更新完成后运行的主程序名称--> <RunMain>ClientMain.exe</RunMain> <!--更新程序名称--> <AutoUpgrade>AutoUpgrade.exe</AutoUpgrade> <Files> <!--更新文件列表,以Version为标志,当Version改变时,客户端启动会自动更新。子路径格式:imageindex.jpg--> <File Version="01" Name="imageindex.jpg" /> <File Version="01" Name="ClientMain.exe" /> <File Version="01" Name="AutoUpgrade.exe" /> </Files> </Upgrade>

服务端主要提供连个可以通过Http的get或post访问的路径。一个用于获取版本Xml文件内容,一个用于下载指定文件的路径。以下代码示例通过asp.net mvc进行实现。大家可以根据自己技术方式参照实现。

自动升级服务Controller

复制代码
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
/// <summary> /// 自动升级服务 /// </summary> public class UpgradeController: Controller { // // GET: /Upgrade/ /// <summary> /// 获取更新文件列表 /// </summary> /// <returns></returns> public object UpgradeList() { string cacheKey = "Upgrade_UpgradeList.xml"; string resStr = CommonLibrary.CacheClass.GetCache < string > (cacheKey); if(string.IsNullOrWhiteSpace(resStr)) { string fileName = Server.MapPath(@"~App_DataUpgradeList.xml"); if(System.IO.File.Exists(fileName)) { resStr = System.IO.File.ReadAllText(fileName); CommonLibrary.CacheClass.SetCacheMins(cacheKey, resStr, 1); } } return resStr; } /// <summary> /// 生成更新文件 /// </summary> /// <returns></returns> public object Create() { UpgradeFileManager.CreateFiles(Server.MapPath("/App_Data")); return "ok"; } /// <summary> /// 下载文件 /// </summary> /// <param name="fileName"></param> /// <returns></returns> public object DownloadFile() { string fileName = PageRequest.GetString("fileName"); fileName = Server.MapPath(string.Format(@"~App_Data{0}", fileName)); return File(fileName, "application/octet-stream"); } /// <summary> /// 异常处理 /// </summary> /// <param name="filterContext"></param> protected override void OnException(ExceptionContext filterContext) { filterContext.HttpContext.Response.StatusCode = 400; filterContext.Result = Content(filterContext.Exception.GetBaseException().Message); filterContext.ExceptionHandled = true; } }

版本文件自动生成帮助类

复制代码
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
/// <summary> /// 此类主要作用,对于项目文件非常多,自己手动编辑很麻烦,可以采用此方法,指定目录自动生成初始化的版本文件 /// </summary> public class UpgradeFileManager { /// <summary> /// 创建版本文件 /// </summary> /// <param name="path"></param> public static void CreateFiles(string path) { List < string > dirList = new List < string > (); GetAllDirt(path, dirList); //获取所有目录 dirList.Add(path); System.Text.StringBuilder xml = new System.Text.StringBuilder(); xml.AppendLine("<?xml version="1.0" encoding="utf-8" ?>"); xml.AppendLine(" <Files>"); foreach(var diry in dirList) { string[] files = Directory.GetFiles(diry); foreach(string filePath in files) { FileInfo info = new FileInfo(filePath); string name = filePath.Replace(path, ""); if(info.Directory.FullName == path) { name = name.Remove(0, 1); } xml.AppendLine(string.Format(" <File Version="1" Name="{0}" />", name)); } } xml.AppendLine("</Files>"); using(StreamWriter sw = new StreamWriter(Path.Combine(path, "UpgradeList_Temp.xml"))) { sw.Write(xml); sw.Close(); } } /// <summary> /// 获取所有子目录 /// </summary> /// <param name="curDir"></param> /// <param name="list"></param> private static void GetAllDirt(string curDir, List < string > list) { string[] dirs = Directory.GetDirectories(curDir); if(dirs.Length > 0) { foreach(string item in dirs) { list.Add(item); GetAllDirt(item, list); } } } }

结语

源代码托管于GitHub,供大伙学习参考,项目地址:https://github.com/keguoquan/AutoUpgrade。感兴趣或觉得不错的望赏个star,不胜感激!

若能顺手点个赞,更加感谢!

以上就是用c# 自动更新程序的详细内容,更多关于c# 自动更新程序的资料请关注靠谱客其它相关文章!

最后

以上就是善良小笼包最近收集整理的关于c# 如何实现自动更新程序的全部内容,更多相关c#内容请搜索靠谱客的其他文章。

本图文内容来源于网友提供,作为学习参考使用,或来自网络收集整理,版权属于原作者所有。
点赞(137)

评论列表共有 0 条评论

立即
投稿
返回
顶部