最近做的一个项目中需要用到客户端自动更新功能,最初的想法是利用ClickOnce技术来完成,但在实践中发现根本行不能,原因如下:
1)项目应用到了DevExpress控件包,用ClickOnce发布的自动更新程序,客户在安装时报在GAC中找不到控件dll的错。
2)ClickOnce安装无法实现根据用户安装时录入的参数(比如数据库服务器名、数据库用户名和密码等)来动态修改配置文件的功能。
3)最后一下其实不重要了,就是ClickOnce无法实现用户自定义安装文件夹。
最后决定放弃使用ClickOnce,使用ftp方式进行,实现思路如下:用户启动程序时,先运行update.exe,该文件会自动比较本地配置文件和ftp服务器上配置文件的异同,会自动下载上次更新后变化的文件以及新加入的文件。(因为都是基本配置文件,所以开发了一个配置文件生成工具,用户只需要选择根目录后,就会自动生成配置文件。)文件下载结束后,再启动实际的客户端程序。
程序的主要代码如:
1
using System;2
using System.Collections.Generic;3
using System.Diagnostics;4
using System.IO;5
using System.Net;6
using System.Threading;7
using System.Windows.Forms;8

9
namespace Update10


{11

/**//// <summary>12
/// Description: 13
/// Author: ZhangRongHua14
/// Create DateTime: 2009-6-21 12:2515
/// UpdateHistory: 16
/// </summary>17
public partial class frmUpdate : Form18

{19

Fields#region Fields20

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
#endregion34

35

Delegates#region Delegates36

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
#endregion46

47
public event UpdateComplete OnUpdateComplete;48

49

Constructor#region Constructor50

51
public frmUpdate()52

{53
InitializeComponent();54
OnUpdateComplete += new UpdateComplete(frmUpdate_OnUpdateComplete);55
}56

57
#endregion58

59

Event Handler#region Event Handler60

61
private void frmUpdate_Load(object sender, EventArgs e)62

{63
if(Directory.Exists(updateTmpPath))64

{65
Directory.Delete(updateTmpPath, true);66
}67
68
// 如果有主程序启动,则关闭69
Process[] ps = Process.GetProcesses();70
foreach (Process p in ps)71

{72
//MessageBox.Show(p.ProcessName);73
if (p.ProcessName.ToLower() == "wat.pms.winform")74

{75
p.Kill();76
break;77
}78
}79

80
GetUpdateFiles();81
}82

83
private void frmUpdate_OnUpdateComplete()84

{85
ExecuteUpdateFiles dExecuteUpdateFiles = new ExecuteUpdateFiles(ExecuteUpdate);86

Invoke(dExecuteUpdateFiles, new object[]
{updateTmpPath, appPath});87
}88

89
private void frmUpdate_Shown(object sender, EventArgs e)90

{91
Thread updateThread = new Thread(new ThreadStart(DownLoadUpdateFiles));92
updateThread.SetApartmentState(ApartmentState.STA);93
updateThread.IsBackground = true;94
Thread.Sleep(500);95
updateThread.Start();96

97
98
}99

100
#endregion101

102

Private Methods#region Private Methods103

104

/**//// <summary>105
/// 将目标文件替换为本地文件106
/// <remark> 107
/// Author:ZhangRongHua 108
/// Create DateTime: 2009-6-21 10:28109
/// 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

{116
if(ftp == null )117

{118
ftp = new FTP(Updater.URL, "", Updater.User, Updater.Password);119
}120

121
122

123
if(!ftp.Connected)124

{125
MessageBox.Show("无法连接远程服务器,无法更新程序", "提示", MessageBoxButtons.OK, MessageBoxIcon.Information);126
Process.Start(Updater.MainProgram);127
Close();128
129
}130

131
// 得到服务器端的配置文件132
ftp.Get(srcFile, updateTmpPath, destFile);133

134
135
136
}137

138

/**//// <summary>139
/// 得到需要更新的文件清单140
/// <remark> 141
/// Author:ZhangRongHua 142
/// Create DateTime: 2009-6-21 10:29143
/// Update History: 144
/// </remark>145
/// </summary>146
private void GetUpdateFiles()147

{148
Updater.GetBaseInfo(locFile);149
url = Updater.URL;150
string svrFile = String.Concat(url, CONFIGFILE);151

152
if (String.IsNullOrEmpty(svrFile))153

{154
BroadCastOnUpdateComplete();155
return;156
}157

158
Directory.CreateDirectory(updateTmpPath);159
try160

{161
DownLoadFile(CONFIGFILE, tmpUpdateFile);162
}163
catch (Exception ex)164

{165
MessageBox.Show("无法连接远程服务器,无法更新程序", "提示", MessageBoxButtons.OK, MessageBoxIcon.Information);166
Process.Start(Updater.MainProgram);167
Close();168
}169

170
updateList = Updater.GetUpdateFileList(locFile, tmpUpdateFile);171
if (updateList == null || updateList.Count < 1)172

{173
BroadCastOnUpdateComplete();174
return;175
}176

177
pbUpdate.Maximum = updateList.Count;178
pbUpdate.Minimum = 0;179
lblInfo.Text = String.Concat(updateList.Count, "个文件需要更新!");180
lblInfo.BringToFront();181
lblState.BringToFront();182
lblInfo.Update();183

184
pbUpdate.Maximum = updateList.Count;185
pbUpdate.Minimum = 0;186

187
for (int i = 0; i < updateList.Count; i++)188

{189
string file = updateList[i];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

{199
pbUpdate.Value = i + 1;200
lblState.Text = message;201
lblState.Update();202
}203

204
private void BroadCastOnUpdateComplete()205

{206
if (OnUpdateComplete != null)207

{208
OnUpdateComplete();209
}210
}211

212
private void DownLoadUpdateFiles()213

{214
if (updateList == null || updateList.Count < 1)215

{216
BroadCastOnUpdateComplete();217
return;218
}219

220
try221

{222
UpdateUI dUpdateUI = new UpdateUI(UpdateUIInfo);223
AsycDownLoadFile dAsycDownLoadFile = new AsycDownLoadFile(DownLoadFile);224
for (int i = 0; i < updateList.Count; i++)225

{226
string file = updateList[i];227
string destFile = String.Concat(updateTmpPath, "\\", file);228
string destFileName = destFile.Substring(destFile.LastIndexOf("/") + 1);229
string srcFile = String.Concat(url, file);230
var srcFileName = file.Trim('/');231
destFile = destFile.Replace("/", "\\");232
Directory.CreateDirectory(destFile.Substring(0, destFile.LastIndexOf("\\")));233
string curentFile = String.Concat("正在更新第", i + 1, "/", updateList.Count, "个", file);234
235

Invoke(dAsycDownLoadFile, new object[]
{ srcFileName, srcFileName, i });236
Thread.Sleep(50);237

Invoke(dUpdateUI, new object[]
{i, curentFile});238
}239
}240
catch (Exception ex)241

{242
Debug.WriteLine(ex.Message);243
}244

245
BroadCastOnUpdateComplete();246
}247

248
249

250
private void CopyUpdateFiles(string srcPath, string destPath)251

{252
string[] files = Directory.GetFiles(srcPath);253
for (int i = 0; i < files.Length; i++)254

{255
string srcFile = files[i];256
string destFile = string.Concat(destPath, "\\", Path.GetFileName(srcFile));257
try258

{259
File.Copy(srcFile, destFile, true);260
}261
catch (System.IO.IOException ex)262

{263
264
265
}266
267
}268

269
string[] dirs = Directory.GetDirectories(srcPath);270
for (int i = 0; i < dirs.Length; i++)271

{272
srcPath = dirs[i];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: 2009-6-21 12:25286
/// 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

{293
if (errorList != null && errorList.Count < 1)294

{295
lblInfo.Text = "正在执行更新";296
lblInfo.Update();297
CopyUpdateFiles(srcPath, destPath);298
File.Copy(tmpUpdateFile, locFile, true);299
}300
Process.Start(Updater.MainProgram);301

302
Close();303
}304

305
private void DownLoadFile(string srcFile, string destFile, ListViewItem lvItem)306

{307
try308

{309
DownLoadFile(srcFile, destFile);310
lvItem.SubItems.Add("Ok");311
}312
catch (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

{326
ListViewItem lvItem = lvUpdateList.Items[i];327

328
lvUpdateList.Items[i].EnsureVisible();329
try330

{331
DownLoadFile(srcFile, destFile);332
lvItem.SubItems.Add("Ok");333
}334
catch (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
#endregion348
}349
}