最近做的一个项目中需要用到客户端自动更新功能,最初的想法是利用ClickOnce技术来完成,但在实践中发现根本行不能,原因如下:
1)项目应用到了DevExpress控件包,用ClickOnce发布的自动更新程序,客户在安装时报在GAC中找不到控件dll的错。
2)ClickOnce安装无法实现根据用户安装时录入的参数(比如数据库服务器名、数据库用户名和密码等)来动态修改配置文件的功能。
3)最后一下其实不重要了,就是ClickOnce无法实现用户自定义安装文件夹。
最后决定放弃使用ClickOnce,使用ftp方式进行,实现思路如下:用户启动程序时,先运行update.exe,该文件会自动比较本地配置文件和ftp服务器上配置文件的异同,会自动下载上次更新后变化的文件以及新加入的文件。(因为都是基本配置文件,所以开发了一个配置文件生成工具,用户只需要选择根目录后,就会自动生成配置文件。)文件下载结束后,再启动实际的客户端程序。
程序的主要代码如: 1using System;
2using System.Collections.Generic;
3using System.Diagnostics;
4using System.IO;
5using ;
6using System.Threading;
7using System.Windows.Forms;
8
9namespace Update
10{
11 /**//// <summary>
12 /// Description:
13 /// Author: ZhangRongHua
14 /// Create DateTime: -6-21 12:25
15 /// UpdateHistory:
16 /// </summary>
17 public partial class frmUpdate : Form
18 {
19 Fields#region Fields
20
21 private const string CONFIGFILE = "update.xml";
22 private const string UPDATEDIR = "PMS";
23 private string appPath = Application.StartupPath;
24 private List<ErrorInfo> errorList = new List<ErrorInfo>();
25 private string locFile = String.Concat(Application.StartupPath, "\\", CONFIGFILE);
26 private string tmpUpdateFile = "TmpUpdate.xml";
27 private List<string> updateList;
28 private string updateTmpPath = string.Concat(Path.GetTempPath(), "\\", UPDATEDIR);
29 private string url = String.Empty;
30
31 private FTP ftp = null;
32
33 #endregion
34
35 Delegates#region Delegates
36
37 public delegate void AsycDownLoadFile(string srcFile, string destFile, int i);
38
39 public delegate void ExecuteUpdateFiles(string srcPath, string destPath);
40
41 public delegate void UpdateComplete();
42
43 public delegate void UpdateUI(int i, string message);
44
45 #endregion
46
47 public event UpdateComplete OnUpdateComplete;
48
49 Constructor#region Constructor
50
51 public frmUpdate()
52 {
53InitializeComponent();
54OnUpdateComplete += new UpdateComplete(frmUpdate_OnUpdateComplete);
55 }
56
57 #endregion
58
59 Event Handler#region Event Handler
60
61 private void frmUpdate_Load(object sender, EventArgs e)
62 {
63if(Directory.Exists(updateTmpPath))
64{
65 Directory.Delete(updateTmpPath, true);
66}
67
68// 如果有主程序启动,则关闭
69Process[] ps = Process.GetProcesses();
70foreach (Process p in ps)
71{
72 //MessageBox.Show(p.ProcessName);
73 if (p.ProcessName.ToLower() == "wat.pms.winform")
74 {
75p.Kill();
76break;
77 }
78}
79
80GetUpdateFiles();
81 }
82
83 private void frmUpdate_OnUpdateComplete()
84 {
85ExecuteUpdateFiles dExecuteUpdateFiles = new ExecuteUpdateFiles(ExecuteUpdate);
86Invoke(dExecuteUpdateFiles, new object[] {updateTmpPath, appPath});
87 }
88
89 private void frmUpdate_Shown(object sender, EventArgs e)
90 {
91Thread updateThread = new Thread(new ThreadStart(DownLoadUpdateFiles));
92updateThread.SetApartmentState(ApartmentState.STA);
93updateThread.IsBackground = true;
94Thread.Sleep(500);
95updateThread.Start();
96
97
98 }
99
100 #endregion
101
102 Private Methods#region Private Methods
103
104 /**//// <summary>
105 /// 将目标文件替换为本地文件
106 /// <remark>
107 /// Author:ZhangRongHua
108 /// Create DateTime: -6-21 10:28
109 /// Update History:
110 /// </remark>
111 /// </summary>
112 /// <param name="srcFile">The SRC file.</param>
113 /// <param name="destFile">The dest file.</param>
114 private void DownLoadFile(string srcFile, string destFile)
115 {
116if(ftp == null )
117{
118 ftp = new FTP(Updater.URL, "", Updater.User, Updater.Password);
119}
120
121
122
123if(!ftp.Connected)
124{
125 MessageBox.Show("无法连接远程服务器,无法更新程序", "提示", MessageBoxButtons.OK, MessageBoxIcon.Information);
126 Process.Start(Updater.MainProgram);
127 Close();
128
129}
130
131// 得到服务器端的配置文件
132ftp.Get(srcFile, updateTmpPath, destFile);
133
134
135
136 }
137
138 /**//// <summary>
139 /// 得到需要更新的文件清单
140 /// <remark>
141 /// Author:ZhangRongHua
142 /// Create DateTime: -6-21 10:29
143 /// Update History:
144 /// </remark>
145 /// </summary>
146 private void GetUpdateFiles()
147 {
148Updater.GetBaseInfo(locFile);
149url = Updater.URL;
150string svrFile = String.Concat(url, CONFIGFILE);
151
152if (String.IsNullOrEmpty(svrFile))
153{
154 BroadCastOnUpdateComplete();
155 return;156}
157
158Directory.CreateDirectory(updateTmpPath);
159try
160{
161 DownLoadFile(CONFIGFILE, tmpUpdateFile);
162}
163catch (Exception ex)
164{
165 MessageBox.Show("无法连接远程服务器,无法更新程序", "提示", MessageBoxButtons.OK, MessageBoxIcon.Information);
166 Process.Start(Updater.MainProgram);
167 Close();
168}
169
170updateList = Updater.GetUpdateFileList(locFile, tmpUpdateFile);
171if (updateList == null || updateList.Count < 1)
172{
173 BroadCastOnUpdateComplete();
174 return;
175}
176
177pbUpdate.Maximum = updateList.Count;
178pbUpdate.Minimum = 0;
179lblInfo.Text = String.Concat(updateList.Count, "个文件需要更新!");
180lblInfo.BringToFront();
181lblState.BringToFront();
182lblInfo.Update();
183
184pbUpdate.Maximum = updateList.Count;
185pbUpdate.Minimum = 0;
186
187for (int i = 0; i < updateList.Count; i++)
188{
189 string file = updateList;
190 ListViewItem lvItem = new ListViewItem();
191 lvItem.Text = file;
192 lvItem.SubItems.Add(Updater.MainProgramVersion);
193 lvUpdateList.Items.Add(lvItem);
194}
195 }
196
197 private void UpdateUIInfo(int i, string message)
198 {
199pbUpdate.Value = i + 1;
200lblState.Text = message;
201lblState.Update();
202 }
203
204 private void BroadCastOnUpdateComplete()
205 {
206if (OnUpdateComplete != null)
207{
208 OnUpdateComplete();
209}
210 }
211
212 private void DownLoadUpdateFiles()
213 {
214if (updateList == null || updateList.Count < 1)
215{
216 BroadCastOnUpdateComplete();
217 return;
218}
219
220try
221{
222 UpdateUI dUpdateUI = new UpdateUI(UpdateUIInfo);
223 AsycDownLoadFile dAsycDownLoadFile = new AsycDownLoadFile(DownLoadFile);
224 for (int i = 0; i < updateList.Count; i++)
225 {
226string file = updateList;
227string destFile = String.Concat(updateTmpPath, "\\", file);
228string destFileName = destFile.Substring(destFile.LastIndexOf("/") + 1);
229string srcFile = String.Concat(url, file);
230var srcFileName = file.Trim("/");
231destFile = destFile.Replace("/", "\\");
232Directory.CreateDirectory(destFile.Substring(0, destFile.LastIndexOf("\\")));
233string curentFile = String.Concat("正在更新第", i + 1, "/", updateList.Count, "个", file);
234
235Invoke(dAsycDownLoadFile, new object[] { srcFileName, srcFileName, i });
236Thread.Sleep(50);
237Invoke(dUpdateUI, new object[] {i, curentFile});
238 }
239}
240catch (Exception ex)
241{
242 Debug.WriteLine(ex.Message);
243}
244
245BroadCastOnUpdateComplete();
246 }
247
248
249
250 private void CopyUpdateFiles(string srcPath, string destPath)
251 {
252string[] files = Directory.GetFiles(srcPath);
253for (int i = 0; i < files.Length; i++)
254{
255 string srcFile = files;
256 string destFile = string.Concat(destPath, "\\", Path.GetFileName(srcFile));
257 try
258 {
259File.Copy(srcFile, destFile, true);
260 }
261 catch (System.IO.IOException ex)
262 {
263
264
265 }
266
267}
268
269string[] dirs = Directory.GetDirectories(srcPath);
270for (int i = 0; i < dirs.Length; i++)
271{
272 srcPath = dirs;
273 string tmpDestPath = String.Concat(destPath, "\\", Path.GetFileName(srcPath));
274 Directory.CreateDirectory(tmpDestPath);
275 CopyUpdateFiles(srcPath, tmpDestPath);
276}
277 }
278
279
280
281 /**//// <summary>
282 /// 更新完成后,要执行的动作(将下载的文件从临时目录复制到主目录,重启主程序)
283 /// <remark>
284 /// Author:ZhangRongHua
285 /// Create DateTime: -6-21 12:25
286 /// Update History:
287 /// </remark>
288 /// </summary>
289 /// <param name="srcPath">The SRC path.</param>
290 /// <param name="destPath">The dest path.</param>
291 private void ExecuteUpdate(string srcPath, string destPath)
292 {
293if (errorList != null && errorList.Count < 1)
294{
295 lblInfo.Text = "正在执行更新";
296 lblInfo.Update();
297 CopyUpdateFiles(srcPath, destPath);
298 File.Copy(tmpUpdateFile, locFile, true);
299}
300Process.Start(Updater.MainProgram);
301
302Close();
303 }
304
305 private void DownLoadFile(string srcFile, string destFile, ListViewItem lvItem)
306 {
307try
308{
309 DownLoadFile(srcFile, destFile);
310 lvItem.SubItems.Add("Ok");
311}
312catch (Exception ex)
313{
314 Debug.WriteLine(ex.Message);
315 lvItem.SubItems.Add("fail");
316 ErrorInfo errorInfo = new ErrorInfo();
317 errorInfo.File = srcFile;
318 errorInfo.ErrorLevel = ErrorLevel.Serious;
319 errorInfo.Message = ex.Message;
320 errorList.Add(errorInfo);
321}
322 }
323
324 private void DownLoadFile(string srcFile, string destFile, int i)
325 {
326ListViewItem lvItem = lvUpdateList.Items;
327
328lvUpdateList.Items.EnsureVisible();
329try
330{
331 DownLoadFile(srcFile, destFile);
332 lvItem.SubItems.Add("Ok");
333}
334catch (Exception ex)
335{
336 Debug.WriteLine(ex.Message);
337 lvItem.SubItems.Add("fail");
338 ErrorInfo errorInfo = new ErrorInfo();
339 errorInfo.File = srcFile;
340 errorInfo.ErrorLevel = ErrorLevel.Serious;
341 errorInfo.Message = ex.Message;
342 errorList.Add(errorInfo);
343 MessageBox.Show(destFile);
344}
345 }
346
347 #endregion
348 }
349}
复制代码