很多朋友开发好程序,又需要时间去开发升级模块,比较麻烦,我在这里给出一个通用升级工具的解决方法。
首先,我们考虑下对这个升级程序或模块的要求,主要是通用性和便捷性这两点;在这里,我忽然想到了网络游戏的更新程序,发现它非常不错,我们可以像它一样,由主程序调用独立的LiveUpdate程序,对自己进行升级,这样就解决了通用性的问题,具体如何做呢?我们可以利用EXE文件参数的方法来实现,也就是给程序添加运行参数。
功能描述:通过参数和配置文件的形式,实现文件更新,采用HTTP协议,可方便的集成到软件中或用于文件升级。
缺点描述:配置时需要手工设置,不是断点续传,单线程。
特别说明:程序会自动开户下载任务,没有设置自动关闭的,可在下载后手工再点更新键下载一次。
参数描述:LiveUpdate 配置文件地址 是否自动关闭 需要执行的文件(只支持一个文件)
具体例子:
自动下载、关闭、启动|LiveUpdate http://localhost/outcall/liveupdate.ini 1 c:\smallarmy.exe
自动下载、关闭|LiveUpdate http://localhost/outcall/liveupdate.ini 1
自动下载|LiveUpdate http://localhost/outcall/liveupdate.ini
[Update] ;需要更新的文件数 后面需要配套 Count=2 ;文件名 File1=picturepartner.rar ;下载地址 FileUrl1=http://esin.onlinedown.net/down/picturepartner.rar ;覆盖路径 FilePath1=$AppPath ;文件版本号 FileVer1=88.88.88.8888 ;更新时间,升级以这个为准,时间由您设置,升级后会强制更新文件的时间为以下时间 FileTime1=2010-10-21 12:12:33 File2=Smallarmy.exe FileUrl2=http://sq.onlinedown.net/down/Smallarmy.exe FilePath2=$AppPath FileVer2=1.0.0.0 FileTime2=2010-10-11 12:12:33
LiveUpdate开发过程:
首先找一些图片和图标资源,主要是与下载有关的即可;然后在Delphi中新建一个工程,在窗口上添加一个ListView用来显示下载项,两个ProgressBar用来显示总进度和下载项进度;由于考虑的是http方式,这个方式也比较方便,只需找个web空间就行,所以,在Delphi中,我们采用自带的indy组件包中的idHttp组件;在窗口中添加IdHttp和IdAntiFreeze各一个;IdHttp组件,用到两个事项,分别是WorkBegin和Work,主要是处理进程条的显示,代码如下:
procedure TfrmMain.IdHTTP1Work(ASender: TObject; AWorkMode: TWorkMode; AWorkCount: Int64); begin ProgressBar1.Position := ProgressBar1.Position + AWorkCount; Application.ProcessMessages; end; procedure TfrmMain.IdHTTP1WorkBegin(ASender: TObject; AWorkMode: TWorkMode; AWorkCountMax: Int64); begin ProgressBar1.Max := AWorkCountMax; ProgressBar1.Min :=0; ProgressBar1.Position :=0; end;
在listview上,我们添加几列内容,分别是图标列、文件、版本、日期、地址、路径,分别调整下宽度,后面地址和路径两列宽度为0,进行隐藏,这是用来保存下载项的放置目标地址和下载地址的;接下去我贴上主要的处理代码:
格式化文件修改日期的函数:
type TFileTimeType = (fttCreation, fttLastAccess, fttLastWrite); function SetFileDateTime(const FileName: string; FileTimeType: TFileTimeType; DateTime: TDateTime): integer; var Handle: THandle; LocalFileTime, FileTime: TFileTime; DosDateTime: integer; I: TFileTimeType; FileTimes: array [TFileTimeType] of Pointer; begin Result := 0; DosDateTime := DateTimeToFileDate(DateTime); Handle := FileOpen(FileName, fmOpenWrite or fmShareDenyNone); if Handle <> INVALID_HANDLE_VALUE then try for I := fttCreation to fttLastWrite do FileTimes[I] := nil; DosDateTimeToFileTime(LongRec(DosDateTime).Hi, LongRec(DosDateTime).Lo, LocalFileTime); LocalFileTimeToFileTime(LocalFileTime, FileTime); FileTimes[FileTimeType] := @FileTime; if SetFileTime(Handle, FileTimes[fttCreation], FileTimes[fttLastAccess], FileTimes[fttLastWrite]) then Exit; finally FileClose(Handle); end; Result := GetLastError; end;
下载处理代码:
procedure TfrmMain.BitBtn1Click(Sender: TObject); var vStream: TMemoryStream; vIni: TIniFile; vSF, vS: string; I, vI: integer; vList: TListItem; vDown: Boolean; begin BitBtn1.Enabled := False; IdAntiFreeze1.OnlyWhenIdle := False; // 设置使程序有反应. vSF := ParamStr(1); // 第一个参数必须是配置文件的地址 vStream := TMemoryStream.Create; try try IdHTTP1.Get(vSF, vStream); // 取升级配置,由参数传进 except Application.MessageBox('网络错误!无法获取升级配置文件。', '', MB_OK + MB_ICONSTOP); Exit; end; vSF := ExtractFilePath(ParamStr(0)) + 'LiveUpdate.ini'; vStream.SaveToFile(vSF); vIni := TIniFile.Create(vSF); try vI := vIni.ReadInteger('Update', 'Count', 0); if vI = 0 then begin Application.MessageBox('没有可升级的文件!', '', MB_OK + MB_ICONINFORMATION); Application.Terminate; Exit; end; ListView1.Clear; for I := 1 to vI do begin vList := ListView1.Items.Add; vList.ImageIndex := 0; vList.SubItems.Add(vIni.ReadString('Update', 'File' + inttostr(I), '')); vList.SubItems.Add( vIni.ReadString('Update', 'FileVer' + inttostr(I), '')); vList.SubItems.Add( vIni.ReadString('Update', 'FileTime' + inttostr(I), '')); vList.SubItems.Add( vIni.ReadString('Update', 'FileUrl' + inttostr(I), '')); vS := vIni.ReadString('Update', 'FilePath' + inttostr(I), ''); if StrUtils.ContainsText(vS, '$AppPath') then vS := StringReplace(vS, '$AppPath', ExtractFilePath(ParamStr(0)), [rfReplaceAll]); // 其实,用相对路径的方式也能实现一样的效果 vList.SubItems.Add(vS); end; ProgressBar1.Position := 0; ProgressBar2.Position := 0; ProgressBar2.Max := ListView1.Items.Count; for I := 0 to ListView1.Items.Count - 1 do begin vStream.Clear; try vS := ListView1.Items.Item[I].SubItems.Strings[4] + ListView1.Items.Item[I].SubItems.Strings[0]; if fileexists(vS) then // 如果文件存在,则进行校验 begin // 只比较到秒的十位,秒的个位不比较 vDown := Copy(FormatDateTime('yyyy-MM-dd hh:mm:ss', FileDateToDateTime(FileAge(vS))), 1, 18) <> Copy(ListView1.Items.Item[I].SubItems.Strings[2], 1, 18); end else vDown := True; if vDown then begin ListView1.Items.Item[I].ImageIndex := 3; IdHTTP1.Get(ListView1.Items.Item[I].SubItems.Strings[3], vStream); ListView1.Items.Item[I].ImageIndex := 4; Application.ProcessMessages; vStream.SaveToFile(vS); SetFileDateTime(vS, fttLastWrite, StrToDateTime(ListView1.Items.Item[I].SubItems.Strings[2])); end; ListView1.Items.Item[I].ImageIndex := 1; except // INDY控件一般要使用这种try..except结构. ListView1.Items.Item[I].ImageIndex := 2; end; ProgressBar2.Position := I + 1; vEnd := True; end; finally FreeAndNil(vIni); end; finally vStream.Free; BitBtn1.Enabled := True; DeleteFile(vSF); end; end;
其他关于界面美化什么的,不再描述。完成后,就可以直接在各个项目中引用它,而无需每个项目都去开发升级程序或模块了。