如何在BatchMove执行期间,监视转换记录个数?在线等

kingsjack 2003-09-14 11:28:18
如何在BatchMove执行期间,监视转换记录个数?
因为转换数据很多,没有显示进度状态非常不方便,想取出已经转换或没有转换的记录个数,加以显示,但不知道如何实现!

请大虾帮忙!
...全文
38 2 打赏 收藏 转发到动态 举报
写回复
用AI写文章
2 条回复
切换为时间正序
请发表友善的回复…
发表回复
空中猎手 2003-09-16
  • 打赏
  • 举报
回复
下面这些是我在网上找到的,不知道对你有没有用,反正我是看不懂的~~~~~~

在实际的MIS系统中,远程数据库访问大多通过Modem连接,出于通信费用及速度方面的考虑,往往采用先将数据保存在本地,然后集中传送到远端的办法。远程数据传送可以有多种方案,最常见的是先将要传送的数据打包成文件,在利用文件传输形式传送到目的地,在目的地对数据恢复后添加到本地数据库中。这种方法普遍地应用于证券交易系统,其优点是速度快,并且可事先对数据压缩,更大限度地节约传送时间及费用。但这种方案也有其不足之处:由于利用文件传输机制,无法利用数据库本身的特性如完整性约束、数据一致性、回滚机制等,因此在比较复杂的数据库系统中较少采用。另一种方法是直接将两端处理成"客户/服务器"模式,将数据传送看成是向Server提交数据。由于这种方案充分利用了数据库服务器的特性,并且实际操作基本与局域网方式一致,因此本文将详细介绍这种方案。
由于传输速度的原因,当传送大量数据时绝对不赞成逐条记录地向服务器提交数据,而应批量地向Server提交,Delphi/CBuilder中提供了一个TBatchMove控件专门用于批量传送数据,利用它可极大减少网络负担,提高传送速度。遗憾的是,TBatchMove控件只提供了简单的错误控制功能,没有提供显示传送进度、用户终止传送等重要功能。然而TBatchMove所依赖的BDE却提供了一种"回调机制"可以完成上述两个功能。所谓"回调"过程是这样的:当BDE执行某种操作时,比如从一张表向另一张表拷贝大量数据的过程中,每过一段时间(如需要显示拷贝进度时),BDE会调用一段你自己写的函数(回调函数),以帮助你更完全地控制程序。这种做法有点想DLPHI中的Event(事件)及事件处理函数--某个具体的操作动作会让VCL触发某个事件,从而调用一段你写好的事件处理函数,不同的事件会触发不同的处理函数。
为了让BDE能正确地与你的函数协同工作,你必须事先"注册"你的函数,让BDE知道某个事件发生时应调用(回调)你的某段代码。BDE提供了一个DbiRegisterCallBack注册函数,不幸的是,BDE的联机帮助中的说明不能适合于Delphi/CBuilder,按照该说明编写的程序根本不能通过编译!笔者通过实践找到了正确使用BDE回调函数的方法,下面将详细介绍该机制的使用。BDE回调机制包含以下几个步骤:
1)按BDE的预定格式编写你的回调函数
2)调用DbiRegisterCallBack函数注册你的回调函数,这样当你执行相关数据库操作时就自然地触发你的回调函数。
3)执行相关数据库操作,比如BatchMove1->Exectue();
4)注销该回调函数
其中最关键的是正确注册你的回调函数,因此先介绍第二步。(注册与注销都调用同一函数,只是最后一个参数略有不同)
首先你应知道在哪类"事件"发生时调用你的回调函数,其次你应明白与该事件相关的参数及数据结构-----这一切都发生在调用DbiRegisterCallBack函数注册时,所以下面先介绍DbiRegisterCallBack的正确用法及说明:
在原BDE帮助中该函数的原形(C)是这样的
DBIResultDBIFNDbiRegisterCallBack(hCursor,ecbType,iClientData,iCbBufLen,pCbBuf,pfCb);
要使用该函数必须include头文件,问题是Delphi/CBuilder中根本没有提供该文件,取而代之的是"BDE.HPP",但是在包含进该文件后程序仍然不能编译通过,因为该文件中没有DBIFN等的说明。一个简单的方法是在代码中去掉DBIFN。函数中各参数解释如下:hCursor是一个BDE中对象的句柄,如果这个参数为NULL,则表示注册的回调函数适合于所有BDE任务;第二个参数ecbType是指回调函数的触发条件的类别,有很多种类型可以选择,其中cbGENPROGRESS表示当需要显示一个长操作的进度时触发这个回调函数;第三个参数iClientData是传递给回调函数的某个数据结构的指针,在我们的例子中为NULL;第四个参数iCbBufLen是指回调Buffer的大小,该大小随第二个参数的不同而不同,比如sizeof(CBPROGRESSDesc);第五个参数pCbBuf是回调Buffer的指针,该指针类型随第二个参数变化,比如cbGENPROGRESS的数据结构是CBPROGRESSDesc;最后一个参数是回调函数的地址指针,当该参数为NULL时表示注销该类型的回调函数。关于回调函数将在稍后详细介绍。下面是注册执行长操作时显示进度的回调函数的格式:
intrst=DbiRegisterCallBack(
NULL,//适合于任何进程
cbGENPROGRESS,//回调类型:显示长操作的进度
NULL,//没有数据
sizeof(CBPROGRESSDesc),//数据结构的大小
&aCBBuf,//数据的内存地址
ApiCallBackFun//回调函数的地址
);
接下来就应该完成第一步:编写回调函数
在C中,回调函数应如下声明:
CBRType__stdcallApiCallBackFun(
CBTypeecbType,//回调类型
intiClientData,//回调数据(指针)
void*pCbInfo//回调数据结构指针
)
第一个参数是回调类型;第二个参数是回调数据,其解释同DbiRegisterCallBack的第三个参数;第三个是回调数据的指针,该数据的结构随回调类型的不同而不同。比如进度指示cbGENPROGRESS的数据结构是CBPROGRESSDesc,其定义如下:
structCBPROGRESSDesc{
shortiPercentDone;//进度的百分比
charszMsg[128];//进度的文本信息
};
该结构的两个域同时只有一个起作用,第一个表示操作的进度百分比,当其为-1时表示第二个域起作用。第二个域用字符串表示进度信息,其格式为<String><:><Value>,比如:RecordsCopied:125
本文主要在回调函数中完成两个工作:
1)显示数据拷贝(BatchMove)进度
2)提供让用户终止长时间拷贝的机制
显示拷贝进度的代码如下:
CBRType__stdcallApiCallBackFun(
CBTypeecbType, //Callbacktype
Int iClientData, //Clientcallbackdata
void*pCbInfo //Callbackinfo/Client
) {
AnsiString str;
if(ecbType==cbGENPROGRESS){
intj=StrToInt(((CBPROGRESSDesc*)pCbInfo)->iPercentDone);
if(j<0) { //如果iPercentDone为-1,则分析szMsg的信息
str=((CBPROGRESSDesc*)pCbInfo)->szMsg;intpos=str.AnsiPos(":")+1; //提取出拷贝的记录数
//下面的代码用来在一个Form中显示拷贝进度及拷贝数量Form1->Label2->Caption=str.SubString(pos,100);
Form1->Label2->Update();
Form1->ProgressBar1->Position=int((str.SubString(pos,100).ToDouble()/Form1->TransNum)*100);
Form1->ProgressBar1->Update();
}else{
Form1->ProgressBar1->Position=j;Form1->ProgressBar1->Update();
}
return cbrCONTINUE;
//必须返回cbrCONTINUE以便让BatchMove继续,若返回cbrABORT则终止拷贝
}
}
一切完成以后,每当调用长时间BDE操作(比如BatchMove1->Exectue())时都会触发该回调函数,注意在不需要时应"注销"这个回调函数。
如果批量传送数据时间很长,则必须为用户提供终止该操作的机会,前面提到,若回调函数返回cbrABORT,则BatchMove过程立即终止。可以在Form上加上一个"停止"按钮和一个全局布尔变量isContinue,当开始拷贝时设该变量为true,当按钮按下后,设该变量为false,每次调用回调函数时检查isContinue的值,若为true则回调函数返回cbrCONTINUE让拷贝继续,否则返回cbrABORT终止拷贝。但是问题在于一旦拷贝过程开始,该进程内所有消息将被阻塞,应用程序在拷贝结束之前没有机会响应键盘、鼠标等一切消息,连屏幕刷新都不能完成,因此必须找到一种避免消息阻塞的方法。
大家知道,Windows是靠事件(消息)驱动的,在WIN32系统中有两种消息队列:系统队列和应用程序队列,当一个程序进行一个长时间操作时,系统分配给该程序的时间片将完全用于处理该操作,换句话说,应用程序没有从它的应用程序队列中取出消息并处理的机会,这样该程序将停止一切对外部事件的响应直到该操作完成为止。具体到本文中就是程序必须等到BatchMove1->Execute()执行完毕后才能响应用户操作,因此用户将完全没有机会终止拷贝过程。
解决的办法是:在回调函数中取出消息队列中的消息,并后台处理它们,这样用户将有机会按下终止按钮。实现的代码很简单,在回调函数中最后加入以下代码即可
CBRType__stdcallApiCallBackFun(…){
……MSGamsg;
while(PeekMessage(&amsg,NULL,0,0,PM_REMOVE)) {//从队列中取消息
TranslateMessage(&amsg);//翻译消息
DispatchMessage(&amsg);//分发消息
}
if(isContinue)
return cbrCONTINUE;
else
returncbrABORT;
}

软件封面的实现

1.先设置两个窗体Form1 Form2其中Form1为主窗体,Form2为动态调用窗体
在工程文件Project1.cpp中加入如下代码:
#include <vcl.h>
#include "Unit2.h"
#pragma hdrstop

USERES("Project1.res");
USEFORM("Unit1.cpp", Form1);
USEFORM("Unit2.cpp", Form2);
//---------------------------------------------------------------------------
WINAPI WinMain(HINSTANCE, HINSTANCE, LPSTR, int)
{
try{
Application->Initialize();
Form2 = new TForm2(Application);
Form2->Show();
Form2->Update();
Application->CreateForm(__classid(TForm1), &Form1);
delete Form2;
Application->Run();
}
catch (Exception &exception){
Application->ShowException(&exception);
}
return 0;
}
2.然后在主窗体unit1.cpp中加入下面代码:
void __fastcall TForm1::FormCreate(TObject *Sender)
{
unsigned long CurTime;
unsigned long DelayTime;
// 设置延迟时间为5秒
DelayTime = 5000;
CurTime = GetTickCount();
// 在这儿可以加上应用程序的初始化过程
unsigned long Inter;
do{
Inter = GetTickCount()-CurTime;
Form2->ProgressBar1->Position = Inter*100.0/DelayTime;
} while( Inter<DelayTime );
}
kingsjack 2003-09-15
  • 打赏
  • 举报
回复
没有人会吗?

1,178

社区成员

发帖
与我相关
我的任务
社区描述
C++ Builder 数据库及相关技术
社区管理员
  • 数据库及相关技术社区
加入社区
  • 近7日
  • 近30日
  • 至今
社区公告
暂无公告

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