客户/处事器长途数据传输处理惩罚能力
副标题#e#
在实际的MIS系统中,长途数据库会见大多通过Modem毗连,出于通信用度及速度方面的思量,往往回收先将数据生存在当地,然后会合传送到远端的步伐。长途数据传送可以有多种方案,最常见的是先将要传送的数据打包成文件,在操作文件传输形式传送到目标地,在目标地对数据规复后添加到当地数据库中。这种要领普各处应用于证券生意业务系统,其利益是速度快,而且可事先对数据压缩,更大限度地节省传送时间及用度。但这种方案也有其不敷之处:由于操作文件传输机制,无法操作数据库自己的特性如完整性约束、数据一致性、回滚机制等,因此在较量巨大的数据库系统中较少回收。另一种要领是直接将两头处理惩罚成"客户/处事器"模式,将数据传送当作是向Server提交数据。由于这种方案充实操作了数据库处事器的特性,而且实际操纵根基与局域网方法一致,因此本文将具体先容这种方案。别的本文的部门内容是基于Delphi/CBuilder的。
由于传输速度的原因,当传送大量数据时绝对不赞成逐笔记录地向处事器提交数据,而应批量地向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)是这样的
DBIResult DBIFN DbiRegisterCallBack (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时暗示注销该范例的回调函数。关于回调函数将在稍后具体先容。下面是注册执行长操纵时显示进度的回调函数的名目:
int rst= DbiRegisterCallBack (NULL,
//适合于任何历程
cbGENPROGRESS, //回调范例:显示长操纵的进度
NULL, //没有数据
sizeof(CBPROGRESSDesc), //数据布局的巨细
&aCBBuf, //数据的内存地点
ApiCallBackFun //回调函数的地点
);
接下来就应该完成第一步:编写回调函数
在C中,回调函数应如下声明:
CBRType__stdcallApiCallBackFun(
CBTyp eecbType,//回调范例
int iClientData,//回调数据(指针)
void *pCbInfo//回调数据布局指针
)
#p#分页标题#e#
第一个参数是回调范例;第二个参数是回调数据,其表明同DbiRegisterCallBack的第三个参数;第三个是回调数据的指针,该数据的布局随回调范例的差异而差异。好比进度指示cbGENPROGRESS的数据布局是CBPROGRESSDesc,其界说如下:
struct CBPROGRESSDesc {
short iPercentDone; //进度的百分比
char szMsg[128]; //进度的文本信息
};
该布局的两个域同时只有一个起浸染,第一个暗示操纵的进度百分比,当其为-1时暗示第二个域起浸染。第二个域用字符串暗示进度信息,其名目为<String><:><Value>,好比:RecordsCopied:125
#p#副标题#e#
本文主要在回调函数中完成两个事情:
1)显示数据拷贝(BatchMove)进度
2)提供让用户终止长时间拷贝的机制
显示拷贝进度的代码如下:
CBRType __stdcall ApiCallBackFun(
CBType ecbType, // Callback type
int iClientData, // Client callback data
void * pCbInfo // Call back info/Client)
{ AnsiString str;
if(ecbType==cbGENPROGRESS)
{
int j= StrToInt( ((CBPROGRESSDesc*)
pCbInfo)- >iPercentDone);
if(j< 0)
//假如iPercentDone为-1,则阐明szMsg的信息
{
str=((CBPROGRESSDesc*)pCbInfo)- >szMsg;
int pos=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 __stdcall ApiCallBackFun(…)
{
……
MSG amsg;
while(PeekMessage(&amsg,NULL,0,0,PM_REMOVE))
//从行列中打动静
{
TranslateMessage(&amsg); //翻译动静
DispatchMessage(&amsg); //分动员静
}
if (isContinue)
return cbrCONTINUE;
else
return cbrABORT;
}
以上的代码固然都用CBuilder编写,可是其道理同样合用于DELPHI。