我设想的程序自升级方法,有没有更好的办法?参与探讨有分!

BCBPLC 2009-06-05 03:01:34
用BCB C/S 方式的系统,有几个客户,程序升级太烦,如果能自行升级,再好不过,我设想的办法:

我的客户程序都是绿色.exe,远程数据库的表中有一BLOB字段,用来存放升级程序exe,有一字段放版本号;

1>自动上传: 当前.exe程序版本号<一全局整数变量>高于库中的程序版本字段时,可以自动上传自已到BLOB字段中;
2>客户升级: 1>运行时,若当前exe版本低于库字段中的版本号时,准备升级;
2》将BLOB字段的程序下载到 当前目录中,新文件名: ~原名.exe
3> 运行 ~原名.exe, 自已退出;
4> ~原名运行后 时,发现自已是升级程序(名前有~)时,
将自已复制到 原名.exe,直到成功为至(.exe退出会占时间);
执行 原名.exe,退出 ~原名.exe
升级结束



...全文
116 16 打赏 收藏 转发到动态 举报
写回复
用AI写文章
16 条回复
切换为时间正序
请发表友善的回复…
发表回复
MrDotWalker 2011-12-19
  • 打赏
  • 举报
回复
感谢YangHua。。。
BCBPLC 2009-06-08
  • 打赏
  • 举报
回复
to castlooo:
共享目录的方法 不好,因为能上载的话,服务器 共享目录的权限必须能写,这非常不安全;
现在的共享目录 要有用户名密码,这样下载也不方便;共享目录还有一个毛病,多个用户访问后,
变得不能访问,所以,决定放弃共享目录的做法。
所以我用远程数据库的记录中做共享;
上面的程序全是自动的,感觉非常方便!

BCBPLC 2009-06-08
  • 打赏
  • 举报
回复

//------------------------------------------------------.
// C/S EXE自动上传/下载类 v1.0 .
// AutoUpgrade.h .
// By YangHua (C) 2009.6.6 .
// QQ:17461273 .
//------------------------------------------------------.

#ifndef AutoUpgrade_CLASS
#define AutoUpgrade_CLASS

typedef
class TVerUpgrade
{
int ServerVer; // 服务器EXE版本号
public:
TADODataSet * ADODataSet; // 数据集
bool Connected; // 是否成功连接
bool Connect(); // 连接数据库
void Disconnect(); // 关闭数据库
//-------------------------------------------------------
String BlobField; // 存放EXE大字段名
String VerField; // 版本号的字段名
String IsUpdatingField; // 正在更新的标志字段名
//-------------------------------------------------------
int ClientVer; // 客户EXE版本号
int GetVer(); // 取服务器EXE版本
bool BlobIsNew(); // 库中EXE是否新版
bool BlobIsOld(); // 库中EXE是否旧版
bool Upload(); // EXE上传到库中
bool Download(); // EXE下传且更新
int Timeout; // 检查的时间ms
static bool AfterDownload(); // 下载后的处理
TVerUpgrade();
} TVerUpgrade;
//--------------------------------------------------------------------------
TVerUpgrade::TVerUpgrade()
{ // 缺省参数值
BlobField="BLOB"; // 字段名BLOB :image
VerField="Version"; // 字段名Version :int
IsUpdatingField="IsUpdating"; // 字段名IsUpdating:boolean
Timeout=10000; // 10 s
Connected=false;
}
bool TVerUpgrade::Connect() // 打开远程表
{
try
{
ADODataSet->Open();
}
catch(...)
{
}
Connected=ADODataSet->Active;
return(Connected);
}
void TVerUpgrade::Disconnect() // 关闭远程表
{
ADODataSet->Close();
Connected=false;
}
int TVerUpgrade::GetVer() // 取库中版本号的
{
if (!Connected)
Connect();
ServerVer=-1; // -1 表不存在
if (Connected)
if (ADODataSet->RecordCount>0)
ServerVer=ADODataSet->FieldByName(VerField)->AsInteger;
return(ServerVer);
}
bool TVerUpgrade::BlobIsNew() // 服务器中是否新版本 ?
{
GetVer();
return(ServerVer>ClientVer);
}
bool TVerUpgrade::BlobIsOld() // 服务器中是否旧版本 ?
{
GetVer();
return(ServerVer<ClientVer && ServerVer>=0 );
}
bool TVerUpgrade::Upload() // 新版上传到服务器
{
bool yn=false;
if (BlobIsOld())
if (Connected)
{
if (ADODataSet->RecordCount==0)
ADODataSet->Append();
else
ADODataSet->Edit();
ADODataSet->FieldByName(IsUpdatingField)->AsBoolean=true;
ADODataSet->Post();
ADODataSet->Edit();
TBlobField *zd=(TBlobField *)ADODataSet->FieldByName(BlobField);
String ExeName=Application->ExeName;
String f1=ExeName+".~";
CopyFile(ExeName.c_str(),f1.c_str(),false); // win2k非得借中间程序
zd->LoadFromFile(f1);
ADODataSet->FieldByName(VerField)->AsInteger=ClientVer;
ADODataSet->FieldByName(IsUpdatingField)->AsBoolean=false;
ADODataSet->Post();
DeleteFile(f1);
Disconnect();
yn=true;
}
return(yn);
}
bool BlobToFile(TField *zd,String f) // BLOB字段送文件中
{ // BLOB字段到文件f
TStream *bs=zd->DataSet->CreateBlobStream(zd,bmRead);
int blob_size=bs->Size;
if (blob_size>1024)
{
TFileStream *fs=new TFileStream(f,fmCreate);
bs->Position=0;
fs->CopyFrom(bs,0);
delete fs;
}
delete bs;
return(blob_size>0);
}
bool TVerUpgrade::Download() // 下载新版本
{ // 返true时,用户还必须自行主动退出
String ExeName=Application->ExeName;
String CurDir=ExtractFilePath(ExeName);
String TmpFile=CurDir+"~"+ExtractFileName(ExeName);
bool yn=false;
if (BlobIsNew())
if (Connected)
{
bool IsUpdating=
ADODataSet->FieldByName(IsUpdatingField)->AsBoolean;
if ( !IsUpdating)
{ // 升级下载
if (FileExists(TmpFile))
DeleteFile(TmpFile);
TField *zd=ADODataSet->FieldByName(BlobField);
yn=BlobToFile(zd,TmpFile);
}
}
if (yn)
{
yn=FileExists(TmpFile);
if (yn)
{ // 中间程序安排带了三个参数
String RunPara=" \""+ExeName+"\" "
+String(Timeout)+" "+String(ClientVer);
ShellExecute(0,0,TmpFile.c_str(),
RunPara.c_str(),0,SW_HIDE);
}
}
Disconnect();
return(yn);
}
bool TVerUpgrade::AfterDownload() // 下载之后的后处理
{
String Exe_Name=ExtractFileName(Application->ExeName);
bool yn=(Exe_Name[1]=='~');
if (yn && ParamCount()==3) // 是否带三个参数
{
String ExeProg=ParamStr(1); // 目的程序
unsigned int timeout=ParamStr(2).ToIntDef(10000); // 10秒超时
int result;
unsigned tk0=GetTickCount();
do
{
Application->ProcessMessages();
result=CopyFile(Application->ExeName.c_str(),
ExeProg.c_str(),false);
// 超时退出,防止死循环
} while (result==0 && GetTickCount()-tk0<timeout);
ShellExecute(0,0,ExeProg.c_str(),0,0,SW_SHOW); // 升级后的主程序
}
if (yn)
{
Application->ShowMainForm=false; // 主窗口不显示
Application->Terminate(); // 中间程序完成任务后自动退出
}
return(yn);
}
//----------------------------------------------------------
#endif
BCBPLC 2009-06-08
  • 打赏
  • 举报
回复
/*
用BCB编的客户程序自动升级的方法

我们做程序的方式一是C/S另一是B/S,B/S的程序做在服务端的,客户端是IE,
涉及不到客户端程度升级,我们有时更喜欢C/S,EXE编程方便,各种功能都可以做
得更好,运行速度快。在一个局域网里,客户端有不少,都在运行你的相同客户程
序,为这些客户程序更换新版本,却很麻烦,一直很想有一个较简单的、标准的升
级功能块,这次我做了尝试,自感很满意,现把完整源码贴出,希望有兴趣的,试
一试,提一点新的建议。
我做的C/S程序或C/S的工控程序都爱做成绿色的EXE,无论是485/232串行通迅
的MSCOMM32.OCX,还是用到本地的MS ACCESS,把这些附加文件以资源方式自动包含在
EXE里,再在EXE程序新建的目录中释放出资源文件或.OCX的自注册, 所以,就一个EXE
自升级还是很方便的。WINDOWS不允许更新正在运行的.EXE,只好借助于中间文件,例
如 TEST.EXE,中间文件名就在前面加个前缀为~TEST.EXE。上传的EXE放在SqlServer
远程数据库中,所以,要手工建入数据库表(例EXE表),表中有三个字段:
IsUpdating 类型boolean,表示是否正在更新,
Version类型int,表示数据库中EXE的版本号,
Blob类型Image,存放EXE代码,
当然字段名可以不用缺省的,但这样调用时须将三个字段名称传到TVerUpgrade的三个
属性中。你要自行在Form中或DataModule中放上ADOConnection和ADODataSet,与远程
数据库的表建入正确联接,只要把ADODataSet的指针传入TVerUpgrade::ADODataSet
就可以了。
为了便于调用,对这个功能块封装成一个类TVerUpgrade,放在一个单独的文件
AutoUpgrade.h中,用#include 嵌入到主程序中就可简单调用。你要在你的EXE程序中,
用一个全局变量例如int Version表示客户的版本号,调用类时,需传入TVerUpgrade
的属性ClientVer中,每当你的程序做了大的改进,你只要改一下放版本号的全局变量
值就可以了,你的程序就能用“上传”功能更新远程库中的内容,各个客户再自动下
载更新。

#include "AutoUpgrade.h"
int Version=10; // 表示客户端是 1.0版

void __fastcall TForm1::UploadClick(TObject *Sender)
{ // 上载按钮
TVerUpgrade *obj=new TVerUpgrade();
obj->ADODataSet=ADODataSet1;
obj->ClientVer=Version; // 客户端版本号
obj->Upload();
obj->Disconnect();
delete obj;
}
这是上传调用的按钮,你的Version值要高于远程库的版本号才会上传.当然,你可
以不用按钮,就放在菜单项中,但菜单项的虚实是要控制的。

void __fastcall TForm1::DownloadClick(TObject *Sender)
{ // 下载按钮
TVerUpgrade *obj=new TVerUpgrade();
obj->ADODataSet=ADODataSet1;
obj->ClientVer=Version; // 客户端版本号
bool yn=obj->Download();
obj->Disconnect();
delete obj;
if (yn)
{
// 客户要做退出准备
Application->Terminate(); // 下载后一定要退出
}
}
上面是下载按钮,客户EXE程序当检测到远程库的版本比自已高时,便下载更新,
因为要涉及自动退出,你要在退出代码中填一些处理代码,因为每人编的程序不一样,
关闭库释放资源等必不可少。当然,你不会用按钮,这段代码你可能会放在主窗口
事件FormCreate()或定时器事件中。

void __fastcall TForm1::FormCreate(TObject *Sender) // 下载后的后处理
{
this->Caption="版本 "+
String(Version/10)+"."+String(Version%10);

// 下载后的后处理
if (TVerUpgrade::AfterDownload()) // 静态调用
{
Application->ShowMainForm=false; // 主窗口不显示
Application->Terminate(); // 中间程序完成任务后自动退出
}
}
这是在主窗口的OnCreate()事件中放的下载“后处理”的调用代码,因
为它是static静态的,不需实例就可直接调用。它的用处是让中间程序更新主EXE,
并重新启动它,一旦完事就必须立即隐藏地退出。
当两端版本有差异时才允许“上传”或“下载”,所以在EXE中,必须控
制按钮或菜单项的虚实,表示有无新版本,这段检查代码可根据情况放到启动
事件FormCreate()或定时器中或弹出菜单项的上一级Click()事件中,

void __fastcall TForm1::Timer1Timer(TObject *Sender) // 定时器
{ // 按钮是否允许 上传/下载
TVerUpgrade *obj=new TVerUpgrade();
obj->ADODataSet=ADODataSet1;
obj->ClientVer=Version; // 客户端版本号
Upload->Enabled=obj->BlobIsOld(); // 允许上传
Download->Enabled=obj->BlobIsNew(); // 允许下载
delete obj;
}
用了这四段调用,你的程序就有了自动更新版本的功能,你改进了EXE程序的功能
后,把源码全局变量的版本号加一个值,激活了上传功能后,各个客户端如在运行,就
可设法自动升级了,当然,上传前要确保EXE程序没问题,例如,须带运行包的程序上载
后就出问题了。不妨试一试,不收你的版权费,如果你要转载,要注明出处,勿改动
题头内容。

*/
hemiya 2009-06-06
  • 打赏
  • 举报
回复
[Quote=引用 3 楼 Ring_Pt 的回复:]
其实这个可以用ini文件控制就可以了
ini文件里写当前的版本好和当前版本与前一个版本比较更新了那些文件,形式大致如下

[Version]
version=1.0
[Files]
0=XXX.dll
1=XXX.txt

客户端在运行的先从服务器下载ini文件并取得里面的版本号,再从本地获得本地的版本号,如果服务器上的版本号是本地版本号的升级,则将File中的文件下载到本地



[/Quote]
最近做了个,思路和这个差不多.
castlooo 2009-06-06
  • 打赏
  • 举报
回复
我也是用ini 文件设置版本,放在网络的共享文件夹,里面放存放升级的文件所在的共享目录,比较版本只需要比较本地的ini和服务器共享文件夹的ini中的版本项,如果小,就直接从共享文件夹下Copy覆盖到本地目录就行了
efeeler1925 2009-06-06
  • 打赏
  • 举报
回复
你可以建立一个SQL Server,制成一个软件版本表,让你用户的软件登录此Server查询最新软件版本,若未更新,就提醒用户进行更新。

此表可以是以下列:

公司,软件名,版本号,发行日期,URL链接
fairchild811 2009-06-05
  • 打赏
  • 举报
回复
http://topic.csdn.net/t/20031208/15/2540426.html //cbuilder

http://topic.csdn.net/t/20051103/20/4370176.html //delphi

http://topic.csdn.net/t/20040405/18/2928482.html //cbuilder

TMSpack好像里面包含升级组件

http://www.tmssoftware.com/site/products.asp 下载组件

http://d.download.csdn.net/down/917120/harkmonkey 本站有help下载
jaffy 2009-06-05
  • 打赏
  • 举报
回复
我写了一个,现在在部门内部的应用使用,感觉还可以,不妨去我的资源看看
BCBPLC 2009-06-05
  • 打赏
  • 举报
回复
过段时间我把整段代码写成通用子程序,让人很简单就能实现自动上传/自动下载!
BCBPLC 2009-06-05
  • 打赏
  • 举报
回复
不用.bat吧, 这批处理是过时的,直接用BCB语句吧.
ccrun.com 2009-06-05
  • 打赏
  • 举报
回复
判断是否有新版本文件--》下载新版本文件另存为临时文件--》启动另一个进程进行更新(替换主执行文件)

可以是写一个临时bat文件来进行文件的替换。
Jim@luckeeinc.com 2009-06-05
  • 打赏
  • 举报
回复
其实这个可以用ini文件控制就可以了
ini文件里写当前的版本好和当前版本与前一个版本比较更新了那些文件,形式大致如下

[Version]
version=1.0
[Files]
0=XXX.dll
1=XXX.txt

客户端在运行的先从服务器下载ini文件并取得里面的版本号,再从本地获得本地的版本号,如果服务器上的版本号是本地版本号的升级,则将File中的文件下载到本地


我不懂电脑 2009-06-05
  • 打赏
  • 举报
回复
可以这样做。
BCBPLC 2009-06-05
  • 打赏
  • 举报
回复
我要将它做成标准子程序
以便 经常调用

13,825

社区成员

发帖
与我相关
我的任务
社区描述
C++ Builder相关内容讨论区
社区管理员
  • 基础类社区
加入社区
  • 近7日
  • 近30日
  • 至今
社区公告
暂无公告

试试用AI创作助手写篇文章吧