在实际的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//回调数据结构指针
)
第一个参数是回调类型;第二个参数是回调数据,其解释同DbiRegisterCallBack的第三个参数;第三个是回调数据的指针,该数据的结构随回调类型的不同而不同。比如进度指示cbGENPROGRESS的数据结构是CBPROGRESSDesc,其定义如下:
struct CBPROGRESSDesc {
short iPercentDone; //进度的百分比
char szMsg[128]; //进度的文本信息
};
该结构的两个域同时只有一个起作用,第一个表示操作的进度百分比,当其为-1时表示第二个域起作用。第二个域用字符串表示进度信息,其格式为<String><:><Value>,比如:RecordsCopied:125