在写C/S结构程序时往往会出现一些升级,因而又要重新编译程序生成新的EXE,但是这样去覆盖每台机器上之前的EXE文件确实太麻烦。我知道有一种方法可以让软件从数据库里自动加载最近EXE,但不知道怎么写,或者另有其他方法解决此问题也可以。最好是之前这种方法。代码和步骤尽量详细一点,解决后另开贴送200分。谢谢。来说废话的就不要说了。
解决方案 »
- 菜鸟提问,什么情况下用access,什么时候用sql
- 怎样给dbcontrolgrid选中的panel划个边框?
- 存储过程的变量问题
- DLL中的一个线程怎么和客户程序共享一段数据空间?
- 郁闷呀 DELPHI 程序员 饱和
- 我剛接觸ADO,請問用哪種CONNECTIONSTRING好?
- 新手在看例子时遇到不明白,请各位指教
- vcl内部消息
- 如何实现动态的增加集合内的元素!!!
- 请问大伙,我把字符串放在qa.res中了,如何调用呢呢?????????急急。例子??
- C/S结构的程序安装在服务器上,各个客户端建立指向这个程序的快捷方式运行程序进行工作,这种方式行得通吗?
- 为什么我在设置 auAutoUpgrader 自动升级,打开软件后。是可以升级了。但升级了一次又
qq则采用一个独立的后来进程做这些操作(txplatform.exe)但更新也是独立的一个exe.判断版本的话也是从web的inf或相关配置文件来检查对比本地版本.
有个东西你可以使用或参考.AutoUpgraderPro
我是这么做的:
建表,有三个字段,文件名、版本号、文件内容。版本可以用时间,即更新的时间,文件内容用Blob字段。
做一个UpLoad程序,维护这个表,就是增删改,软件维护者将DLL、EXE等要升级的文件信息写到这个表里
Update升级程序,先检测注册表里是否有新版本,有就从那个表里读取Blob字段,下载覆盖掉需要升级的文件,完了之后写注册表,更新版本号
项目的执行程序,启动时检测注册表,有新版本就调用Update程序,关闭自身。这样只要连上数据库就可以升级,速度是很快的。
注册表操作使用TRegistry类,常用方法OpenKey、ReadString、WriteString等 实际上版本号也不一定要放在注册表啊,用配置文件也是可以的
INI文件使用TIniFile进行操作,常用方法也有ReadXXX,WriteXXX方法
我们的做法和“老之”的类似,有一个专门的升级程序,启动客户端时先启动升级程序,从服务器端数据库中进行检索,生成一个XML,然后下载XML,分析,下载文件,更新数据库(如果需要的话),下载完成,启动客户端
1. 如果是局域网或不用考虑带宽的
简单办法:服务端开个共享目录每次将新程序放到那个目录里,自己程序中要增加一段比对版本号的代码,和一个升级下载的可执行文件。
客户端程序启动后,执行版本号检测代码,如果有新的版本,运行升级下载的那个可执行文件,关闭掉已经启动的客户端程序,下载到一个临时目录,下载正常完成后拷贝到执行目录。2. 如果网路带宽有限而且慢速,就要用http,tcp,udp了
用这种方法比较好,自己可以花时间写个通用的,以后copy就OK了
1.服务端要建立一个文件更新列表(包括可执行文件的版本和其他动态链接库等),服务端最好写读取列表的程序。
2.客户端程序跟前面一样需要两部分,一是客户端程序,二是升级程序方法跟前面一样。
3.无论你用http,还是tcp或udp都要先获得更新列表,跟现有文件或本地列表进行比较。
获取列表的方法很多,http比较简单些服务端可以不写代码,在IIS等web server 上建立一个虚拟目录,从上面下载就可以了;
TCP和UDP服务端需要写个读取列表的程序,自己可以定义一个格式。
剩下的就属于文件传输了。
网上有很多 http,tcp,udp文件传输的例子(注:udp要根据mtu的大小进行分包)
给个提示自动关闭该程序,同时shellexecute调用update.exe从服务器
下载新文件覆盖原来的.然后在shellexecute现在的系统.
http://www.2ccc.com/article.asp?articleid=4142
uses
IdHashMessageDigest, IdHash;var
md5: TIdHashMessageDigest5;
longWordRec: T4x4LongWordRecord;
fs: TFileStream;
begin
...
md5 := TIdHashMessageDigest5.Create;
fs := TFileStream.Create('C:\INSTALL.LOG', 0) ;
longWordRec := md5.HashValue(fs);
ShowMessage(md5.AsHex(longWordRec)); //该值可以存放到指定位置;
FreeAndNil(md5) ;
...
end;//=====================================================//
比较可以通过网络或者特定位置读取一个MD5值,若不同,则下载更新。
还是喜欢采用8楼的loader.exe的方式启动程序,维护简单。
//在客户端每次运行的时候就
//检测一下服务器的共享目录下的程序与当前的程序比较(比较程序的文件版本号),
//若发现比自己的版本高的话则自动下载并替换自身的程序。
//主程序运行的时候在 onShow事件调用以下过程procedure TMainForm._IsNewVersion;
var
UpDateFileVersion:string;
begin
//1。首先获取GetVersionString当前软件的文件版本号,
MainForm.MyCurVersion:=GetVersionString(Application.ExeName); if MainForm.IsAutoUpdate then //自动检测新版本
begin
//2。检测数据库是否设置了软件升级文件名,如果有,继续第3步
if fileexists(MainForm.UpdateFileName)=false then
begin
Application.MessageBox(#13+'软件自动升级失败! '+#13+#13+' 原因是无法访问服务器上所设置的升级程序['+MainForm.UpdateFileName+']'+#13+#13+'请确认该升级程序是否存在或者尝试您的电脑是否有权限访问该升级文件!');
exit;
end;
//3。检测软件升级文件的文件版本号,如果不一致,继续第4步
UpdateFileVersion:=GetVersionString(MainForm.UpdateFileName);
if f_CheckIsNewVersion(MyCurVersion,UpdateFileVersion) then
begin
//启动升级程序
if fileexists(ExtractFilePath(Application.ExeName)+'\UpdateProgram.exe')=false then
begin
showmessage('在当前软件的安装目录下找不到升级程序UpdateProgram.exe,请确认!');
exit;
end;
//调用另一个小程序用来下载最新版本的程序
WinExec(pchar(ExtractFilePath(Application.ExeName)+'\UpdateProgram.exe'),SW_SHOW);
//结束程序
application.Terminate;
end;
end;
end;
function GetVersionString(FileName: string): string;
var
VerInfoSize: DWORD;
VerInfo: Pointer;
VerValueSize: DWORD;
Dummy: DWORD;
VerValue: PVSFixedFileInfo;
begin
Result := '';
VerInfoSize := GetFileVersionInfoSize(PChar(FileName), Dummy);
if VerInfoSize = 0 then Exit; GetMem(VerInfo, VerInfoSize);
GetFileVersionInfo(PChar(FileName), 0, VerInfoSize, VerInfo);
VerQueryValue(VerInfo, '\', Pointer(VerValue), VerValueSize);
Result := IntToStr(VerValue^.dwFileVersionMS shr 16) + '.' +
IntToStr(VerValue^.dwFileVersionMS and $FFFF) + '.' +
IntToStr(VerValue^.dwFileVersionLS shr 16) + '.' +
IntToStr(VerValue^.dwFileVersionLS and $FFFF);
FreeMem(VerInfo);
end;{
比较两个版本,如果CurVersion<ServerVersion 则返回True ,否则返回False;
版本号的格式是:2008(年份).5(月份).31(日期).2(编译号)
}
function f_CheckIsNewVersion(CurVersion,ServerVersion:string):boolean;
var
i,j,IntTemp1,IntTemp2:integer;
strTemp1,strTemp2,Tmp1,Tmp2:string;
begin
Result:=false; Tmp1:=CurVersion;
Tmp2:=ServerVersion; Try
//比较年份
StrTemp1:=copy(Tmp1,1,4);
IntTemp1:=StrToInt(StrTemp1);
StrTemp2:=copy(Tmp2,1,4);
IntTemp2:=StrToInt(StrTemp2);
if IntTemp1<IntTemp2 then
result:=True
else
begin
if IntTemp1=IntTemp2 then
begin
//比较月份 版本号的格式是:2008(年份).5(月份).31(日期).2(编译号)
i:=pos('.',Tmp1);
Tmp1:=copy(Tmp1,i+1,10);
j:=pos('.',Tmp1);
StrTemp1:=copy(Tmp1,1,j-1);
IntTemp1:=StrToInt(StrTemp1);
i:=pos('.',Tmp2);
Tmp2:=copy(Tmp2,i+1,10);
j:=pos('.',Tmp2);
StrTemp2:=copy(Tmp2,1,j-1);
IntTemp2:=StrToInt(StrTemp2); if IntTemp1<IntTemp2 then
result:=True
else
begin //月份比较完毕
if IntTemp1=IntTemp2 then
begin
//开始比较日期 版本号的格式是: 5(月份).31(日期).2(编译号)
i:=pos('.',Tmp1);
Tmp1:=copy(Tmp1,i+1,10);
i:=pos('.',Tmp1);
StrTemp1:=copy(Tmp1,1,i-1);
IntTemp1:=StrToInt(StrTemp1);
i:=pos('.',Tmp2);
Tmp2:=copy(Tmp2,i+1,10);
i:=pos('.',Tmp2);
StrTemp2:=copy(Tmp2,1,i-1);
IntTemp2:=StrToInt(StrTemp2);
if IntTemp1<IntTemp2 then //比较日期
result:=True
else
begin
if IntTemp1=IntTemp2 then
begin
//开始比较编译号 版本号的格式是: 31(日期).2(编译号)
i:=pos('.',Tmp1);
Tmp1:=copy(Tmp1,i+1,10);
StrTemp1:=trim(Tmp1);
IntTemp1:=StrToInt(StrTemp1); i:=pos('.',Tmp2);
Tmp2:=copy(Tmp2,i+1,10);
StrTemp2:=trim(Tmp2);
IntTemp2:=StrToInt(StrTemp2); if IntTemp1<IntTemp2 then
Result:=true
else
Result:=false;
end
end
end
end
end
end;
Except
result:=false;
end;
end;
//程序 UpdateProgram.exe 里放一个Timer执行以下代码既可
//exeName 是主程序的文件名,如QQ的程序文件名为: "QQ.exe"
procedure TForm1.Timer1Timer(Sender: TObject);
begin
try
if fileexists(pchar(ExtractFilePath(application.ExeName)+'\'+ExeFileName)) then
deletefile(pchar(ExtractFilePath(application.ExeName)+'\'+ExeFileName)); copyfileto(pchar(UpdateFileName),pchar(ExtractFilePath(application.ExeName)+'\'+ExeFileName)); Timer1.Enabled:=false;
//复制完成后自启动主程序
WinExec(pchar(application.ExeName)+'\'+ExeFileName),SW_SHOW);
Application.Terminate;
except
Timer1.Enabled:=false;
showmessage('升级失败!');
end;
Timer1.Enabled:=false;
Application.Terminate;
end;